diff --git a/Core/Resgrid.Localization/Areas/User/CustomStatuses/CustomStatuses.en.resx b/Core/Resgrid.Localization/Areas/User/CustomStatuses/CustomStatuses.en.resx
index 8512684d..b76e04d0 100644
--- a/Core/Resgrid.Localization/Areas/User/CustomStatuses/CustomStatuses.en.resx
+++ b/Core/Resgrid.Localization/Areas/User/CustomStatuses/CustomStatuses.en.resx
@@ -120,6 +120,12 @@
Add Option (Button)
+
+ Available
+
+
+ Base Type
+
Button Background Color
@@ -150,6 +156,9 @@
Changes to custom statues can take up to 30 minutes to fully propagate though the Resgrid network. Mobile app users are encouraged to manually resync (Settings->Advanced Settings->ReSync).
+
+ Cleared
+
Custom Personnel Statuses (Actions)
@@ -180,6 +189,9 @@
Detail Type
+
+ Dispatched
+
Edit Custom State Detail
@@ -195,6 +207,12 @@
Edit Staffing Levels
+
+ Investigating
+
+
+ Made Contact
+
New Custom Statuses
@@ -204,12 +222,21 @@
No Detail
+
+ None
+
No Note
Note Type
+
+ Not Responding
+
+
+ On Scene
+
Personnel Actions
@@ -225,6 +252,12 @@
Require GPS
+
+ Responding
+
+
+ Returning
+
Set Custom Staffing Levels
@@ -234,12 +267,18 @@
Staffing Levels
+
+ Staging
+
Stations
Text Color
+
+ Unavailable
+
Unit Status
diff --git a/Core/Resgrid.Localization/Areas/User/Department/DepartmentTypes.cs b/Core/Resgrid.Localization/Areas/User/Department/DepartmentTypes.cs
new file mode 100644
index 00000000..92545e9c
--- /dev/null
+++ b/Core/Resgrid.Localization/Areas/User/Department/DepartmentTypes.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Resgrid.Localization.Areas.User.Department
+{
+ public class DepartmentTypes
+ {
+ }
+}
diff --git a/Core/Resgrid.Localization/Areas/User/Department/DepartmentTypes.en.resx b/Core/Resgrid.Localization/Areas/User/Department/DepartmentTypes.en.resx
new file mode 100644
index 00000000..e09cf816
--- /dev/null
+++ b/Core/Resgrid.Localization/Areas/User/Department/DepartmentTypes.en.resx
@@ -0,0 +1,282 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Add a new call type
+
+
+ Call Priorities
+
+
+ Alert Sound
+
+
+ Is Default
+
+
+ You have custom Call Priorities enabled. To revert back to the default ones you will need to delete all the custom ones. Editing an existing call priority will update it for all calls that used that priority.
+
+
+ You’re currently using the Default call priorities. If you add any custom call priorities you will no longer be able to utilize the default call priorities (including their sounds). If you don’t specify a custom sound for your new call priorities, they will default to the High call push sound and alert sound. Users on the v5 or lower version of the Responder app and v2 or lower of the Unit app don't support custom call priorities. If you have any users on those versions of the apps we recommend not creating custom call priories.
+
+
+ Call Type Map Icon
+
+
+ Map Icon
+
+
+ Call Type Name
+
+
+ Call Type Name
+
+
+ Name of the Type
+
+
+ Call Types
+
+
+ Certification Name
+
+
+ Name of the Certification
+
+
+ WARNING: This will permanently delete this Certification Type. Are you sure you want to delete the call type
+
+
+ Certification Types
+
+
+ Type Name
+
+
+ Default Unit Actions
+
+
+ WARNING: This will permanently delete this call type. Are you sure you want to delete the call type
+
+
+ WARNING: This will permanently delete this Document Category. Are you sure you want to delete the call category
+
+
+ WARNING: This will permanently delete this note type. Are you sure you want to delete the note type
+
+
+ WARNING: This will permanently delete this unit type. Are you sure you want to delete the unit type
+
+
+ Department Types
+
+
+ Category Name
+
+
+ Name of the new Document Category (Type)
+
+
+ Document Categories
+
+
+ Edit Call Priority
+
+
+ Edit Call Type
+
+
+ Edit Unit Type
+
+
+ Manage List Ordering
+
+
+ Add a new call priority
+
+
+ New Call Priority
+
+
+ New Call Type
+
+
+ New Certification Type
+
+
+ New Document Category
+
+
+ New Document Category
+
+
+ New Note Type
+
+
+ New Unit Type
+
+
+ Name of the Note Type (Category)
+
+
+ Note Types
+
+
+ Note Type
+
+
+ Priority Color
+
+
+ Is Default
+
+
+ Priority Name
+
+
+ Name of the Priority
+
+
+ Dispatch Personnel
+
+
+ Sort Order
+
+
+ Alert Sound
+
+
+ Dispatch Units
+
+
+ Actions (Custom Unit Statuses)
+
+
+ Map Icon
+
+
+ Unit Type Name
+
+
+ Name of the Unit Type
+
+
+ Unit Types
+
+
\ No newline at end of file
diff --git a/Core/Resgrid.Localization/Areas/User/Department/DepartmentTypes.es.resx b/Core/Resgrid.Localization/Areas/User/Department/DepartmentTypes.es.resx
new file mode 100644
index 00000000..4fdb1b6a
--- /dev/null
+++ b/Core/Resgrid.Localization/Areas/User/Department/DepartmentTypes.es.resx
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 1.3
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/Core/Resgrid.Localization/Areas/User/Documents/Documents.cs b/Core/Resgrid.Localization/Areas/User/Documents/Documents.cs
new file mode 100644
index 00000000..9c3c1dc3
--- /dev/null
+++ b/Core/Resgrid.Localization/Areas/User/Documents/Documents.cs
@@ -0,0 +1,10 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Resgrid.Localization.Areas.User.Documents
+{
+ public class Documents
+ {
+ }
+}
diff --git a/Core/Resgrid.Localization/Areas/User/Documents/Documents.en.resx b/Core/Resgrid.Localization/Areas/User/Documents/Documents.en.resx
new file mode 100644
index 00000000..e957f4f6
--- /dev/null
+++ b/Core/Resgrid.Localization/Areas/User/Documents/Documents.en.resx
@@ -0,0 +1,165 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Admins Only
+
+
+ Category
+
+
+ Delete
+
+
+ WARNING: This will permanently delete this document. Are you sure you want to delete the document
+
+
+ Edit Document
+
+
+ Everyone
+
+
+ Images
+
+
+ New Document
+
+
+ Name of the Document
+
+
+ Presentations
+
+
+ Spreadsheets
+
+
+ Upload
+
+
+ Not all files types are allowed! File must be less then 10MB in size and must have one of these extensions (.png, .jpg, .jpeg, .gif, .pdf, .doc, .docx, .txt, .ppt, .pptx, .pps, .ppsx, .odt, .xls, .xlsx, .mp4, .mp3)
+
+
+ Viewable By
+
+
+ View Document
+
+
\ No newline at end of file
diff --git a/Core/Resgrid.Localization/Areas/User/Documents/Documents.es.resx b/Core/Resgrid.Localization/Areas/User/Documents/Documents.es.resx
new file mode 100644
index 00000000..1af7de15
--- /dev/null
+++ b/Core/Resgrid.Localization/Areas/User/Documents/Documents.es.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/Core/Resgrid.Localization/Areas/User/Logs/Logs.en.resx b/Core/Resgrid.Localization/Areas/User/Logs/Logs.en.resx
index ce571174..a8ee1424 100644
--- a/Core/Resgrid.Localization/Areas/User/Logs/Logs.en.resx
+++ b/Core/Resgrid.Localization/Areas/User/Logs/Logs.en.resx
@@ -129,12 +129,18 @@
Body Location is required.
+
+ Call Address (Location)
+
Call Name
Call Priority
+
+ Call Timestamp
+
Case #
@@ -195,6 +201,9 @@
Narrative
+
+ Nature of Call
+
New Call
diff --git a/Core/Resgrid.Localization/Areas/User/Notes/Note.en.resx b/Core/Resgrid.Localization/Areas/User/Notes/Note.en.resx
index de5c8962..8ff5f85d 100644
--- a/Core/Resgrid.Localization/Areas/User/Notes/Note.en.resx
+++ b/Core/Resgrid.Localization/Areas/User/Notes/Note.en.resx
@@ -117,6 +117,9 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+ Admins Only
+
Body
@@ -138,6 +141,9 @@
Edit Note
+
+ Everyone
+
New Note
diff --git a/Core/Resgrid.Localization/Common.en.resx b/Core/Resgrid.Localization/Common.en.resx
index a14630f9..58525661 100644
--- a/Core/Resgrid.Localization/Common.en.resx
+++ b/Core/Resgrid.Localization/Common.en.resx
@@ -240,6 +240,9 @@
Department Settings
+
+ Department Types
+
Description
diff --git a/Core/Resgrid.Model/ActionBaseTypes.cs b/Core/Resgrid.Model/ActionBaseTypes.cs
new file mode 100644
index 00000000..b8672559
--- /dev/null
+++ b/Core/Resgrid.Model/ActionBaseTypes.cs
@@ -0,0 +1,59 @@
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel;
+
+namespace Resgrid.Model
+{
+ ///
+ /// Enumeration of the all the Base System Action Types that a user can perform in the system for both Users and Units
+ ///
+ public enum ActionBaseTypes
+ {
+ [Description("None")]
+ [Display(Name = "None")]
+ None = -1,
+
+ [Description("Available")]
+ [Display(Name = "Available")]
+ Available = 0,
+
+ [Description("Not Responding")]
+ [Display(Name = "Not Responding")]
+ NotResponding = 1,
+
+ [Description("Responding")]
+ [Display(Name = "Responding")]
+ Responding = 2,
+
+ [Description("On Scene")]
+ [Display(Name = "On Scene")]
+ OnScene = 3,
+
+ [Description("Made Contact")]
+ [Display(Name = "Made Contact")]
+ MadeContact = 4,
+
+ [Description("Investigating")]
+ [Display(Name = "Investigating")]
+ Investigating = 5,
+
+ [Description("Dispatched")]
+ [Display(Name = "Dispatched")]
+ Dispatched = 6,
+
+ [Description("Cleared")]
+ [Display(Name = "Cleared")]
+ Cleared = 7,
+
+ [Description("Returning")]
+ [Display(Name = "Returning")]
+ Returning = 8,
+
+ [Description("Staging")]
+ [Display(Name = "Staging")]
+ Staging = 9,
+
+ [Description("Unavailable")]
+ [Display(Name = "Unavailable")]
+ Unavailable = 10
+ }
+}
diff --git a/Core/Resgrid.Model/AuditLogTypes.cs b/Core/Resgrid.Model/AuditLogTypes.cs
index fd4a1a43..4c37d14c 100644
--- a/Core/Resgrid.Model/AuditLogTypes.cs
+++ b/Core/Resgrid.Model/AuditLogTypes.cs
@@ -24,6 +24,35 @@ public enum AuditLogTypes
DeleteDepartmentRequested,
DeleteDepartmentRequestedCancelled,
DeleteStaticShift,
- UpdateStaticShift
+ UpdateStaticShift,
+ // New 7-13-2024
+ CustomStatusAdded,
+ CustomStatusRemoved,
+ CustomStatusUpdated,
+ CustomStatusDetailUpdated,
+ CallTypeAdded,
+ CallTypeEdited,
+ CallTypeRemoved,
+ CallPriorityAdded,
+ CallPriorityEdited,
+ CallPriorityRemoved,
+ UnitTypeAdded,
+ UnitTypeEdited,
+ UnitTypeRemoved,
+ CertificationTypeAdded,
+ CertificationTypeEdited,
+ CertificationTypeRemoved,
+ DocumentCategoryAdded,
+ DocumentCategoryEdited,
+ DocumentCategoryRemoved,
+ DocumentAdded,
+ DocumentEdited,
+ DocumentRemoved,
+ NoteCategoryAdded,
+ NoteCategoryEdited,
+ NoteCategoryRemoved,
+ NoteAdded,
+ NoteEdited,
+ NoteRemoved,
}
}
diff --git a/Core/Resgrid.Model/CustomStateDetail.cs b/Core/Resgrid.Model/CustomStateDetail.cs
index eeab7c89..d1aa9d44 100644
--- a/Core/Resgrid.Model/CustomStateDetail.cs
+++ b/Core/Resgrid.Model/CustomStateDetail.cs
@@ -50,6 +50,12 @@ public class CustomStateDetail : IEntity
[ProtoMember(10)]
public bool IsDeleted { get; set; }
+ [ProtoMember(11)]
+ public int BaseType { get; set; }
+
+ [ProtoMember(12)]
+ public int TTL { get; set; }
+
[NotMapped]
[JsonIgnore]
public object IdValue
diff --git a/Core/Resgrid.Model/CustomStateTypes.cs b/Core/Resgrid.Model/CustomStateTypes.cs
index e38bcf44..d6882952 100644
--- a/Core/Resgrid.Model/CustomStateTypes.cs
+++ b/Core/Resgrid.Model/CustomStateTypes.cs
@@ -3,7 +3,7 @@
public enum CustomStateTypes
{
Personnel = 1,
- Unit = 2,
- Staffing = 3
+ Unit = 2,
+ Staffing = 3
}
-}
\ No newline at end of file
+}
diff --git a/Core/Resgrid.Model/DocumentCategory.cs b/Core/Resgrid.Model/DocumentCategory.cs
new file mode 100644
index 00000000..3d3b56ce
--- /dev/null
+++ b/Core/Resgrid.Model/DocumentCategory.cs
@@ -0,0 +1,50 @@
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace Resgrid.Model
+{
+ [Table("DocumentCategories")]
+ public class DocumentCategory : IEntity
+ {
+ [Key]
+ [Required]
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public string DocumentCategoryId { get; set; }
+
+ [Required]
+ [ForeignKey("Department"), DatabaseGenerated(DatabaseGeneratedOption.None)]
+ public int DepartmentId { get; set; }
+
+ public virtual Department Department { get; set; }
+
+ [Required]
+ public string Name { get; set; }
+
+ public DateTime AddedOn { get; set; }
+
+ public string AddedById { get; set; }
+
+ [NotMapped]
+ [JsonIgnore]
+ public object IdValue
+ {
+ get { return DocumentCategoryId; }
+ set { DocumentCategoryId = (string)value; }
+ }
+
+ [NotMapped]
+ public string TableName => "DocumentCategories";
+
+ [NotMapped]
+ public string IdName => "DocumentCategoryId";
+
+ [NotMapped]
+ public int IdType => 1;
+
+ [NotMapped]
+ public IEnumerable IgnoredProperties => new string[] { "IdValue", "IdType", "TableName", "IdName", "Department" };
+ }
+}
diff --git a/Core/Resgrid.Model/NoteCategory.cs b/Core/Resgrid.Model/NoteCategory.cs
new file mode 100644
index 00000000..722bc70b
--- /dev/null
+++ b/Core/Resgrid.Model/NoteCategory.cs
@@ -0,0 +1,50 @@
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace Resgrid.Model
+{
+ [Table("NoteCategories")]
+ public class NoteCategory : IEntity
+ {
+ [Key]
+ [Required]
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public string NoteCategoryId { get; set; }
+
+ [Required]
+ [ForeignKey("Department"), DatabaseGenerated(DatabaseGeneratedOption.None)]
+ public int DepartmentId { get; set; }
+
+ public virtual Department Department { get; set; }
+
+ [Required]
+ public string Name { get; set; }
+
+ public DateTime AddedOn { get; set; }
+
+ public string AddedById { get; set; }
+
+ [NotMapped]
+ [JsonIgnore]
+ public object IdValue
+ {
+ get { return NoteCategoryId; }
+ set { NoteCategoryId = (string)value; }
+ }
+
+ [NotMapped]
+ public string TableName => "NoteCategories";
+
+ [NotMapped]
+ public string IdName => "NoteCategoryId";
+
+ [NotMapped]
+ public int IdType => 1;
+
+ [NotMapped]
+ public IEnumerable IgnoredProperties => new string[] { "IdValue", "IdType", "TableName", "IdName", "Department" };
+ }
+}
diff --git a/Core/Resgrid.Model/Repositories/IDocumentCategoriesRepository.cs b/Core/Resgrid.Model/Repositories/IDocumentCategoriesRepository.cs
new file mode 100644
index 00000000..066891d4
--- /dev/null
+++ b/Core/Resgrid.Model/Repositories/IDocumentCategoriesRepository.cs
@@ -0,0 +1,11 @@
+namespace Resgrid.Model.Repositories
+{
+ ///
+ /// Interface IDocumentCategoriesRepository
+ /// Implements the
+ ///
+ ///
+ public interface IDocumentCategoriesRepository : IRepository
+ {
+ }
+}
diff --git a/Core/Resgrid.Model/Repositories/INoteCategoriesRepository.cs b/Core/Resgrid.Model/Repositories/INoteCategoriesRepository.cs
new file mode 100644
index 00000000..e48798e1
--- /dev/null
+++ b/Core/Resgrid.Model/Repositories/INoteCategoriesRepository.cs
@@ -0,0 +1,11 @@
+namespace Resgrid.Model.Repositories
+{
+ ///
+ /// Interface INoteCategoriesRepository
+ /// Implements the
+ ///
+ ///
+ public interface INoteCategoriesRepository : IRepository
+ {
+ }
+}
diff --git a/Core/Resgrid.Model/Services/IAuthorizationService.cs b/Core/Resgrid.Model/Services/IAuthorizationService.cs
index 35325068..49179f4b 100644
--- a/Core/Resgrid.Model/Services/IAuthorizationService.cs
+++ b/Core/Resgrid.Model/Services/IAuthorizationService.cs
@@ -274,5 +274,39 @@ public interface IAuthorizationService
Task CanUserAddCallDataAsync(string userId, int callId, int departmentId);
Task CanUserDeleteDepartmentAsync(string userId, int departmentId);
+
+ Task CanUserModifyCustomStatusAsync(string userId, int customStatusId);
+
+ Task CanUserModifyCustomStateDetailAsync(string userId, int customStateDetailId);
+
+ Task CanUserModifyCallTypeAsync(string userId, int callTypeId);
+
+ Task CanUserAddCallTypeAsync(string userId);
+
+ Task CanUserAddCallPriorityAsync(string userId);
+
+ Task CanUserDeleteCallPriorityAsync(string userId, int priorityId);
+
+ Task CanUserEditCallPriorityAsync(string userId, int priorityId);
+
+ Task CanUserAddUnitTypeAsync(string userId);
+
+ Task CanUserEditUnitTypeAsync(string userId, int unitTypeId);
+
+ Task CanUserAddCertificationTypeAsync(string userId);
+
+ Task CanUserAddDocumentTypeAsync(string userId);
+
+ Task CanUserDeleteCertificationTypeAsync(string userId, int certificationTypeId);
+
+ Task CanUserDeleteDocumentTypeAsync(string userId, string documentTypeId);
+
+ Task CanUserAddNoteTypeAsync(string userId);
+
+ Task CanUserDeleteNoteTypeAsync(string userId, string noteTypeId);
+
+ Task CanUserAddNoteAsync(string userId);
+
+ Task CanUserEditNoteAsync(string userId, int noteId);
}
}
diff --git a/Core/Resgrid.Model/Services/ICertificationService.cs b/Core/Resgrid.Model/Services/ICertificationService.cs
index 5e4988a5..5dcdac49 100644
--- a/Core/Resgrid.Model/Services/ICertificationService.cs
+++ b/Core/Resgrid.Model/Services/ICertificationService.cs
@@ -82,5 +82,7 @@ public interface ICertificationService
/// The cancellation token that can be used by other objects or threads to receive notice of cancellation.
/// Task<System.Boolean>.
Task DeleteAllCertificationsForUser(string userId, CancellationToken cancellationToken = default(CancellationToken));
+
+ Task DoesCertificationTypeAlreadyExistAsync(int departmentId, string certificationTypeText);
}
}
diff --git a/Core/Resgrid.Model/Services/IDocumentsService.cs b/Core/Resgrid.Model/Services/IDocumentsService.cs
index 4dfaa1a8..2e03e3ed 100644
--- a/Core/Resgrid.Model/Services/IDocumentsService.cs
+++ b/Core/Resgrid.Model/Services/IDocumentsService.cs
@@ -51,5 +51,15 @@ public interface IDocumentsService
/// The cancellation token that can be used by other objects or threads to receive notice of cancellation.
/// Task<System.Boolean>.
Task DeleteDocumentAsync(Document document, CancellationToken cancellationToken = default(CancellationToken));
+
+ Task SaveDocumentCategoryAsync(DocumentCategory category, CancellationToken cancellationToken = default(CancellationToken));
+
+ Task GetDocumentCategoryByIdAsync(string categoryId);
+
+ Task DeleteDocumentCategoryAsync(DocumentCategory category, CancellationToken cancellationToken = default(CancellationToken));
+
+ Task> GetAllCategoriesByDepartmentIdAsync(int departmentId);
+
+ Task DoesDocumentCategoryAlreadyExistAsync(int departmentId, string documentCategoryText);
}
}
diff --git a/Core/Resgrid.Model/Services/INotesService.cs b/Core/Resgrid.Model/Services/INotesService.cs
index 0bc037ce..1dd0cd21 100644
--- a/Core/Resgrid.Model/Services/INotesService.cs
+++ b/Core/Resgrid.Model/Services/INotesService.cs
@@ -50,5 +50,15 @@ public interface INotesService
/// if set to true [is admin].
/// Task<List<Note>>.
Task> GetNotesForDepartmentFilteredAsync(int departmentId, bool isAdmin);
+
+ Task SaveNoteCategoryAsync(NoteCategory category, CancellationToken cancellationToken = default(CancellationToken));
+
+ Task GetNoteCategoryByIdAsync(string categoryId);
+
+ Task DeleteNoteCategoryAsync(NoteCategory category, CancellationToken cancellationToken = default(CancellationToken));
+
+ Task> GetAllCategoriesByDepartmentIdAsync(int departmentId);
+
+ Task DoesNoteTypeAlreadyExistAsync(int departmentId, string noteTypeText);
}
}
diff --git a/Core/Resgrid.Services/AuthorizationService.cs b/Core/Resgrid.Services/AuthorizationService.cs
index 54939f05..9b5ae967 100644
--- a/Core/Resgrid.Services/AuthorizationService.cs
+++ b/Core/Resgrid.Services/AuthorizationService.cs
@@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Threading.Tasks;
+using Microsoft.VisualBasic;
using Resgrid.Model;
using Resgrid.Model.Services;
@@ -22,12 +23,16 @@ public class AuthorizationService : IAuthorizationService
private readonly ICalendarService _calendarService;
private readonly IProtocolsService _protocolsService;
private readonly IShiftsService _shiftsService;
+ private readonly ICustomStateService _customStateService;
+ private readonly ICertificationService _certificationService;
+ private readonly IDocumentsService _documentsService;
+ private readonly INotesService _notesService;
public AuthorizationService(IDepartmentsService departmentsService, IInvitesService invitesService,
ICallsService callsService, IMessageService messageService, IWorkLogsService workLogsService, ISubscriptionsService subscriptionsService,
IDepartmentGroupsService departmentGroupsService, IPersonnelRolesService personnelRolesService, IUnitsService unitsService,
IPermissionsService permissionsService, ICalendarService calendarService, IProtocolsService protocolsService,
- IShiftsService shiftsService)
+ IShiftsService shiftsService, ICustomStateService customStateService, ICertificationService certificationService, IDocumentsService documentsService, INotesService notesService)
{
_departmentsService = departmentsService;
_invitesService = invitesService;
@@ -42,6 +47,10 @@ public AuthorizationService(IDepartmentsService departmentsService, IInvitesServ
_calendarService = calendarService;
_protocolsService = protocolsService;
_shiftsService = shiftsService;
+ _customStateService = customStateService;
+ _certificationService = certificationService;
+ _documentsService = documentsService;
+ _notesService = notesService;
}
#endregion Private Members and Constructors
@@ -885,5 +894,299 @@ public async Task CanUserDeleteDepartmentAsync(string userId, int departme
return true;
}
+
+ public async Task CanUserModifyCustomStatusAsync(string userId, int customStatusId)
+ {
+ var department = await _departmentsService.GetDepartmentByUserIdAsync(userId);
+ var customState = await _customStateService.GetCustomSateByIdAsync(customStatusId);
+
+ if (department == null || customState == null)
+ return false;
+
+ if (customState.DepartmentId != department.DepartmentId)
+ return false;
+
+ if (department.IsUserAnAdmin(userId))
+ return true;
+
+ return false;
+ }
+
+ public async Task CanUserModifyCustomStateDetailAsync(string userId, int customStateDetailId)
+ {
+ var department = await _departmentsService.GetDepartmentByUserIdAsync(userId);
+ var customStateDetail = await _customStateService.GetCustomDetailByIdAsync(customStateDetailId);
+
+ if (department == null || customStateDetail == null)
+ return false;
+
+ var customState = await _customStateService.GetCustomSateByIdAsync(customStateDetail.CustomStateId);
+
+ if (customState == null)
+ return false;
+
+ if (customState.DepartmentId != department.DepartmentId)
+ return false;
+
+ if (department.IsUserAnAdmin(userId))
+ return true;
+
+ return false;
+ }
+
+ public async Task CanUserModifyCallTypeAsync(string userId, int callTypeId)
+ {
+ var department = await _departmentsService.GetDepartmentByUserIdAsync(userId);
+ var callType = await _callsService.GetCallTypeByIdAsync(callTypeId);
+
+ if (department == null || callType == null)
+ return false;
+
+ if (callType.DepartmentId != department.DepartmentId)
+ return false;
+
+ if (department.IsUserAnAdmin(userId))
+ return true;
+
+ return false;
+ }
+
+ public async Task CanUserAddCallTypeAsync(string userId)
+ {
+ var department = await _departmentsService.GetDepartmentByUserIdAsync(userId);
+
+ if (department == null)
+ return false;
+
+ if (department.IsUserAnAdmin(userId))
+ return true;
+
+ return false;
+ }
+
+ public async Task CanUserAddCallPriorityAsync(string userId)
+ {
+ var department = await _departmentsService.GetDepartmentByUserIdAsync(userId);
+
+ if (department == null)
+ return false;
+
+ if (department.IsUserAnAdmin(userId))
+ return true;
+
+ return false;
+ }
+
+ public async Task CanUserDeleteCallPriorityAsync(string userId, int priorityId)
+ {
+ var department = await _departmentsService.GetDepartmentByUserIdAsync(userId);
+
+ if (department == null)
+ return false;
+
+ var priority = await _callsService.GetCallPrioritiesByIdAsync(department.DepartmentId, priorityId, true);
+
+ if (priority == null)
+ return false;
+
+ if (priority.DepartmentId != department.DepartmentId)
+ return false;
+
+ if (department.IsUserAnAdmin(userId))
+ return true;
+
+ return false;
+ }
+
+ public async Task CanUserEditCallPriorityAsync(string userId, int priorityId)
+ {
+ var department = await _departmentsService.GetDepartmentByUserIdAsync(userId);
+
+ if (department == null)
+ return false;
+
+ var priority = await _callsService.GetCallPrioritiesByIdAsync(department.DepartmentId, priorityId, true);
+
+ if (priority == null)
+ return false;
+
+ if (priority.DepartmentId != department.DepartmentId)
+ return false;
+
+ if (department.IsUserAnAdmin(userId))
+ return true;
+
+ return false;
+ }
+
+ public async Task CanUserAddUnitTypeAsync(string userId)
+ {
+ var department = await _departmentsService.GetDepartmentByUserIdAsync(userId);
+
+ if (department == null)
+ return false;
+
+ if (department.IsUserAnAdmin(userId))
+ return true;
+
+ return false;
+ }
+
+ public async Task CanUserEditUnitTypeAsync(string userId, int unitTypeId)
+ {
+ var department = await _departmentsService.GetDepartmentByUserIdAsync(userId);
+
+ if (department == null)
+ return false;
+
+ var unitType = await _unitsService.GetUnitTypeByIdAsync(unitTypeId);
+
+ if (unitType == null)
+ return false;
+
+ if (unitType.DepartmentId != department.DepartmentId)
+ return false;
+
+ if (department.IsUserAnAdmin(userId))
+ return true;
+
+ return false;
+ }
+
+ public async Task CanUserAddCertificationTypeAsync(string userId)
+ {
+ var department = await _departmentsService.GetDepartmentByUserIdAsync(userId);
+
+ if (department == null)
+ return false;
+
+ if (department.IsUserAnAdmin(userId))
+ return true;
+
+ return false;
+ }
+
+ public async Task CanUserDeleteCertificationTypeAsync(string userId, int certificationTypeId)
+ {
+ var department = await _departmentsService.GetDepartmentByUserIdAsync(userId);
+
+ if (department == null)
+ return false;
+
+ var type = await _certificationService.GetCertificationTypeByIdAsync(certificationTypeId);
+
+ if (type == null)
+ return false;
+
+ if (type.DepartmentId != department.DepartmentId)
+ return false;
+
+ if (department.IsUserAnAdmin(userId))
+ return true;
+
+ return false;
+ }
+
+ public async Task CanUserAddDocumentTypeAsync(string userId)
+ {
+ var department = await _departmentsService.GetDepartmentByUserIdAsync(userId);
+
+ if (department == null)
+ return false;
+
+ if (department.IsUserAnAdmin(userId))
+ return true;
+
+ return false;
+ }
+
+ public async Task CanUserDeleteDocumentTypeAsync(string userId, string documentTypeId)
+ {
+ var department = await _departmentsService.GetDepartmentByUserIdAsync(userId);
+
+ if (department == null)
+ return false;
+
+ var type = await _documentsService.GetDocumentCategoryByIdAsync(documentTypeId);
+
+ if (type == null)
+ return false;
+
+ if (type.DepartmentId != department.DepartmentId)
+ return false;
+
+ if (department.IsUserAnAdmin(userId))
+ return true;
+
+ return false;
+ }
+
+ public async Task CanUserAddNoteTypeAsync(string userId)
+ {
+ var department = await _departmentsService.GetDepartmentByUserIdAsync(userId);
+
+ if (department == null)
+ return false;
+
+ if (department.IsUserAnAdmin(userId))
+ return true;
+
+ return false;
+ }
+
+ public async Task CanUserDeleteNoteTypeAsync(string userId, string noteTypeId)
+ {
+ var department = await _departmentsService.GetDepartmentByUserIdAsync(userId);
+
+ if (department == null)
+ return false;
+
+ var type = await _notesService.GetNoteCategoryByIdAsync(noteTypeId);
+
+ if (type == null)
+ return false;
+
+ if (type.DepartmentId != department.DepartmentId)
+ return false;
+
+ if (department.IsUserAnAdmin(userId))
+ return true;
+
+ return false;
+ }
+
+ public async Task CanUserAddNoteAsync(string userId)
+ {
+ var department = await _departmentsService.GetDepartmentByUserIdAsync(userId);
+
+ if (department == null)
+ return false;
+
+ if (department.IsUserAnAdmin(userId))
+ return true;
+
+ return false;
+ }
+
+ public async Task CanUserEditNoteAsync(string userId, int noteId)
+ {
+ var department = await _departmentsService.GetDepartmentByUserIdAsync(userId);
+
+ if (department == null)
+ return false;
+
+ var type = await _notesService.GetNoteByIdAsync(noteId);
+
+ if (type == null)
+ return false;
+
+ if (type.DepartmentId != department.DepartmentId)
+ return false;
+
+ if (department.IsUserAnAdmin(userId))
+ return true;
+
+ return false;
+ }
}
}
diff --git a/Core/Resgrid.Services/CertificationService.cs b/Core/Resgrid.Services/CertificationService.cs
index 23e2132d..44159a00 100644
--- a/Core/Resgrid.Services/CertificationService.cs
+++ b/Core/Resgrid.Services/CertificationService.cs
@@ -5,6 +5,7 @@
using Resgrid.Model;
using Resgrid.Model.Repositories;
using Resgrid.Model.Services;
+using Resgrid.Repositories.DataRepository;
namespace Resgrid.Services
{
@@ -53,6 +54,16 @@ public async Task GetCertificationTypeByIdAsync(int
return await _departmentCertificationTypeRepository.SaveOrUpdateAsync(newCertType, cancellationToken);
}
+ public async Task DoesCertificationTypeAlreadyExistAsync(int departmentId, string certificationTypeText)
+ {
+ var categories = await _departmentCertificationTypeRepository.GetAllByDepartmentIdAsync(departmentId);
+
+ if (categories == null)
+ return false;
+
+ return categories.Any(x => x.Type == certificationTypeText.Trim());
+ }
+
public async Task> GetCertificationsByUserIdAsync(string userId)
{
var items = await _personnelCertificationRepository.GetCertificationsByUserAsync(userId);
diff --git a/Core/Resgrid.Services/CustomStateService.cs b/Core/Resgrid.Services/CustomStateService.cs
index 0f989b92..834b8b1a 100644
--- a/Core/Resgrid.Services/CustomStateService.cs
+++ b/Core/Resgrid.Services/CustomStateService.cs
@@ -165,7 +165,7 @@ public void InvalidateCustomStateInCache(int departmentId)
{
var saved = await _customStateRepository.SaveOrUpdateAsync(customState, cancellationToken);
- _cacheProvider.Remove(string.Format(CacheKey, customState.DepartmentId));
+ await _cacheProvider.RemoveAsync(string.Format(CacheKey, customState.DepartmentId));
_eventAggregator.SendMessage(new DepartmentSettingsUpdateEvent() { DepartmentId = customState.DepartmentId });
@@ -206,15 +206,33 @@ public async Task GetCustomDetailByIdAsync(int detailId)
public async Task UpdateAsync(CustomState state, List details, CancellationToken cancellationToken = default(CancellationToken))
{
- foreach (var existingDetails in state.Details)
+ var missingDetails = state.Details.Where(p => !details.Any(p2 => p2.CustomStateDetailId == p.CustomStateDetailId)).ToList();
+
+ if (missingDetails != null && missingDetails.Any())
{
- await DeleteDetailAsync(existingDetails, cancellationToken);
+ foreach (var missingDetail in missingDetails)
+ {
+ await DeleteDetailAsync(missingDetail, cancellationToken);
+ state.Details.Remove(missingDetail);
+ }
}
foreach (var detail in details)
{
detail.CustomStateId = state.CustomStateId;
- state.Details.Add(detail);
+ if (detail.CustomStateDetailId == 0)
+ {
+ state.Details.Add(detail);
+ }
+ else
+ {
+ var existingDetail = state.Details.FirstOrDefault(x => x.CustomStateDetailId == detail.CustomStateDetailId);
+
+ if (existingDetail != null)
+ {
+ existingDetail.Order = detail.Order;
+ }
+ }
}
return await SaveAsync(state, cancellationToken);
diff --git a/Core/Resgrid.Services/DepartmentsService.cs b/Core/Resgrid.Services/DepartmentsService.cs
index c8cefbeb..0a776305 100644
--- a/Core/Resgrid.Services/DepartmentsService.cs
+++ b/Core/Resgrid.Services/DepartmentsService.cs
@@ -121,7 +121,7 @@ async Task getDepartment()
var dep = await _departmentRepository.SaveOrUpdateAsync(department, cancellationToken);
- _cacheProvider.Remove(string.Format(CacheKey, department.DepartmentId));
+ await _cacheProvider.RemoveAsync(string.Format(CacheKey, department.DepartmentId));
_eventAggregator.SendMessage(new DepartmentSettingsUpdateEvent()
{
@@ -133,7 +133,7 @@ async Task getDepartment()
public async Task InvalidateAllDepartmentsCache(int departmentId)
{
- _cacheProvider.Remove(string.Format(CacheKey, departmentId));
+ await _cacheProvider.RemoveAsync(string.Format(CacheKey, departmentId));
InvalidateDepartmentUsersInCache(departmentId);
InvalidateDepartmentInCache(departmentId);
InvalidatePersonnelNamesInCache(departmentId);
diff --git a/Core/Resgrid.Services/DocumentsService.cs b/Core/Resgrid.Services/DocumentsService.cs
index 4f432456..1c9472be 100644
--- a/Core/Resgrid.Services/DocumentsService.cs
+++ b/Core/Resgrid.Services/DocumentsService.cs
@@ -7,17 +7,20 @@
using Resgrid.Model.Repositories;
using Resgrid.Model.Services;
using Resgrid.Providers.Bus;
+using Resgrid.Repositories.DataRepository;
namespace Resgrid.Services
{
public class DocumentsService : IDocumentsService
{
private readonly IDocumentRepository _documentRepository;
+ private readonly IDocumentCategoriesRepository _documentCategoriesRepository;
private readonly IEventAggregator _eventAggregator;
- public DocumentsService(IDocumentRepository documentRepository, IEventAggregator eventAggregator)
+ public DocumentsService(IDocumentRepository documentRepository, IDocumentCategoriesRepository documentCategoriesRepository, IEventAggregator eventAggregator)
{
_documentRepository = documentRepository;
+ _documentCategoriesRepository = documentCategoriesRepository;
_eventAggregator = eventAggregator;
}
@@ -82,5 +85,40 @@ public async Task GetDocumentByIdAsync(int documentId)
{
return await _documentRepository.DeleteAsync(document, cancellationToken);
}
+
+ public async Task SaveDocumentCategoryAsync(DocumentCategory category, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ return await _documentCategoriesRepository.SaveOrUpdateAsync(category, cancellationToken);
+ }
+
+ public async Task GetDocumentCategoryByIdAsync(string categoryId)
+ {
+ return await _documentCategoriesRepository.GetByIdAsync(categoryId);
+ }
+
+ public async Task DeleteDocumentCategoryAsync(DocumentCategory category, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ return await _documentCategoriesRepository.DeleteAsync(category, cancellationToken);
+ }
+
+ public async Task> GetAllCategoriesByDepartmentIdAsync(int departmentId)
+ {
+ var categories = await _documentCategoriesRepository.GetAllByDepartmentIdAsync(departmentId);
+
+ if (categories != null)
+ return categories.ToList();
+
+ return new List();
+ }
+
+ public async Task DoesDocumentCategoryAlreadyExistAsync(int departmentId, string documentCategoryText)
+ {
+ var categories = await _documentCategoriesRepository.GetAllByDepartmentIdAsync(departmentId);
+
+ if (categories == null)
+ return false;
+
+ return categories.Any(x => x.Name == documentCategoryText.Trim());
+ }
}
}
diff --git a/Core/Resgrid.Services/NotesService.cs b/Core/Resgrid.Services/NotesService.cs
index 0fba4c11..9783f384 100644
--- a/Core/Resgrid.Services/NotesService.cs
+++ b/Core/Resgrid.Services/NotesService.cs
@@ -9,6 +9,7 @@
using Resgrid.Model.Repositories;
using Resgrid.Model.Services;
using Resgrid.Providers.Bus;
+using Resgrid.Repositories.DataRepository;
namespace Resgrid.Services
{
@@ -16,11 +17,13 @@ public class NotesService : INotesService
{
private readonly INotesRepository _notesRepository;
private readonly IEventAggregator _eventAggregator;
+ private readonly INoteCategoriesRepository _noteCategoriesRepository;
- public NotesService(INotesRepository notesRepository, IEventAggregator eventAggregator)
+ public NotesService(INotesRepository notesRepository, IEventAggregator eventAggregator, INoteCategoriesRepository noteCategoriesRepository)
{
_notesRepository = notesRepository;
_eventAggregator = eventAggregator;
+ _noteCategoriesRepository = noteCategoriesRepository;
}
public async Task> GetAllNotesForDepartmentAsync(int departmentId)
@@ -78,5 +81,40 @@ public async Task> GetNotesForDepartmentFilteredAsync(int departmentI
where note.IsAdminOnly == false
select note).ToList();
}
+
+ public async Task SaveNoteCategoryAsync(NoteCategory category, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ return await _noteCategoriesRepository.SaveOrUpdateAsync(category, cancellationToken);
+ }
+
+ public async Task GetNoteCategoryByIdAsync(string categoryId)
+ {
+ return await _noteCategoriesRepository.GetByIdAsync(categoryId);
+ }
+
+ public async Task DeleteNoteCategoryAsync(NoteCategory category, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ return await _noteCategoriesRepository.DeleteAsync(category, cancellationToken);
+ }
+
+ public async Task> GetAllCategoriesByDepartmentIdAsync(int departmentId)
+ {
+ var categories = await _noteCategoriesRepository.GetAllByDepartmentIdAsync(departmentId);
+
+ if (categories != null)
+ return categories.ToList();
+
+ return new List();
+ }
+
+ public async Task DoesNoteTypeAlreadyExistAsync(int departmentId, string noteTypeText)
+ {
+ var categories = await _noteCategoriesRepository.GetAllByDepartmentIdAsync(departmentId);
+
+ if (categories == null)
+ return false;
+
+ return categories.Any(x => x.Name == noteTypeText.Trim());
+ }
}
}
diff --git a/Providers/Resgrid.Providers.Migrations/Migrations/M0029_AddingCustomStateDetailColumns.cs b/Providers/Resgrid.Providers.Migrations/Migrations/M0029_AddingCustomStateDetailColumns.cs
new file mode 100644
index 00000000..73c35657
--- /dev/null
+++ b/Providers/Resgrid.Providers.Migrations/Migrations/M0029_AddingCustomStateDetailColumns.cs
@@ -0,0 +1,20 @@
+using FluentMigrator;
+using System;
+
+namespace Resgrid.Providers.Migrations.Migrations
+{
+ [Migration(29)]
+ public class M0029_AddingCustomStateDetailColumns : Migration
+ {
+ public override void Up()
+ {
+ Alter.Table("CustomStateDetails").AddColumn("BaseType").AsInt32().NotNullable().WithDefaultValue(-1);
+ Alter.Table("CustomStateDetails").AddColumn("TTL").AsInt32().NotNullable().WithDefaultValue(0);
+ }
+
+ public override void Down()
+ {
+
+ }
+ }
+}
diff --git a/Providers/Resgrid.Providers.Migrations/Migrations/M0030_AddingDocumentCategories.cs b/Providers/Resgrid.Providers.Migrations/Migrations/M0030_AddingDocumentCategories.cs
new file mode 100644
index 00000000..1d4f1ef5
--- /dev/null
+++ b/Providers/Resgrid.Providers.Migrations/Migrations/M0030_AddingDocumentCategories.cs
@@ -0,0 +1,28 @@
+using FluentMigrator;
+using System;
+
+namespace Resgrid.Providers.Migrations.Migrations
+{
+ [Migration(30)]
+ public class M0030_AddingDocumentCategories : Migration
+ {
+ public override void Up()
+ {
+ Create.Table("DocumentCategories")
+ .WithColumn("DocumentCategoryId").AsString(128).NotNullable().PrimaryKey()
+ .WithColumn("DepartmentId").AsInt32().NotNullable()
+ .WithColumn("Name").AsString(512).NotNullable()
+ .WithColumn("AddedOn").AsDateTime2().NotNullable()
+ .WithColumn("AddedById").AsString(128).NotNullable();
+
+ Create.ForeignKey("FK_DocumentCategories_Department")
+ .FromTable("DocumentCategories").ForeignColumn("DepartmentId")
+ .ToTable("Departments").PrimaryColumn("DepartmentId");
+ }
+
+ public override void Down()
+ {
+
+ }
+ }
+}
diff --git a/Providers/Resgrid.Providers.Migrations/Migrations/M0031_AddingNoteCategories.cs b/Providers/Resgrid.Providers.Migrations/Migrations/M0031_AddingNoteCategories.cs
new file mode 100644
index 00000000..8f299130
--- /dev/null
+++ b/Providers/Resgrid.Providers.Migrations/Migrations/M0031_AddingNoteCategories.cs
@@ -0,0 +1,28 @@
+using FluentMigrator;
+using System;
+
+namespace Resgrid.Providers.Migrations.Migrations
+{
+ [Migration(31)]
+ public class M0031_AddingNoteCategories : Migration
+ {
+ public override void Up()
+ {
+ Create.Table("NoteCategories")
+ .WithColumn("NoteCategoryId").AsString(128).NotNullable().PrimaryKey()
+ .WithColumn("DepartmentId").AsInt32().NotNullable()
+ .WithColumn("Name").AsString(512).NotNullable()
+ .WithColumn("AddedOn").AsDateTime2().NotNullable()
+ .WithColumn("AddedById").AsString(128).NotNullable();
+
+ Create.ForeignKey("FK_NoteCategories_Department")
+ .FromTable("NoteCategories").ForeignColumn("DepartmentId")
+ .ToTable("Departments").PrimaryColumn("DepartmentId");
+ }
+
+ public override void Down()
+ {
+
+ }
+ }
+}
diff --git a/Repositories/Resgrid.Repositories.DataRepository/DocumentCategoriesRepository.cs b/Repositories/Resgrid.Repositories.DataRepository/DocumentCategoriesRepository.cs
new file mode 100644
index 00000000..2c675a98
--- /dev/null
+++ b/Repositories/Resgrid.Repositories.DataRepository/DocumentCategoriesRepository.cs
@@ -0,0 +1,25 @@
+using Resgrid.Model;
+using Resgrid.Model.Repositories;
+using Resgrid.Model.Repositories.Connection;
+using Resgrid.Model.Repositories.Queries;
+using Resgrid.Repositories.DataRepository.Configs;
+
+namespace Resgrid.Repositories.DataRepository
+{
+ public class DocumentCategoriesRepository : RepositoryBase, IDocumentCategoriesRepository
+ {
+ private readonly IConnectionProvider _connectionProvider;
+ private readonly SqlConfiguration _sqlConfiguration;
+ private readonly IQueryFactory _queryFactory;
+ private readonly IUnitOfWork _unitOfWork;
+
+ public DocumentCategoriesRepository(IConnectionProvider connectionProvider, SqlConfiguration sqlConfiguration, IUnitOfWork unitOfWork, IQueryFactory queryFactory)
+ : base(connectionProvider, sqlConfiguration, unitOfWork, queryFactory)
+ {
+ _connectionProvider = connectionProvider;
+ _sqlConfiguration = sqlConfiguration;
+ _queryFactory = queryFactory;
+ _unitOfWork = unitOfWork;
+ }
+ }
+}
diff --git a/Repositories/Resgrid.Repositories.DataRepository/Modules/ApiDataModule.cs b/Repositories/Resgrid.Repositories.DataRepository/Modules/ApiDataModule.cs
index ec6d4ee8..0002f2c1 100644
--- a/Repositories/Resgrid.Repositories.DataRepository/Modules/ApiDataModule.cs
+++ b/Repositories/Resgrid.Repositories.DataRepository/Modules/ApiDataModule.cs
@@ -148,6 +148,8 @@ protected override void Load(ContainerBuilder builder)
builder.RegisterType().As().InstancePerLifetimeScope();
builder.RegisterType().As().InstancePerLifetimeScope();
builder.RegisterType().As().InstancePerLifetimeScope();
+ builder.RegisterType().As().InstancePerLifetimeScope();
+ builder.RegisterType().As().InstancePerLifetimeScope();
}
}
}
diff --git a/Repositories/Resgrid.Repositories.DataRepository/Modules/DataModule.cs b/Repositories/Resgrid.Repositories.DataRepository/Modules/DataModule.cs
index cc96234f..ecb3ac38 100644
--- a/Repositories/Resgrid.Repositories.DataRepository/Modules/DataModule.cs
+++ b/Repositories/Resgrid.Repositories.DataRepository/Modules/DataModule.cs
@@ -148,6 +148,8 @@ protected override void Load(ContainerBuilder builder)
builder.RegisterType().As().InstancePerLifetimeScope();
builder.RegisterType().As().InstancePerLifetimeScope();
builder.RegisterType().As().InstancePerLifetimeScope();
+ builder.RegisterType().As().InstancePerLifetimeScope();
+ builder.RegisterType().As().InstancePerLifetimeScope();
}
}
}
diff --git a/Repositories/Resgrid.Repositories.DataRepository/Modules/NonWebDataModule.cs b/Repositories/Resgrid.Repositories.DataRepository/Modules/NonWebDataModule.cs
index 56677c9b..7d70ec61 100644
--- a/Repositories/Resgrid.Repositories.DataRepository/Modules/NonWebDataModule.cs
+++ b/Repositories/Resgrid.Repositories.DataRepository/Modules/NonWebDataModule.cs
@@ -148,6 +148,8 @@ protected override void Load(ContainerBuilder builder)
builder.RegisterType().As().InstancePerLifetimeScope();
builder.RegisterType().As().InstancePerLifetimeScope();
builder.RegisterType().As().InstancePerLifetimeScope();
+ builder.RegisterType().As().InstancePerLifetimeScope();
+ builder.RegisterType().As().InstancePerLifetimeScope();
}
}
}
diff --git a/Repositories/Resgrid.Repositories.DataRepository/Modules/TestingDataModule.cs b/Repositories/Resgrid.Repositories.DataRepository/Modules/TestingDataModule.cs
index 33bfb970..eddc3aee 100644
--- a/Repositories/Resgrid.Repositories.DataRepository/Modules/TestingDataModule.cs
+++ b/Repositories/Resgrid.Repositories.DataRepository/Modules/TestingDataModule.cs
@@ -148,6 +148,8 @@ protected override void Load(ContainerBuilder builder)
builder.RegisterType().As().InstancePerLifetimeScope();
builder.RegisterType().As().InstancePerLifetimeScope();
builder.RegisterType().As().InstancePerLifetimeScope();
+ builder.RegisterType().As().InstancePerLifetimeScope();
+ builder.RegisterType().As().InstancePerLifetimeScope();
}
}
}
diff --git a/Repositories/Resgrid.Repositories.DataRepository/NoteCategoriesRepository.cs b/Repositories/Resgrid.Repositories.DataRepository/NoteCategoriesRepository.cs
new file mode 100644
index 00000000..d00efa9e
--- /dev/null
+++ b/Repositories/Resgrid.Repositories.DataRepository/NoteCategoriesRepository.cs
@@ -0,0 +1,25 @@
+using Resgrid.Model;
+using Resgrid.Model.Repositories;
+using Resgrid.Model.Repositories.Connection;
+using Resgrid.Model.Repositories.Queries;
+using Resgrid.Repositories.DataRepository.Configs;
+
+namespace Resgrid.Repositories.DataRepository
+{
+ public class NoteCategoriesRepository : RepositoryBase, INoteCategoriesRepository
+ {
+ private readonly IConnectionProvider _connectionProvider;
+ private readonly SqlConfiguration _sqlConfiguration;
+ private readonly IQueryFactory _queryFactory;
+ private readonly IUnitOfWork _unitOfWork;
+
+ public NoteCategoriesRepository(IConnectionProvider connectionProvider, SqlConfiguration sqlConfiguration, IUnitOfWork unitOfWork, IQueryFactory queryFactory)
+ : base(connectionProvider, sqlConfiguration, unitOfWork, queryFactory)
+ {
+ _connectionProvider = connectionProvider;
+ _sqlConfiguration = sqlConfiguration;
+ _queryFactory = queryFactory;
+ _unitOfWork = unitOfWork;
+ }
+ }
+}
diff --git a/Web/Resgrid.WebCore/Areas/User/Controllers/CustomStatusesController.cs b/Web/Resgrid.WebCore/Areas/User/Controllers/CustomStatusesController.cs
index 9d802a9d..633b14d4 100644
--- a/Web/Resgrid.WebCore/Areas/User/Controllers/CustomStatusesController.cs
+++ b/Web/Resgrid.WebCore/Areas/User/Controllers/CustomStatusesController.cs
@@ -7,10 +7,16 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
+using Resgrid.Framework;
using Resgrid.Model;
+using Resgrid.Model.Events;
+using Resgrid.Model.Providers;
using Resgrid.Model.Services;
using Resgrid.Providers.Claims;
using Resgrid.Web.Areas.User.Models.CustomStatuses;
+using Resgrid.Web.Helpers;
+using SharpKml.Dom;
+using IAuthorizationService = Resgrid.Model.Services.IAuthorizationService;
namespace Resgrid.Web.Areas.User.Controllers
{
@@ -20,11 +26,15 @@ public class CustomStatusesController : SecureBaseController
#region Private Members and Constructors
private readonly ICustomStateService _customStateService;
private readonly IUnitsService _unitsService;
+ private readonly IAuthorizationService _authorizationService;
+ private readonly IEventAggregator _eventAggregator;
- public CustomStatusesController(ICustomStateService customStateService, IUnitsService unitsService)
+ public CustomStatusesController(ICustomStateService customStateService, IUnitsService unitsService, IAuthorizationService authorizationService, IEventAggregator eventAggregator)
{
_customStateService = customStateService;
_unitsService = unitsService;
+ _authorizationService = authorizationService;
+ _eventAggregator = eventAggregator;
}
#endregion Private Members and Constructors
@@ -84,6 +94,7 @@ where key.ToString().StartsWith("buttonText_")
var color = form["buttonColor_" + i];
var textColor = form["textColor_" + i];
var order = form["order_" + i];
+ var baseType = form["baseType_" + i];
bool gps = false;
var gpsValue = form["requireGps_" + i];
@@ -102,11 +113,23 @@ where key.ToString().StartsWith("buttonText_")
detail.DetailType = detailType;
detail.TextColor = textColor;
detail.Order = int.Parse(order);
+ detail.BaseType = int.Parse(baseType);
model.State.Details.Add(detail);
}
}
+ var auditEvent = new AuditEvent();
+ auditEvent.DepartmentId = DepartmentId;
+ auditEvent.UserId = UserId;
+ auditEvent.Type = AuditLogTypes.CustomStatusAdded;
+ auditEvent.After = model.State.CloneJsonToString();
+ auditEvent.Successful = true;
+ auditEvent.IpAddress = IpAddressHelper.GetRequestIP(Request, true);
+ auditEvent.ServerName = Environment.MachineName;
+ auditEvent.UserAgent = $"{Request.Headers["User-Agent"]} {Request.Headers["Accept-Language"]}";
+ _eventAggregator.SendMessage(auditEvent);
+
await _customStateService.SaveAsync(model.State, cancellationToken);
return RedirectToAction("Index");
@@ -121,9 +144,20 @@ public async Task Delete(int id, CancellationToken cancellationTo
{
var state = await _customStateService.GetCustomSateByIdAsync(id);
- if (state.DepartmentId != DepartmentId)
+ if (!await _authorizationService.CanUserModifyCustomStatusAsync(UserId, state.CustomStateId))
Unauthorized();
+ var auditEvent = new AuditEvent();
+ auditEvent.DepartmentId = DepartmentId;
+ auditEvent.UserId = UserId;
+ auditEvent.Type = AuditLogTypes.CustomStatusRemoved;
+ auditEvent.Before = state.CloneJsonToString();
+ auditEvent.Successful = true;
+ auditEvent.IpAddress = IpAddressHelper.GetRequestIP(Request, true);
+ auditEvent.ServerName = Environment.MachineName;
+ auditEvent.UserAgent = $"{Request.Headers["User-Agent"]} {Request.Headers["Accept-Language"]}";
+ _eventAggregator.SendMessage(auditEvent);
+
await _customStateService.DeleteAsync(state, cancellationToken);
return RedirectToAction("Index");
@@ -133,6 +167,9 @@ public async Task Delete(int id, CancellationToken cancellationTo
[Authorize(Policy = ResgridResources.CustomStates_Update)]
public async Task Edit(int id)
{
+ if (!await _authorizationService.CanUserModifyCustomStatusAsync(UserId, id))
+ Unauthorized();
+
var model = new EditStatusView();
model.State = await _customStateService.GetCustomSateByIdAsync(id);
@@ -143,14 +180,19 @@ public async Task Edit(int id)
[Authorize(Policy = ResgridResources.CustomStates_Update)]
public async Task EditDetail(int stateDetailId)
{
+ if (!await _authorizationService.CanUserModifyCustomStateDetailAsync(UserId, stateDetailId))
+ Unauthorized();
+
var model = new EditDetailView();
model.Detail = await _customStateService.GetCustomDetailByIdAsync(stateDetailId);
model.Detail.CustomState = await _customStateService.GetCustomSateByIdAsync(model.Detail.CustomStateId);
model.DetailTypes = model.DetailType.ToSelectList();
model.NoteTypes = model.NoteType.ToSelectList();
+ model.BaseTypes = model.BaseType.ToSelectList();
model.DetailType = (CustomStateDetailTypes)model.Detail.DetailType;
model.NoteType = (CustomStateNoteTypes)model.Detail.NoteType;
+ model.BaseType = (ActionBaseTypes)model.Detail.BaseType;
if (String.IsNullOrWhiteSpace(model.Detail.TextColor))
model.Detail.TextColor = "#000000";
@@ -162,23 +204,36 @@ public async Task EditDetail(int stateDetailId)
[Authorize(Policy = ResgridResources.CustomStates_Update)]
public async Task EditDetail(EditDetailView model, CancellationToken cancellationToken)
{
+ if (!await _authorizationService.CanUserModifyCustomStateDetailAsync(UserId, model.Detail.CustomStateDetailId))
+ Unauthorized();
+
model.DetailTypes = model.DetailType.ToSelectList();
model.NoteTypes = model.NoteType.ToSelectList();
if (ModelState.IsValid)
{
+ var auditEvent = new AuditEvent();
var detail = await _customStateService.GetCustomDetailByIdAsync(model.Detail.CustomStateDetailId);
+ auditEvent.Before = detail.CloneJsonToString();
+
detail.ButtonColor = model.Detail.ButtonColor;
detail.ButtonText = model.Detail.ButtonText;
detail.TextColor = model.Detail.TextColor;
detail.NoteType = (int)model.NoteType;
detail.Order = model.Detail.Order;
detail.GpsRequired = model.Detail.GpsRequired;
-
- //if (detail.CustomState.Type != (int)CustomStateTypes.Staffing)
- //{
- detail.DetailType = (int)model.DetailType;
- //}
+ detail.DetailType = (int)model.DetailType;
+ detail.BaseType = (int)model.BaseType;
+
+ auditEvent.DepartmentId = DepartmentId;
+ auditEvent.UserId = UserId;
+ auditEvent.Type = AuditLogTypes.CustomStatusDetailUpdated;
+ auditEvent.After = detail.CloneJsonToString();
+ auditEvent.Successful = true;
+ auditEvent.IpAddress = IpAddressHelper.GetRequestIP(Request, true);
+ auditEvent.ServerName = Environment.MachineName;
+ auditEvent.UserAgent = $"{Request.Headers["User-Agent"]} {Request.Headers["Accept-Language"]}";
+ _eventAggregator.SendMessage(auditEvent);
await _customStateService.SaveDetailAsync(detail, DepartmentId, cancellationToken);
@@ -194,15 +249,21 @@ public async Task EditDetail(EditDetailView model, CancellationTo
[Authorize(Policy = ResgridResources.CustomStates_Update)]
public async Task Edit(EditStatusView model, IFormCollection form, CancellationToken cancellationToken)
{
+ if (!await _authorizationService.CanUserModifyCustomStatusAsync(UserId, model.State.CustomStateId))
+ Unauthorized();
+
if (ModelState.IsValid)
{
List options = (from object key in form.Keys
where key.ToString().StartsWith("buttonText_")
select int.Parse(key.ToString().Replace("buttonText_", ""))).ToList();
+ var auditEvent = new AuditEvent();
var details = new List();
var state = await _customStateService.GetCustomSateByIdAsync(model.State.CustomStateId);
+ auditEvent.Before = state.CloneJsonToString();
+
state.Name = model.State.Name;
state.Description = model.State.Description;
@@ -214,6 +275,8 @@ where key.ToString().StartsWith("buttonText_")
var color = form["buttonColor_" + i];
var textColor = form["textColor_" + i];
var order = form["order_" + i];
+ var baseType = form["baseType_" + i];
+ var customStateDetailId = form["customStateDetailId_" + i];
bool gps = false;
var gpsValue = form["requireGps_" + i];
@@ -232,11 +295,27 @@ where key.ToString().StartsWith("buttonText_")
detail.DetailType = detailType;
detail.TextColor = textColor;
detail.Order = int.Parse(order);
+ detail.BaseType = int.Parse(baseType);
+
+ if (!string.IsNullOrWhiteSpace(customStateDetailId))
+ detail.CustomStateDetailId = int.Parse(customStateDetailId);
+ else
+ detail.CustomStateDetailId = 0;
details.Add(detail);
}
}
+ auditEvent.DepartmentId = DepartmentId;
+ auditEvent.UserId = UserId;
+ auditEvent.Type = AuditLogTypes.CustomStatusUpdated;
+ auditEvent.After = details.CloneJsonToString();
+ auditEvent.Successful = true;
+ auditEvent.IpAddress = IpAddressHelper.GetRequestIP(Request, true);
+ auditEvent.ServerName = Environment.MachineName;
+ auditEvent.UserAgent = $"{Request.Headers["User-Agent"]} {Request.Headers["Accept-Language"]}";
+ _eventAggregator.SendMessage(auditEvent);
+
await _customStateService.UpdateAsync(state, details, cancellationToken);
return RedirectToAction("Index");
diff --git a/Web/Resgrid.WebCore/Areas/User/Controllers/DepartmentController.cs b/Web/Resgrid.WebCore/Areas/User/Controllers/DepartmentController.cs
index de621d21..f31325dd 100644
--- a/Web/Resgrid.WebCore/Areas/User/Controllers/DepartmentController.cs
+++ b/Web/Resgrid.WebCore/Areas/User/Controllers/DepartmentController.cs
@@ -62,13 +62,16 @@ public class DepartmentController : SecureBaseController
private readonly ICqrsProvider _cqrsProvider;
private readonly IPrinterProvider _printerProvider;
private readonly IQueueService _queueService;
+ private readonly IDocumentsService _documentsService;
+ private readonly INotesService _notesService;
public DepartmentController(IDepartmentsService departmentsService, IUsersService usersService, IActionLogsService actionLogsService,
IEmailService emailService, IDepartmentGroupsService departmentGroupsService, IUserProfileService userProfileService, IDeleteService deleteService,
IInvitesService invitesService, Model.Services.IAuthorizationService authorizationService, IAddressService addressService, ISubscriptionsService subscriptionsService,
ILimitsService limitsService, ICallsService callsService, IDepartmentSettingsService departmentSettingsService, IUnitsService unitsService,
ICertificationService certificationService, INumbersService numbersService, IScheduledTasksService scheduledTasksService, IPersonnelRolesService personnelRolesService,
- IEventAggregator eventAggregator, ICustomStateService customStateService, ICqrsProvider cqrsProvider, IPrinterProvider printerProvider, IQueueService queueService)
+ IEventAggregator eventAggregator, ICustomStateService customStateService, ICqrsProvider cqrsProvider, IPrinterProvider printerProvider, IQueueService queueService,
+ IDocumentsService documentsService, INotesService notesService)
{
_departmentsService = departmentsService;
_usersService = usersService;
@@ -94,6 +97,8 @@ public DepartmentController(IDepartmentsService departmentsService, IUsersServic
_cqrsProvider = cqrsProvider;
_printerProvider = printerProvider;
_queueService = queueService;
+ _documentsService = documentsService;
+ _notesService = notesService;
}
#endregion Private Members and Constructors
@@ -1095,31 +1100,6 @@ public async Task UnitSettings()
return View(model);
}
-
- [HttpGet]
- [Authorize(Policy = ResgridResources.Department_Update)]
- public async Task DeleteUnitType(int unitTypeId, CancellationToken cancellationToken)
- {
- await _unitsService.DeleteUnitTypeAsync(unitTypeId, cancellationToken);
-
- return RedirectToAction("Types");
- }
-
- [HttpPost]
- [Authorize(Policy = ResgridResources.Department_Update)]
- public async Task NewUnitType(UnitSettingsView model, CancellationToken cancellationToken)
- {
- if (String.IsNullOrWhiteSpace(model.NewUnitType))
- ModelState.AddModelError("NewUnitType", "You Must specify the new unit type.");
-
- if (ModelState.IsValid)
- {
- await _unitsService.AddUnitTypeAsync(DepartmentId, model.NewUnitType, model.UnitCustomStatesId, cancellationToken);
- }
-
- return RedirectToAction("Types");
- }
-
#endregion Unit Settings
#region Types
@@ -1132,6 +1112,8 @@ public async Task Types()
model.CertificationTypes = await _certificationService.GetAllCertificationTypesByDepartmentAsync(DepartmentId);
model.UnitTypes = await _unitsService.GetUnitTypesForDepartmentAsync(DepartmentId);
model.CallTypes = await _callsService.GetCallTypesForDepartmentAsync(DepartmentId);
+ model.DocumentCategories = await _documentsService.GetAllCategoriesByDepartmentIdAsync(DepartmentId);
+ model.NoteCategories = await _notesService.GetAllCategoriesByDepartmentIdAsync(DepartmentId);
var states = new List();
states.Add(new CustomState
@@ -1158,61 +1140,6 @@ public async Task Types()
return View(model);
}
- [HttpGet]
- [Authorize(Policy = ResgridResources.Department_Update)]
- public async Task DeleteCertificationType(int certificationTypeId, CancellationToken cancellationToken)
- {
- await _certificationService.DeleteCertificationTypeByIdAsync(certificationTypeId, cancellationToken);
-
- return RedirectToAction("Types");
- }
-
- [HttpPost]
- [Authorize(Policy = ResgridResources.Department_Update)]
- public async Task NewCertificationType(DepartmentTypesView model, CancellationToken cancellationToken)
- {
- if (String.IsNullOrEmpty(model.NewCertificationType))
- ModelState.AddModelError("NewCertificationType", "You Must specify the new certification type.");
-
- if (ModelState.IsValid)
- {
- await _certificationService.SaveNewCertificationTypeAsync(model.NewCertificationType, DepartmentId, cancellationToken);
- }
-
- return RedirectToAction("Types");
- }
-
- [HttpGet]
- [Authorize(Policy = ResgridResources.Department_Update)]
- public async Task DeleteCallType(int callTypeId, CancellationToken cancellationToken)
- {
- await _callsService.DeleteCallTypeAsync(callTypeId, cancellationToken);
-
- return RedirectToAction("Types");
- }
-
- [HttpPost]
- [Authorize(Policy = ResgridResources.Department_Update)]
- public async Task NewCallType(DepartmentTypesView model, CancellationToken cancellationToken)
- {
- if (String.IsNullOrEmpty(model.NewCallType))
- ModelState.AddModelError("NewCallType", "You Must specify the new call type.");
-
- if (ModelState.IsValid)
- {
- CallType newCallType = new CallType();
- newCallType.DepartmentId = DepartmentId;
- newCallType.Type = model.NewCallType;
-
- if (model.CallTypeIcon >= 0)
- newCallType.MapIconType = model.CallTypeIcon;
-
- await _callsService.SaveCallTypeAsync(newCallType, cancellationToken);
- }
-
- return RedirectToAction("Types");
- }
-
#endregion Types
#region Text Messaging
diff --git a/Web/Resgrid.WebCore/Areas/User/Controllers/DocumentsController.cs b/Web/Resgrid.WebCore/Areas/User/Controllers/DocumentsController.cs
index 3830a742..0b1658ab 100644
--- a/Web/Resgrid.WebCore/Areas/User/Controllers/DocumentsController.cs
+++ b/Web/Resgrid.WebCore/Areas/User/Controllers/DocumentsController.cs
@@ -6,12 +6,16 @@
using Resgrid.Model;
using Resgrid.Model.Events;
using Resgrid.Model.Services;
-using Resgrid.Providers.Bus;
using Resgrid.Providers.Claims;
using Resgrid.Web.Areas.User.Models.Documents;
using Resgrid.Web.Helpers;
using Microsoft.AspNetCore.Authorization;
using Resgrid.Model.Providers;
+using Resgrid.Framework;
+using Microsoft.AspNetCore.Mvc.Rendering;
+using Resgrid.Services;
+using System.Collections.Generic;
+using NuGet.Packaging;
namespace Resgrid.Web.Areas.User.Controllers
{
@@ -37,7 +41,7 @@ public async Task Index(string type, string category)
var model = new IndexView();
model.Department = await _departmentsService.GetDepartmentByIdAsync(DepartmentId, false);
model.Documents = await _documentsService.GetFilteredDocumentsByDepartmentIdAsync(DepartmentId, type, category);
- model.Categories = await _documentsService.GetDistinctCategoriesByDepartmentIdAsync(DepartmentId);
+ model.Categories = await _documentsService.GetAllCategoriesByDepartmentIdAsync(DepartmentId);
model.SelectedCategory = category;
model.SelectedType = type;
@@ -51,7 +55,11 @@ public async Task Index(string type, string category)
[Authorize(Policy = ResgridResources.Documents_Create)]
public async Task NewDocument()
{
+ if (!ClaimsAuthorizationHelper.CanCreateDocument())
+ Unauthorized();
+
NewDocumentView model = new NewDocumentView();
+ model.Categories = new SelectList(await _documentsService.GetAllCategoriesByDepartmentIdAsync(DepartmentId), "Name", "Name");
return View(model);
}
@@ -92,6 +100,17 @@ public async Task DeleteDocument(int documentId, CancellationToke
if (!ClaimsAuthorizationHelper.IsUserDepartmentAdmin() || document.UserId != UserId)
Unauthorized();
+ var auditEvent = new AuditEvent();
+ auditEvent.DepartmentId = DepartmentId;
+ auditEvent.UserId = UserId;
+ auditEvent.Before = $"{document.DocumentId} - {document.Name} - {document.Description} - {document.Category} - {document.AdminsOnly}";
+ auditEvent.Type = AuditLogTypes.DocumentRemoved;
+ auditEvent.Successful = true;
+ auditEvent.IpAddress = IpAddressHelper.GetRequestIP(Request, true);
+ auditEvent.ServerName = Environment.MachineName;
+ auditEvent.UserAgent = $"{Request.Headers["User-Agent"]} {Request.Headers["Accept-Language"]}";
+ _eventAggregator.SendMessage(auditEvent);
+
await _documentsService.DeleteDocumentAsync(document, cancellationToken);
return RedirectToAction("Index", "Documents", new { Area = "User" });
@@ -102,7 +121,8 @@ public async Task DeleteDocument(int documentId, CancellationToke
[Authorize(Policy = ResgridResources.Documents_Create)]
public async Task NewDocument(NewDocumentView model, IFormFile fileToUpload, CancellationToken cancellationToken)
{
- //file = Request.Files["fileToUpload"];
+ if (!ClaimsAuthorizationHelper.CanCreateDocument())
+ Unauthorized();
if (fileToUpload == null || fileToUpload.Length <= 0)
ModelState.AddModelError("fileToUpload", "You must select a document to add.");
@@ -133,17 +153,32 @@ public async Task NewDocument(NewDocumentView model, IFormFile fi
doc.Name = model.Name;
doc.Description = model.Description;
+ if (!String.IsNullOrWhiteSpace(model.Category) && model.Category != "None")
+ doc.Category = model.Category;
+ else
+ doc.Category = null;
+
if (ClaimsAuthorizationHelper.IsUserDepartmentAdmin())
doc.AdminsOnly = bool.Parse(model.AdminOnly);
else
doc.AdminsOnly = false;
doc.Type = fileToUpload.ContentType;
- doc.Category = model.Category;
doc.Filename = fileToUpload.FileName;
+ var auditEvent = new AuditEvent();
+ auditEvent.DepartmentId = DepartmentId;
+ auditEvent.UserId = UserId;
+ auditEvent.After = doc.CloneJsonToString();
+ auditEvent.Type = AuditLogTypes.DocumentAdded;
+ auditEvent.Successful = true;
+ auditEvent.IpAddress = IpAddressHelper.GetRequestIP(Request, true);
+ auditEvent.ServerName = Environment.MachineName;
+ auditEvent.UserAgent = $"{Request.Headers["User-Agent"]} {Request.Headers["Accept-Language"]}";
+ _eventAggregator.SendMessage(auditEvent);
+
byte[] uploadedFile = new byte[fileToUpload.OpenReadStream().Length];
- fileToUpload.OpenReadStream().Read(uploadedFile, 0, uploadedFile.Length);
+ await fileToUpload.OpenReadStream().ReadAsync(uploadedFile, 0, uploadedFile.Length);
doc.Data = uploadedFile;
@@ -171,6 +206,14 @@ public async Task EditDocument(int documentId)
if (document.AdminsOnly && !ClaimsAuthorizationHelper.IsUserDepartmentAdmin())
Unauthorized();
+ if (!ClaimsAuthorizationHelper.CanCreateDocument())
+ Unauthorized();
+
+ List noteCategories = new List();
+ noteCategories.Add(new DocumentCategory { Name = "None" });
+ noteCategories.AddRange(await _documentsService.GetAllCategoriesByDepartmentIdAsync(DepartmentId));
+ model.Categories = new SelectList(noteCategories, "Name", "Name");
+
model.Name = document.Name;
model.Description = document.Description;
model.Category = document.Category;
@@ -194,6 +237,9 @@ public async Task EditDocument(EditDocumentView model, IFormFile
if (document.AdminsOnly && !ClaimsAuthorizationHelper.IsUserDepartmentAdmin())
Unauthorized();
+ if (!ClaimsAuthorizationHelper.CanCreateDocument())
+ Unauthorized();
+
if (fileToUpload != null && fileToUpload.Length > 0)
{
var extenion = fileToUpload.FileName.Substring(fileToUpload.FileName.IndexOf(char.Parse(".")) + 1,
@@ -214,6 +260,9 @@ public async Task EditDocument(EditDocumentView model, IFormFile
if (ModelState.IsValid)
{
+ var auditEvent = new AuditEvent();
+ auditEvent.Before = $"{document.DocumentId} - {document.Name} - {document.Description} - {document.Category} - {document.AdminsOnly}";
+
document.Name = model.Name;
document.Description = model.Description;
@@ -222,7 +271,10 @@ public async Task EditDocument(EditDocumentView model, IFormFile
else
document.AdminsOnly = false;
- document.Category = model.Category;
+ if (!String.IsNullOrWhiteSpace(model.Category) && model.Category != "None")
+ document.Category = model.Category;
+ else
+ document.Category = null;
if (fileToUpload != null && fileToUpload.Length > 0)
{
@@ -230,16 +282,31 @@ public async Task EditDocument(EditDocumentView model, IFormFile
document.Filename = fileToUpload.FileName;
byte[] uploadedFile = new byte[fileToUpload.OpenReadStream().Length];
- fileToUpload.OpenReadStream().Read(uploadedFile, 0, uploadedFile.Length);
+ await fileToUpload.OpenReadStream().ReadAsync(uploadedFile, 0, uploadedFile.Length);
document.Data = uploadedFile;
}
+ auditEvent.DepartmentId = DepartmentId;
+ auditEvent.UserId = UserId;
+ auditEvent.After = $"{document.DocumentId} - {document.Name} - {document.Description} - {document.Category} - {document.AdminsOnly}";
+ auditEvent.Type = AuditLogTypes.DocumentEdited;
+ auditEvent.Successful = true;
+ auditEvent.IpAddress = IpAddressHelper.GetRequestIP(Request, true);
+ auditEvent.ServerName = Environment.MachineName;
+ auditEvent.UserAgent = $"{Request.Headers["User-Agent"]} {Request.Headers["Accept-Language"]}";
+ _eventAggregator.SendMessage(auditEvent);
+
await _documentsService.SaveDocumentAsync(document, cancellationToken);
return RedirectToAction("Index", "Documents", new { Area = "User" });
}
+ List noteCategories = new List();
+ noteCategories.Add(new DocumentCategory { Name = "None" });
+ noteCategories.AddRange(await _documentsService.GetAllCategoriesByDepartmentIdAsync(DepartmentId));
+ model.Categories = new SelectList(noteCategories, "Name", "Name");
+
return View(model);
}
}
diff --git a/Web/Resgrid.WebCore/Areas/User/Controllers/LogsController.cs b/Web/Resgrid.WebCore/Areas/User/Controllers/LogsController.cs
index 3b3e6c92..e4c806d8 100644
--- a/Web/Resgrid.WebCore/Areas/User/Controllers/LogsController.cs
+++ b/Web/Resgrid.WebCore/Areas/User/Controllers/LogsController.cs
@@ -87,6 +87,9 @@ public async Task NewLog()
await PopulateLogViewModel(model);
model.Log = new Log();
+ if (model.Call == null)
+ model.Call = new Call();
+
return View(model);
}
diff --git a/Web/Resgrid.WebCore/Areas/User/Controllers/NotesController.cs b/Web/Resgrid.WebCore/Areas/User/Controllers/NotesController.cs
index fedbb7f4..bfa34205 100644
--- a/Web/Resgrid.WebCore/Areas/User/Controllers/NotesController.cs
+++ b/Web/Resgrid.WebCore/Areas/User/Controllers/NotesController.cs
@@ -1,14 +1,23 @@
using System;
+using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Elasticsearch.Net;
using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Rendering;
using Resgrid.Framework;
+using Resgrid.Localization.Areas.User.Notes;
using Resgrid.Model;
+using Resgrid.Model.Events;
+using Resgrid.Model.Providers;
using Resgrid.Model.Services;
+using Resgrid.Services;
using Resgrid.Web.Areas.User.Models.Notes;
using Resgrid.Web.Helpers;
+using AuditEvent = Resgrid.Model.Events.AuditEvent;
using IndexView = Resgrid.Web.Areas.User.Models.Notes.IndexView;
+using Note = Resgrid.Model.Note;
namespace Resgrid.Web.Areas.User.Controllers
{
@@ -17,11 +26,15 @@ public class NotesController : SecureBaseController
{
private readonly INotesService _notesService;
private readonly IDepartmentsService _departmentsService;
+ private readonly IAuthorizationService _authorizationService;
+ private readonly IEventAggregator _eventAggregator;
- public NotesController(INotesService notesService, IDepartmentsService departmentsService)
+ public NotesController(INotesService notesService, IDepartmentsService departmentsService, IAuthorizationService authorizationService, IEventAggregator eventAggregator)
{
_notesService = notesService;
_departmentsService = departmentsService;
+ _authorizationService = authorizationService;
+ _eventAggregator = eventAggregator;
}
public async Task Index()
@@ -35,14 +48,25 @@ public async Task Index()
[HttpGet]
public async Task NewNote()
{
+ if (!await _authorizationService.CanUserAddNoteAsync(UserId))
+ Unauthorized();
+
NewNoteView model = new NewNoteView();
+ List noteCategories = new List();
+ noteCategories.Add(new NoteCategory { Name = "None" });
+ noteCategories.AddRange(await _notesService.GetAllCategoriesByDepartmentIdAsync(DepartmentId));
+ model.Categories = new SelectList(noteCategories, "Name", "Name");
+
return View(model);
}
[HttpPost]
public async Task NewNote(NewNoteView model, CancellationToken cancellationToken)
{
+ if (!await _authorizationService.CanUserAddNoteAsync(UserId))
+ Unauthorized();
+
if (ModelState.IsValid)
{
Note note = new Note();
@@ -52,18 +76,36 @@ public async Task NewNote(NewNoteView model, CancellationToken ca
note.Title = model.Title;
note.Body = System.Net.WebUtility.HtmlDecode(model.Body);
+ if (!String.IsNullOrWhiteSpace(model.Category) && model.Category != "None")
+ note.Category = model.Category;
+
if (ClaimsAuthorizationHelper.IsUserDepartmentAdmin())
note.IsAdminOnly = bool.Parse(model.IsAdminOnly);
else
note.IsAdminOnly = false;
- note.Category = model.Category;
+ var auditEvent = new AuditEvent();
+ auditEvent.DepartmentId = DepartmentId;
+ auditEvent.UserId = UserId;
+ auditEvent.After = note.CloneJsonToString();
+ auditEvent.Type = AuditLogTypes.NoteAdded;
+ auditEvent.Successful = true;
+ auditEvent.IpAddress = IpAddressHelper.GetRequestIP(Request, true);
+ auditEvent.ServerName = Environment.MachineName;
+ auditEvent.UserAgent = $"{Request.Headers["User-Agent"]} {Request.Headers["Accept-Language"]}";
+ _eventAggregator.SendMessage(auditEvent);
+
await _notesService.SaveAsync(note, cancellationToken);
return RedirectToAction("Index", "Notes", new { Area = "User" });
}
+ List noteCategories = new List();
+ noteCategories.Add(new NoteCategory { Name = "None" });
+ noteCategories.AddRange(await _notesService.GetAllCategoriesByDepartmentIdAsync(DepartmentId));
+ model.Categories = new SelectList(noteCategories, "Name", "Name");
+
return View(model);
}
@@ -90,14 +132,18 @@ public async Task Edit(int noteId)
var note = await _notesService.GetNoteByIdAsync(noteId);
- if (note.DepartmentId != DepartmentId)
+ if (!await _authorizationService.CanUserEditNoteAsync(UserId, noteId))
Unauthorized();
+ List noteCategories = new List();
+ noteCategories.Add(new NoteCategory { Name = "None" });
+ noteCategories.AddRange(await _notesService.GetAllCategoriesByDepartmentIdAsync(DepartmentId));
+ model.Categories = new SelectList(noteCategories, "Name", "Name");
+
model.NoteId = note.NoteId;
model.Title = note.Title;
model.Body = note.Body;
model.IsAdminOnly = note.IsAdminOnly.ToString();
-
model.Category = note.Category;
return View(model);
@@ -113,24 +159,45 @@ public async Task Edit(EditNoteView model, CancellationToken canc
if (savedNote.DepartmentId != DepartmentId)
Unauthorized();
+ var auditEvent = new AuditEvent();
+ auditEvent.Before = savedNote.CloneJsonToString();
+
savedNote.DepartmentId = DepartmentId;
savedNote.UserId = UserId;
savedNote.AddedOn = DateTime.UtcNow;
savedNote.Title = model.Title;
savedNote.Body = System.Net.WebUtility.HtmlDecode(model.Body);
+ if (!String.IsNullOrWhiteSpace(model.Category) && model.Category != "None")
+ savedNote.Category = model.Category;
+ else
+ savedNote.Category = null;
+
if (ClaimsAuthorizationHelper.IsUserDepartmentAdmin())
savedNote.IsAdminOnly = bool.Parse(model.IsAdminOnly);
else
savedNote.IsAdminOnly = false;
- savedNote.Category = model.Category;
+ auditEvent.DepartmentId = DepartmentId;
+ auditEvent.UserId = UserId;
+ auditEvent.After = savedNote.CloneJsonToString();
+ auditEvent.Type = AuditLogTypes.NoteEdited;
+ auditEvent.Successful = true;
+ auditEvent.IpAddress = IpAddressHelper.GetRequestIP(Request, true);
+ auditEvent.ServerName = Environment.MachineName;
+ auditEvent.UserAgent = $"{Request.Headers["User-Agent"]} {Request.Headers["Accept-Language"]}";
+ _eventAggregator.SendMessage(auditEvent);
await _notesService.SaveAsync(savedNote, cancellationToken);
return RedirectToAction("Index", "Notes", new { Area = "User" });
}
+ List noteCategories = new List();
+ noteCategories.Add(new NoteCategory { Name = "None" });
+ noteCategories.AddRange(await _notesService.GetAllCategoriesByDepartmentIdAsync(DepartmentId));
+ model.Categories = new SelectList(noteCategories, "Name", "Name");
+
return View(model);
}
@@ -139,9 +206,20 @@ public async Task Delete(int noteId, CancellationToken cancellati
{
var note = await _notesService.GetNoteByIdAsync(noteId);
- if (note.DepartmentId != DepartmentId)
+ if (!await _authorizationService.CanUserEditNoteAsync(UserId, noteId))
Unauthorized();
+ var auditEvent = new AuditEvent();
+ auditEvent.DepartmentId = DepartmentId;
+ auditEvent.UserId = UserId;
+ auditEvent.Before = note.CloneJsonToString();
+ auditEvent.Type = AuditLogTypes.NoteRemoved;
+ auditEvent.Successful = true;
+ auditEvent.IpAddress = IpAddressHelper.GetRequestIP(Request, true);
+ auditEvent.ServerName = Environment.MachineName;
+ auditEvent.UserAgent = $"{Request.Headers["User-Agent"]} {Request.Headers["Accept-Language"]}";
+ _eventAggregator.SendMessage(auditEvent);
+
await _notesService.DeleteAsync(note, cancellationToken);
return RedirectToAction("Index", "Notes", new { Area = "User" });
diff --git a/Web/Resgrid.WebCore/Areas/User/Controllers/TypesController.cs b/Web/Resgrid.WebCore/Areas/User/Controllers/TypesController.cs
index e3a941da..ece2c92b 100644
--- a/Web/Resgrid.WebCore/Areas/User/Controllers/TypesController.cs
+++ b/Web/Resgrid.WebCore/Areas/User/Controllers/TypesController.cs
@@ -11,6 +11,18 @@
using System.Threading;
using System.Threading.Tasks;
using Resgrid.WebCore.Areas.User.Models.Types;
+using Resgrid.Model.Events;
+using Resgrid.Framework;
+using Resgrid.Providers.Claims;
+using Resgrid.Web.Helpers;
+using Resgrid.Web.Areas.User.Models.Departments;
+using Microsoft.AspNetCore.Authorization;
+using CallType = Resgrid.Model.CallType;
+using IAuthorizationService = Resgrid.Model.Services.IAuthorizationService;
+using SharpKml.Dom;
+using FirebaseAdmin.Messaging;
+using Resgrid.Web.Areas.User.Models.Departments.UnitSettings;
+using Resgrid.Services;
namespace Resgrid.Web.Areas.User.Controllers
{
@@ -21,25 +33,116 @@ public class TypesController : SecureBaseController
private readonly ICustomStateService _customStateService;
private readonly ICallsService _callsService;
private readonly IDepartmentSettingsService _departmentSettingsService;
-
- public TypesController(IUnitsService unitsService, ICustomStateService customStateService, ICallsService callsService, IDepartmentSettingsService departmentSettingsService)
+ private readonly IAuthorizationService _authorizationService;
+ private readonly IEventAggregator _eventAggregator;
+ private readonly ICertificationService _certificationService;
+ private readonly IDocumentsService _documentsService;
+ private readonly INotesService _notesService;
+
+ public TypesController(IUnitsService unitsService, ICustomStateService customStateService, ICallsService callsService, IDepartmentSettingsService departmentSettingsService,
+ IAuthorizationService authorizationService, IEventAggregator eventAggregator, ICertificationService certificationService, IDocumentsService documentsService, INotesService notesService)
{
_unitsService = unitsService;
_customStateService = customStateService;
_callsService = callsService;
_departmentSettingsService = departmentSettingsService;
+ _authorizationService = authorizationService;
+ _eventAggregator = eventAggregator;
+ _certificationService = certificationService;
+ _documentsService = documentsService;
+ _notesService = notesService;
}
#region Edit Unit Type
[HttpGet]
- public async Task EditUnitType(int unitTypeId)
+ [Authorize(Policy = ResgridResources.Department_Update)]
+ public async Task NewUnitType()
{
+ if (!await _authorizationService.CanUserAddUnitTypeAsync(UserId))
+ Unauthorized();
+
var model = new EditUnitTypeView();
- model.UnitType = await _unitsService.GetUnitTypeByIdAsync(unitTypeId);
+ model.UnitType = new UnitType();
+ model.UnitType.MapIconType = -1;
+
+ var states = new List();
+ states.Add(new CustomState
+ {
+ Name = "Standard Actions"
+ });
+ states.AddRange(await _customStateService.GetAllActiveUnitStatesForDepartmentAsync(DepartmentId));
+ model.States = states;
+
+ return View(model);
+ }
+
+ [HttpGet]
+ [Authorize(Policy = ResgridResources.Department_Update)]
+ public async Task DeleteUnitType(int unitTypeId, CancellationToken cancellationToken)
+ {
+ if (!await _authorizationService.CanUserEditUnitTypeAsync(UserId, unitTypeId))
+ Unauthorized();
+
+ var unitType = await _unitsService.GetUnitTypeByIdAsync(unitTypeId);
+
+ var auditEvent = new AuditEvent();
+ auditEvent.DepartmentId = DepartmentId;
+ auditEvent.UserId = UserId;
+ auditEvent.Before = unitType.CloneJsonToString();
+ auditEvent.Type = AuditLogTypes.UnitTypeRemoved;
+ auditEvent.Successful = true;
+ auditEvent.IpAddress = IpAddressHelper.GetRequestIP(Request, true);
+ auditEvent.ServerName = Environment.MachineName;
+ auditEvent.UserAgent = $"{Request.Headers["User-Agent"]} {Request.Headers["Accept-Language"]}";
+ _eventAggregator.SendMessage(auditEvent);
+
+ await _unitsService.DeleteUnitTypeAsync(unitTypeId, cancellationToken);
+
+ return RedirectToAction("Types", "Department", new { Area = "User" });
+ }
+
+ [HttpPost]
+ [Authorize(Policy = ResgridResources.Department_Update)]
+ public async Task NewUnitType(EditUnitTypeView model, CancellationToken cancellationToken)
+ {
+ if (!await _authorizationService.CanUserAddUnitTypeAsync(UserId))
+ Unauthorized();
+
+ if (String.IsNullOrWhiteSpace(model.UnitType.Type))
+ ModelState.AddModelError("NewUnitType", "You Must specify the new unit type.");
- if (model.UnitType == null || model.UnitType.DepartmentId != DepartmentId)
+ if (ModelState.IsValid)
+ {
+ var auditEvent = new AuditEvent();
+ auditEvent.DepartmentId = DepartmentId;
+ auditEvent.UserId = UserId;
+ auditEvent.After = model.UnitType.CloneJsonToString();
+ auditEvent.Type = AuditLogTypes.UnitTypeAdded;
+ auditEvent.Successful = true;
+ auditEvent.IpAddress = IpAddressHelper.GetRequestIP(Request, true);
+ auditEvent.ServerName = Environment.MachineName;
+ auditEvent.UserAgent = $"{Request.Headers["User-Agent"]} {Request.Headers["Accept-Language"]}";
+ _eventAggregator.SendMessage(auditEvent);
+
+ if (model.UnitType.CustomStatesId.HasValue && model.UnitType.CustomStatesId.Value > 0)
+ await _unitsService.AddUnitTypeAsync(DepartmentId, model.UnitType.Type, model.UnitType.CustomStatesId.Value, model.UnitType.MapIconType, cancellationToken);
+ else
+ await _unitsService.AddUnitTypeAsync(DepartmentId, model.UnitType.Type, model.UnitType.MapIconType, cancellationToken);
+ }
+
+ return RedirectToAction("Types", "Department", new { Area = "User" });
+ }
+
+ [HttpGet]
+ [Authorize(Policy = ResgridResources.Department_Update)]
+ public async Task EditUnitType(int unitTypeId)
+ {
+ if (!await _authorizationService.CanUserEditUnitTypeAsync(UserId, unitTypeId))
Unauthorized();
+ var model = new EditUnitTypeView();
+ model.UnitType = await _unitsService.GetUnitTypeByIdAsync(unitTypeId);
+
var states = new List();
states.Add(new CustomState
{
@@ -58,6 +161,9 @@ public async Task EditUnitType(int unitTypeId)
[HttpPost]
public async Task EditUnitType(EditUnitTypeView model, CancellationToken cancellationToken)
{
+ if (!await _authorizationService.CanUserEditUnitTypeAsync(UserId, model.UnitType.UnitTypeId))
+ Unauthorized();
+
var states = new List();
states.Add(new CustomState
{
@@ -74,8 +180,8 @@ public async Task EditUnitType(EditUnitTypeView model, Cancellati
{
var unitType = await _unitsService.GetUnitTypeByIdAsync(model.UnitType.UnitTypeId);
- if (unitType == null || unitType.DepartmentId != DepartmentId)
- Unauthorized();
+ var auditEvent = new AuditEvent();
+ auditEvent.Before = unitType.CloneJsonToString();
unitType.Type = model.UnitType.Type;
@@ -89,6 +195,16 @@ public async Task EditUnitType(EditUnitTypeView model, Cancellati
else
unitType.MapIconType = null;
+ auditEvent.DepartmentId = DepartmentId;
+ auditEvent.UserId = UserId;
+ auditEvent.After = unitType.CloneJsonToString();
+ auditEvent.Type = AuditLogTypes.UnitTypeEdited;
+ auditEvent.Successful = true;
+ auditEvent.IpAddress = IpAddressHelper.GetRequestIP(Request, true);
+ auditEvent.ServerName = Environment.MachineName;
+ auditEvent.UserAgent = $"{Request.Headers["User-Agent"]} {Request.Headers["Accept-Language"]}";
+ _eventAggregator.SendMessage(auditEvent);
+
await _unitsService.SaveUnitTypeAsync(unitType, cancellationToken);
return RedirectToAction("Types", "Department", new { Area = "User" });
@@ -100,14 +216,65 @@ public async Task EditUnitType(EditUnitTypeView model, Cancellati
#region Edit Call Type
[HttpGet]
- public async Task EditCallType(int callTypeId)
+ [Authorize(Policy = ResgridResources.Department_Update)]
+ public async Task NewCallType()
{
+ if (!await _authorizationService.CanUserAddCallTypeAsync(UserId))
+ Unauthorized();
+
var model = new EditCallTypeView();
- model.CallType = await _callsService.GetCallTypeByIdAsync(callTypeId);
+ model.CallType = new CallType();
+ model.CallType.MapIconType = -1;
+
+ return View(model);
+ }
+
+ [HttpPost]
+ [Authorize(Policy = ResgridResources.Department_Update)]
+ public async Task NewCallType(EditCallTypeView model, CancellationToken cancellationToken)
+ {
+ if (String.IsNullOrEmpty(model.CallType.Type))
+ ModelState.AddModelError("NewCallType", "You Must specify the new call type.");
+
+ if (!await _authorizationService.CanUserAddCallTypeAsync(UserId))
+ Unauthorized();
+
+ if (ModelState.IsValid)
+ {
+ CallType newCallType = new CallType();
+ newCallType.DepartmentId = DepartmentId;
+ newCallType.Type = model.CallType.Type;
+
+ if (model.CallTypeIcon >= 0)
+ newCallType.MapIconType = model.CallTypeIcon;
+
+ var auditEvent = new AuditEvent();
+ auditEvent.DepartmentId = DepartmentId;
+ auditEvent.UserId = UserId;
+ auditEvent.Type = AuditLogTypes.CallTypeAdded;
+ auditEvent.After = newCallType.CloneJsonToString();
+ auditEvent.Successful = true;
+ auditEvent.IpAddress = IpAddressHelper.GetRequestIP(Request, true);
+ auditEvent.ServerName = Environment.MachineName;
+ auditEvent.UserAgent = $"{Request.Headers["User-Agent"]} {Request.Headers["Accept-Language"]}";
+ _eventAggregator.SendMessage(auditEvent);
+
+ await _callsService.SaveCallTypeAsync(newCallType, cancellationToken);
+ }
+
+ return RedirectToAction("Types", "Department", new { Area = "User" });
+ }
- if (model.CallType == null || model.CallType.DepartmentId != DepartmentId)
+ [HttpGet]
+ [Authorize(Policy = ResgridResources.Department_Update)]
+ public async Task EditCallType(int callTypeId)
+ {
+ if (!await _authorizationService.CanUserModifyCallTypeAsync(UserId, callTypeId))
Unauthorized();
+ var model = new EditCallTypeView();
+ model.CallType = await _callsService.GetCallTypeByIdAsync(callTypeId);
+
if (model.CallType.MapIconType.HasValue)
model.CallTypeIcon = model.CallType.MapIconType.Value;
else
@@ -117,15 +284,18 @@ public async Task EditCallType(int callTypeId)
}
[HttpPost]
+ [Authorize(Policy = ResgridResources.Department_Update)]
public async Task EditCallType(EditCallTypeView model, CancellationToken cancellationToken)
{
if (ModelState.IsValid)
{
+ var auditEvent = new AuditEvent();
var callType = await _callsService.GetCallTypeByIdAsync(model.CallType.CallTypeId);
- if (callType == null || callType.DepartmentId != DepartmentId)
+ if (!await _authorizationService.CanUserModifyCallTypeAsync(UserId, model.CallType.CallTypeId))
Unauthorized();
-
+
+ auditEvent.Before = callType.CloneJsonToString();
callType.Type = model.CallType.Type;
if (model.CallTypeIcon >= 0)
@@ -133,6 +303,18 @@ public async Task EditCallType(EditCallTypeView model, Cancellati
else
callType.MapIconType = null;
+
+ auditEvent.DepartmentId = DepartmentId;
+ auditEvent.UserId = UserId;
+ auditEvent.Type = AuditLogTypes.CallTypeEdited;
+ auditEvent.After = callType.CloneJsonToString();
+ auditEvent.Successful = true;
+ auditEvent.IpAddress = IpAddressHelper.GetRequestIP(Request, true);
+ auditEvent.ServerName = Environment.MachineName;
+ auditEvent.UserAgent = $"{Request.Headers["User-Agent"]} {Request.Headers["Accept-Language"]}";
+ _eventAggregator.SendMessage(auditEvent);
+
+
await _callsService.SaveCallTypeAsync(callType, cancellationToken);
return RedirectToAction("Types", "Department", new { Area = "User" });
@@ -140,12 +322,40 @@ public async Task EditCallType(EditCallTypeView model, Cancellati
return View(model);
}
+
+ [HttpGet]
+ [Authorize(Policy = ResgridResources.Department_Update)]
+ public async Task DeleteCallType(int callTypeId, CancellationToken cancellationToken)
+ {
+ var callType = await _callsService.GetCallTypeByIdAsync(callTypeId);
+ if (!await _authorizationService.CanUserModifyCallTypeAsync(UserId, callTypeId))
+ Unauthorized();
+
+ var auditEvent = new AuditEvent();
+ auditEvent.DepartmentId = DepartmentId;
+ auditEvent.UserId = UserId;
+ auditEvent.Type = AuditLogTypes.CallTypeRemoved;
+ auditEvent.Before = callType.CloneJsonToString();
+ auditEvent.Successful = true;
+ auditEvent.IpAddress = IpAddressHelper.GetRequestIP(Request, true);
+ auditEvent.ServerName = Environment.MachineName;
+ auditEvent.UserAgent = $"{Request.Headers["User-Agent"]} {Request.Headers["Accept-Language"]}";
+ _eventAggregator.SendMessage(auditEvent);
+
+ await _callsService.DeleteCallTypeAsync(callTypeId, cancellationToken);
+
+ return RedirectToAction("Types", "Department", new { Area = "User" });
+ }
#endregion Edit Call Type
#region Call Priority
[HttpGet]
+ [Authorize(Policy = ResgridResources.Department_Update)]
public async Task NewCallPriority()
{
+ if (!await _authorizationService.CanUserAddCallPriorityAsync(UserId))
+ Unauthorized();
+
var model = new NewCallPriorityView();
model.CallPriority = new DepartmentCallPriority();
model.AlertSounds = model.AudioType.ToSelectListInt();
@@ -154,8 +364,12 @@ public async Task NewCallPriority()
}
[HttpPost]
+ [Authorize(Policy = ResgridResources.Department_Update)]
public async Task NewCallPriority(NewCallPriorityView model, IFormFile pushfileToUpload, IFormFile iOSPushfileToUpload, IFormFile alertfileToUpload, CancellationToken cancellationToken)
{
+ if (!await _authorizationService.CanUserAddCallPriorityAsync(UserId))
+ Unauthorized();
+
var priotiries = await _callsService.GetActiveCallPrioritiesForDepartmentAsync(DepartmentId, true);
if (model.CallPriority.IsDefault && priotiries.Any(x => x.IsDefault && x.DepartmentId == DepartmentId && x.IsDeleted == false))
@@ -170,39 +384,39 @@ public async Task NewCallPriority(NewCallPriorityView model, IFor
return View(model);
}
- if (pushfileToUpload != null && pushfileToUpload.Length > 0)
- {
- var extenion = Path.GetExtension(pushfileToUpload.FileName);
+ //if (pushfileToUpload != null && pushfileToUpload.Length > 0)
+ //{
+ // var extenion = Path.GetExtension(pushfileToUpload.FileName);
- if (!String.IsNullOrWhiteSpace(extenion))
- extenion = extenion.ToLower();
+ // if (!String.IsNullOrWhiteSpace(extenion))
+ // extenion = extenion.ToLower();
- if (extenion != ".mp3")
- ModelState.AddModelError("pushfileToUpload", string.Format("Audio file type ({0}) is not supported for Push Notifications.", extenion));
+ // if (extenion != ".mp3")
+ // ModelState.AddModelError("pushfileToUpload", string.Format("Audio file type ({0}) is not supported for Push Notifications.", extenion));
- if (pushfileToUpload.Length > 1000000)
- ModelState.AddModelError("pushfileToUpload", "Android Push Audio file is too large, must be smaller then 1MB.");
- }
+ // if (pushfileToUpload.Length > 1000000)
+ // ModelState.AddModelError("pushfileToUpload", "Android Push Audio file is too large, must be smaller then 1MB.");
+ //}
- if (iOSPushfileToUpload != null && iOSPushfileToUpload.Length > 0)
- {
- var extenion = Path.GetExtension(iOSPushfileToUpload.FileName);
+ //if (iOSPushfileToUpload != null && iOSPushfileToUpload.Length > 0)
+ //{
+ // var extenion = Path.GetExtension(iOSPushfileToUpload.FileName);
- if (!String.IsNullOrWhiteSpace(extenion))
- extenion = extenion.ToLower();
+ // if (!String.IsNullOrWhiteSpace(extenion))
+ // extenion = extenion.ToLower();
- if (extenion != ".caf")
- ModelState.AddModelError("iOSPushfileToUpload", string.Format("Audio file type ({0}) is not supported for iOS Push Notifications.", extenion));
+ // if (extenion != ".caf")
+ // ModelState.AddModelError("iOSPushfileToUpload", string.Format("Audio file type ({0}) is not supported for iOS Push Notifications.", extenion));
- if (iOSPushfileToUpload.Length > 1000000)
- ModelState.AddModelError("iOSPushfileToUpload", "iOS Push Audio file is too large, must be smaller then 1MB.");
+ // if (iOSPushfileToUpload.Length > 1000000)
+ // ModelState.AddModelError("iOSPushfileToUpload", "iOS Push Audio file is too large, must be smaller then 1MB.");
- //var fileAudioLength = await _audioValidatorProvider.GetWavFileDuration(pushfileToUpload.OpenReadStream());
- //if (fileAudioLength == null)
- // ModelState.AddModelError("iOSPushfileToUpload", string.Format("Audio file type ({0}) is not supported for Push Notifications. CAF Files are required.", extenion));
- //else if (fileAudioLength != null && fileAudioLength.Value > new TimeSpan(0, 0, 25))
- // ModelState.AddModelError("iOSPushfileToUpload", string.Format("iOS Push audio file length is longer then 25 seconds. iOS Push notification sounds must be 25 seconds or shorter.", extenion));
- }
+ // //var fileAudioLength = await _audioValidatorProvider.GetWavFileDuration(pushfileToUpload.OpenReadStream());
+ // //if (fileAudioLength == null)
+ // // ModelState.AddModelError("iOSPushfileToUpload", string.Format("Audio file type ({0}) is not supported for Push Notifications. CAF Files are required.", extenion));
+ // //else if (fileAudioLength != null && fileAudioLength.Value > new TimeSpan(0, 0, 25))
+ // // ModelState.AddModelError("iOSPushfileToUpload", string.Format("iOS Push audio file length is longer then 25 seconds. iOS Push notification sounds must be 25 seconds or shorter.", extenion));
+ //}
if (alertfileToUpload != null && alertfileToUpload.Length > 0)
{
@@ -225,32 +439,43 @@ public async Task NewCallPriority(NewCallPriorityView model, IFor
if (ModelState.IsValid)
{
- if (alertfileToUpload != null && alertfileToUpload.Length > 0)
- {
- byte[] uploadedFile = new byte[alertfileToUpload.OpenReadStream().Length];
- alertfileToUpload.OpenReadStream().Read(uploadedFile, 0, uploadedFile.Length);
+ //if (alertfileToUpload != null && alertfileToUpload.Length > 0)
+ //{
+ // byte[] uploadedFile = new byte[alertfileToUpload.OpenReadStream().Length];
+ // alertfileToUpload.OpenReadStream().Read(uploadedFile, 0, uploadedFile.Length);
- model.CallPriority.ShortNotificationSound = uploadedFile;
- }
+ // model.CallPriority.ShortNotificationSound = uploadedFile;
+ //}
- if (pushfileToUpload != null && pushfileToUpload.Length > 0)
- {
- byte[] uploadedFile = new byte[pushfileToUpload.OpenReadStream().Length];
- pushfileToUpload.OpenReadStream().Read(uploadedFile, 0, uploadedFile.Length);
+ //if (pushfileToUpload != null && pushfileToUpload.Length > 0)
+ //{
+ // byte[] uploadedFile = new byte[pushfileToUpload.OpenReadStream().Length];
+ // pushfileToUpload.OpenReadStream().Read(uploadedFile, 0, uploadedFile.Length);
- model.CallPriority.PushNotificationSound = uploadedFile;
- }
+ // model.CallPriority.PushNotificationSound = uploadedFile;
+ //}
- if (iOSPushfileToUpload != null && iOSPushfileToUpload.Length > 0)
- {
- byte[] uploadedFile = new byte[iOSPushfileToUpload.OpenReadStream().Length];
- iOSPushfileToUpload.OpenReadStream().Read(uploadedFile, 0, uploadedFile.Length);
+ //if (iOSPushfileToUpload != null && iOSPushfileToUpload.Length > 0)
+ //{
+ // byte[] uploadedFile = new byte[iOSPushfileToUpload.OpenReadStream().Length];
+ // iOSPushfileToUpload.OpenReadStream().Read(uploadedFile, 0, uploadedFile.Length);
- model.CallPriority.IOSPushNotificationSound = uploadedFile;
- }
+ // model.CallPriority.IOSPushNotificationSound = uploadedFile;
+ //}
model.CallPriority.DepartmentId = DepartmentId;
+ var auditEvent = new AuditEvent();
+ auditEvent.DepartmentId = DepartmentId;
+ auditEvent.UserId = UserId;
+ auditEvent.Type = AuditLogTypes.CallPriorityAdded;
+ auditEvent.After = model.CallPriority.CloneJsonToString();
+ auditEvent.Successful = true;
+ auditEvent.IpAddress = IpAddressHelper.GetRequestIP(Request, true);
+ auditEvent.ServerName = Environment.MachineName;
+ auditEvent.UserAgent = $"{Request.Headers["User-Agent"]} {Request.Headers["Accept-Language"]}";
+ _eventAggregator.SendMessage(auditEvent);
+
await _callsService.SaveCallPriorityAsync(model.CallPriority, cancellationToken);
return RedirectToAction("Types", "Department", new { Area = "User" });
@@ -261,13 +486,30 @@ public async Task NewCallPriority(NewCallPriorityView model, IFor
}
[HttpGet]
+ [Authorize(Policy = ResgridResources.Department_Update)]
public async Task DeleteCallPriority(int priorityId, CancellationToken cancellationToken)
{
+ if (!await _authorizationService.CanUserDeleteCallPriorityAsync(UserId, priorityId))
+ Unauthorized();
+
var priority = await _callsService.GetCallPrioritiesByIdAsync(DepartmentId, priorityId, true);
if (priority != null)
{
+ var auditEvent = new AuditEvent();
+ auditEvent.Before = priority.CloneJsonToString();
+
priority.IsDeleted = true;
+
+ auditEvent.DepartmentId = DepartmentId;
+ auditEvent.UserId = UserId;
+ auditEvent.Type = AuditLogTypes.CallPriorityRemoved;
+ auditEvent.Successful = true;
+ auditEvent.IpAddress = IpAddressHelper.GetRequestIP(Request, true);
+ auditEvent.ServerName = Environment.MachineName;
+ auditEvent.UserAgent = $"{Request.Headers["User-Agent"]} {Request.Headers["Accept-Language"]}";
+ _eventAggregator.SendMessage(auditEvent);
+
await _callsService.SaveCallPriorityAsync(priority, cancellationToken);
}
@@ -275,8 +517,12 @@ public async Task DeleteCallPriority(int priorityId, Cancellation
}
[HttpGet]
+ [Authorize(Policy = ResgridResources.Department_Update)]
public async Task EditCallPriority(int priorityId)
{
+ if (!await _authorizationService.CanUserEditCallPriorityAsync(UserId, priorityId))
+ Unauthorized();
+
var model = new EditCallPriorityView();
model.CallPriority = await _callsService.GetCallPrioritiesByIdAsync(DepartmentId, priorityId, true);
@@ -289,8 +535,12 @@ public async Task EditCallPriority(int priorityId)
}
[HttpPost]
+ [Authorize(Policy = ResgridResources.Department_Update)]
public async Task EditCallPriority(EditCallPriorityView model, IFormFile pushfileToUpload, IFormFile iOSPushfileToUpload, IFormFile alertfileToUpload, CancellationToken cancellationToken)
{
+ if (!await _authorizationService.CanUserEditCallPriorityAsync(UserId, model.CallPriority.DepartmentCallPriorityId))
+ Unauthorized();
+
var priotiries = await _callsService.GetActiveCallPrioritiesForDepartmentAsync(DepartmentId, true);
if (model.CallPriority.IsDefault && priotiries.Any(x => x.IsDefault && x.DepartmentCallPriorityId != model.CallPriority.DepartmentCallPriorityId))
{
@@ -298,57 +548,61 @@ public async Task EditCallPriority(EditCallPriorityView model, IF
return View(model);
}
- if (pushfileToUpload != null && pushfileToUpload.Length > 0)
- {
- var extenion = Path.GetExtension(pushfileToUpload.FileName);
+ //if (pushfileToUpload != null && pushfileToUpload.Length > 0)
+ //{
+ // var extenion = Path.GetExtension(pushfileToUpload.FileName);
- if (!String.IsNullOrWhiteSpace(extenion))
- extenion = extenion.ToLower();
+ // if (!String.IsNullOrWhiteSpace(extenion))
+ // extenion = extenion.ToLower();
- if (extenion != "wav")
- ModelState.AddModelError("pushfileToUpload", string.Format("Audio file type ({0}) is not supported for Push Notifications.", extenion));
+ // if (extenion != "wav")
+ // ModelState.AddModelError("pushfileToUpload", string.Format("Audio file type ({0}) is not supported for Push Notifications.", extenion));
- if (pushfileToUpload.Length > 1000000)
- ModelState.AddModelError("pushfileToUpload", "Push Audio file is too large, must be smaller then 1MB.");
- }
+ // if (pushfileToUpload.Length > 1000000)
+ // ModelState.AddModelError("pushfileToUpload", "Push Audio file is too large, must be smaller then 1MB.");
+ //}
- if (iOSPushfileToUpload != null && iOSPushfileToUpload.Length > 0)
- {
- var extenion = Path.GetExtension(iOSPushfileToUpload.FileName);
+ //if (iOSPushfileToUpload != null && iOSPushfileToUpload.Length > 0)
+ //{
+ // var extenion = Path.GetExtension(iOSPushfileToUpload.FileName);
- if (!String.IsNullOrWhiteSpace(extenion))
- extenion = extenion.ToLower();
+ // if (!String.IsNullOrWhiteSpace(extenion))
+ // extenion = extenion.ToLower();
- if (extenion != ".caf")
- ModelState.AddModelError("iOSPushfileToUpload", string.Format("Audio file type ({0}) is not supported for iOS Push Notifications.", extenion));
+ // if (extenion != ".caf")
+ // ModelState.AddModelError("iOSPushfileToUpload", string.Format("Audio file type ({0}) is not supported for iOS Push Notifications.", extenion));
- if (iOSPushfileToUpload.Length > 1000000)
- ModelState.AddModelError("iOSPushfileToUpload", "iOS Push Audio file is too large, must be smaller then 1MB.");
+ // if (iOSPushfileToUpload.Length > 1000000)
+ // ModelState.AddModelError("iOSPushfileToUpload", "iOS Push Audio file is too large, must be smaller then 1MB.");
- //var fileAudioLength = await _audioValidatorProvider.GetWavFileDuration(pushfileToUpload.OpenReadStream());
- //if (fileAudioLength == null)
- // ModelState.AddModelError("iOSPushfileToUpload", string.Format("Audio file type ({0}) is not supported for Push Notifications. CAF Files are required.", extenion));
- //else if (fileAudioLength != null && fileAudioLength.Value > new TimeSpan(0, 0, 25))
- // ModelState.AddModelError("iOSPushfileToUpload", string.Format("iOS Push audio file length is longer then 25 seconds. iOS Push notification sounds must be 25 seconds or shorter.", extenion));
- }
+ // //var fileAudioLength = await _audioValidatorProvider.GetWavFileDuration(pushfileToUpload.OpenReadStream());
+ // //if (fileAudioLength == null)
+ // // ModelState.AddModelError("iOSPushfileToUpload", string.Format("Audio file type ({0}) is not supported for Push Notifications. CAF Files are required.", extenion));
+ // //else if (fileAudioLength != null && fileAudioLength.Value > new TimeSpan(0, 0, 25))
+ // // ModelState.AddModelError("iOSPushfileToUpload", string.Format("iOS Push audio file length is longer then 25 seconds. iOS Push notification sounds must be 25 seconds or shorter.", extenion));
+ //}
- if (alertfileToUpload != null && alertfileToUpload.Length > 0)
- {
- var extenion = Path.GetExtension(alertfileToUpload.FileName);
+ //if (alertfileToUpload != null && alertfileToUpload.Length > 0)
+ //{
+ // var extenion = Path.GetExtension(alertfileToUpload.FileName);
- if (!String.IsNullOrWhiteSpace(extenion))
- extenion = extenion.ToLower();
+ // if (!String.IsNullOrWhiteSpace(extenion))
+ // extenion = extenion.ToLower();
- if (extenion != "wav")
- ModelState.AddModelError("alertfileToUpload", string.Format("Audio file type ({0}) is not supported for Alert Notifications.", extenion));
+ // if (extenion != "wav")
+ // ModelState.AddModelError("alertfileToUpload", string.Format("Audio file type ({0}) is not supported for Alert Notifications.", extenion));
- if (alertfileToUpload.Length > 1000000)
- ModelState.AddModelError("alertfileToUpload", "Push Audio file is too large, must be smaller then 1MB.");
- }
+ // if (alertfileToUpload.Length > 1000000)
+ // ModelState.AddModelError("alertfileToUpload", "Push Audio file is too large, must be smaller then 1MB.");
+ //}
if (ModelState.IsValid)
{
var priority = await _callsService.GetCallPrioritiesByIdAsync(DepartmentId, model.CallPriority.DepartmentCallPriorityId, true);
+
+ var auditEvent = new AuditEvent();
+ auditEvent.Before = priority.CloneJsonToString();
+
priority.Name = model.CallPriority.Name;
priority.Color = model.CallPriority.Color;
priority.IsDefault = model.CallPriority.IsDefault;
@@ -357,30 +611,40 @@ public async Task EditCallPriority(EditCallPriorityView model, IF
priority.ForceNotifyAllPersonnel = model.CallPriority.ForceNotifyAllPersonnel;
priority.Tone = model.CallPriority.Tone;
- if (alertfileToUpload != null && alertfileToUpload.Length > 0)
- {
- byte[] uploadedFile = new byte[alertfileToUpload.OpenReadStream().Length];
- alertfileToUpload.OpenReadStream().Read(uploadedFile, 0, uploadedFile.Length);
+ //if (alertfileToUpload != null && alertfileToUpload.Length > 0)
+ //{
+ // byte[] uploadedFile = new byte[alertfileToUpload.OpenReadStream().Length];
+ // alertfileToUpload.OpenReadStream().Read(uploadedFile, 0, uploadedFile.Length);
- priority.ShortNotificationSound = uploadedFile;
- }
+ // priority.ShortNotificationSound = uploadedFile;
+ //}
- if (pushfileToUpload != null && pushfileToUpload.Length > 0)
- {
- byte[] uploadedFile = new byte[pushfileToUpload.OpenReadStream().Length];
- pushfileToUpload.OpenReadStream().Read(uploadedFile, 0, uploadedFile.Length);
+ //if (pushfileToUpload != null && pushfileToUpload.Length > 0)
+ //{
+ // byte[] uploadedFile = new byte[pushfileToUpload.OpenReadStream().Length];
+ // pushfileToUpload.OpenReadStream().Read(uploadedFile, 0, uploadedFile.Length);
- priority.PushNotificationSound = uploadedFile;
- }
+ // priority.PushNotificationSound = uploadedFile;
+ //}
- if (iOSPushfileToUpload != null && iOSPushfileToUpload.Length > 0)
- {
- byte[] uploadedFile = new byte[iOSPushfileToUpload.OpenReadStream().Length];
- iOSPushfileToUpload.OpenReadStream().Read(uploadedFile, 0, uploadedFile.Length);
+ //if (iOSPushfileToUpload != null && iOSPushfileToUpload.Length > 0)
+ //{
+ // byte[] uploadedFile = new byte[iOSPushfileToUpload.OpenReadStream().Length];
+ // iOSPushfileToUpload.OpenReadStream().Read(uploadedFile, 0, uploadedFile.Length);
- model.CallPriority.IOSPushNotificationSound = uploadedFile;
- }
+ // model.CallPriority.IOSPushNotificationSound = uploadedFile;
+ //}
+
+ auditEvent.DepartmentId = DepartmentId;
+ auditEvent.UserId = UserId;
+ auditEvent.Type = AuditLogTypes.CallPriorityEdited;
+ auditEvent.After = priority.CloneJsonToString();
+ auditEvent.Successful = true;
+ auditEvent.IpAddress = IpAddressHelper.GetRequestIP(Request, true);
+ auditEvent.ServerName = Environment.MachineName;
+ auditEvent.UserAgent = $"{Request.Headers["User-Agent"]} {Request.Headers["Accept-Language"]}";
+ _eventAggregator.SendMessage(auditEvent);
await _callsService.SaveCallPriorityAsync(priority, cancellationToken);
return RedirectToAction("Types", "Department", new { Area = "User" });
@@ -390,6 +654,247 @@ public async Task EditCallPriority(EditCallPriorityView model, IF
}
#endregion Call Priority
+ #region Certification Types
+ [HttpGet]
+ [Authorize(Policy = ResgridResources.Department_Update)]
+ public async Task DeleteCertificationType(int certificationTypeId, CancellationToken cancellationToken)
+ {
+ if (certificationTypeId <= 0)
+ return RedirectToAction("Types", "Department", new { Area = "User" });
+
+ if (!await _authorizationService.CanUserDeleteCertificationTypeAsync(UserId, certificationTypeId))
+ Unauthorized();
+
+ var type = await _certificationService.GetCertificationTypeByIdAsync(certificationTypeId);
+
+ var auditEvent = new AuditEvent();
+ auditEvent.DepartmentId = DepartmentId;
+ auditEvent.UserId = UserId;
+ auditEvent.Before = type.CloneJsonToString();
+ auditEvent.Type = AuditLogTypes.CertificationTypeRemoved;
+ auditEvent.Successful = true;
+ auditEvent.IpAddress = IpAddressHelper.GetRequestIP(Request, true);
+ auditEvent.ServerName = Environment.MachineName;
+ auditEvent.UserAgent = $"{Request.Headers["User-Agent"]} {Request.Headers["Accept-Language"]}";
+ _eventAggregator.SendMessage(auditEvent);
+
+ await _certificationService.DeleteCertificationTypeByIdAsync(certificationTypeId, cancellationToken);
+
+ return RedirectToAction("Types", "Department", new { Area = "User" });
+ }
+
+ [HttpGet]
+ [Authorize(Policy = ResgridResources.Department_Update)]
+ public async Task NewCertificationType()
+ {
+ if (!await _authorizationService.CanUserAddCertificationTypeAsync(UserId))
+ Unauthorized();
+
+ var model = new NewCertificationTypeView();
+ model.Type = new DepartmentCertificationType();
+
+ return View(model);
+ }
+
+ [HttpPost]
+ [Authorize(Policy = ResgridResources.Department_Update)]
+ public async Task NewCertificationType(NewCertificationTypeView model, CancellationToken cancellationToken)
+ {
+ if (!await _authorizationService.CanUserAddCertificationTypeAsync(UserId))
+ Unauthorized();
+
+ if (String.IsNullOrEmpty(model.Type.Type))
+ ModelState.AddModelError("NewCertificationType", "You Must specify the new certification type.");
+
+ if (await _certificationService.DoesCertificationTypeAlreadyExistAsync(DepartmentId, model.Type.Type))
+ ModelState.AddModelError("Name", "Supplied new Certification Type already exists, please change the name of the Certification Type and try again.");
+
+ if (ModelState.IsValid)
+ {
+ var auditEvent = new AuditEvent();
+ auditEvent.DepartmentId = DepartmentId;
+ auditEvent.UserId = UserId;
+ auditEvent.After = model.Type.CloneJsonToString();
+ auditEvent.Type = AuditLogTypes.CertificationTypeAdded;
+ auditEvent.Successful = true;
+ auditEvent.IpAddress = IpAddressHelper.GetRequestIP(Request, true);
+ auditEvent.ServerName = Environment.MachineName;
+ auditEvent.UserAgent = $"{Request.Headers["User-Agent"]} {Request.Headers["Accept-Language"]}";
+ _eventAggregator.SendMessage(auditEvent);
+
+ await _certificationService.SaveNewCertificationTypeAsync(model.Type.Type.Trim(), DepartmentId, cancellationToken);
+
+ return RedirectToAction("Types", "Department", new { Area = "User" });
+ }
+
+ return View(model);
+ }
+ #endregion Certification Types
+
+ #region Document Types
+ [HttpGet]
+ [Authorize(Policy = ResgridResources.Department_Update)]
+ public async Task NewDocumentType()
+ {
+ if (!await _authorizationService.CanUserAddDocumentTypeAsync(UserId))
+ Unauthorized();
+
+ var model = new NewDocumentCategoryView();
+
+ return View(model);
+ }
+
+ [HttpPost]
+ [Authorize(Policy = ResgridResources.Department_Update)]
+ public async Task NewDocumentType(NewDocumentCategoryView model, CancellationToken cancellationToken)
+ {
+ if (!await _authorizationService.CanUserAddDocumentTypeAsync(UserId))
+ Unauthorized();
+
+ if (String.IsNullOrEmpty(model.Name))
+ ModelState.AddModelError("Name", "You Must specify the new document category name.");
+
+ if (await _documentsService.DoesDocumentCategoryAlreadyExistAsync(DepartmentId, model.Name))
+ ModelState.AddModelError("Name", "Supplied new Document Category already exists, please change the category name and try again.");
+
+ if (ModelState.IsValid)
+ {
+ var category = new DocumentCategory();
+ category.DepartmentId = DepartmentId;
+ category.AddedById = UserId;
+ category.AddedOn = DateTime.UtcNow;
+ category.Name = model.Name.Trim();
+
+ var auditEvent = new AuditEvent();
+ auditEvent.DepartmentId = DepartmentId;
+ auditEvent.UserId = UserId;
+ auditEvent.After = category.CloneJsonToString();
+ auditEvent.Type = AuditLogTypes.DocumentCategoryAdded;
+ auditEvent.Successful = true;
+ auditEvent.IpAddress = IpAddressHelper.GetRequestIP(Request, true);
+ auditEvent.ServerName = Environment.MachineName;
+ auditEvent.UserAgent = $"{Request.Headers["User-Agent"]} {Request.Headers["Accept-Language"]}";
+ _eventAggregator.SendMessage(auditEvent);
+
+ await _documentsService.SaveDocumentCategoryAsync(category, cancellationToken);
+
+ return RedirectToAction("Types", "Department", new { Area = "User" });
+ }
+
+ return View(model);
+ }
+
+ [HttpGet]
+ [Authorize(Policy = ResgridResources.Department_Update)]
+ public async Task DeleteDocumentType(string documentTypeId, CancellationToken cancellationToken)
+ {
+ if (String.IsNullOrWhiteSpace(documentTypeId))
+ return RedirectToAction("Types", "Department", new { Area = "User" });
+
+ if (!await _authorizationService.CanUserDeleteDocumentTypeAsync(UserId, documentTypeId))
+ Unauthorized();
+
+ var type = await _documentsService.GetDocumentCategoryByIdAsync(documentTypeId);
+
+ var auditEvent = new AuditEvent();
+ auditEvent.DepartmentId = DepartmentId;
+ auditEvent.UserId = UserId;
+ auditEvent.Before = type.CloneJsonToString();
+ auditEvent.Type = AuditLogTypes.DocumentCategoryRemoved;
+ auditEvent.Successful = true;
+ auditEvent.IpAddress = IpAddressHelper.GetRequestIP(Request, true);
+ auditEvent.ServerName = Environment.MachineName;
+ auditEvent.UserAgent = $"{Request.Headers["User-Agent"]} {Request.Headers["Accept-Language"]}";
+ _eventAggregator.SendMessage(auditEvent);
+
+ await _documentsService.DeleteDocumentCategoryAsync(type, cancellationToken);
+
+ return RedirectToAction("Types", "Department", new { Area = "User" });
+ }
+ #endregion Document Types
+
+ #region Note Types
+ [HttpGet]
+ [Authorize(Policy = ResgridResources.Department_Update)]
+ public async Task NewNoteType()
+ {
+ if (!await _authorizationService.CanUserAddNoteTypeAsync(UserId))
+ Unauthorized();
+
+ var model = new NewNoteCategoryView();
+
+ return View(model);
+ }
+
+ [HttpPost]
+ [Authorize(Policy = ResgridResources.Department_Update)]
+ public async Task NewNoteType(NewNoteCategoryView model, CancellationToken cancellationToken)
+ {
+ if (!await _authorizationService.CanUserAddNoteTypeAsync(UserId))
+ Unauthorized();
+
+ if (String.IsNullOrEmpty(model.Name))
+ ModelState.AddModelError("Name", "You Must specify the new note category name.");
+
+ if (await _notesService.DoesNoteTypeAlreadyExistAsync(DepartmentId, model.Name))
+ ModelState.AddModelError("Name", "Supplied new Note Type already exists, please change the type name and try again.");
+
+ if (ModelState.IsValid)
+ {
+ var category = new NoteCategory();
+ category.DepartmentId = DepartmentId;
+ category.AddedById = UserId;
+ category.AddedOn = DateTime.UtcNow;
+ category.Name = model.Name.Trim();
+
+ var auditEvent = new AuditEvent();
+ auditEvent.DepartmentId = DepartmentId;
+ auditEvent.UserId = UserId;
+ auditEvent.After = category.CloneJsonToString();
+ auditEvent.Type = AuditLogTypes.NoteCategoryAdded;
+ auditEvent.Successful = true;
+ auditEvent.IpAddress = IpAddressHelper.GetRequestIP(Request, true);
+ auditEvent.ServerName = Environment.MachineName;
+ auditEvent.UserAgent = $"{Request.Headers["User-Agent"]} {Request.Headers["Accept-Language"]}";
+ _eventAggregator.SendMessage(auditEvent);
+
+ await _notesService.SaveNoteCategoryAsync(category, cancellationToken);
+
+ return RedirectToAction("Types", "Department", new { Area = "User" });
+ }
+
+ return View(model);
+ }
+
+ [HttpGet]
+ [Authorize(Policy = ResgridResources.Department_Update)]
+ public async Task DeleteNoteType(string noteTypeId, CancellationToken cancellationToken)
+ {
+ if (String.IsNullOrWhiteSpace(noteTypeId))
+ return RedirectToAction("Types", "Department", new { Area = "User" });
+
+ if (!await _authorizationService.CanUserDeleteNoteTypeAsync(UserId, noteTypeId))
+ Unauthorized();
+
+ var type = await _notesService.GetNoteCategoryByIdAsync(noteTypeId);
+
+ var auditEvent = new AuditEvent();
+ auditEvent.DepartmentId = DepartmentId;
+ auditEvent.UserId = UserId;
+ auditEvent.Before = type.CloneJsonToString();
+ auditEvent.Type = AuditLogTypes.NoteCategoryRemoved;
+ auditEvent.Successful = true;
+ auditEvent.IpAddress = IpAddressHelper.GetRequestIP(Request, true);
+ auditEvent.ServerName = Environment.MachineName;
+ auditEvent.UserAgent = $"{Request.Headers["User-Agent"]} {Request.Headers["Accept-Language"]}";
+ _eventAggregator.SendMessage(auditEvent);
+
+ await _notesService.DeleteNoteCategoryAsync(type, cancellationToken);
+
+ return RedirectToAction("Types", "Department", new { Area = "User" });
+ }
+ #endregion Note Types
+
#region List Ordering
[HttpGet]
public async Task ListOrdering()
@@ -404,8 +909,8 @@ public async Task ListOrdering()
if (model.AllPersonnelStatuses != null)
{
var availableStatuses = from status in model.AllPersonnelStatuses
- where !model.PersonnelStatusOrders.Select(x => x.StatusId).Contains(status.CustomStateDetailId)
- select status;
+ where !model.PersonnelStatusOrders.Select(x => x.StatusId).Contains(status.CustomStateDetailId)
+ select status;
model.AvailablePersonnelStatuses = availableStatuses.ToList();
}
@@ -417,8 +922,8 @@ public async Task ListOrdering()
public async Task SavePersonnelStatusListOrdering(IFormCollection form, CancellationToken cancellationToken)
{
List options = (from object key in form.Keys
- where key.ToString().StartsWith("personnelStatus_")
- select int.Parse(key.ToString().Replace("personnelStatus_", ""))).ToList();
+ where key.ToString().StartsWith("personnelStatus_")
+ select int.Parse(key.ToString().Replace("personnelStatus_", ""))).ToList();
if (options != null || options.Any())
{
diff --git a/Web/Resgrid.WebCore/Areas/User/Models/CustomStatuses/EditDetailView.cs b/Web/Resgrid.WebCore/Areas/User/Models/CustomStatuses/EditDetailView.cs
index 3d6d1bdd..cd761d4b 100644
--- a/Web/Resgrid.WebCore/Areas/User/Models/CustomStatuses/EditDetailView.cs
+++ b/Web/Resgrid.WebCore/Areas/User/Models/CustomStatuses/EditDetailView.cs
@@ -10,5 +10,7 @@ public class EditDetailView
public SelectList DetailTypes { get; set; }
public CustomStateNoteTypes NoteType { get; set; }
public SelectList NoteTypes { get; set; }
+ public ActionBaseTypes BaseType { get; set; }
+ public SelectList BaseTypes { get; set; }
}
-}
\ No newline at end of file
+}
diff --git a/Web/Resgrid.WebCore/Areas/User/Models/Departments/DepartmentTypesView.cs b/Web/Resgrid.WebCore/Areas/User/Models/Departments/DepartmentTypesView.cs
index cf846c29..da07a8da 100644
--- a/Web/Resgrid.WebCore/Areas/User/Models/Departments/DepartmentTypesView.cs
+++ b/Web/Resgrid.WebCore/Areas/User/Models/Departments/DepartmentTypesView.cs
@@ -18,5 +18,7 @@ public class DepartmentTypesView
public int CallTypeIcon { get; set; }
public List States { get; set; }
public List CallPriorites { get; set; }
+ public List