Skip to content

Commit 2f44a47

Browse files
authored
Merge pull request #649 from TechnologyEnhancedLearning/MergeRC-abelia3
Merge RC Changes to Abelia branch
2 parents 1256cb2 + 0c71eea commit 2f44a47

File tree

67 files changed

+1457
-295
lines changed

Some content is hidden

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

67 files changed

+1457
-295
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,7 @@ obj
4848
/WebAPI/MigrationTool/LearningHub.Nhs.Migration.Staging.Database/LearningHub.Nhs.Migration.Staging.Database.dbmdl
4949
/WebAPI/MigrationTool/LearningHub.Nhs.Migration.Staging.Database/LearningHub.Nhs.Migration.Staging.Database.jfm
5050
/LearningHub.Nhs.WebUI.AutomatedUiTests/appsettings.Development.json
51+
/OpenAPI/LearningHub.Nhs.OpenApi/appsettings.Development.json
52+
/OpenAPI/LearningHub.Nhs.OpenApi/web.config
53+
/AdminUI/LearningHub.Nhs.AdminUI/LearningHub.Nhs.AdminUI.csproj.user
54+
/WebAPI/LearningHub.Nhs.API/LearningHub.Nhs.Api.csproj.user

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.4.0" />
9191
<PackageReference Include="LearningHub.Nhs.Caching" Version="2.0.2" />
92-
<PackageReference Include="LearningHub.Nhs.Models" Version="3.0.39" />
92+
<PackageReference Include="LearningHub.Nhs.Models" Version="3.0.40" />
9393
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.19.0" />
9494
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="6.0.0" />
9595
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.0" />

AdminUI/LearningHub.Nhs.AdminUI/Views/Catalogue/Edit.cshtml

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,11 @@
162162
<span class="input-validation-error keyword-maxlength" style="display: none;">The maximum length is 50 characters.</span>
163163
</div>
164164
</div>
165+
<div class="row">
166+
<div class="col-12">
167+
<span id="keyword-error-span" class="field-validation-error" style="display: none;"></span>
168+
</div>
169+
</div>
165170

166171
</div>
167172
<div class="col-12">
@@ -341,6 +346,9 @@
341346
} else {
342347
$('#add-keyword').removeAttr('disabled');
343348
}
349+
350+
$('#keyword-error-span').hide();
351+
$('#keyword-error-span').html('');
344352
});
345353
346354
$('#add-keyword').on('click', function () {
@@ -352,9 +360,11 @@
352360
353361
// Split the input value by commas and trim each keyword
354362
var values = value.split(',').map(function (item) {
355-
return item.trim();
363+
return item.trim().toLowerCase();
356364
});
357365
366+
var duplicateKeywords = [];
367+
$('#keyword-error-span').hide();
358368
values.forEach(function (value) {
359369
if (value && keywords.indexOf(value) === -1) {
360370
keywords.push(value);
@@ -368,9 +378,15 @@
368378
$(x).attr('name', "Keywords[" + i + "]");
369379
});
370380
}
381+
else
382+
{
383+
duplicateKeywords.push(value);
384+
$('#keyword-error-span').show();
385+
$('#keyword-error-span').html('The keyword(s) have already been added : ' + duplicateKeywords.join(', '))
386+
}
371387
});
372388
373-
$keywordInput.val("");
389+
$keywordInput.val("");
374390
if (keywords.length > 4) {
375391
$('#add-keyword').attr('disabled', 'disabled');
376392
$('#add-keyword-input').attr('disabled', 'disabled');
@@ -441,4 +457,4 @@
441457
442458
</script>
443459
<script src="~/js/imageInput.js"></script>
444-
}
460+
}

LearningHub.Nhs.WebUI/Controllers/CatalogueController.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ public async Task<IActionResult> Index(int pageIndex = 1, string term = null)
110110
});
111111

112112
catalogues.TotalCount = termCatalogues.TotalHits;
113+
catalogues.GroupId = Guid.NewGuid();
113114
catalogues.Catalogues = termCatalogues.DocumentModel.Select(t => new DashboardCatalogueViewModel
114115
{
115116
Url = t.Url,
@@ -124,6 +125,7 @@ public async Task<IActionResult> Index(int pageIndex = 1, string term = null)
124125
NodeId = int.Parse(t.Id),
125126
BadgeUrl = t.BadgeUrl,
126127
Providers = t.Providers,
128+
ClickPayload = t.Click.Payload,
127129
}).ToList();
128130
}
129131
else

