Tuesday, March 21, 2017

Add mobile support to your Moodle plugin - part two

Part Two - Exploring the Moodle Mobile Add-on

In the first part of this series, I set up a development environment on my local machine. I am going to use this same environment to begin coding the mobile add-on portion of my Moodle questionnaire plug-in.

The documentation says I need to develop the mobile side as a standard Moodle Mobile add-on and then package it as a remote add-on, with four steps:
  1. Develop the required Moodle Web Services
  2. Develop a standard Moodle Mobile add-on
  3. Package the Moodle Mobile add-on as a remote add-on
  4. Include the remote add-on in your Moodle plugin
The first two steps are where my work will start. The second two steps are what I will need when I am ready to distribute what I have developed for anyone to use in their mobile apps with their Moodle sites. I am also going to start with step two, as this will help me understand what is needed in the mobile portion and what is needed from the Moodle plugin portion.

Recall, that when I try to access my plugin in the mobile app now, I get the screen:


My goal, for the start, is to provide enough code so that screen changes.

To start, I look at the current structure of the mobile app on my local server. Within it is a www/addons directory that contains all of the core mobile addons. I can see, that like a Moodle site, there is a mod subdirectory, and within that, a number of subdirectories corresponding to each of the supported core activity plugins.

For ease of development, I am going to add my plugin to the same location. That way, I can update my code within the mobile app, and access it with the browser, to see my work. Essentially, my local files will stand-in as the mobile app, and the questionnaire mobile addon will act as a core activity plugin.

For purposes of code storage, I am going to add an addons subdirectory to my questionnaire repository and add the code that will go into the mobile addon there. This code will not be required, nor run, on a Moodle site, but I want to keep it together with my Moodle plugin for development ease.

Through a productive discussion with the mobile development team, I have determined that getting my add-on registered with the mobile app requires code that registers a content handler. In the add-on structure, this happens in the main.js file in a config statement (full code here):
.config(function($mmCourseDelegateProvider) {
    $mmCourseDelegateProvider.registerContentHandler('mmaModQuestionnaire', 'questionnaire', '$mmaModQuestionnaireHandlers.courseContent');
});
Essentially, the mobile framework translates this code such that it knows there must be a services subdirectory and within that a file named handlers.js that contains a courseContent function. I create that structure with a very simple courseContent function as follows (full code here):
self.courseContent = function() {
    var self = {};
    /**
     * Whether or not the module is enabled for the site.
     *
     * @return {Boolean}
     */
    self.isEnabled = function() {
        return true;
    };
    return self;
};
In conversations with the developers, I have determined that I need an isEnabled function, that returns true if the plugin is enabled, for my addon to be acknowledged and not display the "plugin that is not yet supported" message. For now, I just send true with no logic confirming that. Eventually, this will call a web service from the actual questionnaire plugin on the Moodle site to determine this. I also know that when I run my local version of the mobile app using the ionic server, it will build my addons code into the app's main code file, www/build/mm.bundle.js.

After dropping these two files into my mobile app's www/addons/mod/questionnaire directory, I run the ionic server (removed some messages for brevity):
ionic serve --browser chromium
Running 'serve:before' gulp task before serve
[13:43:32] Starting 'build'...
[13:43:32] Starting 'sass-build'...
[13:43:32] Starting 'lang'...
[13:43:32] Starting 'watch'...
[13:43:41] Finished 'watch' after 9.04 s
[13:43:47] Finished 'sass-build' after 15 s
[13:43:47] Starting 'sass'...
[13:43:48] Finished 'lang' after 15 s
[13:43:49] Finished 'sass' after 1.92 s
[13:43:50] Finished 'build' after 18 s
[13:43:50] Starting 'config'...
[13:43:50] Finished 'config' after 17 ms
[13:43:50] Starting 'default'...
[13:43:50] Finished 'default' after 6.02 μs
[13:43:50] Starting 'serve:before'...
[13:43:50] Finished 'serve:before' after 3.15 μs
Running live reload server: http://localhost:35729
Watching: www/**/*.html, www/build/**/*, www/index.html, !www/lib/**/*
√ Running dev server:  http://localhost:8100
Ionic server commands, enter:
  restart or r to restart the client app from the root
  goto or g and a url to have the app navigate to the given url
  consolelogs or c to enable/disable console log output
  serverlogs or s to enable/disable server log output
  quit or q to shutdown the server and exit
