Merge commit 'a9d8e4c015d964b15ddf766327dfa3b0aae55a02'

This commit is contained in:
Chris.Watts90@outlook.com 2018-06-06 12:13:25 +01:00
commit 3699aff489
34 changed files with 521 additions and 159 deletions

View File

@ -12,10 +12,10 @@ namespace CardReaderService
{
private Thread _mainWorkThread;
private bool _stopMainWorkerThread;
private AutoResetEvent _mainWorkerTerminationSignal;
private string _readerName = "";
//private SCardReader _reader;
private SCardMonitor _cardMonitor;
private bool _initialised=false;
public Service1()
{
@ -24,29 +24,30 @@ namespace CardReaderService
public void Start()
{
OnStart(new string[] {});
OnStart(new string[] { });
}
//
protected override void OnStart(string[] args)
{
Console.WriteLine("Starting.. Getting available readers");
var ctxFactory = ContextFactory.Instance;
using(var context = ctxFactory.Establish(SCardScope.System))
{
var readerNames = context.GetReaders();
if (NoReaderAvailable(readerNames))
{
Console.WriteLine("No Card Reader is available, Exiting..");
return;
}
Console.WriteLine("Choosing first available reader: " + readerNames.First());
_readerName = readerNames.First();
_cardMonitor = new SCardMonitor(ctxFactory, SCardScope.System);
_cardMonitor.CardInserted += _cardMonitor_CardInserted;
_cardMonitor.Start(_readerName);
StartWorkerThread();
}
private bool WeHaveValidCardReader()
{
if (_cardMonitor == null)
{
return false;
}
Console.WriteLine(_cardMonitor.GetCurrentState(0));
if (_cardMonitor.GetCurrentState(0) == SCRState.Unknown
|| _cardMonitor.GetCurrentState(0) == SCRState.Unavailable
|| _cardMonitor.GetCurrentState(0) == (SCRState.Ignore | SCRState.Unavailable)
//|| _cardMonitor.GetCurrentState(0) == SCRState.Unaware //if we say this is an invalid state, we cause a memory leak where we create a duplicate card monitor, subscribe and overwrite.
)
{
return false;
}
return true;
}
public void Stop()
@ -57,14 +58,11 @@ namespace CardReaderService
protected override void OnStop()
{
_stopMainWorkerThread = true;
if (_mainWorkThread!= null && _mainWorkThread.IsAlive)
{
_mainWorkThread.Join(3000);
if (_mainWorkThread.IsAlive)
_mainWorkerTerminationSignal.Set();
if (_mainWorkThread != null && _mainWorkThread.IsAlive)
{
_mainWorkThread.Interrupt();
}
}
if (_cardMonitor != null)
{
_cardMonitor.Cancel();
@ -81,6 +79,7 @@ namespace CardReaderService
Name = "CardServiceMainThread",
IsBackground = false
};
_mainWorkerTerminationSignal = new AutoResetEvent(false);
_mainWorkThread.Start();
}
@ -113,7 +112,7 @@ namespace CardReaderService
var atrString = ConvertByteUIDToString(e.Atr);
Console.WriteLine("Card Inserted, ATR: " + atrString + ", and UID is: " + uid);
CardDataPost postObj = new CardDataPost {CardUId = uid};
CardDataPost postObj = new CardDataPost { CardUId = uid };
DataCenterHelper.PostAsync(postObj, "/api/swipedata");
Console.WriteLine("Posted to Server");
}
@ -130,16 +129,48 @@ namespace CardReaderService
{
while (!_stopMainWorkerThread)
{
//dont actually need to do anything.. but cannot exit right away?
Thread.Sleep(3000);
if (!WeHaveValidCardReader())
{ //only do this if we don't have a valid card reader
if (_initialised)
{
//card reader no longer available, tidy up.
_cardMonitor.Cancel();
_cardMonitor.CardInserted -= _cardMonitor_CardInserted;
_cardMonitor.Dispose();
_cardMonitor = null;
_initialised = false;
}
Console.WriteLine("Starting.. Getting available readers");
var ctxFactory = ContextFactory.Instance;
using (var context = ctxFactory.Establish(SCardScope.System))
{
var readerNames = context.GetReaders();
if (!NoReaderAvailable(readerNames))
{
//we have a reader available, so initialise!
Console.WriteLine("Choosing first available reader: " + readerNames.First());
_readerName = readerNames.First();
_cardMonitor = new SCardMonitor(ctxFactory, SCardScope.System);
_cardMonitor.CardInserted += _cardMonitor_CardInserted;
_cardMonitor.Start(_readerName);
_initialised = true;
}
else
{
Console.WriteLine("No Card Reader is available..");
}
}
}
_mainWorkerTerminationSignal.WaitOne(3000);
}
}
private string ConvertByteUIDToString(byte[] uid)
{
StringBuilder sb = new StringBuilder();
var sb = new StringBuilder();
foreach(var b in uid)
foreach (var b in uid)
{
sb.AppendFormat("{0:X2}", b);
}

View File

@ -18,6 +18,9 @@ namespace CardReaderService
private string _readerName = "";
private SCardMonitor _cardMonitor;
private bool _initialised = false;
private AutoResetEvent _mainWorkerTerminationSignal;
private ILogger _logger;
public CardReaderService()
@ -32,46 +35,10 @@ namespace CardReaderService
protected override void OnStart(string[] args)
{
var configPath = string.Concat(System.Reflection.Assembly.GetEntryAssembly().Location, ".config");
_logger = NinjectHelper.GetInstance().Get<ILogger>();
_logger.Trace("Starting Service.. Getting available readers");
var ctxFactory = ContextFactory.Instance;
using(var context = ctxFactory.Establish(SCardScope.System))
{
var readerNames = context.GetReaders();
if (NoReaderAvailable(readerNames))
{
_logger.Trace("No Card Reader is available, Exiting..");
throw new ApplicationException("A card reader must be provided in order to operate.");
}
foreach (var reader in readerNames)
{
_logger.Trace("Found reader: {0}", reader);
}
var readerNameConfig = ConfigurationHandler.ConfigurationHandler.GetConfiguration("ReaderName");
if (string.IsNullOrEmpty(readerNameConfig))
{
if (!readerNames.Contains(readerNameConfig))
{
_logger.Warn("No reader found with the name: {0}, defaulting to first available reader {1}",
readerNameConfig, readerNames.First());
readerNameConfig=readerNames.First();
}
}
_logger.Trace("Choosing reader: {0}", readerNameConfig);
_readerName = readerNameConfig;
_cardMonitor = new SCardMonitor(ctxFactory, SCardScope.System);
_cardMonitor.CardInserted += _cardMonitor_CardInserted;
_cardMonitor.Start(_readerName);
StartWorkerThread();
}
}
public void StopService()
{
@ -81,6 +48,7 @@ namespace CardReaderService
protected override void OnStop()
{
_stopMainWorkerThread = true;
_mainWorkerTerminationSignal.Set();
if (_mainWorkThread!= null && _mainWorkThread.IsAlive)
{
_mainWorkThread.Join(3000);
@ -104,6 +72,7 @@ namespace CardReaderService
Name = "CardServiceMainThread",
IsBackground = false
};
_mainWorkerTerminationSignal = new AutoResetEvent(false);
_mainWorkThread.Start();
}
@ -137,7 +106,7 @@ namespace CardReaderService
_logger.Trace("Card Inserted, ATR: " + atrString + ", and UID is: " + uid);
var postObj = new CardDataPost {CardUId = uid};
DataCenterHelper.PostAsync(postObj, "/api/swipedata");
DataCenterHelper.PostAsync(_logger, postObj, "/api/swipedata");
_logger.Trace("Posted to Server");
}
else
@ -153,7 +122,51 @@ namespace CardReaderService
while (!_stopMainWorkerThread)
{
//dont actually need to do anything.. but cannot exit right away?
Thread.Sleep(3000);
if (!WeHaveValidCardReader())
{
if (_initialised)
{
//card reader no longer available, tidy up.
_cardMonitor.Cancel();
_cardMonitor.CardInserted -= _cardMonitor_CardInserted;
_cardMonitor.Dispose();
_cardMonitor = null;
_initialised = false;
}
var ctxFactory = ContextFactory.Instance;
using (var context = ctxFactory.Establish(SCardScope.System))
{
var readerNames = context.GetReaders();
if (NoReaderAvailable(readerNames))
{
_logger.Trace("No Card Reader is available..");
}
else
{
foreach (var reader in readerNames)
{
_logger.Trace("Found reader: {0}", reader);
}
var readerNameConfig = ConfigurationHandler.ConfigurationHandler.GetConfiguration("ReaderName");
if (string.IsNullOrEmpty(readerNameConfig) || (!readerNames.Contains(readerNameConfig)))
{
_logger.Warn("No reader found with the name: {0}, defaulting to first available reader {1}",
readerNameConfig, readerNames.First());
readerNameConfig = readerNames.First();
}
_logger.Trace("Choosing reader: {0}", readerNameConfig);
_readerName = readerNameConfig;
_cardMonitor = new SCardMonitor(ctxFactory, SCardScope.System);
_cardMonitor.CardInserted += _cardMonitor_CardInserted;
_cardMonitor.Start(_readerName);
_initialised = true;
}
}
}
_mainWorkerTerminationSignal.WaitOne(3000);
}
}
@ -171,5 +184,23 @@ namespace CardReaderService
{
return readerNames == null || readerNames.Count < 1;
}
private bool WeHaveValidCardReader()
{
if (_cardMonitor == null)
{
return false;
}
_logger.Trace(_cardMonitor.GetCurrentState(0).ToString());
if (_cardMonitor.GetCurrentState(0) == SCRState.Unknown
|| _cardMonitor.GetCurrentState(0) == SCRState.Unavailable
|| _cardMonitor.GetCurrentState(0) == (SCRState.Ignore | SCRState.Unavailable)
//|| _cardMonitor.GetCurrentState(0) == SCRState.Unaware //if we say this is an invalid state, we cause a memory leak where we create a duplicate card monitor, subscribe and overwrite.
)
{
return false;
}
return true;
}
}
}

