From 29190702d0fe7c3a8098cc622374281c83e8a984 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Tue, 21 Oct 2025 13:19:30 +0200 Subject: [PATCH 1/2] MDL-86899 mobile: Fix custom langstring format on help --- public/admin/tool/mobile/lang/en/tool_mobile.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/admin/tool/mobile/lang/en/tool_mobile.php b/public/admin/tool/mobile/lang/en/tool_mobile.php index 9ec94bfa568..a9536735cb7 100644 --- a/public/admin/tool/mobile/lang/en/tool_mobile.php +++ b/public/admin/tool/mobile/lang/en/tool_mobile.php @@ -44,8 +44,8 @@ $string['configmobilecssurl'] = 'A CSS file to customise your mobile app interfa $string['customlangstrings'] = 'Custom language strings'; $string['customlangstrings_desc'] = 'Words and phrases displayed in the app can be customised here. Enter each custom language string on a new line with format: string identifier, custom language string and language code, separated by pipe characters. For example:
-mm.user.student|Learner|en
-mm.user.student|Aprendiz|es
+core.user.student|Learner|en
+core.user.student|Aprendiz|es
 
For a complete list of string identifiers, see the documentation.'; $string['custommenuitems'] = 'Custom menu items'; From 2a84772a9c339c951e6c769e9ce1713b538bd673 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Tue, 21 Oct 2025 13:19:58 +0200 Subject: [PATCH 2/2] MDL-86899 mobile: Add custom user menu items setting --- public/admin/tool/mobile/classes/api.php | 3 + .../mobile/classes/output/subscription.php | 17 ++++- .../admin/tool/mobile/lang/en/tool_mobile.php | 14 ++++ public/admin/tool/mobile/settings.php | 22 ++++++- .../tool/mobile/tests/externallib_test.php | 65 ++++++++++--------- public/admin/tool/mobile/version.php | 2 +- 6 files changed, 85 insertions(+), 38 deletions(-) diff --git a/public/admin/tool/mobile/classes/api.php b/public/admin/tool/mobile/classes/api.php index 71f6e9abd39..0681329ebde 100644 --- a/public/admin/tool/mobile/classes/api.php +++ b/public/admin/tool/mobile/classes/api.php @@ -321,13 +321,16 @@ class api { $settings->tool_mobile_disabledfeatures = get_config('tool_mobile', 'disabledfeatures'); $settings->tool_mobile_filetypeexclusionlist = get_config('tool_mobile', 'filetypeexclusionlist'); $custommenuitems = get_config('tool_mobile', 'custommenuitems'); + $customusermenuitems = get_config('tool_mobile', 'customusermenuitems'); // If filtering of the primary custom menu is enabled, apply only the string filters. if (!empty($CFG->navfilter && !empty($CFG->stringfilters))) { // Apply filters that are enabled for Content and Headings. $filtermanager = \filter_manager::instance(); $custommenuitems = $filtermanager->filter_string($custommenuitems, \context_system::instance()); + $customusermenuitems = $filtermanager->filter_string($customusermenuitems, \context_system::instance()); } $settings->tool_mobile_custommenuitems = $custommenuitems; + $settings->tool_mobile_customusermenuitems = $customusermenuitems; $settings->tool_mobile_apppolicy = get_config('tool_mobile', 'apppolicy'); // This setting could be not set in some edge cases such as bad upgrade. $mintimereq = get_config('tool_mobile', 'autologinmintimebetweenreq'); diff --git a/public/admin/tool/mobile/classes/output/subscription.php b/public/admin/tool/mobile/classes/output/subscription.php index 572a91ac400..cf31dc3c2e5 100644 --- a/public/admin/tool/mobile/classes/output/subscription.php +++ b/public/admin/tool/mobile/classes/output/subscription.php @@ -121,12 +121,25 @@ class subscription implements \renderable, \templatable { $custommenuitems = explode("\n", $els); // Get unique custom menu urls. $custommenuitems = array_flip( - array_map(function($val) { + array_map(function ($val) { return explode('|', $val)[1]; }, $custommenuitems) ); } - $feature['status'] = count($custommenuitems); + + $customusermenuitems = []; + $els = rtrim($ms->customusermenuitems, "\n"); + if (!empty($els)) { + $customusermenuitems = explode("\n", $els); + // Get unique custom menu urls. + $customusermenuitems = array_flip( + array_map(function ($val) { + return explode('|', $val)[1]; + }, $customusermenuitems) + ); + } + + $feature['status'] = count($custommenuitems) + count($customusermenuitems); break; // Check language strings. case 'customlanguagestrings': diff --git a/public/admin/tool/mobile/lang/en/tool_mobile.php b/public/admin/tool/mobile/lang/en/tool_mobile.php index a9536735cb7..c7a00fbf1fd 100644 --- a/public/admin/tool/mobile/lang/en/tool_mobile.php +++ b/public/admin/tool/mobile/lang/en/tool_mobile.php @@ -55,6 +55,20 @@ Link-opening methods are: app (for linking to an activity supported by the app), When items are missing a translation for a given language, they will use other languages as fallback unless "_only" is appended to the language code. +For example: +
+App help|https://someurl.xyz/help|inappbrowser
+My grades|https://someurl.xyz/local/mygrades/index.php|embedded|en
+Mis calificaciones|https://someurl.xyz/local/mygrades/index.php|embedded|es
+You will only see this in English|https://someurl.xyz/english|browser|en_only
+
'; +$string['customusermenuitems'] = 'Custom user menu items'; +$string['customusermenuitems_desc'] = 'Additional items can be added to the app\'s user menu by specifying them here. Enter each custom user menu item on a new line with format: item text, link URL, link-opening method and language code (optional, for displaying the item to users of the specified language only), separated by pipe characters. + +Link-opening methods are: app (for linking to an activity supported by the app), inappbrowser (for opening a link in a browser without leaving the app), browser (for opening the link in the device default browser outside the app) and embedded (for displaying the link in an iframe in a new page in the app). + +When items are missing a translation for a given language, they will use other languages as fallback unless "_only" is appended to the language code. + For example:
 App help|https://someurl.xyz/help|inappbrowser
