diff --git a/.github/workflows/continuous-integration-workflow.yml b/.github/workflows/continuous-integration-workflow.yml index ec55bbdde..24c2d6891 100644 --- a/.github/workflows/continuous-integration-workflow.yml +++ b/.github/workflows/continuous-integration-workflow.yml @@ -10,13 +10,75 @@ jobs: steps: - uses: actions/checkout@v3 + - name: Modify web.config files in all apps + shell: pwsh + run: | + $webConfigPaths = @( + "${{ github.workspace }}\AdminUI\LearningHub.Nhs.AdminUI\web.config", + "${{ github.workspace }}\WebAPI\LearningHub.Nhs.Api\web.config", + "${{ github.workspace }}\LearningHub.Nhs.WebUI\web.config" + ) + + foreach ($path in $webConfigPaths) { + if (Test-Path $path) { + Write-Host "Modifying $path" + [xml]$config = Get-Content $path + + if (-not $config.configuration.'system.webServer') { + $systemWebServer = $config.CreateElement("system.webServer") + $config.configuration.AppendChild($systemWebServer) | Out-Null + } else { + $systemWebServer = $config.configuration.'system.webServer' + } + + if (-not $systemWebServer.httpProtocol) { + $httpProtocol = $config.CreateElement("httpProtocol") + $systemWebServer.AppendChild($httpProtocol) | Out-Null + } else { + $httpProtocol = $systemWebServer.httpProtocol + } + + if (-not $httpProtocol.customHeaders) { + $customHeaders = $config.CreateElement("customHeaders") + $httpProtocol.AppendChild($customHeaders) | Out-Null + } else { + $customHeaders = $httpProtocol.customHeaders + } + + foreach ($name in @("X-Powered-By", "Server")) { + $removeNode = $config.CreateElement("remove") + $removeNode.SetAttribute("name", $name) + $customHeaders.AppendChild($removeNode) | Out-Null + } + + if (-not $systemWebServer.security) { + $security = $config.CreateElement("security") + $systemWebServer.AppendChild($security) | Out-Null + } else { + $security = $systemWebServer.security + } + + if (-not $security.requestFiltering) { + $requestFiltering = $config.CreateElement("requestFiltering") + $requestFiltering.SetAttribute("removeServerHeader", "true") + $security.AppendChild($requestFiltering) | Out-Null + } + + $config.Save($path) + } else { + Write-Host "File not found: $path" + } + } + - name: Setup .NET Core SDK 8.0 uses: actions/setup-dotnet@v3 with: dotnet-version: 8.0.x - name: Add Azure artifact - run: dotnet nuget add source 'https://pkgs.dev.azure.com/e-LfH/_packaging/LearningHubFeed/nuget/v3/index.json' --name 'LearningHubFeed' --username 'kevin.whittaker' --password ${{ secrets.AZURE_DEVOPS_PAT }} --store-password-in-clear-text + run: | + dotnet nuget remove source LearningHubFeed || true + dotnet nuget add source 'https://pkgs.dev.azure.com/e-LfH/_packaging/LearningHubFeed/nuget/v3/index.json' --name 'LearningHubFeed' --username 'kevin.whittaker' --password ${{ secrets.AZURE_DEVOPS_PAT }} --store-password-in-clear-text - name: Use Node 20 with Yarn uses: actions/setup-node@v4 diff --git a/.gitignore b/.gitignore index 5e8ca2030..fcd06e014 100644 --- a/.gitignore +++ b/.gitignore @@ -52,3 +52,4 @@ obj /OpenAPI/LearningHub.Nhs.OpenApi/web.config /AdminUI/LearningHub.Nhs.AdminUI/LearningHub.Nhs.AdminUI.csproj.user /WebAPI/LearningHub.Nhs.API/LearningHub.Nhs.Api.csproj.user +/ReportAPI/LearningHub.Nhs.ReportApi/web.config diff --git a/AdminUI/LearningHub.Nhs.AdminUI/Controllers/RoadmapController.cs b/AdminUI/LearningHub.Nhs.AdminUI/Controllers/RoadmapController.cs index 319c8938c..bd7261a87 100644 --- a/AdminUI/LearningHub.Nhs.AdminUI/Controllers/RoadmapController.cs +++ b/AdminUI/LearningHub.Nhs.AdminUI/Controllers/RoadmapController.cs @@ -99,7 +99,7 @@ public async Task AddUpdate(UpdateViewModel update) RoadmapTypeId = update.RoadmapTypeId, }; var roadmapId = await this.roadmapService.AddRoadmap(roadmap); - return this.RedirectToAction("EditUpdate", new { id = roadmapId }); + return this.RedirectToAction("Details", new { id = roadmapId }); } /// @@ -160,7 +160,7 @@ public async Task EditUpdate(UpdateViewModel update) Id = update.Id, }; await this.roadmapService.UpdateRoadmap(roadmap); - return this.RedirectToAction("EditUpdate", new { roadmap.Id }); + return this.RedirectToAction("Details", new { roadmap.Id }); } /// @@ -202,6 +202,18 @@ public async Task Updates(string searchTerm = null) return this.View(model); } + /// + /// The Details. + /// + /// The id. + /// The . + [HttpGet] + public async Task Details(int id) + { + var roadmap = await this.roadmapService.GetIdAsync(id); + return this.View(roadmap); + } + /// /// The UploadFile. /// diff --git a/AdminUI/LearningHub.Nhs.AdminUI/Controllers/UserGroupController.cs b/AdminUI/LearningHub.Nhs.AdminUI/Controllers/UserGroupController.cs index 1be7f211c..05364adaf 100644 --- a/AdminUI/LearningHub.Nhs.AdminUI/Controllers/UserGroupController.cs +++ b/AdminUI/LearningHub.Nhs.AdminUI/Controllers/UserGroupController.cs @@ -197,7 +197,15 @@ public async Task Details(UserGroupAdminDetailViewModel userGroup if (userGroup.IsNew()) { validationResult = await this.userGroupService.CreateUserGroup(userGroup); - userGroup = await this.userGroupService.GetUserGroupAdminDetailbyIdAsync(validationResult.CreatedId.Value); + if (validationResult.IsValid) + { + userGroup = await this.userGroupService.GetUserGroupAdminDetailbyIdAsync(validationResult.CreatedId.Value); + } + else + { + this.ViewBag.ErrorMessage = $"Update failed: {string.Join(Environment.NewLine, validationResult.Details)}"; + return this.View("Details", userGroup); + } } else { diff --git a/AdminUI/LearningHub.Nhs.AdminUI/Interfaces/IRoadmapService.cs b/AdminUI/LearningHub.Nhs.AdminUI/Interfaces/IRoadmapService.cs index cb96b6037..a0606ebc3 100644 --- a/AdminUI/LearningHub.Nhs.AdminUI/Interfaces/IRoadmapService.cs +++ b/AdminUI/LearningHub.Nhs.AdminUI/Interfaces/IRoadmapService.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using LearningHub.Nhs.Models.Entities; + using LearningHub.Nhs.Models.RoadMap; /// /// Defines the . @@ -36,6 +37,13 @@ public interface IRoadmapService /// The . Task> GetUpdates(); + /// + /// The GetIdAsync. + /// + /// The id. + /// The . + Task GetIdAsync(int id); + /// /// The UpdateRoadmap. /// diff --git a/AdminUI/LearningHub.Nhs.AdminUI/LearningHub.Nhs.AdminUI.csproj b/AdminUI/LearningHub.Nhs.AdminUI/LearningHub.Nhs.AdminUI.csproj index 1fd9ba346..ce9830312 100644 --- a/AdminUI/LearningHub.Nhs.AdminUI/LearningHub.Nhs.AdminUI.csproj +++ b/AdminUI/LearningHub.Nhs.AdminUI/LearningHub.Nhs.AdminUI.csproj @@ -1,645 +1,631 @@ - - + net8.0 - 1.0.0.0 - 1.0.0.0 - 1.0.0 + 1.0.0.0 + 1.0.0.0 + 1.0.0 31abd8b9-4223-4ff3-896b-a46530c9e15c /subscriptions/57c55d5f-78c1-4373-a021-ff8357548f51/resourceGroups/LearningHubNhsUk-AdminUI-Prod-RG/providers/microsoft.insights/components/LearningHubNhsUk-AdminUI-Prod true true - x64 + x64 - - - - - - - - - - - - - - - - - - - - - - - <_ContentIncludedByDefault Remove="bundleconfig.json" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Always - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + <_ContentIncludedByDefault Remove="bundleconfig.json" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Always + + + + + + + + \ No newline at end of file diff --git a/AdminUI/LearningHub.Nhs.AdminUI/Scripts/vuesrc/ckeditorwithhint.vue b/AdminUI/LearningHub.Nhs.AdminUI/Scripts/vuesrc/ckeditorwithhint.vue index 824b025cd..b2defb5c2 100644 --- a/AdminUI/LearningHub.Nhs.AdminUI/Scripts/vuesrc/ckeditorwithhint.vue +++ b/AdminUI/LearningHub.Nhs.AdminUI/Scripts/vuesrc/ckeditorwithhint.vue @@ -1,6 +1,6 @@ diff --git a/AdminUI/LearningHub.Nhs.AdminUI/Scripts/vuesrc/mkiomediaplayer.ts b/AdminUI/LearningHub.Nhs.AdminUI/Scripts/vuesrc/mkiomediaplayer.ts index 988dbdace..46704b4df 100644 --- a/AdminUI/LearningHub.Nhs.AdminUI/Scripts/vuesrc/mkiomediaplayer.ts +++ b/AdminUI/LearningHub.Nhs.AdminUI/Scripts/vuesrc/mkiomediaplayer.ts @@ -22,6 +22,7 @@ interface PlayerConfig { } interface SourceConfig { + enableLowLatency: boolean; hls: string; drm: { clearkey: ClearKeyConfig; @@ -57,6 +58,7 @@ function getSourceConfig( authenticationToken: string ): SourceConfig { return { + enableLowLatency: true, hls: locatorUri, drm: { clearkey: { @@ -81,6 +83,7 @@ function initializePlayer(videoContainer: HTMLElement, playerConfig: MKPlayerCon }; const sourceConfig: SourceConfig = { + enableLowLatency: true, hls: playBackUrl, drm: { clearkey: clearKeyConfig diff --git a/AdminUI/LearningHub.Nhs.AdminUI/Services/MKIOMediaService.cs b/AdminUI/LearningHub.Nhs.AdminUI/Services/MKIOMediaService.cs index fb1860afd..dc44a6959 100644 --- a/AdminUI/LearningHub.Nhs.AdminUI/Services/MKIOMediaService.cs +++ b/AdminUI/LearningHub.Nhs.AdminUI/Services/MKIOMediaService.cs @@ -91,19 +91,21 @@ public async Task CreateMediaInputAsset(IFormFile file) /// . public async Task DownloadMediaInputAsset(string inputAssetName, string fileName) { - IAzureMediaServicesClient client = await this.CreateMediaServicesClientAsync(); + var client = this.GetMKIOServicesClientAsync(); + var assets = client.Assets.Get(inputAssetName); + + BlobServiceClient blobServiceClient = new BlobServiceClient(this.settings.MediaKindSettings.MediaKindStorageConnectionString); - AssetContainerSas assetContainerSas = await client.Assets.ListContainerSasAsync( - this.settings.AzureMediaResourceGroup, - this.settings.AzureMediaAccountName, - inputAssetName, - permissions: AssetContainerPermission.Read, - expiryTime: DateTime.UtcNow.AddHours(1).ToUniversalTime()); + BlobContainerClient containerClient = blobServiceClient.GetBlobContainerClient(assets.Properties.Container); + if (!await containerClient.ExistsAsync().ConfigureAwait(false)) + { + await containerClient.CreateIfNotExistsAsync().ConfigureAwait(false); + } - string sasUri = assetContainerSas.AssetContainerSasUrls.First(); + var filename1 = Regex.Replace(fileName, "[^a-zA-Z0-9.]", string.Empty); + filename1 = string.IsNullOrEmpty(filename1) ? "file.txt" : filename1; - var blobServiceClient = new BlobContainerClient(new Uri(sasUri)); - var blobClient = blobServiceClient.GetBlockBlobClient(fileName); + BlobClient blobClient = containerClient.GetBlobClient(filename1); var fileContent = await blobClient.DownloadContentAsync(); return fileContent; diff --git a/AdminUI/LearningHub.Nhs.AdminUI/Services/RoadmapService.cs b/AdminUI/LearningHub.Nhs.AdminUI/Services/RoadmapService.cs index bf4878d8b..48507ae31 100644 --- a/AdminUI/LearningHub.Nhs.AdminUI/Services/RoadmapService.cs +++ b/AdminUI/LearningHub.Nhs.AdminUI/Services/RoadmapService.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using LearningHub.Nhs.AdminUI.Interfaces; using LearningHub.Nhs.Models.Entities; + using LearningHub.Nhs.Models.RoadMap; using Newtonsoft.Json; /// @@ -134,6 +135,34 @@ public async Task> GetUpdates() return viewmodel; } + /// + /// The GetIdAsync. + /// + /// The id. + /// The . + public async Task GetIdAsync(int id) + { + RoadMapViewModel viewmodel = null; + + var client = await this.LearningHubHttpClient.GetClientAsync(); + var request = $"Roadmap/GetRoadMapsById/{id}"; + var response = await client.GetAsync(request).ConfigureAwait(false); + + if (response.IsSuccessStatusCode) + { + var result = response.Content.ReadAsStringAsync().Result; + viewmodel = JsonConvert.DeserializeObject(result); + } + else if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized + || + response.StatusCode == System.Net.HttpStatusCode.Forbidden) + { + throw new Exception("AccessDenied"); + } + + return viewmodel; + } + /// /// The UpdateRoadmap. /// diff --git a/AdminUI/LearningHub.Nhs.AdminUI/Views/Catalogue/CatalogueOwner.cshtml b/AdminUI/LearningHub.Nhs.AdminUI/Views/Catalogue/CatalogueOwner.cshtml index faa03ac40..09709fd29 100644 --- a/AdminUI/LearningHub.Nhs.AdminUI/Views/Catalogue/CatalogueOwner.cshtml +++ b/AdminUI/LearningHub.Nhs.AdminUI/Views/Catalogue/CatalogueOwner.cshtml @@ -47,14 +47,14 @@
- +
- +
diff --git a/AdminUI/LearningHub.Nhs.AdminUI/Views/Catalogue/Edit.cshtml b/AdminUI/LearningHub.Nhs.AdminUI/Views/Catalogue/Edit.cshtml index 19ed8d399..1d8ab66da 100644 --- a/AdminUI/LearningHub.Nhs.AdminUI/Views/Catalogue/Edit.cshtml +++ b/AdminUI/LearningHub.Nhs.AdminUI/Views/Catalogue/Edit.cshtml @@ -3,458 +3,448 @@ @using LearningHub.Nhs.Models.Enums @using Newtonsoft.Json @{ - var page = Model.CatalogueNodeVersionId == 0 ? CatalogueNavPage.Add : CatalogueNavPage.Edit; - ViewData["Title"] = page == CatalogueNavPage.Add ? "Add catalogue" : "Edit catalogue"; - var baseUrl = _settings.Value.LearningHubUrl + "catalogue/"; - var url = baseUrl + (Model.Url ?? ""); - var aToZ = (int)CatalogueOrder.AlphabeticalAscending; - var keywords = Model.Keywords ?? new List(); - var Providers = Model.Providers; - var CatalogueNodeVersionProviderId = Model.CatalogueNodeVersionProvider?.ProviderId ?? 0; - var CatalogueNodeVersionProvider = Model.CatalogueNodeVersionProvider; - var action = page.ToString(); - var keywordsJson = Html.Raw(JsonConvert.SerializeObject(keywords)); - var imageBaseUrl = "/file/download/CatalogueImageDirectory/"; - var lastModifiedDate = Model.LastModifiedDate?.ToString("dd MMM yyyy"); + var page = Model.CatalogueNodeVersionId == 0 ? CatalogueNavPage.Add : CatalogueNavPage.Edit; + ViewData["Title"] = page == CatalogueNavPage.Add ? "Add catalogue" : "Edit catalogue"; + var baseUrl = _settings.Value.LearningHubUrl + "catalogue/"; + var url = baseUrl + (Model.Url ?? ""); + var aToZ = (int)CatalogueOrder.AlphabeticalAscending; + var keywords = Model.Keywords ?? new List(); + var Providers = Model.Providers; + var CatalogueNodeVersionProviderId = Model.CatalogueNodeVersionProvider?.ProviderId ?? 0; + var CatalogueNodeVersionProvider = Model.CatalogueNodeVersionProvider; + var action = page.ToString(); + var keywordsJson = Html.Raw(JsonConvert.SerializeObject(keywords)); + var imageBaseUrl = "/file/download/CatalogueImageDirectory/"; + var lastModifiedDate = Model.LastModifiedDate?.ToString("dd MMM yyyy"); } @section SideMenu { - @{ - await Html.RenderPartialAsync("_NavSection"); - } + @{ + await Html.RenderPartialAsync("_NavSection"); + } } @if (!string.IsNullOrEmpty(ViewBag.ErrorMessage)) { - + } @if (page == CatalogueNavPage.Edit) { -
-
-

@Model.Name

+
+
+

@Model.Name

+
-
} @{ - await Html.RenderPartialAsync("_CatalogueNav.cshtml", new CatalogueNavViewModel { Page = page, CatalogueId = Model.CatalogueNodeVersionId }); + await Html.RenderPartialAsync("_CatalogueNav.cshtml", new CatalogueNavViewModel { Page = page, CatalogueId = Model.CatalogueNodeVersionId }); }
-
- - - - -
-
-
- @if (page == CatalogueNavPage.Edit) - { - var idString = Model.CatalogueNodeVersionId.ToString(); - var paddedIdString = idString.Length > 3 ? idString : idString.PadLeft(3, '0'); - ID: @paddedIdString - } - else - { - NEW - } + + + + + +
+
+
+ @if (page == CatalogueNavPage.Edit) + { + var idString = Model.CatalogueNodeVersionId.ToString(); + var paddedIdString = idString.Length > 3 ? idString : idString.PadLeft(3, '0'); + ID: @paddedIdString + } + else + { + NEW + } +
+ @if (page == CatalogueNavPage.Add || Model.Hidden) + { + Hidden + } + @if (lastModifiedDate != null) + { +

Last modified on: @lastModifiedDate

+ } +
- @if (page == CatalogueNavPage.Add || Model.Hidden) - { - Hidden - } - @if (lastModifiedDate != null) - { -

Last modified on: @lastModifiedDate

- } -
-
- - @if (!ViewData.ModelState.IsValid) - { -
This form failed to save because there are errors below.
- } -
-
- -

You can enter a maximum of 255 characters including spaces

- - -
-
-
-
- - @if (page == CatalogueNavPage.Add) - { - - } - else - { - - } - -
-
- @if (Model.Status == VersionStatusEnum.Published && !Model.Hidden) + @if (!ViewData.ModelState.IsValid) { - +
This form failed to save because there are errors below.
} - else - { -

- } -
-
-
-
- -
-
-
-
- - -
-
-
-
- -
-
-
-
- -
-
-
-
- - - Only the first 3,000 characters of the description will be used by search - -
-
-
-
- -

- Add one or more relevant keywords to help learners find this catalogue, ensuring that the keyword is specifically related to the catalogue rather than the resources associated with it. A maximum of 5 keywords can be added. Enter one keyword and add it before entering another. -

-
-
- -
- + +
+
+ +

You can enter a maximum of 255 characters including spaces

+ +
-
-
-
- - -
+
+
+ + @if (page == CatalogueNavPage.Add) + { + + } + else + { + + } + +
+
+ @if (Model.Status == VersionStatusEnum.Published && !Model.Hidden) + { + + } + else + { +

+ } +
-
-
- -
+
+
+ +
+
+
+
+ + +
+
+
+ +
+
+
+
+ +
+
+
+
+ + + Only the first 3,000 characters of the description will be used by search + +
+
+
+
+ +

+ Add one or more relevant keywords to help learners find this catalogue, ensuring that the keyword is specifically related to the catalogue rather than the resources associated with it. A maximum of 5 keywords can be added. Enter one keyword and add it before entering another. +

+
+
+ +
+ +
+
+
+
+
+ + +
+
+
+
+ +
+
-
-
-

You can enter a maximum of 50 characters per keyword.

-
- @{ - var i = 0; - } - @foreach (var keyword in keywords) - { -
-

@keyword

- -
- } +
+

You can enter a maximum of 50 characters per keyword.

+
+ @{ + var i = 0; + } + @foreach (var keyword in keywords) + { +
+

@keyword

+ + +
+ } +
+
-
-
-
+
-
-
- -
-
+
+
+ +
+
-
-
-
- @if (!Model.RestrictedAccess) - { - - } - else - { - - } - Unrestricted access (default) -
-
- @if (Model.RestrictedAccess) - { - - } - else +
+
+
+ @if (!Model.RestrictedAccess) + { + + } + else + { + + } + Unrestricted access (default) +
+
+ @if (Model.RestrictedAccess) + { + + } + else + { + + } + Restricted access +
+
+
+ @if (!Model.HasUserGroup) { - +
+

There are no user groups associated with this catalogue. You can add some in the User Groups tab.

+
} - Restricted access -
-
-
- @if (!Model.HasUserGroup) - { -
-

There are no user groups associated with this catalogue. You can add some in the User Groups tab.

- } -
-
+
-
-
- -

When applicable please select the provider of this content. This will allow a contributor to flag content from a specific provider.

-

This will enable learners to search for content produced by organisations and help separate learning resources from community contributions.

-

Developed with;

-
-
+
+
+ +

When applicable please select the provider of this content. This will allow a contributor to flag content from a specific provider.

+

This will enable learners to search for content produced by organisations and help separate learning resources from community contributions.

+

Developed with;

