Skip to content

Commit 83daccf

Browse files
committed
TD-4997: Video Contents in eLearning resources not getting played on iOS device (iPhone or iPad)
1 parent 88dac10 commit 83daccf

File tree

2 files changed

+125
-113
lines changed

2 files changed

+125
-113
lines changed
Lines changed: 118 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -1,133 +1,139 @@
11
namespace LearningHub.Nhs.WebUI.Controllers
22
{
3-
using System;
4-
using System.Diagnostics;
5-
using System.IO;
6-
using System.Net;
7-
using System.Threading.Tasks;
8-
using LearningHub.Nhs.WebUI.Configuration;
9-
using LearningHub.Nhs.WebUI.EventSource;
10-
using LearningHub.Nhs.WebUI.Interfaces;
11-
using Microsoft.AspNetCore.Authorization;
12-
using Microsoft.AspNetCore.Mvc;
13-
using Microsoft.AspNetCore.StaticFiles;
14-
using Microsoft.Extensions.Options;
3+
using System;
4+
using System.Diagnostics;
5+
using System.IO;
6+
using System.Net;
7+
using System.Threading.Tasks;
8+
using LearningHub.Nhs.WebUI.Configuration;
9+
using LearningHub.Nhs.WebUI.EventSource;
10+
using LearningHub.Nhs.WebUI.Interfaces;
11+
using Microsoft.AspNetCore.Authorization;
12+
using Microsoft.AspNetCore.Mvc;
13+
using Microsoft.AspNetCore.StaticFiles;
14+
using Microsoft.Extensions.Options;
15+
16+
/// <summary>
17+
/// Defines the <see cref="LearningSessionsController" />.
18+
/// </summary>
19+
public class LearningSessionsController : Controller
20+
{
21+
private readonly IResourceService resourceService;
22+
private readonly IFileService fileService;
23+
private readonly IOptions<Settings> settings;
24+
private readonly IUserGroupService userGroupService;
1525

1626
/// <summary>
17-
/// Defines the <see cref="LearningSessionsController" />.
27+
/// Initializes a new instance of the <see cref="LearningSessionsController"/> class.
1828
/// </summary>
19-
public class LearningSessionsController : Controller
29+
/// <param name="resourceService">Resource service.</param>
30+
/// <param name="fileService">File service.</param>
31+
/// <param name="settings">Settings.</param>
32+
/// <param name="userGroupService">userGroupService.</param>
33+
public LearningSessionsController(
34+
IResourceService resourceService,
35+
IFileService fileService,
36+
IOptions<Settings> settings,
37+
IUserGroupService userGroupService)
2038
{
21-
private readonly IResourceService resourceService;
22-
private readonly IFileService fileService;
23-
private readonly IOptions<Settings> settings;
24-
private readonly IUserGroupService userGroupService;
39+
this.resourceService = resourceService;
40+
this.fileService = fileService;
41+
this.settings = settings;
42+
this.userGroupService = userGroupService;
43+
}
2544

26-
/// <summary>
27-
/// Initializes a new instance of the <see cref="LearningSessionsController"/> class.
28-
/// </summary>
29-
/// <param name="resourceService">Resource service.</param>
30-
/// <param name="fileService">File service.</param>
31-
/// <param name="settings">Settings.</param>
32-
/// <param name="userGroupService">userGroupService.</param>
33-
public LearningSessionsController(
34-
IResourceService resourceService,
35-
IFileService fileService,
36-
IOptions<Settings> settings,
37-
IUserGroupService userGroupService)
38-
{
39-
this.resourceService = resourceService;
40-
this.fileService = fileService;
41-
this.settings = settings;
42-
this.userGroupService = userGroupService;
43-
}
45+
/// <summary>
46+
/// The Scorm.
47+
/// </summary>
48+
/// <param name="id">id.</param>
49+
/// <returns>bool.</returns>
50+
public async Task<IActionResult> Scorm(int id)
51+
{
52+
var rv = await this.resourceService.GetItemByIdAsync(id);
53+
if (rv != null)
54+
{
55+
this.ViewBag.FilePath = $"/ScormContent/{rv.ScormDetails.ContentFilePath}/{rv.ScormDetails.ScormManifest.ManifestUrl}";
56+
}
4457

45-
/// <summary>
46-
/// The Scorm.
47-
/// </summary>
48-
/// <param name="id">id.</param>
49-
/// <returns>bool.</returns>
50-
public async Task<IActionResult> Scorm(int id)
51-
{
52-
var rv = await this.resourceService.GetItemByIdAsync(id);
53-
if (rv != null)
54-
{
55-
this.ViewBag.FilePath = $"/ScormContent/{rv.ScormDetails.ContentFilePath}/{rv.ScormDetails.ScormManifest.ManifestUrl}";
56-
}
58+
this.ViewBag.ResourceReferenceId = id;
59+
this.ViewBag.KeepUserSessionAliveInterval = Convert.ToInt32(this.settings.Value.KeepUserSessionAliveIntervalMins) * 60000;
60+
this.ViewBag.UseTraceWindow = await this.userGroupService.UserHasPermissionAsync("Scorm_Trace_Window");
5761

58-
this.ViewBag.ResourceReferenceId = id;
59-
this.ViewBag.KeepUserSessionAliveInterval = Convert.ToInt32(this.settings.Value.KeepUserSessionAliveIntervalMins) * 60000;
60-
this.ViewBag.UseTraceWindow = await this.userGroupService.UserHasPermissionAsync("Scorm_Trace_Window");
62+
return this.View();
63+
}
6164

62-
return this.View();
63-
}
65+
/// <summary>
66+
/// The ScormContent.
67+
/// </summary>
68+
/// <param name="filePath">filePath.</param>
69+
/// <returns>bool.</returns>
70+
//// [ResponseCache(VaryByQueryKeys = new[] { "*" }, Duration = 0, NoStore = true)] // disable caching
71+
//// Removed Request.Headers["Referer"] Referer URL checking based on issue reported in TD-4283
72+
[AllowAnonymous]
73+
[Route("ScormContent/{*filePath}")]
74+
public async Task<IActionResult> ScormContent(string filePath)
75+
{
76+
IActionResult result;
77+
var sw = Stopwatch.StartNew();
78+
long bytesServed = 0;
79+
string fileName = string.Empty;
6480

65-
/// <summary>
66-
/// The ScormContent.
67-
/// </summary>
68-
/// <param name="filePath">filePath.</param>
69-
/// <returns>bool.</returns>
70-
//// [ResponseCache(VaryByQueryKeys = new[] { "*" }, Duration = 0, NoStore = true)] // disable caching
71-
//// Removed Request.Headers["Referer"] Referer URL checking based on issue reported in TD-4283
72-
[AllowAnonymous]
73-
[Route("ScormContent/{*filePath}")]
74-
public async Task<IActionResult> ScormContent(string filePath)
81+
try
82+
{
83+
if (!this.User.Identity.IsAuthenticated)
7584
{
76-
IActionResult result;
77-
var sw = Stopwatch.StartNew();
78-
long bytesServed = 0;
79-
string fileName = string.Empty;
80-
81-
try
82-
{
83-
if (!this.User.Identity.IsAuthenticated)
84-
{
85-
throw new UnauthorizedAccessException("User is not authenticated.");
86-
}
87-
88-
var directory = filePath.Substring(0, filePath.LastIndexOf("/"));
89-
fileName = filePath.Substring(filePath.LastIndexOf("/") + 1, filePath.Length - filePath.LastIndexOf("/") - 1);
90-
91-
var file = await this.fileService.DownloadFileAsync(directory, fileName);
85+
throw new UnauthorizedAccessException("User is not authenticated.");
86+
}
9287

93-
if (!new FileExtensionContentTypeProvider().TryGetContentType(fileName, out var contentType))
94-
{
95-
contentType = "application/octet-stream";
96-
}
88+
var directory = filePath.Substring(0, filePath.LastIndexOf("/"));
89+
fileName = filePath.Substring(filePath.LastIndexOf("/") + 1, filePath.Length - filePath.LastIndexOf("/") - 1);
90+
string extension = Path.GetExtension(fileName);
9791

98-
result = this.File(file.Content, contentType);
99-
bytesServed = file.ContentLength;
100-
}
101-
catch (Azure.RequestFailedException rfe) when (rfe.Status == (int)HttpStatusCode.NotFound)
102-
{
103-
result = this.NotFound();
104-
}
105-
catch (UnauthorizedAccessException ex)
106-
{
107-
this.ViewBag.FilePath = filePath;
108-
this.ViewBag.ErrorMessage = "Unauthorised";
109-
this.ViewBag.ErrorDetail = ex.Message;
110-
result = this.View();
111-
}
112-
catch (Exception ex)
113-
{
114-
this.ViewBag.FilePath = filePath;
115-
this.ViewBag.ErrorMessage = "Unable to find content.";
116-
this.ViewBag.ErrorDetail = ex.Message;
117-
result = this.View();
118-
}
92+
var file = await this.fileService.DownloadFileAsync(directory, fileName);
11993

120-
ScormContentEventSource.Instance.AddRequestProcessMetadata(sw.ElapsedMilliseconds, bytesServed, Path.GetExtension(fileName));
121-
return result;
94+
if (!new FileExtensionContentTypeProvider().TryGetContentType(fileName, out var contentType))
95+
{
96+
contentType = "application/octet-stream";
12297
}
12398

124-
/// <summary>
125-
/// The TraceWindow.
126-
/// </summary>
127-
/// <returns>bool.</returns>
128-
public IActionResult TraceWindow()
99+
if (extension == ".mp4")
129100
{
130-
return this.View();
101+
contentType = "application/x-mpegURL";
131102
}
103+
104+
result = this.File(file.Content, contentType);
105+
bytesServed = file.ContentLength;
106+
}
107+
catch (Azure.RequestFailedException rfe) when (rfe.Status == (int)HttpStatusCode.NotFound)
108+
{
109+
result = this.NotFound();
110+
}
111+
catch (UnauthorizedAccessException ex)
112+
{
113+
this.ViewBag.FilePath = filePath;
114+
this.ViewBag.ErrorMessage = "Unauthorised";
115+
this.ViewBag.ErrorDetail = ex.Message;
116+
result = this.View();
117+
}
118+
catch (Exception ex)
119+
{
120+
this.ViewBag.FilePath = filePath;
121+
this.ViewBag.ErrorMessage = "Unable to find content.";
122+
this.ViewBag.ErrorDetail = ex.Message;
123+
result = this.View();
124+
}
125+
126+
ScormContentEventSource.Instance.AddRequestProcessMetadata(sw.ElapsedMilliseconds, bytesServed, Path.GetExtension(fileName));
127+
return result;
128+
}
129+
130+
/// <summary>
131+
/// The TraceWindow.
132+
/// </summary>
133+
/// <returns>bool.</returns>
134+
public IActionResult TraceWindow()
135+
{
136+
return this.View();
132137
}
138+
}
133139
}

LearningHub.Nhs.WebUI/Scripts/vuesrc/resource/ResourceContent.vue

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,6 @@
362362
// ]
363363
// }
364364
//};
365-
366365
this.player.load(sourceConfig)
367366
.then(() => {
368367
console.log("Source loaded successfully!");
@@ -723,7 +722,14 @@
723722
async launchScorm() {
724723
var targetWin;
725724
var targetWinName = "lhContent" + this.resourceItem.resourceId;
725+
// Use a placeholder window to avoid popup blockers
726+
targetWin = window.open("about:blank", targetWinName, "location=0,menubar=0,resizable=0,width=" + this.resourceItem.scormDetails.popupWidth + ",height=" + this.resourceItem.scormDetails.popupHeight);
726727
728+
// If the pop-up was blocked
729+
if (!targetWin) {
730+
alert("Please allow pop-ups to view this content.");
731+
return;
732+
}
727733
var activeContent = await userData.getActiveContent();
728734
729735
if (activeContent.filter(ac => ac.resourceId === this.resourceItem.resourceId).length > 0) {

0 commit comments

Comments
 (0)