Merge branch 'MDL-63001_master' of git://github.com/markn86/moodle
authorDavid Monllao <davidm@moodle.com>
Tue, 31 Jul 2018 11:52:49 +0000 (13:52 +0200)
committerDavid Monllao <davidm@moodle.com>
Tue, 31 Jul 2018 11:52:49 +0000 (13:52 +0200)
73 files changed:
admin/tool/policy/tests/behat/consent.feature
auth/shibboleth/auth.php
auth/shibboleth/lang/en/auth_shibboleth.php
auth/shibboleth/lib.php [new file with mode: 0644]
auth/shibboleth/settings.php
auth/tests/behat/displayloginfailures.feature
auth/tests/behat/validateagedigitalconsentmap.feature
backup/moodle2/tests/behat/import_multiple_times.feature
blocks/recent_activity/block_recent_activity.php
blocks/recent_activity/classes/task/cleanup.php [new file with mode: 0644]
blocks/recent_activity/db/tasks.php [moved from lib/classes/task/events_cron_task.php with 51% similarity]
blocks/recent_activity/lang/en/block_recent_activity.php
blocks/recent_activity/version.php
blocks/rss_client/block_rss_client.php
blocks/rss_client/classes/task/refreshfeeds.php [new file with mode: 0644]
blocks/rss_client/db/tasks.php [new file with mode: 0644]
blocks/rss_client/lang/en/block_rss_client.php
blocks/rss_client/tests/cron_test.php
blocks/rss_client/version.php
calendar/classes/local/event/container.php
calendar/classes/local/event/strategies/raw_event_retrieval_strategy.php
calendar/lib.php
calendar/tests/lib_test.php
calendar/tests/raw_event_retrieval_strategy_test.php
calendar/upgrade.txt
enrol/paypal/ipn.php
error/index.php
grade/edit/letter/index.php
lib/adminlib.php
lib/classes/event/base.php
lib/coursecatlib.php
lib/db/tasks.php
lib/deprecatedlib.php
lib/eventslib.php [deleted file]
lib/gradelib.php
lib/medialib.php [deleted file]
lib/outputrenderers.php
lib/phpunit/classes/util.php
lib/setup.php
lib/tests/coursecatlib_test.php
lib/tests/event_test.php
lib/tests/eventslib_test.php [deleted file]
lib/tests/gradelib_test.php
lib/tests/medialib_test.php
lib/upgrade.txt
lib/upgradelib.php
media/classes/player.php
media/upgrade.txt
message/lib.php
mod/assign/lib.php
mod/assign/locallib.php
mod/assign/submission/file/locallib.php
mod/assign/tests/lib_test.php
mod/assign/tests/locallib_test.php
mod/assign/upgrade.txt
mod/feedback/lib.php
mod/forum/lib.php
mod/lesson/essay.php
mod/lesson/locallib.php
mod/quiz/lib.php
mod/quiz/locallib.php
mod/scorm/view.php
mod/upgrade.txt
question/templates/tag_condition.mustache
search/classes/base.php
search/classes/document.php
theme/bootstrapbase/less/moodle/core.less
theme/bootstrapbase/style/moodle.css
user/classes/search/user.php
user/editlib.php
user/tests/behat/view_participants.feature
user/tests/search_test.php
user/upgrade.txt [new file with mode: 0644]

index d345b23..2b9b026 100644 (file)
@@ -364,7 +364,7 @@ Feature: User must accept policy managed by this plugin when logging in and sign
     And I log out
     # Create new policy document.
     And I log in as "admin"
-    And I navigate to "Manage policies" node in "Site administration > Privacy and policies"
+    And I navigate to "Manage policies" node in "Site administration > Users > Privacy and policies"
     And I should see "Policies and agreements"
     And I should see "New policy"
     And I follow "New policy"
@@ -419,7 +419,7 @@ Feature: User must accept policy managed by this plugin when logging in and sign
     And I log out
     # Create new version of the policy document.
     And I log in as "admin"
-    And I navigate to "Manage policies" node in "Site administration > Privacy and policies"
+    And I navigate to "Manage policies" node in "Site administration > Users > Privacy and policies"
     When I follow "Actions"
     Then I should see "View"
     And I should see "Edit"
index fe6244c..39ca032 100644 (file)
@@ -277,6 +277,32 @@ class auth_plugin_shibboleth extends auth_plugin_base {
             return;
         }
     }
+
+    /**
+     * Return a list of identity providers to display on the login page.
+     *
+     * @param string $wantsurl The requested URL.
+     * @return array List of arrays with keys url, iconurl and name.
+     */
+    public function loginpage_idp_list($wantsurl) {
+        $config = get_config('auth_shibboleth');
+        $result = [];
+
+        // Before displaying the button check that Shibboleth is set-up correctly.
+        if (empty($config->user_attribute)) {
+            return $result;
+        }
+
+        $url = new moodle_url('/auth/shibboleth/index.php');
+        $iconurl = moodle_url::make_pluginfile_url(context_system::instance()->id,
+                                                   'auth_shibboleth',
+                                                   'logo',
+                                                   null,
+                                                   '/',
+                                                   $config->auth_logo);
+        $result[] = ['url' => $url, 'iconurl' => $iconurl, 'name' => $config->login_name];
+        return $result;
+    }
 }
 
 
index 659e4d1..8ef9ec1 100644 (file)
@@ -25,6 +25,8 @@
 
 $string['auth_shib_auth_method'] = 'Authentication method name';
 $string['auth_shib_auth_method_description'] = 'Provide a name for the Shibboleth authentication method that is familiar to your users. This could be the name of your Shibboleth federation, e.g. <tt>SWITCHaai Login</tt> or <tt>InCommon Login</tt> or similar.';
+$string['auth_shib_auth_logo'] = 'Authentication method logo';
+$string['auth_shib_auth_logo_description'] = 'Provide a logo for the Shibboleth authentication method that is familiar to your users. This could be the logo of your Shibboleth federation, e.g. <tt>SWITCHaai Login</tt> or <tt>InCommon Login</tt> or similar.';
 $string['auth_shib_contact_administrator'] = 'In case you are not associated with the given organizations and you need access to a course on this server, please contact the <a href="mailto:{$a}">Moodle Administrator</a>.';
 $string['auth_shibbolethdescription'] = 'Using this method users are created and authenticated using <a href="http://shibboleth.internet2.edu/">Shibboleth</a>.<br />Be sure to read the <a href="../auth/shibboleth/README.txt">README</a> for Shibboleth on how to set up your Moodle with Shibboleth';
 $string['auth_shibboleth_errormsg'] = 'Please select the organization you are member of!';
diff --git a/auth/shibboleth/lib.php b/auth/shibboleth/lib.php
new file mode 100644 (file)
index 0000000..f8ac757
--- /dev/null
@@ -0,0 +1,64 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This file contains the hooks for the Shibboleth authentication module.
+ *
+ * @package auth_shibboleth
+ * @copyright 2018 Fabrice Ménard
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+/**
+ * Serves the logo file settings.
+ *
+ * @param stdClass $course course object
+ * @param stdClass $cm course module object
+ * @param stdClass $context context object
+ * @param string $filearea file area
+ * @param array $args extra arguments
+ * @param bool $forcedownload whether or not force download
+ * @param array $options additional options affecting the file serving
+ * @return bool false if file not found, does not return if found - justsend the file
+ */
+function auth_shibboleth_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options=array()) {
+    if ($context->contextlevel != CONTEXT_SYSTEM) {
+        return false;
+    }
+
+    if ($filearea !== 'logo' ) {
+        return false;
+    }
+
+    $itemid = 0;
+
+    $filename = array_pop($args);
+    if (!$args) {
+        $filepath = '/';
+    } else {
+        $filepath = '/'.implode('/', $args).'/';
+    }
+
+    $fs = get_file_storage();
+    $file = $fs->get_file($context->id, 'auth_shibboleth', $filearea, $itemid, $filepath, $filename);
+    if (!$file) {
+        return false;
+    }
+
+    send_stored_file($file, null, 0, $forcedownload, $options);
+}
index c901378..e4b4c3a 100644 (file)
@@ -63,6 +63,11 @@ if ($ADMIN->fulltree) {
             get_string('auth_shib_auth_method', 'auth_shibboleth'),
             get_string('auth_shib_auth_method_description', 'auth_shibboleth'), 'Shibboleth Login', PARAM_RAW_TRIMMED));
 
+    // Authentication method logo.
+    $settings->add(new admin_setting_configstoredfile('auth_shibboleth/auth_logo',
+                get_string('auth_shib_auth_logo', 'auth_shibboleth'),
+                get_string('auth_shib_auth_logo_description', 'auth_shibboleth'), 'logo', 0, ['accepted_types' => ['image']]));
+
     // Login directions.
     $settings->add(new admin_setting_configtextarea('auth_shibboleth/auth_instructions',
             get_string('auth_shib_instructions_key', 'auth_shibboleth'),
index 50b9ec6..4b6e2a9 100644 (file)
@@ -25,11 +25,11 @@ Feature: Test the 'showlogfailures' feature works.
     And I set the field "Password" to "teacher1"
     And I press "Log in"
     # Confirm the notices are displayed.
-    Then I should see "1 failed logins since your last login" in the "nav.navbar" "css_element"
+    Then I should see "1 failed logins since your last login" in the ".navbar" "css_element"
     And I should see "1 failed logins since your last login" in the "page-footer" "region"
     # Confirm the notices disappear when navigating to another page.
     And I am on homepage
-    And I should not see "1 failed logins since your last login" in the "nav.navbar" "css_element"
+    And I should not see "1 failed logins since your last login" in the ".navbar" "css_element"
     And I should not see "1 failed logins since your last login" in the "page-footer" "region"
 
   # Given the user has at least one failed login attempt, when they login, then they should see both header and footer notices.
@@ -46,10 +46,10 @@ Feature: Test the 'showlogfailures' feature works.
     And I set the field "Password" to "admin"
     And I press "Log in"
     # Confirm the notices are displayed.
-    Then I should see "1 failed logins since your last login" in the "nav.navbar" "css_element"
+    Then I should see "1 failed logins since your last login" in the ".navbar" "css_element"
     And I should see "1 failed logins since your last login (Logs)" in the "page-footer" "region"
     # Confirm that the link works and that the notices disappear when navigating to another page.
     And I click on "Logs" "link" in the "page-footer" "region"
     And I should see "User login failed" in the "table.reportlog" "css_element"
-    And I should not see "1 failed logins since your last login" in the "nav.navbar" "css_element"
+    And I should not see "1 failed logins since your last login" in the ".navbar" "css_element"
     And I should not see "1 failed logins since your last login (Logs)" in the "page-footer" "region"
index 9fe1753..7381991 100644 (file)
@@ -6,7 +6,7 @@ Feature: Test validation of 'Age of digital consent' setting.
 
   Background:
     Given I log in as "admin"
-    And I navigate to "Privacy settings" node in "Site administration > Privacy and policies"
+    And I navigate to "Privacy settings" node in "Site administration > Users > Privacy and policies"
 
   Scenario: Admin provides valid value for 'Age of digital consent'.
     Given I set the field "s__agedigitalconsentmap" to multiline:
index ccf6dd1..98d495f 100644 (file)
@@ -27,10 +27,10 @@ Feature: Import course's content's twice
     And I log in as "teacher1"
 
   Scenario: Import course's contents to another course
-    Given I am on "Course 2" course homepage with editing mode on
+    Given I am on "Course 2" course homepage
     And I should not see "Online users"
     And I should not see "Test quiz"
-    And  I import "Course 1" course into "Course 2" course using this options:
+    And I import "Course 1" course into "Course 2" course using this options:
     And I am on "Course 2" course homepage
     And I should see "Online users"
     And I should see "Test quiz"
index 6a46c36..0424b73 100644 (file)
@@ -228,16 +228,6 @@ class block_recent_activity extends block_base {
         return array('all' => true, 'my' => false, 'tag' => false);
     }
 
-    /**
-     * Remove old entries from table block_recent_activity
-     */
-    public function cron() {
-        global $DB;
-        // Those entries will never be displayed as RECENT anyway.
-        $DB->delete_records_select('block_recent_activity', 'timecreated < ?',
-                array(time() - COURSE_MAX_RECENT_PERIOD));
-    }
-
     /**
      * Migrates entries from table {log} into {block_recent_activity}
      *
diff --git a/blocks/recent_activity/classes/task/cleanup.php b/blocks/recent_activity/classes/task/cleanup.php
new file mode 100644 (file)
index 0000000..c3f34c7
--- /dev/null
@@ -0,0 +1,58 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Task for updating RSS feeds for rss client block
+ *
+ * @package   block_recent_activity
+ * @author    Farhan Karmali <farhan6318@gmail.com>
+ * @copyright Farhan Karmali 2018
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace block_recent_activity\task;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Task for updating RSS feeds for rss client block
+ *
+ * @package   block_recent_activity
+ * @author    Farhan Karmali <farhan6318@gmail.com>
+ * @copyright Farhan Karmali 2018
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class cleanup extends \core\task\scheduled_task {
+
+    /**
+     * Name for this task.
+     *
+     * @return string
+     */
+    public function get_name() {
+        return get_string('cleanuptask', 'block_recent_activity');
+    }
+
+    /**
+     * Remove old entries from table block_recent_activity
+     */
+    public function execute() {
+        global $DB;
+        // Those entries will never be displayed as RECENT anyway.
+        $DB->delete_records_select('block_recent_activity', 'timecreated < ?',
+            array(time() - COURSE_MAX_RECENT_PERIOD));
+    }
+}
similarity index 51%
rename from lib/classes/task/events_cron_task.php
rename to blocks/recent_activity/db/tasks.php
index 07f8bae..0da9677 100644 (file)
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
 /**
- * A scheduled task.
- *
- * @package    core
- * @copyright  2013 onwards Martin Dougiamas  http://dougiamas.com
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * Task definition for block_recent_activity.
+ * @author    Farhan Karmali <farhan6318@gmail.com>
+ * @copyright Farhan Karmali 2018
+ * @package   block_recent_activity
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-namespace core\task;
 
-/**
- * Simple task to run the events cron.
- */
-class events_cron_task extends scheduled_task {
-
-    /**
-     * Get a descriptive name for this task (shown to admins).
-     *
-     * @return string
-     */
-    public function get_name() {
-        return get_string('taskeventscron', 'admin');
-    }
+defined('MOODLE_INTERNAL') || die();
 
-    /**
-     * Do the job.
-     * Throw exceptions on errors (the job will be retried).
-     */
-    public function execute() {
-        events_cron();
-    }
+$tasks = array(
+    array(
+        'classname' => '\block_recent_activity\task\cleanup',
+        'blocking' => 0,
+        'minute' => 'R',
+        'hour' => 'R',
+        'day' => '*',
+        'month' => '*',
+        'dayofweek' => '*',
+        'disabled' => 0
+    )
+);
 
-}
index defbc88..c77dca2 100644 (file)
@@ -23,6 +23,7 @@
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
+$string['cleanuptask'] = 'Cleanup task for recent activity block';
 $string['pluginname'] = 'Recent activity';
 $string['privacy:metadata'] = 'The recent activity block contains a cache of data stored elsewhere in Moodle.';
 $string['privacy:metadata:block_recent_activity'] = 'Temporary log of recent teacher activity. Removed after two days';
index 1943157..48711ce 100644 (file)
@@ -24,7 +24,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2018051400;        // The current plugin version (Date: YYYYMMDDXX)
+$plugin->version   = 2018052900;        // The current plugin version (Date: YYYYMMDDXX)
 $plugin->requires  = 2018050800;        // Requires this Moodle version
 $plugin->component = 'block_recent_activity'; // Full name of the plugin (used for diagnostics)
-$plugin->cron      = 24*3600;           // Cron interval 1 day.
\ No newline at end of file
index bc6d7c5..2a2fd78 100644 (file)
         }
     }
 
-    /**
-     * cron - goes through all the feeds. If the feed has a skipuntil value
-     * that is less than the current time cron will attempt to retrieve it
-     * with the cache duration set to 0 in order to force the retrieval of
-     * the item and refresh the cache.
-     *
-     * If a feed fails then the skipuntil time of that feed is set to be
-     * later than the next expected cron time. The amount of time will
-     * increase each time the fetch fails until the maximum is reached.
-     *
-     * If a feed that has been failing is successfully retrieved it will
-     * go back to being handled as though it had never failed.
-     *
-     * CRON should therefor process requests for permanently broken RSS
-     * feeds infrequently, and temporarily unavailable feeds will be tried
-     * less often until they become available again.
-     *
-     * @return boolean Always returns true
-     */
-    function cron() {
-        global $CFG, $DB;
-        require_once($CFG->libdir.'/simplepie/moodle_simplepie.php');
-
-        // Get the legacy cron time, strangely the cron property of block_base
-        // does not seem to get set. This means we must retrive it here.
-        $this->cron = $DB->get_field('block', 'cron', array('name' => 'rss_client'));
-
-        // We are going to measure execution times
-        $starttime =  microtime();
-        $starttimesec = time();
-
-        // Fetch all site feeds.
-        $rs = $DB->get_recordset('block_rss_client');
-        $counter = 0;
-        mtrace('');
-        foreach ($rs as $rec) {
-            mtrace('    ' . $rec->url . ' ', '');
-
-            // Skip feed if it failed recently.
-            if ($starttimesec < $rec->skipuntil) {
-                mtrace('skipping until ' . userdate($rec->skipuntil));
-                continue;
-            }
-
-            // Fetch the rss feed, using standard simplepie caching
-            // so feeds will be renewed only if cache has expired
-            core_php_time_limit::raise(60);
-
-            $feed =  new moodle_simplepie();
-            // set timeout for longer than normal to be agressive at
-            // fetching feeds if possible..
-            $feed->set_timeout(40);
-            $feed->set_cache_duration(0);
-            $feed->set_feed_url($rec->url);
-            $feed->init();
-
-            if ($feed->error()) {
-                // Skip this feed (for an ever-increasing time if it keeps failing).
-                $rec->skiptime = $this->calculate_skiptime($rec->skiptime);
-                $rec->skipuntil = time() + $rec->skiptime;
-                $DB->update_record('block_rss_client', $rec);
-                mtrace("Error: could not load/find the RSS feed - skipping for {$rec->skiptime} seconds.");
-            } else {
-                mtrace ('ok');
-                // It worked this time, so reset the skiptime.
-                if ($rec->skiptime > 0) {
-                    $rec->skiptime = 0;
-                    $rec->skipuntil = 0;
-                    $DB->update_record('block_rss_client', $rec);
-                }
-                // Only increase the counter when a feed is sucesfully refreshed.
-                $counter ++;
-            }
-        }
-        $rs->close();
-
-        // Show times
-        mtrace($counter . ' feeds refreshed (took ' . microtime_diff($starttime, microtime()) . ' seconds)');
-
-        return true;
-    }
-
     /**
      * Calculates a new skip time for a record based on the current skip time.
      *
      * @param int $currentskip The curreent skip time of a record.
      * @return int A new skip time that should be set.
      */