ionic $
I can see a lot of build activity happening. I then point my browser to the server instance and see a lot more activity kick in. When I access my course, and then click on my questionnaire activity, I no longer get the "plugin that is not yet supported" message:


I don't get the questionnaire displayed, but I didn't expect to. I have at least learned how to impact the mobile app with my addon.

I also take a look at the www/build/mm.bundle.js to see if it now contains the code I created for the questionnaire addon. A quick search of the code reveals that it does. So I have confirmed that the mobile app has successfully picked up the questionnaire addon code and added it to the app.

In the next post, I will flesh out more of the mobile add-on code, and begin to look what the web services needed on the questionnaire plugin side.

Friday, March 3, 2017

Add mobile support to your Moodle plugin - part one

Part One - Setting up a Development Environment

With version 3 of Moodle came the Moodle Mobile app. Out of the box, most of the core Moodle plugins came supported. But, third-party plugins do not function by default in the mobile app.

In Moodle 3.1, the mobile app added support for "Remote add-ons". This support allows plugin providers to add support so that their plugin can function within the mobile app.

Now, I should point out that Moodle is currently working on a much simpler system for adding mobile support to your plugin, but it will be a while before it is ready. When it is ready, the work I am about to undertake will likely be moot.

This post will be the first in a series where I will attempt to add mobile support for my questionnaire module. I say "attempt", because this will be using some technologies that I am not familiar with so I will be learning as I go. I won't guarantee success, but I will document the efforts.

To start with, the main developer documents for Moodle Mobile are here. Looking through that list, I'm going to start with setting up a development environment on my Mac.

I already have Chrome installed on my Mac, so I can skip that step. The next step it says is to install "Node.js". Keep in mind, I don't really know what I am doing here (yet), so I'm not sure how this comes into play, but I'm going to follow instructions. :-)

The documentation suggest using Macports to install Nodejs. It also says I need to use version "v0.12.7". But, it looks like Macports only has versions from 4.* to 7.*. So, Macports is out. The documentation also provides a direct download link for "v0.12.7", and it has a Mac "pkg". So I download that and run the Mac installer. As a result, I have "node" installed at /usr/local/bin/node and "npm" installed at /usr/local/bin/npm.

The next step asks me to run the command npm cache clean. When I do this, I get a series of errors. which seem to indicate permission problems. Likely I need to elevate the permission level this command runs at, so I add a sudo to the front of the command and try again. The new command, sudo npm cache clean works fine. I'm going to assume I will need sudo for the rest of the commands in the document as well.

Next, I am asked to install "ionic". I excute the command:
sudo npm install -g cordova ionic
I get some issues displayed:

