Note:

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

Moodle App Plugins Upgrade Guide: Difference between revisions

From MoodleDocs
(Created page with "{{Moodle Mobile}} {{Moodle Mobile 3.5}} ==Introduction== These past few months we've been working on updating the Ionic framework in the mobile app to Ionic 5. As usual, we...")
 
(Update migration status and path)
 
(21 intermediate revisions by 6 users not shown)
Line 1: Line 1:
{{Moodle Mobile}}
{{Template:Migrated|newDocId=/general/app/upgrading/plugins-upgrade-guide}}
{{Moodle Mobile 3.5}}
{{Moodle App (Ionic 5)}}


==Introduction==
Starting with version 3.9.5, the Moodle App uses Ionic 5. As usual, we tried not to change our APIs and components to prevent breaking existing plugins. Unfortunately, Ionic 5 comes with a lot of breaking changes, especially related to templates. This means that plugins need to be adapted in order to look good in the new versions of the app.


These past few months we've been working on updating the Ionic framework in the mobile app to Ionic 5. As usual, we tried not to change our APIs and components to prevent breaking existing plugins. Unfortunately, Ionic 5 comes with a lot of breaking changes, especially related to templates. This means that your plugins will need to be adapted in order to look good in the version 3.9.5 of the app.
Please note that if your plugin doesn't use Ionic components nor JavaScript, it's possible that you don't have to adapt it. However, we recommend you to test the plugin with new versions of the app to check if everything works correctly.
 
We're still in the process of migrating some features and fixing things, but we've reached a point where we have a stable app that you can use to test your plugins. We've published this webapp so you can test them:
 
