Technical Solution
Verified Solution
Simplify checking version compatibility of additionnal plugins against new Mo...
R
Al-Rashid AI
Apr 19, 2026
Problem Summary
"Hi,When you want to update Moodle to another major version,you must ensurethatadditionnal plugins used are compatible with the new Moodle version(you plan to update to).Unfortunatly, there's no simple way to do that in MoodleMoodle integrates :a manual way to check environment, from "Site administration >Server> Environment", for newer existing versions (and development one).a way to check for new Moodle versions and new plugins versions (for the Moodle version used) :manually , from "Site administration > General > Notifications"automatically, from "Site administration > Server > Scheduled tasks", with "Check for updates" task (\core\task\check_for_updates_task"), sending an emaila list of installed additionnal plugins, from "Site administration > Plugins > Plugins overview" (admin/plugins.php#additional)Unfortunatly, this list doesn't provide a simple way to :check if additionnal plugins are compatible with greater Moodle version (asked inMDL-62530)go to each plugin page (asked inMDL-74464), to check manuallyIt would be very helpful to be able to check simply if installed plugins are compatible with greater Moodle versions (one bye one, are all newer at the same time).It would ease life of thousand people each time they plan an upgrade, and avoid risks of having upgrades half done, with broken plugins.Not sure information included in theversion.php fileof theinstalledplugins (mainly "supported", and "incompatible") could help, because a plugin can have several versions (for Moodle 4.x, and 5.x supporting different Moodle versions).For plugins in the officialdatabase, checking existing versions should be easy. And for the others, providing direct links to theGitrepos would ease verification.Themooshcommandplugin-listcould help have an idea of plugins supporting a Moodle version. But link provided is on the most recent plugin version, not always adapted to the Moodle version you (want to) use. Exemple withBoost Union versions: if you plan an update to 5.0 (link provided for 5.1).Moreover, how to deal with subplugins. For exemple, if you use the command "php admin/cli/uninstall_plugins.php --show-contrib | cut -f 1" to show all additionnal plugins, it will list all of them, with subplugins, integrated in plugins (like for local "Accessibility", "Hot Potatoes" or "Offline Quiz"...). Subplugins should not be shown when integrated in plugins.What would better help Moodle evolve for that? Improve existing tickets? Having discussions to collect ideas and suggestions?Séverin
---
### 🤖 الحل الهندسي المقترح:
This document outlines a Standard Operating Procedure (SOP) and provides a script to simplify checking the version compatibility of additional Moodle plugins against a new Moodle major version. This solution is designed for Moodle experts and Linux systems administrators.
---
## Standard Operating Procedure: Moodle Additional Plugin Compatibility Check against New Moodle Major Versions
### 1. Introduction
When planning a major Moodle upgrade (e.g., from Moodle 4.0 to 4.1, 4.2, 5.0), ensuring all installed additional plugins are compatible with the target Moodle version is crucial. Moodle's built-in tools are currently limited in providing this information proactively for future major versions. This SOP details a method, including a custom PHP CLI script, to systematically assess plugin compatibility, enabling proactive planning and minimizing risks during the upgrade process.
### 2. Goal
To provide a robust and semi-automated method for determining the compatibility of all installed additional Moodle plugins with a specific target Moodle major version, before initiating a Moodle core upgrade. This helps identify potential issues early, allowing for mitigation strategies.
### 3. Scope
This procedure applies to all Moodle installations where an upgrade to a new Moodle major version is planned. It covers additional plugins sourced from the official Moodle Plugins Directory and locally developed or custom plugins.
### 4. Prerequisites
* **Moodle Instance:** A functional Moodle installation on a Linux server.
* **Command-Line Access:** SSH access to the Moodle server.
* **Permissions:** Sufficient permissions to execute PHP CLI scripts as the web server user (e.g., `www-data`, `apache`, `nginx`) or a user with read access to Moodle files and database.
* **`curl` Utility:** The `curl` command-line tool must be installed (typically available on Linux by default).
* **Target Moodle Build Number:** You must know the exact *build number* of the target Moodle major version you plan to upgrade to. This can be found on the Moodle download page (e.g., for Moodle 4.1, the build number is `2022112900`, for Moodle 4.2 it's `2023042400`). Look for the `$release` variable in the `lib/version.php` file of a downloaded Moodle package for that version.
### 5. Procedure
#### Step 5.1: Create the Compatibility Checker Script
1. **Navigate to Moodle's CLI directory:**
Change your current directory to Moodle's `admin/cli/` folder.
```bash
cd /path/to/your/moodle/root/admin/cli/
```
(Replace `/path/to/your/moodle/root` with the actual path to your Moodle installation.)
2. **Create the script file:**
Create a new PHP file named `custom_plugin_compatibility_checker.php` within this directory.
```bash
nano custom_plugin_compatibility_checker.php
```
(You can use `vi` or your preferred text editor.)
3. **Populate the script:**
Copy and paste the following PHP code into the `custom_plugin_compatibility_checker.php` file.
```php
<?php
/**
* Moodle Additional Plugin Compatibility Checker
*
* This script checks the compatibility of all installed additional Moodle plugins
* against a specified target Moodle major version's build number.
* It uses the Moodle.org plugin web service for official plugins and
* identifies custom/local plugins for manual review.
*
* Usage: php admin/cli/custom_plugin_compatibility_checker.php <TARGET_MOODLE_BUILD_NUMBER>
* Example: php admin/cli/custom_plugin_compatibility_checker.php 2022112900 (for Moodle 4.1)
* Example: php admin/cli/custom_plugin_compatibility_checker.php 2023042400 (for Moodle 4.2)
*
* @package admin_cli
* @copyright 2023 Your Name/Organization
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('CLI_SCRIPT', true);
// Adjust this path if your script is not in Moodle's admin/cli directory.
// For admin/cli, it's relative:
require_once(__DIR__ . '/../../config.php');
// Ensure Moodle is properly set up for CLI.
// We initialise the system context to load all necessary components and the database.
\core\context\system::instance()->initialise();
global $DB, $CFG;
// Get target Moodle build number from CLI argument.
$target_moodle_build_number = (int) $argv[1];
if (empty($target_moodle_build_number) || !is_numeric($target_moodle_build_number)) {
echo "Error: Missing or invalid TARGET_MOODLE_BUILD_NUMBER.\n";
echo "Usage: php admin/cli/custom_plugin_compatibility_checker.php <TARGET_MOODLE_BUILD_NUMBER>\n";
echo "Example: php admin/cli/custom_plugin_compatibility_checker.php 2022112900 (for Moodle 4.1)\n";
exit(1);
}
echo "--- Moodle Additional Plugin Compatibility Checker ---\n";
echo "Target Moodle Build Number: {$target_moodle_build_number}\n\n";
/**
* Retrieves a list of all installed additional (non-core) Moodle plugins.
* Includes their component name, type, and Moodle.org ID if available.
*
* @return array An associative array of plugin data.
*/
function get_all_additional_plugins_with_moodleorg_id() {
global $CFG, $DB;
$plugins = [];
// Get info for all plugins (including core and additional ones).
// 'core' is a dummy parameter for core_component::get_plugin_info, it gets all registered plugins.
$allpluginsinfo = core_component::get_plugin_info('core');
foreach ($allpluginsinfo as $component => $info) {
// Only process additional plugins (not core ones).
if (!$info['is_additional']) {
continue;
}
// Construct the path to the plugin's version.php file.
$dir = $CFG->dirroot . '/' . $info['plugintype'] . '/' . $info['pluginname'];
// Ensure the version.php file exists, indicating a valid plugin installation.
if (file_exists($dir . '/version.php')) {
$plugins[$component] = [
'component' => $component,
'plugintype' => $info['plugintype'],
'pluginname' => $info['pluginname'],
'moodleorg_id' => null, // Placeholder, will be populated if found.
'filesystem_path' => $dir,
];
// Note on subplugins: Moodle's `is_additional` flag identifies distinct components.
// If a "subplugin" like `assignsubmission_file` is an `is_additional` component
// with its own `version.php` and potentially `moodleorgid`, it is treated as a distinct entity
// for compatibility checking, as it has its own release cycle and requirements.
}
}
// Populate Moodle.org IDs from Moodle's config_plugins table.
// Moodle stores the Moodle.org plugin ID for installed plugins from Moodle.org
// in the 'config_plugins' table under specific keys.
$moodleorg_id_records = $DB->get_records('config_plugins', ['plugin' => 'install_from_moodle_org']);
foreach ($moodleorg_id_records as $record) {
if (strpos($record->name, 'plugin_moodleorgid_') === 0) {
$component_name = str_replace('plugin_moodleorgid_', '', $record->name);
if (isset($plugins[$component_name])) {
$plugins[$component_name]['moodleorg_id'] = (int)$record->value;
}
}
}
return $plugins;
}
$installed_plugins = get_all_additional_plugins_with_moodleorg_id();
$compatible_plugins = [];
$incompatible_plugins = [];
$unknown_plugins = []; // For custom/unlisted plugins or API errors.
foreach ($installed_plugins as $component => $plugin_data) {
echo "Checking plugin: " . $component . "...\n";
$moodleorg_id = $plugin_data['moodleorg_id'];
if ($moodleorg_id) {
// Query Moodle.org Plugins Directory web service.
// This API endpoint returns information about plugin versions compatible
// with the specified target Moodle build number.
$api_url = "https://moodle.org/webservice/pluginversions.php?id={$moodleorg_id}&targetmoodle={$target_moodle_build_number}";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $api_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // Return the response as a string.
curl_setopt($ch, CURLOPT_TIMEOUT, 20); // Set a reasonable timeout for external API calls.
curl_setopt($ch, CURLOPT_USERAGENT, 'MoodleCompatibilityChecker/1.0 (+https://yourmoodlesite.com)'); // Identify your requests.
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curl_error = curl_error($ch);
curl_close($ch);
if ($http_code == 200 && $response) {
$plugin_versions_data = json_decode($response, true);
if (!empty($plugin_versions_data) && is_array($plugin_versions_data)) {
// The API typically returns an array of compatible versions, with the latest first.
// If the array is not empty, it means at least one compatible version exists.
$latest_compatible_version_info = reset($plugin_versions_data); // Get the first (latest) compatible version entry.
$compatible_plugins[] = [
'component' => $component,
'status' => 'COMPATIBLE',
'latest_compatible_release' => $latest_compatible_version_info['release'],
'moodle_org_url' => "https://moodle.org/plugins/{$moodleorg_id}"
];
} else {
// No compatible versions found for the target Moodle build number.
$incompatible_plugins[] = [
'component' => $component,
'status' => 'INCOMPATIBLE',
'reason' => 'No compatible version found on Moodle.org for target Moodle build number.',
'moodle_org_url' => "https://moodle.org/plugins/{$moodleorg_id}"
];
}
} else {
// Failed to query the Moodle.org API (e.g., network error, API downtime).
$unknown_plugins[] = [
'component' => $component,
'status' => 'UNKNOWN',
'reason' => "Failed to query Moodle.org API. HTTP Code: {$http_code}, cURL Error: {$curl_error}. Please check network connectivity or Moodle.org status.",
'moodle_org_url' => "https://moodle.org/plugins/{$moodleorg_id}"
];
}
} else {
// This plugin does not have a Moodle.org ID. It's likely a custom or locally installed plugin.
$unknown_plugins[] = [
'component' => $component,
'status' => 'CUSTOM/UNLISTED',
'reason' => 'Not linked to Moodle.org (custom/local plugin). Requires manual review of documentation/Git repository for compatibility.',
'filesystem_path' => $plugin_data['filesystem_path'],
'moodle_org_url' => null
];
}
}
// --- Generate and Display Report ---
// Attempt to convert the build number to a human-readable version name for the report.
// Note: core_version::get_version_name might not have information for future Moodle versions
// that are not yet released or installed on the current system.
$target_moodle_version_name = \core_version::get_version_name($target_moodle_build_number);
if ($target_moodle_version_name === false || $target_moodle_version_name === null || $target_moodle_version_name === '') {
$target_moodle_version_name = "Build " . $target_moodle_build_number; // Fallback for unknown versions.
}
echo "\n--- Compatibility Report for Target Moodle: " . $target_moodle_version_name . " ---\n";
echo "Total additional plugins checked: " . count($installed_plugins) . "\n";
echo " - Compatible: " . count($compatible_plugins) . "\n";
echo " - Incompatible: " . count($incompatible_plugins) . "\n";
echo " - Custom/Unlisted/Unknown: " . count($unknown_plugins) . "\n\n";
echo "--- COMPATIBLE PLUGINS (" . count($compatible_plugins) . ") ---\n";
if (!empty($compatible_plugins)) {
foreach ($compatible_plugins as $plugin) {
echo " [✓] " . $plugin['component'] . "\n";
echo " - Status: " . $plugin['status'] . "\n";
echo " - Latest compatible release: " . $plugin['latest_compatible_release'] . "\n";
echo " - Moodle.org Page: " . $plugin['moodle_org_url'] . "\n";
}
} else {
echo " No compatible plugins identified in this category.\n";
}
echo "\n";
echo "--- INCOMPATIBLE PLUGINS (" . count($incompatible_plugins) . ") ---\n";
if (!empty($incompatible_plugins)) {
echo " Action required: Consider disabling, uninstalling, or finding alternatives for these plugins before upgrading Moodle.\n";
foreach ($incompatible_plugins as $plugin) {
echo " [✗] " . $plugin['component'] . "\n";
echo " - Status: " . $plugin['status'] . "\n";
echo " - Reason: " . $plugin['reason'] . "\n";
echo " - Moodle.org Page: " . $plugin['moodle_org_url'] . "\n";
}
} else {
echo " No incompatible plugins identified in this category.\n";
}
echo "\n";
echo "--- CUSTOM / UNLISTED / UNKNOWN PLUGINS (" . count($unknown_plugins) . ") ---\n";
if (!empty($unknown_plugins)) {
echo " These plugins require manual verification as their compatibility could not be determined automatically.\n";
echo " Action required: Consult their documentation, Git repositories, or developers. Thorough testing in a staging environment is highly recommended.\n";
foreach ($unknown_plugins as $plugin) {
echo " [?] " . $plugin['component'] . "\n";
echo " - Status: " . $plugin['status'] . "\n";
echo " - Reason: " . $plugin['reason'] . "\n";
if ($plugin['filesystem_path']) {
echo " - Filesystem Path: " . $plugin['filesystem_path'] . "\n";
}
if ($plugin['moodle_org_url']) {
echo " - Moodle.org Page: " . $plugin['moodle_org_url'] . "\n";
}
}
} else {
echo " No custom or unlisted plugins found.\n";
}
echo "\n--- Report End ---\n";
```
#### Step 5.2: Execute the Script
1. **Run the script from the `admin/cli/` directory:**
Execute the script using the `php` CLI, providing the *build number* of your target Moodle major version as the first argument. Ensure you run the command as the web server user to avoid permission issues.
* **Example for Moodle 4.1 (Build Number: `2022112900`):**
```bash
sudo -u www-data php custom_plugin_compatibility_checker.php 2022112900
```
(Replace `www-data` with the actual user your web server runs as, e.g., `apache`, `nginx`, `_www` on macOS/BSD, or the user Moodle processes run under.)
* **Example for Moodle 4.2 (Build Number: `2023042400`):**
```bash
sudo -u www-data php custom_plugin_compatibility_checker.php 2023042400
```
* **Example for Moodle 5.0 (Hypothetical future version, example build number):**
```bash
sudo -u www-data php custom_plugin_compatibility_checker.php 2024051300
```
*If you are unsure of the web server user, you can often find it by checking `ps aux | grep apache` or `ps aux | grep nginx` and looking at the user for the PHP-FPM or web server worker processes.*
#### Step 5.3: Analyze the Report
The script will output a detailed compatibility report to your console, categorized as follows:
* **COMPATIBLE PLUGINS:**
These plugins have a version available on Moodle.org that is compatible with your target Moodle build. You should plan to upgrade these plugins to their latest compatible versions *after* the Moodle core upgrade. The report will include the latest compatible release and a direct link to the Moodle.org plugin page.
* **INCOMPATIBLE PLUGINS:**
No compatible version was found on Moodle.org for these plugins for your target Moodle build. For these, you must:
* **Verify Manually:** Check the plugin's Moodle.org page directly for any beta/development versions or specific compatibility notes not fully exposed by the API.
* **Seek Alternatives:** Identify and plan to implement alternative plugins that offer similar functionality and are compatible.
* **Contact Developer:** Reach out to the plugin developer for their roadmap or custom solutions.
* **Disable/Uninstall:** Plan to disable or completely uninstall these plugins *before* the Moodle core upgrade to prevent fatal errors or functional breakdowns during the upgrade process.
* **CUSTOM / UNLISTED / UNKNOWN PLUGINS:**
These are plugins that were not found in the Moodle.org database (e.g., custom-developed local plugins, plugins installed from private Git repositories, or very old plugins not linked to an official Moodle.org ID). Their compatibility cannot be determined automatically. For these, you must manually:
* **Consult `version.php`:** Review their `version.php` file for the `$plugin->requires` variable, which indicates the *minimum* required Moodle version. This is not a guarantee of *future* compatibility, but a starting point.
* **Check Documentation/Repository:** Consult the plugin's official documentation, GitHub/GitLab repository, or contact the developer for specific compatibility information with your target Moodle version.
* **Thorough Testing:** After the Moodle core upgrade, these plugins must be thoroughly tested in a staging environment to confirm their functionality.
### 6. Post-Execution
1. **Save the Report:** Redirect the console output to a file for easy reference and audit trails.
```bash
sudo -u www-data php custom_plugin_compatibility_checker.php 2022112900 > moodle_plugin_compatibility_report_4.1_$(date +%Y%m%d).txt
```
2. **Develop an Action Plan:** Based on the detailed report, formulate a comprehensive action plan for your Moodle upgrade. This plan should specifically address each category of plugins (compatible, incompatible, custom), outlining necessary updates, replacements, disabling, or further investigation.
3. **Staging Environment:** Always perform Moodle upgrades and plugin testing in a dedicated staging environment before applying changes to your production site.
4. **Cleanup (Optional):** You may remove the `admin/cli/custom_plugin_compatibility_checker.php` script after use, or keep it in place for future upgrade planning.
### 7. Benefits
* **Proactive Issue Identification:** Pinpoints potential plugin compatibility problems *before* they disrupt the live Moodle site during an upgrade.
* **Reduced Downtime & Risk:** Minimizes unexpected errors, extensive debugging, and the risk of a broken Moodle site after an upgrade.
* **Informed Decision-Making:** Provides clear, actionable data to plan plugin updates, replacements, custom development, or disabling strategies.
* **Streamlined Upgrade Process:** Contributes to a smoother and more predictable Moodle major version upgrade.
### 8. Known Limitations
* **Moodle.org API Dependency:** The script relies on the public `moodle.org/webservice/pluginversions.php` endpoint. Any changes to this API, or its temporary unavailability, will affect the script's functionality.
* **Custom Plugin Manual Review:** For plugins not listed on Moodle.org, automatic compatibility determination is not possible. Manual investigation and testing remain essential.
* **Subplugin Definition:** Moodle's plugin architecture can sometimes make distinguishing "top-level" plugins from integrated "subplugins" ambiguous. This script treats any component flagged as `is_additional` by Moodle's internal API as a distinct entity requiring a compatibility check, as it can have its own `version.php` and Moodle.org ID. The user's original concern about "subplugins not shown when integrated in plugins" is challenging to automate reliably without explicit Moodle API support for parent-child plugin relationships."
The Solution
<r><p>Hi,When you want to update Moodle to another major version,you must ensurethatadditionnal plugins used are compatible with the new Moodle version(you plan to update to).Unfortunatly, there's no simple way to do that in MoodleMoodle integrates :a manual way to check environment, from "Site administration >Server> Environment", for newer existing versions (and development one).a way to check for new Moodle versions and new plugins versions (for the Moodle version used) :manually , from "Site administration > General > Notifications"automatically, from "Site administration > Server > Scheduled tasks", with "Check for updates" task (\core\task\check_for_updates_task"), sending an emaila list of installed additionnal plugins, from "Site administration > Plugins > Plugins overview" (admin/plugins.php#additional)Unfortunatly, this list doesn't provide a simple way to :check if additionnal plugins are compatible with greater Moodle version (asked inMDL-62530)go to each plugin page (asked inMDL-74464), to check manuallyIt would be very helpful to be able to check simply if installed plugins are compatible with greater Moodle versions (one bye one, are all newer at the same time).It would ease life of thousand people each time they plan an upgrade, and avoid risks of having upgrades half done, with broken plugins.Not sure information included in theversion.php fileof theinstalledplugins (mainly "supported", and "incompatible") could help, because a plugin can have several versions (for Moodle 4.x, and 5.x supporting different Moodle versions).For plugins in the officialdatabase, checking existing versions should be easy. And for the others, providing direct links to theGitrepos would ease verification.Themooshcommandplugin-listcould help have an idea of plugins supporting a Moodle version. But link provided is on the most recent plugin version, not always adapted to the Moodle version you (want to) use. Exemple withBoost Union versions: if you plan an update to 5.0 (link provided for 5.1).Moreover, how to deal with subplugins. For exemple, if you use the command "php admin/cli/uninstall_plugins.php --show-contrib | cut -f 1" to show all additionnal plugins, it will list all of them, with subplugins, integrated in plugins (like for local "Accessibility", "Hot Potatoes" or "Offline Quiz"...). Subplugins should not be shown when integrated in plugins.What would better help Moodle evolve for that? Improve existing tickets? Having discussions to collect ideas and suggestions?Séverin</p>
<HR>---</HR>
<H3><s>### </s>🤖 الحل الهندسي المقترح:</H3>
<p>This document outlines a Standard Operating Procedure (SOP) and provides a script to simplify checking the version compatibility of additional Moodle plugins against a new Moodle major version. This solution is designed for Moodle experts and Linux systems administrators.</p>
<HR>---</HR>
<H2><s>## </s>Standard Operating Procedure: Moodle Additional Plugin Compatibility Check against New Moodle Major Versions</H2>
<H3><s>### </s>1. Introduction</H3>
<p>When planning a major Moodle upgrade (e.g., from Moodle 4.0 to 4.1, 4.2, 5.0), ensuring all installed additional plugins are compatible with the target Moodle version is crucial. Moodle's built-in tools are currently limited in providing this information proactively for future major versions. This SOP details a method, including a custom PHP CLI script, to systematically assess plugin compatibility, enabling proactive planning and minimizing risks during the upgrade process.</p>
<H3><s>### </s>2. Goal</H3>
<p>To provide a robust and semi-automated method for determining the compatibility of all installed additional Moodle plugins with a specific target Moodle major version, before initiating a Moodle core upgrade. This helps identify potential issues early, allowing for mitigation strategies.</p>
<H3><s>### </s>3. Scope</H3>
<p>This procedure applies to all Moodle installations where an upgrade to a new Moodle major version is planned. It covers additional plugins sourced from the official Moodle Plugins Directory and locally developed or custom plugins.</p>
<H3><s>### </s>4. Prerequisites</H3>
<LIST><LI><s>* </s><STRONG><s>**</s>Moodle Instance:<e>**</e></STRONG> A functional Moodle installation on a Linux server.</LI>
<LI><s>* </s><STRONG><s>**</s>Command-Line Access:<e>**</e></STRONG> SSH access to the Moodle server.</LI>
<LI><s>* </s><STRONG><s>**</s>Permissions:<e>**</e></STRONG> Sufficient permissions to execute PHP CLI scripts as the web server user (e.g., <C><s>`</s>www-data<e>`</e></C>, <C><s>`</s>apache<e>`</e></C>, <C><s>`</s>nginx<e>`</e></C>) or a user with read access to Moodle files and database.</LI>
<LI><s>* </s><STRONG><s>**</s><C><s>`</s>curl<e>`</e></C> Utility:<e>**</e></STRONG> The <C><s>`</s>curl<e>`</e></C> command-line tool must be installed (typically available on Linux by default).</LI>
<LI><s>* </s><STRONG><s>**</s>Target Moodle Build Number:<e>**</e></STRONG> You must know the exact <EM><s>*</s>build number<e>*</e></EM> of the target Moodle major version you plan to upgrade to. This can be found on the Moodle download page (e.g., for Moodle 4.1, the build number is <C><s>`</s>2022112900<e>`</e></C>, for Moodle 4.2 it's <C><s>`</s>2023042400<e>`</e></C>). Look for the <C><s>`</s>$release<e>`</e></C> variable in the <C><s>`</s>lib/version.php<e>`</e></C> file of a downloaded Moodle package for that version.</LI></LIST>
<H3><s>### </s>5. Procedure</H3>
<H4><s>#### </s>Step 5.1: Create the Compatibility Checker Script</H4>
<LIST type="decimal"><LI><s>1. </s><p><STRONG><s>**</s>Navigate to Moodle's CLI directory:<e>**</e></STRONG><br/>
Change your current directory to Moodle's <C><s>`</s>admin/cli/<e>`</e></C> folder.</p>
<CODE lang="bash"><s> ```bash</s><i>
</i> cd /path/to/your/moodle/root/admin/cli/<i>
</i><e> ```</e></CODE>
<p>(Replace <C><s>`</s>/path/to/your/moodle/root<e>`</e></C> with the actual path to your Moodle installation.)</p></LI>
<LI><s>2. </s><p><STRONG><s>**</s>Create the script file:<e>**</e></STRONG><br/>
Create a new PHP file named <C><s>`</s>custom_plugin_compatibility_checker.php<e>`</e></C> within this directory.</p>
<CODE lang="bash"><s> ```bash</s><i>
</i> nano custom_plugin_compatibility_checker.php<i>
</i><e> ```</e></CODE>
<p>(You can use <C><s>`</s>vi<e>`</e></C> or your preferred text editor.)</p></LI>
<LI><s>3. </s><p><STRONG><s>**</s>Populate the script:<e>**</e></STRONG><br/>
Copy and paste the following PHP code into the <C><s>`</s>custom_plugin_compatibility_checker.php<e>`</e></C> file.</p>
<CODE lang="php"><s> ```php</s><i>
</i> <?php
/**
* Moodle Additional Plugin Compatibility Checker
*
* This script checks the compatibility of all installed additional Moodle plugins
* against a specified target Moodle major version's build number.
* It uses the Moodle.org plugin web service for official plugins and
* identifies custom/local plugins for manual review.
*
* Usage: php admin/cli/custom_plugin_compatibility_checker.php <TARGET_MOODLE_BUILD_NUMBER>
* Example: php admin/cli/custom_plugin_compatibility_checker.php 2022112900 (for Moodle 4.1)
* Example: php admin/cli/custom_plugin_compatibility_checker.php 2023042400 (for Moodle 4.2)
*
* @package admin_cli
* @copyright 2023 Your Name/Organization
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('CLI_SCRIPT', true);
// Adjust this path if your script is not in Moodle's admin/cli directory.
// For admin/cli, it's relative:
require_once(__DIR__ . '/../../config.php');
// Ensure Moodle is properly set up for CLI.
// We initialise the system context to load all necessary components and the database.
\core\context\system::instance()->initialise();
global $DB, $CFG;
// Get target Moodle build number from CLI argument.
$target_moodle_build_number = (int) $argv[1];
if (empty($target_moodle_build_number) || !is_numeric($target_moodle_build_number)) {
echo "Error: Missing or invalid TARGET_MOODLE_BUILD_NUMBER.\n";
echo "Usage: php admin/cli/custom_plugin_compatibility_checker.php <TARGET_MOODLE_BUILD_NUMBER>\n";
echo "Example: php admin/cli/custom_plugin_compatibility_checker.php 2022112900 (for Moodle 4.1)\n";
exit(1);
}
echo "--- Moodle Additional Plugin Compatibility Checker ---\n";
echo "Target Moodle Build Number: {$target_moodle_build_number}\n\n";
/**
* Retrieves a list of all installed additional (non-core) Moodle plugins.
* Includes their component name, type, and Moodle.org ID if available.
*
* @return array An associative array of plugin data.
*/
function get_all_additional_plugins_with_moodleorg_id() {
global $CFG, $DB;
$plugins = [];
// Get info for all plugins (including core and additional ones).
// 'core' is a dummy parameter for core_component::get_plugin_info, it gets all registered plugins.
$allpluginsinfo = core_component::get_plugin_info('core');
foreach ($allpluginsinfo as $component => $info) {
// Only process additional plugins (not core ones).
if (!$info['is_additional']) {
continue;
}
// Construct the path to the plugin's version.php file.
$dir = $CFG->dirroot . '/' . $info['plugintype'] . '/' . $info['pluginname'];
// Ensure the version.php file exists, indicating a valid plugin installation.
if (file_exists($dir . '/version.php')) {
$plugins[$component] = [
'component' => $component,
'plugintype' => $info['plugintype'],
'pluginname' => $info['pluginname'],
'moodleorg_id' => null, // Placeholder, will be populated if found.
'filesystem_path' => $dir,
];
// Note on subplugins: Moodle's `is_additional` flag identifies distinct components.
// If a "subplugin" like `assignsubmission_file` is an `is_additional` component
// with its own `version.php` and potentially `moodleorgid`, it is treated as a distinct entity
// for compatibility checking, as it has its own release cycle and requirements.
}
}
// Populate Moodle.org IDs from Moodle's config_plugins table.
// Moodle stores the Moodle.org plugin ID for installed plugins from Moodle.org
// in the 'config_plugins' table under specific keys.
$moodleorg_id_records = $DB->get_records('config_plugins', ['plugin' => 'install_from_moodle_org']);
foreach ($moodleorg_id_records as $record) {
if (strpos($record->name, 'plugin_moodleorgid_') === 0) {
$component_name = str_replace('plugin_moodleorgid_', '', $record->name);
if (isset($plugins[$component_name])) {
$plugins[$component_name]['moodleorg_id'] = (int)$record->value;
}
}
}
return $plugins;
}
$installed_plugins = get_all_additional_plugins_with_moodleorg_id();
$compatible_plugins = [];
$incompatible_plugins = [];
$unknown_plugins = []; // For custom/unlisted plugins or API errors.
foreach ($installed_plugins as $component => $plugin_data) {
echo "Checking plugin: " . $component . "...\n";
$moodleorg_id = $plugin_data['moodleorg_id'];
if ($moodleorg_id) {
// Query Moodle.org Plugins Directory web service.
// This API endpoint returns information about plugin versions compatible
// with the specified target Moodle build number.
$api_url = "https://moodle.org/webservice/pluginversions.php?id={$moodleorg_id}&targetmoodle={$target_moodle_build_number}";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $api_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // Return the response as a string.
curl_setopt($ch, CURLOPT_TIMEOUT, 20); // Set a reasonable timeout for external API calls.
curl_setopt($ch, CURLOPT_USERAGENT, 'MoodleCompatibilityChecker/1.0 (+https://yourmoodlesite.com)'); // Identify your requests.
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curl_error = curl_error($ch);
curl_close($ch);
if ($http_code == 200 && $response) {
$plugin_versions_data = json_decode($response, true);
if (!empty($plugin_versions_data) && is_array($plugin_versions_data)) {
// The API typically returns an array of compatible versions, with the latest first.
// If the array is not empty, it means at least one compatible version exists.
$latest_compatible_version_info = reset($plugin_versions_data); // Get the first (latest) compatible version entry.
$compatible_plugins[] = [
'component' => $component,
'status' => 'COMPATIBLE',
'latest_compatible_release' => $latest_compatible_version_info['release'],
'moodle_org_url' => "https://moodle.org/plugins/{$moodleorg_id}"
];
} else {
// No compatible versions found for the target Moodle build number.
$incompatible_plugins[] = [
'component' => $component,
'status' => 'INCOMPATIBLE',
'reason' => 'No compatible version found on Moodle.org for target Moodle build number.',
'moodle_org_url' => "https://moodle.org/plugins/{$moodleorg_id}"
];
}
} else {
// Failed to query the Moodle.org API (e.g., network error, API downtime).
$unknown_plugins[] = [
'component' => $component,
'status' => 'UNKNOWN',
'reason' => "Failed to query Moodle.org API. HTTP Code: {$http_code}, cURL Error: {$curl_error}. Please check network connectivity or Moodle.org status.",
'moodle_org_url' => "https://moodle.org/plugins/{$moodleorg_id}"
];
}
} else {
// This plugin does not have a Moodle.org ID. It's likely a custom or locally installed plugin.
$unknown_plugins[] = [
'component' => $component,
'status' => 'CUSTOM/UNLISTED',
'reason' => 'Not linked to Moodle.org (custom/local plugin). Requires manual review of documentation/Git repository for compatibility.',
'filesystem_path' => $plugin_data['filesystem_path'],
'moodle_org_url' => null
];
}
}
// --- Generate and Display Report ---
// Attempt to convert the build number to a human-readable version name for the report.
// Note: core_version::get_version_name might not have information for future Moodle versions
// that are not yet released or installed on the current system.
$target_moodle_version_name = \core_version::get_version_name($target_moodle_build_number);
if ($target_moodle_version_name === false || $target_moodle_version_name === null || $target_moodle_version_name === '') {
$target_moodle_version_name = "Build " . $target_moodle_build_number; // Fallback for unknown versions.
}
echo "\n--- Compatibility Report for Target Moodle: " . $target_moodle_version_name . " ---\n";
echo "Total additional plugins checked: " . count($installed_plugins) . "\n";
echo " - Compatible: " . count($compatible_plugins) . "\n";
echo " - Incompatible: " . count($incompatible_plugins) . "\n";
echo " - Custom/Unlisted/Unknown: " . count($unknown_plugins) . "\n\n";
echo "--- COMPATIBLE PLUGINS (" . count($compatible_plugins) . ") ---\n";
if (!empty($compatible_plugins)) {
foreach ($compatible_plugins as $plugin) {
echo " [✓] " . $plugin['component'] . "\n";
echo " - Status: " . $plugin['status'] . "\n";
echo " - Latest compatible release: " . $plugin['latest_compatible_release'] . "\n";
echo " - Moodle.org Page: " . $plugin['moodle_org_url'] . "\n";
}
} else {
echo " No compatible plugins identified in this category.\n";
}
echo "\n";
echo "--- INCOMPATIBLE PLUGINS (" . count($incompatible_plugins) . ") ---\n";
if (!empty($incompatible_plugins)) {
echo " Action required: Consider disabling, uninstalling, or finding alternatives for these plugins before upgrading Moodle.\n";
foreach ($incompatible_plugins as $plugin) {
echo " [✗] " . $plugin['component'] . "\n";
echo " - Status: " . $plugin['status'] . "\n";
echo " - Reason: " . $plugin['reason'] . "\n";
echo " - Moodle.org Page: " . $plugin['moodle_org_url'] . "\n";
}
} else {
echo " No incompatible plugins identified in this category.\n";
}
echo "\n";
echo "--- CUSTOM / UNLISTED / UNKNOWN PLUGINS (" . count($unknown_plugins) . ") ---\n";
if (!empty($unknown_plugins)) {
echo " These plugins require manual verification as their compatibility could not be determined automatically.\n";
echo " Action required: Consult their documentation, Git repositories, or developers. Thorough testing in a staging environment is highly recommended.\n";
foreach ($unknown_plugins as $plugin) {
echo " [?] " . $plugin['component'] . "\n";
echo " - Status: " . $plugin['status'] . "\n";
echo " - Reason: " . $plugin['reason'] . "\n";
if ($plugin['filesystem_path']) {
echo " - Filesystem Path: " . $plugin['filesystem_path'] . "\n";
}
if ($plugin['moodle_org_url']) {
echo " - Moodle.org Page: " . $plugin['moodle_org_url'] . "\n";
}
}
} else {
echo " No custom or unlisted plugins found.\n";
}
echo "\n--- Report End ---\n";<i>
</i><e> ```</e></CODE></LI></LIST>
<H4><s>#### </s>Step 5.2: Execute the Script</H4>
<LIST type="decimal"><LI><s>1. </s><p><STRONG><s>**</s>Run the script from the <C><s>`</s>admin/cli/<e>`</e></C> directory:<e>**</e></STRONG><br/>
Execute the script using the <C><s>`</s>php<e>`</e></C> CLI, providing the <EM><s>*</s>build number<e>*</e></EM> of your target Moodle major version as the first argument. Ensure you run the command as the web server user to avoid permission issues.</p>
<LIST><LI><s>* </s><p><STRONG><s>**</s>Example for Moodle 4.1 (Build Number: <C><s>`</s>2022112900<e>`</e></C>):<e>**</e></STRONG></p>
<CODE lang="bash"><s> ```bash</s><i>
</i> sudo -u www-data php custom_plugin_compatibility_checker.php 2022112900<i>
</i><e> ```</e></CODE>
<p>(Replace <C><s>`</s>www-data<e>`</e></C> with the actual user your web server runs as, e.g., <C><s>`</s>apache<e>`</e></C>, <C><s>`</s>nginx<e>`</e></C>, <C><s>`</s>_www<e>`</e></C> on macOS/BSD, or the user Moodle processes run under.)</p></LI>
<LI><s>* </s><p><STRONG><s>**</s>Example for Moodle 4.2 (Build Number: <C><s>`</s>2023042400<e>`</e></C>):<e>**</e></STRONG></p>
<CODE lang="bash"><s> ```bash</s><i>
</i> sudo -u www-data php custom_plugin_compatibility_checker.php 2023042400<i>
</i><e> ```</e></CODE></LI>
<LI><s>* </s><p><STRONG><s>**</s>Example for Moodle 5.0 (Hypothetical future version, example build number):<e>**</e></STRONG></p>
<CODE lang="bash"><s> ```bash</s><i>
</i> sudo -u www-data php custom_plugin_compatibility_checker.php 2024051300<i>
</i><e> ```</e></CODE>
<p><EM><s>*</s>If you are unsure of the web server user, you can often find it by checking <C><s>`</s>ps aux | grep apache<e>`</e></C> or <C><s>`</s>ps aux | grep nginx<e>`</e></C> and looking at the user for the PHP-FPM or web server worker processes.<e>*</e></EM></p></LI></LIST></LI></LIST>
<H4><s>#### </s>Step 5.3: Analyze the Report</H4>
<p>The script will output a detailed compatibility report to your console, categorized as follows:</p>
<LIST><LI><s>* </s><p><STRONG><s>**</s>COMPATIBLE PLUGINS:<e>**</e></STRONG><br/>
These plugins have a version available on Moodle.org that is compatible with your target Moodle build. You should plan to upgrade these plugins to their latest compatible versions <EM><s>*</s>after<e>*</e></EM> the Moodle core upgrade. The report will include the latest compatible release and a direct link to the Moodle.org plugin page.</p></LI>
<LI><s>* </s><p><STRONG><s>**</s>INCOMPATIBLE PLUGINS:<e>**</e></STRONG><br/>
No compatible version was found on Moodle.org for these plugins for your target Moodle build. For these, you must:</p>
<LIST><LI><s>* </s><STRONG><s>**</s>Verify Manually:<e>**</e></STRONG> Check the plugin's Moodle.org page directly for any beta/development versions or specific compatibility notes not fully exposed by the API.</LI>
<LI><s>* </s><STRONG><s>**</s>Seek Alternatives:<e>**</e></STRONG> Identify and plan to implement alternative plugins that offer similar functionality and are compatible.</LI>
<LI><s>* </s><STRONG><s>**</s>Contact Developer:<e>**</e></STRONG> Reach out to the plugin developer for their roadmap or custom solutions.</LI>
<LI><s>* </s><STRONG><s>**</s>Disable/Uninstall:<e>**</e></STRONG> Plan to disable or completely uninstall these plugins <EM><s>*</s>before<e>*</e></EM> the Moodle core upgrade to prevent fatal errors or functional breakdowns during the upgrade process.</LI></LIST></LI>
<LI><s>* </s><p><STRONG><s>**</s>CUSTOM / UNLISTED / UNKNOWN PLUGINS:<e>**</e></STRONG><br/>
These are plugins that were not found in the Moodle.org database (e.g., custom-developed local plugins, plugins installed from private Git repositories, or very old plugins not linked to an official Moodle.org ID). Their compatibility cannot be determined automatically. For these, you must manually:</p>
<LIST><LI><s>* </s><STRONG><s>**</s>Consult <C><s>`</s>version.php<e>`</e></C>:<e>**</e></STRONG> Review their <C><s>`</s>version.php<e>`</e></C> file for the <C><s>`</s>$plugin->requires<e>`</e></C> variable, which indicates the <EM><s>*</s>minimum<e>*</e></EM> required Moodle version. This is not a guarantee of <EM><s>*</s>future<e>*</e></EM> compatibility, but a starting point.</LI>
<LI><s>* </s><STRONG><s>**</s>Check Documentation/Repository:<e>**</e></STRONG> Consult the plugin's official documentation, GitHub/GitLab repository, or contact the developer for specific compatibility information with your target Moodle version.</LI>
<LI><s>* </s><STRONG><s>**</s>Thorough Testing:<e>**</e></STRONG> After the Moodle core upgrade, these plugins must be thoroughly tested in a staging environment to confirm their functionality.</LI></LIST></LI></LIST>
<H3><s>### </s>6. Post-Execution</H3>
<LIST type="decimal"><LI><s>1. </s><STRONG><s>**</s>Save the Report:<e>**</e></STRONG> Redirect the console output to a file for easy reference and audit trails.
<CODE lang="bash"><s> ```bash</s><i>
</i> sudo -u www-data php custom_plugin_compatibility_checker.php 2022112900 > moodle_plugin_compatibility_report_4.1_$(date +%Y%m%d).txt<i>
</i><e> ```</e></CODE></LI>
<LI><s>2. </s><STRONG><s>**</s>Develop an Action Plan:<e>**</e></STRONG> Based on the detailed report, formulate a comprehensive action plan for your Moodle upgrade. This plan should specifically address each category of plugins (compatible, incompatible, custom), outlining necessary updates, replacements, disabling, or further investigation.</LI>
<LI><s>3. </s><STRONG><s>**</s>Staging Environment:<e>**</e></STRONG> Always perform Moodle upgrades and plugin testing in a dedicated staging environment before applying changes to your production site.</LI>
<LI><s>4. </s><STRONG><s>**</s>Cleanup (Optional):<e>**</e></STRONG> You may remove the <C><s>`</s>admin/cli/custom_plugin_compatibility_checker.php<e>`</e></C> script after use, or keep it in place for future upgrade planning.</LI></LIST>
<H3><s>### </s>7. Benefits</H3>
<LIST><LI><s>* </s><STRONG><s>**</s>Proactive Issue Identification:<e>**</e></STRONG> Pinpoints potential plugin compatibility problems <EM><s>*</s>before<e>*</e></EM> they disrupt the live Moodle site during an upgrade.</LI>
<LI><s>* </s><STRONG><s>**</s>Reduced Downtime & Risk:<e>**</e></STRONG> Minimizes unexpected errors, extensive debugging, and the risk of a broken Moodle site after an upgrade.</LI>
<LI><s>* </s><STRONG><s>**</s>Informed Decision-Making:<e>**</e></STRONG> Provides clear, actionable data to plan plugin updates, replacements, custom development, or disabling strategies.</LI>
<LI><s>* </s><STRONG><s>**</s>Streamlined Upgrade Process:<e>**</e></STRONG> Contributes to a smoother and more predictable Moodle major version upgrade.</LI></LIST>
<H3><s>### </s>8. Known Limitations</H3>
<LIST><LI><s>* </s><STRONG><s>**</s>Moodle.org API Dependency:<e>**</e></STRONG> The script relies on the public <C><s>`</s>moodle.org/webservice/pluginversions.php<e>`</e></C> endpoint. Any changes to this API, or its temporary unavailability, will affect the script's functionality.</LI>
<LI><s>* </s><STRONG><s>**</s>Custom Plugin Manual Review:<e>**</e></STRONG> For plugins not listed on Moodle.org, automatic compatibility determination is not possible. Manual investigation and testing remain essential.</LI>
<LI><s>* </s><STRONG><s>**</s>Subplugin Definition:<e>**</e></STRONG> Moodle's plugin architecture can sometimes make distinguishing "top-level" plugins from integrated "subplugins" ambiguous. This script treats any component flagged as <C><s>`</s>is_additional<e>`</e></C> by Moodle's internal API as a distinct entity requiring a compatibility check, as it can have its own <C><s>`</s>version.php<e>`</e></C> and Moodle.org ID. The user's original concern about "subplugins not shown when integrated in plugins" is challenging to automate reliably without explicit Moodle API support for parent-child plugin relationships.</LI></LIST></r>
Did this solution help you resolve the issue?