Skip to content

Commit 9b600dd

Browse files
authored
Merge pull request #1530 from TechnologyEnhancedLearning/RC
TEST
2 parents f5907e8 + e155788 commit 9b600dd

File tree

178 files changed

+7065
-351
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

178 files changed

+7065
-351
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
namespace LearningHub.Nhs.AdminUI.Configuration
2+
{
3+
/// <summary>
4+
/// The Moodle Settings.
5+
/// </summary>
6+
public class MoodleApiConfig
7+
{
8+
/// <summary>
9+
/// Gets or sets the base url for the Moodle service.
10+
/// </summary>
11+
public string BaseUrl { get; set; } = null!;
12+
13+
/// <summary>
14+
/// Gets or sets the Web service Rest Format.
15+
/// </summary>
16+
public string MoodleWSRestFormat { get; set; } = null!;
17+
18+
/// <summary>
19+
/// Gets or sets the token.
20+
/// </summary>
21+
public string WSToken { get; set; } = null!;
22+
23+
/// <summary>
24+
/// Gets or sets the token.
25+
/// </summary>
26+
public string ApiPath { get; set; } = "webservice/rest/server.php";
27+
28+
/// <summary>
29+
/// Gets or sets the token.
30+
/// </summary>
31+
public string CoursePath { get; set; } = "course/view.php";
32+
}
33+
}

AdminUI/LearningHub.Nhs.AdminUI/Controllers/CatalogueController.cs

Lines changed: 149 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,28 @@
11
namespace LearningHub.Nhs.AdminUI.Controllers
22
{
3-
using System;
4-
using System.Collections.Generic;
5-
using System.ComponentModel.DataAnnotations;
6-
using System.Drawing;
7-
using System.Linq;
8-
using System.Net.Mail;
9-
using System.Text.RegularExpressions;
10-
using System.Threading.Tasks;
113
using LearningHub.Nhs.AdminUI.Configuration;
124
using LearningHub.Nhs.AdminUI.Extensions;
135
using LearningHub.Nhs.AdminUI.Interfaces;
146
using LearningHub.Nhs.AdminUI.Models;
157
using LearningHub.Nhs.Models.Catalogue;
168
using LearningHub.Nhs.Models.Common;
17-
using LearningHub.Nhs.Models.Common.Enums;
9+
using LearningHub.Nhs.Models.Moodle;
1810
using LearningHub.Nhs.Models.Paging;
1911
using LearningHub.Nhs.Models.Resource;
2012
using LearningHub.Nhs.Models.User;
2113
using Microsoft.AspNetCore.Hosting;
2214
using Microsoft.AspNetCore.Http;
2315
using Microsoft.AspNetCore.Mvc;
16+
using Microsoft.AspNetCore.Mvc.Rendering;
2417
using Microsoft.Extensions.Logging;
2518
using Microsoft.Extensions.Options;
19+
using System;
20+
using System.Collections.Generic;
21+
using System.Drawing;
22+
using System.Linq;
23+
using System.Net;
24+
using System.Text.RegularExpressions;
25+
using System.Threading.Tasks;
2626

2727
/// <summary>
2828
/// Defines the <see cref="CatalogueController" />.
@@ -65,6 +65,11 @@ public class CatalogueController : BaseController
6565
/// </summary>
6666
private readonly IProviderService providerService;
6767

68+
/// <summary>
69+
/// Defines the moodleApiService.
70+
/// </summary>
71+
private readonly IMoodleApiService moodleApiService;
72+
6873
/// <summary>
6974
/// Defines the _settings.
7075
/// </summary>
@@ -98,6 +103,7 @@ public class CatalogueController : BaseController
98103
/// <param name="userGroupService">The userService<see cref="IUserGroupService"/>.</param>
99104
/// <param name="fileService">The fileService<see cref="IFileService"/>.</param>
100105
/// <param name="providerService">The providerService<see cref="IProviderService"/>.</param>
106+
/// <param name="moodleApiService">The moodleApiService<see cref="IMoodleApiService"/>.</param>
101107
/// <param name="logger">The logger<see cref="ILogger{LogController}"/>.</param>
102108
/// <param name="options">The options<see cref="IOptions{WebSettings}"/>.</param>
103109
/// <param name="websettings">The websettings<see cref="IOptions{WebSettings}"/>.</param>
@@ -107,6 +113,7 @@ public CatalogueController(
107113
IUserGroupService userGroupService,
108114
IFileService fileService,
109115
IProviderService providerService,
116+
IMoodleApiService moodleApiService,
110117
ILogger<LogController> logger,
111118
IOptions<WebSettings> options,
112119
IOptions<WebSettings> websettings)
@@ -116,6 +123,7 @@ public CatalogueController(
116123
this.userGroupService = userGroupService;
117124
this.fileService = fileService;
118125
this.providerService = providerService;
126+
this.moodleApiService = moodleApiService;
119127
this.logger = logger;
120128
this.websettings = websettings;
121129
this.settings = options.Value;
@@ -254,6 +262,38 @@ public async Task<IActionResult> CatalogueOwner(int id)
254262
return this.View(vm);
255263
}
256264

265+
/// <summary>
266+
/// The CatalogueOwner.
267+
/// </summary>
268+
/// <param name="id">The id<see cref="int"/>.</param>
269+
/// <returns>The <see cref="Task{IActionResult}"/>.</returns>
270+
[HttpGet]
271+
[Route("MoodleCategory/{id}")]
272+
public async Task<IActionResult> MoodleCategory(int id)
273+
{
274+
var vm = await this.catalogueService.GetCatalogueAsync(id);
275+
if (vm == null)
276+
{
277+
return this.RedirectToAction("Error");
278+
}
279+
280+
var categories = await this.moodleApiService.GetAllMoodleCategoriesAsync();
281+
282+
vm.MoodleCategories = categories;
283+
284+
// Build hierarchical select list
285+
var selectList = BuildList(categories, parentId: null, depth: 0);
286+
foreach (var item in selectList)
287+
{
288+
item.Text = WebUtility.HtmlDecode(item.Text);
289+
}
290+
vm.MoodleCategorySelectList = new SelectList(selectList, "Value", "Text");
291+
this.ViewData["CatalogueName"] = vm.Name;
292+
this.ViewData["id"] = id;
293+
294+
return this.View(vm);
295+
}
296+
257297
/// <summary>
258298
/// The UserGroups.
259299
/// </summary>
@@ -678,6 +718,77 @@ public async Task<IActionResult> AddUserGroupsToCatalogue(int catalogueNodeId, i
678718
}
679719
}
680720

