Compare commits

..

No commits in common. "8c14a30a6349498da9e7c9fa28c5cddf3a530fe1" and "1e633e97c753ad3caafde28c3b2fa66da6b9bed9" have entirely different histories.

267 changed files with 580 additions and 44663 deletions

6
.gitignore vendored
View File

@ -8,9 +8,3 @@
**.csproj.user
*spa.min.*
**spa.min.*
/FlexitimeUI/_ReSharper.Caches/*
*/_ReSharper.Caches/*
**/_ReSharper.Caches/**
**v3.ncrunchsolution.user
**UpgradeLog*.htm

View File

@ -2,7 +2,7 @@
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:bal="http://schemas.microsoft.com/wix/BalExtension">
<Bundle Name="FlexiTimeSystemInstaller"
Version="0.2.3.0"
Version="0.2.1.0"
Manufacturer="Chris Watts"
UpgradeCode="d38e92db-48f9-40c3-9a6f-d76fbd07326e">
<BootstrapperApplicationRef Id="WixStandardBootstrapperApplication.RtfLicense">

View File

@ -12,7 +12,7 @@ namespace Interfaces
/// Returns <see cref="UserList"/> with full list of users,
/// plus a total user count. Pagination options are supported.
/// </returns>
UserList GetUsers(int pageNumber = -1, int pageSize = -1, int groupId = -1, SortOptions sort = SortOptions.None);
UserList GetUsers(int pageNumber = -1, int pageSize = -1, int groupId = -1);
/// <summary>
/// Search the user list for the following string
/// </summary>

View File

@ -72,7 +72,6 @@
<Compile Include="OperationResponse.cs" />
<Compile Include="Policy.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SortOptions.cs" />
<Compile Include="TimeLog.cs" />
<Compile Include="TimeLogList.cs" />
<Compile Include="User.cs" />

View File

@ -1,17 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Interfaces
{
public enum SortOptions
{
None,
FirstNameAscending,
FirstNameDescending,
LastNameAscending,
LastNameDescending,
}
}

View File

@ -1,33 +1,30 @@
using System;
using System.Collections.Generic;
namespace Interfaces
{
public class UserList
{
public UserList()
{
Users = new List<User>();
PageSize = 10;
}
public string Query { get; set; }
public int UserCount { get { return Users.Count; } }
public int TotalUserCount { get; set; }
public List<User> Users { get; set; }
public SortOptions SelectedSortOption { get; set; }
public int PageCount
{
get
{
if (TotalUserCount < PageSize)
return 1;
return (int)Math.Ceiling(Convert.ToDouble(TotalUserCount) / Convert.ToDouble(PageSize));
}
}
public Group GroupFilter { get; set; }
public int PageSize { get; set; }
public int PageNumber { get; set; }
}
using System.Collections.Generic;
namespace Interfaces
{
public class UserList
{
public UserList()
{
Users = new List<User>();
PageSize = 10;
}
public string Query { get; set; }
public int UserCount { get { return Users.Count; } }
public int TotalUserCount { get; set; }
public List<User> Users { get; set; }
public int PageCount
{
get
{
if (TotalUserCount < PageSize)
return 1;
return (TotalUserCount / PageSize);
}
}
public Group GroupFilter { get; set; }
public int PageSize { get; set; }
public int PageNumber { get; set; }
}
}

View File

@ -16,18 +16,20 @@ namespace SQLiteRepository
nameof(TimeLogDb.CalendarWeek) + "=? and " + nameof(TimeLogDb.Year) + "=?)";
public const string GET_ALL_USERS =
"select * from " + nameof(UserIdentity) + " ut "
"select * from " + nameof(UserIdentity) + " ut "
+ "where "
+ "EXISTS( select " + nameof(GroupDb.GroupId)
+ " from " + nameof(GroupDb)
+ " where " + nameof(GroupDb.GroupName) + " = 'Archived') AND "
+ "NOT EXISTS( select * from " + nameof(UserGroupJoinDb) + " ugp where "
+ nameof(UserGroupJoinDb.UserId_FK) + " = ut.Id"
+ " and " + nameof(UserGroupJoinDb.GroupId_FK) + " = ( "
+ "select " + nameof(GroupDb.GroupId)
+ " from " + nameof(GroupDb)
+ " where " + nameof(GroupDb.GroupName) + " = 'Archived') )"
+ "order by ? collate nocase ?, ? collate nocase ?";
+ " from " + nameof(GroupDb)
+ " where " + nameof(GroupDb.GroupName) + " = 'Archived') AND "
+ "NOT EXISTS( select * from " + nameof(UserGroupJoinDb) + " ugp where "
+ nameof(UserGroupJoinDb.UserId_FK) + " = ut.Id"
+ " and " + nameof(UserGroupJoinDb.GroupId_FK) + " = ( "
+ "select " + nameof(GroupDb.GroupId)
+ " from " + nameof(GroupDb)
+ " where " + nameof(GroupDb.GroupName) + " = 'Archived') )"
+ "order by "
+ nameof(UserIdentity.LastName) + " collate nocase, "
+ nameof(UserIdentity.FirstName) + " collate nocase";
public const string GET_ALL_USERS_PAGINATE =
"select * from " + nameof(UserIdentity) + " ut "
@ -41,18 +43,18 @@ namespace SQLiteRepository
+ "select " + nameof(GroupDb.GroupId)
+ " from " + nameof(GroupDb)
+ " where " + nameof(GroupDb.GroupName) + " = 'Archived') )"
+ " order by "
+ "{0} collate nocase {1}, "
+ "{2} collate nocase {3} "
+ "limit {4} offset {5}";
+ "order by "
+ nameof(UserIdentity.LastName) + " collate nocase, "
+ nameof(UserIdentity.FirstName) + " collate nocase "
+ "limit ? offset ?";
public const string GET_ALL_USERS_BY_GROUP =
"select u." + nameof(UserIdentity.Id) + ", u." + nameof(UserIdentity.FirstName) + ", u." +
nameof(UserIdentity.LastName) + ", u." + nameof(UserIdentity.HoursPerWeek) + ", u." +
nameof(UserIdentity.IsContractor) + " from " + nameof(UserIdentity) + " u left join " +
nameof(UserGroupJoinDb) + " ugj on ugj." + nameof(UserGroupJoinDb.UserId_FK) + " = u." +
nameof(UserIdentity.Id) + " where ugj." + nameof(UserGroupJoinDb.GroupId_FK) + "= {0} " +
"order by u.{1} collate nocase {2}, u.{3} collate nocase {4}";
nameof(UserIdentity.Id) + " where ugj." + nameof(UserGroupJoinDb.GroupId_FK) + "=? order by u." +
nameof(UserIdentity.LastName) + " collate nocase, u." + nameof(UserIdentity.LastName) + " collate nocase";
public const string GET_USER_BY_ID =
"select * from " + nameof(UserIdentity) + " where " + nameof(UserIdentity.Id) + "=?";
@ -94,18 +96,7 @@ namespace SQLiteRepository
nameof(TimeLogDb.SwipeEventDateTime) + " desc LIMIT 1";
public const string GET_TOTAL_USER_COUNT =
"select count(1) from " + nameof(UserIdentity) + " ut "
+ "where "
+ "EXISTS( select " + nameof(GroupDb.GroupId)
+ " from " + nameof(GroupDb)
+ " where " + nameof(GroupDb.GroupName) + " = 'Archived') "
+ "AND NOT EXISTS( select * from " + nameof(UserGroupJoinDb) + " ugp where "
+ nameof(UserGroupJoinDb.UserId_FK) + " = ut.Id"
+ " and " + nameof(UserGroupJoinDb.GroupId_FK) + " = ( "
+ "select " + nameof(GroupDb.GroupId)
+ " from " + nameof(GroupDb)
+ " where " + nameof(GroupDb.GroupName) + " = 'Archived') )";
//"select Max(" + nameof(UserIdentity.Id) + ") from " + nameof(UserIdentity);
"select Max(" + nameof(UserIdentity.Id) + ") from " + nameof(UserIdentity);
public const string GET_USER_CONTRACTED_HOURS =
"select " + nameof(UserIdentity.HoursPerWeek) + " From UserIdentity where " + nameof(UserIdentity.Id) + "=?";

View File

@ -45,10 +45,10 @@ namespace SQLiteRepository
CheckForDbUpgrade();
}
public UserList GetUsers(int pageNumber = -1, int pageSize = -1, int groupId = -1, SortOptions sort = SortOptions.None)
public UserList GetUsers(int pageNumber = -1, int pageSize = -1, int groupId = -1)
{
var ret = new UserList();
List<UserIdentity> users = GetUserList(groupId, pageSize, pageNumber, sort);
List<UserIdentity> users = GetUserList(groupId, pageSize, pageNumber);
var userCount = GetUserCount();
@ -115,7 +115,7 @@ namespace SQLiteRepository
ret.Users.Add(userObj);
}
ret.PageSize = 20;
ret.PageNumber = (int)Math.Ceiling((double)ret.Users.Count / (double)ret.PageSize);
ret.PageNumber = (int)Math.Ceiling((double)ret.Users.Count/(double)ret.PageSize);
return ret;
}
@ -249,7 +249,7 @@ namespace SQLiteRepository
_connection.Query<CardUniqueId>(SQLiteProcedures.GET_CARDS_BY_USER_ID, user.UserId)
.Select(IdentifierConverter.ConvertToIdentifierDto).ToList();
var cardsToRemove = currentCards.Exclude(user.AssociatedIdentifiers, x => x.Id).Select(x => x.Id).ToList();
#region Update/Create User
int userId;
@ -290,7 +290,7 @@ namespace SQLiteRepository
#region Update Card/Unique Id entries/associations
//make sure all identifiers are associated to the right user
UpdateIdentifierUserId(existingCards.Select(x => x.Id).ToList(), userId);
UpdateIdentifierUserId(existingCards.Select(x=>x.Id).ToList(), userId);
//make sure to remove all identifiers that have been deselected in edit are removed from user
UpdateIdentifierUserId(cardsToRemove, Constants.UNASSIGNED_CARD_USER_ID);
@ -317,7 +317,7 @@ namespace SQLiteRepository
else
{
_logger.Debug("Using supplied log time: {0}", logTime.ToString("o"));
}
}
#endregion
var ret = new LogEventResponse();
@ -336,7 +336,7 @@ namespace SQLiteRepository
UpdateIdentifierLastUsed(DateTimeOffset.UtcNow, ident.Id);
logId = -1;
//dont try to log any timelogs against this card, as it is unassigned to a user.
ret.ProcessResponse = OperationResponse.SUCCESS;
ret.ProcessResponse=OperationResponse.SUCCESS;
ret.Direction = LogDirection.UNKNOWN;
return ret;
}
@ -373,11 +373,11 @@ namespace SQLiteRepository
#endregion
#region Get the current time (for swiping). and calendar week/year to help recall the data.
var calendarWeek = GetIso8601CalendarWeek(logTime);
var year = logTime.Year;
#endregion
var timeLog = new TimeLogDb
{
SwipeEventDateTime = DateTime.UtcNow,
@ -397,7 +397,7 @@ namespace SQLiteRepository
ret.ProcessResponse = OperationResponse.SUCCESS;
return ret;
}
public OperationResponse CreateGroup(Group group, out int groupId)
{
var query = _connection.Query<GroupDb>(SQLiteProcedures.GET_GROUP_BY_NAME, group.Name);
@ -485,8 +485,8 @@ namespace SQLiteRepository
public OperationResponse CreateLog(TimeLog log)
{
log.CalendarWeek = GetIso8601CalendarWeek(log.EventTime.UtcDateTime);
log.Year = log.EventTime.Year;
log.Year= log.EventTime.Year;
var dbLog = TimeLogConverter.ConvertFromTimeLogDto(log);
dbLog.IdentifierId = ConvertSourceToIdentifierId(log.Source);
@ -497,13 +497,13 @@ namespace SQLiteRepository
_connection.Insert(dbLog);
return OperationResponse.CREATED;
}
public OperationResponse UpdateLog(TimeLog log)
{
var query = _connection.Query<TimeLogDb>(
SQLiteProcedures.GET_TIMELOG_ENTRY, log.Id);
if (!query.Any())
if(!query.Any())
return OperationResponse.FAILED;
if (log.CalendarWeek > 52 || log.CalendarWeek < 1)
@ -516,8 +516,8 @@ namespace SQLiteRepository
}
_connection.ExecuteScalar<TimeLogDb>(
SQLiteProcedures.UPDATE_TIMELOG_ENTRY,
log.UserId, (LogDirectionDb)(int)log.Direction, log.EventTime, log.CalendarWeek, log.Year,
(LogSourceDb)(int)log.Source, log.Id);
log.UserId, (LogDirectionDb) (int) log.Direction, log.EventTime, log.CalendarWeek, log.Year,
(LogSourceDb) (int) log.Source, log.Id);
return OperationResponse.UPDATED;
}
@ -526,19 +526,8 @@ namespace SQLiteRepository
{
var query = $"select * from {nameof(PolicyDb)}";
var policies = _connection.Query<PolicyDb>(query);
if (!policies.Any())
{
return new Policy()
{
ChangeAuthor = string.Empty,
ChangeDate = DateTime.UtcNow,
ChangeDescription = string.Empty,
Html=string.Empty,
Markdown = string.Empty,
Version = "0"
};
}
return PolicyConverter.ConvertToPolicyDto(policies.OrderByDescending(x => x.Version).FirstOrDefault());
}
@ -546,7 +535,7 @@ namespace SQLiteRepository
{
if (string.IsNullOrEmpty(policy.Version))
{
policy.Version = (GetNextPolicyVersion() + 1).ToString();
policy.Version = (GetNextPolicyVersion()+1).ToString();
}
var policyDb = PolicyConverter.ConvertFromPolicyDto(policy);
_connection.Insert(policyDb);
@ -566,56 +555,24 @@ namespace SQLiteRepository
return _connection.ExecuteScalar<int>(SQLiteProcedures.GET_TOTAL_USER_COUNT);
}
private List<UserIdentity> GetUserList(int groupId = -1, int pageSize = -1, int pageNumber = -1, SortOptions sort = SortOptions.None)
private List<UserIdentity> GetUserList(int groupId = -1, int pageSize=-1, int pageNumber=-1)
{
List<UserIdentity> users;
var orderByFirst = nameof(UserIdentity.LastName);
var firstOrderDir = "ASC";
var orderBySecond = nameof(UserIdentity.FirstName);
var secondOrderDir = "ASC";
if (sort != SortOptions.None)
{
switch (sort)
{
case SortOptions.FirstNameAscending:
orderByFirst = nameof(UserIdentity.FirstName);
firstOrderDir = "ASC";
orderBySecond = nameof(UserIdentity.LastName);
secondOrderDir = "ASC";
break;
case SortOptions.FirstNameDescending:
orderByFirst = nameof(UserIdentity.FirstName);
firstOrderDir = "DESC";
orderBySecond = nameof(UserIdentity.LastName);
secondOrderDir = "ASC";
break;
case SortOptions.LastNameDescending:
firstOrderDir = "DESC";
break;
}
}
if (pageNumber != -1 && pageSize != -1)
{
var qString = string.Format(SQLiteProcedures.GET_ALL_USERS_PAGINATE, orderByFirst, firstOrderDir,
orderBySecond, secondOrderDir, pageSize, (pageNumber - 1) * pageSize);
users = _connection.Query<UserIdentity>(qString);
users = _connection.Query<UserIdentity>(SQLiteProcedures.GET_ALL_USERS_PAGINATE,
pageSize, (pageNumber - 1) * pageSize);
}
else if (groupId != -1)
{
var qString = string.Format(SQLiteProcedures.GET_ALL_USERS_BY_GROUP, groupId,
orderByFirst, firstOrderDir,
orderBySecond, secondOrderDir);
users =
_connection.Query<UserIdentity>(qString);
_connection.Query<UserIdentity>(
SQLiteProcedures.GET_ALL_USERS_BY_GROUP,
groupId);
}
else
{
users = _connection.Query<UserIdentity>(SQLiteProcedures.GET_ALL_USERS,
orderByFirst, firstOrderDir,
orderBySecond, secondOrderDir);
users = _connection.Query<UserIdentity>(SQLiteProcedures.GET_ALL_USERS);
}
return users;
@ -663,7 +620,7 @@ namespace SQLiteRepository
foreach (var upgradeScript in upgradeScripts)
{
using (var stream = assembly.GetManifestResourceStream(upgradeScript))
using (var str = new StreamReader(stream))
using(var str = new StreamReader(stream))
{
var script = str.ReadToEnd();
_logger.Trace("Executing upgrade script with name: {0}", upgradeScript);
@ -676,7 +633,7 @@ namespace SQLiteRepository
private void SetDbVersion(string vers)
{
_connection.DeleteAll<DbVersion>();
_connection.Insert(new DbVersion { VersionNumber = vers });
_connection.Insert(new DbVersion {VersionNumber = vers});
_logger.Trace("Set Database version to: {0}", vers);
}
@ -757,7 +714,7 @@ namespace SQLiteRepository
EventTime = x.SwipeEventDateTime,
UserId = x.UserId_FK,
Year = x.Year
}).OrderBy(x => x.EventTime.UtcDateTime).ToList();
}).OrderBy(x=>x.EventTime.UtcDateTime).ToList();
var dict = new Dictionary<DayOfWeek, DailyLogs>();
var logList = new List<DailyLogs>();
@ -769,7 +726,7 @@ namespace SQLiteRepository
}
//add the logs to the respective day of the week.
foreach (var log in timeLogs.OrderBy(x => x.EventTime))
foreach (var log in timeLogs.OrderBy(x=>x.EventTime))
{
dict[log.EventTime.DayOfWeek].Logs.Add(log);
}

View File

@ -1,247 +1,217 @@
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
<Fragment>
<PropertyRef Id="STARTSERVICEONINSTALL"/>
<ComponentGroup Id="DataCenterComponents">
<ComponentRef Id="URLReservation"/>
<ComponentRef Id="DataCenterFirewallConfig"/>
<ComponentGroupRef Id="DataCenterFileGroup"/>
<ComponentRef Id="ServiceConfiguration"/>
</ComponentGroup>
<ComponentGroup Id="DataCenterFileGroup" Directory="INSTALLFOLDER">
<Component Id="CastleCore" Guid="{1B3813B5-C09B-47FB-85C7-6D3F37CADD8C}">
<File Id="Castle.Core.dll"
Source="$(var.WindowsDataCenter.TargetDir)\Castle.Core.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="MicrosoftOwin" Guid="{45563976-8620-4447-9DFE-216DEF263D5A}">
<File Id="Microsoft.Owin.dll"
Source="$(var.WindowsDataCenter.TargetDir)\Microsoft.Owin.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="MicrosoftOwinFileSystems" Guid="{45B6E4C5-FDD3-4B59-84A3-C58C8626BF61}">
<File Id="Microsoft.Owin.FileSystems.dll"
Source="$(var.WindowsDataCenter.TargetDir)Microsoft.Owin.FileSystems.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="MicrosoftOwinHostHttpListener" Guid="{5C9206E7-8C1B-4713-BE3B-8E754172ABE5}">
<File Id="Microsoft.Owin.Host.HttpListener.dll"
Source="$(var.WindowsDataCenter.TargetDir)Microsoft.Owin.Host.HttpListener.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="MicrosoftOwinHosting" Guid="{1A250453-173F-4FC6-BD72-5E3B3A67569B}">
<File Id="Microsoft.Owin.Hosting.dll"
Source="$(var.WindowsDataCenter.TargetDir)Microsoft.Owin.Hosting.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="MicrosoftOwinStaticFiles" Guid="{8F6B1D17-8D7C-4320-8BF6-26D6B3C5115E}">
<File Id="Microsoft.Owin.StaticFiles.dll"
Source="$(var.WindowsDataCenter.TargetDir)Microsoft.Owin.StaticFiles.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="MicrosoftWebInsfrastructure" Guid="{BCBEE2B7-3E75-4744-8B95-5928E69A8807}">
<File Id="Microsoft.Web.Infrastructure.dll"
Source="$(var.WindowsDataCenter.TargetDir)Microsoft.Web.Infrastructure.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="NewtonsoftJson" Guid="{972634F3-DEC1-41D9-B48E-3836D6C1FD1D}">
<File Id="Newtonsoft.Json.dll"
Source="$(var.WindowsDataCenter.TargetDir)Newtonsoft.Json.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="Ninject" Guid="{31D0E89E-F1F7-4B7D-84E9-229D5C7042B5}">
<File Id="Ninject.dll"
Source="$(var.WindowsDataCenter.TargetDir)Ninject.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="NinjectExtensionsContextPreservationDll" Guid="{580EAE73-1DBF-4F79-9153-8D5055C3A31F}">
<File Id="Ninject.Extensions.ContextPreservation.dll"
Source="$(var.WindowsDataCenter.TargetDir)Ninject.Extensions.ContextPreservation.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="NinjectExtensionsFactory" Guid="{AA81AB6B-A980-4CF5-AC4D-E5E80ECD52C7}">
<File Id="Ninject.Extensions.Factory.dll"
Source="$(var.WindowsDataCenter.TargetDir)Ninject.Extensions.Factory.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="NinjectExtensionsNamedScopeDll" Guid="{76602BE7-8A83-4BC2-AAA3-2A06F82353F0}">
<File Id="Ninject.Extensions.NamedScope.dll"
Source="$(var.WindowsDataCenter.TargetDir)Ninject.Extensions.NamedScope.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="NinjectExtensionsXmlDll" Guid="{3809511F-9C51-4C05-AFE5-F59160EDCB71}">
<File Id="Ninject.Extensions.Xml.dll"
Source="$(var.WindowsDataCenter.TargetDir)Ninject.Extensions.Xml.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="NinjectWebCommon" Guid="{A4D175A1-D68C-4E20-9E52-A0A8052B9E1D}">
<File Id="Ninject.Web.Common.dll"
Source="$(var.WindowsDataCenter.TargetDir)Ninject.Web.Common.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="NinjectWebCommonOwinHost" Guid="{156FF6D0-00DA-46BC-80C9-816F2CA8975E}">
<File Id="Ninject.Web.Common.OwinHost.dll"
Source="$(var.WindowsDataCenter.TargetDir)Ninject.Web.Common.OwinHost.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="NinjectWebCommonWebHost" Guid="{595CDF8D-3728-4342-BFD9-34AD23294C6C}">
<File Id="Ninject.Web.Common.Web.dll"
Source="$(var.WindowsDataCenter.TargetDir)Ninject.Web.Common.WebHost.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="NinjectWebWebApi" Guid="{20D06854-C822-4780-8EDC-D1601A835626}">
<File Id="Ninject.Web.WebApi.dll"
Source="$(var.WindowsDataCenter.TargetDir)Ninject.Web.WebApi.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="NinjectWebWebApiOwinHost" Guid="{024E48A9-CFAA-4AE1-9B55-399AC27E93B0}">
<File Id="Ninject.Web.WebApi.OwinHost.dll"
Source="$(var.WindowsDataCenter.TargetDir)Ninject.Web.WebApi.OwinHost.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="NinjectConfig" Guid="{85F6271F-7251-4E88-B44D-8EBDA4CBD29D}">
<File Id="NinjectConfig.xml"
Source="$(var.WindowsDataCenter.TargetDir)NinjectConfig.xml"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="Owin" Guid="{B4A90231-65AB-40F1-BEEC-EE02B9DBD4EC}">
<File Id="Owin.dll"
Source="$(var.WindowsDataCenter.TargetDir)Owin.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="RazorEngine" Guid="{494124DC-1BD1-42C2-A249-53757289AAA4}">
<File Id="RazorEngine.dll"
Source="$(var.WindowsDataCenter.TargetDir)RazorEngine.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="SystemDataSQLite" Guid="{C56CC08B-BCC5-494E-9E48-3E01402FFF97}">
<File Id="System.Data.SQLite.dll"
Source="$(var.WindowsDataCenter.TargetDir)System.Data.SQLite.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="SystemNetHttpFormatting" Guid="{53AB40B1-7A74-4B6B-9F0E-B1A74EE27467}">
<File Id="System.Net.Http.Formatting.dll"
Source="$(var.WindowsDataCenter.TargetDir)System.Net.Http.Formatting.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="SystemWebHttp" Guid="{B94609B3-B65E-4FB5-B226-B831D8597A9B}">
<File Id="System.Web.Http.dll"
Source="$(var.WindowsDataCenter.TargetDir)System.Web.Http.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="SystemWebHttpOwin" Guid="{99FF5381-765A-4B03-8668-1083ED498CAB}">
<File Id="System.Web.Http.Owin.dll"
Source="$(var.WindowsDataCenter.TargetDir)System.Web.Http.Owin.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="SystemWebHttpWebHost" Guid="{29E7462F-32C3-46E2-8295-FBF561D37E40}">
<File Id="System.Web.Http.WebHost.dll"
Source="$(var.WindowsDataCenter.TargetDir)System.Web.Http.WebHost.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="SystemWebRazor" Guid="{EB7F6B70-F224-4FD7-93DF-138CC06F01CB}">
<File Id="System.Web.Razor.dll"
Source="$(var.WindowsDataCenter.TargetDir)System.Web.Razor.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="WebActivatorEx" Guid="{7AD6A619-5946-4E94-AAD3-1AEEB88272C0}">
<File Id="WebActivatorEx.dll"
Source="$(var.WindowsDataCenter.TargetDir)WebActivatorEx.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="WindowsDataCenter" Guid="{CD36814E-8C1B-4AC1-BDC7-D1595D2A4728}">
<File Id="WindowsDataCenter.exe"
Source="$(var.WindowsDataCenter.TargetDir)WindowsDataCenter.exe"
KeyPath="yes"/>
<ServiceInstall Id="DataCenterService"
Start="auto"
ErrorControl="normal"
Type="ownProcess"
Vital="yes"
Name="FlexitimeDataCenterService"
DisplayName="FlexitimeDataCenterService"
Description="Flexitime API Database and Website"
Account="LocalSystem">
<util:PermissionEx User="Everyone"
ServicePauseContinue="yes"
ServiceQueryStatus="yes"
ServiceStart="yes"
ServiceStop="yes"
ServiceUserDefinedControl="yes" />
<ServiceConfig Id="FlexiTimeServiceConfig"
OnInstall="yes"
DelayedAutoStart="0" />
<util:ServiceConfig FirstFailureActionType="restart"
SecondFailureActionType="restart"
ThirdFailureActionType="restart"
ResetPeriodInDays="1"
RestartServiceDelayInSeconds="10" />
</ServiceInstall>
</Component>
<Component Id="StartDataCenterServiceOnInsall"
Guid="{884828DC-A47E-4CEF-AEEB-E46FFDFFBED3}"
KeyPath="yes">
<ServiceControl Id="FlexitimeDataCenterStarter"
Start="install"
Stop="uninstall"
Remove="uninstall"
Name="FlexitimeDataCenterService"
Wait="yes"/>
<Condition><![CDATA[STARTSERVICEONINSTALL <> "false"]]></Condition>
</Component>
<Component Id="WindowsDataCenterExeConfig" Guid="{CBFA53EB-0663-4A79-9990-451FC1105A12}">
<File Id="WindowsDataCenter.exe.config"
Source="$(var.WindowsDataCenter.TargetDir)WindowsDataCenter.exe.config"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="WindowsDataCenterXml" Guid="{35A62230-0052-4EBE-9775-DE0266BADB4E}">
<File Id="WindowsDataCenter.xml"
Source="$(var.WindowsDataCenter.TargetDir)WindowsDataCenter.XML"
KeyPath="yes"
Checksum="yes"/>
</Component>
</ComponentGroup>
<Component Id="ServiceConfiguration" Directory="INSTALLFOLDER" Guid="{E49ABD61-0CCA-45A2-A525-DF85A0493FF7}" KeyPath="yes">
<util:XmlFile Id="SetServicePort"
Action="setValue"
ElementPath="//appSettings/add[\[]@key='WebsiteHttpPort'[\]]/@value"
Value="[SERVICEPORT]"
File="[#WindowsDataCenter.exe.config]"
SelectionLanguage="XPath"
Sequence="1" />
</Component>
</Fragment>
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
<Fragment>
<PropertyRef Id="STARTSERVICEONINSTALL"/>
<ComponentGroup Id="DataCenterComponents">
<ComponentRef Id="URLReservation"/>
<ComponentRef Id="DataCenterFirewallConfig"/>
<ComponentGroupRef Id="DataCenterFileGroup"/>
<ComponentRef Id="ServiceConfiguration"/>
</ComponentGroup>
<ComponentGroup Id="DataCenterFileGroup" Directory="INSTALLFOLDER">
<Component Id="MicrosoftOwin" Guid="{45563976-8620-4447-9DFE-216DEF263D5A}">
<File Id="Microsoft.Owin.dll"
Source="$(var.WindowsDataCenter.TargetDir)\Microsoft.Owin.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="MicrosoftOwinFileSystems" Guid="{45B6E4C5-FDD3-4B59-84A3-C58C8626BF61}">
<File Id="Microsoft.Owin.FileSystems.dll"
Source="$(var.WindowsDataCenter.TargetDir)Microsoft.Owin.FileSystems.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="MicrosoftOwinHostHttpListener" Guid="{5C9206E7-8C1B-4713-BE3B-8E754172ABE5}">
<File Id="Microsoft.Owin.Host.HttpListener.dll"
Source="$(var.WindowsDataCenter.TargetDir)Microsoft.Owin.Host.HttpListener.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="MicrosoftOwinHosting" Guid="{1A250453-173F-4FC6-BD72-5E3B3A67569B}">
<File Id="Microsoft.Owin.Hosting.dll"
Source="$(var.WindowsDataCenter.TargetDir)Microsoft.Owin.Hosting.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="MicrosoftOwinStaticFiles" Guid="{8F6B1D17-8D7C-4320-8BF6-26D6B3C5115E}">
<File Id="Microsoft.Owin.StaticFiles.dll"
Source="$(var.WindowsDataCenter.TargetDir)Microsoft.Owin.StaticFiles.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="MicrosoftWebInsfrastructure" Guid="{BCBEE2B7-3E75-4744-8B95-5928E69A8807}">
<File Id="Microsoft.Web.Infrastructure.dll"
Source="$(var.WindowsDataCenter.TargetDir)Microsoft.Web.Infrastructure.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="NewtonsoftJson" Guid="{972634F3-DEC1-41D9-B48E-3836D6C1FD1D}">
<File Id="Newtonsoft.Json.dll"
Source="$(var.WindowsDataCenter.TargetDir)Newtonsoft.Json.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="Ninject" Guid="{31D0E89E-F1F7-4B7D-84E9-229D5C7042B5}">
<File Id="Ninject.dll"
Source="$(var.WindowsDataCenter.TargetDir)Ninject.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="NinjectExtensionsContextPreservationDll" Guid="{580EAE73-1DBF-4F79-9153-8D5055C3A31F}">
<File Id="Ninject.Extensions.ContextPreservation.dll"
Source="$(var.WindowsDataCenter.TargetDir)Ninject.Extensions.ContextPreservation.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="NinjectExtensionsNamedScopeDll" Guid="{76602BE7-8A83-4BC2-AAA3-2A06F82353F0}">
<File Id="Ninject.Extensions.NamedScope.dll"
Source="$(var.WindowsDataCenter.TargetDir)Ninject.Extensions.NamedScope.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="NinjectExtensionsXmlDll" Guid="{3809511F-9C51-4C05-AFE5-F59160EDCB71}">
<File Id="Ninject.Extensions.Xml.dll"
Source="$(var.WindowsDataCenter.TargetDir)Ninject.Extensions.Xml.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="NinjectWebCommon" Guid="{A4D175A1-D68C-4E20-9E52-A0A8052B9E1D}">
<File Id="Ninject.Web.Common.dll"
Source="$(var.WindowsDataCenter.TargetDir)Ninject.Web.Common.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="NinjectWebCommonOwinHost" Guid="{156FF6D0-00DA-46BC-80C9-816F2CA8975E}">
<File Id="Ninject.Web.Common.OwinHost.dll"
Source="$(var.WindowsDataCenter.TargetDir)Ninject.Web.Common.OwinHost.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="NinjectWebWebApi" Guid="{20D06854-C822-4780-8EDC-D1601A835626}">
<File Id="Ninject.Web.WebApi.dll"
Source="$(var.WindowsDataCenter.TargetDir)Ninject.Web.WebApi.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="NinjectWebWebApiOwinHost" Guid="{024E48A9-CFAA-4AE1-9B55-399AC27E93B0}">
<File Id="Ninject.Web.WebApi.OwinHost.dll"
Source="$(var.WindowsDataCenter.TargetDir)Ninject.Web.WebApi.OwinHost.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="NinjectConfig" Guid="{85F6271F-7251-4E88-B44D-8EBDA4CBD29D}">
<File Id="NinjectConfig.xml"
Source="$(var.WindowsDataCenter.TargetDir)NinjectConfig.xml"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="Owin" Guid="{B4A90231-65AB-40F1-BEEC-EE02B9DBD4EC}">
<File Id="Owin.dll"
Source="$(var.WindowsDataCenter.TargetDir)Owin.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="RazorEngine" Guid="{494124DC-1BD1-42C2-A249-53757289AAA4}">
<File Id="RazorEngine.dll"
Source="$(var.WindowsDataCenter.TargetDir)RazorEngine.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="SystemNetHttpFormatting" Guid="{53AB40B1-7A74-4B6B-9F0E-B1A74EE27467}">
<File Id="System.Net.Http.Formatting.dll"
Source="$(var.WindowsDataCenter.TargetDir)System.Net.Http.Formatting.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="SystemWebHttp" Guid="{B94609B3-B65E-4FB5-B226-B831D8597A9B}">
<File Id="System.Web.Http.dll"
Source="$(var.WindowsDataCenter.TargetDir)System.Web.Http.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="SystemWebHttpOwin" Guid="{99FF5381-765A-4B03-8668-1083ED498CAB}">
<File Id="System.Web.Http.Owin.dll"
Source="$(var.WindowsDataCenter.TargetDir)System.Web.Http.Owin.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="SystemWebHttpWebHost" Guid="{29E7462F-32C3-46E2-8295-FBF561D37E40}">
<File Id="System.Web.Http.WebHost.dll"
Source="$(var.WindowsDataCenter.TargetDir)System.Web.Http.WebHost.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="WebActivatorEx" Guid="{7AD6A619-5946-4E94-AAD3-1AEEB88272C0}">
<File Id="WebActivatorEx.dll"
Source="$(var.WindowsDataCenter.TargetDir)WebActivatorEx.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="WindowsDataCenter" Guid="{CD36814E-8C1B-4AC1-BDC7-D1595D2A4728}">
<File Id="WindowsDataCenter.exe"
Source="$(var.WindowsDataCenter.TargetDir)WindowsDataCenter.exe"
KeyPath="yes"/>
<ServiceInstall Id="DataCenterService"
Start="auto"
ErrorControl="normal"
Type="ownProcess"
Vital="yes"
Name="FlexitimeDataCenterService"
DisplayName="FlexitimeDataCenterService"
Description="Flexitime API Database and Website"
Account="LocalSystem">
<util:PermissionEx User="Everyone"
ServicePauseContinue="yes"
ServiceQueryStatus="yes"
ServiceStart="yes"
ServiceStop="yes"
ServiceUserDefinedControl="yes" />
<ServiceConfig Id="FlexiTimeServiceConfig"
OnInstall="yes"
DelayedAutoStart="0" />
<util:ServiceConfig FirstFailureActionType="restart"
SecondFailureActionType="restart"
ThirdFailureActionType="restart"
ResetPeriodInDays="1"
RestartServiceDelayInSeconds="10" />
</ServiceInstall>
</Component>
<Component Id="StartDataCenterServiceOnInsall"
Guid="{884828DC-A47E-4CEF-AEEB-E46FFDFFBED3}"
KeyPath="yes">
<ServiceControl Id="FlexitimeDataCenterStarter"
Start="install"
Stop="uninstall"
Remove="uninstall"
Name="FlexitimeDataCenterService"
Wait="yes"/>
<Condition><![CDATA[STARTSERVICEONINSTALL <> "false"]]></Condition>
</Component>
<Component Id="WindowsDataCenterExeConfig" Guid="{CBFA53EB-0663-4A79-9990-451FC1105A12}">
<File Id="WindowsDataCenter.exe.config"
Source="$(var.WindowsDataCenter.TargetDir)WindowsDataCenter.exe.config"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="WindowsDataCenterXml" Guid="{35A62230-0052-4EBE-9775-DE0266BADB4E}">
<File Id="WindowsDataCenter.xml"
Source="$(var.WindowsDataCenter.TargetDir)WindowsDataCenter.XML"
KeyPath="yes"
Checksum="yes"/>
</Component>
</ComponentGroup>
<Component Id="ServiceConfiguration" Directory="INSTALLFOLDER" Guid="{E49ABD61-0CCA-45A2-A525-DF85A0493FF7}" KeyPath="yes">
<util:XmlFile Id="SetServicePort"
Action="setValue"
ElementPath="//appSettings/add[\[]@key='WebsiteHttpPort'[\]]/@value"
Value="[SERVICEPORT]"
File="[#WindowsDataCenter.exe.config]"
SelectionLanguage="XPath"
Sequence="1" />
</Component>
</Fragment>
</Wix>

View File

@ -1,37 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<ComponentGroup Id="NLogComponents">
<ComponentGroupRef Id="NLoggerGroup"/>
<ComponentGroupRef Id="NLogConfigFiles"/>
</ComponentGroup>
<ComponentGroup Id="NLoggerGroup" Directory="INSTALLFOLDER">
<Component Id="NLoggerDll" Guid="{48765F51-86CA-4AD8-A34E-E4705353E101}">
<File Id="NLogLoggerDll"
Source="$(var.NLogLogger.TargetDir)\NLogLogger.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="NLogDll" Guid="{D9D15A6F-9602-4B6B-BB76-AD0768D14780}">
<File Id="NLog.dll"
Source="$(var.NLogLogger.TargetDir)\NLog.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="NLogLoggerDllConfig" Guid="{E3808BFF-5156-4123-B108-BECD0A5A99BC}">
<File Id="NLogLoggerDllConfig"
Source="$(var.NLogLogger.TargetDir)\NLogLogger.dll.config"
KeyPath="yes" />
</Component>
</ComponentGroup>
<ComponentGroup Id="NLogConfigFiles" Directory="CONFIGFILES">
<Component Id="NLogConfigFile" Guid="{DE1F65F2-C286-4495-984C-9BF5861A0567}">
<File Id="NLogConfigXml"
Source="$(var.NLogLogger.TargetDir)\NLogConfig.xml"
KeyPath="yes" />
</Component>
</ComponentGroup>
</Fragment>
</Wix>
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<ComponentGroup Id="NLogComponents">
<ComponentGroupRef Id="NLoggerGroup"/>
<ComponentGroupRef Id="NLogConfigFiles"/>
</ComponentGroup>
<ComponentGroup Id="NLoggerGroup" Directory="INSTALLFOLDER">
<Component Id="NLoggerDll" Guid="{48765F51-86CA-4AD8-A34E-E4705353E101}">
<File Id="NLogLoggerDll"
Source="$(var.NLogLogger.TargetDir)\NLogLogger.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="NLogDll" Guid="{D9D15A6F-9602-4B6B-BB76-AD0768D14780}">
<File Id="NLog.dll"
Source="$(var.NLogLogger.TargetDir)\NLog.dll"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="NLogLoggerDllConfig" Guid="{E3808BFF-5156-4123-B108-BECD0A5A99BC}">
<File Id="NLogLoggerDllConfig"
Source="$(var.NLogLogger.TargetDir)\NLogLogger.dll.config"
KeyPath="yes" />
</Component>
</ComponentGroup>
<ComponentGroup Id="NLogConfigFiles" Directory="CONFIGFILES">
<Component Id="NLogConfigFile" Guid="{DE1F65F2-C286-4495-984C-9BF5861A0567}">
<File Id="NLogConfigXml"
Source="$(var.NLogLogger.TargetDir)\NLogConfig.xml"
KeyPath="yes" />
</Component>
</ComponentGroup>
</Fragment>
</Wix>

View File

@ -1,243 +1,195 @@
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<DirectoryRef Id="INSTALLFOLDER">
<Directory Id="STATICWWW" Name="www" >
<Directory Id="STATICWWWCSS" Name="css"/>
<Directory Id="STATICWWWFONTS" Name="fonts" />
<Directory Id="STATICWWWJS" Name="js" />
</Directory>
</DirectoryRef>
<ComponentGroup Id="WebSiteComponents">
<ComponentGroupRef Id="WWWStaticFiles"/>
<ComponentGroupRef Id="WWWStaticJs"/>
<ComponentGroupRef Id="WWWStaticFonts"/>
<ComponentGroupRef Id="WWWStaticCss"/>
</ComponentGroup>
<ComponentGroup Id="WWWStaticFiles" Directory="STATICWWW">
<Component Id="FavIcon" Guid="{8A5C2ED6-C261-4733-9E11-63D937AF0222}">
<File Id="favicon.ico"
Source="$(var.WindowsDataCenter.TargetDir)\www\favicon.ico"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="IndexHtml" Guid="{418BCB07-AB9B-4A67-AB31-E9378B806A2E}">
<File Id="index.html"
Source="$(var.WindowsDataCenter.TargetDir)\www\index.html"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="AdminHtml" Guid="{3094817F-74B4-4848-A882-7FDC6B401D86}">
<File Id="admin.html"
Source="$(var.WindowsDataCenter.TargetDir)\www\admin.html"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="SpaCss" Guid="{A5998F36-2CD9-4EB3-A008-12AE1018F153}">
<File Id="spa.css"
Source="$(var.WindowsDataCenter.TargetDir)\www\spa.css"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="SpaMinCss" Guid="{668AC0CC-8139-4B87-8507-44FC83F6FE07}">
<File Id="spa.min.css"
Source="$(var.WindowsDataCenter.TargetDir)\www\spa.min.css"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="SpaJs" Guid="{0A669ADE-9804-4C2A-9970-BF7EFE52F003}">
<File Id="spa.js"
Source="$(var.WindowsDataCenter.TargetDir)\www\spa.js"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="SpaMinJs" Guid="{5B29F7F4-F805-4066-8F65-848119AD5EFD}">
<File Id="spa.min.js"
Source="$(var.WindowsDataCenter.TargetDir)\www\spa.min.js"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="AdminJs" Guid="{B529C781-4AE7-48F1-A2E7-6987A37BD225}">
<File Id="admin.js"
Source="$(var.WindowsDataCenter.TargetDir)\www\admin.js"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="AdminMinJs" Guid="{1AEBE106-FA98-45E6-B2ED-69806BF9AAFE}">
<File Id="admin.min.js"
Source="$(var.WindowsDataCenter.TargetDir)\www\admin.min.js"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="HelpersJs" Guid="{4C8A424D-023E-4311-B100-5BA5AA7DFBAD}">
<File Id="Helpers.js"
Source="$(var.WindowsDataCenter.TargetDir)\www\Helpers.js"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="HelpersMinJs" Guid="{42142DE2-3952-4B14-A64A-12D67DD657C4}">
<File Id="Helpers.min.js"
Source="$(var.WindowsDataCenter.TargetDir)\www\Helpers.min.js"
KeyPath="yes"
Checksum="yes"/>
</Component>
</ComponentGroup>
<ComponentGroup Id="WWWStaticJs" Directory="STATICWWWJS">
<Component Id="BootstrapJs" Guid="{DF7034BA-B315-4AB5-983A-6470D773DF27}">
<File Id="Bootstrap.js"
Source="$(var.WindowsDataCenter.TargetDir)www\js\bootstrap.js"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="BootstrapMinJs" Guid="{48C14202-7096-4FCA-91BA-7BB24F0D5C22}">
<File Id="Bootstrap.min.js"
Source="$(var.WindowsDataCenter.TargetDir)www\js\bootstrap.min.js"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="KnockoutContextMenuJs" Guid="{C48C75F7-E4C3-45D7-8041-0BF7A8EEC077}">
<File Id="knockout.contextmenu.js"
Source="$(var.WindowsDataCenter.TargetDir)www\js\knockout.contextmenu.js"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="EasyMdeMinJs" Guid="{F5659507-81DF-4ACC-9996-7CBFEF0CBE26}">
<File Id="easymde.min.js"
Source="$(var.WindowsDataCenter.TargetDir)www\js\easymde.min.js"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="HighlightJsMinJs" Guid="{E37EF672-31A0-45E4-A0ED-707685EA2E81}">
<File Id="highlightjs.min.js"
Source="$(var.WindowsDataCenter.TargetDir)www\js\highlightjs.min.js"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="HtmlParserMinJs" Guid="{788487B1-CC7C-4E8B-93C0-B865CEA916DB}">
<File Id="htmlparser.min.js"
Source="$(var.WindowsDataCenter.TargetDir)www\js\htmlparser.min.js"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="MarkedMinJs" Guid="{4B70E7F7-A883-4BB5-9F34-85BA9C585741}">
<File Id="marked.min.js"
Source="$(var.WindowsDataCenter.TargetDir)www\js\marked.min.js"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="PolicyObject" Guid="{6A5A8235-33CA-460C-A721-7078DB51925F}">
<File Id="PolicyObject.Js"
Source="$(var.WindowsDataCenter.TargetDir)www\js\PolicyObject.js"
KeyPath="yes"
Checksum="yes"/>
</Component>
</ComponentGroup>
<ComponentGroup Id="WWWStaticFonts" Directory="STATICWWWFONTS" >
<Component Id="GlyphiconsHalflingsRegular" Guid="{AB0C30AC-B19F-4CC1-BE18-6C83BDD16950}">
<File Id="Glyphicons_Halflings_RegularEot"
Source="$(var.WindowsDataCenter.TargetDir)www\fonts\glyphicons-halflings-regular.eot"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="GlyphiconsHalflingsRegularSvg" Guid="{1B67BD41-8328-463C-AEB1-EAA5496D6086}">
<File Id="Glyphicons_Halflings_RegularSvg"
Source="$(var.WindowsDataCenter.TargetDir)www\fonts\glyphicons-halflings-regular.svg"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="GlyphiconsHalflingsRegularTtf" Guid="{F2B0B4D8-5809-4524-AFD5-9E25F04EA3AE}">
<File Id="Glyphicons_Halflings_RegularTtf"
Source="$(var.WindowsDataCenter.TargetDir)www\fonts\glyphicons-halflings-regular.ttf"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="GlyphiconsHalflingsRegularWoff" Guid="{5D644DF7-23C6-4B8B-9FCF-F6F9F0A1FFDC}">
<File Id="Glyphicons_Halflings_RegularWoff"
Source="$(var.WindowsDataCenter.TargetDir)www\fonts\glyphicons-halflings-regular.woff"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="GlyphiconsHalflingsRegularWoff2" Guid="{87EE2C7C-D6A6-424B-A30B-06E771406C71}">
<File Id="Glyphicons_Halflings_RegularWoff2"
Source="$(var.WindowsDataCenter.TargetDir)www\fonts\glyphicons-halflings-regular.woff2"
KeyPath="yes"
Checksum="yes"/>
</Component>
</ComponentGroup>
<ComponentGroup Id="WWWStaticCss" Directory="STATICWWWCSS">
<Component Id="BootstrapCss" Guid="{EBB792CD-23B2-4806-B92B-CA4CD3293148}">
<File Id="bootstrap.css"
Source="$(var.WindowsDataCenter.TargetDir)www\css\bootstrap.css"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="BootstrapCssMap" Guid="{6E7BEFA7-61F3-4740-96A2-9036F6ABF273}">
<File Id="bootstrap.css.map"
Source="$(var.WindowsDataCenter.TargetDir)www\css\bootstrap.css.map"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="BootstrapMinCss" Guid="{10BBCC84-63EB-4B4A-816B-460888896A79}">
<File Id="bootstrap.min.css"
Source="$(var.WindowsDataCenter.TargetDir)www\css\bootstrap.min.css"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="BootstrapMinCssMap" Guid="{909F1752-1472-48DD-99DA-116D391FB12B}">
<File Id="bootstrap.min.css.map"
Source="$(var.WindowsDataCenter.TargetDir)www\css\bootstrap.min.css.map"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="BootstrapThemeCss" Guid="{0EA9E1E9-F077-4B75-A829-6CC93AF55684}">
<File Id="bootstrap_theme.css"
Source="$(var.WindowsDataCenter.TargetDir)www\css\bootstrap-theme.css"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="BootstrapGridCssMap" Guid="{5732BF4E-5963-49BD-B466-D9D3CF0B137A}">
<File Id="bootstrap_theme.css.map"
Source="$(var.WindowsDataCenter.TargetDir)www\css\bootstrap-theme.css.map"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="BootstrapThemeMinCss" Guid="{1281A9F1-28E0-4748-B9B4-8C8C68EDD9E5}">
<File Id="bootstrap_theme.min.css"
Source="$(var.WindowsDataCenter.TargetDir)www\css\bootstrap-theme.min.css"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="BootstrapThemeMinCssMap" Guid="{F054150C-0E9A-4EB8-8EB2-B5BD81D004CE}">
<File Id="bootstrap_theme.min.css.map"
Source="$(var.WindowsDataCenter.TargetDir)www\css\bootstrap-theme.min.css.map"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="KnockoutContextMenuCss" Guid="{D5C5C0F3-500C-419C-A327-5342AFF08204}">
<File Id="knockout.contextmenu.css"
Source="$(var.WindowsDataCenter.TargetDir)www\css\knockout.contextmenu.css"
KeyPath="yes"
Checksum="yes" />
</Component>
<Component Id="EasyMdeMinCss" Guid="{F84BEE5C-1682-4D1D-97F4-6A68D8090BA1}">
<File Id="easymde.min.css"
Source="$(var.WindowsDataCenter.TargetDir)www\css\easymde.min.css"
KeyPath="yes"
Checksum="yes" />
</Component>
<Component Id="HighlightjsMinCss" Guid="{F65BABFC-1EDB-4F60-807B-3BB23FE6E144}">
<File Id="highlightjs.min.css"
Source="$(var.WindowsDataCenter.TargetDir)www\css\highlightjs.min.css"
KeyPath="yes"
Checksum="yes" />
</Component>
</ComponentGroup>
</Fragment>
</Wix>
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<DirectoryRef Id="INSTALLFOLDER">
<Directory Id="STATICWWW" Name="www" >
<Directory Id="STATICWWWCSS" Name="css"/>
<Directory Id="STATICWWWFONTS" Name="fonts" />
<Directory Id="STATICWWWJS" Name="js" />
</Directory>
</DirectoryRef>
<ComponentGroup Id="WebSiteComponents">
<ComponentGroupRef Id="WWWStaticFiles"/>
<ComponentGroupRef Id="WWWStaticJs"/>
<ComponentGroupRef Id="WWWStaticFonts"/>
<ComponentGroupRef Id="WWWStaticCss"/>
</ComponentGroup>
<ComponentGroup Id="WWWStaticFiles" Directory="STATICWWW">
<Component Id="IndexHtml" Guid="{418BCB07-AB9B-4A67-AB31-E9378B806A2E}">
<File Id="index.html"
Source="$(var.WindowsDataCenter.TargetDir)\www\index.html"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="AdminHtml" Guid="{3094817F-74B4-4848-A882-7FDC6B401D86}">
<File Id="admin.html"
Source="$(var.WindowsDataCenter.TargetDir)\www\admin.html"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="SpaCss" Guid="{A5998F36-2CD9-4EB3-A008-12AE1018F153}">
<File Id="spa.css"
Source="$(var.WindowsDataCenter.TargetDir)\www\spa.css"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="SpaMinCss" Guid="{668AC0CC-8139-4B87-8507-44FC83F6FE07}">
<File Id="spa.min.css"
Source="$(var.WindowsDataCenter.TargetDir)\www\spa.min.css"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="SpaJs" Guid="{0A669ADE-9804-4C2A-9970-BF7EFE52F003}">
<File Id="spa.js"
Source="$(var.WindowsDataCenter.TargetDir)\www\spa.js"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="SpaMinJs" Guid="{5B29F7F4-F805-4066-8F65-848119AD5EFD}">
<File Id="spa.min.js"
Source="$(var.WindowsDataCenter.TargetDir)\www\spa.min.js"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="AdminJs" Guid="{B529C781-4AE7-48F1-A2E7-6987A37BD225}">
<File Id="admin.js"
Source="$(var.WindowsDataCenter.TargetDir)\www\admin.js"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="AdminMinJs" Guid="{1AEBE106-FA98-45E6-B2ED-69806BF9AAFE}">
<File Id="admin.min.js"
Source="$(var.WindowsDataCenter.TargetDir)\www\admin.min.js"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="HelpersJs" Guid="{4C8A424D-023E-4311-B100-5BA5AA7DFBAD}">
<File Id="Helpers.js"
Source="$(var.WindowsDataCenter.TargetDir)\www\Helpers.js"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="HelpersMinJs" Guid="{42142DE2-3952-4B14-A64A-12D67DD657C4}">
<File Id="Helpers.min.js"
Source="$(var.WindowsDataCenter.TargetDir)\www\Helpers.min.js"
KeyPath="yes"
Checksum="yes"/>
</Component>
</ComponentGroup>
<ComponentGroup Id="WWWStaticJs" Directory="STATICWWWJS">
<Component Id="BootstrapJs" Guid="{DF7034BA-B315-4AB5-983A-6470D773DF27}">
<File Id="Bootstrap.js"
Source="$(var.WindowsDataCenter.TargetDir)www\js\bootstrap.js"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="BootstrapMinJs" Guid="{48C14202-7096-4FCA-91BA-7BB24F0D5C22}">
<File Id="Bootstrap.min.js"
Source="$(var.WindowsDataCenter.TargetDir)www\js\bootstrap.min.js"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="KnockoutContextMenuJs" Guid="{C48C75F7-E4C3-45D7-8041-0BF7A8EEC077}">
<File Id="knockout.contextmenu.js"
Source="$(var.WindowsDataCenter.TargetDir)www\js\knockout.contextmenu.js"
KeyPath="yes"
Checksum="yes"/>
</Component>
</ComponentGroup>
<ComponentGroup Id="WWWStaticFonts" Directory="STATICWWWFONTS" >
<Component Id="GlyphiconsHalflingsRegular" Guid="{AB0C30AC-B19F-4CC1-BE18-6C83BDD16950}">
<File Id="Glyphicons_Halflings_RegularEot"
Source="$(var.WindowsDataCenter.TargetDir)www\fonts\glyphicons-halflings-regular.eot"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="GlyphiconsHalflingsRegularSvg" Guid="{1B67BD41-8328-463C-AEB1-EAA5496D6086}">
<File Id="Glyphicons_Halflings_RegularSvg"
Source="$(var.WindowsDataCenter.TargetDir)www\fonts\glyphicons-halflings-regular.svg"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="GlyphiconsHalflingsRegularTtf" Guid="{F2B0B4D8-5809-4524-AFD5-9E25F04EA3AE}">
<File Id="Glyphicons_Halflings_RegularTtf"
Source="$(var.WindowsDataCenter.TargetDir)www\fonts\glyphicons-halflings-regular.ttf"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="GlyphiconsHalflingsRegularWoff" Guid="{5D644DF7-23C6-4B8B-9FCF-F6F9F0A1FFDC}">
<File Id="Glyphicons_Halflings_RegularWoff"
Source="$(var.WindowsDataCenter.TargetDir)www\fonts\glyphicons-halflings-regular.woff"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="GlyphiconsHalflingsRegularWoff2" Guid="{87EE2C7C-D6A6-424B-A30B-06E771406C71}">
<File Id="Glyphicons_Halflings_RegularWoff2"
Source="$(var.WindowsDataCenter.TargetDir)www\fonts\glyphicons-halflings-regular.woff2"
KeyPath="yes"
Checksum="yes"/>
</Component>
</ComponentGroup>
<ComponentGroup Id="WWWStaticCss" Directory="STATICWWWCSS">
<Component Id="BootstrapCss" Guid="{EBB792CD-23B2-4806-B92B-CA4CD3293148}">
<File Id="bootstrap.css"
Source="$(var.WindowsDataCenter.TargetDir)www\css\bootstrap.css"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="BootstrapCssMap" Guid="{6E7BEFA7-61F3-4740-96A2-9036F6ABF273}">
<File Id="bootstrap.css.map"
Source="$(var.WindowsDataCenter.TargetDir)www\css\bootstrap.css.map"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="BootstrapMinCss" Guid="{10BBCC84-63EB-4B4A-816B-460888896A79}">
<File Id="bootstrap.min.css"
Source="$(var.WindowsDataCenter.TargetDir)www\css\bootstrap.min.css"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="BootstrapMinCssMap" Guid="{909F1752-1472-48DD-99DA-116D391FB12B}">
<File Id="bootstrap.min.css.map"
Source="$(var.WindowsDataCenter.TargetDir)www\css\bootstrap.min.css.map"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="BootstrapThemeCss" Guid="{0EA9E1E9-F077-4B75-A829-6CC93AF55684}">
<File Id="bootstrap_theme.css"
Source="$(var.WindowsDataCenter.TargetDir)www\css\bootstrap-theme.css"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="BootstrapGridCssMap" Guid="{5732BF4E-5963-49BD-B466-D9D3CF0B137A}">
<File Id="bootstrap_theme.css.map"
Source="$(var.WindowsDataCenter.TargetDir)www\css\bootstrap-theme.css.map"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="BootstrapThemeMinCss" Guid="{1281A9F1-28E0-4748-B9B4-8C8C68EDD9E5}">
<File Id="bootstrap_theme.min.css"
Source="$(var.WindowsDataCenter.TargetDir)www\css\bootstrap-theme.min.css"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="BootstrapThemeMinCssMap" Guid="{F054150C-0E9A-4EB8-8EB2-B5BD81D004CE}">
<File Id="bootstrap_theme.min.css.map"
Source="$(var.WindowsDataCenter.TargetDir)www\css\bootstrap-theme.min.css.map"
KeyPath="yes"
Checksum="yes"/>
</Component>
<Component Id="KnockoutContextMenuCss" Guid="{D5C5C0F3-500C-419C-A327-5342AFF08204}">
<File Id="knockout.contextmenu.css"
Source="$(var.WindowsDataCenter.TargetDir)www\css\knockout.contextmenu.css"
KeyPath="yes"
Checksum="yes" />
</Component>
</ComponentGroup>
</Fragment>
</Wix>

View File

@ -30,18 +30,17 @@ namespace WindowsDataCenter
public IHttpActionResult GetUsers([FromUri] string query = ""
, [FromUri] int pageSize = -1
, [FromUri] int pageNumber = -1
, [FromUri] int groupId = -1
, [FromUri] SortOptions sort = SortOptions.None)
, [FromUri] int groupId = -1)
{
_logger.Trace("GetUsers called with arguments >> query: {0}, pageSize: {1}, pageNumber: {2}, groupFilter: {3}, sort: {4}", query, pageSize, pageNumber, groupId, sort);
_logger.Trace("GetUsers called with arguments >> query: {0}, pageSize: {1}, pageNumber: {2}, groupFilter: {3}", query, pageSize, pageNumber, groupId);
pageNumber = pageNumber == -1 ? 1 : pageNumber;
pageSize = GetPageSize(pageSize);
var userList = query != string.Empty
? _repo.Search(query)
: groupId != -1
? _repo.GetUsers(groupId: groupId, sort:sort)
: _repo.GetUsers(pageNumber, pageSize, sort:sort);
? _repo.GetUsers(groupId: groupId)
: _repo.GetUsers(pageNumber, pageSize);
_logger.Trace("Got UserList from Repository, UserCount: {0}", userList.UserCount);
if (query != string.Empty)
{
@ -58,7 +57,6 @@ namespace WindowsDataCenter
userList.PageNumber = pageNumber;
userList.PageSize = pageSize;
userList.SelectedSortOption = sort;
_logger.Trace("Returning UserList from GetUsers.");
var msg = Request.CreateResponse(HttpStatusCode.OK, userList);

View File

@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.2.3.0")]
[assembly: AssemblyFileVersion("0.2.3.0")]
[assembly: AssemblyVersion("0.2.1.0")]
[assembly: AssemblyFileVersion("0.2.1.0")]

View File

@ -119,7 +119,7 @@
<textarea id="policyEditor"></textarea>
</div>
<div id="preview" class="tab-pane fade in">
<div data-bind="html: policyData.Html" class="well-lg"></div>
<div data-bind="html: policyData.html" class="well-lg"></div>
</div>
</div>
<button class="btn btn-primary pull-right" data-toggle="modal" data-target="#saveDialog">Save</button><!--data-bind="click: $root.policySave"-->

View File

@ -22,7 +22,7 @@
var escaped = "unknown";
if (parserHandler.dom.length > 0) {
escaped = parserHandler.dom[0].raw.toLowerCase().trim().replace(/ /g, "-").replace(/#+/g,"");
escaped = parserHandler.dom[0].raw.toLowerCase().trim().replace(/ /g, "-");
}
return "<h" + level + " id=\"" + escaped + "\">"
@ -66,7 +66,7 @@
});
self.editor.codemirror.on("changes",
function () {
self.policyData.Html(self.editor.options.previewRender(self.editor.value()));
self.policyData.html(self.editor.options.previewRender(self.editor.value()));
});
self.uiPages = {
overview: "overview",
@ -169,9 +169,8 @@
self.policySave = function () {
var url = self.helpers.createRequestUrl(self.apiEndpoints.savePolicy, null, false);
self.policyData.Markdown(self.editor.value()); //make sure we update it, as it doesnt push the value back into the variable
//console.log(self.policyData();)rea#
var data = ko.toJS(self.policyData);
$.post(url, data, function() {
//console.log(self.policyData());
$.post(url, self.policyData(), function() {
}, "json") //todo: check this serialisation as the object is now complex.
.done(function() {
self.policyReload();

File diff suppressed because one or more lines are too long

View File

@ -86,23 +86,8 @@
<thead>
<tr>
<th class="col-md-1"></th>
<th class="col-md-3">
<div class="col-md-1">
<a href="#users?sort=firstAsc"><span data-bind="css:{ 'text-warning': $root.sortIsActive('firstAsc')}" class="glyphicon glyphicon-chevron-up"></span></a>
</div>
<div class="col-md-1">
<a href="#users?sort=firstDesc"><span data-bind="css:{ 'text-warning': $root.sortIsActive('firstDesc')}" class="glyphicon glyphicon-chevron-down"></span></a>
</div>
<span class="col-md-9">First Name</span>
</th>
<th class="col-md-3">
<div class="col-md-1">
<a href="#users?sort=lastAsc"><span data-bind="css:{ 'text-warning': $root.sortIsActive('lastAsc')}" class="glyphicon glyphicon-chevron-up"></span></a>
</div>
<div class="col-md-1">
<a href="#users?sort=lastDesc"><span data-bind="css:{ 'text-warning': $root.sortIsActive('lastDesc')}"class="glyphicon glyphicon-chevron-down"></span></a>
</div>
<span class="col-md-9">Last Name</span></th>
<th class="col-md-3">First Name</th>
<th class="col-md-3">Last Name</th>
<th class="col-md-1 text-center">Contractor</th>
<th/>
<th/>

View File

@ -3,29 +3,28 @@
if (typeof data === "undefined") {
data = createDefaultPolicy();
}
self.ChangeDate = ko.observable(data.ChangeDate);
self.ChangeDescription = ko.observable(data.ChangeDescription);
self.ChangeAuthor = ko.observable(data.ChangeAuthor);
self.Version = ko.observable(data.Version);
self.Markdown = ko.observable(data.Markdown);
self.Html = ko.observable(data.Html);
self.changeDate = ko.observable(data.changeDate);
self.description = ko.observable(data.description);
self.changeAuthor = ko.observable(data.changeAuthor);
self.version = ko.observable(data.version);
self.markdown = ko.observable(data.markdown);
self.html = ko.observable(data.html);
function createDefaultPolicy() {
return {
ChangeDate: moment().toISOString(),
Description: "",
ChangeAuthor: "",
Version: -1,
Markdown: "",
Html: ""
changeDate: moment().format(),
description: "",
changeAuthor: "",
version: -1,
markdown: "",
html: ""
};
}
self.update = function (data) {
if (!data) return;
self.ChangeDate(data.ChangeDate);
self.ChangeDescription(data.ChangeDescription);
self.ChangeAuthor(data.ChangeAuthor);
self.Version(data.Version);
self.Markdown(data.Markdown);
self.Html(data.Html);
self.update = function(data) {
self.changeDate(data.changeDate);
self.description(data.description);
self.changeAuthor(data.changeAuthor);
self.version(data.version);
self.markdown(data.markdown);
self.html(data.html);
}
}

View File

@ -1,10 +1,4 @@
const SortOptions = {
LastAsc: "LastNameAscending",
LastDesc: "LastNameDescending",
FirstAsc: "FirstNameAscending",
FirstDesc: "FirstNameDescending"
};
function DataVM() {
function DataVM() {
"use strict";
var self = this;
self.helpers = new Helpers();
@ -29,7 +23,6 @@ function DataVM() {
self.policyChangeDate = ko.observable(null);
self.policyChangeAuthor = ko.observable(null);
self.policyVersion = ko.observable(null);
self.selectedSort = ko.observable(null);
self.apiEndpoints = {
root: "http://localhost:8800",
getUserList: "/api/users",
@ -163,7 +156,7 @@ function DataVM() {
var args = [
{
key: "groupId",
value: data
value: datacreateContextMenu
}
];
var url = self.helpers.createRequestUrl("users", args, false, false);
@ -228,32 +221,25 @@ function DataVM() {
});
}
};
self.getUserList = function (pageSize, pageNumber, groupId, sort) {
var args = [];
self.getUserList = function (pageSize, pageNumber, groupId) {
var args = null;
if (pageSize && pageNumber) {
args.push(
args = [
{
key: "pageSize",
value: pageSize
});
args.push(
},
{
key: "pageNumber",
value: pageNumber
});
}
if (groupId) {
args.push(
}
];
} else if(groupId) {
args = [
{
key: "groupId",
value: groupId
});
}
if (sort) {
args.push({
key: "sort",
value: sort
});
}];
}
var url = self.helpers.createRequestUrl(self.apiEndpoints.getUserList, args, false);
$.getJSON(url, function (res) {
@ -275,9 +261,9 @@ function DataVM() {
self.assignErrorObject(errObj.errorCode, errObj.errorMessage, "getUserList");
});
};
self.searchUsers = function(query, sort) {
self.searchUsers = function(query) {
var url = self.helpers.createRequestUrl(self.apiEndpoints.getUserList,
[{ key: "query", value: query }, {key:"sort",value:sort}], false, false);
[{ key: "query", value: query }], false, false);
$.getJSON(url,
function(res) {
self.userList(res);
@ -410,27 +396,18 @@ function DataVM() {
self.assignUpdateHandler();
};
function createlog(data, event) {
var createDate = "";
if (data && data.UserId && data.IdentifierId) {
createDate = data.EventTime;
} else if (data && data.Day) {
var mondayDate = moment(moment(self.userTimeLogData().SelectedDate).day("Monday").toISOString());
createDate = mondayDate.add(data.Day-1, 'days').toISOString();
} else {
createDate = self.userTimeLogData().SelectedDate;
}
var logDateInitVal = moment(self.selectedTimeLogDate()).add((data.Day - 1), 'days').toISOString();
self.manualLog({
CalendarWeek:-1,
Direction:-1,
EventTime: createDate,
EventTime: logDateInitVal,
Id: -1,
IdentifierId: -1,
UserId: self.chosenTimeLogUserId,
Year: 0
});
$('#manualLogDialog').modal("show");
self.initialiseManualLogDateTimePicker(createDate);
self.initialiseManualLogDateTimePicker(logDateInitVal);
self.assignUpdateHandler();
};
function deleteLog(data) {
@ -449,46 +426,12 @@ function DataVM() {
maxDate: moment(date).endOf("week")
});
};
function convertSortOption(opt) {
if (!opt) {
return null
}
if (opt === "firstAsc") {
return SortOptions.FirstAsc;
}
if (opt === "firstDesc") {
return SortOptions.FirstDesc;
}
if (opt === "lastAsc") {
return SortOptions.LastAsc;
}
if (opt === "lastDesc") {
return SortOptions.LastDesc;
}
};
self.sortIsActive = function (option) {
if (option === "firstAsc" && self.userList().SelectedSortOption === SortOptions.FirstAsc) {
return true;
}
if (option === "firstDesc" && self.userList().SelectedSortOption === SortOptions.FirstDesc) {
return true;
}
if (option === "lastAsc" && self.userList().SelectedSortOption === SortOptions.LastAsc) {
return true;
}
if (option === "lastDesc" && self.userList().SelectedSortOption === SortOptions.LastDesc) {
return true;
}
return false;
};
Sammy(function () {
this.get("#users", function () {
var query = this.params.query;
var pageSize = this.params.pageSize;
var pageNumber = this.params.pageNumber;
var groupId = this.params.groupId;
var sort = convertSortOption(this.params.sort);
console.log(sort);
self.chosenMenuItemId("Home");
self.groupsList(null);
self.chosenUserDetails(null);
@ -501,11 +444,11 @@ function DataVM() {
}
self.getGroups(function (data) { self.groupsList(data); });
if (query)
self.searchUsers(query, sort);
self.searchUsers(query);
else if (groupId && groupId > 0)
self.getUserList(null, null, groupId, sort);
self.getUserList(null, null, groupId);
else
self.getUserList(pageSize, pageNumber, null,sort);
self.getUserList(pageSize, pageNumber);
});
this.get("#userData/:userId", function () {
self.chosenMenuItemId("Data");
@ -585,6 +528,7 @@ function DataVM() {
this.post("#manualLog",
function() {
self.createManualLog(self.manualLog());
$('#manualLogDialog').modal("hide");
//self.goToTimeLogs(self.chosenTimeLogUserId, null, [{ key: "selectedDate", value: self.selectedTimeLogDate() }]);
});

View File

@ -1,17 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Flexitime.DataAccess\Flexitime.DataAccess.csproj" />
<ProjectReference Include="..\Flexitime.Interfaces\Flexitime.Interfaces.csproj" />
</ItemGroup>
</Project>

View File

@ -1,97 +0,0 @@
using System;
using System.Linq;
using Flexitime.DataAccess;
using Flexitime.DataAccess.EF;
using Flexitime.DataAccess.Objects;
using Flexitime.Interfaces;
namespace DBTest
{
class Program
{
static void Main(string[] args)
{
var dbconnprovider = new TestDbConnProvider();
var db = new Database(dbconnprovider);
//var users = db.Users;
//var userDb = new FlexitimeDbContext(dbconnprovider);
//userDb.Database.EnsureCreated();
//var users = userDb.Users.ToList();
//AddUser(userDb);
//AddIdentifier( userDb);
//AddTimeLog(userDb);
//users = userDb.Users.ToList();
//var tlogs = userDb.TimeLogs.ToList();
//var idents = userDb.Identifiers.ToList();
}
private static void AddIdentifier(FlexitimeDbContext flexitimeDb)
{
var ident = new IdentifierDb
{
UniqueId = Guid.NewGuid(),
LastUsed = DateTime.UtcNow,
IsAssociatedToUser = true
};
var createdEntry = flexitimeDb.Add(ident).Entity;
var user = flexitimeDb.Users.First();
user.AssociatedIdentifiers.Add(createdEntry);
flexitimeDb.Update(user);
flexitimeDb.SaveChanges();
flexitimeDb.SaveChanges();
}
private static void AddUser(FlexitimeDbContext flexitimeDb)
{
var user = new UserDb
{
FirstName = "admin",
LastName = "admin",
UserName = "admin",
Password = "P@ssw0rd!",
IsContractor = false,
HoursPerWeek = 37.0
};
flexitimeDb.Add(user);
flexitimeDb.SaveChanges();
}
private static void AddTimeLog(FlexitimeDbContext flexitimeDbContext)
{
var timelog = new TimeLogDb
{
LogTime = DateTimeOffset.UtcNow
};
var ident = flexitimeDbContext.Identifiers.First();
timelog.Identifier = ident;
timelog.IdentifierId = ident.Id;
flexitimeDbContext.Add(timelog);
flexitimeDbContext.SaveChanges();
}
}
class TestDbConnProvider : IConnectionStringProvider
{
public string ConnectionString =>
"Data Source=.\\SQLSERVER2019;Initial Catalog=FlexitimeData;Integrated Security=SSPI;";
}
}

View File

@ -1,228 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AutoMapper;
using Flexitime.DataAccess.EF;
using Flexitime.DataAccess.Objects;
using Flexitime.Interfaces;
using Flexitime.Objects;
using Microsoft.EntityFrameworkCore;
using LogSourceDescriptor = Flexitime.Objects.LogSourceDescriptor;
namespace Flexitime.DataAccess
{
public class Database : IDataAccess
{
private FlexitimeDbContext db;
private IMapper mapper;
public Database(IConnectionStringProvider connectionStringProvider)
{
db = new FlexitimeDbContext(connectionStringProvider);
db.Database.EnsureCreated();
db.InsertBaseData();
mapper = MappingHelper.CreateMapper(new UserProfile(), new TimeLogProfile(), new GroupProfile(),
new TeamProfile(), new PermissionsProfile(), new ApplicationProfile(), new IdentifierProfile(), new LogDescriptorProfile());
}
public Task<List<User>> GetUsers()
{
return Task.Run(() => mapper.Map<List<User>>(db.Users
.Include(x => x.AssociatedIdentifiers)
.Include(x => x.DirectReports)
//.Include(x=>x.Groups)
//.Include(x=>x.Permissions)
.Include(x => x.Team)));
}
public Task<User> GetUserById(Guid id)
{
return Task.Run(()=>mapper.Map<User>(
db.Users.Where(x => x.Id == id)
.Include(x => x.AssociatedIdentifiers)
.Include(x => x.DirectReports)
//.Include(x=>x.Groups)
.Include(x => x.Permissions)
.Include(x => x.Team)
.FirstOrDefault()));
}
public async Task<User> AddUser(User newUser)
{
if (newUser.Id != Guid.Empty)
{
var existingUser = await GetUserById(newUser.Id);
if (existingUser != null)
{
return null;
}
}
newUser.Id = Guid.Empty;
var userToAdd = mapper.Map<UserDb>(newUser);
userToAdd.Id = Guid.Empty;
var result = db.Users.Add(userToAdd);
await db.SaveChangesAsync();
return mapper.Map<User>(result.Entity);
}
public Task<User> GetUserByUsername(string userName)
{
return Task.Run(() => mapper.Map<User>(
db.Users.Where(x => x.UserName == userName)
.Include(x => x.AssociatedIdentifiers)
.Include(x => x.DirectReports)
//.Include(x=>x.Groups)
.Include(x => x.Permissions)
.Include(x => x.Team)
.FirstOrDefault()));
}
public async Task<User> UpdateUser(User user)
{
var userToUpdate = mapper.Map<UserDb>(user);
var existing = db.Users.FirstOrDefault(x => x.Id == user.Id);
existing.AssociatedIdentifiers = userToUpdate.AssociatedIdentifiers;
existing.LineManager = userToUpdate.LineManager;
existing.Permissions = userToUpdate.Permissions;
existing.UserName = userToUpdate.UserName;
existing.DirectReports = userToUpdate.DirectReports;
existing.FirstName = userToUpdate.FirstName;
existing.LastName = userToUpdate.LastName;
existing.IsContractor = userToUpdate.IsContractor;
existing.Groups = userToUpdate.Groups;
existing.HoursPerWeek = userToUpdate.HoursPerWeek;
existing.Password = userToUpdate.Password;
existing.Team = userToUpdate.Team;
existing.TeamId = userToUpdate.Team.Id;
db.Users.Update(existing);
await db.SaveChangesAsync();
return await GetUserById(user.Id);
}
}
internal static class MappingHelper
{
public static IMapper CreateMapper(params Profile[] profiles)
{
var _profiles = profiles.ToList();
var mapperCfg = new MapperConfiguration(x => _profiles.ForEach(x.AddProfile));
mapperCfg.AssertConfigurationIsValid();
return new Mapper(mapperCfg);
}
}
public class UserProfile : Profile
{
public UserProfile()
{
CreateMap<User, UserDb>()
.ForMember(x => x.State,
opt => opt.MapFrom(x => (int)x.State))
.ReverseMap();
CreateMap<UserDb, User>()
.ForMember(dst => dst.State,
opt => opt.MapFrom(x => Enum.Parse(typeof(UserState), x.State.ToString())))
.ForMember(dst => dst.DirectReportIds,
opt => opt.MapFrom(src => src.DirectReports.Select(x => x.Id)))
.ReverseMap();
}
}
public class TimeLogProfile : Profile
{
public TimeLogProfile()
{
CreateMap<TimeLog, TimeLogDb>()
.ReverseMap();
CreateMap<TimeLogDb, TimeLog>()
.ReverseMap();
}
}
public class GroupProfile : Profile
{
public GroupProfile()
{
CreateMap<Group, GroupDb>()
.ReverseMap();
CreateMap<GroupDb, Group>()
.ForMember(dst => dst.UserCount, opt => opt.Ignore())
.ReverseMap();
}
}
public class TeamProfile : Profile
{
public TeamProfile()
{
CreateMap<Team, TeamDb>()
.ReverseMap();
CreateMap<TeamDb, Team>()
.ForMember(dst => dst.Users, opt => opt.MapFrom(src => src.Members.Users))
.ReverseMap();
}
}
public class PermissionsProfile : Profile
{
public PermissionsProfile()
{
CreateMap<Permission, PermissionDb>()
.ReverseMap();
CreateMap<PermissionDb, Permission>()
.ReverseMap();
}
}
public class ApplicationProfile : Profile
{
public ApplicationProfile()
{
CreateMap<Application, ApplicationDb>()
.ReverseMap();
CreateMap<ApplicationDb, Application>()
.ReverseMap();
}
}
public class IdentifierProfile : Profile
{
public IdentifierProfile()
{
CreateMap<Identifier, IdentifierDb>()
.ReverseMap();
CreateMap<IdentifierDb, Identifier>()
.ReverseMap();
}
}
public class LogDescriptorProfile : Profile
{
public LogDescriptorProfile()
{
CreateMap<LogSourceDescriptor, LogSourceDescriptorDb>()
.ReverseMap();
CreateMap<LogSourceDescriptorDb, LogSourceDescriptor>()
.ReverseMap();
}
}
}

View File

@ -1,153 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Flexitime.DataAccess.Objects;
using Flexitime.Interfaces;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Flexitime.DataAccess.EF
{
public class FlexitimeDbContext : DbContext
{
private readonly IConnectionStringProvider _connectionStringProvider;
public FlexitimeDbContext(IConnectionStringProvider connectionStringProvider)
{
_connectionStringProvider = connectionStringProvider;
}
public DbSet<UserDb> Users { get; set; }
public DbSet<TimeLogDb> TimeLogs { get; set; }
public DbSet<IdentifierDb> Identifiers { get; set; }
public DbSet<ApplicationDb> Applications { get; set; }
public DbSet<GroupDb> Groups { get; set; }
public DbSet<TeamDb> Teams { get; set; }
public DbSet<LogSourceDescriptorDb> LogSources { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(_connectionStringProvider.ConnectionString);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new UserMap());
modelBuilder.ApplyConfiguration(new IdentifierMap());
base.OnModelCreating(modelBuilder);
}
public void InsertBaseData()
{
AddAdminUser();
AddRootApplication();
}
private void AddRootApplication()
{
var systemApp = new ApplicationDb
{
Id = Guid.NewGuid(),
Name = "System",
Permissions = new List<PermissionDb>
{
CreatePermission("Add/Edit User", "s.u.e"),
CreatePermission("Delete User", "s.u.d"),
CreatePermission("Add/Edit Group", "s.g.e"),
CreatePermission("Delete Group", "s.g.d"),
CreatePermission("Associate Identifier", "s.i.e")
}
};
var existingSystemApp = Applications.Where(x => x.Name == "System")
.Include(x=>x.Permissions)
.FirstOrDefault();
if (existingSystemApp == null)
{
Applications.Add(systemApp);
var adminUser = Users.FirstOrDefault(x => x.UserName == "admin");
adminUser?.Permissions.AddRange(systemApp.Permissions);
SaveChanges();
}
else
{
var missingPermissions = systemApp.Permissions
.Where(x=> existingSystemApp.Permissions.All(y => y.Tag != x.Tag))
.ToList();
if (missingPermissions.Any())
{
existingSystemApp.Permissions.AddRange(missingPermissions);
Applications.Update(existingSystemApp);
SaveChanges();
}
}
}
private PermissionDb CreatePermission(string name, string tag)
{
return new()
{
Id = Guid.NewGuid(),
Name = name,
Tag = tag
};
}
private void AddAdminUser()
{
if (!Users.Any())
{
Users.Add(new UserDb
{
Id = Guid.NewGuid(),
FirstName = "Admin",
LastName = "Admin",
Password = "P@ssw0rd!",
UserName = "admin"
});
SaveChanges();
}
}
}
public class UserMap : IEntityTypeConfiguration<UserDb>
{
public void Configure(EntityTypeBuilder<UserDb> builder)
{
builder.ToTable("UserDb")
.HasKey(x => x.Id);
builder
.HasOne(x => x.LineManager)
.WithOne().OnDelete(DeleteBehavior.ClientSetNull);
builder
.HasOne(x => x.Team)
.WithOne();
builder
.HasMany(x => x.AssociatedIdentifiers)
.WithOne().OnDelete(DeleteBehavior.Cascade);
}
}
public class IdentifierMap : IEntityTypeConfiguration<IdentifierDb>
{
public void Configure(EntityTypeBuilder<IdentifierDb> builder)
{
builder.ToTable("IdentifierDb")
.HasKey(x => x.Id);
}
}
public class TimeLogsMap : IEntityTypeConfiguration<TimeLogDb>
{
public void Configure(EntityTypeBuilder<TimeLogDb> builder)
{
builder.ToTable("TimeLogDb")
.HasKey(x => x.Id);
}
}
}

View File

@ -1,243 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<DirectedGraph GraphDirection="LeftToRight" xmlns="http://schemas.microsoft.com/vs/2009/dgml">
<Nodes>
<Node Id="ApplicationDb" Category="EntityType" Annotations="ConstructorBinding: Microsoft.EntityFrameworkCore.Metadata.ConstructorBinding Relational:DefaultMappings: System.Collections.Generic.List`1[Microsoft.EntityFrameworkCore.Metadata.Internal.TableMappingBase] Relational:TableMappings: System.Collections.Generic.List`1[Microsoft.EntityFrameworkCore.Metadata.Internal.TableMapping] ServiceOnlyConstructorBinding: Microsoft.EntityFrameworkCore.Metadata.ConstructorBinding" BaseClass="" Bounds="20,611.62,171.696666666667,141.92" ChangeTrackingStrategy="ChangeTrackingStrategy.Snapshot" Group="Expanded" IsAbstract="False" Label="ApplicationDb" Name="ApplicationDb" />
<Node Id="ApplicationDb.Id" Category="Property Primary" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="40,651.62,50,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="True" IsRequired="True" IsShadow="False" IsUnicode="True" Label="Id" MaxLength="None" Name="Id" PropertyAccessMode="PropertyAccessMode.Default" Type="Guid" ValueGenerated="ValueGenerated.OnAdd" />
<Node Id="ApplicationDb.Name" Category="Property Optional" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="120,651.62,51.6966666666667,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="False" IsShadow="False" IsUnicode="True" Label="Name" MaxLength="None" Name="Name" PropertyAccessMode="PropertyAccessMode.Default" Type="string" ValueGenerated="None" />
<Node Id="ApplicationDb.Permissions" Category="Navigation Collection" Bounds="40,707.58,97.5066666666667,25.96" Dependent="PermissionDb" Field="" Inverse="Application" Label="Permissions (*)" Name="Permissions" Principal="" PropertyAccessMode="PropertyAccessMode.Default" Type="List&lt;PermissionDb&gt;" />
<Node Id="GroupDb" Category="EntityType" Annotations="ConstructorBinding: Microsoft.EntityFrameworkCore.Metadata.ConstructorBinding Relational:DefaultMappings: System.Collections.Generic.List`1[Microsoft.EntityFrameworkCore.Metadata.Internal.TableMappingBase] Relational:TableMappings: System.Collections.Generic.List`1[Microsoft.EntityFrameworkCore.Metadata.Internal.TableMapping] ServiceOnlyConstructorBinding: Microsoft.EntityFrameworkCore.Metadata.ConstructorBinding" BaseClass="" Bounds="834.158222459071,780.113078871474,200.160049641927,197.8804" ChangeTrackingStrategy="ChangeTrackingStrategy.Snapshot" Group="Expanded" IsAbstract="False" Label="GroupDb" Name="GroupDb" UseManualLocation="True" />
<Node Id="GroupDb.Id" Category="Property Primary" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="854.158192588721,820.113261937001,50,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="True" IsRequired="True" IsShadow="False" IsUnicode="True" Label="Id" MaxLength="None" Name="Id" PropertyAccessMode="PropertyAccessMode.Default" Type="Guid" ValueGenerated="ValueGenerated.OnAdd" />
<Node Id="GroupDb.IsPrivate" Category="Property Required" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="854.158188519711,876.073344944814,64.2833333333333,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="True" IsShadow="False" IsUnicode="True" Label="IsPrivate" MaxLength="None" Name="IsPrivate" PropertyAccessMode="PropertyAccessMode.Default" Type="bool" ValueGenerated="None" />
<Node Id="GroupDb.Name" Category="Property Optional" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="934.158369997576,820.113261937001,51.6966666666667,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="False" IsShadow="False" IsUnicode="True" Label="Name" MaxLength="None" Name="Name" PropertyAccessMode="PropertyAccessMode.Default" Type="string" ValueGenerated="None" />
<Node Id="GroupDb.Owners" Category="Navigation Property" Bounds="854.158186485206,932.033427952626,77.0500000000001,25.96" Dependent="GroupOwners" Field="" Inverse="Group" Label="Owners (1)" Name="Owners" Principal="" PropertyAccessMode="PropertyAccessMode.Default" Type="GroupOwners" />
<Node Id="GroupDb.Users" Category="Navigation Property" Bounds="948.441752565935,876.073344944814,65.8766666666667,25.96" Dependent="" Field="" Inverse="Groups" Label="Users (1)" Name="Users" Principal="" PropertyAccessMode="PropertyAccessMode.Default" Type="List&lt;UserDb&gt;" />
<Node Id="GroupDbUserDb" Category="EntityType" Annotations="" BaseClass="" Bounds="649.848354695638,306.756624112899,128,141.9203" ChangeTrackingStrategy="ChangeTrackingStrategy.Snapshot" Group="Expanded" IsAbstract="False" Label="GroupDbUserDb" Name="GroupDbUserDb" />
<Node Id="GroupDbUserDb.GroupsId" Category="Property Primary" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="679.53169148763,402.716824112899,68.6333333333333,25.96" Field="nofield" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="True" IsIndexed="False" IsPrimaryKey="True" IsRequired="True" IsShadow="False" IsUnicode="True" Label="GroupsId" MaxLength="None" Name="GroupsId" PropertyAccessMode="PropertyAccessMode.Default" Type="Guid" ValueGenerated="None" />
<Node Id="GroupDbUserDb.UsersId" Category="Property Primary" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="679.531684570312,346.756724112899,59.14,25.96" Field="nofield" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="True" IsIndexed="True" IsPrimaryKey="True" IsRequired="True" IsShadow="False" IsUnicode="True" Label="UsersId" MaxLength="None" Name="UsersId" PropertyAccessMode="PropertyAccessMode.Default" Type="Guid" ValueGenerated="None" />
<Node Id="GroupOwners" Category="EntityType" Annotations="ConstructorBinding: Microsoft.EntityFrameworkCore.Metadata.ConstructorBinding Relational:DefaultMappings: System.Collections.Generic.List`1[Microsoft.EntityFrameworkCore.Metadata.Internal.TableMappingBase] Relational:TableMappings: System.Collections.Generic.List`1[Microsoft.EntityFrameworkCore.Metadata.Internal.TableMapping] ServiceOnlyConstructorBinding: Microsoft.EntityFrameworkCore.Metadata.ConstructorBinding" BaseClass="" Bounds="541.465026855469,821.136654557235,210.766606445312,141.920344042969" ChangeTrackingStrategy="ChangeTrackingStrategy.Snapshot" Group="Expanded" IsAbstract="False" Label="GroupOwners" Name="GroupOwners" />
<Node Id="GroupOwners.Group" Category="Navigation Property" Bounds="561.465029703776,917.096898600204,70.2766666666666,25.96" Dependent="" Field="" Inverse="Owners" Label="Group (1)" Name="Group" Principal="GroupDb" PropertyAccessMode="PropertyAccessMode.Default" Type="GroupDb" />
<Node Id="GroupOwners.GroupId" Category="Property Foreign" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="641.465015869141,861.136754557235,63.54,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="True" IsIndexed="True" IsPrimaryKey="False" IsRequired="True" IsShadow="False" IsUnicode="True" Label="GroupId" MaxLength="None" Name="GroupId" PropertyAccessMode="PropertyAccessMode.Default" Type="Guid" ValueGenerated="None" />
<Node Id="GroupOwners.Id" Category="Property Primary" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping] SqlServer:ValueGenerationStrategy: IdentityColumn" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="561.465026855469,861.136754557235,50,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="True" IsRequired="True" IsShadow="False" IsUnicode="True" Label="Id" MaxLength="None" Name="Id" PropertyAccessMode="PropertyAccessMode.Default" Type="int" ValueGenerated="ValueGenerated.OnAdd" />
<Node Id="GroupOwners.Owner" Category="Navigation Collection" Bounds="661.741633300781,917.096898600204,70.49,25.96" Dependent="UserDb" Field="" Inverse="" Label="Owner (*)" Name="Owner" Principal="" PropertyAccessMode="PropertyAccessMode.Default" Type="List&lt;UserDb&gt;" />
<Node Id="IModel" Category="Model" Annotations="Relational:MaxIdentifierLength: 128 Relational:RelationalModel: Microsoft.EntityFrameworkCore.Metadata.Internal.RelationalModel SqlServer:ValueGenerationStrategy: IdentityColumn" Bounds="-1.13686837721616E-13,-5.06377059229567,1078.46166666667,1050.42406059802" ChangeTrackingStrategy="ChangeTrackingStrategy.Snapshot" Group="Expanded" Label="FlexitimeDbContext" ProductVersion="5.0.5" PropertyAccessMode="PropertyAccessMode.Default" />
<Node Id="IdentifierDb" Category="EntityType" Annotations="ConstructorBinding: Microsoft.EntityFrameworkCore.Metadata.ConstructorBinding Relational:DefaultMappings: System.Collections.Generic.List`1[Microsoft.EntityFrameworkCore.Metadata.Internal.TableMappingBase] Relational:TableMappings: System.Collections.Generic.List`1[Microsoft.EntityFrameworkCore.Metadata.Internal.TableMapping] ServiceOnlyConstructorBinding: Microsoft.EntityFrameworkCore.Metadata.ConstructorBinding" BaseClass="" Bounds="411.92497639974,253.455354581649,207.846735432943,197.880413525391" ChangeTrackingStrategy="ChangeTrackingStrategy.Snapshot" Group="Expanded" IsAbstract="False" Label="IdentifierDb" Name="IdentifierDb" />
<Node Id="IdentifierDb.Id" Category="Property Primary" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="431.924987792969,293.455471573836,50,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="True" IsRequired="True" IsShadow="False" IsUnicode="True" Label="Id" MaxLength="None" Name="Id" PropertyAccessMode="PropertyAccessMode.Default" Type="Guid" ValueGenerated="ValueGenerated.OnAdd" />
<Node Id="IdentifierDb.IsAssociatedToUser" Category="Property Required" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="431.92497639974,405.37566810704,121.143333333333,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="True" IsShadow="False" IsUnicode="True" Label="IsAssociatedToUser" MaxLength="None" Name="IsAssociatedToUser" PropertyAccessMode="PropertyAccessMode.Default" Type="bool" ValueGenerated="None" />
<Node Id="IdentifierDb.LastUsed" Category="Property Required" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="511.924994303385,293.455454581649,67.5966666666667,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="True" IsShadow="False" IsUnicode="True" Label="LastUsed" MaxLength="None" Name="LastUsed" PropertyAccessMode="PropertyAccessMode.Default" Type="DateTime" ValueGenerated="None" />
<Node Id="IdentifierDb.UniqueId" Category="Property Required" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="431.924992879232,349.415585099227,68.3333333333333,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="True" IsShadow="False" IsUnicode="True" Label="UniqueId" MaxLength="None" Name="UniqueId" PropertyAccessMode="PropertyAccessMode.Default" Type="Guid" ValueGenerated="None" />
<Node Id="IdentifierDb.UserDbId" Category="Property Foreign" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="530.258378499349,349.41556810704,69.5133333333333,25.96" Field="nofield" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="True" IsIndexed="True" IsPrimaryKey="False" IsRequired="False" IsShadow="True" IsUnicode="True" Label="UserDbId" MaxLength="None" Name="UserDbId" PropertyAccessMode="PropertyAccessMode.Default" Type="Nullable&lt;Guid&gt;" ValueGenerated="None" />
<Node Id="LogSourceDescriptor" Category="EntityType" Annotations="ConstructorBinding: Microsoft.EntityFrameworkCore.Metadata.ConstructorBinding Relational:DefaultMappings: System.Collections.Generic.List`1[Microsoft.EntityFrameworkCore.Metadata.Internal.TableMappingBase] Relational:TableMappings: System.Collections.Generic.List`1[Microsoft.EntityFrameworkCore.Metadata.Internal.TableMapping] ServiceOnlyConstructorBinding: Microsoft.EntityFrameworkCore.Metadata.ConstructorBinding" BaseClass="" Bounds="807.906666666667,283.84,207.883333333333,197.88" ChangeTrackingStrategy="ChangeTrackingStrategy.Snapshot" Group="Expanded" IsAbstract="False" Label="LogSourceDescriptor" Name="LogSourceDescriptor" />
<Node Id="LogSourceDescriptor.Id" Category="Property Primary" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping] SqlServer:ValueGenerationStrategy: IdentityColumn" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="827.906666666667,323.84,50,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="True" IsRequired="True" IsShadow="False" IsUnicode="True" Label="Id" MaxLength="None" Name="Id" PropertyAccessMode="PropertyAccessMode.Default" Type="int" ValueGenerated="ValueGenerated.OnAdd" />
<Node Id="LogSourceDescriptor.LocalDateTime" Category="Property Required" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="827.906666666667,435.76,97.9066666666666,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="True" IsShadow="False" IsUnicode="True" Label="LocalDateTime" MaxLength="None" Name="LocalDateTime" PropertyAccessMode="PropertyAccessMode.Default" Type="DateTimeOffset" ValueGenerated="None" />
<Node Id="LogSourceDescriptor.SourceApiKey" Category="Property Optional" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="827.906666666667,379.8,92.7733333333333,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="False" IsShadow="False" IsUnicode="True" Label="SourceApiKey" MaxLength="None" Name="SourceApiKey" PropertyAccessMode="PropertyAccessMode.Default" Type="string" ValueGenerated="None" />
<Node Id="LogSourceDescriptor.SourceName" Category="Property Optional" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="907.906666666667,323.84,87.8833333333333,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="False" IsShadow="False" IsUnicode="True" Label="SourceName" MaxLength="None" Name="SourceName" PropertyAccessMode="PropertyAccessMode.Default" Type="string" ValueGenerated="None" />
<Node Id="PermissionDb" Category="EntityType" Annotations="ConstructorBinding: Microsoft.EntityFrameworkCore.Metadata.ConstructorBinding Relational:DefaultMappings: System.Collections.Generic.List`1[Microsoft.EntityFrameworkCore.Metadata.Internal.TableMappingBase] Relational:TableMappings: System.Collections.Generic.List`1[Microsoft.EntityFrameworkCore.Metadata.Internal.TableMapping] ServiceOnlyConstructorBinding: Microsoft.EntityFrameworkCore.Metadata.ConstructorBinding" BaseClass="" Bounds="130,253.455351981552,251.696660970052,197.880416992188" ChangeTrackingStrategy="ChangeTrackingStrategy.Snapshot" Group="Expanded" IsAbstract="False" Label="PermissionDb" Name="PermissionDb" />
<Node Id="PermissionDb.Application" Category="Navigation Property" Bounds="150.000003051758,405.375668973739,97.1,25.96" Dependent="" Field="" Inverse="Permissions" Label="Application (1)" Name="Application" Principal="ApplicationDb" PropertyAccessMode="PropertyAccessMode.Default" Type="ApplicationDb" />
<Node Id="PermissionDb.ApplicationId" Category="Property Foreign" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="249.513340657552,349.41556810704,90.3633333333333,25.96" Field="nofield" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="True" IsIndexed="True" IsPrimaryKey="False" IsRequired="False" IsShadow="True" IsUnicode="True" Label="ApplicationId" MaxLength="None" Name="ApplicationId" PropertyAccessMode="PropertyAccessMode.Default" Type="Nullable&lt;Guid&gt;" ValueGenerated="None" />
<Node Id="PermissionDb.Id" Category="Property Primary" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="150,293.455468973739,50,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="True" IsRequired="True" IsShadow="False" IsUnicode="True" Label="Id" MaxLength="None" Name="Id" PropertyAccessMode="PropertyAccessMode.Default" Type="Guid" ValueGenerated="ValueGenerated.OnAdd" />
<Node Id="PermissionDb.Name" Category="Property Optional" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="309.999994303385,293.455454581649,51.6966666666667,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="False" IsShadow="False" IsUnicode="True" Label="Name" MaxLength="None" Name="Name" PropertyAccessMode="PropertyAccessMode.Default" Type="string" ValueGenerated="None" />
<Node Id="PermissionDb.Tag" Category="Property Optional" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="230,293.455454581649,49.9999999999999,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="False" IsShadow="False" IsUnicode="True" Label="Tag" MaxLength="None" Name="Tag" PropertyAccessMode="PropertyAccessMode.Default" Type="string" ValueGenerated="None" />
<Node Id="PermissionDb.UserDbId" Category="Property Foreign" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="150.000001424153,349.415568973739,69.5133333333334,25.96" Field="nofield" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="True" IsIndexed="True" IsPrimaryKey="False" IsRequired="False" IsShadow="True" IsUnicode="True" Label="UserDbId" MaxLength="None" Name="UserDbId" PropertyAccessMode="PropertyAccessMode.Default" Type="Nullable&lt;Guid&gt;" ValueGenerated="None" />
<Node Id="TeamDb" Category="EntityType" Annotations="ConstructorBinding: Microsoft.EntityFrameworkCore.Metadata.ConstructorBinding Relational:DefaultMappings: System.Collections.Generic.List`1[Microsoft.EntityFrameworkCore.Metadata.Internal.TableMappingBase] Relational:TableMappings: System.Collections.Generic.List`1[Microsoft.EntityFrameworkCore.Metadata.Internal.TableMapping] ServiceOnlyConstructorBinding: Microsoft.EntityFrameworkCore.Metadata.ConstructorBinding" BaseClass="" Bounds="339,883.440000000001,171.696666666667,141.92" ChangeTrackingStrategy="ChangeTrackingStrategy.Snapshot" Group="Expanded" IsAbstract="False" Label="TeamDb" Name="TeamDb" />
<Node Id="TeamDb.Id" Category="Property Primary" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="359,923.440000000001,50,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="True" IsRequired="True" IsShadow="False" IsUnicode="True" Label="Id" MaxLength="None" Name="Id" PropertyAccessMode="PropertyAccessMode.Default" Type="Guid" ValueGenerated="ValueGenerated.OnAdd" />
<Node Id="TeamDb.Name" Category="Property Optional" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="439,923.440000000001,51.6966666666667,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="False" IsShadow="False" IsUnicode="True" Label="Name" MaxLength="None" Name="Name" PropertyAccessMode="PropertyAccessMode.Default" Type="string" ValueGenerated="None" />
<Node Id="TeamDb.Users" Category="Navigation Collection" Bounds="359,979.400000000001,64.41,25.96" Dependent="UserDb" Field="" Inverse="Team" Label="Users (*)" Name="Users" Principal="" PropertyAccessMode="PropertyAccessMode.Default" Type="List&lt;UserDb&gt;" />
<Node Id="TimeLogDb" Category="EntityType" Annotations="ConstructorBinding: Microsoft.EntityFrameworkCore.Metadata.ConstructorBinding Relational:DefaultMappings: System.Collections.Generic.List`1[Microsoft.EntityFrameworkCore.Metadata.Internal.TableMappingBase] Relational:TableMappings: System.Collections.Generic.List`1[Microsoft.EntityFrameworkCore.Metadata.Internal.TableMapping] Relational:TableName: TimeLogs ServiceOnlyConstructorBinding: Microsoft.EntityFrameworkCore.Metadata.ConstructorBinding" BaseClass="" Bounds="765.234983723958,34.9364225870201,293.226682942709,197.8804" ChangeTrackingStrategy="ChangeTrackingStrategy.Snapshot" Group="Expanded" IsAbstract="False" Label="TimeLogDb" Name="TimeLogDb" />
<Node Id="TimeLogDb.Id" Category="Property Primary" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="785.234985351562,74.9365225870201,50,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="True" IsRequired="True" IsShadow="False" IsUnicode="True" Label="Id" MaxLength="None" Name="Id" PropertyAccessMode="PropertyAccessMode.Default" Type="Guid" ValueGenerated="ValueGenerated.OnAdd" />
<Node Id="TimeLogDb.Identifier" Category="Navigation Property" Bounds="785.235002441406,130.89662258702,84.41,25.96" Dependent="" Field="" Inverse="" Label="Identifier (1)" Name="Identifier" Principal="IdentifierDb" PropertyAccessMode="PropertyAccessMode.Default" Type="IdentifierDb" />
<Node Id="TimeLogDb.IdentifierId" Category="Property Foreign" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="960.788333333334,74.9365225870201,77.6733333333331,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="True" IsIndexed="True" IsPrimaryKey="False" IsRequired="True" IsShadow="False" IsUnicode="True" Label="IdentifierId" MaxLength="None" Name="IdentifierId" PropertyAccessMode="PropertyAccessMode.Default" Type="Guid" ValueGenerated="None" />
<Node Id="TimeLogDb.LogTime" Category="Property Required" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="865.234991048177,74.9365225870201,65.5533333333333,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="True" IsShadow="False" IsUnicode="True" Label="LogTime" MaxLength="None" Name="LogTime" PropertyAccessMode="PropertyAccessMode.Default" Type="DateTimeOffset" ValueGenerated="None" />
<Node Id="TimeLogDb.SourceDescriptor" Category="Navigation Property" Bounds="785.234983723958,186.85672258702,127.913333333333,25.96" Dependent="" Field="" Inverse="" Label="SourceDescriptor (1)" Name="SourceDescriptor" Principal="LogSourceDescriptor" PropertyAccessMode="PropertyAccessMode.Default" Type="LogSourceDescriptor" />
<Node Id="TimeLogDb.SourceDescriptorId" Category="Property Foreign" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="899.645004069011,130.89662258702,121.176666666667,25.96" Field="nofield" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="True" IsIndexed="True" IsPrimaryKey="False" IsRequired="False" IsShadow="True" IsUnicode="True" Label="SourceDescriptorId" MaxLength="None" Name="SourceDescriptorId" PropertyAccessMode="PropertyAccessMode.Default" Type="Nullable&lt;int&gt;" ValueGenerated="None" />
<Node Id="UserDb" Category="EntityType" Annotations="ConstructorBinding: Microsoft.EntityFrameworkCore.Metadata.ConstructorBinding Relational:DefaultMappings: System.Collections.Generic.List`1[Microsoft.EntityFrameworkCore.Metadata.Internal.TableMappingBase] Relational:TableMappings: System.Collections.Generic.List`1[Microsoft.EntityFrameworkCore.Metadata.Internal.TableMapping] Relational:TableName: Users ServiceOnlyConstructorBinding: Microsoft.EntityFrameworkCore.Metadata.ConstructorBinding" BaseClass="" Bounds="279.384977914315,481.335910514266,472.926718207467,309.800644042969" ChangeTrackingStrategy="ChangeTrackingStrategy.Snapshot" Group="Expanded" IsAbstract="False" Label="UserDb" Name="UserDb" />
<Node Id="UserDb.AssociatedIdentifiers" Category="Navigation Collection" Bounds="587.351693454319,745.176454557235,144.96,25.96" Dependent="IdentifierDb" Field="" Inverse="" Label="AssociatedIdentifiers (*)" Name="AssociatedIdentifiers" Principal="" PropertyAccessMode="PropertyAccessMode.Default" Type="List&lt;IdentifierDb&gt;" />
<Node Id="UserDb.DirectReports" Category="Navigation Collection" Bounds="299.385004001194,745.176454557235,107.43,25.96" Dependent="UserDb" Field="" Inverse="" Label="DirectReports (*)" Name="DirectReports" Principal="" PropertyAccessMode="PropertyAccessMode.Default" Type="List&lt;UserDb&gt;" />
<Node Id="UserDb.FirstName" Category="Property Optional" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="401.998318888346,577.296110514266,73.7966666666667,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="False" IsShadow="False" IsUnicode="True" Label="FirstName" MaxLength="None" Name="FirstName" PropertyAccessMode="PropertyAccessMode.Default" Type="string" ValueGenerated="None" />
<Node Id="UserDb.GroupOwnersId" Category="Property Foreign" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="428.564997151693,689.216310514267,103.593333333333,25.96" Field="nofield" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="True" IsIndexed="True" IsPrimaryKey="False" IsRequired="False" IsShadow="True" IsUnicode="True" Label="GroupOwnersId" MaxLength="None" Name="GroupOwnersId" PropertyAccessMode="PropertyAccessMode.Default" Type="Nullable&lt;int&gt;" ValueGenerated="None" />
<Node Id="UserDb.Groups" Category="Navigation Property" Bounds="505.795026245117,577.296110514266,75.37,25.96" Dependent="" Field="" Inverse="Users" Label="Groups (1)" Name="Groups" Principal="" PropertyAccessMode="PropertyAccessMode.Default" Type="List&lt;GroupDb&gt;" />
<Node Id="UserDb.HoursPerWeek" Category="Property Required" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="541.58833396629,633.256210514267,97.59,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="True" IsShadow="False" IsUnicode="True" Label="HoursPerWeek" MaxLength="None" Name="HoursPerWeek" PropertyAccessMode="PropertyAccessMode.Default" Type="float" ValueGenerated="None" />
<Node Id="UserDb.Id" Category="Property Primary" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="299.384979248047,521.336010514266,50,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="True" IsRequired="True" IsShadow="False" IsUnicode="True" Label="Id" MaxLength="None" Name="Id" PropertyAccessMode="PropertyAccessMode.Default" Type="Guid" ValueGenerated="ValueGenerated.OnAdd" />
<Node Id="UserDb.IsContractor" Category="Property Required" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="299.385000632957,633.256210514266,84.6966666666667,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="True" IsShadow="False" IsUnicode="True" Label="IsContractor" MaxLength="None" Name="IsContractor" PropertyAccessMode="PropertyAccessMode.Default" Type="bool" ValueGenerated="None" />
<Node Id="UserDb.LastEventDateTime" Category="Property Required" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="436.815008070205,745.176454557235,120.536666666667,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="True" IsShadow="False" IsUnicode="True" Label="LastEventDateTime" MaxLength="None" Name="LastEventDateTime" PropertyAccessMode="PropertyAccessMode.Default" Type="DateTime" ValueGenerated="None" />
<Node Id="UserDb.LastName" Category="Property Optional" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="299.384983723958,577.296110514266,72.6133333333333,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="False" IsShadow="False" IsUnicode="True" Label="LastName" MaxLength="None" Name="LastName" PropertyAccessMode="PropertyAccessMode.Default" Type="string" ValueGenerated="None" />
<Node Id="UserDb.LineManager" Category="Navigation Property" Bounds="562.158335367839,689.216310514267,105.916666666667,25.96" Dependent="" Field="" Inverse="" Label="LineManager (1)" Name="LineManager" Principal="UserDb" PropertyAccessMode="PropertyAccessMode.Default" Type="UserDb" />
<Node Id="UserDb.LineManagerId" Category="Property Foreign" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="299.385006103516,689.216310514266,99.18,25.96" Field="nofield" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="True" IsIndexed="True" IsPrimaryKey="False" IsRequired="False" IsShadow="True" IsUnicode="True" Label="LineManagerId" MaxLength="None" Name="LineManagerId" PropertyAccessMode="PropertyAccessMode.Default" Type="Nullable&lt;Guid&gt;" ValueGenerated="None" />
<Node Id="UserDb.Password" Category="Property Optional" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="661.828379652236,521.336010514266,69.5466666666666,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="False" IsShadow="False" IsUnicode="True" Label="Password" MaxLength="None" Name="Password" PropertyAccessMode="PropertyAccessMode.Default" Type="string" ValueGenerated="None" />
<Node Id="UserDb.Permissions" Category="Navigation Collection" Bounds="414.081667299624,633.256210514266,97.5066666666667,25.96" Dependent="PermissionDb" Field="" Inverse="" Label="Permissions (*)" Name="Permissions" Principal="" PropertyAccessMode="PropertyAccessMode.Default" Type="List&lt;PermissionDb&gt;" />
<Node Id="UserDb.Team" Category="Navigation Property" Bounds="467.48166402181,521.336010514266,64.8333333333333,25.96" Dependent="" Field="" Inverse="Users" Label="Team (1)" Name="Team" Principal="TeamDb" PropertyAccessMode="PropertyAccessMode.Default" Type="TeamDb" />
<Node Id="UserDb.TeamId" Category="Property Foreign" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="379.385016276042,521.336010514266,58.0966666666667,25.96" Field="nofield" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="True" IsIndexed="True" IsPrimaryKey="False" IsRequired="False" IsShadow="True" IsUnicode="True" Label="TeamId" MaxLength="None" Name="TeamId" PropertyAccessMode="PropertyAccessMode.Default" Type="Nullable&lt;Guid&gt;" ValueGenerated="None" />
<Node Id="UserDb.UserDbId" Category="Property Foreign" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="562.315019124349,521.336010514266,69.5133333333333,25.96" Field="nofield" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="True" IsIndexed="True" IsPrimaryKey="False" IsRequired="False" IsShadow="True" IsUnicode="True" Label="UserDbId" MaxLength="None" Name="UserDbId" PropertyAccessMode="PropertyAccessMode.Default" Type="Nullable&lt;Guid&gt;" ValueGenerated="None" />
<Node Id="UserDb.UserName" Category="Property Optional" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="611.165041097005,577.296110514267,75.4833333333333,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="False" IsShadow="False" IsUnicode="True" Label="UserName" MaxLength="None" Name="UserName" PropertyAccessMode="PropertyAccessMode.Default" Type="string" ValueGenerated="None" />
</Nodes>
<Links>
<Link Source="ApplicationDb" Target="ApplicationDb.Id" Category="Contains" />
<Link Source="ApplicationDb" Target="ApplicationDb.Name" Category="Contains" />
<Link Source="ApplicationDb" Target="ApplicationDb.Permissions" Category="Contains" />
<Link Source="GroupDb" Target="GroupDb.Id" Category="Contains" />
<Link Source="GroupDb" Target="GroupDb.IsPrivate" Category="Contains" />
<Link Source="GroupDb" Target="GroupDb.Name" Category="Contains" />
<Link Source="GroupDb" Target="GroupDb.Owners" Category="Contains" />
<Link Source="GroupDb" Target="GroupDb.Users" Category="Contains" />
<Link Source="GroupDbUserDb" Target="GroupDb" Category="Foreign Key" Annotations="Relational:ForeignKeyMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ForeignKeyConstraint]" Bounds="745.042771395463,448.676924112899,142.078978384132,323.197119375579" From="GroupDbUserDb.GroupsId" IsUnique="False" Label="1:*" LabelBounds="783.503404761899,515.236310602442,14.0733333333333,15.96" Name="GroupDbUserDb -&gt; GroupDb" To="GroupDb.Id" />
<Link Source="GroupDbUserDb" Target="GroupDbUserDb.GroupsId" Category="Contains" />
<Link Source="GroupDbUserDb" Target="GroupDbUserDb.UsersId" Category="Contains" />
<Link Source="GroupDbUserDb" Target="UserDb" Category="Foreign Key" Annotations="Relational:ForeignKeyMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ForeignKeyConstraint]" Bounds="639.958904952466,448.676924112899,19.5410806102647,25.5138844714087" From="GroupDbUserDb.UsersId" IsUnique="False" Label="1:*" LabelBounds="651.317245686478,462.64996247887,14.0733333333334,15.96" Name="GroupDbUserDb -&gt; UserDb" To="UserDb.Id" />
<Link Source="GroupOwners" Target="GroupDb" Category="Foreign Key" Annotations="Relational:ForeignKeyMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ForeignKeyConstraint]" Bounds="752.231633300781,884.003590975451,72.9358444603664,3.31028372898231" From="GroupOwners.GroupId" IsUnique="True" Label="1:1" LabelBounds="780.838876398715,865.698730722688,15.54,15.96" Name="GroupOwners -&gt; GroupDb" To="GroupDb.Id" />
<Link Source="GroupOwners" Target="GroupOwners.Group" Category="Contains" />
<Link Source="GroupOwners" Target="GroupOwners.GroupId" Category="Contains" />
<Link Source="GroupOwners" Target="GroupOwners.Id" Category="Contains" />
<Link Source="GroupOwners" Target="GroupOwners.Owner" Category="Contains" />
<Link Source="IModel" Target="ApplicationDb" Category="Contains" />
<Link Source="IModel" Target="GroupDb" Category="Contains" />
<Link Source="IModel" Target="GroupDbUserDb" Category="Contains" />
<Link Source="IModel" Target="GroupOwners" Category="Contains" />
<Link Source="IModel" Target="IdentifierDb" Category="Contains" />
<Link Source="IModel" Target="LogSourceDescriptor" Category="Contains" />
<Link Source="IModel" Target="PermissionDb" Category="Contains" />
<Link Source="IModel" Target="TeamDb" Category="Contains" />
<Link Source="IModel" Target="TimeLogDb" Category="Contains" />
<Link Source="IModel" Target="UserDb" Category="Contains" />
<Link Source="IdentifierDb" Target="IdentifierDb.Id" Category="Contains" />
<Link Source="IdentifierDb" Target="IdentifierDb.IsAssociatedToUser" Category="Contains" />
<Link Source="IdentifierDb" Target="IdentifierDb.LastUsed" Category="Contains" />
<Link Source="IdentifierDb" Target="IdentifierDb.UniqueId" Category="Contains" />
<Link Source="IdentifierDb" Target="IdentifierDb.UserDbId" Category="Contains" />
<Link Source="IdentifierDb" Target="UserDb" Category="Foreign Key" Annotations="Relational:ForeignKeyMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ForeignKeyConstraint]" Bounds="515.848341116795,451.33576810704,5.25162363373965E-07,21.0001424072267" From="IdentifierDb.UserDbId" IsUnique="False" Label="1:*" LabelBounds="519.848341379377,453.855839360668,14.0733333333333,15.96" Name="IdentifierDb -&gt; UserDb" To="UserDb.Id" />
<Link Source="LogSourceDescriptor" Target="LogSourceDescriptor.Id" Category="Contains" />
<Link Source="LogSourceDescriptor" Target="LogSourceDescriptor.LocalDateTime" Category="Contains" />
<Link Source="LogSourceDescriptor" Target="LogSourceDescriptor.SourceApiKey" Category="Contains" />
<Link Source="LogSourceDescriptor" Target="LogSourceDescriptor.SourceName" Category="Contains" />
<Link Source="PermissionDb" Target="ApplicationDb" Category="Foreign Key" Annotations="Relational:ForeignKeyMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ForeignKeyConstraint]" Bounds="141.807363012455,451.335768973739,69.0932678957193,152.09014912077" From="PermissionDb.ApplicationId" IsUnique="False" Label="1:*" LabelBounds="178.174904050424,528.208066218285,14.0733333333333,15.96" Name="PermissionDb -&gt; ApplicationDb" To="ApplicationDb.Id" />
<Link Source="PermissionDb" Target="PermissionDb.Application" Category="Contains" />
<Link Source="PermissionDb" Target="PermissionDb.ApplicationId" Category="Contains" />
<Link Source="PermissionDb" Target="PermissionDb.Id" Category="Contains" />
<Link Source="PermissionDb" Target="PermissionDb.Name" Category="Contains" />
<Link Source="PermissionDb" Target="PermissionDb.Tag" Category="Contains" />
<Link Source="PermissionDb" Target="PermissionDb.UserDbId" Category="Contains" />
<Link Source="PermissionDb" Target="UserDb" Category="Foreign Key" Annotations="Relational:ForeignKeyMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ForeignKeyConstraint]" Bounds="346.478241515172,451.335768973739,21.4011939896662,23.3635735855307" From="PermissionDb.UserDbId" IsUnique="False" Label="1:*" LabelBounds="341.630712297784,464.36847618029,14.0733333333333,15.96" Name="PermissionDb -&gt; UserDb" To="UserDb.Id" />
<Link Source="TeamDb" Target="TeamDb.Id" Category="Contains" />
<Link Source="TeamDb" Target="TeamDb.Name" Category="Contains" />
<Link Source="TeamDb" Target="TeamDb.Users" Category="Contains" />
<Link Source="TimeLogDb" Target="IdentifierDb" Category="Foreign Key" Annotations="Relational:ForeignKeyMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ForeignKeyConstraint]" Bounds="627.65160636805,214.780141063669,137.583377355908,75.92064405795" From="TimeLogDb.IdentifierId" IsUnique="False" Label="1:*" LabelBounds="697.409572416184,254.49155076717,14.0733333333333,15.96" Name="TimeLogDb -&gt; IdentifierDb" To="IdentifierDb.Id" />
<Link Source="TimeLogDb" Target="LogSourceDescriptor" Category="Foreign Key" Annotations="Relational:ForeignKeyMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ForeignKeyConstraint]" Bounds="911.848328430212,232.81682258702,1.37396887112118E-06,42.0231774129799" From="TimeLogDb.SourceDescriptorId" IsUnique="False" Label="1:*" LabelBounds="915.848329117196,245.848411228119,14.0733333333333,15.96" Name="TimeLogDb -&gt; LogSourceDescriptor" To="LogSourceDescriptor.Id" />
<Link Source="TimeLogDb" Target="TimeLogDb.Id" Category="Contains" />
<Link Source="TimeLogDb" Target="TimeLogDb.Identifier" Category="Contains" />
<Link Source="TimeLogDb" Target="TimeLogDb.IdentifierId" Category="Contains" />
<Link Source="TimeLogDb" Target="TimeLogDb.LogTime" Category="Contains" />
<Link Source="TimeLogDb" Target="TimeLogDb.SourceDescriptor" Category="Contains" />
<Link Source="TimeLogDb" Target="TimeLogDb.SourceDescriptorId" Category="Contains" />
<Link Source="UserDb" Target="GroupOwners" Category="Foreign Key" Annotations="Relational:ForeignKeyMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ForeignKeyConstraint]" Bounds="595.15691982549,791.136554557235,11.258348353236,21.9890675588098" From="UserDb.GroupOwnersId" IsUnique="False" Label="1:*" LabelBounds="584.932531237399,803.042561414501,14.0733333333334,15.96" Name="UserDb -&gt; GroupOwners" To="GroupOwners.Id" />
<Link Source="UserDb" Target="TeamDb" Category="Foreign Key" Annotations="Relational:ForeignKeyMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ForeignKeyConstraint]" Bounds="447.618951206691,791.136554557235,23.9253785164266,83.6504204238083" From="UserDb.TeamId" IsUnique="False" Label="1:*" LabelBounds="463.584732296623,826.238739302674,14.0733333333334,15.96" Name="UserDb -&gt; TeamDb" To="TeamDb.Id" />
<Link Source="UserDb" Target="UserDb" Category="Foreign Key" Annotations="Relational:ForeignKeyMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ForeignKeyConstraint]" Bounds="419.513458251953,451.335906982422,186.029510498047,30" From="UserDb.UserDbId" IsUnique="False" Label="1:*" LabelBounds="500.600439099212,455.453098361185,14.0733333333333,15.96" Name="UserDb -&gt; UserDb" To="UserDb.Id" />
<Link Source="UserDb" Target="UserDb.AssociatedIdentifiers" Category="Contains" />
<Link Source="UserDb" Target="UserDb.DirectReports" Category="Contains" />
<Link Source="UserDb" Target="UserDb.FirstName" Category="Contains" />
<Link Source="UserDb" Target="UserDb.GroupOwnersId" Category="Contains" />
<Link Source="UserDb" Target="UserDb.Groups" Category="Contains" />
<Link Source="UserDb" Target="UserDb.HoursPerWeek" Category="Contains" />
<Link Source="UserDb" Target="UserDb.Id" Category="Contains" />
<Link Source="UserDb" Target="UserDb.IsContractor" Category="Contains" />
<Link Source="UserDb" Target="UserDb.LastEventDateTime" Category="Contains" />
<Link Source="UserDb" Target="UserDb.LastName" Category="Contains" />
<Link Source="UserDb" Target="UserDb.LineManager" Category="Contains" />
<Link Source="UserDb" Target="UserDb.LineManagerId" Category="Contains" />
<Link Source="UserDb" Target="UserDb.Password" Category="Contains" />
<Link Source="UserDb" Target="UserDb.Permissions" Category="Contains" />
<Link Source="UserDb" Target="UserDb.Team" Category="Contains" />
<Link Source="UserDb" Target="UserDb.TeamId" Category="Contains" />
<Link Source="UserDb" Target="UserDb.UserDbId" Category="Contains" />
<Link Source="UserDb" Target="UserDb.UserName" Category="Contains" />
</Links>
<Categories>
<Category Id="Contains" Label="Contains" Description="Whether the source of the link contains the target object" CanBeDataDriven="False" CanLinkedNodesBeDataDriven="True" IncomingActionLabel="Contained By" IsContainment="True" OutgoingActionLabel="Contains" />
<Category Id="EntityType" />
<Category Id="Foreign Key" />
<Category Id="Model" />
<Category Id="Navigation Collection" />
<Category Id="Navigation Property" />
<Category Id="Property Foreign" />
<Category Id="Property Optional" />
<Category Id="Property Primary" />
<Category Id="Property Required" />
</Categories>
<Properties>
<Property Id="AfterSaveBehavior" Group="Property Flags" DataType="System.String" />
<Property Id="Annotations" Description="Annotations" Group="Model Properties" DataType="System.String" />
<Property Id="BaseClass" Description="Base class" Group="Model Properties" DataType="System.String" />
<Property Id="BeforeSaveBehavior" Group="Property Flags" DataType="System.String" />
<Property Id="Bounds" DataType="System.Windows.Rect" />
<Property Id="CanBeDataDriven" Label="CanBeDataDriven" Description="CanBeDataDriven" DataType="System.Boolean" />
<Property Id="CanLinkedNodesBeDataDriven" Label="CanLinkedNodesBeDataDriven" Description="CanLinkedNodesBeDataDriven" DataType="System.Boolean" />
<Property Id="ChangeTrackingStrategy" Description="Change tracking strategy" Group="Model Properties" DataType="System.String" />
<Property Id="Dependent" Description="Dependent entity" Group="Model Properties" DataType="System.String" />
<Property Id="Expression" DataType="System.String" />
<Property Id="Field" Description="Backing field" Group="Model Properties" DataType="System.String" />
<Property Id="From" Description="Target property" Group="Model Properties" DataType="System.String" />
<Property Id="GraphDirection" DataType="Microsoft.VisualStudio.Diagrams.Layout.LayoutOrientation" />
<Property Id="Group" Label="Group" Description="Display the node as a group" DataType="Microsoft.VisualStudio.GraphModel.GraphGroupStyle" />
<Property Id="GroupLabel" DataType="System.String" />
<Property Id="IncomingActionLabel" Label="IncomingActionLabel" Description="IncomingActionLabel" DataType="System.String" />
<Property Id="Inverse" Description="Inverse entity" Group="Model Properties" DataType="System.String" />
<Property Id="IsAbstract" Label="IsAbstract" Description="IsAbstract" Group="Model Properties" DataType="System.Boolean" />
<Property Id="IsAlternateKey" Group="Property Flags" DataType="System.Boolean" />
<Property Id="IsConcurrencyToken" Group="Property Flags" DataType="System.Boolean" />
<Property Id="IsContainment" DataType="System.Boolean" />
<Property Id="IsEnabled" DataType="System.Boolean" />
<Property Id="IsForeignKey" Group="Property Flags" DataType="System.Boolean" />
<Property Id="IsIndexed" Group="Property Flags" DataType="System.Boolean" />
<Property Id="IsPrimaryKey" Group="Property Flags" DataType="System.Boolean" />
<Property Id="IsRequired" Group="Property Flags" DataType="System.Boolean" />
<Property Id="IsShadow" Group="Property Flags" DataType="System.Boolean" />
<Property Id="IsUnicode" Group="Property Flags" DataType="System.Boolean" />
<Property Id="IsUnique" Group="Model Properties" DataType="System.Boolean" />
<Property Id="Label" Label="Label" Description="Displayable label of an Annotatable object" DataType="System.String" />
<Property Id="LabelBounds" DataType="System.Windows.Rect" />
<Property Id="MaxLength" DataType="System.String" />
<Property Id="Name" Group="Model Properties" DataType="System.String" />
<Property Id="OutgoingActionLabel" Label="OutgoingActionLabel" Description="OutgoingActionLabel" DataType="System.String" />
<Property Id="Principal" Description="Principal entity" Group="Model Properties" DataType="System.String" />
<Property Id="ProductVersion" Label="Product Version" Description="EF Core product version" Group="Model Properties" DataType="System.String" />
<Property Id="PropertyAccessMode" Group="Property Flags" DataType="System.String" />
<Property Id="TargetType" DataType="System.Type" />
<Property Id="To" Description="Source property" Group="Model Properties" DataType="System.String" />
<Property Id="Type" Description="CLR data type" Group="Model Properties" DataType="System.String" />
<Property Id="UseManualLocation" DataType="System.Boolean" />
<Property Id="Value" DataType="System.String" />
<Property Id="ValueGenerated" Group="Property Flags" DataType="System.String" />
<Property Id="ValueLabel" DataType="System.String" />
</Properties>
<Styles>
<Style TargetType="Node" GroupLabel="EntityType" ValueLabel="True">
<Condition Expression="HasCategory('EntityType')" />
<Setter Property="Background" Value="#FFC0C0C0" />
</Style>
<Style TargetType="Node" GroupLabel="Property Primary" ValueLabel="True">
<Condition Expression="HasCategory('Property Primary')" />
<Setter Property="Background" Value="#FF008000" />
</Style>
<Style TargetType="Node" GroupLabel="Property Optional" ValueLabel="True">
<Condition Expression="HasCategory('Property Optional')" />
<Setter Property="Background" Value="#FF808040" />
</Style>
<Style TargetType="Node" GroupLabel="Property Foreign" ValueLabel="True">
<Condition Expression="HasCategory('Property Foreign')" />
<Setter Property="Background" Value="#FF8080FF" />
</Style>
<Style TargetType="Node" GroupLabel="Property Required" ValueLabel="True">
<Condition Expression="HasCategory('Property Required')" />
<Setter Property="Background" Value="#FFC0A000" />
</Style>
<Style TargetType="Node" GroupLabel="Navigation Property" ValueLabel="True">
<Condition Expression="HasCategory('Navigation Property')" />
<Setter Property="Background" Value="#FF990000" />
</Style>
<Style TargetType="Node" GroupLabel="Navigation Collection" ValueLabel="True">
<Condition Expression="HasCategory('Navigation Collection')" />
<Setter Property="Background" Value="#FFFF3232" />
</Style>
<Style TargetType="Node" GroupLabel="Model" ValueLabel="True">
<Condition Expression="HasCategory('Model')" />
<Setter Property="Background" Value="#FFFFFFFF" />
</Style>
</Styles>
</DirectedGraph>

View File

@ -1,21 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="10.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.5" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Flexitime.Interfaces\Flexitime.Interfaces.csproj" />
</ItemGroup>
</Project>

View File

@ -1,21 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Flexitime.DataAccess.Objects
{
public class ApplicationDb
{
public ApplicationDb()
{
Permissions = new List<PermissionDb>();
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public string Name { get; set; }
public List<PermissionDb> Permissions { get; set; }
}
}

View File

@ -1,25 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Flexitime.DataAccess.Objects
{
public class GroupDb
{
public GroupDb()
{
IsPrivate = false;
Users = null;
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public string Name { get; set; }
public bool IsPrivate { get; set; }
public List<UserDb> Users { get; set; }
public GroupOwners Owners { get; set; }
public List<PermissionDb> Permissions { get; set; }
}
}

View File

@ -1,17 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Flexitime.DataAccess.Objects
{
public class GroupOwners
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public Guid GroupId { get; set; }
public GroupDb Group { get; set; }
public List<UserDb> Owner { get; set; }
}
}

View File

@ -1,16 +0,0 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Flexitime.DataAccess.Objects
{
public class IdentifierDb
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public Guid UniqueId { get; set; }
public bool IsAssociatedToUser { get; set; }
public DateTime LastUsed { get; set; }
}
}

View File

@ -1,16 +0,0 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Flexitime.DataAccess.Objects
{
public class LogSourceDescriptorDb
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string SourceName { get; set; }
public DateTimeOffset LocalDateTime { get; set; }
public string SourceApiKey { get; set; }
}
}

View File

@ -1,16 +0,0 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Flexitime.DataAccess.Objects
{
public class PermissionDb
{
public string Name { get; set; }
public string Tag { get; set; }
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
}
}

View File

@ -1,17 +0,0 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Flexitime.DataAccess.Objects
{
public class TeamDb
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public string Name { get; set; }
public TeamMembers Members{ get; set; }
//team owner/manager?
public UserDb Manager { get; set; }
}
}

View File

@ -1,15 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace Flexitime.DataAccess.Objects
{
public class TeamMembers
{
[Key]
public int Id { get; set; }
public Guid TeamId { get; set; }
public TeamDb Team { get; set; }
public List<UserDb> Users { get; set; }
}
}

View File

@ -1,20 +0,0 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Flexitime.DataAccess.Objects
{
public class TimeLogDb
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public Guid IdentifierId { get; set; }
public IdentifierDb Identifier { get; set; }
public DateTimeOffset LogTime { get; set; }
public LogSourceDescriptorDb SourceDescriptor { get; set; }
}
}

View File

@ -1,62 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Flexitime.DataAccess.Objects
{
public class UserDb
{
public UserDb()
{
AssociatedIdentifiers = new List<IdentifierDb>();
Groups = new List<GroupDb>();
DirectReports = new List<UserDb>();
Permissions = new List<PermissionDb>();
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public double HoursPerWeek { get; set; }
public bool IsContractor { get; set; }
public int AssociatedIdentifierCount => AssociatedIdentifiers.Count;
public DateTime LastEventDateTime { get; set; }
public List<IdentifierDb> AssociatedIdentifiers { get; set; }
//TODO: Is this that the user is in the group or this is their groups?
public List<GroupDb> Groups { get; set; }
public int State { get; set; }
public Guid? TeamId { get; set; }
/// <summary>
/// user that belongs to team
/// </summary>
/// <remarks>differs from Groups in that a group is a symbolic collection where a team is a publicly identifiable entity</remarks>
public TeamDb Team { get; set; }
/// <summary>
/// Id of the Users Line Manager
/// </summary>
public UserDb LineManager { get; set; }
/// <summary>
/// Ids of the users direct reports
/// </summary>
public List<UserDb> DirectReports { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public List<PermissionDb> Permissions { get; set; }
}
}

View File

@ -1,11 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Flexitime.Objects\Flexitime.Objects.csproj" />
</ItemGroup>
</Project>

View File

@ -1,11 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Flexitime.Interfaces
{
public interface IConnectionStringProvider
{
string ConnectionString { get; }
}
}

View File

@ -1,18 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Flexitime.Objects;
namespace Flexitime.Interfaces
{
public interface IDataAccess
{
Task<List<User>> GetUsers();
Task<User> GetUserById(Guid id);
Task<User> AddUser(User newUser);
Task<User> GetUserByUsername(string userName);
Task<User> UpdateUser(User user);
}
}

View File

@ -1,7 +0,0 @@
namespace Flexitime.Interfaces
{
public interface ITokenFactory
{
string Generate(int size = 32);
}
}

View File

@ -1,10 +0,0 @@
using System;
namespace Flexitime.Objects.API
{
public class AssociateIdentifierRequest
{
public Identifier Identifier { get; set; }
public Guid UserId { get; set; }
}
}

View File

@ -1,7 +0,0 @@
namespace Flexitime.Objects.API
{
public class UsernameValidCheckRequest
{
public string Username { get; set; }
}
}

View File

@ -1,17 +0,0 @@
using System;
using System.Collections.Generic;
namespace Flexitime.Objects
{
public class Application
{
public Application()
{
Permissions = new List<Permission>();
}
public Guid Id { get; set; }
public string Name { get; set; }
public List<Permission> Permissions { get; set; }
}
}

View File

@ -1,20 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<NoWarn>1701;1702;</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="cd &quot;$(ProjectDir)&quot;&#xD;&#xA;csharptojs $(ProjectDir)" />
</Target>
</Project>

View File

@ -1,19 +0,0 @@
using System;
using System.Collections.Generic;
namespace Flexitime.Objects
{
public class Group
{
public Group()
{
IsPrivate = false;
Users = new List<User>();
}
public Guid Id { get; set; }
public string Name { get; set; }
public int UserCount { get; set; }
public bool IsPrivate { get; set; }
public List<User> Users { get; set; }
}
}

View File

@ -1,27 +0,0 @@
using System;
namespace Flexitime.Objects
{
public class Identifier
{
public Identifier() { }
public Guid Id { get; set; }
public string UniqueId { get; set; }
public bool IsAssociatedToUser { get; set; }
public DateTime LastUsed { get; set; }
public override bool Equals(object obj)
{
var identObj = obj as Identifier;
if (identObj == null) return false;
return identObj.Id == Id
&& identObj.IsAssociatedToUser == IsAssociatedToUser
&& identObj.UniqueId == UniqueId;
}
public override int GetHashCode()
{
return Id.GetHashCode() ^ UniqueId.GetHashCode() ^ IsAssociatedToUser.GetHashCode() ^ LastUsed.GetHashCode();
}
}
}

View File

@ -1,11 +0,0 @@
using System;
namespace Flexitime.Objects
{
public class LogSourceDescriptor
{
public string SourceName { get; set; }
public DateTimeOffset LocalDateTime { get; set; }
public string SourceApiKey { get; set; }
}
}

View File

@ -1,11 +0,0 @@
using System.ComponentModel.DataAnnotations;
namespace Flexitime.Objects
{
public class LoginRequest
{
[Required] public string Username { get; set; }
[Required] public string Password { get; set; }
}
}

View File

@ -1,27 +0,0 @@
using System;
namespace Flexitime.Objects
{
public class LoginResponse
{
public LoginResponse()
{
}
public LoginResponse(User user, string token)
{
Id = user.Id;
FirstName = user.FirstName;
LastName = user.LastName;
Username = user.UserName;
Token = token;
}
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Username { get; set; }
public string Token { get; set; }
public string RefreshToken { get; set; }
}
}

View File

@ -1,11 +0,0 @@
using System;
namespace Flexitime.Objects
{
public class Permission
{
public string Name { get; set; }
public string Tag { get; set; }
public Guid Id { get; set; }
}
}

View File

@ -1,13 +0,0 @@
using System;
namespace Flexitime.Objects
{
public class Policy
{
public Version Version { get; set; }
public string DescriptionOfChanges { get; set; }
public User Author { get; set; }
public DateTimeOffset DateOfChange { get; set; }
public string Markdown { get; set; }
}
}

View File

@ -1,17 +0,0 @@
using System;
using System.Collections.Generic;
namespace Flexitime.Objects
{
public class Team
{
public Team()
{
Users = new List<User>();
}
public Guid Id { get; set; }
public string Name { get; set; }
public List<User> Users { get; set; }
}
}

View File

@ -1,13 +0,0 @@
using System;
namespace Flexitime.Objects
{
public class TimeLog
{
public string IdentifierId { get; set; }
public DateTimeOffset LogTime { get; set; }
public LogSourceDescriptor SourceDescriptor { get; set; }
}
}

View File

@ -1,16 +0,0 @@
using System.Collections.Generic;
namespace Flexitime.Objects
{
public class TokenLoginRequest : LoginRequest
{
public TokenLoginRequest()
{
Attributes = new List<KeyValuePair<string, string>>();
}
public string SourceName { get; set; }
public string SourceAddress { get; set; }
public List<KeyValuePair<string, string>> Attributes { get; set; }
}
}

View File

@ -1,61 +0,0 @@
using System;
using System.Collections.Generic;
namespace Flexitime.Objects
{
public class User
{
public User()
{
AssociatedIdentifiers = new List<Identifier>();
Groups = new List<Group>();
Permissions = new List<Permission>();
FirstName = string.Empty;
LastName = string.Empty;
DirectReportIds = new List<int>();
Team = null;
}
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public float HoursPerWeek { get; set; }
public bool IsContractor { get; set; }
public int AssociatedIdentifierCount => AssociatedIdentifiers.Count;
public DateTime LastEventDateTime { get; set; }
public List<Identifier> AssociatedIdentifiers { get; set; }
public List<Group> Groups { get; set; }
/// <summary>
/// user that belongs to team
/// </summary>
/// <remarks>differs from Groups in that a group is a symbolic collection where a team is a publically identifiable entity</remarks>
public Team Team { get; set; }
public UserState State { get; set; }
/// <summary>
/// Id of the Users Line Manager
/// </summary>
public User LineManager { get; set; }
/// <summary>
/// Ids of the users direct reports
/// </summary>
public List<int> DirectReportIds { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public List<Permission> Permissions { get; set; }
}
}

View File

@ -1,11 +0,0 @@
namespace Flexitime.Objects
{
public enum UserState
{
Unknown,
In,
Out,
OutOfOffice,
Remote
}
}

View File

@ -1,12 +0,0 @@
{
"assemblies": [
{
"name": "Flexitime.Objects",
"include": [ "Flexitime.Objects" ]
}
],
"assembliesPath": "./bin/debug/netcoreapp2.2",
"outputPath": "../flexitimeui/src/models",
"noClean": true,
"useNugetCacheResolver": true
}

View File

@ -1,13 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace FlexitimeAPI.Constants
{
public static class Constants
{
public const string InvalidAppSettings =
"There is a problem with the structure of the appsettings.json. Require sections or items may be missing or named incorrectly. Details: {0}";
}
}

View File

@ -1,55 +0,0 @@
using System;
using System.Threading.Tasks;
using Flexitime.Objects;
using FlexitimeAPI.Helpers;
using FlexitimeAPI.Services;
using Microsoft.AspNetCore.Mvc;
namespace FlexitimeAPI.Controllers
{
[ApiController]
[Route("[controller]")]
public class AuthenticationController : ControllerBase
{
private readonly IUserService _userService;
public AuthenticationController(IUserService userService)
{
_userService = userService;
}
[Route("login")]
[HttpPost]
public async Task<IActionResult> Authenticate(LoginRequest model)
{
var response = await _userService.Authenticate(model);
if (response == null)
return BadRequest(new { message = "Username or password is incorrect" });
return Ok(response);
}
[Authorize]
[Route("logout")]
[HttpGet]
public IActionResult Logout()
{
//should be able to get the id here and log a message of that user logging out..
return Ok();
}
[Route("token")]
[HttpPost]
public IActionResult TokenAuthenticationRequest(TokenLoginRequest model)
{
throw new NotImplementedException();
var response = _userService.Authenticate(model);
if (response == null)
return BadRequest(new { message = "Username or password is incorrect" });
return Ok(response);
}
}
}

View File

@ -1,44 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Flexitime.Objects.API;
using FlexitimeAPI.Exceptions;
using FlexitimeAPI.Helpers;
using FlexitimeAPI.Interfaces;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace FlexitimeAPI.Controllers
{
[ApiController]
[Route("[controller]")]
public class IdentifierController : ControllerBase
{
private readonly IIdentifierService _identifierService;
public IdentifierController(IIdentifierService identifierService)
{
_identifierService = identifierService;
}
[Authorize] //permission?
[HttpPost]
public async Task<IActionResult> Associate(AssociateIdentifierRequest associateIdentifierRequest)
{
try
{
await _identifierService.Associate(associateIdentifierRequest.Identifier,
associateIdentifierRequest.UserId);
return Ok();
}
catch (InvalidUserIdException iUidEx)
{
var modelStateDictionary = new ModelStateDictionary();
modelStateDictionary.AddModelError(nameof(Flexitime.Objects.User.Id), iUidEx.Message);
return BadRequest(modelStateDictionary);
}
}
}
}

View File

@ -1,27 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using Flexitime.Objects;
using FlexitimeAPI.Helpers;
namespace FlexitimeAPI.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class PolicyController : ControllerBase
{
[HttpGet]
public List<Policy> GetPolicies()
{
throw new NotImplementedException();
}
[Authorize(Permissions=new []{"p.w"})]
[HttpPost]
public IActionResult UpdatePolicy(Policy newPolicy)
{
//get the authenticated user and populate the author property
throw new NotImplementedException();
}
}
}

View File

@ -1,37 +0,0 @@
using Flexitime.Objects;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
namespace FlexitimeAPI.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class TeamsController : ControllerBase
{
[HttpGet]
public List<Team> GetTeams()
{
throw new NotImplementedException();
}
[HttpGet]
[Route("{id}")]
public Team GetTeam(int id)
{
throw new NotImplementedException();
}
[HttpPost]
public Team CreateTeam(Team team)
{
throw new NotImplementedException();
}
[HttpPut]
public Team UpdateTeam(Team team)
{
throw new NotImplementedException();
}
}
}

View File

@ -1,18 +0,0 @@
using System;
using Flexitime.Objects;
using Microsoft.AspNetCore.Mvc;
namespace FlexitimeAPI.Controllers
{
[Route("[controller]")]
[ApiController]
public class TimeController : ControllerBase
{
[HttpPost]
[Route("log")]
public LoginResponse StoreLog(TimeLog log)
{
throw new NotImplementedException();
}
}
}

View File

@ -1,89 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Flexitime.Objects;
using Flexitime.Objects.API;
using FlexitimeAPI.Exceptions;
using FlexitimeAPI.Helpers;
using FlexitimeAPI.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Server.IIS;
using Microsoft.AspNetCore.Server.Kestrel.Core.Features;
using Microsoft.Extensions.Logging;
namespace FlexitimeAPI.Controllers
{
[ApiController]
[Route("[controller]")]
public class UsersController : ControllerBase
{
private readonly ILogger<UsersController> _logger;
private readonly IUserService _userService;
public UsersController(ILogger<UsersController> logger, IUserService userService)
{
_logger = logger;
_userService = userService;
}
[HttpGet]
public async Task<IActionResult> Get()
{
var userList = await _userService.GetAll();
if (!userList.Any())
{
return NoContent();
}
return Ok(userList);
}
[Authorize]
[HttpGet]
[Route("{id}")]
public async Task<IActionResult> Get(Guid id)
{
var user = await _userService.GetById(id);
if (user == null)
{
return NotFound();
}
return Ok(user);
}
[Authorize("s.u.e")]
[HttpPost]
public async Task<IActionResult> Create([FromBody] User newUser)
{
try
{
var createdUser = await _userService.Add(newUser);
if (createdUser == null)
{
return BadRequest();
}
return CreatedAtAction(nameof(Get), createdUser.Id);
}
catch (InvalidUserNameException iuex)
{
var modelStateDictionary = new ModelStateDictionary();
modelStateDictionary.AddModelError(nameof(Flexitime.Objects.User.UserName), iuex.Message);
return BadRequest(modelStateDictionary);
}
}
[Authorize("s.u.e")]
[HttpPost]
[Route("usernamevalid")]
public Task<IActionResult> UsernameValid([FromBody] UsernameValidCheckRequest usernameValidCheck)
{
throw new NotImplementedException();
}
}
}

View File

@ -1,38 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
namespace FlexitimeAPI.Controllers
{
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
}
}

View File

@ -1,8 +0,0 @@
namespace FlexitimeAPI.Exceptions
{
public static class ExceptionConstants
{
public const string InvalidUsername = "Invalid Username supplied, the username: {0} is a duplicate or banned.";
public const string InvalidUserId = "Invalid User Id supplied: {0}";
}
}

View File

@ -1,20 +0,0 @@
using System;
namespace FlexitimeAPI.Exceptions
{
public class InvalidUserIdException : Exception
{
public InvalidUserIdException(Guid userId)
: base(BuildExceptionMessage(userId))
{
}
public InvalidUserIdException(Guid userId, Exception exception)
: base(BuildExceptionMessage(userId), exception)
{
}
private static string BuildExceptionMessage(Guid userId)
=> string.Format(ExceptionConstants.InvalidUserId, userId);
}
}

View File

@ -1,20 +0,0 @@
using System;
namespace FlexitimeAPI.Exceptions
{
public class InvalidUserNameException : Exception
{
public InvalidUserNameException(string username)
: base(BuildExceptionMessage(username))
{
}
public InvalidUserNameException(string username, Exception exception)
: base(BuildExceptionMessage(username), exception)
{
}
private static string BuildExceptionMessage(string username)
=> string.Format(ExceptionConstants.InvalidUsername, username);
}
}

View File

@ -1,28 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="5.0.0" />
<PackageReference Include="RandomNameGeneratorLibrary" Version="1.2.2" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.8.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Flexitime.DataAccess\Flexitime.DataAccess.csproj" />
<ProjectReference Include="..\Flexitime.Interfaces\Flexitime.Interfaces.csproj" />
<ProjectReference Include="..\Flexitime.Objects\Flexitime.Objects.csproj" />
<ProjectReference Include="..\Infrastructure\Infrastructure.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Repositories\" />
</ItemGroup>
</Project>

View File

@ -1,25 +0,0 @@
using System;
using System.Diagnostics.CodeAnalysis;
using FlexitimeAPI.Models;
using Newtonsoft.Json;
namespace FlexitimeAPI.Functions
{
[ExcludeFromCodeCoverage]
public static class AppSettingsExtensions
{
public static void Verify<TApplicationSettings>(this AppSettings appSettings)
where TApplicationSettings : AppSettings
{
try
{
var applicationSettingsJson = JsonConvert.SerializeObject(appSettings);
JsonConvert.DeserializeObject<TApplicationSettings>(applicationSettingsJson);
}
catch (Exception exception)
{
throw new Exception(string.Format(Constants.Constants.InvalidAppSettings, exception.Message));
}
}
}
}

View File

@ -1,17 +0,0 @@
using Flexitime.Interfaces;
using FlexitimeAPI.Interfaces;
namespace FlexitimeAPI.Functions
{
public class ConnectionStringProvider : IConnectionStringProvider
{
private readonly IApplicationSettings _appSettings;
public ConnectionStringProvider(IApplicationSettings appSettings)
{
_appSettings = appSettings;
}
public string ConnectionString => _appSettings.ConnectionString;
}
}

View File

@ -1,43 +0,0 @@
using System;
using System.Linq;
using Flexitime.Objects;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
namespace FlexitimeAPI.Helpers
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AuthorizeAttribute : Attribute, IAuthorizationFilter
{
public AuthorizeAttribute()
{
Permissions = new string[] { };
}
public AuthorizeAttribute(params string[] permissions)
{
Permissions = permissions;
}
public string[] Permissions { get; set; }
public void OnAuthorization(AuthorizationFilterContext context)
{
var user = (User) context.HttpContext.Items["User"];
if (user == null)
// not logged in
context.Result = new JsonResult(
new { message = "Unauthorized" })
{ StatusCode = StatusCodes.Status401Unauthorized };
else if (Permissions.Any()
&& user.Permissions != null
&& !user.Permissions.Select(y => y.Tag)
.Intersect(Permissions.ToList())
.Any()) //check we have permissions if they have been specified
context.Result = new JsonResult(
new { message = "Unauthorized" })
{ StatusCode = StatusCodes.Status401Unauthorized };
}
}
}

View File

@ -1,64 +0,0 @@
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FlexitimeAPI.Interfaces;
using FlexitimeAPI.Models;
using FlexitimeAPI.Services;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
namespace FlexitimeAPI.Helpers
{
public class JwtMiddleware
{
private readonly IApplicationSettings _appSettings;
private readonly RequestDelegate _next;
public JwtMiddleware(RequestDelegate next, IApplicationSettings appSettings)
{
_next = next;
_appSettings = appSettings;
}
public async Task Invoke(HttpContext context, IUserService userService)
{
var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
if (token != null)
await AttachUserToContext(context, userService, token);
await _next(context);
}
private async Task AttachUserToContext(HttpContext context, IUserService userService, string token)
{
try
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
tokenHandler.ValidateToken(token, new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false,
// set clockskew to zero so tokens expire exactly at token expiration time (instead of 5 minutes later)
ClockSkew = TimeSpan.Zero
}, out var validatedToken);
var jwtToken = (JwtSecurityToken) validatedToken;
var userId = Guid.Parse(jwtToken.Claims.First(x => x.Type == "id").Value);
// attach user to context on successful jwt validation
context.Items["User"] = await userService.GetById(userId);
}
catch(Exception ex)
{
// do nothing if jwt validation fails
// user is not attached to context so request won't have access to secure routes
}
}
}
}

View File

@ -1,19 +0,0 @@
using Flexitime.Interfaces;
using System;
using System.Security.Cryptography;
namespace FlexitimeAPI.Helpers
{
public class TokenGenerator:ITokenFactory
{
public string Generate(int size = 32)
{
var randomNumber = new byte[size];
using (var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(randomNumber);
return Convert.ToBase64String(randomNumber);
}
}
}
}

View File

@ -1,29 +0,0 @@
using System;
using System.Threading.Tasks;
using Flexitime.Objects;
namespace FlexitimeAPI.Interfaces
{
public interface ITimePersistenceService
{
UserState StoreTime(TimeLog logEntry);
void UpdateTimeLog(Guid logToUpdate, TimeLog updatedLogEntry);
}
public interface IApplicationSettings
{
public string Secret { get; }
public string ConnectionString { get; }
}
public interface ILoginService
{
string GenerateJwtToken(User user);
}
public interface IIdentifierService
{
Task Associate(Identifier identifier, Guid userId);
}
}

View File

@ -1,18 +0,0 @@
using FlexitimeAPI.Interfaces;
using Infrastructure.Functions;
using Newtonsoft.Json;
namespace FlexitimeAPI.Models
{
public class AppSettings:IApplicationSettings
{
[JsonProperty(Required = Required.Always)]
[JsonConverter(typeof(NonEmptyStringConverter))]
public string Secret { get; set; }
[JsonProperty(Required = Required.Always)]
[JsonConverter(typeof(NonEmptyStringConverter))]
public string ConnectionString { get; set; }
}
}

View File

@ -1,20 +0,0 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
namespace FlexitimeAPI
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}

View File

@ -1,31 +0,0 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:46914",
"sslPort": 44370
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"FlexitimeAPI": {
"commandName": "Project",
"dotnetRunMessages": "true",
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@ -1,31 +0,0 @@
using System;
using System.Threading.Tasks;
using Flexitime.Objects;
using FlexitimeAPI.Exceptions;
using FlexitimeAPI.Interfaces;
namespace FlexitimeAPI.Services
{
public class IdentifierService : IIdentifierService
{
private readonly IUserService _userService;
public IdentifierService(IUserService userService)
{
_userService = userService;
}
public async Task Associate(Identifier identifier, Guid userId)
{
var user = await _userService.GetById(userId);
if (user == null)
{
throw new InvalidUserIdException(userId);
}
user.AssociatedIdentifiers.Add(identifier);
await _userService.Update(user);
}
}
}

View File

@ -1,50 +0,0 @@
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using Flexitime.Objects;
using FlexitimeAPI.Interfaces;
using Microsoft.IdentityModel.Tokens;
namespace FlexitimeAPI.Services
{
public class LoginService : ILoginService
{
private readonly IApplicationSettings _appSettings;
public LoginService(IApplicationSettings appSettings)
{
_appSettings = appSettings;
}
public string GenerateJwtToken(User user)
{
// generate token that is valid for 7 days
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
var claims = new Dictionary<string, object>
{
{"permissions",user.Permissions.Select(x=>x.Tag)}
};
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[] {new Claim("id", user.Id.ToString())}),
Expires = DateTime.UtcNow.AddHours(2),
Issuer = "FlexitimeUI",
NotBefore = DateTime.UtcNow.AddSeconds(-5),
IssuedAt = DateTime.UtcNow,
Claims = claims,
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key),
SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
//tokenHandler.
return tokenHandler.WriteToken(token);
}
}
}

View File

@ -1,22 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Flexitime.Objects;
using FlexitimeAPI.Interfaces;
namespace FlexitimeAPI.Services
{
public class TimePersistenceService:ITimePersistenceService
{
public UserState StoreTime(TimeLog logEntry)
{
throw new NotImplementedException();
}
public void UpdateTimeLog(Guid logToUpdate, TimeLog updatedLogEntry)
{
throw new NotImplementedException();
}
}
}

View File

@ -1,104 +0,0 @@
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using AutoMapper;
using Flexitime.DataAccess.Objects;
using Flexitime.Interfaces;
using Flexitime.Objects;
using FlexitimeAPI.Exceptions;
using FlexitimeAPI.Interfaces;
using FlexitimeAPI.Models;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using RandomNameGeneratorLibrary;
namespace FlexitimeAPI.Services
{
public interface IUserService
{
Task<LoginResponse> Authenticate(LoginRequest model);
Task<IEnumerable<User>> GetAll();
Task<User> GetById(Guid id);
Task<User> Add(User newUser);
Task<bool> UsernameIsValid(string username);
Task<User> Update(User user);
}
public class UserService : IUserService
{
private readonly IDataAccess _dataSource;
private readonly ILoginService _loginService;
public UserService(IDataAccess dataSource, ILoginService loginService)
{
_dataSource = dataSource;
_loginService = loginService;
}
public async Task<LoginResponse> Authenticate(LoginRequest model)
{
var user = (await _dataSource.GetUsers()).SingleOrDefault(x => x.UserName == model.Username && x.Password == model.Password);
// return null if user not found
if (user == null) return null;
// authentication successful so generate jwt token
var token = _loginService.GenerateJwtToken(user);
return new LoginResponse(user, token);
}
public async Task<IEnumerable<User>> GetAll()
{
return await _dataSource.GetUsers();
}
public async Task<User> GetById(Guid id)
{
return await _dataSource.GetUserById(id);
}
public async Task<User> Add(User newUser)
{
var userNameIsValid = await UsernameIsValid(newUser.UserName);
if (!userNameIsValid)
{
throw new InvalidUserNameException(newUser.UserName);
}
return await _dataSource.AddUser(newUser);
}
public async Task<bool> UsernameIsValid(string userName)
{
if (string.IsNullOrWhiteSpace(userName))
{
return false;
}
//TODO: blacklisted username check
var existingUser = await _dataSource.GetUserByUsername(userName);
return existingUser == null;
}
public async Task<User> Update(User user)
{
if (user == null) return null;
if ((await GetById(user.Id)) == null)
{
return null;
}
return await _dataSource.UpdateUser(user);
}
}
}

View File

@ -1,97 +0,0 @@
using System.IO;
using System.Text.Json.Serialization;
using Flexitime.DataAccess;
using Flexitime.Interfaces;
using FlexitimeAPI.Functions;
using FlexitimeAPI.Helpers;
using FlexitimeAPI.Interfaces;
using FlexitimeAPI.Models;
using FlexitimeAPI.Services;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
namespace FlexitimeAPI
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
var settings = InitialiseAppSettings();
services.AddSingleton(typeof(IApplicationSettings), settings);
services.AddSingleton<ILoginService, LoginService>();
services.AddSingleton<IConnectionStringProvider, ConnectionStringProvider>();
services.AddSingleton<IDataAccess, Database>();
services.AddSingleton<IUserService, UserService>();
services.AddCors(options =>
{
options.AddDefaultPolicy(
builder =>
{
builder
.WithOrigins("http://localhost:3000")
.AllowAnyMethod()
.AllowAnyHeader();
});
});
services.AddControllers().AddJsonOptions(opts =>
{
opts.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
opts.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
});
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo {Title = "FlexitimeAPI", Version = "v1"});
});
}
private IApplicationSettings InitialiseAppSettings()
{
var appSettings= new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.Build()
.GetSection("Settings")
.Get<AppSettings>();
appSettings.Verify<AppSettings>();
return appSettings;
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "FlexitimeAPI v1"));
}
app.UseCors();
//app.UseHttpsRedirection();
app.UseRouting();
app.UseMiddleware<JwtMiddleware>();
app.UseAuthorization();
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
}
}
}

View File

@ -1,15 +0,0 @@
using System;
namespace FlexitimeAPI
{
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public string Summary { get; set; }
}
}

View File

@ -1,13 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"Settings": {
"Secret": "JiminiyCricket|{08EBD219-AABA-4C33-8312-AE93EECE77D2}",
"ConnectionString": "Data Source=.\\SQLSERVER2019;Initial Catalog=FlexitimeData;Integrated Security=SSPI;"
}
}

View File

@ -1,14 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"Settings": {
"Secret": "JiminiyCricket|{08EBD219-AABA-4C33-8312-AE93EECE77D2}",
"ConnectionString": "Data Source=.\\SQLSERVER2019;Initial Catalog=FlexitimeData;Integrated Security=SSPI;"
}
}

View File

@ -1,95 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using AutoFixture;
using Flexitime.Objects;
using FlexitimeAPI.Helpers;
using FluentAssertions;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Moq;
using Xunit;
namespace FlexitimeApi.UnitTests
{
public class AuthorizeAttributeTests
{
private readonly Fixture _f = new();
[Fact]
public void AuthorizeAttribute_Should_AuthorizeWithNoExplicitPermissions()
{
var sut = new AuthorizeAttribute();
var mockUser = _f.Build<User>()
.Without(x => x.Permissions)
.Without(x => x.Groups)
.Without(x => x.Team)
.Create();
var ctx = SetupContext(mockUser);
sut.OnAuthorization(ctx);
ctx.Result.Should().BeNull();
}
[Fact]
public void AuthorizeAttribute_ShouldNot_AuthorizeWhenUserHasInvalidPermission()
{
var mockUser = _f.Build<User>()
.With(x => x.Permissions,
_f.Build<Permission>().With(y => y.Tag, "g.x").Without(y => y.Application).CreateMany(1).ToList())
.Without(x => x.Groups)
.Without(x => x.Team)
.Create();
var ctx = SetupContext(mockUser);
var sut = new AuthorizeAttribute {Permissions = new[] {"u.w"}};
sut.OnAuthorization(ctx);
ctx.Result.Should().BeOfType<JsonResult>();
var actual = ctx.Result as JsonResult;
actual.StatusCode.Should().Be(401);
}
[Fact]
public void AuthorizeAttribute_Should_AuthorizeWithCorrectExplicitPermission()
{
var mockUser = _f.Build<User>()
.With(x => x.Permissions,
_f.Build<Permission>().With(y => y.Tag, "g.x").Without(y => y.Application).CreateMany(1).ToList())
.Without(x => x.Groups)
.Without(x => x.Team)
.Create();
var ctx = SetupContext(mockUser);
var sut = new AuthorizeAttribute {Permissions = new[] {"g.x"}};
sut.OnAuthorization(ctx);
ctx.Result.Should().BeNull();
}
private AuthorizationFilterContext SetupContext(User mockUser)
{
var httpContextMock = new Mock<HttpContext>();
httpContextMock
.Setup(a => a.Request.Headers["Authorization"])
.Returns("mock WRONG apikey");
httpContextMock.SetupGet(x => x.Items)
.Returns(new Dictionary<object, object?> {{"User", mockUser}});
ActionContext fakeActionContext =
new ActionContext(httpContextMock.Object,
new Microsoft.AspNetCore.Routing.RouteData(),
new Microsoft.AspNetCore.Mvc.Abstractions.ActionDescriptor());
var ret = new AuthorizationFilterContext(fakeActionContext,
new List<IFilterMetadata>());
return ret;
}
}
}

View File

@ -1,19 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Autofixture" Version="4.15.0" />
<PackageReference Include="FluentAssertions" Version="5.10.3" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" />
<PackageReference Include="Moq" Version="4.16.1" />
<PackageReference Include="XUnit" Version="2.4.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\FlexitimeAPI\FlexitimeAPI.csproj" />
</ItemGroup>
</Project>

File diff suppressed because it is too large Load Diff

View File

@ -1,19 +0,0 @@
<prism:PrismApplication x:Class="FlexitimeTaskbarUtility.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:FlexitimeTaskbarUtility.Converters"
ShutdownMode="OnExplicitShutdown"
xmlns:prism="http://prismlibrary.com/">
<prism:PrismApplication.Resources>
<!-- merge NotifyIcon and related stuff into the application -->
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="NotifyIconResources.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</prism:PrismApplication.Resources>
</prism:PrismApplication>

View File

@ -1,60 +0,0 @@
using System.Windows;
using System.Windows.Controls;
using FlexitimeTaskbarUtility.Extensions;
using FlexitimeTaskbarUtility.Interfaces;
using FlexitimeTaskbarUtility.Services;
using FlexitimeTaskbarUtility.ViewModels;
using Hardcodet.Wpf.TaskbarNotification;
using Prism.DryIoc;
using Prism.Events;
using Prism.Ioc;
using Prism.Regions;
namespace FlexitimeTaskbarUtility
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : PrismApplication
{
private TaskbarIcon notifyIcon;
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
}
protected override void ConfigureRegionAdapterMappings(RegionAdapterMappings regionAdapterMappings)
{
base.ConfigureRegionAdapterMappings(regionAdapterMappings);
regionAdapterMappings.RegisterMapping(typeof(ContextMenu), Container.Resolve<ContextMenuRegionAdapter>());
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.Register<ILoginService, LoginService>();
containerRegistry.Register<ISignInService, SignInService>();
containerRegistry.Register<NotifyIconViewModel>();
containerRegistry.Register<LoginControlViewModel>();
}
protected override Window CreateShell()
{
return null;
}
protected override void OnInitialized()
{
base.OnInitialized();
notifyIcon = (TaskbarIcon)FindResource("NotifyIcon");
notifyIcon.DataContext = Container.Resolve<NotifyIconViewModel>();
}
protected override void OnExit(ExitEventArgs e)
{
notifyIcon.Dispose(); //the icon would clean up automatically, but this is cleaner
base.OnExit(e);
}
}
}

View File

@ -1,10 +0,0 @@
using System.Windows;
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]

View File

@ -1,72 +0,0 @@
<UserControl x:Class="FlexitimeTaskbarUtility.Components.LoginControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:viewModels="clr-namespace:FlexitimeTaskbarUtility.ViewModels"
xmlns:components="clr-namespace:FlexitimeTaskbarUtility.Components"
xmlns:prism="http://prismlibrary.com/"
d:DataContext="{d:DesignInstance Type=viewModels:LoginControlViewModel}"
mc:Ignorable="d"
MinHeight="150"
MinWidth="300"
prism:ViewModelLocator.AutoWireViewModel="True">
<Grid Background="Azure">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0"
FontWeight="Bold"
FontSize="22"
Margin="10,10,0,0">
Login
</TextBlock>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0"
Grid.Column="0"
FontSize="18"
Margin="10,0,0,0">
Username
</TextBlock>
<TextBox Grid.Row="0"
Grid.Column="1"
FontSize="18"
Text="{Binding UserName}"
Margin="0,0,10,0"/>
<TextBlock Grid.Row="1"
Grid.Column="0"
FontSize="18"
Margin="10,0,0,0">
Password
</TextBlock>
<PasswordBox Grid.Row="1"
Grid.Column="1"
Name="PasswordBox"
components:PasswordBoxAssistant.BindPassword="True"
components:PasswordBoxAssistant.BoundPassword="{Binding Path=Password, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,0,10,0"/>
</Grid>
<Button Grid.Row="2"
Height="30"
Width="120"
FontSize="18"
VerticalAlignment="Bottom"
HorizontalAlignment="Right"
Margin="0,15,0,0"
Command="{Binding LoginCommand}"
CommandParameter="{Binding ElementName=PasswordBox}">
Login
</Button>
</Grid>
</UserControl>

View File

@ -1,15 +0,0 @@
using System.Windows.Controls;
namespace FlexitimeTaskbarUtility.Components
{
/// <summary>
/// Interaction logic for LoginControl.xaml
/// </summary>
public partial class LoginControl : UserControl
{
public LoginControl()
{
InitializeComponent();
}
}
}

View File

@ -1,108 +0,0 @@
using System.Windows;
using System.Windows.Controls;
namespace FlexitimeTaskbarUtility.Components
{
public static class PasswordBoxAssistant
{
public static readonly DependencyProperty BoundPassword =
DependencyProperty.RegisterAttached(nameof(BoundPassword), typeof(string), typeof(PasswordBoxAssistant), new PropertyMetadata(string.Empty, OnBoundPasswordChanged));
public static readonly DependencyProperty BindPassword = DependencyProperty.RegisterAttached(
nameof(BindPassword), typeof(bool), typeof(PasswordBoxAssistant), new PropertyMetadata(false, OnBindPasswordChanged));
private static readonly DependencyProperty UpdatingPassword =
DependencyProperty.RegisterAttached(nameof(UpdatingPassword), typeof(bool), typeof(PasswordBoxAssistant), new PropertyMetadata(false));
private static void OnBoundPasswordChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
PasswordBox box = d as PasswordBox;
// only handle this event when the property is attached to a PasswordBox
// and when the BindPassword attached property has been set to true
if (d == null || !GetBindPassword(d))
{
return;
}
// avoid recursive updating by ignoring the box's changed event
box.PasswordChanged -= HandlePasswordChanged;
string newPassword = (string)e.NewValue;
if (!GetUpdatingPassword(box))
{
box.Password = newPassword;
}
box.PasswordChanged += HandlePasswordChanged;
}
private static void OnBindPasswordChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
{
// when the BindPassword attached property is set on a PasswordBox,
// start listening to its PasswordChanged event
PasswordBox box = dp as PasswordBox;
if (box == null)
{
return;
}
bool wasBound = (bool)(e.OldValue);
bool needToBind = (bool)(e.NewValue);
if (wasBound)
{
box.PasswordChanged -= HandlePasswordChanged;
}
if (needToBind)
{
box.PasswordChanged += HandlePasswordChanged;
}
}
private static void HandlePasswordChanged(object sender, RoutedEventArgs e)
{
PasswordBox box = sender as PasswordBox;
// set a flag to indicate that we're updating the password
SetUpdatingPassword(box, true);
// push the new password into the BoundPassword property
SetBoundPassword(box, box.Password);
SetUpdatingPassword(box, false);
}
public static void SetBindPassword(DependencyObject dp, bool value)
{
dp.SetValue(BindPassword, value);
}
public static bool GetBindPassword(DependencyObject dp)
{
return (bool)dp.GetValue(BindPassword);
}
public static string GetBoundPassword(DependencyObject dp)
{
return (string)dp.GetValue(BoundPassword);
}
public static void SetBoundPassword(DependencyObject dp, string value)
{
dp.SetValue(BoundPassword, value);
}
private static bool GetUpdatingPassword(DependencyObject dp)
{
return (bool)dp.GetValue(UpdatingPassword);
}
private static void SetUpdatingPassword(DependencyObject dp, bool value)
{
dp.SetValue(UpdatingPassword, value);
}
}
}

View File

@ -1,13 +0,0 @@
<UserControl x:Class="FlexitimeTaskbarUtility.Components.SigninControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
Height="100"
Width="200">
<Grid Background="Green">
<TextBlock HorizontalAlignment="Center" Foreground="White" VerticalAlignment="Center">Sign in control</TextBlock>
<Button Height="30" VerticalAlignment="Bottom" Command="{Binding SignInCommand}">Sign In</Button>
</Grid>
</UserControl>

View File

@ -1,15 +0,0 @@
using System.Windows.Controls;
namespace FlexitimeTaskbarUtility.Components
{
/// <summary>
/// Interaction logic for SigninControl.xaml
/// </summary>
public partial class SigninControl : UserControl
{
public SigninControl()
{
InitializeComponent();
}
}
}

View File

@ -1,52 +0,0 @@
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Flexitime.Objects;
namespace FlexitimeTaskbarUtility.Converters
{
public class UserStatusToImageSourceConverter:IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value.GetType() != typeof(UserState))
{
throw new ArgumentException(nameof(value));
}
var userState = (UserState) value;
string basePath = string.Intern("pack://application:,,,/FlexitimeTaskbarUtility;component/Images/");
string imageName;
switch (userState)
{
case UserState.Unknown:
imageName = "Clock_LoggedOut.ico";
break;
case UserState.In:
imageName = "Clock_In.ico";
break;
case UserState.Out:
imageName = "Clock_Out.ico";
break;
case UserState.OutOfOffice:
imageName = "Clock_OutOfOffice.ico";
break;
case UserState.Remote:
imageName = "Clock_Remote.ico";
break;
default:
throw new ArgumentOutOfRangeException();
}
ImageSource ret = new BitmapImage(new Uri($"{basePath}{imageName}"));
return ret;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@ -1,38 +0,0 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using Flexitime.Objects;
namespace FlexitimeTaskbarUtility.Converters
{
public class UserStatusToLoginDialogVisibilityConverter:IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value.GetType() != typeof(UserState))
{
throw new ArgumentException(nameof(value));
}
var userState = (UserState)value;
Visibility ret;
switch (userState)
{
case UserState.Unknown:
ret = Visibility.Visible;
break;
default:
ret = Visibility.Collapsed;
break;
}
return ret;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@ -1,38 +0,0 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using Flexitime.Objects;
namespace FlexitimeTaskbarUtility.Converters
{
public class UserStatusToSigninControlVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value.GetType() != typeof(UserState))
{
throw new ArgumentException(nameof(value));
}
var userState = (UserState)value;
Visibility ret;
switch (userState)
{
case UserState.Unknown:
ret = Visibility.Collapsed;
break;
default:
ret = Visibility.Visible;
break;
}
return ret;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@ -1,30 +0,0 @@
using System;
using System.Windows.Input;
namespace NotifyIconWpf.Sample.Windowless
{
/// <summary>
/// Simplistic delegate command for the demo.
/// </summary>
public class DelegateCommand : ICommand
{
public Action CommandAction { get; set; }
public Func<bool> CanExecuteFunc { get; set; }
public void Execute(object parameter)
{
CommandAction();
}
public bool CanExecute(object parameter)
{
return CanExecuteFunc == null || CanExecuteFunc();
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
}

Some files were not shown because too many files have changed in this diff Show More