-    protected function calculate_skiptime($currentskip) {
+    public function calculate_skiptime($currentskip) {
         // The default time to skiptime.
         $newskiptime = $this->cron * 1.1;
         if ($currentskip > 0) {
diff --git a/blocks/rss_client/classes/task/refreshfeeds.php b/blocks/rss_client/classes/task/refreshfeeds.php
new file mode 100644 (file)
index 0000000..3e34e1b
--- /dev/null
@@ -0,0 +1,124 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Task for updating RSS feeds for rss client block
+ *
+ * @package   block_rss_client
+ * @author    Farhan Karmali <farhan6318@gmail.com>
+ * @copyright Farhan Karmali 2018
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace block_rss_client\task;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Task for updating RSS feeds for rss client block
+ *
+ * @package   block_rss_client
+ * @author    Farhan Karmali <farhan6318@gmail.com>
+ * @copyright Farhan Karmali 2018
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class refreshfeeds extends \core\task\scheduled_task {
+
+    /**
+     * Name for this task.
+     *
+     * @return string
+     */
+    public function get_name() {
+        return get_string('refreshfeedstask', 'block_rss_client');
+    }
+
+    /**
+     * This task goes through all the feeds. If the feed has a skipuntil value
+     * that is less than the current time cron will attempt to retrieve it
+     * with the cache duration set to 0 in order to force the retrieval of
+     * the item and refresh the cache.
+     *
+     * If a feed fails then the skipuntil time of that feed is set to be
+     * later than the next expected task time. The amount of time will
+     * increase each time the fetch fails until the maximum is reached.
+     *
+     * If a feed that has been failing is successfully retrieved it will
+     * go back to being handled as though it had never failed.
+     *
+     * Task should therefore process requests for permanently broken RSS
+     * feeds infrequently, and temporarily unavailable feeds will be tried
+     * less often until they become available again.
+     */
+    public function execute() {
+        global $CFG, $DB;
+        require_once($CFG->libdir.'/simplepie/moodle_simplepie.php');
+
+        // We are going to measure execution times.
+        $starttime = microtime();
+        $starttimesec = time();
+
+        // Fetch all site feeds.
+        $rs = $DB->get_recordset('block_rss_client');
+        $counter = 0;
+        mtrace('');
+        foreach ($rs as $rec) {
+            mtrace('    ' . $rec->url . ' ', '');
+
+            // Skip feed if it failed recently.
+            if ($starttimesec < $rec->skipuntil) {
+                mtrace('skipping until ' . userdate($rec->skipuntil));
+                continue;
+            }
+
+            // Fetch the rss feed, using standard simplepie caching
+            // so feeds will be renewed only if cache has expired.
+            \core_php_time_limit::raise(60);
+
+            $feed = new \moodle_simplepie();
+            // Set timeout for longer than normal to be agressive at
+            // fetching feeds if possible..
+            $feed->set_timeout(40);
+            $feed->set_cache_duration(0);
+            $feed->set_feed_url($rec->url);
+            $feed->init();
+
+            if ($feed->error()) {
+                // Skip this feed (for an ever-increasing time if it keeps failing).
+                $block = new \block_rss_client();
+                $rec->skiptime = $block->calculate_skiptime($rec->skiptime);
+                $rec->skipuntil = time() + $rec->skiptime;
+                $DB->update_record('block_rss_client', $rec);
+                mtrace("Error: could not load/find the RSS feed - skipping for {$rec->skiptime} seconds.");
+            } else {
+                mtrace ('ok');
+                // It worked this time, so reset the skiptime.
+                if ($rec->skiptime > 0) {
+                    $rec->skiptime = 0;
+                    $rec->skipuntil = 0;
+                    $DB->update_record('block_rss_client', $rec);
+                }
+                // Only increase the counter when a feed is sucesfully refreshed.
+                $counter ++;
+            }
+        }
+        $rs->close();
+
+        // Show times.
+        mtrace($counter . ' feeds refreshed (took ' . microtime_diff($starttime, microtime()) . ' seconds)');
+
+    }
+}
diff --git a/blocks/rss_client/db/tasks.php b/blocks/rss_client/db/tasks.php
new file mode 100644 (file)
index 0000000..254e8ae
--- /dev/null
@@ -0,0 +1,39 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Task definition for block_rss_client.
+ * @author    Farhan Karmali <farhan6318@gmail.com>
+ * @copyright Farhan Karmali 2018
+ * @package   block_rss_client
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$tasks = array(
+    array(
+        'classname' => '\block_rss_client\task\refreshfeeds',
+        'blocking' => 0,
+        'minute' => '*/5',
+        'hour' => '*',
+        'day' => '*',
+        'month' => '*',
+        'dayofweek' => '*',
+        'disabled' => 0
+    )
+);
+
index 3045074..19a9ca5 100644 (file)
@@ -72,6 +72,7 @@ $string['privacy:metadata:block_rss_client:title'] = 'The title of the RSS feed.
 $string['privacy:metadata:block_rss_client:url'] = 'The URL of the RSS feed.';
 $string['privacy:metadata:block_rss_client:userid'] = 'The ID of the user that added the RSS feed.';
 $string['remotenewsfeed'] = 'Remote news feed';
+$string['refreshfeedstask'] = 'Refresh RSS feeds task';
 $string['rss_client:addinstance'] = 'Add a new remote RSS feeds block';
 $string['rss_client:createprivatefeeds'] = 'Create private RSS feeds';
 $string['rss_client:createsharedfeeds'] = 'Create shared RSS feeds';
index 7f99275..bae4cde 100644 (file)
@@ -55,12 +55,12 @@ class block_rss_client_cron_testcase extends advanced_testcase {
         );
         $DB->insert_record('block_rss_client', $record);
 
-        $block = new block_rss_client();
+        $task = new \block_rss_client\task\refreshfeeds();
         ob_start();
 
         // Silence SimplePie php notices.
         $errorlevel = error_reporting($CFG->debug & ~E_USER_NOTICE);
-        $block->cron();
+        $task->execute();
         error_reporting($errorlevel);
 
         $cronoutput = ob_get_clean();
@@ -69,7 +69,7 @@ class block_rss_client_cron_testcase extends advanced_testcase {
     }
 
     /**
-     * Test that when a feed has an error the skip time is increaed correctly.
+     * Test that when a feed has an error the skip time is increased correctly.
      */
     public function test_error() {
         global $DB, $CFG;
@@ -114,20 +114,20 @@ class block_rss_client_cron_testcase extends advanced_testcase {
         );
         $record3->id = $DB->insert_record('block_rss_client', $record3);
 
-        // Run the cron.
-        $block = new block_rss_client();
+        // Run the scheduled task.
+        $task = new \block_rss_client\task\refreshfeeds();
         ob_start();
 
         // Silence SimplePie php notices.
         $errorlevel = error_reporting($CFG->debug & ~E_USER_NOTICE);
-        $block->cron();
+        $task->execute();
         error_reporting($errorlevel);
 
         $cronoutput = ob_get_clean();
         $skiptime1 = $record->skiptime * 2;
         $message1 = 'http://example.com/rss Error: could not load/find the RSS feed - skipping for ' . $skiptime1 . ' seconds.';
         $this->assertContains($message1, $cronoutput);
-        $skiptime2 = 330; // Assumes that the cron time in the version file is 300.
+        $skiptime2 = 0;
         $message2 = 'http://example.com/rss2 Error: could not load/find the RSS feed - skipping for ' . $skiptime2 . ' seconds.';
         $this->assertContains($message2, $cronoutput);
         $skiptime3 = block_rss_client::CLIENT_MAX_SKIPTIME;
index c0e9e59..6f9eac3 100644 (file)
@@ -24,7 +24,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2018051400;        // The current plugin version (Date: YYYYMMDDXX)
+$plugin->version   = 2018052900;        // The current plugin version (Date: YYYYMMDDXX)
 $plugin->requires  = 2018050800;        // Requires this Moodle version
 $plugin->component = 'block_rss_client'; // Full name of the plugin (used for diagnostics)
-$plugin->cron      = 300;               // Set min time between cron executions to 300 secs (5 mins)
index 597eb70..0afc594 100644 (file)
@@ -81,6 +81,11 @@ class container {
      */
     protected static $modulecache = array();
 
+    /**
+     * @var int The requesting user. All capability checks are done against this user.
+     */
+    protected static $requestinguserid;
+
     /**
      * Initialises the dependency graph if it hasn't yet been.
      */
@@ -117,11 +122,13 @@ class container {
                 [self::class, 'apply_component_provide_event_action'],
                 [self::class, 'apply_component_is_event_visible'],
                 function ($dbrow) {
+                    $requestinguserid = self::get_requesting_user();
+
                     if (!empty($dbrow->categoryid)) {
                         // This is a category event. Check that the category is visible to this user.
-                        $category = \coursecat::get($dbrow->categoryid, IGNORE_MISSING, true);
+                        $category = \coursecat::get($dbrow->categoryid, IGNORE_MISSING, true, $requestinguserid);
 
-                        if (empty($category) || !$category->is_uservisible()) {
+                        if (empty($category) || !$category->is_uservisible($requestinguserid)) {
                             return true;
                         }
                     }
@@ -131,7 +138,7 @@ class container {
                         return false;
                     }
 
-                    $instances = get_fast_modinfo($dbrow->courseid)->instances;
+                    $instances = get_fast_modinfo($dbrow->courseid, $requestinguserid)->instances;
 
                     // If modinfo doesn't know about the module, we should ignore it.
                     if (!isset($instances[$dbrow->modulename]) || !isset($instances[$dbrow->modulename][$dbrow->instance])) {
@@ -156,11 +163,13 @@ class container {
                     }
 
                     $coursecontext = \context_course::instance($dbrow->courseid);
-                    if (!$cm->get_course()->visible && !has_capability('moodle/course:viewhiddencourses', $coursecontext)) {
+                    if (!$cm->get_course()->visible &&
+                            !has_capability('moodle/course:viewhiddencourses', $coursecontext, $requestinguserid)) {
                         return true;
                     }
 
-                    if (!has_capability('moodle/course:view', $coursecontext) && !is_enrolled($coursecontext)) {
+                    if (!has_capability('moodle/course:view', $coursecontext, $requestinguserid) &&
+                            !is_enrolled($coursecontext, $requestinguserid)) {
                         return true;
                     }
 
@@ -191,6 +200,7 @@ class container {
      * Reset all static caches, called between tests.
      */
     public static function reset_caches() {
+        self::$requestinguserid = null;
         self::$eventfactory = null;
         self::$eventmapper = null;
         self::$eventvault = null;
@@ -230,6 +240,31 @@ class container {
         return self::$eventvault;
     }
 
+    /**
+     * Sets the requesting user so that all capability checks are done against this user.
+     * Setting the requesting user (hence calling this function) is optional and if you do not so,
+     * $USER will be used as the requesting user. However, if you wish to set the requesting user yourself,
+     * you should call this function before any other function of the container class is called.
+     *
+     * @param int $userid The user id.
+     * @throws \coding_exception
+     */
+    public static function set_requesting_user($userid) {
+        self::$requestinguserid = $userid;
+    }
+
+    /**
+     * Returns the requesting user id.
+     * It usually is the current user unless it has been set explicitly using set_requesting_user.
+     *
+     * @return int
+     */
+    public static function get_requesting_user() {
+        global $USER;
+
+        return empty(self::$requestinguserid) ? $USER->id : self::$requestinguserid;
+    }
+
     /**
      * Calls callback 'core_calendar_provide_event_action' from the component responsible for the event
      *
@@ -245,14 +280,23 @@ class container {
         $mapper = self::$eventmapper;
         $action = null;
         if ($event->get_course_module()) {
+            $requestinguserid = self::get_requesting_user();
+            $legacyevent = $mapper->from_event_to_legacy_event($event);
+            // We know for a fact that the the requesting user might be different from the logged in user,
+            // but the event mapper is not aware of that.
+            if (empty($event->user) && !empty($legacyevent->userid)) {
+                $legacyevent->userid = $requestinguserid;
+            }
+
             // TODO MDL-58866 Only activity modules currently support this callback.
             // Any other event will not be displayed on the dashboard.
             $action = component_callback(
                 'mod_' . $event->get_course_module()->get('modname'),
                 'core_calendar_provide_event_action',
                 [
-                    $mapper->from_event_to_legacy_event($event),
-                    self::$actionfactory
+                    $legacyevent,
+                    self::$actionfactory,
+                    $requestinguserid
                 ]
             );
         }
@@ -279,12 +323,21 @@ class container {
         $mapper = self::$eventmapper;
         $eventvisible = null;
         if ($event->get_course_module()) {
+            $requestinguserid = self::get_requesting_user();
+            $legacyevent = $mapper->from_event_to_legacy_event($event);
+            // We know for a fact that the the requesting user might be different from the logged in user,
+            // but the event mapper is not aware of that.
+            if (empty($event->user) && !empty($legacyevent->userid)) {
+                $legacyevent->userid = $requestinguserid;
+            }
+
             // TODO MDL-58866 Only activity modules currently support this callback.
             $eventvisible = component_callback(
                 'mod_' . $event->get_course_module()->get('modname'),
                 'core_calendar_is_event_visible',
                 [
-                    $mapper->from_event_to_legacy_event($event)
+                    $legacyevent,
+                    $requestinguserid
                 ]
             );
         }
index 5b863b2..37d1a80 100644 (file)
@@ -96,11 +96,24 @@ class raw_event_retrieval_strategy implements raw_event_retrieval_strategy_inter
             return array();
         }
 
+        if (is_numeric($users)) {
+            $users = array($users);
+        }
+        if (is_numeric($groups)) {
+            $groups = array($groups);
+        }
+        if (is_numeric($courses)) {
+            $courses = array($courses);
+        }
+        if (is_numeric($categories)) {
+            $categories = array($categories);
+        }
+
         // Array of filter conditions. To be concatenated by the OR operator.
         $filters = [];
 
         // User filter.
-        if ((is_array($users) && !empty($users)) or is_numeric($users)) {
+        if (is_array($users) && !empty($users)) {
             // Events from a number of users.
             list($insqlusers, $inparamsusers) = $DB->get_in_or_equal($users, SQL_PARAMS_NAMED);
             $filters[] = "(e.userid $insqlusers AND e.courseid = 0 AND e.groupid = 0 AND e.categoryid = 0)";
@@ -112,7 +125,7 @@ class raw_event_retrieval_strategy implements raw_event_retrieval_strategy_inter
         // Boolean false (no users at all): We don't need to do anything.
 
         // Group filter.
-        if ((is_array($groups) && !empty($groups)) or is_numeric($groups)) {
+        if (is_array($groups) && !empty($groups)) {
             // Events from a number of groups.
             list($insqlgroups, $inparamsgroups) = $DB->get_in_or_equal($groups, SQL_PARAMS_NAMED);
             $filters[] = "e.groupid $insqlgroups";
@@ -124,7 +137,7 @@ class raw_event_retrieval_strategy implements raw_event_retrieval_strategy_inter
         // Boolean false (no groups at all): We don't need to do anything.
 
         // Course filter.
-        if ((is_array($courses) && !empty($courses)) or is_numeric($courses)) {
+        if (is_array($courses) && !empty($courses)) {
             list($insqlcourses, $inparamscourses) = $DB->get_in_or_equal($courses, SQL_PARAMS_NAMED);
             $filters[] = "(e.groupid = 0 AND e.courseid $insqlcourses)";
             $params = array_merge($params, $inparamscourses);
@@ -134,7 +147,7 @@ class raw_event_retrieval_strategy implements raw_event_retrieval_strategy_inter
         }
 
         // Category filter.
-        if ((is_array($categories) && !empty($categories)) or is_numeric($categories)) {
+        if (is_array($categories) && !empty($categories)) {
             list($insqlcategories, $inparamscategories) = $DB->get_in_or_equal($categories, SQL_PARAMS_NAMED);
             $filters[] = "(e.groupid = 0 AND e.courseid = 0 AND e.categoryid $insqlcategories)";
             $params = array_merge($params, $inparamscategories);
@@ -168,54 +181,81 @@ class raw_event_retrieval_strategy implements raw_event_retrieval_strategy_inter
         // Build SQL subquery and conditions for filtered events based on priorities.
         $subquerywhere = '';
         $subqueryconditions = [];
-
-        // Get the user's courses. Otherwise, get the default courses being shown by the calendar.
-        $usercourses = calendar_get_default_courses(null, 'id, category, groupmode, groupmodeforce');
-
-        // Set calendar filters.
-        list($usercourses, $usergroups, $user) = calendar_set_filters($usercourses, true);
         $subqueryparams = [];
-
-        // Flag to indicate whether the query needs to exclude group overrides.
-        $viewgroupsonly = false;
-
-        if ($user) {
-            // Set filter condition for the user's events.
-            $subqueryconditions[] = "(ev.userid = :user AND ev.courseid = 0 AND ev.groupid = 0 AND ev.categoryid = 0)";
-            $subqueryparams['user'] = $user;
-
-            foreach ($usercourses as $courseid) {
-                if (has_capability('moodle/site:accessallgroups', \context_course::instance($courseid))) {
-                    $usergroupmembership = groups_get_all_groups($courseid, $user, 0, 'g.id');
-                    if (count($usergroupmembership) == 0) {
-                        $viewgroupsonly = true;
-                        break;
+        $allusercourses = [];
+
+        if (is_array($users) && !empty($users)) {
+            $userrecords = $DB->get_records_sql("SELECT * FROM {user} WHERE id $insqlusers", $inparamsusers);
+            foreach ($userrecords as $userrecord) {
+                // Get the user's courses. Otherwise, get the default courses being shown by the calendar.
+                $usercourses = calendar_get_default_courses(null, 'id, category, groupmode, groupmodeforce',
+                        false, $userrecord->id);
+
+                // Set calendar filters.
+                list($usercourses, $usergroups, $user) = calendar_set_filters($usercourses, true, $userrecord);
+
+                $allusercourses = array_merge($allusercourses, $usercourses);
+
+                // Flag to indicate whether the query needs to exclude group overrides.
+                $viewgroupsonly = false;
+
+                if ($user) {
+                    // Set filter condition for the user's events.
+                    // Even though $user is a single scalar, we still use get_in_or_equal() because we are inside a loop.
+                    list($inusers, $inuserparams) = $DB->get_in_or_equal($user, SQL_PARAMS_NAMED);
+                    $subqueryconditions[] = "(ev.userid $inusers AND ev.courseid = 0 AND ev.groupid = 0 AND ev.categoryid = 0)";
+                    $subqueryparams = array_merge($subqueryparams, $inuserparams);
+
+                    foreach ($usercourses as $courseid) {
+                        if (has_capability('moodle/site:accessallgroups', \context_course::instance($courseid), $userrecord)) {
+                            $usergroupmembership = groups_get_all_groups($courseid, $user, 0, 'g.id');
+                            if (count($usergroupmembership) == 0) {
+                                $viewgroupsonly = true;
+                                break;
+                            }
+                        }
                     }
                 }
+
+                // Set filter condition for the user's group events.
+                if ($usergroups === true || $viewgroupsonly) {
+                    // Fetch group events, but not group overrides.
+                    $subqueryconditions[] = "(ev.groupid != 0 AND ev.eventtype = 'group')";
+                } else if (!empty($usergroups)) {
+                    // Fetch group events and group overrides.
+                    list($inusergroups, $inusergroupparams) = $DB->get_in_or_equal($usergroups, SQL_PARAMS_NAMED);
+                    $subqueryconditions[] = "(ev.groupid $inusergroups)";
+                    $subqueryparams = array_merge($subqueryparams, $inusergroupparams);
+                }
+            }
+        } else if ($users === true) {
+            // Events from ALL users.
+            $subqueryconditions[] = "(ev.userid != 0 AND ev.courseid = 0 AND ev.groupid = 0 AND ev.categoryid = 0)";
+
+            if (is_array($groups)) {
+                // Events from a number of groups.
+                list($insqlgroups, $inparamsgroups) = $DB->get_in_or_equal($groups, SQL_PARAMS_NAMED);
+                $subqueryconditions[] = "ev.groupid $insqlgroups";
+                $subqueryparams = array_merge($subqueryparams, $inparamsgroups);
+            } else if ($groups === true) {
+                // Events from ALL groups.
+                $subqueryconditions[] = "ev.groupid != 0";
             }
-        }
 
-        // Set filter condition for the user's group events.
-        if ($usergroups === true || $viewgroupsonly) {
-            // Fetch group events, but not group overrides.
-            $subqueryconditions[] = "(ev.groupid != 0 AND ev.eventtype = 'group')";
-        } else if (!empty($usergroups)) {
-            // Fetch group events and group overrides.
-            list($inusergroups, $inusergroupparams) = $DB->get_in_or_equal($usergroups, SQL_PARAMS_NAMED);
-            $subqueryconditions[] = "(ev.groupid $inusergroups)";
-            $subqueryparams = array_merge($subqueryparams, $inusergroupparams);
+            if ($courses === true) {
+                // ALL course events. It's not needed to worry about users' access as $users = true.
+                $subqueryconditions[] = "(ev.groupid = 0 AND ev.courseid != 0 AND ev.categoryid = 0)";
+            }
         }
 
         // Get courses to be used for the subquery.
         $subquerycourses = [];
         if (is_array($courses)) {
             $subquerycourses = $courses;
-        } else if (is_numeric($courses)) {
-            $subquerycourses[] = $courses;
         }
         // Merge with user courses, if necessary.
-        if (!empty($usercourses)) {
-            $subquerycourses = array_merge($subquerycourses, $usercourses);
+        if (!empty($allusercourses)) {
+            $subquerycourses = array_merge($subquerycourses, $allusercourses);
             // Make sure we remove duplicate values.
             $subquerycourses = array_unique($subquerycourses);
         }
index 15dee2a..c3c7aac 100644 (file)
@@ -2039,34 +2039,29 @@ function calendar_events_by_day($events, $month, $year, &$eventsbyday, &$duratio
  *
  * @param array $courseeventsfrom An array of courses to load calendar events for
  * @param bool $ignorefilters specify the use of filters, false is set as default
+ * @param stdClass $user The user object. This defaults to the global $USER object.
  * @return array An array of courses, groups, and user to load calendar events for based upon filters
  */
-function calendar_set_filters(array $courseeventsfrom, $ignorefilters = false) {
-    global $USER, $CFG;
+function calendar_set_filters(array $courseeventsfrom, $ignorefilters = false, stdClass $user = null) {
+    global $CFG, $USER;
 
-    // For backwards compatability we have to check whether the courses array contains
-    // just id's in which case we need to load course objects.
-    $coursestoload = array();
-    foreach ($courseeventsfrom as $id => $something) {
-        if (!is_object($something)) {
-            $coursestoload[] = $id;
-            unset($courseeventsfrom[$id]);
-        }
+    if (is_null($user)) {
+        $user = $USER;
     }
 
     $courses = array();
-    $user = false;
+    $userid = false;
     $group = false;
 
     // Get the capabilities that allow seeing group events from all groups.
     $allgroupscaps = array('moodle/site:accessallgroups', 'moodle/calendar:manageentries');
 
-    $isloggedin = isloggedin();
+    $isvaliduser = !empty($user->id);
 
-    if ($ignorefilters || calendar_show_event_type(CALENDAR_EVENT_COURSE)) {
+    if ($ignorefilters || calendar_show_event_type(CALENDAR_EVENT_COURSE, $user)) {
         $courses = array_keys($courseeventsfrom);
     }
-    if ($ignorefilters || calendar_show_event_type(CALENDAR_EVENT_GLOBAL)) {
+    if ($ignorefilters || calendar_show_event_type(CALENDAR_EVENT_GLOBAL, $user)) {
         $courses[] = SITEID;
     }
     $courses = array_unique($courses);
@@ -2080,11 +2075,11 @@ function calendar_set_filters(array $courseeventsfrom, $ignorefilters = false) {
         $courses[] = SITEID;
     }
 
-    if ($ignorefilters || ($isloggedin && calendar_show_event_type(CALENDAR_EVENT_USER))) {
-        $user = $USER->id;
+    if ($ignorefilters || ($isvaliduser && calendar_show_event_type(CALENDAR_EVENT_USER, $user))) {
+        $userid = $user->id;
     }
 
-    if (!empty($courseeventsfrom) && (calendar_show_event_type(CALENDAR_EVENT_GROUP) || $ignorefilters)) {
+    if (!empty($courseeventsfrom) && (calendar_show_event_type(CALENDAR_EVENT_GROUP, $user) || $ignorefilters)) {
 
         if (count($courseeventsfrom) == 1) {
             $course = reset($courseeventsfrom);
@@ -2096,16 +2091,16 @@ function calendar_set_filters(array $courseeventsfrom, $ignorefilters = false) {
         if ($group === false) {
             if (!empty($CFG->calendar_adminseesall) && has_any_capability($allgroupscaps, \context_system::instance())) {
                 $group = true;
-            } else if ($isloggedin) {
+            } else if ($isvaliduser) {
                 $groupids = array();
                 foreach ($courseeventsfrom as $courseid => $course) {
                     // If the user is an editing teacher in there.
-                    if (!empty($USER->groupmember[$course->id])) {
+                    if (!empty($user->groupmember[$course->id])) {
                         // We've already cached the users groups for this course so we can just use that.
-                        $groupids = array_merge($groupids, $USER->groupmember[$course->id]);
+                        $groupids = array_merge($groupids, $user->groupmember[$course->id]);
                     } else if ($course->groupmode != NOGROUPS || !$course->groupmodeforce) {
                         // If this course has groups, show events from all of those related to the current user.
-                        $coursegroups = groups_get_user_groups($course->id, $USER->id);
+                        $coursegroups = groups_get_user_groups($course->id, $user->id);
                         $groupids = array_merge($groupids, $coursegroups['0']);
                     }
                 }
@@ -2119,7 +2114,7 @@ function calendar_set_filters(array $courseeventsfrom, $ignorefilters = false) {
         $courses = false;
     }
 
-    return array($courses, $group, $user);
+    return array($courses, $group, $userid);
 }
 
 /**
@@ -2317,20 +2312,25 @@ function calendar_delete_event_allowed($event) {
  *
  * @param int $courseid (optional) If passed, an additional course can be returned for admins (the current course).
  * @param string $fields Comma separated list of course fields to return.
- * @param bool $canmanage If true, this will return the list of courses the current user can create events in, rather
+ * @param bool $canmanage If true, this will return the list of courses the user can create events in, rather
  *                        than the list of courses they see events from (an admin can always add events in a course
  *                        calendar, even if they are not enrolled in the course).
+ * @param int $userid (optional) The user which this function returns the default courses for.
+ *                        By default the current user.
  * @return array $courses Array of courses to display
  */
-function calendar_get_default_courses($courseid = null, $fields = '*', $canmanage=false) {
-    global $CFG, $DB;
+function calendar_get_default_courses($courseid = null, $fields = '*', $canmanage = false, int $userid = null) {
+    global $CFG, $USER;
 
-    if (!isloggedin()) {
-        return array();
+    if (!$userid) {
+        if (!isloggedin()) {
+            return array();
+        }
+        $userid = $USER->id;
     }
 
-    if (has_capability('moodle/calendar:manageentries', context_system::instance()) &&
-            (!empty($CFG->calendar_adminseesall) || $canmanage)) {
+    if ((!empty($CFG->calendar_adminseesall) || $canmanage) &&
+            has_capability('moodle/calendar:manageentries', context_system::instance(), $userid)) {
 
         // Add a c. prefix to every field as expected by get_courses function.
         $fieldlist = explode(',', $fields);
@@ -2340,11 +2340,11 @@ function calendar_get_default_courses($courseid = null, $fields = '*', $canmanag
         }, $fieldlist);
         $courses = get_courses('all', 'c.shortname', implode(',', $prefixedfields));
     } else {
-        $courses = enrol_get_my_courses($fields);
+        $courses = enrol_get_users_courses($userid, true, $fields);
     }
 
     if ($courseid && $courseid != SITEID) {
-        if (empty($courses[$courseid]) && has_capability('moodle/calendar:manageentries', context_system::instance())) {
+        if (empty($courses[$courseid]) && has_capability('moodle/calendar:manageentries', context_system::instance(), $userid)) {
             // Allow a site admin to see calendars from courses he is not enrolled in.
             // This will come from $COURSE.
             $courses[$courseid] = get_course($courseid);
@@ -3258,6 +3258,11 @@ function calendar_get_legacy_events($tstart, $tend, $users, $groups, $courses,
         return $param;
     }, [$users, $groups, $courses, $categories]);
 
+    // If a single user is provided, we can use that for capability checks.
+    // Otherwise current logged in user is used - See MDL-58768.
+    if (is_array($userparam) && count($userparam) == 1) {
+        \core_calendar\local\event\container::set_requesting_user($userparam[0]);
+    }
     $mapper = \core_calendar\local\event\container::get_event_mapper();
     $events = \core_calendar\local\api::get_events(
         $tstart,
index e50e58c..35809ec 100644 (file)
@@ -472,6 +472,26 @@ class core_calendar_lib_testcase extends advanced_testcase {
         // Enrolled course only (ignore current).
         $this->assertCount(1, $courses);
 
+        // Now, log out and test again.
+        $this->setUser();
+
+        $CFG->calendar_adminseesall = false;
+
+        $courses = calendar_get_default_courses(null, '*', false, $teacher->id);
+        // Only enrolled in one course.
+        $this->assertCount(1, $courses);
+        $courses = calendar_get_default_courses($course2->id, '*', false, $teacher->id);
+        // Enrolled course only (ignore current).
+        $this->assertCount(1, $courses);
+        // This setting should not affect teachers.
+        $CFG->calendar_adminseesall = true;
+        $courses = calendar_get_default_courses(null, '*', false, $teacher->id);
+        // Only enrolled in one course.
+        $this->assertCount(1, $courses);
+        $courses = calendar_get_default_courses($course2->id, '*', false, $teacher->id);
+        // Enrolled course only (ignore current).
+        $this->assertCount(1, $courses);
+
     }
 
     /**
@@ -627,4 +647,129 @@ class core_calendar_lib_testcase extends advanced_testcase {
         $types = calendar_get_allowed_event_types($course->id);
         $this->assertTrue($types['group']);
     }
+
+    /**
+     * This is a setup helper function that create some users, courses, groups and group memberships.
+     * This is useful to prepare the environment for testing the calendar_set_filters function.
+     *
+     * @return array An array of ($users, $courses, $coursegroups)
+     */
+    protected function setup_test_calendar_set_filters() {
+        $generator = $this->getDataGenerator();
+
+        // Create some users.
+        $users = [];
+        $users[] = $generator->create_user();
+        $users[] = $generator->create_user();
+        $users[] = $generator->create_user();
+
+        // Create some courses.
+        $courses = [];
+        $courses[] = $generator->create_course();
+        $courses[] = $generator->create_course();
+        $courses[] = $generator->create_course();
+        $courses[] = $generator->create_course();
+
+        // Create some groups.
+        $coursegroups = [];
+        $coursegroups[$courses[0]->id] = [];
+        $coursegroups[$courses[0]->id][] = $generator->create_group(['courseid' => $courses[0]->id]);
+        $coursegroups[$courses[0]->id][] = $generator->create_group(['courseid' => $courses[0]->id]);
+        $coursegroups[$courses[2]->id] = [];
+        $coursegroups[$courses[2]->id][] = $generator->create_group(['courseid' => $courses[2]->id]);
+        $coursegroups[$courses[2]->id][] = $generator->create_group(['courseid' => $courses[2]->id]);
+        $coursegroups[$courses[3]->id] = [];
+        $coursegroups[$courses[3]->id][] = $generator->create_group(['courseid' => $courses[3]->id]);
+        $coursegroups[$courses[3]->id][] = $generator->create_group(['courseid' => $courses[3]->id]);
+
+        // Create some enrolments and group memberships.
+        $generator->enrol_user($users[0]->id, $courses[0]->id, 'student');
+        $generator->create_group_member(['groupid' => $coursegroups[$courses[0]->id][0]->id, 'userid' => $users[0]->id]);
+        $generator->enrol_user($users[1]->id, $courses[0]->id, 'student');
+        $generator->create_group_member(['groupid' => $coursegroups[$courses[0]->id][1]->id, 'userid' => $users[1]->id]);
+        $generator->enrol_user($users[0]->id, $courses[1]->id, 'student');
+        $generator->enrol_user($users[0]->id, $courses[2]->id, 'student');
+
+        return array($users, $courses, $coursegroups);
+    }
+
+    /**
+     * This function tests calendar_set_filters for the case when user is not logged in.
+     */
+    public function test_calendar_set_filters_not_logged_in() {
+        $this->resetAfterTest();
+
+        list($users, $courses, $coursegroups) = $this->setup_test_calendar_set_filters();
+
+        $defaultcourses = calendar_get_default_courses(null, '*', false, $users[0]->id);
+        list($courseids, $groupids, $userid) = calendar_set_filters($defaultcourses);
+
+        $this->assertEquals(
+                [$courses[0]->id, $courses[1]->id, $courses[2]->id, SITEID],
+                array_values($courseids),
+                '', 0.0, 10, true);
+        $this->assertFalse($groupids);
+        $this->assertFalse($userid);
+    }
+
+    /**
+     * This function tests calendar_set_filters for the case when no one is logged in, but a user id is provided.
+     */
+    public function test_calendar_set_filters_not_logged_in_with_user() {
+        $this->resetAfterTest();
+
+        list($users, $courses, $coursegroups) = $this->setup_test_calendar_set_filters();
+
+        $defaultcourses = calendar_get_default_courses(null, '*', false, $users[1]->id);
+        list($courseids, $groupids, $userid) = calendar_set_filters($defaultcourses, false, $users[1]);
+
+        $this->assertEquals(array($courses[0]->id, SITEID), array_values($courseids));
+        $this->assertEquals(array($coursegroups[$courses[0]->id][1]->id), $groupids);
+        $this->assertEquals($users[1]->id, $userid);
+
+        $defaultcourses = calendar_get_default_courses(null, '*', false, $users[0]->id);
+        list($courseids, $groupids, $userid) = calendar_set_filters($defaultcourses, false, $users[0]);
+
+        $this->assertEquals(
+                [$courses[0]->id, $courses[1]->id, $courses[2]->id, SITEID],
+                array_values($courseids),
+                '', 0.0, 10, true);
+        $this->assertEquals(array($coursegroups[$courses[0]->id][0]->id), $groupids);
+        $this->assertEquals($users[0]->id, $userid);
+
+    }
+
+    /**
+     * This function tests calendar_set_filters for the case when user is logged in, but no user id is provided.
+     */
+    public function test_calendar_set_filters_logged_in_no_user() {
+        $this->resetAfterTest();
+
+        list($users, $courses, $coursegroups) = $this->setup_test_calendar_set_filters();
+
+        $this->setUser($users[0]);
+        $defaultcourses = calendar_get_default_courses(null, '*', false, $users[0]->id);
+        list($courseids, $groupids, $userid) = calendar_set_filters($defaultcourses, false);
+        $this->assertEquals([$courses[0]->id, $courses[1]->id, $courses[2]->id, SITEID], array_values($courseids), '', 0.0, 10,
+                true);
+        $this->assertEquals(array($coursegroups[$courses[0]->id][0]->id), $groupids);
+        $this->assertEquals($users[0]->id, $userid);
+    }
+
+    /**
+     * This function tests calendar_set_filters for the case when a user is logged in, but another user id is provided.
+     */
+    public function test_calendar_set_filters_logged_in_another_user() {
+        $this->resetAfterTest();
+
+        list($users, $courses, $coursegroups) = $this->setup_test_calendar_set_filters();
+
+        $this->setUser($users[0]);
+        $defaultcourses = calendar_get_default_courses(null, '*', false, $users[1]->id);
+        list($courseids, $groupids, $userid) = calendar_set_filters($defaultcourses, false, $users[1]);
+
+        $this->assertEquals(array($courses[0]->id, SITEID), array_values($courseids));
+        $this->assertEquals(array($coursegroups[$courses[0]->id][1]->id), $groupids);
+        $this->assertEquals($users[1]->id, $userid);
+    }
 }
index 1a927c2..82cd7a8 100644 (file)
@@ -90,15 +90,29 @@ class core_calendar_raw_event_retrieval_strategy_testcase extends advanced_testc
         $this->assertCount(2, $events);
 
         // Disable the lesson module.
-        $modulerecord = $DB->get_record('modules', ['name' => 'lesson']);
-        $modulerecord->visible = 0;
-        $DB->update_record('modules', $modulerecord);
+        $DB->set_field('modules', 'visible', 0, ['name' => 'lesson']);
 
         // Check that we only return the assign event.
         $events = $retrievalstrategy->get_raw_events(null, [0], null);
         $this->assertCount(1, $events);
         $event = reset($events);
         $this->assertEquals('assign', $event->modulename);
+
+        // Now, log out and repeat the above test in the reverse order.
+        $this->setUser();
+
+        // Check that we only return the assign event (given that the lesson module is still disabled).
+        $events = $retrievalstrategy->get_raw_events([$student->id], [0], null);
+        $this->assertCount(1, $events);
+        $event = reset($events);
+        $this->assertEquals('assign', $event->modulename);
+
+        // Enable the lesson module.
+        $DB->set_field('modules', 'visible', 1, ['name' => 'lesson']);
+
+        // Get all events.
+        $events = $retrievalstrategy->get_raw_events(null, [0], null);
+        $this->assertCount(2, $events);
     }
 
     /**
@@ -209,37 +223,37 @@ class core_calendar_raw_event_retrieval_strategy_testcase extends advanced_testc
             calendar_event::create($event, false);
         }
 
-        $timestart = $now - 100;
-        $timeend = $now + (3 * 86400);
         $groups = [$group1->id, $group2->id];
 
-        // Get user override events.
-        $this->setUser($useroverridestudent);
-        $events = $retrievalstrategy->get_raw_events([$useroverridestudent->id], $groups, [$course->id]);
-        $this->assertCount(1, $events);
-        $event = reset($events);
-        $this->assertEquals('Assignment 1 due date - User override', $event->name);
-
-        // Get events for user that does not belong to any group and has no user override events.
-        $this->setUser($nogroupstudent);
-        $events = $retrievalstrategy->get_raw_events([$nogroupstudent->id], $groups, [$course->id]);
-        $this->assertCount(1, $events);
-        $event = reset($events);
-        $this->assertEquals('Assignment 1 due date', $event->name);
-
-        // Get events for user that belongs to groups A and B and has no user override events.
-        $this->setUser($group12student);
-        $events = $retrievalstrategy->get_raw_events([$group12student->id], $groups, [$course->id]);
-        $this->assertCount(1, $events);
-        $event = reset($events);
-        $this->assertEquals('Assignment 1 due date - Group A override', $event->name);
-
-        // Get events for user that belongs to group A and has no user override events.
-        $this->setUser($group1student);
-        $events = $retrievalstrategy->get_raw_events([$group1student->id], $groups, [$course->id]);
-        $this->assertCount(1, $events);
-        $event = reset($events);
-        $this->assertEquals('Assignment 1 due date - Group A override', $event->name);
+        // Do the following tests multiple times when logged in with different users. Also run the whole set when logged out.
+        // In any cases, the tests should not depend on the logged-in user.
+        foreach ([$useroverridestudent, $nogroupstudent, $group12student, $group1student, null] as $login) {
+            $this->setUser($login);
+
+            // Get user override events.
+            $events = $retrievalstrategy->get_raw_events([$useroverridestudent->id], $groups, [$course->id]);
+            $this->assertCount(1, $events);
+            $event = reset($events);
+            $this->assertEquals('Assignment 1 due date - User override', $event->name);
+
+            // Get events for user that does not belong to any group and has no user override events.
+            $events = $retrievalstrategy->get_raw_events([$nogroupstudent->id], $groups, [$course->id]);
+            $this->assertCount(1, $events);
+            $event = reset($events);
+            $this->assertEquals('Assignment 1 due date', $event->name);
+
+            // Get events for user that belongs to groups A and B and has no user override events.
+            $events = $retrievalstrategy->get_raw_events([$group12student->id], $groups, [$course->id]);
+            $this->assertCount(1, $events);
+            $event = reset($events);
+            $this->assertEquals('Assignment 1 due date - Group A override', $event->name);
+
+            // Get events for user that belongs to group A and has no user override events.
+            $events = $retrievalstrategy->get_raw_events([$group1student->id], $groups, [$course->id]);
+            $this->assertCount(1, $events);
+            $event = reset($events);
+            $this->assertEquals('Assignment 1 due date - Group A override', $event->name);
+        }
 
         // Add repeating events.
         $repeatingevents = [
@@ -290,8 +304,6 @@ class core_calendar_raw_event_retrieval_strategy_testcase extends advanced_testc
      * Test retrieval strategy with category specifications.
      */
     public function test_get_raw_events_category() {
-        global $DB;
-
         $this->resetAfterTest();
         $retrievalstrategy = new raw_event_retrieval_strategy();
         $generator = $this->getDataGenerator();
@@ -351,4 +363,88 @@ class core_calendar_raw_event_retrieval_strategy_testcase extends advanced_testc
         $events = $retrievalstrategy->get_raw_events(null, null, null, [$category1->id, $category2->id]);
         $this->assertCount(2, $events);
     }
+
+    public function test_get_raw_events_for_multiple_users() {
+        $this->resetAfterTest();
+
+        $generator = $this->getDataGenerator();
+
+        // Create users.
+        $user1 = $generator->create_user();
+        $user2 = $generator->create_user();
+        $user3 = $generator->create_user();
+
+        // Create user events.
+        $events = [
+            [
+                'name' => 'User1 Event',
+                'eventtype' => 'user',
+                'userid' => $user1->id,
+                'timestart' => time(),
+            ], [
+                'name' => 'User2 Event',
+                'eventtype' => 'user',
+                'userid' => $user2->id,
+                'timestart' => time(),
+            ], [
+                'name' => 'User3 Event',
+                'eventtype' => 'user',
+                'userid' => $user3->id,
+                'timestart' => time(),
+            ]
+        ];
+        foreach ($events as $event) {
+            calendar_event::create($event, false);
+        }
+
+        $retrievalstrategy = new raw_event_retrieval_strategy();
+
+        // Get all events.
+        $events = $retrievalstrategy->get_raw_events([$user1->id, $user2->id]);
+        $this->assertCount(2, $events);
+        $this->assertEquals(
+                ['User1 Event', 'User2 Event'],
+                array_column($events, 'name'),
+                '', 0.0, 10, true);
+    }
+
+    public function test_get_raw_events_for_groups_with_no_members() {
+        $this->resetAfterTest();
+
+        $generator = $this->getDataGenerator();
+
+        $course = $generator->create_course();
+
+        // Create groups.
+        $group1 = $generator->create_group(['courseid' => $course->id, 'name' => 'Group 1']);
+        $group2 = $generator->create_group(['courseid' => $course->id, 'name' => 'Group 2']);
+
+        // Create group events.
+        $events = [
+            [
+                'name' => 'Group 1 Event',
+                'eventtype' => 'group',
+                'groupid' => $group1->id,
+                'timestart' => time(),
+            ], [
+                'name' => 'Group 2 Event',
+                'eventtype' => 'group',
+                'groupid' => $group2->id,
+                'timestart' => time(),
+            ]
+        ];
+        foreach ($events as $event) {
+            calendar_event::create($event, false);
+        }
+
+        $retrievalstrategy = new raw_event_retrieval_strategy;
+
+        // Get group eventsl.
+        $events = $retrievalstrategy->get_raw_events(null, [$group1->id, $group2->id]);
+        $this->assertCount(2, $events);
+        $this->assertEquals(
+                ['Group 1 Event', 'Group 2 Event'],
+                array_column($events, 'name'),
+                '', 0.0, 10, true);
+    }
 }
index 359ee3e..6d3489e 100644 (file)
@@ -1,6 +1,12 @@
 This files describes API changes in /calendar/* ,
 information provided here is intended especially for developers.
 
+=== 3.6 ===
+* calendar_get_default_courses() function now has optional $userid parameter.
+* calendar_set_filters() function now has optional $user parameter.
+* The core_calendar\local\event\container class now provides two new helper methods for getting and setting the requesting user:
+  set_requesting_user() and get_requesting_user().
+
 === 3.5 ===
 * core_calendar_external::get_calendar_events now returns the categoryid for category events.
 
index a686f13..388b9b7 100644 (file)
@@ -35,7 +35,6 @@ define('NO_DEBUG_DISPLAY', true);
 // @codingStandardsIgnoreLine This script does not require login.
 require("../../config.php");
 require_once("lib.php");
-require_once($CFG->libdir.'/eventslib.php');
 require_once($CFG->libdir.'/enrollib.php');
 require_once($CFG->libdir . '/filelib.php');
 
index 2e3280d..dbb69cd 100644 (file)
@@ -1,7 +1,6 @@
 <?php
 
     require('../config.php');
-    require_once($CFG->libdir.'/eventslib.php');
 
     // Form submitted, do not check referer (original page unknown).
     if ($form = data_submitted()) {
index 393afe4..dedd4bf 100644 (file)
@@ -141,7 +141,7 @@ if (!$edit) {
         if (!$admin and empty($data->override)) {
             $records = $DB->get_records('grade_letters', array('contextid' => $context->id));
             foreach ($records as $record) {
-                $DB->delete_record('grade_letters', array('id' => $record->id));
+                $DB->delete_records('grade_letters', array('id' => $record->id));
                 // Trigger the letter grade deleted event.
                 $event = \core\event\grade_letter_deleted::create(array(
                     'objectid' => $record->id,
index c3e39cd..0c7b156 100644 (file)
@@ -234,9 +234,6 @@ function uninstall_plugin($type, $name) {
     // delete the capabilities that were defined by this module
     capabilities_cleanup($component);
 
-    // remove event handlers and dequeue pending events
-    events_uninstall($component);
-
     // Delete all remaining files in the filepool owned by the component.
     $fs = get_file_storage();
     $fs->delete_component_files($component);
index 27f74c0..0d7831f 100644 (file)
@@ -834,10 +834,6 @@ abstract class base implements \IteratorAggregate {
         \core\event\manager::dispatch($this);
 
         $this->dispatched = true;
-
-        if ($legacyeventname = static::get_legacy_eventname()) {
-            events_trigger_legacy($legacyeventname, $this->get_legacy_eventdata());
-        }
     }
 
     /**
index 5ad20b7..8833387 100644 (file)
@@ -212,7 +212,7 @@ class coursecat implements renderable, cacheable_object, IteratorAggregate {
     /**
      * Returns coursecat object for requested category
      *
-     * If category is not visible to user it is treated as non existing
+     * If category is not visible to the given user, it is treated as non existing
      * unless $alwaysreturnhidden is set to true
      *
      * If id is 0, the pseudo object for root category is returned (convenient
@@ -226,10 +226,11 @@ class coursecat implements renderable, cacheable_object, IteratorAggregate {
      *     returned even if this category is not visible to the current user
      *     (category is hidden and user does not have
      *     'moodle/category:viewhiddencategories' capability). Use with care!
+     * @param int|stdClass $user The user id or object. By default (null) checks the visibility to the current user.
      * @return null|coursecat
      * @throws moodle_exception
      */
-    public static function get($id, $strictness = MUST_EXIST, $alwaysreturnhidden = false) {
+    public static function get($id, $strictness = MUST_EXIST, $alwaysreturnhidden = false, $user = null) {
         if (!$id) {
             if (!isset(self::$coursecat0)) {
                 $record = new stdClass();
@@ -251,7 +252,7 @@ class coursecat implements renderable, cacheable_object, IteratorAggregate {
                 $coursecatrecordcache->set($id, $coursecat);
             }
         }
-        if ($coursecat && ($alwaysreturnhidden || $coursecat->is_uservisible())) {
+        if ($coursecat && ($alwaysreturnhidden || $coursecat->is_uservisible($user))) {
             return $coursecat;
         } else {
             if ($strictness == MUST_EXIST) {
@@ -580,17 +581,18 @@ class coursecat implements renderable, cacheable_object, IteratorAggregate {
     }
 
     /**
-     * Checks if this course category is visible to current user
+     * Checks if this course category is visible to a user.
      *
      * Please note that methods coursecat::get (without 3rd argumet),
      * coursecat::get_children(), etc. return only visible categories so it is
      * usually not needed to call this function outside of this class
      *
+     * @param int|stdClass $user The user id or object. By default (null) checks the visibility to the current user.
      * @return bool
      */
-    public function is_uservisible() {
+    public function is_uservisible($user = null) {
         return !$this->id || $this->visible ||
-                has_capability('moodle/category:viewhiddencategories', $this->get_context());
+                has_capability('moodle/category:viewhiddencategories', $this->get_context(), $user);
     }
 
     /**
index 6873e42..38e8d85 100644 (file)
@@ -149,15 +149,6 @@ $tasks = array(
         'dayofweek' => '*',
         'month' => '*'
     ),
-    array(
-        'classname' => 'core\task\events_cron_task',
-        'blocking' => 0,
-        'minute' => '*',
-        'hour' => '*',
-        'day' => '*',
-        'dayofweek' => '*',
-        'month' => '*'
-    ),
     array(
         'classname' => 'core\task\completion_regular_task',
         'blocking' => 0,
index daf12c4..1c3c922 100644 (file)
@@ -58,17 +58,10 @@ function add_to_log($courseid, $module, $action, $url='', $info='', $cm=0, $user
 }
 
 /**
- * Function to call all event handlers when triggering an event
- *
  * @deprecated since 2.6
- *
- * @param string $eventname name of the event
- * @param mixed $eventdata event data object
- * @return int number of failed events
  */
-function events_trigger($eventname, $eventdata) {
-    debugging('events_trigger() is deprecated, please use new events instead', DEBUG_DEVELOPER);
-    return events_trigger_legacy($eventname, $eventdata);
+function events_trigger() {
+    throw new coding_exception('events_trigger() has been deprecated along with all Events 1 API in favour of Events 2 API.');
 }
 
 /**
@@ -3484,497 +3477,71 @@ function tag_cloud_sort($a, $b) {
 }
 
 /**
- * Loads the events definitions for the component (from file). If no
- * events are defined for the component, we simply return an empty array.
- *
- * @access protected To be used from eventslib only
  * @deprecated since Moodle 3.1
- * @param string $component examples: 'moodle', 'mod_forum', 'block_quiz_results'
- * @return array Array of capabilities or empty array if not exists
  */
-function events_load_def($component) {
-    global $CFG;
-    if ($component === 'unittest') {
-        $defpath = $CFG->dirroot.'/lib/tests/fixtures/events.php';
-    } else {
-        $defpath = core_component::get_component_directory($component).'/db/events.php';
-    }
-
-    $handlers = array();
-
-    if (file_exists($defpath)) {
-        require($defpath);
-    }
+function events_load_def() {
+    throw new coding_exception('events_load_def() has been deprecated along with all Events 1 API in favour of Events 2 API.');
 
-    // make sure the definitions are valid and complete; tell devs what is wrong
-    foreach ($handlers as $eventname => $handler) {
-        if ($eventname === 'reset') {
-            debugging("'reset' can not be used as event name.");
-            unset($handlers['reset']);
-            continue;
-        }
-        if (!is_array($handler)) {
-            debugging("Handler of '$eventname' must be specified as array'");
-            unset($handlers[$eventname]);
-            continue;
-        }
-        if (!isset($handler['handlerfile'])) {
-            debugging("Handler of '$eventname' must include 'handlerfile' key'");
-            unset($handlers[$eventname]);
-            continue;
-        }
-        if (!isset($handler['handlerfunction'])) {
-            debugging("Handler of '$eventname' must include 'handlerfunction' key'");
-            unset($handlers[$eventname]);
-            continue;
-        }
-        if (!isset($handler['schedule'])) {
-            $handler['schedule'] = 'instant';
-        }
-        if ($handler['schedule'] !== 'instant' and $handler['schedule'] !== 'cron') {
-            debugging("Handler of '$eventname' must include valid 'schedule' type (instant or cron)'");
-            unset($handlers[$eventname]);
-            continue;
-        }
-        if (!isset($handler['internal'])) {
-            $handler['internal'] = 1;
-        }
-        $handlers[$eventname] = $handler;
-    }
-
-    return $handlers;
 }
 
 /**
- * Puts a handler on queue
- *
- * @access protected To be used from eventslib only
  * @deprecated since Moodle 3.1
- * @param stdClass $handler event handler object from db
- * @param stdClass $event event data object
- * @param string $errormessage The error message indicating the problem
- * @return int id number of new queue handler
  */
-function events_queue_handler($handler, $event, $errormessage) {
-    global $DB;
-
-    if ($qhandler = $DB->get_record('events_queue_handlers', array('queuedeventid'=>$event->id, 'handlerid'=>$handler->id))) {
-        debugging("Please check code: Event id $event->id is already queued in handler id $qhandler->id");
-        return $qhandler->id;
-    }
-
-    // make a new queue handler
-    $qhandler = new stdClass();
-    $qhandler->queuedeventid  = $event->id;
-    $qhandler->handlerid      = $handler->id;
-    $qhandler->errormessage   = $errormessage;
-    $qhandler->timemodified   = time();
-    if ($handler->schedule === 'instant' and $handler->status == 1) {
-        $qhandler->status     = 1; //already one failed attempt to dispatch this event
-    } else {
-        $qhandler->status     = 0;
-    }
-
-    return $DB->insert_record('events_queue_handlers', $qhandler);
+function events_queue_handler() {
+    throw new coding_exception('events_queue_handler() has been deprecated along with all Events 1 API in favour of Events 2 API.');
 }
 
 /**
- * trigger a single event with a specified handler
- *
- * @access protected To be used from eventslib only
  * @deprecated since Moodle 3.1
- * @param stdClass $handler This shoudl be a row from the events_handlers table.
- * @param stdClass $eventdata An object containing information about the event
- * @param string $errormessage error message indicating problem
- * @return bool|null True means event processed, false means retry event later; may throw exception, NULL means internal error
  */
-function events_dispatch($handler, $eventdata, &$errormessage) {
-    global $CFG;
-
-    debugging('Events API using $handlers array has been deprecated in favour of Events 2 API, please use it instead.', DEBUG_DEVELOPER);
-
-    $function = unserialize($handler->handlerfunction);
-
-    if (is_callable($function)) {
-        // oki, no need for includes
-
-    } else if (file_exists($CFG->dirroot.$handler->handlerfile)) {
-        include_once($CFG->dirroot.$handler->handlerfile);
-
-    } else {
-        $errormessage = "Handler file of component $handler->component: $handler->handlerfile can not be found!";
-        return null;
-    }
-
-    // checks for handler validity
-    if (is_callable($function)) {
-        $result = call_user_func($function, $eventdata);
-        if ($result === false) {
-            $errormessage = "Handler function of component $handler->component: $handler->handlerfunction requested resending of event!";
-            return false;
-        }
-        return true;
-
-    } else {
-        $errormessage = "Handler function of component $handler->component: $handler->handlerfunction not callable function or class method!";
-        return null;
-    }
+function events_dispatch() {
+    throw new coding_exception('events_dispatch() has been deprecated along with all Events 1 API in favour of Events 2 API.');
 }
 
 /**
- * given a queued handler, call the respective event handler to process the event
- *
- * @access protected To be used from eventslib only
  * @deprecated since Moodle 3.1
- * @param stdClass $qhandler events_queued_handler row from db
- * @return boolean true means event processed, false means retry later, NULL means fatal failure
  */
-function events_process_queued_handler($qhandler) {
-    global $DB;
-
-    // get handler
-    if (!$handler = $DB->get_record('events_handlers', array('id'=>$qhandler->handlerid))) {
-        debugging("Error processing queue handler $qhandler->id, missing handler id: $qhandler->handlerid");
-        //irrecoverable error, remove broken queue handler
-        events_dequeue($qhandler);
-        return NULL;
-    }
-
-    // get event object
-    if (!$event = $DB->get_record('events_queue', array('id'=>$qhandler->queuedeventid))) {
-        // can't proceed with no event object - might happen when two crons running at the same time
-        debugging("Error processing queue handler $qhandler->id, missing event id: $qhandler->queuedeventid");
-        //irrecoverable error, remove broken queue handler
-        events_dequeue($qhandler);
-        return NULL;
-    }
-
-    // call the function specified by the handler
-    try {
-        $errormessage = 'Unknown error';
-        if (events_dispatch($handler, unserialize(base64_decode($event->eventdata)), $errormessage)) {
-            //everything ok
-            events_dequeue($qhandler);
-            return true;
-        }
-    } catch (Exception $e) {
-        // the problem here is that we do not want one broken handler to stop all others,
-        // cron handlers are very tricky because the needed data might have been deleted before the cron execution
-        $errormessage = "Handler function of component $handler->component: $handler->handlerfunction threw exception :" .
-                $e->getMessage() . "\n" . format_backtrace($e->getTrace(), true);
-        if (!empty($e->debuginfo)) {
-            $errormessage .= $e->debuginfo;
-        }
-    }
-
-    //dispatching failed
-    $qh = new stdClass();
-    $qh->id           = $qhandler->id;
-    $qh->errormessage = $errormessage;
-    $qh->timemodified = time();
-    $qh->status       = $qhandler->status + 1;
-    $DB->update_record('events_queue_handlers', $qh);
-
-    debugging($errormessage);
-
-    return false;
+function events_process_queued_handler() {
+    throw new coding_exception(
+        'events_process_queued_handler() has been deprecated along with all Events 1 API in favour of Events 2 API.'
+    );
 }
 
 /**
- * Updates all of the event definitions within the database.
- *
- * Unfortunately this isn't as simple as removing them all and then readding
- * the updated event definitions. Chances are queued items are referencing the
- * existing definitions.
- *
- * Note that the absence of the db/events.php event definition file
- * will cause any queued events for the component to be removed from
- * the database.
- *
- * @category event
  * @deprecated since Moodle 3.1
- * @param string $component examples: 'moodle', 'mod_forum', 'block_quiz_results'
- * @return boolean always returns true
  */
-function events_update_definition($component='moodle') {
-    global $DB;
-
-    // load event definition from events.php
-    $filehandlers = events_load_def($component);
-
-    if ($filehandlers) {
-        debugging('Events API using $handlers array has been deprecated in favour of Events 2 API, please use it instead.', DEBUG_DEVELOPER);
-    }
-
-    // load event definitions from db tables
-    // if we detect an event being already stored, we discard from this array later
-    // the remaining needs to be removed
-    $cachedhandlers = events_get_cached($component);
-
-    foreach ($filehandlers as $eventname => $filehandler) {
-        if (!empty($cachedhandlers[$eventname])) {
-            if ($cachedhandlers[$eventname]['handlerfile'] === $filehandler['handlerfile'] &&
-                $cachedhandlers[$eventname]['handlerfunction'] === serialize($filehandler['handlerfunction']) &&
-                $cachedhandlers[$eventname]['schedule'] === $filehandler['schedule'] &&
-                $cachedhandlers[$eventname]['internal'] == $filehandler['internal']) {
-                // exact same event handler already present in db, ignore this entry
-
-                unset($cachedhandlers[$eventname]);
-                continue;
-
-            } else {
-                // same event name matches, this event has been updated, update the datebase
-                $handler = new stdClass();
-                $handler->id              = $cachedhandlers[$eventname]['id'];
-                $handler->handlerfile     = $filehandler['handlerfile'];
-                $handler->handlerfunction = serialize($filehandler['handlerfunction']); // static class methods stored as array
-                $handler->schedule        = $filehandler['schedule'];
-                $handler->internal        = $filehandler['internal'];
-
-                $DB->update_record('events_handlers', $handler);
-
-                unset($cachedhandlers[$eventname]);
-                continue;
-            }
-
-        } else {
-            // if we are here, this event handler is not present in db (new)
-            // add it
-            $handler = new stdClass();
-            $handler->eventname       = $eventname;
-            $handler->component       = $component;
-            $handler->handlerfile     = $filehandler['handlerfile'];
-            $handler->handlerfunction = serialize($filehandler['handlerfunction']); // static class methods stored as array
-            $handler->schedule        = $filehandler['schedule'];
-            $handler->status          = 0;
-            $handler->internal        = $filehandler['internal'];
-
-            $DB->insert_record('events_handlers', $handler);
-        }
-    }
-
-    // clean up the left overs, the entries in cached events array at this points are deprecated event handlers
-    // and should be removed, delete from db
-    events_cleanup($component, $cachedhandlers);
-
-    events_get_handlers('reset');
-
-    return true;
+function events_update_definition() {
+    throw new coding_exception(
+        'events_update_definition has been deprecated along with all Events 1 API in favour of Events 2 API.'
+    );
 }
 
 /**
- * Events cron will try to empty the events queue by processing all the queued events handlers
- *
- * @access public Part of the public API
  * @deprecated since Moodle 3.1
- * @category event
- * @param string $eventname empty means all
- * @return int number of dispatched events
  */
-function events_cron($eventname='') {
-    global $DB;
-
-    $failed = array();
-    $processed = 0;
-
-    if ($eventname) {
-        $sql = "SELECT qh.*
-                  FROM {events_queue_handlers} qh, {events_handlers} h
-                 WHERE qh.handlerid = h.id AND h.eventname=?
-              ORDER BY qh.id";
-        $params = array($eventname);
-    } else {
-        $sql = "SELECT *
-                  FROM {events_queue_handlers}
-              ORDER BY id";
-        $params = array();
-    }
-
-    $rs = $DB->get_recordset_sql($sql, $params);
-    if ($rs->valid()) {
-        debugging('Events API using $handlers array has been deprecated in favour of Events 2 API, please use it instead.', DEBUG_DEVELOPER);
-    }
-
-    foreach ($rs as $qhandler) {
-        if (isset($failed[$qhandler->handlerid])) {
-            // do not try to dispatch any later events when one already asked for retry or ended with exception
-            continue;
-        }
-        $status = events_process_queued_handler($qhandler);
-        if ($status === false) {
-            // handler is asking for retry, do not send other events to this handler now
-            $failed[$qhandler->handlerid] = $qhandler->handlerid;
-        } else if ($status === NULL) {
-            // means completely broken handler, event data was purged
-            $failed[$qhandler->handlerid] = $qhandler->handlerid;
-        } else {
-            $processed++;
-        }
-    }
-    $rs->close();
-
-    // remove events that do not have any handlers waiting
-    $sql = "SELECT eq.id
-              FROM {events_queue} eq
-              LEFT JOIN {events_queue_handlers} qh ON qh.queuedeventid = eq.id
-             WHERE qh.id IS NULL";
-    $rs = $DB->get_recordset_sql($sql);
-    foreach ($rs as $event) {
-        //debugging('Purging stale event '.$event->id);
-        $DB->delete_records('events_queue', array('id'=>$event->id));
-    }
-    $rs->close();
-
-    return $processed;
+function events_cron() {
+    throw new coding_exception('events_cron() has been deprecated along with all Events 1 API in favour of Events 2 API.');
 }
 
 /**
- * Do not call directly, this is intended to be used from new event base only.
- *
- * @private
  * @deprecated since Moodle 3.1
- * @param string $eventname name of the event
- * @param mixed $eventdata event data object
- * @return int number of failed events
  */
-function events_trigger_legacy($eventname, $eventdata) {
-    global $CFG, $USER, $DB;
-
-    $failedcount = 0; // number of failed events.
-
-    // pull out all registered event handlers
-    if ($handlers = events_get_handlers($eventname)) {
-        foreach ($handlers as $handler) {
-            $errormessage = '';
-
-            if ($handler->schedule === 'instant') {
-                if ($handler->status) {
-                    //check if previous pending events processed
-                    if (!$DB->record_exists('events_queue_handlers', array('handlerid'=>$handler->id))) {
-                        // ok, queue is empty, lets reset the status back to 0 == ok
-                        $handler->status = 0;
-                        $DB->set_field('events_handlers', 'status', 0, array('id'=>$handler->id));
-                        // reset static handler cache
-                        events_get_handlers('reset');
-                    }
-                }
-
-                // dispatch the event only if instant schedule and status ok
-                if ($handler->status or (!$handler->internal and $DB->is_transaction_started())) {
-                    // increment the error status counter
-                    $handler->status++;
-                    $DB->set_field('events_handlers', 'status', $handler->status, array('id'=>$handler->id));
-                    // reset static handler cache
-                    events_get_handlers('reset');
-
-                } else {
-                    $errormessage = 'Unknown error';
-                    $result = events_dispatch($handler, $eventdata, $errormessage);
-                    if ($result === true) {
-                        // everything is fine - event dispatched
-                        continue;
-                    } else if ($result === false) {
-                        // retry later - set error count to 1 == send next instant into cron queue
-                        $DB->set_field('events_handlers', 'status', 1, array('id'=>$handler->id));
-                        // reset static handler cache
-                        events_get_handlers('reset');
-                    } else {
-                        // internal problem - ignore the event completely
-                        $failedcount ++;
-                        continue;
-                    }
-                }
-
-                // update the failed counter
-                $failedcount ++;
-
-            } else if ($handler->schedule === 'cron') {
-                //ok - use queueing of events only
-
-            } else {
-                // unknown schedule - ignore event completely
-                debugging("Unknown handler schedule type: $handler->schedule");
-                $failedcount ++;
-                continue;
-            }
-
-            // if even type is not instant, or dispatch asked for retry, queue it
-            $event = new stdClass();
-            $event->userid      = $USER->id;
-            $event->eventdata   = base64_encode(serialize($eventdata));
-            $event->timecreated = time();
-            if (debugging()) {
-                $dump = '';
-                $callers = debug_backtrace();
-                foreach ($callers as $caller) {
-                    if (!isset($caller['line'])) {
-                        $caller['line'] = '?';
-                    }
-                    if (!isset($caller['file'])) {
-                        $caller['file'] = '?';
-                    }
-                    $dump .= 'line ' . $caller['line'] . ' of ' . substr($caller['file'], strlen($CFG->dirroot) + 1);
-                    if (isset($caller['function'])) {
-                        $dump .= ': call to ';
-                        if (isset($caller['class'])) {
-                            $dump .= $caller['class'] . $caller['type'];
-                        }
-                        $dump .= $caller['function'] . '()';
-                    }
-                    $dump .= "\n";
-                }
-                $event->stackdump = $dump;
-            } else {
-                $event->stackdump = '';
-            }
-            $event->id = $DB->insert_record('events_queue', $event);
-            events_queue_handler($handler, $event, $errormessage);
-        }
-    } else {
-        // No handler found for this event name - this is ok!
-    }
-
-    return $failedcount;
+function events_trigger_legacy() {
+    throw new coding_exception('events_trigger_legacy() has been deprecated along with all Events 1 API in favour of Events 2 API.');
 }
 
 /**
- * checks if an event is registered for this component
- *
- * @access public Part of the public API
  * @deprecated since Moodle 3.1
- * @param string $eventname name of the event
- * @param string $component component name, can be mod/data or moodle
- * @return bool
  */
-function events_is_registered($eventname, $component) {
-    global $DB;
-
-    debugging('events_is_registered() has been deprecated along with all Events 1 API in favour of Events 2 API,' .
-        ' please use it instead.', DEBUG_DEVELOPER);
-
-    return $DB->record_exists('events_handlers', array('component'=>$component, 'eventname'=>$eventname));
+function events_is_registered() {
+    throw new coding_exception('events_is_registered() has been deprecated along with all Events 1 API in favour of Events 2 API.');
 }
 
 /**
- * checks if an event is queued for processing - either cron handlers attached or failed instant handlers
- *
- * @access public Part of the public API
  * @deprecated since Moodle 3.1
- * @param string $eventname name of the event
- * @return int number of queued events
  */
-function events_pending_count($eventname) {
-    global $DB;
-
-    debugging('events_pending_count() has been deprecated along with all Events 1 API in favour of Events 2 API,' .
-        ' please use it instead.', DEBUG_DEVELOPER);
-
-    $sql = "SELECT COUNT('x')
-              FROM {events_queue_handlers} qh
-              JOIN {events_handlers} h ON h.id = qh.handlerid
-             WHERE h.eventname = ?";
-
-    return $DB->count_records_sql($sql, array($eventname));
+function events_pending_count() {
+    throw new coding_exception('events_pending_count() has been deprecated along with all Events 1 API in favour of Events 2 API.');
 }
 
 /**
@@ -6613,3 +6180,136 @@ function groups_get_all_groups_for_courses($courses) {
 
     return $groups;
 }
+
+/**
+ * Gets the capabilities that have been cached in the database for this
+ * component.
+ * @deprecated since Moodle 3.6. Please use the Events 2 API.
+ * @todo final deprecation. To be removed in Moodle 4.0
+ *
+ * @access protected To be used from eventslib only
+ *
+ * @param string $component examples: 'moodle', 'mod_forum', 'block_quiz_results'
+ * @return array of events
+ */
+function events_get_cached($component) {
+    global $DB;
+
+    debugging('Events API using $handlers array has been deprecated in favour of Events 2 API, please use it instead.',
+            DEBUG_DEVELOPER);
+
+    $cachedhandlers = array();
+
+    if ($storedhandlers = $DB->get_records('events_handlers', array('component'=>$component))) {
+        foreach ($storedhandlers as $handler) {
+            $cachedhandlers[$handler->eventname] = array (
+                'id'              => $handler->id,
+                'handlerfile'     => $handler->handlerfile,
+                'handlerfunction' => $handler->handlerfunction,
+                'schedule'        => $handler->schedule,
+                'internal'        => $handler->internal);
+        }
+    }
+
+    return $cachedhandlers;
+}
+
+/**
+ * Remove all event handlers and queued events
+ * @deprecated since Moodle 3.6. Please use the Events 2 API.
+ * @todo final deprecation. To be removed in Moodle 4.0
+ *
+ * @category event
+ * @param string $component examples: 'moodle', 'mod_forum', 'block_quiz_results'
+ */
+function events_uninstall($component) {
+    debugging('Events API using $handlers array has been deprecated in favour of Events 2 API, please use it instead.',
+            DEBUG_DEVELOPER);
+    $cachedhandlers = events_get_cached($component);
+    events_cleanup($component, $cachedhandlers);
+
+    events_get_handlers('reset');
+}
+
+/**
+ * Deletes cached events that are no longer needed by the component.
+ * @deprecated since Moodle 3.6. Please use the Events 2 API.
+ * @todo final deprecation. To be removed in Moodle 4.0
+ *
+ * @access protected To be used from eventslib only
+ *
+ * @param string $component examples: 'moodle', 'mod_forum', 'block_quiz_results'
+ * @param array $cachedhandlers array of the cached events definitions that will be
+ * @return int number of unused handlers that have been removed
+ */
+function events_cleanup($component, $cachedhandlers) {
+    global $DB;
+    debugging('Events API using $handlers array has been deprecated in favour of Events 2 API, please use it instead.',
+            DEBUG_DEVELOPER);
+    $deletecount = 0;
+    foreach ($cachedhandlers as $eventname => $cachedhandler) {
+        if ($qhandlers = $DB->get_records('events_queue_handlers', array('handlerid'=>$cachedhandler['id']))) {
+            //debugging("Removing pending events from queue before deleting of event handler: $component - $eventname");
+            foreach ($qhandlers as $qhandler) {
+                events_dequeue($qhandler);
+            }
+        }
+        $DB->delete_records('events_handlers', array('eventname'=>$eventname, 'component'=>$component));
+        $deletecount++;
+    }
+
+    return $deletecount;
+}
+
+/**
+ * Removes this queued handler from the events_queued_handler table
+ *
+ * Removes events_queue record from events_queue if no more references to this event object exists
+ * @deprecated since Moodle 3.6. Please use the Events 2 API.
+ * @todo final deprecation. To be removed in Moodle 4.0
+ *
+ * @access protected To be used from eventslib only
+ *
+ * @param stdClass $qhandler A row from the events_queued_handler table
+ */
+function events_dequeue($qhandler) {
+    global $DB;
+    debugging('Events API using $handlers array has been deprecated in favour of Events 2 API, please use it instead.',
+            DEBUG_DEVELOPER);
+    // first delete the queue handler
+    $DB->delete_records('events_queue_handlers', array('id'=>$qhandler->id));
+
+    // if no more queued handler is pointing to the same event - delete the event too
+    if (!$DB->record_exists('events_queue_handlers', array('queuedeventid'=>$qhandler->queuedeventid))) {
+        $DB->delete_records('events_queue', array('id'=>$qhandler->queuedeventid));
+    }
+}
+
+/**
+ * Returns handlers for given event. Uses caching for better perf.
+ * @deprecated since Moodle 3.6. Please use the Events 2 API.
+ * @todo final deprecation. To be removed in Moodle 4.0
+ *
+ * @access protected To be used from eventslib only
+ *
+ * @staticvar array $handlers
+ * @param string $eventname name of event or 'reset'
+ * @return array|false array of handlers or false otherwise
+ */
+function events_get_handlers($eventname) {
+    global $DB;
+    static $handlers = array();
+    debugging('Events API using $handlers array has been deprecated in favour of Events 2 API, please use it instead.',
+            DEBUG_DEVELOPER);
+
+    if ($eventname === 'reset') {
+        $handlers = array();
+        return false;
+    }
+
+    if (!array_key_exists($eventname, $handlers)) {
+        $handlers[$eventname] = $DB->get_records('events_handlers', array('eventname'=>$eventname));
+    }
+
+    return $handlers[$eventname];
+}
diff --git a/lib/eventslib.php b/lib/eventslib.php
deleted file mode 100644 (file)
index 2474aa1..0000000
+++ /dev/null
@@ -1,141 +0,0 @@
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Library of functions for events manipulation.
- *
- * The public API is all at the end of this file.
- *
- * @package core
- * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die();
-
-/**
- * Gets the capabilities that have been cached in the database for this
- * component.
- *
- * @access protected To be used from eventslib only
- *
- * @param string $component examples: 'moodle', 'mod_forum', 'block_quiz_results'
- * @return array of events
- */
-function events_get_cached($component) {
-    global $DB;
-
-    $cachedhandlers = array();
-
-    if ($storedhandlers = $DB->get_records('events_handlers', array('component'=>$component))) {
-        foreach ($storedhandlers as $handler) {
-            $cachedhandlers[$handler->eventname] = array (
-                'id'              => $handler->id,
-                'handlerfile'     => $handler->handlerfile,
-                'handlerfunction' => $handler->handlerfunction,
-                'schedule'        => $handler->schedule,
-                'internal'        => $handler->internal);
-        }
-    }
-
-    return $cachedhandlers;
-}
-
-/**
- * Remove all event handlers and queued events
- *
- * @category event
- * @param string $component examples: 'moodle', 'mod_forum', 'block_quiz_results'
- */
-function events_uninstall($component) {
-    $cachedhandlers = events_get_cached($component);
-    events_cleanup($component, $cachedhandlers);
-
-    events_get_handlers('reset');
-}
-
-/**
- * Deletes cached events that are no longer needed by the component.
- *
- * @access protected To be used from eventslib only
- *
- * @param string $component examples: 'moodle', 'mod_forum', 'block_quiz_results'
- * @param array $cachedhandlers array of the cached events definitions that will be
- * @return int number of unused handlers that have been removed
- */
-function events_cleanup($component, $cachedhandlers) {
-    global $DB;
-
-    $deletecount = 0;
-    foreach ($cachedhandlers as $eventname => $cachedhandler) {
-        if ($qhandlers = $DB->get_records('events_queue_handlers', array('handlerid'=>$cachedhandler['id']))) {
-            //debugging("Removing pending events from queue before deleting of event handler: $component - $eventname");
-            foreach ($qhandlers as $qhandler) {
-                events_dequeue($qhandler);
-            }
-        }
-        $DB->delete_records('events_handlers', array('eventname'=>$eventname, 'component'=>$component));
-        $deletecount++;
-    }
-
-    return $deletecount;
-}
-
-/**
- * Removes this queued handler from the events_queued_handler table
- *
- * Removes events_queue record from events_queue if no more references to this event object exists
- *
- * @access protected To be used from eventslib only
- *
- * @param stdClass $qhandler A row from the events_queued_handler table
- */
-function events_dequeue($qhandler) {
-    global $DB;
-
-    // first delete the queue handler
-    $DB->delete_records('events_queue_handlers', array('id'=>$qhandler->id));
-
-    // if no more queued handler is pointing to the same event - delete the event too
-    if (!$DB->record_exists('events_queue_handlers', array('queuedeventid'=>$qhandler->queuedeventid))) {
-        $DB->delete_records('events_queue', array('id'=>$qhandler->queuedeventid));
-    }
-}
-
-/**
- * Returns handlers for given event. Uses caching for better perf.
- *
- * @access protected To be used from eventslib only
- *
- * @staticvar array $handlers
- * @param string $eventname name of event or 'reset'
- * @return array|false array of handlers or false otherwise
- */
-function events_get_handlers($eventname) {
-    global $DB;
-    static $handlers = array();
-
-    if ($eventname === 'reset') {
-        $handlers = array();
-        return false;
-    }
-
-    if (!array_key_exists($eventname, $handlers)) {
-        $handlers[$eventname] = $DB->get_records('events_handlers', array('eventname'=>$eventname));
-    }
-
-    return $handlers[$eventname];
-}
index cb31417..9e37c42 100644 (file)
@@ -1401,7 +1401,7 @@ function remove_grade_letters($context, $showfeedback) {
 
     $records = $DB->get_records('grade_letters', array('contextid' => $context->id));
     foreach ($records as $record) {
-        $DB->delete_record('grade_letters', array('id' => $record->id));
+        $DB->delete_records('grade_letters', array('id' => $record->id));
         // Trigger the letter grade deleted event.
         $event = \core\event\grade_letter_deleted::create(array(
             'objectid' => $record->id,
@@ -1473,7 +1473,7 @@ function grade_course_category_delete($categoryid, $newparentid, $showfeedback)
     $context = context_coursecat::instance($categoryid);
     $records = $DB->get_records('grade_letters', array('contextid' => $context->id));
     foreach ($records as $record) {
-        $DB->delete_record('grade_letters', array('id' => $record->id));
+        $DB->delete_records('grade_letters', array('id' => $record->id));
         // Trigger the letter grade deleted event.
         $event = \core\event\grade_letter_deleted::create(array(
             'objectid' => $record->id,
diff --git a/lib/medialib.php b/lib/medialib.php
deleted file mode 100644 (file)
index 3c51122..0000000
+++ /dev/null
@@ -1,152 +0,0 @@
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Deprecated classes and constants.
- *
- * DO NOT INCLUDE THIS FILE
- *
- * use $CFG->media_default_width instead of CORE_MEDIA_VIDEO_WIDTH,
- * $CFG->media_default_height instead of CORE_MEDIA_VIDEO_HEIGHT,
- * core_media_manager::instance() instead of static methods in core_media,
- * core_media_manager::OPTION_zzz instead of core_media::OPTION_zzz
- *
- * New syntax to include media files:
- *
- * $mediamanager = core_media_manager::instance();
- * echo $mediamanager->embed_url(new moodle_url('http://example.org/a.mp3'));
- *
- * @package core_media
- * @copyright 2012 The Open University
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die();
-
-if (!defined('CORE_MEDIA_VIDEO_WIDTH')) {
-    // Default video width if no width is specified; some players may do something
-    // more intelligent such as use real video width.
-    // May be defined in config.php if required.
-    define('CORE_MEDIA_VIDEO_WIDTH', 400);
-}
-if (!defined('CORE_MEDIA_VIDEO_HEIGHT')) {
-    // Default video height. May be defined in config.php if required.
-    define('CORE_MEDIA_VIDEO_HEIGHT', 300);
-}
-if (!defined('CORE_MEDIA_AUDIO_WIDTH')) {
-    // Default audio width if no width is specified.
-    // May be defined in config.php if required.
-    define('CORE_MEDIA_AUDIO_WIDTH', 300);
-}
-
-debugging('Do not include lib/medialib.php, use $CFG->media_default_width instead of CORE_MEDIA_VIDEO_WIDTH, ' .
-    '$CFG->media_default_height instead of CORE_MEDIA_VIDEO_HEIGHT, ' .
-    'core_media_manager::instance() instead of static methods in core_media, ' .
-    'core_media_manager::OPTION_zzz instead of core_media::OPTION_zzz',
-    DEBUG_DEVELOPER);
-
-/**
- * Constants and static utility functions for use with core_media_renderer.
- *
- * @deprecated since Moodle 3.2
- *
- * @copyright 2011 The Open University
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-abstract class core_media {
-    /**
-     * Option: Disable text link fallback.
-     *
-     * Use this option if you are going to print a visible link anyway so it is
-     * pointless to have one as fallback.
-     *
-     * To enable, set value to true.
-     */
-    const OPTION_NO_LINK = 'nolink';
-
-    /**
-     * Option: When embedding, if there is no matching embed, do not use the
-     * default link fallback player; instead return blank.
-     *
-     * This is different from OPTION_NO_LINK because this option still uses the
-     * fallback link if there is some kind of embedding. Use this option if you
-     * are going to check if the return value is blank and handle it specially.
-     *
-     * To enable, set value to true.
-     */
-    const OPTION_FALLBACK_TO_BLANK = 'embedorblank';
-
-    /**
-     * Option: Enable players which are only suitable for use when we trust the
-     * user who embedded the content.
-     *
-     * At present, this option enables the SWF player.
-     *
-     * To enable, set value to true.
-     */
-    const OPTION_TRUSTED = 'trusted';
-
-    /**
-     * Option: Put a div around the output (if not blank) so that it displays
-     * as a block using the 'resourcecontent' CSS class.
-     *
-     * To enable, set value to true.
-     */
-    const OPTION_BLOCK = 'block';
-
-    /**
-     * Given a string containing multiple URLs separated by #, this will split
-     * it into an array of moodle_url objects suitable for using when calling
-     * embed_alternatives.
-     *
-     * Note that the input string should NOT be html-escaped (i.e. if it comes
-     * from html, call html_entity_decode first).
-     *
-     * @param string $combinedurl String of 1 or more alternatives separated by #
-     * @param int $width Output variable: width (will be set to 0 if not specified)
-     * @param int $height Output variable: height (0 if not specified)
-     * @return array Array of 1 or more moodle_url objects
-     */
-    public static function split_alternatives($combinedurl, &$width, &$height) {
-        return core_media_manager::instance()->split_alternatives($combinedurl, $width, $height);
-    }
-
-    /**
-     * Returns the file extension for a URL.
-     * @param moodle_url $url URL
-     */
-    public static function get_extension(moodle_url $url) {
-        return core_media_manager::instance()->get_extension($url);
-    }
-
-    /**
-     * Obtains the filename from the moodle_url.
-     * @param moodle_url $url URL
-     * @return string Filename only (not escaped)
-     */
-    public static function get_filename(moodle_url $url) {
-        return core_media_manager::instance()->get_filename($url);
-    }
-
-    /**
-     * Guesses MIME type for a moodle_url based on file extension.
-     * @param moodle_url $url URL
-     * @return string MIME type
-     */
-    public static function get_mimetype(moodle_url $url) {
-        return core_media_manager::instance()->get_mimetype($url);
-    }
-}
index 1223ef6..1736379 100644 (file)
@@ -2681,29 +2681,12 @@ EOD;
     }
 
     /**
-     * Returns HTML to display the 'Update this Modulename' button that appears on module pages.
-     *
      * @deprecated since Moodle 3.2
-     *
-     * @param string $cmid the course_module id.
-     * @param string $modulename the module name, eg. "forum", "quiz" or "workshop"
-     * @return string the HTML for the button, if this user has permission to edit it, else an empty string.
      */
-    public function update_module_button($cmid, $modulename) {
-        global $CFG;
-
-        debugging('core_renderer::update_module_button() has been deprecated and should not be used anymore. Activity modules ' .
-            'should not add the edit module button, the link is already available in the Administration block. Themes can choose ' .
-            'to display the link in the buttons row consistently for all module types.', DEBUG_DEVELOPER);
-
-        if (has_capability('moodle/course:manageactivities', context_module::instance($cmid))) {
-            $modulename = get_string('modulename', $modulename);
-            $string = get_string('updatethis', '', $modulename);
-            $url = new moodle_url("$CFG->wwwroot/course/mod.php", array('update' => $cmid, 'return' => true, 'sesskey' => sesskey()));
-            return $this->single_button($url, $string);
-        } else {
-            return '';
-        }
+    public function update_module_button() {
+        throw new coding_exception('core_renderer::update_module_button() can not be used anymore. Activity ' .
+            'modules should not add the edit module button, the link is already available in the Administration block. ' .
+            'Themes can choose to display the link in the buttons row consistently for all module types.');
     }
 
     /**
@@ -4739,133 +4722,6 @@ class core_renderer_ajax extends core_renderer {
 }
 
 
-/**
- * Renderer for media files.
- *
- * Used in file resources, media filter, and any other places that need to
- * output embedded media.
- *
- * @deprecated since Moodle 3.2
- * @copyright 2011 The Open University
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class core_media_renderer extends plugin_renderer_base {
-    /** @var array Array of available 'player' objects */
-    private $players;
-    /** @var string Regex pattern for links which may contain embeddable content */
-    private $embeddablemarkers;
-
-    /**
-     * Constructor
-     *
-     * This is needed in the constructor (not later) so that you can use the
-     * constants and static functions that are defined in core_media class
-     * before you call renderer functions.
-     */
-    public function __construct() {
-        debugging('Class core_media_renderer is deprecated, please use core_media_manager::instance()', DEBUG_DEVELOPER);
-    }
-
-    /**
-     * Renders a media file (audio or video) using suitable embedded player.
-     *
-     * See embed_alternatives function for full description of parameters.
-     * This function calls through to that one.
-     *
-     * When using this function you can also specify width and height in the
-     * URL by including ?d=100x100 at the end. If specified in the URL, this
-     * will override the $width and $height parameters.
-     *
-     * @param moodle_url $url Full URL of media file
-     * @param string $name Optional user-readable name to display in download link
-     * @param int $width Width in pixels (optional)
-     * @param int $height Height in pixels (optional)
-     * @param array $options Array of key/value pairs
-     * @return string HTML content of embed
-     */
-    public function embed_url(moodle_url $url, $name = '', $width = 0, $height = 0,
-            $options = array()) {
-        return core_media_manager::instance()->embed_url($url, $name, $width, $height, $options);
-    }
-
-    /**
-     * Renders media files (audio or video) using suitable embedded player.
-     * The list of URLs should be alternative versions of the same content in
-     * multiple formats. If there is only one format it should have a single
-     * entry.
-     *
-     * If the media files are not in a supported format, this will give students
-     * a download link to each format. The download link uses the filename
-     * unless you supply the optional name parameter.
-     *
-     * Width and height are optional. If specified, these are suggested sizes
-     * and should be the exact values supplied by the user, if they come from
-     * user input. These will be treated as relating to the size of the video
-     * content, not including any player control bar.
-     *
-     * For audio files, height will be ignored. For video files, a few formats
-     * work if you specify only width, but in general if you specify width
-     * you must specify height as well.
-     *
-     * The $options array is passed through to the core_media_player classes
-     * that render the object tag. The keys can contain values from
-     * core_media::OPTION_xx.
-     *
-     * @param array $alternatives Array of moodle_url to media files
-     * @param string $name Optional user-readable name to display in download link
-     * @param int $width Width in pixels (optional)
-     * @param int $height Height in pixels (optional)
-     * @param array $options Array of key/value pairs
-     * @return string HTML content of embed
-     */
-    public function embed_alternatives($alternatives, $name = '', $width = 0, $height = 0,
-            $options = array()) {
-        return core_media_manager::instance()->embed_alternatives($alternatives, $name, $width, $height, $options);
-    }
-
-    /**
-     * Checks whether a file can be embedded. If this returns true you will get
-     * an embedded player; if this returns false, you will just get a download
-     * link.
-     *
-     * This is a wrapper for can_embed_urls.
-     *
-     * @param moodle_url $url URL of media file
-     * @param array $options Options (same as when embedding)
-     * @return bool True if file can be embedded
-     */
-    public function can_embed_url(moodle_url $url, $options = array()) {
-        return core_media_manager::instance()->can_embed_url($url, $options);
-    }
-
-    /**
-     * Checks whether a file can be embedded. If this returns true you will get
-     * an embedded player; if this returns false, you will just get a download
-     * link.
-     *
-     * @param array $urls URL of media file and any alternatives (moodle_url)
-     * @param array $options Options (same as when embedding)
-     * @return bool True if file can be embedded
-     */
-    public function can_embed_urls(array $urls, $options = array()) {
-        return core_media_manager::instance()->can_embed_urls($urls, $options);
-    }
-
-    /**
-     * Obtains a list of markers that can be used in a regular expression when
-     * searching for URLs that can be embedded by any player type.
-     *
-     * This string is used to improve peformance of regex matching by ensuring
-     * that the (presumably C) regex code can do a quick keyword check on the
-     * URL part of a link to see if it matches one of these, rather than having
-     * to go into PHP code for every single link to see if it can be embedded.
-     *
-     * @return string String suitable for use in regex such as '(\.mp4|\.flv)'
-     */
-    public function get_embeddable_markers() {
-        return core_media_manager::instance()->get_embeddable_markers();
-    }
-}
 
 /**
  * The maintenance renderer.
index 3252bf6..965ce0f 100644 (file)
@@ -210,7 +210,6 @@ class phpunit_util extends testing_util {
         accesslib_clear_all_caches(true);
         get_string_manager()->reset_caches(true);
         reset_text_filters_cache(true);
-        events_get_handlers('reset');
         core_text::reset_caches();
         get_message_processors(false, true, true);
         filter_manager::reset_caches();
index 8ee0219..1fc52f0 100644 (file)
@@ -606,7 +606,6 @@ require_once($CFG->libdir .'/moodlelib.php');       // Other general-purpose fun
 require_once($CFG->libdir .'/enrollib.php');        // Enrolment related functions
 require_once($CFG->libdir .'/pagelib.php');         // Library that defines the moodle_page class, used for $PAGE
 require_once($CFG->libdir .'/blocklib.php');        // Library for controlling blocks
-require_once($CFG->libdir .'/eventslib.php');       // Events functions
 require_once($CFG->libdir .'/grouplib.php');        // Groups functions
 require_once($CFG->libdir .'/sessionlib.php');      // All session and cookie related stuff
 require_once($CFG->libdir .'/editorlib.php');       // All text editor related functions and classes
index 54b1099..1b7e280 100644 (file)
@@ -797,6 +797,83 @@ class core_coursecatlib_testcase extends advanced_testcase {
         $this->assertEquals("{$cat1name} / {$cat2name} / {$cat4name}", $category4->get_nested_name(false));
     }
 
+    public function test_coursecat_is_uservisible() {
+        global $USER;
+
+        // Create category 1 as visible.
+        $category1 = coursecat::create(array('name' => 'Cat1', 'visible' => 1));
+        // Create category 2 as hidden.
+        $category2 = coursecat::create(array('name' => 'Cat2', 'visible' => 0));
+
+        $this->assertTrue($category1->is_uservisible());
+        $this->assertFalse($category2->is_uservisible());
+
+        $this->assign_capability('moodle/category:viewhiddencategories');
+
+        $this->assertTrue($category1->is_uservisible());
+        $this->assertTrue($category2->is_uservisible());
+
+        // First, store current user's id, then login as another user.
+        $userid = $USER->id;
+        $this->setUser($this->getDataGenerator()->create_user());
+
+        // User $user should still have the moodle/category:viewhiddencategories capability.
+        $this->assertTrue($category1->is_uservisible($userid));
+        $this->assertTrue($category2->is_uservisible($userid));
+
+        $this->assign_capability('moodle/category:viewhiddencategories', CAP_INHERIT);
+
+        $this->assertTrue($category1->is_uservisible());
+        $this->assertFalse($category2->is_uservisible());
+    }
+
+    public function test_current_user_coursecat_get() {
+        $this->assign_capability('moodle/category:viewhiddencategories');
+
+        // Create category 1 as visible.
+        $category1 = coursecat::create(array('name' => 'Cat1', 'visible' => 1));
+        // Create category 2 as hidden.
+        $category2 = coursecat::create(array('name' => 'Cat2', 'visible' => 0));
+
+        $this->assertEquals($category1->id, coursecat::get($category1->id)->id);
+        $this->assertEquals($category2->id, coursecat::get($category2->id)->id);
+
+        // Login as another user to test coursecat::get.
+        $this->setUser($this->getDataGenerator()->create_user());
+        $this->assertEquals($category1->id, coursecat::get($category1->id)->id);
+
+        // Expecting to get an exception as this new user does not have the moodle/category:viewhiddencategories capability.
+        $this->expectException('moodle_exception');
+        $this->expectExceptionMessage('unknowncategory');
+        coursecat::get($category2->id);
+    }
+
+    public function test_another_user_coursecat_get() {
+        global $USER;
+
+        $this->assign_capability('moodle/category:viewhiddencategories');
+
+        // Create category 1 as visible.
+        $category1 = coursecat::create(array('name' => 'Cat1', 'visible' => 1));
+        // Create category 2 as hidden.
+        $category2 = coursecat::create(array('name' => 'Cat2', 'visible' => 0));
+
+        // First, store current user's object, then login as another user.
+        $user1 = $USER;
+        $user2 = $this->getDataGenerator()->create_user();
+        $this->setUser($user2);
+
+        $this->assertEquals($category1->id, coursecat::get($category1->id, MUST_EXIST, false, $user1)->id);
+        $this->assertEquals($category2->id, coursecat::get($category2->id, MUST_EXIST, false, $user1)->id);
+
+        $this->setUser($user1);
+
+        $this->assertEquals($category1->id, coursecat::get($category1->id, MUST_EXIST, false, $user2)->id);
+        $this->expectException('moodle_exception');
+        $this->expectExceptionMessage('unknowncategory');
+        coursecat::get($category2->id, MUST_EXIST, false, $user2);
+    }
+
     /**
      * Creates a draft area for current user and fills it with fake files
      *
index a2b0df2..8c094f3 100644 (file)
@@ -592,10 +592,12 @@ class core_event_testcase extends advanced_testcase {
         );
 
         $DB->delete_records('log', array());
+        $this->expectException('coding_exception');
         events_update_definition('unittest');
-        $this->assertDebuggingCalled(self::DEBUGGING_MSG, DEBUG_DEVELOPER);
+
         $DB->delete_records_select('events_handlers', "component <> 'unittest'");
         events_get_handlers('reset');
+        $this->assertDebuggingCalled(self::DEBUGGING_MSG, DEBUG_DEVELOPER);
         $this->assertEquals(3, $DB->count_records('events_handlers'));
         set_config('loglifetime', 60*60*24*5);
 
@@ -604,20 +606,17 @@ class core_event_testcase extends advanced_testcase {
 
         $event1 = \core_tests\event\unittest_executed::create(array('context'=>\context_system::instance(), 'other'=>array('sample'=>5, 'xx'=>10)));
         $event1->trigger();
-        $this->assertDebuggingCalled(self::DEBUGGING_MSG, DEBUG_DEVELOPER);
 
         $event2 = \core_tests\event\unittest_executed::create(array('context'=>\context_system::instance(), 'other'=>array('sample'=>6, 'xx'=>11)));
         $event2->nest = true;
         $event2->trigger();
-        $this->assertDebuggingCalledCount(2, array(self::DEBUGGING_MSG, self::DEBUGGING_MSG), array(DEBUG_DEVELOPER, DEBUG_DEVELOPER));
 
         $this->assertSame(
-            array('observe_all-5', 'observe_one-5', 'legacy_handler-0', 'observe_all-nesting-6', 'legacy_handler-0', 'observe_one-6', 'observe_all-666', 'observe_one-666', 'legacy_handler-0'),
+            array('observe_all-5', 'observe_one-5', 'observe_all-nesting-6', 'observe_one-6', 'observe_all-666', 'observe_one-666'),
             \core_tests\event\unittest_observer::$info);
 
         $this->assertSame($event1, \core_tests\event\unittest_observer::$event[0]);
         $this->assertSame($event1, \core_tests\event\unittest_observer::$event[1]);
-        $this->assertSame(array(0, 5), \core_tests\event\unittest_observer::$event[2]);
 
         $logs = $DB->get_records('log', array(), 'id ASC');
         $this->assertCount(0, $logs);
diff --git a/lib/tests/eventslib_test.php b/lib/tests/eventslib_test.php
deleted file mode 100644 (file)
index c62d5cf..0000000
+++ /dev/null
@@ -1,303 +0,0 @@
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Tests events subsystem.
- *
- * @package    core_event
- * @subpackage phpunit
- * @copyright  2007 onwards Martin Dougiamas (http://dougiamas.com)
- * @author     Petr Skoda {@link http://skodak.org}
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die();
-
-
-class core_eventslib_testcase extends advanced_testcase {
-
-    const DEBUGGING_MSG = 'Events API using $handlers array has been deprecated in favour of Events 2 API, please use it instead.';
-
-    /**
-     * Create temporary entries in the database for these tests.
-     * These tests have to work no matter the data currently in the database
-     * (meaning they should run on a brand new site). This means several items of
-     * data have to be artificially inseminated (:-) in the DB.
-     */
-    protected function setUp() {
-        parent::setUp();
-        // Set global category settings to -1 (not force).
-        eventslib_sample_function_handler('reset');
-        eventslib_sample_handler_class::static_method('reset');
-
-        $this->resetAfterTest();
-    }
-
-    /**
-     * Tests the installation of event handlers from file
-     */
-    public function test_events_update_definition__install() {
-        global $DB;
-
-        events_update_definition('unittest');
-        $this->assertDebuggingCalled(self::DEBUGGING_MSG, DEBUG_DEVELOPER);
-
-        $dbcount = $DB->count_records('events_handlers', array('component'=>'unittest'));
-        $handlers = array();
-        require(__DIR__.'/fixtures/events.php');
-        $this->assertCount($dbcount, $handlers, 'Equal number of handlers in file and db: %s');
-    }
-
-    /**
-     * Tests the uninstallation of event handlers from file.
-     */
-    public function test_events_update_definition__uninstall() {
-        global $DB;
-
-        events_update_definition('unittest');
-        $this->assertDebuggingCalled(self::DEBUGGING_MSG, DEBUG_DEVELOPER);
-
-        events_uninstall('unittest');
-        $this->assertEquals(0, $DB->count_records('events_handlers', array('component'=>'unittest')), 'All handlers should be uninstalled: %s');
-    }
-
-    /**
-     * Tests the update of event handlers from file.
-     */
-    public function test_events_update_definition__update() {
-        global $DB;
-
-        events_update_definition('unittest');
-        $this->assertDebuggingCalled(self::DEBUGGING_MSG, DEBUG_DEVELOPER);
-
-        // First modify directly existing handler.
-        $handler = $DB->get_record('events_handlers', array('component'=>'unittest', 'eventname'=>'test_instant'));
-
-        $original = $handler->handlerfunction;
-
-        // Change handler in db.
-        $DB->set_field('events_handlers', 'handlerfunction', serialize('some_other_function_handler'), array('id'=>$handler->id));
-
-        // Update the definition, it should revert the handler back.
-        events_update_definition('unittest');
-        $this->assertDebuggingCalled(self::DEBUGGING_MSG, DEBUG_DEVELOPER);
-        $handler = $DB->get_record('events_handlers', array('component'=>'unittest', 'eventname'=>'test_instant'));
-        $this->assertSame($handler->handlerfunction, $original, 'update should sync db with file definition: %s');
-    }
-
-    /**
-     * Tests events_trigger_is_registered() function.
-     */
-    public function test_events_is_registered() {
-
-        events_update_definition('unittest');
-        $this->assertDebuggingCalled(self::DEBUGGING_MSG, DEBUG_DEVELOPER);
-
-        $this->assertTrue(events_is_registered('test_instant', 'unittest'));
-        $this->assertDebuggingCalled('events_is_registered() has been deprecated along with all Events 1 API in favour of Events 2' .
-            ' API, please use it instead.', DEBUG_DEVELOPER);
-    }
-
-    /**
-     * Tests events_trigger_legacy() function.
-     */
-    public function test_events_trigger_legacy_instant() {
-
-        events_update_definition('unittest');
-        $this->assertDebuggingCalled(self::DEBUGGING_MSG, DEBUG_DEVELOPER);
-
-        $this->assertEquals(0, events_trigger_legacy('test_instant', 'ok'));
-        $this->assertDebuggingCalled(self::DEBUGGING_MSG, DEBUG_DEVELOPER);
-        $this->assertEquals(0, events_trigger_legacy('test_instant', 'ok'));
-        $this->assertDebuggingCalled(self::DEBUGGING_MSG, DEBUG_DEVELOPER);
-        $this->assertEquals(2, eventslib_sample_function_handler('status'));
-    }
-
-    /**
-     * Tests events_trigger_legacy() function.
-     */
-    public function test_events_trigger__cron() {
-
-        events_update_definition('unittest');
-        $this->assertDebuggingCalled(self::DEBUGGING_MSG, DEBUG_DEVELOPER);
-
-        $this->assertEquals(0, events_trigger_legacy('test_cron', 'ok'));
-        $this->assertEquals(0, eventslib_sample_handler_class::static_method('status'));
-        events_cron('test_cron');
-        // The events_cron one + one for each triggered event above (triggered in events_dispatch).
-        $this->assertDebuggingCalledCount(2, array(self::DEBUGGING_MSG, self::DEBUGGING_MSG),
-            array(DEBUG_DEVELOPER, DEBUG_DEVELOPER));
-        $this->assertEquals(1, eventslib_sample_handler_class::static_method('status'));
-    }
-
-    /**
-     * Tests events_pending_count() function.
-     */
-    public function test_events_pending_count() {
-
-        events_update_definition('unittest');
-        $this->assertDebuggingCalled(self::DEBUGGING_MSG, DEBUG_DEVELOPER);
-
-        events_trigger_legacy('test_cron', 'ok');
-        $this->assertDebuggingNotCalled();
-        events_trigger_legacy('test_cron', 'ok');
-        $this->assertDebuggingNotCalled();
-        events_cron('test_cron');
-        // The events_cron one + one for each triggered event above (triggered in events_dispatch).
-        $this->assertDebuggingCalledCount(3);
-        $this->assertEquals(0, events_pending_count('test_cron'), 'all messages should be already dequeued: %s');
-        $this->assertDebuggingCalled('events_pending_count() has been deprecated along with all Events 1 API in favour of Events 2' .
-            ' API, please use it instead.', DEBUG_DEVELOPER);
-    }
-
-    /**
-     * Tests events_trigger_legacy() function when instant handler fails.
-     */
-    public function test_events_trigger__failed_instant() {
-        global $CFG;
-
-        events_update_definition('unittest');
-        $this->assertDebuggingCalled(self::DEBUGGING_MSG, DEBUG_DEVELOPER);
-
-        $olddebug = $CFG->debug;
-
-        $this->assertEquals(1, events_trigger_legacy('test_instant', 'fail'), 'fail first event: %s');
-        $this->assertDebuggingCalled(self::DEBUGGING_MSG, DEBUG_DEVELOPER);
-        $this->assertEquals(1, events_trigger_legacy('test_instant', 'ok'), 'this one should fail too: %s');
-        $this->assertDebuggingNotCalled();
-
-        $this->assertEquals(0, events_cron('test_instant'), 'all events should stay in queue: %s');
-        // events_cron + one for each dispatched event.
-        $this->assertDebuggingCalledCount(3);
-
-        $this->assertEquals(2, events_pending_count('test_instant'), 'two events should in queue: %s');
-        $this->assertDebuggingCalled('events_pending_count() has been deprecated along with all Events 1 API in favour of Events 2' .
-            ' API, please use it instead.', DEBUG_DEVELOPER);
-
-        $this->assertEquals(0, eventslib_sample_function_handler('status'), 'verify no event dispatched yet: %s');
-        eventslib_sample_function_handler('ignorefail'); // Ignore "fail" eventdata from now on.
-        $this->assertEquals(1, events_trigger_legacy('test_instant', 'ok'), 'this one should go to queue directly: %s');
-        $this->assertDebuggingNotCalled();
-
-        $this->assertEquals(3, events_pending_count('test_instant'), 'three events should in queue: %s');
-        $this->assertDebuggingCalled('events_pending_count() has been deprecated along with all Events 1 API in favour of Events 2' .
-            ' API, please use it instead.', DEBUG_DEVELOPER);
-
-        $this->assertEquals(0, eventslib_sample_function_handler('status'), 'verify previous event was not dispatched: %s');
-        $this->assertEquals(3, events_cron('test_instant'), 'all events should be dispatched: %s');
-        // events_cron + one for each dispatched event.
-        $this->assertDebuggingCalledCount(4);
-
-        $this->assertEquals(3, eventslib_sample_function_handler('status'), 'verify three events were dispatched: %s');
-        $this->assertEquals(0, events_pending_count('test_instant'), 'no events should in queue: %s');
-        $this->assertDebuggingCalled('events_pending_count() has been deprecated along with all Events 1 API in favour of Events 2' .
-            ' API, please use it instead.', DEBUG_DEVELOPER);
-
-        $this->assertEquals(0, events_trigger_legacy('test_instant', 'ok'), 'this event should be dispatched immediately: %s');
-        $this->assertDebuggingCalled(self::DEBUGGING_MSG, DEBUG_DEVELOPER);
-
-        $this->assertEquals(4, eventslib_sample_function_handler('status'), 'verify event was dispatched: %s');
-        $this->assertEquals(0, events_pending_count('test_instant'), 'no events should in queue: %s');
-        $this->assertDebuggingCalled('events_pending_count() has been deprecated along with all Events 1 API in favour of Events 2' .
-            ' API, please use it instead.', DEBUG_DEVELOPER);
-    }
-
-    /**
-     * Tests events_trigger() function.
-     */
-    public function test_events_trigger_debugging() {
-
-        events_update_definition('unittest');
-        $this->assertDebuggingCalled(self::DEBUGGING_MSG, DEBUG_DEVELOPER);
-
-        $this->assertEquals(0, events_trigger('test_instant', 'ok'));
-        $debugmessages = array('events_trigger() is deprecated, please use new events instead', self::DEBUGGING_MSG);
-        $this->assertDebuggingCalledCount(2, $debugmessages, array(DEBUG_DEVELOPER, DEBUG_DEVELOPER));
-    }
-}
-
-/**
- * Test handler function.
- */
-function eventslib_sample_function_handler($eventdata) {
-    static $called = 0;
-    static $ignorefail = false;
-
-    if ($eventdata == 'status') {
-        return $called;
-
-    } else if ($eventdata == 'reset') {
-        $called = 0;
-        $ignorefail = false;
-        return;
-
-    } else if ($eventdata == 'fail') {
-        if ($ignorefail) {
-            $called++;
-            return true;
-        } else {
-            return false;
-        }
-
-    } else if ($eventdata == 'ignorefail') {
-        $ignorefail = true;
-        return;
-
-    } else if ($eventdata == 'ok') {
-        $called++;
-        return true;
-    }
-
-    print_error('invalideventdata', '', '', $eventdata);
-}
-
-
-/**
- * Test handler class with static method.
- */
-class eventslib_sample_handler_class {
-    public static function static_method($eventdata) {
-        static $called = 0;
-        static $ignorefail = false;
-
-        if ($eventdata == 'status') {
-            return $called;
-
-        } else if ($eventdata == 'reset') {
-            $called = 0;
-            $ignorefail = false;
-            return;
-
-        } else if ($eventdata == 'fail') {
-            if ($ignorefail) {
-                $called++;
-                return true;
-            } else {
-                return false;
-            }
-
-        } else if ($eventdata == 'ignorefail') {
-            $ignorefail = true;
-            return;
-
-        } else if ($eventdata == 'ok') {
-            $called++;
-            return true;
-        }
-
-        print_error('invalideventdata', '', '', $eventdata);
-    }
-}
index f178315..fcc42ad 100644 (file)
@@ -56,4 +56,52 @@ class core_gradelib_testcase extends advanced_testcase {
 
         $this->assertTrue(grade_update_mod_grades($modinstance));
     }
+
+    /**
+     * Tests the function remove_grade_letters().
+     */
+    public function test_remove_grade_letters() {
+        global $DB;
+
+        $this->resetAfterTest();
+
+        $course = $this->getDataGenerator()->create_course();
+
+        $context = context_course::instance($course->id);
+
+        // Add a grade letter to the course.
+        $letter = new stdClass();
+        $letter->letter = 'M';
+        $letter->lowerboundary = '100';
+        $letter->contextid = $context->id;
+        $DB->insert_record('grade_letters', $letter);
+
+        remove_grade_letters($context, false);
+
+        // Confirm grade letter was deleted.
+        $this->assertEquals(0, $DB->count_records('grade_letters'));
+    }
+
+    /**
+     * Tests the function grade_course_category_delete().
+     */
+    public function test_grade_course_category_delete() {
+        global $DB;
+
+        $this->resetAfterTest();
+
+        $category = coursecat::create(array('name' => 'Cat1'));
+
+        // Add a grade letter to the category.
+        $letter = new stdClass();
+        $letter->letter = 'M';
+        $letter->lowerboundary = '100';
+        $letter->contextid = context_coursecat::instance($category->id)->id;
+        $DB->insert_record('grade_letters', $letter);
+
+        grade_course_category_delete($category->id, '', false);
+
+        // Confirm grade letter was deleted.
+        $this->assertEquals(0, $DB->count_records('grade_letters'));
+    }
 }
index 20e5d6d..194cc19 100644 (file)
@@ -129,7 +129,7 @@ class core_medialib_testcase extends advanced_testcase {
     }
 
     /**
-     * Test for core_media_renderer get_players
+     * Test for get_players
      */
     public function test_get_players() {
         // All players are initially disabled (except link, which you can't).
@@ -158,7 +158,7 @@ class core_medialib_testcase extends advanced_testcase {
     }
 
     /**
-     * Test for core_media_renderer can_embed_url
+     * Test for can_embed_url
      */
     public function test_can_embed_url() {
         // All players are initially disabled, so mp4 cannot be rendered.
@@ -188,7 +188,7 @@ class core_medialib_testcase extends advanced_testcase {
     }
 
     /**
-     * Test for core_media_renderer embed_url.
+     * Test for embed_url.
      * Checks multiple format/fallback support.
      */
     public function test_embed_url_fallbacks() {
@@ -264,7 +264,7 @@ class core_medialib_testcase extends advanced_testcase {
     }
 
     /**
-     * Test for core_media_renderer embed_url.
+     * Test for embed_url.
      * Check SWF works including the special option required to enable it
      */
     public function test_embed_url_swf() {
@@ -302,7 +302,7 @@ class core_medialib_testcase extends advanced_testcase {
     }
 
     /**
-     * Test for core_media_renderer embed_url.
+     * Test for embed_url.
      * Checks the EMBED_OR_BLANK option.
      */
     public function test_embed_or_blank() {
@@ -325,7 +325,7 @@ class core_medialib_testcase extends advanced_testcase {
     }
 
     /**
-     * Test for core_media_renderer embed_url.
+     * Test for embed_url.
      * Checks that size is passed through correctly to player objects and tests
      * size support in html5video output.
      */
@@ -358,7 +358,7 @@ class core_medialib_testcase extends advanced_testcase {
     }
 
     /**
-     * Test for core_media_renderer embed_url.
+     * Test for embed_url.
      * Checks that name is passed through correctly to player objects and tests
      * name support in html5video output.
      */
@@ -379,7 +379,7 @@ class core_medialib_testcase extends advanced_testcase {
     }
 
     /**
-     * Test for core_media_renderer split_alternatives.
+     * Test for split_alternatives.
      */
     public function test_split_alternatives() {
         $mediamanager = core_media_manager::instance();
@@ -416,7 +416,7 @@ class core_medialib_testcase extends advanced_testcase {
     }
 
     /**
-     * Test for core_media_renderer embed_alternatives (with multiple urls)
+     * Test for embed_alternatives (with multiple urls)
      */
     public function test_embed_alternatives() {
         // Most aspects of this are same as single player so let's just try
index 6be11a1..69c49d3 100644 (file)
@@ -7,7 +7,21 @@ information provided here is intended especially for developers.
   callback. If a string is returned, it is displayed instead of the list of suggested items. This can be used, for
   example, to inform the user that there are too many items matching the current search criteria.
 * The following functions have been finally deprecated and can not be used any more:
-  - external_function_info()
+    - external_function_info()
+    - core_renderer::update_module_button()
+    - events_trigger()
+    - events_cron()
+    - events_dispatch()
+    - events_is_registered()
+    - events_load_def()
+    - events_pending_count()
+    - events_process_queued_handler()
+    - events_queue_handler()
+    - events_trigger_legacy()
+    - events_update_definition()
+* The following classes have been finally deprecated and can not be used any more:
+    - core_media_renderer
+    - core_media
 * Following api's have been removed in behat_config_manager, please use behat_config_util instead.
     - get_features_with_tags()
     - get_components_steps_definitions()
@@ -22,6 +36,14 @@ information provided here is intended especially for developers.
     - I set the field "<field_string>" to multiline
     - I follow "<link_string>"" in the open menu
 * Removed the lib/password_compat/lib/password.php file.
+* The eventslib.php file has been deleted and its functions have been moved to deprecatedlib.php. The affected functions are:
+  - events_get_cached()
+  - events_uninstall()
+  - events_cleanup()
+  - events_dequeue()
+  - events_get_handlers()
+* coursecat::get() now has optional $user parameter.
+* coursecat::is_uservisible() now has optional $user parameter.
 * Removed the lib/form/submitlink.php element which was deprecated in 3.2.
 
 === 3.5 ===
index 8dc6d31..6f35c36 100644 (file)
@@ -431,6 +431,7 @@ function upgrade_stale_php_files_present() {
 
     $someexamplesofremovedfiles = array(
         // Removed in 3.6.
+        '/lib/medialib.php',
         '/lib/password_compat/lib/password.php',
         // Removed in 3.5.
         '/lib/dml/mssql_native_moodle_database.php',
@@ -573,7 +574,6 @@ function upgrade_plugins($type, $startcallback, $endcallback, $verbose) {
                     update_capabilities($component);
                     log_update_descriptions($component);
                     external_update_descriptions($component);
-                    events_update_definition($component);
                     \core\task\manager::reset_scheduled_tasks_for_component($component);
                     message_update_providers($component);
                     \core\message\inbound\manager::update_handlers_for_component($component);
@@ -612,7 +612,6 @@ function upgrade_plugins($type, $startcallback, $endcallback, $verbose) {
             update_capabilities($component);
             log_update_descriptions($component);
             external_update_descriptions($component);
-            events_update_definition($component);
             \core\task\manager::reset_scheduled_tasks_for_component($component);
             message_update_providers($component);
             \core\message\inbound\manager::update_handlers_for_component($component);
@@ -646,7 +645,6 @@ function upgrade_plugins($type, $startcallback, $endcallback, $verbose) {
             update_capabilities($component);
             log_update_descriptions($component);
             external_update_descriptions($component);
-            events_update_definition($component);
             \core\task\manager::reset_scheduled_tasks_for_component($component);
             message_update_providers($component);
             \core\message\inbound\manager::update_handlers_for_component($component);
@@ -754,7 +752,6 @@ function upgrade_plugins_modules($startcallback, $endcallback, $verbose) {
                     update_capabilities($component);
                     log_update_descriptions($component);
                     external_update_descriptions($component);
-                    events_update_definition($component);
                     \core\task\manager::reset_scheduled_tasks_for_component($component);
                     message_update_providers($component);
                     \core\message\inbound\manager::update_handlers_for_component($component);
@@ -789,7 +786,6 @@ function upgrade_plugins_modules($startcallback, $endcallback, $verbose) {
             update_capabilities($component);
             log_update_descriptions($component);
             external_update_descriptions($component);
-            events_update_definition($component);
             \core\task\manager::reset_scheduled_tasks_for_component($component);
             message_update_providers($component);
             \core\message\inbound\manager::update_handlers_for_component($component);
@@ -826,7 +822,6 @@ function upgrade_plugins_modules($startcallback, $endcallback, $verbose) {
             update_capabilities($component);
             log_update_descriptions($component);
             external_update_descriptions($component);
-            events_update_definition($component);
             \core\task\manager::reset_scheduled_tasks_for_component($component);
             message_update_providers($component);
             \core\message\inbound\manager::update_handlers_for_component($component);
@@ -948,7 +943,6 @@ function upgrade_plugins_blocks($startcallback, $endcallback, $verbose) {
                     update_capabilities($component);
                     log_update_descriptions($component);
                     external_update_descriptions($component);
-                    events_update_definition($component);
                     \core\task\manager::reset_scheduled_tasks_for_component($component);
                     message_update_providers($component);
                     \core\message\inbound\manager::update_handlers_for_component($component);
@@ -989,7 +983,6 @@ function upgrade_plugins_blocks($startcallback, $endcallback, $verbose) {
             update_capabilities($component);
             log_update_descriptions($component);
             external_update_descriptions($component);
-            events_update_definition($component);
             \core\task\manager::reset_scheduled_tasks_for_component($component);
             message_update_providers($component);
             \core\message\inbound\manager::update_handlers_for_component($component);
@@ -1025,7 +1018,6 @@ function upgrade_plugins_blocks($startcallback, $endcallback, $verbose) {
             update_capabilities($component);
             log_update_descriptions($component);
             external_update_descriptions($component);
-            events_update_definition($component);
             \core\task\manager::reset_scheduled_tasks_for_component($component);
             message_update_providers($component);
             \core\message\inbound\manager::update_handlers_for_component($component);
@@ -1742,7 +1734,6 @@ function install_core($version, $verbose) {
         // Continue with the installation
         log_update_descriptions('moodle');
         external_update_descriptions('moodle');
-        events_update_definition('moodle');
         \core\task\manager::reset_scheduled_tasks_for_component('moodle');
         message_update_providers('moodle');
         \core\message\inbound\manager::update_handlers_for_component('moodle');
@@ -1810,7 +1801,6 @@ function upgrade_core($version, $verbose) {
         update_capabilities('moodle');
         log_update_descriptions('moodle');
         external_update_descriptions('moodle');
-        events_update_definition('moodle');
         \core\task\manager::reset_scheduled_tasks_for_component('moodle');
         message_update_providers('moodle');
         \core\message\inbound\manager::update_handlers_for_component('moodle');
index 7171c05..407aabd 100644 (file)
@@ -154,21 +154,10 @@ abstract class core_media_player {
     public abstract function get_rank();
 
     /**
-     * Returns if the current player is enabled.
-     *
      * @deprecated since Moodle 3.2
-     * @return bool True if player is enabled
      */
     public function is_enabled() {
-        debugging('Function core_media_player::is_enabled() is deprecated without replacement', DEBUG_DEVELOPER);
-
-        $enabled = \core\plugininfo\media::get_enabled_plugins();
-
-        if ($enabled && preg_match('/^media_(.*)_plugin$/', get_class($this), $matches)) {
-            return array_key_exists($matches[1], $enabled);
-        }
-
-        return false;
+        throw new coding_exception('core_media_player::is_enabled() can not be used anymore.');
     }
 
     /**
@@ -217,15 +206,10 @@ abstract class core_media_player {
     }
 
     /**
-     * Compares by rank order, highest first. Used for sort functions.
      * @deprecated since Moodle 3.2
-     * @param core_media_player $a Player A
-     * @param core_media_player $b Player B
-     * @return int Negative if A should go before B, positive for vice versa
      */
-    public static function compare_by_rank(core_media_player $a, core_media_player $b) {
-        debugging('Function core_media_player::compare_by_rank() is deprecated without replacement', DEBUG_DEVELOPER);
-        return $b->get_rank() - $a->get_rank();
+    public static function compare_by_rank() {
+        throw new coding_exception('core_media_player::compare_by_rank() can not be used anymore.');
     }
 
     /**
index 2deef55..8e1594c 100644 (file)
@@ -1,6 +1,12 @@
 This files describes API changes in /media/ plugins,
 information provided here is intended especially for developers.
 
+=== 3.6 ===
+
+* The following functions have been finally deprecated and can not be used anymore:
+  * core_media_player::is_enabled()
+  * core_media_player::compare_by_rank()
+
 === 3.3 ===
 * core_media_manager setup() is now deprecated as it is now called when initialising core_media_manager::instance().
 * core_media_manager is now final. Do not extend core_media_manager, instead create a media plugin.
index 48c3686..2c1df4a 100644 (file)
@@ -22,8 +22,6 @@
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-require_once($CFG->libdir.'/eventslib.php');
-
 define('MESSAGE_SHORTLENGTH', 300);
 
 define('MESSAGE_HISTORY_ALL', 1);
index 5eeccf3..002404d 100644 (file)
@@ -1826,20 +1826,25 @@ function assign_check_updates_since(cm_info $cm, $from, $filter = array()) {
  * the ASSIGN_EVENT_TYPE_GRADINGDUE event will not be shown to students on their calendar.
  *
  * @param calendar_event $event
+ * @param int $userid User id to use for all capability checks, etc. Set to 0 for current user (default).
  * @return bool Returns true if the event is visible to the current user, false otherwise.
  */
-function mod_assign_core_calendar_is_event_visible(calendar_event $event) {
+function mod_assign_core_calendar_is_event_visible(calendar_event $event, $userid = 0) {
     global $CFG, $USER;
 
     require_once($CFG->dirroot . '/mod/assign/locallib.php');
 
-    $cm = get_fast_modinfo($event->courseid)->instances['assign'][$event->instance];
+    if (empty($userid)) {
+        $userid = $USER->id;
+    }
+
+    $cm = get_fast_modinfo($event->courseid, $userid)->instances['assign'][$event->instance];
     $context = context_module::instance($cm->id);
 
     $assign = new assign($context, $cm, null);
 
     if ($event->eventtype == ASSIGN_EVENT_TYPE_GRADINGDUE) {
-        return $assign->can_grade();
+        return $assign->can_grade($userid);
     } else {
         return true;
     }
@@ -1853,22 +1858,28 @@ function mod_assign_core_calendar_is_event_visible(calendar_event $event) {
  *
  * @param calendar_event $event
  * @param \core_calendar\action_factory $factory
+ * @param int $userid User id to use for all capability checks, etc. Set to 0 for current user (default).
  * @return \core_calendar\local\event\entities\action_interface|null
  */
 function mod_assign_core_calendar_provide_event_action(calendar_event $event,
-                                                       \core_calendar\action_factory $factory) {
+                                                       \core_calendar\action_factory $factory,
+                                                       $userid = 0) {
 
     global $CFG, $USER;
 
     require_once($CFG->dirroot . '/mod/assign/locallib.php');
 
-    $cm = get_fast_modinfo($event->courseid)->instances['assign'][$event->instance];
+    if (empty($userid)) {
+        $userid = $USER->id;
+    }
+
+    $cm = get_fast_modinfo($event->courseid, $userid)->instances['assign'][$event->instance];
     $context = context_module::instance($cm->id);
 
     $assign = new assign($context, $cm, null);
 
     // Apply overrides.
-    $assign->update_effective_access($USER->id);
+    $assign->update_effective_access($userid);
 
     if ($event->eventtype == ASSIGN_EVENT_TYPE_GRADINGDUE) {
         $name = get_string('grade');
@@ -1877,16 +1888,16 @@ function mod_assign_core_calendar_provide_event_action(calendar_event $event,
             'action' => 'grader'
         ]);
         $itemcount = $assign->count_submissions_need_grading();
-        $actionable = $assign->can_grade() && (time() >= $assign->get_instance()->allowsubmissionsfromdate);
+        $actionable = $assign->can_grade($userid) && (time() >= $assign->get_instance()->allowsubmissionsfromdate);
     } else {
-        $usersubmission = $assign->get_user_submission($USER->id, false);
+        $usersubmission = $assign->get_user_submission($userid, false);
         if ($usersubmission && $usersubmission->status === ASSIGN_SUBMISSION_STATUS_SUBMITTED) {
             // The user has already submitted.
             // We do not want to change the text to edit the submission, we want to remove the event from the Dashboard entirely.
             return null;
         }
 
-        $participant = $assign->get_participant($USER->id);
+        $participant = $assign->get_participant($userid);
 
         if (!$participant) {
             // If the user is not a participant in the assignment then they have
@@ -1901,7 +1912,7 @@ function mod_assign_core_calendar_provide_event_action(calendar_event $event,
             'action' => 'editsubmission'
         ]);
         $itemcount = 1;
-        $actionable = $assign->is_any_submission_plugin_enabled() && $assign->can_edit_submission($USER->id);
+        $actionable = $assign->is_any_submission_plugin_enabled() && $assign->can_edit_submission($userid, $userid);
     }
 
     return $factory->create_instance(
index 55f734c..a607b2c 100644 (file)
@@ -87,7 +87,6 @@ require_once($CFG->dirroot . '/mod/assign/feedbackplugin.php');
 require_once($CFG->dirroot . '/mod/assign/submissionplugin.php');
 require_once($CFG->dirroot . '/mod/assign/renderable.php');
 require_once($CFG->dirroot . '/mod/assign/gradingtable.php');
-require_once($CFG->libdir . '/eventslib.php');
 require_once($CFG->libdir . '/portfolio/caller.php');
 
 use \mod_assign\output\grading_app;
@@ -1504,8 +1503,9 @@ class assign {
         } else if ($plugin->is_visible() && $plugin->is_configurable()) {
             $name = $plugin->get_subtype() . '_' . $plugin->get_type() . '_enabled';
             $label = $plugin->get_name();
-            $label .= ' ' . $this->get_renderer()->help_icon('enabled', $plugin->get_subtype() . '_' . $plugin->get_type());
             $pluginsenabled[] = $mform->createElement('checkbox', $name, '', $label);
+            $helpicon = $this->get_renderer()->help_icon('enabled', $plugin->get_subtype() . '_' . $plugin->get_type());
+            $pluginsenabled[] = $mform->createElement('static', '', '', $helpicon);
 
             $default = get_config($plugin->get_subtype() . '_' . $plugin->get_type(), 'default');
             if ($plugin->get_config('enabled') !== false) {
@@ -3316,11 +3316,12 @@ class assign {
     /**
      * Does this user have grade permission for this assignment?
      *
+     * @param int|stdClass $user The object or id of the user who will do the editing (default to current user).
      * @return bool
      */
-    public function can_grade() {
+    public function can_grade($user = null) {
         // Permissions check.
-        if (!has_capability('mod/assign:grade', $this->context)) {
+        if (!has_capability('mod/assign:grade', $this->context, $user)) {
             return false;
         }
 
index 3b1959b..99b0309 100644 (file)
@@ -24,8 +24,6 @@
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-require_once($CFG->libdir.'/eventslib.php');
-
 defined('MOODLE_INTERNAL') || die();
 
 // File areas for file submission assignment.
index 9c758c1..4d47f85 100644 (file)
@@ -426,6 +426,24 @@ class mod_assign_lib_testcase extends advanced_testcase {
         $this->assertTrue(mod_assign_core_calendar_is_event_visible($event));
     }
 
+    public function test_assign_core_calendar_is_event_visible_duedate_event_for_teacher() {
+        $this->resetAfterTest();
+        $course = $this->getDataGenerator()->create_course();
+        $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
+        $assign = $this->create_instance($course);
+
+        $this->setAdminUser();
+
+        // Create a calendar event.
+        $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_DUE);
+
+        // Now, log out.
+        $this->setUser();
+
+        // The teacher should see the due date event.
+        $this->assertTrue(mod_assign_core_calendar_is_event_visible($event, $teacher->id));
+    }
+
     public function test_assign_core_calendar_is_event_visible_duedate_event_as_student() {
         $this->resetAfterTest();
         $course = $this->getDataGenerator()->create_course();
@@ -443,6 +461,25 @@ class mod_assign_lib_testcase extends advanced_testcase {
         $this->assertTrue(mod_assign_core_calendar_is_event_visible($event));
     }
 
+    public function test_assign_core_calendar_is_event_visible_duedate_event_for_student() {
+        $this->resetAfterTest();
+        $course = $this->getDataGenerator()->create_course();
+        $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
+        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
+        $assign = $this->create_instance($course, ['assignsubmission_onlinetext_enabled' => 1]);
+
+        $this->setAdminUser();
+
+        // Create a calendar event.
+        $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_DUE);
+
+        // Now, log out.
+        $this->setUser();
+
+        // The student should care about the due date event.
+        $this->assertTrue(mod_assign_core_calendar_is_event_visible($event, $student->id));
+    }
+
     public function test_assign_core_calendar_is_event_visible_gradingduedate_event_as_teacher() {
         $this->resetAfterTest();
         $course = $this->getDataGenerator()->create_course();
@@ -458,6 +495,24 @@ class mod_assign_lib_testcase extends advanced_testcase {
         $this->assertTrue(mod_assign_core_calendar_is_event_visible($event));
     }
 
+
+    public function test_assign_core_calendar_is_event_visible_gradingduedate_event_for_teacher() {
+        $this->resetAfterTest();
+        $course = $this->getDataGenerator()->create_course();
+        $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
+        $assign = $this->create_instance($course);
+
+        // Create a calendar event.
+        $this->setAdminUser();
+        $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_GRADINGDUE);
+
+        // Now, log out.
+        $this->setUser();
+
+        // The teacher should see the due date event.
+        $this->assertTrue(mod_assign_core_calendar_is_event_visible($event, $teacher->id));
+    }
+
     public function test_assign_core_calendar_is_event_visible_gradingduedate_event_as_student() {
         $this->resetAfterTest();
         $course = $this->getDataGenerator()->create_course();
@@ -473,6 +528,24 @@ class mod_assign_lib_testcase extends advanced_testcase {
         $this->assertFalse(mod_assign_core_calendar_is_event_visible($event));
     }
 
+
+    public function test_assign_core_calendar_is_event_visible_gradingduedate_event_for_student() {
+        $this->resetAfterTest();
+        $course = $this->getDataGenerator()->create_course();
+        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
+        $assign = $this->create_instance($course);
+
+        // Create a calendar event.
+        $this->setAdminUser();
+        $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_GRADINGDUE);
+
+        // Now, log out.
+        $this->setUser();
+
+        // The student should not see the due date event.
+        $this->assertFalse(mod_assign_core_calendar_is_event_visible($event, $student->id));
+    }
+
     public function test_assign_core_calendar_provide_event_action_duedate_as_teacher() {
         $this->resetAfterTest();
         $course = $this->getDataGenerator()->create_course();
@@ -492,6 +565,27 @@ class mod_assign_lib_testcase extends advanced_testcase {
         $this->assertNull($actionevent);
     }
 
+    public function test_assign_core_calendar_provide_event_action_duedate_for_teacher() {
+        $this->resetAfterTest();
+        $course = $this->getDataGenerator()->create_course();
+        $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
+        $assign = $this->create_instance($course);
+
+        // Create a calendar event.
+        $this->setAdminUser();
+        $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_DUE);
+
+        // Now, log out.
+        $this->setUser();
+
+        // Decorate action event for a teacher.
+        $factory = new \core_calendar\action_factory();
+        $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory, $teacher->id);
+
+        // The teacher should not have an action for a due date event.
+        $this->assertNull($actionevent);
+    }
+
     public function test_assign_core_calendar_provide_event_action_duedate_as_student() {
         $this->resetAfterTest();
         $course = $this->getDataGenerator()->create_course();
@@ -515,6 +609,31 @@ class mod_assign_lib_testcase extends advanced_testcase {
         $this->assertTrue($actionevent->is_actionable());
     }
 
+    public function test_assign_core_calendar_provide_event_action_duedate_for_student() {
+        $this->resetAfterTest();
+        $course = $this->getDataGenerator()->create_course();
+        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
+        $assign = $this->create_instance($course, ['assignsubmission_onlinetext_enabled' => 1]);
+
+        // Create a calendar event.
+        $this->setAdminUser();
+        $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_DUE);
+
+        // Now, log out.
+        $this->setUser();
+
+        // Decorate action event for a student.
+        $factory = new \core_calendar\action_factory();
+        $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory, $student->id);
+
+        // Confirm the event was decorated.
+        $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
+        $this->assertEquals(get_string('addsubmission', 'assign'), $actionevent->get_name());
+        $this->assertInstanceOf('moodle_url', $actionevent->get_url());
+        $this->assertEquals(1, $actionevent->get_item_count());
+        $this->assertTrue($actionevent->is_actionable());
+    }
+
     public function test_assign_core_calendar_provide_event_action_gradingduedate_as_teacher() {
         $this->resetAfterTest();
         $course = $this->getDataGenerator()->create_course();
@@ -537,6 +656,31 @@ class mod_assign_lib_testcase extends advanced_testcase {
         $this->assertTrue($actionevent->is_actionable());
     }
 
+    public function test_assign_core_calendar_provide_event_action_gradingduedate_for_teacher() {
+        $this->resetAfterTest();
+        $course = $this->getDataGenerator()->create_course();
+        $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
+        $assign = $this->create_instance($course);
+
+        // Create a calendar event.
+        $this->setAdminUser();
+        $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_GRADINGDUE);
+
+        // Now, log out.
+        $this->setUser();
+
+        // Decorate action event for a teacher.
+        $factory = new \core_calendar\action_factory();
+        $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory, $teacher->id);
+
+        // Confirm the event was decorated.
+        $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
+        $this->assertEquals(get_string('grade'), $actionevent->get_name());
+        $this->assertInstanceOf('moodle_url', $actionevent->get_url());
+        $this->assertEquals(0, $actionevent->get_item_count());
+        $this->assertTrue($actionevent->is_actionable());
+    }
+
     public function test_assign_core_calendar_provide_event_action_gradingduedate_as_student() {
         $this->resetAfterTest();
         $course = $this->getDataGenerator()->create_course();
@@ -559,6 +703,31 @@ class mod_assign_lib_testcase extends advanced_testcase {
         $this->assertFalse($actionevent->is_actionable());
     }
 
+    public function test_assign_core_calendar_provide_event_action_gradingduedate_for_student() {
+        $this->resetAfterTest();
+        $course = $this->getDataGenerator()->create_course();
+        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
+        $assign = $this->create_instance($course);
+
+        // Create a calendar event.
+        $this->setAdminUser();
+        $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_GRADINGDUE);
+
+        // Now, log out.
+        $this->setUser();
+
+        // Decorate action event for a student.
+        $factory = new \core_calendar\action_factory();
+        $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory, $student->id);
+
+        // Confirm the event was decorated.
+        $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
+        $this->assertEquals(get_string('grade'), $actionevent->get_name());
+        $this->assertInstanceOf('moodle_url', $actionevent->get_url());
+        $this->assertEquals(0, $actionevent->get_item_count());
+        $this->assertFalse($actionevent->is_actionable());
+    }
+
     public function test_assign_core_calendar_provide_event_action_duedate_as_student_submitted() {
         $this->resetAfterTest();
         $course = $this->getDataGenerator()->create_course();
@@ -584,6 +753,34 @@ class mod_assign_lib_testcase extends advanced_testcase {
         $this->assertNull($actionevent);
     }
 
+    public function test_assign_core_calendar_provide_event_action_duedate_for_student_submitted() {
+        $this->resetAfterTest();
+        $course = $this->getDataGenerator()->create_course();
+        $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
+        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
+        $assign = $this->create_instance($course, ['assignsubmission_onlinetext_enabled' => 1]);
+
+        $this->setAdminUser();
+
+        // Create a calendar event.
+        $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_DUE);
+
+        // Create an action factory.
+        $factory = new \core_calendar\action_factory();
+
+        // Submit as the student.
+        $this->add_submission($student, $assign);
+        $this->submit_for_grading($student, $assign);
+
+        // Now, log out.
+        $this->setUser();
+
+        // Confirm there was no event to action.
+        $factory = new \core_calendar\action_factory();
+        $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory, $student->id);
+        $this->assertNull($actionevent);
+    }
+
     /**
      * Creates an action event.
      *
index 80ec0cd..d4b17e9 100644 (file)
@@ -1720,6 +1720,11 @@ class mod_assign_locallib_testcase extends advanced_testcase {
         $this->setUser($teacher);
         $this->assertEquals(true, $assign->can_grade());
 
+        // Test the viewgrades capability for other users.
+        $this->setUser();
+        $this->assertTrue($assign->can_grade($teacher->id));
+        $this->assertFalse($assign->can_grade($student->id));
+
         // Test the viewgrades capability - without mod/assign:grade.
         $this->setUser($student);
 
index d32481c..dbcd9fd 100644 (file)
@@ -4,6 +4,7 @@ This files describes API changes in the assign code.
 * The mod_assign_base_testcase unit test base class has been deprecated.
   It encouraged poor unit test design and led to significant performance issues with unit tests. See MDL-55609 for
   further information.
+* The function can_grade() now has optional $user parameter.
 
 === 3.5 ===
 * Functions assign:get_assign_grading_summary_renderable, assign:can_view_submission, assign:count_submissions_with_status,
index 0881cc9..0b2d3e5 100644 (file)
@@ -25,8 +25,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-/** Include eventslib.php */
-require_once($CFG->libdir.'/eventslib.php');
 // Include forms lib.
 require_once($CFG->libdir.'/formslib.php');
 
index e97ad19..d032f57 100644 (file)
@@ -25,7 +25,6 @@ defined('MOODLE_INTERNAL') || die();
 /** Include required files */
 require_once(__DIR__ . '/deprecatedlib.php');
 require_once($CFG->libdir.'/filelib.php');
-require_once($CFG->libdir.'/eventslib.php');
 
 /// CONSTANTS ///////////////////////////////////////////////////////////
 
index a67623c..19382e9 100644 (file)
@@ -27,7 +27,6 @@ require_once('../../config.php');
 require_once($CFG->dirroot.'/mod/lesson/locallib.php');
 require_once($CFG->dirroot.'/mod/lesson/pagetypes/essay.php');
 require_once($CFG->dirroot.'/mod/lesson/essay_form.php');
-require_once($CFG->libdir.'/eventslib.php');
 
 $id   = required_param('id', PARAM_INT);             // Course Module ID
 $mode = optional_param('mode', 'display', PARAM_ALPHA);
index 767b580..0d8d407 100644 (file)
@@ -4360,7 +4360,7 @@ abstract class lesson_page extends lesson_base {
             if (count($answers) > 1) {
                 $answer = array_shift($answers);
                 foreach ($answers as $a) {
-                    $DB->delete_record('lesson_answers', array('id' => $a->id));
+                    $DB->delete_records('lesson_answers', array('id' => $a->id));
                 }
             } else if (count($answers) == 1) {
                 $answer = array_shift($answers);
index dfa3046..a8caba4 100644 (file)
@@ -28,7 +28,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-require_once($CFG->libdir . '/eventslib.php');
 require_once($CFG->dirroot . '/calendar/lib.php');
 
 
index f6c72d6..e27fbc8 100644 (file)
@@ -37,7 +37,6 @@ require_once($CFG->dirroot . '/mod/quiz/accessmanager_form.php');
 require_once($CFG->dirroot . '/mod/quiz/renderer.php');
 require_once($CFG->dirroot . '/mod/quiz/attemptlib.php');
 require_once($CFG->libdir . '/completionlib.php');
-require_once($CFG->libdir . '/eventslib.php');
 require_once($CFG->libdir . '/filelib.php');
 require_once($CFG->libdir . '/questionlib.php');
 
index 1a17296..9cdbf76 100644 (file)
@@ -66,23 +66,22 @@ $contextmodule = context_module::instance($cm->id);
 
 $launch = false; // Does this automatically trigger a launch based on skipview.
 if (!empty($scorm->popup)) {
-    $orgidentifier = '';
-
     $scoid = 0;
     $orgidentifier = '';
 
     $result = scorm_get_toc($USER, $scorm, $cm->id, TOCFULLURL);
     // Set last incomplete sco to launch first.
     if (!empty($result->sco->id)) {
-        $scoid = $result->sco->id;
+        $sco = $result->sco;
     } else {
-        if ($sco = scorm_get_sco($scorm->launch, SCO_ONLY)) {
-            if (($sco->organization == '') && ($sco->launch == '')) {
-                $orgidentifier = $sco->identifier;
-            } else {
-                $orgidentifier = $sco->organization;
-            }
-            $scoid = $sco->id;
+        $sco = scorm_get_sco($scorm->launch, SCO_ONLY);
+    }
+    if (!empty($sco)) {
+        $scoid = $sco->id;
+        if (($sco->organization == '') && ($sco->launch == '')) {
+            $orgidentifier = $sco->identifier;
+        } else {
+            $orgidentifier = $sco->organization;
         }
     }
 
index 2688662..7a0e029 100644 (file)
@@ -6,6 +6,8 @@ information provided here is intended especially for developers.
 * The final deprecation of xxx_get_types() callback means that this function will no longer be called.
   Please use get_shortcuts() instead.
 * lti_get_shortcuts has been deprecated. Please use get_shortcuts() instead to add items to the activity chooser.
+* Now, when mod_<modname>_core_calendar_is_event_visible or mod_<modname>_core_calendar_provide_event_action callback functions
+  are called, the userid of the requesting user is also passed to them.
 
 === 3.5 ===
 
@@ -44,7 +46,7 @@ information provided here is intended especially for developers.
   MDL-55611 can be found at https://docs.moodle.org/dev/Calendar_API. The 3 new callbacks are:
   - mod_<modname>_core_calendar_is_event_visible
   - mod_<modname>_core_calendar_provide_event_action
-  - mod_<modname>_core_calendar_event_action_show_items_acount
+  - mod_<modname>_core_calendar_event_action_shows_item_count
 * Changes to the moodleform_mod class and its usage (MDL-58138):
   - the get_data() method has been overriden. The implementation calls parent::get_data() and a new data_postprocessing() method
   - new data_postprocessing() method added. Mods can override this in their mod_form subclass to modify the submit data. Previously
index 1da0211..41e25ae 100644 (file)
@@ -70,8 +70,8 @@ function(
     var root = $('[data-region="tag-condition-container-{{uniqid}}"]');
     var selectElement = root.find('[data-region="tag-select"]');
     var loadingContainer = root.find('[data-region="overlay-icon-container"]');
-    var placeholderText = '{{#str}} filterbytags, core_question {{/str}}';
-    var noSelectionText = '{{#str}} notagfiltersapplied, core_question {{/str}}';
+    var placeholderText = {{#quote}}{{#str}} filterbytags, core_question {{/str}}{{/quote}};
+    var noSelectionText = {{#quote}}{{#str}} notagfiltersapplied, core_question {{/str}}{{/quote}};
 
     AutoComplete.enhance(
         selectElement, // Element to enhance.
index 97fb8ab..4a619cd 100644 (file)
@@ -340,6 +340,19 @@ abstract class base {
      */
     abstract public function get_document($record, $options = array());
 
+    /**
+     * Returns the document title to display.
+     *
+     * Allow to customize the document title string to display.
+     *
+     * @param \core_search\document $doc
+     * @return string Document title to display in the search results page
+     */
+    public function get_document_display_title(\core_search\document $doc) {
+
+        return $doc->get('title');
+    }
+
     /**
      * Return the context info required to index files for
      * this search area.
index 0808066..5f76f94 100644 (file)
@@ -592,7 +592,8 @@ class document implements \renderable, \templatable {
     public function export_for_template(\renderer_base $output) {
         list($componentname, $areaname) = \core_search\manager::extract_areaid_parts($this->get('areaid'));
 
-        $title = $this->is_set('title') ? $this->format_text($this->get('title')) : '';
+        $searcharea = \core_search\manager::get_search_area($this->data['areaid']);
+        $title = $this->is_set('title') ? $this->format_text($searcharea->get_document_display_title($this)) : '';
         $data = [
             'componentname' => $componentname,
             'areaname' => $areaname,
index 3855b8b..ab8a4ee 100644 (file)
@@ -2102,12 +2102,6 @@ img#persona_signin {
                 color: @dropdownLinkColorHover;
                 background-color: @dropdownLinkBackgroundHover;
             }
-            &:first-child {
-                .border-top-radius(4px);
-            }
-            &:last-child {
-                .border-bottom-radius(4px);
-            }
         }
         a.hidden {
             display: none;
@@ -2122,6 +2116,12 @@ img#persona_signin {
         }
         > li {
             display: block;
+            &:first-child a {
+                .border-top-radius(4px);
+            }
+            &:last-child a {
+                .border-bottom-radius(4px);
+            }
         }
 
         /** bottom left of button **/
index 0198314..e66b22e 100644 (file)
@@ -4534,22 +4534,6 @@ img#persona_signin {
   color: #fff;
   background-color: #0070a8;
 }
-.moodle-actionmenu[data-enhanced].show .menu a:first-child {
-  -webkit-border-top-right-radius: 4px;
-  -moz-border-radius-topright: 4px;
-  border-top-right-radius: 4px;
-  -webkit-border-top-left-radius: 4px;
-  -moz-border-radius-topleft: 4px;
-  border-top-left-radius: 4px;
-}
-.moodle-actionmenu[data-enhanced].show .menu a:last-child {
-  -webkit-border-bottom-right-radius: 4px;
-  -moz-border-radius-bottomright: 4px;
-  border-bottom-right-radius: 4px;
-  -webkit-border-bottom-left-radius: 4px;
-  -moz-border-radius-bottomleft: 4px;
-  border-bottom-left-radius: 4px;
-}
 .moodle-actionmenu[data-enhanced].show .menu a.hidden {
   display: none;
 }
@@ -4564,6 +4548,22 @@ img#persona_signin {
 .moodle-actionmenu[data-enhanced].show .menu > li {
   display: block;
 }
+.moodle-actionmenu[data-enhanced].show .menu > li:first-child a {
+  -webkit-border-top-right-radius: 4px;
+  -moz-border-radius-topright: 4px;
+  border-top-right-radius: 4px;
+  -webkit-border-top-left-radius: 4px;
+  -moz-border-radius-topleft: 4px;
+  border-top-left-radius: 4px;
+}
+.moodle-actionmenu[data-enhanced].show .menu > li:last-child a {
+  -webkit-border-bottom-right-radius: 4px;
+  -moz-border-radius-bottomright: 4px;
+  border-bottom-right-radius: 4px;
+  -webkit-border-bottom-left-radius: 4px;
+  -moz-border-radius-bottomleft: 4px;
+  border-bottom-left-radius: 4px;
+}
 .moodle-actionmenu[data-enhanced].show .menu.align-tl-bl {
   top: 100%;
   left: 0;
index 5724b69..337fa28 100644 (file)
@@ -92,8 +92,14 @@ class user extends \core_search\base {
 
         // Prepare associative array with data from DB.
         $doc = \core_search\document_factory::instance($record->id, $this->componentname, $this->areaname);
+        // Include all alternate names in title.
+        $array = [];
+        foreach (get_all_user_name_fields(false, null, null, null, true) as $field) {
+            $array[$field] = $record->$field;
+        }
+        $fullusername = join(' ', $array);
         // Assigning properties to our document.
-        $doc->set('title', content_to_text(fullname($record), false));
+        $doc->set('title', content_to_text($fullusername, false));
         $doc->set('contextid', $context->id);
         $doc->set('courseid', SITEID);
         $doc->set('itemid', $record->id);
@@ -110,6 +116,18 @@ class user extends \core_search\base {
         return $doc;
     }
 
+    /**
+     * Returns the user fullname to display as document title
+     *
+     * @param \core_search\document $doc
+     * @return string User fullname
+     */
+    public function get_document_display_title(\core_search\document $doc) {
+
+        $user = \core_user::get_user($doc->get('itemid'));
+        return fullname($user);
+    }
+
     /**
      * Checking whether I can access a document
      *
index b387464..f22e2cb 100644 (file)
@@ -184,21 +184,12 @@ function useredit_update_user_preference($usernew) {
 }
 
 /**
- * Updates the provided users profile picture based upon the expected fields returned from the edit or edit_advanced forms.
- *
- * @deprecated since Moodle 3.2 MDL-51789 - please use core_user::update_picture() instead.
- * @todo MDL-54858 This will be deleted in Moodle 3.6.
+ * @deprecated since Moodle 3.2
  * @see core_user::update_picture()
- *
- * @global moodle_database $DB
- * @param stdClass $usernew An object that contains some information about the user being updated
- * @param moodleform $userform The form that was submitted to edit the form (unused)
- * @param array $filemanageroptions
- * @return bool True if the user was updated, false if it stayed the same.
  */
-function useredit_update_picture(stdClass $usernew, moodleform $userform, $filemanageroptions = array()) {
-    debugging('useredit_update_picture() is deprecated. Please use core_user::update_picture() instead.', DEBUG_DEVELOPER);
-    return core_user::update_picture($usernew, $filemanageroptions);
+function useredit_update_picture() {
+    throw new coding_exception('useredit_update_picture() can not be used anymore. Please use ' .
+        'core_user::update_picture() instead.');
 }
 
 /**
index 5c6d4f8..9ec26e4 100644 (file)
@@ -270,7 +270,7 @@ Feature: View course participants
     Given I log in as "admin"
     And I am on "Course 1" course homepage
     And I navigate to course participants
-    When I navigate to "Enrolment methods" in current page administration
+    When I navigate to "Users > Enrolment methods" in current page administration
     And I click on "Disable" "link" in the "Manual enrolments" "table_row"
     Then I navigate to course participants
     And I should see "Not current" in the "student0x" "table_row"
index 589665a..5e7f5dc 100644 (file)
@@ -119,7 +119,7 @@ class user_search_testcase extends advanced_testcase {
         $this->assertEquals(SITEID, $doc->get('courseid'));
         $this->assertFalse($doc->is_set('userid'));
         $this->assertEquals(\core_search\manager::NO_OWNER_ID, $doc->get('owneruserid'));
-        $this->assertEquals(content_to_text(fullname($user), false), $doc->get('title'));
+        $this->assertEquals(content_to_text(fullname($user), false), $searcharea->get_document_display_title($doc));
         $this->assertEquals(content_to_text($user->description, $user->descriptionformat), $doc->get('content'));
     }
 
diff --git a/user/upgrade.txt b/user/upgrade.txt
new file mode 100644 (file)
index 0000000..c82a29a
--- /dev/null
@@ -0,0 +1,6 @@
+This files describes API changes for code that uses the user API.
+
+=== 3.6 ===
+
+* The following functions have been finally deprecated and can not be used anymore:
+  * useredit_update_picture()