diff --git a/public/admin/tool/mobile/settings.php b/public/admin/tool/mobile/settings.php
index 8055b2f3c18..e0ce32ede18 100644
--- a/public/admin/tool/mobile/settings.php
+++ b/public/admin/tool/mobile/settings.php
@@ -230,9 +230,25 @@ if ($hassiteconfig) {
                 new lang_string('disabledfeatures', 'tool_mobile'),
                 new lang_string('disabledfeatures_desc', 'tool_mobile'), array(), $options));
 
-    $temp->add(new admin_setting_configtextarea('tool_mobile/custommenuitems',
-                new lang_string('custommenuitems', 'tool_mobile'),
-                new lang_string('custommenuitems_desc', 'tool_mobile'), '', PARAM_RAW, '50', '10'));
+    $temp->add(new admin_setting_configtextarea(
+        'tool_mobile/custommenuitems',
+        new lang_string('custommenuitems', 'tool_mobile'),
+        new lang_string('custommenuitems_desc', 'tool_mobile'),
+        '',
+        PARAM_RAW,
+        '50',
+        '10',
+    ));
+
+    $temp->add(new admin_setting_configtextarea(
+        'tool_mobile/customusermenuitems',
+        new lang_string('customusermenuitems', 'tool_mobile'),
+        new lang_string('customusermenuitems_desc', 'tool_mobile'),
+        '',
+        PARAM_RAW,
+        '50',
+        '10',
+    ));
 
     // File type exclusionlist.
     $choices = [];