721+
/// <summary>
722+
/// The AddCategoryToCatalogue.
723+
/// </summary>
724+
/// <param name="catalogueViewModel">The catalogueViewModel<see cref="CatalogueViewModel"/>.</param>
725+
/// <returns>The <see cref="Task{IActionResult}"/>.</returns>
726+
[HttpPost]
727+
[Route("AddCategoryToCatalogue")]
728+
public async Task<IActionResult> AddCategoryToCatalogue(CatalogueViewModel catalogueViewModel)
729+
{
730+
if (catalogueViewModel.SelectedCategoryId == 0)
731+
{
732+
this.ModelState.AddModelError("SelectedCategoryId", "Please select a category.");
733+
}
734+
var vm = await this.catalogueService.GetCatalogueAsync(catalogueViewModel.CatalogueNodeVersionId);
735+
vm.SelectedCategoryId = catalogueViewModel.SelectedCategoryId;
736+
var vr = await this.catalogueService.AddCategoryToCatalogue(vm);
737+
if (vr.Success)
738+
{
739+
var categories = await this.moodleApiService.GetAllMoodleCategoriesAsync();
740+
vm.MoodleCategories = categories;
741+
// Build hierarchical select list
742+
var selectList = BuildList(categories, parentId: null, depth: 0);
743+
744+
foreach (var item in selectList)
745+
{
746+
item.Text = WebUtility.HtmlDecode(item.Text);
747+
}
748+
749+
vm.MoodleCategorySelectList = new SelectList(selectList, "Value", "Text");
750+
return this.View("MoodleCategory", vm);
751+
}
752+
else
753+
{
754+
this.ViewBag.ErrorMessage = $"Category Update failed.";
755+
return this.View("MoodleCategory", vm);
756+
}
757+
}
758+
759+
/// <summary>
760+
/// The RemoveCategoryFromCatalogue.
761+
/// </summary>
762+
/// <param name="categoryId">The categoryId/>.</param>
763+
/// <param name="catalogueNodeVersionId">The CatalogueNodeVersionId.</param>
764+
/// <returns>The <see cref="Task{IActionResult}"/>.</returns>
765+
[HttpGet]
766+
[Route("RemoveCategoryFromCatalogue/{categoryId}/{catalogueNodeVersionId}")]
767+
public async Task<IActionResult> RemoveCategoryFromCatalogue(int categoryId, int catalogueNodeVersionId)
768+
{
769+
var vm = await this.catalogueService.GetCatalogueAsync(catalogueNodeVersionId);
770+
vm.SelectedCategoryId = categoryId;
771+
var vr = await this.catalogueService.RemoveCategoryFromCatalogue(vm);
772+
if (vr.Success)
773+
{
774+
var categories = await this.moodleApiService.GetAllMoodleCategoriesAsync();
775+
vm.MoodleCategories = categories;
776+
vm.SelectedCategoryId = 0;
777+
// Build hierarchical select list
778+
var selectList = BuildList(categories, parentId: null, depth: 0);
779+
foreach (var item in selectList)
780+
{
781+
item.Text = WebUtility.HtmlDecode(item.Text);
782+
}
783+
vm.MoodleCategorySelectList = new SelectList(selectList, "Value", "Text");
784+
return this.View("MoodleCategory", vm);
785+
}
786+
else
787+
{
788+
this.ViewBag.ErrorMessage = $"Category update failed.";
789+
return this.View("MoodleCategory", vm);
790+
}
791+
}
681792
/// <summary>
682793
/// The CreateCatalogue.
683794
/// </summary>
@@ -978,5 +1089,34 @@ private void ValidateCatalogueOwnerVm(CatalogueOwnerViewModel vm)
9781089
this.ModelState.AddModelError("Notes", "The notes are required.");
9791090
}
9801091
}
1092+
1093+
private List<SelectListItem> BuildList(IEnumerable<MoodleCategory> allCategories, int? parentId, int depth)
1094+
{
1095+
var selectList = new List<SelectListItem>();
1096+
1097+
// Handle both null and 0 as top-level depending on Moodle data
1098+
var children = allCategories
1099+
.Where(c => c.Parent == parentId || (parentId == null && (c.Parent == 0 || c.Parent == 0)))
1100+
.OrderBy(c => c.Name)
1101+
.ToList();
1102+
1103+
foreach (var child in children)
1104+
{
1105+
// Indent with non-breaking spaces so browser keeps them
1106+
string indent = new string('\u00A0', depth * 3);
1107+
1108+
selectList.Add(new SelectListItem
1109+
{
1110+
Value = child.Id.ToString(),
1111+
Text = $"{indent}{child.Name}"
1112+
});
1113+
1114+
// Recursively add nested children
1115+
selectList.AddRange(BuildList(allCategories, child.Id, depth + 1));
1116+
}
1117+
1118+
return selectList;
1119+
}
1120+
9811121
}
9821122
}