[https://ionic5.apps.moodledemo.net/ Ionic 5 WebApp]
 
If you prefer to test this in a local environment you can use the ionic5 branch in our repository:
 
[https://github.com/moodlehq/moodleapp/tree/ionic5 App source code]
 
==When will the app with Ionic 5 be published?==
 
We’re planning to publish the app at the end of June. This means you have about 2 months to adapt your plugin.
 
==My plugin doesn’t use Ionic components or Javascript, do I need to adapt it?==
 
In that case it’s possible that you don’t need to adapt the plugin to make it work. We recommend you to test the plugin in the Ionic 5 app to check if everything works.
 
==Supporting both Ionic 3 and Ionic 5==
 
Your plugin should still support Ionic 3 so it works in devices that haven’t updated the app yet. This can easily be done by checking the value of ''appversioncode'' sent by the app. I uploaded an example applied to the ''choicegroup'' plugin:
 
[https://github.com/dpalou/moodle-mod_choicegroup/blob/ionic5/classes/output/mobile.php#L52 Choicegroup code]
 
As you’ll see, I duplicated the JS and the templates so I have a file to support Ionic 3 and a file to support Ionic 5. I decided to name them “ionic3” and “latest”, but you can structure this as you prefer. You can also have a single file with different HTML depending on the appversioncode, that’s up to you.


==Ionic changes==
==Ionic changes==


As commented before, Ionic has changed a lot of their components, directives and utilities.
Previous versions of the app used Ionic 3, so the update involved an increase in two versions and Ionic changed a lot of their components, directives and utilities.


In the following 2 pages you’ll find most of the changes done by Ionic and how do you need to change your code to make it work in Ionic 5:
You can read the official [https://ionicframework.com/docs/reference/migration Ionic migration documentation]. Even if your plugins are not Ionic applications themselves, you can find information about components and other changes.


https://github.com/ionic-team/ionic-framework/blob/master/BREAKING.md#version-5x
One relevant change is that all functions related to modals are now asynchronous. This means that if your plugin is displaying a modal in JavaScript, you’ll probably need to adapt your code.


https://github.com/ionic-team/ionic-framework/blob/master/angular/BREAKING.md
Another important change is that text inside of <code><ion-item></code> should always be placed inside of an <code><ion-label></code>, otherwise it might not look good in some cases. For example:


Besides the changes in templates, it’s important to notice that all functions related to modals are now asynchronous. This means that if your plugin is displaying a modal in Javascript then you’ll probably need to adapt your code too.
<syntaxhighlight lang="html">
<!-- Ionic 3 -->
<ion-item>My text</ion-item>


Another important thing to notice is that the text inside an ''<ion-item>'' now should always be inside an ''<ion-label>'', otherwise it might not look good in some cases. E.g.:
<!-- Ionic 5 -->
<ion-item><ion-label>My text</ion-label></ion-item>
</syntaxhighlight>


<code html>
Finally, all Ionic directives are now components, like <code><ion-label></code> or <code><ion-avatar></code>. This means that these directives cannot be used in combination with another component. Some common cases that will need to be modified:
<ion-item>My text</ion-item></code>


Should now be:
<syntaxhighlight lang="html">
<!-- Ionic 3 -->
<ion-label core-mark-required="true">...</ion-label>


<code html>
<ion-avatar core-user-avatar ...>
<ion-item><ion-label>My text</ion-label></ion-item></code>


Finally, another important change is that now all Ionic tags are components, e.g. ''<ion-label>'' or ''<ion-avatar>''. This means that these tags cannot have another component in them. Some common cases that will need to be modified:
<!-- Ionic 5 -->
<ion-label><span core-mark-required="true">...</span></ion-label>


<code html>
<core-user-avatar ...>
<ion-label core-mark-required=”true></code>
</syntaxhighlight>
now needs to be:
<code html>
<ion-label><span core-mark-required="true"></code>
 
 
<code html>
<ion-avatar core-user-avatar …></code>
now needs to be:
<code html>
<core-user-avatar …></code>


==You can now use ES6==
==You can now use ES6==


In v3.9.5 we will increase the minimum Android version to use the app. The newer Cordova and Ionic versions only support Android 5.1+ with newer WebViews, so that will also be the requirement of the app.
The minimum platform requirements for Cordova and Ionic increased, and so it also affected the Moodle App. The new version requires Android 5.1 with WebView 61+, which means that the JavaScript for the app can now be compiled to ES6.


The new app will require Android 5.1 with WebView 61+. This means that the Javascript for the app can now be developed in ES6 instead of ES5. Also, you can use arrow functions since they were introduced in WebView 55 and iOS 10.3.
Please notice that you '''cannot''' use async/await, as they aren't part of ES6 and Android WebView 61 doesn't support them.


This means that the app is no longer transpiled to ES5, and that can break your plugin’s Javascript if you’re extending a class. In Ionic 3, when your plugin receives a class it actually receives a function, and that affects the way to extend it. Now your plugin will receive a Javascript class.
One issue that can break your plugin’s JavaScript is extending classes. In Ionic 3, when your plugin extends a class it's actually getting a function. In Ionic 5, your plugin will receive a JavaScript class and can be extended using class syntax:


E.g. to create a subclass of ''CoreContentLinksModuleIndexHandler'' you had to do:
Here's an example to create a subclass of <code>CoreContentLinksModuleIndexHandler</code>:


<code javascript>
<syntaxhighlight lang="javascript">
// Ionic 3
function AddonModCertificateModuleLinkHandler() {
function AddonModCertificateModuleLinkHandler() {
     that.CoreContentLinksModuleIndexHandler.call(this, that.CoreCourseHelperProvider, 'mmaModCertificate', 'certificate');
     that.CoreContentLinksModuleIndexHandler.call(this, that.CoreCourseHelperProvider, 'mmaModCertificate', 'certificate');
   
   
     // Rest of constructor.
     this.name = 'AddonModCertificateLinkHandler';
}
}
   
   
AddonModCertificateModuleLinkHandler.prototype = Object.create(this.CoreContentLinksModuleIndexHandler.prototype);
AddonModCertificateModuleLinkHandler.prototype = Object.create(this.CoreContentLinksModuleIndexHandler.prototype);
AddonModCertificateModuleLinkHandler.prototype.constructor = AddonModCertificateModuleLinkHandler;</code>
AddonModCertificateModuleLinkHandler.prototype.constructor = AddonModCertificateModuleLinkHandler;


Now it’s done like this:
// Ionic 5
class AddonModCertificateModuleLinkHandler extends this.CoreContentLinksModuleIndexHandler {


<code javascript>
class AddonModChoiceGroupLinkHandler extends this.CoreContentLinksModuleIndexHandler {
     constructor() {
     constructor() {
         super('AddonModChoiceGroup', 'choicegroup');
         super('mmaModCertificate', 'certificate');


         // Rest of constructor.
         this.name = 'AddonModCertificateLinkHandler';
     }
     }
}</code>
 
}
</syntaxhighlight>


==Changes in the app’s code==
==Changes in the app’s code==


We’ve also done some changes to the code of the app. Most of these changes probably won’t affect your plugin, but we still list them all just in case:
We’ve also done some changes to the code of the app. Most of these changes probably don’t affect your plugin, but you should still check this out just in case:
 
* <code><core-icon></code> is now deprecated, please use <code><ion-icon></code> instead. Right now you can use font-awesome icons with <code>ion-icon</code>. However, it still hasn’t been decided whether font awesome will be used in Moodle 4.0 or not, so font-awesome may be removed from the app in the future.
* To “cross out” an icon using <code>ion-icon</code> you need to use <code>class="icon-slash"</code> instead of <code>slash="true"</code>.
* The function <code>syncOnSites</code> from <code>CoreSyncBaseProvider</code> now expects to receive a function with the parameters already bound:
  <syntaxhighlight lang="javascript">
  // Ionic 3
  syncOnSites('events', this.syncAllEventsFunc.bind(this), [force], siteId);
 
  // Ionic 5
  syncOnSites('events', this.syncAllEventsFunc.bind(this, force), siteId);
  </syntaxhighlight>
* All the delegates that previously supplied an injector parameter to its handlers no longer do that. For example, the function <code>getComponent()</code> in <code>CoreUserProfileFieldDelegate</code> used to receive an injector as a parameter, but now it won’t receive any parameter.
* All the delegates that previously supplied a <code>NavController</code> parameter to its handlers no longer do that. For example, the function <code>openCourse()</code> in <code>CoreCourseFormatDelegate</code> no longer receive the <code>NavController</code> parameter.
* The handlers registered in <code>CoreCourseOptionsDelegate</code> now need to return the properties <code>page</code> and <code>pageParams</code> instead of <code>component</code> and <code>componentData</code>. Please notice this only affects your plugin if you’re creating the handler yourself using JavaScript code.
* The handlers registered in <code>CoreUserDelegate</code> have changed a bit. Please notice this only affects your plugin if you’re creating the handler yourself using JavaScript code.
** Handlers can now define a <code>cacheEnabled</code> property (<code>false</code> by default) to cache <code>isEnabledForUser</code> calls.
** In the function <code>isEnabledForUser</code>, the <code>navOptions</code> and <code>admOptions</code> parameters have been removed.
** <code>isEnabledForUser</code> is now optional and defaults to <code>true</code>.
** They can implement a new function called <code>isEnabledForCourse</code>; this function will receive the <code>navOptions</code> and <code>admOptions</code> parameters. If it's not defined, it'll default to <code>true</code>.
* The function <code>prefetchPackage</code> in the <code>CoreCourseActivityPrefetchHandlerBase</code> has changed. If you were using this class to implement your own prefetch handler you might need to update its code.
* <code>CoreInitDelegate</code> has been deleted. Now the initialisation of the app is done via Angular’s <code>APP_INITIALIZER</code>. Please notice that <code>APP_INITIALIZER</code> cannot and shouldn’t be used by plugins.
* The function <code>getAdditionalDownloadableFiles</code> in question types now needs to return a list of <code>CoreWSExternalFile</code>, it no longer accepts a list of strings.
* Files stored to be uploaded later using <code>CoreFileUploaderProvider</code> no longer have an <code>offline</code> property, now they’re just instances of <code>FileEntry</code>.
* <code>ionViewCanLeave</code> function has been renamed to <code>canLeave</code>.
* The <code>onchange</code> method of the <code>Network</code> service is now called <code>onChange</code>.
 
==Supporting both Ionic 3 and Ionic 5==
 
Your plugin should still support Ionic 3 so it works in devices that haven’t updated the app yet. This can be done by checking the value of <code>appversioncode</code> sent by the app. Here you can find an example applied to the <code>choicegroup</code> plugin: [https://github.com/dpalou/moodle-mod_choicegroup/blob/ionic5/classes/output/mobile.php#L52 Choicegroup plugin].
 
As you can see in that repository, the JS and the templates are duplicated in order to have one file to support Ionic 3 and another file to support Ionic 5. In this example, they are called "ionic3" and "latest", but you can structure this as you prefer. You can also have a single file with different HTML depending on the <code>appversioncode</code>. That’s up to you.
 
==Is there any example I can look at?==


* ''<core-icon>'' is now deprecated, please use ''<ion-icon>'' instead. Right now you can use font-awesome icons with ion-icon. However, it still hasn’t been decided whether font awesome will be used in Moodle 4.0 or not, so it might happen that font-awesome is removed from the app in the future.
If you used the app’s code as an example to build your plugin, you can do the same. There are also some plugins that have been updated, for example you can see the following PRs on the <code>choicegroup</code> plugin:
* To “cross out” an icon using ion-icon now you need to use ''class=”icon-slash”'' instead of ''slash=”true”''.
* The function ''syncOnSites'' from ''CoreSyncBaseProvider'' now expects to receive a function with the parameters already bound. That is, instead of:


<code javascript>
* https://github.com/ndunand/moodle-mod_choicegroup/pull/149
syncOnSites(‘events', this.syncAllEventsFunc.bind(this), [force], siteId);</code>
* https://github.com/ndunand/moodle-mod_choicegroup/pull/150


You should now do:


<code javascript>
You can also look at the [[Moodle App Plugins Development Guide]], it has been updated to reflect how to write plugins for the latest version of the app.
syncOnSites(‘events', this.syncAllEventsFunc.bind(this, force), siteId);</code>


* All the delegates that previously supplied an injector parameter to its handlers now no longer do that. E.g. the function ''getComponent()'' in ''CoreUserProfileFieldDelegate'' used to receive an injector as a parameter, now it won’t receive any parameter.
== Before 3.5 ==
* All the delegates that previously supplied a ''NavController'' parameter to its handlers now no longer do that. E.g. the function ''openCourse()'' in ''CoreCourseFormatDelegate'' will no longer receive the ''NavController'' parameter.
* The handlers registered in ''CoreCourseOptionsDelegate'' now need to return the properties page+pageParams instead of component+componentData. Please notice this only affects you if you’re creating the handler yourself using Javascript code.
* The handlers registered in ''CoreUserDelegate'' have changed a bit. Please notice this only affects you if you’re creating the handler yourself using Javascript code.
** Handlers can now define a ''cacheEnabled'' property (false by default) to cache ''isEnabledForUser'' calls.
** In the function ''isEnabledForUser'', the params ''navOptions'' and ''admOptions'' have been removed.
** ''isEnabledForUser'' is now optional and defaults to true.
** Now they can implement a new function ''isEnabledForCourse'', and this function will receive the ''navOptions'' and ''admOptions''. If not defined defaults to true.
* The function ''prefetchPackage'' in the ''CoreCourseActivityPrefetchHandlerBase'' has changed. If you were using this class to implement your own prefetch handler you might need to update its code.
* ''CoreInitDelegate'' has been deleted. Now the initialization of the app is done via Angular’s ''APP_INITIALIZER''. Please notice that ''APP_INITIALIZER'' cannot and shouldn’t be used by plugins.
* The function ''getAdditionalDownloadableFiles'' in question types now needs to return a list of ''CoreWSExternalFile'', it no longer accepts a list of strings.
* Files stored to be uploaded later using ''CoreFileUploaderProvider'' no longer have an “offline” property, now they’re just instances of ''FileEntry''.
* ''ionViewCanLeave'' function has been renamed to ''canLeave''.
* The ''onchange'' method of the ''Network'' service is now called ''onChange''.


==Is there any example I can look at?==
Before 3.5, the app was written using Ionic 1 and Moodle plugins could add mobile support by writing an Angular JS/Ionic module, compiling it to a zip, and including it in the plugin.


If you used the app’s code as an example to build your plugin you can do it again. Most of the templates in the app have already been adapted to Ionic 5. You can see the Ionic 5 code in this branch:
Nowadays, you need to install the [https://docs.moodle.org/311/en/Moodle_app_additional_features Moodle App Additional Features] plugin to make these plugins compatible with the latest versions of Moodle.


[https://github.com/moodlehq/moodleapp/tree/ionic5 App source code]
You can read about [[Moodle_Mobile_2_(Ionic_1)_Remote_add-ons|Remote add-ons]] for more details.


I also adapted the choicegroup plugin to make it work in ionic5, you can take a look too. This hasn’t been integrated into the plugin yet because I haven’t done thorough testing yet, you can find it here:
== See also ==


[https://github.com/dpalou/moodle-mod_choicegroup/tree/ionic5 Choicegroup plugin]
* [[Moodle App Remote Themes Upgrade Guide]]
* [[Acceptance testing for the Moodle App#Upgrading_tests_from_an_older_version|Moodle App Acceptance Tests Upgrade Guide]]

Latest revision as of 13:03, 14 July 2022

Important:

This content of this page has been updated and migrated to the new Moodle Developer Resources. The information contained on the page should no longer be seen up-to-date.

Why not view this page on the new site and help us to migrate more content to the new site!


Starting with version 3.9.5, the Moodle App uses Ionic 5. As usual, we tried not to change our APIs and components to prevent breaking existing plugins. Unfortunately, Ionic 5 comes with a lot of breaking changes, especially related to templates. This means that plugins need to be adapted in order to look good in the new versions of the app.

Please note that if your plugin doesn't use Ionic components nor JavaScript, it's possible that you don't have to adapt it. However, we recommend you to test the plugin with new versions of the app to check if everything works correctly.

Ionic changes

Previous versions of the app used Ionic 3, so the update involved an increase in two versions and Ionic changed a lot of their components, directives and utilities.

You can read the official Ionic migration documentation. Even if your plugins are not Ionic applications themselves, you can find information about components and other changes.

One relevant change is that all functions related to modals are now asynchronous. This means that if your plugin is displaying a modal in JavaScript, you’ll probably need to adapt your code.

Another important change is that text inside of <ion-item> should always be placed inside of an <ion-label>, otherwise it might not look good in some cases. For example:

<!-- Ionic 3 -->
<ion-item>My text</ion-item>

<!-- Ionic 5 -->
<ion-item><ion-label>My text</ion-label></ion-item>

Finally, all Ionic directives are now components, like <ion-label> or <ion-avatar>. This means that these directives cannot be used in combination with another component. Some common cases that will need to be modified:

<!-- Ionic 3 -->
<ion-label core-mark-required="true">...</ion-label>

<ion-avatar core-user-avatar ...>

<!-- Ionic 5 -->
<ion-label><span core-mark-required="true">...</span></ion-label>

<core-user-avatar ...>

You can now use ES6

The minimum platform requirements for Cordova and Ionic increased, and so it also affected the Moodle App. The new version requires Android 5.1 with WebView 61+, which means that the JavaScript for the app can now be compiled to ES6.

Please notice that you cannot use async/await, as they aren't part of ES6 and Android WebView 61 doesn't support them.

One issue that can break your plugin’s JavaScript is extending classes. In Ionic 3, when your plugin extends a class it's actually getting a function. In Ionic 5, your plugin will receive a JavaScript class and can be extended using class syntax:

Here's an example to create a subclass of CoreContentLinksModuleIndexHandler:

// Ionic 3
function AddonModCertificateModuleLinkHandler() {
    that.CoreContentLinksModuleIndexHandler.call(this, that.CoreCourseHelperProvider, 'mmaModCertificate', 'certificate');
 
    this.name = 'AddonModCertificateLinkHandler';
}
 
AddonModCertificateModuleLinkHandler.prototype = Object.create(this.CoreContentLinksModuleIndexHandler.prototype);
AddonModCertificateModuleLinkHandler.prototype.constructor = AddonModCertificateModuleLinkHandler;

// Ionic 5
class AddonModCertificateModuleLinkHandler extends this.CoreContentLinksModuleIndexHandler {

    constructor() {
        super('mmaModCertificate', 'certificate');

        this.name = 'AddonModCertificateLinkHandler';
    }

}

Changes in the app’s code

We’ve also done some changes to the code of the app. Most of these changes probably don’t affect your plugin, but you should still check this out just in case:

  • <core-icon> is now deprecated, please use <ion-icon> instead. Right now you can use font-awesome icons with ion-icon. However, it still hasn’t been decided whether font awesome will be used in Moodle 4.0 or not, so font-awesome may be removed from the app in the future.
  • To “cross out” an icon using ion-icon you need to use class="icon-slash" instead of slash="true".
  • The function syncOnSites from CoreSyncBaseProvider now expects to receive a function with the parameters already bound:
  // Ionic 3
  syncOnSites('events', this.syncAllEventsFunc.bind(this), [force], siteId);
  
  // Ionic 5
  syncOnSites('events', this.syncAllEventsFunc.bind(this, force), siteId);
  • All the delegates that previously supplied an injector parameter to its handlers no longer do that. For example, the function getComponent() in CoreUserProfileFieldDelegate used to receive an injector as a parameter, but now it won’t receive any parameter.
  • All the delegates that previously supplied a NavController parameter to its handlers no longer do that. For example, the function openCourse() in CoreCourseFormatDelegate no longer receive the NavController parameter.
  • The handlers registered in CoreCourseOptionsDelegate now need to return the properties page and pageParams instead of component and componentData. Please notice this only affects your plugin if you’re creating the handler yourself using JavaScript code.
  • The handlers registered in CoreUserDelegate have changed a bit. Please notice this only affects your plugin if you’re creating the handler yourself using JavaScript code.
    • Handlers can now define a cacheEnabled property (false by default) to cache isEnabledForUser calls.
    • In the function isEnabledForUser, the navOptions and admOptions parameters have been removed.
    • isEnabledForUser is now optional and defaults to true.
    • They can implement a new function called isEnabledForCourse; this function will receive the navOptions and admOptions parameters. If it's not defined, it'll default to true.
  • The function prefetchPackage in the CoreCourseActivityPrefetchHandlerBase has changed. If you were using this class to implement your own prefetch handler you might need to update its code.
  • CoreInitDelegate has been deleted. Now the initialisation of the app is done via Angular’s APP_INITIALIZER. Please notice that APP_INITIALIZER cannot and shouldn’t be used by plugins.
  • The function getAdditionalDownloadableFiles in question types now needs to return a list of CoreWSExternalFile, it no longer accepts a list of strings.
  • Files stored to be uploaded later using CoreFileUploaderProvider no longer have an offline property, now they’re just instances of FileEntry.
  • ionViewCanLeave function has been renamed to canLeave.
  • The onchange method of the Network service is now called onChange.

Supporting both Ionic 3 and Ionic 5

Your plugin should still support Ionic 3 so it works in devices that haven’t updated the app yet. This can be done by checking the value of appversioncode sent by the app. Here you can find an example applied to the choicegroup plugin: Choicegroup plugin.

As you can see in that repository, the JS and the templates are duplicated in order to have one file to support Ionic 3 and another file to support Ionic 5. In this example, they are called "ionic3" and "latest", but you can structure this as you prefer. You can also have a single file with different HTML depending on the appversioncode. That’s up to you.

Is there any example I can look at?

If you used the app’s code as an example to build your plugin, you can do the same. There are also some plugins that have been updated, for example you can see the following PRs on the choicegroup plugin:


You can also look at the Moodle App Plugins Development Guide, it has been updated to reflect how to write plugins for the latest version of the app.

Before 3.5

Before 3.5, the app was written using Ionic 1 and Moodle plugins could add mobile support by writing an Angular JS/Ionic module, compiling it to a zip, and including it in the plugin.

Nowadays, you need to install the Moodle App Additional Features plugin to make these plugins compatible with the latest versions of Moodle.

You can read about Remote add-ons for more details.

See also