@chrismhs
This is an interesting concept. I'm going to take a different approach than others have suggested. In the end, you may decide their approach is easier to accomplish because it doesn't require anything special.
I'll offer a suggestion before I go into answering what you really asked about. If you want to keep the resource near the module item, then you could have the primary module item and then, right below it and indented a level (or two), have a link to the resource. This keeps the resource right next to the item as opposed to having it in a different module. The primary module item for the students needs to be published, but the ones for the teacher should remain unpublished.
As for actually adding a link to each module item ...
I checked the list of supported placements for external tools and do not see that location in any of them. That means that this is likely custom JavaScript that has been created that will add this. That custom JavaScript could point to an external system or it could point to pages within Canvas. However, custom JavaScript can only be installed at the [sub]account level, not at the course or user level (although people have suggested that there should be more flexibility). That means that a Canvas admin would have to install it and it would need to be for the entire account (or a subaccount). That's unlikely to happen unless a third-party vendor is supplying the solution.
The benefit of an external solution is that you can create a link -- using existing content on the modules page -- that will link to the external system and pass the module item ID. That external system could, if necessary, query Canvas to get information about the module item and present information when you click on it. If it has existing resources, it shows them to you and if not, it allows you to create them.
That external system is likely going to be a commercial solution. You will need to host the information on their systems and that takes resources. There may be open source solutions, but then you would need to set up a server to manage it and that takes resources. Your institution may be able to support that or they may not. None of those are solutions that an instructor can set up on their own without the help of IT and your Canvas admin.
Let me talk about something that you can do on your own, completely within Canvas.
We get around the inability to install a global custom JavaScript package through the theme editor by using userscripts. Userscripts are scripts ran in the browser for a particular user. They have to be installed using a userscript manager such as Tampermonkey. You, the user, control which browsers you install it in. If you're using a school computer, they may block userscripts (they can be used maliciously so it can be a security risk if you're not sure what you're running). Basically, it allows you to inject JavaScript into your page in addition to whatever JavaScript is already being loaded by the site. That can often be bypassed if you have a non-school computer.
Because it doesn't run for everyone, it doesn't slow other people down and poorly written code doesn't crash the whole system. I develop my code using userscripts and then may publish them for all of our users after I've worked out the kinks.
How could you implement this within Canvas.
It depends on how your resources are created, but the simplest way would be to have a resource that is named the same as your main module item but with an extra word, phrase, prefix, or suffix in the name. For example, if you have a published student page called "1.1 Introduction to Calculus" then you could have another, unpublished page, called "1.1 Introduction to Calculus Resources". Or you might name it "Resources: 1.1 Introduction to Calculus". It comes down to where they appear in the list of pages. With a suffix, it's sorted next to the regular item and with a prefix, all the pages occur in one place. You may want to use something like "ZZ: 1.1 Introduction to Calculus" just to put them all that the end. If you don't ever use content pages for the students, then you could make the entire content pages system be the resources system and just use the same name as the assignment.
When the modules page is loaded on a computer that has the userscript running, it could can the page and decide what to do with each item.
Some information comes preloaded with the page and is available for immediate access. Think about clicking on the + to add a module item and some is already there while others have to be loaded. The list of assignments, quizzes, pages, and discussions is immediately available. The list of files is loaded after you select File. That means that if you want to use content pages for your resources, this can be super-fast.
If your resources are files, it's going to be much slower, especially with a lot of files.
There is another potential reason for using content pages. You could create a link to a content page for every assignment, discussion, quiz, or content page without checking to see if it exists. Then, when you click on the button, it would allow you to create a new content page if it didn't already exist. This adds extra complexity and I don't really recommend it, although you could scan for the existing page and make your link go to a "create new page" if the page doesn't already exist.
Files as resources are not recommended for a couple of reasons. Not only do you have to load the list of files before you can create the link to them, but you won't be able to add a new file through this like you could with a new content page.
I'll move forward assuming that you want to use content pages. Again, this is not working code, just explaining what could be done.
You could get the information about the assignment (or other types) from the Document Object Model (DOM) using JavaScript. Take the title and look through the list of pages that was loaded for the + button and look for a match.
Here is some code to get the list of existing pages.
function getExistingPages() {
const items = [];
document.querySelectorAll('#wiki_pages_select select option').forEach(e => {
const item = {id:e.value, title:e.textContent};
items.push(item);
});
return items;
}
You can get a list of all the module items on the modules page with this code.
document.querySelectorAll('ul.context_module_items li.context_module_item');
Within each result is everything that Canvas shares about the module item.
Where is code that will take every module item (regardless of type) and look for an existing page with the same name but the word "Instructions" at the end. That's because that's how I name my resource pages so it's something I could test.
function updateModuleItems() {
const existingPages = getExistingPages();
document.querySelectorAll('ul.context_module_items li.context_module_item').forEach(mi => {
const title = mi.querySelector('div.module-item-title a.ig-title.title').getAttribute('title');
const resourceTitle = `${title} Instructions`;
const matchingPage = existingPages.find(e => e.title === resourceTitle);
if (matchingPage) {
// We have a match, need to do something with it
console.log(matchingPage);
}
});
}
That code just shows the matching pages, you still have to do something with it.
There is an element with CSS selector 'div.ig-admin' that will give you the right side where the published icon is. You could insert your icon with a link to the resource page as the first element there. Note that we don't have the URL to the page, but we do have the ID and pages can be accessed by ID. /courses/1234/pages/3456 is just as valid as /courses/1234/pages/backwards-elimination (assuming the ID for the backwards-elimination page is 3456).
Again, that's not a complete working example, but it gets you most of the way there. You still have to add code to make sure that it only runs on the modules page and to add the icons with links. But at least it's enough to see what might be involved in creating such a system and that can help you decide whether it's something you want to pursue or not.
Also note that the code wasn't fully debugged to see what happens if you rename a module item from the original name. I do that with files because "the_table.pdf" isn't as nice looking as "The Table!" but you can rename module items and you would have to decide whether you wanted to use the original name or the module item name.
Using files for resources adds a whole other level of complexity to the mix and slows things down as it loads the list of files. My course has lots of stuff in it: I had 323 module items, 187 pages, and 540 files. It already takes too long to load the modules page but it takes another couple of seconds to load the list of files. The modules page would display and then your script could start showing the icons for file resources after the couple of seconds has passed. With content pages, it would be nearly instantaneous once the modules themselves loaded.