Working with the Canvas API in plain JS – Pt 1

With the Canvas app not supporting JQuery (unlike the web application, which does), it make sense to write all custom JS in plain Javascript so it works across both platforms. This makes sending requests to the api harder than it would be in JQuery – apparently, JQuery $ajax request already has the necessary credentials attached.

With some help from various wonderful people on the Canvas Developers forum, the code below will query the Canvas API as the logged in user and log to the console(!).

var csrfToken = getCsrfToken();
console.log('crsfToken', csrfToken);
fetch('/api/v1/conversations/unread_count',{
          method: 'GET',
          credentials: 'include',
          headers: {
               "Accept": "application/json",
               "X-CSRF-Token": csrfToken
          }
     })
     .then(status)
     .then(json)
     .then(function(data) {
          console.log(data);
     })
     .catch(function(error) {
          console.log('Request failed', error);
     }
);

/*
 * Function which returns csrf_token from cookie see: https://community.canvaslms.com/thread/22500-mobile-javascript-development
 */
function getCsrfToken() {
     var csrfRegex = new RegExp('^_csrf_token=(.*)$');
     var csrf;
     var cookies = document.cookie.split(';');
     for (var i = 0; i < cookies.length; i++) { var cookie = cookies[i].trim(); var match = csrfRegex.exec(cookie); if (match) { csrf = decodeURIComponent(match[1]); break; } } return csrf; } /* * Function which returns a promise (and error if rejected) if response status is OK */ function status(response) { if (response.status >= 200 && response.status < 300) {
          return Promise.resolve(response)
     } else {
          return Promise.reject(new Error(response.statusText))
     }
}
/*
 * Function which returns json from response
 */
