Ignore:
Timestamp:
May 2, 2016, 12:09:23 PM (3 years ago)
Author:
jrpelegrina
Message:

Updated to moodle 3.0.3

Location:
moodle/trunk/fuentes/lib/classes
Files:
30 added
3 deleted
130 edited

Legend:

Unmodified
Added
Removed
  • moodle/trunk/fuentes/lib/classes/component.php

    r136 r1331  
    101101            $debugging = "Class '%s' has been renamed for the autoloader and is now deprecated. Please use '%s' instead.";
    102102            debugging(sprintf($debugging, $classname, $newclassname), DEBUG_DEVELOPER);
     103            if (PHP_VERSION_ID >= 70000 && preg_match('#\\\null(\\\|$)#', $classname)) {
     104                throw new \coding_exception("Cannot alias $classname to $newclassname");
     105            }
    103106            class_alias($newclassname, $classname);
    104107            return;
     
    349352            'calendar'    => $CFG->dirroot.'/calendar',
    350353            'cohort'      => $CFG->dirroot.'/cohort',
    351             'completion'  => null,
     354            'comment'     => $CFG->dirroot.'/comment',
     355            'completion'  => $CFG->dirroot.'/completion',
    352356            'countries'   => null,
    353357            'course'      => $CFG->dirroot.'/course',
     
    648652        }
    649653
     654        if (!is_readable($fulldir)) {
     655            // TODO: MDL-51711 We should generate some diagnostic debugging information in this case
     656            // because its pretty likely to lead to a missing class error further down the line.
     657            // But our early setup code can't handle errors this early at the moment.
     658            return;
     659        }
     660
    650661        $items = new \DirectoryIterator($fulldir);
    651662        foreach ($items as $item) {
     
    707718    protected static function load_psr_classes($basedir, $subdir = null) {
    708719        if ($subdir) {
    709             $fulldir = implode(DIRECTORY_SEPARATOR, array($basedir, $subdir));
    710             $classnameprefix = preg_replace('/\//', '_', $subdir);
     720            $fulldir = realpath($basedir . DIRECTORY_SEPARATOR . $subdir);
     721            $classnameprefix = preg_replace('#' . preg_quote(DIRECTORY_SEPARATOR) . '#', '_', $subdir);
    711722        } else {
    712723            $fulldir = $basedir;
    713724        }
    714         if (!is_dir($fulldir)) {
     725        if (!$fulldir || !is_dir($fulldir)) {
    715726            return;
    716727        }
     
    11021113                $plugin->version = null;
    11031114                $module = $plugin;
    1104                 @include($fullplug.'/version.php');
     1115                include($fullplug.'/version.php');
    11051116                $versions[$type.'_'.$plug] = $plugin->version;
    11061117            }
  • moodle/trunk/fuentes/lib/classes/event/assessable_uploaded.php

    r136 r1331  
    7878    }
    7979
     80    public static function get_other_mapping() {
     81        // Nothing to map.
     82        return false;
     83    }
    8084}
  • moodle/trunk/fuentes/lib/classes/event/base.php

    r136 r1331  
    7575     */
    7676    const LEVEL_PARTICIPATING = 2;
     77
     78    /**
     79     * The value used when an id can not be mapped during a restore.
     80     */
     81    const NOT_MAPPED = -31337;
     82
     83    /**
     84     * The value used when an id can not be found during a restore.
     85     */
     86    const NOT_FOUND = -31338;
    7787
    7888    /** @var array event data */
     
    480490
    481491    /**
     492     * This is used when restoring course logs where it is required that we
     493     * map the objectid to it's new value in the new course.
     494     *
     495     * Does nothing in the base class except display a debugging message warning
     496     * the user that the event does not contain the required functionality to
     497     * map this information. For events that do not store an objectid this won't
     498     * be called, so no debugging message will be displayed.
     499     *
     500     * Example of usage:
     501     *
     502     * return array('db' => 'assign_submissions', 'restore' => 'submission');
     503     *
     504     * If the objectid can not be mapped during restore set the value to \core\event\base::NOT_MAPPED, example -
     505     *
     506     * return array('db' => 'some_table', 'restore' => \core\event\base::NOT_MAPPED);
     507     *
     508     * Note - it isn't necessary to specify the 'db' and 'restore' values in this case, so you can also use -
     509     *
     510     * return \core\event\base::NOT_MAPPED;
     511     *
     512     * The 'db' key refers to the database table and the 'restore' key refers to
     513     * the name of the restore element the objectid is associated with. In many
     514     * cases these will be the same.
     515     *
     516     * @return string the name of the restore mapping the objectid links to
     517     */
     518    public static function get_objectid_mapping() {
     519        debugging('In order to restore course logs accurately the event "' . get_called_class() . '" must define the
     520            function get_objectid_mapping().', DEBUG_DEVELOPER);
     521
     522        return false;
     523    }
     524
     525    /**
     526     * This is used when restoring course logs where it is required that we
     527     * map the information in 'other' to it's new value in the new course.
     528     *
     529     * Does nothing in the base class except display a debugging message warning
     530     * the user that the event does not contain the required functionality to
     531     * map this information. For events that do not store any other information this
     532     * won't be called, so no debugging message will be displayed.
     533     *
     534     * Example of usage:
     535     *
     536     * $othermapped = array();
     537     * $othermapped['discussionid'] = array('db' => 'forum_discussions', 'restore' => 'forum_discussion');
     538     * $othermapped['forumid'] = array('db' => 'forum', 'restore' => 'forum');
     539     * return $othermapped;
     540     *
     541     * If an id can not be mapped during restore we set it to \core\event\base::NOT_MAPPED, example -
     542     *
     543     * $othermapped = array();
     544     * $othermapped['someid'] = array('db' => 'some_table', 'restore' => \core\event\base::NOT_MAPPED);
     545     * return $othermapped;
     546     *
     547     * Note - it isn't necessary to specify the 'db' and 'restore' values in this case, so you can also use -
     548     *
     549     * $othermapped = array();
     550     * $othermapped['someid'] = \core\event\base::NOT_MAPPED;
     551     * return $othermapped;
     552     *
     553     * The 'db' key refers to the database table and the 'restore' key refers to
     554     * the name of the restore element the other value is associated with. In many
     555     * cases these will be the same.
     556     *
     557     * @return array an array of other values and their corresponding mapping
     558     */
     559    public static function get_other_mapping() {
     560        debugging('In order to restore course logs accurately the event "' . get_called_class() . '" must define the
     561            function get_other_mapping().', DEBUG_DEVELOPER);
     562    }
     563
     564    /**
    482565     * Get static information about an event.
    483566     * This is used in reports and is not for general use.
  • moodle/trunk/fuentes/lib/classes/event/blog_association_created.php

    r136 r1331  
    126126        }
    127127    }
     128
     129    public static function get_objectid_mapping() {
     130        // Blogs are not included in backups, so no mapping required for restore.
     131        return array('db' => 'blog_association', 'restore' => base::NOT_MAPPED);
     132    }
     133
     134    public static function get_other_mapping() {
     135        // Blogs are not included in backups, so no mapping required for restore.
     136        $othermapped = array();
     137        $othermapped['blogid'] = array('db' => 'post', 'restore' => base::NOT_MAPPED);
     138        // The associateid field is varying (context->instanceid) so cannot be mapped.
     139
     140        return $othermapped;
     141    }
    128142}
  • moodle/trunk/fuentes/lib/classes/event/blog_comment_created.php

    r136 r1331  
    5353        return "The user with id '$this->userid' added the comment to the blog with id '{$this->other['itemid']}'.";
    5454    }
     55
     56    public static function get_other_mapping() {
     57        $othermapped = array();
     58        $othermapped['itemid'] = array('db' => 'post', 'restore' => base::NOT_MAPPED);
     59        return $othermapped;
     60    }
    5561}
  • moodle/trunk/fuentes/lib/classes/event/blog_comment_deleted.php

    r136 r1331  
    5353        return "The user with id '$this->userid' deleted the comment for the blog with id '{$this->other['itemid']}'.";
    5454    }
     55
     56    public static function get_other_mapping() {
     57        $othermapped = array();
     58        $othermapped['itemid'] = array('db' => 'post', 'restore' => base::NOT_MAPPED);
     59        return $othermapped;
     60    }
    5561}
  • moodle/trunk/fuentes/lib/classes/event/blog_entries_viewed.php

    r136 r1331  
    107107        return array (SITEID, 'blog', 'view', $url->out(), 'view blog entry');
    108108    }
     109
     110    public static function get_other_mapping() {
     111        $othermapped = array();
     112        $othermapped['entryid'] = array('db' => 'post', 'restore' => base::NOT_MAPPED);
     113        $othermapped['tagid'] = array('db' => 'tag', 'restore' => base::NOT_MAPPED);
     114        $othermapped['userid'] = array('db' => 'user', 'restore' => 'user');
     115        $othermapped['modid'] = array('db' => 'course_modules', 'restore' => 'course_module');
     116        $othermapped['groupid'] = array('db' => 'groups', 'restore' => 'group');
     117        $othermapped['courseid'] = array('db' => 'course', 'restore' => 'course');
     118
     119        return $othermapped;
     120    }
    109121}
  • moodle/trunk/fuentes/lib/classes/event/blog_entry_created.php

    r136 r1331  
    141141        }
    142142    }
     143
     144    public static function get_objectid_mapping() {
     145        // Blogs are not backed up, so no mapping required for restore.
     146        return array('db' => 'post', 'restore' => base::NOT_MAPPED);
     147    }
    143148}
  • moodle/trunk/fuentes/lib/classes/event/blog_entry_deleted.php

    r136 r1331  
    132132        }
    133133    }
     134
     135    public static function get_objectid_mapping() {
     136        // Blogs are not backed up, so no need for mapping for restore.
     137        return array('db' => 'post', 'restore' => base::NOT_MAPPED);
     138    }
    134139}
  • moodle/trunk/fuentes/lib/classes/event/blog_entry_updated.php

    r136 r1331  
    139139        }
    140140    }
     141
     142    public static function get_objectid_mapping() {
     143        // Blogs are not backed up, so no need for mapping for restore.
     144        return array('db' => 'post', 'restore' => NOT_MAPPED);
     145    }
    141146}
    142147
  • moodle/trunk/fuentes/lib/classes/event/calendar_event_created.php

    r136 r1331  
    105105            throw new \coding_exception('The \'repeatid\' value must be set in other.');
    106106        }
    107         if (empty($this->other['name'])) {
     107        if (!isset($this->other['name'])) {
    108108            throw new \coding_exception('The \'name\' value must be set in other.');
    109109        }
     
    112112        }
    113113    }
     114
     115    public static function get_objectid_mapping() {
     116        return array('db' => 'event', 'restore' => 'event');
     117    }
     118
     119    public static function get_other_mapping() {
     120        $othermapped = array();
     121        $othermapped['repeatid'] = array('db' => 'event', 'restore' => 'event');
     122
     123        return $othermapped;
     124    }
    114125}
  • moodle/trunk/fuentes/lib/classes/event/calendar_event_deleted.php

    r136 r1331  
    8686            throw new \coding_exception('The \'repeatid\' value must be set in other.');
    8787        }
    88         if (empty($this->other['name'])) {
     88        if (!isset($this->other['name'])) {
    8989            throw new \coding_exception('The \'name\' value must be set in other.');
    9090        }
     
    9393        }
    9494    }
     95
     96    public static function get_objectid_mapping() {
     97        return array('db' => 'event', 'restore' => 'event');
     98    }
     99
     100    public static function get_other_mapping() {
     101        $othermapped = array();
     102        $othermapped['repeatid'] = array('db' => 'event', 'restore' => 'event');
     103
     104        return $othermapped;
     105    }
    95106}
  • moodle/trunk/fuentes/lib/classes/event/calendar_event_updated.php

    r136 r1331  
    104104            throw new \coding_exception('The \'repeatid\' value must be set in other.');
    105105        }
    106         if (empty($this->other['name'])) {
     106        if (!isset($this->other['name'])) {
    107107            throw new \coding_exception('The \'name\' value must be set in other.');
    108108        }
     
    111111        }
    112112    }
     113
     114    public static function get_objectid_mapping() {
     115        return array('db' => 'event', 'restore' => 'event');
     116    }
     117
     118    public static function get_other_mapping() {
     119        $othermapped = array();
     120        $othermapped['repeatid'] = array('db' => 'event', 'restore' => 'event');
     121
     122        return $othermapped;
     123    }
    113124}
  • moodle/trunk/fuentes/lib/classes/event/cohort_created.php

    r136 r1331  
    9191        return $this->get_record_snapshot('cohort', $this->objectid);
    9292    }
     93
     94    public static function get_objectid_mapping() {
     95        // Cohorts are not included in backups, so no mapping is needed for restore.
     96        return array('db' => 'cohort', 'restore' => base::NOT_MAPPED);
     97    }
    9398}
  • moodle/trunk/fuentes/lib/classes/event/cohort_deleted.php

    r136 r1331  
    9191        return $this->get_record_snapshot('cohort', $this->objectid);
    9292    }
     93
     94    public static function get_objectid_mapping() {
     95        // Cohorts are not included in backups, so no mapping is needed for restore.
     96        return array('db' => 'cohort', 'restore' => base::NOT_MAPPED);
     97    }
    9398}
  • moodle/trunk/fuentes/lib/classes/event/cohort_member_added.php

    r136 r1331  
    109109        }
    110110    }
     111
     112    public static function get_objectid_mapping() {
     113        // Cohorts are not included in backups, so no mapping is needed for restore.
     114        return array('db' => 'cohort', 'restore' => base::NOT_MAPPED);
     115    }
    111116}
  • moodle/trunk/fuentes/lib/classes/event/cohort_member_removed.php

    r136 r1331  
    110110        }
    111111    }
     112
     113    public static function get_objectid_mapping() {
     114        // Cohorts are not included in backups, so no mapping is needed for restore.
     115        return array('db' => 'cohort', 'restore' => base::NOT_MAPPED);
     116    }
    112117}
  • moodle/trunk/fuentes/lib/classes/event/cohort_updated.php

    r136 r1331  
    9191        return $this->get_record_snapshot('cohort', $this->objectid);
    9292    }
     93
     94    public static function get_objectid_mapping() {
     95        // Cohorts are not included in backups, so no mapping is needed for restore.
     96        return array('db' => 'cohort', 'restore' => base::NOT_MAPPED);
     97    }
    9398}
  • moodle/trunk/fuentes/lib/classes/event/comment_created.php

    r136 r1331  
    101101        }
    102102    }
     103
     104    public static function get_objectid_mapping() {
     105        return array('db' => 'comments', 'restore' => 'comment');
     106    }
     107
     108    public static function get_other_mapping() {
     109        // We cannot map fields that do not have a 1:1 mapping.
     110        $othermapped = array();
     111        $othermapped['itemid'] = base::NOT_MAPPED;
     112        return $othermapped;
     113    }
    103114}
  • moodle/trunk/fuentes/lib/classes/event/comment_deleted.php

    r136 r1331  
    101101        }
    102102    }
     103
     104    public static function get_objectid_mapping() {
     105        return array('db' => 'comments', 'restore' => 'comment');
     106    }
     107
     108    public static function get_other_mapping() {
     109        // We cannot map fields that do not have a 1:1 mapping.
     110        $othermapped = array();
     111        $othermapped['itemid'] = base::NOT_MAPPED;
     112        return $othermapped;
     113    }
    103114}
  • moodle/trunk/fuentes/lib/classes/event/content_viewed.php

    r136 r1331  
    127127        }
    128128    }
     129
     130    public static function get_other_mapping() {
     131        return false;
     132    }
    129133}
    130134
  • moodle/trunk/fuentes/lib/classes/event/course_category_created.php

    r136 r1331  
    8181        return array(SITEID, 'category', 'add', 'editcategory.php?id=' . $this->objectid, $this->objectid);
    8282    }
     83
     84    public static function get_objectid_mapping() {
     85        // Categories are not backed up, so no need to map them on restore.
     86        return array('db' => 'course_categories', 'restore' => base::NOT_MAPPED);
     87    }
    8388}
  • moodle/trunk/fuentes/lib/classes/event/course_category_deleted.php

    r136 r1331  
    137137        }
    138138    }
     139
     140    public static function get_objectid_mapping() {
     141        // Categories are not backed up, so no need to map them on restore.
     142        return array('db' => 'course_categories', 'restore' => base::NOT_MAPPED);
     143    }
     144
     145    public static function get_other_mapping() {
     146        return false;
     147    }
    139148}
  • moodle/trunk/fuentes/lib/classes/event/course_category_updated.php

    r136 r1331  
    9797        return array(SITEID, 'category', 'update', 'editcategory.php?id=' . $this->objectid, $this->objectid);
    9898    }
     99
     100    public static function get_objectid_mapping() {
     101        // Categories are not backed up, so no need to map them on restore.
     102        return array('db' => 'course_categories', 'restore' => base::NOT_MAPPED);
     103    }
    99104}
  • moodle/trunk/fuentes/lib/classes/event/course_completed.php

    r136 r1331  
    135135        }
    136136    }
     137
     138    public static function get_objectid_mapping() {
     139        // Sorry - there is no mapping available for completion records.
     140        return array('db' => 'course_completions', 'restore' => base::NOT_MAPPED);
     141    }
     142
     143    public static function get_other_mapping() {
     144        $othermapped = array();
     145        $othermapped['relateduserid'] = array('db' => 'user', 'restore' => 'user');
     146        return $othermapped;
     147    }
    137148}
  • moodle/trunk/fuentes/lib/classes/event/course_content_deleted.php

    r136 r1331  
    105105        }
    106106    }
     107
     108    public static function get_objectid_mapping() {
     109        return array('db' => 'course', 'restore' => 'course');
     110    }
     111
     112    public static function get_other_mapping() {
     113        return false;
     114    }
    107115}
  • moodle/trunk/fuentes/lib/classes/event/course_created.php

    r136 r1331  
    120120        }
    121121    }
     122
     123    public static function get_objectid_mapping() {
     124        return array('db' => 'course', 'restore' => 'course');
     125    }
     126
     127    public static function get_other_mapping() {
     128        // Nothing to map.
     129        return false;
     130    }
    122131}
  • moodle/trunk/fuentes/lib/classes/event/course_deleted.php

    r136 r1331  
    115115        }
    116116    }
     117
     118    public static function get_objectid_mapping() {
     119        return array('db' => 'course', 'restore' => 'course');
     120    }
     121
     122    public static function get_other_mapping() {
     123        // Nothing to map.
     124        return false;
     125    }
    117126}
  • moodle/trunk/fuentes/lib/classes/event/course_module_completion_updated.php

    r136 r1331  
    2929/**
    3030 * Course module completion event class.
     31 *
     32 * @property-read array $other {
     33 *      Extra information about event.
     34 *
     35 *      - int relateduserid: (optional) the related user id.
     36 * }
    3137 *
    3238 * @package    core
     
    108114        }
    109115    }
     116
     117    public static function get_objectid_mapping() {
     118        // Sorry mapping info is not available for course modules completion records.
     119        return array('db' => 'course_modules_completion', 'restore' => base::NOT_MAPPED);
     120    }
     121
     122    public static function get_other_mapping() {
     123        $othermapped = array();
     124        $othermapped['relateduserid'] = array('db' => 'user', 'restore' => 'user');
     125
     126        return $othermapped;
     127    }
    110128}
  • moodle/trunk/fuentes/lib/classes/event/course_module_created.php

    r136 r1331  
    166166        }
    167167    }
     168
     169    public static function get_objectid_mapping() {
     170        return array('db' => 'course_modules', 'restore' => 'course_module');
     171    }
     172
     173    public static function get_other_mapping() {
     174        $othermapped = array();
     175        $othermapped['instanceid'] = base::NOT_MAPPED;
     176
     177        return $othermapped;
     178    }
    168179}
    169180
  • moodle/trunk/fuentes/lib/classes/event/course_module_deleted.php

    r136 r1331  
    120120        }
    121121    }
     122
     123    public static function get_objectid_mapping() {
     124        return array('db' => 'course_modules', 'restore' => 'course_module');
     125    }
     126
     127    public static function get_other_mapping() {
     128        $othermapped = array();
     129        $othermapped['instanceid'] = base::NOT_MAPPED;
     130
     131        return $othermapped;
     132    }
    122133}
    123134
  • moodle/trunk/fuentes/lib/classes/event/course_module_updated.php

    r136 r1331  
    164164        return $event;
    165165    }
     166
     167    public static function get_objectid_mapping() {
     168        return array('db' => 'course_modules', 'restore' => 'course_module');
     169    }
     170
     171    public static function get_other_mapping() {
     172        $othermapping = array();
     173        $othermapping['instanceid'] = base::NOT_MAPPED;
     174
     175        return $othermapping;
     176    }
    166177}
    167178
  • moodle/trunk/fuentes/lib/classes/event/course_reset_ended.php

    r136 r1331  
    9191        }
    9292    }
     93
     94    public static function get_other_mapping() {
     95        // Nothing to map.
     96        return false;
     97    }
    9398}
  • moodle/trunk/fuentes/lib/classes/event/course_reset_started.php

    r136 r1331  
    9191        }
    9292    }
     93
     94    public static function get_other_mapping() {
     95        return false;
     96    }
    9397}
  • moodle/trunk/fuentes/lib/classes/event/course_restored.php

    r136 r1331  
    138138        }
    139139    }
     140
     141    public static function get_objectid_mapping() {
     142        return array('db' => 'course', 'restore' => 'course');
     143    }
     144
     145    public static function get_other_mapping() {
     146        // No need to map anything.
     147        return false;
     148    }
    140149}
  • moodle/trunk/fuentes/lib/classes/event/course_section_updated.php

    r136 r1331  
    105105        }
    106106    }
     107
     108    public static function get_objectid_mapping() {
     109        return array('db' => 'course_sections', 'restore' => 'course_section');
     110    }
     111
     112    public static function get_other_mapping() {
     113        // Nothing to map.
     114        return false;
     115    }
    107116}
  • moodle/trunk/fuentes/lib/classes/event/course_updated.php

    r136 r1331  
    118118        return $this->legacylogdata;
    119119    }
     120
     121    public static function get_objectid_mapping() {
     122        return array('db' => 'course', 'restore' => 'course');
     123    }
     124
     125    public static function get_other_mapping() {
     126        // Nothing to map.
     127        return false;
     128    }
    120129}
  • moodle/trunk/fuentes/lib/classes/event/course_user_report_viewed.php

    r136 r1331  
    115115        }
    116116    }
     117
     118    public static function get_other_mapping() {
     119        // Nothing to map.
     120        return false;
     121    }
    117122}
  • moodle/trunk/fuentes/lib/classes/event/course_viewed.php

    r136 r1331  
    3434 *      Extra information about the event.
    3535 *
    36  *      - int coursesectionid: (optional) The course section ID.
     36 *      - int coursesectionnumber: (optional) The course section number.
    3737 * }
    3838 *
     
    6161    public function get_description() {
    6262
     63        // We keep compatibility with 2.7 and 2.8 other['coursesectionid'].
    6364        $sectionstr = '';
    64         if (!empty($this->other['coursesectionid'])) {
     65        if (!empty($this->other['coursesectionnumber'])) {
     66            $sectionstr = "section number '{$this->other['coursesectionnumber']}' of the ";
     67        } else if (!empty($this->other['coursesectionid'])) {
    6568            $sectionstr = "section number '{$this->other['coursesectionid']}' of the ";
    6669        }
    6770        $description = "The user with id '$this->userid' viewed the " . $sectionstr . "course with id '$this->courseid'.";
     71
    6872        return $description;
    6973    }
     
    8589    public function get_url() {
    8690        global $CFG;
    87         $sectionid = null;
    88         if (isset($this->other['coursesectionid'])) {
    89             $sectionid = $this->other['coursesectionid'];
     91
     92        // We keep compatibility with 2.7 and 2.8 other['coursesectionid'].
     93        $sectionnumber = null;
     94        if (isset($this->other['coursesectionnumber'])) {
     95            $sectionnumber = $this->other['coursesectionnumber'];
     96        } else if (isset($this->other['coursesectionid'])) {
     97            $sectionnumber = $this->other['coursesectionid'];
    9098        }
    9199        require_once($CFG->dirroot . '/course/lib.php');
    92100        try {
    93             return course_get_url($this->courseid, $sectionid);
     101            return course_get_url($this->courseid, $sectionnumber);
    94102        } catch (\Exception $e) {
    95103            return null;
     
    108116        }
    109117
    110         if (isset($this->other['coursesectionid'])) {
     118        // We keep compatibility with 2.7 and 2.8 other['coursesectionid'].
     119        if (isset($this->other['coursesectionnumber']) || isset($this->other['coursesectionid'])) {
     120            if (isset($this->other['coursesectionnumber'])) {
     121                $sectionnumber = $this->other['coursesectionnumber'];
     122            } else {
     123                $sectionnumber = $this->other['coursesectionid'];
     124            }
    111125            return array($this->courseid, 'course', 'view section', 'view.php?id=' . $this->courseid . '&section='
    112                     . $this->other['coursesectionid'], $this->other['coursesectionid']);
     126                    . $sectionnumber, $sectionnumber);
    113127        }
    114128        return array($this->courseid, 'course', 'view', 'view.php?id=' . $this->courseid, $this->courseid);
     
    128142        }
    129143    }
     144
     145    public static function get_other_mapping() {
     146        // No mapping required.
     147        return false;
     148    }
    130149}
  • moodle/trunk/fuentes/lib/classes/event/email_failed.php

    r136 r1331  
    3030/**
    3131 * Email failed event class.
     32 *
     33 * @property-read array $other {
     34 *      Extra information about event.
     35 *
     36 *      - string subject: the message subject.
     37 *      - string message: the message text.
     38 *      - string errorinfo: the error info.
     39 * }
    3240 *
    3341 * @package    core
     
    95103        }
    96104    }
     105
     106    public static function get_other_mapping() {
     107        return false;
     108    }
    97109}
  • moodle/trunk/fuentes/lib/classes/event/grade_deleted.php

    r136 r1331  
    132132        }
    133133    }
     134
     135    public static function get_objectid_mapping() {
     136        return array('db' => 'grade_grades', 'restore' => 'grade_grades');
     137    }
     138
     139    public static function get_other_mapping() {
     140        $othermapped = array();
     141        $othermapped['itemid'] = array('db' => 'grade_items', 'restore' => 'grade_item');
     142
     143        return $othermapped;
     144    }
    134145}
  • moodle/trunk/fuentes/lib/classes/event/group_created.php

    r136 r1331  
    9191        $this->data['objecttable'] = 'groups';
    9292    }
     93
     94    public static function get_objectid_mapping() {
     95        return array('db' => 'groups', 'restore' => 'group');
     96    }
    9397}
  • moodle/trunk/fuentes/lib/classes/event/group_deleted.php

    r136 r1331  
    9191        $this->data['objecttable'] = 'groups';
    9292    }
     93
     94    public static function get_objectid_mapping() {
     95        return array('db' => 'groups', 'restore' => 'group');
     96    }
    9397}
  • moodle/trunk/fuentes/lib/classes/event/group_member_added.php

    r136 r1331  
    126126        }
    127127    }
     128
     129    public static function get_objectid_mapping() {
     130        return array('db' => 'groups', 'restore' => 'group');
     131    }
     132
     133    public static function get_other_mapping() {
     134        $othermapped = array();
     135        $othermapped['itemid'] = base::NOT_MAPPED;
     136
     137        return $othermapped;
     138    }
    128139}
  • moodle/trunk/fuentes/lib/classes/event/group_member_removed.php

    r136 r1331  
    109109        }
    110110    }
     111
     112    public static function get_objectid_mapping() {
     113        return array('db' => 'groups', 'restore' => 'group');
     114    }
     115
    111116}
  • moodle/trunk/fuentes/lib/classes/event/group_updated.php

    r136 r1331  
    9191        $this->data['objecttable'] = 'groups';
    9292    }
     93
     94    public static function get_objectid_mapping() {
     95        return array('db' => 'groups', 'restore' => 'group');
     96    }
    9397}
  • moodle/trunk/fuentes/lib/classes/event/grouping_created.php

    r136 r1331  
    9191        $this->data['objecttable'] = 'groupings';
    9292    }
     93
     94    public static function get_objectid_mapping() {
     95        return array('db' => 'groupings', 'restore' => 'grouping');
     96    }
    9397}
  • moodle/trunk/fuentes/lib/classes/event/grouping_deleted.php

    r136 r1331  
    9898        $this->data['objecttable'] = 'groupings';
    9999    }
     100
     101    public static function get_objectid_mapping() {
     102        return array('db' => 'groupings', 'restore' => 'grouping');
     103    }
    100104}
  • moodle/trunk/fuentes/lib/classes/event/grouping_updated.php

    r136 r1331  
    9191        $this->data['objecttable'] = 'groupings';
    9292    }
     93
     94    public static function get_objectid_mapping() {
     95        return array('db' => 'groupings', 'restore' => 'grouping');
     96    }
    9397}
  • moodle/trunk/fuentes/lib/classes/event/manager.php

    r136 r1331  
    177177     */
    178178    protected static function get_observing_classes(\core\event\base $event) {
    179         $observers = array('\core\event\base', '\\'.get_class($event));
    180         return $observers;
    181 
    182         // Note if we ever decide to observe events by parent class name use the following code instead.
    183         /*
    184179        $classname = get_class($event);
    185180        $observers = array('\\'.$classname);
     
    190185
    191186        return $observers;
    192         */
    193187    }
    194188
  • moodle/trunk/fuentes/lib/classes/event/message_contact_added.php

    r136 r1331  
    9595        }
    9696    }
     97
     98    public static function get_objectid_mapping() {
     99        // Messaging contacts are not backed up, so no need to map them on restore.
     100        return array('db' => 'message_contacts', 'restore' => base::NOT_MAPPED);
     101    }
    97102}
  • moodle/trunk/fuentes/lib/classes/event/message_contact_blocked.php

    r136 r1331  
    9595        }
    9696    }
     97
     98    public static function get_objectid_mapping() {
     99        // Messaging contacts are not backed up, so no need to map them on restore.
     100        return array('db' => 'message_contacts', 'restore' => base::NOT_MAPPED);
     101    }
    97102}
  • moodle/trunk/fuentes/lib/classes/event/message_contact_removed.php

    r136 r1331  
    9595        }
    9696    }
     97
     98    public static function get_objectid_mapping() {
     99        // Messaging contacts are not backed up, so no need to map them on restore.
     100        return array('db' => 'message_contacts', 'restore' => base::NOT_MAPPED);
     101    }
    97102}
  • moodle/trunk/fuentes/lib/classes/event/message_contact_unblocked.php

    r136 r1331  
    9595        }
    9696    }
     97
     98    public static function get_objectid_mapping() {
     99        // Messaging contacts are not backed up, so no need to map them on restore.
     100        return array('db' => 'message_contacts', 'restore' => base::NOT_MAPPED);
     101    }
    97102}
  • moodle/trunk/fuentes/lib/classes/event/message_sent.php

    r136 r1331  
    145145        }
    146146    }
     147
     148    public static function get_objectid_mapping() {
     149        // Messages are not backed up, so no need to map them.
     150        return false;
     151    }
     152
     153    public static function get_other_mapping() {
     154        // Messages are not backed up, so no need to map them on restore.
     155        $othermapped = array();
     156        // The messages table could vary for older events - so cannot be mapped.
     157        $othermapped['messageid'] = array('db' => base::NOT_MAPPED, 'restore' => base::NOT_MAPPED);
     158        return $othermapped;
     159    }
    147160}
  • moodle/trunk/fuentes/lib/classes/event/message_viewed.php

    r136 r1331  
    9696        }
    9797    }
     98
     99    public static function get_objectid_mapping() {
     100        // Messages are not backed up, so no need to map them.
     101        return array('db' => 'message_read', 'restore' => base::NOT_MAPPED);
     102    }
     103
     104    public static function get_other_mapping() {
     105        // Messages are not backed up, so no need to map them on restore.
     106        $othermapped = array();
     107        // The messages table could vary for older events - so cannot be mapped.
     108        $othermapped['messageid'] = array('db' => base::NOT_MAPPED, 'restore' => base::NOT_MAPPED);
     109        return $othermapped;
     110    }
    98111}
  • moodle/trunk/fuentes/lib/classes/event/mnet_access_control_created.php

    r136 r1331  
    114114        }
    115115    }
     116
     117    public static function get_objectid_mapping() {
     118        // Mnet info is not backed up, so no need to map on restore.
     119        return array('db' => 'mnet_sso_access_control', 'restore' => base::NOT_MAPPED);
     120    }
     121
     122    public static function get_other_mapping() {
     123        // Nothing to map.
     124        return false;
     125    }
    116126}
  • moodle/trunk/fuentes/lib/classes/event/mnet_access_control_updated.php

    r136 r1331  
    114114        }
    115115    }
     116
     117    public static function get_objectid_mapping() {
     118        // Mnet info is not backed up, so no need to map on restore.
     119        return array('db' => 'mnet_sso_access_control', 'restore' => base::NOT_MAPPED);
     120    }
     121
     122    public static function get_other_mapping() {
     123        // Nothing to map.
     124        return false;
     125    }
    116126}
  • moodle/trunk/fuentes/lib/classes/event/note_created.php

    r136 r1331  
    107107        }
    108108    }
     109
     110    public static function get_objectid_mapping() {
     111        // Notes are not backed up, so no need to map on restore.
     112        return array('db' => 'post', 'restore' => base::NOT_MAPPED);
     113    }
     114
     115    public static function get_other_mapping() {
     116        // Nothing to map.
     117        return false;
     118    }
    109119}
  • moodle/trunk/fuentes/lib/classes/event/note_deleted.php

    r136 r1331  
    9797        }
    9898    }
     99
     100    public static function get_objectid_mapping() {
     101        // Notes are not backed up, so no need to map on restore.
     102        return array('db' => 'post', 'restore' => base::NOT_MAPPED);
     103    }
     104
     105    public static function get_other_mapping() {
     106        // Nothing to map.
     107        return false;
     108    }
    99109}
  • moodle/trunk/fuentes/lib/classes/event/note_updated.php

    r136 r1331  
    107107        }
    108108    }
     109
     110    public static function get_objectid_mapping() {
     111        // Notes are not backed up, so no need to map on restore.
     112        return array('db' => 'post', 'restore' => base::NOT_MAPPED);
     113    }
     114
     115    public static function get_other_mapping() {
     116        // Nothing to map.
     117        return false;
     118    }
    109119}
  • moodle/trunk/fuentes/lib/classes/event/question_category_created.php

    r136 r1331  
    9797        return null;
    9898    }
     99
     100    public static function get_objectid_mapping() {
     101        return array('db' => 'question_categories', 'restore' => 'question_category');
     102    }
    99103}
  • moodle/trunk/fuentes/lib/classes/event/role_assigned.php

    r136 r1331  
    132132        }
    133133    }
     134
     135    public static function get_objectid_mapping() {
     136        return array('db' => 'role', 'restore' => 'role');
     137    }
     138
     139    public static function get_other_mapping() {
     140        $othermapped = array();
     141        $othermapped['id'] = array('db' => 'role_assignments', 'restore' => base::NOT_MAPPED);
     142        $othermapped['itemid'] = base::NOT_MAPPED;
     143
     144        return $othermapped;
     145    }
    134146}
  • moodle/trunk/fuentes/lib/classes/event/role_capabilities_updated.php

    r136 r1331  
    7373     */
    7474    public function get_url() {
    75         if ($this->contextlevel === CONTEXT_SYSTEM) {
     75        if ($this->contextlevel == CONTEXT_SYSTEM) {
    7676            return new \moodle_url('/admin/roles/define.php', array('action' => 'view', 'roleid' => $this->objectid));
    7777        } else {
     
    9999        return $this->legacylogdata;
    100100    }
     101
     102    public static function get_objectid_mapping() {
     103        return array('db' => 'role', 'restore' => 'role');
     104    }
    101105}
  • moodle/trunk/fuentes/lib/classes/event/role_deleted.php

    r136 r1331  
    103103        }
    104104    }
     105
     106    public static function get_objectid_mapping() {
     107        return array('db' => 'role', 'restore' => 'role');
     108    }
     109
     110    public static function get_other_mapping() {
     111        // Nothing to map.
     112        return false;
     113    }
    105114}
  • moodle/trunk/fuentes/lib/classes/event/role_unassigned.php

    r136 r1331  
    129129        }
    130130    }
     131
     132    public static function get_objectid_mapping() {
     133        return array('db' => 'role', 'restore' => 'role');
     134    }
     135
     136    public static function get_other_mapping() {
     137        $othermapped = array();
     138        $othermapped['id'] = array('db' => 'role_assignments', 'restore' => base::NOT_MAPPED);
     139        $othermapped['itemid'] = base::NOT_MAPPED;
     140
     141        return $othermapped;
     142    }
    131143}
  • moodle/trunk/fuentes/lib/classes/event/tag_added.php

    r136 r1331  
    118118        }
    119119    }
     120
     121    public static function get_objectid_mapping() {
     122        // Tags cannot be mapped.
     123        return array('db' => 'tag_instance', 'restore' => base::NOT_MAPPED);
     124    }
     125
     126    public static function get_other_mapping() {
     127        $othermapped = array();
     128        $othermapped['tagid'] = array('db' => 'tag', 'restore' => base::NOT_MAPPED);
     129        $othermapped['itemid'] = base::NOT_MAPPED;
     130
     131        return $othermapped;
     132    }
    120133}
  • moodle/trunk/fuentes/lib/classes/event/tag_created.php

    r136 r1331  
    8888        }
    8989    }
     90
     91    public static function get_objectid_mapping() {
     92        // Tags cannot be mapped.
     93        return array('db' => 'tag', 'restore' => base::NOT_MAPPED);
     94    }
     95
     96    public static function get_other_mapping() {
     97        return false;
     98    }
    9099}
  • moodle/trunk/fuentes/lib/classes/event/tag_deleted.php

    r136 r1331  
    8888        }
    8989    }
     90
     91    public static function get_objectid_mapping() {
     92        // Tags cannot be mapped.
     93        return array('db' => 'tag', 'restore' => base::NOT_MAPPED);
     94    }
     95
     96    public static function get_other_mapping() {
     97        return false;
     98    }
    9099}
  • moodle/trunk/fuentes/lib/classes/event/tag_flagged.php

    r136 r1331  
    9797        }
    9898    }
     99
     100    public static function get_objectid_mapping() {
     101        // Tags cannot be mapped.
     102        return array('db' => 'tag', 'restore' => base::NOT_MAPPED);
     103    }
     104
     105    public static function get_other_mapping() {
     106        return false;
     107    }
    99108}
  • moodle/trunk/fuentes/lib/classes/event/tag_removed.php

    r136 r1331  
    104104        }
    105105    }
     106
     107    public static function get_objectid_mapping() {
     108        // Tags cannot be mapped.
     109        return array('db' => 'tag_instance', 'restore' => base::NOT_MAPPED);
     110    }
     111
     112    public static function get_other_mapping() {
     113        $othermapped = array();
     114        $othermapped['tagid'] = array('db' => 'tag', 'restore' => base::NOT_MAPPED);
     115        $othermapped['itemid'] = base::NOT_MAPPED;
     116
     117        return $othermapped;
     118    }
     119
    106120}
  • moodle/trunk/fuentes/lib/classes/event/tag_unflagged.php

    r136 r1331  
    8888        }
    8989    }
     90
     91    public static function get_objectid_mapping() {
     92        // Tags cannot be mapped.
     93        return array('db' => 'tag', 'restore' => base::NOT_MAPPED);
     94    }
     95
     96    public static function get_other_mapping() {
     97        return false;
     98    }
     99
    90100}
  • moodle/trunk/fuentes/lib/classes/event/tag_updated.php

    r136 r1331  
    113113        }
    114114    }
     115
     116    public static function get_objectid_mapping() {
     117        // Tags cannot be mapped.
     118        return array('db' => 'tag', 'restore' => base::NOT_MAPPED);
     119    }
     120
     121    public static function get_other_mapping() {
     122        return false;
     123    }
     124
    115125}
  • moodle/trunk/fuentes/lib/classes/event/user_created.php

    r136 r1331  
    134134        return $event;
    135135    }
     136
     137    public static function get_objectid_mapping() {
     138        return array('db' => 'user', 'restore' => 'user');
     139    }
    136140}
  • moodle/trunk/fuentes/lib/classes/event/user_deleted.php

    r136 r1331  
    142142        }
    143143    }
     144
     145    public static function get_objectid_mapping() {
     146        return array('db' => 'user', 'restore' => 'user');
     147    }
     148
     149    public static function get_other_mapping() {
     150        $othermapped = array();
     151        $othermapped['mnethostid'] = array('db' => 'mnet_host', 'restore' => base::NOT_MAPPED);
     152
     153        return $othermapped;
     154    }
    144155}
  • moodle/trunk/fuentes/lib/classes/event/user_enrolment_created.php

    r136 r1331  
    126126        }
    127127    }
     128
     129    public static function get_objectid_mapping() {
     130        // User enrolments table is not mappable.
     131        return array('db' => 'user_enrolments', 'restore' => base::NOT_MAPPED);
     132    }
     133
     134    public static function get_other_mapping() {
     135        return false;
     136    }
    128137}
  • moodle/trunk/fuentes/lib/classes/event/user_enrolment_deleted.php

    r136 r1331  
    125125        }
    126126    }
     127
     128    public static function get_objectid_mapping() {
     129        // User enrolments table is not mappable.
     130        return array('db' => 'user_enrolments', 'restore' => base::NOT_MAPPED);
     131    }
     132
     133    public static function get_other_mapping() {
     134        return false;
     135    }
    127136}
  • moodle/trunk/fuentes/lib/classes/event/user_enrolment_updated.php

    r136 r1331  
    115115        }
    116116    }
     117
     118    public static function get_objectid_mapping() {
     119        // User enrolments table is not mappable.
     120        return array('db' => 'user_enrolments', 'restore' => base::NOT_MAPPED);
     121    }
     122
     123    public static function get_other_mapping() {
     124        return false;
     125    }
    117126}
  • moodle/trunk/fuentes/lib/classes/event/user_graded.php

    r136 r1331  
    157157        }
    158158    }
     159
     160    public static function get_objectid_mapping() {
     161        return array('db' => 'grade_grades', 'restore' => 'grade_grades');
     162    }
     163
     164    public static function get_other_mapping() {
     165        $othermapped = array();
     166        $othermapped['itemid'] = array('db' => 'grade_items', 'restore' => 'grade_item');
     167
     168        return $othermapped;
     169    }
    159170}
  • moodle/trunk/fuentes/lib/classes/event/user_list_viewed.php

    r136 r1331  
    8989        return array($this->courseid, 'user', 'view all', 'index.php?id=' . $this->courseid, '');
    9090    }
     91
     92    public static function get_objectid_mapping() {
     93        return array('db' => 'course', 'restore' => 'course');
     94    }
     95
     96    public static function get_other_mapping() {
     97        return false;
     98    }
    9199}
  • moodle/trunk/fuentes/lib/classes/event/user_loggedin.php

    r136 r1331  
    114114        }
    115115    }
     116
     117    public static function get_objectid_mapping() {
     118        return array('db' => 'user', 'restore' => 'user');
     119    }
     120
     121    public static function get_other_mapping() {
     122        return false;
     123    }
    116124}
  • moodle/trunk/fuentes/lib/classes/event/user_loggedinas.php

    r136 r1331  
    113113        }
    114114    }
     115
     116    public static function get_objectid_mapping() {
     117        return array('db' => 'user', 'restore' => 'user');
     118    }
     119
     120    public static function get_other_mapping() {
     121        return false;
     122    }
    115123}
  • moodle/trunk/fuentes/lib/classes/event/user_loggedout.php

    r136 r1331  
    106106            $this->objectid);
    107107    }
     108
     109    public static function get_objectid_mapping() {
     110        return array('db' => 'user', 'restore' => 'user');
     111    }
     112
     113    public static function get_other_mapping() {
     114        return false;
     115    }
    108116}
  • moodle/trunk/fuentes/lib/classes/event/user_login_failed.php

    r136 r1331  
    114114    }
    115115
     116    public static function get_other_mapping() {
     117        return false;
     118    }
    116119}
  • moodle/trunk/fuentes/lib/classes/event/user_password_updated.php

    r136 r1331  
    131131        }
    132132    }
     133
     134    public static function get_other_mapping() {
     135        return false;
     136    }
    133137}
  • moodle/trunk/fuentes/lib/classes/event/user_profile_viewed.php

    r136 r1331  
    112112        }
    113113    }
     114
     115    public static function get_objectid_mapping() {
     116        return array('db' => 'user', 'restore' => 'user');
     117    }
     118
     119    public static function get_other_mapping() {
     120        $othermapped = array();
     121        $othermapped['courseid'] = array('db' => 'course', 'restore' => 'course');
     122
     123        return $othermapped;
     124    }
    114125}
  • moodle/trunk/fuentes/lib/classes/event/user_updated.php

    r136 r1331  
    134134        return $event;
    135135    }
     136
     137    public static function get_objectid_mapping() {
     138        return array('db' => 'user', 'restore' => 'user');
     139    }
    136140}
  • moodle/trunk/fuentes/lib/classes/event/webservice_function_called.php

    r136 r1331  
    106106        }
    107107    }
     108
     109    public static function get_other_mapping() {
     110        return false;
     111    }
    108112}
  • moodle/trunk/fuentes/lib/classes/event/webservice_login_failed.php

    r136 r1331  
    124124        }
    125125    }
     126
     127    public static function get_other_mapping() {
     128        return false;
     129    }
    126130}
  • moodle/trunk/fuentes/lib/classes/event/webservice_service_created.php

    r136 r1331  
    9292        $this->data['objecttable'] = 'external_services';
    9393    }
     94
     95    public static function get_objectid_mapping() {
     96        // Webservices are not included in the backups.
     97        return array('db' => 'external_services', 'restore' => NOT_MAPPED);
     98    }
     99
     100    public static function get_other_mapping() {
     101        return false;
     102    }
    94103}
  • moodle/trunk/fuentes/lib/classes/event/webservice_service_deleted.php

    r136 r1331  
    8686        $this->data['objecttable'] = 'external_services';
    8787    }
     88
     89    public static function get_objectid_mapping() {
     90        // Webservices are not included in backups.
     91        return array('db' => 'external_services', 'restore' => base::NOT_MAPPED);
     92    }
     93
    8894}
  • moodle/trunk/fuentes/lib/classes/event/webservice_service_updated.php

    r136 r1331  
    8686        $this->data['objecttable'] = 'external_services';
    8787    }
     88
     89    public static function get_objectid_mapping() {
     90        // Webservices are not included in backups.
     91        return array('db' => 'external_services', 'restore' => base::NOT_MAPPED);
     92    }
     93
    8894}
  • moodle/trunk/fuentes/lib/classes/event/webservice_service_user_added.php

    r136 r1331  
    9999        }
    100100    }
     101
     102    public static function get_objectid_mapping() {
     103        // Webservices are not included in backups.
     104        return array('db' => 'external_services', 'restore' => base::NOT_MAPPED);
     105    }
     106
    101107}
  • moodle/trunk/fuentes/lib/classes/event/webservice_service_user_removed.php

    r136 r1331  
    9999        }
    100100    }
     101
     102    public static function get_objectid_mapping() {
     103        // Webservices are not included in backups.
     104        return array('db' => 'external_services', 'restore' => base::NOT_MAPPED);
     105    }
     106
    101107}
  • moodle/trunk/fuentes/lib/classes/event/webservice_token_created.php

    r136 r1331  
    109109        }
    110110    }
     111
     112    public static function get_objectid_mapping() {
     113        // Webservices are not included in backups.
     114        return array('db' => 'external_tokens', 'restore' => base::NOT_MAPPED);
     115    }
     116
     117    public static function get_other_mapping() {
     118        return false;
     119    }
    111120}
  • moodle/trunk/fuentes/lib/classes/event/webservice_token_sent.php

    r136 r1331  
    7474        $this->data['objecttable'] = 'external_tokens';
    7575    }
     76
     77    public static function get_objectid_mapping() {
     78        // Webservices are not included in backups.
     79        return array('db' => 'external_tokens', 'restore' => base::NOT_MAPPED);
     80    }
    7681}
  • moodle/trunk/fuentes/lib/classes/grades_external.php

    r136 r1331  
    156156                }
    157157
     158                $context = $coursecontext;
     159
    158160                if ($activitygrade->itemtype == 'course') {
    159161                    $item = grade_get_course_grades($course->id, $params['userids']);
     
    167169                    $cm = $activityinstances[$activitygrade->itemmodule][$activitygrade->iteminstance];
    168170                    $instance = $cm->instance;
     171                    $context = context_module::instance($cm->id, IGNORE_MISSING);
     172
    169173                    $grades = grade_get_grades($params['courseid'], $activitygrade->itemtype,
    170174                                                $activitygrade->itemmodule, $instance, $params['userids']);
     
    212216                                list($studentgrade->feedback, $studentgrade->feedbackformat) =
    213217                                    external_format_text($studentgrade->feedback, $studentgrade->feedbackformat,
    214                                     $cm->id, $params['component'], 'feedback', null);
     218                                    $context->id, $params['component'], 'feedback', null);
    215219                            }
    216220
     
    266270                                list($studentgrade->feedback, $studentgrade->feedbackformat) =
    267271                                    external_format_text($studentgrade->feedback, $studentgrade->feedbackformat,
    268                                     $cm->id, $params['component'], 'feedback', null);
     272                                    $context->id, $params['component'], 'feedback', null);
    269273                            }
    270274
  • moodle/trunk/fuentes/lib/classes/lock/db_record_lock_factory.php

    r136 r1331  
    104104     */
    105105    protected function generate_unique_token() {
    106         $uuid = '';
    107 
    108         if (function_exists("uuid_create")) {
    109             $context = null;
    110             uuid_create($context);
    111 
    112             uuid_make($context, UUID_MAKE_V4);
    113             uuid_export($context, UUID_FMT_STR, $uuid);
    114         } else {
    115             // Fallback uuid generation based on:
    116             // "http://www.php.net/manual/en/function.uniqid.php#94959".
    117             $uuid = sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
    118 
    119                 // 32 bits for "time_low".
    120                 mt_rand(0, 0xffff), mt_rand(0, 0xffff),
    121 
    122                 // 16 bits for "time_mid".
    123                 mt_rand(0, 0xffff),
    124 
    125                 // 16 bits for "time_hi_and_version",
    126                 // four most significant bits holds version number 4.
    127                 mt_rand(0, 0x0fff) | 0x4000,
    128 
    129                 // 16 bits, 8 bits for "clk_seq_hi_res",
    130                 // 8 bits for "clk_seq_low",
    131                 // two most significant bits holds zero and one for variant DCE1.1.
    132                 mt_rand(0, 0x3fff) | 0x8000,
    133 
    134                 // 48 bits for "node".
    135                 mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff));
    136         }
    137         return trim($uuid);
    138     }
    139 
     106        return generate_uuid();
     107    }
    140108
    141109    /**
     
    249217        foreach ($this->openlocks as $key => $unused) {
    250218            $lock = new lock($key, $this);
    251             $this->release_lock($lock);
     219            $lock->release();
    252220        }
    253221    }
  • moodle/trunk/fuentes/lib/classes/lock/lock.php

    r136 r1331  
    5050    protected $released;
    5151
     52    /** @var string $caller Where was this called from? Stored for when a warning is shown */
     53    protected $caller = 'unknown';
     54
    5255    /**
    5356     * Construct a lock containing the unique key required to release it.
     
    6063        $this->key = $key;
    6164        $this->released = false;
     65        $caller = debug_backtrace(true, 2)[1];
     66        if ($caller && array_key_exists('file', $caller ) ) {
     67            $this->caller = $caller['file'] . ' on line ' . $caller['line'];
     68        } else if ($caller && array_key_exists('class', $caller)) {
     69            $this->caller = $caller['class'] . $caller['type'] . $caller['function'];
     70        }
    6271    }
    6372
     
    104113    public function __destruct() {
    105114        if (!$this->released && defined('PHPUNIT_TEST')) {
     115            $key = $this->key;
    106116            $this->release();
    107             throw new \coding_exception('\core\lock\lock(' . $this->key . ') has fallen out of scope ' .
    108                                         'without being released.' . "\n" .
    109                                         'Locks must ALWAYS be released by calling $mylock->release().');
     117            throw new \coding_exception("A lock was created but not released at:\n" .
     118                                        $this->caller . "\n\n" .
     119                                        " Code should look like:\n\n" .
     120                                        " \$factory = \core\lock\lock_config::get_lock_factory('type');\n" .
     121                                        " \$lock = \$factory->get_lock($key);\n" .
     122                                        " \$lock->release();  // Locks must ALWAYS be released like this.\n\n");
    110123        }
    111124    }
  • moodle/trunk/fuentes/lib/classes/lock/postgres_lock_factory.php

    r136 r1331  
    237237        foreach ($this->openlocks as $key => $unused) {
    238238            $lock = new lock($key, $this);
    239             $this->release_lock($lock);
     239            $lock->release();
    240240        }
    241241    }
  • moodle/trunk/fuentes/lib/classes/log/sql_internal_reader.php

    r136 r1331  
    2727defined('MOODLE_INTERNAL') || die();
    2828
     29/**
     30 * Sql internal reader.
     31 *
     32 * @deprecated since Moodle 2.9 MDL-48595 - please do not use this interface any more.
     33 * @see        sql_reader
     34 * @todo       MDL-49291 This will be deleted in Moodle 3.1.
     35 * @package    core
     36 * @copyright  2013 Petr Skoda {@link http://skodak.org}
     37 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
     38 */
    2939interface sql_internal_reader extends sql_select_reader {
    3040
    3141    /**
    32      * Returns name of the table or database view that
    33      * holds the log data in standardised format.
     42     * Returns name of the table or database view that holds the log data in standardised format.
    3443     *
    3544     * Note: this table must be used for reading only,
  • moodle/trunk/fuentes/lib/classes/log/sql_select_reader.php

    r136 r1331  
    2727defined('MOODLE_INTERNAL') || die();
    2828
     29/**
     30 * Sql select reader.
     31 *
     32 * @deprecated since Moodle 2.9 MDL-48595 - please do not use this interface any more.
     33 * @see        sql_reader
     34 * @todo       MDL-49291 This will be deleted in Moodle 3.1.
     35 * @package    core
     36 * @copyright  2013 Petr Skoda {@link http://skodak.org}
     37 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
     38 */
    2939interface sql_select_reader extends reader {
    3040    /**
  • moodle/trunk/fuentes/lib/classes/message/inbound/address_manager.php

    r136 r1331  
    326326                // Any 64-bit integer which is greater than the 32-bit integer size will have a non-zero value in the first
    327327                // half of the integer.
    328                 throw new moodle_exception('Mixed environment.' +
    329                         ' Key generated with a 64-bit machine but received into a 32-bit machine');
     328                throw new \moodle_exception('Mixed environment.' .
     329                    ' Key generated with a 64-bit machine but received into a 32-bit machine.');
    330330            }
    331331            $content['handlerid'] = $content['handlerid2'];
  • moodle/trunk/fuentes/lib/classes/message/inbound/handler.php

    r136 r1331  
    142142
    143143    /**
     144     * Whether the current handler allows changes to expiry of the generated email address.
     145     *
     146     * By default this will return true, but for some handlers it may be
     147     * necessary to disallow such changes.
     148     *
     149     * @return boolean
     150     */
     151    public function can_change_defaultexpiration() {
     152        return true;
     153    }
     154
     155    /**
    144156     * Whether this handler can be disabled (or enabled).
    145157     *
     
    226238    }
    227239
     240    /**
     241     * Remove quoted message string from the text (NOT HTML) message.
     242     *
     243     * @param \stdClass $messagedata The Inbound Message record
     244     *
     245     * @return array message and message format to use.
     246     */
     247    protected static function remove_quoted_text($messagedata) {
     248        if (!empty($messagedata->plain)) {
     249            $text = $messagedata->plain;
     250        } else {
     251            $text = html_to_text($messagedata->html);
     252        }
     253        $messageformat = FORMAT_PLAIN;
     254
     255        $splitted = preg_split("/\n|\r/", $text);
     256        if (empty($splitted)) {
     257            return array($text, $messageformat);
     258        }
     259
     260        $i = 0;
     261        $flag = false;
     262        foreach ($splitted as $i => $element) {
     263            if (stripos($element, ">") === 0) {
     264                // Quoted text found.
     265                $flag = true;
     266                // Remove 2 non empty line before this.
     267                for ($j = $i - 1; ($j >= 0); $j--) {
     268                    $element = $splitted[$j];
     269                    if (!empty($element)) {
     270                        unset($splitted[$j]);
     271                        break;
     272                    }
     273                }
     274                break;
     275            }
     276        }
     277        if ($flag) {
     278            // Quoted text was found.
     279            // Retrieve everything from the start until the line before the quoted text.
     280            $splitted = array_slice($splitted, 0, $i-1);
     281
     282            // Strip out empty lines towards the end, since a lot of clients add a huge chunk of empty lines.
     283            $reverse = array_reverse($splitted);
     284            foreach ($reverse as $i => $line) {
     285                if (empty($line)) {
     286                    unset($reverse[$i]);
     287                } else {
     288                    // Non empty line found.
     289                    break;
     290                }
     291            }
     292
     293            $replaced = implode(PHP_EOL, array_reverse($reverse));
     294            $message = trim($replaced);
     295        } else {
     296            // No quoted text, fallback to original text.
     297            if (!empty($messagedata->html)) {
     298                $message = $messagedata->html;
     299                $messageformat = FORMAT_HTML;
     300            } else {
     301                $message = $messagedata->plain;
     302            }
     303        }
     304        return array($message, $messageformat);
     305    }
    228306}
  • moodle/trunk/fuentes/lib/classes/message/inbound/private_files_handler.php

    r136 r1331  
    3535 */
    3636class private_files_handler extends handler {
     37
     38    /**
     39     * Email generated by this handler should not expire.
     40     *
     41     * @return bool
     42     */
     43    public function can_change_defaultexpiration() {
     44        return false;
     45    }
    3746
    3847    /**
  • moodle/trunk/fuentes/lib/classes/message/inbound/processing_failed_exception.php

    r136 r1331  
    3939     * @param string $identifier The string identifier to use when displaying this exception.
    4040     * @param string $component The string component
    41      * @param stdClass $data The data to pass to get_string
     41     * @param \stdClass $data The data to pass to get_string
    4242     */
    4343    public function __construct($identifier, $component, \stdClass $data = null) {
  • moodle/trunk/fuentes/lib/classes/message/manager.php

    r136 r1331  
    5151     * NOTE: to be used from message_send() only.
    5252     *
    53      * @param \stdClass $eventdata fully prepared event data for processors
     53     * @param \stdClass|\core\message\message $eventdata fully prepared event data for processors
    5454     * @param \stdClass $savemessage the message saved in 'message' table
    5555     * @param array $processorlist list of processors for target user
    5656     * @return int $messageid the id from 'message' or 'message_read' table (false is not returned)
    5757     */
    58     public static function send_message(\stdClass $eventdata, \stdClass $savemessage, array $processorlist) {
     58    public static function send_message($eventdata, \stdClass $savemessage, array $processorlist) {
    5959        global $CFG;
     60
     61        if (!($eventdata instanceof \stdClass) && !($eventdata instanceof message)) {
     62            // Not a valid object.
     63            throw new \coding_exception('Message should be of type stdClass or \core\message\message');
     64        }
     65
    6066        require_once($CFG->dirroot.'/message/lib.php'); // This is most probably already included from messagelib.php file.
    6167
     
    8692     * Send message to message processors.
    8793     *
    88      * @param \stdClass $eventdata
     94     * @param \stdClass|\core\message\message $eventdata
    8995     * @param \stdClass $savemessage
    9096     * @param array $processorlist
    9197     * @return int $messageid
    9298     */
    93     protected static function send_message_to_processors(\stdClass $eventdata, \stdClass $savemessage, array $processorlist) {
     99    protected static function send_message_to_processors($eventdata, \stdClass $savemessage, array
     100    $processorlist) {
    94101        global $CFG, $DB;
    95102
     
    115122        $failed = false;
    116123        foreach ($processorlist as $procname) {
    117             if (!$processors[$procname]->object->send_message($eventdata)) {
     124            // Let new messaging class add custom content based on the processor.
     125            $proceventdata = ($eventdata instanceof message) ? $eventdata->get_eventobject_for_processor($procname) : $eventdata;
     126            if (!$processors[$procname]->object->send_message($proceventdata)) {
    118127                debugging('Error calling message processor ' . $procname);
    119128                $failed = true;
  • moodle/trunk/fuentes/lib/classes/plugin_manager.php

    r136 r1331  
    5454    const PLUGIN_STATUS_MISSING     = 'missing';
    5555
     56    /** the given requirement/dependency is fulfilled */
     57    const REQUIREMENT_STATUS_OK = 'ok';
     58    /** the plugin requires higher core/other plugin version than is currently installed */
     59    const REQUIREMENT_STATUS_OUTDATED = 'outdated';
     60    /** the required dependency is not installed */
     61    const REQUIREMENT_STATUS_MISSING = 'missing';
     62
     63    /** the required dependency is available in the plugins directory */
     64    const REQUIREMENT_AVAILABLE = 'available';
     65    /** the required dependency is available in the plugins directory */
     66    const REQUIREMENT_UNAVAILABLE = 'unavailable';
     67
    5668    /** @var core_plugin_manager holds the singleton instance */
    5769    protected static $singletoninstance;
     
    6072    /** @var array of raw subplugins information */
    6173    protected $subpluginsinfo = null;
     74    /** @var array cache information about availability in the plugins directory if requesting "at least" version */
     75    protected $remotepluginsinfoatleast = null;
     76    /** @var array cache information about availability in the plugins directory if requesting exact version */
     77    protected $remotepluginsinfoexact = null;
    6278    /** @var array list of installed plugins $name=>$version */
    6379    protected $installedplugins = null;
     
    6884    /** @var array reordered list of plugin types */
    6985    protected $plugintypes = null;
     86    /** @var \core\update\code_manager code manager to use for plugins code operations */
     87    protected $codemanager = null;
     88    /** @var \core\update\api client instance to use for accessing download.moodle.org/api/ */
     89    protected $updateapiclient = null;
    7090
    7191    /**
     
    87107     */
    88108    public static function instance() {
    89         if (is_null(self::$singletoninstance)) {
    90             self::$singletoninstance = new self();
    91         }
    92         return self::$singletoninstance;
     109        if (is_null(static::$singletoninstance)) {
     110            static::$singletoninstance = new static();
     111        }
     112        return static::$singletoninstance;
    93113    }
    94114
     
    99119    public static function reset_caches($phpunitreset = false) {
    100120        if ($phpunitreset) {
    101             self::$singletoninstance = null;
     121            static::$singletoninstance = null;
    102122        } else {
    103             if (self::$singletoninstance) {
    104                 self::$singletoninstance->pluginsinfo = null;
    105                 self::$singletoninstance->subpluginsinfo = null;
    106                 self::$singletoninstance->installedplugins = null;
    107                 self::$singletoninstance->enabledplugins = null;
    108                 self::$singletoninstance->presentplugins = null;
    109                 self::$singletoninstance->plugintypes = null;
     123            if (static::$singletoninstance) {
     124                static::$singletoninstance->pluginsinfo = null;
     125                static::$singletoninstance->subpluginsinfo = null;
     126                static::$singletoninstance->remotepluginsinfoatleast = null;
     127                static::$singletoninstance->remotepluginsinfoexact = null;
     128                static::$singletoninstance->installedplugins = null;
     129                static::$singletoninstance->enabledplugins = null;
     130                static::$singletoninstance->presentplugins = null;
     131                static::$singletoninstance->plugintypes = null;
     132                static::$singletoninstance->codemanager = null;
     133                static::$singletoninstance->updateapiclient = null;
    110134            }
    111135        }
     
    239263        $plugintypes = core_component::get_plugin_types();
    240264        foreach ($plugintypes as $plugintype => $fulldir) {
    241             $plugininfoclass = self::resolve_plugininfo_class($plugintype);
     265            $plugininfoclass = static::resolve_plugininfo_class($plugintype);
    242266            if (class_exists($plugininfoclass)) {
    243267                $enabled = $plugininfoclass::get_enabled_plugins();
     
    289313            $plugs = core_component::get_plugin_list($type);
    290314            foreach ($plugs as $plug => $fullplug) {
     315                $module = new stdClass();
    291316                $plugin = new stdClass();
    292317                $plugin->version = null;
    293                 $module = $plugin;
    294                 @include($fullplug.'/version.php');
     318                include($fullplug.'/version.php');
     319
     320                // Check if the legacy $module syntax is still used.
     321                if (!is_object($module) or (count((array)$module) > 0)) {
     322                    debugging('Unsupported $module syntax detected in version.php of the '.$type.'_'.$plug.' plugin.');
     323                    $skipcache = true;
     324                }
     325
     326                // Check if the component is properly declared.
     327                if (empty($plugin->component) or ($plugin->component !== $type.'_'.$plug)) {
     328                    debugging('Plugin '.$type.'_'.$plug.' does not declare valid $plugin->component in its version.php.');
     329                    $skipcache = true;
     330                }
     331
    295332                $this->presentplugins[$type][$plug] = $plugin;
    296333            }
    297334        }
    298335
    299         $cache->set('present', $this->presentplugins);
     336        if (empty($skipcache)) {
     337            $cache->set('present', $this->presentplugins);
     338        }
    300339    }
    301340
     
    360399        if (!isset($types[$type])) {
    361400            // Orphaned subplugins!
    362             $plugintypeclass = self::resolve_plugininfo_class($type);
    363             $this->pluginsinfo[$type] = $plugintypeclass::get_plugins($type, null, $plugintypeclass);
     401            $plugintypeclass = static::resolve_plugininfo_class($type);
     402            $this->pluginsinfo[$type] = $plugintypeclass::get_plugins($type, null, $plugintypeclass, $this);
    364403            return $this->pluginsinfo[$type];
    365404        }
    366405
    367406        /** @var \core\plugininfo\base $plugintypeclass */
    368         $plugintypeclass = self::resolve_plugininfo_class($type);
    369         $plugins = $plugintypeclass::get_plugins($type, $types[$type], $plugintypeclass);
     407        $plugintypeclass = static::resolve_plugininfo_class($type);
     408        $plugins = $plugintypeclass::get_plugins($type, $types[$type], $plugintypeclass, $this);
    370409        $this->pluginsinfo[$type] = $plugins;
    371 
    372         if (empty($CFG->disableupdatenotifications) and !during_initial_install()) {
    373             // Append the information about available updates provided by {@link \core\update\checker()}.
    374             $provider = \core\update\checker::instance();
    375             foreach ($plugins as $plugininfoholder) {
    376                 $plugininfoholder->check_available_updates($provider);
    377             }
    378         }
    379410
    380411        return $this->pluginsinfo[$type];
     
    637668     * Check to see if the current version of the plugin seems to be a checkout of an external repository.
    638669     *
    639      * @see \core\update\deployer::plugin_external_source()
    640670     * @param string $component frankenstyle component name
    641671     * @return false|string
     
    742772
    743773        return $return;
     774    }
     775
     776    /**
     777     * Resolve requirements and dependencies of a plugin.
     778     *
     779     * Returns an array of objects describing the requirement/dependency,
     780     * indexed by the frankenstyle name of the component. The returned array
     781     * can be empty. The objects in the array have following properties:
     782     *
     783     *  ->(numeric)hasver
     784     *  ->(numeric)reqver
     785     *  ->(string)status
     786     *  ->(string)availability
     787     *
     788     * @param \core\plugininfo\base $plugin the plugin we are checking
     789     * @param null|string|int|double $moodleversion explicit moodle core version to check against, defaults to $CFG->version
     790     * @param null|string|int $moodlebranch explicit moodle core branch to check against, defaults to $CFG->branch
     791     * @return array of objects
     792     */
     793    public function resolve_requirements(\core\plugininfo\base $plugin, $moodleversion=null, $moodlebranch=null) {
     794        global $CFG;
     795
     796        if ($plugin->versiondisk === null) {
     797            // Missing from disk, we have no version.php to read from.
     798            return array();
     799        }
     800
     801        if ($moodleversion === null) {
     802            $moodleversion = $CFG->version;
     803        }
     804
     805        if ($moodlebranch === null) {
     806            $moodlebranch = $CFG->branch;
     807        }
     808
     809        $reqs = array();
     810        $reqcore = $this->resolve_core_requirements($plugin, $moodleversion);
     811
     812        if (!empty($reqcore)) {
     813            $reqs['core'] = $reqcore;
     814        }
     815
     816        foreach ($plugin->get_other_required_plugins() as $reqplug => $reqver) {
     817            $reqs[$reqplug] = $this->resolve_dependency_requirements($plugin, $reqplug, $reqver, $moodlebranch);
     818        }
     819
     820        return $reqs;
     821    }
     822
     823    /**
     824     * Helper method to resolve plugin's requirements on the moodle core.
     825     *
     826     * @param \core\plugininfo\base $plugin the plugin we are checking
     827     * @param string|int|double $moodleversion moodle core branch to check against
     828     * @return stdObject
     829     */
     830    protected function resolve_core_requirements(\core\plugininfo\base $plugin, $moodleversion) {
     831
     832        $reqs = (object)array(
     833            'hasver' => null,
     834            'reqver' => null,
     835            'status' => null,
     836            'availability' => null,
     837        );
     838
     839        $reqs->hasver = $moodleversion;
     840
     841        if (empty($plugin->versionrequires)) {
     842            $reqs->reqver = ANY_VERSION;
     843        } else {
     844            $reqs->reqver = $plugin->versionrequires;
     845        }
     846
     847        if ($plugin->is_core_dependency_satisfied($moodleversion)) {
     848            $reqs->status = self::REQUIREMENT_STATUS_OK;
     849        } else {
     850            $reqs->status = self::REQUIREMENT_STATUS_OUTDATED;
     851        }
     852
     853        return $reqs;
     854    }
     855
     856    /**
     857     * Helper method to resolve plugin's dependecies on other plugins.
     858     *
     859     * @param \core\plugininfo\base $plugin the plugin we are checking
     860     * @param string $otherpluginname
     861     * @param string|int $requiredversion
     862     * @param string|int $moodlebranch explicit moodle core branch to check against, defaults to $CFG->branch
     863     * @return stdClass
     864     */
     865    protected function resolve_dependency_requirements(\core\plugininfo\base $plugin, $otherpluginname,
     866            $requiredversion, $moodlebranch) {
     867
     868        $reqs = (object)array(
     869            'hasver' => null,
     870            'reqver' => null,
     871            'status' => null,
     872            'availability' => null,
     873        );
     874
     875        $otherplugin = $this->get_plugin_info($otherpluginname);
     876
     877        if ($otherplugin !== null) {
     878            // The required plugin is installed.
     879            $reqs->hasver = $otherplugin->versiondisk;
     880            $reqs->reqver = $requiredversion;
     881            // Check it has sufficient version.
     882            if ($requiredversion == ANY_VERSION or $otherplugin->versiondisk >= $requiredversion) {
     883                $reqs->status = self::REQUIREMENT_STATUS_OK;
     884            } else {
     885                $reqs->status = self::REQUIREMENT_STATUS_OUTDATED;
     886            }
     887
     888        } else {
     889            // The required plugin is not installed.
     890            $reqs->hasver = null;
     891            $reqs->reqver = $requiredversion;
     892            $reqs->status = self::REQUIREMENT_STATUS_MISSING;
     893        }
     894
     895        if ($reqs->status !== self::REQUIREMENT_STATUS_OK) {
     896            if ($this->is_remote_plugin_available($otherpluginname, $requiredversion, false)) {
     897                $reqs->availability = self::REQUIREMENT_AVAILABLE;
     898            } else {
     899                $reqs->availability = self::REQUIREMENT_UNAVAILABLE;
     900            }
     901        }
     902
     903        return $reqs;
     904    }
     905
     906    /**
     907     * Is the given plugin version available in the plugins directory?
     908     *
     909     * See {@link self::get_remote_plugin_info()} for the full explanation of how the $version
     910     * parameter is interpretted.
     911     *
     912     * @param string $component plugin frankenstyle name
     913     * @param string|int $version ANY_VERSION or the version number
     914     * @param bool $exactmatch false if "given version or higher" is requested
     915     * @return boolean
     916     */
     917    public function is_remote_plugin_available($component, $version, $exactmatch) {
     918
     919        $info = $this->get_remote_plugin_info($component, $version, $exactmatch);
     920
     921        if (empty($info)) {
     922            // There is no available plugin of that name.
     923            return false;
     924        }
     925
     926        if (empty($info->version)) {
     927            // Plugin is known, but no suitable version was found.
     928            return false;
     929        }
     930
     931        return true;
     932    }
     933
     934    /**
     935     * Can the given plugin version be installed via the admin UI?
     936     *
     937     * This check should be used whenever attempting to install a plugin from
     938     * the plugins directory (new install, available update, missing dependency).
     939     *
     940     * @param string $component
     941     * @param int $version version number
     942     * @param string $reason returned code of the reason why it is not
     943     * @return boolean
     944     */
     945    public function is_remote_plugin_installable($component, $version, &$reason=null) {
     946        global $CFG;
     947
     948        // Make sure the feature is not disabled.
     949        if (!empty($CFG->disableupdateautodeploy)) {
     950            $reason = 'disabled';
     951            return false;
     952        }
     953
     954        // Make sure the version is available.
     955        if (!$this->is_remote_plugin_available($component, $version, true)) {
     956            $reason = 'remoteunavailable';
     957            return false;
     958        }
     959
     960        // Make sure the plugin type root directory is writable.
     961        list($plugintype, $pluginname) = core_component::normalize_component($component);
     962        if (!$this->is_plugintype_writable($plugintype)) {
     963            $reason = 'notwritableplugintype';
     964            return false;
     965        }
     966
     967        $remoteinfo = $this->get_remote_plugin_info($component, $version, true);
     968        $localinfo = $this->get_plugin_info($component);
     969
     970        if ($localinfo) {
     971            // If the plugin is already present, prevent downgrade.
     972            if ($localinfo->versiondb > $remoteinfo->version->version) {
     973                $reason = 'cannotdowngrade';
     974                return false;
     975            }
     976
     977            // Make sure we have write access to all the existing code.
     978            if (is_dir($localinfo->rootdir)) {
     979                if (!$this->is_plugin_folder_removable($component)) {
     980                    $reason = 'notwritableplugin';
     981                    return false;
     982                }
     983            }
     984        }
     985
     986        // Looks like it could work.
     987        return true;
     988    }
     989
     990    /**
     991     * Given the list of remote plugin infos, return just those installable.
     992     *
     993     * This is typically used on lists returned by
     994     * {@link self::available_updates()} or {@link self::missing_dependencies()}
     995     * to perform bulk installation of remote plugins.
     996     *
     997     * @param array $remoteinfos list of {@link \core\update\remote_info}
     998     * @return array
     999     */
     1000    public function filter_installable($remoteinfos) {
     1001        global $CFG;
     1002
     1003        if (!empty($CFG->disableupdateautodeploy)) {
     1004            return array();
     1005        }
     1006        if (empty($remoteinfos)) {
     1007            return array();
     1008        }
     1009        $installable = array();
     1010        foreach ($remoteinfos as $index => $remoteinfo) {
     1011            if ($this->is_remote_plugin_installable($remoteinfo->component, $remoteinfo->version->version)) {
     1012                $installable[$index] = $remoteinfo;
     1013            }
     1014        }
     1015        return $installable;
     1016    }
     1017
     1018    /**
     1019     * Returns information about a plugin in the plugins directory.
     1020     *
     1021     * This is typically used when checking for available dependencies (in
     1022     * which case the $version represents minimal version we need), or
     1023     * when installing an available update or a new plugin from the plugins
     1024     * directory (in which case the $version is exact version we are
     1025     * interested in). The interpretation of the $version is controlled
     1026     * by the $exactmatch argument.
     1027     *
     1028     * If a plugin with the given component name is found, data about the
     1029     * plugin are returned as an object. The ->version property of the object
     1030     * contains the information about the particular plugin version that
     1031     * matches best the given critera. The ->version property is false if no
     1032     * suitable version of the plugin was found (yet the plugin itself is
     1033     * known).
     1034     *
     1035     * See {@link \core\update\api::validate_pluginfo_format()} for the
     1036     * returned data structure.
     1037     *
     1038     * @param string $component plugin frankenstyle name
     1039     * @param string|int $version ANY_VERSION or the version number
     1040     * @param bool $exactmatch false if "given version or higher" is requested
     1041     * @return \core\update\remote_info|bool
     1042     */
     1043    public function get_remote_plugin_info($component, $version, $exactmatch) {
     1044
     1045        if ($exactmatch and $version == ANY_VERSION) {
     1046            throw new coding_exception('Invalid request for exactly any version, it does not make sense.');
     1047        }
     1048
     1049        $client = $this->get_update_api_client();
     1050
     1051        if ($exactmatch) {
     1052            // Use client's get_plugin_info() method.
     1053            if (!isset($this->remotepluginsinfoexact[$component][$version])) {
     1054                $this->remotepluginsinfoexact[$component][$version] = $client->get_plugin_info($component, $version);
     1055            }
     1056            return $this->remotepluginsinfoexact[$component][$version];
     1057
     1058        } else {
     1059            // Use client's find_plugin() method.
     1060            if (!isset($this->remotepluginsinfoatleast[$component][$version])) {
     1061                $this->remotepluginsinfoatleast[$component][$version] = $client->find_plugin($component, $version);
     1062            }
     1063            return $this->remotepluginsinfoatleast[$component][$version];
     1064        }
     1065    }
     1066
     1067    /**
     1068     * Obtain the plugin ZIP file from the given URL
     1069     *
     1070     * The caller is supposed to know both downloads URL and the MD5 hash of
     1071     * the ZIP contents in advance, typically by using the API requests against
     1072     * the plugins directory.
     1073     *
     1074     * @param string $url
     1075     * @param string $md5
     1076     * @return string|bool full path to the file, false on error
     1077     */
     1078    public function get_remote_plugin_zip($url, $md5) {
     1079        global $CFG;
     1080
     1081        if (!empty($CFG->disableupdateautodeploy)) {
     1082            return false;
     1083        }
     1084        return $this->get_code_manager()->get_remote_plugin_zip($url, $md5);
     1085    }
     1086
     1087    /**
     1088     * Extracts the saved plugin ZIP file.
     1089     *
     1090     * Returns the list of files found in the ZIP. The format of that list is
     1091     * array of (string)filerelpath => (bool|string) where the array value is
     1092     * either true or a string describing the problematic file.
     1093     *
     1094     * @see zip_packer::extract_to_pathname()
     1095     * @param string $zipfilepath full path to the saved ZIP file
     1096     * @param string $targetdir full path to the directory to extract the ZIP file to
     1097     * @param string $rootdir explicitly rename the root directory of the ZIP into this non-empty value
     1098     * @return array list of extracted files as returned by {@link zip_packer::extract_to_pathname()}
     1099     */
     1100    public function unzip_plugin_file($zipfilepath, $targetdir, $rootdir = '') {
     1101        return $this->get_code_manager()->unzip_plugin_file($zipfilepath, $targetdir, $rootdir);
     1102    }
     1103
     1104    /**
     1105     * Detects the plugin's name from its ZIP file.
     1106     *
     1107     * Plugin ZIP packages are expected to contain a single directory and the
     1108     * directory name would become the plugin name once extracted to the Moodle
     1109     * dirroot.
     1110     *
     1111     * @param string $zipfilepath full path to the ZIP files
     1112     * @return string|bool false on error
     1113     */
     1114    public function get_plugin_zip_root_dir($zipfilepath) {
     1115        return $this->get_code_manager()->get_plugin_zip_root_dir($zipfilepath);
     1116    }
     1117
     1118    /**
     1119     * Return a list of missing dependencies.
     1120     *
     1121     * This should provide the full list of plugins that should be installed to
     1122     * fulfill the requirements of all plugins, if possible.
     1123     *
     1124     * @param bool $availableonly return only available missing dependencies
     1125     * @return array of \core\update\remote_info|bool indexed by the component name
     1126     */
     1127    public function missing_dependencies($availableonly=false) {
     1128
     1129        $dependencies = array();
     1130
     1131        foreach ($this->get_plugins() as $plugintype => $pluginfos) {
     1132            foreach ($pluginfos as $pluginname => $pluginfo) {
     1133                foreach ($this->resolve_requirements($pluginfo) as $reqname => $reqinfo) {
     1134                    if ($reqname === 'core') {
     1135                        continue;
     1136                    }
     1137                    if ($reqinfo->status != self::REQUIREMENT_STATUS_OK) {
     1138                        if ($reqinfo->availability == self::REQUIREMENT_AVAILABLE) {
     1139                            $remoteinfo = $this->get_remote_plugin_info($reqname, $reqinfo->reqver, false);
     1140
     1141                            if (empty($dependencies[$reqname])) {
     1142                                $dependencies[$reqname] = $remoteinfo;
     1143                            } else {
     1144                                // If resolving requirements has led to two different versions of the same
     1145                                // remote plugin, pick the higher version. This can happen in cases like one
     1146                                // plugin requiring ANY_VERSION and another plugin requiring specific higher
     1147                                // version with lower maturity of a remote plugin.
     1148                                if ($remoteinfo->version->version > $dependencies[$reqname]->version->version) {
     1149                                    $dependencies[$reqname] = $remoteinfo;
     1150                                }
     1151                            }
     1152
     1153                        } else {
     1154                            if (!isset($dependencies[$reqname])) {
     1155                                // Unable to find a plugin fulfilling the requirements.
     1156                                $dependencies[$reqname] = false;
     1157                            }
     1158                        }
     1159                    }
     1160                }
     1161            }
     1162        }
     1163
     1164        if ($availableonly) {
     1165            foreach ($dependencies as $component => $info) {
     1166                if (empty($info) or empty($info->version)) {
     1167                    unset($dependencies[$component]);
     1168                }
     1169            }
     1170        }
     1171
     1172        return $dependencies;
    7441173    }
    7451174
     
    7941223
    7951224    /**
     1225     * Perform the installation of plugins.
     1226     *
     1227     * If used for installation of remote plugins from the Moodle Plugins
     1228     * directory, the $plugins must be list of {@link \core\update\remote_info}
     1229     * object that represent installable remote plugins. The caller can use
     1230     * {@link self::filter_installable()} to prepare the list.
     1231     *
     1232     * If used for installation of plugins from locally available ZIP files,
     1233     * the $plugins should be list of objects with properties ->component and
     1234     * ->zipfilepath.
     1235     *
     1236     * The method uses {@link mtrace()} to produce direct output and can be
     1237     * used in both web and cli interfaces.
     1238     *
     1239     * @param array $plugins list of plugins
     1240     * @param bool $confirmed should the files be really deployed into the dirroot?
     1241     * @param bool $silent perform without output
     1242     * @return bool true on success
     1243     */
     1244    public function install_plugins(array $plugins, $confirmed, $silent) {
     1245        global $CFG, $OUTPUT;
     1246
     1247        if (!empty($CFG->disableupdateautodeploy)) {
     1248            return false;
     1249        }
     1250
     1251        if (empty($plugins)) {
     1252            return false;
     1253        }
     1254
     1255        $ok = get_string('ok', 'core');
     1256
     1257        // Let admins know they can expect more verbose output.
     1258        $silent or $this->mtrace(get_string('packagesdebug', 'core_plugin'), PHP_EOL, DEBUG_NORMAL);
     1259
     1260        // Download all ZIP packages if we do not have them yet.
     1261        $zips = array();
     1262        foreach ($plugins as $plugin) {
     1263            if ($plugin instanceof \core\update\remote_info) {
     1264                $zips[$plugin->component] = $this->get_remote_plugin_zip($plugin->version->downloadurl,
     1265                    $plugin->version->downloadmd5);
     1266                $silent or $this->mtrace(get_string('packagesdownloading', 'core_plugin', $plugin->component), ' ... ');
     1267                $silent or $this->mtrace(PHP_EOL.' <- '.$plugin->version->downloadurl, '', DEBUG_DEVELOPER);
     1268                $silent or $this->mtrace(PHP_EOL.' -> '.$zips[$plugin->component], ' ... ', DEBUG_DEVELOPER);
     1269                if (!$zips[$plugin->component]) {
     1270                    $silent or $this->mtrace(get_string('error'));
     1271                    return false;
     1272                }
     1273                $silent or $this->mtrace($ok);
     1274            } else {
     1275                if (empty($plugin->zipfilepath)) {
     1276                    throw new coding_exception('Unexpected data structure provided');
     1277                }
     1278                $zips[$plugin->component] = $plugin->zipfilepath;
     1279                $silent or $this->mtrace('ZIP '.$plugin->zipfilepath, PHP_EOL, DEBUG_DEVELOPER);
     1280            }
     1281        }
     1282
     1283        // Validate all downloaded packages.
     1284        foreach ($plugins as $plugin) {
     1285            $zipfile = $zips[$plugin->component];
     1286            $silent or $this->mtrace(get_string('packagesvalidating', 'core_plugin', $plugin->component), ' ... ');
     1287            list($plugintype, $pluginname) = core_component::normalize_component($plugin->component);
     1288            $tmp = make_request_directory();
     1289            $zipcontents = $this->unzip_plugin_file($zipfile, $tmp, $pluginname);
     1290            if (empty($zipcontents)) {
     1291                $silent or $this->mtrace(get_string('error'));
     1292                $silent or $this->mtrace('Unable to unzip '.$zipfile, PHP_EOL, DEBUG_DEVELOPER);
     1293                return false;
     1294            }
     1295
     1296            $validator = \core\update\validator::instance($tmp, $zipcontents);
     1297            $validator->assert_plugin_type($plugintype);
     1298            $validator->assert_moodle_version($CFG->version);
     1299            // TODO Check for missing dependencies during validation.
     1300            $result = $validator->execute();
     1301            if (!$silent) {
     1302                $result ? $this->mtrace($ok) : $this->mtrace(get_string('error'));
     1303                foreach ($validator->get_messages() as $message) {
     1304                    if ($message->level === $validator::INFO) {
     1305                        // Display [OK] validation messages only if debugging mode is DEBUG_NORMAL.
     1306                        $level = DEBUG_NORMAL;
     1307                    } else if ($message->level === $validator::DEBUG) {
     1308                        // Display [Debug] validation messages only if debugging mode is DEBUG_ALL.
     1309                        $level = DEBUG_ALL;
     1310                    } else {
     1311                        // Display [Warning] and [Error] always.
     1312                        $level = null;
     1313                    }
     1314                    if ($message->level === $validator::WARNING and !CLI_SCRIPT) {
     1315                        $this->mtrace('  <strong>['.$validator->message_level_name($message->level).']</strong>', ' ', $level);
     1316                    } else {
     1317                        $this->mtrace('  ['.$validator->message_level_name($message->level).']', ' ', $level);
     1318                    }
     1319                    $this->mtrace($validator->message_code_name($message->msgcode), ' ', $level);
     1320                    $info = $validator->message_code_info($message->msgcode, $message->addinfo);
     1321                    if ($info) {
     1322                        $this->mtrace('['.s($info).']', ' ', $level);
     1323                    } else if (is_string($message->addinfo)) {
     1324                        $this->mtrace('['.s($message->addinfo, true).']', ' ', $level);
     1325                    } else {
     1326                        $this->mtrace('['.s(json_encode($message->addinfo, true)).']', ' ', $level);
     1327                    }
     1328                    if ($icon = $validator->message_help_icon($message->msgcode)) {
     1329                        if (CLI_SCRIPT) {
     1330                            $this->mtrace(PHP_EOL.'  ^^^ '.get_string('help').': '.
     1331                                get_string($icon->identifier.'_help', $icon->component), '', $level);
     1332                        } else {
     1333                            $this->mtrace($OUTPUT->render($icon), ' ', $level);
     1334                        }
     1335                    }
     1336                    $this->mtrace(PHP_EOL, '', $level);
     1337                }
     1338            }
     1339            if (!$result) {
     1340                $silent or $this->mtrace(get_string('packagesvalidatingfailed', 'core_plugin'));
     1341                return false;
     1342            }
     1343        }
     1344        $silent or $this->mtrace(PHP_EOL.get_string('packagesvalidatingok', 'core_plugin'));
     1345
     1346        if (!$confirmed) {
     1347            return true;
     1348        }
     1349
     1350        // Extract all ZIP packs do the dirroot.
     1351        foreach ($plugins as $plugin) {
     1352            $silent or $this->mtrace(get_string('packagesextracting', 'core_plugin', $plugin->component), ' ... ');
     1353            $zipfile = $zips[$plugin->component];
     1354            list($plugintype, $pluginname) = core_component::normalize_component($plugin->component);
     1355            $target = $this->get_plugintype_root($plugintype);
     1356            if (file_exists($target.'/'.$pluginname)) {
     1357                $this->remove_plugin_folder($this->get_plugin_info($plugin->component));
     1358            }
     1359            if (!$this->unzip_plugin_file($zipfile, $target, $pluginname)) {
     1360                $silent or $this->mtrace(get_string('error'));
     1361                $silent or $this->mtrace('Unable to unzip '.$zipfile, PHP_EOL, DEBUG_DEVELOPER);
     1362                if (function_exists('opcache_reset')) {
     1363                    opcache_reset();
     1364                }
     1365                return false;
     1366            }
     1367            $silent or $this->mtrace($ok);
     1368        }
     1369        if (function_exists('opcache_reset')) {
     1370            opcache_reset();
     1371        }
     1372
     1373        return true;
     1374    }
     1375
     1376    /**
     1377     * Outputs the given message via {@link mtrace()}.
     1378     *
     1379     * If $debug is provided, then the message is displayed only at the given
     1380     * debugging level (e.g. DEBUG_DEVELOPER to display the message only if the
     1381     * site has developer debugging level selected).
     1382     *
     1383     * @param string $msg message
     1384     * @param string $eol end of line
     1385     * @param null|int $debug null to display always, int only on given debug level
     1386     */
     1387    protected function mtrace($msg, $eol=PHP_EOL, $debug=null) {
     1388        global $CFG;
     1389
     1390        if ($debug !== null and !debugging(null, $debug)) {
     1391            return;
     1392        }
     1393
     1394        mtrace($msg, $eol);
     1395    }
     1396
     1397    /**
    7961398     * Returns uninstall URL if exists.
    7971399     *
     
    8731475
    8741476    /**
     1477     * Returns list of available updates for the given component.
     1478     *
     1479     * This method should be considered as internal API and is supposed to be
     1480     * called by {@link \core\plugininfo\base::available_updates()} only
     1481     * to lazy load the data once they are first requested.
     1482     *
     1483     * @param string $component frankenstyle name of the plugin
     1484     * @return null|array array of \core\update\info objects or null
     1485     */
     1486    public function load_available_updates_for_plugin($component) {
     1487        global $CFG;
     1488
     1489        $provider = \core\update\checker::instance();
     1490
     1491        if (!$provider->enabled() or during_initial_install()) {
     1492            return null;
     1493        }
     1494
     1495        if (isset($CFG->updateminmaturity)) {
     1496            $minmaturity = $CFG->updateminmaturity;
     1497        } else {
     1498            // This can happen during the very first upgrade to 2.3.
     1499            $minmaturity = MATURITY_STABLE;
     1500        }
     1501
     1502        return $provider->get_update_info($component, array('minmaturity' => $minmaturity));
     1503    }
     1504
     1505    /**
     1506     * Returns a list of all available updates to be installed.
     1507     *
     1508     * This is used when "update all plugins" action is performed at the
     1509     * administration UI screen.
     1510     *
     1511     * Returns array of remote info objects indexed by the plugin
     1512     * component. If there are multiple updates available (typically a mix of
     1513     * stable and non-stable ones), we pick the most mature most recent one.
     1514     *
     1515     * Plugins without explicit maturity are considered more mature than
     1516     * release candidates but less mature than explicit stable (this should be
     1517     * pretty rare case).
     1518     *
     1519     * @return array (string)component => (\core\update\remote_info)remoteinfo
     1520     */
     1521    public function available_updates() {
     1522
     1523        $updates = array();
     1524
     1525        foreach ($this->get_plugins() as $type => $plugins) {
     1526            foreach ($plugins as $plugin) {
     1527                $availableupdates = $plugin->available_updates();
     1528                if (empty($availableupdates)) {
     1529                    continue;
     1530                }
     1531                foreach ($availableupdates as $update) {
     1532                    if (empty($updates[$plugin->component])) {
     1533                        $updates[$plugin->component] = $update;
     1534                        continue;
     1535                    }
     1536                    $maturitycurrent = $updates[$plugin->component]->maturity;
     1537                    if (empty($maturitycurrent)) {
     1538                        $maturitycurrent = MATURITY_STABLE - 25;
     1539                    }
     1540                    $maturityremote = $update->maturity;
     1541                    if (empty($maturityremote)) {
     1542                        $maturityremote = MATURITY_STABLE - 25;
     1543                    }
     1544                    if ($maturityremote < $maturitycurrent) {
     1545                        continue;
     1546                    }
     1547                    if ($maturityremote > $maturitycurrent) {
     1548                        $updates[$plugin->component] = $update;
     1549                        continue;
     1550                    }
     1551                    if ($update->version > $updates[$plugin->component]->version) {
     1552                        $updates[$plugin->component] = $update;
     1553                        continue;
     1554                    }
     1555                }
     1556            }
     1557        }
     1558
     1559        foreach ($updates as $component => $update) {
     1560            $remoteinfo = $this->get_remote_plugin_info($component, $update->version, true);
     1561            if (empty($remoteinfo) or empty($remoteinfo->version)) {
     1562                unset($updates[$component]);
     1563            } else {
     1564                $updates[$component] = $remoteinfo;
     1565            }
     1566        }
     1567
     1568        return $updates;
     1569    }
     1570
     1571    /**
    8751572     * Check to see if the given plugin folder can be removed by the web server process.
    8761573     *
     
    8931590        // Check that the folder and all its content is writable (thence removable).
    8941591        return $this->is_directory_removable($pluginfo->rootdir);
     1592    }
     1593
     1594    /**
     1595     * Is it possible to create a new plugin directory for the given plugin type?
     1596     *
     1597     * @throws coding_exception for invalid plugin types or non-existing plugin type locations
     1598     * @param string $plugintype
     1599     * @return boolean
     1600     */
     1601    public function is_plugintype_writable($plugintype) {
     1602
     1603        $plugintypepath = $this->get_plugintype_root($plugintype);
     1604
     1605        if (is_null($plugintypepath)) {
     1606            throw new coding_exception('Unknown plugin type: '.$plugintype);
     1607        }
     1608
     1609        if ($plugintypepath === false) {
     1610            throw new coding_exception('Plugin type location does not exist: '.$plugintype);
     1611        }
     1612
     1613        return is_writable($plugintypepath);
     1614    }
     1615
     1616    /**
     1617     * Returns the full path of the root of the given plugin type
     1618     *
     1619     * Null is returned if the plugin type is not known. False is returned if
     1620     * the plugin type root is expected but not found. Otherwise, string is
     1621     * returned.
     1622     *
     1623     * @param string $plugintype
     1624     * @return string|bool|null
     1625     */
     1626    public function get_plugintype_root($plugintype) {
     1627
     1628        $plugintypepath = null;
     1629        foreach (core_component::get_plugin_types() as $type => $fullpath) {
     1630            if ($type === $plugintype) {
     1631                $plugintypepath = $fullpath;
     1632                break;
     1633            }
     1634        }
     1635        if (is_null($plugintypepath)) {
     1636            return null;
     1637        }
     1638        if (!is_dir($plugintypepath)) {
     1639            return false;
     1640        }
     1641
     1642        return $plugintypepath;
    8951643    }
    8961644
     
    9141662            'enrol' => array('authorize'),
    9151663            'tinymce' => array('dragmath'),
    916             'tool' => array('bloglevelupgrade', 'qeupgradehelper'),
     1664            'tool' => array('bloglevelupgrade', 'qeupgradehelper', 'timezoneimport'),
    9171665            'theme' => array('mymobile', 'afterburner', 'anomaly', 'arialist', 'binarius', 'boxxie', 'brick', 'formal_white',
    9181666                'formfactor', 'fusion', 'leatherbound', 'magazine', 'nimble', 'nonzero', 'overlay', 'serenity', 'sky_high',
     
    9681716
    9691717            'block' => array(
    970                 'activity_modules', 'admin_bookmarks', 'badges', 'blog_menu',
    971                 'blog_recent', 'blog_tags', 'calendar_month',
     1718                'activity_modules', 'activity_results', 'admin_bookmarks', 'badges',
     1719                'blog_menu', 'blog_recent', 'blog_tags', 'calendar_month',
    9721720                'calendar_upcoming', 'comments', 'community',
    9731721                'completionstatus', 'course_list', 'course_overview',
     
    10541802
    10551803            'ltiservice' => array(
    1056                 'profile', 'toolproxy', 'toolsettings'
     1804                'memberships', 'profile', 'toolproxy', 'toolsettings'
    10571805            ),
    10581806
     
    10971845            'qtype' => array(
    10981846                'calculated', 'calculatedmulti', 'calculatedsimple',
    1099                 'description', 'essay', 'match', 'missingtype', 'multianswer',
     1847                'ddimageortext', 'ddmarker', 'ddwtos', 'description',
     1848                'essay', 'gapselect', 'match', 'missingtype', 'multianswer',
    11001849                'multichoice', 'numerical', 'random', 'randomsamatch',
    11011850                'shortanswer', 'truefalse'
     
    11131862            'report' => array(
    11141863                'backups', 'completion', 'configlog', 'courseoverview', 'eventlist',
    1115                 'log', 'loglive', 'outline', 'participation', 'progress', 'questioninstances', 'security', 'stats', 'performance'
     1864                'log', 'loglive', 'outline', 'participation', 'progress', 'questioninstances', 'security', 'stats', 'performance',
     1865                'usersessions',
    11161866            ),
    11171867
     
    11411891            'tool' => array(
    11421892                'assignmentupgrade', 'availabilityconditions', 'behat', 'capability', 'customlang',
    1143                 'dbtransfer', 'generator', 'health', 'innodb', 'installaddon',
     1893                'dbtransfer', 'filetypes', 'generator', 'health', 'innodb', 'installaddon',
    11441894                'langimport', 'log', 'messageinbound', 'multilangupgrade', 'monitor', 'phpunit', 'profiling',
    1145                 'replace', 'spamcleaner', 'task', 'timezoneimport',
     1895                'replace', 'spamcleaner', 'task', 'templatelibrary',
    11461896                'unittest', 'uploadcourse', 'uploaduser', 'unsuproles', 'xmldb'
    11471897            ),
     
    11691919            return false;
    11701920        }
     1921    }
     1922
     1923    /**
     1924     * Remove the current plugin code from the dirroot.
     1925     *
     1926     * If removing the currently installed version (which happens during
     1927     * updates), we archive the code so that the upgrade can be cancelled.
     1928     *
     1929     * To prevent accidental data-loss, we also archive the existing plugin
     1930     * code if cancelling installation of it, so that the developer does not
     1931     * loose the only version of their work-in-progress.
     1932     *
     1933     * @param \core\plugininfo\base $plugin
     1934     */
     1935    public function remove_plugin_folder(\core\plugininfo\base $plugin) {
     1936
     1937        if (!$this->is_plugin_folder_removable($plugin->component)) {
     1938            throw new moodle_exception('err_removing_unremovable_folder', 'core_plugin', '',
     1939                array('plugin' => $pluginfo->component, 'rootdir' => $pluginfo->rootdir),
     1940                'plugin root folder is not removable as expected');
     1941        }
     1942
     1943        if ($plugin->get_status() === self::PLUGIN_STATUS_UPTODATE or $plugin->get_status() === self::PLUGIN_STATUS_NEW) {
     1944            $this->archive_plugin_version($plugin);
     1945        }
     1946
     1947        remove_dir($plugin->rootdir);
     1948        clearstatcache();
     1949        if (function_exists('opcache_reset')) {
     1950            opcache_reset();
     1951        }
     1952    }
     1953
     1954    /**
     1955     * Can the installation of the new plugin be cancelled?
     1956     *
     1957     * Subplugins can be cancelled only via their parent plugin, not separately
     1958     * (they are considered as implicit requirements if distributed together
     1959     * with the main package).
     1960     *
     1961     * @param \core\plugininfo\base $plugin
     1962     * @return bool
     1963     */
     1964    public function can_cancel_plugin_installation(\core\plugininfo\base $plugin) {
     1965        global $CFG;
     1966
     1967        if (!empty($CFG->disableupdateautodeploy)) {
     1968            return false;
     1969        }
     1970
     1971        if (empty($plugin) or $plugin->is_standard() or $plugin->is_subplugin()
     1972                or !$this->is_plugin_folder_removable($plugin->component)) {
     1973            return false;
     1974        }
     1975
     1976        if ($plugin->get_status() === self::PLUGIN_STATUS_NEW) {
     1977            return true;
     1978        }
     1979
     1980        return false;
     1981    }
     1982
     1983    /**
     1984     * Can the upgrade of the existing plugin be cancelled?
     1985     *
     1986     * Subplugins can be cancelled only via their parent plugin, not separately
     1987     * (they are considered as implicit requirements if distributed together
     1988     * with the main package).
     1989     *
     1990     * @param \core\plugininfo\base $plugin
     1991     * @return bool
     1992     */
     1993    public function can_cancel_plugin_upgrade(\core\plugininfo\base $plugin) {
     1994        global $CFG;
     1995
     1996        if (!empty($CFG->disableupdateautodeploy)) {
     1997            // Cancelling the plugin upgrade is actually installation of the
     1998            // previously archived version.
     1999            return false;
     2000        }
     2001
     2002        if (empty($plugin) or $plugin->is_standard() or $plugin->is_subplugin()
     2003                or !$this->is_plugin_folder_removable($plugin->component)) {
     2004            return false;
     2005        }
     2006
     2007        if ($plugin->get_status() === self::PLUGIN_STATUS_UPGRADE) {
     2008            if ($this->get_code_manager()->get_archived_plugin_version($plugin->component, $plugin->versiondb)) {
     2009                return true;
     2010            }
     2011        }
     2012
     2013        return false;
     2014    }
     2015
     2016    /**
     2017     * Removes the plugin code directory if it is not installed yet.
     2018     *
     2019     * This is intended for the plugins check screen to give the admin a chance
     2020     * to cancel the installation of just unzipped plugin before the database
     2021     * upgrade happens.
     2022     *
     2023     * @param string $component
     2024     */
     2025    public function cancel_plugin_installation($component) {
     2026        global $CFG;
     2027
     2028        if (!empty($CFG->disableupdateautodeploy)) {
     2029            return false;
     2030        }
     2031
     2032        $plugin = $this->get_plugin_info($component);
     2033
     2034        if ($this->can_cancel_plugin_installation($plugin)) {
     2035            $this->remove_plugin_folder($plugin);
     2036        }
     2037
     2038        return false;
     2039    }
     2040
     2041    /**
     2042     * Returns plugins, the installation of which can be cancelled.
     2043     *
     2044     * @return array [(string)component] => (\core\plugininfo\base)plugin
     2045     */
     2046    public function list_cancellable_installations() {
     2047        global $CFG;
     2048
     2049        if (!empty($CFG->disableupdateautodeploy)) {
     2050            return array();
     2051        }
     2052
     2053        $cancellable = array();
     2054        foreach ($this->get_plugins() as $type => $plugins) {
     2055            foreach ($plugins as $plugin) {
     2056                if ($this->can_cancel_plugin_installation($plugin)) {
     2057                    $cancellable[$plugin->component] = $plugin;
     2058                }
     2059            }
     2060        }
     2061
     2062        return $cancellable;
     2063    }
     2064
     2065    /**
     2066     * Archive the current on-disk plugin code.
     2067     *
     2068     * @param \core\plugiinfo\base $plugin
     2069     * @return bool
     2070     */
     2071    public function archive_plugin_version(\core\plugininfo\base $plugin) {
     2072        return $this->get_code_manager()->archive_plugin_version($plugin->rootdir, $plugin->component, $plugin->versiondisk);
     2073    }
     2074
     2075    /**
     2076     * Returns list of all archives that can be installed to cancel the plugin upgrade.
     2077     *
     2078     * @return array [(string)component] => {(string)->component, (string)->zipfilepath}
     2079     */
     2080    public function list_restorable_archives() {
     2081        global $CFG;
     2082
     2083        if (!empty($CFG->disableupdateautodeploy)) {
     2084            return false;
     2085        }
     2086
     2087        $codeman = $this->get_code_manager();
     2088        $restorable = array();
     2089        foreach ($this->get_plugins() as $type => $plugins) {
     2090            foreach ($plugins as $plugin) {
     2091                if ($this->can_cancel_plugin_upgrade($plugin)) {
     2092                    $restorable[$plugin->component] = (object)array(
     2093                        'component' => $plugin->component,
     2094                        'zipfilepath' => $codeman->get_archived_plugin_version($plugin->component, $plugin->versiondb)
     2095                    );
     2096                }
     2097            }
     2098        }
     2099
     2100        return $restorable;
    11712101    }
    11722102
     
    12412171     * @return boolean
    12422172     */
    1243     protected function is_directory_removable($fullpath) {
     2173    public function is_directory_removable($fullpath) {
    12442174
    12452175        if (!is_writable($fullpath)) {
     
    12892219        }
    12902220
    1291         if ($pluginfo->get_status() === self::PLUGIN_STATUS_NEW) {
     2221        if ($pluginfo->get_status() === static::PLUGIN_STATUS_NEW) {
    12922222            // The plugin is not installed. It should be either installed or removed from the disk.
    12932223            // Relying on this temporary state may be tricky.
     
    13042234        return true;
    13052235    }
     2236
     2237    /**
     2238     * Returns a code_manager instance to be used for the plugins code operations.
     2239     *
     2240     * @return \core\update\code_manager
     2241     */
     2242    protected function get_code_manager() {
     2243
     2244        if ($this->codemanager === null) {
     2245            $this->codemanager = new \core\update\code_manager();
     2246        }
     2247
     2248        return $this->codemanager;
     2249    }
     2250
     2251    /**
     2252     * Returns a client for https://download.moodle.org/api/
     2253     *
     2254     * @return \core\update\api
     2255     */
     2256    protected function get_update_api_client() {
     2257
     2258        if ($this->updateapiclient === null) {
     2259            $this->updateapiclient = \core\update\api::client();
     2260        }
     2261
     2262        return $this->updateapiclient;
     2263    }
    13062264}
  • moodle/trunk/fuentes/lib/classes/plugininfo/availability.php

    r136 r1331  
    2323 */
    2424namespace core\plugininfo;
     25
     26use admin_settingpage;
    2527
    2628defined('MOODLE_INTERNAL') || die();
     
    6365        return true;
    6466    }
     67
     68    /**
     69     * Get the name for the settings section.
     70     *
     71     * @return string
     72     */
     73    public function get_settings_section_name() {
     74        return 'availabilitysetting' . $this->name;
     75    }
     76
     77    /**
     78     * Load the global settings for a particular availability plugin (if there are any)
     79     *
     80     * @param \part_of_admin_tree $adminroot
     81     * @param string $parentnodename
     82     * @param bool $hassiteconfig
     83     */
     84    public function load_settings(\part_of_admin_tree $adminroot, $parentnodename, $hassiteconfig) {
     85        global $CFG, $USER, $DB, $OUTPUT, $PAGE; // In case settings.php wants to refer to them.
     86        $ADMIN = $adminroot; // May be used in settings.php.
     87        $plugininfo = $this; // Also can be used inside settings.php
     88        $availability = $this; // Also to be used inside settings.php.
     89
     90        if (!$this->is_installed_and_upgraded()) {
     91            return;
     92        }
     93
     94        if (!$hassiteconfig) {
     95            return;
     96        }
     97
     98        $section = $this->get_settings_section_name();
     99
     100        $settings = null;
     101        if (file_exists($this->full_path('settings.php'))) {
     102            $settings = new admin_settingpage($section, $this->displayname, 'moodle/site:config', $this->is_enabled() === false);
     103            include($this->full_path('settings.php')); // This may also set $settings to null.
     104        }
     105        if ($settings) {
     106            $ADMIN->add($parentnodename, $settings);
     107        }
     108    }
    65109}
  • moodle/trunk/fuentes/lib/classes/plugininfo/base.php

    r136 r1331  
    6262    /** @var int order of the plugin among other plugins of the same type - not supported yet */
    6363    public $sortorder;
     64    /** @var core_plugin_manager the plugin manager this plugin info is part of */
     65    public $pluginman;
     66
    6467    /** @var array|null array of {@link \core\update\info} for this plugin */
    65     public $availableupdates;
     68    protected $availableupdates;
    6669
    6770    /**
     
    7679     * Gathers and returns the information about all plugins of the given type,
    7780     * 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.
    7884     *
    7985     * @param string $type the name of the plugintype, eg. mod, auth or workshopform
    8086     * @param string $typerootdir full path to the location of the plugin dir
    8187     * @param string $typeclass the name of the actually called class
     88     * @param core_plugin_manager $pluginman the plugin manager calling this method
    8289     * @return array of plugintype classes, indexed by the plugin name
    8390     */
    84     public static function get_plugins($type, $typerootdir, $typeclass) {
     91    public static function get_plugins($type, $typerootdir, $typeclass, $pluginman) {
    8592        // Get the information about plugins at the disk.
    8693        $plugins = core_component::get_plugin_list($type);
     
    8895        foreach ($plugins as $pluginname => $pluginrootdir) {
    8996            $return[$pluginname] = self::make_plugin_instance($type, $typerootdir,
    90                 $pluginname, $pluginrootdir, $typeclass);
     97                $pluginname, $pluginrootdir, $typeclass, $pluginman);
    9198        }
    9299
    93100        // Fetch missing incorrectly uninstalled plugins.
    94         $manager = core_plugin_manager::instance();
    95         $plugins = $manager->get_installed_plugins($type);
     101        $plugins = $pluginman->get_installed_plugins($type);
    96102
    97103        foreach ($plugins as $name => $version) {
     
    106112            $plugin->displayname = $name;
    107113            $plugin->versiondb   = $version;
     114            $plugin->pluginman   = $pluginman;
    108115            $plugin->init_is_standard();
    109116
     
    122129     * @param string $namerootdir full path to the location of the plugin
    123130     * @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
    124132     * @return base the instance of $typeclass
    125133     */
    126     protected static function make_plugin_instance($type, $typerootdir, $name, $namerootdir, $typeclass) {
     134    protected static function make_plugin_instance($type, $typerootdir, $name, $namerootdir, $typeclass, $pluginman) {
    127135        $plugin              = new $typeclass();
    128136        $plugin->type        = $type;
     
    130138        $plugin->name        = $name;
    131139        $plugin->rootdir     = $namerootdir;
     140        $plugin->pluginman   = $pluginman;
    132141
    133142        $plugin->init_display_name();
     
    148157        }
    149158        if ($this->versiondb === null and $this->versiondisk === null) {
    150             // There is no version.php or version info inside,
    151             // for now let's pretend it is ok.
    152             // TODO: return false once we require version in each plugin.
    153             return true;
     159            // There is no version.php or version info inside it.
     160            return false;
    154161        }
    155162
     
    208215     */
    209216    public function load_disk_version() {
    210         $versions = core_plugin_manager::instance()->get_present_plugins($this->type);
     217        $versions = $this->pluginman->get_present_plugins($this->type);
    211218
    212219        $this->versiondisk = null;
     
    262269     */
    263270    public function get_parent_plugin() {
    264         return $this->get_plugin_manager()->get_parent_of_subplugin($this->type);
     271        return $this->pluginman->get_parent_of_subplugin($this->type);
    265272    }
    266273
     
    274281     */
    275282    public function load_db_version() {
    276         $versions = core_plugin_manager::instance()->get_installed_plugins($this->type);
     283        $versions = $this->pluginman->get_installed_plugins($this->type);
    277284
    278285        if (isset($versions[$this->name])) {
     
    293300    public function init_is_standard() {
    294301
    295         $standard = core_plugin_manager::standard_plugins_list($this->type);
     302        $pluginman = $this->pluginman;
     303        $standard = $pluginman::standard_plugins_list($this->type);
    296304
    297305        if ($standard !== false) {
     
    300308                $this->source = core_plugin_manager::PLUGIN_SOURCE_STANDARD;
    301309            } else if (!is_null($this->versiondb) and is_null($this->versiondisk)
    302                 and core_plugin_manager::is_deleted_standard_plugin($this->type, $this->name)) {
     310                and $pluginman::is_deleted_standard_plugin($this->type, $this->name)) {
    303311                $this->source = core_plugin_manager::PLUGIN_SOURCE_STANDARD; // To be deleted.
    304312            } else {
     
    341349    public function get_status() {
    342350
     351        $pluginman = $this->pluginman;
     352
    343353        if (is_null($this->versiondb) and is_null($this->versiondisk)) {
    344354            return core_plugin_manager::PLUGIN_STATUS_NODB;
     
    348358
    349359        } else if (!is_null($this->versiondb) and is_null($this->versiondisk)) {
    350             if (core_plugin_manager::is_deleted_standard_plugin($this->type, $this->name)) {
     360            if ($pluginman::is_deleted_standard_plugin($this->type, $this->name)) {
    351361                return core_plugin_manager::PLUGIN_STATUS_DELETE;
    352362            } else {
     
    387397        }
    388398
    389         $enabled = core_plugin_manager::instance()->get_enabled_plugins($this->type);
     399        $enabled = $this->pluginman->get_enabled_plugins($this->type);
    390400
    391401        if (!is_array($enabled)) {
     
    394404
    395405        return isset($enabled[$this->name]);
    396     }
    397 
    398     /**
    399      * Populates the property {@link $availableupdates} with the information provided by
    400      * available update checker
    401      *
    402      * @param \core\update\checker $provider the class providing the available update info
    403      */
    404     public function check_available_updates(\core\update\checker $provider) {
    405         global $CFG;
    406 
    407         if (isset($CFG->updateminmaturity)) {
    408             $minmaturity = $CFG->updateminmaturity;
    409         } else {
    410             // This can happen during the very first upgrade to 2.3 .
    411             $minmaturity = MATURITY_STABLE;
    412         }
    413 
    414         $this->availableupdates = $provider->get_update_info($this->component,
    415             array('minmaturity' => $minmaturity));
    416406    }
    417407
     
    423413     * availability is unknown.
    424414     *
     415     * Populates the property {@link $availableupdates} on first call (lazy
     416     * loading).
     417     *
    425418     * @return array|null
    426419     */
    427420    public function available_updates() {
    428421
     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
    429427        if (empty($this->availableupdates) or !is_array($this->availableupdates)) {
     428            $this->availableupdates = array();
    430429            return null;
    431430        }
     
    587586        ));
    588587    }
    589 
    590     /**
    591      * Provides access to the core_plugin_manager singleton.
    592      *
    593      * @return core_plugin_manager
    594      */
    595     protected function get_plugin_manager() {
    596         return core_plugin_manager::instance();
    597     }
    598588}
  • moodle/trunk/fuentes/lib/classes/plugininfo/cachestore.php

    r136 r1331  
    2727
    2828/**
    29  * Class for admin tool plugins
     29 * Class for cache store plugins
    3030 */
    3131class cachestore extends base {
    3232
    3333    public function is_uninstall_allowed() {
    34         return false;
     34        $instance = \cache_config::instance();
     35        foreach ($instance->get_all_stores() as $store) {
     36            if ($store['plugin'] == $this->name) {
     37                return false;
     38            }
     39        }
     40        return true;
    3541    }
    3642}
  • moodle/trunk/fuentes/lib/classes/plugininfo/calendartype.php

    r136 r1331  
    2323 */
    2424namespace core\plugininfo;
     25
     26use part_of_admin_tree, admin_settingpage;
    2527
    2628defined('MOODLE_INTERNAL') || die();
  • moodle/trunk/fuentes/lib/classes/plugininfo/format.php

    r136 r1331  
    7272     * @param string $typerootdir full path to the location of the plugin dir
    7373     * @param string $typeclass the name of the actually called class
     74     * @param core_plugin_manager $pluginman the plugin manager calling this method
    7475     * @return array of plugintype classes, indexed by the plugin name
    7576     */
    76     public static function get_plugins($type, $typerootdir, $typeclass) {
     77    public static function get_plugins($type, $typerootdir, $typeclass, $pluginman) {
    7778        global $CFG;
    78         $formats = parent::get_plugins($type, $typerootdir, $typeclass);
    7979        require_once($CFG->dirroot.'/course/lib.php');
     80
     81        $formats = parent::get_plugins($type, $typerootdir, $typeclass, $pluginman);
    8082        $order = get_sorted_course_formats();
    8183        $sortedformats = array();
     
    138140        }
    139141
    140         $defaultformat = $this->get_plugin_manager()->plugin_name('format_'.get_config('moodlecourse', 'format'));
     142        $defaultformat = $this->pluginman->plugin_name('format_'.get_config('moodlecourse', 'format'));
    141143        $message = get_string(
    142144            'formatuninstallwithcourses', 'core_admin',
  • moodle/trunk/fuentes/lib/classes/plugininfo/orphaned.php

    r136 r1331  
    6565     * @param string $typerootdir full path to the location of the plugin dir
    6666     * @param string $typeclass the name of the actually called class
     67     * @param core_plugin_manager $pluginman the plugin manager calling this method
    6768     * @return array of plugintype classes, indexed by the plugin name
    6869     */
    69     public static function get_plugins($type, $typerootdir, $typeclass) {
     70    public static function get_plugins($type, $typerootdir, $typeclass, $pluginman) {
    7071        $return = array();
    71         $manager = \core_plugin_manager::instance();
    72         $plugins = $manager->get_installed_plugins($type);
     72        $plugins = $pluginman->get_installed_plugins($type);
    7373
    7474        foreach ($plugins as $name => $version) {
     
    8080            $plugin->displayname = $name;
    8181            $plugin->versiondb   = $version;
     82            $plugin->pluginman   = $pluginman;
    8283            $plugin->init_is_standard();
    8384
  • moodle/trunk/fuentes/lib/classes/plugininfo/portfolio.php

    r136 r1331  
    5555        return new moodle_url('/admin/portfolio.php');
    5656    }
     57
     58    /**
     59     * Defines if there should be a way to uninstall the plugin via the administration UI.
     60     * @return boolean
     61     */
     62    public function is_uninstall_allowed() {
     63        return true;
     64    }
     65
     66    /**
     67     * Pre-uninstall hook.
     68     * This is intended for disabling of plugin, some DB table purging, etc.
     69     */
     70    public function uninstall_cleanup() {
     71        global $DB;
     72
     73        // Get all instances of this portfolio.
     74        $count = $DB->count_records('portfolio_instance', array('plugin' => $this->name));
     75        if ($count > 0) {
     76            // This portfolio is in use, get the it's ID.
     77            $rec = $DB->get_record('portfolio_instance', array('plugin' => $this->name));
     78
     79            // Remove all records from portfolio_instance_config.
     80            $DB->delete_records('portfolio_instance_config', array('instance' => $rec->id));
     81            // Remove all records from portfolio_instance_user.
     82            $DB->delete_records('portfolio_instance_user', array('instance' => $rec->id));
     83            // Remove all records from portfolio_log.
     84            $DB->delete_records('portfolio_log', array('portfolio' => $rec->id));
     85            // Remove all records from portfolio_tempdata.
     86            $DB->delete_records('portfolio_tempdata', array('instance' => $rec->id));
     87
     88            // Remove the record from the portfolio_instance table.
     89            $DB->delete_records('portfolio_instance', array('id' => $rec->id));
     90        }
     91
     92        parent::uninstall_cleanup();
     93    }
    5794}
  • moodle/trunk/fuentes/lib/classes/plugininfo/repository.php

    r136 r1331  
    6868        return new moodle_url('/admin/repository.php');
    6969    }
     70
     71    /**
     72     * Defines if there should be a way to uninstall the plugin via the administration UI.
     73     * @return boolean
     74     */
     75    public function is_uninstall_allowed() {
     76        if ($this->name === 'upload' || $this->name === 'coursefiles' || $this->name === 'user' || $this->name === 'recent') {
     77            return false;
     78        } else {
     79            return true;
     80        }
     81    }
     82
     83    /**
     84     * Pre-uninstall hook.
     85     * This is intended for disabling of plugin, some DB table purging, etc.
     86     * Converts all linked files to standard files when repository is removed
     87     * and cleans up all records in the DB for that repository.
     88     */
     89    public function uninstall_cleanup() {
     90        global $CFG;
     91        require_once($CFG->dirroot.'/repository/lib.php');
     92
     93        $repo = \repository::get_type_by_typename($this->name);
     94        if ($repo) {
     95            $repo->delete(true);
     96        }
     97
     98        parent::uninstall_cleanup();
     99    }
    70100}
  • moodle/trunk/fuentes/lib/classes/plugininfo/theme.php

    r136 r1331  
    3535        global $CFG;
    3636
    37         if ($this->name === 'standard' or $this->name === 'base' or $this->name === 'bootstrapbase') {
     37        if ($this->name === 'base' or $this->name === 'bootstrapbase') {
    3838            // All of these are protected for now.
    3939            return false;
  • moodle/trunk/fuentes/lib/classes/session/manager.php

    r136 r1331  
    378378
    379379        if (!empty($CFG->opentogoogle)) {
    380             if (is_web_crawler()) {
     380            if (\core_useragent::is_web_crawler()) {
    381381                $user = guest_user();
    382382            }
    383             if (!empty($CFG->guestloginbutton) and !$user and !empty($_SERVER['HTTP_REFERER'])) {
     383            $referer = get_local_referer(false);
     384            if (!empty($CFG->guestloginbutton) and !$user and !empty($referer)) {
    384385                // Automatically log in users coming from search engine results.
    385                 if (strpos($_SERVER['HTTP_REFERER'], 'google') !== false ) {
     386                if (strpos($referer, 'google') !== false ) {
    386387                    $user = guest_user();
    387                 } else if (strpos($_SERVER['HTTP_REFERER'], 'altavista') !== false ) {
     388                } else if (strpos($referer, 'altavista') !== false ) {
    388389                    $user = guest_user();
    389390                }
     
    609610     * Terminate all sessions of given user unconditionally.
    610611     * @param int $userid
    611      */
    612     public static function kill_user_sessions($userid) {
     612     * @param string $keepsid keep this sid if present
     613     */
     614    public static function kill_user_sessions($userid, $keepsid = null) {
    613615        global $DB;
    614616
    615617        $sessions = $DB->get_records('sessions', array('userid'=>$userid), 'id DESC', 'id, sid');
    616618        foreach ($sessions as $session) {
     619            if ($keepsid and $keepsid === $session->sid) {
     620                continue;
     621            }
     622            self::kill_session($session->sid);
     623        }
     624    }
     625
     626    /**
     627     * Terminate other sessions of current user depending
     628     * on $CFG->limitconcurrentlogins restriction.
     629     *
     630     * This is expected to be called right after complete_user_login().
     631     *
     632     * NOTE:
     633     *  * Do not use from SSO auth plugins, this would not work.
     634     *  * Do not use from web services because they do not have sessions.
     635     *
     636     * @param int $userid
     637     * @param string $sid session id to be always keep, usually the current one
     638     * @return void
     639     */
     640    public static function apply_concurrent_login_limit($userid, $sid = null) {
     641        global $CFG, $DB;
     642
     643        // NOTE: the $sid parameter is here mainly to allow testing,
     644        //       in most cases it should be current session id.
     645
     646        if (isguestuser($userid) or empty($userid)) {
     647            // This applies to real users only!
     648            return;
     649        }
     650
     651        if (empty($CFG->limitconcurrentlogins) or $CFG->limitconcurrentlogins < 0) {
     652            return;
     653        }
     654
     655        $count = $DB->count_records('sessions', array('userid' => $userid));
     656
     657        if ($count <= $CFG->limitconcurrentlogins) {
     658            return;
     659        }
     660
     661        $i = 0;
     662        $select = "userid = :userid";
     663        $params = array('userid' => $userid);
     664        if ($sid) {
     665            if ($DB->record_exists('sessions', array('sid' => $sid, 'userid' => $userid))) {
     666                $select .= " AND sid <> :sid";
     667                $params['sid'] = $sid;
     668                $i = 1;
     669            }
     670        }
     671
     672        $sessions = $DB->get_records_select('sessions', $select, $params, 'timecreated DESC', 'id, sid');
     673        foreach ($sessions as $session) {
     674            $i++;
     675            if ($i <= $CFG->limitconcurrentlogins) {
     676                continue;
     677            }
    617678            self::kill_session($session->sid);
    618679        }
     
    788849        $event->trigger();
    789850    }
     851
     852    /**
     853     * Add a JS session keepalive to the page.
     854     *
     855     * A JS session keepalive script will be called to update the session modification time every $frequency seconds.
     856     *
     857     * Upon failure, the specified error message will be shown to the user.
     858     *
     859     * @param string $identifier The string identifier for the message to show on failure.
     860     * @param string $component The string component for the message to show on failure.
     861     * @param int $frequency The update frequency in seconds.
     862     * @throws coding_exception IF the frequency is longer than the session lifetime.
     863     */
     864    public static function keepalive($identifier = 'sessionerroruser', $component = 'error', $frequency = null) {
     865        global $CFG, $PAGE;
     866
     867        if ($frequency) {
     868            if ($frequency > $CFG->sessiontimeout) {
     869                // Sanity check the frequency.
     870                throw new \coding_exception('Keepalive frequency is longer than the session lifespan.');
     871            }
     872        } else {
     873            // A frequency of sessiontimeout / 3 allows for one missed request whilst still preserving the session.
     874            $frequency = $CFG->sessiontimeout / 3;
     875        }
     876
     877        // Add the session keepalive script to the list of page output requirements.
     878        $sessionkeepaliveurl = new \moodle_url('/lib/sessionkeepalive_ajax.php');
     879        $PAGE->requires->string_for_js($identifier, $component);
     880        $PAGE->requires->yui_module('moodle-core-checknet', 'M.core.checknet.init', array(array(
     881            // The JS config takes this is milliseconds rather than seconds.
     882            'frequency' => $frequency * 1000,
     883            'message' => array($identifier, $component),
     884            'uri' => $sessionkeepaliveurl->out(),
     885        )));
     886    }
     887
    790888}
  • moodle/trunk/fuentes/lib/classes/session/memcached.php

    r136 r1331  
    4545    /**
    4646     * @var int $lockexpire how long to wait before expiring the lock so that other requests
    47      * may continue execution, ignored if memcached <= 2.1.0.
     47     * may continue execution, ignored if PECL memcached is below version 2.2.0.
    4848     */
    4949    protected $lockexpire = 7200;
     
    8787     */
    8888    public function start() {
    89         // NOTE: memcached <= 2.1.0 expires session locks automatically after max_execution_time,
     89        // NOTE: memcached before 2.2.0 expires session locks automatically after max_execution_time,
    9090        //       this leads to major difference compared to other session drivers that timeout
    9191        //       and stop the second request execution instead.
     
    120120        ini_set('memcached.sess_locking', '1'); // Locking is required!
    121121
    122         // Try to configure lock and expire timeouts - ignored if memcached <=2.1.0.
     122        // Try to configure lock and expire timeouts - ignored if memcached is before version 2.2.0.
    123123        ini_set('memcached.sess_lock_max_wait', $this->acquiretimeout);
    124124        ini_set('memcached.sess_lock_expire', $this->lockexpire);
  • moodle/trunk/fuentes/lib/classes/shutdown_manager.php

    r136 r1331  
    8282            } catch (Exception $e) {
    8383                error_log('Exception ignored in shutdown function '.var_export($callback, true).':'.$e->getMessage());
     84            } catch (Throwable $e) {
     85                // Engine errors in PHP7 throw exceptions of type Throwable (this "catch" will be ignored in PHP5).
     86                error_log('Exception ignored in shutdown function '.var_export($callback, true).':'.$e->getMessage());
    8487            }
    8588        }
  • moodle/trunk/fuentes/lib/classes/string_manager_standard.php

    r136 r1331  
    257257        $deprecated = $this->load_deprecated_strings();
    258258        list($plugintype, $pluginname) = core_component::normalize_component($component);
    259         return isset($deprecated[$identifier . ',' . $plugintype . '_' . $pluginname]);
     259        $normcomponent = $pluginname ? ($plugintype . '_' . $pluginname) : $plugintype;
     260        return isset($deprecated[$identifier . ',' . $normcomponent]);
    260261    }
    261262
     
    384385            if ($this->string_deprecated($identifier, $component)) {
    385386                list($plugintype, $pluginname) = core_component::normalize_component($component);
    386                 debugging("String [{$identifier},{$plugintype}_{$pluginname}] is deprecated. ".
     387                $normcomponent = $pluginname ? ($plugintype . '_' . $pluginname) : $plugintype;
     388                debugging("String [{$identifier},{$normcomponent}] is deprecated. ".
    387389                    'Either you should no longer be using that string, or the string has been incorrectly deprecated, in which case you should report this as a bug. '.
    388390                    'Please refer to https://docs.moodle.org/dev/String_deprecation', DEBUG_DEVELOPER);
     
    531533        $langdirs["$CFG->dirroot/lang/en"] = 'en';
    532534
     535        // We use left to right mark to demark the shortcodes contained in LTR brackets, but we need to do
     536        // this hacky thing to have the utf8 char until we go php7 minimum and can simply put \u200E in
     537        // a double quoted string.
     538        $lrm = json_decode('"\u200E"');
     539
    533540        // Loop through all langs and get info.
    534541        foreach ($langdirs as $lang) {
     
    547554            $string = $this->load_component_strings('langconfig', $lang);
    548555            if (!empty($string['thislanguage'])) {
    549                 $languages[$lang] = $string['thislanguage'].' ('. $lang .')';
     556                $languages[$lang] = $string['thislanguage'].' '.$lrm.'('. $lang .')'.$lrm;
    550557            }
    551558        }
  • moodle/trunk/fuentes/lib/classes/task/delete_incomplete_users_task.php

    r136 r1331  
    6060                    continue;
    6161                }
     62                if ($user->lastname !== '' and $user->firstname !== '' and $user->email !== '') {
     63                    // This can happen on MySQL - see MDL-52831.
     64                    continue;
     65                }
    6266                delete_user($user);
    6367                mtrace(" Deleted not fully setup user $user->username ($user->id)");
  • moodle/trunk/fuentes/lib/classes/task/file_temp_cleanup_task.php

    r136 r1331  
    4747        $tmpdir = $CFG->tempdir;
    4848        // Default to last weeks time.
    49         $time = strtotime('-1 week');
     49        $time = time() - ($CFG->tempdatafoldercleanup * 3600);
    5050
    5151        $dir = new \RecursiveDirectoryIterator($tmpdir);
     
    8989                }
    9090            } else {
    91                 // Return the time modified to the original date.
    92                 touch($node, $modifieddateobject[$node]);
     91                // Return the time modified to the original date only for real files.
     92                if ($iter->isDir() && !$iter->isDot()) {
     93                    touch($node, $modifieddateobject[$node]);
     94                }
    9395            }
    9496        }
  • moodle/trunk/fuentes/lib/classes/task/legacy_plugin_cron_task.php

    r136 r1331  
    8080        // Run all cron jobs for each module.
    8181        mtrace("Starting activity modules");
    82         get_mailer('buffer');
    8382        if ($mods = $DB->get_records_select("modules", "cron > 0 AND ((? - lastcron) > cron) AND visible = 1", array($timenow))) {
    8483            foreach ($mods as $mod) {
     
    106105            }
    107106        }
    108         get_mailer('close');
    109107        mtrace("Finished activity modules");
    110108
  • moodle/trunk/fuentes/lib/classes/task/manager.php

    r136 r1331  
    5656
    5757        $tasks = null;
    58         require_once($file);
     58        include($file);
    5959
    6060        if (!isset($tasks)) {
     
    8686    public static function reset_scheduled_tasks_for_component($componentname) {
    8787        global $DB;
    88         $cronlockfactory = \core\lock\lock_config::get_lock_factory('cron');
    89 
    90         if (!$cronlock = $cronlockfactory->get_lock('core_cron', 10, 60)) {
    91             throw new \moodle_exception('locktimeout');
    92         }
    9388        $tasks = self::load_default_scheduled_tasks_for_component($componentname);
    94 
    95         $tasklocks = array();
     89        $validtasks = array();
     90
    9691        foreach ($tasks as $taskid => $task) {
    9792            $classname = get_class($task);
     
    10095            }
    10196
    102             // If there is an existing task with a custom schedule, do not override it.
    103             $currenttask = self::get_scheduled_task($classname);
    104             if ($currenttask && $currenttask->is_customised()) {
    105                 $tasks[$taskid] = $currenttask;
    106             }
    107 
    108             if (!$lock = $cronlockfactory->get_lock($classname, 10, 60)) {
    109                 // Could not get all the locks required - release all locks and fail.
    110                 foreach ($tasklocks as $tasklock) {
    111                     $tasklock->release();
     97            $validtasks[] = $classname;
     98
     99            if ($currenttask = self::get_scheduled_task($classname)) {
     100                if ($currenttask->is_customised()) {
     101                    // If there is an existing task with a custom schedule, do not override it.
     102                    continue;
    112103                }
    113                 $cronlock->release();
    114                 throw new \moodle_exception('locktimeout');
    115             }
    116             $tasklocks[] = $lock;
    117         }
    118 
    119         // Got a lock on cron and all the tasks for this component, time to reset the config.
    120         $DB->delete_records('task_scheduled', array('component' => $componentname));
    121         foreach ($tasks as $task) {
    122             $record = self::record_from_scheduled_task($task);
    123             $DB->insert_record('task_scheduled', $record);
    124         }
    125 
    126         // Release the locks.
    127         foreach ($tasklocks as $tasklock) {
    128             $tasklock->release();
    129         }
    130 
    131         $cronlock->release();
     104
     105                // Update the record from the default task data.
     106                self::configure_scheduled_task($task);
     107            } else {
     108                // Ensure that the first run follows the schedule.
     109                $task->set_next_run_time($task->get_next_scheduled_time());
     110
     111                // Insert the new task in the database.
     112                $record = self::record_from_scheduled_task($task);
     113                $DB->insert_record('task_scheduled', $record);
     114            }
     115        }
     116
     117        // Delete any task that is not defined in the component any more.
     118        $sql = "component = :component";
     119        $params = array('component' => $componentname);
     120        if (!empty($validtasks)) {
     121            list($insql, $inparams) = $DB->get_in_or_equal($validtasks, SQL_PARAMS_NAMED, 'param', false);
     122            $sql .= ' AND classname ' . $insql;
     123            $params = array_merge($params, $inparams);
     124        }
     125        $DB->delete_records_select('task_scheduled', $sql, $params);
    132126    }
    133127
     
    158152    public static function configure_scheduled_task(scheduled_task $task) {
    159153        global $DB;
    160         $cronlockfactory = \core\lock\lock_config::get_lock_factory('cron');
    161 
    162         if (!$cronlock = $cronlockfactory->get_lock('core_cron', 10, 60)) {
    163             throw new \moodle_exception('locktimeout');
    164         }
    165154
    166155        $classname = get_class($task);
    167156        if (strpos($classname, '\\') !== 0) {
    168157            $classname = '\\' . $classname;
    169         }
    170         if (!$lock = $cronlockfactory->get_lock($classname, 10, 60)) {
    171             $cronlock->release();
    172             throw new \moodle_exception('locktimeout');
    173158        }
    174159
     
    180165        $result = $DB->update_record('task_scheduled', $record);
    181166
    182         $lock->release();
    183         $cronlock->release();
    184167        return $result;
    185168    }
     
    479462        $where = "(lastruntime IS NULL OR lastruntime < :timestart1)
    480463                  AND (nextruntime IS NULL OR nextruntime < :timestart2)
    481                   AND disabled = 0";
     464                  AND disabled = 0
     465                  ORDER BY lastruntime, id ASC";
    482466        $params = array('timestart1' => $timestart, 'timestart2' => $timestart);
    483467        $records = $DB->get_records_select('task_scheduled', $where, $params);
     
    503487                if ($plugininfo) {
    504488                    if (($plugininfo->is_enabled() === false) && !$task->get_run_if_component_disabled()) {
    505                         mtrace($task->get_name().' skipped - the component '.$task->get_component().' is disabled');
    506489                        $lock->release();
    507490                        continue;
    508491                    }
     492                }
     493
     494                // Make sure the task data is unchanged.
     495                if (!$DB->record_exists('task_scheduled', (array) $record)) {
     496                    $lock->release();
     497                    continue;
    509498                }
    510499
  • moodle/trunk/fuentes/lib/classes/task/scheduled_task.php

    r136 r1331  
    3636    /** Maximum minute value. */
    3737    const MINUTEMAX = 59;
     38
    3839    /** Minimum hour value. */
    3940    const HOURMIN = 0;
     
    4142    const HOURMAX = 23;
    4243
     44    /** Minimum dayofweek value. */
     45    const DAYOFWEEKMIN = 0;
     46    /** Maximum dayofweek value. */
     47    const DAYOFWEEKMAX = 6;
     48
    4349    /** @var string $hour - Pattern to work out the valid hours */
    4450    private $hour = '*';
     
    174180     */
    175181    public function set_day_of_week($dayofweek) {
     182        if ($dayofweek === 'R') {
     183            $dayofweek = mt_rand(self::DAYOFWEEKMIN, self::DAYOFWEEKMAX);
     184        }
    176185        $this->dayofweek = $dayofweek;
    177186    }
     
    324333
    325334        // We need to change to the server timezone before using php date() functions.
    326         $origtz = date_default_timezone_get();
    327         if (!empty($CFG->timezone) && $CFG->timezone != 99) {
    328             date_default_timezone_set($CFG->timezone);
    329         }
     335        \core_date::set_default_server_timezone();
    330336
    331337        $daysinmonth = date("t");
     
    396402                           $nextvalidyear);
    397403
    398         // We need to change the timezone back so other date functions in moodle do not get confused.
    399         if (!empty($CFG->timezone) && $CFG->timezone != 99) {
    400             date_default_timezone_set($origtz);
    401         }
    402 
    403404        return $nexttime;
    404405    }
  • moodle/trunk/fuentes/lib/classes/task/send_failed_login_notifications_task.php

    r136 r1331  
    2929class send_failed_login_notifications_task extends scheduled_task {
    3030
     31    /** The maximum time period to look back (30 days = 30 * 24 * 3600) */
     32    const NOTIFY_MAXIMUM_TIME = 2592000;
     33
    3134    /**
    3235     * Get a descriptive name for this task (shown to admins).
     
    5154        $recip = get_users_from_config($CFG->notifyloginfailures, 'moodle/site:config');
    5255
    53         if (empty($CFG->lastnotifyfailure)) {
    54             $CFG->lastnotifyfailure = 0;
     56        // Do not look back more than 1 month to avoid crashes due to huge number of records.
     57        $maximumlastnotifytime = time() - self::NOTIFY_MAXIMUM_TIME;
     58        if (empty($CFG->lastnotifyfailure) || ($CFG->lastnotifyfailure < $maximumlastnotifytime)) {
     59            $CFG->lastnotifyfailure = $maximumlastnotifytime;
    5560        }
    5661
     
    6873        // and insert them into the cache_flags temp table.
    6974        $logmang = get_log_manager();
    70         $readers = $logmang->get_readers('\core\log\sql_internal_reader');
     75        $readers = $logmang->get_readers('\core\log\sql_internal_table_reader');
    7176        $reader = reset($readers);
    7277        $readername = key($readers);
     
    111116        // Now, select all the login error logged records belonging to the ips and infos
    112117        // since lastnotifyfailure, that we have stored in the cache_flags table.
     118        $namefields = get_all_user_name_fields(true, 'u');
    113119        $sql = "SELECT * FROM (
    114                         SELECT l.*, u.username
     120                        SELECT l.*, u.username, $namefields
    115121                          FROM {" . $logtable . "} l
    116122                          JOIN {cache_flags} cf ON l.ip = cf.name
     
    120126                               AND cf.flagtype = 'login_failure_by_ip'
    121127                    UNION ALL
    122                         SELECT l.*, u.username
     128                        SELECT l.*, u.username, $namefields
    123129                          FROM {" . $logtable . "} l
    124130                          JOIN {cache_flags} cf ON l.userid = " . $DB->sql_cast_char2int('cf.name') . "
     
    142148                $other = unserialize($log->other);
    143149                $a->info = empty($other['username']) ? '' : $other['username'];
     150                $a->name = get_string('unknownuser');
    144151            } else {
    145152                $a->info = $log->username;
     153                $a->name = fullname($log);
    146154            }
    147155            $a->ip = $log->ip;
  • moodle/trunk/fuentes/lib/classes/update/checker.php

    r136 r1331  
    8383
    8484    /**
    85      * Is automatic deployment enabled?
     85     * Is checking for available updates enabled?
     86     *
     87     * The feature is enabled unless it is prohibited via config.php.
     88     * If enabled, the button for manual checking for available updates is
     89     * displayed at admin screens. To perform scheduled checks for updates
     90     * automatically, the admin setting $CFG->updateautocheck has to be enabled.
    8691     *
    8792     * @return bool
     
    9095        global $CFG;
    9196
    92         // The feature can be prohibited via config.php.
    93         return empty($CFG->disableupdateautodeploy);
     97        return empty($CFG->disableupdatenotifications);
    9498    }
    9599
     
    193197        global $CFG;
    194198
    195         if (!$this->cron_autocheck_enabled()) {
     199        if (!$this->enabled() or !$this->cron_autocheck_enabled()) {
    196200            $this->cron_mtrace('Automatic check for available updates not enabled, skipping.');
    197201            return;
     
    263267        }
    264268
    265         if (empty($response['apiver']) or $response['apiver'] !== '1.2') {
     269        if (empty($response['apiver']) or $response['apiver'] !== '1.3') {
    266270            throw new checker_exception('err_response_format_version', $response['apiver']);
    267271        }
     
    410414            return $CFG->config_php_settings['alternativeupdateproviderurl'];
    411415        } else {
    412             return 'https://download.moodle.org/api/1.2/updates.php';
     416            return 'https://download.moodle.org/api/1.3/updates.php';
    413417        }
    414418    }
     
    755759            $a = array('url' => html_writer::link($CFG->wwwroot.'/'.$CFG->admin.'/index.php', $CFG->wwwroot.'/'.$CFG->admin.'/index.php'));
    756760            $html .= html_writer::tag('p', get_string('updateavailabledetailslink', 'core_admin', $a)) . PHP_EOL;
     761
     762            $text .= PHP_EOL . get_string('updateavailablerecommendation', 'core_admin') . PHP_EOL;
     763            $html .= html_writer::tag('p', get_string('updateavailablerecommendation', 'core_admin')) . PHP_EOL;
    757764        }
    758765
     
    786793
    787794        $a = array('siteurl' => $CFG->wwwroot);
    788         $text .= get_string('updatenotificationfooter', 'core_admin', $a) . PHP_EOL;
     795        $text .= PHP_EOL . get_string('updatenotificationfooter', 'core_admin', $a) . PHP_EOL;
    789796        $a = array('siteurl' => html_writer::link($CFG->wwwroot, $CFG->wwwroot));
    790797        $html .= html_writer::tag('footer', html_writer::tag('p', get_string('updatenotificationfooter', 'core_admin', $a),
  • moodle/trunk/fuentes/lib/classes/user.php

    r136 r1331  
    5050    public static $supportuser = false;
    5151
     52    /** @var array store user fields properties cache. */
     53    protected static $propertiescache = null;
     54
    5255    /**
    5356     * Return user object from db or create noreply or support user,
     
    7073        switch ($userid) {
    7174            case self::NOREPLY_USER:
    72                 return self::get_noreply_user($strictness);
     75                return self::get_noreply_user();
    7376                break;
    7477            case self::SUPPORT_USER:
    75                 return self::get_support_user($strictness);
     78                return self::get_support_user();
    7679                break;
    7780            default:
     
    239242        }
    240243    }
     244
     245    /**
     246     * Check if the given user is an active user in the site.
     247     *
     248     * @param  stdClass  $user         user object
     249     * @param  boolean $checksuspended whether to check if the user has the account suspended
     250     * @param  boolean $checknologin   whether to check if the user uses the nologin auth method
     251     * @throws moodle_exception
     252     * @since  Moodle 3.0
     253     */
     254    public static function require_active_user($user, $checksuspended = false, $checknologin = false) {
     255
     256        if (!self::is_real_user($user->id)) {
     257            throw new moodle_exception('invaliduser', 'error');
     258        }
     259
     260        if ($user->deleted) {
     261            throw new moodle_exception('userdeleted');
     262        }
     263
     264        if (empty($user->confirmed)) {
     265            throw new moodle_exception('usernotconfirmed', 'moodle', '', $user->username);
     266        }
     267
     268        if (isguestuser($user)) {
     269            throw new moodle_exception('guestsarenotallowed', 'error');
     270        }
     271
     272        if ($checksuspended and $user->suspended) {
     273            throw new moodle_exception('suspended', 'auth');
     274        }
     275
     276        if ($checknologin and $user->auth == 'nologin') {
     277            throw new moodle_exception('suspended', 'auth');
     278        }
     279    }
     280
     281    /**
     282     * Definition of user profile fields and the expected parameter type for data validation.
     283     *
     284     * @return void
     285     */
     286    protected static function fill_properties_cache() {
     287
     288        if (self::$propertiescache !== null) {
     289            return;
     290        }
     291
     292        // Array of user fields properties and expected parameters.
     293        // Every new field on the user table should be added here otherwise it won't be validated.
     294        $fields = array();
     295        $fields['id'] = array('type' => PARAM_INT);
     296        $fields['auth'] = array('type' => PARAM_NOTAGS);
     297        $fields['confirmed'] = array('type' => PARAM_BOOL);
     298        $fields['policyagreed'] = array('type' => PARAM_BOOL);
     299        $fields['deleted'] = array('type' => PARAM_BOOL);
     300        $fields['suspended'] = array('type' => PARAM_BOOL);
     301        $fields['mnethostid'] = array('type' => PARAM_BOOL);
     302        $fields['username'] = array('type' => PARAM_USERNAME);
     303        $fields['password'] = array('type' => PARAM_NOTAGS);
     304        $fields['idnumber'] = array('type' => PARAM_NOTAGS);
     305        $fields['firstname'] = array('type' => PARAM_NOTAGS);
     306        $fields['lastname'] = array('type' => PARAM_NOTAGS);
     307        $fields['surname'] = array('type' => PARAM_NOTAGS);
     308        $fields['email'] = array('type' => PARAM_RAW_TRIMMED);
     309        $fields['emailstop'] = array('type' => PARAM_INT);
     310        $fields['icq'] = array('type' => PARAM_NOTAGS);
     311        $fields['skype'] = array('type' => PARAM_NOTAGS);
     312        $fields['aim'] = array('type' => PARAM_NOTAGS);
     313        $fields['yahoo'] = array('type' => PARAM_NOTAGS);
     314        $fields['msn'] = array('type' => PARAM_NOTAGS);
     315        $fields['phone1'] = array('type' => PARAM_NOTAGS);
     316        $fields['phone2'] = array('type' => PARAM_NOTAGS);
     317        $fields['institution'] = array('type' => PARAM_TEXT);
     318        $fields['department'] = array('type' => PARAM_TEXT);
     319        $fields['address'] = array('type' => PARAM_TEXT);
     320        $fields['city'] = array('type' => PARAM_TEXT);
     321        $fields['country'] = array('type' => PARAM_TEXT);
     322        $fields['lang'] = array('type' => PARAM_TEXT);
     323        $fields['calendartype'] = array('type' => PARAM_NOTAGS);
     324        $fields['theme'] = array('type' => PARAM_NOTAGS);
     325        $fields['timezones'] = array('type' => PARAM_TEXT);
     326        $fields['firstaccess'] = array('type' => PARAM_INT);
     327        $fields['lastaccess'] = array('type' => PARAM_INT);
     328        $fields['lastlogin'] = array('type' => PARAM_INT);
     329        $fields['currentlogin'] = array('type' => PARAM_INT);
     330        $fields['lastip'] = array('type' => PARAM_NOTAGS);
     331        $fields['secret'] = array('type' => PARAM_TEXT);
     332        $fields['picture'] = array('type' => PARAM_INT);
     333        $fields['url'] = array('type' => PARAM_URL);
     334        $fields['description'] = array('type' => PARAM_CLEANHTML);
     335        $fields['descriptionformat'] = array('type' => PARAM_INT);
     336        $fields['mailformat'] = array('type' => PARAM_INT);
     337        $fields['maildigest'] = array('type' => PARAM_INT);
     338        $fields['maildisplay'] = array('type' => PARAM_INT);
     339        $fields['autosubscribe'] = array('type' => PARAM_INT);
     340        $fields['trackforums'] = array('type' => PARAM_INT);
     341        $fields['timecreated'] = array('type' => PARAM_INT);
     342        $fields['timemodified'] = array('type' => PARAM_INT);
     343        $fields['trustbitmask'] = array('type' => PARAM_INT);
     344        $fields['imagealt'] = array('type' => PARAM_TEXT);
     345        $fields['lastnamephonetic'] = array('type' => PARAM_NOTAGS);
     346        $fields['firstnamephonetic'] = array('type' => PARAM_NOTAGS);
     347        $fields['middlename'] = array('type' => PARAM_NOTAGS);
     348        $fields['alternatename'] = array('type' => PARAM_NOTAGS);
     349
     350        self::$propertiescache = $fields;
     351    }
     352
     353    /**
     354     * Get properties of a user field.
     355     *
     356     * @param string $property property name to be retrieved.
     357     * @throws coding_exception if the requested property name is invalid.
     358     * @return array the property definition.
     359     */
     360    public static function get_property_definition($property) {
     361
     362        self::fill_properties_cache();
     363
     364        if (!array_key_exists($property, self::$propertiescache)) {
     365            throw new coding_exception('Invalid property requested.');
     366        }
     367
     368        return self::$propertiescache[$property];
     369    }
     370
     371    /**
     372     * Clean the properties cache.
     373     *
     374     * During unit tests we need to be able to reset all caches so that each new test starts in a known state.
     375     * Intended for use only for testing, phpunit calls this before every test.
     376     */
     377    public static function reset_caches() {
     378        self::$propertiescache = null;
     379    }
    241380}
  • moodle/trunk/fuentes/lib/classes/useragent.php

    r136 r1331  
    7272        self::DEVICETYPE_LEGACY,
    7373        self::DEVICETYPE_MOBILE,
    74         self::DEVICETYPE_TABLET
     74        self::DEVICETYPE_TABLET,
    7575    );
    7676
     
    177177        }
    178178        if ($this->is_useragent_mobile()) {
    179             $this->devicetype = 'mobile';
     179            $this->devicetype = self::DEVICETYPE_MOBILE;
    180180        } else if ($this->is_useragent_tablet()) {
    181             $this->devicetype = 'tablet';
    182         } else if (substr($this->useragent, 0, 34) === 'Mozilla/4.0 (compatible; MSIE 6.0;') {
    183             // Safe way to check for IE6 and not get false positives for some IE 7/8 users.
    184             $this->devicetype = 'legacy';
     181            $this->devicetype = self::DEVICETYPE_TABLET;
     182        } else if (self::check_ie_version('0') && !self::check_ie_version('7.0')) {
     183            // IE 6 and before are considered legacy.
     184            $this->devicetype = self::DEVICETYPE_LEGACY;
    185185        } else {
    186186            $this->devicetype = self::DEVICETYPE_DEFAULT;
     
    202202    /**
    203203     * Returns true if the user appears to be on a tablet.
     204     *
    204205     * @return int
    205206     */
     
    207208        $tabletregex = '/Tablet browser|android|iPad|iProd|GT-P1000|GT-I9000|SHW-M180S|SGH-T849|SCH-I800|Build\/ERE27|sholest/i';
    208209        return (preg_match($tabletregex, $this->useragent));
     210    }
     211
     212    /**
     213     * Whether the user agent relates to a web crawler.
     214     * This includes all types of web crawler.
     215     * @return bool
     216     */
     217    protected function is_useragent_web_crawler() {
     218        $regex = '/Googlebot|google\.com|Yahoo! Slurp|\[ZSEBOT\]|msnbot|bingbot|BingPreview|Yandex|AltaVista|Baiduspider|Teoma/i';
     219        return (preg_match($regex, $this->useragent));
    209220    }
    210221
     
    472483
    473484    /**
     485     * Checks the user agent is Edge (of any version).
     486     *
     487     * @return bool true if Edge
     488     */
     489    public static function is_edge() {
     490        return self::check_edge_version();
     491    }
     492
     493    /**
     494     * Check the User Agent for the version of Edge.
     495     *
     496     * @param string|int $version A version to check for, returns true if its equal to or greater than that specified.
     497     * @return bool
     498     */
     499    public static function check_edge_version($version = null) {
     500        $useragent = self::get_user_agent_string();
     501
     502        if ($useragent === false) {
     503            // No User Agent found.
     504            return false;
     505        }
     506
     507        if (strpos($useragent, 'Edge/') === false) {
     508            // Edge was not found in the UA - this is not Edge.
     509            return false;
     510        }
     511
     512        if (empty($version)) {
     513            // No version to check.
     514            return true;
     515        }
     516
     517        // Find the version.
     518        // Edge versions are always in the format:
     519        //      Edge/<version>.<OS build number>
     520        preg_match('%Edge/([\d]+)\.(.*)$%', $useragent, $matches);
     521
     522        // Just to be safe, round the version being tested.
     523        // Edge only uses integer versions - the second component is the OS build number.
     524        $version = round($version);
     525
     526        // Check whether the version specified is >= the version found.
     527        return version_compare($matches[1], $version, '>=');
     528    }
     529
     530    /**
    474531     * Checks the user agent is IE (of any version).
    475532     *
     
    506563            return false;
    507564        }
     565
    508566        $compatview = false;
    509567        // IE8 and later versions may pretend to be IE7 for intranet sites, use Trident version instead,
     
    686744            return false;
    687745        }
    688         if (strpos($useragent, 'Chrome')) { // Reject chrome browsers - it needs to be tested explicitly.
     746        if (strpos($useragent, 'Chrome')) {
     747            // Reject chrome browsers - it needs to be tested explicitly.
     748            // This will also reject Edge, which pretends to be both Chrome, and Safari.
    689749            return false;
    690750        }
     
    757817            return false;
    758818        }
    759         if (strpos($useragent, 'Linux; U; Android') === false) {
     819        if (strpos($useragent, 'Android') === false) {
    760820            return false;
    761821        }
     
    807867        }
    808868        return false;
     869    }
     870
     871    /**
     872     * Checks if the user agent is MS Word.
     873     * Not perfect, as older versions of Word use standard IE6/7 user agents without any identifying traits.
     874     *
     875     * @return bool true if user agent could be identified as MS Word.
     876     */
     877    public static function is_msword() {
     878        $useragent = self::get_user_agent_string();
     879        if (!preg_match('/(\bWord\b|ms-office|MSOffice|Microsoft Office)/i', $useragent)) {
     880            return false;
     881        } else if (strpos($useragent, 'Outlook') !== false) {
     882            return false;
     883        } else if (strpos($useragent, 'Meridio') !== false) {
     884            return false;
     885        }
     886        // It's Office, not Outlook and not Meridio - so it's probably Word, but we can't really be sure in most cases.
     887        return true;
    809888    }
    810889
     
    866945                // Can't be sure, just say no.
    867946                $instance->supportssvg = false;
    868             } else if (self::is_ie() and !self::check_ie_version('9')) {
     947            } else if (self::check_ie_version('0') and !self::check_ie_version('9')) {
    869948                // IE < 9 doesn't support SVG. Say no.
    870949                $instance->supportssvg = false;
     
    893972    public static function supports_json_contenttype() {
    894973        // Modern browsers other than IE correctly supports 'application/json' media type.
    895         if (!self::is_ie()) {
     974        if (!self::check_ie_version('0')) {
    896975            return true;
    897976        }
     
    908987        return false;
    909988    }
     989
     990    /**
     991     * Returns true if the client appears to be some kind of web crawler.
     992     * This may include other types of crawler.
     993     *
     994     * @return bool
     995     */
     996    public static function is_web_crawler() {
     997        $instance = self::instance();
     998        return (bool) $instance->is_useragent_web_crawler();
     999    }
    9101000}
Note: See TracChangeset for help on using the changeset viewer.