+
+
-
-
- @if (Providers != null && Providers.Count() > 0) - { -
- @foreach (var provider in Providers) - { - - @provider.Name -
- } - - Not applicable +
+
+ @if (Providers != null && Providers.Count() > 0) + { +
+ @foreach (var provider in Providers) + { + + @provider.Name +
+ } + + Not applicable +
+ } +
- }
-
-
-
-
- @if (page == CatalogueNavPage.Add) - { - - } - else - { - - } -
-
- -
+
+
+ @if (page == CatalogueNavPage.Add) + { + + } + else + { + + } +
+
+ +
@section Scripts { - - + + } diff --git a/AdminUI/LearningHub.Nhs.AdminUI/Views/Log/Index.cshtml b/AdminUI/LearningHub.Nhs.AdminUI/Views/Log/Index.cshtml index 8e9c75ae2..8f3eb95d3 100644 --- a/AdminUI/LearningHub.Nhs.AdminUI/Views/Log/Index.cshtml +++ b/AdminUI/LearningHub.Nhs.AdminUI/Views/Log/Index.cshtml @@ -23,6 +23,8 @@ + @if (Model.Results != null) + { @foreach (var item in Model.Results.Items) { @@ -46,6 +48,7 @@ } + }
diff --git a/AdminUI/LearningHub.Nhs.AdminUI/Views/Notifications/CreateEdit.cshtml b/AdminUI/LearningHub.Nhs.AdminUI/Views/Notifications/CreateEdit.cshtml index 327cce4b8..627deb40b 100644 --- a/AdminUI/LearningHub.Nhs.AdminUI/Views/Notifications/CreateEdit.cshtml +++ b/AdminUI/LearningHub.Nhs.AdminUI/Views/Notifications/CreateEdit.cshtml @@ -73,7 +73,7 @@
- +
diff --git a/AdminUI/LearningHub.Nhs.AdminUI/Views/Resource/Details.cshtml b/AdminUI/LearningHub.Nhs.AdminUI/Views/Resource/Details.cshtml index 5c7b99069..a7a2942e8 100644 --- a/AdminUI/LearningHub.Nhs.AdminUI/Views/Resource/Details.cshtml +++ b/AdminUI/LearningHub.Nhs.AdminUI/Views/Resource/Details.cshtml @@ -5,919 +5,948 @@ @inject IOptions webSettings @{ - ViewData["Title"] = "Details"; - var activetab = this.ViewBag.ActiveTab; + ViewData["Title"] = "Details"; + var activetab = this.ViewBag.ActiveTab; } -@section Styles{ - +@section Styles { + }
- -
-
-
- - @if (Model.ResourceVersionId == 0) - { -
Resource Version not found
- } - else - { -
-
@Model.Title (@Model.VersionStatusDescription)
-
- @if (Model.VersionStatusEnum == VersionStatusEnum.Published) - { - - } - @if (Model.VersionStatusEnum == VersionStatusEnum.Publishing || Model.VersionStatusEnum == VersionStatusEnum.FailedToPublish) - { - - } - -
-
- -
public string GoogleAnalyticsId { get; set; } + /// + /// Gets or sets the PasswordRequestLimitingPeriod. + /// + public int PasswordRequestLimitingPeriod { get; set; } + + /// + /// Gets or sets the PasswordRequestLimit. + /// + public int PasswordRequestLimit { get; set; } + /// /// Gets or sets the SupportUrls. /// diff --git a/LearningHub.Nhs.WebUI/Controllers/AccountController.cs b/LearningHub.Nhs.WebUI/Controllers/AccountController.cs index 979c1ac33..2d2017cd2 100644 --- a/LearningHub.Nhs.WebUI/Controllers/AccountController.cs +++ b/LearningHub.Nhs.WebUI/Controllers/AccountController.cs @@ -581,7 +581,18 @@ public async Task CreateAccountSearchRole(AccountCreationViewMode { var accountCreation = await this.multiPageFormService.GetMultiPageFormData(MultiPageFormDataFeature.AddRegistrationPrompt, this.TempData); - return this.View("CreateAccountSearchRole", new AccountCreationViewModel { CountryId = accountCreationViewModel.CountryId }); + var currentJobRole = int.TryParse(accountCreation.CurrentRole, out int currentRole); + if (currentJobRole && currentRole > 0) + { + var filterText = await this.jobRoleService.GetByIdAsync(currentRole); + accountCreationViewModel.FilterText = filterText.Name; + var jobrole = await this.jobRoleService.GetByIdAsync(currentRole); + return this.View("CreateAccountCurrentRole", new AccountCreationListViewModel { RoleList = new List { jobrole }, AccountCreationPaging = new AccountCreationPagingModel { TotalItems = 1, PageSize = UserRegistrationContentPageSize, HasItems = jobrole != null, CurrentPage = 1 }, CurrentRole = accountCreation.CurrentRole, CountryId = accountCreation.CountryId, RegionId = accountCreation.RegionId, FilterText = accountCreationViewModel.FilterText, ReturnToConfirmation = accountCreationViewModel.ReturnToConfirmation }); + } + else + { + return this.View("CreateAccountSearchRole", new AccountCreationViewModel { CountryId = accountCreationViewModel.CountryId }); + } } /// @@ -886,7 +897,7 @@ public async Task CreateAccountWorkStartDate(AccountCreationViewM } await this.multiPageFormService.SetMultiPageFormData(accountCreation, MultiPageFormDataFeature.AddRegistrationPrompt, this.TempData); - var dateVM = accountCreation.StartDate.HasValue ? new AccountCreationDateViewModel() { Day = accountCreation.StartDate.Value.Day, Month = accountCreation.StartDate.GetValueOrDefault().Month, Year = accountCreation.StartDate.Value.Year, FilterText = accountCreationViewModel.FilterText, ReturnToConfirmation = accountCreationViewModel.ReturnToConfirmation } : new AccountCreationDateViewModel() { FilterText = accountCreationViewModel.FilterText, ReturnToConfirmation = accountCreationViewModel.ReturnToConfirmation }; + var dateVM = accountCreation.StartDate.HasValue ? new AccountCreationDateViewModel() { Day = accountCreation.StartDate.HasValue ? accountCreation.StartDate.Value.Day.ToString() : string.Empty, Month = accountCreation.StartDate.HasValue ? accountCreation.StartDate.GetValueOrDefault().Month.ToString() : string.Empty, Year = accountCreation.StartDate.HasValue ? accountCreation.StartDate.Value.Year.ToString() : string.Empty, FilterText = accountCreationViewModel.FilterText, ReturnToConfirmation = accountCreationViewModel.ReturnToConfirmation } : new AccountCreationDateViewModel() { FilterText = accountCreationViewModel.FilterText, ReturnToConfirmation = accountCreationViewModel.ReturnToConfirmation }; if (!string.IsNullOrWhiteSpace(accountCreationViewModel.PrimarySpecialtyId) && string.IsNullOrWhiteSpace(accountCreationViewModel.FilterText)) { var specialty = this.specialtyService.GetSpecialtiesAsync().Result.FirstOrDefault(x => x.Id == specialtyId); @@ -948,7 +959,7 @@ public async Task CreateAccountStartDate(bool? returnToConfirmati } } - var dateVM = accountCreation.StartDate.HasValue ? new AccountCreationDateViewModel() { Day = accountCreation.StartDate.Value.Day, Month = accountCreation.StartDate.GetValueOrDefault().Month, Year = accountCreation.StartDate.Value.Year, ReturnToConfirmation = returnToConfirmation } : new AccountCreationDateViewModel() { ReturnToConfirmation = returnToConfirmation }; + var dateVM = accountCreation.StartDate.HasValue ? new AccountCreationDateViewModel() { Day = accountCreation.StartDate.HasValue ? accountCreation.StartDate.Value.Day.ToString() : string.Empty, Month = accountCreation.StartDate.HasValue ? accountCreation.StartDate.GetValueOrDefault().Month.ToString() : string.Empty, Year = accountCreation.StartDate.HasValue ? accountCreation.StartDate.Value.Year.ToString() : string.Empty, ReturnToConfirmation = returnToConfirmation } : new AccountCreationDateViewModel() { ReturnToConfirmation = returnToConfirmation }; return this.View("CreateAccountWorkStartDate", dateVM); } @@ -1156,6 +1167,16 @@ public IActionResult InvalidUserAccount() return this.View(); } + /// + /// The user already has an already active session. Then prevent concurrent access to the Learning Hub. + /// + /// The . + [HttpGet] + public IActionResult AlreadyAnActiveSession() + { + return this.View(); + } + /// /// The ForgotPassword. /// @@ -1187,8 +1208,20 @@ public async Task ForgotPassword(Models.Account.ForgotPasswordVie return this.Ok(new { duplicate = true }); } - await this.userService.ForgotPasswordAsync(model.EmailAddress); - return this.View("ForgotPasswordAcknowledgement"); + var passwordRequestLimitingPeriod = this.Settings.PasswordRequestLimitingPeriod; + var passwordRequestLimit = this.Settings.PasswordRequestLimit; + var status = await this.userService.CanRequestPasswordResetAsync(model.EmailAddress, passwordRequestLimitingPeriod, passwordRequestLimit); + if (status) + { + await this.userService.ForgotPasswordAsync(model.EmailAddress); + return this.View("ForgotPasswordAcknowledgement"); + } + else + { + this.ViewBag.Period = passwordRequestLimitingPeriod; + this.ViewBag.Limit = passwordRequestLimit; + return this.View("TooManyRequests"); + } } /// diff --git a/LearningHub.Nhs.WebUI/Controllers/Api/ResourceController.cs b/LearningHub.Nhs.WebUI/Controllers/Api/ResourceController.cs index e5e8ab35e..294785798 100644 --- a/LearningHub.Nhs.WebUI/Controllers/Api/ResourceController.cs +++ b/LearningHub.Nhs.WebUI/Controllers/Api/ResourceController.cs @@ -2,7 +2,10 @@ namespace LearningHub.Nhs.WebUI.Controllers.Api { using System; using System.Collections.Generic; + using System.IO; using System.Linq; + using System.Net.Http.Headers; + using System.Threading; using System.Threading.Tasks; using LearningHub.Nhs.Models.Enums; using LearningHub.Nhs.Models.Resource; @@ -10,6 +13,7 @@ namespace LearningHub.Nhs.WebUI.Controllers.Api using LearningHub.Nhs.Models.Resource.Contribute; using LearningHub.Nhs.WebUI.Interfaces; using Microsoft.AspNetCore.Authorization; + using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; @@ -69,9 +73,10 @@ public async Task DownloadResource(string filePath, string fileNa } var file = await this.fileService.DownloadFileAsync(filePath, fileName); + if (file != null) { - return this.File(file.Content, file.ContentType, fileName); + return !string.IsNullOrEmpty(file.DownloadUrl) ? this.Redirect(file.DownloadUrl) : this.File(file.Content, file.ContentType, fileName); } else { @@ -106,7 +111,8 @@ public async Task DownloadResourceAndRecordActivity(int resourceV ActivityStatus = ActivityStatusEnum.Completed, }; await this.activityService.CreateResourceActivityAsync(activity); - return this.File(file.Content, file.ContentType, fileName); + + return !string.IsNullOrEmpty(file.DownloadUrl) ? this.Redirect(file.DownloadUrl) : this.File(file.Content, file.ContentType, fileName); } else { @@ -252,6 +258,33 @@ public async Task RecordExternalReferenceUserAgreementAsync([FromB } } + /// + /// The RecordExternalReferenceUserAgreementAsync. + /// + /// model. + /// A representing the result of the asynchronous operation. + [HttpPost] + [Route(" CreateResourceVersionValidationResult")] + public async Task CreateResourceVersionValidationResultAsync([FromBody] ResourceVersionValidationResultViewModel model) + { + await this.resourceService.CreateResourceVersionValidationResultAsync(model); + return this.Ok(); + } + + /// + /// The RecordExternalReferenceUserAgreementAsync. + /// + /// model. + /// A representing the result of the asynchronous operation. + [HttpPost] + [Route(" CreateResourceVersionValidationResults")] + public async Task CreateResourceVersionValidationResultAsync([FromBody] string filename) + { + ResourceVersionValidationResultViewModel model = new ResourceVersionValidationResultViewModel(); + await this.resourceService.CreateResourceVersionValidationResultAsync(model); + return this.Ok(); + } + /// /// The GetHeaderById. /// @@ -585,5 +618,20 @@ public async Task> GetObsoleteResourceFile(int resourceVersionId, b var result = await this.resourceService.GetObsoleteResourceFile(resourceVersionId, deletedResource); return result; } + + /// + /// Reads from the source stream in chunks and writes to the destination stream, + /// flushing after each chunk to help keep the connection active. + /// + private async Task StreamFileWithKeepAliveAsync(Stream source, Stream destination, CancellationToken cancellationToken) + { + byte[] buffer = new byte[8192]; + int bytesRead; + while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken)) > 0) + { + await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken); + await destination.FlushAsync(cancellationToken); + } + } } } diff --git a/LearningHub.Nhs.WebUI/Controllers/Api/UserController.cs b/LearningHub.Nhs.WebUI/Controllers/Api/UserController.cs index fc5c5579a..35294cc11 100644 --- a/LearningHub.Nhs.WebUI/Controllers/Api/UserController.cs +++ b/LearningHub.Nhs.WebUI/Controllers/Api/UserController.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using elfhHub.Nhs.Models.Common; + using elfhHub.Nhs.Models.Enums; using LearningHub.Nhs.WebUI.Configuration; using LearningHub.Nhs.WebUI.Interfaces; using Microsoft.AspNetCore.Authorization; @@ -98,6 +99,27 @@ public async Task CheckUserRole() return this.Ok(isSystemAdmin); } + /// + /// to check user password is correct. + /// + /// The currentPassword. + /// The . + [HttpGet] + [Route("ConfirmPassword/{currentPassword}")] + public async Task ConfirmPassword(string currentPassword) + { + string passwordHash = this.userService.Base64MD5HashDigest(currentPassword); + var userPersonalDetails = await this.userService.GetCurrentUserPersonalDetailsAsync(); + if (userPersonalDetails != null && userPersonalDetails.PasswordHash == passwordHash) + { + return this.Ok(true); + } + else + { + return this.Ok(false); + } + } + /// /// The GetCurrentUserPersonalDetails. /// diff --git a/LearningHub.Nhs.WebUI/Controllers/BaseController.cs b/LearningHub.Nhs.WebUI/Controllers/BaseController.cs index 8972fa822..6b3ad9a20 100644 --- a/LearningHub.Nhs.WebUI/Controllers/BaseController.cs +++ b/LearningHub.Nhs.WebUI/Controllers/BaseController.cs @@ -3,6 +3,7 @@ using System.Net.Http; using LearningHub.Nhs.Models.Extensions; using LearningHub.Nhs.WebUI.Configuration; + using LearningHub.Nhs.WebUI.Extensions; using LearningHub.Nhs.WebUI.Filters; using LearningHub.Nhs.WebUI.Helpers; using LearningHub.Nhs.WebUI.Models; @@ -76,6 +77,11 @@ protected BaseController( /// protected int CurrentUserId => this.User.Identity.GetCurrentUserId(); + /// + /// Gets the CurrentUserId. + /// + protected int CurrentMoodleUserId => this.User.Identity.GetMoodleUserId(); + /// /// The OnActionExecuting. /// diff --git a/LearningHub.Nhs.WebUI/Controllers/CatalogueController.cs b/LearningHub.Nhs.WebUI/Controllers/CatalogueController.cs index 2fca0012e..12d9973ac 100644 --- a/LearningHub.Nhs.WebUI/Controllers/CatalogueController.cs +++ b/LearningHub.Nhs.WebUI/Controllers/CatalogueController.cs @@ -587,8 +587,7 @@ public async Task RequestPreviewAccess(CatalogueRequestAccessView [Route("/allcatalogue/{filterChar}")] public async Task GetAllCatalogue(string filterChar = "a") { - var pageSize = this.settings.AllCataloguePageSize; - var catalogues = await this.catalogueService.GetAllCatalogueAsync(filterChar, pageSize); + var catalogues = await this.catalogueService.GetAllCatalogueAsync(filterChar); return this.View("allcatalogue", catalogues); } diff --git a/LearningHub.Nhs.WebUI/Controllers/HomeController.cs b/LearningHub.Nhs.WebUI/Controllers/HomeController.cs index 87d01e668..7f40afe15 100644 --- a/LearningHub.Nhs.WebUI/Controllers/HomeController.cs +++ b/LearningHub.Nhs.WebUI/Controllers/HomeController.cs @@ -7,6 +7,7 @@ namespace LearningHub.Nhs.WebUI.Controllers using System.Net.Http; using System.Threading.Tasks; using elfhHub.Nhs.Models.Common; + using elfhHub.Nhs.Models.Enums; using LearningHub.Nhs.Models.Content; using LearningHub.Nhs.Models.Enums.Content; using LearningHub.Nhs.Models.Extensions; @@ -15,13 +16,13 @@ namespace LearningHub.Nhs.WebUI.Controllers using LearningHub.Nhs.WebUI.Helpers; using LearningHub.Nhs.WebUI.Interfaces; using LearningHub.Nhs.WebUI.Models; - using Microsoft.ApplicationInsights.AspNetCore; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Diagnostics; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; + using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Microsoft.FeatureManagement; @@ -39,6 +40,7 @@ public class HomeController : BaseController private readonly IDashboardService dashboardService; private readonly IContentService contentService; private readonly IFeatureManager featureManager; + private readonly Microsoft.Extensions.Configuration.IConfiguration configuration; /// /// Initializes a new instance of the class. @@ -53,6 +55,7 @@ public class HomeController : BaseController /// Dashboard service. /// Content service. /// featureManager. + /// config. public HomeController( IHttpClientFactory httpClientFactory, IWebHostEnvironment hostingEnvironment, @@ -63,7 +66,8 @@ public HomeController( LearningHubAuthServiceConfig authConfig, IDashboardService dashboardService, IContentService contentService, - IFeatureManager featureManager) + IFeatureManager featureManager, + Microsoft.Extensions.Configuration.IConfiguration configuration) : base(hostingEnvironment, httpClientFactory, logger, settings.Value) { this.authConfig = authConfig; @@ -72,6 +76,7 @@ public HomeController( this.dashboardService = dashboardService; this.contentService = contentService; this.featureManager = featureManager; + this.configuration = configuration; } /// @@ -133,11 +138,12 @@ public IActionResult CreateAccount() public IActionResult Error(int? httpStatusCode) { string originalPathUrlMessage = null; - + string originalPath = null; if (httpStatusCode.HasValue && httpStatusCode.Value == 404) { var exceptionHandlerPathFeature = this.HttpContext.Features.Get(); - originalPathUrlMessage = $"Page Not Found url: {exceptionHandlerPathFeature?.OriginalPath}. "; + originalPath = exceptionHandlerPathFeature?.OriginalPath; + originalPathUrlMessage = $"Page Not Found url: {originalPath}. "; } if (this.User.Identity.IsAuthenticated) @@ -207,6 +213,15 @@ public async Task Index(string myLearningDashboard = "my-in-progr var resourcesTask = this.dashboardService.GetResourcesAsync(resourceDashboard, 1); var cataloguesTask = this.dashboardService.GetCataloguesAsync(catalogueDashboard, 1); + var enrolledCoursesTask = Task.FromResult(new List()); + var enableMoodle = Task.Run(() => this.featureManager.IsEnabledAsync(FeatureFlags.EnableMoodle)).Result; + this.ViewBag.EnableMoodle = enableMoodle; + this.ViewBag.ValidMoodleUser = this.CurrentMoodleUserId > 0; + if (enableMoodle && myLearningDashboard == "my-enrolled-courses") + { + enrolledCoursesTask = this.dashboardService.GetEnrolledCoursesFromMoodleAsync(this.CurrentMoodleUserId, 1); + } + await Task.WhenAll(learningTask, resourcesTask, cataloguesTask); var model = new DashboardViewModel() @@ -214,6 +229,7 @@ public async Task Index(string myLearningDashboard = "my-in-progr MyLearnings = await learningTask, Resources = await resourcesTask, Catalogues = await cataloguesTask, + EnrolledCourses = await enrolledCoursesTask, }; if (!string.IsNullOrEmpty(this.Request.Query["preview"]) && Convert.ToBoolean(this.Request.Query["preview"])) @@ -320,14 +336,19 @@ public IActionResult NhsSites() } /// - /// The Logout. - /// This is directly referenced in the LoginWizardFilter to allow - /// logouts to bypass LoginWizard redirects. - /// If the name is changed, the LoginWizardFilter MUST be updated. + /// The ChangePasswordAcknowledgement. /// /// The . - [AllowAnonymous] - public IActionResult Logout() + public IActionResult ChangePasswordAcknowledgement() + { + return this.View(); + } + + /// + /// StatusUpdate. + /// + /// Actionresult. + public IActionResult UserLogout() { if (!(this.User?.Identity.IsAuthenticated ?? false)) { @@ -337,6 +358,20 @@ public IActionResult Logout() return new SignOutResult(new[] { CookieAuthenticationDefaults.AuthenticationScheme, "oidc" }); } + /// + /// The Logout. + /// This is directly referenced in the LoginWizardFilter to allow + /// logouts to bypass LoginWizard redirects. + /// If the name is changed, the LoginWizardFilter MUST be updated. + /// + /// The . + [AllowAnonymous] + public IActionResult Logout() + { + var redirectUri = $"{this.configuration["LearningHubAuthServiceConfig:Authority"]}/Home/SetIsPasswordUpdate?isLogout=true"; + return this.Redirect(redirectUri); + } + /// /// The SessionTimeout. /// @@ -352,6 +387,7 @@ public IActionResult SessionTimeout(string returnUrl = "/") this.ViewBag.AuthTimeout = this.authConfig.AuthTimeout; this.ViewBag.ReturnUrl = returnUrl; + return this.View(); } diff --git a/LearningHub.Nhs.WebUI/Controllers/MyAccountController.cs b/LearningHub.Nhs.WebUI/Controllers/MyAccountController.cs index 4494d9463..6fd57f208 100644 --- a/LearningHub.Nhs.WebUI/Controllers/MyAccountController.cs +++ b/LearningHub.Nhs.WebUI/Controllers/MyAccountController.cs @@ -21,10 +21,12 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Routing; + using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using NHSUKViewComponents.Web.ViewModels; using ChangePasswordViewModel = LearningHub.Nhs.WebUI.Models.UserProfile.ChangePasswordViewModel; + using IConfiguration = Microsoft.Extensions.Configuration.IConfiguration; /// /// The UserController. @@ -43,6 +45,7 @@ public partial class MyAccountController : BaseController private readonly ISpecialtyService specialtyService; private readonly ILocationService locationService; private readonly ICacheService cacheService; + private readonly IConfiguration configuration; /// /// Initializes a new instance of the class. @@ -61,6 +64,7 @@ public partial class MyAccountController : BaseController /// The locationService. /// The multiPageFormService. /// The cacheService. + /// The cacheService. public MyAccountController( IWebHostEnvironment hostingEnvironment, ILogger logger, @@ -75,7 +79,8 @@ public MyAccountController( ISpecialtyService specialtyService, ILocationService locationService, IMultiPageFormService multiPageFormService, - ICacheService cacheService) + ICacheService cacheService, + IConfiguration configuration) : base(hostingEnvironment, httpClientFactory, logger, settings.Value) { this.userService = userService; @@ -88,6 +93,7 @@ public MyAccountController( this.locationService = locationService; this.multiPageFormService = multiPageFormService; this.cacheService = cacheService; + this.configuration = configuration; } private string LoginWizardCacheKey => $"{this.CurrentUserId}:LoginWizard"; @@ -452,9 +458,8 @@ public async Task UpdatePassword(ChangePasswordViewModel model) if (this.ModelState.IsValid) { await this.userService.UpdatePassword(model.NewPassword); - - this.ViewBag.SuccessMessage = CommonValidationErrorMessages.PasswordSuccessMessage; - return this.View("SuccessMessage"); + var redirectUri = $"{this.configuration["LearningHubAuthServiceConfig:Authority"]}/Home/SetIsPasswordUpdate?isLogout=false"; + return this.Redirect(redirectUri); } else { @@ -673,29 +678,37 @@ public async Task ChangeCurrentRole([FromQuery] UserJobRoleUpdate return this.View("ChangeCurrentRole", viewModel); } - if (formSubmission && viewModel.SelectedJobRoleId.HasValue) + if (!string.IsNullOrWhiteSpace(viewModel.FilterText)) { - var newRoleId = viewModel.SelectedJobRoleId.Value; - var jobRole = await this.jobRoleService.GetByIdAsync(newRoleId); + var jobRoles = await this.jobRoleService.GetPagedFilteredAsync(viewModel.FilterText, viewModel.CurrentPage, viewModel.PageSize); + viewModel.RoleList = jobRoles.Item2; + viewModel.TotalItems = jobRoles.Item1; + viewModel.HasItems = jobRoles.Item1 > 0; + } - if (jobRole.MedicalCouncilId > 0 && jobRole.MedicalCouncilId < 4) + if (formSubmission) + { + if (viewModel.SelectedJobRoleId.HasValue) { - return this.RedirectToAction(nameof(this.ChangeMedicalCouncilNo), new UserMedicalCouncilNoUpdateViewModel { SelectedJobRoleId = newRoleId }); + var newRoleId = viewModel.SelectedJobRoleId.Value; + var jobRole = await this.jobRoleService.GetByIdAsync(newRoleId); + + if (jobRole.MedicalCouncilId > 0 && jobRole.MedicalCouncilId < 4) + { + return this.RedirectToAction(nameof(this.ChangeMedicalCouncilNo), new UserMedicalCouncilNoUpdateViewModel { SelectedJobRoleId = newRoleId }); + } + else + { + return this.RedirectToAction(nameof(this.ChangeGrade), new UserGradeUpdateViewModel { SelectedJobRoleId = newRoleId }); + } } else { - return this.RedirectToAction(nameof(this.ChangeGrade), new UserGradeUpdateViewModel { SelectedJobRoleId = newRoleId }); + this.ModelState.AddModelError(nameof(viewModel.SelectedJobRoleId), CommonValidationErrorMessages.RoleRequired); + return this.View("ChangeCurrentRole", viewModel); } } - if (!string.IsNullOrWhiteSpace(viewModel.FilterText)) - { - var jobRoles = await this.jobRoleService.GetPagedFilteredAsync(viewModel.FilterText, viewModel.CurrentPage, viewModel.PageSize); - viewModel.RoleList = jobRoles.Item2; - viewModel.TotalItems = jobRoles.Item1; - viewModel.HasItems = jobRoles.Item1 > 0; - } - return this.View("ChangeCurrentRole", viewModel); } @@ -795,26 +808,37 @@ public async Task ChangeGrade([FromQuery] UserGradeUpdateViewMode viewModel.Grade = profile.Grade; viewModel.SelectedJobRole = jobRole.NameWithStaffGroup; viewModel.SelectedMedicalCouncilId = jobRole.MedicalCouncilId; - - if (this.User.IsInRole("BasicUser") || (formSubmission && viewModel.SelectedGradeId.HasValue)) + if (formSubmission) { - var medicalCouncilNoRequired = jobRole.MedicalCouncilId > 0 && jobRole.MedicalCouncilId < 4; - await this.userService.UpdateUserEmployment( - new elfhHub.Nhs.Models.Entities.UserEmployment - { - Id = profile.EmploymentId, - UserId = profile.Id, - JobRoleId = viewModel.SelectedJobRoleId, - MedicalCouncilId = medicalCouncilNoRequired ? jobRole.MedicalCouncilId : null, - MedicalCouncilNo = medicalCouncilNoRequired ? (viewModel.SelectedMedicalCouncilNo ?? profile.MedicalCouncilNo) : null, - GradeId = viewModel.SelectedGradeId, - SpecialtyId = profile.SpecialtyId, - StartDate = profile.JobStartDate, - LocationId = profile.LocationId, - }); + if (this.User.IsInRole("BasicUser") || viewModel.SelectedGradeId != null) + { + var medicalCouncilNoRequired = jobRole.MedicalCouncilId > 0 && jobRole.MedicalCouncilId < 4; + await this.userService.UpdateUserEmployment( + new elfhHub.Nhs.Models.Entities.UserEmployment + { + Id = profile.EmploymentId, + UserId = profile.Id, + JobRoleId = viewModel.SelectedJobRoleId, + MedicalCouncilId = medicalCouncilNoRequired ? jobRole.MedicalCouncilId : null, + MedicalCouncilNo = medicalCouncilNoRequired ? (viewModel.SelectedMedicalCouncilNo ?? profile.MedicalCouncilNo) : null, + GradeId = Convert.ToInt32(viewModel.SelectedGradeId), + SpecialtyId = profile.SpecialtyId, + StartDate = profile.JobStartDate, + LocationId = profile.LocationId, + }); - this.ViewBag.SuccessMessage = "Your job details have been changed"; - return this.View("SuccessMessage"); + this.ViewBag.SuccessMessage = "Your job details have been changed"; + return this.View("SuccessMessage"); + } + else + { + this.ModelState.AddModelError(nameof(viewModel.SelectedGradeId), CommonValidationErrorMessages.GradeRequired); + return this.View("ChangeGrade", viewModel); + } + } + else + { + viewModel.SelectedGradeId = profile.GradeId.ToString(); } return this.View("ChangeGrade", viewModel); diff --git a/LearningHub.Nhs.WebUI/Controllers/PoliciesController.cs b/LearningHub.Nhs.WebUI/Controllers/PoliciesController.cs index 4d6a54c4f..9f619e572 100644 --- a/LearningHub.Nhs.WebUI/Controllers/PoliciesController.cs +++ b/LearningHub.Nhs.WebUI/Controllers/PoliciesController.cs @@ -100,5 +100,15 @@ public IActionResult AcceptableUsePolicy() { return this.View(); } + + /// + /// AI-generated images statement. + /// + /// The . + [Route("/policies/ai-generated-images-statement")] + public IActionResult AIGeneratedImagesStatement() + { + return this.View(); + } } } diff --git a/LearningHub.Nhs.WebUI/Extensions/ClaimsPrincipalExtensions.cs b/LearningHub.Nhs.WebUI/Extensions/ClaimsPrincipalExtensions.cs index 30653fcbc..186c54eb8 100644 --- a/LearningHub.Nhs.WebUI/Extensions/ClaimsPrincipalExtensions.cs +++ b/LearningHub.Nhs.WebUI/Extensions/ClaimsPrincipalExtensions.cs @@ -2,6 +2,7 @@ { using System; using System.Security.Claims; + using System.Security.Principal; /// /// Defines the . @@ -23,5 +24,21 @@ public static string GetTimezoneOffsetCacheKey(this ClaimsPrincipal claimsPrinci return $"usr_{userId}_tz"; } + + /// + /// Get MoodleUserId. + /// + /// The identity. + /// The System.Int32. + public static int GetMoodleUserId(this IIdentity identity) + { + Claim claim = (identity as ClaimsIdentity)?.FindFirst("moodle_username"); + if (claim != null) + { + return int.Parse(claim.Value); + } + + return 0; + } } } \ No newline at end of file diff --git a/LearningHub.Nhs.WebUI/Helpers/CommonValidationErrorMessages.cs b/LearningHub.Nhs.WebUI/Helpers/CommonValidationErrorMessages.cs index 3e2753448..c9b3aa7bc 100644 --- a/LearningHub.Nhs.WebUI/Helpers/CommonValidationErrorMessages.cs +++ b/LearningHub.Nhs.WebUI/Helpers/CommonValidationErrorMessages.cs @@ -58,7 +58,7 @@ public static class CommonValidationErrorMessages /// /// Grade Required. /// - public const string GradeRequired = "Select a grade"; + public const string GradeRequired = "Select a pay band. You can find this information from your paper payslip or online via ESR"; /// /// Primary specialty Not Applicable. diff --git a/LearningHub.Nhs.WebUI/Helpers/FeatureFlags.cs b/LearningHub.Nhs.WebUI/Helpers/FeatureFlags.cs index f4b01af4d..7f019e336 100644 --- a/LearningHub.Nhs.WebUI/Helpers/FeatureFlags.cs +++ b/LearningHub.Nhs.WebUI/Helpers/FeatureFlags.cs @@ -14,5 +14,10 @@ public static class FeatureFlags /// The DisplayAudioVideoResource. /// public const string DisplayAudioVideoResource = "DisplayAudioVideoResource"; + + /// + /// The EnableMoodle. + /// + public const string EnableMoodle = "EnableMoodle"; } } diff --git a/LearningHub.Nhs.WebUI/Helpers/InMemoryTicketStore.cs b/LearningHub.Nhs.WebUI/Helpers/InMemoryTicketStore.cs new file mode 100644 index 000000000..4d17cc5b7 --- /dev/null +++ b/LearningHub.Nhs.WebUI/Helpers/InMemoryTicketStore.cs @@ -0,0 +1,104 @@ +namespace LearningHub.Nhs.WebUI.Helpers +{ + using System; + using System.Collections.Concurrent; + using System.Linq; + using System.Threading.Tasks; + using Microsoft.AspNetCore.Authentication; + using Microsoft.AspNetCore.Authentication.Cookies; + + /// + /// Defines the . + /// + public class InMemoryTicketStore : ITicketStore + { + private readonly ConcurrentDictionary cache; + + /// + /// Initializes a new instance of the class. + /// The InMemoryTicketStore. + /// + /// the cache. + public InMemoryTicketStore(ConcurrentDictionary cache) + { + this.cache = cache; + } + + /// + /// The StoreAsync. + /// + /// The ticket. + /// The key. + public async Task StoreAsync(AuthenticationTicket ticket) + { + var ticketUserId = ticket.Principal.Claims.Where(c => c.Type == "sub") + .FirstOrDefault() + .Value; + var matchingAuthTicket = this.cache.Values.FirstOrDefault( + t => t.Principal.Claims.FirstOrDefault( + c => c.Type == "sub" + && c.Value == ticketUserId) != null); + if (matchingAuthTicket != null) + { + var cacheKey = this.cache.Where( + entry => entry.Value == matchingAuthTicket) + .Select(entry => entry.Key) + .FirstOrDefault(); + this.cache.TryRemove( + cacheKey, + out _); + } + + var key = Guid + .NewGuid() + .ToString(); + await this.RenewAsync( + key, + ticket); + return key; + } + + /// + /// The RenewAsync. + /// + /// The key. + /// The ticket. + /// The Task. + public Task RenewAsync( + string key, + AuthenticationTicket ticket) + { + this.cache.AddOrUpdate( + key, + ticket, + (_, _) => ticket); + return Task.CompletedTask; + } + + /// + /// The RetrieveAsync. + /// + /// The Key. + /// The Task. + public Task RetrieveAsync(string key) + { + this.cache.TryGetValue( + key, + out var ticket); + return Task.FromResult(ticket); + } + + /// + /// The RemoveAsync. + /// + /// The key. + /// The Task. + public Task RemoveAsync(string key) + { + this.cache.TryRemove( + key, + out _); + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/LearningHub.Nhs.WebUI/Interfaces/ICatalogueService.cs b/LearningHub.Nhs.WebUI/Interfaces/ICatalogueService.cs index 635eee151..e7770fd86 100644 --- a/LearningHub.Nhs.WebUI/Interfaces/ICatalogueService.cs +++ b/LearningHub.Nhs.WebUI/Interfaces/ICatalogueService.cs @@ -143,8 +143,7 @@ public interface ICatalogueService /// The GetAllCatalogueAsync. /// /// The letter. - /// The pageSize. /// The allcatalogue result based on letters. - Task GetAllCatalogueAsync(string filterChar, int pageSize); + Task GetAllCatalogueAsync(string filterChar); } } diff --git a/LearningHub.Nhs.WebUI/Interfaces/IDashboardService.cs b/LearningHub.Nhs.WebUI/Interfaces/IDashboardService.cs index de8996766..9eb8c3266 100644 --- a/LearningHub.Nhs.WebUI/Interfaces/IDashboardService.cs +++ b/LearningHub.Nhs.WebUI/Interfaces/IDashboardService.cs @@ -1,5 +1,6 @@ namespace LearningHub.Nhs.WebUI.Interfaces { + using System.Collections.Generic; using System.Threading.Tasks; using LearningHub.Nhs.Models.Dashboard; using LearningHub.Nhs.WebUI.Models; @@ -39,5 +40,13 @@ public interface IDashboardService /// dashboardEventViewModel. /// A representing the result of the asynchronous operation. Task RecordDashBoardEventAsync(DashboardEventViewModel dashboardEventViewModel); + + /// + /// GetEnrolledCoursesFromMoodleAsync. + /// + /// The current User Id type. + /// The page Number. + /// A representing the result of the asynchronous operation. + Task> GetEnrolledCoursesFromMoodleAsync(int currentUserId, int pageNumber); } } diff --git a/LearningHub.Nhs.WebUI/Interfaces/IMoodleApiService.cs b/LearningHub.Nhs.WebUI/Interfaces/IMoodleApiService.cs new file mode 100644 index 000000000..d92c01fad --- /dev/null +++ b/LearningHub.Nhs.WebUI/Interfaces/IMoodleApiService.cs @@ -0,0 +1,30 @@ +namespace LearningHub.Nhs.WebUI.Interfaces +{ + using System.Collections.Generic; + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Dashboard; + using LearningHub.Nhs.WebUI.Models; + + /// + /// IMoodleApiService. + /// + public interface IMoodleApiService + { + /// + /// GetEnrolledCoursesAsync. + /// + /// Moodle user id. + /// pageNumber. + /// List of MoodleCourseResponseViewModel. + Task> GetEnrolledCoursesAsync(int currentUserId, int pageNumber); + + /// + /// GetEnrolledCoursesAsync. + /// + /// Moodle user id. + /// Moodle course id. + /// pageNumber. + /// List of MoodleCourseResponseViewModel. + Task GetCourseCompletionAsync(int userId, int courseId, int pageNumber); + } +} diff --git a/LearningHub.Nhs.WebUI/Interfaces/IMoodleHttpClient.cs b/LearningHub.Nhs.WebUI/Interfaces/IMoodleHttpClient.cs new file mode 100644 index 000000000..3348a20a9 --- /dev/null +++ b/LearningHub.Nhs.WebUI/Interfaces/IMoodleHttpClient.cs @@ -0,0 +1,23 @@ +namespace LearningHub.Nhs.Services.Interface +{ + using System.Net.Http; + using System.Threading.Tasks; + + /// + /// The Moodle Http Client interface. + /// + public interface IMoodleHttpClient + { + /// + /// The get cient async. + /// + /// The . + Task GetClient(); + + /// + /// GetDefaultParameters. + /// + /// defaultParameters. + string GetDefaultParameters(); + } +} \ No newline at end of file diff --git a/LearningHub.Nhs.WebUI/Interfaces/IResourceService.cs b/LearningHub.Nhs.WebUI/Interfaces/IResourceService.cs index c09ab0704..f5057402d 100644 --- a/LearningHub.Nhs.WebUI/Interfaces/IResourceService.cs +++ b/LearningHub.Nhs.WebUI/Interfaces/IResourceService.cs @@ -269,6 +269,13 @@ public interface IResourceService /// The . Task CreateResourceVersionProviderAsync(ResourceVersionProviderViewModel model); + /// + /// Creates resource version validation results corresponding to the value in the corresponding input view model. + /// + /// Details of the validation results. + /// A representing the result of the asynchronous operation. + Task CreateResourceVersionValidationResultAsync(ResourceVersionValidationResultViewModel validationResultViewModel); + /// /// Delete resource version provider. /// diff --git a/LearningHub.Nhs.WebUI/Interfaces/IUserService.cs b/LearningHub.Nhs.WebUI/Interfaces/IUserService.cs index 0b9dd4b77..19ee0a927 100644 --- a/LearningHub.Nhs.WebUI/Interfaces/IUserService.cs +++ b/LearningHub.Nhs.WebUI/Interfaces/IUserService.cs @@ -418,6 +418,15 @@ public interface IUserService /// A representing the result of the asynchronous operation. Task RegenerateEmailChangeValidationTokenAsync(string newPrimaryEmail, bool isUserRoleUpgrade); + /// + /// User Can request for password reset. + /// + /// The email Address. + /// The passwordRequestLimitingPeriod. + /// ThepasswordRequestLimit. + /// A representing the result of the asynchronous operation. + Task CanRequestPasswordResetAsync(string emailAddress, int passwordRequestLimitingPeriod, int passwordRequestLimit); + /// /// GenerateEmailChangeValidationTokenAndSendEmail. /// @@ -456,6 +465,13 @@ public interface IUserService /// providers. Task> GetProvidersByUserIdAsync(int userId); + /// + /// To Check User Has An ActiveSession. + /// + /// The userId. + /// A representing the result of the asynchronous operation. + Task> CheckUserHasAnActiveSessionAsync(int userId); + /// /// To get the Base64MD5HashDigest value. /// diff --git a/LearningHub.Nhs.WebUI/LearningHub.Nhs.WebUI.csproj b/LearningHub.Nhs.WebUI/LearningHub.Nhs.WebUI.csproj index d8698b4f9..24d3c72f8 100644 --- a/LearningHub.Nhs.WebUI/LearningHub.Nhs.WebUI.csproj +++ b/LearningHub.Nhs.WebUI/LearningHub.Nhs.WebUI.csproj @@ -1,254 +1,242 @@  - - - net8.0 - 1.0.0.0 - 1.0.0.0 - 1.0.0 - InProcess - a2ecb5d2-cf13-4551-9cb6-3d86dfbcf8ef - true - true - x64 - true - - - - Project - http://localhost:5001 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - Always - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - - - - - - - - + + net8.0 + 1.0.0.0 + 1.0.0.0 + 1.0.0 + InProcess + a2ecb5d2-cf13-4551-9cb6-3d86dfbcf8ef + true + true + x64 + true + + + Project + http://localhost:5001 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + Always + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + + + + + + \ No newline at end of file diff --git a/LearningHub.Nhs.WebUI/Models/Account/AccountCreationDateViewModel.cs b/LearningHub.Nhs.WebUI/Models/Account/AccountCreationDateViewModel.cs index c17fad8a9..68727eb35 100644 --- a/LearningHub.Nhs.WebUI/Models/Account/AccountCreationDateViewModel.cs +++ b/LearningHub.Nhs.WebUI/Models/Account/AccountCreationDateViewModel.cs @@ -13,17 +13,32 @@ public class AccountCreationDateViewModel : AccountCreationViewModel, IValidatab /// /// Gets or sets the Day. /// - public int? Day { get; set; } + public string Day { get; set; } + + /// + /// Gets or sets the Day Field. + /// + public int? DayInput { get; set; } /// /// Gets or sets the Country. /// - public int? Month { get; set; } + public string Month { get; set; } + + /// + /// Gets or sets the Month input. + /// + public int? MonthInput { get; set; } /// /// Gets or sets the Year. /// - public int? Year { get; set; } + public string Year { get; set; } + + /// + /// Gets or sets YearInput. + /// + public int? YearInput { get; set; } /// /// Gets or sets the GetDate. @@ -31,14 +46,46 @@ public class AccountCreationDateViewModel : AccountCreationViewModel, IValidatab /// DateTime. public DateTime? GetDate() { - return (this.Day.HasValue && this.Month.HasValue && this.Year.HasValue) ? new DateTime(this.Year!.Value, this.Month!.Value, this.Day!.Value) : (DateTime?)null; + return (this.DayInput.HasValue && this.MonthInput.HasValue && this.YearInput.HasValue) ? new DateTime(this.YearInput!.Value, this.MonthInput!.Value, this.DayInput!.Value) : (DateTime?)null; } /// public IEnumerable Validate(ValidationContext validationContext) { - return DateValidator.ValidateDate(this.Day, this.Month, this.Year, "valid start date") - .ToValidationResultList(nameof(this.Day), nameof(this.Month), nameof(this.Year)); + var validationResults = new List(); + int parsedDay = 0; + int parsedMonth = 0; + int parsedYear = 0; + + if (!string.IsNullOrWhiteSpace(this.Day) && !int.TryParse(this.Day, out parsedDay)) + { + validationResults.Add(new ValidationResult( + $"The value '{this.Day}' is not valid for Day.", new[] { nameof(this.Day) })); + } + + if (!string.IsNullOrWhiteSpace(this.Month) && !int.TryParse(this.Month, out parsedMonth)) + { + validationResults.Add(new ValidationResult( + $"The value '{this.Month}' is not valid for Month.", new[] { nameof(this.Month) })); + } + + if (!string.IsNullOrWhiteSpace(this.Year) && !int.TryParse(this.Year, out parsedYear)) + { + validationResults.Add(new ValidationResult( + $"The value '{this.Year}' is not valid for Year.", new[] { nameof(this.Year) })); + } + + if (validationResults.Count > 0) + { + return validationResults; + } + + this.DayInput = parsedDay; + this.MonthInput = parsedMonth; + this.YearInput = parsedYear; + + return DateValidator.ValidateDate(this.DayInput, this.MonthInput, this.YearInput, "valid start date") + .ToValidationResultList(nameof(this.Day), nameof(this.Month), nameof(this.Year)); } } } diff --git a/LearningHub.Nhs.WebUI/Models/DashboardViewModel.cs b/LearningHub.Nhs.WebUI/Models/DashboardViewModel.cs index c1358827f..9ac98c7e0 100644 --- a/LearningHub.Nhs.WebUI/Models/DashboardViewModel.cs +++ b/LearningHub.Nhs.WebUI/Models/DashboardViewModel.cs @@ -1,5 +1,6 @@ namespace LearningHub.Nhs.WebUI.Models { + using System.Collections.Generic; using LearningHub.Nhs.Models.Dashboard; /// @@ -28,5 +29,10 @@ public DashboardViewModel() /// Gets or sets a list of catalogues to be displayed in the dashboard. /// public DashboardCatalogueResponseViewModel Catalogues { get; set; } + + /// + /// Gets or sets a list of enrolled courses to be displayed in the dashboard. + /// + public List EnrolledCourses { get; set; } } } diff --git a/LearningHub.Nhs.WebUI/Models/FileDownloadResponse.cs b/LearningHub.Nhs.WebUI/Models/FileDownloadResponse.cs index 8036e9320..1a4d76679 100644 --- a/LearningHub.Nhs.WebUI/Models/FileDownloadResponse.cs +++ b/LearningHub.Nhs.WebUI/Models/FileDownloadResponse.cs @@ -21,5 +21,10 @@ public class FileDownloadResponse /// Gets or sets the ContentType. /// public long ContentLength { get; set; } + + /// + /// Gets or sets when downloading large files, a SAS URL is returned so the client can download directly from Azure Files. + /// + public string DownloadUrl { get; set; } } } \ No newline at end of file diff --git a/LearningHub.Nhs.WebUI/Models/MoodleCompletionResponseViewModel.cs b/LearningHub.Nhs.WebUI/Models/MoodleCompletionResponseViewModel.cs new file mode 100644 index 000000000..1692a32e6 --- /dev/null +++ b/LearningHub.Nhs.WebUI/Models/MoodleCompletionResponseViewModel.cs @@ -0,0 +1,28 @@ +namespace LearningHub.Nhs.WebUI.Models +{ + /// + /// MoodleCompletionResponseViewModel. + /// + public class MoodleCompletionResponseViewModel + { + /// + /// Gets or sets the completion status. + /// + public string Exception { get; set; } + + /// + /// Gets or sets error code. + /// + public string Errorcode { get; set; } + + /// + /// Gets or sets Error message. + /// + public string Message { get; set; } + + /// + /// Gets or sets Debug info. + /// + public string Debuginfo { get; set; } + } +} diff --git a/LearningHub.Nhs.WebUI/Models/MoodleCourseCompletionViewModel.cs b/LearningHub.Nhs.WebUI/Models/MoodleCourseCompletionViewModel.cs new file mode 100644 index 000000000..06eb5cb8f --- /dev/null +++ b/LearningHub.Nhs.WebUI/Models/MoodleCourseCompletionViewModel.cs @@ -0,0 +1,103 @@ +namespace LearningHub.Nhs.WebUI.Models +{ + using System.Collections.Generic; + + /// + /// MoodleCourseCompletionViewModel. + /// + public class MoodleCourseCompletionViewModel + { + /// + /// Gets or sets the completion status. + /// + public CompletStatus CompletionStatus { get; set; } + + /// + /// Gets or sets the list of warnings. + /// + public List Warnings { get; set; } + + /// + /// CompletionStatus. + /// + public class CompletStatus + { + /// + /// Gets or sets a value indicating whether the course is completed. + /// + public bool Completed { get; set; } + + /// + /// Gets or sets the aggregation method. + /// + public int Aggregation { get; set; } + + /// + /// Gets or sets the list of completions. + /// + public List Completions { get; set; } + + /// + /// Completion. + /// + public class Completion + { + /// + /// Gets or sets the type of completion. + /// + public int Type { get; set; } + + /// + /// Gets or sets the title of the completion requirement. + /// + public string Title { get; set; } + + /// + /// Gets or sets the status of the completion. + /// + public string Status { get; set; } + + /// + /// Gets or sets a value indicating whether the requirement is complete. + /// + public bool Complete { get; set; } + + /// + /// Gets or sets the timestamp when completion was achieved. + /// + public long? TimeCompleted { get; set; } + + /// + /// Gets or sets the completion details. + /// + public CompletionDetails Details { get; set; } + + /// + /// CompletionDetails. + /// + public class CompletionDetails + { + /// + /// Gets or sets the type of completion requirement. + /// + public string Type { get; set; } + + /// + /// Gets or sets the criteria for completion. + /// + public string Criteria { get; set; } + + /// + /// Gets or sets the requirement for completion. + /// + public string Requirement { get; set; } + + /// + /// Gets or sets the status of the requirement. + /// + public string Status { get; set; } + } + } + } + } +} \ No newline at end of file diff --git a/LearningHub.Nhs.WebUI/Models/MoodleCourseResponseViewModel.cs b/LearningHub.Nhs.WebUI/Models/MoodleCourseResponseViewModel.cs new file mode 100644 index 000000000..ad94f7b7a --- /dev/null +++ b/LearningHub.Nhs.WebUI/Models/MoodleCourseResponseViewModel.cs @@ -0,0 +1,160 @@ +namespace LearningHub.Nhs.WebUI.Models +{ + using System.Collections.Generic; + + /// + /// MoodleCourseResponseViewModel. + /// + public class MoodleCourseResponseViewModel + { + /// + /// Gets or sets the ID. + /// + public int? Id { get; set; } + + /// + /// Gets or sets the short name. + /// + public string ShortName { get; set; } + + /// + /// Gets or sets the full name. + /// + public string FullName { get; set; } + + /// + /// Gets or sets the display name. + /// + public string DisplayName { get; set; } + + /// + /// Gets or sets the enrolled user count. + /// + public int? EnrolledUserCount { get; set; } + + /// + /// Gets or sets the ID number. + /// + public string IdNumber { get; set; } + + /// + /// Gets or sets the visibility status. + /// + public int? Visible { get; set; } + + /// + /// Gets or sets the summary. + /// + public string Summary { get; set; } + + /// + /// Gets or sets the summary format. + /// + public int? SummaryFormat { get; set; } + + /// + /// Gets or sets the format. + /// + public string Format { get; set; } + + /// + /// Gets or sets the course image URL. + /// + public string CourseImage { get; set; } + + /// + /// Gets or sets a value indicating whether grades are shown. + /// + public bool? ShowGrades { get; set; } + + /// + /// Gets or sets the language. + /// + public string Lang { get; set; } + + /// + /// Gets or sets a value indicating whether completion is enabled. + /// + public bool? EnableCompletion { get; set; } + + /// + /// Gets or sets a value indicating whether completion has criteria. + /// + public bool? CompletionHasCriteria { get; set; } + + /// + /// Gets or sets a value indicating whether completion is user-tracked. + /// + public bool? CompletionUserTracked { get; set; } + + /// + /// Gets or sets the category ID. + /// + public int? Category { get; set; } + + /// + /// Gets or sets the progress. + /// + public int? Progress { get; set; } + + /// + /// Gets or sets the completion status. + /// + public bool? Completed { get; set; } + + /// + /// Gets or sets the start date (Unix timestamp). + /// + public long? StartDate { get; set; } + + /// + /// Gets or sets the end date. + /// + public int? EndDate { get; set; } + + /// + /// Gets or sets the marker. + /// + public int? Marker { get; set; } + + /// + /// Gets or sets the last access timestamp. + /// + public int? LastAccess { get; set; } + + /// + /// Gets or sets a value indicating whether the course is a favorite. + /// + public bool? IsFavourite { get; set; } + + /// + /// Gets or sets a value indicating whether the course is hidden. + /// + public bool? Hidden { get; set; } + + /// + /// Gets or sets the list of overview files. + /// + public List OverviewFiles { get; set; } + + /// + /// Gets or sets a value indicating whether activity dates are shown. + /// + public bool? ShowActivityDates { get; set; } + + /// + /// Gets or sets a value indicating whether completion conditions are shown. + /// + public bool? ShowCompletionConditions { get; set; } + + /// + /// Gets or sets the last modified timestamp (Unix timestamp). + /// + public long? TimeModified { get; set; } + + /// + /// Gets or sets the moodle course completion view model. + /// + public MoodleCourseCompletionViewModel CourseCompletionViewModel { get; set; } + } +} diff --git a/LearningHub.Nhs.WebUI/Models/MoodleOverviewFileViewModel.cs b/LearningHub.Nhs.WebUI/Models/MoodleOverviewFileViewModel.cs new file mode 100644 index 000000000..3dd335c41 --- /dev/null +++ b/LearningHub.Nhs.WebUI/Models/MoodleOverviewFileViewModel.cs @@ -0,0 +1,38 @@ +namespace LearningHub.Nhs.WebUI.Models +{ + /// + /// MoodleOverviewFileViewModel. + /// + public class MoodleOverviewFileViewModel + { + /// + /// Gets or sets the file name. + /// + public string? FileName { get; set; } + + /// + /// Gets or sets the file path. + /// + public string? FilePath { get; set; } + + /// + /// Gets or sets the file size in bytes. + /// + public int FileSize { get; set; } + + /// + /// Gets or sets the file URL. + /// + public string? FileUrl { get; set; } + + /// + /// Gets or sets the time the file was modified (Unix timestamp). + /// + public long TimeModified { get; set; } + + /// + /// Gets or sets the MIME type of the file. + /// + public string? MimeType { get; set; } + } +} diff --git a/LearningHub.Nhs.WebUI/Models/MoodleUserResponseViewModel.cs b/LearningHub.Nhs.WebUI/Models/MoodleUserResponseViewModel.cs new file mode 100644 index 000000000..7940dc725 --- /dev/null +++ b/LearningHub.Nhs.WebUI/Models/MoodleUserResponseViewModel.cs @@ -0,0 +1,121 @@ +namespace LearningHub.Nhs.WebUI.Models +{ + using System.Collections.Generic; + + /// + /// MoodleUserResponseViewModel. + /// + public class MoodleUserResponseViewModel + { + /// + /// Gets or sets the list of users. + /// + public List Users { get; set; } + + /// + /// Gets or sets the warnings. + /// + public List Warnings { get; set; } + + /// + /// MoodleUser. + /// + public class MoodleUser + { + /// + /// Gets or sets the user ID. + /// + public int Id { get; set; } + + /// + /// Gets or sets the username. + /// + public string Username { get; set; } + + /// + /// Gets or sets the first name. + /// + public string FirstName { get; set; } + + /// + /// Gets or sets the last name. + /// + public string LastName { get; set; } + + /// + /// Gets or sets the full name. + /// + public string FullName { get; set; } + + /// + /// Gets or sets the email. + /// + public string Email { get; set; } + + /// + /// Gets or sets the department. + /// + public string Department { get; set; } + + /// + /// Gets or sets the first access timestamp. + /// + public long FirstAccess { get; set; } + + /// + /// Gets or sets the last access timestamp. + /// + public long LastAccess { get; set; } + + /// + /// Gets or sets the authentication method. + /// + public string Auth { get; set; } + + /// + /// Gets or sets a value indicating whether the user is suspended. + /// + public bool Suspended { get; set; } + + /// + /// Gets or sets a value indicating whether the user is confirmed. + /// + public bool Confirmed { get; set; } + + /// + /// Gets or sets the language. + /// + public string Lang { get; set; } + + /// + /// Gets or sets the theme. + /// + public string Theme { get; set; } + + /// + /// Gets or sets the timezone. + /// + public string Timezone { get; set; } + + /// + /// Gets or sets the mail format. + /// + public int MailFormat { get; set; } + + /// + /// Gets or sets the forum tracking preference. + /// + public int TrackForums { get; set; } + + /// + /// Gets or sets the small profile image URL. + /// + public string ProfileImageUrlSmall { get; set; } + + /// + /// Gets or sets the profile image URL. + /// + public string ProfileImageUrl { get; set; } + } + } +} diff --git a/LearningHub.Nhs.WebUI/Models/UserProfile/UserGradeUpdateViewModel.cs b/LearningHub.Nhs.WebUI/Models/UserProfile/UserGradeUpdateViewModel.cs index af9915c18..5394cf09b 100644 --- a/LearningHub.Nhs.WebUI/Models/UserProfile/UserGradeUpdateViewModel.cs +++ b/LearningHub.Nhs.WebUI/Models/UserProfile/UserGradeUpdateViewModel.cs @@ -38,7 +38,7 @@ public class UserGradeUpdateViewModel /// /// Gets or sets the selected grade id. /// - public int? SelectedGradeId { get; set; } + public string SelectedGradeId { get; set; } /// /// Gets or sets the grade list. diff --git a/LearningHub.Nhs.WebUI/Program.cs b/LearningHub.Nhs.WebUI/Program.cs index 8aee50089..c24d9057c 100644 --- a/LearningHub.Nhs.WebUI/Program.cs +++ b/LearningHub.Nhs.WebUI/Program.cs @@ -84,7 +84,6 @@ app.UseAuthorization(); app.UseMiddleware(); - app.UseStaticFiles(); app.Map(TimezoneInfoMiddleware.TimezoneInfoUrl, b => b.UseMiddleware()); diff --git a/LearningHub.Nhs.WebUI/Scripts/vuesrc/catalogue/catalogueaccessrequest.vue b/LearningHub.Nhs.WebUI/Scripts/vuesrc/catalogue/catalogueaccessrequest.vue index 89d5bba9d..b178e70f3 100644 --- a/LearningHub.Nhs.WebUI/Scripts/vuesrc/catalogue/catalogueaccessrequest.vue +++ b/LearningHub.Nhs.WebUI/Scripts/vuesrc/catalogue/catalogueaccessrequest.vue @@ -99,7 +99,7 @@
- +
diff --git a/LearningHub.Nhs.WebUI/Scripts/vuesrc/ckeditorwithhint.vue b/LearningHub.Nhs.WebUI/Scripts/vuesrc/ckeditorwithhint.vue index 80415a53d..e45ba54a2 100644 --- a/LearningHub.Nhs.WebUI/Scripts/vuesrc/ckeditorwithhint.vue +++ b/LearningHub.Nhs.WebUI/Scripts/vuesrc/ckeditorwithhint.vue @@ -1,15 +1,26 @@ + - diff --git a/LearningHub.Nhs.WebUI/Scripts/vuesrc/contribute/ContentAccess.vue b/LearningHub.Nhs.WebUI/Scripts/vuesrc/contribute/ContentAccess.vue index 777f3e173..47eb992bd 100644 --- a/LearningHub.Nhs.WebUI/Scripts/vuesrc/contribute/ContentAccess.vue +++ b/LearningHub.Nhs.WebUI/Scripts/vuesrc/contribute/ContentAccess.vue @@ -13,14 +13,14 @@
@@ -148,5 +148,5 @@ left: 4px; width: 12px; height: 12px; - } + } \ No newline at end of file diff --git a/LearningHub.Nhs.WebUI/Scripts/vuesrc/contribute/ContentArticle.vue b/LearningHub.Nhs.WebUI/Scripts/vuesrc/contribute/ContentArticle.vue index 14b529e10..d84a4e962 100644 --- a/LearningHub.Nhs.WebUI/Scripts/vuesrc/contribute/ContentArticle.vue +++ b/LearningHub.Nhs.WebUI/Scripts/vuesrc/contribute/ContentArticle.vue @@ -29,7 +29,7 @@
- No file chosen + No file chosen
diff --git a/LearningHub.Nhs.WebUI/Scripts/vuesrc/contribute/ContentAudio.vue b/LearningHub.Nhs.WebUI/Scripts/vuesrc/contribute/ContentAudio.vue index 1d8fbc8ab..34fbeef97 100644 --- a/LearningHub.Nhs.WebUI/Scripts/vuesrc/contribute/ContentAudio.vue +++ b/LearningHub.Nhs.WebUI/Scripts/vuesrc/contribute/ContentAudio.vue @@ -25,7 +25,7 @@
- No file chosen + No file chosen
@@ -43,7 +43,7 @@ for example, how it was developed or what is required for it to be used.
- +
/// The filterChar. - /// the pageSize. - /// A representing the result of the asynchronous operation. - public async Task GetAllCatalogueAsync(string filterChar, int pageSize) + /// /// A representing the result of the asynchronous operation. + public async Task GetAllCatalogueAsync(string filterChar) { AllCatalogueResponseViewModel viewmodel = new AllCatalogueResponseViewModel { }; var client = await this.LearningHubHttpClient.GetClientAsync(); - var request = $"catalogue/allcatalogues/{pageSize}/{filterChar}"; + var request = $"catalogue/allcatalogues/{filterChar}"; var response = await client.GetAsync(request).ConfigureAwait(false); if (response.IsSuccessStatusCode) diff --git a/LearningHub.Nhs.WebUI/Services/ContributeService.cs b/LearningHub.Nhs.WebUI/Services/ContributeService.cs index 7da5a8844..a61487e4e 100644 --- a/LearningHub.Nhs.WebUI/Services/ContributeService.cs +++ b/LearningHub.Nhs.WebUI/Services/ContributeService.cs @@ -17,6 +17,8 @@ using LearningHub.Nhs.WebUI.Models.Contribute; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; + using Microsoft.VisualBasic; + using MK.IO.Models; using Newtonsoft.Json; /// @@ -38,7 +40,7 @@ public class ContributeService : BaseService, IContributeServ /// MKIO media service. /// Learning hub http client. /// Logger. - public ContributeService(IFileService fileService, IResourceService resourceService, IAzureMediaService azureMediaService, ILearningHubHttpClient learningHubHttpClient, ILogger logger, IAzureMediaService mediaService) + public ContributeService(IFileService fileService, IResourceService resourceService, IAzureMediaService azureMediaService, ILearningHubHttpClient learningHubHttpClient, ILogger logger, IAzureMediaService mediaService) : base(learningHubHttpClient, logger) { this.fileService = fileService; @@ -502,6 +504,31 @@ public async Task ProcessResourceFileAsync(int resourceVersion if ((fileType == null) || (fileType != null && fileType.NotAllowed) || file.Length <= 0) { + // Define dangerous file extensions + string[] dangerousExtensions = { ".exe", ".dll", ".bat", ".js", ".vbs", ".sh", ".ps1" }; + if (dangerousExtensions.Any(ext => file.FileName.EndsWith(ext, StringComparison.OrdinalIgnoreCase))) + { + var error = $"A potentially harmful file has been detected and blocked: {file.FileName}."; + var validationDetail = new ResourceVersionValidationResultViewModel + { + ResourceVersionId = resourceVersionId, + Success = false, + Details = string.Empty, + AmendUserId = currentUserId, + ResourceVersionValidationRuleResultViewModels = new[] + { + new ResourceVersionValidationRuleResultViewModel + { + ResourceTypeValidationRuleEnum = ResourceTypeValidationRuleEnum.HtmlResource_RootIndexPresent, + Success = false, + Details = error, + }, + }.ToList(), + }; + + await this.resourceService.CreateResourceVersionValidationResultAsync(validationDetail); + } + return new FileUploadResult() { FileName = file.FileName, diff --git a/LearningHub.Nhs.WebUI/Services/DashboardService.cs b/LearningHub.Nhs.WebUI/Services/DashboardService.cs index f3ffdbc3a..55f5153bf 100644 --- a/LearningHub.Nhs.WebUI/Services/DashboardService.cs +++ b/LearningHub.Nhs.WebUI/Services/DashboardService.cs @@ -1,10 +1,15 @@ namespace LearningHub.Nhs.WebUI.Services { using System; + using System.Collections.Generic; + using System.Net.Http; + using System.Runtime.InteropServices.WindowsRuntime; using System.Text; using System.Threading.Tasks; using LearningHub.Nhs.Models.Dashboard; using LearningHub.Nhs.Models.Entities.Analytics; + using LearningHub.Nhs.Models.Entities.Reporting; + using LearningHub.Nhs.Services.Interface; using LearningHub.Nhs.WebUI.Interfaces; using LearningHub.Nhs.WebUI.Models; using Microsoft.Extensions.Logging; @@ -15,14 +20,18 @@ /// public class DashboardService : BaseService, IDashboardService { + private readonly IMoodleHttpClient moodleHttpClient; + /// /// Initializes a new instance of the class. /// /// learningHubHttpClient. /// logger. - public DashboardService(ILearningHubHttpClient learningHubHttpClient, ILogger logger) + /// MoodleHttpClient. + public DashboardService(ILearningHubHttpClient learningHubHttpClient, ILogger logger, IMoodleHttpClient moodleHttpClient) : base(learningHubHttpClient, logger) { + this.moodleHttpClient = moodleHttpClient; } /// @@ -112,6 +121,20 @@ public async Task GetResourcesAsync(string d return viewmodel; } + /// + /// GetEnrolledCoursesFromMoodleAsync. + /// + /// The dashboard type. + /// The page Number. + /// A representing the result of the asynchronous operation. + public async Task> GetEnrolledCoursesFromMoodleAsync(int currentUserId, int pageNumber) + { + List viewmodel = new List { }; + MoodleApiService moodleApiService = new MoodleApiService(this.moodleHttpClient); + viewmodel = await moodleApiService.GetEnrolledCoursesAsync(currentUserId, pageNumber); + return viewmodel; + } + /// /// Logs Dashboared viewed event. /// diff --git a/LearningHub.Nhs.WebUI/Services/FileService.cs b/LearningHub.Nhs.WebUI/Services/FileService.cs index e0d19d7f2..a09195d62 100644 --- a/LearningHub.Nhs.WebUI/Services/FileService.cs +++ b/LearningHub.Nhs.WebUI/Services/FileService.cs @@ -1,17 +1,13 @@ namespace LearningHub.Nhs.WebUI.Services { using System; - using System.Buffers; using System.Collections.Generic; using System.IO; using System.Linq; - using System.Threading; using System.Threading.Tasks; - using System.Threading.Tasks.Dataflow; - using Azure; - using Azure.Storage; using Azure.Storage.Files.Shares; using Azure.Storage.Files.Shares.Models; + using Azure.Storage.Sas; using LearningHub.Nhs.Models.Resource; using LearningHub.Nhs.WebUI.Configuration; using LearningHub.Nhs.WebUI.Interfaces; @@ -145,26 +141,19 @@ public async Task DownloadFileAsync(string filePath, strin try { - if (fileSize <= 900 * 1024 * 1024) + var response = new FileDownloadResponse { - // Directly download the entire file as a stream - var response = await file.DownloadAsync(); - return new FileDownloadResponse - { - Content = response.Value.Content, - ContentType = properties.Value.ContentType, - ContentLength = fileSize, - }; - } - else + ContentType = properties.Value.ContentType, + ContentLength = fileSize, + Content = await file.OpenReadAsync(), + }; + + if (fileSize >= 999 * 1024 * 1024) { - return new FileDownloadResponse - { - Content = await file.OpenReadAsync(), - ContentType = properties.Value.ContentType, - ContentLength = fileSize, - }; + response.DownloadUrl = this.GenerateSasUriForFile(file); } + + return response; } catch (Exception ex) { @@ -304,12 +293,12 @@ private async Task MoveOutPutDirectoryToArchive(List allDirectoryRef) } else { - var destinationFileClient = archiveDirectory.GetFileClient(fileItem.Name); - var uri = sourceFileClient.GenerateSasUri(Azure.Storage.Sas.ShareFileSasPermissions.Read, DateTime.UtcNow.AddHours(24)); + var destinationFileClient = archiveDirectory.GetFileClient(fileItem.Name); + var uri = sourceFileClient.GenerateSasUri(Azure.Storage.Sas.ShareFileSasPermissions.Read, DateTime.UtcNow.AddHours(24)); - await destinationFileClient.StartCopyAsync(uri); + await destinationFileClient.StartCopyAsync(uri); - await WaitForCopyAsync(destinationFileClient); + await WaitForCopyAsync(destinationFileClient); } } @@ -460,5 +449,23 @@ private async Task FindFileAsync(string filePath, string fileNa return null; } + + private string GenerateSasUriForFile(ShareFileClient fileClient) + { + if (fileClient.CanGenerateSasUri) + { + ShareSasBuilder sasBuilder = new ShareSasBuilder(ShareFileSasPermissions.Read, DateTimeOffset.UtcNow.AddMinutes(20)) + { + Protocol = SasProtocol.Https, + }; + + Uri sasUri = fileClient.GenerateSasUri(sasBuilder); + return sasUri.ToString(); + } + else + { + throw new InvalidOperationException("Unable to generate SAS URI for the file."); + } + } } } \ No newline at end of file diff --git a/LearningHub.Nhs.WebUI/Services/MoodleApiService.cs b/LearningHub.Nhs.WebUI/Services/MoodleApiService.cs new file mode 100644 index 000000000..068219b42 --- /dev/null +++ b/LearningHub.Nhs.WebUI/Services/MoodleApiService.cs @@ -0,0 +1,120 @@ +namespace LearningHub.Nhs.WebUI.Services +{ + using System; + using System.Collections.Generic; + using System.Net.Http; + using System.Text.Json; + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Reporting; + using LearningHub.Nhs.Services.Interface; + using LearningHub.Nhs.WebUI.Interfaces; + using LearningHub.Nhs.WebUI.Models; + using Microsoft.Extensions.Configuration; + using Microsoft.Extensions.Logging; + using Newtonsoft.Json; + + /// + /// MoodleApiService. + /// + public class MoodleApiService : IMoodleApiService + { + private readonly IMoodleHttpClient moodleHttpClient; + + /// + /// Initializes a new instance of the class. + /// + /// moodleHttpClient. + public MoodleApiService(IMoodleHttpClient moodleHttpClient) + { + this.moodleHttpClient = moodleHttpClient; + } + + /// + /// GetEnrolledCoursesAsync. + /// + /// Moodle user id. + /// The page Number. + /// A representing the result of the asynchronous operation. + public async Task> GetEnrolledCoursesAsync(int userId, int pageNumber) + { + List viewmodel = new List { }; + MoodleApiService moodleApiService = new MoodleApiService(this.moodleHttpClient); + + var client = await this.moodleHttpClient.GetClient(); + string additionalParameters = $"userid={userId}"; + string defaultParameters = this.moodleHttpClient.GetDefaultParameters(); + string url = $"&wsfunction=core_enrol_get_users_courses&{additionalParameters}"; + + HttpResponseMessage response = await client.GetAsync("?" + defaultParameters + url); + + if (response.IsSuccessStatusCode) + { + var result = response.Content.ReadAsStringAsync().Result; + + using var document = JsonDocument.Parse(result); + var root = document.RootElement; + + // Check if it's a JSON object and contains "exception" + if (!(root.ValueKind == JsonValueKind.Object && root.TryGetProperty("exception", out _))) + { + viewmodel = JsonConvert.DeserializeObject>(result); + + foreach (var course in viewmodel) + { + course.CourseCompletionViewModel = await moodleApiService.GetCourseCompletionAsync(userId, course.Id.Value, pageNumber); + } + } + else + { + // Contains error, handle it as needed. + } + } + else if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized || + response.StatusCode == System.Net.HttpStatusCode.Forbidden) + { + throw new Exception("AccessDenied"); + } + + return viewmodel; + } + + /// + /// GetEnrolledCoursesAsync. + /// + /// Moodle user id. + /// Moodle course id. + /// pageNumber. + /// List of MoodleCourseResponseViewModel. + public async Task GetCourseCompletionAsync(int userId, int courseId, int pageNumber) + { + MoodleCourseCompletionViewModel viewmodel = new MoodleCourseCompletionViewModel { }; + MoodleApiService moodleApiService = new MoodleApiService(this.moodleHttpClient); + + var client = await this.moodleHttpClient.GetClient(); + string additionalParameters = $"userid={userId}&courseid={courseId}"; + string defaultParameters = this.moodleHttpClient.GetDefaultParameters(); + string url = $"&wsfunction=core_completion_get_course_completion_status&{additionalParameters}"; + + HttpResponseMessage response = await client.GetAsync("?" + defaultParameters + url); + + if (response.IsSuccessStatusCode) + { + var result = response.Content.ReadAsStringAsync().Result; + + var canViewReport = JsonConvert.DeserializeObject(result); + + if (string.IsNullOrEmpty(canViewReport.Exception)) + { + viewmodel = JsonConvert.DeserializeObject(result); + } + } + else if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized || + response.StatusCode == System.Net.HttpStatusCode.Forbidden) + { + throw new Exception("AccessDenied"); + } + + return viewmodel; + } + } +} diff --git a/LearningHub.Nhs.WebUI/Services/MoodleHttpClient.cs b/LearningHub.Nhs.WebUI/Services/MoodleHttpClient.cs new file mode 100644 index 000000000..79f44027a --- /dev/null +++ b/LearningHub.Nhs.WebUI/Services/MoodleHttpClient.cs @@ -0,0 +1,87 @@ +namespace LearningHub.Nhs.Services +{ + using System; + using System.Net.Http; + using System.Net.Http.Headers; + using System.Threading.Tasks; + using LearningHub.Nhs.Services.Interface; + using Microsoft.Extensions.Configuration; + + /// + /// The Moodle Http Client. + /// + public class MoodleHttpClient : IMoodleHttpClient, IDisposable + { + private readonly HttpClient httpClient = new (); + private bool initialised = false; + private string moodleAPIBaseUrl; + private string moodleAPIMoodleWSRestFormat; + private string moodleAPIWSToken; + + /// + /// Initializes a new instance of the class. + /// + /// httpClient. + /// config. + public MoodleHttpClient(HttpClient httpClient, IConfiguration config) + { + this.httpClient = httpClient; + this.moodleAPIBaseUrl = config["MoodleAPIConfig:BaseUrl"] + "webservice/rest/server.php"; + this.moodleAPIMoodleWSRestFormat = config["MoodleAPIConfig:MoodleWSRestFormat"]; + this.moodleAPIWSToken = config["MoodleAPIConfig:WSToken"]; + } + + /// + /// The Get Client method. + /// + /// The . + public async Task GetClient() + { + this.Initialise(this.moodleAPIBaseUrl); + return this.httpClient; + } + + /// + /// GetDefaultParameters. + /// + /// defaultParameters. + public string GetDefaultParameters() + { + string defaultParameters = $"wstoken={this.moodleAPIWSToken}" + + $"&moodlewsrestformat={this.moodleAPIMoodleWSRestFormat}"; + + return defaultParameters; + } + + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// The dispoase. + /// + /// disposing. + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + this.httpClient.Dispose(); + } + } + + private void Initialise(string httpClientUrl) + { + if (this.initialised == false) + { + this.httpClient.BaseAddress = new Uri(httpClientUrl); + this.httpClient.DefaultRequestHeaders.Accept.Clear(); + this.httpClient.DefaultRequestHeaders.Accept.Add( + new MediaTypeWithQualityHeaderValue("application/json")); + this.initialised = true; + } + } + } +} diff --git a/LearningHub.Nhs.WebUI/Services/ResourceService.cs b/LearningHub.Nhs.WebUI/Services/ResourceService.cs index dfc5d4d17..02a233ea0 100644 --- a/LearningHub.Nhs.WebUI/Services/ResourceService.cs +++ b/LearningHub.Nhs.WebUI/Services/ResourceService.cs @@ -1195,6 +1195,38 @@ public async Task CreateResourceVersionProviderAsyn return apiResponse.ValidationResult; } + /// + /// Creates resource version validation results corresponding to the value in the corresponding input view model. + /// + /// Details of the validation results. + /// A representing the result of the asynchronous operation. + public async Task CreateResourceVersionValidationResultAsync(ResourceVersionValidationResultViewModel validationResultViewModel) + { + ApiResponse apiResponse = null; + var json = JsonConvert.SerializeObject(validationResultViewModel); + var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); + + var client = await this.LearningHubHttpClient.GetClientAsync(); + + var request = $"Resource/CreateResourceVersionValidationResult"; + var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); + + if (response.IsSuccessStatusCode) + { + var result = response.Content.ReadAsStringAsync().Result; + apiResponse = JsonConvert.DeserializeObject(result); + + if (!apiResponse.Success) + { + throw new Exception("save failed!"); + } + } + else if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized || response.StatusCode == System.Net.HttpStatusCode.Forbidden) + { + throw new Exception("AccessDenied"); + } + } + /// /// The delete resource version provider. /// diff --git a/LearningHub.Nhs.WebUI/Services/UserService.cs b/LearningHub.Nhs.WebUI/Services/UserService.cs index 83b8f64d8..a0ba11d9a 100644 --- a/LearningHub.Nhs.WebUI/Services/UserService.cs +++ b/LearningHub.Nhs.WebUI/Services/UserService.cs @@ -1640,6 +1640,31 @@ public async Task RegenerateEmailChangeVali return viewmodel; } + /// + public async Task CanRequestPasswordResetAsync(string emailAddress, int passwordRequestLimitingPeriod, int passwordRequestLimit) + { + bool status = false; + + var client = await this.LearningHubHttpClient.GetClientAsync(); + + var request = $"User/CanRequestPasswordReset/{emailAddress}/{passwordRequestLimitingPeriod}/{passwordRequestLimit}"; + var response = await client.GetAsync(request).ConfigureAwait(false); + + if (response.IsSuccessStatusCode) + { + var result = await response.Content.ReadAsStringAsync(); + status = JsonConvert.DeserializeObject(result); + } + else if (response.StatusCode == HttpStatusCode.Unauthorized + || + response.StatusCode == HttpStatusCode.Forbidden) + { + throw new Exception("AccessDenied"); + } + + return status; + } + /// public async Task GenerateEmailChangeValidationTokenAndSendEmailAsync(string emailAddress, bool isUserRoleUpgrade) { @@ -1839,6 +1864,28 @@ public async Task> GetProvidersByUserIdAsync(int userId) return viewmodel; } + /// + public async Task> CheckUserHasAnActiveSessionAsync(int userId) + { + PagedResultSet userHistoryViewModel = new PagedResultSet(); + + var client = await this.userApiHttpClient.GetClientAsync(); + var request = $"UserHistory/CheckUserHasActiveSession/{userId}"; + var response = await client.GetAsync(request).ConfigureAwait(false); + + if (response.IsSuccessStatusCode) + { + var result = await response.Content.ReadAsStringAsync(); + userHistoryViewModel = JsonConvert.DeserializeObject>(result); + } + else if (response.StatusCode == HttpStatusCode.Unauthorized || response.StatusCode == HttpStatusCode.Forbidden) + { + throw new Exception("AccessDenied"); + } + + return userHistoryViewModel; + } + /// /// The base 64 m d 5 hash digest. /// diff --git a/LearningHub.Nhs.WebUI/Startup/AuthenticationConfiguration.cs b/LearningHub.Nhs.WebUI/Startup/AuthenticationConfiguration.cs index 40caab241..e939bb2ea 100644 --- a/LearningHub.Nhs.WebUI/Startup/AuthenticationConfiguration.cs +++ b/LearningHub.Nhs.WebUI/Startup/AuthenticationConfiguration.cs @@ -1,12 +1,14 @@ namespace LearningHub.Nhs.WebUI.Startup { using System; + using System.Collections.Concurrent; using System.Linq; using System.Threading.Tasks; using IdentityModel; using LearningHub.Nhs.Caching; using LearningHub.Nhs.WebUI.Configuration; using LearningHub.Nhs.WebUI.Handlers; + using LearningHub.Nhs.WebUI.Helpers; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.OpenIdConnect; @@ -68,6 +70,7 @@ public static void ConfigureAuthentication(this IServiceCollection services, Lea options.ClaimActions.MapUniqueJsonKey("role", "role"); options.ClaimActions.MapUniqueJsonKey("name", "elfh_userName"); + options.ClaimActions.MapUniqueJsonKey("moodle_username", "preferred_username"); options.TokenValidationParameters = new TokenValidationParameters { NameClaimType = JwtClaimTypes.Name, diff --git a/LearningHub.Nhs.WebUI/Startup/ServiceMappings.cs b/LearningHub.Nhs.WebUI/Startup/ServiceMappings.cs index a0cc16237..fdab0fc8a 100644 --- a/LearningHub.Nhs.WebUI/Startup/ServiceMappings.cs +++ b/LearningHub.Nhs.WebUI/Startup/ServiceMappings.cs @@ -3,6 +3,8 @@ using System.Net.Http; using GDS.MultiPageFormData; using LearningHub.Nhs.Models.OpenAthens; + using LearningHub.Nhs.Services; + using LearningHub.Nhs.Services.Interface; using LearningHub.Nhs.WebUI.Filters; using LearningHub.Nhs.WebUI.Helpers; using LearningHub.Nhs.WebUI.Interfaces; @@ -52,12 +54,20 @@ public static void AddLearningHubMappings(this IServiceCollection services, ICon ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator, }); + services.AddHttpClient() + .ConfigurePrimaryHttpMessageHandler( + () => new HttpClientHandler + { + ServerCertificateCustomValidationCallback = + HttpClientHandler.DangerousAcceptAnyServerCertificateValidator, + }); } else { services.AddHttpClient(); services.AddHttpClient(); services.AddHttpClient(); + services.AddHttpClient(); } // Config diff --git a/LearningHub.Nhs.WebUI/Styles/nhsuk/common.scss b/LearningHub.Nhs.WebUI/Styles/nhsuk/common.scss index ec574a288..1c18cc3cc 100644 --- a/LearningHub.Nhs.WebUI/Styles/nhsuk/common.scss +++ b/LearningHub.Nhs.WebUI/Styles/nhsuk/common.scss @@ -228,3 +228,24 @@ form label.nhsuk-u-visually-hidden { margin-top: auto !important; margin-bottom: auto !important; } +.btn:focus, .btn.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem $nhsuk-yellow !important; + background-color: $govuk-focus-highlight-yellow; +} +/*Add a background color to the radio button when focused */ +.nhsuk-radios__input:focus + .radioButton { + box-shadow: 0 0 0 3px $nhsuk-yellow; +} +/*Add a background color to the radio button when focused */ +.nhsuk-checkboxes__input:focus + .checkmark { + box-shadow: 0 0 0 4px $nhsuk-yellow; +} + +.accessible-link:focus { + outline: none; + text-decoration: none; + color: $nhsuk-black; + box-shadow: 0 0 0 4px $nhsuk-yellow; +} + diff --git a/LearningHub.Nhs.WebUI/Styles/nhsuk/layout.scss b/LearningHub.Nhs.WebUI/Styles/nhsuk/layout.scss index 03c179388..4cb2822fe 100644 --- a/LearningHub.Nhs.WebUI/Styles/nhsuk/layout.scss +++ b/LearningHub.Nhs.WebUI/Styles/nhsuk/layout.scss @@ -411,6 +411,90 @@ li.autosuggestion-option:last-of-type { .autosuggestion-menu { top: 100%; } + + .nhsuk-header__not-mobile { + display: none; + } + + .nhsuk-header__mobile-only-nav { + display: flex; + order: 1; + justify-content: space-around; + gap: 0 px2rem(16); + align-items: flex-start; + flex-wrap: wrap; + width: px2rem(166); + } + + .nhsuk-header__mobile-only-nav .nhsuk-header__menu { + margin-right: px2rem(12); + } + + .nhsuk-header__mobile-only-nav .nhsuk-header__search-toggle { + margin-left: px2rem(12); + } + + .nhsuk-header__break { + display: none; + } + + .nhsuk-header__mobile-break { + display: block; + width: 100%; + height: 0; + } + + .nhsuk-header__link--service { + flex-direction: column; + align-items: flex-start; + } + + .nhsuk-header__notification-dot { + top: px2rem(13); + left: px2rem(100); + } + + .nhsuk-header__service-name { + padding: px2rem(12) 0 0; + } + + .nhsuk-header__search-toggle { + position: relative; + height: px2rem(40); + order: 2; + padding: px2rem(7) px2rem(10) 0; + margin: 0 + } + + .nhsuk-header__search .nhsuk-search__submit { + padding-top: nhsuk-spacing(1); + } + + .nhsuk-header__menu { + order: 3; + } + + .nhsuk-header__search { + order: 4; + width: 100%; + flex-grow: 1; + margin: px2rem(16) px2rem(-16) 0; + border-bottom: 1px solid $color_nhsuk-grey-4; + } + + #header-mobile-search-control { + display: block; + opacity: 0; + position: absolute; + } + + #header-mobile-search-control:checked ~ .nhsuk-header__search .nhsuk-header__search-wrap { + display: block; + } + + .nhsuk-width-container.nhsuk-header__container.app-width-container { + padding-bottom: 0; + } } /* mobile */ diff --git a/LearningHub.Nhs.WebUI/Styles/nhsuk/pages/bookmark.scss b/LearningHub.Nhs.WebUI/Styles/nhsuk/pages/bookmark.scss index 6378eae15..126c5c35f 100644 --- a/LearningHub.Nhs.WebUI/Styles/nhsuk/pages/bookmark.scss +++ b/LearningHub.Nhs.WebUI/Styles/nhsuk/pages/bookmark.scss @@ -112,6 +112,23 @@ td.col-type { tr.bookmark-item:hover td { background-color: $nhsuk-white; } +.my-bookmark-btn:focus { + outline: none; + text-decoration: none !important; + color: $nhsuk-black !important; + background-color: $nhsuk-yellow !important; + box-shadow: 0 -2px $govuk-focus-highlight-yellow, 0 4px $nhsuk-black; +} + +.my-bookmark-btn:hover { + outline: none; + text-decoration: none !important; +} + +.my-bookmark-btn { + transition: none !important; + border-radius: 0 !important; +} @media (min-width: 768px) { diff --git a/LearningHub.Nhs.WebUI/Styles/nhsuk/pages/search.scss b/LearningHub.Nhs.WebUI/Styles/nhsuk/pages/search.scss index 4cc9653d5..31a2d0d24 100644 --- a/LearningHub.Nhs.WebUI/Styles/nhsuk/pages/search.scss +++ b/LearningHub.Nhs.WebUI/Styles/nhsuk/pages/search.scss @@ -44,7 +44,7 @@ justify-content: space-between; .nhsuk-list { - width: 360px; + width: 378px; li { width: 33.33%; diff --git a/LearningHub.Nhs.WebUI/Views/Account/AlreadyAnActiveSession.cshtml b/LearningHub.Nhs.WebUI/Views/Account/AlreadyAnActiveSession.cshtml new file mode 100644 index 000000000..a0e908c15 --- /dev/null +++ b/LearningHub.Nhs.WebUI/Views/Account/AlreadyAnActiveSession.cshtml @@ -0,0 +1,15 @@ +@{ + ViewData["Title"] = "Session already active"; +} +
+
+
+
+