LearningHub.Nhs.WebUI/Controllers/LearningSessionsController.cs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ public async Task<IActionResult> Scorm(int id)
6868
/// <param name="filePath">filePath.</param>
6969
/// <returns>bool.</returns>
7070
//// [ResponseCache(VaryByQueryKeys = new[] { "*" }, Duration = 0, NoStore = true)] // disable caching
71+
//// Removed Request.Headers["Referer"] Referer URL checking based on issue reported in TD-4283
7172
[AllowAnonymous]
7273
[Route("ScormContent/{*filePath}")]
7374
public async Task<IActionResult> ScormContent(string filePath)
@@ -79,12 +80,6 @@ public async Task<IActionResult> ScormContent(string filePath)
7980

8081
try
8182
{
82-
var referringUrl = this.Request.Headers["Referer"].ToString();
83-
if (string.IsNullOrEmpty(referringUrl))
84-
{
85-
throw new UnauthorizedAccessException("Referer URL is required.");
86-
}
87-
8883
if (!this.User.Identity.IsAuthenticated)
8984
{
9085
throw new UnauthorizedAccessException("User is not authenticated.");

LearningHub.Nhs.WebUI/Controllers/SearchController.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,8 +214,9 @@ public async Task<IActionResult> RecordCatalogueNavigation(SearchRequestViewMode
214214
/// <param name="timeOfSearch">time of search.</param>
215215
/// <param name="userQuery">user query.</param>
216216
/// <param name="query">search query.</param>
217+
/// <param name="title">the title.</param>
217218
[HttpGet("record-resource-click")]
218-
public void RecordResourceClick(string url, int nodePathId, int itemIndex, int pageIndex, int totalNumberOfHits, string searchText, int resourceReferenceId, Guid groupId, string searchId, long timeOfSearch, string userQuery, string query)
219+
public void RecordResourceClick(string url, int nodePathId, int itemIndex, int pageIndex, int totalNumberOfHits, string searchText, int resourceReferenceId, Guid groupId, string searchId, long timeOfSearch, string userQuery, string query, string title)
219220
{
220221
var searchActionResourceModel = new SearchActionResourceModel
221222
{
@@ -230,6 +231,7 @@ public void RecordResourceClick(string url, int nodePathId, int itemIndex, int p
230231
TimeOfSearch = timeOfSearch,
231232
UserQuery = userQuery,
232233
Query = query,
234+
Title = title,
233235
};
234236

235237
this.searchService.CreateResourceSearchActionAsync(searchActionResourceModel);
@@ -251,9 +253,10 @@ public void RecordResourceClick(string url, int nodePathId, int itemIndex, int p
251253
/// <param name="timeOfSearch">time of search.</param>
252254
/// <param name="userQuery">user query.</param>
253255
/// <param name="query">search query.</param>
256+
/// <param name="name">the name.</param>
254257
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
255258
[HttpGet("record-catalogue-click")]
256-
public async Task<IActionResult> RecordCatalogueClick(string url, int nodePathId, int itemIndex, int pageIndex, int totalNumberOfHits, string searchText, int catalogueId, Guid groupId, string searchId, long timeOfSearch, string userQuery, string query)
259+
public async Task<IActionResult> RecordCatalogueClick(string url, int nodePathId, int itemIndex, int pageIndex, int totalNumberOfHits, string searchText, int catalogueId, Guid groupId, string searchId, long timeOfSearch, string userQuery, string query, string name)
257260
{
258261
SearchActionCatalogueModel searchActionCatalogueModel = new SearchActionCatalogueModel
259262
{
@@ -268,6 +271,7 @@ public async Task<IActionResult> RecordCatalogueClick(string url, int nodePathId
268271
TimeOfSearch = timeOfSearch,
269272
UserQuery = userQuery,
270273
Query = query,
274+
Name = name,
271275
};
272276

273277
await this.searchService.CreateCatalogueSearchActionAsync(searchActionCatalogueModel);

LearningHub.Nhs.WebUI/LearningHub.Nhs.WebUI.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@
108108
<PackageReference Include="HtmlAgilityPack" Version="1.11.38" />
109109
<PackageReference Include="IdentityModel" Version="4.3.0" />
110110
<PackageReference Include="LearningHub.Nhs.Caching" Version="2.0.0" />
111-
<PackageReference Include="LearningHub.Nhs.Models" Version="3.0.39" />
111+
<PackageReference Include="LearningHub.Nhs.Models" Version="3.0.40" />
112112
<PackageReference Include="linqtotwitter" Version="6.9.0" />
113113
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.19.0" />
114114
<PackageReference Include="Microsoft.ApplicationInsights.EventCounterCollector" Version="2.21.0" />

LearningHub.Nhs.WebUI/Scripts/vuesrc/contribute-resource/ContributeAssessmentSettings.vue

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -65,22 +65,21 @@
6565

6666
<div class="d-flex">
6767
<div class="selection pr-50">
68-
<div>Provide guidance for the learner at the end of this assessment.</div>
69-
<EditSaveFieldWithCharacterCount
70-
v-model="assessmentDetails.endGuidance.blocks[0].title"
71-
addEditLabel="title"
72-
v-bind:characterLimit="60"
73-
v-bind:isH3="true" />
74-
<ckeditorwithhint v-on:blur="setEndGuidance"
68+
<div>Provide guidance for the learner at the end of this assessment. <i v-if="!IsVisible" class="warningTriangle fas fa-exclamation-triangle warm-yellow"></i></div>
69+
<EditSaveFieldWithCharacterCount v-model="assessmentDetails.endGuidance.blocks[0].title"
70+
addEditLabel="title"
71+
v-bind:characterLimit="60"
72+
v-bind:isH3="true" />
73+
<ckeditorwithhint v-on:blur="setEndGuidance"
7574
v-on:inputValidity="setGuidanceValidity"
76-
:maxLength="1000"
75+
:maxLength="1000"
7776
:initialValue="endGuidance" />
7877
</div>
7978
<div class="tip">
8079
<h3>Tip</h3>
8180
You can offer guidance to the learner at the end of the assessment such as next steps or recommendations on other learning resources to try. </div>
8281
</div>
83-
<Button class="mt-5" color="green" v-on:click="isOpen = false" :disabled="!canSaveAll">Save settings</Button>
82+
<Button class="mt-5" color="green" v-on:click="isOpen = false" :disabled="!IsVisible">Save settings</Button>
8483
</div>
8584
</div>
8685
</v-card>
@@ -132,6 +131,7 @@
132131
endGuidance: "",
133132
initialGuidance: "",
134133
guidanceValid: true,
134+
IsVisible: false,
135135
}
136136
},
137137
watch: {
@@ -140,7 +140,7 @@
140140
{
141141
this.assessmentDetails.endGuidance.addBlock(BlockTypeEnum.Text);
142142
}
143-
this.assessmentDetails.endGuidance.blocks[0].textBlock.content = this.endGuidance;
143+
this.assessmentDetails.endGuidance.blocks[0].textBlock.content = this.endGuidance;
144144
},
145145
["assessmentDetails.passMark"](value){ this.assessmentDetails.passMark = this.capNumberFieldBy(value, 100)},
146146
["assessmentDetails.maximumAttempts"](value){ this.assessmentDetails.maximumAttempts = this.capNumberFieldBy(value, 10)},
@@ -157,6 +157,14 @@
157157
}
158158
159159
this.assessmentDetails.assessmentSettingsAreValid = settingsAreValid;
160+
161+
if (this.endGuidance != "") {
162+
this.IsVisible = true;
163+
}
164+
else {
165+
this.IsVisible = false;
166+
}
167+
160168
return settingsAreValid;
161169
},
162170
},
@@ -170,9 +178,23 @@
170178
{
171179
this.endGuidance = description;
172180
}
181+
182+
if (this.endGuidance != "") {
183+
this.IsVisible = true;
184+
}
185+
else {
186+
this.IsVisible = false;
187+
}
173188
},
174189
setGuidanceValidity(valid: boolean) {
175-
this.guidanceValid = valid;
190+
if (this.endGuidance == "") {
191+
this.guidanceValid = false;
192+
this.IsVisible = false;
193+
}
194+
else {
195+
this.guidanceValid = valid;
196+
this.IsVisible = true;
197+
}
176198
}
177199
}
178200
});

