openwebwork / webwork2

Course management front end for WeBWorK
http://webwork.maa.org/wiki/Main_Page
Other
141 stars 164 forks source link

Add content item selection for LTI. #2322

Closed drgrice1 closed 3 months ago

drgrice1 commented 5 months ago

See https://www.imsglobal.org/specs/lticiv1p0/specification and https://www.imsglobal.org/spec/lti-dl/v2p0.

This will be needed for Moodle 4.03 (and beyond?) but might be nice for other LMS's as well. If the new content selection endpoints (https://your.webwork2.server.domain/ltiadvanced/content_selection for LTI 1.1 and https://your.webwork2.server.domain/ltiadvantage/content_selection for LTI 1.3) are added to the LMS external tool configuration, then instructors can select from a list of homework assignments in the webwork2 course, and it will add the correct link(s) to the LMS. For Moodle (at least for 4.1 and up) you can add links to all assignments with a few clicks of the mouse. Canvas is not quite as nice as it only allows selection one assignment at a time (at least as far as I have been able to figure out at this point). As usual, I am not sure what D2L can do.

In order for this to work each webwork2 course must set the new LTI configuration variable $LMSCourseName in its course.conf file. This is the name of the LMS course that is associated with the webwork2 course. This must be done because these content item requests do not have the webwork2 course name in the link like the usual webwork2 course or assignment links.

In order to support multiple LMS's with a single webwork2 server there is a little more that must be done for LTI 1.1, particularly if there is the possibility that courses from different LMS's might have the same LMS course name. In this case each LMS must use a different consumer key for its external tool for the webwork2 server, and the $LTI{v1p1}{ConsumerKey} must be set in each courses course.conf file. For LTI 1.3 no additional configuration is needed because the authentication parameters already uniquely identify an LMS external tool.

Note that none of this new configuration is needed in the case that the webwork2 server administrator does not want instructors to be able to utilize the new content item selection routes. The $LTICourseName does not need to be set in the course.conf files, the $LTI{v1p1}{ConsumerKey} does not need to be set, and both versions of LTI will continue to work as they did before.

Now for implementation details.

For LTI 1.1 nothing is changed in the way that authentication works unless the LMS sends the user to the new ltiadvanced_content_selection route. In that case the correct course is identified by iterating through the webwork2 courses on the server, loading the course environment for each one, and finding the one with the correct LMS course name (and ConsumerKey if there is more than one with the same LMS course name). This is probably rather inefficient, but fortunately this is only done for content item selection requests.

For LTI 1.3 things needed to be reworked quite a bit more. All of the hackery that was previously implemented to pass the course id, state, and nonce from the login request to the launch request using the key table had to go. Instead there is now a non-native table (the lti_launch_data table) that this information is stored in, identified by the state generated from the parameters provided by the LMS in the login request which are unique to the LMS user and request. This table can be accessed without a webwork2 course id since it is non-native. This allows for the state to be retrieved from the database in the launch request, and the JWT to be decoded earlier in that request (previously this was done in the authentication step, and now it is done before that in the webwork2 dispatch step). It needs to be done at dispatch time because the JWT contains the information that determines if it is a deep linking request or not. For both the login and launch requests a webwork2 course is identified by iterating through courses, loading course environments, and finding an LTI 1.3 tool configuration that matches that in the request. Again this is probably rather inefficient, but again this is only done for content item selection requests.

Note that the new non-native table will be automatically created the first time that you sign in to the admin course. This is how it has always worked for non-native tables.

There is a new hook that can be utilized by content generator modules to modify parameters, route captures, and do other things early in the dispatch phase before a course id has been determined and a course environment and database instance is initialized. The hook can be utilized by the content generator module providing an initializeRoute method. This is just clean up really. A lot of route specific special cases were starting to build up in the dispatch method in lib/WeBWork.pm, and this makes it possible for that code to be moved out of there.

Alex-Jordan commented 4 months ago

How does one identify the "LMSCourseName"? I haven't tried this out, but my best guess with D2L is that it's what D2L calls the "org unit number". A course in D2L at our school is at like : https://online.pcc.edu/d2l/home/123456 and 123456 is the "org unit number".

