Note:

If you want to create a new page for developers, you should create it on the Moodle Developer Resource site.

Moodle Mobile 2 (Ionic 1) Plugins Development: Difference between revisions

From MoodleDocs
No edit summary
 
(31 intermediate revisions by 4 users not shown)
Line 1: Line 1:
{{Moodle 2.4}}
{{Template:WillNotMigrate}}
{{Moodle Mobile 2 (Ionic 1)}}


=== Overview ===
IMPORTANT: This documentation will be obsolete in May 2018, please [https://moodle.org/mod/forum/discuss.php?d=360412 read this announcement].


Plugins allow developers to extend the app functionalities.
== Overview ==
A plugin it’s a subdirectory that implements a set of required functionalities.


IMPORTANT: Plugins are not automatically loaded, you must indicate the plugins to be loaded in the config.json file (plugins option)
Plugins (known as Moodle Mobile 2 addons) allow developers to extend the app functionalities.


IMPORTANT: Read carefully this document ([[Moodle Mobile Customization]]) before start developing
An addon is a subdirectory that implements a set of required functionalities. For those familiars with AngularJS, addones are AngularJS modules.


=== Types of plugins ===
IMPORTANT: Read carefully this document ([[Moodle Mobile Customization]]) before you start developing!


* General: Interactions over the global app. Like the Notifications, Upload, Help and Web
Please, note also that this documentation is oriented to Moodle Mobile specific APIs, before starting developing you should get familiar with AngularJS and Ionic, here you have some resources:
* 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 ===
'''Ionic'''
* http://ionicframework.com - Official Ionic website
* https://www.airpair.com/ionic-framework/posts/the-definitive-ionic-starter-guide - Definitive ionic starter guide
* http://mcgivery.com/100-ionic-framework-resources/ - +100 Ionic resources
* https://github.com/juarezpaf/ionic-adventures?utm_source=mobilewebweekly&utm_medium=email - More ionic resources
* http://codepen.io/ionic/public-list/ - Ionic sample code lists
* https://www.youtube.com/channel/UChYheBnVeCfhCmqZfCUdJQw - YouTube channel from Ionic
* http://www.manning.com/wilken/ - Ionic in action book


We are going to use this plugin: https://github.com/moodlehq/moodlemobile/tree/master/plugins/participants as an example:
'''Angular'''
* https://angularjs.org - The official documentation for AngularJS
* http://manning.com/bford - AngularJS in Action book
* http://manning.com/aden - AngularJS in Depth book


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
'''Cordova'''
* http://cordova.apache.org - Official Cordova site
* http://plugins.cordova.io - Cordova official plugin registry.
* http://ngcordova.com - ngCordova AngularJS wrappers for cordova


A plugin must be a directory under the plugins/ dir containing:
== Moodle Mobile 2 addons ==


* A main.js file, where the plugin is declared
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).
* Templates html files (if used)


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.


'''Defining a plugin'''
The app comes with a predefined set of addons: messages, forum, notifications, etc. All these addons are inside the www/addons folder.


<code javascript>
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.
var templates = [
    "root/lib/text!root/plugins/participants/participants.html",
    "root/lib/text!root/plugins/participants/participant.html"
];


