From b20a506d3a64d4d4f64328023c8ab8e6a18e08e6 Mon Sep 17 00:00:00 2001 From: "Chris.Watts90@outlook.com" Date: Tue, 21 Feb 2017 11:48:24 +0000 Subject: [PATCH 01/49] removed hardcoded string for WebApp endpoint (previously "http://*:8800") The app now pulls in the port from the app.config file. Also added some commented out code to load a config file in a different directory (i.e.: to allow config files in "configs" directory to be pulled in). If the port isnt specified in the app.config, will default to 8800. #27 --- .../CardReaderService/Configuration.cs | 1 - .../WindowsDataCenter/App.config | 1 + .../WindowsDataCenter/DataCenterService.cs | 15 +++++++++++++-- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/DataCenter_Windows/WindowsDataCenter/CardReaderService/Configuration.cs b/DataCenter_Windows/WindowsDataCenter/CardReaderService/Configuration.cs index a045fde..508a4d1 100644 --- a/DataCenter_Windows/WindowsDataCenter/CardReaderService/Configuration.cs +++ b/DataCenter_Windows/WindowsDataCenter/CardReaderService/Configuration.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics; using System.IO; using System.Reflection; using CardReaderService.DefaultComponents; diff --git a/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/App.config b/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/App.config index e54d778..7d38816 100644 --- a/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/App.config +++ b/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/App.config @@ -3,6 +3,7 @@ + diff --git a/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/DataCenterService.cs b/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/DataCenterService.cs index 8acb365..b3c551b 100644 --- a/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/DataCenterService.cs +++ b/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/DataCenterService.cs @@ -1,4 +1,7 @@ using System; +using System.Configuration; +using System.IO; +using System.Reflection; using System.ServiceProcess; using System.Threading; using Interfaces; @@ -31,6 +34,12 @@ namespace WindowsDataCenter protected override void OnStart(string[] args) { var configPath = string.Concat(System.Reflection.Assembly.GetEntryAssembly().Location, ".config"); + //var configsDir = new Uri(Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "configs")).LocalPath; + //var exeName = Assembly.GetEntryAssembly().ManifestModule.ScopeName; + //var appConfigPath = Path.Combine(configsDir, string.Format("{0}.config", exeName)); + //AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", appConfigPath); + //var val = ConfigurationManager.AppSettings["TEST"]; + //Initialise the Ninject system. var ninjectInstance = NinjectHelper.GetInstance(); _logger = NinjectHelper.GetInstance().Get(); @@ -42,10 +51,12 @@ namespace WindowsDataCenter IsBackground = false, Name = "OWIN SELF HOST MAIN THREAD" }; - //TODO: use app.config for endpoint config. + + var endpointPort = ConfigurationManager.AppSettings["WebsiteHttpPort"] ?? "8800"; try { - _webApp = WebApp.Start("http://*:8800"); + var endpoint = string.Format("http://*:{0}", endpointPort); + _webApp = WebApp.Start(endpoint); } catch (Exception ex) { From 3acb51ff911b92b44820fde470ea25c23a3d272f Mon Sep 17 00:00:00 2001 From: "Chris.Watts90@outlook.com" Date: Tue, 21 Feb 2017 12:08:36 +0000 Subject: [PATCH 02/49] changed the main DataCenterService thread name to make it useful/relevant. --- .../WindowsDataCenter/WindowsDataCenter/DataCenterService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/DataCenterService.cs b/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/DataCenterService.cs index b3c551b..516e566 100644 --- a/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/DataCenterService.cs +++ b/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/DataCenterService.cs @@ -49,7 +49,7 @@ namespace WindowsDataCenter _mainWorkerThread = new Thread(MainWorkerThread) { IsBackground = false, - Name = "OWIN SELF HOST MAIN THREAD" + Name = "DATACENTER SELF HOST MAIN THREAD" }; var endpointPort = ConfigurationManager.AppSettings["WebsiteHttpPort"] ?? "8800"; From bb05dd982873005ce9dbadd39277d1e1bcdd77cc Mon Sep 17 00:00:00 2001 From: "chris.watts90@outlook.com" Date: Tue, 21 Feb 2017 22:24:17 +0000 Subject: [PATCH 03/49] create initial bundle installer using wix bootstrapper added custom ui elements to conditionally install the card reader and data center services. #26 --- .../FlexiTimeSystemInstaller/Bundle.wxs | 31 +++++++ .../FlexiTimeSystemInstaller.wixproj | 66 +++++++++++++++ .../FlexiTimeSystemInstaller/License.rtf | Bin 0 -> 652 bytes .../FlexiTimeSystemInstaller/Theme.xml | 79 ++++++++++++++++++ .../ThemeLocalisation.wxl | 63 ++++++++++++++ .../WindowsDataCenter/WindowsDataCenter.sln | 9 ++ 6 files changed, 248 insertions(+) create mode 100644 DataCenter_Windows/WindowsDataCenter/FlexiTimeSystemInstaller/Bundle.wxs create mode 100644 DataCenter_Windows/WindowsDataCenter/FlexiTimeSystemInstaller/FlexiTimeSystemInstaller.wixproj create mode 100644 DataCenter_Windows/WindowsDataCenter/FlexiTimeSystemInstaller/License.rtf create mode 100644 DataCenter_Windows/WindowsDataCenter/FlexiTimeSystemInstaller/Theme.xml create mode 100644 DataCenter_Windows/WindowsDataCenter/FlexiTimeSystemInstaller/ThemeLocalisation.wxl diff --git a/DataCenter_Windows/WindowsDataCenter/FlexiTimeSystemInstaller/Bundle.wxs b/DataCenter_Windows/WindowsDataCenter/FlexiTimeSystemInstaller/Bundle.wxs new file mode 100644 index 0000000..f98b537 --- /dev/null +++ b/DataCenter_Windows/WindowsDataCenter/FlexiTimeSystemInstaller/Bundle.wxs @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + diff --git a/DataCenter_Windows/WindowsDataCenter/FlexiTimeSystemInstaller/FlexiTimeSystemInstaller.wixproj b/DataCenter_Windows/WindowsDataCenter/FlexiTimeSystemInstaller/FlexiTimeSystemInstaller.wixproj new file mode 100644 index 0000000..2fe0780 --- /dev/null +++ b/DataCenter_Windows/WindowsDataCenter/FlexiTimeSystemInstaller/FlexiTimeSystemInstaller.wixproj @@ -0,0 +1,66 @@ + + + + Debug + x86 + 3.10 + d38e92db-48f9-40c3-9a6f-d76fbd07326e + 2.0 + FlexiTimeSystemInstaller + Bundle + $(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets + $(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.targets + + + bin\$(Configuration)\ + obj\$(Configuration)\ + Debug + + + bin\$(Configuration)\ + obj\$(Configuration)\ + + + + + + + $(WixExtDir)\WixBalExtension.dll + WixBalExtension + + + + + + + + + + + + CardReaderServiceInstaller + {119216de-fc7f-408a-9c2c-105874449e42} + True + True + Binaries;Content;Satellites + INSTALLFOLDER + + + DataCenterHostInstaller + {c5a4cdc3-849c-4166-bdc3-56bdb307126d} + True + True + Binaries;Content;Satellites + INSTALLFOLDER + + + + + \ No newline at end of file diff --git a/DataCenter_Windows/WindowsDataCenter/FlexiTimeSystemInstaller/License.rtf b/DataCenter_Windows/WindowsDataCenter/FlexiTimeSystemInstaller/License.rtf new file mode 100644 index 0000000000000000000000000000000000000000..044a0b05d5a76a1577d915f42226b54a147a46ea GIT binary patch literal 652 zcmXw1JCEBi4DK8t|ABLHFl?tyQFLv$PDQ(E;YpN3RIf->?tmcwz0|q42&6=QFZ*t) z_{Csq{C3!~E$ipiIP!irDR;Ix7L;)jFm20xI=$DPMt3(pCz0o%#cX_g;ns{V^|~5w zFIXGN48QS`BYt~4j)%MXXf{ezQF4V}Y%k>28P3yb`g)${hq*Dm{N?06nQj+29!$aN zOv81aO$+Onv!1aBS$00%{h2F8;L>^omxIDK7QjgI1U9EeR!DjUbX>SuP8(2QeQe}( z0f>8x*?~|jy3EP4W7m^F7YO*Q@IbOd6GYq+0tUWzoZycDC60;>kw2-o1ua=6C2G&?J$krlR zowL2LLq%0v2};fED2)_HQc7xjC^&pW_MR;xIBHbKYs_KDz!4B@9PNLuJx=5E9uN9s GeE0{jCG2tl literal 0 HcmV?d00001 diff --git a/DataCenter_Windows/WindowsDataCenter/FlexiTimeSystemInstaller/Theme.xml b/DataCenter_Windows/WindowsDataCenter/FlexiTimeSystemInstaller/Theme.xml new file mode 100644 index 0000000..ef2cc2e --- /dev/null +++ b/DataCenter_Windows/WindowsDataCenter/FlexiTimeSystemInstaller/Theme.xml @@ -0,0 +1,79 @@ + + #(loc.Caption) + Segoe UI + Segoe UI + Segoe UI + Segoe UI + Segoe UI + + + #(loc.Title) + + + #(loc.HelpHeader) + #(loc.HelpText) + + + + + #(loc.InstallAcceptCheckbox) + Install Card Service + Install Data Center + + + + + + #(loc.OptionsHeader) + #(loc.OptionsLocationLabel) + + + + + + + #(loc.FilesInUseHeader) + #(loc.FilesInUseLabel) + + + + + + + + + + #(loc.ProgressHeader) + #(loc.ProgressLabel) + #(loc.OverallProgressPackageText) + + + + + #(loc.ModifyHeader) + + + + + + #(loc.SuccessHeader) + #(loc.SuccessInstallHeader) + #(loc.SuccessRepairHeader) + #(loc.SuccessUninstallHeader) + + #(loc.SuccessRestartText) + + + + + #(loc.FailureHeader) + #(loc.FailureInstallHeader) + #(loc.FailureUninstallHeader) + #(loc.FailureRepairHeader) + #(loc.FailureHyperlinkLogText) + + #(loc.FailureRestartText) + + + + \ No newline at end of file diff --git a/DataCenter_Windows/WindowsDataCenter/FlexiTimeSystemInstaller/ThemeLocalisation.wxl b/DataCenter_Windows/WindowsDataCenter/FlexiTimeSystemInstaller/ThemeLocalisation.wxl new file mode 100644 index 0000000..027be42 --- /dev/null +++ b/DataCenter_Windows/WindowsDataCenter/FlexiTimeSystemInstaller/ThemeLocalisation.wxl @@ -0,0 +1,63 @@ + + + + + + [WixBundleName] Setup + [WixBundleName] + Version [WixBundleVersion] + Are you sure you want to cancel? + Previous version + Setup Help + + /install | /repair | /uninstall | /layout [directory] - installs, repairs, uninstalls or + creates a complete local copy of the bundle in directory. Install is the default. + + /passive | /quiet - displays minimal UI with no prompts or displays no UI and + no prompts. By default UI and all prompts are displayed. + + /norestart - suppress any attempts to restart. By default UI will prompt before restart. + /log log.txt - logs to a specific file. By default a log file is created in %TEMP%. + + &Close + I &agree to the license terms and conditions + &Options + &Install + &Close + Setup Options + Install location: + &Browse + &OK + &Cancel + Setup Progress + Processing: + Initializing... + &Cancel + Modify Setup + &Repair + &Uninstall + &Close + Repair Successfully Completed + Uninstall Successfully Completed + Installation Successfully Completed + Setup Successful + &Launch + You must restart your computer before you can use the software. + &Restart + &Close + Setup Failed + Setup Failed + Uninstall Failed + Repair Failed + One or more issues caused the setup to fail. Please fix the issues and then retry setup. For more information see the <a href="#">log file</a>. + You must restart your computer to complete the rollback of the software. + &Restart + &Close + Files In Use + The following applications are using files that need to be updated: + Close the &applications and attempt to restart them. + &Do not close applications. A reboot will be required. + &OK + &Cancel + No action was taken as a system reboot is required. + \ No newline at end of file diff --git a/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter.sln b/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter.sln index 8f6ddca..2fa981c 100644 --- a/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter.sln +++ b/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter.sln @@ -30,6 +30,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Installers", "Installers", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConfigurationHandler", "ConfigurationHandler\ConfigurationHandler.csproj", "{115250F6-F8C4-4F9B-A15F-251EA258D963}" EndProject +Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "FlexiTimeSystemInstaller", "FlexiTimeSystemInstaller\FlexiTimeSystemInstaller.wixproj", "{D38E92DB-48F9-40C3-9A6F-D76FBD07326E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -114,6 +116,12 @@ Global {115250F6-F8C4-4F9B-A15F-251EA258D963}.Release|Any CPU.Build.0 = Release|Any CPU {115250F6-F8C4-4F9B-A15F-251EA258D963}.Release|x86.ActiveCfg = Release|Any CPU {115250F6-F8C4-4F9B-A15F-251EA258D963}.Release|x86.Build.0 = Release|Any CPU + {D38E92DB-48F9-40C3-9A6F-D76FBD07326E}.Debug|Any CPU.ActiveCfg = Debug|x86 + {D38E92DB-48F9-40C3-9A6F-D76FBD07326E}.Debug|x86.ActiveCfg = Debug|x86 + {D38E92DB-48F9-40C3-9A6F-D76FBD07326E}.Debug|x86.Build.0 = Debug|x86 + {D38E92DB-48F9-40C3-9A6F-D76FBD07326E}.Release|Any CPU.ActiveCfg = Release|x86 + {D38E92DB-48F9-40C3-9A6F-D76FBD07326E}.Release|x86.ActiveCfg = Release|x86 + {D38E92DB-48F9-40C3-9A6F-D76FBD07326E}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -121,5 +129,6 @@ Global GlobalSection(NestedProjects) = preSolution {C5A4CDC3-849C-4166-BDC3-56BDB307126D} = {10A7E78C-0D11-40DD-AEC3-27C2C507A926} {119216DE-FC7F-408A-9C2C-105874449E42} = {10A7E78C-0D11-40DD-AEC3-27C2C507A926} + {D38E92DB-48F9-40C3-9A6F-D76FBD07326E} = {10A7E78C-0D11-40DD-AEC3-27C2C507A926} EndGlobalSection EndGlobal From 092f116218202a22f6bbaa268912faca6250e26a Mon Sep 17 00:00:00 2001 From: "chris.watts90@outlook.com" Date: Fri, 24 Mar 2017 21:40:47 +0000 Subject: [PATCH 04/49] change minifier to run minimised. --- .../WindowsDataCenter/WindowsDataCenter/Minifier/Minify.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/Minifier/Minify.bat b/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/Minifier/Minify.bat index 8675f9f..69bc5c4 100644 --- a/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/Minifier/Minify.bat +++ b/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/Minifier/Minify.bat @@ -5,7 +5,7 @@ echo "1 - %Directory%" echo "1a - %SolutionDir%" call :normalise "%Directory%" -start /wait "" "%Directory%" -xml "%SolutionDir%WindowsDataCenter\Minifier\MinifierConfig.xml" +start /wait /min "" "%Directory%" -xml "%SolutionDir%WindowsDataCenter\Minifier\MinifierConfig.xml" GOTO :EOF From c6eab48f04ea59f4d5f61be06c7cee06dd4cc8eb Mon Sep 17 00:00:00 2001 From: "chris.watts90@outlook.com" Date: Fri, 24 Mar 2017 21:45:01 +0000 Subject: [PATCH 05/49] fix tooltip display to show the date, rather than the day of the week. updated minified version. #70 --- .../WindowsDataCenter/WindowsDataCenter/www/spa.js | 2 +- .../WindowsDataCenter/WindowsDataCenter/www/spa.min.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/www/spa.js b/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/www/spa.js index ae8cf12..c62c7de 100644 --- a/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/www/spa.js +++ b/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/www/spa.js @@ -163,7 +163,7 @@ } 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.getDay() + " " + return date.getDate() + " " + date.toLocaleString("en-us", { month: "long" }) + " " + (date.getYear()-100) + " " + self.padNumber(date.getHours()) + ":" diff --git a/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/www/spa.min.js b/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/www/spa.min.js index a05021f..47caf59 100644 --- a/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/www/spa.min.js +++ b/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/www/spa.min.js @@ -1 +1 @@ -function DataVM(){"use strict";var n=this;n.menuOptions=["Home"];n.chosenMenuItemId=ko.observable();n.appDetails=ko.observable(null);n.userList=ko.observable(null);n.groupsList=ko.observable(null);n.chosenUserDetails=ko.observable(null);n.userTimeLogData=ko.observable(null);n.unassignedCardData=ko.observable(null);n.chosenTimeLogUserId=-1;n.selectedCalendarWeek=ko.observable(0);n.errorData=ko.observable(null);n.apiEndpoints={root:"http://localhost:8800",getUserList:"/api/users",getUserDetails:"/api/users",editUser:"/api/users/edit",getTimeLogs:"/api/timelogs",getUnassignedCards:"/api/cards/unassigned",getGroups:"/api/groups",getAppDetails:"/api/app"};n.uiPages={users:"users",userDetails:"userData",timeLogs:"timelogs",home:function(){return this.users}};n.goToMenuOption=function(n){location.hash=n;console.log("goToMenuOption: "+n)};n.goToUserDetails=function(t){location.hash=n.uiPages.userDetails+"/"+t.UserId};n.goToTimeLogs=function(t,i,r){var f,u;f=t.UserId?t.UserId:t;u="timelogs/"+f;r&&(u=n.createRequestUrl(u,r,!1,!1));location.hash=u};n.assignErrorObject=function(t,i,r){var u={errorCode:t,errorMessage:i,errorSource:r,errorDate:(new Date).toDateString("yyyy-mm-dd")};n.errorData(u)};n.processRequestFailure=function(n){return n.readyState===4?{errorCode:n.status,errorMessage:n.statusText,errorSource:""}:n.readyState===0?{errorCode:n.status,errorMessage:"Network Error - Is the server available?",errorSource:""}:{errorCode:n.status,errorMessage:"Unknown Error",errorSource:""}};n.createRequestUrl=function(t,i,r,u){var o="?",f="",e;if(u&&(f=n.apiEndpoints.root),f=f+t,i!==undefined&&i!==null&&i.length>0)for(e=0;e0?n.getUserList(null,null,t):n.getUserList(r,u)});this.get("#userData/:userId",function(){n.chosenMenuItemId("Data");n.userList(null);n.getUserDetails(this.params.userId);n.userTimeLogData(null);n.getUnassignedCardData()});this.get("#timelogs/:userId",function(){var t=this.params.selectedDate;n.chosenMenuItemId("Other");n.userList(null);n.chosenUserDetails(null);n.chosenTimeLogUserId=this.params.userId;n.getTimeLogData(this.params.userId,t)});this.get("#newUser",function(){n.chosenMenuItemId("newUser");n.userList(null);n.userTimeLogData(null);n.chosenUserDetails({UserId:-1,FirstName:null,LastName:null,HoursPerWeek:null,AssociatedIdentifiers:[],Groups:[],IsContractor:!1});n.getGroups(function(t){n.chosenUserDetails().Groups=t;n.chosenUserDetails.valueHasMutated()});n.getUnassignedCardData()});this.get("#stats",function(){n.goToMenuOption("users")});this.post("#edituser",function(){return $.each(n.chosenUserDetails().AssociatedIdentifiers,function(t,i){i.IsAssociatedToUser!==!0&&n.chosenUserDetails().AssociatedIdentifiers.splice(t,1)}),$.each(n.unassignedCardData().data,function(t,i){i.IsAssociatedToUser===!0&&n.chosenUserDetails().AssociatedIdentifiers.push(i)}),n.submitChangedUser(n.chosenUserDetails()),!1});this.get("",function(){this.app.runRoute("get","#"+n.uiPages.home())})}).run()}ko.applyBindings(new DataVM);$(document).on("mouseenter",".datepicker-days tbody tr",function(){$(this).addClass("highlight")});$(document).on("mouseleave",".datepicker-days tbody tr",function(){$(this).removeClass("highlight")}) \ No newline at end of file +function DataVM(){"use strict";var n=this;n.menuOptions=["Home"];n.chosenMenuItemId=ko.observable();n.appDetails=ko.observable(null);n.userList=ko.observable(null);n.groupsList=ko.observable(null);n.chosenUserDetails=ko.observable(null);n.userTimeLogData=ko.observable(null);n.unassignedCardData=ko.observable(null);n.chosenTimeLogUserId=-1;n.selectedCalendarWeek=ko.observable(0);n.errorData=ko.observable(null);n.apiEndpoints={root:"http://localhost:8800",getUserList:"/api/users",getUserDetails:"/api/users",editUser:"/api/users/edit",getTimeLogs:"/api/timelogs",getUnassignedCards:"/api/cards/unassigned",getGroups:"/api/groups",getAppDetails:"/api/app"};n.uiPages={users:"users",userDetails:"userData",timeLogs:"timelogs",home:function(){return this.users}};n.goToMenuOption=function(n){location.hash=n;console.log("goToMenuOption: "+n)};n.goToUserDetails=function(t){location.hash=n.uiPages.userDetails+"/"+t.UserId};n.goToTimeLogs=function(t,i,r){var f,u;f=t.UserId?t.UserId:t;u="timelogs/"+f;r&&(u=n.createRequestUrl(u,r,!1,!1));location.hash=u};n.assignErrorObject=function(t,i,r){var u={errorCode:t,errorMessage:i,errorSource:r,errorDate:(new Date).toDateString("yyyy-mm-dd")};n.errorData(u)};n.processRequestFailure=function(n){return n.readyState===4?{errorCode:n.status,errorMessage:n.statusText,errorSource:""}:n.readyState===0?{errorCode:n.status,errorMessage:"Network Error - Is the server available?",errorSource:""}:{errorCode:n.status,errorMessage:"Unknown Error",errorSource:""}};n.createRequestUrl=function(t,i,r,u){var o="?",f="",e;if(u&&(f=n.apiEndpoints.root),f=f+t,i!==undefined&&i!==null&&i.length>0)for(e=0;e0?n.getUserList(null,null,t):n.getUserList(r,u)});this.get("#userData/:userId",function(){n.chosenMenuItemId("Data");n.userList(null);n.getUserDetails(this.params.userId);n.userTimeLogData(null);n.getUnassignedCardData()});this.get("#timelogs/:userId",function(){var t=this.params.selectedDate;n.chosenMenuItemId("Other");n.userList(null);n.chosenUserDetails(null);n.chosenTimeLogUserId=this.params.userId;n.getTimeLogData(this.params.userId,t)});this.get("#newUser",function(){n.chosenMenuItemId("newUser");n.userList(null);n.userTimeLogData(null);n.chosenUserDetails({UserId:-1,FirstName:null,LastName:null,HoursPerWeek:null,AssociatedIdentifiers:[],Groups:[],IsContractor:!1});n.getGroups(function(t){n.chosenUserDetails().Groups=t;n.chosenUserDetails.valueHasMutated()});n.getUnassignedCardData()});this.get("#stats",function(){n.goToMenuOption("users")});this.post("#edituser",function(){return $.each(n.chosenUserDetails().AssociatedIdentifiers,function(t,i){i.IsAssociatedToUser!==!0&&n.chosenUserDetails().AssociatedIdentifiers.splice(t,1)}),$.each(n.unassignedCardData().data,function(t,i){i.IsAssociatedToUser===!0&&n.chosenUserDetails().AssociatedIdentifiers.push(i)}),n.submitChangedUser(n.chosenUserDetails()),!1});this.get("",function(){this.app.runRoute("get","#"+n.uiPages.home())})}).run()}ko.applyBindings(new DataVM);$(document).on("mouseenter",".datepicker-days tbody tr",function(){$(this).addClass("highlight")});$(document).on("mouseleave",".datepicker-days tbody tr",function(){$(this).removeClass("highlight")}) \ No newline at end of file From 8fdbb909b47ee0f5043e9a5e4549fc2a31875b29 Mon Sep 17 00:00:00 2001 From: "chris.watts90@outlook.com" Date: Sun, 9 Apr 2017 22:02:08 +0100 Subject: [PATCH 06/49] add knockout contextmenu plugin code to support data bound context menus #29 --- .../www/css/knockout.contextmenu.css | 59 +++ .../www/js/knockout.contextmenu.js | 364 ++++++++++++++++++ 2 files changed, 423 insertions(+) create mode 100644 DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/www/css/knockout.contextmenu.css create mode 100644 DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/www/js/knockout.contextmenu.js diff --git a/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/www/css/knockout.contextmenu.css b/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/www/css/knockout.contextmenu.css new file mode 100644 index 0000000..bedb129 --- /dev/null +++ b/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/www/css/knockout.contextmenu.css @@ -0,0 +1,59 @@ +/* knockout.contextmenu v1.0.0 + Nicolás Escalante - nlante@gmail.com + Issues: https://github.com/nescalante/knockout.contextmenu/issues + License: MIT */ + +.context-menu { + position: absolute; + padding: 0; + margin: 0; + z-index: 1030; + background-color: #ffffff; +} +.context-menu ul { + line-height: 1.6; + padding: 0; + margin: 0; + border: 1px solid #dddddd; + box-shadow: 4px 4px 8px rgba(0, 0, 0, 0.3); +} +.context-menu ul > li { + padding: 4px 20px; + margin: 0; + z-index: 1031; + list-style-type: none; + cursor: pointer; + white-space: nowrap; + color: #333333; +} +.context-menu ul > li:hover { + background-color: #eeeeee; +} +.context-menu ul > li.disabled, +.context-menu ul > li.disabled a { + color: #666666; + cursor: default; +} +.context-menu ul > li.checked:before { + position: absolute; + content: "\2713"; + left: 7px; +} +.context-menu ul > li.with-url { + padding: 0; +} +.context-menu ul > li.with-url a { + display: block; + padding: 4px 20px; + text-decoration: none; + color: #333333; +} +.context-menu ul > li.separator { + margin: 4px 0; + padding: 0; + border-bottom: 1px solid #cccccc; + cursor: default; +} +.context-menu ul > li.separator:hover { + background-color: #ffffff; +} \ No newline at end of file diff --git a/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/www/js/knockout.contextmenu.js b/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/www/js/knockout.contextmenu.js new file mode 100644 index 0000000..ca91eff --- /dev/null +++ b/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/www/js/knockout.contextmenu.js @@ -0,0 +1,364 @@ +/* knockout.contextmenu v1.0.0 + Nicolás Escalante - nlante@gmail.com + Issues: https://github.com/nescalante/knockout.contextmenu/issues + License: MIT */ + +(function (undefined) { + 'use strict'; + + // client + if (typeof ko !== undefined + '') { + bindContextMenu(ko); + } + + // node + if (typeof module !== undefined + '' && module.exports && typeof require !== undefined + '') { + bindContextMenu(require('knockout')); + } + + function bindContextMenu(ko) { + var currentMenu; + var elementMapping = []; + var utils = ko.utils; + var registerEvent = utils.registerEventHandler; + var isObservable = ko.isObservable; + + registerEvent(document, 'click', function (event) { + var button = event.which || event.button; + if (!event.defaultPrevented && button < 2) { + hideCurrentMenu(); + } + }); + + utils.contextMenu = { + getMenuFor: function (element, event) { + var result = getMapping(element); + + if (result) { + return result.get(event); + } + }, + + openMenuFor: function (element, event) { + var result = getMapping(element); + + if (result) { + return result.open(event); + } + }, + + }; + + ko.bindingHandlers.contextMenu = { + init: function (element, valueAccessor, allBindingsAccessor, viewModel) { + var eventsToHandle = valueAccessor() || {}; + var allBindings = allBindingsAccessor(); + var defaultClass = allBindings.contextMenuClass || 'context-menu'; + var activeElement; + + // bind on click? bind on context click? + if (allBindings.bindMenuOnClick) { + registerEvent(element, 'click', openMenu); + } + + if (allBindings.bindMenuOnContextMenu === undefined || allBindings.bindMenuOnContextMenu) { + registerEvent(element, 'contextmenu', openMenu); + } + + elementMapping.push({ + element: element, + get: function () { + return activeElement; + }, + + open: openMenu, + hide: function () { + if (activeElement) { + activeElement.hide(); + } + }, + }); + + function mouseX(evt) { + if (evt.pageX) { + return evt.pageX; + } else if (evt.clientX) { + return evt.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft); + } else { + return null; + } + } + + function mouseY(evt) { + if (evt.pageY) { + return evt.pageY; + } else if (evt.clientY) { + return evt.clientY + (document.documentElement.scrollTop || document.body.scrollTop); + } else { + return null; + } + } + + function openMenu(event) { + var menuElement; + + activeElement = getMenu(event); + menuElement = activeElement.element; + + hideCurrentMenu(); + + if (menuElement) { + // make visibility hidden, then add to DOM so that we can get the height/width of the menu + menuElement.style.visibility = 'hidden'; + (document.body || document).appendChild(menuElement); + + // set location + if (event) { + var bottomOfViewport = window.innerHeight + window.pageYOffset; + var rightOfViewport = window.innerWidth + window.pageXOffset; + + if (mouseY(event) + menuElement.offsetHeight > bottomOfViewport) { + menuElement.style.top = 1 * (bottomOfViewport - menuElement.offsetHeight - 10) + 'px'; + } else { + menuElement.style.top = mouseY(event) + 'px'; + } + + if (mouseX(event) + menuElement.offsetWidth > rightOfViewport) { + menuElement.style.left = 1 * (rightOfViewport - menuElement.offsetWidth - 10) + 'px'; + } else { + menuElement.style.left = mouseX(event) + 'px'; + } + + event.preventDefault(); + event.stopPropagation(); + } else { + menuElement.style.top = (element.offsetTop + element.offsetHeight) + 'px'; + menuElement.style.left = (element.offsetLeft + element.offsetWidth) + 'px'; + } + + // now set to visible + menuElement.style.visibility = ''; + } + + // replace current menu with the recently created + currentMenu = menuElement; + + return activeElement; + } + + function getMenu(event) { + var menu; + var hasChecks = false; + var elements = []; + var actions = []; + var items = []; + var props = Object.keys( + ko.isObservable(eventsToHandle) ? + eventsToHandle() : + eventsToHandle + ); + + props.forEach(function (eventNameOutsideClosure) { + pushItem(eventNameOutsideClosure); + }); + + if (elements.length) { + menu = document.createElement('div'); + menu.className = defaultClass; + + // you may need padding to menus that has checks + menu.innerHTML = '
    ' + + elements.join('') + + '