AdminUI/LearningHub.Nhs.AdminUI/Interfaces/ICatalogueService.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,5 +69,19 @@ public interface ICatalogueService
6969
/// <param name="catalogueOwner">The catalogue owner<see cref="CatalogueOwnerViewModel"/>.</param>
7070
/// <returns>The <see cref="Task"/>.</returns>
7171
Task<ApiResponse> UpdateCatalogueOwnerAsync(CatalogueOwnerViewModel catalogueOwner);
72+
73+
/// <summary>
74+
/// AddCategoryToCatalogue
75+
/// </summary>
76+
/// <param name="catalogue">The catalogue.</param>
77+
/// <returns></returns>
78+
Task<ApiResponse> AddCategoryToCatalogue(CatalogueViewModel catalogue);
79+
80+
/// <summary>
81+
/// RemoveCategoryFromCatalogue
82+
/// </summary>
83+
/// <param name="catalogue">The catalogue.</param>
84+
/// <returns></returns>
85+
Task<ApiResponse> RemoveCategoryFromCatalogue(CatalogueViewModel catalogue);
7286
}
7387
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
namespace LearningHub.Nhs.AdminUI.Interfaces
2+
{
3+
using System.Collections.Generic;
4+
using System.Threading.Tasks;
5+
using LearningHub.Nhs.Models.Moodle;
6+
using LearningHub.Nhs.Models.Moodle.API;
7+
8+
/// <summary>
9+
/// IMoodleApiService.
10+
/// </summary>
11+
public interface IMoodleApiService
12+
{
13+
/// <summary>
14+
/// GetMoodleUserIdByUsernameAsync.
15+
/// </summary>
16+
/// <param name="currentUserId">The current LH User Id.</param>
17+
/// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
18+
Task<int> GetMoodleUserIdByUsernameAsync(int currentUserId);
19+
20+
/// <summary>
21+
/// GetEnrolledCoursesAsync.
22+
/// </summary>
23+
/// <returns> List of MoodleCategory.</returns>
24+
Task<List<MoodleCategory>> GetAllMoodleCategoriesAsync();
25+
}
26+
}