LearningHub.Nhs.WebUI/Scripts/vuesrc/contribute-resource/components/KeyWordsEditor.vue

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<div class="row">
55
<div class="form-group" v-bind:class="{ 'input-validation-error': keywordError }">
66
<div class="col-12 mb-0 error-text" v-if="keywordError">
7-
<span class="text-danger">This keyword has already been added.</span>
7+
<span class="text-danger">The keyword(s) have already been added : {{formattedkeywordErrorMessage}}</span>
88
</div>
99
<div class="col-12 mb-0 error-text" v-if="keywordLengthExceeded">
1010
<span class="text-danger">
@@ -60,6 +60,7 @@
6060
newKeyword: '',
6161
keywordError: false,
6262
keywordLengthExceeded: false,
63+
keywordErrorMessage: []
6364
}
6465
},
6566
computed: {
@@ -69,19 +70,23 @@
6970
newKeywordTrimmed(): string {
7071
return this.newKeyword?.trim().replace(/ +(?= )/g, '').toLowerCase();
7172
},
73+
formattedkeywordErrorMessage(): string {
74+
return this.keywordErrorMessage.join(', ');
75+
},
7276
},
7377
methods: {
7478
keywordChange() {
7579
this.keywordError = false;
7680
this.keywordLengthExceeded = false;
81+
this.keywordErrorMessage = [];
7782
},
7883
async addKeyword() {
7984
if (this.newKeyword && this.newKeywordTrimmed.length > 0) {
85+
this.keywordChange();
8086
let allTrimmedKeyword = this.newKeywordTrimmed.toLowerCase().split(',');
8187
allTrimmedKeyword = allTrimmedKeyword.filter(e => String(e).trim());
82-
if (!this.resourceDetails.resourceKeywords.find(_keyword => allTrimmedKeyword.includes(_keyword.keyword.toLowerCase()))) {
8388
for (var i = 0; i < allTrimmedKeyword.length; i++) {
84-
let item = allTrimmedKeyword[i];
89+
let item = allTrimmedKeyword[i].trim();
8590
if (item.length > 0 && item.length <= 50) {
8691
let newKeywordObj = new KeywordModel({
8792
keyword: item,
@@ -90,8 +95,11 @@
9095
newKeywordObj = await resourceData.addKeyword(this.resourceVersionId, newKeywordObj);
9196
if (newKeywordObj.id > 0) {
9297
this.resourceDetails.resourceKeywords.push(newKeywordObj);
93-
this.keywordError = false;
9498
this.newKeyword = '';
99+
} else if (newKeywordObj.id == 0) {
100+
this.newKeyword = '';
101+
this.keywordError = true;
102+
this.keywordErrorMessage.push(item);
95103
}
96104
else {
97105
this.keywordError = true;
@@ -103,10 +111,6 @@
103111
this.keywordLengthExceeded = true;
104112
}
105113
}
106-
}
107-
else {
108-
this.keywordError = true;
109-
}
110114
}
111115
},
112116
async deleteKeyword(keywordId: number) {

LearningHub.Nhs.WebUI/Scripts/vuesrc/contribute/ContentCommon.vue

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@
8484
<div class="row">
8585
<div class="form-group" v-bind:class="{ 'input-validation-error': keywordError }">
8686
<div class="col-12 mb-0 error-text" v-if="keywordError">
87-
<span class="text-danger">This keyword has already been added.</span>
87+
<span class="text-danger">The keyword(s) have already been added : {{formattedkeywordErrorMessage}}</span>
8888
</div>
8989
<div class="col-12 mb-0 error-text" v-if="keywordLengthExceeded">
9090
<span class="text-danger">
@@ -292,6 +292,7 @@
292292
ResourceType,
293293
resourceProviderId: null,
294294
keywordLengthExceeded: false,
295+
keywordErrorMessage:[]
295296
};
296297
},
297298
computed: {
@@ -340,6 +341,9 @@
340341
return this.$store.state.userProviders.length > 0;
341342
}
342343
},
344+
formattedkeywordErrorMessage(): string {
345+
return this.keywordErrorMessage.join(', ');
346+
},
343347
},
344348
created() {
345349
this.setInitialValues();
@@ -485,6 +489,7 @@
485489
keywordChange() {
486490
this.keywordError = false;
487491
this.keywordLengthExceeded = false;
492+
this.keywordErrorMessage = [];
488493
},
489494
resetSelectedLicence() {
490495
this.resourceLicenceId = 0;
@@ -532,11 +537,11 @@
532537
},
533538
async addKeyword() {
534539
if (this.newKeyword && this.newKeywordTrimmed.length > 0) {
540+
this.keywordChange();
535541
let allTrimmedKeyword = this.newKeywordTrimmed.toLowerCase().split(',');
536542
allTrimmedKeyword = allTrimmedKeyword.filter(e => String(e).trim());
537-
if (!this.keywords.find(_keyword => allTrimmedKeyword.includes(_keyword.keyword.toLowerCase()))) {
538543
for (var i = 0; i < allTrimmedKeyword.length; i++) {
539-
let item = allTrimmedKeyword[i];
544+
let item = allTrimmedKeyword[i].trim();
540545
if (item.length > 0 && item.length <= 50) {
541546
let newkeywordObj = new KeywordModel();
542547
newkeywordObj.keyword = item;
@@ -548,22 +553,22 @@
548553
if (this.resourceDetail.resourceVersionId == 0) {
549554
this.$store.commit('setResourceVersionId', newkeywordObj.resourceVersionId)
550555
}
551-
this.keywordError = false;
552556
this.newKeyword = '';
553-
} else {
557+
} else if (newkeywordObj.id == 0) {
558+
this.newKeyword = '';
554559
this.keywordError = true;
555-
break;
560+
this.keywordErrorMessage.push(item);
556561
}
562+
else {
563+
this.keywordError = true;
564+
break;
565+
}
557566
}
558567
else {
559568
this.keywordLengthExceeded = true;
560569
break;
561570
}
562571
}
563-
}
564-
else {
565-
this.keywordError = true;
566-
}
567572
}
568573
else {
569574
this.newKeyword = '';

0 commit comments

Comments
 (0)