'; + + // map items to actions + elements.forEach(function (item, index) { + registerEvent(menu.children[0].children[index], 'click', function (event) { + var result = actions[index](viewModel, event); + + if (!result && event) { + event.preventDefault(); + } + }); + }); + } + + return { + element: menu, + items: items, + open: openMenu, + hide: function () { + if (menu && menu.parentNode) { + menu.parentNode.removeChild(menu); + } + + currentMenu = null; + }, + }; + + function pushItem(eventName) { + var item = getMenuProperties(eventName); + var classes = []; + var id = ''; + var liHtml; + + if (item.isVisible) { + hasChecks = hasChecks || item.isBoolean; + + if (item.id) { + id = item.id; + } + + // set css classes + if (item.isChecked) { + classes.push('checked'); + } + + if (item.isDisabled) { + classes.push('disabled'); + } + + if (item.isSeparator) { + classes.push('separator'); + } + + if (item.url) { + classes.push('with-url'); + } + + liHtml = '
  • ' + + item.html + + '
  • '; + + elements.push(liHtml); + actions.push(item.action); + } + + items.push(item); + } + } + + function getMenuProperties(eventName) { + var text = ''; + var html = ''; + var currentEvent = ko.isObservable(eventsToHandle) ? + eventsToHandle()[eventName] : + eventsToHandle[eventName]; + var item = currentEvent || {}; + var id = item.id; + var url = (isObservable(item.url) ? item.url() : item.url); + var isVisible = item.visible === undefined || item.visible === null || + (isObservable(item.visible) && item.visible()) || + (!isObservable(item.visible) && !!item.visible); + var isChecked = false; + var isEnabled = !item.disabled || + (isObservable(item.disabled) && !item.disabled()) || + (isObservable(item.enabled) && item.enabled()) || + (!isObservable(item.enabled) && !!item.enabled); + var isBoolean = false; + var isDisabled = !isEnabled; + var isSeparator = !!currentEvent.separator; + + if (!isSeparator) { + text = isObservable(item.text) ? item.text() : item.text; + + if (!text) { + text = eventName; + } + + if (url) { + html = '' + text + ''; + } else { + html = text; + } + } + + if ((isObservable(item) && typeof item() === 'boolean') || + (isObservable(item.action) && typeof item.action() === 'boolean')) { + isBoolean = true; + + if ((item.action && item.action()) || + (typeof item === 'function' && item())) { + isChecked = true; + } + } + + return { + html: html, + text: text, + url: url, + id: id, + isVisible: isVisible, + isChecked: isChecked, + isEnabled: isEnabled, + isDisabled: isDisabled, + isBoolean: isBoolean, + isSeparator: isSeparator, + action: action, + }; + + function action(viewModel, event) { + var error = eventName + ' option must have an action or an url.'; + + if (isDisabled) { + return false; + } + + // check if option is a boolean + if (isObservable(item) && typeof item() === 'boolean') { + item(!item()); + } + + // is an object? well, lets check it properties + else if (typeof item === 'object') { + // check if has an action or if its a separator + if (!item.action && !url && !isSeparator) { + throw error; + } + + // evaluate action + else if (item.action) { + if (isObservable(item.action) && typeof item.action() === 'boolean') { + item.action(!item.action()); + } else { + item.action(viewModel, event); + } + } + } + + // its not an observable, should be a function + else if (typeof item === 'function') { + item(viewModel, event); + } + + // nothing to do with this + else { + throw error; + } + + return true; + } + } + }, + }; + + function hideCurrentMenu() { + if (currentMenu && currentMenu.parentNode) { + currentMenu.parentNode.removeChild(currentMenu); + } + + currentMenu = null; + } + + function getMapping(element) { + var i = 0; + + for (; i < elementMapping.length; i++) { + if (elementMapping[i].element === element) { + return elementMapping[i]; + } + } + } + } +})(); \ No newline at end of file From 6061b790333d992f77fe57805b9acbff64c0aa64 Mon Sep 17 00:00:00 2001 From: "chris.watts90@outlook.com" Date: Sun, 9 Apr 2017 22:02:40 +0100 Subject: [PATCH 07/49] proj update for new files #29 --- .../WindowsDataCenter/WindowsDataCenter.csproj | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/WindowsDataCenter.csproj b/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/WindowsDataCenter.csproj index b86b216..74cf1e5 100644 --- a/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/WindowsDataCenter.csproj +++ b/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/WindowsDataCenter.csproj @@ -199,6 +199,9 @@ PreserveNewest + + Always + Always @@ -220,6 +223,9 @@ PreserveNewest + + Always + Always From 0bbbd7625332455c8e274eee111ed37f46eab471 Mon Sep 17 00:00:00 2001 From: "chris.watts90@outlook.com" Date: Sun, 9 Apr 2017 22:04:03 +0100 Subject: [PATCH 08/49] add references to knockout.contextmenu files. add data bindings to timelog table to create the correct create+edit or create only context menus. #29 --- .../WindowsDataCenter/www/index.html | 54 ++++++++++++++----- 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/www/index.html b/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/www/index.html index 079b15d..0e1c97a 100644 --- a/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/www/index.html +++ b/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/www/index.html @@ -4,12 +4,14 @@ Flexi Time Data Viewer - - + + + + @@ -246,9 +248,6 @@

    -
    @@ -259,7 +258,7 @@ - + @@ -271,24 +270,30 @@ - + - + - + - + - - + +
    Day Of Week In
    Weekly TotalWeekly Total
    +
    - +
    @@ -333,6 +359,6 @@
    - + \ No newline at end of file From 86e0b60386eba3f3652f706cc64112e74f1feb6d Mon Sep 17 00:00:00 2001 From: "chris.watts90@outlook.com" Date: Sun, 9 Apr 2017 22:05:18 +0100 Subject: [PATCH 09/49] added create and create+edit context menus with options. created stub methods for performing the edit/create actions. #29 --- .../WindowsDataCenter/www/spa.js | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/www/spa.js b/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/www/spa.js index c62c7de..cdc8192 100644 --- a/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/www/spa.js +++ b/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/www/spa.js @@ -12,6 +12,7 @@ self.chosenTimeLogUserId = -1; self.selectedCalendarWeek = ko.observable(0); self.errorData = ko.observable(null); + self.manualLog = ko.observable(null); self.apiEndpoints = { root: "http://localhost:8800", getUserList: "/api/users", @@ -382,6 +383,32 @@ self.assignErrorObject(errObj.errorCode, errObj.errorMessage, "getGroups"); }); }; + self.createContextMenu = ko.observableArray([ + { text: "Create", action: createlog } + ]); + self.editContextMenu = ko.observableArray([ + { text: "text", action: clicked }, + { text: "Edit", action: editlog }, + { text: "Create", action: createlog } + ]); + function editlog (data) { + alert("edit"); + } + function createlog(data) { + self.manualLog({ + CalendarWeek:-1, + Direction:-1, + EventTime: "2017-03-01T07:44:41.0861152+00:00", + Id: -1, + IdentifierId: -1, + UserId: self.chosenTimeLogUserId(), + Year: 0 + }); + $('#manualLogDialog').modal("show"); + } + function clicked(data) { + alert('oh, you clicked me! ah, and you typed "' + data.value() + '"'); + } Sammy(function () { this.get("#users", function () { var query = this.params.query; From 55a761f2858b53767e032e488a087bdbc5e08820 Mon Sep 17 00:00:00 2001 From: "chris.watts90@outlook.com" Date: Mon, 10 Apr 2017 22:21:27 +0100 Subject: [PATCH 10/49] reference bootstrap datetime picker, remove bootstrap datepicker update edit/create dialog to pull in datetime picker editor to the create edit dialog. #29 --- .../WindowsDataCenter/www/index.html | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/www/index.html b/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/www/index.html index 0e1c97a..e325cd7 100644 --- a/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/www/index.html +++ b/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/www/index.html @@ -5,17 +5,19 @@ - + - + + - + +