Note:

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

Local plugins: Difference between revisions

From MoodleDocs
m (Protected "Local plugins": Developer Docs Migration ([Edit=Allow only administrators] (indefinite)))
 
(83 intermediate revisions by 24 users not shown)
Line 1: Line 1:
== Disclaimer ==
{{Template:Migrated|newDocId=/docs/apis/plugintypes/local}}
{{Infobox Project
|name = Local customisations
|state = Implemented
|tracker = MDL-17376, MDL-16438
|discussion = http://moodle.org/mod/forum/discuss.php?d=126017 http://moodle.org/mod/forum/discuss.php?d=86903
|assignee = [[User:Petr Škoda (škoďák)|Petr Škoda (škoďák)]], some parts were originally proposed and implemented in 1.9 by Penny Leach
}}
{{Moodle 2.0}}


Some of these entries are things that Penny has patches for and has not yet committed, and are under discussion. The meta bug for all these items is here:  http://tracker.moodle.org/browse/MDL-17376
The recommended way to add new functionality to Moodle is to create a new standard plugin (module, block, auth, enrol, etc.).The /local/ plugins are mostly suitable for things that do not fit standard plugins.


== General customisations ==
== Custom /local/ plugins ==


Moodle has been designed with extensibility in mind. There are many plug-in points available throughout Moodle to allow developers add new functionality to Moodle without modifying core code.
Local plugins are used in cases when no standard plugin fits, examples are:
* event consumers communicating with external systems
* custom definitions of web services and external functions
* applications that extend moodle at the system level (hub server, amos server, etc.)
* new database tables used in core hacks (discouraged)
* new capability definitions used in core hacks
* custom admin settings
* extending the navigation block with custom menus [http://moodle.org/mod/forum/discuss.php?d=170325&parent=753095]


See the [[Developer_documentation#Make_a_new_plugin|make a new plugin section of the Developer documentation page]] for the different plugin types available, and documentation on how to develop for them.
=== Standard plugin features: ===
* /local/xxx/[[version.php]] - version of script (must be incremented after changes)
* /local/xxx/db/install.xml - executed during install (new version.php found)
* /local/xxx/db/install.php - executed right after install.xml
* /local/xxx/db/uninstall.php - executed during uninstallation
* /local/xxx/db/upgrade.php - executed after version.php change
* /local/xxx/db/access.php - definition of capabilities
* /local/xxx/db/events.php - event handlers and subscripts
* /local/xxx/db/messages.php - messaging registration
* /local/xxx/db/external.php - web services and external functions descriptions
* /local/xxx/cron.php - cron job, run at the interval defined in version.php. Alternatively, you can define <tt>local_xxx_cron()</tt> in lib.php. Between those two methods, the lib.php one is preferred. But both of them are considered legacy and have been deprecated for Moodle 3.5 (MDL-52846) and will be deleted for Moodle 3.9 (MDL-61165). [[https://docs.moodle.org/dev/Task_API#Legacy_cron More info here]]. They are being replaced with the [[Task_API]].
* /local/xxx/lang/en/local_pluginname.php - language file
* /local/xxx/lib.php - function library, automatically included with by config.php.  Hook functions <tt>local_xxx_extend_navigation()</tt> and <tt>local_xxx_extend_settings_navigation()</tt> can be used to add items to the navigation and settings blocks
* /local/xxx/settings.php - configuration options. These get added to the admin menu.


== local/ folder for 'hacky' customisations ==
The ''xxx'' is used instead of your local plugin name, plugins of the same type are installed/upgraded in alphabetical order.


Sometimes it is not possible to use the available plug-in points to make your change. In situations like this then the local folder is for you. The idea is that instead of scattering your changes throughout the code base, you put them all in a folder called 'local'. Using this folder means you won't have to deal with merging problems when you upgrade the rest of your Moodle installation.
=== List of differences from normal plugins: ===
* always executed last during install/upgrade - guaranteed by order of plugins in <syntaxhighlight lang="php">get_plugin_types()</syntaxhighlight>
* are expected to use event handlers - events are intended for communication core-->plugins only, local plugins are the best candidates for event handlers
* can add admin settings to any settings page - loaded last when constructing admin tree
* do not need to have any UI - other plugins are usually visible somewhere
* some extra hooks (not implemented yet)


The local folder has some of the plug-in points available which are available to other modules. Perhaps most useful the local/db/ folder can be used to make database schema changes and custom role permissions.
== /local/xxx/db/messages.php ==
Example File Structure:
<syntaxhighlight lang="php">
<?php


However, using the local folder should be absolutely the last resort. Long term, you will almost certainly find it easier to maintain your changes if you can package them up as one of the standard types of plugins.
/**
* Defines message providers (types of messages being sent)
*
* @package mod-forum
* @copyright  1999 onwards  Martin Dougiamas  http://moodle.com
* @license  http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/


=== Local database changes and version ===
$messageproviders = array (


If you need to make local database customisations that are not easily encapsulated by a block or module, Moodle does support the use of a local db upgrade script, and local version number.
/// Ordinary single forum posts
    'posts' => array (
    )


This is almost exactly the same as every other db/upgrade.php and version.php except for the following points:
);
</syntaxhighlight>


==== local/version.php ====
== Other /local/ customisation files==  


local/version.php must look like:
===Customised site defaults===


<code php>
Different default site settings can be stored in file /local/defaults.php.
$local_version = 2008121700;
These new defaults are used during installation, upgrade and later are
</code>
displayed as default values in admin settings. This means that the content
of the defaults files is usually updated BEFORE installation or upgrade.


==== local/db/install.xml ====
These customised defaults are useful especially when using CLI tools
for installation and upgrade.


Local/ has no install.xml - only an upgrade.php. This is because often the changes that you want to make are not full tables, but just extra columns, and a local install.xml makes less sense than just upgrade.php.  
Sample /local/defaults.php file content:
<syntaxhighlight lang="php">
<?php
$defaults['moodle']['forcelogin'] = 1; // new default for $CFG->forcelogin
$defaults['scorm']['maxgrade'] = 20;    // default for get_config('scorm', 'maxgrade')
$defaults['moodlecourse']['numsections'] = 11;
$defaults['moodle']['hiddenuserfields'] = array('city', 'country');
</syntaxhighlight>
First bracket contains string from column plugin of config_plugins table.
Second bracket is the name of setting. In the admin settings UI the plugin and
name of setting is separated by "|".


If you would like to create tables using using an install.xml this can be achieved by putting something like that this in your upgrade.php file:
The values usually correspond to the raw string in config table, with the exception
of comma separated lists that are usually entered as real arrays.


<code php>
Please note that not all settings are converted to admin_tree,
$result = install_from_xmldb_file(dirname(__FILE__).'/install.xml');
they are mostly intended to be set directly in config.php.
</code>


==== local/db/upgrade.php ====
=== 2.0 pre-upgrade script===


local/db/upgrade.php must look like:
You can use /local/upgrade_pre20.php script for any code that needs to
be executed before the main upgrade to 2.0. Most probably this will
be used for undoing of old hacks that would otherwise break normal
2.0 upgrade.


<code php>
This file is just included directly, there does not need to be any
function xmldb_local_upgrade($oldversion) {
function inside. If the execution stops the script is executed again
    global $CFG, $db;
during the next upgrade. The first execution of lib/db/upgrade.php
increments the version number and the pre upgrade script is not
executed any more.


    $result = true;
== Customisations outside of /local/ directory==


    if ($result && $oldversion < 2008121700) {
=== Forced settings===
        $result = $result && create_table($table);
    }
    return $result;
}
</code>


=== Local post-installation data insertion ===
Sometimes it is useful to force some settings and prevent any changes of these settings via the standard admin UI. It is possible to hardcode these settings in config.php.


In discussion - see http://tracker.moodle.org/browse/MDL-17440
In case of course settings it is very simply, the values are assigned directly to $CFG properties. In case of plugins the values are specified in a multidimensional array in $CFG->force_plugin_settings.


=== Local capabilities ===
Sample code in config.php
<syntaxhighlight lang="php">
$CFG->allowobjectembed = 0;
$CFG->forced_plugin_settings = array('page'=>array('displayoptions'=>5, 'requiremodintro'=>1), 'folder'=>array('requiremodintro'=>1));
</syntaxhighlight>


Just like core and modules, Moodle supports the use of a '''db/access.php''' inside local/ to define local capabilities.
=== Local language customisations===


The formatting is exactly the same as the other db/access.php scripts  - an array keyed by capability name, containing arrays of capability data, like so:
Moodle supports other type of local customisation of standard language
packs. If you want to create your own language pack based on another
language create new dataroot directory with "_local" suffix, for example
following file with content changes string "Login" to "Sign in":
moodledata/lang/en_local
<syntaxhighlight lang="php">
<?php
  $string['login'] = 'Sign in';
</syntaxhighlight>
See also https://docs.moodle.org/en/Language_editing


<code php>
=== Custom script injection===  
$local_capabilities = array(
    'moodle/local:capability' => array(
        'captype'      => 'write',
        'contextlevel' => CONTEXT_SYSTEM,
        'riskbitmask'  => RISK_SPAM,
    ),
</code>


Note that for all local capabilities you add, you'll need to add language strings. Moodle will expect to find them in '''local/lang/en_utf8/local.php''' (eg for English) with a key (following the above example) of local:capability, like so:
Very old customisation option that allows you to modify scripts by injecting
<code php>
code right after the require 'config.php' call.
$string['local:capability'] =  'The language-string';
</code>


=== Local event subscriptions ===
This setting is enabled by manually setting $CFG->customscripts variable
in config.php script. The value is expected to be full path to directory
with the same structure as dirroot. Please note this hack only affects
files that actually include the config.php!


''' Pending commit '''
; Examples:
* disable one specific moodle page without code modification
* alter page parameters on the fly


It is often very helpful to be able to write custom code that subscribes to normal events that Moodle throws. It's also handy to be able to throw and catch your own custom events, as the Event API provides a very handy mechanism to do signal handling.
=== Direct code modifications===
This is usually the last resort, if possible do not do it. And if you still do it use some version control system (preferably git).


Local event handlers get registered at install/upgrade time just as the event handlers for modules do. To trigger an update when you add a new event handler, you must bump the local version number.
=== Direct database modifications===
Very strongly discouraged! Sometimes field lengths may be modified without side effects. Adding or removing of db fields will most probably cause major problems during future upgrades. New database tables should be added only from plugins.


Event handlers must be defined in an array, keyed by event name, with each entry in the array information about the handler, like so:
== Examples ==


<code php>
=== Adding an element to the settings menu ===
    $handlers = array(
        'some_core_event'    => array(            // eg 'user_created'
            'handlerfile'    => '/local/lib.php', // example
            'handlerfunction' => 'local_user_create_handler',
            'schedule'        => 'cron'
        )
    );
</code>


=== Local admin menu items and settings ===
In local/*pluginname*/lib.php, define a function named local_*pluginname*_extend_settings_navigation, this will get called when Moodle builds the settings block.
This example adds a link to the bottom of the course administration section of the settings block.
<syntaxhighlight lang="php">
function local_myplugin_extend_settings_navigation($settingsnav, $context) {
    global $CFG, $PAGE;


You can add extra configuration items to Moodle by creating a file, local/settings.php which accesses the $ADMIN variable directly and adds new items to it.  This will make them appear in the site administration block on the homepage, and create the config options that administrators can change. You can also add whole new custom config pages (admin_externalpage).  For example:
    // Only add this settings item on non-site course pages.
    if (!$PAGE->course or $PAGE->course->id == 1) {
        return;
    }


<code php>
    // Only let users with the appropriate capability see this settings item.
 
    if (!has_capability('moodle/backup:backupcourse', context_course::instance($PAGE->course->id))) {
$ADMIN->add('root', new admin_category($name, $title));
        return;
$ADMIN->add('foo', new admin_externalpage($name, $title, $url, $cap));
    }
 
</code>
 
=== Local backup and restore hooks ===
 
In discussion - see http://tracker.moodle.org/browse/MDL-17444
 
=== Local course deletion hook ===
 
This is due to be removed, and replaced with an event.


Previously, when you emptied ('''not deleted''') a course, the notify_local_course_delete method was called, which looked for a local_delete_course method in local/lib.php.  The naming of this is a little ambiguous because the course is being emptied, not deleted.
    if ($settingnode = $settingsnav->find('courseadmin', navigation_node::TYPE_COURSE)) {
 
        $strfoo = get_string('foo', 'local_myplugin');
Going forwards, we aim to have two events - course_emptied and course_deleted.  Support for the local_delete_course method will be removed.
        $url = new moodle_url('/local/myplugin/foo.php', array('id' => $PAGE->course->id));
 
        $foonode = navigation_node::create(
See http://tracker.moodle.org/browse/MDL-17445 for more info.
            $strfoo,
 
            $url,
=== Local my moodle overrides ===
            navigation_node::NODETYPE_LEAF,
 
             'myplugin',
('''pending commit''')
             'myplugin',
 
             new pix_icon('t/addcontact', $strfoo)
By default, the My Moodle page shows a course overview in the center column.  Some sites might want to replace that with some custom code, or even just some static content.  This is very easily accomplished by:
         );
 
        if ($PAGE->url->compare($url, URL_MATCH_BASE)) {
* Creating a local/lib.php
             $foonode->make_active();
* Putting a function in there, called local_my_moodle.
 
This function will be called '''instead of'' (rather than in addition to), the default center column.
 
=== Local stickyblocks targets ===
 
('''pending commit''')
 
In the case where you're developing a heavily customised site, it might happen that you develop a pagetype that has the ability for people to add blocks to.  In this case, you might want to also make it stickyblock enabled.
 
This is easily achieved by:
 
* Create local/lib.php
* Create a method in there called local_get_sticky_pagetypes that returns an array just like the $pagetypes array at the top of admin/stickyblocks.php.
 
For example:
 
<code php>
 
function local_get_sticky_pagetypes() {
    return array(
        'custom_pagetype' => array(
             'id' => 'custom_pagetype',
             'lib' => '/local/lib.php',
             'name' => get_string('custom_pagetype', 'local')
         )
    );
}
 
</code>
 
You then must create a page that extends page_base.  Don't forget that if your pagetype is stickyblock enabled, it needs to take this into account in some of its methods.  For example:
 
<code php>
 
class custom_pagetype extends page_base {
 
    // normal page type class
 
    function url_get_path() {
        global $CFG;
        if (defined('ADMIN_STICKYBLOCKS')) { // admin is editing stickyblocks, not a normal page
             return $CFG->wwwroot . '/admin/stickyblocks.php';
         }
         }
         return '';
         $settingnode->add_node($foonode);
     }
     }
}
</syntaxhighlight>


    function url_get_parameters() {
=== Removing the "Site Home" link from the navigation menu ===
        global $CFG;
<syntaxhighlight lang="php">
        if (defined('ADMIN_STICKYBLOCKS')) {
function local_xxx_extend_navigation(global_navigation $navigation) {
            return array('pt' =>  'custom_pagetype');
    if ($home = $navigation->find('home', global_navigation::TYPE_SETTING)) {
        }
        $home->remove();
     }
     }
}
}
</code>
</syntaxhighlight>


and map it with page_map_class:
=== Adding Site Wide Settings For Your Local Plugin ===


<code php>
If you need to add site wide settings for your local plugin, perhaps connection details for an API, you will need to do something similar to the below in your settings file (local/yourplugin/settings.php)
page_map_class('custom_pagetype', 'custom_pagetype');
</code>


=== Local user profile view hook ===
<syntaxhighlight lang="php">
// Ensure the configurations for this site are set
if ( $hassiteconfig ){


('''pending commit''')
// Create the new settings page
// - in a local plugin this is not defined as standard, so normal $settings->methods will throw an error as
// $settings will be NULL
$settings = new admin_settingpage( 'local_yourplugin', 'Your Settings Page Title' );


Sometimes, it may be desirable to do something like add extra buttons to the bottom of the user's profile page - if you've developed some custom code to perform extra actions on a user, for example.  This is very easy to do.
// Create
$ADMIN->add( 'localplugins', $settings );


* Create local/lib.php
// Add a setting field to the settings for this page
* Create a function in there, local_user_view.
$settings->add( new admin_setting_configtext(
 
<code php>
// This is the reference you will use to your configuration
 
'local_yourplugin/apikey',
function local_user_view($user, $course) {
 
// This is the friendly title for the config, which will be displayed
    // capability check
'External API: Key',
 
    // extra stuff
// This is helper text for this config field
'This is the key used to access the External API',
// This is the default value
'No Key Defined',
// This is the type of Parameter this config is
PARAM_TEXT
) );


}
}
</syntaxhighlight>


</code>
== Local customisations in previous versions ==
Previous versions include only partial support for customisations in /local/ directory.


=== Local language strings ===
=== List of local customisations in 1.9.x: ===
* /local/cron.php - custom cron jobs
* /local/settings.php - custom admin settings
* /local/db/upgrade.php - general modifications
* /local/lang/* - custom strings
* /local/lib.php - local_delete_course()


Moodle already supports local overriding of any language strings, or the creation of new strings. Just create a folder lang/XX_utf8_local where XX is a language code. Any language files you put in there will be used before the standard files. So, for example, can can create a file lang/en_utf8_local/moodle.php containing
=== Migration from old 1.9.x /local/: ===
$strings['login'] = 'Sign in';
* <syntaxhighlight lang="php">local/*</syntaxhighlight> needs to be copied to new directory
and that will change the string 'Login' to 'Sign in'. (See [[Language editing]] for another way to achieve this.)
* <syntaxhighlight lang="php">local/xxxx/db/install.php</syntaxhighlight> is intended for first installation, originally everything was in upgrade.php
 
* events are used instead of hooks
This mechanism can also be used to create completely new language files. For example, suppose you have created some code in local/myfeature.php that needs some language strings. You can put those strings in the file lang/en_utf8_local/local_myfeature.php, and then access then using get_string('mystring', 'local_myfeature'). (Note that you do not have to call the file local_myfeature.php, you can call it anything you like, however, the convention of calling lang files for local/ code local_somthing is recommended.)
* upgrade code needs to migrate old settings, events, etc. directly in core db tables - such as change component strings and capability names from db/install.php or manually before/after upgrade
 
In addition, there is one other mechanism that is available. Strings from the 'local' language file (for example get_string('mystring', 'local') will be found if the string is defined in the file local/lang/en_utf8/local.php. Since the lang file 'local' is used for a lot of things like capability names, you can use this file for things like that.
 
=== Local cron ===
 
If the file exists, local/cron.php is included by admin/cron.php every time cron is run.


==See also==
==See also==


*[http://cvs.moodle.org/moodle/lib/locallib.php?view=markup CVS:moodle/lib/locallib.php]
* [http://moodle.org/mod/forum/discuss.php?d=170325&parent=753095 Extending navigation block]
*Using Moodle [http://moodle.org/mod/forum/discuss.php?d=86903 Local Customisations] forum discussion
* Using Moodle [http://moodle.org/mod/forum/discuss.php?d=86903 Local Customisations] forum discussion
 
* [[Local customisation (Moodle 1.9)]]
=Local plugins in 2.0 proposal=
'''Author:''' [[User:Petr Škoda (škoďák)|Petr Škoda (škoďák)]]
 
'''Tracker issues:''' MDL-16438
 
The information above is already a bit obsolete due to recent major refactoring of install and upgrade scripts. Instead of hardcoding ''local'' exceptions all over the code base we can simply add new plugin type ''local'' and finish implementation of events notifications. Implementation above is never ending battle of keeping local/* hacks in sync with Moodle development which is imo not optimal solution.
 
 
Benefits:
* local plugins would be far less hacky
* absolutely no cost of ''local'' maintenance in core - it is just another general plugin
* much easier to implement
* more concurrent plugin modifications possible, not just one huge local hack
* this would motivate use to finally finish events
 
 
'''Migration from old local/*'''
* <code>local/*</code> needs to be copied to new directory
* <code>local/xxxx/db/install.php</code> is intended for first installation, originally everything was in upgrade.php
* events are used instead of hooks
 
 
List of differences from normal plugins:
* always executed last during install/upgrade
* are expected to use event handlers
*


[[Category:Local customisation]]
[[Category:Plugins]]

Latest revision as of 07:27, 6 May 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!

Local customisations
Project state Implemented
Tracker issue MDL-17376, MDL-16438
Discussion http://moodle.org/mod/forum/discuss.php?d=126017 http://moodle.org/mod/forum/discuss.php?d=86903
Assignee Petr Škoda (škoďák), some parts were originally proposed and implemented in 1.9 by Penny Leach

Moodle 2.0


The recommended way to add new functionality to Moodle is to create a new standard plugin (module, block, auth, enrol, etc.).The /local/ plugins are mostly suitable for things that do not fit standard plugins.

Custom /local/ plugins

Local plugins are used in cases when no standard plugin fits, examples are:

  • event consumers communicating with external systems
  • custom definitions of web services and external functions
  • applications that extend moodle at the system level (hub server, amos server, etc.)
  • new database tables used in core hacks (discouraged)
  • new capability definitions used in core hacks
  • custom admin settings
  • extending the navigation block with custom menus [1]

Standard plugin features:

  • /local/xxx/version.php - version of script (must be incremented after changes)
  • /local/xxx/db/install.xml - executed during install (new version.php found)
  • /local/xxx/db/install.php - executed right after install.xml
  • /local/xxx/db/uninstall.php - executed during uninstallation
  • /local/xxx/db/upgrade.php - executed after version.php change
  • /local/xxx/db/access.php - definition of capabilities
  • /local/xxx/db/events.php - event handlers and subscripts
  • /local/xxx/db/messages.php - messaging registration
  • /local/xxx/db/external.php - web services and external functions descriptions
  • /local/xxx/cron.php - cron job, run at the interval defined in version.php. Alternatively, you can define local_xxx_cron() in lib.php. Between those two methods, the lib.php one is preferred. But both of them are considered legacy and have been deprecated for Moodle 3.5 (MDL-52846) and will be deleted for Moodle 3.9 (MDL-61165). [More info here]. They are being replaced with the Task_API.
  • /local/xxx/lang/en/local_pluginname.php - language file
  • /local/xxx/lib.php - function library, automatically included with by config.php. Hook functions local_xxx_extend_navigation() and local_xxx_extend_settings_navigation() can be used to add items to the navigation and settings blocks
  • /local/xxx/settings.php - configuration options. These get added to the admin menu.

The xxx is used instead of your local plugin name, plugins of the same type are installed/upgraded in alphabetical order.

List of differences from normal plugins:

  • always executed last during install/upgrade - guaranteed by order of plugins in
    get_plugin_types()
    
  • are expected to use event handlers - events are intended for communication core-->plugins only, local plugins are the best candidates for event handlers
  • can add admin settings to any settings page - loaded last when constructing admin tree
  • do not need to have any UI - other plugins are usually visible somewhere
  • some extra hooks (not implemented yet)

/local/xxx/db/messages.php

Example File Structure:

<?php

/**
 * Defines message providers (types of messages being sent)
 *
 * @package mod-forum
 * @copyright  1999 onwards  Martin Dougiamas  http://moodle.com
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */

$messageproviders = array (

/// Ordinary single forum posts
    'posts' => array (
    )

);

Other /local/ customisation files

Customised site defaults

Different default site settings can be stored in file /local/defaults.php. These new defaults are used during installation, upgrade and later are displayed as default values in admin settings. This means that the content of the defaults files is usually updated BEFORE installation or upgrade.

These customised defaults are useful especially when using CLI tools for installation and upgrade.

Sample /local/defaults.php file content:

<?php
$defaults['moodle']['forcelogin'] = 1;  // new default for $CFG->forcelogin
$defaults['scorm']['maxgrade'] = 20;    // default for get_config('scorm', 'maxgrade')
$defaults['moodlecourse']['numsections'] = 11;
$defaults['moodle']['hiddenuserfields'] = array('city', 'country');

First bracket contains string from column plugin of config_plugins table. Second bracket is the name of setting. In the admin settings UI the plugin and name of setting is separated by "|".

The values usually correspond to the raw string in config table, with the exception of comma separated lists that are usually entered as real arrays.

Please note that not all settings are converted to admin_tree, they are mostly intended to be set directly in config.php.

2.0 pre-upgrade script

You can use /local/upgrade_pre20.php script for any code that needs to be executed before the main upgrade to 2.0. Most probably this will be used for undoing of old hacks that would otherwise break normal 2.0 upgrade.

This file is just included directly, there does not need to be any function inside. If the execution stops the script is executed again during the next upgrade. The first execution of lib/db/upgrade.php increments the version number and the pre upgrade script is not executed any more.

Customisations outside of /local/ directory

Forced settings

Sometimes it is useful to force some settings and prevent any changes of these settings via the standard admin UI. It is possible to hardcode these settings in config.php.

In case of course settings it is very simply, the values are assigned directly to $CFG properties. In case of plugins the values are specified in a multidimensional array in $CFG->force_plugin_settings.

Sample code in config.php

$CFG->allowobjectembed = 0;
$CFG->forced_plugin_settings = array('page'=>array('displayoptions'=>5, 'requiremodintro'=>1), 'folder'=>array('requiremodintro'=>1));

Local language customisations

Moodle supports other type of local customisation of standard language packs. If you want to create your own language pack based on another language create new dataroot directory with "_local" suffix, for example following file with content changes string "Login" to "Sign in": moodledata/lang/en_local

<?php
  $string['login'] = 'Sign in';

See also https://docs.moodle.org/en/Language_editing

Custom script injection

Very old customisation option that allows you to modify scripts by injecting code right after the require 'config.php' call.

This setting is enabled by manually setting $CFG->customscripts variable in config.php script. The value is expected to be full path to directory with the same structure as dirroot. Please note this hack only affects files that actually include the config.php!

Examples
  • disable one specific moodle page without code modification
  • alter page parameters on the fly

Direct code modifications

This is usually the last resort, if possible do not do it. And if you still do it use some version control system (preferably git).

Direct database modifications

Very strongly discouraged! Sometimes field lengths may be modified without side effects. Adding or removing of db fields will most probably cause major problems during future upgrades. New database tables should be added only from plugins.

Examples

Adding an element to the settings menu

In local/*pluginname*/lib.php, define a function named local_*pluginname*_extend_settings_navigation, this will get called when Moodle builds the settings block. This example adds a link to the bottom of the course administration section of the settings block.

function local_myplugin_extend_settings_navigation($settingsnav, $context) {
    global $CFG, $PAGE;

    // Only add this settings item on non-site course pages.
    if (!$PAGE->course or $PAGE->course->id == 1) {
        return;
    }

    // Only let users with the appropriate capability see this settings item.
    if (!has_capability('moodle/backup:backupcourse', context_course::instance($PAGE->course->id))) {
        return;
    }

    if ($settingnode = $settingsnav->find('courseadmin', navigation_node::TYPE_COURSE)) {
        $strfoo = get_string('foo', 'local_myplugin');
        $url = new moodle_url('/local/myplugin/foo.php', array('id' => $PAGE->course->id));
        $foonode = navigation_node::create(
            $strfoo,
            $url,
            navigation_node::NODETYPE_LEAF,
            'myplugin',
            'myplugin',
            new pix_icon('t/addcontact', $strfoo)
        );
        if ($PAGE->url->compare($url, URL_MATCH_BASE)) {
            $foonode->make_active();
        }
        $settingnode->add_node($foonode);
    }
}

Removing the "Site Home" link from the navigation menu

function local_xxx_extend_navigation(global_navigation $navigation) {
    if ($home = $navigation->find('home', global_navigation::TYPE_SETTING)) {
        $home->remove();
    }
}

Adding Site Wide Settings For Your Local Plugin

If you need to add site wide settings for your local plugin, perhaps connection details for an API, you will need to do something similar to the below in your settings file (local/yourplugin/settings.php)

// Ensure the configurations for this site are set
if ( $hassiteconfig ){

	// Create the new settings page
	// - in a local plugin this is not defined as standard, so normal $settings->methods will throw an error as
	// $settings will be NULL
	$settings = new admin_settingpage( 'local_yourplugin', 'Your Settings Page Title' );

	// Create 
	$ADMIN->add( 'localplugins', $settings );

	// Add a setting field to the settings for this page
	$settings->add( new admin_setting_configtext(
		
		// This is the reference you will use to your configuration
		'local_yourplugin/apikey',
	
		// This is the friendly title for the config, which will be displayed
		'External API: Key',
	
		// This is helper text for this config field
		'This is the key used to access the External API',
	
		// This is the default value
		'No Key Defined',
	
		// This is the type of Parameter this config is
		PARAM_TEXT
	
	) );

}

Local customisations in previous versions

Previous versions include only partial support for customisations in /local/ directory.

List of local customisations in 1.9.x:

  • /local/cron.php - custom cron jobs
  • /local/settings.php - custom admin settings
  • /local/db/upgrade.php - general modifications
  • /local/lang/* - custom strings
  • /local/lib.php - local_delete_course()

Migration from old 1.9.x /local/:

  • local/*
    
    needs to be copied to new directory
  • local/xxxx/db/install.php
    
    is intended for first installation, originally everything was in upgrade.php
  • events are used instead of hooks
  • upgrade code needs to migrate old settings, events, etc. directly in core db tables - such as change component strings and capability names from db/install.php or manually before/after upgrade

See also