Designing E-Learning 3.0 in gRSShopper - 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?)
We've already done the first three steps of this workflow, and they are basically one-time per course. We also receive the link via the harvester. Now we want a mechanism to award the badge in the harvester. What would that look like? Well, let's say, a button in the reader saying 'Award Badge'.

The Reader

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
Clicking on 'Read what's New' opens the reader:

Figure 151 - gRSShopper Reader
The reader is called from api.cgi and placed into the Reader tab. It defines the options and the arrows, including the 'Post' button (which is used to convert a 'Link' into a 'Post', and thereby to include it in the newsletter (I have in the past simply place Link items directly into the newsletter, but either way, I need a means to edit before I place into the newsletter.)

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
I stashed it in the 'Contact' tab (which disappears when you edit anything, because as mentioned, Edit reloads all the tabs). The button and modal are default defined by Bootstrap. My big problem with these modals was that I couldn't figure out how to work with them (especially, I couldn't figure out how to make them wider). So I spent some time working on this.

Here's the Bootstrap modal:

Figure 153 - Bootstrap Modal HTML
The contents of the modal are divided into a header, body and footer, which are all containted withing the content. The buttons are in the footer. The Bootstrap elements wrap around the content. The outside wrapper gives it a name and a role (role="dialog" aria-labelledby="exampleModalLabel"). The inside wrapper defines how to display the modal contents (by creating a class (class="modal-dialog") that can be modified by CSS).

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
I create some CSS that applies if I have a wider viewport (min-width: 768px) called .modal-xl that defines a wide width (90%) but puts a limit on it (max-width:1200px;). Then I apply that style to the inner wrapper. Presto, wide modal.

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.

This is important. As the Mozilla page notes, "Accessible Rich Internet Applications (ARIA) is a set of attributes that define ways to make Web content and Web applications (especially those developed with JavaScript) more accessible to people with disabilities." That's the main reason for me to keep using Bootstrap (and eventually, I will need to fix the tabs in the left and right sidebars to they are also 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
Put content in the modal

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('');"

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

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.
Interactive Views

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.
You can see the buttons right next to the title. Clicking them on and off changes the colour of the button, and toggles a yes/no value in the Link record. These two buttons - read/unread and starred/unstarred allows you to save or flag items for later viewing. Note that I have to make sure I have fields for link_read and link_star in the Link table definition (edited in the Form Editor).

The buttons have a JavaScript function associated with them, defined in make_status_buttons(). This function changes the colour and the value, and then calls parent.submit_function() to update the value in the database. submit_function() uses an Ajax request to contact api.cgi and sends an update command. It's all pretty seamless and something like this would work well for badge. Except: which badge? It's not just an off-on - I need a way to associate the link (and the author) with a badge.

(Time Passes)

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 div is the box titled 'Award a Badge'. It opens and closes when you clock the award button (the one with the little ribbon) (I had to update my 'font awesome' CSS to get this new icon, and I'll have to revisit this for the gRSShopper interface as a whole, to fix references to different versions of font-awesome).

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.

Send WebReference
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.

Popular Posts