This commit is contained in:
@@ -2,6 +2,15 @@
|
||||
|
||||
Main case is 3373 https://rockfish.ayanova.com/default.htm#!/rfcaseEdit/3373
|
||||
|
||||
TODO: TAG-GROUP - bag of tags
|
||||
Modify the below and coded and tests and tables to now have feature for tag groups
|
||||
They will be in their own table with a dictionary table of tag ids and tag group id's to link
|
||||
They are used for read purposes, you never tag an item with a tag group, only query by group.
|
||||
If a group is deleted tags it contains are NOT deleted or changed
|
||||
If a tag is deleted it must be removed from any tag groups and if there are no other items in that tag group then the tag group must be deleted as well??
|
||||
- Or maybe is just an empty group with a warning symbol in ui
|
||||
Tag groups will become a major way to save filters for things because a lot of filtering will be by tags so this should be a solid feature.
|
||||
|
||||
FORMAT
|
||||
=-=-=-
|
||||
Copied from stack overflow
|
||||
@@ -38,9 +47,9 @@ Add a tag to an object type and id, start typing and a selection list fills in t
|
||||
- Don't allow the same tag more than once
|
||||
- Create a tag if not present (rights?)
|
||||
Show tags on an object
|
||||
Find any type or specific type items by a set of tags to include and a set of tags to exclude (i.e. has "red, yellow" but not "green")
|
||||
Search for text must allow specifying tags to refine
|
||||
Reporting will require filtering sources of report data by tags
|
||||
Find any type or specific type items by a set of tags (and/or tag groups) to include and a set of tags to exclude (i.e. has "red, yellow" but not "green")
|
||||
Search for text must allow specifying tags (and/or tag groups) to refine
|
||||
Reporting will require filtering sources of report data by tags (and/or tag groups)
|
||||
|
||||
METHODS REQUIRED IN TAG CONTROLLER
|
||||
|
||||
|
||||
10
devdocs/specs/dispatch-zone.txt
Normal file
10
devdocs/specs/dispatch-zone.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
DISPATCH ZONE SPECS
|
||||
|
||||
CASES
|
||||
|
||||
3472, 3473, 3439
|
||||
|
||||
REQUIREMENTS
|
||||
|
||||
Dispatch zones will be a collection now for users and also will replace scheduleable user groups
|
||||
|
||||
@@ -23,6 +23,7 @@ Overall plan for now: anything standing in the way of making the initial client
|
||||
- v7importusers
|
||||
- User route and controller and biz object
|
||||
- v7import once have users imported then proper attribution in eventlog of creator,modifier, created,modified
|
||||
- Tag groups (modify tags already coded)
|
||||
- Localized text
|
||||
- Search and search text indexing
|
||||
- Auto visible id number assigning code
|
||||
|
||||
315
server/AyaNova/biz/UserBiz.cs
Normal file
315
server/AyaNova/biz/UserBiz.cs
Normal file
@@ -0,0 +1,315 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.JsonPatch;
|
||||
using EnumsNET;
|
||||
using AyaNova.Util;
|
||||
using AyaNova.Api.ControllerHelpers;
|
||||
using AyaNova.Biz;
|
||||
using AyaNova.Models;
|
||||
|
||||
|
||||
namespace AyaNova.Biz
|
||||
{
|
||||
|
||||
|
||||
internal class UserBiz : BizObject, IJobObject
|
||||
{
|
||||
private readonly AyContext ct;
|
||||
public readonly long userId;
|
||||
private readonly AuthorizationRoles userRoles;
|
||||
|
||||
|
||||
internal UserBiz(AyContext dbcontext, long currentUserId, AuthorizationRoles UserRoles)
|
||||
{
|
||||
ct = dbcontext;
|
||||
userId = currentUserId;
|
||||
userRoles = UserRoles;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//CREATE
|
||||
internal async Task<User> CreateAsync(User inObj)
|
||||
{
|
||||
Validate(inObj, true);
|
||||
if (HasErrors)
|
||||
return null;
|
||||
else
|
||||
{
|
||||
//do stuff with User
|
||||
User outObj = inObj;
|
||||
outObj.OwnerId = userId;
|
||||
//SearchHelper(break down text fields, save to db)
|
||||
//TagHelper(collection of tags??)
|
||||
await ct.User.AddAsync(outObj);
|
||||
return outObj;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// GET
|
||||
|
||||
//Get one
|
||||
internal async Task<User> GetAsync(long fetchId)
|
||||
{
|
||||
//This is simple so nothing more here, but often will be copying to a different output object or some other ops
|
||||
return await ct.User.SingleOrDefaultAsync(m => m.Id == fetchId);
|
||||
}
|
||||
|
||||
//get many (paged)
|
||||
internal async Task<ApiPagedResponse<User>> GetManyAsync(IUrlHelper Url, string routeName, PagingOptions pagingOptions)
|
||||
{
|
||||
|
||||
pagingOptions.Offset = pagingOptions.Offset ?? PagingOptions.DefaultOffset;
|
||||
pagingOptions.Limit = pagingOptions.Limit ?? PagingOptions.DefaultLimit;
|
||||
|
||||
var items = await ct.User
|
||||
.OrderBy(m => m.Id)
|
||||
.Skip(pagingOptions.Offset.Value)
|
||||
.Take(pagingOptions.Limit.Value)
|
||||
.ToArrayAsync();
|
||||
|
||||
var totalRecordCount = await ct.User.CountAsync();
|
||||
var pageLinks = new PaginationLinkBuilder(Url, routeName, null, pagingOptions, totalRecordCount).PagingLinksObject();
|
||||
|
||||
ApiPagedResponse<User> pr = new ApiPagedResponse<User>(items, pageLinks);
|
||||
return pr;
|
||||
}
|
||||
|
||||
|
||||
//get picklist (paged)
|
||||
internal async Task<ApiPagedResponse<NameIdItem>> GetPickListAsync(IUrlHelper Url, string routeName, PagingOptions pagingOptions, string q)
|
||||
{
|
||||
pagingOptions.Offset = pagingOptions.Offset ?? PagingOptions.DefaultOffset;
|
||||
pagingOptions.Limit = pagingOptions.Limit ?? PagingOptions.DefaultLimit;
|
||||
|
||||
NameIdItem[] items;
|
||||
int totalRecordCount = 0;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(q))
|
||||
{
|
||||
items = await ct.User
|
||||
.Where(m => EF.Functions.ILike(m.Name, q))
|
||||
.OrderBy(m => m.Name)
|
||||
.Skip(pagingOptions.Offset.Value)
|
||||
.Take(pagingOptions.Limit.Value)
|
||||
.Select(m => new NameIdItem()
|
||||
{
|
||||
Id = m.Id,
|
||||
Name = m.Name
|
||||
}).ToArrayAsync();
|
||||
|
||||
totalRecordCount = await ct.User.Where(m => EF.Functions.ILike(m.Name, q)).CountAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
items = await ct.User
|
||||
.OrderBy(m => m.Name)
|
||||
.Skip(pagingOptions.Offset.Value)
|
||||
.Take(pagingOptions.Limit.Value)
|
||||
.Select(m => new NameIdItem()
|
||||
{
|
||||
Id = m.Id,
|
||||
Name = m.Name
|
||||
}).ToArrayAsync();
|
||||
|
||||
totalRecordCount = await ct.User.CountAsync();
|
||||
}
|
||||
|
||||
|
||||
|
||||
var pageLinks = new PaginationLinkBuilder(Url, routeName, null, pagingOptions, totalRecordCount).PagingLinksObject();
|
||||
|
||||
ApiPagedResponse<NameIdItem> pr = new ApiPagedResponse<NameIdItem>(items, pageLinks);
|
||||
return pr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//UPDATE
|
||||
//
|
||||
|
||||
//put
|
||||
internal bool Put(User dbObj, User inObj)
|
||||
{
|
||||
|
||||
//Replace the db object with the PUT object
|
||||
CopyObject.Copy(inObj, dbObj, "Id");
|
||||
//Set "original" value of concurrency token to input token
|
||||
//this will allow EF to check it out
|
||||
ct.Entry(dbObj).OriginalValues["ConcurrencyToken"] = inObj.ConcurrencyToken;
|
||||
|
||||
Validate(dbObj, false);
|
||||
if (HasErrors)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//patch
|
||||
internal bool Patch(User dbObj, JsonPatchDocument<User> objectPatch, uint concurrencyToken)
|
||||
{
|
||||
//Do the patching
|
||||
objectPatch.ApplyTo(dbObj);
|
||||
ct.Entry(dbObj).OriginalValues["ConcurrencyToken"] = concurrencyToken;
|
||||
Validate(dbObj, false);
|
||||
if (HasErrors)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//DELETE
|
||||
//
|
||||
|
||||
internal bool Delete(User dbObj)
|
||||
{
|
||||
//Determine if the object can be deleted, do the deletion tentatively
|
||||
//Probably also in here deal with tags and associated search text etc
|
||||
|
||||
ValidateCanDelete(dbObj);
|
||||
if (HasErrors)
|
||||
return false;
|
||||
ct.User.Remove(dbObj);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete child objects like tags and attachments and etc
|
||||
/// </summary>
|
||||
/// <param name="dbObj"></param>
|
||||
internal void DeleteChildren(User dbObj)
|
||||
{
|
||||
//TAGS
|
||||
TagMapBiz.DeleteAllForObject(new AyaTypeId(AyaType.User, dbObj.Id), ct);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//VALIDATION
|
||||
//
|
||||
|
||||
//Can save or update?
|
||||
private void Validate(User inObj, bool isNew)
|
||||
{
|
||||
//run validation and biz rules
|
||||
if (isNew)
|
||||
{
|
||||
//NEW Users must be active
|
||||
if (inObj.Active == null || ((bool)inObj.Active) == false)
|
||||
{
|
||||
AddError(ValidationErrorType.InvalidValue, "Active", "New User must be active");
|
||||
}
|
||||
}
|
||||
|
||||
//OwnerId required
|
||||
if (!isNew)
|
||||
{
|
||||
if (inObj.OwnerId == 0)
|
||||
AddError(ValidationErrorType.RequiredPropertyEmpty, "OwnerId");
|
||||
}
|
||||
|
||||
//Name required
|
||||
if (string.IsNullOrWhiteSpace(inObj.Name))
|
||||
AddError(ValidationErrorType.RequiredPropertyEmpty, "Name");
|
||||
|
||||
//Name must be less than 255 characters
|
||||
if (inObj.Name.Length > 255)
|
||||
AddError(ValidationErrorType.LengthExceeded, "Name", "255 max");
|
||||
|
||||
//If name is otherwise OK, check that name is unique
|
||||
if (!PropertyHasErrors("Name"))
|
||||
{
|
||||
//Use Any command is efficient way to check existance, it doesn't return the record, just a true or false
|
||||
if (ct.User.Any(m => m.Name == inObj.Name && m.Id != inObj.Id))
|
||||
{
|
||||
AddError(ValidationErrorType.NotUnique, "Name");
|
||||
}
|
||||
}
|
||||
|
||||
//Start date AND end date must both be null or both contain values
|
||||
if (inObj.StartDate == null && inObj.EndDate != null)
|
||||
AddError(ValidationErrorType.RequiredPropertyEmpty, "StartDate");
|
||||
|
||||
if (inObj.StartDate != null && inObj.EndDate == null)
|
||||
AddError(ValidationErrorType.RequiredPropertyEmpty, "EndDate");
|
||||
|
||||
//Start date before end date
|
||||
if (inObj.StartDate != null && inObj.EndDate != null)
|
||||
if (inObj.StartDate > inObj.EndDate)
|
||||
AddError(ValidationErrorType.StartDateMustComeBeforeEndDate, "StartDate");
|
||||
|
||||
//Enum is valid value
|
||||
|
||||
if (!inObj.Roles.IsValid())
|
||||
{
|
||||
AddError(ValidationErrorType.InvalidValue, "Roles");
|
||||
}
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//Can delete?
|
||||
private void ValidateCanDelete(User inObj)
|
||||
{
|
||||
//whatever needs to be check to delete this object
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//JOB / OPERATIONS
|
||||
//
|
||||
public async Task HandleJobAsync(OpsJob job)
|
||||
{
|
||||
//Hand off the particular job to the corresponding processing code
|
||||
//NOTE: If this code throws an exception the caller (JobsBiz::ProcessJobsAsync) will automatically set the job to failed and log the exeption so
|
||||
//basically any error condition during job processing should throw up an exception if it can't be handled
|
||||
switch (job.JobType)
|
||||
{
|
||||
case JobType.TestUserJob:
|
||||
await ProcessTestJobAsync(job);
|
||||
break;
|
||||
default:
|
||||
throw new System.ArgumentOutOfRangeException($"UserBiz.HandleJob-> Invalid job type{job.JobType.ToString()}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// /// Handle the test job
|
||||
/// </summary>
|
||||
/// <param name="job"></param>
|
||||
private async Task ProcessTestJobAsync(OpsJob job)
|
||||
{
|
||||
var sleepTime = 30 * 1000;
|
||||
//Simulate a long running job here
|
||||
JobsBiz.UpdateJobStatus(job.GId, JobStatus.Running, ct);
|
||||
JobsBiz.LogJob(job.GId, $"UserBiz::ProcessTestJob started, sleeping for {sleepTime} seconds...", ct);
|
||||
//Uncomment this to test if the job prevents other routes from running
|
||||
//result is NO it doesn't prevent other requests, so we are a-ok for now
|
||||
await Task.Delay(sleepTime);
|
||||
JobsBiz.LogJob(job.GId, "UserBiz::ProcessTestJob done sleeping setting job to finished", ct);
|
||||
JobsBiz.UpdateJobStatus(job.GId, JobStatus.Completed, ct);
|
||||
|
||||
}
|
||||
|
||||
//Other job handlers here...
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
}//eoc
|
||||
|
||||
|
||||
}//eons
|
||||
|
||||
72
server/AyaNova/biz/UserType.cs
Normal file
72
server/AyaNova/biz/UserType.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
namespace AyaNova.Biz
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// All AyaNova user types
|
||||
/// </summary>
|
||||
public enum UserType : int
|
||||
{
|
||||
Administrator = 1,
|
||||
Schedulable = 2,
|
||||
NonSchedulable = 3,
|
||||
Client = 4,
|
||||
HeadOffice = 5,
|
||||
Utility = 6,
|
||||
Subcontractor = 7
|
||||
}
|
||||
|
||||
|
||||
}//eons
|
||||
|
||||
/*
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
// UserTypes.cs
|
||||
// Implementation of Class UserTypes
|
||||
// CSLA type: enumeration
|
||||
// Created on: 07-Jun-2004 8:41:44 AM
|
||||
// Object design: Joyce
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
using System.ComponentModel;
|
||||
|
||||
|
||||
namespace GZTW.AyaNova.BLL {
|
||||
/// <summary>
|
||||
/// Variations of <see cref="User"/> types
|
||||
/// </summary>
|
||||
public enum UserTypes : int {
|
||||
/// <summary>
|
||||
/// This is the special account used only for updates, for customizing layout, etc
|
||||
/// </summary>
|
||||
[Description("LT:UserTypes.Label.Administrator")] Administrator = 1,
|
||||
/// <summary>
|
||||
/// This is a user that can be assigned to a workorder or schedule and consumes an AyaNova license
|
||||
/// </summary>
|
||||
[Description("LT:UserTypes.Label.Schedulable")] Schedulable = 2,
|
||||
/// <summary>
|
||||
/// Standard user that can use all aspects of AyaNova but can not be chosen to assign to a work order or schedule
|
||||
/// Does not consume an AyaNova license
|
||||
/// </summary>
|
||||
[Description("LT:UserTypes.Label.NonSchedulable")] NonSchedulable = 3,
|
||||
/// <summary>
|
||||
/// Client's login user account for WBI
|
||||
/// </summary>
|
||||
[Description("LT:UserTypes.Label.Client")] Client = 4,
|
||||
/// <summary>
|
||||
/// HeadOffice's login user account for WBI
|
||||
/// </summary>
|
||||
[Description("LT:UserTypes.Label.HeadOffice")] HeadOffice = 5,
|
||||
/// <summary>
|
||||
/// Utility user type for internal processing with limited functionality
|
||||
/// ATTN. API users: Do not create or set a user to this type in your code, you will be dissapointed and will break things. :)
|
||||
/// </summary>
|
||||
[Description("UTILITY")] Utility = 6
|
||||
//*** IF MORE ADDED USER OBJECT USERTYPE PROPERTIES
|
||||
// BROKEN RULES MUST BE UPDATED AND SQL SERVER USER TABLE CHECK CONSTRAINT
|
||||
// FOR USERTYPE VALUE ***
|
||||
|
||||
}//end UserTypes
|
||||
|
||||
}//end namespace GZTW.AyaNova.BLL
|
||||
*/
|
||||
@@ -11,14 +11,107 @@ namespace AyaNova.Models
|
||||
public uint ConcurrencyToken { get; set; }
|
||||
[Required]
|
||||
public long OwnerId { get; set; }
|
||||
[Required]
|
||||
public bool Active { get; set; }
|
||||
[Required]
|
||||
public string Name { get; set; }
|
||||
[Required]
|
||||
public string Login { get; set; }
|
||||
[Required]
|
||||
public string Password { get; set; }
|
||||
[Required]
|
||||
public string Salt { get; set; }
|
||||
public AuthorizationRoles Roles { get; set; }
|
||||
[Required]
|
||||
public AuthorizationRoles Roles { get; set; }
|
||||
[Required]
|
||||
public long LocaleId { get; set; }
|
||||
public string DlKey { get; set; }
|
||||
public DateTime? DlKeyExpire { get; set; }
|
||||
public long LocaleId { get; set; }
|
||||
|
||||
[Required]
|
||||
public UserType UserType { get; set; }
|
||||
public string EmployeeNumber { get; set; }
|
||||
public string Notes { get; set; }
|
||||
public long? ClientId { get; set; }
|
||||
public long? HeadOfficeId { get; set; }
|
||||
public long? SubVendorId { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
/*
|
||||
v7 export record sample
|
||||
{
|
||||
"DefaultLanguage": "Custom English",
|
||||
"DefaultServiceTemplateID": "ca83a7b8-4e5f-4a7b-a02b-9cf78d5f983f",
|
||||
"UserType": 2,
|
||||
"Active": true,
|
||||
"ClientID": "00000000-0000-0000-0000-000000000000",
|
||||
"HeadOfficeID": "00000000-0000-0000-0000-000000000000",
|
||||
"MemberOfGroup": "0f8a80ff-4b03-4114-ae51-2d13b812dd65",
|
||||
"Created": "03/21/2005 07:19 AM",
|
||||
"Modified": "09/15/2015 12:22 PM",
|
||||
"Creator": "2ecc77fc-69e2-4a7e-b88d-bd0ecaf36aed",
|
||||
"Modifier": "1d859264-3f32-462a-9b0c-a67dddfdf4d3",
|
||||
"ID": "1d859264-3f32-462a-9b0c-a67dddfdf4d3",
|
||||
"FirstName": "Hank",
|
||||
"LastName": "Rearden",
|
||||
"Initials": "HR",
|
||||
"EmployeeNumber": "EMP1236",
|
||||
"PageAddress": "",
|
||||
"PageMaxText": 24,
|
||||
"Phone1": "",
|
||||
"Phone2": "",
|
||||
"EmailAddress": "",
|
||||
"UserCertifications": [
|
||||
{
|
||||
"Created": "12/22/2005 02:07 PM",
|
||||
"Creator": "2ecc77fc-69e2-4a7e-b88d-bd0ecaf36aed",
|
||||
"Modified": "12/22/2005 02:08 PM",
|
||||
"Modifier": "2ecc77fc-69e2-4a7e-b88d-bd0ecaf36aed",
|
||||
"ID": "4492360c-43e4-4209-9f33-30691b0808ed",
|
||||
"UserCertificationID": "b2f26359-7c42-4218-923a-e949f3ef1f85",
|
||||
"UserID": "1d859264-3f32-462a-9b0c-a67dddfdf4d3",
|
||||
"ValidStartDate": "2005-10-11T00:00:00-07:00",
|
||||
"ValidStopDate": "2006-10-11T00:00:00-07:00"
|
||||
}
|
||||
],
|
||||
"UserSkills": [
|
||||
{
|
||||
"Created": "12/22/2005 02:06 PM",
|
||||
"Creator": "2ecc77fc-69e2-4a7e-b88d-bd0ecaf36aed",
|
||||
"Modified": "12/22/2005 02:08 PM",
|
||||
"Modifier": "2ecc77fc-69e2-4a7e-b88d-bd0ecaf36aed",
|
||||
"ID": "1dc5ce96-f411-4885-856e-5bdb3ad79728",
|
||||
"UserSkillID": "2e6f8b65-594c-4f6c-9cd6-e14a562daba8",
|
||||
"UserID": "1d859264-3f32-462a-9b0c-a67dddfdf4d3"
|
||||
},
|
||||
{
|
||||
"Created": "12/22/2005 02:06 PM",
|
||||
"Creator": "2ecc77fc-69e2-4a7e-b88d-bd0ecaf36aed",
|
||||
"Modified": "12/22/2005 02:08 PM",
|
||||
"Modifier": "2ecc77fc-69e2-4a7e-b88d-bd0ecaf36aed",
|
||||
"ID": "88e476d3-7526-45f5-a0dd-706c8053a63f",
|
||||
"UserSkillID": "47a4ee94-b0e9-41b5-afe5-4b4f2c981877",
|
||||
"UserID": "1d859264-3f32-462a-9b0c-a67dddfdf4d3"
|
||||
}
|
||||
],
|
||||
"Notes": "",
|
||||
"VendorID": "06e502c2-69ba-4e88-8efb-5b53c1687740",
|
||||
"RegionID": "f856423a-d468-4344-b7b8-121e466738c6",
|
||||
"DispatchZoneID": "00000000-0000-0000-0000-000000000000",
|
||||
"SubContractor": false,
|
||||
"DefaultWarehouseID": "d45eab37-b6e6-4ad2-9163-66d7ba83a98c",
|
||||
"Custom1": "",
|
||||
"Custom2": "",
|
||||
"Custom3": "",
|
||||
"Custom4": "",
|
||||
"Custom5": "",
|
||||
"Custom6": "",
|
||||
"Custom7": "",
|
||||
"Custom8": "",
|
||||
"Custom9": "",
|
||||
"Custom0": "",
|
||||
"ScheduleBackColor": -2097216,
|
||||
"TimeZoneOffset": null
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -139,8 +139,8 @@ namespace AyaNova.Util
|
||||
AyaNova.Biz.PrimeData.PrimeLocales(ct);
|
||||
|
||||
//Add user table
|
||||
exec("CREATE TABLE auser (id BIGSERIAL PRIMARY KEY, ownerid bigint not null, name varchar(255) not null, " +
|
||||
"login text not null, password text not null, salt text not null, roles integer not null, localeid bigint REFERENCES alocale (id), " +
|
||||
exec("CREATE TABLE auser (id BIGSERIAL PRIMARY KEY, ownerid bigint not null, active bool not null, name varchar(255) not null, " +
|
||||
"login text not null, password text not null, salt text not null, roles integer not null, localeid bigint not null REFERENCES alocale (id), " +
|
||||
"dlkey text, dlkeyexpire timestamp)");
|
||||
|
||||
//Prime the db with the default MANAGER account
|
||||
|
||||
Reference in New Issue
Block a user