diff --git a/DigitalLearningSolutions.Web.AutomatedUiTests/AccessibilityTests/BasicAccessibilityTests.cs b/DigitalLearningSolutions.Web.AutomatedUiTests/AccessibilityTests/BasicAccessibilityTests.cs index bc9e6bc2d4..a94d430a02 100644 --- a/DigitalLearningSolutions.Web.AutomatedUiTests/AccessibilityTests/BasicAccessibilityTests.cs +++ b/DigitalLearningSolutions.Web.AutomatedUiTests/AccessibilityTests/BasicAccessibilityTests.cs @@ -39,6 +39,7 @@ public void Page_has_no_accessibility_errors(string url, string pageTitle) [InlineData("/NotificationPreferences/Edit/AdminUser", "Update notification preferences")] [InlineData("/NotificationPreferences/Edit/DelegateUser", "Update notification preferences")] [InlineData("/ChangePassword", "Change password")] + [InlineData("/TrackingSystem/Support", "Support")] public void Authenticated_page_has_no_accessibility_errors(string url, string pageTitle) { // when diff --git a/DigitalLearningSolutions.Web.Tests/Controllers/LearningSolutions/LearningSolutionsControllerTests.cs b/DigitalLearningSolutions.Web.Tests/Controllers/LearningSolutions/LearningSolutionsControllerTests.cs index 40c9a59739..934c777b48 100644 --- a/DigitalLearningSolutions.Web.Tests/Controllers/LearningSolutions/LearningSolutionsControllerTests.cs +++ b/DigitalLearningSolutions.Web.Tests/Controllers/LearningSolutions/LearningSolutionsControllerTests.cs @@ -3,7 +3,7 @@ using System.Security.Claims; using DigitalLearningSolutions.Data.DataServices; using DigitalLearningSolutions.Data.Services; - using DigitalLearningSolutions.Web.Controllers; + using DigitalLearningSolutions.Web.Controllers.LearningSolutions; using DigitalLearningSolutions.Web.ViewModels.LearningSolutions; using FakeItEasy; using FluentAssertions; diff --git a/DigitalLearningSolutions.Web.Tests/Controllers/Support/SupportControllerTests.cs b/DigitalLearningSolutions.Web.Tests/Controllers/Support/SupportControllerTests.cs new file mode 100644 index 0000000000..236507cb31 --- /dev/null +++ b/DigitalLearningSolutions.Web.Tests/Controllers/Support/SupportControllerTests.cs @@ -0,0 +1,38 @@ +namespace DigitalLearningSolutions.Web.Tests.Controllers.Support +{ + using DigitalLearningSolutions.Web.Controllers.Support; + using FluentAssertions.AspNetCore.Mvc; + using NUnit.Framework; + + public class SupportControllerTests + { + private SupportController controller = null!; + + [SetUp] + public void SetUp() + { + controller = new SupportController(); + } + + [TestCase("TrackingSystem")] + [TestCase("Frameworks")] + public void Support_page_should_be_shown_for_valid_application_names(string applicationName) + { + // When + var result = controller.Index(applicationName); + + // Then + result.Should().BeViewResult().WithViewName("Support"); + } + + [Test] + public void Invalid_application_name_should_redirect_to_404_page() + { + // When + var result = controller.Index("Main"); + + // Then + result.Should().BeNotFoundResult(); + } + } +} diff --git a/DigitalLearningSolutions.Web/Controllers/LearningSolutionsController.cs b/DigitalLearningSolutions.Web/Controllers/LearningSolutions/LearningSolutionsController.cs similarity index 93% rename from DigitalLearningSolutions.Web/Controllers/LearningSolutionsController.cs rename to DigitalLearningSolutions.Web/Controllers/LearningSolutions/LearningSolutionsController.cs index dd39592451..c87bd6c838 100644 --- a/DigitalLearningSolutions.Web/Controllers/LearningSolutionsController.cs +++ b/DigitalLearningSolutions.Web/Controllers/LearningSolutions/LearningSolutionsController.cs @@ -1,10 +1,9 @@ -namespace DigitalLearningSolutions.Web.Controllers +namespace DigitalLearningSolutions.Web.Controllers.LearningSolutions { using DigitalLearningSolutions.Data.DataServices; using DigitalLearningSolutions.Data.Services; using DigitalLearningSolutions.Web.Helpers; using DigitalLearningSolutions.Web.ViewModels.LearningSolutions; - using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; diff --git a/DigitalLearningSolutions.Web/Controllers/Support/SupportController.cs b/DigitalLearningSolutions.Web/Controllers/Support/SupportController.cs new file mode 100644 index 0000000000..abef7e18ca --- /dev/null +++ b/DigitalLearningSolutions.Web/Controllers/Support/SupportController.cs @@ -0,0 +1,25 @@ +namespace DigitalLearningSolutions.Web.Controllers.Support +{ + using DigitalLearningSolutions.Web.Helpers; + using DigitalLearningSolutions.Web.Models.Enums; + using DigitalLearningSolutions.Web.ViewModels.Support; + using Microsoft.AspNetCore.Authorization; + using Microsoft.AspNetCore.Mvc; + + public class SupportController : Controller + { + [Route("/{application}/Support")] + [Authorize(Policy = CustomPolicies.UserCentreAdminOrFrameworksAdmin)] + public IActionResult Index(ApplicationType application) + { + if (ApplicationType.TrackingSystem.Equals(application) || + ApplicationType.Frameworks.Equals(application)) + { + var model = new SupportViewModel(application, SupportPage.Support); + return View("Support", model); + } + + return NotFound(); + } + } +} diff --git a/DigitalLearningSolutions.Web/Helpers/CustomClaimHelper.cs b/DigitalLearningSolutions.Web/Helpers/CustomClaimHelper.cs index 738867bf64..7d80d07f65 100644 --- a/DigitalLearningSolutions.Web/Helpers/CustomClaimHelper.cs +++ b/DigitalLearningSolutions.Web/Helpers/CustomClaimHelper.cs @@ -83,5 +83,13 @@ public static bool HasCentreManagerPermissions(this ClaimsPrincipal user) return (user.GetCustomClaimAsBool(CustomClaimTypes.UserCentreManager) ?? false) || (user.GetCustomClaimAsBool(CustomClaimTypes.UserUserAdmin) ?? false); } + + public static bool HasFrameworksAdminPermissions(this ClaimsPrincipal user) + { + return user.GetCustomClaimAsBool(CustomClaimTypes.IsFrameworkDeveloper) == true || + user.GetCustomClaimAsBool(CustomClaimTypes.IsFrameworkContributor) == true || + user.GetCustomClaimAsBool(CustomClaimTypes.IsWorkforceManager) == true || + user.GetCustomClaimAsBool(CustomClaimTypes.IsWorkforceContributor) == true; + } } } diff --git a/DigitalLearningSolutions.Web/Helpers/CustomPolicies.cs b/DigitalLearningSolutions.Web/Helpers/CustomPolicies.cs index e24e8b57bc..85ad89697e 100644 --- a/DigitalLearningSolutions.Web/Helpers/CustomPolicies.cs +++ b/DigitalLearningSolutions.Web/Helpers/CustomPolicies.cs @@ -8,6 +8,7 @@ public class CustomPolicies public const string UserCentreAdmin = "UserCentreAdmin"; public const string UserFrameworksAdminOnly = "UserFrameworksAdminOnly"; public const string UserCentreManager = "UserCentreManager"; + public const string UserCentreAdminOrFrameworksAdmin = "UserCentreAdminOrFrameworksAdmin"; public static AuthorizationPolicyBuilder ConfigurePolicyUserOnly(AuthorizationPolicyBuilder policy) { @@ -48,5 +49,16 @@ AuthorizationPolicyBuilder policy context.User.GetCustomClaimAsBool(CustomClaimTypes.UserUserAdmin) == true) ); } + + public static AuthorizationPolicyBuilder ConfigurePolicyUserCentreAdminOrFrameworksAdmin( + AuthorizationPolicyBuilder policy + ) + { + return policy.RequireAssertion( + context => context.User.GetCustomClaimAsInt(CustomClaimTypes.UserAdminId) != null + && (context.User.HasCentreAdminPermissions() + || context.User.HasFrameworksAdminPermissions()) + ); + } } } diff --git a/DigitalLearningSolutions.Web/Models/Enums/ApplicationType.cs b/DigitalLearningSolutions.Web/Models/Enums/ApplicationType.cs new file mode 100644 index 0000000000..83e68bd054 --- /dev/null +++ b/DigitalLearningSolutions.Web/Models/Enums/ApplicationType.cs @@ -0,0 +1,39 @@ +namespace DigitalLearningSolutions.Web.Models.Enums +{ + using System; + using DigitalLearningSolutions.Data.Enums; + using DigitalLearningSolutions.Web.Helpers; + + public class ApplicationType : Enumeration + { + public static readonly ApplicationType TrackingSystem = new ApplicationType(0, nameof(TrackingSystem), "Tracking System"); + public static readonly ApplicationType Frameworks = new ApplicationType(1, nameof(Frameworks), "Frameworks"); + public static readonly ApplicationType Main = new ApplicationType(2, nameof(Main), "Main"); + + private ApplicationType(int id, string name, string applicationName) : base(id, name) + { + ApplicationName = applicationName; + + HeaderPath = name.Equals("TrackingSystem") + ? $"{ConfigHelper.GetAppConfig()["AppRootPath"]}/TrackingSystem/Centre/Dashboard" + : null; + } + + public readonly string ApplicationName; + public readonly string? HeaderPath; + + public static implicit operator ApplicationType(string value) + { + try + { + return FromName(value); + } + catch (InvalidOperationException e) + { + throw new InvalidCastException(e.Message); + } + } + + public static implicit operator string(ApplicationType applicationType) => applicationType.Name; + } +} diff --git a/DigitalLearningSolutions.Web/Models/Enums/SupportPage.cs b/DigitalLearningSolutions.Web/Models/Enums/SupportPage.cs new file mode 100644 index 0000000000..64411a9e37 --- /dev/null +++ b/DigitalLearningSolutions.Web/Models/Enums/SupportPage.cs @@ -0,0 +1,12 @@ +namespace DigitalLearningSolutions.Web.Models.Enums +{ + public enum SupportPage + { + Support, + HelpDocumentation, + Faqs, + Resources, + SupportTickets, + ChangeRequests + } +} diff --git a/DigitalLearningSolutions.Web/Startup.cs b/DigitalLearningSolutions.Web/Startup.cs index 0803d8b771..b5771c4f73 100644 --- a/DigitalLearningSolutions.Web/Startup.cs +++ b/DigitalLearningSolutions.Web/Startup.cs @@ -77,6 +77,10 @@ public void ConfigureServices(IServiceCollection services) CustomPolicies.UserCentreManager, policy => CustomPolicies.ConfigurePolicyUserCentreManager(policy) ); + options.AddPolicy( + CustomPolicies.UserCentreAdminOrFrameworksAdmin, + policy => CustomPolicies.ConfigurePolicyUserCentreAdminOrFrameworksAdmin(policy) + ); } ); @@ -230,4 +234,4 @@ private Task RedirectToHome(RedirectContext context return Task.CompletedTask; } } -} +} diff --git a/DigitalLearningSolutions.Web/Styles/support/support.scss b/DigitalLearningSolutions.Web/Styles/support/support.scss new file mode 100644 index 0000000000..702ec2ae08 --- /dev/null +++ b/DigitalLearningSolutions.Web/Styles/support/support.scss @@ -0,0 +1,5 @@ +@import "~nhsuk-frontend/packages/core/all"; + +ol>li { + @include nhsuk-responsive-margin(5, "bottom"); +} diff --git a/DigitalLearningSolutions.Web/ViewModels/Support/SupportViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/Support/SupportViewModel.cs new file mode 100644 index 0000000000..ee5a4917fb --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/Support/SupportViewModel.cs @@ -0,0 +1,17 @@ +namespace DigitalLearningSolutions.Web.ViewModels.Support +{ + using DigitalLearningSolutions.Web.Models.Enums; + + public class SupportViewModel + { + public SupportViewModel(ApplicationType application, SupportPage currentPage) + { + CurrentPage = currentPage; + + Application = application; + } + + public SupportPage CurrentPage { get; set; } + public ApplicationType Application { get; set; } + } +} diff --git a/DigitalLearningSolutions.Web/Views/ApplicationSelector/Index.cshtml b/DigitalLearningSolutions.Web/Views/ApplicationSelector/Index.cshtml index 2b3de9e898..88dd18f088 100644 --- a/DigitalLearningSolutions.Web/Views/ApplicationSelector/Index.cshtml +++ b/DigitalLearningSolutions.Web/Views/ApplicationSelector/Index.cshtml @@ -5,6 +5,10 @@ @model ApplicationSelectorViewModel +@section NavMenuItems { + +} + @{ ViewData["Title"] = "Switch Application"; } diff --git a/DigitalLearningSolutions.Web/Views/ChangePassword/Index.cshtml b/DigitalLearningSolutions.Web/Views/ChangePassword/Index.cshtml index 44e6ca4e3a..78f6997da4 100644 --- a/DigitalLearningSolutions.Web/Views/ChangePassword/Index.cshtml +++ b/DigitalLearningSolutions.Web/Views/ChangePassword/Index.cshtml @@ -8,6 +8,10 @@ ViewData["Title"] = errorHasOccurred ? "Error: Change Your Password" : "Change Your Password"; } +@section NavMenuItems { + +} +
@if (errorHasOccurred) { @@ -50,6 +54,6 @@ - +
diff --git a/DigitalLearningSolutions.Web/Views/ChangePassword/Success.cshtml b/DigitalLearningSolutions.Web/Views/ChangePassword/Success.cshtml index 6be7b18457..4b12399efb 100644 --- a/DigitalLearningSolutions.Web/Views/ChangePassword/Success.cshtml +++ b/DigitalLearningSolutions.Web/Views/ChangePassword/Success.cshtml @@ -2,6 +2,10 @@ ViewData["Title"] = "Password Changed Successfully"; } +@section NavMenuItems { + +} +

Change password

diff --git a/DigitalLearningSolutions.Web/Views/FindYourCentre/Index.cshtml b/DigitalLearningSolutions.Web/Views/FindYourCentre/Index.cshtml index f9ae903148..04b1c1fda1 100644 --- a/DigitalLearningSolutions.Web/Views/FindYourCentre/Index.cshtml +++ b/DigitalLearningSolutions.Web/Views/FindYourCentre/Index.cshtml @@ -4,6 +4,10 @@ ViewData["Title"] = "Find Your Centre"; } +@section NavMenuItems { + +} +

