Designing E-Learning 3.0 in gRSShopper - 13
E-Learning 3.0 - Part 1 - Part 2 - Part 3 - Part 4 - Part 5 - Part 6 - Part 7 - Part 8 - Part 9 - Part 10 - Part 11 - Part 12 - Part 13
The next step in the use of badges is to develop a mechanism for awarding them. This is something that can generate a lot of labour in a hurry (9 modules times 10 people means 90 badges issued...). I'd like to automate this as much as possible, but this means having a workflow so that I know what I'm automating.
A badge is awarded to a person on the basis of some evidence. In this course the evidence comes in the form a blog post. The posts are in some way submitted (I prefer to use the harvester but it would make sense to have a submission form as well, or even to use WebReference pings). The post is reviewed, and awarded the badge, which entails creating the badge with (say) Badgr and notifying them. Then a local record is created.
It works like this:
- Create module (associated with knowledge (in the future, maybe, associated with competencies or whatever)
- Create Task (associated with Module)
- Create Badge (associated with Badge)
- Receive Link (to a webpage) (associated with Task)
- Award Badge (to author associated with link)
- Notify author (via Badgr, WebReference, other?)
- Save record (to LRS? to Blockchain?)
To access the reader now, I open the PLE page, which looks like this when it starts (the contents are defined in a box called 'Start' and opens in the 'Reader' tab.
|Figure 150 - Start gRSShopper|
|Figure 151 - gRSShopper Reader|
The display of the harvested Link content is defined with a view, link_viewer. So I can very easily change what a harvested link looks like in the Reader.
There are two major problems with the Reader as it is currently designed:
- it's slow. Each time I hit the arrow to move on the the next Link, it loads the link at that time. There's no buffering, which means I'm waiting to see the Link.
- it has to be re-loaded every time I edit an item. If I click on 'Post', for example, it copies the link content into a post editing window, and I do whatever I need to do to process the post (right now, I have to manually associate the post with a module and actually publish the post). But when I open the edit window, all the tabs are reloaded, including the Reader tab, which means that when I'm done editing, I have to click on the grasshopper (to get to the home screen) and then click on "Read What's New" all over again.
In a nutshell: the Reader needs its own window. It shouldn't be reloaded every time I edit a post.
I though about this quite a bit and decided that the easiest may to do this is with a 'modal'. A modal is a window that opens on top of the other contents (it replaces the old 'pop-up' window, which was the bane of web users everywhere). Basically, it's just a web page 'div' I can toggle on and off.
When I first designed the PLE interface, I created a test modal (which I had planned to use for a login screen). It looked like this:
|Figure 152 - Default Bootstrap Modal|
Here's the Bootstrap modal:
|Figure 153 - Bootstrap Modal HTML|
Make it wider
To make it wider, then I modify the class of the inner wrapper using CSS to make it wider:
|Figure 154 - Make Bootstrap modal wider|
Open and close the modal
The modal is opened and closed using Booytstrap function. This makes it fade in and out gracefully. As well, Bootstrap functions are ARIA-compliant.
Bootstrap uses classes to control modals (and 'div' elements in general). Look at the button that closes the modal:
button type="button" class="btn btn-secondary" data-dismiss="modal"
Clicking the button basically calls 'data-dismiss' for the div called 'modal' (which is the outer wrapper of the modal). This function essentially changes the value of 'show' from 'true' to 'false'. Making the modal disappear. Note: it's still there. We're just not showing it al the moment.
To open the modal, we have a similar button:
button type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModal"
This button applies to a specific modal (data-target) and toggles it (change 'show' from false to true if false, or to true otherwise. So I have a way to open a modal from anywhere, and a way to close a modal from anywhere.
The thing is, it doesn't have to be a button. It can be (for example) a span. And that's what I did - I converted the gRSShopper logo so that it opens the modal.
|Figure 155 - Open Reader Button in Span using Image|
As mentioned before, the reader is generated by api.cgi (I should probably give it its own script). So I need to call the Reader from the script and put it in the modal. The URL for the Reader is very straightforward:
Putting it into a modal is also very straightforward:
button type="button" class="btn btn-primary" data-target="#exampleModal" onClick="$('.modal-content').load('https://el30.mooc.ca/cgi-bin/api.cgi?cmd=reader&table=link');"
The onClick listener will call load(), loading the URL into modal-content. We only actually need to do this once, when we start gRSShopper. The reader loads into the modal and then stays there, always available for use. Here's what it looks like when opened:
|Figure 156 - Reader in a Modal|
Now to make the one actual change to a CGI script so far: to redefine how main_window() draws the gRSShopper Icon when it creates an Editor, which I did by inserting the new tab code into Tab_Left_Sidebar() in grsshopper.pl.
There are some odds & ends to fix to make this all work perfectly, but this is the gist. Now I can get to the awarding of badges.
The 'view' is what we use to define how a record is displayed. In the Reader, we're using the 'link_viewer' view. Here's what it looks like at the moment:
|Figure 157 - link_viewer in the View Editor|
The easiest way to create interactivity is through some buttons. Here's the same view with some buttons added:
|Figure 158 - link_viewer View with buttons added|
You can see the buttons in the second line, in the statusbutton elements. This is gRSShopper-specific code that puts a button you can toggle on and off into the view. Here's what it looks like:
|Figure 159 - Buttons in the link_viewer view.|
The Reader (Again)
Even just testing different ways to set this up revealed some significant flaws in the feed viewer. In a nutshell, any time I make a selection from the top drop-down options, it reloads the viewer. But if the viewer is in a modal, that means it reloads gRSShopper as a whole, replacing it with the feed viewer by itself. I need to design the feed viewer so that it's in two frames, such that the top drop-down menu frame (which I call the viewer 'controls') change the content of the lower frame (which I call the viewer 'body' in which we display the arrow controls and the individual links.
So I did that. Basically what I did is this: when a person clicks on 'Submit' I take the current form contents, compose them into a URL calling an viewer.cgi search command to return a list of links I can view, and then load the results of that URL into the lower frame.
Now that the controls keep the viewer in the modal, I need to make sure than when I click the buttons, they stay in the modal as well. No problem for read/unread and starred/unstarred - they just send a command to the api and change the colour of the button. But for the badge I need an interactive element so I can select the badge to award.
It wasn't the most efficient thing in the world to do, but I decided to build this into the link view itself. I spent some time trying to make this another modal, but it's not straightfoward to build modals within modals in Bootstrap - it can be done, sure, and I tested a few solutions that worked, but making the modals different sizes proved to be too much. So I decided to just open and close a div in the link_viewer view, and then load it with the content I wanted.
It looks like this:
|Figure 150 - Viewer modal with 'Award a Badge' div opened|
The dropdown in the box automatically loads all badges that have been created. I'll eventually need a way to narrow this down by limiting the selection to a specific content, like (say) the course I'm currently working on (in general, gRSShopper needs a wider 'context' functionality to limit scope (and track working time) to (say) a given course, project, whatever).
Now the actual interface becomes very straightforward for the user. Clock on the button to open the viewer, select the search (if necessary, though it defaults to "what's new" so you don't usually need that), use the arrow buttons to step through the results, and click on the things you need to click on to interact with the links. Clicking on the badge button opens the toggle, select the badge (it may be preselected if I set up a way for the link to declare what badge it's seeking) and then click 'Award'. A message comes up saying the badge is awarded.
'Post' now works equally smoothly. Click on 'Post' and the edit screen loads in the background. Click on the screen (anywhere in the grey) to work with the edit screen. Right now all I have to do is type in the module and then click 'Publish'. If I make these steps automatic I won't even have to do that.
Now all I need to do is set up the badge-awarding mechanism in the back end, so that when I click on 'Award' it actually does through the process of awarding the badge.
Awarding the Badge
Here's what should happen behind the scenes:
- gRSShopper finds the author of the link being awarded the badge
- it associates both the link and the author with the badge
- it awards the badge using Badgr
- it sends a WebMention to the page notifying it that it has awarded the badge
- (optionally) it records a blockchain entry for the awarding of the badge
Finding the Author
Here's how I set this up (this is all new code):
When gRSShopper harvests a feed, it analyzes each link as it comes in to find the author of that link, create the author if necessary, and associate the author with the link. So most of the time, we just need to check the graph to find the author associated with the link.
In less common circumstances, the author might not be known. So gRSShopper looks to the feed and tries to find the author associated with the feed. If so, then this is the author associated with the link. If it still can't find an author, it gives up trying to award the badge, and reports an error (this might change for WebMention-only badges).
Once it finds the author, it needs to have an author identity to give to Badgr. Recall that Badgr requires either an email address, a URL, or a phone number. Sometimes none of these are available, so gRSShopper will offer to allow you to edit the Author entity (click on the link and the Author Editor appears under the modal). Edit the Author record, then click on the grasshopper and you're right back where you were, and you can award the badge.
Associating the Badge
Here's how I set this up (this is all new code):
First I check the graph to see whether this badge has already been awarded to this author. If it has, I stop the process here.
If not, I simply update the graph, associating bage:author and badge:link
(I actually wrote the code for the whole badge system pretty generically so it doesn't have to be a 'link' - it can be anything I choose to award a badge for, so long as it has an associated URL. Thus, eg., I could choose to set it up so I awarded a badge to an author for a company, provided I had a URL for that company to count as evidence.)
Awarding Using Badgr
Here I used the code I wrote earlier to award the badge.
I start up Badgr, which gives me an access code. Then Badgr requires information about the author, the badge, and the evidence. I provide the author information found earlier, the URL for the link as the evidence, and the Badgr entityId for the badge, which is stored in the badge record.
Here I need a WebReference endpoint, and I cannot assume I have one stored in the link record (because it might not be a link per se). So first I get the content of the evidence URL, analyze it to find the endpoint (the function is called find_webmention_endpoint()), and then if I find it, I send the WebMention.
Since the WebMention requires a page on my site to act as the location of the reference, I send the badge page (I should probably set up unique pages for each author - I need to get some feedback on what this page needs to contain so I'll leave this here.)
Record Blockchain Entry
I'm going to save this for the next article.
So that's the badging workflow. All of this described here works in gRSShopper. I haven't uploaded it to GitHub yet - I'll finish off the last bits here, then upload it as a new version.