Moodle Mobile 2 (Ionic 1)
Note: This page is a work-in-progress. Feedback and suggested improvements are welcome. Please join the discussion on moodle.org or use the page comments.
Moodle Mobile | |
---|---|
Project state | Implementation |
Tracker issue | MOBILE-153 |
Discussion | https://moodle.org/mod/forum/discuss.php?d=206736 |
Assignee | Juan Leyva |
General Overview
MM (Moodle Mobile) is the official mobile app for Moodle. MM is an HTML5 app that uses common web technologies.
Basically, is a WebServices client that using REST as protocol obtain and send information to your/s Moodle installation/s.
The layout is created using HTML5, CSS3, interaction with the Phone and packaging is done using Phonegap. For calling the WebServices, manipulating the DOM and interact with Phonegap we use jQuery as our Javascript framework.
This app is a replacement of the old Mobile app, see: Change in our Moodle mobile app strategy
Features
The features are going to be the same that the old Mobile app plus:
- PUSH Notifications
- Mobile and tablet version (Responsive design)
- Support for contrib plugins
- Support for external settings: Extra CSS
- Support for external translation
- New development and debugging features
Features for future versions:
- Calendar sync
- Offline browsing and posting in forums
- Offline grading
Requirements
- Moodle 2.4
Future versions will require the last minor versions if WebServices are not backported
Technologies used
- Media queries for screen width and height http://www.w3.org/TR/css3-mediaqueries/
- jQuery http://jquery.com/
- jQuery UI http://jqueryui.com/
- jQuery touchSwipe http://labs.skinkers.com/touchSwipe/
- matchMedia https://github.com/paulirish/matchMedia.js/
- Backbone and Underscore http://backbonejs.org/
- RequireJS requirejs.org
Phonegap
MM uses Phonegap for using the Moodle hardware/software feautres like Camera, Audio/video recorder, access to the filesystem, etc.. Phonegap is loaded in the index page of the app (jQuery, the main lib of the app are also loaded in the index page)
jQuery
For DOM manipulation and some helper functios, is a requirement of others libraries listed bellow
jQuery UI
Dialogs and also for enhancing forms elements (buttons, checkboxes, etc...)
jQuery touchSwipe
For detecting mouse gestures
matchMedia
Javascript detection of media queries
Backbone and Underscore
For storage (Models and Collections), Underscore templates, and URL Routing
RequireJS
For handling modules dependencies and loading plugins
Structure of the app (only main directories)
css/ - App main layout and styles img/ - App images lang/ - Default lang lib/ - Main libraries plugins/ - Plugins test/ - Test data when developing in Test mode config.json - Main app configuration file (presets) index.html - Index page of the app
Getting and sending information to Moodle
MM uses standard Moodle WebServices for getting and sending information to Moodle. AJAX (jQuery) and REST + JSON are the technologies used.
Notice that mobile HTML5 apps doesn’t have crossdomain restrictions so you can make AJAX calls to any domain.
External settings
In your Moodle installation you can specify and extra CSS file for customizing remotely the app.
This CSS is downloaded and stored in the device database for further uses.
You can also translate the app strings using your Moodle translation tool because strings are sync using a specific WebService
Storage
We use HTML5 localStorage that is cross browser implemented and there are libraries that can work of top of it.
We use Backbone Models and Collections as a wrapper for localStorage
Notice that localStorage have some limitations like a top of 5MB size in some devices and also is consider "Temporal data" for some devices like new iPhones
Internationalization
The app includes a language file in JSON (English is the default language)
Once the user is logged-in, the app automatically synchronizes the string translations using a specific WebService, translations are stored in the device local database in JSON format
Plugins
Overview
Plugins allow developers to extend the app functionalities. A plugin it’s a subdirectory that implements a set of required functionalities.
Types of plugins
- General: Interactions over the global app. Like the Notifications, Upload, Help and Web
- Course: Interactions over a course. Like course contents or participants
- User: Interactions over an user. Like send a message, add as a contact, write a private note
- Settings: Additional settings for the app
Structure of a plugin
We are going to use this plugin: https://github.com/moodlehq/moodlemobile/tree/master/plugins/participants as an example:
Plugins have always the same structure (no matter the type of plugin), the app uses a Register App where you declare a Plugin and then register it to the App
A plugin must be a directory under the plugins/ dir containing:
- A main.js file, where the plugin is declared
- Templates html files (if used)
Defining a plugin
var templates = [
"root/lib/text!root/plugins/participants/participants.html",
"root/lib/text!root/plugins/participants/participant.html"
];
define(templates,function (participantsTpl, participantTpl) {
For loading plugins we use the RequireJS library, a plugin is a module that we must define using the lines above:
We define a new module that depends on the html files (templates) listed above, once loaded, the contents of the HTML files will be available in the participantsTpl and participantTpl variables.
Notice that the app doesn't automatically load the plugins, you need to edit the config.json file for indicating the plugins to be loaded
"plugins" : ["notifications", "upload", "contents", "participants", "addcontact", "addnote", "sendmessage", "yourpluginname"],
Global settings of the plugin
settings: {
name: "participants",
type: "course",
menuURL: "#participants/",
lang: {
component: "moodle"
}
},
The name of the plugin (must be the same that the directory name)
The type of plugin (general, course, user, settings)
The main link to the plugin in the App
The language file to use, here we are using moodle because we are using the main language file, for plugins, you should use the Moodle franken-style plugin name where the lang file is located in your Moodle installation i.e: "local_mycustomplugin"
This means that you need to put your language files in a local plugin (called mycustomplugin) in your Moodle installation, this local plugin should be the same where you are going to add your custom WebServices
Storage
storage: {
participant: {type: "model"},
participants: {type: "collection", model: "participant"}
},
Here we declare the "tables" and their "structure"
If you are not familiar with Models, you must think that participants is a table that contains the participant's records.
Notice that we are not indicating the "fields" of the "tables" this is not necessary.
Routes
routes: [
["participants/:courseId", "participants", "showParticipants"],
["participant/:courseId/:userId", "participants", "showParticipant"],
],
For avoid binding DOM elements to functions, we use a Route navigation model, any time an user clicks on a button or link, the hash part of the URL is changed and the function linked to the hash part is triggered. So, if you want to trigger the function ShowParticipant, you must create a button or link that points to "#participant/courseid/userid", when the user click on the link the function showParticipant(courseid, userid) is triggered One of the advantages of using roues, is that we preserve the browser history so the "back" button on an Android device will work as expected without any extra code.
Functions
showParticipants: function(courseId) {
MM.panels.showLoading('center');
if (MM.deviceType == "tablet") {
MM.panels.showLoading('right');
}
var data = {
"courseid" : courseId
};
MM.moodleWSCall('moodle_user_get_users_by_courseid', data, function(users) {
var tpl = {users: users, deviceType: MM.deviceType, courseId: courseId};
var html = _.template(MM.plugins.participants.templates.participants.html, tpl);
MM.panels.show('center', html);
// Load the first user
if (MM.deviceType == "tablet" && users.length > 0) {
MM.plugins.participants.showParticipant(courseId, users.shift().id);
}
});
},
showParticipant: function(courseId, userId) {
var data = {
"userlist[0][userid]": userId,
"userlist[0][courseid]": courseId
}
MM.moodleWSCall('moodle_user_get_course_participants_by_id', data, function(users) {
// Load the active user plugins.
var userPlugins = [];
for (var el in MM.plugins) {
var plugin = MM.plugins[el];
if (plugin.settings.type == "user") {
userPlugins.push(plugin.settings);
}
}
var tpl = {"user": users.shift(), "plugins": userPlugins, "courseid": courseId};
var html = _.template(MM.plugins.participants.templates.participant.html, tpl);
MM.panels.show('right', html);
});
},
Here are the main plugin functions, as you can see we create a function for any single route defined.
As you can see, you don't need much code:
The showParticipants function does:
- Show a loading icon in the center panel
- If we are using a tablet, an additional loading icon is displayed in the right panel
- Then we call a Moodle Webservice, using the MM.moodleWSCall function, indicating the name of the WS, the parameters, an a callback function.
- We can add an extra parameter, indicating that this function perform write actions in the server, see: https://github.com/moodlehq/moodlemobile/blob/master/plugins/addnote/main.js#L36
- When the Webservice returns info, the next step is to render a Template using the app template function and then display the template in the center panel.
Notice that for referencing the template, we use the MM global object: MM.plugins.participants.templates.participants.html (MM . registered plugins . name of the plugin . property . template name . contents of the template
- If we are using a tablet, we load in the right panel the first participant calling the showParticipant function
Notice that for referencing the function, we use the MM global object: M.plugins.participants.showParticipant
Templates
templates: {
"participant": {
model: "participant",
html: participantTpl
},
"participants": {
html: participantsTpl
}
}
</code
Here we declare the templates we are going to use for further references.
Notice that the attribute html cointains the HTML template files contents (as mentioned above).
Register the plugin
MM.registerPlugin(plugin);
</code
With this single line we register the plugin in the global Namespace MM (the main library of the app)
Testing and developing
Ripple
Requeriments: Google Chrome browser + Ripple mobile enviroment emulator plugin (http://ripple.tinyhippos.com/)
You must run Google Chrome in Unsafe mode adding this params: --allow-file-access-from-files --disable-web-security
IMPORTANT: I strong recommend you to create a new link or application launch called "Google Unsafe" and use it only for testing the app
"Path to chrome\chrome.exe" --allow-file-access-from-files --disable-web-security
Open the index.html file in the Google Chrome unsafe and click on the Ripple icon to activate the emulator
Once opened in the Ripple settings block change Cross Domain Proxy to Disabled
Please note that some functionallities (camera, audio recording, contact) will not work in the emulator
Platform SDK
Install the Android or iPhone SDK and follow instructions in http://phonegap.com/start/
Local web server
If you deploy the html files in a server under the same domain that the Moodle your are going to connect to you can test the application without emulator or changing the Security settings of your browser.
I.e:
Your Moodle at:
http://myhost.com/moodle
Your app at:
http://myhost.com/umm/
Notice that the mobile - related modules will not work.
Custom Phonegap plugins
Phonegap hast a set of limited features, sometimes it’s necessary implement features that are missing in Phonegap.
Push notifications is one of the most important features missing that can be solved developing a plugin for Phonegap for every specific platform you want handle this kind of notifications.
Developing a Plugin for phonegap means develop using the specific platform framework and language (Objetive c for iOs).
One of the disadvantages of custom plugins is that you can still use the Phonegap online Build service for cross compiling but the feature will not be present because the online services does not allow to add custom plugins.
The app can detect if the app have been compiled using the online service for disabling the plugins that uses custom Phonegap plugins automatically
Build process
Limitations and disadvantages of HTML5 apps
- Less speed and smoothness
- Limited to the Mobile functionalities provided from Phonegap
FAQ
Is the app a replacement of the MyMobile theme?
No, see http://moodle.org/mod/forum/discuss.php?d=206736#p901751
Whats the difference between a native app and a Mobile specific theme or responsive theme?
See http://moodle.org/mod/forum/discuss.php?d=206736#p901475
Also http://moodle.org/mod/forum/discuss.php?d=206736#p901751