@ViewData["Title"]

+

You are already logged in from another browser. Please continue using the same browser or close the existing session and try again with a new one.

+

If you have any questions, please contact the support team.

+

@DateTimeOffset.Now.ToString("d MMMM yyyy HH:mm:ss")

+
+
+
+
\ No newline at end of file diff --git a/LearningHub.Nhs.WebUI/Views/Account/ChangePassword.cshtml b/LearningHub.Nhs.WebUI/Views/Account/ChangePassword.cshtml index d7819faea..6bf906653 100644 --- a/LearningHub.Nhs.WebUI/Views/Account/ChangePassword.cshtml +++ b/LearningHub.Nhs.WebUI/Views/Account/ChangePassword.cshtml @@ -1,6 +1,7 @@ @model LearningHub.Nhs.WebUI.Models.Account.PasswordValidateViewModel @{ + ViewData["DisableValidation"] = true; ViewData["Title"] = "Account - Change password"; var errorHasOccurred = !ViewData.ModelState.IsValid; } diff --git a/LearningHub.Nhs.WebUI/Views/Account/CreateAccountEmailVerification.cshtml b/LearningHub.Nhs.WebUI/Views/Account/CreateAccountEmailVerification.cshtml index 06da30f1b..693c82116 100644 --- a/LearningHub.Nhs.WebUI/Views/Account/CreateAccountEmailVerification.cshtml +++ b/LearningHub.Nhs.WebUI/Views/Account/CreateAccountEmailVerification.cshtml @@ -37,13 +37,13 @@