define(templates,function (participantsTpl, participantTpl) {
You can see the list of available delegates [https://docs.moodle.org/dev/Moodle_Mobile#Addons in this section].
</code>


For loading plugins we use the RequireJS library, a plugin is a module that we must define using the lines above:
=== Structure of an addon ===


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.
If you are not familiar with AngularJS, please, read the [https://docs.angularjs.org/guide AngularJS] guide to a complete understanding of the different terms used in this document.


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
Forum addon directory:


  "plugins" : ["notifications", "upload", "contents", "participants", "addcontact", "addnote", "sendmessage", "yourpluginname"],
  controllers/
  discussion.js
  discussions.js
lang/
  en.json
services/
  forum.js   
templates/
  discussion.html
  discussions.html
main.js


'''Global settings of the plugin'''
'''controllers and templates'''


<code javascript>
The controllers directory contains the module controllers, usually you are going to have there a controller per view and per template.
settings: {
            name: "participants",
            type: "course",
            menuURL: "#participants/",
            lang: {
                component: "moodle"
            }
        },
</code>


The name of the plugin (must be the same that the directory name)
In the forum example, you can see that there are two controllers and two templates:


The type of plugin (general, course, user, settings)
* discussions.js|html for displaying the complete list of discussions of a forum in a course
* discussion.js|html  for displaying a single discussion.


The main link to the plugin in the App
Each controller is responsible of rendering the view, for example, the discussions.js will fetch and display the list of discussions of the forum using the helper service defined in forum.js


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"
The controller will also implement additional functionalities like support to "Pull down to refresh"


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
'''lang'''


Example settings for custom plugins:
The lang directory contain the language file with the translated strings.


<code javascript>
'''services'''
settings: {
            name: "mycustomplugin",
            type: "general",
            menuURL: "#custom/",
            lang: {
                component: "local_mycustomplugin",
                strings: {
                "stringid1": "string contents",
                "stringid2": "string contents 2"
                }
            }
        },
</code>


Notice that the lang.strings property is needed because until we sync to the remote Moodle installation for downloading the language pack, we are going to need this temporal strings.
This directory contains the app services, usually for non-complex addons you will find a single file inside containing all the code logic used for providing the main functionality of the addon like the code for retrieving the discussions via Moodle Web Services.


You can also can add the strings in a json file and uses the same method that for loading templates (add the lang json file as a dependency and then assign to the lang.strings property the variable returned by the define function, see above)
'''main.js'''


This is a mandatory file, it:
* Creates the AngularJS module
* Declares all the state routes the plugin is going to user. Attaching a controller and template for each different state.
* Initializes the addon and register it into one or more delegates (this determines where the plugin will be displayed in the app).


'''Storage'''
== See also ==
<code javascript>
        storage: {
            participant: {type: "model"},
            participants: {type: "collection", model: "participant"}
        },
</code>


Here we declare the "tables" and their "structure"
* [[Moodle Mobile Developing a plugin tutorial]]
 
* [[Moodle Mobile Developing a plugin tutorial part 2]]
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'''
<code javascript>
        routes: [
            ["participants/:courseId", "participants", "showParticipants"],
            ["participant/:courseId/:userId", "participants", "showParticipant"],
        ],
</code>
 
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'''
<code javascript>
 
        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 = MM.tpl.render(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 = MM.tpl.render(MM.plugins.participants.templates.participant.html, tpl);
                MM.panels.show('right', html);
            });
        },
 
</code>
 
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'''
<code javascript>
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'''
<code javascript>
MM.registerPlugin(plugin);
</code>
 
With this single line we register the plugin in the global Namespace MM (the main library of the app)
 
== Moodle Mobile (MM) API ==
 
Most of the MM APIs are wrappers for other libraries like Backbone.
 
=== DB/Storage functions ===
 
'''Getting an element from storage by id'''
 
MM.db.get(collection, id);
 
 
'''Getting elements from storage using conditions'''
 
MM.db.where(collection, {name: value});
 
Sample code:
 
<code javascript>
var notificationsFilter = MM.db.where("notifications", {siteid: MM.config.current_site.id});
</code>
 
'''Adding a Model to a Collection (inserting an element into a table)'''
 
MM.db.insert(collection, {id: xx, name: yy, value: zz})
 
MM.db.insert(collection, {name: yy, value: zz}) (This will create a random Unique id)
 
=== Internationalization functions ===
 
'''Getting a translated string'''
 
MM.lang.s("string_id");
 
Sample code:
 
<code javascript>
MM.lang.s("therearenotnotificationsyet");
</code>
 
=== Templating functions ===
 
'''Render a template'''
 
MM.tpl.render(html, elements);
 
Sample code:
 
 
<code javascript>
var tpl = {users: users, deviceType: MM.deviceType, courseId: courseId};
var html = MM.tpl.render(MM.plugins.participants.templates.participants.html, tpl);
</code>
 
Template:
 
<code html4strict>
    <section class="users-index-list">
        <form class="search">
            <input type="search" results="5" placeholder="Search...">
        </form>
        <ul class="nav nav-v">
            <% _.each(users, function(user) { %>
            <li class="nav-item">
                <a href="#participant/<%= courseId %>/<%= user.id %>" class="media">
                    <div class="img">
                    <img width="35" src="<%= MM.fixPluginfile(user.profileimageurlsmall) %>" alt="img">
                    </div>
                    <div class="bd">
                      <h3><%= user.fullname %></h3>
                    </div>
                </a>
            </li>
            <% }); %>
        </ul>
    </section>
</code>
 
=== Message popups/dialogs===
 
MM.popMessage(text, options);
 
MM.popErrorMessage(text, options);
 
=== Device ===
 
MM.deviceType (returns phone or tablet)
 
MM.deviceConnected() (returns true or false if the device has Internet access)
 
 
 
 
[[Category: Mobile]]

Latest revision as of 07:14, 29 April 2022


Warning: This page is no longer in use. The information contained on the page should NOT be seen as relevant or reliable.



Warning: This page is no longer in use. The information contained on the page should NOT be seen as relevant or reliable.


IMPORTANT: This documentation will be obsolete in May 2018, please read this announcement.

Overview

Plugins (known as Moodle Mobile 2 addons) allow developers to extend the app functionalities.

An addon is a subdirectory that implements a set of required functionalities. For those familiars with AngularJS, addones are AngularJS modules.

IMPORTANT: Read carefully this document (Moodle Mobile Customization) before you start developing!

Please, note also that this documentation is oriented to Moodle Mobile specific APIs, before starting developing you should get familiar with AngularJS and Ionic, here you have some resources:

Ionic

Angular

Cordova

Moodle Mobile 2 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 comes with a predefined set of addons: messages, forum, notifications, etc. All these addons are inside the www/addons folder.

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.

You can see the list of available delegates in this section.

Structure of an addon

If you are not familiar with AngularJS, please, read the AngularJS guide to a complete understanding of the different terms used in this document.

Forum addon directory:

controllers/
 discussion.js
 discussions.js
lang/
  en.json
services/
 forum.js    
templates/
 discussion.html
 discussions.html
main.js

controllers and templates

The controllers directory contains the module controllers, usually you are going to have there a controller per view and per template.

In the forum example, you can see that there are two controllers and two templates:

  • discussions.js|html for displaying the complete list of discussions of a forum in a course
  • discussion.js|html for displaying a single discussion.

Each controller is responsible of rendering the view, for example, the discussions.js will fetch and display the list of discussions of the forum using the helper service defined in forum.js

The controller will also implement additional functionalities like support to "Pull down to refresh"

lang

The lang directory contain the language file with the translated strings.

services

This directory contains the app services, usually for non-complex addons you will find a single file inside containing all the code logic used for providing the main functionality of the addon like the code for retrieving the discussions via Moodle Web Services.

main.js

This is a mandatory file, it:

  • Creates the AngularJS module
  • Declares all the state routes the plugin is going to user. Attaching a controller and template for each different state.
  • Initializes the addon and register it into one or more delegates (this determines where the plugin will be displayed in the app).

See also