npm WARN engine cordova@6.5.0: wanted: {"node":">=4.0.0"} (current: {"node":"0.12.7","npm":"2.11.3"})
npm WARN engine request@2.79.0: wanted: {"node":">= 4"} (current: {"node":"0.12.7","npm":"2.11.3"})
npm WARN deprecated node-uuid@1.4.7: use uuid module instead
/usr/local/bin/cordova -> /usr/local/lib/node_modules/cordova/bin/cordova
/usr/local/bin/ionic -> /usr/local/lib/node_modules/ionic/bin/ionic
cordova@6.5.0 /usr/local/lib/node_modules/cordova
├── underscore@1.7.0
├── q@1.0.1
├── nopt@3.0.1 (abbrev@1.1.0)
├── update-notifier@0.5.0 (is-npm@1.0.0, semver-diff@2.1.0, chalk@1.1.3, string-length@1.0.1, repeating@1.1.3, configstore@1.4.0, latest-version@1.0.1)
├── insight@0.8.4 (object-assign@4.1.1, uuid@3.0.1, lodash.debounce@3.1.1, async@1.5.2, chalk@1.1.3, configstore@1.4.0, os-name@1.0.3, tough-cookie@2.3.2, request@2.79.0, inquirer@0.10.1)
├── cordova-common@2.0.0 (cordova-registry-mapper@1.1.15, ansi@0.3.1, semver@5.3.0, osenv@0.1.4, underscore@1.8.3, q@1.4.1, unorm@1.4.1, shelljs@0.5.3, minimatch@3.0.3, glob@5.0.15, bplist-parser@0.1.1, elementtree@0.1.7, plist@1.2.0)
└── cordova-lib@6.5.0 (valid-identifier@0.0.1, opener@1.4.1, cordova-registry-mapper@1.1.15, properties-parser@0.2.3, nopt@3.0.6, unorm@1.3.3, shelljs@0.3.0, semver@4.3.6, glob@5.0.15, dep-graph@1.1.0, xcode@0.9.1, elementtree@0.1.6, init-package-json@1.9.4, cordova-serve@1.0.1, request@2.47.0, tar@1.0.2, cordova-fetch@1.0.2, aliasify@1.9.0, plist@1.2.0, cordova-js@4.2.1, cordova-create@1.0.2, npm@2.15.11)

ionic@2.2.1 /usr/local/lib/node_modules/ionic
└── @ionic/app-generators@0.0.3
But they seem to be warnings about outdated "node". I know we are using an older version of "node", so I believe this will not be an issue.

Now I need to install the "bower" and "gulp" node packages.

Bower:
sudo npm install -g bower
/usr/local/bin/bower -> /usr/local/lib/node_modules/bower/bin/bower
bower@1.8.0 /usr/local/lib/node_modules/bower
Gulp:

sudo npm install -g gulp
npm WARN deprecated minimatch@2.0.10: Please update to minimatch 3.0.2 or higher to avoid a RegExp DoS issue
npm WARN deprecated minimatch@0.2.14: Please update to minimatch 3.0.2 or higher to avoid a RegExp DoS issue
npm WARN deprecated graceful-fs@1.2.3: graceful-fs v3.0.0 and before will fail on node releases >= v7.0. Please update to graceful-fs@^4.0.0 as soon as possible. Use 'npm ls graceful-fs' to find it in the tree.
/usr/local/bin/gulp -> /usr/local/lib/node_modules/gulp/bin/gulp.js
gulp@3.9.1 /usr/local/lib/node_modules/gulp
├── interpret@1.0.1
├── pretty-hrtime@1.0.3
├── deprecated@0.0.1
├── archy@1.0.0
├── tildify@1.2.0 (os-homedir@1.0.2)
├── minimist@1.2.0
├── v8flags@2.0.11 (user-home@1.1.1)
├── chalk@1.1.3 (escape-string-regexp@1.0.5, supports-color@2.0.0, ansi-styles@2.2.1, has-ansi@2.0.0, strip-ansi@3.0.1)
├── semver@4.3.6
├── orchestrator@0.3.8 (sequencify@0.0.7, stream-consume@0.1.0, end-of-stream@0.1.5)
├── gulp-util@3.0.8 (object-assign@3.0.0, array-differ@1.0.0, lodash._reescape@3.0.0, lodash._reinterpolate@3.0.0, lodash._reevaluate@3.0.0, beeper@1.1.1, array-uniq@1.0.3, replace-ext@0.0.1, dateformat@2.0.0, has-gulplog@0.1.0, fancy-log@1.3.0, vinyl@0.5.3, gulplog@1.0.0, lodash.template@3.6.2, through2@2.0.3, multipipe@0.1.2)
├── liftoff@2.3.0 (lodash.isstring@4.0.1, lodash.isplainobject@4.0.6, rechoir@0.6.2, extend@3.0.0, flagged-respawn@0.3.2, lodash.mapvalues@4.6.0, resolve@1.3.2, fined@1.0.2, findup-sync@0.4.3)
└── vinyl-fs@0.3.14 (strip-bom@1.0.0, graceful-fs@3.0.11, vinyl@0.4.6, defaults@1.0.3, mkdirp@0.5.1, through2@0.6.5, glob-stream@3.1.18, glob-watcher@0.0.6)
Again, there are some warnings about outdated releases. But I'm just going to leave them as is for now.