View File

@ -5,13 +5,14 @@ using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Interfaces;
using Newtonsoft.Json;
namespace CardReaderService
{
static class DataCenterHelper
{
public static void Post(CardDataPost postObject, string url)
public static void Post(ILogger logger, CardDataPost postObject, string url)
{
var endpointConfig = ConfigurationHandler.ConfigurationHandler.GetConfiguration("DataCenterServiceEndpoint") ??
"http://localhost:8800";
@ -22,10 +23,10 @@ namespace CardReaderService
var content = new StringContent(jsonObject, Encoding.UTF8, "application/json");
try
{
Console.WriteLine("Writing");
logger.Trace("Writing");
var fullUrl = endpointConfig + url;
var response = client.PostAsync(fullUrl, content).Result;
Console.WriteLine("Written");
logger.Trace("Written");
}
catch (Exception)
{
@ -35,9 +36,9 @@ namespace CardReaderService
}
}
public static Task PostAsync(CardDataPost postObject, string url)
public static Task PostAsync(ILogger logger, CardDataPost postObject, string url)
{
return Task.Run(() => Post(postObject, url));
return Task.Run(() => Post(logger, postObject, url));
}
}

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.1.5.0")]
[assembly: AssemblyFileVersion("0.1.5.0")]
[assembly: AssemblyVersion("0.2.1.0")]
[assembly: AssemblyFileVersion("0.2.1.0")]

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("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyVersion("0.2.1.0")]
[assembly: AssemblyFileVersion("0.2.1.0")]

View File

@ -37,6 +37,14 @@
<RefProjectOutputGroups>Binaries;Content;Satellites</RefProjectOutputGroups>
<RefTargetDir>INSTALLFOLDER</RefTargetDir>
</ProjectReference>
<ProjectReference Include="..\ConfigurationHandler\ConfigurationHandler.csproj">
<Name>ConfigurationHandler</Name>
<Project>{115250f6-f8c4-4f9b-a15f-251ea258d963}</Project>
<Private>True</Private>
<DoNotHarvest>True</DoNotHarvest>
<RefProjectOutputGroups>Binaries;Content;Satellites</RefProjectOutputGroups>
<RefTargetDir>INSTALLFOLDER</RefTargetDir>
</ProjectReference>
<ProjectReference Include="..\Interfaces\Interfaces.csproj">
<Name>Interfaces</Name>
<Project>{b7347b72-e208-423a-9d99-723b558ea3d7}</Project>

View File

@ -8,6 +8,12 @@
KeyPath="yes"
Source="$(var.Interfaces.TargetDir)Interfaces.dll" />
</Component>
<Component Id="ConfigurationHandler" Guid="{54C7ACE7-02DE-4EDC-BDDC-7DC6F34F6AF8}">
<File Id="ConfigurationHandlerDll"
Checksum="yes"
KeyPath="yes"
Source="$(var.ConfigurationHandler.TargetDir)ConfigurationHandler.dll"/>
</Component>
</ComponentGroup>
</Fragment>
</Wix>

View File

@ -3,7 +3,7 @@
<Product Id="{DA4797C8-E60E-499F-95A3-B7BD9D19D6DA}"
Name="CardReaderServiceInstaller"
Language="1033"
Version="0.1.5.0"
Version="0.2.1.0"
Manufacturer="ChrisWatts"
UpgradeCode="3b61e4ae-d717-4c95-9ce5-a53a1c180bf6">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />

View File

@ -1,4 +1,5 @@
using System.Configuration;
using System.Linq;
namespace ConfigurationHandler
{
@ -7,7 +8,9 @@ namespace ConfigurationHandler
public static string GetConfiguration(string keyName)
{
var appSettings = ConfigurationManager.OpenExeConfiguration(System.Reflection.Assembly.GetEntryAssembly().Location).AppSettings;
if(appSettings.Settings.AllKeys.Contains(keyName))
return appSettings.Settings[keyName].Value;
return string.Empty;
}
}
}

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.1.5.0")]
[assembly: AssemblyFileVersion("0.1.5.0")]
[assembly: AssemblyVersion("0.2.1.0")]
[assembly: AssemblyFileVersion("0.2.1.0")]

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="1.0.0.0"
Version="0.2.1.0"
Manufacturer="Chris Watts"
UpgradeCode="d38e92db-48f9-40c3-9a6f-d76fbd07326e">
<BootstrapperApplicationRef Id="WixStandardBootstrapperApplication.RtfLicense">

View File

@ -66,6 +66,10 @@ namespace Interfaces
/// <see cref="IdentifierList"/> with nested <see cref="Identifier"/> list
/// </returns>
IdentifierList GetUnassignedIdentifierList();
/// <summary>
/// Remove all unassigned identifiers from the system.
/// </summary>
void ClearUnassignedIdentifiers();
/// <summary>
/// Update a user in the system with the new values.

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.1.5.0")]
[assembly: AssemblyFileVersion("0.1.5.0")]
[assembly: AssemblyVersion("0.2.1.0")]
[assembly: AssemblyFileVersion("0.2.1.0")]

View File

@ -4,8 +4,12 @@
autoReload="true">
<targets>
<target name="viewer" xsi:type="Chainsaw" address="udp://127.0.0.1:4000" />
<target xsi:type="Console" name="consoleViewer"
layout="${longdate}|${level:uppercase=true}|${message}"
detectConsoleAvailable="true" />
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="viewer" />
<logger name="*" minlevel="Trace" writeTo="consoleViewer" />
</rules>
</nlog>

View File

@ -9,14 +9,14 @@ namespace NLogLogger
{
public class NLogger:ILogger
{
private NLog.Logger _logger;
private readonly Logger _logger;
public NLogger()
{
var nlogConfigPathOption =
ConfigurationHandler.ConfigurationHandler.GetConfiguration("NLogConfigFilePath");
if (nlogConfigPathOption == null)
{
throw new ArgumentNullException("nlogConfigPath");
throw new ArgumentNullException("nlogConfigPath", "NLogConfigFilePath missing from application config file.");
}
var nlogConfigPath = new Uri(Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().CodeBase), nlogConfigPathOption)).LocalPath;
LogManager.Configuration = new XmlLoggingConfiguration(nlogConfigPath);

View File

@ -4,7 +4,7 @@
<Product Id="{34DA87B7-752D-4A37-928B-650160269E70}"
Name="SQLiteRepositoryInstaller"
Language="1033"
Version="1.0.0.0"
Version="0.2.1.0"
Manufacturer="Chris Watts" UpgradeCode="ec13c45c-6b63-49a6-b058-39e3069a0d6b">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SQLiteRepository
{
class DbVersion
{
public string VersionNumber { get; set; }
}
}

View File

@ -1,6 +1,6 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SQLiteRepository.Properties;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
@ -32,5 +32,14 @@ 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.1.5.0")]
[assembly: AssemblyFileVersion("0.1.5.0")]
[assembly: AssemblyVersion(AssemblyInfo.ASSEMBLY_VERSION)]
[assembly: AssemblyFileVersion(AssemblyInfo.ASSEMBLY_FILE_VERSION)]
namespace SQLiteRepository.Properties
{
internal static class AssemblyInfo
{
internal const string ASSEMBLY_VERSION = "0.2.1.0";
internal const string ASSEMBLY_FILE_VERSION = "0.2.1.0";
}
}

View File

@ -11,12 +11,37 @@ namespace SQLiteRepository
nameof(TimeLogDb.CalendarWeek) + "=? and " + nameof(TimeLogDb.Year) + "=?)";
public const string GET_ALL_USERS =
"select * from " + nameof(UserIdentity) + " order by " + nameof(UserIdentity.LastName) + " collate nocase, " +
nameof(UserIdentity.FirstName) + " collate nocase";
"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 "
+ nameof(UserIdentity.LastName) + " collate nocase, "
+ nameof(UserIdentity.FirstName) + " collate nocase";
public const string GET_ALL_USERS_PAGINATE =
"select * from " + nameof(UserIdentity) + " order by " + nameof(UserIdentity.LastName) + " collate nocase, " +
nameof(UserIdentity.FirstName) + " collate nocase limit ? offset ?";
"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 "
+ 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." +
@ -38,6 +63,9 @@ namespace SQLiteRepository
public const string GET_UNASSIGNED_CARD_LIST =
"select * from " + nameof(CardUniqueId) + " where " + nameof(CardUniqueId.UserId_FK) + "=?";
public const string CLEAR_UNASSIGNED_CARDS =
"delete from " + nameof(CardUniqueId) + " where " + nameof(CardUniqueId.UserId_FK) + "=?";
public const string UPDATE_CARD_USER_ID =
"update " + nameof(CardUniqueId) + " set " + nameof(CardUniqueId.UserId_FK) + "=? where " +
nameof(CardUniqueId.Id) + "=?";

View File

@ -8,6 +8,7 @@ using System.Reflection;
using Interfaces;
using SQLite.Net;
using SQLite.Net.Platform.Win32;
using SQLiteRepository.Properties;
namespace SQLiteRepository
{
@ -32,7 +33,68 @@ namespace SQLiteRepository
_connection.CreateTable<TimeLogDb>();
_connection.CreateTable<GroupDb>();
_connection.CreateTable<UserGroupJoinDb>();
_connection.CreateTable<DbVersion>();
_logger.Trace("Initialised SQLite Repository");
_logger.Trace("Checking For Upgrades");
CheckForDbUpgrade();
}
private void CheckForDbUpgrade()
{
var data = _connection.Query<DbVersion>("select * from DbVersion");
if (!data.Any())
{
//Pre-Upgrade database, need upgrading
_logger.Trace("Pre version 0.2 RC database found, performing update..");
ExecuteUpgradeFromVersion("0.1"); //execute 0.2 upgrade scripts onwards.
}
else
{
var installedVersion = new Version(data.First().VersionNumber);
var currentVersion = new Version(AssemblyInfo.ASSEMBLY_VERSION);
if (currentVersion.CompareTo(installedVersion) > 0) //greater than 0 - current version is newer
{
_logger.Trace("Installed Database Version: {0} is older than current version {1}");
ExecuteUpgradeFromVersion(installedVersion.ToString());
}
}
}
private void ExecuteUpgradeFromVersion(string installedVersion)
{
const string PREFIX = "SQLiteRepository.UpgradeScripts.";
var instVers = new Version(installedVersion);
//so we have established that each script for upgrade will be <version>.sql, so now start at lowest version and work up!
var assembly = Assembly.GetExecutingAssembly();
var upgradeScripts = assembly.GetManifestResourceNames()
.Where(x=>x.StartsWith(PREFIX))
.OrderBy(x=>new Version(Path.GetFileNameWithoutExtension(x.Replace(PREFIX, ""))))
.Where(x =>
{
var scriptVersion = new Version(Path.GetFileNameWithoutExtension(x.Replace(PREFIX, ""))).CompareTo(instVers);
return scriptVersion > 0;
}
)
.ToList();
//now have an ordered list of upgrade script files, so execute in order!
foreach (var upgradeScript in upgradeScripts)
{
using (var stream = assembly.GetManifestResourceStream(upgradeScript))
using(var str = new StreamReader(stream))
{
var script = str.ReadToEnd();
_logger.Trace("Executing upgrade script with name: {0}", upgradeScript);
_connection.Execute(script);
}
}
SetDbVersion(AssemblyInfo.ASSEMBLY_VERSION);
}
private void SetDbVersion(string vers)
{
_connection.DeleteAll<DbVersion>();
_connection.Insert(new DbVersion {VersionNumber = vers});
_logger.Trace("Set Database version to: {0}", vers);
}
public UserList GetUsers(int pageNumber = -1, int pageSize = -1, int groupId = -1)
@ -247,6 +309,13 @@ namespace SQLiteRepository
return ret;
}
public void ClearUnassignedIdentifiers()
{
_connection.Execute(
SQLiteProcedures.CLEAR_UNASSIGNED_CARDS,
Constants.UNASSIGNED_CARD_USER_ID);
}
//TODO: Check time logs table on update to ensure associated cards/unique identifiers are removed/added as appropriate.
public OperationResponse UpdateUser(User user, out int userIdResult)
{
@ -487,7 +556,7 @@ namespace SQLiteRepository
{
//TODO: I would probably prefer to do this manually....
var resp = _connection.Query<GroupDb>("update GroupDb set GroupName=? where GroupId=?", group.Name, group.Id);
//var resp = _connection.Update(groupDb);
return OperationResponse.UPDATED;
}
@ -505,6 +574,8 @@ namespace SQLiteRepository
if (!query.Any())
return OperationResponse.FAILED;
UpdateExistingLogDirections(log);
_connection.ExecuteScalar<TimeLogDb>("delete from TimeLogDb where Id=?", log.Id);
return OperationResponse.DELETED;
@ -514,6 +585,8 @@ namespace SQLiteRepository
{
var calendarWeek = GetIso8601CalendarWeek(log.EventTime.UtcDateTime);
var year = log.EventTime.Year;
log.CalendarWeek = calendarWeek;
log.Year = year;
var dbLog = new TimeLogDb
{
SwipeEventDateTime = log.EventTime,
@ -524,6 +597,10 @@ namespace SQLiteRepository
UserId_FK = log.UserId,
IdentifierId = ConvertSourceToIdentifierId(log.Source),
};
#region update in/out directions for manual logs.
UpdateExistingLogDirections(log);
#endregion
//and now insert the new log.
_connection.Insert(dbLog);
return OperationResponse.CREATED;
}
@ -720,10 +797,7 @@ namespace SQLiteRepository
else
{
// we have a time log from today already, so just do the opposite of what we last did!
if (lastEntry.Direction == LogDirectionDb.IN)
logDirection = LogDirectionDb.OUT;
else if (lastEntry.Direction == LogDirectionDb.OUT)
logDirection = LogDirectionDb.IN;
logDirection = InvertLogDirectionDb(lastEntry.Direction);
}
}
else
@ -809,5 +883,47 @@ namespace SQLiteRepository
IsContractor = user.IsContractor
};
}
private void UpdateExistingLogDirections(TimeLog log)
{
//need to make this generic so that both create and delete will update the log directions.. but ARGH.
//if you want to use this as a delete, you would delete and call this, if you want to create, call this then create.
//should look at how to improve this method so that it will compensate for if the log hasnt been deleted yet, but ott?
var weekLogs = GetTimeLogList(log.UserId, log.CalendarWeek, log.Year);
//Get the same logs of the day that the log has been entered for
var todaysLogs = weekLogs.FirstOrDefault(x => x.Day == log.EventTime.DayOfWeek);
if (todaysLogs != null)
{
//Get the days logs that are after the manually created date
var logs = todaysLogs.Logs.Where(x => x.EventTime.CompareTo(log.EventTime) >= 0).OrderBy(x => x.EventTime).ToList();
//Update each log with the inverse progressively
var currentlogDirection = log.Direction;
for (var i = 0; i < logs.Count; i++)
{
logs[i].Direction = InvertLogDirection(currentlogDirection);
UpdateLog(logs[i]);
currentlogDirection = logs[i].Direction;
}
}
}
private LogDirectionDb InvertLogDirectionDb(LogDirectionDb direction)
{
return (LogDirectionDb) (int) InvertLogDirection((LogDirection) (int) direction);
}
private LogDirection InvertLogDirection(LogDirection direction)
{
switch (direction)
{
case LogDirection.IN:
return LogDirection.OUT;
case LogDirection.OUT:
return LogDirection.IN;
default:
return LogDirection.UNKNOWN;
}
}
}
}