Find your centre

diff --git a/DigitalLearningSolutions.Web/Views/ForgotPassword/Confirm.cshtml b/DigitalLearningSolutions.Web/Views/ForgotPassword/Confirm.cshtml index 8953bb867c..4a9b74e00f 100644 --- a/DigitalLearningSolutions.Web/Views/ForgotPassword/Confirm.cshtml +++ b/DigitalLearningSolutions.Web/Views/ForgotPassword/Confirm.cshtml @@ -1,15 +1,17 @@ -@using DigitalLearningSolutions.Web.Helpers -@{ - ViewData["Title"] = "Password Reset Email Sent"; -} - -
-
-

Password reset email sent

-

An email has been sent to you giving details of how to reset your password.

-

The link provided in that email will expire in two hours.

-

If you have not received an email, please contact your centre administrator.

-

To find out your administrator details, search for your centre here.

-
-
- +@{ + ViewData["Title"] = "Password Reset Email Sent"; +} + +@section NavMenuItems { + +} + +
+
+

Password reset email sent

+

An email has been sent to you giving details of how to reset your password.

+

The link provided in that email will expire in two hours.

+

If you have not received an email, please contact your centre administrator.

+

To find out your administrator details, search for your centre here.

+
+
diff --git a/DigitalLearningSolutions.Web/Views/ForgotPassword/Index.cshtml b/DigitalLearningSolutions.Web/Views/ForgotPassword/Index.cshtml index a3677a8690..4f547d463a 100644 --- a/DigitalLearningSolutions.Web/Views/ForgotPassword/Index.cshtml +++ b/DigitalLearningSolutions.Web/Views/ForgotPassword/Index.cshtml @@ -1,47 +1,51 @@ -@using DigitalLearningSolutions.Web.ViewModels.ForgotPassword -@model ForgotPasswordViewModel -@{ - bool errorHasOccurred = !ViewData.ModelState.IsValid; - ViewData["Title"] = errorHasOccurred ? "Error: Reset Your Password" : "Reset Your Password"; - var inputErrorClass = errorHasOccurred ? "nhsuk-input--error" : ""; - var formErrorClass = errorHasOccurred ? "nhsuk-form-group--error" : ""; -} - -
-
- @if (errorHasOccurred) - { - - } - -

