From 9dd38723162e022b899497b9233f1643fdbc2427 Mon Sep 17 00:00:00 2001 From: Tom Mooney Date: Tue, 16 Mar 2021 05:56:31 -0700 Subject: [PATCH 01/17] Fix project references. Fix null reference errors in Tool.cshtml --- AdvantageTool.sln | 24 ++++++++++++------------ src/AdvantageTool.csproj | 4 ++-- src/Pages/Tool.cshtml | 10 +++++----- src/appsettings.json | 2 +- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/AdvantageTool.sln b/AdvantageTool.sln index 046b74b..322206b 100644 --- a/AdvantageTool.sln +++ b/AdvantageTool.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.28010.2019 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30804.86 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{777D9453-8256-4AD3-ADDF-BE34047DFFEB}" ProjectSection(SolutionItems) = preProject @@ -10,9 +10,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AdvantageTool", "src\AdvantageTool.csproj", "{4EBF015D-9B8C-4A2E-8A72-658F931718C0}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LtiAdvantage", "..\..\LTI\LtiAdvantage\src\LtiAdvantage\LtiAdvantage.csproj", "{097C6757-60C8-44C2-B378-F9A8E3A08377}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LtiAdvantage", "..\LtiAdvantage\src\LtiAdvantage\LtiAdvantage.csproj", "{52E66378-7670-4E6E-978C-D3B16883506C}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LtiAdvantage.IdentityModel", "..\..\LTI\LtiAdvantage\src\LtiAdvantage.IdentityModel\LtiAdvantage.IdentityModel.csproj", "{C55CD2BF-0A0E-43F2-A285-E82904CCD727}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LtiAdvantage.IdentityModel", "..\LtiAdvantage\src\LtiAdvantage.IdentityModel\LtiAdvantage.IdentityModel.csproj", "{B04A07BC-C551-478E-8B6C-377D219EC8FF}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -24,14 +24,14 @@ Global {4EBF015D-9B8C-4A2E-8A72-658F931718C0}.Debug|Any CPU.Build.0 = Debug|Any CPU {4EBF015D-9B8C-4A2E-8A72-658F931718C0}.Release|Any CPU.ActiveCfg = Release|Any CPU {4EBF015D-9B8C-4A2E-8A72-658F931718C0}.Release|Any CPU.Build.0 = Release|Any CPU - {097C6757-60C8-44C2-B378-F9A8E3A08377}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {097C6757-60C8-44C2-B378-F9A8E3A08377}.Debug|Any CPU.Build.0 = Debug|Any CPU - {097C6757-60C8-44C2-B378-F9A8E3A08377}.Release|Any CPU.ActiveCfg = Release|Any CPU - {097C6757-60C8-44C2-B378-F9A8E3A08377}.Release|Any CPU.Build.0 = Release|Any CPU - {C55CD2BF-0A0E-43F2-A285-E82904CCD727}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C55CD2BF-0A0E-43F2-A285-E82904CCD727}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C55CD2BF-0A0E-43F2-A285-E82904CCD727}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C55CD2BF-0A0E-43F2-A285-E82904CCD727}.Release|Any CPU.Build.0 = Release|Any CPU + {52E66378-7670-4E6E-978C-D3B16883506C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {52E66378-7670-4E6E-978C-D3B16883506C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {52E66378-7670-4E6E-978C-D3B16883506C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {52E66378-7670-4E6E-978C-D3B16883506C}.Release|Any CPU.Build.0 = Release|Any CPU + {B04A07BC-C551-478E-8B6C-377D219EC8FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B04A07BC-C551-478E-8B6C-377D219EC8FF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B04A07BC-C551-478E-8B6C-377D219EC8FF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B04A07BC-C551-478E-8B6C-377D219EC8FF}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/AdvantageTool.csproj b/src/AdvantageTool.csproj index c218444..2896f20 100644 --- a/src/AdvantageTool.csproj +++ b/src/AdvantageTool.csproj @@ -21,8 +21,8 @@ - - + + diff --git a/src/Pages/Tool.cshtml b/src/Pages/Tool.cshtml index bf45bfe..1d2c996 100644 --- a/src/Pages/Tool.cshtml +++ b/src/Pages/Tool.cshtml @@ -74,14 +74,14 @@
Platform Details
- @(Model.LtiRequest?.Platform.Name ?? "(no name)") + @(Model.LtiRequest?.Platform?.Name ?? "(no name)")
- @(Model.LtiRequest?.Platform.Description ?? "(no description)") + @(Model.LtiRequest?.Platform?.Description ?? "(no description)")
- @(Model.LtiRequest?.Platform.ProductFamilyCode ?? "(no family code)") - @(Model.LtiRequest?.Platform.Version ?? "(no version)") + @(Model.LtiRequest?.Platform?.ProductFamilyCode ?? "(no family code)") + @(Model.LtiRequest?.Platform?.Version ?? "(no version)")
@@ -100,7 +100,7 @@ @(Model.LtiRequest?.Email ?? "(no email)")
- @(string.Join(", ", Model.LtiRequest?.Roles)) + @(Model.LtiRequest?.Roles != null ? string.Join(", ", Model.LtiRequest?.Roles) : "(no roles)")
@(Model.LtiRequest?.Lis?.PersonSourcedId ?? "(no sis id)") diff --git a/src/appsettings.json b/src/appsettings.json index ae5c2d0..6cc3155 100644 --- a/src/appsettings.json +++ b/src/appsettings.json @@ -1,6 +1,6 @@ { "ConnectionStrings": { - "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=aspnet-AdvantageTool-53bc9b9d-9d6a-45d4-8429-2a2761773502;Trusted_Connection=True;MultipleActiveResultSets=true" + "DefaultConnection": "Server=localhost;Database=aspnet-AdvantageTool-53bc9b9d-9d6a-45d4-8429-2a2761773502;User Id=sa;Password=Password_123;" }, "Logging": { "LogLevel": { From 93ea2e02d4b13c3b3f75ddc8d24200b5363510fa Mon Sep 17 00:00:00 2001 From: Will Wolff-Myren Date: Mon, 5 Apr 2021 11:04:33 -0700 Subject: [PATCH 02/17] Include LtiAdvantage as submodule and adjust references https://github.com/learningcom/LtiAdvantage --- .gitmodules | 3 +++ shared/LtiAdvantage | 1 + 2 files changed, 4 insertions(+) create mode 100644 .gitmodules create mode 160000 shared/LtiAdvantage diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..31b9a0b --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "shared/LtiAdvantage"] + path = shared/LtiAdvantage + url = https://github.com/learningcom/LtiAdvantage diff --git a/shared/LtiAdvantage b/shared/LtiAdvantage new file mode 160000 index 0000000..b6272de --- /dev/null +++ b/shared/LtiAdvantage @@ -0,0 +1 @@ +Subproject commit b6272de80e1f2ce5f6c499f9c7e095d927b15213 From 9f70f459b7113ff34d4fd123886b87d9985c690b Mon Sep 17 00:00:00 2001 From: Will Wolff-Myren Date: Mon, 5 Apr 2021 11:04:56 -0700 Subject: [PATCH 03/17] Update project references --- AdvantageTool.sln | 4 ++-- src/AdvantageTool.csproj | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/AdvantageTool.sln b/AdvantageTool.sln index 322206b..4253cb2 100644 --- a/AdvantageTool.sln +++ b/AdvantageTool.sln @@ -10,9 +10,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AdvantageTool", "src\AdvantageTool.csproj", "{4EBF015D-9B8C-4A2E-8A72-658F931718C0}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LtiAdvantage", "..\LtiAdvantage\src\LtiAdvantage\LtiAdvantage.csproj", "{52E66378-7670-4E6E-978C-D3B16883506C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LtiAdvantage", "shared\LtiAdvantage\src\LtiAdvantage\LtiAdvantage.csproj", "{52E66378-7670-4E6E-978C-D3B16883506C}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LtiAdvantage.IdentityModel", "..\LtiAdvantage\src\LtiAdvantage.IdentityModel\LtiAdvantage.IdentityModel.csproj", "{B04A07BC-C551-478E-8B6C-377D219EC8FF}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LtiAdvantage.IdentityModel", "shared\LtiAdvantage\src\LtiAdvantage.IdentityModel\LtiAdvantage.IdentityModel.csproj", "{B04A07BC-C551-478E-8B6C-377D219EC8FF}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/src/AdvantageTool.csproj b/src/AdvantageTool.csproj index 2896f20..287d61b 100644 --- a/src/AdvantageTool.csproj +++ b/src/AdvantageTool.csproj @@ -21,8 +21,8 @@ - - + + From 353565aa67be2944a44e0b76ecd0a55efe5c7751 Mon Sep 17 00:00:00 2001 From: Will Wolff-Myren Date: Mon, 5 Apr 2021 11:28:31 -0700 Subject: [PATCH 04/17] Convert to relative link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9bd6a2e..02355d4 100644 --- a/README.md +++ b/README.md @@ -4,4 +4,4 @@ Sample LTI Advantage Tool for .NET Core. See https://advantagetool.azurewebsites.net/ -Most of the interesting stuff is in https://github.com/andyfmiller/LtiAdvantageTool/blob/master/src/Pages/Tool.cshtml.cs +Most of the interesting stuff is in [src/Pages/Tool.cshtml.cs](src/Pages/Tool.cshtml.cs) From 56d34b19e14f8c3674a1f6299c74c124da166685 Mon Sep 17 00:00:00 2001 From: Will Wolff-Myren Date: Mon, 5 Apr 2021 11:29:14 -0700 Subject: [PATCH 05/17] Remove broken URL --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 02355d4..92c9d9a 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,4 @@ # LTI Advantage Tool -Sample LTI Advantage Tool for .NET Core. See https://advantagetool.azurewebsites.net/ - Most of the interesting stuff is in [src/Pages/Tool.cshtml.cs](src/Pages/Tool.cshtml.cs) From c81e6494670769daaa94c285a092cd156dd68435 Mon Sep 17 00:00:00 2001 From: Will Wolff-Myren Date: Mon, 5 Apr 2021 18:30:18 -0700 Subject: [PATCH 06/17] Add links to README.md --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 92c9d9a..631c42e 100644 --- a/README.md +++ b/README.md @@ -3,3 +3,14 @@ # LTI Advantage Tool Most of the interesting stuff is in [src/Pages/Tool.cshtml.cs](src/Pages/Tool.cshtml.cs) + +## Useful Links + +Debugging Tools + +* [OpenID Connect debugger](https://oidcdebugger.com/) + +LTI Specifications +* [Learning Tools Interoperability Core Specification 1.3 | IMS Global Learning Consortium](https://www.imsglobal.org/spec/lti/v1p3/) +* [Learning Tools Interoperability Names and Role Provisioning Services Version 2.0 | IMS Global Learning Consortium](https://www.imsglobal.org/spec/lti-nrps/v2p0) +* [Learning Tools Interoperability Assignment and Grade Services Version 2.0 | IMS Global Learning Consortium](https://www.imsglobal.org/spec/lti-ags/v2p0) \ No newline at end of file From 4a24e403730571197259b0c0b63b9d9032c7da8b Mon Sep 17 00:00:00 2001 From: Tom Mooney Date: Tue, 27 Apr 2021 07:15:14 -0700 Subject: [PATCH 07/17] Fix error when platform doesn't return line items. Add a way to submit score for line item when lineitem url is present. --- .../LineItems/LineItemsViewComponent.cs | 2 +- src/Pages/Tool.cshtml | 33 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/Pages/Components/LineItems/LineItemsViewComponent.cs b/src/Pages/Components/LineItems/LineItemsViewComponent.cs index 9458a42..85b1347 100644 --- a/src/Pages/Components/LineItems/LineItemsViewComponent.cs +++ b/src/Pages/Components/LineItems/LineItemsViewComponent.cs @@ -92,7 +92,7 @@ public async Task InvokeAsync(string idToken) catch (Exception e) { model.Status = e.Message; - return View(); + return View(model); } // Get all the members of the course diff --git a/src/Pages/Tool.cshtml b/src/Pages/Tool.cshtml index 1d2c996..ad79c43 100644 --- a/src/Pages/Tool.cshtml +++ b/src/Pages/Tool.cshtml @@ -186,6 +186,39 @@
+ @if (!string.IsNullOrEmpty(Model.LtiRequest.AssignmentGradeServices.LineItemUrl)) + { +
+
+
+
Line Item
+
+
+
+ Line Item Url +
+
+ @(Model.LtiRequest.AssignmentGradeServices.LineItemUrl) +
+
+
+ + +
+
+
+
+
+
+
+ } + + @await Component.InvokeAsync("LineItems", Model.IdToken) From 1456c8cf92bea98b17d8bb9e9a9a71b3ea6ded9d Mon Sep 17 00:00:00 2001 From: Tom Mooney Date: Tue, 4 May 2021 14:43:41 -0700 Subject: [PATCH 08/17] Retrieve and display any context members on load --- src/Pages/Components/LineItems/Default.cshtml | 41 ++++++++++++++++- .../LineItems/LineItemsViewComponent.cs | 44 +++++++++---------- 2 files changed, 61 insertions(+), 24 deletions(-) diff --git a/src/Pages/Components/LineItems/Default.cshtml b/src/Pages/Components/LineItems/Default.cshtml index 72591f0..e4175ba 100644 --- a/src/Pages/Components/LineItems/Default.cshtml +++ b/src/Pages/Components/LineItems/Default.cshtml @@ -3,7 +3,7 @@ @if (Model != null) {
-
+
@@ -74,4 +74,41 @@
-} \ No newline at end of file +
+
+
+
+ Members +
+
+ @if (Model.Members == null) + { +

+ This context does not have any members. +

+ } + else + { + + + + + + + + @foreach (var member in Model.Members) + { + + + + } + +
UserId
+ @member.Key +
+ } +
+
+
+
+ } diff --git a/src/Pages/Components/LineItems/LineItemsViewComponent.cs b/src/Pages/Components/LineItems/LineItemsViewComponent.cs index 85b1347..7fd16df 100644 --- a/src/Pages/Components/LineItems/LineItemsViewComponent.cs +++ b/src/Pages/Components/LineItems/LineItemsViewComponent.cs @@ -62,16 +62,19 @@ public async Task InvokeAsync(string idToken) return View(model); } - // Get all the line items + // Get all the members of the course + model.Members = new Dictionary(); + try { var httpClient = _httpClientFactory.CreateClient(); httpClient.SetBearerToken(tokenResponse.AccessToken); + httpClient.DefaultRequestHeaders.Accept.Clear(); httpClient.DefaultRequestHeaders.Accept - .Add(new MediaTypeWithQualityHeaderValue(Constants.MediaTypes.LineItemContainer)); + .Add(new MediaTypeWithQualityHeaderValue(Constants.MediaTypes.MembershipContainer)); - using (var response = await httpClient.GetAsync(model.LtiRequest.AssignmentGradeServices?.LineItemsUrl)) + using (var response = await httpClient.GetAsync(model.LtiRequest.NamesRoleService.ContextMembershipUrl)) { if (!response.IsSuccessStatusCode) { @@ -80,13 +83,14 @@ public async Task InvokeAsync(string idToken) } var content = await response.Content.ReadAsStringAsync(); - model.LineItems = JsonConvert.DeserializeObject>(content) - .Select(i => new MyLineItem + var membership = JsonConvert.DeserializeObject(content); + foreach (var member in membership.Members.OrderBy(m => m.FamilyName).ThenBy(m => m.GivenName)) + { + if (!model.Members.ContainsKey(member.UserId)) { - AgsLineItem = i, - Header = i.Label ?? $"Tag: {i.Tag}" - }) - .ToList(); + model.Members.Add(member.UserId, $"{member.FamilyName}, {member.GivenName}"); + } + } } } catch (Exception e) @@ -95,19 +99,16 @@ public async Task InvokeAsync(string idToken) return View(model); } - // Get all the members of the course - model.Members = new Dictionary(); - + // Get all the line items try { var httpClient = _httpClientFactory.CreateClient(); httpClient.SetBearerToken(tokenResponse.AccessToken); - httpClient.DefaultRequestHeaders.Accept.Clear(); httpClient.DefaultRequestHeaders.Accept - .Add(new MediaTypeWithQualityHeaderValue(Constants.MediaTypes.MembershipContainer)); + .Add(new MediaTypeWithQualityHeaderValue(Constants.MediaTypes.LineItemContainer)); - using (var response = await httpClient.GetAsync(model.LtiRequest.NamesRoleService.ContextMembershipUrl)) + using (var response = await httpClient.GetAsync(model.LtiRequest.AssignmentGradeServices?.LineItemsUrl)) { if (!response.IsSuccessStatusCode) { @@ -116,14 +117,13 @@ public async Task InvokeAsync(string idToken) } var content = await response.Content.ReadAsStringAsync(); - var membership = JsonConvert.DeserializeObject(content); - foreach (var member in membership.Members.OrderBy(m => m.FamilyName).ThenBy(m => m.GivenName)) - { - if (!model.Members.ContainsKey(member.UserId)) + model.LineItems = JsonConvert.DeserializeObject>(content) + .Select(i => new MyLineItem { - model.Members.Add(member.UserId, $"{member.FamilyName}, {member.GivenName}"); - } - } + AgsLineItem = i, + Header = i.Label ?? $"Tag: {i.Tag}" + }) + .ToList(); } } catch (Exception e) From 20468c0111ab39e5f92a0c25692d44177811c7c6 Mon Sep 17 00:00:00 2001 From: Tom Mooney Date: Mon, 10 May 2021 11:32:52 -0700 Subject: [PATCH 09/17] Show results on load --- src/Pages/Tool.cshtml | 30 +++++++++++++++++++++++++++++- src/Pages/Tool.cshtml.cs | 17 +++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/Pages/Tool.cshtml b/src/Pages/Tool.cshtml index ad79c43..9a4b408 100644 --- a/src/Pages/Tool.cshtml +++ b/src/Pages/Tool.cshtml @@ -189,7 +189,7 @@ @if (!string.IsNullOrEmpty(Model.LtiRequest.AssignmentGradeServices.LineItemUrl)) {
-
+
Line Item
@@ -200,6 +200,34 @@
@(Model.LtiRequest.AssignmentGradeServices.LineItemUrl)
+
+ Results +
+
+ + + + + + + + + + + + @foreach (var result in Model.Results) + { + + + + + + + + } + +
IdUserIdResultScoreResultMaximumScoreOf
@result.Id@result.UserId@result.ResultScore@result.ResultMaximum@result.ScoreOf
+
diff --git a/src/Pages/Tool.cshtml.cs b/src/Pages/Tool.cshtml.cs index ec09b1a..1cdb747 100644 --- a/src/Pages/Tool.cshtml.cs +++ b/src/Pages/Tool.cshtml.cs @@ -63,6 +63,8 @@ public ToolModel( /// public LtiResourceLinkRequest LtiRequest { get; set; } + public ResultContainer Results { get; set; } + /// /// Handle the LTI POST request from the Authorization Server. /// @@ -214,6 +216,21 @@ public async Task OnPostAsync( IdToken = idToken; LtiRequest = new LtiResourceLinkRequest(jwt.Payload); + var tokenResponse = await _accessTokenService.GetAccessTokenAsync( + LtiRequest.Iss, + Constants.LtiScopes.Ags.LineItem); + + var resultsClient = _httpClientFactory.CreateClient(); + resultsClient.SetBearerToken(tokenResponse.AccessToken); + resultsClient.DefaultRequestHeaders.Accept + .Add(new MediaTypeWithQualityHeaderValue(Constants.MediaTypes.LineItem)); + + var resultsUrl = $"{LtiRequest.AssignmentGradeServices.LineItemUrl}/{Constants.ServiceEndpoints.Ags.ResultsService}"; + var response = await resultsClient.GetAsync(resultsUrl); + var content = await response.Content.ReadAsStringAsync(); + var results = JsonConvert.DeserializeObject(content); + Results = results; + return Page(); } From c9387397e0ef35897d04db9ac78ac8b61b2f4c9f Mon Sep 17 00:00:00 2001 From: Tom Mooney Date: Wed, 12 May 2021 13:58:01 -0700 Subject: [PATCH 10/17] Retrieve line item data on load --- src/Pages/Tool.cshtml | 29 +++++++++++++++++++---------- src/Pages/Tool.cshtml.cs | 19 +++++++++++++------ 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/src/Pages/Tool.cshtml b/src/Pages/Tool.cshtml index 9a4b408..56bec13 100644 --- a/src/Pages/Tool.cshtml +++ b/src/Pages/Tool.cshtml @@ -195,13 +195,22 @@
- Line Item Url + ID: @(Model.LineItem.Id)
- @(Model.LtiRequest.AssignmentGradeServices.LineItemUrl) + Label: @(Model.LineItem.Label ?? "(no label)")
- Results + Resource Id: @(Model.LineItem.ResourceId ?? "(no resource id)") +
+
+ ResourceLink Id: @(Model.LineItem.ResourceLinkId ?? "(no resourcelink id)") +
+
+ ScoreMaximum: @(Model.LineItem.ScoreMaximum) +
+
+ Results
@@ -217,13 +226,13 @@ @foreach (var result in Model.Results) { - - - - - - - + + + + + + + }
@result.Id@result.UserId@result.ResultScore@result.ResultMaximum@result.ScoreOf
@result.Id@result.UserId@result.ResultScore@result.ResultMaximum@result.ScoreOf
diff --git a/src/Pages/Tool.cshtml.cs b/src/Pages/Tool.cshtml.cs index 1cdb747..f4f7886 100644 --- a/src/Pages/Tool.cshtml.cs +++ b/src/Pages/Tool.cshtml.cs @@ -65,6 +65,8 @@ public ToolModel( public ResultContainer Results { get; set; } + public LineItem LineItem { get; set; } + /// /// Handle the LTI POST request from the Authorization Server. /// @@ -220,17 +222,22 @@ public async Task OnPostAsync( LtiRequest.Iss, Constants.LtiScopes.Ags.LineItem); - var resultsClient = _httpClientFactory.CreateClient(); - resultsClient.SetBearerToken(tokenResponse.AccessToken); - resultsClient.DefaultRequestHeaders.Accept + var lineItemClient = _httpClientFactory.CreateClient(); + lineItemClient.SetBearerToken(tokenResponse.AccessToken); + lineItemClient.DefaultRequestHeaders.Accept .Add(new MediaTypeWithQualityHeaderValue(Constants.MediaTypes.LineItem)); var resultsUrl = $"{LtiRequest.AssignmentGradeServices.LineItemUrl}/{Constants.ServiceEndpoints.Ags.ResultsService}"; - var response = await resultsClient.GetAsync(resultsUrl); - var content = await response.Content.ReadAsStringAsync(); - var results = JsonConvert.DeserializeObject(content); + var resultsResponse = await lineItemClient.GetAsync(resultsUrl); + var resultsContent = await resultsResponse.Content.ReadAsStringAsync(); + var results = JsonConvert.DeserializeObject(resultsContent); Results = results; + var lineItemResponse = await lineItemClient.GetAsync(LtiRequest.AssignmentGradeServices.LineItemUrl); + var lineItemContent = await lineItemResponse.Content.ReadAsStringAsync(); + var lineItem = JsonConvert.DeserializeObject(lineItemContent); + LineItem = lineItem; + return Page(); } From ba1cfae5ffa5aa39e0664fbe02c97f4f5c3f3b97 Mon Sep 17 00:00:00 2001 From: Tom Mooney Date: Fri, 14 May 2021 13:28:57 -0700 Subject: [PATCH 11/17] Allow the resourcelinkid for a new line item to be specified --- src/Pages/Components/LineItems/Default.cshtml | 11 ++++++++--- src/Pages/Tool.cshtml.cs | 5 +++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Pages/Components/LineItems/Default.cshtml b/src/Pages/Components/LineItems/Default.cshtml index e4175ba..9c5e2d6 100644 --- a/src/Pages/Components/LineItems/Default.cshtml +++ b/src/Pages/Components/LineItems/Default.cshtml @@ -9,11 +9,16 @@
Gradebook -
+
+
+ + + +
@Model.Status @if (Model.LineItems == null) { diff --git a/src/Pages/Tool.cshtml.cs b/src/Pages/Tool.cshtml.cs index f4f7886..4eff97a 100644 --- a/src/Pages/Tool.cshtml.cs +++ b/src/Pages/Tool.cshtml.cs @@ -245,7 +245,7 @@ public async Task OnPostAsync( /// Handler for creating a line item. /// /// The result. - public async Task OnPostCreateLineItemAsync([FromForm(Name = "id_token")] string idToken) + public async Task OnPostCreateLineItemAsync([FromForm(Name = "id_token")] string idToken, [FromForm(Name = "resource_link_id")] string resourceLinkId) { if (idToken.IsMissing()) { @@ -275,11 +275,12 @@ public async Task OnPostCreateLineItemAsync([FromForm(Name = "id_ try { + var lineItem = new LineItem { EndDateTime = DateTime.UtcNow.AddMonths(3), Label = LtiRequest.ResourceLink.Title, - ResourceLinkId = LtiRequest.ResourceLink.Id, + ResourceLinkId = string.IsNullOrEmpty(resourceLinkId) ? LtiRequest.ResourceLink.Id : resourceLinkId, ScoreMaximum = 100, StartDateTime = DateTime.UtcNow }; From bac40f911fd0b247b09e04a97c2bc826a5a37e6a Mon Sep 17 00:00:00 2001 From: Tom Mooney Date: Mon, 17 May 2021 11:24:51 -0700 Subject: [PATCH 12/17] Allow for deletion of line items --- src/Pages/Components/LineItems/Default.cshtml | 11 +++- src/Pages/Tool.cshtml.cs | 56 +++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/src/Pages/Components/LineItems/Default.cshtml b/src/Pages/Components/LineItems/Default.cshtml index 9c5e2d6..befdc22 100644 --- a/src/Pages/Components/LineItems/Default.cshtml +++ b/src/Pages/Components/LineItems/Default.cshtml @@ -34,7 +34,16 @@ @foreach (var lineItem in Model.LineItems.OrderBy(l => l.Header)) { - @lineItem.Header + + @lineItem.Header + @{var deleteRouteData = new Dictionary { { "lineItemUrl", lineItem.AgsLineItem.Id } };} + + } diff --git a/src/Pages/Tool.cshtml.cs b/src/Pages/Tool.cshtml.cs index 4eff97a..37e038a 100644 --- a/src/Pages/Tool.cshtml.cs +++ b/src/Pages/Tool.cshtml.cs @@ -302,6 +302,62 @@ public async Task OnPostCreateLineItemAsync([FromForm(Name = "id_ return Page(); } + return Relaunch( + LtiRequest.Iss, + LtiRequest.UserId, + LtiRequest.ResourceLink.Id, + LtiRequest.Context.Id); + } + + /// + /// Handler for creating a line item. + /// + /// The result. + public async Task OnPostDeleteLineItemAsync([FromForm(Name = "id_token")] string idToken, string lineItemUrl) + { + if (idToken.IsMissing()) + { + Error = $"{nameof(idToken)} is missing."; + return Page(); + } + + var handler = new JwtSecurityTokenHandler(); + var jwt = handler.ReadJwtToken(idToken); + LtiRequest = new LtiResourceLinkRequest(jwt.Payload); + + var tokenResponse = await _accessTokenService.GetAccessTokenAsync( + LtiRequest.Iss, + Constants.LtiScopes.Ags.LineItem); + + // The IMS reference implementation returns "Created" with success. + if (tokenResponse.IsError && tokenResponse.Error != "Created") + { + Error = tokenResponse.Error; + return Page(); + } + + var httpClient = _httpClientFactory.CreateClient(); + httpClient.SetBearerToken(tokenResponse.AccessToken); + httpClient.DefaultRequestHeaders.Accept + .Add(new MediaTypeWithQualityHeaderValue(Constants.MediaTypes.LineItem)); + + try + { + using (var response = await httpClient.DeleteAsync(lineItemUrl)) + { + if (!response.IsSuccessStatusCode) + { + Error = response.ReasonPhrase; + return Page(); + } + } + } + catch (Exception e) + { + Error = e.Message; + return Page(); + } + return Relaunch( LtiRequest.Iss, LtiRequest.UserId, From 7182a46e0d83ba83211ab2fc1563ab22caa9ff1d Mon Sep 17 00:00:00 2001 From: Tom Mooney Date: Fri, 21 May 2021 16:26:28 -0700 Subject: [PATCH 13/17] Fix the redirect to the /Catalog page on a DeepLinkRequest launch. Change the token parameter name sent back to the Platform when assigning deeplinks to the standard "JWT" as defined in the spec at https://www.imsglobal.org/spec/security/v1p0/#form-parameter --- src/Pages/Catalog.cshtml.cs | 2 +- src/Pages/Tool.cshtml.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Pages/Catalog.cshtml.cs b/src/Pages/Catalog.cshtml.cs index 56a714f..089caff 100644 --- a/src/Pages/Catalog.cshtml.cs +++ b/src/Pages/Catalog.cshtml.cs @@ -147,7 +147,7 @@ public async Task OnPostAssignActivities() var credentials = PemHelper.SigningCredentialsFromPemString(platform.PrivateKey); var jwt = handler.WriteToken(new JwtSecurityToken(new JwtHeader(credentials), response)); - return Post("id_token", jwt, LtiRequest.DeepLinkingSettings.DeepLinkReturnUrl); + return Post("JWT", jwt, LtiRequest.DeepLinkingSettings.DeepLinkReturnUrl); } /// diff --git a/src/Pages/Tool.cshtml.cs b/src/Pages/Tool.cshtml.cs index 37e038a..3071c29 100644 --- a/src/Pages/Tool.cshtml.cs +++ b/src/Pages/Tool.cshtml.cs @@ -212,7 +212,7 @@ public async Task OnPostAsync( if (messageType == Constants.Lti.LtiDeepLinkingRequestMessageType) { - return Post("./Catalog", new { idToken }); + return Post("/Catalog", new { idToken }); } IdToken = idToken; From cfd35650e98035a2853b885be718879a942d5948 Mon Sep 17 00:00:00 2001 From: Tom Mooney Date: Wed, 2 Jun 2021 09:50:34 -0700 Subject: [PATCH 14/17] Generate correct tool Url when assigning a deep link item --- src/Pages/Catalog.cshtml.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Pages/Catalog.cshtml.cs b/src/Pages/Catalog.cshtml.cs index 089caff..209e8fd 100644 --- a/src/Pages/Catalog.cshtml.cs +++ b/src/Pages/Catalog.cshtml.cs @@ -107,15 +107,18 @@ public async Task OnPostAssignActivities() var contentItems = new List(); var customParameters = LtiRequest.Custom; + var platform = await _context.GetPlatformByIssuerAsync(LtiRequest.Iss); + foreach (var activity in Activities) { if (activity.Selected) { + var url = Url.Page("/Tool", null, new { platformId = platform.PlatformId }, Request.Scheme); var contentItem = new LtiLinkItem { Title = activity.Title, Text = activity.Description, - Url = Url.Page("./Tool", null, null, Request.Scheme), + Url = url, Custom = new Dictionary { { "activity_id", activity.Id.ToString() } @@ -143,7 +146,6 @@ public async Task OnPostAssignActivities() response.AddClaim(new Claim(JwtRegisteredClaimNames.Exp, EpochTime.GetIntDate(DateTime.UtcNow.AddMinutes(5)).ToString())); response.AddClaim(new Claim(JwtRegisteredClaimNames.Nonce, IdentityModel.CryptoRandom.CreateRandomKeyString(8))); - var platform = await _context.GetPlatformByIssuerAsync(LtiRequest.Iss); var credentials = PemHelper.SigningCredentialsFromPemString(platform.PrivateKey); var jwt = handler.WriteToken(new JwtSecurityToken(new JwtHeader(credentials), response)); From b697007c99d53da57cf89d2f21c24a03367d2499 Mon Sep 17 00:00:00 2001 From: Will Wolff-Myren Date: Fri, 11 Jun 2021 17:03:19 -0700 Subject: [PATCH 15/17] Add PowerShell convenience script for console startup --- Start-AdvantageTool.ps1 | 1 + 1 file changed, 1 insertion(+) create mode 100644 Start-AdvantageTool.ps1 diff --git a/Start-AdvantageTool.ps1 b/Start-AdvantageTool.ps1 new file mode 100644 index 0000000..2e9d313 --- /dev/null +++ b/Start-AdvantageTool.ps1 @@ -0,0 +1 @@ +dotnet run --project .\src\AdvantageTool.csproj \ No newline at end of file From e3cb912fcbd26b84b13d1a0eb96381ae997c4783 Mon Sep 17 00:00:00 2001 From: Tom Mooney Date: Wed, 7 Jul 2021 14:59:45 -0700 Subject: [PATCH 16/17] Display custom params from launch request --- src/Pages/Tool.cshtml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/Pages/Tool.cshtml b/src/Pages/Tool.cshtml index 56bec13..c8d4900 100644 --- a/src/Pages/Tool.cshtml +++ b/src/Pages/Tool.cshtml @@ -135,7 +135,7 @@

Context

-
+
Context Details
@@ -151,7 +151,7 @@
-
+
Member
@@ -171,7 +171,7 @@
-
+
Resource Link
@@ -184,6 +184,16 @@
+
+
+
Custom
+
+
+ @(Model.LtiRequest.Custom == null ? "" : string.Join(",", Model.LtiRequest.Custom.OrderBy(kv => kv.Key))) +
+
+
+
@if (!string.IsNullOrEmpty(Model.LtiRequest.AssignmentGradeServices.LineItemUrl)) From 860cc248de5e8b9e7911be62b8ef6ddbf30c2916 Mon Sep 17 00:00:00 2001 From: tmooney_lcom Date: Fri, 4 Aug 2023 10:26:53 -0700 Subject: [PATCH 17/17] Allow invalid certificates in HttpCleint --- src/Startup.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Startup.cs b/src/Startup.cs index 22ef0da..64a3bf2 100644 --- a/src/Startup.cs +++ b/src/Startup.cs @@ -8,6 +8,8 @@ using AdvantageTool.Utility; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using System.Net.Http; namespace AdvantageTool { @@ -60,7 +62,17 @@ public void ConfigureServices(IServiceCollection services) .AddRazorPagesOptions(options => options.Conventions.AuthorizeFolder("/Platforms")) .SetCompatibilityVersion(CompatibilityVersion.Version_2_2); - services.AddHttpClient(); + services.AddHttpClient(Options.DefaultName, c => + { + }).ConfigurePrimaryHttpMessageHandler(() => + { + return new HttpClientHandler + { + ClientCertificateOptions = ClientCertificateOption.Manual, + ServerCertificateCustomValidationCallback = + (httpRequestMessage, cert, certChain, policyErrors) => true + }; + }); // Make AccessTokenService available for dependency injection. services.AddTransient();