Also I was wondering if whatever "LMSCourseName" is can be saved into the database when an LTI connection is initially made, similar to how we store "lis_source_did" for each user. Or is this all needed before you've set up an external learning tool in the first place?

drgrice1 commented 4 months ago

The LMS course name is whatever the LMS sends as the context title. For LTI 1.1 that is a form parameter named context_title, and for LTI 1.3 that is the `'https://purl.imsglobal.org/spec/lti/claim/context#title' claim in the JWT. For Moodle and Canvas this is what is displayed for the course name when you are in the course in the LMS itself.

The external tool needs to be set up in the LMS before the content item selection endpoint can be used. However, that does not identify the webwork course/LMS course pairing. This is not something that can be saved in the database like the "lis_source_did" because at the time of the request there is nothing that identifies the webwork course to associate the LMS course with.

drgrice1 commented 4 months ago

Just to clarify, since I understand that you use an unusual setup for LTI. In the typical setup there is one external tool in the LMS that all courses use. WeBWorK is configured for that external tool (it can of course be configured for external tools from multiple LMS's). Now, if an instructor wants to use a WeBWorK course, then the instructor needs to create links in the LMS. That is what the content item selection request is for. So at that point, there is no link in the LMS course to the WeBWorK course.

So what happens with this change is that a content item selection URL is added to the configuration of the external tool. All courses use that same URL. When the content item request is made, there are not yet any links in the LMS to WeBWorK. So there is no way that a user can sign in to WeBWorK for the first time and make the association that you are suggesting. That is the point of the content item request. To add those links to the LMS.

Alex-Jordan commented 4 months ago

"Titles" generically are not unique, so can you confirm that setting $LMSCourseName is something that is supposed to be unique over courses within the same LMS? In the example at https://www.imsglobal.org/spec/lti-dl/v2p0, the "title" is "Economics as a Social Science". I'm not understanding the importance of this if it does not uniquely identify an LMS course, even within the same LMS.

Alex-Jordan commented 4 months ago

In the typical setup there is one external tool in the LMS that all courses use.

I don't think this is the case with D2L.

drgrice1 commented 4 months ago

It should be unique for a single LMS. It does not matter if courses from different LMS's have the same context title.

Alex-Jordan commented 4 months ago

I must be misunderstanding something. It seems like I could have two distinct courses in the LMS that both have title "College Algebra". And the example from https://www.imsglobal.org/spec/lti-dl/v2p0 suggests that is the kind of "title" it references, as opposed to an id.

drgrice1 commented 4 months ago

In the typical setup there is one external tool in the LMS that all courses use.

I don't think this is the case with D2L.

I believe that is the case with D2L. Just not your usage with WeBWorK.

Alex-Jordan commented 4 months ago

@dlglin also uses D2L so can see if our use at PCC is a-typical. At PCC, with D2L and LTI 1.1, the D2L admins have configured https://webwork.pcc.edu/webwork2 to be a "tool provider". Each instructor of an LMS course must go to the external tools manager and create an "external learning tool" where they specify the actual path to their WW course. Then they place links using that tool. I think you already know all this from an earlier conversation, so I'm curious what makes that sound atypical for D2L? Because if it is atypical, I might want to ask our D2L people if they could please dispense with making us go through that extra step. If there were one global tool, and we were just prompted for the WW course launch URL when placing the links, that would be so much nicer.

drgrice1 commented 4 months ago

I must be misunderstanding something. It seems like I could have two distinct courses in the LMS that both have title "College Algebra". And the example from https://www.imsglobal.org/spec/lti-dl/v2p0 suggests that is the kind of "title" it references, as opposed to an id.

Yeah, so it seems this could be the same for two courses. It never would be at MWSU. Our college algebra courses have titles like "MAT-116-01 (Spring 2024)". I will look to see what I can use instead. Probably the context id. Unfortunately that is something more obscure that will be harder to find, which is why I chose the context title initially.

drgrice1 commented 4 months ago

@dlglin also uses D2L so can see if our use at PCC is a-typical. At PCC, with D2L and LTI 1.1, the D2L admins have configured https://webwork.pcc.edu/webwork2 to be a "tool provider". Each instructor of an LMS course must go to the external tools manager and create an "external learning tool" where they specify the actual path to their WW course. Then they place links using that tool. I think you already know all this from an earlier conversation, so I'm curious what makes that sound atypical for D2L? Because if it is atypical, I might want to ask our D2L people if they could please dispense with making us go through that extra step. If there were one global tool, and we were just prompted for the WW course launch URL when placing the links, that would be so much nicer.

That is not how LTI is designed to be used. You are doing what is needed to make it work with the way that WeBWorK has designed its LTI tool provider, which is also not how LTI is meant to be used. If you were using Pearson or WebAssign things would be different.

drgrice1 commented 4 months ago

If there were one global tool, and we were just prompted for the WW course launch URL when placing the links, that would be so much nicer.

That is actually exactly the point of this pull request. I suspect that if the external tool is configured correctly for this, then you could do exactly what you are saying.

Alex-Jordan commented 4 months ago

Stepping back to what I intended with my first question. I think you're proposing scenario (A):

My head went to how course instructors are not always super competent with accessing tech details in their LMS course. Here, even misspelling or miscapitalizing would mess it up, let alone getting the right string in the first place. Also course instructors don't have access to write to course.conf file unless they know workaround tricks. So I am wondering about scenario (B):

Where would that mapping be? One way would be using the database, with the WW admin inputting that LMS id in the same place where they enter or update "courseTitle", "courseInstitution", etc. It would be written to the course-name_setting table. The con here is that would be a project to extend the database and make those changes to the admin course.

An alternative would be to make a hash that the WW admin maintains in some higher-up config file, like authen_LTI.conf. But I like the DB entry better at first taste.

drgrice1 commented 4 months ago

That is how this is set up. The webwork admin manages the LMSCourseID (what this will be changed to using soon). I initially implemented this with a hash that was in webwork2.mojolicious.yml. I switched to what is used here instead. A database could be switched to as well, but it won't simplify things.

Note that we generally consider the course.conf file webwork admin editable, and not instructor editable (although it can be done).

drgrice1 commented 4 months ago

Also, note that you would not extend the admin course database if you were to store this in the database. You would use a new non-native table. It makes no sense and is a pain in the but to extend the admin database separately from other courses. This is what non-native tables are for. This pull request already adds another non-native table.

Alex-Jordan commented 4 months ago

Yeah, I guess I'm not suggesting extending the database. I mean add a row to the WW course's _settings table. (The table that stores the WW course's name, institution, etc.)

However it is done, it could be a challenge for the WW admin to be able to see (all by themselves) what the LMS course id is, when it is some other instructor's course. I know in may case, I'd request special access from our D2L admins to start peeking at those details for all math courses in D2L.

Once my local LTI 1.3 stuff is stable, I want to experiment with a new endpoint that is for creation of WW courses. Imagine an external learning tool in the LMS using this hypothetical new endpoint, and it creates a WW course automatically using these .../content attributes to make the WW course's name, title, etc. It would be good for the thing we've been discussing to be a database value here, handled just like the WW course title. And all automated.

drgrice1 commented 4 months ago

Creating a course is the next step up from this pull request. But this achieves much of what has made things difficult in the past. In Moodle with this pull request, you can add links to all assignments in a few mouse clicks. Unfortunately, Canvas does not support multiple links, so you still would have to add the links one at a time, but you don't have to manually create them. This takes care of that part.

drgrice1 commented 4 months ago

This has now been switched to using the context id instead of the context title. Unfortunately, that is not something easy to find in the LMS. For Moodle this is shown in the url when you are viewing the course (for example my local test instance shows http://localhost/moodle/course/view.php?id=2 and 2 is the context id). However, for Canvas, this is not shown anywhere. Even Canvas admins are not shown this context id anywhere in the user interface. There is a post where this is discussed at https://community.canvaslms.com/t5/Canvas-Developers-Group/How-to-view-the-context-id-for-a-course/td-p/234141. The only way I could find this was to enable $debug_lti_parameters and find it in the parameters when trying to use the content item selection end point.

drgrice1 commented 4 months ago

I want to add that this pull request was not implemented for Canvas (where I don't think it will be that useful due to Canvas' limitations) or D2L (where it may also not be of much use -- but I don't know on that). This was implemented for Moodle. As noted in my initial comment, this is needed for Moodle 4.3 (and beyond) since they have removed the capability of entering individual links as we have always done. See https://webwork.maa.org/moodle/mod/forum/discuss.php?d=8421 (where you have already commented). It is not realistic to expect that those that have been able to use per homework grade pass back now need to create a separate external tool and link for each assignment to continue this practice.

drgrice1 commented 4 months ago

I mean add a row to the WW course's _settings table. (The table that stores the WW course's name, institution, etc.)

However it is done, it could be a challenge for the WW admin to be able to see (all by themselves) what the LMS course id is, when it is some other instructor's course. I know in may case, I'd request special access from our D2L admins to start peeking at those details for all math courses in D2L.

If the LMS course ID were in the database, then you would need to provide a way for the webwork admin to add the LMS course ID to the database. It would not be realistic to expect the admin to add it manually by themself. Many webwork admins are not mysql savvy. I have a branch (a not fully developed initial approach) that adds another non-native table for this. Note that adding it to the course's settings table won't work since then in addition to loading each course's course environment in a loop (as this pull request currently does) you would need to load each course's database. That is just way to much overhead. In testing that more than tripled the memory used. The reason I didn't persist with the non-native table approach is because the only way to do that efficiently and correctly would be to completely change the way that LTI 1.1 and 1.3 work. The authentication parameters for both versions would need to be removed from the course environment, and instead stored in the database in this new non-native table.

Alex-Jordan commented 4 months ago

If the LMS course ID were in the database, then you would need to provide a way for the webwork admin to add the LMS course ID to the database. It would not be realistic to expect the admin to add it manually by themself. Many webwork admins are not mysql savvy.

We are miscommunicating. What I meant is isomorphic to how right now you could go into the admin course and change some other course's title, or its institution. No SQL savviness required. And I'm referring to it as "in the database" beacause it is in the settings table.

...you would need to load each course's database. That is just way to much overhead.

I don't follow this though. There is just one database, webwork, with many tables. And what you would do here is UNION all of the foo_settings tables (each of which is super short) and then SELECT the appropriate row. It seems like it would be an efficient process.

Alex-Jordan commented 4 months ago

You have to modify what I wrote there for it to make total sense, but I swear it makes sense that this ought to be doable with a single SQL query. I'm still in covid fog, so please forgive me.

drgrice1 commented 4 months ago

We are miscommunicating. What I meant is isomorphic to how right now you could go into the admin course and change some other course's title, or its institution. No SQL savviness required. And I'm referring to it as "in the database" beacause it is in the settings table.

Yes, my initial approach was going to add a page in the admin course for this.

...you would need to load each course's database. That is just way to much overhead.

I don't follow this though. There is just one database, webwork, with many tables. And what you would do here is UNION all of the foo_settings tables (each of which is super short) and then SELECT the appropriate row. It seems like it would be an efficient process.

It wouldn't be efficient because you have to bring up a separate database instance for each course. We don't have a way to join tables from different courses as you are suggesting.

Alex-Jordan commented 4 months ago

We don't have a way to join tables from different courses as you are suggesting.

OK, I'm starting to understand. With the limitations of WeBWorK::DB objects I see what you are saying. I'm suggesting using a more primitive, direct use of a perl SQL module to execute a query. Running a single SQL query string that gets pre-constructed using perl, with awareness of all the course IDs. I confess not having looked into the feasibility of that.

drgrice1 commented 4 months ago

While that could be done with raw SQL (even SQL::Abstract doesn't support table joins directly), I don't think that would be the best idea. In general, I don't see the benefit since you would still need to iterate through the course environments. That is unless you do as I said, and you rework LTI entirely to move the LTI authentication configurations into the database. It also doesn't make sense when a non-native table could be used, and we already have the structure for that. You don't need a course to access a non-native table (as this pull request already does).

Alex-Jordan commented 4 months ago

So at this point, if I'm still following (which is highly questionable!) an individual course needs to set something like $LMSCourseID = 'something'; in its course.conf file. This is the thing that is bothering me and why I want to brainstorm an alternative. We don't want instructors editing course.conf, so this really means a WW admin has to:

  1. determine the context id for each LMS course
  2. edit lots of course.conf files (yuck)

For me doing (1) with the Runestone server will be a nightmare with back-and-forth communication with various instructors at various schools using various LMSs. I'd seriously consider just not offering the feature to Runestone subscribers.

You seem to not want to use something like SQL::Abstract::More to do this the way I tried to describe using a single database connection and query that UNIONs (not JOINs) the settings tables. You said we'd still need to iterate through the course environments, but I have missed why we'd need to do that if we use a table union. Would it be better to talk about it Monday or Wednesday?

If it really does need to be a course environment variable instead of a database entry, then can I request that it go into the Course Config page, LTI tab? This way I could put the onus on instructors to figure out their own LMS course context id and enter it themselves.

drgrice1 commented 4 months ago

So at this point, if I'm still following (which is highly questionable!) an individual course needs to set something like $LMSCourseID = 'something'; in its course.conf file. This is the thing that is bothering me and why I want to brainstorm an alternative. We don't want instructors editing course.conf, so this really means a WW admin has to:

1. determine the context id for each LMS course

This step is unavoidable (at least for someone -- that may be the WW admin or the instructor). As I have already said, this context id is an obscure value that is not easily discoverable. Unfortunately, there is nothing else that uniquely identifies an LMS course. It was much simpler when this was the quite transparent course title, but unfortunately, the course title is not guaranteed to be unique.

2. edit lots of course.conf files (yuck)

This can be made easier. Perhaps...

For me doing (1) with the Runestone server will be a nightmare with back-and-forth communication with various instructors at various schools using various LMSs. I'd seriously consider just not offering the feature to Runestone subscribers.

That is fine. You can continue to use LTI as before. That is largely why this design was chosen. This is because both versions of LTI will continue to work as before without needing changes.

You seem to not want to use something like SQL::Abstract::More to do this the way I tried to describe using a single database connection and query that UNIONs (not JOINs) the settings tables. You said we'd still need to iterate through the course environments, but I have missed why we'd need to do that if we use a table union. Would it be better to talk about it Monday or Wednesday?

The reason you still need to iterate through the course environments is because the LMS course by itself is still not enough to identify the webwork course. That is at least in the case of multiple LMS's being served by the same webwork server. In the case of LTI 1.1 using a database could limit the courses and course environments you need to look at. But, it is only that easy with LTI 1.1. LTI 1.3 is much more complicated because on the login request you don't even get the LMS course name. So you have zero identification of any course on either side. All you have are the external tool authentication parameters (the platform id, client id, and deployment id), and until the JWT is decoded in the next request (the launch request) you don't have anything more. So the database doesn't help until that second request, and the first request your are stuck with iterating through the courses and loading each course's course environment to find a matching external tool configuration.

I don't understand why you keep insisting on using a union of tables which involves pulling in new dependencies (or constructing a messy raw SQL statement) when, as I keep pointing out, a non-native table can do the job much more concisely and with the infrastructure that webwork2 already has. I also don't think you have thought out the union concept all the way. For example, say you have a setting named 'LMSCourseID' saved in the settings table for courses 'a' and 'b' (and assume for simplicity these are all courses) with the values 'lms_a' and 'lms_b' respectively. Now you have a statement SELECT value FROM a WHERE name='LMSCourseID' UNION SELECT value FROM b WHERE name='LMSCourseID';. That might give you the list lms_a,lms_b (in that order) if you are lucky. But it might give you lms_b,lms_a. The order of what is returned by a UNION statement is not guaranteed. So you really don't know which LMS course id belongs to which course. You might be able to get creative with an ORDER BY statement and get the order to match up, but the point here is that this is messy, involves a new dependency, and a non-native table can do this quite concisely.

If it really does need to be a course environment variable instead of a database entry, then can I request that it go into the Course Config page, LTI tab? This way I could put the onus on instructors to figure out their own LMS course context id and enter it themselves.

This can be done. However, this conceptually limits the whole point of the content item request. If $external_auth = 1, then this wouldn't work at all. The point of the content item request is that there are no links in the LMS course that get you into the webwork course. That is what the content item request creates for you.

Unfortunately, everything is complicated by webwork2's rather flawed general structure with courses being rather separate entities each with its own set of database tables and even really its own configuration (inherited from a global site configuration).

Alex-Jordan commented 4 months ago

I have thought out the union thing all the way, and for brevity I just did not lay it out in the thread. You would select an additional column that is constant, the name of the course, alongside the value before making the union. It would end up like:

WWcourseID1 LMSCourseID1 WWcourseID2 LMSCourseID2 Etc

And I'm just not following you about the need to iterate through all environments once a single query to the database produces that. Let's talk live Monday or Wednesday.

On Sat, Mar 9, 2024, 4:13 AM Glenn Rice @.***> wrote:

So at this point, if I'm still following (which is highly questionable!) an individual course needs to set something like $LMSCourseID = 'something'; in its course.conf file. This is the thing that is bothering me and why I want to brainstorm an alternative. We don't want instructors editing course.conf, so this really means a WW admin has to:

  1. determine the context id for each LMS course

This step is unavoidable (at least for someone -- that may be the WW admin or the instructor). As I have already said, this context id is an obscure value that is not easily discoverable. Unfortunately, there is nothing else that uniquely identifies an LMS course. It was much simpler when this was the quite transparent course title, but unfortunately, the course title is not guaranteed to be unique.

  1. edit lots of course.conf files (yuck)

This can be made easier. Perhaps...

For me doing (1) with the Runestone server will be a nightmare with back-and-forth communication with various instructors at various schools using various LMSs. I'd seriously consider just not offering the feature to Runestone subscribers.

That is fine. You can continue to use LTI as before. That is largely why this design was chosen. This is because both versions of LTI will continue to work as before without needing changes.

You seem to not want to use something like SQL::Abstract::More to do this the way I tried to describe using a single database connection and query that UNIONs (not JOINs) the settings tables. You said we'd still need to iterate through the course environments, but I have missed why we'd need to do that if we use a table union. Would it be better to talk about it Monday or Wednesday?

The reason you still need to iterate through the course environments is because the LMS course by itself is still not enough to identify the webwork course. That is at least in the case of multiple LMS's being served by the same webwork server. In the case of LTI 1.1 using a database could limit the courses and course environments you need to look at. But, it is only that easy with LTI 1.1. LTI 1.3 is much more complicated because on the login request you don't even get the LMS course name. So you have zero identification of any course on either side. All you have are the external tool authentication parameters (the platform id, client id, and deployment id), and until the JWT is decoded in the next request (the launch request) you don't have anything more. So the database doesn't help until that second request, and the first request your are stuck with iterating through the courses and loading each course's course environment to find a matching external tool configuration.

I don't understand why you keep insisting on using a union of tables which involves pulling in new dependencies (or constructing a messy raw SQL statement) when, as I keep pointing out, a non-native table can do the job much more concisely and with the infrastructure that webwork2 already has. I also don't think you have thought out the union concept all the way. For example, say you have a setting named 'LMSCourseID' saved in the settings table for courses 'a' and 'b' (and assume for simplicity these are all courses) with the values 'lms_a' and 'lms_b' respectively. Now you have a statement SELECT value FROM a WHERE name='LMSCourseID' UNION SELECT value FROM b WHERE name='LMSCourseID';. That might give you the list lms_a,lms_b (in that order) if you are lucky. But it might give you lms_b,lms_a. The order of what is returned by a UNION statement is not guaranteed. So you really don't know which LMS course id belongs to which course. You might be able to get creative with an ORDER BY statement and get the order to match up, but the point here is that this is messy, involves a new dependency, and a non-native table can do this quite concisely.

If it really does need to be a course environment variable instead of a database entry, then can I request that it go into the Course Config page, LTI tab? This way I could put the onus on instructors to figure out their own LMS course context id and enter it themselves.

This can be done. However, this conceptually limits the whole point of the content item request. If $external_auth = 1, then this wouldn't work at all. The point of the content item request is that there are no links in the LMS course that get you into the webwork course. That is what the content item request creates for you.

Unfortunately, everything is complicated by webwork2's rather flawed general structure with courses being rather separate entities each with its own set of database tables and even really its own configuration (inherited from a global site configuration).

— Reply to this email directly, view it on GitHub https://protect2.fireeye.com/v1/url?k=31323334-501d2dca-3132feb7-454455534531-6e33837e705bed50&q=1&e=40f86b82-a235-47d2-84ec-bd20e970537e&u=https%3A%2F%2Fgithub.com%2Fopenwebwork%2Fwebwork2%2Fpull%2F2322%23issuecomment-1986840617, or unsubscribe https://protect2.fireeye.com/v1/url?k=31323334-501d2dca-3132feb7-454455534531-413a52229696df80&q=1&e=40f86b82-a235-47d2-84ec-bd20e970537e&u=https%3A%2F%2Fgithub.com%2Fnotifications%2Funsubscribe-auth%2FABEDOAFGVY3IGH3QZQ5AFPDYXL4G5AVCNFSM6AAAAABDEZ3GD2VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTSOBWHA2DANRRG4 . You are receiving this because you commented.Message ID: @.***>

drgrice1 commented 4 months ago

The context id is now stored in the database in a new non-native table. These context ids can be managed from a new page in the admin course.

drgrice1 commented 4 months ago

Note also, that if an instructor attempts to use a content item selection request from the LMS, then the context id for the LMS course will be displayed for the instructor. So it is much easier to find the context id now.

drgrice1 commented 4 months ago

This is now confirmed to work on D2L. Thanks to @dlglin and @Alex-Jordan for your help on figuring things out there.

As noted in the meeting, since all LMS's open this content item selection/deep linking request in an iframe, you need to either use $CookieSameSite = "None" and disable cross site tracking in the browser, or switch to $session_management_via="key". It seems that other external tool providers have the same issue, and even document it in their help (according to @dlglin).

Alex-Jordan commented 4 months ago

Is there any potential to handle session management differently for this endpoint?

drgrice1 commented 4 months ago

I don't know what you mean?

Alex-Jordan commented 4 months ago

Sorry if this doesn't make sense. But when starting a session where the purpose is to use the new context_selection endpoint, it could use $CookieSameSite = "None" (for example). But regular sessions for users still use $CookieSameSite = "Lax" (or whatever).

drgrice1 commented 4 months ago

Yeah, that makes sense. I think that might be possible. I will see what I can do with it.

drgrice1 commented 4 months ago

This now always uses SameSite None (ignoring the course environment $CookieSameSite setting for content item selection sessions. There is no reason to make this configurable I think because there is no other SameSite setting that will work in this case.

drgrice1 commented 4 months ago

Of course, depending on your browser, you may still need to ensure that your browser is not blocking third party cookies (i.e., that cross site tracking is allowed).

I said to make sure that cross site tracking is "disabled" before, but I should have said make sure cross site tracking is "enabled" or "allowed". I believe in Safari the option is titled "Prevent Cross-Site Tracking". So what I meant, is make sure that option is "disabled".

Alex-Jordan commented 4 months ago

That's nice that you found a way to do that! I was pessimistic it would be doable, but you found a way. I am in the midst of a few things at PCC:

  1. Setting up our production D2L to connect with our production WW server using LTI 1.3. For the moment, our WW server is 2.18, plus the minimum cherry-picked commits to make grade passback work.
  2. Setting up our test WW server to be on develop, with a public URL, so that our test D2L server can start trying these newer LTI features. It may be another week or two given how busy our staff is. I haven't got the public URL yet for the test WW server, and will need that before I can proceed.
Alex-Jordan commented 4 months ago

Do you have any recommendations for width and height on this, for the content selector iframe? I know D2L will ask for that, not sure about other LMSs.

drgrice1 commented 4 months ago

Moodle doesn't even give an option to set the height and width. The height and width that it uses is something like 770px by 600px.

Canvas does have the option, and I was using something like 600px by 600px. Although, I noticed that if you don't set the width and height it seems to basically give a dialog that fits the window. That may have been considerably wider than needed, but the height was good.

So probably it will take some experimentation to determine what is best. The width just needs to fit the length of your set ids plus the checkboxes and padding. My set ids are short (things like "Section 1.1"), so the title of the page "Available Content" is the widest element. The height should be as large as possible to avoid the need to scroll so much. Usually the list of all visible sets will be more than can fit in the entire window, and so some scrolling is unavoidable.

Alex-Jordan commented 4 months ago

Note also, that if an instructor attempts to use a content item selection request from the LMS, then the context id for the LMS course will be displayed for the instructor. So it is much easier to find the context id now.

Trying this out now and I'm getting "No WeBWorK course was found associated to this LMS course. If this is an error, please contact the WeBWorK system administrator." I have entered what D2L calls the "Org Unit ID" in the admin course LTI Course Map. I'm not 100% sure that's the ID that it would want, but it seems like the right choice given how D2L works. In any case, it's not showing the message that states the context id.

Alex-Jordan commented 4 months ago

I'm getting the "No WeBWorK course was found associated to this LMS course. If this is an error, please contact the WeBWorK system administrator." from lib/WeBWorK.pm, not from lib/WeBWorK/ContentGenerator/LTIAdvantage.pm.

drgrice1 commented 4 months ago

Try enabling debugging for LTI, and then look at what is shown when you try to access the content selection endpoint. That should show a dump of the JWT. In that there should be a claim named https://purl.imsglobal.org/spec/lti/claim/context with several sub claims, label, title, id, etc. The id is the one you need.

Alex-Jordan commented 4 months ago

Do you mean try to access the content selection endpoint by triggering the tool in the LMS? It still just takes me to a page with No WeBWorK course was found associated to this LMS course. If this is an error, please contact the WeBWorK system administrator.

I can tell that this message is generated by lib/WeBWorK.pm, not one of the LTI modules. The logic there is triggering this message because $c->stash->{'mojo.captures'}{courseID} is undefined.

Alex-Jordan commented 4 months ago

I did not witness the LTI admin setting up this tool, but he sent these screenshots, and I don't see anything wrong.

image

drgrice1 commented 4 months ago

The URL in that screenshot is missing webwork2. Do you have your server set to run on the root url?

Alex-Jordan commented 4 months ago

Oh, that's my bad then. I sent him the wrong URL. Thanks for the extra eyes on this.

On Tue, Mar 26, 2024 at 12:47 PM Glenn Rice @.***> wrote:

The URL in that screenshot is missing webwork2. Do you have your server set to run on the root url?

— Reply to this email directly, view it on GitHub https://protect2.fireeye.com/v1/url?k=31323334-501d2dca-3132feb7-454455534531-2204518d2e1a72fd&q=1&e=5505cfd0-1e09-45d2-8201-cfc183f02fde&u=https%3A%2F%2Fgithub.com%2Fopenwebwork%2Fwebwork2%2Fpull%2F2322%23issuecomment-2021332830, or unsubscribe https://protect2.fireeye.com/v1/url?k=31323334-501d2dca-3132feb7-454455534531-048c2c3f3d93ecee&q=1&e=5505cfd0-1e09-45d2-8201-cfc183f02fde&u=https%3A%2F%2Fgithub.com%2Fnotifications%2Funsubscribe-auth%2FABEDOABDOZ6CVW2R443RUW3Y2HGFNAVCNFSM6AAAAABDEZ3GD2VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDAMRRGMZTEOBTGA . You are receiving this because you were mentioned.Message ID: @.***>

-- Alex Jordan Mathematics Instructor Portland Community College

drgrice1 commented 4 months ago

I have done that too. I sent an instructor the link to their webwork course as https://jasper.csmp.missouriwestern.edu/course_id. The instructor replied, and said the link wasn't working!

Alex-Jordan commented 4 months ago

Progress. Now we see:

Screenshot 2024-03-26 at 1 15 30 PM

It seems that $c->stash->{lti_jwt_claims}{'https://purl.imsglobal.org/spec/lti/claim/context'} is an empty hash.

drgrice1 commented 4 months ago

There are options somewhere in the LMS that select what is sent with these requests. Most likely one of those that is needed is not turned on or not set.