Reset your password

- -
-
- - @if (errorHasOccurred) - { - - Error: @ViewData.ModelState["EmailAddress"].Errors[0].ErrorMessage - - } - -
- - -
- -

- Alternatively, log in or register for an account: -

- - Log in - - - Register - - -
-
+@using DigitalLearningSolutions.Web.ViewModels.ForgotPassword +@model ForgotPasswordViewModel +@{ + bool errorHasOccurred = !ViewData.ModelState.IsValid; + ViewData["Title"] = errorHasOccurred ? "Error: Reset Your Password" : "Reset Your Password"; + var inputErrorClass = errorHasOccurred ? "nhsuk-input--error" : ""; + var formErrorClass = errorHasOccurred ? "nhsuk-form-group--error" : ""; +} + +@section NavMenuItems { + +} + +
+
+ @if (errorHasOccurred) + { + + } + +

Reset your password

+ +
+
+ + @if (errorHasOccurred) + { + + Error: @ViewData.ModelState["EmailAddress"].Errors[0].ErrorMessage + + } + +
+ + +
+ +

+ Alternatively, log in or register for an account: +

+ + Log in + + + Register + + +
+
diff --git a/DigitalLearningSolutions.Web/Views/Home/LearningContent.cshtml b/DigitalLearningSolutions.Web/Views/Home/LearningContent.cshtml index 760d2d1ece..e3da0f3eee 100644 --- a/DigitalLearningSolutions.Web/Views/Home/LearningContent.cshtml +++ b/DigitalLearningSolutions.Web/Views/Home/LearningContent.cshtml @@ -5,7 +5,11 @@ ViewData["Title"] = "Welcome - Learning Content"; } - + + +@section NavMenuItems { + +}
@@ -40,7 +44,7 @@
- Reasonable Adjustment Flag logo + Reasonable Adjustment Flag logo