function json(response) {
     return response.json()
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

Some things to note:

  • I’m using the fetch() method as I like working with promises – fingers crossed that it’ll work in the app!
  • using James Jones csrf extracting function to populate the X-CSRF-Token header;
  • using “Accept”: “application/json” to tell Canvas that we want json and it then doesn’t include the while(1) stuff (try it without to see what I mean)
  • using credentials: ‘include’ to tell fetch() to include the Cookie in the request as this is what Canvas seems to expect

The next step is to do something useful with the api – the first goal is to add next and previous buttons to pages linked to in the mobile app (currently you will only see these if you launch into a page through the app-specific Modules page).

Remote Debugging Samsung Galaxy 7 Edge with Chrome

If you’re confused as to why something which is working well on your Canvas site, isn’t working on the Student App, Google Chrome gives you a great way to find out what’s going wrong with its Remote Debugging Android Devices

Annoyingly, it’s not quite as easy as one would hope on a Galaxy S7 Edge or, by the sounds of it, any Samsung device, on Windows 10 anyway.

Extra steps required above what’s on the page above, for me anyway, were:

  1. Enabling USB Debugging. On A Galaxy S7, to turn developer mode on, you have to go to About phone | Software info then tap Build number 7 times(!?). You can then access the Developer options (at bottom of Settings) to switch on USB Debugging.
  2. On Windows 10, to even get the Device to be recognised, you need to:

If you have a Mac available, you don’t seem to need to bother with Step 2 above – it just works!

 

Notes on Styling Instructure Canvas

WARNING: Note that themes allow you to upload .css and .js files for both the web and app versions of your Canvas site BUT the app does not provide JQuery so you’ll need a plain JS version of your code for the app…or pull in JQuery.

Layout & css

While Canvas appears to provide parts of Bootstrap 3, JQuery UI and Flexbox Grid, not everything is available within an individual page. Some things to be aware of:

Grid

When setting out grids, instead of:

<div class="row">

, use,

<div class="grid-row">
Responsive, centred YouTube embed

Sadly, Canvas doesn’t seem to have included the Bootstrap styles for this, so I use the CSS upload to add the standard Bootsrtap responsive embed styles.

HTML

<div class="grid-row center-xs">
    <div class="col-xs-12 col-md-9 col-lg-6">
        <div class="embed-responsive embed-responsive-16by9"><iframe class="embed-responsive-item" src="https://www.youtube.com/embed/DFthTUAAMRI?rel=0&amp;showinfo=0" width="300" height="150"></iframe></div>
    </div>
</div>

(Note that the width and height attributes are inserted automatically by Canvas but are ignored)

CSS

.embed-responsive {
  position: relative;
  display: block;
  height: 0;
  padding: 0;
  overflow: hidden;
}
.embed-responsive .embed-responsive-item,
.embed-responsive iframe,
.embed-responsive embed,
.embed-responsive object,
.embed-responsive video {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 100%;
  border: 0;
}
.embed-responsive-16by9 {
  padding-bottom: 56.25%;
}
.embed-responsive-4by3 {
  padding-bottom: 75%;
}
Progress Bar

The Bootstrap progress bar doesn’t appear to be available within pages. Instead, use the JQueryUI progress bar (for the web version of Canvas anyway):

I have styled this in one of our courses as follows.

HTML

<div class="ui-progressbar ui-widget ui-widget-content ou-ProgressBar module_6" style="width: 100%; height: 15px;" role="progressbar" aria-valuemax="100" aria-valuemin="0" aria-valuenow="19">
    <div class="ui-progressbar-value ui-widget-header ui-corner-left ou-ProgressBarBar" style="width: 19%;"></div>
</div>

CSS

.ou-ProgressBar {
 height: 15px;
 margin-bottom: 3em;
}

.ou-ProgressBar.module_6 .ou-ProgressBarBar {
 border: 1px solid #640D14;
 background-image: linear-gradient(to bottom, #640D14, #640D14) !important;
}

TL;DR

In fact, I actually use JS to insert the HTML above based upon a div like this:

HTML

<div id="module_6" class="ou-insert-progress-bar">19</div>

JS

if($('div.ou-insert-progress-bar').length){
    $('div.ou-insert-progress-bar').each( function(index) {
        //get data from insert-progress-bar div
        var value = $(this).text();
        var className = $(this).attr('id');
        //UC first letter
        var viewName = className.toLowerCase().replace(/\b[a-z]/g, function(letter) {
            return letter.toUpperCase();
        });
        viewName = viewName.replace(/_/g, ' ');
        //create progress bar and title
        $(this).after(''+
            '<h4 class="ou-space-before-progress-bar">Current position in ' + viewName + ':</h4>' +
            '<div class="ui-progressbar ui-widget ui-widget-content ui-corner-all ou-ProgressBar ' + className + '" style="width: 100%; height: 15px;" role="progressbar" aria-valuemax="100" aria-valuemin="0" aria-valuenow="'+ value +'">' +
                ' <div class="ui-progressbar-value ui-widget-header ui-corner-left ou-ProgressBarBar" style="width: '+ value +'%;"></div>' +
            '</div>' +
            '');
    });
}
//End by deleting div.insert-progress-bars
$('div.ou-insert-progress-bar').remove();

Far TL;DR

If you also want this to appear in the app (where neither JQuery nor JQuery UI are available), you’ll need to use the plain JS version of the above and update the styles:

JS

//From: https://stackoverflow.com/questions/4793604/how-to-insert-an-element-after-another-element-in-javascript-without-using-a-lib
function insertAfter(newNode, referenceNode) {
    referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}

/* Trying plain JS so that it works in the app as well */
function domReady () {
	//get all elements with classname ou-insert-progress-bar
	var progressBarPlaceholders = document.getElementsByClassName('ou-insert-progress-bar');
	Array.prototype.forEach.call(progressBarPlaceholders, function(progressBarPlaceholder) {
		var value = progressBarPlaceholder.innerHTML;
		var className = progressBarPlaceholder.id;
		//UC first letter
		var viewName = className.toLowerCase().replace(/\b[a-z]/g, function(letter) {
			return letter.toUpperCase();
		});
		//replace underscore with space
		viewName = viewName.replace(/_/g, ' ');
		//create our new element
		var progressBarContainer = document.createElement("div");
                progressBarContainer.innerHTML = ''+
                    '<h4 class="ou-space-before-progress-bar">Current position in ' + viewName + ':</h4>' +
                    '<div class="ou-ProgressBar ' + className + '" style="width: 100%; height: 15px;" role="progressbar" aria-valuemax="100" aria-valuemin="0" aria-valuenow="'+ value +'">' +
                    ' <div class="ou-ProgressBarBar" style="width: '+ value +'%;"></div>' +
                    '</div>';
                //insert it after the placeholder using the function insertAfter
                insertAfter(progressBarContainer, progressBarPlaceholder);
                //now delete the placeholder
                progressBarPlaceholder.parentNode.removeChild(progressBarPlaceholder);
       });
}
//Function to work out when the DOM is ready: https://stackoverflow.com/questions/1795089/how-can-i-detect-dom-ready-and-add-a-class-without-jquery/1795167#1795167
// Mozilla, Opera, Webkit 
if ( document.addEventListener ) {
	document.addEventListener( "DOMContentLoaded", function(){
		document.removeEventListener( "DOMContentLoaded", arguments.callee, false);
		domReady();
	}, false );
// If IE event model is used
} else if ( document.attachEvent ) {
	// ensure firing before onload
	document.attachEvent("onreadystatechange", function(){
		if ( document.readyState === "complete" ) {
			document.detachEvent( "onreadystatechange", arguments.callee );
			domReady();
		}
	});
}

CSS

.ou-ProgressBar {
  height: 15px;
  margin-bottom: 3em;
  width: 100%;
  border-radius: 3px;
  border: 1px solid #aaa;
  background: #fff;
  overflow: hidden;
}

.ou-ProgressBarBar {
  height: 100%;
}

.ou-ProgressBar.welcome .ou-ProgressBarBar {
  border: 1px solid #D89E17;
  background-image: linear-gradient(to bottom, #D89E17, #D89E17) !important;
}

Materilize CSS: Change Tab text and indicator colour using only HTML and colour classes

When using the excellent Materialize CSS framework, it isn’t immediately obvious how to to change both text and indicator/underline colour of Tabs using only HTML and the Materialize colour classes (i.e. no changes to CSS).

To change the text color, just add the text-colour classes to the <a> tags, e.g.:

<a class="indigo-text text-darken-4" href="#test1">Test 1</a>

To change the indicator/underline colour, you can insert this before the </ul>:

<div class="indicator indigo darken-4" style="z-index: 1;"></div>

To wrap this up as a complete, 2-tab, example:

<div class="row">
  <div class="col s12">
    <ul class="tabs">
      <li class="tab col s6">
        <a class="active indigo-text text-darken-4" href="#test1">Test 1</a>
      </li>
      <li class="tab col s6">
        <a class="indigo-text text-darken-4" href="#test2">Test 2</a>   
      </li>
      <div class="indicator indigo darken-4" style="z-index: 1;">
      </div>
    </ul>
  </div>
  <div id="test1" class="col s12">Test 1</div>
  <div id="test2" class="col s12">Test 2</div>
</div>

A dashboard view of Modules in Canvas

Canvas’s Modules page is a great way to show students both what is available to them and their progress through those materials. However, I’m concerned that:

  • it may be overwhelming to see everything in your course on a single page
  • it present a very linear view of a course – this may be appropriate in some circumstances but is certainly not appropriate in many HE courses.

The typical solution to this appears to be to create a course home Page where you can wrap/organise your content as you see fit. Two great guides to this are:

However, this approach has two disadvantages:

  • I’ve already spent time and energy organising, titling and indenting my content – why should I have to create a page of links pointing to that content?
  • The Modules page gives you great feedback on progress through your course – which content you have ‘completed’, what’s next, etc. A links-based home page is going to hide all this from you.

So, what we need is some way of making the Modules page more navigable. There are plenty of requests for this on Canvas’s Community pages and it would indeed be more sensible for Instructure to tackle this themeselves. However, if you’d like something that will work in the shorter term and are happy to add your own js/css, you might find the approach below interesting.

Autogenerating a Modules ‘Dashboard’

With some fairly simple JQuery, a custom stylesheet and a module naming convention it’s relatively simple to autogenerate a ‘dashboard’ of Canvas-style cards which gives students an overview of, and an easy way to navigate to your modules. ‘Top’ buttons on every Module give a quick way back to the dashboard no matter how many modules your site contains.

The JS:

Note that this assumed that Modules are named:

Module x. My module description

The code splits the Module name, based on the characters specified by the ‘delimiter’. So, in this case, my title will be ‘Module x’ and the sub-title will be ‘My module description’. You will need to chnage this caharacter if you want to split on say ‘:’.

and the CSS:

You’ll see that this css is very largely the same as the Canvas Dashboard styles but I have renamed them ‘ou-‘ so that we can make sure they are available on the Modules page and so that we can alter them as necessary.

Hope that’s useful to someone. It’s a work in progress e.g thinking about pulling in Module progress indicator(s), number of items in module, etc. Do get in touch is you spot any problems.

ical/ics feed from Rapla

We’ve been using Rapla resource booking system for years now to manage room (and associated resources such as laptops, furniture, etc) bookings and have been using the Export2iCal plugin to pull data from Rapla and display it in various other ways. I thought it would be useful to just jot down a couple of features of Rapla/iCal that may not be obvious for anyone else thinking of doing this.

  1. The url for the ical feed is:
     http://myraplaserver/rapla?page=ical&user=username[&file=fileName]

    Where:

    • username is the name of the user who has published a view on Rapla
    • fileName [for the optional file parameter] is the name of the view to return. If the file parameter is omitted, it will return the Default view.

    The thing to note here is that the ical feed returned contains the events for those resources that are shown in the Default or other named view for the user, username.

  2. In order to access a view via ical, it must first be published. This is achieved by selecting the desired view (or Default) and then clicking the Publish button, and ticking the ICAL Publish checkbox.
  3. To set which resources are shown in a given view, simply Ctrl-click to highlight them then click the Save button for the view.

 

 

 

CakePHP 3: Access a method from one Behavior (or the Table class) in another Behavior

Please note that this post refers to CakePHP 3.

Short answer is, as long as you have added both Behaviors in the Table class, you can call a method from one Behavior in another as follows:

class SecondBehavior extends Behavior {
    public function secondBehaviorMethod() {
        return $this->_table->firstBehaviorMethod();
    }
}

I couldn’t find anything about this in the docs, so initially I hoped/presumed that, as long as I had added both Behaviors to my Table class, it was simply a case of calling the method as I would in a Table, so:

In MyTable.php:

public function initialize(array $config) {
    ...
    $this->addBehavior('FirstBehavior');
    $this->addBehavior('SecondBehavior');
}

In FirstBehavior.php:

class FirstBehavior extends Behavior {
    public function firstBehaviorMethod() {
        return "done";
    }
}

In SecondBehavior.php:

class SecondBehavior extends Behavior {
    public function secondBehaviourMethod() {
        return $this->firstBehaviorMethod();  //Gives "Call to undefined method" error
    }
}

However, this just results in a “Call to undefined method” error. However, Behaviors have the $_table property (see the Behavior API) that allows you to access other methods of the current Table. Therefore, just adding _table to the call to firstBehaviorMethod ($this->_table->firstBehaviorMethod()) fixes the issue. As well as accessing methods from other added Behaviors, you can also use find ($this->_table->find(…)), etc.

New assessment services for maths, statistics and free text from MSDLT

 

Mobile MedLearn
A view of MedLearn in Moodle, shown on a phone screen

MSDLT can now offer formative assessments in mathematics and statistics. To overcome the common criticism of multiple choice questions – that the answer is on the screen – our new service marks typed algebraic and numerical answers, understanding concepts such as simplification, significant figures, decimal places, etc.

Our aim is to improve students’ understanding of the criteria against which they are assessed – an area highlighted by students in the 2016 National Student Survey – without a corresponding increase in academic marking time. Computer-marked self-assessment should enable them to obtain feedback on their performance over a wider cross-section of their course’s learning objectives.

Cancelling Algebraic Fractions
Cancelling algebraic fractions. The CAS generates random numbers for the questions, and uses them to mark and display the worked answers.

Behind the scenes our service connects to a Computer Algebra System so questions can use random variables and can address probability, inference and descriptive statistics. Quizzes launch from seamlessly from WebLearn, without requiring a separate login.

As well as the algebraic and numeric questions, we can offer ordering; drag and drop; and a variety of multiple choice questions. We are currently also piloting computer marking of phrases and sentences with Experimental Psychology.

Embryo Grading Exercise
Video of embryos with questions and cue points

The system has already delivered a maths bridging course for incoming Biochemistry students over summer 2016. It has also enabled students on the MSc in Clinical Embryology to take a novel ‘authentic’ assessment of their ability to assess the viability of embryos for implantation.

“Excellent practice, Thanks!” – Student on MSc in Clinical Embryology

Self-test questions in Moodle

In an earlier post I outlined plans for migration of MedLearn to Moodle. The technical work is now done except for editor plugins. The migration is well underway.

The tricky bit was getting the self test questions to work. The purpose of self test questions is to enliven content and enhance learning. They are not intended to be an assessment, although it may be useful to record the answers.

The Quiz activity provides the best range of question types but it also forces questions into an assessment with a very specific workflow (start attempt; answer questions; finish; submit; review). We wanted a much looser workflow which did not present questions as a quiz. The answer (suggested by Tim Hunt) was to embed a preview question in an iFrame. We chose the Book module for the content into which the questions were embedded.

Students log in to our VLE (Sakai). They then launch an LTI tool which automatically provisions them in Moodle. They are assigned an adapted set of permissions to avoid the issues highlighted by Tim Hunt in this post. The LTI provider plugin does all the provisioning, including the assignment of the role with the permissions.

Questions are inserted into the book content using Generico filter and Atto plugin. I’ve defined a template which just takes the question number and applies the correct iFrame. A Javascript frame resize script triggers on load (and on change) so that the question appears seamlessly in the content. The plugin also has an Atto editor plugin to simplify editing.

I have modified the preview PHP so that the title and config have been stripped off, leaving just bare question with answer box and ‘check’ button. The preview PHP still accepts the querystring configuration so I passed &correctness=0&marks=0&markdp=0&feedback=1&generalfeedback=1&rightanswer=1&history=0 to it in the Generico template.

The script tag to load the JavaScript resize code is in AdditionalHTML config.

We’ve also made the content more mobile friendly using Bas Brand’s Bootstrap  3 theme and more semantically relevant HTML5 tags such as <figure>, <cite>, <dfn> and <dl>. We are about to start authoring Atto plugins to support these tags. JQuery is used to for image swaps, replacing the mouseovers and flash which aren’t mobile friendly.

The only missing feature which would be really nice to have is a way of browsing the questions from the question bank for insertion. It’s currently a little clunky to find the question ID for use in the Generico Atto plugin.

My thanks go to Tim Hunt, Juan Leyva (LTI Provider) and Justin Hunt (Generico) for making the tools which underpin this integration.

Sakai Entity Broker methods – the missing documentation

An attempt to collect together some understandable documentation on how to use the Sakai (@Oxford Sakai=WebLearn) Entity Broker (EB) methods in the face of continued frustration with Sakai documentation accessed via /direct/describe (@Oxford https://www.weblearn.ox.ac.uk/direct/describe).

I’m only going to cover methods to return JSON  as that’s all we ever use and am not bothering to describe the format of the returned JSON as it’s easier just to look at what’s returned in the browser.

  • Announcement
  • Assignment
  • Content
  • Forums (including Topic, Thread and Message)
  • Group
  • Lesson
  • Lessons
  • Membership
  • Polls
  • Portal-hierarchy
  • User

announcement

Gets the announcements for a site for the logged in user.

/direct/announcement/site/siteId.json

e.g @Oxford

https://weblearn.ox.ac.uk/direct/announcement/site/d1506490-a468-46ca-a915-417d9d11be2f.json

assignment

Gets the assignments for a site for the logged in user.

/direct/assignment/site/siteId.json

e.g @Oxford

https://weblearn.ox.ac.uk/direct/assignment/site/d1506490-a468-46ca-a915-417d9d11be2f.json

content

Gets the contents of the Resources tool.

/direct/content/resources/group/siteId[/subFolderPath].json[?depth=noOfSteps]

where:

  • subFolderPath (optional) is the path a subfolder (path relative to root of Resources)
  • noOfSteps (optional) is the no of ‘layers’ of content to return (seems to have a maximum value of 3?). If not specified defaults to 1.

e.g @Oxford

https://weblearn.ox.ac.uk/direct/content/resources/group/1e969f60-9ebf-48b7-bb0f-54f598de05da/Online%20assessments%20documents.json?depth=2

forums (including topic, thread and message)

Get all fora for a site:

/direct/forums/site/siteId.json

Get all topics for a forum:

/direct/forums/site/siteId/forum/forumId.json

Get all conversations for a topic:

/direct/forums/site/siteId/forum/forumId/topic/topicId.json

Get a fully populated conversation or message. This retrieves the message tree from the specified message downwards. If the messsageId is the top level message in a conversation, the entire conversation is returned:

/direct/forums/site/siteId/forum/forumId/topic/topicId/message/messsageId .json

…and to link directly to individual parts of forums:

For direct forum link url is :

/portal/hierarchytool/siteId/discussionForum/forum/dfForumDirect.jsf?forumId=forumId

Direct forum_topic link is :

/portal/hierarchytool/siteId/discussionForum/message/dfAllMessagesDirect.jsf?topicId=topicId

Direct link to individual message is :

/portal/hierarchytool/siteId/discussionForum/message/dfViewMessageDirect.jsf?messageId=messsageId &topicId=topicId&forumId=forumId

group

Get the membership of a group.

/direct/membership/group/groupId.json

whee groupId can be found by looking at the address of the page when editing a group in Siet Info | Manage Subgroups – may also be possible to find by name with /direct/site methods?

e.g @Oxford

https://weblearn.ox.ac.uk/direct/membership/group/8513fad8-656f-4265-9573-f00b8a781d02.json

lesson

Get the contents of a lesson.

/direct/lessons/lesson/lessonId.json

where lessonId is the id or entityId returned by direct/lessons (above)

e.g @Oxford

https://weblearn.ox.ac.uk/direct/lessons/lesson/35538.json

lessons

Get a list of the lessons in a site.

/direct/lessons/site/siteId.json

e.g @Oxford

https://weblearn.ox.ac.uk/direct/lessons/site/1e969f60-9ebf-48b7-bb0f-54f598de05da.json

Note that, in Sakai 11 at Oxford anyway, the lessons EB method will not currently (03/10/16) return lessons to access users – only those with elevated rights e.g for the same call:

As a maintainer:

{"entityPrefix": "lessons", "lessons_collection": [
    {
    "contentsURL": "https:\/\/weblearn.ox.ac.uk\/direct\/lessons\/lesson\/78676",
    "gradebookPoints": null,
    "id": 78676,
    "lessonTitle": "Module 1",
    "releaseDate": null,
    "hidden": false,
    "entityReference": "\/lessons\/78676",
    "entityURL": "https:\/\/weblearn.ox.ac.uk\/direct\/lessons\/78676",
    "entityId": "78676"
    }
]}

As an access user:

{"entityPrefix": "lessons", "lessons_collection": [

]}

membership

Get information about membership of a given user in a given site

/direct/membership/userId::site:siteId.json

where:

  • userId is the id (Sakai’s internal reference) of user about which you want info (obtainable for logged in user from user/current.json as below)
  • siteId  is the Sakai siteId.

e.g @Oxford

https://weblearn.ox.ac.uk/direct/membership/d00ca345-4e31-485c-00e9-0fc880da2dc0::site:d1506490-a468-46ca-a915-417d9d11be2f.json

polls

Can’t find any way to get a list of polls for a site!

Get poll text and options

/direct/polls/pollId/poll-view.json?includeOptions=true

where:

  • pollId is the id (Sakai’s internal reference) of the poll. This is most easily obtained by hovering over the poll name in the edit view and picking out the number(!)
  • includeOptions (boolean) specifies whether to also return the options for the poll and;
  • includeVotes (boolean) specifies whether to also return the votes cast

e.g @Oxford

https://weblearn.ox.ac.uk/direct/polls/pollId/poll-view.json?includeOptions=true

portal-hierarchy (maybe only @Oxford?)

Provides a list of all the sites (to which the user has access) under a given site – one level down only.

/direct/portal-hierarchy/site.json?portalpath=pathFromRoot

e.g @Oxford

https://weblearn.ox.ac.uk/direct/portal-hierarchy/site.json?portalpath=/medsci

user

Information about the logged-in user – mainly used to get interal Sakai id for use in other methods.

/direct/user/current.json

e.g @Oxford

https://weblearn.ox.ac.uk/direct/user/current.json

more..

I’ll try to remember to add to these as I use them.