source: moodle/trunk/fuentes/lib/classes/plugininfo/base.php @ 1331

Last change on this file since 1331 was 1331, checked in by jrpelegrina, 3 years ago

Updated to moodle 3.0.3

File size: 19.6 KB
Line 
1<?php
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
17/**
18 * Defines classes used for plugin info.
19 *
20 * @package    core
21 * @copyright  2011 David Mudrak <david@moodle.com>
22 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23 */
24namespace core\plugininfo;
25
26use core_component, core_plugin_manager, moodle_url, coding_exception;
27
28defined('MOODLE_INTERNAL') || die();
29
30
31/**
32 * Base class providing access to the information about a plugin
33 *
34 * @property-read string component the component name, type_name
35 */
36abstract class base {
37
38    /** @var string the plugintype name, eg. mod, auth or workshopform */
39    public $type;
40    /** @var string full path to the location of all the plugins of this type */
41    public $typerootdir;
42    /** @var string the plugin name, eg. assignment, ldap */
43    public $name;
44    /** @var string the localized plugin name */
45    public $displayname;
46    /** @var string the plugin source, one of core_plugin_manager::PLUGIN_SOURCE_xxx constants */
47    public $source;
48    /** @var string fullpath to the location of this plugin */
49    public $rootdir;
50    /** @var int|string the version of the plugin's source code */
51    public $versiondisk;
52    /** @var int|string the version of the installed plugin */
53    public $versiondb;
54    /** @var int|float|string required version of Moodle core  */
55    public $versionrequires;
56    /** @var mixed human-readable release information */
57    public $release;
58    /** @var array other plugins that this one depends on, lazy-loaded by {@link get_other_required_plugins()} */
59    public $dependencies;
60    /** @var int number of instances of the plugin - not supported yet */
61    public $instances;
62    /** @var int order of the plugin among other plugins of the same type - not supported yet */
63    public $sortorder;
64    /** @var core_plugin_manager the plugin manager this plugin info is part of */
65    public $pluginman;
66
67    /** @var array|null array of {@link \core\update\info} for this plugin */
68    protected $availableupdates;
69
70    /**
71     * Finds all enabled plugins, the result may include missing plugins.
72     * @return array|null of enabled plugins $pluginname=>$pluginname, null means unknown
73     */
74    public static function get_enabled_plugins() {
75        return null;
76    }
77
78    /**
79     * Gathers and returns the information about all plugins of the given type,
80     * either on disk or previously installed.
81     *
82     * This is supposed to be used exclusively by the plugin manager when it is
83     * populating its tree of plugins.
84     *
85     * @param string $type the name of the plugintype, eg. mod, auth or workshopform
86     * @param string $typerootdir full path to the location of the plugin dir
87     * @param string $typeclass the name of the actually called class
88     * @param core_plugin_manager $pluginman the plugin manager calling this method
89     * @return array of plugintype classes, indexed by the plugin name
90     */
91    public static function get_plugins($type, $typerootdir, $typeclass, $pluginman) {
92        // Get the information about plugins at the disk.
93        $plugins = core_component::get_plugin_list($type);
94        $return = array();
95        foreach ($plugins as $pluginname => $pluginrootdir) {
96            $return[$pluginname] = self::make_plugin_instance($type, $typerootdir,
97                $pluginname, $pluginrootdir, $typeclass, $pluginman);
98        }
99
100        // Fetch missing incorrectly uninstalled plugins.
101        $plugins = $pluginman->get_installed_plugins($type);
102
103        foreach ($plugins as $name => $version) {
104            if (isset($return[$name])) {
105                continue;
106            }
107            $plugin              = new $typeclass();
108            $plugin->type        = $type;
109            $plugin->typerootdir = $typerootdir;
110            $plugin->name        = $name;
111            $plugin->rootdir     = null;
112            $plugin->displayname = $name;
113            $plugin->versiondb   = $version;
114            $plugin->pluginman   = $pluginman;
115            $plugin->init_is_standard();
116
117            $return[$name] = $plugin;
118        }
119
120        return $return;
121    }
122
123    /**
124     * Makes a new instance of the plugininfo class
125     *
126     * @param string $type the plugin type, eg. 'mod'
127     * @param string $typerootdir full path to the location of all the plugins of this type
128     * @param string $name the plugin name, eg. 'workshop'
129     * @param string $namerootdir full path to the location of the plugin
130     * @param string $typeclass the name of class that holds the info about the plugin
131     * @param core_plugin_manager $pluginman the plugin manager of the new instance
132     * @return base the instance of $typeclass
133     */
134    protected static function make_plugin_instance($type, $typerootdir, $name, $namerootdir, $typeclass, $pluginman) {
135        $plugin              = new $typeclass();
136        $plugin->type        = $type;
137        $plugin->typerootdir = $typerootdir;
138        $plugin->name        = $name;
139        $plugin->rootdir     = $namerootdir;
140        $plugin->pluginman   = $pluginman;
141
142        $plugin->init_display_name();
143        $plugin->load_disk_version();
144        $plugin->load_db_version();
145        $plugin->init_is_standard();
146
147        return $plugin;
148    }
149
150    /**
151     * Is this plugin already installed and updated?
152     * @return bool true if plugin installed and upgraded.
153     */
154    public function is_installed_and_upgraded() {
155        if (!$this->rootdir) {
156            return false;
157        }
158        if ($this->versiondb === null and $this->versiondisk === null) {
159            // There is no version.php or version info inside it.
160            return false;
161        }
162
163        return ((float)$this->versiondb === (float)$this->versiondisk);
164    }
165
166    /**
167     * Sets {@link $displayname} property to a localized name of the plugin
168     */
169    public function init_display_name() {
170        if (!get_string_manager()->string_exists('pluginname', $this->component)) {
171            $this->displayname = '[pluginname,' . $this->component . ']';
172        } else {
173            $this->displayname = get_string('pluginname', $this->component);
174        }
175    }
176
177    /**
178     * Magic method getter, redirects to read only values.
179     *
180     * @param string $name
181     * @return mixed
182     */
183    public function __get($name) {
184        switch ($name) {
185            case 'component': return $this->type . '_' . $this->name;
186
187            default:
188                debugging('Invalid plugin property accessed! '.$name);
189                return null;
190        }
191    }
192
193    /**
194     * Return the full path name of a file within the plugin.
195     *
196     * No check is made to see if the file exists.
197     *
198     * @param string $relativepath e.g. 'version.php'.
199     * @return string e.g. $CFG->dirroot . '/mod/quiz/version.php'.
200     */
201    public function full_path($relativepath) {
202        if (empty($this->rootdir)) {
203            return '';
204        }
205        return $this->rootdir . '/' . $relativepath;
206    }
207
208    /**
209     * Sets {@link $versiondisk} property to a numerical value representing the
210     * version of the plugin's source code.
211     *
212     * If the value is null after calling this method, either the plugin
213     * does not use versioning (typically does not have any database
214     * data) or is missing from disk.
215     */
216    public function load_disk_version() {
217        $versions = $this->pluginman->get_present_plugins($this->type);
218
219        $this->versiondisk = null;
220        $this->versionrequires = null;
221        $this->dependencies = array();
222
223        if (!isset($versions[$this->name])) {
224            return;
225        }
226
227        $plugin = $versions[$this->name];
228
229        if (isset($plugin->version)) {
230            $this->versiondisk = $plugin->version;
231        }
232        if (isset($plugin->requires)) {
233            $this->versionrequires = $plugin->requires;
234        }
235        if (isset($plugin->release)) {
236            $this->release = $plugin->release;
237        }
238        if (isset($plugin->dependencies)) {
239            $this->dependencies = $plugin->dependencies;
240        }
241    }
242
243    /**
244     * Get the list of other plugins that this plugin requires to be installed.
245     *
246     * @return array with keys the frankenstyle plugin name, and values either
247     *      a version string (like '2011101700') or the constant ANY_VERSION.
248     */
249    public function get_other_required_plugins() {
250        if (is_null($this->dependencies)) {
251            $this->load_disk_version();
252        }
253        return $this->dependencies;
254    }
255
256    /**
257     * Is this is a subplugin?
258     *
259     * @return boolean
260     */
261    public function is_subplugin() {
262        return ($this->get_parent_plugin() !== false);
263    }
264
265    /**
266     * If I am a subplugin, return the name of my parent plugin.
267     *
268     * @return string|bool false if not a subplugin, name of the parent otherwise
269     */
270    public function get_parent_plugin() {
271        return $this->pluginman->get_parent_of_subplugin($this->type);
272    }
273
274    /**
275     * Sets {@link $versiondb} property to a numerical value representing the
276     * currently installed version of the plugin.
277     *
278     * If the value is null after calling this method, either the plugin
279     * does not use versioning (typically does not have any database
280     * data) or has not been installed yet.
281     */
282    public function load_db_version() {
283        $versions = $this->pluginman->get_installed_plugins($this->type);
284
285        if (isset($versions[$this->name])) {
286            $this->versiondb = $versions[$this->name];
287        } else {
288            $this->versiondb = null;
289        }
290    }
291
292    /**
293     * Sets {@link $source} property to one of core_plugin_manager::PLUGIN_SOURCE_xxx
294     * constants.
295     *
296     * If the property's value is null after calling this method, then
297     * the type of the plugin has not been recognized and you should throw
298     * an exception.
299     */
300    public function init_is_standard() {
301
302        $pluginman = $this->pluginman;
303        $standard = $pluginman::standard_plugins_list($this->type);
304
305        if ($standard !== false) {
306            $standard = array_flip($standard);
307            if (isset($standard[$this->name])) {
308                $this->source = core_plugin_manager::PLUGIN_SOURCE_STANDARD;
309            } else if (!is_null($this->versiondb) and is_null($this->versiondisk)
310                and $pluginman::is_deleted_standard_plugin($this->type, $this->name)) {
311                $this->source = core_plugin_manager::PLUGIN_SOURCE_STANDARD; // To be deleted.
312            } else {
313                $this->source = core_plugin_manager::PLUGIN_SOURCE_EXTENSION;
314            }
315        }
316    }
317
318    /**
319     * Returns true if the plugin is shipped with the official distribution
320     * of the current Moodle version, false otherwise.
321     *
322     * @return bool
323     */
324    public function is_standard() {
325        return $this->source === core_plugin_manager::PLUGIN_SOURCE_STANDARD;
326    }
327
328    /**
329     * Returns true if the the given Moodle version is enough to run this plugin
330     *
331     * @param string|int|double $moodleversion
332     * @return bool
333     */
334    public function is_core_dependency_satisfied($moodleversion) {
335
336        if (empty($this->versionrequires)) {
337            return true;
338
339        } else {
340            return (double)$this->versionrequires <= (double)$moodleversion;
341        }
342    }
343
344    /**
345     * Returns the status of the plugin
346     *
347     * @return string one of core_plugin_manager::PLUGIN_STATUS_xxx constants
348     */
349    public function get_status() {
350
351        $pluginman = $this->pluginman;
352
353        if (is_null($this->versiondb) and is_null($this->versiondisk)) {
354            return core_plugin_manager::PLUGIN_STATUS_NODB;
355
356        } else if (is_null($this->versiondb) and !is_null($this->versiondisk)) {
357            return core_plugin_manager::PLUGIN_STATUS_NEW;
358
359        } else if (!is_null($this->versiondb) and is_null($this->versiondisk)) {
360            if ($pluginman::is_deleted_standard_plugin($this->type, $this->name)) {
361                return core_plugin_manager::PLUGIN_STATUS_DELETE;
362            } else {
363                return core_plugin_manager::PLUGIN_STATUS_MISSING;
364            }
365
366        } else if ((float)$this->versiondb === (float)$this->versiondisk) {
367            // Note: the float comparison should work fine here
368            //       because there are no arithmetic operations with the numbers.
369            return core_plugin_manager::PLUGIN_STATUS_UPTODATE;
370
371        } else if ($this->versiondb < $this->versiondisk) {
372            return core_plugin_manager::PLUGIN_STATUS_UPGRADE;
373
374        } else if ($this->versiondb > $this->versiondisk) {
375            return core_plugin_manager::PLUGIN_STATUS_DOWNGRADE;
376
377        } else {
378            // $version = pi(); and similar funny jokes - hopefully Donald E. Knuth will never contribute to Moodle ;-)
379            throw new coding_exception('Unable to determine plugin state, check the plugin versions');
380        }
381    }
382
383    /**
384     * Returns the information about plugin availability
385     *
386     * True means that the plugin is enabled. False means that the plugin is
387     * disabled. Null means that the information is not available, or the
388     * plugin does not support configurable availability or the availability
389     * can not be changed.
390     *
391     * @return null|bool
392     */
393    public function is_enabled() {
394        if (!$this->rootdir) {
395            // Plugin missing.
396            return false;
397        }
398
399        $enabled = $this->pluginman->get_enabled_plugins($this->type);
400
401        if (!is_array($enabled)) {
402            return null;
403        }
404
405        return isset($enabled[$this->name]);
406    }
407
408    /**
409     * If there are updates for this plugin available, returns them.
410     *
411     * Returns array of {@link \core\update\info} objects, if some update
412     * is available. Returns null if there is no update available or if the update
413     * availability is unknown.
414     *
415     * Populates the property {@link $availableupdates} on first call (lazy
416     * loading).
417     *
418     * @return array|null
419     */
420    public function available_updates() {
421
422        if ($this->availableupdates === null) {
423            // Lazy load the information about available updates.
424            $this->availableupdates = $this->pluginman->load_available_updates_for_plugin($this->component);
425        }
426
427        if (empty($this->availableupdates) or !is_array($this->availableupdates)) {
428            $this->availableupdates = array();
429            return null;
430        }
431
432        $updates = array();
433
434        foreach ($this->availableupdates as $availableupdate) {
435            if ($availableupdate->version > $this->versiondisk) {
436                $updates[] = $availableupdate;
437            }
438        }
439
440        if (empty($updates)) {
441            return null;
442        }
443
444        return $updates;
445    }
446
447    /**
448     * Returns the node name used in admin settings menu for this plugin settings (if applicable)
449     *
450     * @return null|string node name or null if plugin does not create settings node (default)
451     */
452    public function get_settings_section_name() {
453        return null;
454    }
455
456    /**
457     * Returns the URL of the plugin settings screen
458     *
459     * Null value means that the plugin either does not have the settings screen
460     * or its location is not available via this library.
461     *
462     * @return null|moodle_url
463     */
464    public function get_settings_url() {
465        $section = $this->get_settings_section_name();
466        if ($section === null) {
467            return null;
468        }
469        $settings = admin_get_root()->locate($section);
470        if ($settings && $settings instanceof \admin_settingpage) {
471            return new moodle_url('/admin/settings.php', array('section' => $section));
472        } else if ($settings && $settings instanceof \admin_externalpage) {
473            return new moodle_url($settings->url);
474        } else {
475            return null;
476        }
477    }
478
479    /**
480     * Loads plugin settings to the settings tree
481     *
482     * This function usually includes settings.php file in plugins folder.
483     * Alternatively it can create a link to some settings page (instance of admin_externalpage)
484     *
485     * @param \part_of_admin_tree $adminroot
486     * @param string $parentnodename
487     * @param bool $hassiteconfig whether the current user has moodle/site:config capability
488     */
489    public function load_settings(\part_of_admin_tree $adminroot, $parentnodename, $hassiteconfig) {
490    }
491
492    /**
493     * Should there be a way to uninstall the plugin via the administration UI.
494     *
495     * By default uninstallation is not allowed, plugin developers must enable it explicitly!
496     *
497     * @return bool
498     */
499    public function is_uninstall_allowed() {
500        return false;
501    }
502
503    /**
504     * Optional extra warning before uninstallation, for example number of uses in courses.
505     *
506     * @return string
507     */
508    public function get_uninstall_extra_warning() {
509        return '';
510    }
511
512    /**
513     * Pre-uninstall hook.
514     *
515     * This is intended for disabling of plugin, some DB table purging, etc.
516     *
517     * NOTE: to be called from uninstall_plugin() only.
518     * @private
519     */
520    public function uninstall_cleanup() {
521        // Override when extending class,
522        // do not forget to call parent::pre_uninstall_cleanup() at the end.
523    }
524
525    /**
526     * Returns relative directory of the plugin with heading '/'
527     *
528     * @return string
529     */
530    public function get_dir() {
531        global $CFG;
532
533        return substr($this->rootdir, strlen($CFG->dirroot));
534    }
535
536    /**
537     * Hook method to implement certain steps when uninstalling the plugin.
538     *
539     * This hook is called by {@link core_plugin_manager::uninstall_plugin()} so
540     * it is basically usable only for those plugin types that use the default
541     * uninstall tool provided by {@link self::get_default_uninstall_url()}.
542     *
543     * @param \progress_trace $progress traces the process
544     * @return bool true on success, false on failure
545     */
546    public function uninstall(\progress_trace $progress) {
547        return true;
548    }
549
550    /**
551     * Where should we return after plugin of this type is uninstalled?
552     * @param string $return
553     * @return moodle_url
554     */
555    public function get_return_url_after_uninstall($return) {
556        if ($return === 'manage') {
557            if ($url = $this->get_manage_url()) {
558                return $url;
559            }
560        }
561        return new moodle_url('/admin/plugins.php#plugin_type_cell_'.$this->type);
562    }
563
564    /**
565     * Return URL used for management of plugins of this type.
566     * @return moodle_url
567     */
568    public static function get_manage_url() {
569        return null;
570    }
571
572    /**
573     * Returns URL to a script that handles common plugin uninstall procedure.
574     *
575     * This URL is intended for all plugin uninstallations.
576     *
577     * @param string $return either 'overview' or 'manage'
578     * @return moodle_url
579     */
580    public final function get_default_uninstall_url($return = 'overview') {
581        return new moodle_url('/admin/plugins.php', array(
582            'sesskey' => sesskey(),
583            'uninstall' => $this->component,
584            'confirm' => 0,
585            'return' => $return,
586        ));
587    }
588}
Note: See TracBrowser for help on using the repository browser.