The reasonable adjustment flag enables health and care professionals to record, share and view patients’ reasonable adjustments across the NHS. This enables staff and services to carry out this anticipatory duty wherever the patient is treated.

@@ -58,7 +62,7 @@
- NHS Digital logo + NHS Digital logo

Learning resources relating to Clinical and Pharmacy Terminology and Clinical Classifications products, including ICD, OPCS, SNOMED CT and dm+d. These resources will help you to understand and make best use of these products.

diff --git a/DigitalLearningSolutions.Web/Views/Home/Products.cshtml b/DigitalLearningSolutions.Web/Views/Home/Products.cshtml index ca0751cb9e..0895beeac8 100644 --- a/DigitalLearningSolutions.Web/Views/Home/Products.cshtml +++ b/DigitalLearningSolutions.Web/Views/Home/Products.cshtml @@ -5,14 +5,18 @@ ViewData["Title"] = "Welcome - Products"; } - + + +@section NavMenuItems { + +}
- + - +

Tracking System

@@ -44,6 +48,6 @@
- +
diff --git a/DigitalLearningSolutions.Web/Views/Home/Welcome.cshtml b/DigitalLearningSolutions.Web/Views/Home/Welcome.cshtml index f60f57d8c4..175c35e312 100644 --- a/DigitalLearningSolutions.Web/Views/Home/Welcome.cshtml +++ b/DigitalLearningSolutions.Web/Views/Home/Welcome.cshtml @@ -5,14 +5,19 @@ ViewData["Title"] = "Welcome"; } - + + +@section NavMenuItems { + +}
- +
- @if (Model.UserIsLoggedIn) { + @if (Model.UserIsLoggedIn) + { Switch application @@ -26,10 +31,10 @@ }
- +

Our products and services can help you manage digital learning delivery for the benefit of the people in your organisation.

- +
diff --git a/DigitalLearningSolutions.Web/Views/LearningContent/Index.cshtml b/DigitalLearningSolutions.Web/Views/LearningContent/Index.cshtml index 6b41dc3eb0..bb8921bb87 100644 --- a/DigitalLearningSolutions.Web/Views/LearningContent/Index.cshtml +++ b/DigitalLearningSolutions.Web/Views/LearningContent/Index.cshtml @@ -9,6 +9,10 @@ var url = ConfigHelper.GetAppConfig()["CurrentSystemBaseUrl"] + "/Learning?brand=" + Model.Brand + "&nonav=true"; } +@section NavMenuItems { + +} + @section NavBreadcrumbs {