AdminUI/LearningHub.Nhs.AdminUI/LearningHub.Nhs.AdminUI.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@
8989
<PackageReference Include="HtmlSanitizer" Version="6.0.453" />
9090
<PackageReference Include="IdentityModel" Version="4.6.0" />
9191
<PackageReference Include="LearningHub.Nhs.Caching" Version="2.0.2" />
92-
<PackageReference Include="LearningHub.Nhs.Models" Version="4.0.2" />
92+
<PackageReference Include="LearningHub.Nhs.Models" Version="4.0.3" />
9393
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.19.0" />
9494
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="6.0.36" />
9595
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.36" />

AdminUI/LearningHub.Nhs.AdminUI/Models/CatalogueNavViewModel.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ public enum CatalogueNavPage
3434
/// Defines the Catalogue Owner.
3535
/// </summary>
3636
CatalogueOwner,
37+
38+
/// <summary>
39+
/// Defines the Category.
40+
/// </summary>
41+
Category
3742
}
3843

3944
/// <summary>

AdminUI/LearningHub.Nhs.AdminUI/ServiceCollectionExtension.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ public static void ConfigureServices(this IServiceCollection services, IConfigur
106106
services.AddTransient<IUserSessionHelper, UserSessionHelper>();
107107
services.AddTransient<IInternalSystemService, InternalSystemService>();
108108
services.AddScoped<IProviderService, ProviderService>();
109+
services.AddScoped<IMoodleApiService, MoodleApiService>();
109110

110111
// web settings binding
111112
var webSettings = new WebSettings();

AdminUI/LearningHub.Nhs.AdminUI/Services/CatalogueService.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
namespace LearningHub.Nhs.AdminUI.Services
22
{
3+
using System;
34
using System.Collections.Generic;
45
using System.Net;
6+
using System.Net.Http;
7+
using System.Text;
58
using System.Threading.Tasks;
69
using LearningHub.Nhs.AdminUI.Helpers;
710
using LearningHub.Nhs.AdminUI.Interfaces;
811
using LearningHub.Nhs.Models.Catalogue;
912
using LearningHub.Nhs.Models.Common;
13+
using LearningHub.Nhs.Models.Enums;
1014
using LearningHub.Nhs.Models.Paging;
1115
using LearningHub.Nhs.Models.Provider;
16+
using LearningHub.Nhs.Models.User;
17+
using LearningHub.Nhs.Models.Validation;
1218
using Newtonsoft.Json;
1319

1420
/// <summary>
@@ -132,5 +138,25 @@ public async Task<ApiResponse> UpdateCatalogueOwnerAsync(CatalogueOwnerViewModel
132138
{
133139
return await this.facade.PutAsync("Catalogue/UpdateCatalogueOwner", catalogueOwner);
134140
}
141+
142+
/// <summary>
143+
/// The AddUserGroupsToCatalogue.
144+
/// </summary>
145+
/// <param name="catalogue">The CatalogueViewModel.</param>
146+
/// <returns>The <see cref="Task{IActionResult}"/>.</returns>
147+
public async Task<ApiResponse> AddCategoryToCatalogue(CatalogueViewModel catalogue)
148+
{
149+
return await this.facade.PostAsync<ApiResponse, CatalogueViewModel>("Catalogue/AddCategoryToCatalogue", catalogue);
150+
}
151+
152+
/// <summary>
153+
/// The RemoveCategoryFromCatalogue.
154+
/// </summary>
155+
/// <param name="catalogue">The CatalogueViewModel.</param>
156+
/// <returns>The <see cref="Task{IActionResult}"/>.</returns>
157+
public async Task<ApiResponse> RemoveCategoryFromCatalogue(CatalogueViewModel catalogue)
158+
{
159+
return await this.facade.PostAsync<ApiResponse, CatalogueViewModel>("Catalogue/RemoveCategoryFromCatalogue", catalogue);
160+
}
135161
}
136162
}

0 commit comments

Comments
 (0)