Merge commit 'a9d8e4c015d964b15ddf766327dfa3b0aae55a02'
This commit is contained in:
commit
3699aff489
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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")]
|
||||
|
||||
@ -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")]
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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" />
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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")]
|
||||
|
||||
@ -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">
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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")]
|
||||
|
||||
@ -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>
|
||||
@ -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);
|
||||
|
||||
@ -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" />
|
||||
|
||||
|
||||
@ -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; }
|
||||
}
|
||||
}
|
||||
@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@ -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) + "=?";
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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">
|
||||
|
||||
@ -0,0 +1 @@
|
||||
insert into GroupDb values ((select max(groupId) from GroupDb)+1,'Archived',0)
|
||||
@ -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" />
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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" />
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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")]
|
||||
|
||||
@ -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>
|
||||
@ -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());
|
||||
|
||||
@ -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)
|
||||
@ -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">×</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>
|
||||
|
||||
@ -30,3 +30,7 @@
|
||||
/* Margin bottom by footer height */
|
||||
margin-bottom: 132px;
|
||||
}
|
||||
/*Make the cursor a pointer for all hyperlinks*/
|
||||
a:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
@ -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();
|
||||
|
||||
@ -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")]
|
||||
|
||||
Loading…
Reference in New Issue
Block a user