Create an account

Information: -

Ideally you should provide a work email address so that your registration results in the most appropriate access.

+

Providing an email address will ensure your registration receives the appropriate access.

diff --git a/LearningHub.Nhs.WebUI/Views/Account/CreateAccountPrimarySpecialty.cshtml b/LearningHub.Nhs.WebUI/Views/Account/CreateAccountPrimarySpecialty.cshtml index 0bc9979cf..fb35d21ad 100644 --- a/LearningHub.Nhs.WebUI/Views/Account/CreateAccountPrimarySpecialty.cshtml +++ b/LearningHub.Nhs.WebUI/Views/Account/CreateAccountPrimarySpecialty.cshtml @@ -63,7 +63,7 @@
-
diff --git a/LearningHub.Nhs.WebUI/Views/Account/CreateAccountPrimarySpecialtySelection.cshtml b/LearningHub.Nhs.WebUI/Views/Account/CreateAccountPrimarySpecialtySelection.cshtml index 495df4081..393d5c76a 100644 --- a/LearningHub.Nhs.WebUI/Views/Account/CreateAccountPrimarySpecialtySelection.cshtml +++ b/LearningHub.Nhs.WebUI/Views/Account/CreateAccountPrimarySpecialtySelection.cshtml @@ -145,7 +145,7 @@
-
diff --git a/LearningHub.Nhs.WebUI/Views/Account/CreateAccountRegistrationInformation.cshtml b/LearningHub.Nhs.WebUI/Views/Account/CreateAccountRegistrationInformation.cshtml index 12d51bbd2..d39a34171 100644 --- a/LearningHub.Nhs.WebUI/Views/Account/CreateAccountRegistrationInformation.cshtml +++ b/LearningHub.Nhs.WebUI/Views/Account/CreateAccountRegistrationInformation.cshtml @@ -24,7 +24,7 @@