View File

@ -78,6 +78,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="CardUniqueId.cs" />
<Compile Include="DBVersion.cs" />
<Compile Include="GroupDb.cs" />
<Compile Include="LogSourceDb.cs" />
<Compile Include="SQLiteRepository.cs" />
@ -102,6 +103,9 @@
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="UpgradeScripts\0.2.sql" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\packages\System.Data.SQLite.Core.1.0.104.0\build\net451\System.Data.SQLite.Core.targets" Condition="Exists('..\packages\System.Data.SQLite.Core.1.0.104.0\build\net451\System.Data.SQLite.Core.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">

View File

@ -0,0 +1 @@
insert into GroupDb values ((select max(groupId) from GroupDb)+1,'Archived',0)

View File

@ -3,7 +3,7 @@
<Product Id="{52F7296E-1C7E-497E-9B25-4FE715D7AD1C}"
Name="DataCentreHostInstaller"
Language="1033"
Version="0.1.5.0"
Version="0.2.1.0"
Manufacturer="ChrisWatts"
UpgradeCode="7282166b-691e-4caa-9a80-f348b8e5ffef">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />

View File

@ -93,6 +93,12 @@
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" >
@ -177,6 +183,12 @@
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>

View File

@ -5,7 +5,7 @@
<add key="DefaultPageSize" value="20" />
<add key="WebsiteHttpPort" value="8800"/>
<add key="SwipeTimeGap" value="3" />
<add key="BugSubmissionEmailAddress" value="incoming+WattsC/FlexiTimeTrackerTool+24qrefn8e1urhl4iqct7we2jl@gitlab.com"/>
<add key="BugSubmissionEmailAddress" value="incoming+WattsC/FlexiTimeTrackerTool@incoming.gitlab.com"/>
</appSettings>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />

View File

@ -27,5 +27,15 @@ namespace WindowsDataCenter
_logger.Trace("Call to GetUnassignedCards, returning {0} items", unassignedCards.data.Count);
return Ok(unassignedCards);
}
[HttpDelete]
[Route("unassigned")]
[CacheControl(MaxAge = 0)]
public IHttpActionResult ClearUnassignedCards()
{
_repo.ClearUnassignedIdentifiers();
_logger.Trace("Call to ClearUnassignedCards, removed all identifiers.");
return Ok();
}
}
}

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.1.5.0")]
[assembly: AssemblyFileVersion("0.1.5.0")]
[assembly: AssemblyVersion("0.2.1.0")]
[assembly: AssemblyFileVersion("0.2.1.0")]

