diff --git a/Disco.Web/Areas/API/Controllers/DeviceController.cs b/Disco.Web/Areas/API/Controllers/DeviceController.cs index 33226759..a589985e 100644 --- a/Disco.Web/Areas/API/Controllers/DeviceController.cs +++ b/Disco.Web/Areas/API/Controllers/DeviceController.cs @@ -7,6 +7,7 @@ using Disco.Services.Devices.Importing; using Disco.Services.Exporting; using Disco.Services.Interop; using Disco.Services.Interop.ActiveDirectory; +using Disco.Services.Interop.DiscoServices.Upload; using Disco.Services.Logging; using Disco.Services.Users; using Disco.Services.Web; @@ -16,6 +17,7 @@ using System; using System.Collections.Generic; using System.Data.Entity; using System.Linq; +using System.Threading.Tasks; using System.Web; using System.Web.Caching; using System.Web.Mvc; @@ -602,6 +604,38 @@ namespace Disco.Web.Areas.API.Controllers return Json("Invalid Attachment Number", JsonRequestBehavior.AllowGet); } + [DiscoAuthorize(Claims.Device.Actions.AddAttachments)] + [HttpPost, ValidateAntiForgeryToken] + public virtual async Task AttachmentOnlineUploadSession(string id) + { + var device = Database.Devices.Find(id) ?? throw new InvalidOperationException("Unknown Device Serial Number"); + + try + { + if (!Database.DiscoConfiguration.IsActivated) + throw new InvalidOperationException("Activation is required to use this feature (See: Configuration > System)"); + + var (uri, expiration) = await UploadOnlineService.CreateSession(CurrentUser, device); + + UploadOnlineSyncTask.ScheduleInOneHour(); + + return Json(new + { + Success = true, + Expiration = expiration.ToUnixEpoc(), + SessionUri = uri.ToString(), + }); + } + catch (InvalidOperationException ex) + { + return Json(new + { + Success = false, + ErrorMessage = ex.Message, + }); + } + } + #endregion #region Importing diff --git a/Disco.Web/Areas/API/Controllers/JobController.cs b/Disco.Web/Areas/API/Controllers/JobController.cs index 8cb398d2..7f884ecc 100644 --- a/Disco.Web/Areas/API/Controllers/JobController.cs +++ b/Disco.Web/Areas/API/Controllers/JobController.cs @@ -5,6 +5,7 @@ using Disco.Services; using Disco.Services.Authorization; using Disco.Services.Exporting; using Disco.Services.Interop; +using Disco.Services.Interop.DiscoServices.Upload; using Disco.Services.Jobs; using Disco.Services.Jobs.JobLists; using Disco.Services.Jobs.Statistics; @@ -16,6 +17,7 @@ using System; using System.Collections.Generic; using System.Data.Entity; using System.Linq; +using System.Threading.Tasks; using System.Web.Caching; using System.Web.Mvc; @@ -2015,6 +2017,38 @@ namespace Disco.Web.Areas.API.Controllers return Json("Invalid Attachment Number", JsonRequestBehavior.AllowGet); } + [DiscoAuthorize(Claims.Job.Actions.AddAttachments)] + [HttpPost, ValidateAntiForgeryToken] + public virtual async Task AttachmentOnlineUploadSession(int id) + { + var job = Database.Jobs.Find(id) ?? throw new InvalidOperationException("Unknown Job Id"); + + try + { + if (!Database.DiscoConfiguration.IsActivated) + throw new InvalidOperationException("Activation is required to use this feature (See: Configuration > System)"); + + var (uri, expiration) = await UploadOnlineService.CreateSession(CurrentUser, job); + + UploadOnlineSyncTask.ScheduleInOneHour(); + + return Json(new + { + Success = true, + Expiration = expiration.ToUnixEpoc(), + SessionUri = uri.ToString(), + }); + } + catch (InvalidOperationException ex) + { + return Json(new + { + Success = false, + ErrorMessage = ex.Message, + }); + } + } + #endregion #region Job Components diff --git a/Disco.Web/Areas/API/Controllers/UserController.cs b/Disco.Web/Areas/API/Controllers/UserController.cs index a9638f2e..969c8045 100644 --- a/Disco.Web/Areas/API/Controllers/UserController.cs +++ b/Disco.Web/Areas/API/Controllers/UserController.cs @@ -2,12 +2,15 @@ using Disco.Services.Authorization; using Disco.Services.Interop; using Disco.Services.Interop.ActiveDirectory; +using Disco.Services.Interop.DiscoServices.Upload; using Disco.Services.Plugins.Features.DetailsProvider; using Disco.Services.Users; using Disco.Services.Web; using System; using System.Data.Entity; +using System.DirectoryServices.ActiveDirectory; using System.Linq; +using System.Threading.Tasks; using System.Web.Mvc; namespace Disco.Web.Areas.API.Controllers @@ -52,9 +55,9 @@ namespace Disco.Web.Areas.API.Controllers } [DiscoAuthorize(Claims.User.Actions.AddAttachments), ValidateAntiForgeryToken] - public virtual ActionResult AttachmentUpload(string id, string Domain, string comments) + public virtual ActionResult AttachmentUpload(string id, string domain, string comments) { - id = ActiveDirectory.ParseDomainAccountId(id, Domain); + id = ActiveDirectory.ParseDomainAccountId(id, domain); var u = Database.Users.Find(id); if (u != null) @@ -117,9 +120,9 @@ namespace Disco.Web.Areas.API.Controllers } [DiscoAuthorize(Claims.User.ShowAttachments)] - public virtual ActionResult Attachments(string id, string Domain) + public virtual ActionResult Attachments(string id, string domain) { - id = ActiveDirectory.ParseDomainAccountId(id, Domain); + id = ActiveDirectory.ParseDomainAccountId(id, domain); var u = Database.Users.Include("UserAttachments.DocumentTemplate").Include("UserAttachments.TechUser").Where(m => m.UserId == id).FirstOrDefault(); if (u != null) @@ -153,31 +156,65 @@ namespace Disco.Web.Areas.API.Controllers return Json("Invalid Attachment Number", JsonRequestBehavior.AllowGet); } + [DiscoAuthorize(Claims.User.Actions.AddAttachments)] + [HttpPost, ValidateAntiForgeryToken] + public virtual async Task AttachmentOnlineUploadSession(string id, string domain) + { + var userId = ActiveDirectory.ParseDomainAccountId(id, domain); + if (!UserService.TryGetUser(userId, Database, false,out var user)) + throw new InvalidOperationException("Unknown User"); + + try + { + if (!Database.DiscoConfiguration.IsActivated) + throw new InvalidOperationException("Activation is required to use this feature (See: Configuration > System)"); + + var (uri, expiration) = await UploadOnlineService.CreateSession(CurrentUser, user); + + UploadOnlineSyncTask.ScheduleInOneHour(); + + return Json(new + { + Success = true, + Expiration = expiration.ToUnixEpoc(), + SessionUri = uri.ToString(), + }); + } + catch (InvalidOperationException ex) + { + return Json(new + { + Success = false, + ErrorMessage = ex.Message, + }); + } + } + #endregion [DiscoAuthorize(Claims.User.Actions.GenerateDocuments)] - public virtual ActionResult GeneratePdf(string id, string Domain, string DocumentTemplateId) + public virtual ActionResult GeneratePdf(string id, string domain, string DocumentTemplateId) { if (string.IsNullOrEmpty(id)) throw new ArgumentNullException(nameof(id)); if (string.IsNullOrEmpty(DocumentTemplateId)) throw new ArgumentNullException(nameof(DocumentTemplateId)); - var userId = ActiveDirectory.ParseDomainAccountId(id, Domain); + var userId = ActiveDirectory.ParseDomainAccountId(id, domain); // Obsolete: Use API\DocumentTemplate\Generate instead return RedirectToAction(MVC.API.DocumentTemplate.Generate(DocumentTemplateId, userId)); } [DiscoAuthorize(Claims.User.Actions.GenerateDocuments)] - public virtual ActionResult GeneratePdfPackage(string id, string Domain, string DocumentTemplatePackageId) + public virtual ActionResult GeneratePdfPackage(string id, string domain, string DocumentTemplatePackageId) { if (string.IsNullOrEmpty(id)) throw new ArgumentNullException(nameof(id)); if (string.IsNullOrEmpty(DocumentTemplatePackageId)) throw new ArgumentNullException(nameof(DocumentTemplatePackageId)); - var userId = ActiveDirectory.ParseDomainAccountId(id, Domain); + var userId = ActiveDirectory.ParseDomainAccountId(id, domain); // Obsolete: Use API\DocumentTemplatePackage\Generate instead return RedirectToAction(MVC.API.DocumentTemplatePackage.Generate(DocumentTemplatePackageId, userId)); diff --git a/Disco.Web/ClientSource/Scripts/Modules/Disco-AttachmentUploader.js b/Disco.Web/ClientSource/Scripts/Modules/Disco-AttachmentUploader.js index 984757d8..6381cfcb 100644 --- a/Disco.Web/ClientSource/Scripts/Modules/Disco-AttachmentUploader.js +++ b/Disco.Web/ClientSource/Scripts/Modules/Disco-AttachmentUploader.js @@ -161,6 +161,94 @@ }; // #endregion + // #region Online Upload + self.onlineUpload = async function () { + const onlineUploadUrl = self.$host.attr('data-onlineuploadurl'); + const qrCodeUrl = self.$host.attr('data-qrcodeurl'); + const $button = self.$host.find('.attachmentInput span.online-upload'); + + if ($button.hasClass('fa-spinner')) + return; + + $button + .removeClass('fa-qrcode') + .addClass('fa-spinner fa-spin d-green'); + + if (!window.QRCode) { + const qrCodeElement = document.createElement('script'); + qrCodeElement.src = qrCodeUrl; + qrCodeElement.type = 'text/javascript'; + qrCodeElement.onload = function () { + self.onlineUploadDisplay(); + }; + document.body.appendChild(qrCodeElement); + } + + const formData = new FormData(); + formData.append('__RequestVerificationToken', self.$host.find('input[name="__RequestVerificationToken"]').val()); + const result = await fetch(onlineUploadUrl, { + method: 'POST', + body: formData + }); + + if (!result.ok) { + alert('Error creating online upload session: ' + result.statusText); + return; + } + + const resultModel = await result.json(); + + if (!resultModel.Success) { + alert('Unable to create online upload session: ' + result.ErrorMessage); + return; + } + + self.onlineUploadSession = resultModel; + self.onlineUploadDisplay(); + + $button + .removeClass('fa-spinner fa-spin d-green') + .addClass('fa-qrcode'); + } + self.onlineUploadDisplay = function () { + if (!!window.QRCode && !!self.onlineUploadSession) { + var dialog = $('
') + .attr({ + title: 'Online Upload', + 'class': 'dialog Disco-AttachmentUpload-OnlineUploadDialog' + }); + var qrCode = QRCode({ + msg: self.onlineUploadSession.SessionUri, + ecl: 'L' + }); + dialog.append(qrCode); + $('') + .val(self.onlineUploadSession.SessionUri) + .appendTo(dialog); + $('

