Moodle Mobile 2 (Ionic 1)

Revision as of 13:26, 29 June 2015 by Juan Leyva (talk | contribs)

Jump to: navigation, search

For user documentation see Moodle Mobile.

Note: This page is a work-in-progress. Feedback and suggested improvements are welcome. Please join the discussion on or use the page comments.

General Overview

Moodle Mobile 2 (MM2) is the official mobile app for Moodle. MM2 is an HTML5 app that uses the Ionic hybrid apps framework. Former Moodle Mobile versions (1.x) used jQuery and Backbone (se Moodle Mobile 1 for documentation about the former app).

Basically, the app is a Web Services client that uses REST as protocol to obtain and send information to your Moodle installation/s.

Interaction with the phone and compiling/packaging is done using the Cordova framework (also commonly known as Phonegap).

MM1 plugins are not going to work in MM2.


  • Moodle 2.4

Future versions will require the last minor versions if Web Services are not backported.


MM2 is built with ionic framework, ionic is a full stack mobile hybrid apps that uses AngularJS framework.

AngularJS is a structural framework for dynamic web apps. It lets you use HTML as your template language and lets you extend HTML's syntax to express your application's components clearly and succinctly.

AngularJS is quite popular, you may find lot of sites with updated documentation and free courses, the same applies for Ionic.

Ionic / AngularJS

The app architecture is the typical for an AngularJS app. We use controllers, factories, directives, providers, etc..

The app is designed in a modular way, most of the functionalities are AngularJs modules that implements plugins (in a similar way to Moodle plugins).

There are a set of core services for handling authentication, configuration, plugin management, cache, etc… and specific modules for tasks like internationalization.

Most of the services are implemented as factories.

The app uses AngularJs logging to create an application log (only in developer mode).

App structure and naming conventions

The app is structured in two parts: core and addons.

In core we have the code that the app needs to be able to work (basic version). There are services, components, directives, filters, etc.

The components inside core are similar to addons, but the app needs them to work: login, sidemenu, courses, etc. Each component needs to define a main.js file to initialize it and to define the module.

Naming conventions for core:

  • The module name for components needs to be mm.core.componentname and it should be defined in the component’s main.js. The services, filters and directives that don’t belong to a component need to use the module mm.core (defined in www/core/main.js).
  • All the services names need to start with $mm, followed by the service name in camel-case. For example, $mmSitesManager.
  • The filters and directives use camel-case starting with mm. For example, mmFormatText.
  • The controllers names need to start with mm (without dollar), followed by the controller name in camel-case. The controller name needs to contain Ctrl to easily identify it as a controller. For example, mmCourseListCtrl.

In addons we have the official MM2 addons. Each addon can have services, controllers, templates and lang files. The addon needs to specify a main.js file to initialize the addon and to register itself into one or more delegates (this determines where will the plugin be shown).

Naming conventions for addons:

  • The module name for addons needs to be mm.addons.addonname and it should be defined in the addon main.js.
  • All the services names inside an addon need to start with $mma, followed by the service name in camel-case. For example, $mmaForumData.
  • The controllers names need to start with mma (without dollar), followed by the controller name in camel-case. The controller name needs to contain Ctrl to easily identify it as a controller. For * example, mmaForumListCtrl.


The app is translated to several languages, we use a module called angular-translate that supports lazy-loading of languages and pluralization. All the languages files are shipped with the app except the ones for remote addons.

To load the language files we use angular-translate-loader-partial, a loader developed by angular-translate authors that lets you load “parts” of the translation table. In MM 2.0 we’ll only have a single language file under www/build/lang, but with this loader we should be able to add remote plugins lang files too when they are implemented.

Each plugin/component needs to define its own lang files if they need any. These files need to be placed inside a lang folder and must be named languagecode.json, where languagecode is the ISO code of the language (i.e. en.json). The structure of those files must be the following:

   “langkey””: “The string to be shown”

All the lang files are merged into a single lang file per language inside www/build/lang. In this merge process, the strings are prefixed to prevent naming collisions. The prefixes are:

  • Core strings (www/core/lang): mm.core.*
  • Component strings (www/core/components/mycomponent/lang): mm.mycomponent.*
  • Addons strings (www/addons/myaddon/lang): mma.myaddon.*

For example, if the example lang file from above was inside the folder www/core/courses/component/lang, the merged file would have an entry like this:

“”: “The string to be shown”

To use the language string, the prefixed version must be used. For example, to use the langkey string from the courses component we need to do this:

$translate('’) - Returns a Promise
Template:'’ - Used in a view, shows the translated string.

Angular-translate is initialized inside www/core/lib/lang.js, where we set English as the fallback language and we load the current language based on the user app settings and the device locale. Notice that locales like “en-uk” or “es-mx” will be treated as “en” or “es”.


The app needs to store some information in the mobile device: the current site information, the courses where a user is enrolled, a cache of the REST calls, etc…

For app storage we use the ydn-db javascript library, it supports storage in IndexedDB, WebSQL and localStorage implementing polyfills, promises, automatic storage system detection, etc...

We have two levels of databases: the app database (1 DB for the whole app) and the site databases (1 DB per site). All the data that is site-related is stored in the site database, so it’s easier to retrieve and delete (when a site is deleted).

The app DB is defined inside www/core/lib/app.js. This file implements a provider with the functions $mmAppProvider.registerStore and $mmAppProvider.registerStores to add new stores to the app DB.

The site DB is defined inside www/core/lib/site.js. This factory manages the creation and delete of the sites DB, creating a DB per site.

IMPORTANT: Modifying the schema of an already existing store deletes all its data in WebSQL Storage (in IndexedDB the data is kept). If a store schema needs to be modified, the data should be manually migrated to the new store.


./scss/app.scss The main SCSS file which a themer should be using to customise the look and feel of the app. That will also be the file we start from to make colour palettes. As core developers we will use this app to override the core ionic variables, as well as modifying existing ionic styles.

./www/core/scss This directory holds the styling components we want to offer to our themers. The variables associated with those components should be held in the same files and flagged as !default to allow themers to override them. We should not override those variables in ./scss/app.scss because default values should be provided by us and work with our core app style.

./www/core/components/X/scss & ./www/core/addons/X/scss Those directories will contain styles that are specific and should not be re-use by other components/addons. The same goes with the variables from those addons, they should not be re-used from another component/addon.

To prevent naming collisions between SCSS files, the classes and IDs should be prefixed. The naming conventions for those prefixes are similar to the rest of Moodle Mobile 2 prefixes:

  • Core styles should be prefixed with mm-. For example, .mm-split-pane-menu.
  • Components styles should be prefixed with mm-componentname-. For example, .mm-login-myclass.
  • Addons styles should be prefixed with mma-addonname-. For example, .mma-grades-table.


We use Cordova to access the mobile device APIs. We try to use only core Cordova plugins but sometimes that’s impossible. If we have to use additional plugins (like Webintents, local notifications, custom URL schemes) we use only plugins well-maintained, generally those listed here:

For integrating Cordova plugins we use ngCordova; ngCordova is a collection of AngularJS extensions on top of the Cordova API that make it easy to build, test, and deploy Cordova mobile apps with AngularJS. The main feature of ngCordova is that instead callbacks it uses promises, so it integrate very well with AngularJS.


Tests are written using Jasmine and runned using Karma. Karma is a JavaScript command line tool that can be used to spawn a web server which loads your application's source code and executes your tests.

Jasmine is a behavior-driven development framework for testing JavaScript code. It does not depend on any other JavaScript frameworks. It does not require a DOM. And it has a clean, obvious syntax so that you can easily write tests.


The app comes with a predefined set of addons: messages, course contents, notifications, etc. All these addons are inside the www/addons folder. The structure of the files inside an addon is explained in the section App structure and naming conventions.

An addon can be shown in several places. The app has one delegate per each place an addon can be shown, so each addon can register itself to any set of delegates.

The delegates we have are:

  • $mmSideMenuDelegate: The addons registered in here will be shown in the app’s side menu.
  • $mmCourseDelegate: The addons registered in here will be shown under each course in the course list.
  • $mmUserDelegate: The addons registered in here will be shown in a user page.
  • $mmSettingsDelegate: The addons registered in here will be shown in the preferences page.


We use angular’s $log service to log app messages.

A decorator has been applied to $log to add the current date and time and the component to the $log service. If the following instruction is used:

$log.debug(“My message”)

A message like the following is written in the log:

4/27/2015, 11:31:33 AM : My message

To add the component to the message, the following must be done before using $log:

$log = $log.getInstance(“Component”)

From now on the $log messages will have the component, so using the previous $log.debug instruction would print the following now:

4/27/2015, 11:31:33 AM Component: My message

Log can be enabled and disabled. The default value for this is defined in the constant mmCoreDebugDefault.

Any user can enable/disable the log in the app no matter the value of mmCoreDebugDefault. To do so, the following instruction needs to be used:


That change is permanent, so if the app is closed it’s going to remember if the log was enabled or disabled.

There are 5 levels of messages:

  • debug: To write messages for debug purposes. It’s the most used in the app.
  • error: To log error messages.
  • warn: To log warnings (for example, it is used to warn that the Moodle version should be updated).
  • log: To log a generic message. Not used.
  • info: To write an info message. Not used.


MoodleMobile has a $mmEvents service that allows sending and listening to events in the app.

These are the events defined in the app: sessionexpired: Thrown when the user token expires. login logout

Opening links

Links that are not part of the app (e.g. not buttons) should be opened in a separate browser, or other, depending on the platform they are being opened with. A directive was created to make this easy:


This will catch a click on the link and open it as it should, whether it opens a file, a URL, etc…

Please note that the content present within the directive mm-format-text is handled automatically.

External content

External content is anything that is not locally accessible by the app. If some content has to be made available offline, or saved locally for performance reason, you should be using $mmFilepool. This factory allows you to download files locally, add them to a queue to be downloaded asynchronously, invalidate the local copy, etc…

You can also use the directive mm-external-content which will automatically do the job of adding the files to the queue if they are not find locally.

Please note that at the moment it was decided that mm-external-content would ony work for content that is coming directly from Moodle (pluginfile.php), though it is possible for a developer to be using $mmFilepool with any kind of URLs.


See Moodle Mobile Plugins Development.

Source code:

Creating your custom app

See Moodle Mobile Customization.


Is the app a replacement of the MyMobile theme?

No, see

What is the difference between a native app and a Mobile specific theme or responsive theme?



See also