View File

@ -77,7 +77,33 @@
</form>
</div>
</div>
<div id="CardManagement" class="container">
<div class="row">
<h2 class="col-md-4">Unassigned Cards</h2>
<button class="col-md-1 btn btn-default pull-right" style="margin-top: 20px;" data-bind="click: $root.clearUnassignedCards">
<span class="glyphicon glyphicon-trash"></span>
</button>
</div>
<div class="row">
<div data-bind="with: unassignedCardList">
<table class="table table-striped">
<thead>
<tr>
<th>Card Unique Id</th>
<th>Last Used Date</th>
</tr>
</thead>
<tbody data-bind="foreach: data">
<tr>
<td data-bind="text: UniqueId"></td>
<td data-bind="text: $root.convertToDisplayDateTime(LastUsed)"></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<script src="Helpers.min.js" type="text/javascript"></script>
<script src="admin.min.js" type="text/javascript"></script>
<script src="admin.js" type="text/javascript"></script>
</body>
</html>

View File

@ -2,6 +2,7 @@
var self = this;
self.groupsList = ko.observable(null);
self.groupEditItem = ko.observable(null);
self.unassignedCardList = ko.observable(null);
self.helpers = new Helpers();
self.uiPages = {
overview: "overview",
@ -11,7 +12,9 @@
self.apiEndpoints = {
deleteGroups:"/api/groups/delete",
getGroups: "/api/groups",
editGroup: "/api/groups/edit"
editGroup: "/api/groups/edit",
getUnassignedCards: "/api/cards/unassigned",
clearUnassignedCards: "/api/cards/unassigned"
};
self.clearGroupForm = function () {
self.helpers.goToMenuOption(self.uiPages.group);
@ -69,10 +72,48 @@
var errorObj = self.helpers.processRequestFailure(resp, status, error);
});
};
self.getUnassignedCardData = function() {
var url = self.helpers.createRequestUrl(self.apiEndpoints.getUnassignedCards, null, false);
$.getJSON(url,
function(res) {
self.unassignedCardList(res);
}).fail(function(resp, status, error) {
console.log("error - getUnassignedCards");
var errorObj = self.helpers.processRequestFailure(resp, status, error);
});
};
self.clearUnassignedCards = function() {
var url = self.helpers.createRequestUrl(self.apiEndpoints.clearUnassignedCards, null, false);
$.ajax({
type: "DELETE",
url: url,
data: "",
success: function() {
self.getUnassignedCardData(); //update
},
error: function(jqxhr, status, error) {
console.log("error - clearUnassignedCards");
var errorObj = self.helpers.processRequestFailure(resp, status, error);
}
});
};
self.padNumber = function (number) {
return (number < 10 ? "0" : "") + number;
};
self.convertToDisplayDateTime = function (dateValue) {
var date = new Date(dateValue); // dd MM YY HH:mm:ss e.g.: 01 Mar 17 17:34:02
return date.getDate() + " "
+ date.toLocaleString("en-us", { month: "long" }) + " "
+ (date.getYear()-100) + " "
+ self.padNumber(date.getHours()) + ":"
+ self.padNumber(date.getMinutes()) + ":"
+ self.padNumber(date.getSeconds());
};
Sammy(function () {
this.disable_push_state = true;
this.get("#overview", function () {
self.getGroups();
self.getUnassignedCardData();
});
this.post("#editgroup", function () {
self.submitGroupEdit(self.groupEditItem());

View File

@ -1 +1 @@
function AdminVM(){var n=this;n.groupsList=ko.observable(null);n.groupEditItem=ko.observable(null);n.helpers=new Helpers;n.uiPages={overview:"overview",group:"groups",home:function(){return this.overview}};n.apiEndpoints={deleteGroups:"/api/groups/delete",getGroups:"/api/groups",editGroup:"/api/groups/edit"};n.clearGroupForm=function(){n.helpers.goToMenuOption(n.uiPages.group);n.groupEditItem(null)};n.hideGroupForm=function(){n.groupEditItem(null)};n.newGroupForm=function(){n.groupEditItem({Id:-1,Name:""});n.helpers.goToMenuOption(n.uiPages.group)};n.groupFormHidden=ko.computed(function(){return n.groupEditItem()==null},n);n.editGroupClick=function(t){n.helpers.goToMenuOption(n.uiPages.group);n.groupEditItem(t)};n.getGroups=function(){var t=n.helpers.createRequestUrl(n.apiEndpoints.getGroups,null,!1);$.getJSON(t,function(t){n.groupsList(t)}).fail(function(t,i,r){console.log("error - getGroups");var u=n.helpers.processRequestFailure(t,i,r)})};n.deleteGroup=function(t){var i=n.helpers.createRequestUrl(n.apiEndpoints.deleteGroups,[{key:"groupId",value:t}],!1,!1);$.ajax({url:i,type:"DELETE",success:function(){console.log("deleted "+t);n.hideGroupForm();n.helpers.goToMenuOption(n.uiPages.home())}});console.log("delete: "+t)};n.submitGroupEdit=function(t){var i=n.helpers.createRequestUrl(n.apiEndpoints.editGroup,null,!1);$.post(i,t,function(){},"json").done(function(){n.groupEditItem(null);n.helpers.goToMenuOption(n.uiPages.home())}).fail(function(t,i,r){n.helpers.goToMenuOption(n.uiPages.home());var u=n.helpers.processRequestFailure(t,i,r)})};Sammy(function(){this.disable_push_state=!0;this.get("#overview",function(){n.getGroups()});this.post("#editgroup",function(){return n.submitGroupEdit(n.groupEditItem()),!1});this.get("",function(){this.app.runRoute("get","#"+n.uiPages.home())})}).run()}ko.applyBindings(new AdminVM)
function AdminVM(){var n=this;n.groupsList=ko.observable(null);n.groupEditItem=ko.observable(null);n.unassignedCardList=ko.observable(null);n.helpers=new Helpers;n.uiPages={overview:"overview",group:"groups",home:function(){return this.overview}};n.apiEndpoints={deleteGroups:"/api/groups/delete",getGroups:"/api/groups",editGroup:"/api/groups/edit",getUnassignedCards:"/api/cards/unassigned",clearUnassignedCards:"/api/cards/unassigned"};n.clearGroupForm=function(){n.helpers.goToMenuOption(n.uiPages.group);n.groupEditItem(null)};n.hideGroupForm=function(){n.groupEditItem(null)};n.newGroupForm=function(){n.groupEditItem({Id:-1,Name:""});n.helpers.goToMenuOption(n.uiPages.group)};n.groupFormHidden=ko.computed(function(){return n.groupEditItem()==null},n);n.editGroupClick=function(t){n.helpers.goToMenuOption(n.uiPages.group);n.groupEditItem(t)};n.getGroups=function(){var t=n.helpers.createRequestUrl(n.apiEndpoints.getGroups,null,!1);$.getJSON(t,function(t){n.groupsList(t)}).fail(function(t,i,r){console.log("error - getGroups");var u=n.helpers.processRequestFailure(t,i,r)})};n.deleteGroup=function(t){var i=n.helpers.createRequestUrl(n.apiEndpoints.deleteGroups,[{key:"groupId",value:t}],!1,!1);$.ajax({url:i,type:"DELETE",success:function(){console.log("deleted "+t);n.hideGroupForm();n.helpers.goToMenuOption(n.uiPages.home())}});console.log("delete: "+t)};n.submitGroupEdit=function(t){var i=n.helpers.createRequestUrl(n.apiEndpoints.editGroup,null,!1);$.post(i,t,function(){},"json").done(function(){n.groupEditItem(null);n.helpers.goToMenuOption(n.uiPages.home())}).fail(function(t,i,r){n.helpers.goToMenuOption(n.uiPages.home());var u=n.helpers.processRequestFailure(t,i,r)})};n.getUnassignedCardData=function(){var t=n.helpers.createRequestUrl(n.apiEndpoints.getUnassignedCards,null,!1);$.getJSON(t,function(t){n.unassignedCardList(t)}).fail(function(t,i,r){console.log("error - getUnassignedCards");var u=n.helpers.processRequestFailure(t,i,r)})};n.clearUnassignedCards=function(){var t=n.helpers.createRequestUrl(n.apiEndpoints.clearUnassignedCards,null,!1);$.ajax({type:"DELETE",url:t,data:"",success:function(){n.getUnassignedCardData()},error:function(t,i,r){console.log("error - clearUnassignedCards");var u=n.helpers.processRequestFailure(resp,i,r)}})};n.padNumber=function(n){return(n<10?"0":"")+n};n.convertToDisplayDateTime=function(t){var i=new Date(t);return i.getDate()+" "+i.toLocaleString("en-us",{month:"long"})+" "+(i.getYear()-100)+" "+n.padNumber(i.getHours())+":"+n.padNumber(i.getMinutes())+":"+n.padNumber(i.getSeconds())};Sammy(function(){this.disable_push_state=!0;this.get("#overview",function(){n.getGroups();n.getUnassignedCardData()});this.post("#editgroup",function(){return n.submitGroupEdit(n.groupEditItem()),!1});this.get("",function(){this.app.runRoute("get","#"+n.uiPages.home())})}).run()}ko.applyBindings(new AdminVM)

View File

@ -10,14 +10,14 @@
<link href="css/knockout.contextmenu.css" rel="stylesheet" type="text/css"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.47/css/bootstrap-datetimepicker.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.0.0/knockout-min.js" type="text/javascript"></script>
<script src="js/knockout.contextmenu.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/sammy.js/0.7.6/sammy.js" type="text/javascript"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://cdn.jsdelivr.net/momentjs/2.10.6/moment.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" type="text/javascript"></script>
<script src="https://cdn.jsdelivr.net/momentjs/2.10.6/moment.min.js" type="text/javascript"></script>
<!--<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.6.1/js/bootstrap-datepicker.js"></script>-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.47/js/bootstrap-datetimepicker.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.47/js/bootstrap-datetimepicker.min.js" type="text/javascript"></script>
</head>
<body data-bind="css: {footerBody: errorData() !== null}">
<nav class="navbar navbar-default">
@ -254,6 +254,9 @@
<div class="col-md-3 col-xs-12">
<div id="datePickerContainer">
<div id="weeklyDatePicker"></div>
<div class="text-center">
<button class="btn btn-default form-control" type="button" id="manualTodayButton" data-bind="click: $root.goToToday">Today</button>
</div>
</div>
</div>
<div class="col-md-9 col-xs-12 well">
@ -271,6 +274,7 @@
</thead>
<tbody>
<!-- ko foreach: TimeLogs-->
<!-- ko if: !((DayOfWeek === "Saturday" || DayOfWeek=== "Sunday") && LogCount===0)-->
<tr>
<td class="valign" data-bind="text: DayOfWeek, contextMenu: $root.createContextMenu"></td>
<!-- ko foreach: Logs -->
@ -282,6 +286,7 @@
<td class="valign" data-bind="text: $root.convertToHours(DailyTotal), contextMenu: $root.createContextMenu"></td>
</tr>
<!-- /ko -->
<!-- /ko -->
<tr>
<td class="valign" data-bind="attr:{colspan: $root.correctLogOffset(MaxDailyLogCount)+1}, contextMenu: $root.createContextMenu">Weekly Total</td>
<td class="valign" for="dailyHrsTotal" data-bind="text: $root.convertToHours(WeeklyTotal), contextMenu: $root.createContextMenu"></td>
@ -332,20 +337,16 @@
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h4>MANUAL EDIT</h4>
</div>
<div class="modal-body" style="height: 300px;">
<div class="modal-body" style="height: 400px;">
<form action="#manualLog" method="post" class="form-group">
<input type="hidden" name="Id" data-bind="value: Id"/>
<div class="form-group">
<div class="input-group date" id="datetimepicker1">
<input type="text" class="form-control" />
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
<div id="datetimepicker1"></div>
</div>
<br/>
<div>
<div class="input-group">
<select data-bind="options: $root.possibleLogDirections,
<div class="input-group col-md-offset-1">
<select class="form-control" data-bind="options: $root.possibleLogDirections,
optionsText: function(item) { return item.Text },
optionsValue: function(item){ return item.value },
value: Direction,
@ -353,8 +354,10 @@
</div>
</div>
<br/>
<div class="pull-right">
<button type="submit" class="btn btn-primary">Submit</button>
<button type="button" class="btn btn-secondary close" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
</div>
</form>
</div>
</div>

View File

@ -30,3 +30,7 @@
/* Margin bottom by footer height */
margin-bottom: 132px;
}
/*Make the cursor a pointer for all hyperlinks*/
a:hover {
cursor: pointer;
}

View File

@ -52,6 +52,11 @@
}
location.hash = url;
};
self.goToToday = function() {
self.goToTimeLogs(self.chosenTimeLogUserId,
null,
null);
};
self.assignErrorObject = function(errCode, errMessage, errorSource) {
var errDat = {
errorCode: errCode,
@ -176,9 +181,9 @@
}
moment.locale("en", { week: { dow: 1 } });
$("#weeklyDatePicker").datetimepicker({
//showTodayButton: true,
format: "DD/MM/YYYY",
inline: true,
showTodayButton: true,
calendarWeeks: true,
maxDate: "now",
date: selectedDate
@ -338,7 +343,7 @@
.done(function () {
self.manualLog(null);
$('#manualLogDialog').modal("hide");
self.goToMenuOption(self.uiPages.home());
location.reload(); //stay on this users logs page, but just reload the timelogs.
})
.fail(function (resp, status, error) {
var errObj = self.helpers.processRequestFailure(resp, status, error);
@ -355,7 +360,7 @@
data: logToDelete,
success: function (result) {
console.log("successfully deleted .." + result);
self.goToMenuOption(self.uiPages.home());
location.reload(); //stay on this page but just reload the timelogs.
}
});
};
@ -369,16 +374,11 @@
]);
function editlog (data) {
self.manualLog(data);
$('#manualLogDialog').modal("show");
$('#datetimepicker1').datetimepicker({
format: "YYYY-DD-MM HH:mm:ss",
date: new Date(data.EventTime),
minDate: moment(new Date(data.EventTime)).startOf('week'),
maxDate: moment(new Date(data.EventTime)).endOf('week')
});
$("#manualLogDialog").modal("show");
self.initialiseManualLogDateTimePicker(data.EventTime);
self.assignUpdateHandler();
};
function createlog(data) {
function createlog(data, event) {
self.manualLog({
CalendarWeek:-1,
Direction:-1,
@ -389,11 +389,7 @@
Year: 0
});
$('#manualLogDialog').modal("show");
$('#datetimepicker1').datetimepicker({
format: "YYYY-DD-MM HH:mm:ss",
minDate: moment(self.selectedTimeLogDate()).startOf("week"),
maxDate: moment(self.selectedTimeLogDate()).endOf("week")
});
self.initialiseManualLogDateTimePicker(self.selectedTimeLogDate());
self.assignUpdateHandler();
};
function deleteLog(data) {
@ -401,6 +397,17 @@
self.deleteManualLog(data);
}
};
self.initialiseManualLogDateTimePicker = function (date) {
$('#datetimepicker1')
.datetimepicker({
format: "YYYY-DD-MM HH:mm:ss",
date: new Date(date),
inline:true,
sideBySide:true,
minDate: moment(date).startOf("week"),
maxDate: moment(date).endOf("week")
});
};
Sammy(function () {
this.get("#users", function () {
var query = this.params.query;
@ -463,7 +470,7 @@
"IsContractor": false
});
self.getGroups(function(data) {
self.chosenUserDetails().Groups = data;
self.chosenUserDetails().Groups = data.Groups;
self.chosenUserDetails.valueHasMutated();
});
self.getUnassignedCardData();

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.1.4.0")]
[assembly: AssemblyFileVersion("0.1.4.0")]
[assembly: AssemblyVersion("0.2.1.0")]
[assembly: AssemblyFileVersion("0.2.1.0")]