diff --git a/Disco.BI/BI/Extensions/JobTableExtensions.cs b/Disco.BI/BI/Extensions/JobTableExtensions.cs index 4e45e1eb..2721d8b0 100644 --- a/Disco.BI/BI/Extensions/JobTableExtensions.cs +++ b/Disco.BI/BI/Extensions/JobTableExtensions.cs @@ -11,8 +11,10 @@ namespace Disco.BI.Extensions public static class JobTableExtensions { - public static void Fill(this JobTableModel model, DiscoDataContext dbContext, IQueryable Jobs) + public static List DetermineItems(this JobTableModel model, DiscoDataContext dbContext, IQueryable Jobs) { + List items; + if (model.ShowStatus) { @@ -50,18 +52,18 @@ namespace Disco.BI.Extensions JobMetaNonWarranty_RepairerName = j.JobMetaNonWarranty.RepairerName }); - model.Items = new List(); + items = new List(); foreach (var j in jobItems) { j.StatusId = j.CalculateStatusId(); j.StatusDescription = JobBI.Utilities.JobStatusDescription(j.StatusId, j); - model.Items.Add(j); + items.Add(j); } } else { - model.Items = Jobs.Select(j => new JobTableModel.JobTableItemModel() + items = Jobs.Select(j => new JobTableModel.JobTableItemModel() { Id = j.Id, DeviceAddressId = j.Device.DeviceProfile.DefaultOrganisationAddress, @@ -84,11 +86,17 @@ namespace Disco.BI.Extensions if (model.ShowDeviceAddress.Value) { - foreach (var j in model.Items) + foreach (var j in items) if (j.DeviceAddressId.HasValue) j.DeviceAddress = dbContext.DiscoConfiguration.OrganisationAddresses.GetAddress(j.DeviceAddressId.Value).Name; } - } + return items; + } + + public static void Fill(this JobTableModel model, DiscoDataContext dbContext, IQueryable Jobs) + { + model.Items = model.DetermineItems(dbContext, Jobs); + } } } diff --git a/Disco.BI/BI/Interop/SignalRHandlers/RepositoryMonitorNotifications.cs b/Disco.BI/BI/Interop/SignalRHandlers/RepositoryMonitorNotifications.cs new file mode 100644 index 00000000..bd99182e --- /dev/null +++ b/Disco.BI/BI/Interop/SignalRHandlers/RepositoryMonitorNotifications.cs @@ -0,0 +1,37 @@ +using Disco.Data.Repository.Monitor; +using SignalR; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Disco.BI.Interop.SignalRHandlers +{ + public class RepositoryMonitorNotifications : PersistentConnection + { + public static void Initialize() + { + RepositoryMonitor.StreamAfterCommit.Subscribe(AfterCommit); + } + + protected override System.Threading.Tasks.Task OnReceivedAsync(IRequest request, string connectionId, string data) + { + // Add to Group + if (!string.IsNullOrWhiteSpace(data) && data.StartsWith("/addToGroups:") && data.Length > 13) + { + var groups = data.Substring(13).Split(','); + foreach (var g in groups) + { + this.Groups.Add(connectionId, g); + } + } + return base.OnReceivedAsync(request, connectionId, data); + } + + private static void AfterCommit(RepositoryMonitorEvent e) + { + GlobalHost.ConnectionManager.GetConnectionContext().Groups.Send(e.EntityType.Name, e); + } + } +} diff --git a/Disco.BI/BI/JobBI/ManagedJobList.cs b/Disco.BI/BI/JobBI/ManagedJobList.cs new file mode 100644 index 00000000..e9f604ae --- /dev/null +++ b/Disco.BI/BI/JobBI/ManagedJobList.cs @@ -0,0 +1,80 @@ +using Disco.Data.Repository; +using Disco.Data.Repository.Monitor; +using Disco.Models.BI.Job; +using Disco.Models.Repository; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Disco.BI.Extensions; +using System.Reactive.Linq; + +namespace Disco.BI.JobBI +{ + public class ManagedJobList : JobTableModel, IDisposable + { + public string Name { get; set; } + public Func, IQueryable> FilterFunction {get;set;} + public Func, IEnumerable> SortFunction { get; set; } + private IDisposable unsubscribeToken; + + public ManagedJobList Initialize(DiscoDataContext dbContext) + { + // Initially fill table + this.Items = this.SortFunction(this.DetermineItems(dbContext, this.FilterFunction(dbContext.Jobs))).ToList(); + + // Subscribe for Changes + unsubscribeToken = RepositoryMonitor.StreamAfterCommit + .Where(n => n.EntityType == typeof(Job) || + n.EntityType == typeof(JobMetaWarranty) || + n.EntityType == typeof(JobMetaNonWarranty) || + n.EntityType == typeof(JobMetaInsurance)) + .Subscribe(JobNotification); + + return this; + } + + private void JobNotification(RepositoryMonitorEvent e) + { + int jobId; + + if (e.EntityType == typeof(Job)) + jobId = ((Job)e.Entity).Id; + else + if (e.EntityType == typeof(JobMetaWarranty)) + jobId = ((JobMetaWarranty)e.Entity).JobId; + else + if (e.EntityType == typeof(JobMetaNonWarranty)) + jobId = ((JobMetaNonWarranty)e.Entity).JobId; + else + if (e.EntityType == typeof(JobMetaInsurance)) + jobId = ((JobMetaInsurance)e.Entity).JobId; + else + return; // Subscription should never reach + + var existingItem = this.Items.FirstOrDefault(i => i.Id == jobId); + var updatedItem = this.DetermineItems(e.dbContext, this.FilterFunction(e.dbContext.Jobs.Where(j => j.Id == jobId))); + + var updatedItems = this.Items.ToList(); + + // Remove Existing + if (existingItem != null) + updatedItems.Remove(existingItem); + + if (updatedItem.Count > 0) + { + // Add Item + updatedItems.Add(updatedItem.First()); + } + + // Reorder + this.Items = this.SortFunction(updatedItems).ToList(); + } + + public void Dispose() + { + unsubscribeToken.Dispose(); + } + } +} diff --git a/Disco.BI/Disco.BI.csproj b/Disco.BI/Disco.BI.csproj index 802cf2de..fc403727 100644 --- a/Disco.BI/Disco.BI.csproj +++ b/Disco.BI/Disco.BI.csproj @@ -78,6 +78,21 @@ + + False + ..\packages\Rx-Core.2.1.30214.0\lib\Net45\System.Reactive.Core.dll + + + False + ..\packages\Rx-Interfaces.2.1.30214.0\lib\Net45\System.Reactive.Interfaces.dll + + + False + ..\packages\Rx-Linq.2.1.30214.0\lib\Net45\System.Reactive.Linq.dll + + + ..\packages\Rx-PlatformServices.2.1.30214.0\lib\Net45\System.Reactive.PlatformServices.dll + @@ -157,7 +172,9 @@ + + diff --git a/Disco.BI/Properties/AssemblyInfo.cs b/Disco.BI/Properties/AssemblyInfo.cs index b85991a8..274ee1a8 100644 --- a/Disco.BI/Properties/AssemblyInfo.cs +++ b/Disco.BI/Properties/AssemblyInfo.cs @@ -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.2.0405.1309")] -[assembly: AssemblyFileVersion("1.2.0405.1309")] +[assembly: AssemblyVersion("1.2.0411.1833")] +[assembly: AssemblyFileVersion("1.2.0411.1833")] diff --git a/Disco.BI/packages.config b/Disco.BI/packages.config index c5ef15ef..86ad8192 100644 --- a/Disco.BI/packages.config +++ b/Disco.BI/packages.config @@ -3,6 +3,11 @@ + + + + + diff --git a/Disco.Client/Properties/AssemblyInfo.cs b/Disco.Client/Properties/AssemblyInfo.cs index 3c59e5f6..fe777b84 100644 --- a/Disco.Client/Properties/AssemblyInfo.cs +++ b/Disco.Client/Properties/AssemblyInfo.cs @@ -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.2.0404.1414")] -[assembly: AssemblyFileVersion("1.2.0404.1414")] +[assembly: AssemblyVersion("1.2.0411.1608")] +[assembly: AssemblyFileVersion("1.2.0411.1608")] diff --git a/Disco.ClientBootstrapper/Properties/AssemblyInfo.cs b/Disco.ClientBootstrapper/Properties/AssemblyInfo.cs index fc635d37..3bd4623d 100644 --- a/Disco.ClientBootstrapper/Properties/AssemblyInfo.cs +++ b/Disco.ClientBootstrapper/Properties/AssemblyInfo.cs @@ -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.2.0404.1358")] -[assembly: AssemblyFileVersion("1.2.0404.1358")] +[assembly: AssemblyVersion("1.2.0411.1608")] +[assembly: AssemblyFileVersion("1.2.0411.1608")] diff --git a/Disco.Data/Disco.Data.csproj b/Disco.Data/Disco.Data.csproj index 923e39d1..a59d31e8 100644 --- a/Disco.Data/Disco.Data.csproj +++ b/Disco.Data/Disco.Data.csproj @@ -49,6 +49,18 @@ + + ..\packages\Rx-Core.2.1.30214.0\lib\Net45\System.Reactive.Core.dll + + + ..\packages\Rx-Interfaces.2.1.30214.0\lib\Net45\System.Reactive.Interfaces.dll + + + ..\packages\Rx-Linq.2.1.30214.0\lib\Net45\System.Reactive.Linq.dll + + + ..\packages\Rx-PlatformServices.2.1.30214.0\lib\Net45\System.Reactive.PlatformServices.dll + @@ -109,6 +121,9 @@ + + + @@ -144,7 +159,7 @@ - + diff --git a/Disco.Data/Properties/AssemblyInfo.cs b/Disco.Data/Properties/AssemblyInfo.cs index b6f5e73f..59b2ff10 100644 --- a/Disco.Data/Properties/AssemblyInfo.cs +++ b/Disco.Data/Properties/AssemblyInfo.cs @@ -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.2.0405.1309")] -[assembly: AssemblyFileVersion("1.2.0405.1309")] +[assembly: AssemblyVersion("1.2.0411.1908")] +[assembly: AssemblyFileVersion("1.2.0411.1908")] diff --git a/Disco.Data/Repository/DiscoDataContext.cs b/Disco.Data/Repository/DiscoDataContext.cs index 85280eda..a1b1762d 100644 --- a/Disco.Data/Repository/DiscoDataContext.cs +++ b/Disco.Data/Repository/DiscoDataContext.cs @@ -68,5 +68,24 @@ namespace Disco.Data.Repository modelBuilder.Entity().Property(DeviceProfile.PropertyAccessExpressions.DistributionTypeDb); } + // Hook for Repository Monitor + public override int SaveChanges() + { + int changeCount = 0; + + // Notify before changes are committed + var changes = Monitor.RepositoryMonitor.BeforeSaveChanges(this); + + changeCount = base.SaveChanges(); + + if (changes.Length > 0) + { + // Notify after changes are committed + Monitor.RepositoryMonitor.AfterSaveChanges(this, changes); + } + + return changeCount; + } + } } diff --git a/Disco.Data/Repository/Monitor/RepositoryMonitor.cs b/Disco.Data/Repository/Monitor/RepositoryMonitor.cs new file mode 100644 index 00000000..b739b17a --- /dev/null +++ b/Disco.Data/Repository/Monitor/RepositoryMonitor.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Reactive; +using System.Reactive.Subjects; +using System.Data.Entity.Infrastructure; +using System.Collections.Concurrent; +using System.Data.Objects; + +namespace Disco.Data.Repository.Monitor +{ + public static class RepositoryMonitor + { + private static Subject streamBefore = new Subject(); + private static Subject streamAfter = new Subject(); + private static ConcurrentDictionary entityProxyTypeCache = new ConcurrentDictionary(); + + public static Subject StreamBeforeCommit { get { return streamBefore; } } + public static Subject StreamAfterCommit { get { return streamAfter; } } + + internal static RepositoryMonitorEvent[] BeforeSaveChanges(DiscoDataContext dbContext) + { + var contextStateManager = ((IObjectContextAdapter)dbContext).ObjectContext.ObjectStateManager; + //var changes = contextStateManager.GetObjectStateEntries(System.Data.EntityState.Added).Concat(contextStateManager.GetObjectStateEntries(System.Data.EntityState.Deleted)).Concat(contextStateManager.GetObjectStateEntries(System.Data.EntityState.Modified)); + + dbContext.ChangeTracker.DetectChanges(); + + var events = dbContext.ChangeTracker.Entries().Where(e => e.State != System.Data.EntityState.Unchanged && e.State != System.Data.EntityState.Detached).Select(entryState => + { + var monitorEvent = EventFromEntryState(dbContext, entryState, contextStateManager.GetObjectStateEntry(entryState.Entity)); + // Push to Stream + streamBefore.OnNext(monitorEvent); + return monitorEvent; + }).ToArray(); + + return events; + } + internal static void AfterSaveChanges(DiscoDataContext dbContext, IEnumerable changes) + { + var contextStateManager = ((IObjectContextAdapter)dbContext).ObjectContext.ObjectStateManager; + + foreach (var change in changes) + { + UpdateAfterEventFromEntryState(change, contextStateManager); + + streamAfter.OnNext(change); + } + } + + private static Type EntityTypeFromProxy(Type EntityProxyType) + { + Type EntityType; + + if (entityProxyTypeCache.TryGetValue(EntityProxyType, out EntityType)) + return EntityType; + + EntityType = EntityProxyType; + do + { + if (EntityType.Namespace.StartsWith("Disco.Models.Repository")) + { + entityProxyTypeCache.TryAdd(EntityProxyType, EntityType); + return EntityType; + } + + EntityType = EntityType.BaseType; + } while (EntityType != null); + + throw new ArgumentException("The EntryProxyType does not inherit from any Repository Models", "EntityProxyType"); + } + + internal static void UpdateAfterEventFromEntryState(RepositoryMonitorEvent monitorEvent, ObjectStateManager stateManager) + { + if (monitorEvent.EventType == RepositoryMonitorEventType.Added) + { + // Update Entity Key for Added Events + var entryState = stateManager.GetObjectStateEntry(monitorEvent.Entity); + monitorEvent.EntityKey = entryState.EntityKey.EntityKeyValues.Select(kv => kv.Value).ToArray(); + } + } + + internal static RepositoryMonitorEvent EventFromEntryState(DiscoDataContext dbContext, DbEntityEntry dbEntryState, ObjectStateEntry entryState) + { + RepositoryMonitorEventType eventType; + string[] modifiedProperties = null; + object[] entityKey = null; + Type entityType; + + switch (dbEntryState.State) + { + case System.Data.EntityState.Added: + eventType = RepositoryMonitorEventType.Added; + break; + case System.Data.EntityState.Deleted: + eventType = RepositoryMonitorEventType.Deleted; + break; + case System.Data.EntityState.Detached: + eventType = RepositoryMonitorEventType.Detached; + break; + case System.Data.EntityState.Modified: + eventType = RepositoryMonitorEventType.Modified; + break; + case System.Data.EntityState.Unchanged: + eventType = RepositoryMonitorEventType.Unchanged; + break; + default: + throw new NotSupportedException(string.Format("Database Entry State not supported: {0}", dbEntryState.State.ToString())); + } + + entityType = EntityTypeFromProxy(dbEntryState.Entity.GetType()); + + // Only pass modified properties on Modified Event (Ignore Added/Deleted) + if (eventType == RepositoryMonitorEventType.Modified) + modifiedProperties = entryState.GetModifiedProperties().ToArray(); + + // Don't pass entity key when entity newly added + if (eventType != RepositoryMonitorEventType.Added) + entityKey = entryState.EntityKey.EntityKeyValues.Select(kv => kv.Value).ToArray(); + + return new RepositoryMonitorEvent() + { + dbContext = dbContext, + EventType = eventType, + Entity = dbEntryState.Entity, + EntityKey = entityKey, + EntityType = entityType, + ModifiedProperties = modifiedProperties + }; + } + } +} diff --git a/Disco.Data/Repository/Monitor/RepositoryMonitorEvent.cs b/Disco.Data/Repository/Monitor/RepositoryMonitorEvent.cs new file mode 100644 index 00000000..d355693d --- /dev/null +++ b/Disco.Data/Repository/Monitor/RepositoryMonitorEvent.cs @@ -0,0 +1,35 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Disco.Data.Repository.Monitor +{ + public class RepositoryMonitorEvent + { + [JsonIgnore] + public DiscoDataContext dbContext { get; set; } + + public RepositoryMonitorEventType EventType { get; set; } + + [JsonIgnore] + public Type EntityType { get; set; } + + public string EntityTypeName + { + get + { + return EntityType.Name; + } + } + + [JsonIgnore] + public object Entity { get; set; } + + public object[] EntityKey { get; set; } + + public string[] ModifiedProperties { get; set; } + } +} diff --git a/Disco.Data/Repository/Monitor/RepositoryMonitorEventType.cs b/Disco.Data/Repository/Monitor/RepositoryMonitorEventType.cs new file mode 100644 index 00000000..68e7aa10 --- /dev/null +++ b/Disco.Data/Repository/Monitor/RepositoryMonitorEventType.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Disco.Data.Repository.Monitor +{ + public enum RepositoryMonitorEventType + { + Added = 0, + Deleted = 1, + Modified = 2, + Detached = 3, + Unchanged = 4 + } +} diff --git a/Disco.Data/packages.config b/Disco.Data/packages.config index 027ca130..cb3b8e32 100644 --- a/Disco.Data/packages.config +++ b/Disco.Data/packages.config @@ -2,4 +2,9 @@ + + + + + \ No newline at end of file diff --git a/Disco.Models/BI/Job/JobTableModel.cs b/Disco.Models/BI/Job/JobTableModel.cs index d5dd35a9..d7cc989d 100644 --- a/Disco.Models/BI/Job/JobTableModel.cs +++ b/Disco.Models/BI/Job/JobTableModel.cs @@ -18,7 +18,7 @@ namespace Disco.Models.BI.Job public bool ShowStatus { get; set; } public bool IsSmallTable { get; set; } public bool HideClosedJobs { get; set; } - public List Items { get; set; } + public virtual List Items { get; set; } public JobTableModel() { diff --git a/Disco.Models/Properties/AssemblyInfo.cs b/Disco.Models/Properties/AssemblyInfo.cs index a1d74020..455361ee 100644 --- a/Disco.Models/Properties/AssemblyInfo.cs +++ b/Disco.Models/Properties/AssemblyInfo.cs @@ -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.2.0405.1309")] -[assembly: AssemblyFileVersion("1.2.0405.1309")] +[assembly: AssemblyVersion("1.2.0411.1908")] +[assembly: AssemblyFileVersion("1.2.0411.1908")] diff --git a/Disco.Services/Plugins/Plugins.cs b/Disco.Services/Plugins/Plugins.cs index b582faee..118a96e8 100644 --- a/Disco.Services/Plugins/Plugins.cs +++ b/Disco.Services/Plugins/Plugins.cs @@ -503,7 +503,7 @@ namespace Disco.Services.Plugins public static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { - if (args.RequestingAssembly.Location.StartsWith(PluginPath, StringComparison.InvariantCultureIgnoreCase) && _PluginManifests != null) + if (args.RequestingAssembly != null && args.RequestingAssembly.Location.StartsWith(PluginPath, StringComparison.InvariantCultureIgnoreCase) && _PluginManifests != null) { // Try best guess first PluginManifest requestingPlugin = _PluginManifests.Values.Where(p => p.Type.Assembly == args.RequestingAssembly).FirstOrDefault(); diff --git a/Disco.Services/Properties/AssemblyInfo.cs b/Disco.Services/Properties/AssemblyInfo.cs index 934ead97..83542eaa 100644 --- a/Disco.Services/Properties/AssemblyInfo.cs +++ b/Disco.Services/Properties/AssemblyInfo.cs @@ -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.2.0405.1309")] -[assembly: AssemblyFileVersion("1.2.0405.1309")] +[assembly: AssemblyVersion("1.2.0411.1843")] +[assembly: AssemblyFileVersion("1.2.0411.1843")] diff --git a/Disco.Web.Extensions/Properties/AssemblyInfo.cs b/Disco.Web.Extensions/Properties/AssemblyInfo.cs index aeb31e28..173e1cd2 100644 --- a/Disco.Web.Extensions/Properties/AssemblyInfo.cs +++ b/Disco.Web.Extensions/Properties/AssemblyInfo.cs @@ -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.2.0405.1309")] -[assembly: AssemblyFileVersion("1.2.0405.1309")] +[assembly: AssemblyVersion("1.2.0411.1833")] +[assembly: AssemblyFileVersion("1.2.0411.1833")] diff --git a/Disco.Web/App_Start/AppConfig.cs b/Disco.Web/App_Start/AppConfig.cs index 5edad1ad..02b27ae4 100644 --- a/Disco.Web/App_Start/AppConfig.cs +++ b/Disco.Web/App_Start/AppConfig.cs @@ -63,6 +63,9 @@ namespace Disco.Web } DiscoApplication.DocumentDropBoxMonitor.StartWatching(); DiscoApplication.DocumentDropBoxMonitor.ScheduleCurrentFiles(10); + + // Enable SignalR-based Repository Notifications + Disco.BI.Interop.SignalRHandlers.RepositoryMonitorNotifications.Initialize(); } public static void InitializeUpdateEnvironment() diff --git a/Disco.Web/Areas/API/APIAreaRegistration.cs b/Disco.Web/Areas/API/APIAreaRegistration.cs index 31325dfc..fb5b21de 100644 --- a/Disco.Web/Areas/API/APIAreaRegistration.cs +++ b/Disco.Web/Areas/API/APIAreaRegistration.cs @@ -22,6 +22,9 @@ namespace Disco.Web.Areas.API context.Routes.MapConnection( "API_Logging_TaskStatusNotifications", "API/Logging/TaskStatusNotifications/{*operation}"); + context.Routes.MapConnection( + "API_Repository_Notifications", "API/Repository/Notifications/{*operation}"); + context.MapRoute( "API_Update", "API/{controller}/Update/{id}/{key}", diff --git a/Disco.Web/Areas/API/Controllers/JobController.cs b/Disco.Web/Areas/API/Controllers/JobController.cs index 552cdba2..41da195b 100644 --- a/Disco.Web/Areas/API/Controllers/JobController.cs +++ b/Disco.Web/Areas/API/Controllers/JobController.cs @@ -1731,6 +1731,17 @@ namespace Disco.Web.Areas.API.Controllers } return Json(new Models.Job.CommentsModel() { Result = "Invalid Job Number" }, JsonRequestBehavior.AllowGet); } + public virtual ActionResult Comment(int id) + { + var jl = dbContext.JobLogs.Include("TechUser").FirstOrDefault(l => l.Id == id); + if (jl != null) + { + var c = Models.Job._CommentModel.FromJobLog(jl); + + return Json(c, JsonRequestBehavior.AllowGet); + } + return Json(new Models.Job.CommentsModel() { Result = "Invalid Comment Id" }, JsonRequestBehavior.AllowGet); + } public virtual ActionResult CommentPost(int id, string comment) { var j = dbContext.Jobs.Find(id); diff --git a/Disco.Web/Areas/API/Models/Job/_CommentModel.cs b/Disco.Web/Areas/API/Models/Job/_CommentModel.cs index d9d9c721..25fb3590 100644 --- a/Disco.Web/Areas/API/Models/Job/_CommentModel.cs +++ b/Disco.Web/Areas/API/Models/Job/_CommentModel.cs @@ -10,6 +10,7 @@ namespace Disco.Web.Areas.API.Models.Job public class _CommentModel { public int Id { get; set; } + public int JobId { get; set; } public string Author { get; set; } public DateTime Timestamp { get; set; } public string Comments { get; set; } @@ -41,6 +42,7 @@ namespace Disco.Web.Areas.API.Models.Job return new _CommentModel { Id = jl.Id, + JobId = jl.JobId, Author = jl.TechUser.ToString(), Timestamp = jl.Timestamp, Comments = jl.Comments diff --git a/Disco.Web/Controllers/JobController.cs b/Disco.Web/Controllers/JobController.cs index 80db2edb..1c7671ba 100644 --- a/Disco.Web/Controllers/JobController.cs +++ b/Disco.Web/Controllers/JobController.cs @@ -12,6 +12,7 @@ using Disco.Services.Plugins.Features.WarrantyProvider; using Disco.Services.Plugins; using Disco.Models.UI.Job; using Disco.Services.Plugins.Features.UIExtension; +using Disco.BI.JobBI; namespace Disco.Web.Controllers { @@ -19,51 +20,57 @@ namespace Disco.Web.Controllers { #region Index + private static object jobListCreationLock = new object(); + + private static ManagedJobList jobList_OpenJobs; + private static ManagedJobList jobList_LongRunning; + public virtual ActionResult Index() { var m = new Models.Job.IndexModel(); - //m.MyJobs = JobBI.SelectJobSearchResultItem((from j in dbContext.Jobs - // where j.OpenedTechUserId == DiscoApplication.CurrentUser.Id && j.ClosedDate == null && (j.DeviceHeld == null || j.DeviceReturnedDate != null || j.DeviceReadyForReturn == null) - // select j)); - //m.OpenJobs = JobBI.SelectJobSearchResultItem((from j in dbContext.Jobs - // where j.OpenedTechUserId != DiscoApplication.CurrentUser.Id && j.ClosedDate == null && (j.DeviceHeld == null || j.DeviceReturnedDate != null || j.DeviceReadyForReturn == null) - // select j)); + if (jobList_OpenJobs == null) + { + lock (jobListCreationLock) + { + if (jobList_OpenJobs == null) + { + jobList_OpenJobs = new ManagedJobList() + { + Name = "Open Jobs Awaiting Technician Action", + FilterFunction = q => q.Where(j => j.ClosedDate == null && !j.WaitingForUserAction.HasValue + && !(j.JobTypeId == JobType.JobTypeIds.HNWar && (j.JobMetaNonWarranty.RepairerLoggedDate.HasValue && j.JobMetaNonWarranty.IsInsuranceClaim && !j.JobMetaInsurance.ClaimFormSentDate.HasValue)) + && !(j.JobTypeId == JobType.JobTypeIds.HNWar && (j.JobMetaNonWarranty.RepairerLoggedDate.HasValue && !j.JobMetaNonWarranty.RepairerCompletedDate.HasValue)) + && !(j.JobTypeId == JobType.JobTypeIds.HNWar && (j.JobMetaNonWarranty.RepairerLoggedDate.HasValue && j.JobMetaNonWarranty.AccountingChargeAddedDate.HasValue && !j.JobMetaNonWarranty.AccountingChargePaidDate.HasValue)) + && !(j.JobTypeId == JobType.JobTypeIds.HWar && (j.JobMetaWarranty.ExternalLoggedDate.HasValue && !j.JobMetaWarranty.ExternalCompletedDate.HasValue)) + && (j.DeviceHeld == null || j.DeviceReturnedDate != null || j.DeviceReadyForReturn == null)), + SortFunction = q => q.OrderBy(j => j.Id), + ShowStatus = true + }.Initialize(dbContext); + } + } + } + if (jobList_LongRunning == null) + { + lock (jobListCreationLock) + { + if (jobList_LongRunning == null) + { + var longRunningThreshold = DateTime.Today.AddDays(-7); - dbContext.Configuration.LazyLoadingEnabled = true; + jobList_LongRunning = new ManagedJobList() + { + Name = "Long Running Jobs", + FilterFunction = q => q.Where(j => j.ClosedDate == null && j.OpenedDate < longRunningThreshold), + SortFunction = q => q.OrderBy(j => j.Id), + ShowStatus = true + }.Initialize(dbContext); + } + } + } - m.OpenJobs = new Disco.Models.BI.Job.JobTableModel() { ShowStatus = true }; - m.OpenJobs.Fill(dbContext, BI.JobBI.Searching.BuildJobTableModel(dbContext).Where(j => j.ClosedDate == null - && !j.WaitingForUserAction.HasValue - && !(j.JobTypeId == JobType.JobTypeIds.HNWar && (j.JobMetaNonWarranty.RepairerLoggedDate.HasValue && j.JobMetaNonWarranty.IsInsuranceClaim && !j.JobMetaInsurance.ClaimFormSentDate.HasValue)) - && !(j.JobTypeId == JobType.JobTypeIds.HNWar && (j.JobMetaNonWarranty.RepairerLoggedDate.HasValue && !j.JobMetaNonWarranty.RepairerCompletedDate.HasValue)) - && !(j.JobTypeId == JobType.JobTypeIds.HNWar && (j.JobMetaNonWarranty.RepairerLoggedDate.HasValue && j.JobMetaNonWarranty.AccountingChargeAddedDate.HasValue && !j.JobMetaNonWarranty.AccountingChargePaidDate.HasValue)) - && !(j.JobTypeId == JobType.JobTypeIds.HWar && (j.JobMetaWarranty.ExternalLoggedDate.HasValue && !j.JobMetaWarranty.ExternalCompletedDate.HasValue)) - && (j.DeviceHeld == null || j.DeviceReturnedDate != null || j.DeviceReadyForReturn == null)).OrderBy(j => j.Id)); - - var longRunningThreshold = DateTime.Now.AddDays(-7); - m.LongRunningJobs = new Disco.Models.BI.Job.JobTableModel() { ShowStatus = true }; - m.LongRunningJobs.Fill(dbContext, BI.JobBI.Searching.BuildJobTableModel(dbContext).Where(j => j.ClosedDate == null - && j.OpenedDate < longRunningThreshold).OrderBy(j => j.Id)); - - //m.WaitingForUserActionJobs = new Disco.Models.BI.Job.JobTableModel(); - //m.WaitingForUserActionJobs.Fill(Disco.BI.JobTableModelBI.BuildQuery(dbContext).Where(j => j.WaitingForUserAction.HasValue - // && j.ClosedDate == null)); - - //m.ReadyForReturnJobs = new Disco.Models.BI.Job.JobTableModel(); - //m.ReadyForReturnJobs.Fill(BI.JobTableModelBI.BuildQuery(dbContext).Where(j => !j.WaitingForUserAction.HasValue - // && j.DeviceHeld != null && j.DeviceReturnedDate == null && j.DeviceReadyForReturn != null - // && j.ClosedDate == null)); - - //// 2 Days ago - Ignore Weekend - //var dateTimeNow = DateTime.Now; - //var closedThreshold = dateTimeNow.AddDays(-2); - //if (dateTimeNow.DayOfWeek == DayOfWeek.Monday) - // closedThreshold = closedThreshold.AddDays(-2); - //if (dateTimeNow.DayOfWeek == DayOfWeek.Tuesday) - // closedThreshold = closedThreshold.AddDays(-1); - //m.RecentlyClosedJobs = new Disco.Models.BI.Job.JobTableModel(); - //m.RecentlyClosedJobs.Fill(BI.JobTableModelBI.BuildQuery(dbContext).Where(j => j.ClosedDate > closedThreshold)); + m.OpenJobs = jobList_OpenJobs; + m.LongRunningJobs = jobList_LongRunning; // UI Extensions UIExtensions.ExecuteExtensions(this.ControllerContext, m); diff --git a/Disco.Web/Disco.Web.csproj b/Disco.Web/Disco.Web.csproj index 70525e15..7e0446c4 100644 --- a/Disco.Web/Disco.Web.csproj +++ b/Disco.Web/Disco.Web.csproj @@ -83,6 +83,18 @@ + + ..\packages\Rx-Core.2.1.30214.0\lib\Net45\System.Reactive.Core.dll + + + ..\packages\Rx-Interfaces.2.1.30214.0\lib\Net45\System.Reactive.Interfaces.dll + + + ..\packages\Rx-Linq.2.1.30214.0\lib\Net45\System.Reactive.Linq.dll + + + ..\packages\Rx-PlatformServices.2.1.30214.0\lib\Net45\System.Reactive.PlatformServices.dll + @@ -1784,7 +1796,6 @@ - @@ -1838,7 +1849,7 @@ False - + diff --git a/Disco.Web/Properties/AssemblyInfo.cs b/Disco.Web/Properties/AssemblyInfo.cs index 8bed00f1..1a21f026 100644 --- a/Disco.Web/Properties/AssemblyInfo.cs +++ b/Disco.Web/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ using System.Runtime.InteropServices; // // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: -[assembly: AssemblyVersion("1.2.0405.1309")] -[assembly: AssemblyFileVersion("1.2.0405.1309")] +[assembly: AssemblyVersion("1.2.0411.1833")] +[assembly: AssemblyFileVersion("1.2.0411.1833")] diff --git a/Disco.Web/T4MVC.cs b/Disco.Web/T4MVC.cs index eb6d94dd..3a64bf2d 100644 --- a/Disco.Web/T4MVC.cs +++ b/Disco.Web/T4MVC.cs @@ -126,20 +126,6 @@ public class T4MVC_System_Web_Mvc_ActionResult : System.Web.Mvc.ActionResult, IT namespace Links { - [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode] - public static class scripts { - private const string URLPATH = "~/scripts"; - public static string Url() { return T4MVCHelpers.ProcessVirtualPath(URLPATH); } - public static string Url(string fileName) { return T4MVCHelpers.ProcessVirtualPath(URLPATH + "/" + fileName); } - [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode] - public static class tinymce { - private const string URLPATH = "~/scripts/tinymce"; - public static string Url() { return T4MVCHelpers.ProcessVirtualPath(URLPATH); } - public static string Url(string fileName) { return T4MVCHelpers.ProcessVirtualPath(URLPATH + "/" + fileName); } - } - - } - [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode] public static class ClientSource { private const string URLPATH = "~/ClientSource"; @@ -5647,6 +5633,12 @@ namespace Disco.Web.Areas.API.Controllers } [NonAction] [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode] + public virtual System.Web.Mvc.ActionResult Comment() + { + return new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.Comment); + } + [NonAction] + [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode] public virtual System.Web.Mvc.ActionResult CommentPost() { return new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.CommentPost); @@ -5791,6 +5783,7 @@ namespace Disco.Web.Areas.API.Controllers public readonly string Delete = "Delete"; public readonly string ConvertHWarToHNWar = "ConvertHWarToHNWar"; public readonly string Comments = "Comments"; + public readonly string Comment = "Comment"; public readonly string CommentPost = "CommentPost"; public readonly string CommentRemove = "CommentRemove"; public readonly string AttachmentDownload = "AttachmentDownload"; @@ -5862,6 +5855,7 @@ namespace Disco.Web.Areas.API.Controllers public const string Delete = "Delete"; public const string ConvertHWarToHNWar = "ConvertHWarToHNWar"; public const string Comments = "Comments"; + public const string Comment = "Comment"; public const string CommentPost = "CommentPost"; public const string CommentRemove = "CommentRemove"; public const string AttachmentDownload = "AttachmentDownload"; @@ -6394,6 +6388,14 @@ namespace Disco.Web.Areas.API.Controllers { public readonly string id = "id"; } + static readonly ActionParamsClass_Comment s_params_Comment = new ActionParamsClass_Comment(); + [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode] + public ActionParamsClass_Comment CommentParams { get { return s_params_Comment; } } + [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode] + public class ActionParamsClass_Comment + { + public readonly string id = "id"; + } static readonly ActionParamsClass_CommentPost s_params_CommentPost = new ActionParamsClass_CommentPost(); [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode] public ActionParamsClass_CommentPost CommentPostParams { get { return s_params_CommentPost; } } @@ -7143,6 +7145,16 @@ namespace Disco.Web.Areas.API.Controllers return callInfo; } + partial void CommentOverride(T4MVC_System_Web_Mvc_ActionResult callInfo, int id); + + public override System.Web.Mvc.ActionResult Comment(int id) + { + var callInfo = new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.Comment); + ModelUnbinderHelpers.AddRouteValues(callInfo.RouteValueDictionary, "id", id); + CommentOverride(callInfo, id); + return callInfo; + } + partial void CommentPostOverride(T4MVC_System_Web_Mvc_ActionResult callInfo, int id, string comment); public override System.Web.Mvc.ActionResult CommentPost(int id, string comment) diff --git a/Disco.Web/Views/Job/JobParts/Components.generated.cs b/Disco.Web/Views/Job/JobParts/Components.generated.cs index a5bdb6c0..73e9aa52 100644 --- a/Disco.Web/Views/Job/JobParts/Components.generated.cs +++ b/Disco.Web/Views/Job/JobParts/Components.generated.cs @@ -2,7 +2,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.17929 +// Runtime Version:4.0.30319.18033 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -31,9 +31,9 @@ namespace Disco.Web.Views.Job.JobParts using Disco.Web; using Disco.Web.Extensions; - [System.CodeDom.Compiler.GeneratedCodeAttribute("RazorGenerator", "1.5.0.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("RazorGenerator", "1.5.4.0")] [System.Web.WebPages.PageVirtualPathAttribute("~/Views/Job/JobParts/Components.cshtml")] - public class Components : System.Web.Mvc.WebViewPage + public partial class Components : System.Web.Mvc.WebViewPage { public Components() { diff --git a/Disco.Web/Views/Job/JobParts/Resources.cshtml b/Disco.Web/Views/Job/JobParts/Resources.cshtml index 7debc24b..9ee80fb6 100644 --- a/Disco.Web/Views/Job/JobParts/Resources.cshtml +++ b/Disco.Web/Views/Job/JobParts/Resources.cshtml @@ -2,6 +2,7 @@ @{ Html.BundleDeferred("~/Style/Shadowbox"); Html.BundleDeferred("~/ClientScripts/Modules/Shadowbox"); + Html.BundleDeferred("~/ClientScripts/Modules/jQuery-SignalR"); } @@ -21,6 +22,7 @@ diff --git a/Disco.Web/Views/Job/JobParts/Resources.generated.cs b/Disco.Web/Views/Job/JobParts/Resources.generated.cs index 3b9a3b76..e8fd4d4c 100644 --- a/Disco.Web/Views/Job/JobParts/Resources.generated.cs +++ b/Disco.Web/Views/Job/JobParts/Resources.generated.cs @@ -2,7 +2,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.17929 +// Runtime Version:4.0.30319.18033 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -31,9 +31,9 @@ namespace Disco.Web.Views.Job.JobParts using Disco.Web; using Disco.Web.Extensions; - [System.CodeDom.Compiler.GeneratedCodeAttribute("RazorGenerator", "1.5.0.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("RazorGenerator", "1.5.4.0")] [System.Web.WebPages.PageVirtualPathAttribute("~/Views/Job/JobParts/Resources.cshtml")] - public class Resources : System.Web.Mvc.WebViewPage + public partial class Resources : System.Web.Mvc.WebViewPage { public Resources() { @@ -45,6 +45,7 @@ namespace Disco.Web.Views.Job.JobParts Html.BundleDeferred("~/Style/Shadowbox"); Html.BundleDeferred("~/ClientScripts/Modules/Shadowbox"); + Html.BundleDeferred("~/ClientScripts/Modules/jQuery-SignalR"); #line default @@ -64,13 +65,13 @@ WriteLiteral(" class=\"commentOutput\""); WriteLiteral(">\r\n"); - #line 10 "..\..\Views\Job\JobParts\Resources.cshtml" + #line 11 "..\..\Views\Job\JobParts\Resources.cshtml" #line default #line hidden - #line 10 "..\..\Views\Job\JobParts\Resources.cshtml" + #line 11 "..\..\Views\Job\JobParts\Resources.cshtml" foreach (var jl in Model.Job.JobLogs.OrderBy(m => m.Timestamp)) { @@ -82,7 +83,7 @@ WriteLiteral(" "); - #line 13 "..\..\Views\Job\JobParts\Resources.cshtml" + #line 14 "..\..\Views\Job\JobParts\Resources.cshtml" Write(jl.TechUser.ToString()); @@ -111,20 +112,20 @@ WriteLiteral(">(jl.Timestamp.ToFullDateTime() + #line 14 "..\..\Views\Job\JobParts\Resources.cshtml" + , Tuple.Create(Tuple.Create("", 616), Tuple.Create(jl.Timestamp.ToFullDateTime() #line default #line hidden -, 548), false) +, 616), false) ); WriteLiteral(">"); - #line 13 "..\..\Views\Job\JobParts\Resources.cshtml" + #line 14 "..\..\Views\Job\JobParts\Resources.cshtml" Write(jl.Timestamp.ToFuzzy()); @@ -137,7 +138,7 @@ WriteLiteral(" class=\"comment\""); WriteLiteral(">"); - #line 14 "..\..\Views\Job\JobParts\Resources.cshtml" + #line 15 "..\..\Views\Job\JobParts\Resources.cshtml" Write(jl.Comments.ToMultilineJobRefString()); @@ -146,7 +147,7 @@ WriteLiteral(">"); WriteLiteral("\r\n \r\n"); - #line 16 "..\..\Views\Job\JobParts\Resources.cshtml" + #line 17 "..\..\Views\Job\JobParts\Resources.cshtml" } @@ -170,33 +171,41 @@ WriteLiteral(">\r\n \r\n \r\n $(function () {\r\n $Comments = $(\'#Comments\'" + -");\r\n $CommentOutput = $Comments.find(\'.commentOutput\');\r\n " + -" $CommentOutputContainer = $Comments.find(\'.commentOutputContaine" + -"r\');\r\n $CommentInput = $Comments.find(\'textarea.commentInput\'" + -");\r\n\r\n window.setTimeout(function () {\r\n " + -" $CommentOutput[0].scrollTop = $CommentOutput[0].scrollHeight; // Scroll to Bo" + -"ttom\r\n }, 0);\r\n $(\'#jobDetailTabs\').on(\'ta" + -"bsactivate\', function (event, ui) {\r\n if (ui.newPanel && " + -"ui.newPanel.is(\'#jobDetailTab-Resources\')) {\r\n $Comme" + -"ntOutput[0].scrollTop = $CommentOutput[0].scrollHeight; // Scroll to Bottom\r\n " + -" }\r\n });\r\n\r\n $Comments" + -".find(\'.commentInputPost\').click(postComment);\r\n $CommentInpu" + -"t.keypress(function (e) {\r\n if (e.which == 13 && !e.shift" + -"Key) {\r\n postComment();\r\n " + -"return false;\r\n }\r\n });\r\n " + -" $CommentOutput.find(\'span.remove\').click(removePost);\r\n\r\n " + -" $dialogRemoveLog = $(\'#dialogRemoveLog\');\r\n $dialogRemove" + -"Log.dialog({\r\n resizable: false,\r\n " + -" height: 140,\r\n modal: true,\r\n aut" + -"oOpen: false\r\n });\r\n\r\n function postCommen" + -"t() {\r\n var comment = $CommentInput.val();\r\n " + -" if (comment != \'\') {\r\n var data = { commen" + -"t: comment }\r\n $.ajax({\r\n " + -" url: \'"); +WriteLiteral(">\r\n $(function () {\r\n var jobId = parseInt(\'"); - #line 60 "..\..\Views\Job\JobParts\Resources.cshtml" + #line 25 "..\..\Views\Job\JobParts\Resources.cshtml" + Write(Model.Job.Id); + + + #line default + #line hidden +WriteLiteral("\');\r\n $Comments = $(\'#Comments\');\r\n $Commen" + +"tOutput = $Comments.find(\'.commentOutput\');\r\n $CommentOutputC" + +"ontainer = $Comments.find(\'.commentOutputContainer\');\r\n $Comm" + +"entInput = $Comments.find(\'textarea.commentInput\');\r\n\r\n windo" + +"w.setTimeout(function () {\r\n $CommentOutput[0].scrollTop " + +"= $CommentOutput[0].scrollHeight; // Scroll to Bottom\r\n }, 0)" + +";\r\n $(\'#jobDetailTabs\').on(\'tabsactivate\', function (event, u" + +"i) {\r\n if (ui.newPanel && ui.newPanel.is(\'#jobDetailTab-R" + +"esources\')) {\r\n $CommentOutput[0].scrollTop = $Commen" + +"tOutput[0].scrollHeight; // Scroll to Bottom\r\n }\r\n " + +" });\r\n\r\n $Comments.find(\'.commentInputPost\').clic" + +"k(postComment);\r\n $CommentInput.keypress(function (e) {\r\n " + +" if (e.which == 13 && !e.shiftKey) {\r\n " + +" postComment();\r\n return false;\r\n " + +" }\r\n });\r\n $CommentOutput.find(\'spa" + +"n.remove\').click(removePost);\r\n\r\n $dialogRemoveLog = $(\'#dial" + +"ogRemoveLog\');\r\n $dialogRemoveLog.dialog({\r\n " + +" resizable: false,\r\n height: 140,\r\n " + +" modal: true,\r\n autoOpen: false\r\n " + +" });\r\n\r\n function postComment() {\r\n " + +"var comment = $CommentInput.val();\r\n if (comment != \'\') {" + +"\r\n var data = { comment: comment }\r\n " + +" $.ajax({\r\n url: \'"); + + + #line 62 "..\..\Views\Job\JobParts\Resources.cshtml" Write(Url.Action(MVC.API.Job.CommentPost(Model.Job.Id, null))); @@ -205,27 +214,28 @@ WriteLiteral(">\r\n $(function () {\r\n $Comme WriteLiteral("\',\r\n dataType: \'json\',\r\n " + " data: data,\r\n success: function (d) {\r\n " + " if (d.Result == \'OK\') {\r\n " + -" addComment(d.Comment, false);\r\n " + -" $CommentInput.val(\'\').attr(\'disabled\', false).focus();\r\n " + -" } else {\r\n alert(\'Una" + -"ble to post comment: \' + d.Result);\r\n $Co" + -"mmentInput.attr(\'disabled\', false);\r\n }\r\n " + -" },\r\n error: function " + -"(jqXHR, textStatus, errorThrown) {\r\n alert(\'U" + -"nable to post comment: \' + textStatus);\r\n $Co" + -"mmentInput.attr(\'disabled\', false);\r\n }\r\n " + -" });\r\n }\r\n }\r\n " + -" function removePost() {\r\n $this = $(this);" + -"\r\n var data = { id: $this.closest(\'div\').attr(\'data-logid" + -"\') };\r\n\r\n $dialogRemoveLog.dialog(\"enable\");\r\n " + -" $dialogRemoveLog.dialog(\'option\', \'buttons\', {\r\n " + -" \"Remove\": function () {\r\n $dialogRemoveL" + -"og.dialog(\"disable\");\r\n $dialogRemoveLog.dialog(\"" + -"option\", \"buttons\", null);\r\n $.ajax({\r\n " + -" url: \'"); +" // Should be added via Repository Notifications\r\n " + +" // addComment(d.Comment, false);\r\n " + +" $CommentInput.val(\'\').attr(\'disabled\', false).focus();\r\n " + +" } else {\r\n " + +" alert(\'Unable to post comment: \' + d.Result);\r\n " + +" $CommentInput.attr(\'disabled\', false);\r\n " + +" }\r\n },\r\n erro" + +"r: function (jqXHR, textStatus, errorThrown) {\r\n " + +" alert(\'Unable to post comment: \' + textStatus);\r\n " + +" $CommentInput.attr(\'disabled\', false);\r\n " + +" }\r\n });\r\n }\r\n " + +" }\r\n function removePost() {\r\n $thi" + +"s = $(this);\r\n var data = { id: $this.closest(\'div\').attr" + +"(\'data-logid\') };\r\n\r\n $dialogRemoveLog.dialog(\"enable\");\r" + +"\n $dialogRemoveLog.dialog(\'option\', \'buttons\', {\r\n " + +" \"Remove\": function () {\r\n $d" + +"ialogRemoveLog.dialog(\"disable\");\r\n $dialogRemove" + +"Log.dialog(\"option\", \"buttons\", null);\r\n $.ajax({" + +"\r\n url: \'"); - #line 89 "..\..\Views\Job\JobParts\Resources.cshtml" + #line 92 "..\..\Views\Job\JobParts\Resources.cshtml" Write(Url.Action(MVC.API.Job.CommentRemove())); @@ -234,33 +244,57 @@ WriteLiteral("\',\r\n dataType: \'json\',\r\n WriteLiteral("\',\r\n dataType: \'json\',\r\n " + " data: data,\r\n success: function" + " (d) {\r\n if (d == \'OK\') {\r\n " + -" $this.closest(\'div\').slideUp(300).delay(300).queue" + -"(function () {\r\n $(this).remove()" + -";\r\n });\r\n " + -" } else {\r\n alert(\'Unable " + -"to remove comment: \' + d);\r\n }\r\n " + -" $dialogRemoveLog.dialog(\"close\");\r\n " + -" },\r\n error: function (j" + -"qXHR, textStatus, errorThrown) {\r\n alert(" + -"\'Unable to remove comment: \' + textStatus);\r\n " + -" $dialogRemoveLog.dialog(\"close\");\r\n }\r\n " + -" });\r\n },\r\n " + -" \"Cancel\": function () {\r\n $dialog" + -"RemoveLog.dialog(\"close\");\r\n }\r\n " + -" });\r\n\r\n $dialogRemoveLog.dialog(\'open\');\r\n\r\n " + -" return false;\r\n }\r\n function" + -" addComment(c, quick) {\r\n var e = $(\'
\');\r\n e.attr(\'data-logid\', c.Id);\r\n " + -" e.find(\'.author\').text(c.Author);\r\n e.find(\'.tim" + -"estamp\').text(c.TimestampFuzzy).attr(\'title\', c.TimestampFull);\r\n " + -" e.find(\'.remove\').click(removePost);\r\n var eComm" + -"ent = e.find(\'.comment\').text(c.Comments);\r\n var commentH" + -"tml = eComment.text().replace(/\\r\\n|\\r|\\n/g, \'
\');\r\n " + -" commentHtml = commentHtml.replace(/\\#(\\d+)/g, \'#$1'); $CommentOutput.animate({ scrollTop: $CommentOutput[0].scrollHeight }, 250) } } + + // Sign up for Live Events + function liveMessageRecieved(d) { + if (d) { + switch (d.EventType) { + case 0: // Added + loadLiveComment(d.EntityKey[0]); + break; + case 1: // Removed + $CommentOutput.children('div[data-logid=""' + d.EntityKey[0] + '""]').slideUp(300).delay(300).queue(function () { + $(this).remove(); + }); + break; + } + } + } + var liveMessagesConnection = $.connection('"); + + + #line 172 "..\..\Views\Job\JobParts\Resources.cshtml" + Write(Url.Content("~/API/Repository/Notifications")); + + + #line default + #line hidden +WriteLiteral(@"') + liveMessagesConnection.received(liveMessageRecieved); + liveMessagesConnection.error(function (e) { + alert('Error: ' + JSON.stringify(e)); + }); + liveMessagesConnection.start(function () { + liveMessagesConnection.send('/addToGroups:JobLog'); + }); + }); @@ -292,13 +360,13 @@ WriteLiteral(" class=\"attachmentOutput\""); WriteLiteral(">\r\n"); - #line 142 "..\..\Views\Job\JobParts\Resources.cshtml" + #line 186 "..\..\Views\Job\JobParts\Resources.cshtml" #line default #line hidden - #line 142 "..\..\Views\Job\JobParts\Resources.cshtml" + #line 186 "..\..\Views\Job\JobParts\Resources.cshtml" foreach (var ja in Model.Job.JobAttachments) { @@ -307,20 +375,20 @@ WriteLiteral(">\r\n"); #line hidden WriteLiteral(" (Url.Action(MVC.API.Job.AttachmentDownload(ja.Id)) + #line 188 "..\..\Views\Job\JobParts\Resources.cshtml" +, Tuple.Create(Tuple.Create("", 9912), Tuple.Create(Url.Action(MVC.API.Job.AttachmentDownload(ja.Id)) #line default #line hidden -, 7544), false) +, 9912), false) ); WriteLiteral(" data-attachmentid=\""); - #line 144 "..\..\Views\Job\JobParts\Resources.cshtml" + #line 188 "..\..\Views\Job\JobParts\Resources.cshtml" Write(ja.Id); @@ -331,7 +399,7 @@ WriteLiteral("\""); WriteLiteral(" data-mimetype=\""); - #line 144 "..\..\Views\Job\JobParts\Resources.cshtml" + #line 188 "..\..\Views\Job\JobParts\Resources.cshtml" Write(ja.MimeType); @@ -343,68 +411,68 @@ WriteLiteral(">\r\n (ja.Filename + #line 189 "..\..\Views\Job\JobParts\Resources.cshtml" +, Tuple.Create(Tuple.Create("", 10072), Tuple.Create(ja.Filename #line default #line hidden -, 7704), false) +, 10072), false) ); WriteLiteral(">\r\n (Url.Action(MVC.API.Job.AttachmentThumbnail(ja.Id)) + #line 190 "..\..\Views\Job\JobParts\Resources.cshtml" +, Tuple.Create(Tuple.Create("", 10153), Tuple.Create(Url.Action(MVC.API.Job.AttachmentThumbnail(ja.Id)) #line default #line hidden -, 7785), false) +, 10153), false) ); WriteLiteral(" />\r\n (ja.Comments + #line 191 "..\..\Views\Job\JobParts\Resources.cshtml" +, Tuple.Create(Tuple.Create("", 10273), Tuple.Create(ja.Comments #line default #line hidden -, 7905), false) +, 10273), false) ); WriteLiteral(">\r\n"); - #line 148 "..\..\Views\Job\JobParts\Resources.cshtml" + #line 192 "..\..\Views\Job\JobParts\Resources.cshtml" #line default #line hidden - #line 148 "..\..\Views\Job\JobParts\Resources.cshtml" + #line 192 "..\..\Views\Job\JobParts\Resources.cshtml" if (!string.IsNullOrEmpty(ja.DocumentTemplateId)) { #line default #line hidden - #line 149 "..\..\Views\Job\JobParts\Resources.cshtml" + #line 193 "..\..\Views\Job\JobParts\Resources.cshtml" Write(ja.DocumentTemplate.Description); #line default #line hidden - #line 149 "..\..\Views\Job\JobParts\Resources.cshtml" + #line 193 "..\..\Views\Job\JobParts\Resources.cshtml" } else { @@ -412,14 +480,14 @@ WriteLiteral(">\r\n"); #line default #line hidden - #line 151 "..\..\Views\Job\JobParts\Resources.cshtml" + #line 195 "..\..\Views\Job\JobParts\Resources.cshtml" Write(ja.Comments); #line default #line hidden - #line 151 "..\..\Views\Job\JobParts\Resources.cshtml" + #line 195 "..\..\Views\Job\JobParts\Resources.cshtml" } #line default @@ -431,7 +499,7 @@ WriteLiteral(" class=\"author\""); WriteLiteral(">"); - #line 152 "..\..\Views\Job\JobParts\Resources.cshtml" + #line 196 "..\..\Views\Job\JobParts\Resources.cshtml" Write(ja.TechUser.ToString()); @@ -445,20 +513,20 @@ WriteLiteral(">(ja.Timestamp.ToFullDateTime() + #line 196 "..\..\Views\Job\JobParts\Resources.cshtml" + , Tuple.Create(Tuple.Create("", 10663), Tuple.Create(ja.Timestamp.ToFullDateTime() #line default #line hidden -, 8295), false) +, 10663), false) ); WriteLiteral(">"); - #line 152 "..\..\Views\Job\JobParts\Resources.cshtml" + #line 196 "..\..\Views\Job\JobParts\Resources.cshtml" Write(ja.Timestamp.ToFuzzy()); @@ -467,7 +535,7 @@ WriteLiteral(">"); WriteLiteral("\r\n \r\n"); - #line 154 "..\..\Views\Job\JobParts\Resources.cshtml" + #line 198 "..\..\Views\Job\JobParts\Resources.cshtml" } @@ -530,7 +598,7 @@ WriteLiteral(@"> '"); - #line 197 "..\..\Views\Job\JobParts\Resources.cshtml" + #line 241 "..\..\Views\Job\JobParts\Resources.cshtml" Write(Links.ClientBin.Disco_Silverlight_AttachmentUpload_xap); @@ -551,7 +619,7 @@ WriteLiteral(@"', 'UploadUrl="); - #line 209 "..\..\Views\Job\JobParts\Resources.cshtml" + #line 253 "..\..\Views\Job\JobParts\Resources.cshtml" Write(Url.Action(MVC.API.Job.AttachmentUpload(Model.Job.Id, null))); @@ -583,7 +651,7 @@ WriteLiteral(@"' url: '"); - #line 232 "..\..\Views\Job\JobParts\Resources.cshtml" + #line 276 "..\..\Views\Job\JobParts\Resources.cshtml" Write(Url.Action(MVC.API.Job.Attachment())); @@ -601,7 +669,7 @@ WriteLiteral(@"', e.attr('data-attachmentid', a.Id).attr('data-mimetype', a.MimeType).attr('href', '"); - #line 241 "..\..\Views\Job\JobParts\Resources.cshtml" + #line 285 "..\..\Views\Job\JobParts\Resources.cshtml" Write(Url.Action(MVC.API.Job.AttachmentDownload())); @@ -611,7 +679,7 @@ WriteLiteral("/\' + a.Id);\r\n e.find(\'.icon "\'"); - #line 242 "..\..\Views\Job\JobParts\Resources.cshtml" + #line 286 "..\..\Views\Job\JobParts\Resources.cshtml" Write(Url.Action(MVC.API.Job.AttachmentThumbnail())); @@ -644,7 +712,7 @@ WriteLiteral("/\' + a.Id);\r\n e.find(\'.comm " $.ajax({\r\n url: \'"); - #line 275 "..\..\Views\Job\JobParts\Resources.cshtml" + #line 319 "..\..\Views\Job\JobParts\Resources.cshtml" Write(Url.Action(MVC.API.Job.AttachmentRemove())); diff --git a/Disco.Web/Web.config b/Disco.Web/Web.config index e4baa8bb..d837532c 100644 --- a/Disco.Web/Web.config +++ b/Disco.Web/Web.config @@ -1,89 +1,93 @@ - + + -
+
- - - - - - - - - - + + + + + + + + - + - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - + + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + + + + - + - - + + \ No newline at end of file diff --git a/Disco.Web/packages.config b/Disco.Web/packages.config index 65a6d30e..afde9aae 100644 --- a/Disco.Web/packages.config +++ b/Disco.Web/packages.config @@ -23,6 +23,11 @@ + + + + + diff --git a/Disco.sln b/Disco.sln index 6f823c64..4af9d51e 100644 --- a/Disco.sln +++ b/Disco.sln @@ -121,12 +121,12 @@ Global HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution - BuildVersion_UseGlobalSettings = False - BuildVersion_DetectChanges = True - BuildVersion_UpdateFileVersion = False - BuildVersion_UpdateAssemblyVersion = False - BuildVersion_BuildVersioningStyle = None.None.None.None - BuildVersion_StartDate = 2001/1/1 BuildVersion_BuildAction = Both + BuildVersion_StartDate = 2001/1/1 + BuildVersion_BuildVersioningStyle = None.None.None.None + BuildVersion_UpdateAssemblyVersion = False + BuildVersion_UpdateFileVersion = False + BuildVersion_DetectChanges = True + BuildVersion_UseGlobalSettings = False EndGlobalSection EndGlobal