General user account

- General users can access resources made available by contributors to all users. + General users have access to the resources that contributors have made available for public access.

Required information

@@ -40,12 +40,12 @@

Additional information needed

    -
  • Qualifying work email address (e.g. NHS email address)
  • +
  • Qualifying work email address (for example, NHS email address)
  • Current role
  • Professional registration number (if applicable)
  • -
  • Grade / Band
  • +
  • Grade or Band
  • Start date
  • -
  • Primary specialty (if applicable)
  • +
  • Primary speciality (if applicable)
  • Country
  • Place of work details
  • diff --git a/LearningHub.Nhs.WebUI/Views/Account/ForgotUserPassword.cshtml b/LearningHub.Nhs.WebUI/Views/Account/ForgotUserPassword.cshtml index 91160751e..27e886f88 100644 --- a/LearningHub.Nhs.WebUI/Views/Account/ForgotUserPassword.cshtml +++ b/LearningHub.Nhs.WebUI/Views/Account/ForgotUserPassword.cshtml @@ -5,6 +5,7 @@ @model ForgotPasswordViewModel; @{ + ViewData["DisableValidation"] = true; ViewData["Title"] = "ForgotPassword"; var errorHasOccurred = !ViewData.ModelState.IsValid; } diff --git a/LearningHub.Nhs.WebUI/Views/Account/TooManyRequests.cshtml b/LearningHub.Nhs.WebUI/Views/Account/TooManyRequests.cshtml new file mode 100644 index 000000000..fbc8a468d --- /dev/null +++ b/LearningHub.Nhs.WebUI/Views/Account/TooManyRequests.cshtml @@ -0,0 +1,16 @@ +@{ + ViewData["Title"] = "Reset limit reached"; + // Get the value from ViewBag + var period = ViewBag.Period.ToString(); +} +
    +
    +
    +
    +

    @ViewData["Title"]

    +

    You've requested the maximum number of password resets (@ViewBag.Limit). Please wait @period minutes before trying again or contact the support team.

    +

    @DateTimeOffset.Now.ToString("d MMMM yyyy HH:mm:ss")

    +
    +
    +
    +
    \ No newline at end of file diff --git a/LearningHub.Nhs.WebUI/Views/Bookmark/Move.cshtml b/LearningHub.Nhs.WebUI/Views/Bookmark/Move.cshtml index 462133de2..10677a0b6 100644 --- a/LearningHub.Nhs.WebUI/Views/Bookmark/Move.cshtml +++ b/LearningHub.Nhs.WebUI/Views/Bookmark/Move.cshtml @@ -52,7 +52,7 @@ }
    - +
    @@ -72,6 +72,7 @@ required="@(Model.SelectedFolderId == 0)" />
    +
    @if (Model.Bookmark.ParentId.HasValue || (Model.Folders != null && Model.Folders.Any())) { diff --git a/LearningHub.Nhs.WebUI/Views/Bookmark/_BookmarkAction.cshtml b/LearningHub.Nhs.WebUI/Views/Bookmark/_BookmarkAction.cshtml index 5349fbb30..e3b338884 100644 --- a/LearningHub.Nhs.WebUI/Views/Bookmark/_BookmarkAction.cshtml +++ b/LearningHub.Nhs.WebUI/Views/Bookmark/_BookmarkAction.cshtml @@ -6,7 +6,7 @@ @if (Model.IsFirst == false) { - } @@ -15,25 +15,25 @@ @if (Model.IsLast == false) { - } - + @if (Model.IsFolder == false) { - + } - +
\ No newline at end of file diff --git a/LearningHub.Nhs.WebUI/Views/Bookmark/_BookmarkItem.cshtml b/LearningHub.Nhs.WebUI/Views/Bookmark/_BookmarkItem.cshtml index d3698d691..19d19e07a 100644 --- a/LearningHub.Nhs.WebUI/Views/Bookmark/_BookmarkItem.cshtml +++ b/LearningHub.Nhs.WebUI/Views/Bookmark/_BookmarkItem.cshtml @@ -24,11 +24,11 @@ @Model.Bookmark.Title - + Type @GetResourceTypeName(Model.Bookmark.BookmarkTypeId, Model.Bookmark.ResourceTypeId) - + Organise diff --git a/LearningHub.Nhs.WebUI/Views/Catalogue/AccessRequested.cshtml b/LearningHub.Nhs.WebUI/Views/Catalogue/AccessRequested.cshtml index 3fa90a2ae..10f2af3dd 100644 --- a/LearningHub.Nhs.WebUI/Views/Catalogue/AccessRequested.cshtml +++ b/LearningHub.Nhs.WebUI/Views/Catalogue/AccessRequested.cshtml @@ -13,7 +13,7 @@

Access requested

-

Your request to access this cataloque has seen sent to the cataloque administrator.

+

Your request to access this catalogue has seen sent to the catalogue administrator.

What happens next?

The catalogue administrator will review your request and you will be notified of their decision by email.

diff --git a/LearningHub.Nhs.WebUI/Views/Catalogue/RequestAccess.cshtml b/LearningHub.Nhs.WebUI/Views/Catalogue/RequestAccess.cshtml index dc7a202e3..b67917e35 100644 --- a/LearningHub.Nhs.WebUI/Views/Catalogue/RequestAccess.cshtml +++ b/LearningHub.Nhs.WebUI/Views/Catalogue/RequestAccess.cshtml @@ -2,68 +2,82 @@ @using LearningHub.Nhs.WebUI.Models.Catalogue @using LearningHub.Nhs.Models.Enums @{ - ViewData["Title"] = "Request Access"; + ViewData["Title"] = "Request Access"; } -@section styles{ - +@section styles { + }
-
- -

Catalogue access request

+
+ +

Catalogue access request

-
- @using (Html.BeginForm("RequestAccessPost", "Catalogue", FormMethod.Post)) - { - @if (Model.CatalogueAccessRequest != null && Model.CatalogueAccessRequest.Status == CatalogueAccessRequestStatus.Rejected) - { -

Your last request to access this catalogue was denied on @Model.CatalogueAccessRequest.DateRejected.Value.ToString("dd MMM yyyy") for the following reason:

-

@Model.CatalogueAccessRequest.ResponseMessage

- } +
+ @using (Html.BeginForm("RequestAccessPost", "Catalogue", FormMethod.Post)) + { + @if (Model.CatalogueAccessRequest != null && Model.CatalogueAccessRequest.Status == CatalogueAccessRequestStatus.Rejected) + { +

Your last request to access this catalogue was denied on @Model.CatalogueAccessRequest.DateRejected.Value.ToString("dd MMM yyyy") for the following reason:

+

@Model.CatalogueAccessRequest.ResponseMessage

+ } + else if (Model.CatalogueAccessRequest != null && Model.CatalogueAccessRequest.Status == CatalogueAccessRequestStatus.Pending) + { +

Your access request which was sent on @Model.CatalogueAccessRequest.DateRequested.ToString("dd MMM yyyy") is pending a decision.

+

If you need further information contact support

+ } -

You are requesting access to the @Model.CatalogueName catalogue. Enter the information below which will be sent to the catalogue administrator. You will be notified of their decision by email.

+

You are requesting access to the @Model.CatalogueName catalogue. Enter the information below which will be sent to the catalogue administrator. You will be notified of their decision by email.

- - - - - - - - - -
Name:@Model.CurrentUser.FirstName @Model.CurrentUser.LastName
Email:@Model.CurrentUser.EmailAddress
+ + + + + + + + + +
Name:@Model.CurrentUser.FirstName @Model.CurrentUser.LastName
Email:@Model.CurrentUser.EmailAddress
-
-
- Enter your role, place of work, professional body number (if applicable) and information on why you are requesting access to this catalogue. -
+
+
+ Enter your role, place of work, professional body number (if applicable) and information on why you are requesting access to this catalogue. +
- -
-
+ +
+
- - - - - - - - @if (Model.CatalogueAccessRequest != null) - { - - - - } + + + + + + + + @if (Model.CatalogueAccessRequest != null) + { + + + + } -
- Cancel - -
- } +
+ + @if (Model.CatalogueAccessRequest != null && Model.CatalogueAccessRequest.Status == CatalogueAccessRequestStatus.Pending) + { + Cancel + + } + else + { + Cancel + + }
+ }
+
\ No newline at end of file diff --git a/LearningHub.Nhs.WebUI/Views/Home/ChangePasswordAcknowledgement.cshtml b/LearningHub.Nhs.WebUI/Views/Home/ChangePasswordAcknowledgement.cshtml new file mode 100644 index 000000000..28bf1b66a --- /dev/null +++ b/LearningHub.Nhs.WebUI/Views/Home/ChangePasswordAcknowledgement.cshtml @@ -0,0 +1,24 @@ +@{ + ViewData["Title"] = "Change password Acknowledgement"; +} + +
+
+
+
+
+

Your password has been changed successfully.

+ +

+ You have been logged out. Please log in using your username and new password to explore the Learning Hub. +

+
+
+ +
+
+
+
+
+
+
\ No newline at end of file diff --git a/LearningHub.Nhs.WebUI/Views/Home/Contactus.cshtml b/LearningHub.Nhs.WebUI/Views/Home/Contactus.cshtml index 83f69b5eb..a91205c49 100644 --- a/LearningHub.Nhs.WebUI/Views/Home/Contactus.cshtml +++ b/LearningHub.Nhs.WebUI/Views/Home/Contactus.cshtml @@ -31,9 +31,9 @@
  • -

    +

    Support site -

    +

    Visit our support site for help on using the Learning Hub.

    @@ -44,9 +44,9 @@
  • -

    +

    Support team -

    +

    Complete our form to ask the support team for help.

    @@ -58,9 +58,9 @@
  • -

    +

    Share feedback -

    +

    Share your feedback to help us improve the platform.

    @@ -71,9 +71,9 @@
  • -

    +

    Contribute to user research -

    +

    Influence the future development of the Learning Hub.

    diff --git a/LearningHub.Nhs.WebUI/Views/Home/Dashboard.cshtml b/LearningHub.Nhs.WebUI/Views/Home/Dashboard.cshtml index 532e2c363..4a7b55c08 100644 --- a/LearningHub.Nhs.WebUI/Views/Home/Dashboard.cshtml +++ b/LearningHub.Nhs.WebUI/Views/Home/Dashboard.cshtml @@ -3,86 +3,86 @@ @using LearningHub.Nhs.Models.Resource.ResourceDisplay; @using LearningHub.Nhs.Models.Enums; @{ - ViewData["Title"] = "Learning Hub - Home"; + ViewData["Title"] = "Learning Hub - Home"; - var isReadOnly = User.IsInRole("ReadOnly") || User.IsInRole("BasicUser"); + var isReadOnly = User.IsInRole("ReadOnly") || User.IsInRole("BasicUser"); } -@section styles{ - +@section styles { + }
    -
    -
    - - - - - - - -
    +
    +
    + + + + + + + +
    -
    -
    - -
    -
    -
    +
    +
    + +
    +
    +
    -
    -
    -
    - -
    -
    -
    +
    +
    +
    + +
    +
    +
    - @if (!isReadOnly) - { -
    -
    -
    -

    Contribute a resource

    -

    Share your knowledge, insights and expertise by contributing elearning, video, audio, images, web links, documents and articles on a range of subjects.

    - -
    -
    -
    -
    - } + @if (!isReadOnly) + { +
    +
    +
    +

    Contribute a resource

    +

    Share your knowledge, insights and expertise by contributing elearning, video, audio, images, web links, documents and articles on a range of subjects.

    + +
    +
    +
    +
    + } -
    -
    -
    - -
    +
    +
    +
    + +
    -
    - -
    -
    -
    +
    + +
    +
    +
    @section Scripts { - + return false; + } + } \ No newline at end of file diff --git a/LearningHub.Nhs.WebUI/Views/Home/_CmsVideo.cshtml b/LearningHub.Nhs.WebUI/Views/Home/_CmsVideo.cshtml index 1b9433b83..bfa65821f 100644 --- a/LearningHub.Nhs.WebUI/Views/Home/_CmsVideo.cshtml +++ b/LearningHub.Nhs.WebUI/Views/Home/_CmsVideo.cshtml @@ -1,11 +1,11 @@ @using LearningHub.Nhs.Models.Content @model PageSectionDetailViewModel @{ - string mkPlayerLicence = (string)ViewData["mkPlayerLicenceKey"]; - var scheme = Context?.Request?.Scheme ?? "undefined"; - var host = Context?.Request?.Host; - var path = Context?.Request?.Path ?? "undefined"; - var requestURL = $"{scheme}://{host}{path}"; + string mkPlayerLicence = (string)ViewData["mkPlayerLicenceKey"]; + var scheme = Context?.Request?.Scheme ?? "undefined"; + var host = Context?.Request?.Host; + var path = Context?.Request?.Path ?? "undefined"; + var requestURL = $"{scheme}://{host}{path}"; } @@ -55,130 +55,161 @@ \ No newline at end of file diff --git a/LearningHub.Nhs.WebUI/Views/Home/_CourseEnrolled.cshtml b/LearningHub.Nhs.WebUI/Views/Home/_CourseEnrolled.cshtml new file mode 100644 index 000000000..058c39165 --- /dev/null +++ b/LearningHub.Nhs.WebUI/Views/Home/_CourseEnrolled.cshtml @@ -0,0 +1,114 @@ +@using LearningHub.Nhs.Models.Dashboard +@using LearningHub.Nhs.WebUI.Extensions +@using LearningHub.Nhs.WebUI.Helpers +@model MoodleCourseResponseViewModel +@inject Microsoft.Extensions.Configuration.IConfiguration Configuration; + +@{ + bool providerExists = false; + string cardStyle = "card-provider-details--blank"; + + string GetMoodleCourseUrl(int courseId) + { + var apiBaseUrl = Configuration["MoodleAPIConfig:BaseUrl"]; + string path = $"course/view.php"; + string returnUrl = $@"{apiBaseUrl}/{path}?id={courseId}"; + + return returnUrl; + } +} +
    +
    + @if (!string.IsNullOrWhiteSpace(Model.CourseImage)) + { + if (Model.CourseImage.EndsWith(".svg", StringComparison.OrdinalIgnoreCase)) + { + + Fallback image + + } + else + { + Course image + } + } + else + { +
    + } +
    + @if (providerExists) + { +
    +
    + } + else + { +
    + } + +
    +

    + @Model.DisplayName +

    +
    + +
    + @UtilityHelper.StripHtmlFromString(Model.Summary) +
    + +
    + @if (@Model?.Progress != null) + { +
    Progress: @Model.Progress%
    + } +
    + +
    + Completed: @Model.CourseCompletionViewModel?.CompletionStatus?.Completed + + @if (Model?.CourseCompletionViewModel?.CompletionStatus?.Completions != null) + { + @foreach (var item in Model.CourseCompletionViewModel.CompletionStatus.Completions) + { + if (item.Type == 2) + { +
    Target Completion Date: @item.Status
    + } + } + } +
    + +
    +
    +
    + @* @if (Model.RestrictedAccess) + { +
    @((Model.HasAccess || this.User.IsInRole("Administrator")) ? "Access granted" : "Access restricted")
    + } *@ +
    + +
    +
    + @* @if (providerExists) + { + var provider = @Model.Providers.First(); + @provider.Name catalogue badge + } + else if (!string.IsNullOrEmpty(Model.BadgeUrl)) + { + Provider's catalogue badge + } *@ +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/LearningHub.Nhs.WebUI/Views/Home/_MyAccessedLearningTray.cshtml b/LearningHub.Nhs.WebUI/Views/Home/_MyAccessedLearningTray.cshtml index adefabd93..8b8d25f8a 100644 --- a/LearningHub.Nhs.WebUI/Views/Home/_MyAccessedLearningTray.cshtml +++ b/LearningHub.Nhs.WebUI/Views/Home/_MyAccessedLearningTray.cshtml @@ -2,133 +2,157 @@ @using LearningHub.Nhs.Models.Dashboard; @using LearningHub.Nhs.WebUI.Models.DashBoard; @{ - string GetActiveTabName() - { - switch (Model.MyLearnings.Type) - { - case "in-progress": - return "In Progress"; - case "my-recent-completed": - return "Recently Completed"; - case "my-certificates": - return "Certificates"; - case "my-catalogues": - return "Catalogues"; - default: - return "In Progress"; - } - } + string GetActiveTabName() + { + switch (Model.MyLearnings.Type) + { + case "in-progress": + return "In Progress"; + case "my-recent-completed": + return "Recently Completed"; + case "my-certificates": + return "Certificates"; + case "my-catalogues": + return "Catalogues"; + case "my-enrolled-courses": + return "Enrolled Courses"; + default: + return "In Progress"; + } + } - var pagingViewModel = new DashBoardPagingViewModel - { - DashboardTrayType = "my-learning", - MyLearningDashboard = Model.MyLearnings.Type, - ResourceDashboard = Model.Resources.Type, - CatalogueDashboard = Model.Catalogues.Type, - CurrentPage = Model.MyLearnings.CurrentPage, - TotalPages = Model.MyLearnings.TotalPages, - TotalCount = Model.MyLearnings.TotalCount - }; + var pagingViewModel = new DashBoardPagingViewModel + { + DashboardTrayType = "my-learning", + MyLearningDashboard = Model.MyLearnings.Type, + ResourceDashboard = Model.Resources.Type, + CatalogueDashboard = Model.Catalogues.Type, + CurrentPage = Model.MyLearnings.CurrentPage, + TotalPages = Model.MyLearnings.TotalPages, + TotalCount = Model.MyLearnings.TotalCount + }; + + var enableMoodle = this.ViewBag.EnableMoodle; + var validMoodleUser = this.ViewBag.ValidMoodleUser; }
    -

    My accessed learning

    +

    My accessed learning

    + + - +
    +
    + @if (Model.MyLearnings.Resources?.Count() > 0 || Model.MyLearnings.Catalogues?.Count() > 0) + { +
      + @if (Model.MyLearnings.Type != "my-catalogues") + { + @foreach (var resource in Model.MyLearnings.Resources) + { -
      -
      - @if (Model.MyLearnings.Resources?.Count() > 0 || Model.MyLearnings.Catalogues?.Count() > 0) - { -
        - @if (Model.MyLearnings.Type != "my-catalogues") - { - @foreach (var resource in Model.MyLearnings.Resources) - { + var resourceViewModel = new Tuple("my-learning", resource); +
      • + +
      • + } + } + @if (Model.MyLearnings.Type == "my-catalogues") + { + @foreach (var catalogue in Model.MyLearnings.Catalogues) + { +
      • + +
      • + } + } +
      + } + else if (enableMoodle && validMoodleUser && Model.EnrolledCourses?.Count() > 0) + { +
        - var resourceViewModel = new Tuple("my-learning", resource); -
      • - -
      • - } - } - @if (Model.MyLearnings.Type == "my-catalogues") - { - @foreach (var catalogue in Model.MyLearnings.Catalogues) - { -
      • - -
      • - } - } -
      - } - else - { -
      - @if (Model.MyLearnings.Type == "my-in-progress" && Model.MyLearnings.TotalCount <= 0) - { -

      You have not accessed any learning yet.

      -

      Accessed resources will be displayed here. Browse from the list of resources below or search for a specific term using the search bar at the top of the screen.

      - } - else if (Model.MyLearnings.Type == "my-recent-completed" && Model.MyLearnings.TotalCount <= 0) - { -

      You have not recently completed any learning.

      -

      When available your completed learning will be displayed here.

      - } - else if (Model.MyLearnings.Type == "my-certificates" && Model.MyLearnings.TotalCount <= 0) - { -

      You have no certificates.

      -

      Certificates when available will be displayed here.

      - } - else if (Model.MyLearnings.Type == "my-catalogues" && Model.MyLearnings.TotalCount <= 0) - { -

      You have no recently viewed catalogues.

      -

      When available your catalogues will be displayed here. Browse from the list of catalogues below or search for a specific term using the search bar at the top of the screen.

      - } -
      - } -
      -
      -
      -
      - @if (Model.MyLearnings.Type != "my-catalogues") - { - View in My Learning - } -
      -
      -
      - -
      -
      + @foreach (var enrolledCourses in Model.EnrolledCourses) + { +
    • + +
    • + } +
    + } + else + { +
    + @if (Model.MyLearnings.Type == "my-in-progress" && Model.MyLearnings.TotalCount <= 0) + { +

    You have not accessed any learning yet.

    +

    Accessed resources will be displayed here. Browse from the list of resources below or search for a specific term using the search bar at the top of the screen.

    + } + else if (Model.MyLearnings.Type == "my-recent-completed" && Model.MyLearnings.TotalCount <= 0) + { +

    You have not recently completed any learning.

    +

    When available your completed learning will be displayed here.

    + } + else if (Model.MyLearnings.Type == "my-certificates" && Model.MyLearnings.TotalCount <= 0) + { +

    You have no certificates.

    +

    Certificates when available will be displayed here.

    + } + else if (Model.MyLearnings.Type == "my-catalogues" && Model.MyLearnings.TotalCount <= 0) + { +

    You have no recently viewed catalogues.

    +

    When available your catalogues will be displayed here. Browse from the list of catalogues below or search for a specific term using the search bar at the top of the screen.

    + } +
    + } +
    +
    +
    +
    + @if (Model.MyLearnings.Type != "my-catalogues") + { + View in My Learning + } +
    +
    +
    + +
    +
    \ No newline at end of file diff --git a/LearningHub.Nhs.WebUI/Views/MyAccount/ChangeCountry.cshtml b/LearningHub.Nhs.WebUI/Views/MyAccount/ChangeCountry.cshtml index 2ad51ede1..0f8436e71 100644 --- a/LearningHub.Nhs.WebUI/Views/MyAccount/ChangeCountry.cshtml +++ b/LearningHub.Nhs.WebUI/Views/MyAccount/ChangeCountry.cshtml @@ -1,6 +1,7 @@ @using LearningHub.Nhs.WebUI.Models.UserProfile @model Tuple @{ + ViewData["DisableValidation"] = true; ViewData["Title"] = "My Account - Change country"; var errorHasOccurred = !ViewData.ModelState.IsValid; } diff --git a/LearningHub.Nhs.WebUI/Views/MyAccount/ChangeCurrentRole.cshtml b/LearningHub.Nhs.WebUI/Views/MyAccount/ChangeCurrentRole.cshtml index 40938608d..5cde300d8 100644 --- a/LearningHub.Nhs.WebUI/Views/MyAccount/ChangeCurrentRole.cshtml +++ b/LearningHub.Nhs.WebUI/Views/MyAccount/ChangeCurrentRole.cshtml @@ -1,97 +1,97 @@ @model LearningHub.Nhs.WebUI.Models.UserProfile.UserJobRoleUpdateViewModel @{ - ViewData["Title"] = "Update Current Role"; - var errorHasOccurred = !ViewData.ModelState.IsValid; + ViewData["DisableValidation"] = true; + ViewData["Title"] = "Update Current Role"; + var errorHasOccurred = !ViewData.ModelState.IsValid; }
  • +
    +
    +

    + Acceptable use policy +

    +

    + The acceptable use policy describes the conditions of use of the Learning Hub +

    +
    +
    +
  • +
  • +
    +
    +

    + AI-generated images statement +

    +

    + Use of generative AI for images, videos, and alt text in educational content +

    +
    +
    +
  • + +
    \ No newline at end of file diff --git a/LearningHub.Nhs.WebUI/Views/Shared/Components/NavigationItems/Searchbar.cshtml b/LearningHub.Nhs.WebUI/Views/Shared/Components/NavigationItems/Searchbar.cshtml index 2a2bbb281..8f7f8a738 100644 --- a/LearningHub.Nhs.WebUI/Views/Shared/Components/NavigationItems/Searchbar.cshtml +++ b/LearningHub.Nhs.WebUI/Views/Shared/Components/NavigationItems/Searchbar.cshtml @@ -9,7 +9,7 @@
    @if (Model.ShowSearch) { - + }

    - Forgotten username or password + Forgotten username or password

    Create new account diff --git a/LearningHub.Nhs.WebUI/Views/Shared/_NavPartial.cshtml b/LearningHub.Nhs.WebUI/Views/Shared/_NavPartial.cshtml index 54146c0cd..594c62f7c 100644 --- a/LearningHub.Nhs.WebUI/Views/Shared/_NavPartial.cshtml +++ b/LearningHub.Nhs.WebUI/Views/Shared/_NavPartial.cshtml @@ -49,7 +49,7 @@

    @if (User.Identity.IsAuthenticated) { - +
    public string Catalogue { get; set; } = null!; + + /// + /// Gets or sets the AutoSuggestion collection id. + /// + public string AutoSuggestion { get; set; } = null!; } } diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Models/Configuration/FindwiseConfig.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Models/Configuration/FindwiseConfig.cs index dd84799c5..009509127 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Models/Configuration/FindwiseConfig.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Models/Configuration/FindwiseConfig.cs @@ -5,6 +5,11 @@ namespace LearningHub.Nhs.OpenApi.Models.Configuration /// public class FindwiseConfig { + /// + /// Gets or sets the base url for the Findwise Index endpoint. + /// + public string IndexUrl { get; set; } = null!; + /// /// Gets or sets the base url for the Findwise search service. /// @@ -25,6 +30,16 @@ public class FindwiseConfig /// public int DefaultItemLimitForSearch { get; set; } + /// + /// Gets or sets the description limit. + /// + public int DescriptionLengthLimit { get; set; } + + /// + /// Gets or sets the description length. + /// + public int MaximumDescriptionLength { get; set; } + /// /// Gets or sets the collection ids. /// @@ -34,5 +49,10 @@ public class FindwiseConfig /// Gets or sets the special search characters. /// public string SpecialSearchCharacters { get; set; } = null!; + + /// + /// Gets or sets the index method. + /// + public string IndexMethod { get; set; } = null!; } } diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Models/Configuration/LearningHubConfig.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Models/Configuration/LearningHubConfig.cs index 9e2086ee3..6ccd4f37c 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Models/Configuration/LearningHubConfig.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Models/Configuration/LearningHubConfig.cs @@ -14,5 +14,46 @@ public class LearningHubConfig /// Gets or sets . /// public string LaunchResourceEndpoint { get; set; } = null!; + + /// + /// Gets or sets . + /// + public string ContentServerUrl { get; set; } = null!; + + /// + /// Gets or sets . + /// + public int EmailConfirmationTokenExpiryMinutes { get; set; } = 0; + + /// + /// Gets or sets . + /// + public int LearningHubTenantId { get; set; } = 0; + + /// + /// Gets or sets . + /// + public string SupportPages { get; set; } = null!; + + /// + /// Gets or sets . + /// + public string SupportForm { get; set; } = null!; + + /// + /// Gets or sets a value indicating whether . + /// + public bool UseRedisCache { get; set; } = false; + + /// + /// Gets or sets . + /// + public string HierarchyEditPublishQueueName { get; set; } = null!; + + /// + /// Gets or sets . + /// + public string ResourcePublishQueueRouteName { get; set; } = null!; + } } diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Models/LearningHub.Nhs.OpenApi.Models.csproj b/OpenAPI/LearningHub.Nhs.OpenApi.Models/LearningHub.Nhs.OpenApi.Models.csproj index c1cb2bd0e..baf3dee90 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Models/LearningHub.Nhs.OpenApi.Models.csproj +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Models/LearningHub.Nhs.OpenApi.Models.csproj @@ -1,23 +1,18 @@ - - - net8.0 - true - enable - x64 - - - - - - - - - - - - - - - - + + net8.0 + true + enable + x64 + + + + + + + + + + + + \ No newline at end of file diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/LearningHub.Nhs.OpenApi.Repositories.Interface.csproj b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/LearningHub.Nhs.OpenApi.Repositories.Interface.csproj index 3444f18a2..1617cfa0d 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/LearningHub.Nhs.OpenApi.Repositories.Interface.csproj +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/LearningHub.Nhs.OpenApi.Repositories.Interface.csproj @@ -1,27 +1,21 @@ - - - net8.0 - LearningHub.Nhs.OpenApi.Repositories.Interface - enable - true - x64 - - - - - - - - - - - - - - - - - - - + + net8.0 + LearningHub.Nhs.OpenApi.Repositories.Interface + enable + true + x64 + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Activity/IAssessmentResourceActivityInteractionAnswerRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Activity/IAssessmentResourceActivityInteractionAnswerRepository.cs new file mode 100644 index 000000000..30a798b72 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Activity/IAssessmentResourceActivityInteractionAnswerRepository.cs @@ -0,0 +1,18 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Activity +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Activity; + + /// + /// The AssessmentResourceActivityInteractionAnswer interface. + /// + public interface IAssessmentResourceActivityInteractionAnswerRepository : IGenericRepository + { + /// + /// Get Assessment Resource Activity Interaction Answer By Id. + /// + /// The id. + /// AssessmentResourceActivityInteractionAnswer. + Task GetByIdAsync(int id); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Activity/IAssessmentResourceActivityInteractionRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Activity/IAssessmentResourceActivityInteractionRepository.cs new file mode 100644 index 000000000..5d9f8c175 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Activity/IAssessmentResourceActivityInteractionRepository.cs @@ -0,0 +1,43 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Activity +{ + using System.Collections.Generic; + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Activity; + + /// + /// The AssessmentResourceActivityInteraction interface. + /// + public interface IAssessmentResourceActivityInteractionRepository : IGenericRepository + { + /// + /// Get Assessment Resource Activity Interaction By Id. + /// + /// The id. + /// AssessmentResourceActivityInteraction. + Task GetByIdAsync(int id); + + /// + /// Get the assessment resource activity interaction for the given user, activity, and question block. + /// + /// The user id. + /// The assessment resource activity id. + /// The question block id. + /// AssessmentResourceActivityInteraction. + Task GetInteractionForQuestion(int userId, int assessmentResourceActivityId, int questionBlockId); + + /// + /// Creates an assessment activity interaction. + /// + /// The user id. + /// The interaction. + /// The task. + Task CreateInteraction(int userId, AssessmentResourceActivityInteraction interaction); + + /// + /// Gets all the interactions for a given assessment resource activity. + /// + /// The assessment resource activity id. + /// The list of interactions. + Task> GetInteractionsForAssessmentResourceActivity(int assessmentResourceActivityId); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Activity/IAssessmentResourceActivityMatchQuestionRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Activity/IAssessmentResourceActivityMatchQuestionRepository.cs new file mode 100644 index 000000000..e08ecf2f9 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Activity/IAssessmentResourceActivityMatchQuestionRepository.cs @@ -0,0 +1,18 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Activity +{ + using System.Collections.Generic; + using LearningHub.Nhs.Models.Entities.Activity; + + /// + /// The IAssessmentResourceActivityMatchQuestionRepository interface. + /// + public interface IAssessmentResourceActivityMatchQuestionRepository : IGenericRepository + { + /// + /// Get Assessment Resource Activity by Assessment Resource Activity Id. + /// + /// The assessment resource activity id. + /// The Assessment Resource Activity. + IEnumerable GetByAssessmentResourceActivityIdAsync(int id); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Activity/IAssessmentResourceActivityRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Activity/IAssessmentResourceActivityRepository.cs new file mode 100644 index 000000000..ae7e74153 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Activity/IAssessmentResourceActivityRepository.cs @@ -0,0 +1,26 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Activity +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Activity; + + /// + /// The AssessmentResourceActivity interface. + /// + public interface IAssessmentResourceActivityRepository : IGenericRepository + { + /// + /// Get Assessment Resource Activity By Id. + /// + /// The id. + /// AssessmentResourceActivity. + Task GetByIdAsync(int id); + + /// + /// Gets the latest assessment resource activity for the given resource version id and user id. + /// + /// The resource version id. + /// The user id. + /// The assessment resource activity task. + Task GetLatestAssessmentResourceActivity(int resourceVersionId, int userId); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Activity/IMediaResourceActivityInteractionRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Activity/IMediaResourceActivityInteractionRepository.cs new file mode 100644 index 000000000..29c4faa12 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Activity/IMediaResourceActivityInteractionRepository.cs @@ -0,0 +1,27 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Activity +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Activity; + + /// + /// The MediaResourceActivityInteraction interface. + /// + public interface IMediaResourceActivityInteractionRepository : IGenericRepository + { + /// + /// Get Media Resource Activity Interaction By Id. + /// + /// The id. + /// MediaResourceActivityInteraction. + Task GetByIdAsync(int id); + + /// + /// Performs the analysis of media resource activity to populate the played segment data. + /// + /// The user id. + /// The resource version id. + /// The mediaResourceActivityId. + /// The . + Task CalculatePlayedMediaSegments(int userId, int resourceVersionId, int mediaResourceActivityId); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Activity/IMediaResourceActivityRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Activity/IMediaResourceActivityRepository.cs new file mode 100644 index 000000000..2dd227402 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Activity/IMediaResourceActivityRepository.cs @@ -0,0 +1,18 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Activity +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Activity; + + /// + /// The MediaResourceActivity interface. + /// + public interface IMediaResourceActivityRepository : IGenericRepository + { + /// + /// Get Media Resource Activity By Id. + /// + /// The id. + /// MediaResourceActivity. + Task GetByIdAsync(int id); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Activity/INodeActivityRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Activity/INodeActivityRepository.cs new file mode 100644 index 000000000..3c16ea9af --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Activity/INodeActivityRepository.cs @@ -0,0 +1,14 @@ +// +// Copyright (c) NHS England. +// +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Activity +{ + using LearningHub.Nhs.Models.Entities.Activity; + + /// + /// INodeActivityRepository. + /// + public interface INodeActivityRepository : IGenericRepository + { + } +} \ No newline at end of file diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Activity/IResourceActivityRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Activity/IResourceActivityRepository.cs new file mode 100644 index 000000000..0c778605c --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Activity/IResourceActivityRepository.cs @@ -0,0 +1,108 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Activity +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Activity; + using LearningHub.Nhs.Models.Enums; + using LearningHub.Nhs.Models.MyLearning; + + /// + /// The ResourceActivity interface. + /// + public interface IResourceActivityRepository : IGenericRepository + { + /// + /// Get Resource Activity By Id. + /// + /// The id. + /// ResourceActivity. + Task GetByIdAsync(int id); + + /// + /// Create activity record against a ResourceVersion. + /// + /// The user id. + /// The resource version id. + /// The node path id. + /// The launch resource activity id. + /// The activity Status Enum. + /// The activity Start. + /// The activity End. + /// Activity Id. + int CreateActivity( + int userId, + int resourceVersionId, + int nodePathId, + int? launchResourceActivityId, + ActivityStatusEnum activityStatusEnum, + DateTimeOffset? activityStart, + DateTimeOffset? activityEnd); + + /// + /// Get Resource Activity By user id. + /// + /// The user id. + /// ResourceActivity. + IQueryable GetByUserId(int userId); + + /// + /// Gets a list of incomplete media activities. Those that for any reason, the end of the user's activity was not recorded normally. For example - browser crash, power loss, connection loss. + /// + /// The user id. + /// The . + Task> GetIncompleteMediaActivities(int userId); + + /// + /// Gets a list of all the user's activities for a given resource. + /// + /// The user id.> + /// The resource id. + /// The . + Task> GetAllTheActivitiesFor(int userId, int resourceId); + + /// + /// Check if scorm activity has been completed. + /// + /// The user id. + /// The scormActivityId id. + /// The . + Task IsScormActivityFinished(int userId, int scormActivityId); + + /// + /// Get Resource Activity By user id. + /// + /// The user id. + /// requestModel. + /// detailedMediaActivityRecordingStartDate. + /// ResourceActivity. + Task> GetByUserIdFromSP(int userId, Nhs.Models.MyLearning.MyLearningRequestModel requestModel, DateTimeOffset detailedMediaActivityRecordingStartDate); + + /// + /// Check if scorm activity has been completed. + /// + /// The user id. + /// requestModel. + /// detailedMediaActivityRecordingStartDate. + /// The . + int GetTotalCount(int userId, MyLearningRequestModel requestModel, DateTimeOffset detailedMediaActivityRecordingStartDate); + + /// + /// Gets a list of all the user's activities for a given resource. + /// + /// The user id.> + /// The resource id. + /// The . + Task> GetAllTheActivitiesFromSP(int userId, int resourceId); + + /// + /// Get the assessment activity completion details. + /// + /// The userId. + /// The resourceVersionId. + /// The activityId. + /// The . + Task GetAssessmentActivityCompletionPercentage(int userId, int resourceVersionId, int activityId); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Activity/IScormActivityRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Activity/IScormActivityRepository.cs new file mode 100644 index 000000000..19757c6d8 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Activity/IScormActivityRepository.cs @@ -0,0 +1,66 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Activity +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Dto; + using LearningHub.Nhs.Models.Entities.Activity; + + /// + /// The Scorm Activity interface. + /// + public interface IScormActivityRepository : IGenericRepository + { + /// + /// Get Scorm Activity By Id. + /// + /// The id. + /// Scorm Activity. + Task GetByIdAsync(int id); + + /// + /// The create activity. + /// + /// The user id. + /// The resource version id. + /// Scorm Activity Id. + int Create(int userId, int resourceVersionId); + + /// + /// Complete scorm activity. + /// Returns the resource activity id of the completion event. + /// + /// The user id. + /// The scorm activity id. + /// Resource Activity Id. + int Complete(int userId, int scormActivityId); + + /// + /// Resolve scorm activity. + /// + /// The scorm activity id. + void Resolve(int scormActivityId); + + /// + /// Gets the previously launched incomplete scrom activity id. + /// + /// userId. + /// resourceReferenceId. + /// ScormActivitySummaryDto. + ScormActivitySummaryDto GetScormActivitySummary(int userId, int resourceReferenceId); + + /// + /// Clones the incomplete scrom activity session with the newly created session. + /// + /// incompleteScromActivityId. + /// scromActivityId. + /// ScormActivity. + ScormActivity Clone(int incompleteScromActivityId, int scromActivityId); + + /// + /// Check user scorm activity data suspend data need to be cleared. + /// + /// last scorm activity id. + /// resource version id. + /// boolean. + Task CheckUserScormActivitySuspendDataToBeCleared(int lastScormActivityId, int resourceVersionId); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Hierarchy/ICatalogueAccessRequestRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Hierarchy/ICatalogueAccessRequestRepository.cs new file mode 100644 index 000000000..05fa86695 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Hierarchy/ICatalogueAccessRequestRepository.cs @@ -0,0 +1,46 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Hierarchy +{ + using System.Linq; + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Hierarchy; + + /// + /// The ICatalogueAccessRequestRepository interface. + /// + public interface ICatalogueAccessRequestRepository : IGenericRepository + { + /// + /// The GetByUserIdAndCatalogueId. + /// + /// The catalogueId. + /// The userId. + /// The catalogueAccessRequest. + CatalogueAccessRequest GetByUserIdAndCatalogueId(int catalogueNodeId, int userId); + + /// + /// The GetAllByUserIdAndCatalogueId. + /// + /// The catalogueId. + /// The userId. + /// The catalogueAccessRequest. + IQueryable GetAllByUserIdAndCatalogueId(int catalogueNodeId, int userId); + + /// + /// The CreateCatalogueAccessRequestAsync. + /// + /// The currentUserId. + /// The reference. + /// The message. + /// The roleId. + /// The catalogueManageAccessUrl. + /// The accessType. + /// The task. + Task CreateCatalogueAccessRequestAsync( + int currentUserId, + string reference, + string message, + int roleId, + string catalogueManageAccessUrl, + string accessType); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Hierarchy/ICatalogueNodeVersionRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Hierarchy/ICatalogueNodeVersionRepository.cs new file mode 100644 index 000000000..e1137c520 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Hierarchy/ICatalogueNodeVersionRepository.cs @@ -0,0 +1,141 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Hierarchy +{ + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Catalogue; + using LearningHub.Nhs.Models.Dashboard; + using LearningHub.Nhs.Models.Entities.Hierarchy; + + /// + /// The ICatalogueNodeVersionRepository. + /// + public interface ICatalogueNodeVersionRepository : IGenericRepository + { + /// + /// The get catalogues. + /// + /// The catalogue ids. + /// A representing the result of the asynchronous operation. + Task> GetCatalogues(List catalogueIds); + + /// + /// The get catalogues. + /// + /// The . + Task> GetPublishedCatalogues(); + + /// + /// The get catalogues for user. + /// + /// The userId. + /// The . + IQueryable GetPublishedCataloguesForUserAsync(int userId); + + /// + /// The get basic catalogue. + /// + /// The catalogueNodeId. + /// The . + CatalogueNodeVersion GetBasicCatalogue(int catalogueNodeId); + + /// + /// The UpdateCatalogueAsync. + /// + /// The userId. + /// The catalogue view model. + /// The task. + Task UpdateCatalogueAsync(int userId, CatalogueViewModel vm); + + /// + /// The UpdateCatalogueOwnerAsync. + /// + /// The userId. + /// The catalogue owner view model. + /// The task. + Task UpdateCatalogueOwnerAsync(int userId, CatalogueOwnerViewModel vm); + + /// + /// The CreateCatalogueAsync. + /// + /// The userId. + /// The catalogue view model. + /// The catalogueNodeVersionId. + Task CreateCatalogueAsync(int userId, CatalogueViewModel vm); + + /// + /// Get Catlogue by reference. + /// + /// The reference. + /// The CatalogueViewModel. + Task GetCatalogueAsync(string reference); + + /// + /// Get list of Restricted Catalogue AccessRequests for the supplied request. + /// + /// The restrictedCatalogueAccessRequestsRequestViewModel. + /// A RestrictedCatalogueAccessRequestsViewModel. + List GetRestrictedCatalogueAccessRequests(RestrictedCatalogueAccessRequestsRequestViewModel restrictedCatalogueAccessRequestsRequestViewModel); + + /// + /// Get list of RestrictedCatalogueUsersRequestViewModel for the supplied request. + /// + /// The restrictedCatalogueUsersRequestViewModel. + /// A RestrictedCatalogueUsersViewModel. + RestrictedCatalogueUsersViewModel GetRestrictedCatalogueUsers(RestrictedCatalogueUsersRequestViewModel restrictedCatalogueUsersRequestViewModel); + + /// + /// The ShowCatalogue. + /// + /// The UserId. + /// The NodeId. + /// The Task. + Task ShowCatalogue(int userId, int nodeId); + + /// + /// Get Restricted Catalogue Summary for the supplied catalogue node id. + /// + /// The catalogueNodeId. + /// A RestrictedCatalogueUsersViewModel. + RestrictedCatalogueSummaryViewModel GetRestrictedCatalogueSummary(int catalogueNodeId); + + /// + /// Gets catalogues for dashboard based on type. + /// + /// The dashboard type. + /// The page Number. + /// The userId. + /// Catalogue totals and records. + (int TotalCount, List Catalogues) GetCatalogues(string dashboardType, int pageNumber, int userId); + + /// + /// Check if a Catalogue with a specific name exists or not. + /// + /// The catalogue name. + /// True if the catalogue exists, otherwise false. + Task ExistsAsync(string name); + + /// + /// Gets the Node Id for a particular catalogue name. + /// + /// The catalogue name. + /// The catalogue's node id. + Task GetNodeIdByCatalogueName(string catalogueName); + + /// + /// Gets the catalogues count in alphabet list. + /// + /// The userId. + /// The catalogues alphabet count list. + List GetAllCataloguesAlphaCount(int userId); + + /// + /// Gets catalogues based on filter character. + /// + /// The pageSize. + /// The filterChar. + /// The userId. + /// The catalogues. + Task> GetAllCataloguesAsync(int pageSize, string filterChar, int userId); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Hierarchy/IFolderNodeVersionRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Hierarchy/IFolderNodeVersionRepository.cs new file mode 100644 index 000000000..2facce74e --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Hierarchy/IFolderNodeVersionRepository.cs @@ -0,0 +1,18 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Hierarchy +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Hierarchy; + + /// + /// The IFolderNodeVersionRepository. + /// + public interface IFolderNodeVersionRepository : IGenericRepository + { + /// + /// The GetFolder. + /// + /// The node version id. + /// The folder node version. + Task GetFolderAsync(int nodeVersionId); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Hierarchy/IHierarchyEditDetailRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Hierarchy/IHierarchyEditDetailRepository.cs new file mode 100644 index 000000000..ae8f37969 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Hierarchy/IHierarchyEditDetailRepository.cs @@ -0,0 +1,19 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Hierarchy +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Hierarchy; + + /// + /// The HierarchyEditDetailRepository interface. + /// + public interface IHierarchyEditDetailRepository : IGenericRepository + { + /// + /// The get root hierarchy detail by hierarchy edit id async. + /// + /// The hierarchy edit id. + /// The node id. + /// The . + Task GetByNodeIdAsync(long hierarchyEditId, int nodeId); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Hierarchy/IHierarchyEditRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Hierarchy/IHierarchyEditRepository.cs new file mode 100644 index 000000000..3337ab4e1 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Hierarchy/IHierarchyEditRepository.cs @@ -0,0 +1,177 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Hierarchy +{ + using System.Collections.Generic; + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Hierarchy; + using LearningHub.Nhs.Models.Hierarchy; + + /// + /// The HierarchyEditRepository interface. + /// + public interface IHierarchyEditRepository : IGenericRepository + { + /// + /// The get by id async. + /// + /// The id. + /// The . + Task GetByIdAsync(int id); + + /// + /// The get by root node id async. + /// + /// The id. + /// The . + Task> GetByRootNodeIdAsync(int rootNodeId); + + /// + /// The create. + /// + /// The root node id. + /// The user id. + /// The hierarchy edit id. + Task Create(int rootNodeId, int userId); + + /// + /// The discard. + /// + /// The root node id. + /// The user id. + /// The . + Task Discard(int hierarchyEditId, int userId); + + /// + /// Creates a new folder. + /// + /// The folderEditViewModel. + /// The user id. + /// The . + Task CreateFolder(FolderEditViewModel folderEditViewModel, int userId); + + /// + /// Updates a folder. + /// + /// The folderEditViewModel. + /// The user id. + /// The . + Task UpdateFolder(FolderEditViewModel folderEditViewModel, int userId); + + /// + /// Deletes a folder. + /// + /// The id. + /// The user id. + /// The . + Task DeleteFolder(int hierarchyEditDetailId, int userId); + + /// + /// Moves a node up. + /// + /// The id. + /// The user id. + /// The . + Task MoveNodeUp(int hierarchyEditDetailId, int userId); + + /// + /// Moves a node down. + /// + /// The id. + /// The user id. + /// The . + Task MoveNodeDown(int hierarchyEditDetailId, int userId); + + /// + /// Moves a node. + /// + /// The moveNodeViewModel . + /// The user id. + /// The . + Task MoveNode(MoveNodeViewModel moveNodeViewModel, int userId); + + /// + /// Moves a resource up in a hierarchy edit. + /// + /// The id. + /// The user id. + /// The . + Task HierarchyEditMoveResourceUp(int hierarchyEditDetailId, int userId); + + /// + /// Moves a resource down in a hierarchy edit. + /// + /// The id. + /// The user id. + /// The . + Task HierarchyEditMoveResourceDown(int hierarchyEditDetailId, int userId); + + /// + /// Moves a resource in a HierarchyEdit. + /// + /// The view model . + /// The user id. + /// The . + Task HierarchyEditMoveResource(HierarchyEditMoveResourceViewModel moveResourceViewModel, int userId); + + /// + /// Moves a resource up. + /// ITERATION 1 - Not moved within a hierarchy edit, update happens immediately. + /// + /// The id of the node containing the resource. + /// The resource id. + /// The user id. + /// The . + Task MoveResourceUp(int nodeId, int resourceId, int userId); + + /// + /// Moves a resource down. + /// ITERATION 1 - Not moved within a hierarchy edit, update happens immediately. + /// + /// The id of the node containing the resource. + /// The resource id. + /// The user id. + /// The . + Task MoveResourceDown(int nodeId, int resourceId, int userId); + + /// + /// Moves a resource. + /// ITERATION 1 - Not moved within a hierarchy edit, update happens immediately. + /// + /// The id of the node to move the resource from. + /// The id of the node to move the resource to. + /// The resource id. + /// The user id. + /// A list of nodeIds affected by the moved resource. The nodes that will need to be refreshed in the UI. + Task> MoveResource(int sourceNodeId, int destinationNodeId, int resourceId, int userId); + + /// + /// The publishing. + /// + /// The hierarchy edit id. + /// The user id. + void SubmitForPublishing(int hierarchyEditId, int userId); + + /// + /// The publish. + /// + /// The hierarchy edit id. + /// The is major revision. + /// The notes. + /// The user id. + /// The publication id. + int Publish(int hierarchyEditId, bool isMajorRevision, string notes, int userId); + + /// + /// Set hierarchy edit to "publishing". + /// + /// The hierarchy edit id. + /// The user id. + void Publishing(int hierarchyEditId, int userId); + + /// + /// Set hierarchy edit to "failed to publish". + /// + /// The hierarchy edit id. + /// The user id. + void FailedToPublish(int hierarchyEditId, int userId); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Hierarchy/INodePathRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Hierarchy/INodePathRepository.cs new file mode 100644 index 000000000..a24b8c6d3 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Hierarchy/INodePathRepository.cs @@ -0,0 +1,34 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Hierarchy +{ + using System.Collections.Generic; + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Hierarchy; + using LearningHub.Nhs.Models.Hierarchy; + + /// + /// The INodePathRepository. + /// + public interface INodePathRepository : IGenericRepository + { + /// + /// Gets the root catalogue nodeId for any given nodeId (i.e. folder/course). + /// + /// The folder/course nodeId. + /// The catalogue nodeId. + Task GetCatalogueRootNodeId(int folderNodeId); + + /// + /// Gets the node paths to the supplied node id. + /// + /// The nodeId. + /// The list of NodePaths. + Task> GetNodePathsForNodeId(int nodeId); + + /// + /// Gets the basic details of all Nodes in a particular NodePath. + /// + /// The NodePath id. + /// The . + Task> GetNodePathNodes(int nodePathId); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Hierarchy/INodeRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Hierarchy/INodeRepository.cs new file mode 100644 index 000000000..297be93f1 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Hierarchy/INodeRepository.cs @@ -0,0 +1,53 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Hierarchy +{ + using System.Collections.Generic; + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Hierarchy; + using LearningHub.Nhs.Models.Hierarchy; + + /// + /// The NodeRepository interface. + /// + public interface INodeRepository : IGenericRepository + { + /// + /// The get by id async. + /// + /// The id. + /// The . + Task GetByIdAsync(int id); + + /// + /// Gets the basic details of a node. Currently catalogues or folders. + /// + /// The node id. + /// The node details. + Task GetNodeDetails(int nodeId); + + /// + /// Gets the contents of a node for the catalogue landing page - i.e. published folders and published resources only. + /// Only returns the items found directly in the specified node, does not recurse down through subfolders. + /// + /// The node id. + /// Include Empty Folder or not. + /// The . + Task> GetNodeContentsForCatalogueBrowse(int nodeId, bool includeEmptyFolder); + + /// + /// Gets the contents of a node for the My Contributions page - i.e. published folders only, and all resources (i.e. all statuses). + /// Only returns the items found directly in the specified node, does not recurse down through subfolders. + /// + /// The node id. + /// The . + Task> GetNodeContentsForCatalogueEditor(int nodeId); + + /// + /// Gets the contents of a node (catalogue/folder/course) - i.e. returns a list of subfolders and resources. Only returns the + /// items from the first level down. Doesn't recurse through subfolders. + /// + /// The node id. + /// Set to true if read only data set is required. + /// The . + Task> GetNodeContentsAdminAsync(int nodeId, bool readOnly); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Hierarchy/INodeResourceLookupRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Hierarchy/INodeResourceLookupRepository.cs new file mode 100644 index 000000000..733b1e7eb --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Hierarchy/INodeResourceLookupRepository.cs @@ -0,0 +1,19 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Hierarchy +{ + using System.Collections.Generic; + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Hierarchy; + + /// + /// The INodeResourceLookupRepository interface. + /// + public interface INodeResourceLookupRepository : IGenericRepository + { + /// + /// The get by node id async. + /// + /// The node id. + /// The . + Task> GetByNodeIdAsync(int nodeId); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Hierarchy/INodeResourceRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Hierarchy/INodeResourceRepository.cs new file mode 100644 index 000000000..dce087b47 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Hierarchy/INodeResourceRepository.cs @@ -0,0 +1,53 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Hierarchy +{ + using System.Collections.Generic; + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Catalogue; + using LearningHub.Nhs.Models.Entities.Hierarchy; + using LearningHub.Nhs.Models.Enums; + using LearningHub.Nhs.Models.Hierarchy; + + /// + /// The NodeResourceRepository interface. + /// + public interface INodeResourceRepository : IGenericRepository + { + /// + /// The get by resource id async. + /// + /// The resource id. + /// The . + Task> GetByResourceIdAsync(int resourceId); + + /// + /// The get catalogue locations for resource. + /// + /// The resource id. + /// The CatalogueLocationViewModel list. + List GetCatalogueLocationsForResource(int resourceId); + + /// + /// GetResourcesAsync. + /// + /// nodeId. + /// catalogueOrder. + /// offset. + /// A representing the result of the asynchronous operation. + Task GetResourcesAsync(int nodeId, CatalogueOrder catalogueOrder, int offset); + + /// + /// Get All published resources id. + /// + /// The . + Task> GetAllPublishedResourceAsync(); + + /// + /// Creates or updates the NodeResource record for a draft resource in a node. + /// + /// The nodeId. + /// The resourceId. + /// The userId. + /// The . + Task CreateOrUpdateAsync(int nodeId, int resourceId, int userId); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Hierarchy/IPublicationRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Hierarchy/IPublicationRepository.cs new file mode 100644 index 000000000..ee6743a0e --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Hierarchy/IPublicationRepository.cs @@ -0,0 +1,48 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Hierarchy +{ + using System.Collections.Generic; + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Hierarchy; + using LearningHub.Nhs.Models.Hierarchy; + + /// + /// The PublicationRepository interface. + /// + public interface IPublicationRepository : IGenericRepository + { + /// + /// The get by id async. + /// + /// The id. + /// The . + Task GetByIdAsync(int id); + + /// + /// The get by id. + /// + /// The id. + /// The . + Publication GetById(int id); + + /// + /// The get by resourceVersionId. + /// + /// The resourceVersionId. + /// The Publication. + Task GetByResourceVersionIdAsync(int resourceVersionId); + + /// + /// Get cache operations for the supplied publication id. + /// + /// The publicationId. + /// A list of . + Task> GetCacheOperations(int publicationId); + + /// + /// Get resource first publication record. + /// + /// resource id. + /// publish view model. + Task GetResourceFirstPublication(int resourceId); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IBookmarkRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IBookmarkRepository.cs new file mode 100644 index 000000000..35fefbefe --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IBookmarkRepository.cs @@ -0,0 +1,26 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities; + + /// + /// The IRoadmapRepository. + /// + public interface IBookmarkRepository : IGenericRepository + { + /// + /// The GetById. + /// + /// The bookmarkId. + /// The . + Task GetById(int bookmarkId); + + /// + /// DeleteFolder. + /// + /// bookmarkId. + /// userId. + /// A representing the result of the asynchronous operation. + Task DeleteFolder(int bookmarkId, int userId); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IEmailChangeValidationTokenRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IEmailChangeValidationTokenRepository.cs new file mode 100644 index 000000000..390f1c39a --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IEmailChangeValidationTokenRepository.cs @@ -0,0 +1,72 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities; + + /// + /// The EmailChangeValidationTokenRepository interface. + /// + public interface IEmailChangeValidationTokenRepository + { + /// + /// The get by token. + /// + /// + /// The lookup. + /// + /// + /// The . + /// + Task GetByToken(string lookup); + + /// + /// The GetLastIssuedEmailChangeValidationToken. + /// + /// + /// The lookup. + /// + /// + /// The . + /// + Task GetLastIssuedEmailChangeValidationToken(int userId); + + /// + /// The expire email change validation token. + /// + /// + /// The lookup. + /// + /// + /// The . + /// + Task ExpireEmailChangeValidationToken(string lookup); + + /// + /// The create async. + /// + /// + /// The user id. + /// + /// + /// The email change validation token. + /// + /// + /// The . + /// + Task CreateAsync(int userId, EmailChangeValidationToken emailChangeValidationToken); + + /// + /// The create async. + /// + /// + /// The user id. + /// + /// + /// The email change validation token. + /// + /// + /// The . + /// + Task UpdateAsync(int userId, EmailChangeValidationToken emailChangeValidationToken); + } +} \ No newline at end of file diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IGenericRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IGenericRepository.cs new file mode 100644 index 000000000..25969ea32 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IGenericRepository.cs @@ -0,0 +1,43 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories +{ + using System.Linq; + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities; + + /// + /// The GenericRepository interface. + /// + /// Imput type. + public interface IGenericRepository + where TEntity : EntityBase + { + /// + /// The get all. + /// + /// The . + IQueryable GetAll(); + + /// + /// The create async. + /// + /// The user id. + /// The entity. + /// The . + Task CreateAsync(int userId, TEntity entity); + + /// + /// The update async. + /// + /// The user id. + /// The entity. + /// The . + Task UpdateAsync(int userId, TEntity entity); + + /// + /// The update. + /// + /// The user id. + /// The entity. + void Update(int userId, TEntity entity); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/INotificationTemplateRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/INotificationTemplateRepository.cs new file mode 100644 index 000000000..b434a2095 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/INotificationTemplateRepository.cs @@ -0,0 +1,18 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories +{ + using LearningHub.Nhs.Models.Entities; + using LearningHub.Nhs.Models.Enums; + + /// + /// The INotificationTemplateRepository interface. + /// + public interface INotificationTemplateRepository : IGenericRepository + { + /// + /// The GetById. + /// + /// The templateId. + /// The notification template. + NotificationTemplate GetById(NotificationTemplates templateId); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IProviderRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IProviderRepository.cs new file mode 100644 index 000000000..3e3e5ed7d --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IProviderRepository.cs @@ -0,0 +1,48 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories +{ + using System.Linq; + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities; + + /// + /// The ProviderRepository interface. + /// + public interface IProviderRepository : IGenericRepository + { + /// + /// The get by id async. + /// + /// The id. + /// The . + Task GetByIdAsync(int id); + + /// + /// The get by id async. + /// + /// The id. + /// The include children. + /// The . + Task GetByIdAsync(int id, bool includeChildren); + + /// + /// The get by user id async. + /// + /// The user id. + /// The . + IQueryable GetProvidersByUserIdAsync(int userId); + + /// + /// The get by resource version id async. + /// + /// The resource version id. + /// The . + IQueryable GetProvidersByResourceIdAsync(int resourceVersionId); + + /// + /// The get by node version id async. + /// + /// The node version id. + /// The . + IQueryable GetProvidersByCatalogueIdAsync(int nodeVersionId); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IResourceRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IResourceRepository.cs index 52b3dee08..1b9d45411 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IResourceRepository.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IResourceRepository.cs @@ -4,25 +4,26 @@ namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories using System.Threading.Tasks; using LearningHub.Nhs.Models.Entities.Activity; using LearningHub.Nhs.Models.Entities.Resource; + using LearningHub.Nhs.Models.Enums; /// /// Resource repository interface. /// - public interface IResourceRepository + public interface IResourceRepository : IGenericRepository { /// /// Gets resources from ids. /// /// . /// Resources with details. - public Task> GetResourcesFromIds(IEnumerable resourceIds); + Task> GetResourcesFromIds(IEnumerable resourceIds); /// /// Gets resource references by their original resource reference Ids. /// /// . /// Resource references. - public Task> GetResourceReferencesByOriginalResourceReferenceIds( + Task> GetResourceReferencesByOriginalResourceReferenceIds( IEnumerable originalResourceReferenceIds); /// @@ -37,6 +38,39 @@ public Task> GetResourceReferencesByOriginalResou /// GetAchievedCertificatedResourceIds /// /// . - public Task> GetAchievedCertificatedResourceIds(int currentUserId); + Task> GetAchievedCertificatedResourceIds(int currentUserId); + + /// + /// The get by id async. + /// + /// The id. + /// The . + Task GetByIdAsync(int id); + + + /// + /// Returns true if the user has any resources published. + /// + /// The user id. + /// If the user has any resources published. + Task UserHasPublishedResourcesAsync(int userId); + + /// + /// The create resource async. + /// + /// The resource type. + /// The title. + /// The description. + /// The user id. + /// The . + Task CreateResourceAsync(ResourceTypeEnum resourceType, string title, string description, int userId); + + /// + /// The get by resourve version id async. + /// + /// The resource version id. + /// The . + Task GetByResourceVersionIdAsync(int resourceVersionId); + } } diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IRoleUserGroupRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IRoleUserGroupRepository.cs new file mode 100644 index 000000000..7fd1426a1 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IRoleUserGroupRepository.cs @@ -0,0 +1,67 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories +{ + using System.Collections.Generic; + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities; + using LearningHub.Nhs.Models.User; + + /// + /// The IRoleUserGroupRepository interface. + /// + public interface IRoleUserGroupRepository : IGenericRepository + { + /// + /// The get by id async. + /// + /// The id. + /// The . + Task GetByIdAsync(int id); + + /// + /// The get by catalogueNodeId async. + /// + /// The role id. + /// The userGroup id. + /// The scope id. + /// The . + Task GetByRoleIdUserGroupIdScopeIdAsync(int roleId, int userGroupId, int scopeId); + + /// + /// The get by role id and catalogue id. + /// + /// The role id. + /// The catalogue node id. + /// The . + Task> GetByRoleIdCatalogueId(int roleId, int catalogueNodeId); + + /// + /// The get by role id and catalogue id that has users. + /// + /// The role id. + /// The catalogue node id. + /// The . + Task> GetByRoleIdCatalogueIdWithUsers(int roleId, int catalogueNodeId); + + /// + /// Get list of RoleUserGroupViewModel for a supplied User Group. + /// + /// The userGroupId. + /// A list of RoleUserGroupViewModel. + Task> GetRoleUserGroupViewModelsByUserGroupId(int userGroupId); + + /// + /// Get list of RoleUserGroupViewModel for a supplied User Group. + /// + /// The userGroupId. + /// A list of RoleUserGroupViewModel. + Task> GetRoleUserGroupViewModelsByUserId(int userId); + + /// + /// The get all for search. + /// + /// The catalogueNodeId. + /// The userId. + /// The . + Task> GetAllforSearch(int catalogueNodeId, int userId); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/ITimezoneOffsetManager.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/ITimezoneOffsetManager.cs new file mode 100644 index 000000000..15f4b8bf1 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/ITimezoneOffsetManager.cs @@ -0,0 +1,22 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories +{ + using System; + + /// + /// The TimezoneOffsetManager interface. + /// + public interface ITimezoneOffsetManager + { + /// + /// Gets User Timezone Offset. + /// + int? UserTimezoneOffset { get; } + + /// + /// Converts a UTC DateTimeOffset to the timezone of the current user. + /// + /// The UTC DateTimeOffset to convert. + /// . + DateTimeOffset ConvertToUserTimezone(DateTimeOffset utcDate); + } +} \ No newline at end of file diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IUserProfileRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IUserProfileRepository.cs new file mode 100644 index 000000000..1611e99b3 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IUserProfileRepository.cs @@ -0,0 +1,32 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities; + + /// + /// The UserProfileRepository interface. + /// + public interface IUserProfileRepository : IGenericRepository + { + /// + /// The get by id async. + /// + /// The id. + /// The . + Task GetByIdAsync(int id); + + /// + /// The GetByEmailAddressAsync. + /// + /// The email address. + /// The userProfile. + Task GetByEmailAddressAsync(string emailAddress); + + /// + /// The GetByUsernameAsync. + /// + /// The userName. + /// The userProfile. + Task GetByUsernameAsync(string userName); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IUserRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IUserRepository.cs new file mode 100644 index 000000000..ff8b6cb5b --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IUserRepository.cs @@ -0,0 +1,40 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities; + + /// + /// The UserRepository interface. + /// + public interface IUserRepository : IGenericRepository + { + /// + /// The get by id async. + /// + /// The id. + /// The . + Task GetByIdAsync(int id); + + /// + /// The get by id include roles async. + /// + /// The id. + /// The . + Task GetByIdIncludeRolesAsync(int id); + + /// + /// The get by username async. + /// + /// The username. + /// The include roles. + /// The . + Task GetByUsernameAsync(string username, bool includeRoles); + + /// + /// Returns indication of whether the user in an Admin. + /// + /// The user id. + /// The . + bool IsAdminUser(int userId); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IUserUserGroupRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IUserUserGroupRepository.cs new file mode 100644 index 000000000..ee39c0e79 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IUserUserGroupRepository.cs @@ -0,0 +1,26 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities; + + /// + /// The UserUserGroupRepository interface. + /// + public interface IUserUserGroupRepository : IGenericRepository + { + /// + /// The get by id async. + /// + /// The id. + /// The . + Task GetByIdAsync(int id); + + /// + /// to get the user user group details by user id and usergroup id. + /// + /// userId. + /// userGroupId. + /// The . + Task GetByUserIdandUserGroupIdAsync(int userId, int userGroupId); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Maintenance/IInternalSystemRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Maintenance/IInternalSystemRepository.cs new file mode 100644 index 000000000..e5b4b6304 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Maintenance/IInternalSystemRepository.cs @@ -0,0 +1,11 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Maintenance +{ + using LearningHub.Nhs.Models.Entities.Maintenance; + + /// + /// The InternalSystemRepository. + /// + public interface IInternalSystemRepository : IGenericRepository + { + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Messaging/IEmailTemplateRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Messaging/IEmailTemplateRepository.cs new file mode 100644 index 000000000..2ea140c2d --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Messaging/IEmailTemplateRepository.cs @@ -0,0 +1,17 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Messaging +{ + using LearningHub.Nhs.Models.Entities.Messaging; + + /// + /// The IEmailTemplateRepository. + /// + public interface IEmailTemplateRepository + { + /// + /// The GetTemplate. + /// + /// The email template id. + /// The Email Template. + EmailTemplate GetTemplate(int id); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Messaging/IMessageRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Messaging/IMessageRepository.cs new file mode 100644 index 000000000..67ed87b2a --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Messaging/IMessageRepository.cs @@ -0,0 +1,78 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Messaging +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Messaging; + + /// + /// The IMessageRepository class. + /// + public interface IMessageRepository + { + /// + /// Gets a list of all messages which have a message send which hasn't been sent. + /// + /// The messages. + IQueryable GetPendingMessages(); + + /// + /// Creates an email to be sent. + /// + /// The user id. + /// The subject. + /// The body. + /// The recipient user id. + /// The task. + Task CreateEmailAsync(int userId, string subject, string body, int recipientUserId); + + /// + /// The CreateEmailAsync. + /// + /// The user id. + /// The subject. + /// The body. + /// The recipientEmailAddress. + /// The task. + Task CreateEmailAsync(int userId, string subject, string body, string recipientEmailAddress); + + /// + /// The CreateNotificationForUserAsync. + /// + /// The userId. + /// The subject. + /// The body. + /// The recipientUserId. + /// The notificationStartDate. + /// The notificationEndDate. + /// The notificationPriority. + /// The notificationType. + /// The task. + Task CreateNotificationForUserAsync( + int userId, + string subject, + string body, + int recipientUserId, + DateTimeOffset notificationStartDate, + DateTimeOffset notificationEndDate, + int notificationPriority, + int notificationType); + + /// + /// Marks a message send as having been successful. + /// + /// The userId. + /// The messageSends. + /// The task. + Task MessageSendSuccess(int userId, List messageSends); + + /// + /// Either marks a message as failed, or queues it for a retry. + /// + /// The userId. + /// The messageSends. + /// The task. + Task MessageSendFailure(int userId, List messageSends); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Migrations/IMigrationSourceRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Migrations/IMigrationSourceRepository.cs new file mode 100644 index 000000000..f12fe6179 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Migrations/IMigrationSourceRepository.cs @@ -0,0 +1,18 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Migrations +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Migration; + + /// + /// The MigrationSourceRepository interface. + /// + public interface IMigrationSourceRepository : IGenericRepository + { + /// + /// The get by id async. + /// + /// The id. + /// The . + Task GetByIdAsync(int id); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IArticleResourceVersionFileRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IArticleResourceVersionFileRepository.cs new file mode 100644 index 000000000..6907ae321 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IArticleResourceVersionFileRepository.cs @@ -0,0 +1,26 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Resources +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Resource; + + /// + /// The ArticleResourceVersionFileRepository interface. + /// + public interface IArticleResourceVersionFileRepository : IGenericRepository + { + /// + /// The get by id async. + /// + /// The id. + /// The . + Task GetByIdAsync(int id); + + /// + /// The get by id async. + /// + /// The resourceVersionId. + /// The fileId. + /// The . + Task GetByResourceVersionAndFileAsync(int resourceVersionId, int fileId); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IArticleResourceVersionRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IArticleResourceVersionRepository.cs new file mode 100644 index 000000000..048fe9f4d --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IArticleResourceVersionRepository.cs @@ -0,0 +1,26 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Resources +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Resource; + + /// + /// The ArticleResourceVersionRepository interface. + /// + public interface IArticleResourceVersionRepository : IGenericRepository + { + /// + /// The get by id async. + /// + /// The id. + /// The . + Task GetByIdAsync(int id); + + /// + /// The get by resource version id async. + /// + /// The id. + /// Allows deleted items to be returned. + /// The . + Task GetByResourceVersionIdAsync(int id, bool includeDeleted = false); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IAssessmentResourceVersionRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IAssessmentResourceVersionRepository.cs new file mode 100644 index 000000000..028800c44 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IAssessmentResourceVersionRepository.cs @@ -0,0 +1,25 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Resources +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Resource; + + /// + /// The AssessmentResourceVersionRepository interface. + /// + public interface IAssessmentResourceVersionRepository : IGenericRepository + { + /// + /// The get by Assessment Version Id async. + /// + /// The Assessment Version Id. + /// The . + Task GetByResourceVersionIdAsync(int assessmentVersionId); + + /// + /// The get by Assessment Content Id async. + /// + /// The Assessment Content Id. + /// The . + Task GetByAssessmentContentBlockCollectionIdAsync(int assessmentContentId); + } +} \ No newline at end of file diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IAudioResourceVersionRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IAudioResourceVersionRepository.cs new file mode 100644 index 000000000..9d1690217 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IAudioResourceVersionRepository.cs @@ -0,0 +1,26 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Resources +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Resource; + + /// + /// The AudioResourceVersionRepository interface. + /// + public interface IAudioResourceVersionRepository : IGenericRepository + { + /// + /// The get by id async. + /// + /// The id. + /// The . + Task GetByIdAsync(int id); + + /// + /// The get by resource version id async. + /// + /// The id. + /// Allows deleted items to be returned. + /// The . + Task GetByResourceVersionIdAsync(int id, bool includeDeleted = false); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IBlockCollectionRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IBlockCollectionRepository.cs new file mode 100644 index 000000000..cadadd20b --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IBlockCollectionRepository.cs @@ -0,0 +1,43 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Resources +{ + using System.Collections.Generic; + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Resource.Blocks; + using LearningHub.Nhs.Models.Enums; + + /// + /// The BlockRepository interface. + /// + public interface IBlockCollectionRepository : IGenericRepository + { + /// + /// Delete the Block Collection. + /// + /// The User Id. + /// The Block Collection Id. + /// The . + Task DeleteBlockCollection(int userId, int blockCollectionId); + + /// + /// Gets the Block Collection. + /// + /// The Block Collection Id. + /// The . + Task GetBlockCollection(int? blockCollectionId); + + /// + /// Gets the Question blocks for a particular blockCollectionId. + /// + /// The Block Collection Id. + /// The . + Task> GetQuestionBlocks(int blockCollectionId); + + /// + /// Gets the Case, AssessmentContent, AssessmentGuidance Block Collections (including child Blocks, TextBlocks, WholeSlideImageBlocks and Files) except that of the provided resource version. + /// + /// The excluded ResourceVersion Id. + /// The resource type. + /// The . + Task GetResourceBlockCollectionsFileAsync(int excludeResourceVersionId, ResourceTypeEnum resourceTypeEnum); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/ICaseResourceVersionRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/ICaseResourceVersionRepository.cs new file mode 100644 index 000000000..af9c600bc --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/ICaseResourceVersionRepository.cs @@ -0,0 +1,18 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Resources +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Resource; + + /// + /// The CaseResourceVersionRepository interface. + /// + public interface ICaseResourceVersionRepository : IGenericRepository + { + /// + /// The get by Resource Version Id async. + /// + /// The Resource Version Id. + /// The . + Task GetByResourceVersionIdAsync(int resourceVersionId); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IEmbeddedResourceVersionRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IEmbeddedResourceVersionRepository.cs new file mode 100644 index 000000000..b35680f24 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IEmbeddedResourceVersionRepository.cs @@ -0,0 +1,18 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Resources +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Resource; + + /// + /// The EmbeddedResourceVersionRepository interface. + /// + public interface IEmbeddedResourceVersionRepository : IGenericRepository + { + /// + /// The get by id async. + /// + /// The id. + /// The . + Task GetByIdAsync(int id); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IEquipmentResourceVersionRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IEquipmentResourceVersionRepository.cs new file mode 100644 index 000000000..1f9807c7c --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IEquipmentResourceVersionRepository.cs @@ -0,0 +1,18 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Resources +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Resource; + + /// + /// The EquipmentResourceVersionRepository interface. + /// + public interface IEquipmentResourceVersionRepository : IGenericRepository + { + /// + /// The get by resource version id async. + /// + /// The id. + /// The . + Task GetByResourceVersionIdAsync(int id); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IFileChunkDetailRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IFileChunkDetailRepository.cs new file mode 100644 index 000000000..4f6b7dea4 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IFileChunkDetailRepository.cs @@ -0,0 +1,26 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Resources +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Resource; + + /// + /// The FileChunkDetailRepository interface. + /// + public interface IFileChunkDetailRepository : IGenericRepository + { + /// + /// The get by id async. + /// + /// The id. + /// The . + Task GetByIdAsync(int id); + + /// + /// Delete a file chunk detail. + /// + /// The file Chunk Detail id. + /// The user id. + /// The . + Task Delete(int fileChunkDetailId, int userId); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IFileRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IFileRepository.cs new file mode 100644 index 000000000..7baf4ed9d --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IFileRepository.cs @@ -0,0 +1,18 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Resources +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Resource; + + /// + /// The FileRepository interface. + /// + public interface IFileRepository : IGenericRepository + { + /// + /// The get by id async. + /// + /// The id. + /// The . + Task GetByIdAsync(int id); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IFileTypeRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IFileTypeRepository.cs new file mode 100644 index 000000000..33ca0ec23 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IFileTypeRepository.cs @@ -0,0 +1,18 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Resources +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Resource; + + /// + /// The FileTypeRepository interface. + /// + public interface IFileTypeRepository : IGenericRepository + { + /// + /// The get by id async. + /// + /// The id. + /// The . + Task GetByIdAsync(int id); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IGenericFileResourceVersionRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IGenericFileResourceVersionRepository.cs new file mode 100644 index 000000000..28eabdede --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IGenericFileResourceVersionRepository.cs @@ -0,0 +1,34 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Resources +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Resource; + + /// + /// The GenericFileResourceVersionRepository interface. + /// + public interface IGenericFileResourceVersionRepository : IGenericRepository + { + /// + /// The get by id async. + /// + /// The id. + /// The . + Task GetByIdAsync(int id); + + /// + /// The get by resource version id async. + /// + /// The id. + /// Allows deleted items to be returned. + /// The . + Task GetByResourceVersionIdAsync(int id, bool includeDeleted = false); + + /// + /// The get by resource version id. + /// + /// The id. + /// Allows deleted items to be returned. + /// The generic file resource version. + GenericFileResourceVersion GetByResourceVersionId(int id, bool includeDeleted = false); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IHtmlResourceVersionRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IHtmlResourceVersionRepository.cs new file mode 100644 index 000000000..3def3f0b9 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IHtmlResourceVersionRepository.cs @@ -0,0 +1,34 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Resources +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Resource; + + /// + /// The GenericFileResourceVersionRepository interface. + /// + public interface IHtmlResourceVersionRepository : IGenericRepository + { + /// + /// The get by id async. + /// + /// The id. + /// The . + Task GetByIdAsync(int id); + + /// + /// The get by resource version id async. + /// + /// The id. + /// Allows deleted items to be returned. + /// The . + Task GetByResourceVersionIdAsync(int id, bool includeDeleted = false); + + /// + /// The get by resource version id. + /// + /// The id. + /// Allows deleted items to be returned. + /// The html resource version. + HtmlResourceVersion GetByResourceVersionId(int id, bool includeDeleted = false); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IImageResourceVersionRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IImageResourceVersionRepository.cs new file mode 100644 index 000000000..1a2af621e --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IImageResourceVersionRepository.cs @@ -0,0 +1,26 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Resources +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Resource; + + /// + /// The ImageResourceVersionRepository interface. + /// + public interface IImageResourceVersionRepository : IGenericRepository + { + /// + /// The get by id async. + /// + /// The id. + /// The . + Task GetByIdAsync(int id); + + /// + /// The get by resource version id async. + /// + /// The id. + /// Allows deleted items to be returned. + /// The . + Task GetByResourceVersionIdAsync(int id, bool includeDeleted = false); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IQuestionBlockRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IQuestionBlockRepository.cs new file mode 100644 index 000000000..ea88cc732 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IQuestionBlockRepository.cs @@ -0,0 +1,18 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Resources +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Resource.Blocks; + + /// + /// The QuestionBlockRepository interface. + /// + public interface IQuestionBlockRepository : IGenericRepository + { + /// + /// The get by Block Collection Id async. + /// + /// The block collection Id. + /// The . + Task GetByQuestionBlockCollectionIdAsync(int blockCollectionId); + } +} \ No newline at end of file diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IResourceLicenceRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IResourceLicenceRepository.cs new file mode 100644 index 000000000..876227b27 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IResourceLicenceRepository.cs @@ -0,0 +1,11 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Resources +{ + using LearningHub.Nhs.Models.Entities.Resource; + + /// + /// The FileTypeRepository interface. + /// + public interface IResourceLicenceRepository : IGenericRepository + { + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IResourceReferenceRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IResourceReferenceRepository.cs new file mode 100644 index 000000000..2042a5c9a --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IResourceReferenceRepository.cs @@ -0,0 +1,34 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Resources +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Resource; + + /// + /// The ResourceReferenceRepository interface. + /// + public interface IResourceReferenceRepository : IGenericRepository + { + /// + /// The get by id async. + /// + /// The id. + /// The include NodePath. + /// The . + Task GetByIdAsync(int id, bool includeNodePath); + + /// + /// The get by original resource reference id async. + /// + /// The id. + /// The include NodePath. + /// The . + Task GetByOriginalResourceReferenceIdAsync(int id, bool includeNodePath); + + /// + /// The get default by resource id async. + /// + /// The resource id. + /// The . + Task GetDefaultByResourceIdAsync(int resourceId); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IResourceVersionAuthorRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IResourceVersionAuthorRepository.cs new file mode 100644 index 000000000..e96c4cf09 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IResourceVersionAuthorRepository.cs @@ -0,0 +1,27 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Resources +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Resource; + + /// + /// The ResourceVersionAuthorRepository interface. + /// + public interface IResourceVersionAuthorRepository : IGenericRepository + { + /// + /// The delete async. + /// + /// The user id. + /// The resource version author id. + /// The . + Task DeleteAsync(int userId, int resourceVersionAuthorId); + + /// + /// The delete async. + /// + /// The resource version id. + /// The author id. + /// The . + Task GetByResourceVersionAndAuthorAsync(int resourceVersionId, int authorId); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IResourceVersionFlagRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IResourceVersionFlagRepository.cs new file mode 100644 index 000000000..ba4876732 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IResourceVersionFlagRepository.cs @@ -0,0 +1,36 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Resources +{ + using System.Linq; + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Resource; + using LearningHub.Nhs.Models.Validation; + + /// + /// The ResourceVersionFlagRepository interface. + /// + public interface IResourceVersionFlagRepository : IGenericRepository + { + /// + /// The get by resource version id async. + /// + /// The resource version id. + /// The . + IQueryable GetByResourceVersionIdAsync(int resourceVersionId); + + /// + /// The update resource version flag async. + /// + /// The user id. + /// The resource version flag. + /// The . + Task UpdateResourceVersionFlagAsync(int userId, ResourceVersionFlag resourceVersionFlag); + + /// + /// The delete async. + /// + /// The user id. + /// The resource version flag id. + /// The . + Task DeleteAsync(int userId, int resourceVersionFlagId); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IResourceVersionKeywordRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IResourceVersionKeywordRepository.cs new file mode 100644 index 000000000..78127d86f --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IResourceVersionKeywordRepository.cs @@ -0,0 +1,35 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Resources +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Resource; + + /// + /// The ResourceVersionKeywordRepository interface. + /// + public interface IResourceVersionKeywordRepository : IGenericRepository + { + /// + /// The delete async. + /// + /// The user id. + /// The resource version keyword id. + /// The . + Task DeleteAsync(int userId, int resourceVersionKeywordId); + + /// + /// The delete async. + /// + /// The resource version id. + /// The keyword id. + /// The . + Task GetByResourceVersionAndKeywordAsync(int resourceVersionId, int keywordId); + + /// + /// Check if a specific keyword exists for the Resource Version. + /// + /// The resource version id. + /// The keyword. + /// The . + Task DoesResourceVersionKeywordAlreadyExistAsync(int resourceVersionId, string keyword); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IResourceVersionProviderRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IResourceVersionProviderRepository.cs new file mode 100644 index 000000000..6a1bb065b --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IResourceVersionProviderRepository.cs @@ -0,0 +1,28 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Resources +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Resource; + + /// + /// The ResourceVersionEventRepository interface. + /// + public interface IResourceVersionProviderRepository : IGenericRepository + { + /// + /// Delete resource version provider. + /// + /// resource version id. + /// provider id. + /// user id. + /// The . + Task DeleteAsync(int resourceVersionId, int providerId, int userId); + + /// + /// Delete all resource version provider. + /// + /// resource version id. + /// user id. + /// The . + Task DeleteAllAsync(int resourceVersionId, int userId); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IResourceVersionRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IResourceVersionRepository.cs new file mode 100644 index 000000000..b2e296682 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IResourceVersionRepository.cs @@ -0,0 +1,303 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Resources +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Dashboard; + using LearningHub.Nhs.Models.Entities.Resource; + using LearningHub.Nhs.Models.Enums; + using LearningHub.Nhs.Models.Resource; + using LearningHub.Nhs.Models.Resource.ResourceDisplay; + + /// + /// The ResourceVersionRepository interface. + /// + public interface IResourceVersionRepository : IGenericRepository + { + /// + /// The get all admin search. + /// + /// The userId. + /// The . + IQueryable GetAllAdminSearch(int userId); + + /// + /// The get by id async. + /// + /// The id. + /// The include events. + /// The . + Task GetByIdAsync(int id, bool includeEvents); + + /// + /// The get basic by id async. + /// + /// The id. + /// The . + Task GetBasicByIdAsync(int id); + + /// + /// The contribution totals. + /// + /// The user id. + /// The catalogue id. + /// The MyContributionsTotalsViewModel. + MyContributionsTotalsViewModel GetMyContributionTotals(int userId, int catalogueId); + + /// + /// Get resource cards. + /// + /// The include events. + /// The . + Task> GetResourceCards(bool includeEvents); + + /// + /// The get current for resource async. + /// + /// The resource Id. + /// The . + Task GetCurrentForResourceAsync(int resourceId); + + /// + /// The get current resource for resourceid async. + /// + /// The resource Id. + /// The . + Task GetCurrentResourceDetailsAsync(int resourceId); + + /// + /// The get resource version details by id async. + /// + /// The resourceVersionId. + /// The . + Task GetByResourceVersionByIdAsync(int resourceVersionId); + + /// + /// The check dev id already exists in the table async. + /// + /// The devId. + /// The . + Task DoesDevIdExistsAync(string devId); + + /// + /// The get current published for resource async. + /// + /// The resource id. + /// The . + Task GetCurrentPublicationForResourceAsync(int resourceId); + + /// + /// The get current for resource reference id async. + /// + /// The resource reference id. + /// The . + Task GetCurrentForResourceReferenceIdAsync(int resourceReferenceId); + + /// + /// The get current resource for resource reference id async. + /// + /// The resource reference id. + /// The . + Task GetCurrentResourceForResourceReferenceIdAsync(int resourceReferenceId); + + /// + /// The get current published for resource reference id async. + /// + /// The resource reference id. + /// The . + Task GetCurrentPublicationForResourceReferenceIdAsync(int resourceReferenceId); + + /// + /// The set resource type. + /// + /// The resource version id. + /// The resource type enum. + /// The user id. + void SetResourceType(int resourceVersionId, ResourceTypeEnum resourceTypeEnum, int userId); + + /// + /// Gets the resource type of a ResourceVersion. + /// + /// The resourceVersionId. + /// The . + Task GetResourceType(int resourceVersionId); + + /// + /// The publish. + /// + /// The resource version id. + /// The is major revision. + /// The notes. + /// The publication date. Set to null if not giving the resource a publication date in the past. This parameter is intended for use by the migration tool. + /// The user id. + /// The publication id. + int Publish(int resourceVersionId, bool isMajorRevision, string notes, DateTimeOffset? publicationDate, int userId); + + /// + /// The unpublish. + /// + /// The resource version id. + /// The details. + /// The user id. + void Unpublish(int resourceVersionId, string details, int userId); + + /// + /// The revert to draft. + /// + /// The resource version id. + /// The user id. + void RevertToDraft(int resourceVersionId, int userId); + + /// + /// Delete a resource version. + /// + /// The resource version id. + /// The user id. + void Delete(int resourceVersionId, int userId); + + /// + /// The get resource versions. + /// + /// The resource id. + /// The . + Task> GetResourceVersionsAsync(int resourceId); + + ///// + ///// The create activity. + ///// + ///// + ///// The user id. + ///// + ///// + ///// The resource version id. + ///// + ///// + ///// The activity status enum. + ///// + ///// + ///// The activity start. + ///// + ///// + ///// The activity end. + ///// + ////void CreateActivity( + // int userId, + // int resourceVersionId, + // ActivityStatusEnum activityStatusEnum, + // DateTimeOffset activityStart, + // DateTimeOffset activityEnd); + + /// + /// Check if a user has completed the activity corresponding to the resource version. + /// + /// The user id. + /// The resource version id. + /// A boolean. + Task HasUserCompletedActivity(int userId, int resourceVersionId); + + /// + /// Return whether a version at a specific status currently exists. + /// + /// The resource id. + /// The resource version status id. + /// Whether the resource version exists or not. + Task DoesVersionExist(int resourceId, VersionStatusEnum draft); + + /// + /// Return whether a version at a specific status currently exists. + /// + /// The resource id. + /// The user id. + /// Returns the new resource version id. + Task CreateNextVersionAsync(int resourceId, int userId); + + /// + /// Duplicates a resource version. + /// + /// The resource id. + /// The user id. + /// Returns the new resource version id. + Task CreateDuplicateVersionAsync(int resourceId, int userId); + + /// + /// The publishing. + /// + /// The resource version id. + /// The user id. + void Publishing(int resourceVersionId, int userId); + + /// + /// "Failed to publish". + /// + /// The resource version id. + /// The user id. + void FailedToPublish(int resourceVersionId, int userId); + + /// + /// The submit publishing. + /// + /// The resource version id. + /// The user id. + void SubmitForPublishing(int resourceVersionId, int userId); + + /// + /// Create resource version event. + /// + /// resourceVersionId. + /// resourceVersionEventType. + /// details. + /// user id. + void CreateResourceVersionEvent(int resourceVersionId, ResourceVersionEventTypeEnum resourceVersionEventType, string details, int userId); + + /// + /// Get Contributions. + /// + /// The userId. + /// The ResourceContributionsRequestViewModel. + /// A list of contributed resources. + List GetContributions(int userId, ResourceContributionsRequestViewModel resourceContributionsRequestViewModel); + + /// + /// The GetResourceVersionForIdList. + /// + /// List of resource version ids. + /// The resource version list. + Task> GetResourceVersionsForSearchSubmission(List resourceVersionIds); + + /// + /// Gets resources for dashboard based on type. + /// + /// The dashboard type. + /// The page Number. + /// The userId. + /// resources. + (int resourceCount, List resources) GetResources(string dashboardType, int pageNumber, int userId); + + /// + /// Copy the blocks from source to destination. + /// + /// The user id. + /// The source block collection id. + /// The destination block collection id. + /// The blocks to be duplicated. + /// The . + Task FractionalDuplication(int userId, int sourceBlockCollectionId, int destinationBlockCollectionId, List blocks); + + /// + /// Gets external content details. + /// + /// resourceVersionId. + /// userId. + /// A representing the result of the asynchronous operation. + Task GetExternalContentDetails(int resourceVersionId, int userId); + + /// + /// To update dev id details. + /// + /// The userId. + /// The resourceVersionDevIdViewModel. + /// The . + Task UpdateDevIdAsync(int userId, ResourceVersionDevIdViewModel resourceVersionDevIdViewModel); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IResourceVersionUserAcceptanceRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IResourceVersionUserAcceptanceRepository.cs new file mode 100644 index 000000000..4b0c1bc5a --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IResourceVersionUserAcceptanceRepository.cs @@ -0,0 +1,11 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Resources +{ + using LearningHub.Nhs.Models.Entities.Resource; + + /// + /// The ResourceVersionUserAcceptanceRepository interface. + /// + public interface IResourceVersionUserAcceptanceRepository : IGenericRepository + { + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IResourceVersionValidationResultRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IResourceVersionValidationResultRepository.cs new file mode 100644 index 000000000..202df4ce1 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IResourceVersionValidationResultRepository.cs @@ -0,0 +1,18 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Resources +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Resource; + + /// + /// The IResourceVersionValidationResultRepository interface. + /// + public interface IResourceVersionValidationResultRepository : IGenericRepository + { + /// + /// The GetByResourceVersionIdAsync. + /// + /// The resourceVersionId. + /// ResourceVersionValidationResult. + Task GetByResourceVersionIdAsync(int resourceVersionId); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IScormResourceVersionRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IScormResourceVersionRepository.cs new file mode 100644 index 000000000..2fefbe739 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IScormResourceVersionRepository.cs @@ -0,0 +1,50 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Resources +{ + using System.Collections.Generic; + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Resource; + using LearningHub.Nhs.Models.Resource; + + /// + /// The ScormResourceVersionRepository interface. + /// + public interface IScormResourceVersionRepository : IGenericRepository + { + /// + /// The get by id async. + /// + /// The id. + /// The . + Task GetByIdAsync(int id); + + /// + /// The get by resource version id async. + /// + /// The id. + /// The includeDeleted. + /// The . + Task GetByResourceVersionIdAsync(int id, bool includeDeleted = false); + + /// + /// Gets the content details for a particular Learning Hub external reference (guid). + /// + /// The external reference (guid). + /// A ContentServerViewModel. + Task GetContentServerDetailsByLHExternalReference(string externalReference); + + /// + /// Gets the SCORM content details for a particular historic external URL. These historic URLs have to be supported to + /// allow historic ESR links on migrated resources to continue to work. + /// + /// The external Url. + /// A ContentServerViewModel. + Task GetScormContentServerDetailsByHistoricExternalUrl(string externalUrl); + + /// + /// GetExternalReferenceByResourceId. + /// + /// Resource id. + /// External Reference. + Task> GetExternalReferenceByResourceId(int resourceId); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IVideoRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IVideoRepository.cs new file mode 100644 index 000000000..26ebf43ad --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IVideoRepository.cs @@ -0,0 +1,18 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Resources +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Resource.Blocks; + + /// + /// The video repository interface. + /// + public interface IVideoRepository : IGenericRepository