diff --git a/LearningHub.Nhs.WebUI/Controllers/Api/HierarchyController.cs b/LearningHub.Nhs.WebUI/Controllers/Api/HierarchyController.cs index 082a75fdc..82c21438c 100644 --- a/LearningHub.Nhs.WebUI/Controllers/Api/HierarchyController.cs +++ b/LearningHub.Nhs.WebUI/Controllers/Api/HierarchyController.cs @@ -109,6 +109,19 @@ public async Task GetNodeContentsForCatalogueEditor(int nodePathI return this.Ok(viewModels); } + /// + /// Check Catalogue has external reference. + /// + /// The node id. + /// The . + [HttpGet] + [Route("CheckCatalogueHasExternalReference/{nodeId}")] + public async Task CheckCatalogueHasExternalReference(int nodeId) + { + var val = await this.hierarchyService.CheckCatalogueHasExternalReference(nodeId); + return val; + } + /// /// Gets the contents of a node path (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. diff --git a/LearningHub.Nhs.WebUI/Interfaces/IHierarchyService.cs b/LearningHub.Nhs.WebUI/Interfaces/IHierarchyService.cs index 80c2703c1..cb49c8d33 100644 --- a/LearningHub.Nhs.WebUI/Interfaces/IHierarchyService.cs +++ b/LearningHub.Nhs.WebUI/Interfaces/IHierarchyService.cs @@ -236,5 +236,12 @@ public interface IHierarchyService /// The referenceExternalResourceViewModel. /// IActionResult. Task HierarchyEditReferenceExternalResource(ReferenceExternalResourceViewModel referenceExternalResourceViewModel); + + /// + /// Check catalogue has external reference. + /// + /// The nodeId. + /// The . + Task CheckCatalogueHasExternalReference(int nodeId); } } diff --git a/LearningHub.Nhs.WebUI/Scripts/vuesrc/content-structure-editor/contentStructureState.ts b/LearningHub.Nhs.WebUI/Scripts/vuesrc/content-structure-editor/contentStructureState.ts index 9fd7f2ad6..17934f2a3 100644 --- a/LearningHub.Nhs.WebUI/Scripts/vuesrc/content-structure-editor/contentStructureState.ts +++ b/LearningHub.Nhs.WebUI/Scripts/vuesrc/content-structure-editor/contentStructureState.ts @@ -23,6 +23,7 @@ export class State { updatedNode: NodeContentEditorModel = null; inError: boolean = false; lastErrorMessage: string = ""; + hasExternalReference: boolean = false; } const state = new State(); @@ -51,6 +52,13 @@ function loadNodeContents(state: State) { state.rootNode.childrenLoaded = false; state.rootNode.inEdit = state.editMode == EditModeEnum.Structure; state.rootNode.showInTreeView = false; + }).then(async y => { + await contentStructureData.checkCatalogueHasExternalReference(state.catalogue.nodeId).then(response => { + state.hasExternalReference = response; + }) + .catch(e => { + console.log(e); + }); }).then(async y => { await contentStructureData.getNodeContentsForCatalogueEditor(state.catalogue.rootNodePathId).then(response => { state.rootNode.children = response; diff --git a/LearningHub.Nhs.WebUI/Scripts/vuesrc/content-structure-editor/treeItem.vue b/LearningHub.Nhs.WebUI/Scripts/vuesrc/content-structure-editor/treeItem.vue index 6928b9823..251ec5ed6 100644 --- a/LearningHub.Nhs.WebUI/Scripts/vuesrc/content-structure-editor/treeItem.vue +++ b/LearningHub.Nhs.WebUI/Scripts/vuesrc/content-structure-editor/treeItem.vue @@ -82,9 +82,9 @@ @@ -248,6 +248,9 @@ } else return 0; }, + hasExternalCatalogueReference(): boolean { + return this.$store.state.contentStructureState.hasExternalReference; + }, }, mounted() { Vue.set(this, "isVisible", this.item.showInTreeView); @@ -288,7 +291,7 @@ } } } - + // Load the data. if (!this.isOpen && (!this.item.childrenLoaded || this.isError)) { this.isOpen = true; diff --git a/LearningHub.Nhs.WebUI/Scripts/vuesrc/data/contentStructure.ts b/LearningHub.Nhs.WebUI/Scripts/vuesrc/data/contentStructure.ts index f751df246..f07d98823 100644 --- a/LearningHub.Nhs.WebUI/Scripts/vuesrc/data/contentStructure.ts +++ b/LearningHub.Nhs.WebUI/Scripts/vuesrc/data/contentStructure.ts @@ -42,7 +42,16 @@ const getNodeContentsForCatalogueEditor = async function (nodePathId: number): P throw e; }); }; - +const checkCatalogueHasExternalReference = async function (nodeId: number): Promise { + return await axios.get('/api/hierarchy/CheckCatalogueHasExternalReference/' + nodeId + `?timestamp=${new Date().getTime()}`) + .then(response => { + return response.data; + }) + .catch(e => { + console.log('CheckCatalogueHasExternalReference:' + e); + throw e; + }); +}; const getNodeContentsAdmin = async function (nodePathId: number, readOnly: boolean): Promise { return await axios.get('/api/hierarchy/GetNodeContentsAdmin/' + nodePathId + '/' + readOnly + `?timestamp=${new Date().getTime()}`) .then(response => { @@ -464,5 +473,6 @@ export const contentStructureData = { getReferencableCatalogues, hierarchyEditReferenceExternalResource, referenceExternalNode, - removeReferenceNode + removeReferenceNode, + checkCatalogueHasExternalReference } diff --git a/LearningHub.Nhs.WebUI/Services/HierarchyService.cs b/LearningHub.Nhs.WebUI/Services/HierarchyService.cs index fb8c241b4..63b80561e 100644 --- a/LearningHub.Nhs.WebUI/Services/HierarchyService.cs +++ b/LearningHub.Nhs.WebUI/Services/HierarchyService.cs @@ -1,5 +1,6 @@ namespace LearningHub.Nhs.WebUI.Services { + using System; using System.Collections.Generic; using System.Threading.Tasks; using LearningHub.Nhs.Models.Common; @@ -337,5 +338,33 @@ public async Task HierarchyEditReferenceExternalResource(ReferenceE { return await this.facade.PostAsync("Hierarchy/HierarchyEditReferenceExternalResource", referenceExternalResourceViewModel); } + + /// + /// Check catalogue has external reference. + /// + /// nodeId. + /// IActionResult. + public async Task CheckCatalogueHasExternalReference(int nodeId) + { + var request = $"Hierarchy/CheckCatalogueHasExternalReference/{nodeId}"; + + var client = await this.LearningHubHttpClient.GetClientAsync(); + var response = await client.GetAsync(request).ConfigureAwait(false); + var hasExternalCatalogueReference = false; + + if (response.IsSuccessStatusCode) + { + var result = await response.Content.ReadAsStringAsync(); + hasExternalCatalogueReference = bool.Parse(result); + } + else if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized + || + response.StatusCode == System.Net.HttpStatusCode.Forbidden) + { + throw new Exception("AccessDenied"); + } + + return hasExternalCatalogueReference; + } } } diff --git a/WebAPI/LearningHub.Nhs.API/Controllers/HierarchyController.cs b/WebAPI/LearningHub.Nhs.API/Controllers/HierarchyController.cs index 10f377e71..484f32e58 100644 --- a/WebAPI/LearningHub.Nhs.API/Controllers/HierarchyController.cs +++ b/WebAPI/LearningHub.Nhs.API/Controllers/HierarchyController.cs @@ -103,6 +103,18 @@ public async Task GetNodeContentsForCatalogueEditor(int nodePathI return this.Ok(await this.hierarchyService.GetNodeContentsForCatalogueEditor(nodePathId)); } + /// + /// Check Catalogue has external reference. + /// + /// The node id. + /// The . + [HttpGet] + [Route("checkCatalogueHasExternalReference/{nodeId}")] + public async Task CheckCatalogueHasExternalReference(int nodeId) + { + return this.Ok(await this.hierarchyService.CheckCatalogueHasExternalReference(nodeId)); + } + /// /// Gets the contents of a node path (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. diff --git a/WebAPI/LearningHub.Nhs.Database/LearningHub.Nhs.Database.sqlproj b/WebAPI/LearningHub.Nhs.Database/LearningHub.Nhs.Database.sqlproj index 2884fa678..e9164f283 100644 --- a/WebAPI/LearningHub.Nhs.Database/LearningHub.Nhs.Database.sqlproj +++ b/WebAPI/LearningHub.Nhs.Database/LearningHub.Nhs.Database.sqlproj @@ -543,6 +543,7 @@ + diff --git a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Hierarchy/CheckCatalogueHasExternalReference.sql b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Hierarchy/CheckCatalogueHasExternalReference.sql new file mode 100644 index 000000000..fe21cbdd5 --- /dev/null +++ b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Hierarchy/CheckCatalogueHasExternalReference.sql @@ -0,0 +1,98 @@ +------------------------------------------------------------------------------- +-- Author Sarathlal +-- Created 29 August 2024 +-- Purpose Return if Node has an external reference +-- +-- Modification History +-- +-- 29-08-2024 SS Initial Revision. + +------------------------------------------------------------------------------- +CREATE PROCEDURE [hierarchy].[CheckCatalogueHasExternalReference] +( + @CatalogueNodeId int, + @HasExternalCatalogueReference bit OUTPUT +) +AS + +BEGIN + ;WITH + cteNode(NodeId, ParentNodeId, PrimaryCatalogueNodeId, InitialNodePath) + AS + ( + SELECT + n.Id AS NodeId, + ParentNodeId = NULL, + nv.PrimaryCatalogueNodeId, + CAST(n.Id AS nvarchar(128)) AS InitialNodePath + FROM + hierarchy.NodePath np + INNER JOIN + hierarchy.[Node] n ON np.NodeId = n.Id + INNER JOIN + hierarchy.NodeVersion nv ON n.CurrentNodeVersionId = nv.Id + WHERE + nv.NodeId=@catalogueNodeId + AND nv.VersionStatusId = 2 -- Published + AND np.Deleted = 0 + AND n.Deleted = 0 + AND nv.Deleted = 0 + + UNION ALL + + SELECT + ChildNodeId AS NodeId, + nl.ParentNodeId, + nv.PrimaryCatalogueNodeId, + CAST(cte.InitialNodePath + '\' + CAST(ChildNodeId AS nvarchar(8)) AS nvarchar(128)) AS InitialNodePath + FROM + hierarchy.NodeLink nl + INNER JOIN + hierarchy.[Node] n ON nl.ChildNodeId = n.Id + INNER JOIN + cteNode cte ON nl.ParentNodeId = cte.NodeId + INNER JOIN + hierarchy.NodeVersion nv ON n.CurrentNodeVersionId = nv.Id + WHERE + n.CurrentNodeVersionId IS NOT NULL + AND nv.VersionStatusId = 2 -- Published + AND n.Deleted = 0 + AND nl.Deleted = 0 + + ), + + cteResource + AS ( + SELECT cte.PrimaryCatalogueNodeId + FROM + cteNode cte + UNION + + SELECT + rv.PrimaryCatalogueNodeId AS PrimaryCatalogueNodeId + FROM + hierarchy.NodePath np + INNER JOIN + hierarchy.NodeResource nr ON np.NodeId = nr.NodeId + INNER JOIN + resources.[Resource] r ON nr.ResourceId = r.Id + INNER JOIN + resources.ResourceVersion rv ON rv.resourceId = nr.ResourceId + INNER JOIN + resources.ResourceReference rr ON rr.ResourceId = nr.ResourceId AND rr.NodePathId = np.Id AND rr.Deleted = 0 + + WHERE + np.CatalogueNodeId = @CatalogueNodeId + and rv.PrimaryCatalogueNodeId <>np.CatalogueNodeId + AND r.CurrentResourceVersionId IS not NULL + AND nr.VersionStatusId=2 + AND nr.Deleted = 0 + AND r.Deleted = 0 + AND rv.Deleted = 0 + ) + SELECT @HasExternalCatalogueReference=case when count(distinct cte.PrimaryCatalogueNodeId ) > 1 then 1 ELSE 0 END + + FROM + cteResource cte +END +GO \ No newline at end of file diff --git a/WebAPI/LearningHub.Nhs.Repository.Interface/Hierarchy/INodeRepository.cs b/WebAPI/LearningHub.Nhs.Repository.Interface/Hierarchy/INodeRepository.cs index 6d48e68f6..403614480 100644 --- a/WebAPI/LearningHub.Nhs.Repository.Interface/Hierarchy/INodeRepository.cs +++ b/WebAPI/LearningHub.Nhs.Repository.Interface/Hierarchy/INodeRepository.cs @@ -50,5 +50,12 @@ public interface INodeRepository : IGenericRepository /// Set to true if read only data set is required. /// The . Task> GetNodeContentsAdminAsync(int nodeId, bool readOnly); + + /// + /// Check catalogue has external reference. + /// + /// The node id. + /// The . + Task CheckCatalogueHasExternalReference(int nodeId); } } diff --git a/WebAPI/LearningHub.Nhs.Repository/Hierarchy/NodeRepository.cs b/WebAPI/LearningHub.Nhs.Repository/Hierarchy/NodeRepository.cs index c840d64db..55743fc26 100644 --- a/WebAPI/LearningHub.Nhs.Repository/Hierarchy/NodeRepository.cs +++ b/WebAPI/LearningHub.Nhs.Repository/Hierarchy/NodeRepository.cs @@ -86,6 +86,23 @@ public async Task> GetNodeContentsForCatalogueE return retVal; } + /// + /// Check catalogue has external reference. + /// + /// The node path id. + /// The . + public async Task CheckCatalogueHasExternalReference(int nodeId) + { + var param0 = new SqlParameter("@p0", SqlDbType.Int) { Value = nodeId }; + var param1 = new SqlParameter("@p1", SqlDbType.Bit) { Direction = ParameterDirection.Output }; + string sql = "hierarchy.CheckCatalogueHasExternalReference @p0, @p1 output"; + var sqlParams = new List() { param0, param1 }; + + await this.DbContext.Database.ExecuteSqlRawAsync(sql, sqlParams); + + return (bool)param1.Value; + } + /// /// Gets the contents of a node path (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. diff --git a/WebAPI/LearningHub.Nhs.Services.Interface/IHierarchyService.cs b/WebAPI/LearningHub.Nhs.Services.Interface/IHierarchyService.cs index 93d6595c0..1883b121a 100644 --- a/WebAPI/LearningHub.Nhs.Services.Interface/IHierarchyService.cs +++ b/WebAPI/LearningHub.Nhs.Services.Interface/IHierarchyService.cs @@ -293,5 +293,12 @@ public interface IHierarchyService /// The nodeId. /// The . Task> GetNodePathsForNodeAsync(int nodeId); + + /// + /// Check catalogue has external reference. + /// + /// The nodeId. + /// The . + Task CheckCatalogueHasExternalReference(int nodeId); } } diff --git a/WebAPI/LearningHub.Nhs.Services/HierarchyService.cs b/WebAPI/LearningHub.Nhs.Services/HierarchyService.cs index 80f5085f8..314419003 100644 --- a/WebAPI/LearningHub.Nhs.Services/HierarchyService.cs +++ b/WebAPI/LearningHub.Nhs.Services/HierarchyService.cs @@ -348,6 +348,19 @@ public async Task> GetNodeContentsForCatalogueE return vm; } + /// + /// Check catalogue has external reference. + /// + /// The node path id. + /// The . + public async Task CheckCatalogueHasExternalReference(int nodeId) + { + // Not cached, retrieve directly from the database. + var vm = await this.nodeRepository.CheckCatalogueHasExternalReference(nodeId); + + return vm; + } + /// /// Gets the contents of a node path (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.