The next step talks about "Push notifications for Mac", and refers me to "https://cocoapods.org/" and the Moodle tracker item MOBILE-1970. It looks like this will resolve a problem I may have later on when running iOS versions of the app. So, I execute the command:
sudo gem install cocoapods
At the end of that, I have 27 new gems installed in /Library/Ruby/Gems/2.0.0/gems/cocoapods-1.2.0/.

I already have my git clone of the moodlemobile2 repository, so I set my current directory to it. From there, I execute:
sudo npm install
There are a number of warnings and errors that are output as this executes. Most of them appear to be problems with outdated modules. I am going to disregard those for now.

Now I run the following three commands, one after the other:
sudo ionic platform add android@5.1.1
sudo ionic platform add ios@4.1.0
sudo ionic state restore
All three seem to install okay. The only warnings I receive are for outdated dependencies.

So, I execute:
sudo bower install
And I get:
bower ESUDO         Cannot be run with sudo
So, guess I won't be using sudo here.

I rerun the command without sudo, and get a long list of files being installed. All of them went into the local repo below www/lib/.

So, gulp:

gulp
[16:30:43] Using gulpfile ~/www/moodlemobile2/gulpfile.js
[16:30:43] Starting 'build'...
[16:30:43] Starting 'sass-build'...
[16:30:43] Starting 'lang'...
[16:30:48] Finished 'sass-build' after 5.38 s
[16:30:48] Starting 'sass'...
[16:30:49] Finished 'lang' after 5.59 s
[16:30:50] Finished 'sass' after 1.7 s
[16:30:50] Finished 'build' after 7.46 s
[16:30:50] Starting 'config'...
[16:30:51] Finished 'config' after 16 ms
[16:30:51] Starting 'default'...
[16:30:51] Finished 'default' after 5.49 μs
Which again added files below my local repo.

At this point, I think I have everything ready to be able to access the Moodle Mobile app from my local development environment. The links tell me to start Chrome using:

    open -a "Google Chrome" --args --allow-file-access-from-files --disable-web-security --user-data-dir


That seems to just move my Chrome window to the front. Probably because I already had it open? I'll assume it's all good.

Now, to run the ionic server:
ionic serve --browser chromium
******************************************************
 Dependency warning - for the CLI to run correctly,    
 it is highly recommended to install/upgrade the following:  
 Please install your Cordova CLI to version  >=4.2.0 `npm install -g cordova`
 Install ios-sim to deploy iOS applications.`npm install -g ios-sim` (may require sudo)
 Install ios-deploy to deploy iOS applications to devices.  `npm install -g ios-deploy` (may require sudo)
******************************************************
WARN: ionic.project has been renamed to ionic.config.json, please rename it.
WARN: ionic.project has been renamed to ionic.config.json, please rename it.
Multiple addresses available.
Please select which address to use by entering its number from the list below:
 1) 10.120.211.137 (en4)
 2) 10.120.211.40 (en0)
 3) localhost
Address Selection: 3
Selected address: localhost
Running live reload server: http://localhost:35729
Watching: www/**/*, !www/lib/**/*
√ Running dev server:  http://localhost:8100
Ionic server commands, enter:
  restart or r to restart the client app from the root
  goto or g and a url to have the app navigate to the given url
  consolelogs or c to enable/disable console log output
  serverlogs or s to enable/disable server log output
  quit or q to shutdown the server and exit
Since I chose the 'localhost' option I enter 'http://localhost:8100' in my chrome browser window. The browser displays the Moodle Mobile app! I configure it to my local running copy of Moodle, and I'm in business!

Using the simulated mobile browser, I navigate to my test course:


I can see a "questionnaire" instance, so I select that:


And, I see that I really do need to add mobile support to the plugin!

Next posts will be me trying to make this happen.