Scan the QR Code or send the link to upload files

') + .appendTo(dialog); + + var expiration = new Date(self.onlineUploadSession.Expiration); + var sessionExpiration = setTimeout(function () { + dialog.dialog('close'); + }, expiration.getTime() - new Date().getTime()); + + dialog.dialog({ + resizable: false, + width: 500, + modal: true, + autoOpen: true, + close: function () { + if (!!sessionExpiration) { + window.clearTimeout(sessionExpiration); + } + dialog.dialog('destroy').remove(); + } + }); + } + } + // #endregion + // #region Helpers self.getFileComments = function (fileName, thumbnailHandler, complete) { var result = false; diff --git a/Disco.Web/ClientSource/Scripts/Modules/Disco-AttachmentUploader.min.js b/Disco.Web/ClientSource/Scripts/Modules/Disco-AttachmentUploader.min.js index f55e78c8..324c1d96 100644 --- a/Disco.Web/ClientSource/Scripts/Modules/Disco-AttachmentUploader.min.js +++ b/Disco.Web/ClientSource/Scripts/Modules/Disco-AttachmentUploader.min.js @@ -1 +1 @@ -(function(n,t,i){"use strict";var r=function(r){var u=this;if(u.$host=r,u.uploadUrl=r.attr("data-uploadurl"),u.dropTarget=r.find(".Disco-AttachmentUpload-DropTarget"),u.uploadProgressContainer=r.find(".Disco-AttachmentUpload-Progress"),u._uploadFilesInput=null,u.uploadFiles=function(){!u._uploadFilesInput||u._uploadFilesInput.remove();u._uploadFilesInput=i("");u._uploadFilesInput.attr({type:"file",multiple:"multiple",title:"Disco ICT File Uploading"}).hide().change(function(n){var t=n.target.files;!!t&&t.length>0&&u._uploadFiles(t);u._uploadFilesInput.remove()}).appendTo(u.uploadProgressContainer).click()},!!u.dropTarget){var o=i(t),f=!1,e=null;o.on("dragover",function(){u.dropTarget.addClass("dragHighlight");u.dropTarget.removeClass("dragHover");f=!1});o.on("dragleave",function(){!e||n.clearInterval(e);f=!0;n.setTimeout(function(){f&&u.dropTarget.removeClass("dragHighlight");e=null},200)});u.dropTarget.on("dragover",function(n){n.stopPropagation();n.preventDefault();u.dropTarget.addClass("dragHover");f=!1;n.originalEvent.dataTransfer.dropEffect="copy"});u.dropTarget.on("drop",function(n){n.stopPropagation();n.preventDefault();f=!0;u.dropTarget.removeClass("dragHighlight");var t=n.originalEvent.dataTransfer.files;u._uploadFiles(t)})}return u.uploadImage=function(){let e=null,o=!1;var f=i("