diff --git a/public/admin/tool/mobile/tests/externallib_test.php b/public/admin/tool/mobile/tests/externallib_test.php
index 69720ceab4c..8d69ebd4638 100644
--- a/public/admin/tool/mobile/tests/externallib_test.php
+++ b/public/admin/tool/mobile/tests/externallib_test.php
@@ -191,38 +191,39 @@ final class externallib_test extends \core_external\tests\externallib_testcase {
 
         // Test default values.
         $context = \context_system::instance();
-        $expected = array(
-            array('name' => 'fullname', 'value' => $SITE->fullname),
-            array('name' => 'shortname', 'value' => $SITE->shortname),
-            array('name' => 'summary', 'value' => $sitesummary),
-            array('name' => 'summaryformat', 'value' => $summaryformat),
-            array('name' => 'frontpage', 'value' => $CFG->frontpage),
-            array('name' => 'frontpageloggedin', 'value' => $CFG->frontpageloggedin),
-            array('name' => 'maxcategorydepth', 'value' => $CFG->maxcategorydepth),
-            array('name' => 'frontpagecourselimit', 'value' => $CFG->frontpagecourselimit),
-            array('name' => 'numsections', 'value' => course_get_format($SITE)->get_last_section_number()),
-            array('name' => 'newsitems', 'value' => $SITE->newsitems),
-            array('name' => 'commentsperpage', 'value' => $CFG->commentsperpage),
-            array('name' => 'sitepolicy', 'value' => $mysitepolicy),
-            array('name' => 'sitepolicyhandler', 'value' => ''),
-            array('name' => 'disableuserimages', 'value' => $CFG->disableuserimages),
-            array('name' => 'mygradesurl', 'value' => user_mygrades_url()->out(false)),
-            array('name' => 'tool_mobile_forcelogout', 'value' => 0),
-            array('name' => 'tool_mobile_customlangstrings', 'value' => ''),
-            array('name' => 'tool_mobile_disabledfeatures', 'value' => ''),
-            array('name' => 'tool_mobile_filetypeexclusionlist', 'value' => ''),
-            array('name' => 'tool_mobile_custommenuitems', 'value' => ''),
-            array('name' => 'tool_mobile_apppolicy', 'value' => ''),
-            array('name' => 'tool_mobile_autologinmintimebetweenreq', 'value' => 6 * MINSECS),
-            array('name' => 'tool_mobile_autologout', 'value' => get_config('tool_mobile', 'autologout')),
-            array('name' => 'tool_mobile_autologouttime', 'value' => get_config('tool_mobile', 'autologouttime')),
-            array('name' => 'calendartype', 'value' => $CFG->calendartype),
-            array('name' => 'calendar_site_timeformat', 'value' => $CFG->calendar_site_timeformat),
-            array('name' => 'calendar_startwday', 'value' => $CFG->calendar_startwday),
-            array('name' => 'calendar_adminseesall', 'value' => $CFG->calendar_adminseesall),
-            array('name' => 'calendar_lookahead', 'value' => $CFG->calendar_lookahead),
-            array('name' => 'calendar_maxevents', 'value' => $CFG->calendar_maxevents),
-        );
+        $expected = [
+            [ 'name' => 'fullname', 'value' => $SITE->fullname ],
+            [ 'name' => 'shortname', 'value' => $SITE->shortname ],
+            [ 'name' => 'summary', 'value' => $sitesummary ],
+            [ 'name' => 'summaryformat', 'value' => $summaryformat ],
+            [ 'name' => 'frontpage', 'value' => $CFG->frontpage ],
+            [ 'name' => 'frontpageloggedin', 'value' => $CFG->frontpageloggedin ],
+            [ 'name' => 'maxcategorydepth', 'value' => $CFG->maxcategorydepth ],
+            [ 'name' => 'frontpagecourselimit', 'value' => $CFG->frontpagecourselimit ],
+            [ 'name' => 'numsections', 'value' => course_get_format($SITE)->get_last_section_number() ],
+            [ 'name' => 'newsitems', 'value' => $SITE->newsitems ],
+            [ 'name' => 'commentsperpage', 'value' => $CFG->commentsperpage ],
+            [ 'name' => 'sitepolicy', 'value' => $mysitepolicy ],
+            [ 'name' => 'sitepolicyhandler', 'value' => '' ],
+            [ 'name' => 'disableuserimages', 'value' => $CFG->disableuserimages ],
+            [ 'name' => 'mygradesurl', 'value' => user_mygrades_url()->out(false) ],
+            [ 'name' => 'tool_mobile_forcelogout', 'value' => 0 ],
+            [ 'name' => 'tool_mobile_customlangstrings', 'value' => '' ],
+            [ 'name' => 'tool_mobile_disabledfeatures', 'value' => '' ],
+            [ 'name' => 'tool_mobile_filetypeexclusionlist', 'value' => '' ],
+            [ 'name' => 'tool_mobile_custommenuitems', 'value' => '' ],
+            [ 'name' => 'tool_mobile_customusermenuitems', 'value' => '' ],
+            [ 'name' => 'tool_mobile_apppolicy', 'value' => '' ],
+            [ 'name' => 'tool_mobile_autologinmintimebetweenreq', 'value' => 6 * MINSECS ],
+            [ 'name' => 'tool_mobile_autologout', 'value' => get_config('tool_mobile', 'autologout') ],
+            [ 'name' => 'tool_mobile_autologouttime', 'value' => get_config('tool_mobile', 'autologouttime') ],
+            [ 'name' => 'calendartype', 'value' => $CFG->calendartype ],
+            [ 'name' => 'calendar_site_timeformat', 'value' => $CFG->calendar_site_timeformat ],
+            [ 'name' => 'calendar_startwday', 'value' => $CFG->calendar_startwday ],
+            [ 'name' => 'calendar_adminseesall', 'value' => $CFG->calendar_adminseesall ],
+            [ 'name' => 'calendar_lookahead', 'value' => $CFG->calendar_lookahead ],
+            [ 'name' => 'calendar_maxevents', 'value' => $CFG->calendar_maxevents ],
+        ];
         $colornumbers = range(1, 10);
         foreach ($colornumbers as $number) {
             $expected[] = [
diff --git a/public/admin/tool/mobile/version.php b/public/admin/tool/mobile/version.php
index b8330efa391..aa53d539583 100644
--- a/public/admin/tool/mobile/version.php
+++ b/public/admin/tool/mobile/version.php
@@ -23,7 +23,7 @@
  */
 
 defined('MOODLE_INTERNAL') || die();
-$plugin->version   = 2025100600; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->version   = 2025102200; // The current plugin version (Date: YYYYMMDDXX).
 $plugin->requires  = 2025092600; // Requires this Moodle version.
 $plugin->component = 'tool_mobile'; // Full name of the plugin (used for diagnostics).
 $plugin->dependencies = [