Moodle Mobile Developing a plugin tutorial

Jump to: navigation, search

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.


Introduction

This tutorial describes all the steps needed for developing an add-on for the Moodle Mobile app.

In this tutorial we make the following assumptions:

  • You have a medium/good skills/understand of AngularJS/Ionic, PHP and Moodle architecture
  • You know what a Web Service is and how it works in Moodle
  • The plugin you are going to develop requires a Web Service in your Moodle installation to work

There is a second part of this tutorial where we extend the add-on features Moodle Mobile Developing a plugin tutorial part 2

Add-on spec

We want to develop a plugin that will display a new button in the user profile for adding new notes to the user. This plugin is suitable for course managers of teachers.

A couple of screenshots with the desired result:

Initial view
Course contents view

Steps

Set up your development environment

Please, read Setting up your development environment for Moodle Mobile 2

Set up your Moodle installation

Enable debugging, disable caches, etc... the typical settings for developing.

Enable the Mobile Service via Admin / Plugins / Web Services / Mobile

Develop the Moodle Web Services you are going to need

In this case we'll use an existing web service (core_notes_create_notes) that is already available in the Moodle Mobile app service, if you need custom functions you will need to develop a local plugin including there your new Web Services (see https://moodle.org/plugins/view/local_wstemplate) and create a custom service.

Develop the Moodle Mobile add-on

The full source code can be found here: https://github.com/moodlehq/moodlemobile2/tree/master/www/addons/notes

www/addons/notes/main.js

angular.module('mm.addons.notes', [])
 
.run(function($mmUserDelegate, $mmaNotesHandlers) {
 
    $mmUserDelegate.registerPlugin('mmaNotes:addNote', $mmaNotesHandlers.addNote);
 
});

Here we declare the plugin, and register the Plugin (this means that the plugin will be displayed in the User profile.

We will use the $mmaNotesHandlers service for holding all the code related to the plugin availability and main functions

There are more delegates for registering your plugin, see:

  • www/addons/mod_forum/main.js (Module delegate, and module actions delegate)
  • www/addons/messages/main.js (Main side menu delegate, and multiple delegates for the user profile)

www/addons/notes/services/handlers.js

self.isEnabled = function() {
    return $mmaNotes.isPluginEnabled();
};
 
self.isEnabledForUser = function(user, courseId) {
   // Active course required.
   return courseId && user.id != $mmSite.getUserId();
};

These are mandatory functions that will check our plugin availability, the first one will rely in the $mmaNotes service (isPluginEnabled function), the second ones checks that the user profile is being viewed inside a course (because the WS requires it) and that the current user is not the required user (so you don't see the buttons in your own profile).

self.getController = function(user, courseid) {
 
            return function($scope) {
 
                // Button title.
                $scope.title = 'mma.notes.addnewnote';
 
                $ionicModal.fromTemplateUrl('addons/notes/templates/add.html', {
                    scope: $scope,
                    animation: 'slide-in-up'
                }).then(function(m) {
                    $scope.modal = m;
                });
 
                $scope.closeModal = function(){
                    $scope.modal.hide();
                };
 
                $scope.addNote = function(){
                    // Freeze the add note button.
                    $scope.processing = true;
 
                    $mmaNotes.addNote(user.id, courseid, $scope.note.publishstate, $scope.note.text).then(function() {
                        $translate('mma.notes.eventnotecreated').then(function(str) {
                            $ionicLoading.show({
                                template: str,
                                duration: 2000
                            });
                        });
                    }, function(error) {
                        $mmUtil.showErrorModal(error);
                    }).finally(function() {
                        $scope.closeModal();
                    });
                };
 
                $scope.action = function($event) {
                    $event.preventDefault();
                    $event.stopPropagation();
 
                    $scope.note = {
                        publishstate: 'personal',
                        text: ''
                    };
                    $scope.processing = false;
 
                    $scope.modal.show();
                };
            };
        };

These is the main function code, it does the following:

  • $scope.title = 'mma.notes.addnewnote'; This sets the button title, note that we reference to the addnewnote string that is in the www/addons/notes/lang/en.json file (we need to prefix the string id to identify the plugin)
  • $ionicModal.fromTemplateUrl - Creates the modal we are going to use for displaying the form, the HTML code of the form is in www/addons/notes/templates/add.html
  • $scope.closeModal - Helper function for closing the modal inside the modal via a button
  • $scope.addNote - This function will be called when the user clicks the "Add a note" button in the modal, it will use the $mmaNotes service that handles all the logic WS logic for creating the note
  • $scope.action - This function will be called when the user clicks the "Add a note" button in the user profile view, it will open the modal and set the default values

www/addons/notes/services/notes.js

angular.module('mm.addons.notes')
 
.factory('$mmaNotes', function($mmSite, $log, $q) {
    $log = $log.getInstance('$mmaNotes');
 
    var self = {};
 
    self.addNote = function(userId, courseId, publishState, noteText) {
        var data = {
            "notes[0][userid]" : userId,
            "notes[0][publishstate]": publishState,
            "notes[0][courseid]": courseId,
            "notes[0][text]": noteText,
            "notes[0][format]": 1
        };
        return $mmSite.write('core_notes_create_notes', data);
    };
 
    self.isPluginEnabled = function() {
        var infos;
 
        if (!$mmSite.isLoggedIn()) {
            return false;
        } else if (!$mmSite.canUseAdvancedFeature('enablenotes')) {
            return false;
        } else if (!$mmSite.wsAvailable('core_notes_create_notes')) {
            return false;
        }
 
        return true;
    };
 
    return self;
});

This file contains the notes service:

  • isPluginEnabled - Checks if the notes functionality is available by checking Moodle settings and the required WS availability
  • addNote - Will call the remote WS for adding the note via the $mmSite.write helper method (the $mmService has also a read method for retrieving data)

/www/addons/notes/templates/add.html

<ion-modal-view class="product edit create">
    <form name="itemEdit" ng-submit="addNote()">
        <ion-header-bar class="bar-header">
            <h1 class="title">{{ 'mma.notes.addnewnote' | translate }}</h1>
            <a class="button" ng-click="closeModal()">{{ 'mm.core.cancel' | translate }}</a>
        </ion-header-bar>
        <ion-content has-bouncing="true">
            <div class="row">
                <div class="col">
                    <label class="item item-input item-select">
                        <div class="input-label">
                          {{ 'mma.notes.publishstate' | translate }}
                        </div>
                        <select ng-model="note.publishstate" name="publishstate">
                            <option value="personal">{{ 'mma.notes.personalnotes' | translate }}</option>
                            <option value="course">{{ 'mma.notes.coursenotes' | translate }}</option>
                            <option value="site">{{ 'mma.notes.sitenotes' | translate }}</option>
                        </select>
                    </label>
                </div>
            </div>
            <div class="row">
                <div class="col">
                    <label class="item item-input text">
                        <textarea placeholder="{{ 'mma.notes.note' | translate }}" rows="5" ng-model="note.text" name="text" lose-focus-on-return required="required"></textarea>
                    </label>
                </div>
            </div>
            <div class="padding">
                <button type="submit" class="button button-block" ng-disabled="processing || !note.text.length || note.text.length < 2">
                    {{ 'mma.notes.addnewnote' | translate }}
                </button>
            </div>
        </ion-content>
    </form>
</ion-modal-view>

This file includes the code for the modal. As you can see the close button will call the closeModal() function meanwhile the Add a note button will submit the form that will invoke the addNote function

For more information, refer to http://ionicframework.com/docs/api/service/$ionicModal/

See also

Moodle Mobile Developing a plugin tutorial part 2