Merge branch 'MDL-50937-master' of git://github.com/damyon/moodle
authorDan Poltawski <dan@moodle.com>
Tue, 19 Jul 2016 09:27:10 +0000 (10:27 +0100)
committerDan Poltawski <dan@moodle.com>
Tue, 19 Jul 2016 09:27:10 +0000 (10:27 +0100)
71 files changed:
.eslintignore
admin/tool/task/cli/schedule_task.php
auth/db/auth.php
auth/db/tests/db_test.php
blocks/navigation/renderer.php
blocks/settings/renderer.php
calendar/tests/behat/export.feature
course/externallib.php
course/tests/externallib_test.php
lang/en/admin.php
lib/adminlib.php
lib/adodb/drivers/adodb-oci8po.inc.php
lib/adodb/readme_moodle.txt
lib/amd/build/templates.min.js
lib/amd/src/templates.js
lib/boxlib.php
lib/db/services.php
lib/deprecatedlib.php
lib/dml/oci_native_moodle_database.php
lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js
lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js
lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor.js
lib/editor/atto/yui/src/editor/js/clean.js
lib/filelib.php
lib/moodlelib.php
lib/password_compat/lib/password.php
lib/password_compat/readme_moodle.txt [deleted file]
lib/password_compat/tests/PasswordGetInfoTest.php [deleted file]
lib/password_compat/tests/PasswordHashTest.php [deleted file]
lib/password_compat/tests/PasswordNeedsRehashTest.php [deleted file]
lib/password_compat/tests/PasswordVerifyTest.php [deleted file]
lib/testing/classes/util.php
lib/tests/filelib_test.php
lib/thirdpartylibs.xml
lib/upgrade.txt
mod/assign/db/services.php
mod/assign/externallib.php
mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor-debug.js
mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor-min.js
mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor.js
mod/assign/feedback/editpdf/yui/src/editor/js/annotationstamp.js
mod/assign/feedback/editpdf/yui/src/editor/js/comment.js
mod/assign/locallib.php
mod/assign/tests/externallib_test.php
mod/assign/version.php
mod/assign/view.php
mod/data/lang/en/data.php
mod/data/lib.php
mod/data/locallib.php
mod/data/tests/lib_test.php
mod/feedback/lang/en/deprecated.txt
mod/feedback/lang/en/feedback.php
mod/feedback/lib.php
mod/feedback/tests/lib_test.php [new file with mode: 0644]
mod/forum/deprecatedlib.php
mod/forum/tests/subscriptions_test.php
mod/forum/upgrade.txt
mod/lesson/db/access.php
mod/lesson/lang/en/lesson.php
mod/lesson/version.php
mod/survey/lang/en/survey.php
mod/survey/report.php
phpunit.xml.dist
question/type/ddmarker/yui/build/moodle-qtype_ddmarker-form/moodle-qtype_ddmarker-form-debug.js
question/type/ddmarker/yui/build/moodle-qtype_ddmarker-form/moodle-qtype_ddmarker-form-min.js
question/type/ddmarker/yui/build/moodle-qtype_ddmarker-form/moodle-qtype_ddmarker-form.js
question/type/ddmarker/yui/src/form/js/form.js
search/engine/solr/classes/engine.php
search/index.php
user/lib.php
version.php

index 4c89fdb..2ec29c4 100644 (file)
@@ -24,7 +24,6 @@ lib/htmlpurifier/
 lib/jabber/
 lib/minify/
 lib/flowplayer/
-lib/password_compat/
 lib/pear/Auth/RADIUS.php
 lib/pear/Crypt/CHAP.php
 lib/pear/HTML/Common.php
index 545e190..7e94fcf 100644 (file)
@@ -29,7 +29,7 @@ require_once("$CFG->libdir/clilib.php");
 require_once("$CFG->libdir/cronlib.php");
 
 list($options, $unrecognized) = cli_get_params(
-    array('help' => false, 'list' => false, 'execute' => false),
+    array('help' => false, 'list' => false, 'execute' => false, 'showsql' => false, 'showdebugging' => false),
     array('h' => 'help')
 );
 
@@ -45,6 +45,8 @@ if ($options['help'] or (!$options['list'] and !$options['execute'])) {
 Options:
 --execute=\\\\some\\\\task  Execute scheduled task manually
 --list                List all scheduled tasks
+--showsql             Show sql queries before they are executed
+--showdebugging       Show developer level debugging information
 -h, --help            Print out this help
 
 Example:
@@ -56,6 +58,13 @@ Example:
     die;
 }
 
+if ($options['showdebugging']) {
+    set_debugging(DEBUG_DEVELOPER, true);
+}
+
+if ($options['showsql']) {
+    $DB->set_debug(true);
+}
 if ($options['list']) {
     cli_heading("List of scheduled tasks ($CFG->wwwroot)");
 
index b1fa091..8048a30 100644 (file)
@@ -136,7 +136,6 @@ class auth_plugin_db extends auth_plugin_base {
             } else if ($this->config->passtype === 'sha1') {
                 return (strtolower($fromdb) == sha1($extpassword));
             } else if ($this->config->passtype === 'saltedcrypt') {
-                require_once($CFG->libdir.'/password_compat/lib/password.php');
                 return password_verify($extpassword, $fromdb);
             } else {
                 return false;
index f1471cf..09a287e 100644 (file)
@@ -308,7 +308,6 @@ class auth_db_testcase extends advanced_testcase {
         $DB->update_record('auth_db_users', $user3);
         $this->assertTrue($auth->user_login('u3', 'heslo'));
 
-        require_once($CFG->libdir.'/password_compat/lib/password.php');
         set_config('passtype', 'saltedcrypt', 'auth/db');
         $auth->config->passtype = 'saltedcrypt';
         $user3->pass = password_hash('heslo', PASSWORD_BCRYPT);
index 8ead8fe..c64e55d 100644 (file)
@@ -86,7 +86,7 @@ class block_navigation_renderer extends plugin_renderer_base {
                 continue;
             }
 
-            $id = $item->id ? $item->id : uniqid();
+            $id = $item->id ? $item->id : html_writer::random_id();
             $content = $item->get_content();
             $title = $item->get_title();
             $ulattr = ['id' => $id . '_group', 'role' => 'group'];
index 10afa75..34e99ef 100644 (file)
@@ -75,7 +75,7 @@ class block_settings_renderer extends plugin_renderer_base {
             }
 
             $content = $this->output->render($item);
-            $id = $item->id ? $item->id : uniqid();
+            $id = $item->id ? $item->id : html_writer::random_id();
             $ulattr = ['id' => $id . '_group', 'role' => 'group'];
             $liattr = ['class' => [$item->get_css_type(), 'depth_'.$depth], 'tabindex' => '-1'];
             $pattr = ['class' => ['tree_item'], 'role' => 'treeitem'];
index 97d9d73..c7e9cec 100644 (file)
@@ -28,7 +28,7 @@ Feature: Export calendar events
     Given I follow "This month"
     And I click on "Export calendar" "button"
     And I set the field "All events" to "1"
-    And I set the field "This week" to "1"
+    And I set the field "Recent and next 60 days" to "1"
     When I click on "Get calendar URL" "button"
     Then I should see "&preset_what=all&"
 
@@ -36,7 +36,7 @@ Feature: Export calendar events
     Given I follow "This month"
     And I click on "Export calendar" "button"
     And I set the field "Events related to courses" to "1"
-    And I set the field "This week" to "1"
+    And I set the field "Recent and next 60 days" to "1"
     When I click on "Get calendar URL" "button"
     Then I should see "&preset_what=courses&"
 
@@ -44,7 +44,7 @@ Feature: Export calendar events
     Given I follow "This month"
     And I click on "Export calendar" "button"
     And I set the field "Events related to groups" to "1"
-    And I set the field "This week" to "1"
+    And I set the field "Recent and next 60 days" to "1"
     When I click on "Get calendar URL" "button"
     Then I should see "&preset_what=groups&"
 
@@ -52,6 +52,6 @@ Feature: Export calendar events
     Given I follow "This month"
     And I click on "Export calendar" "button"
     And I set the field "My personal events" to "1"
-    And I set the field "This week" to "1"
+    And I set the field "Recent and next 60 days" to "1"
     When I click on "Get calendar URL" "button"
     Then I should see "&preset_what=user&"
index 3458723..0f2aaf6 100644 (file)
@@ -2501,4 +2501,104 @@ class core_course_external extends external_api {
         return self::get_course_module_returns();
     }
 
+    /**
+     * Returns description of method parameters
+     *
+     * @return external_function_parameters
+     * @since Moodle 3.2
+     */
+    public static function get_activities_overview_parameters() {
+        return new external_function_parameters(
+            array(
+                'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'Course id.')),
+            )
+        );
+    }
+
+    /**
+     * Return activities overview for the given courses.
+     *
+     * @param array $courseids a list of course ids
+     * @return array of warnings and the activities overview
+     * @since Moodle 3.2
+     * @throws moodle_exception
+     */
+    public static function get_activities_overview($courseids) {
+        global $USER;
+
+        // Parameter validation.
+        $params = self::validate_parameters(self::get_activities_overview_parameters(), array('courseids' => $courseids));
+        $courseoverviews = array();
+
+        list($courses, $warnings) = external_util::validate_courses($params['courseids']);
+
+        if (!empty($courses)) {
+            // Add lastaccess to each course (required by print_overview function).
+            // We need the complete user data, the ws server does not load a complete one.
+            $user = get_complete_user_data('id', $USER->id);
+            foreach ($courses as $course) {
+                if (isset($user->lastcourseaccess[$course->id])) {
+                    $course->lastaccess = $user->lastcourseaccess[$course->id];
+                } else {
+                    $course->lastaccess = 0;
+                }
+            }
+
+            $overviews = array();
+            if ($modules = get_plugin_list_with_function('mod', 'print_overview')) {
+                foreach ($modules as $fname) {
+                    $fname($courses, $overviews);
+                }
+            }
+
+            // Format output.
+            foreach ($overviews as $courseid => $modules) {
+                $courseoverviews[$courseid]['id'] = $courseid;
+                $courseoverviews[$courseid]['overviews'] = array();
+
+                foreach ($modules as $modname => $overviewtext) {
+                    $courseoverviews[$courseid]['overviews'][] = array(
+                        'module' => $modname,
+                        'overviewtext' => $overviewtext // This text doesn't need formatting.
+                    );
+                }
+            }
+        }
+
+        $result = array(
+            'courses' => $courseoverviews,
+            'warnings' => $warnings
+        );
+        return $result;
+    }
+
+    /**
+     * Returns description of method result value
+     *
+     * @return external_description
+     * @since Moodle 3.2
+     */
+    public static function get_activities_overview_returns() {
+        return new external_single_structure(
+            array(
+                'courses' => new external_multiple_structure(
+                    new external_single_structure(
+                        array(
+                            'id' => new external_value(PARAM_INT, 'Course id'),
+                            'overviews' => new external_multiple_structure(
+                                new external_single_structure(
+                                    array(
+                                        'module' => new external_value(PARAM_PLUGIN, 'Module name'),
+                                        'overviewtext' => new external_value(PARAM_RAW, 'Overview text'),
+                                    )
+                                )
+                            )
+                        )
+                    ), 'List of courses'
+                ),
+                'warnings' => new external_warnings()
+            )
+        );
+    }
+
 }
index 71372e7..fb1814a 100644 (file)
@@ -1691,4 +1691,47 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
         }
 
     }
+
+    /**
+     * Test get_activities_overview
+     */
+    public function test_get_activities_overview() {
+        global $USER;
+
+        $this->resetAfterTest();
+        $course1 = self::getDataGenerator()->create_course();
+        $course2 = self::getDataGenerator()->create_course();
+
+        // Create a viewer user.
+        $viewer = self::getDataGenerator()->create_user((object) array('trackforums' => 1));
+        $this->getDataGenerator()->enrol_user($viewer->id, $course1->id);
+        $this->getDataGenerator()->enrol_user($viewer->id, $course2->id);
+
+        // Create two forums - one in each course.
+        $record = new stdClass();
+        $record->course = $course1->id;
+        $forum1 = self::getDataGenerator()->create_module('forum', (object) array('course' => $course1->id));
+        $forum2 = self::getDataGenerator()->create_module('forum', (object) array('course' => $course2->id));
+
+        $this->setAdminUser();
+        // A standard post in the forum.
+        $record = new stdClass();
+        $record->course = $course1->id;
+        $record->userid = $USER->id;
+        $record->forum = $forum1->id;
+        $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
+
+        $this->setUser($viewer->id);
+        $courses = array($course1->id , $course2->id);
+
+        $result = core_course_external::get_activities_overview($courses);
+        $result = external_api::clean_returnvalue(core_course_external::get_activities_overview_returns(), $result);
+
+        // There should be one entry for course1, and no others.
+        $this->assertCount(1, $result['courses']);
+        $this->assertEquals($course1->id, $result['courses'][0]['id']);
+        // Check expected overview data for the module.
+        $this->assertEquals('forum', $result['courses'][0]['overviews'][0]['module']);
+        $this->assertContains('1 total unread', $result['courses'][0]['overviews'][0]['overviewtext']);
+    }
 }
index eafa560..58448b6 100644 (file)
@@ -594,7 +594,7 @@ $string['invalidsection'] = 'Invalid section.';
 $string['invaliduserchangeme'] = 'Username "changeme" is reserved -- you cannot create an account with it.';
 $string['ipblocked'] = 'This site is not available currently.';
 $string['ipblocker'] = 'IP blocker';
-$string['ipblockersyntax'] = 'Put every entry on one line. Valid entries are either full IP address (such as <b>192.168.10.1</b>) which matches a single host; or partial address (such as <b>192.168.</b>) which matches any address starting with those numbers; or CIDR notation (such as <b>231.54.211.0/20</b>); or a range of IP addresses (such as <b>231.3.56.10-20</b>) where the range applies to the last part of the address. Text domain names (like \'example.com\') are not supported. Blank lines are ignored.';
+$string['ipblockersyntax'] = 'Put every entry on one line. Valid entries are either full IP address (such as <b>192.168.10.1</b>) which matches a single host; or partial address (such as <b>192.168</b>) which matches any address starting with those numbers; or CIDR notation (such as <b>231.54.211.0/20</b>); or a range of IP addresses (such as <b>231.3.56.10-20</b>) where the range applies to the last part of the address. Text domain names (like \'example.com\') are not supported. Blank lines are ignored.';
 $string['iplookup'] = 'IP address lookup';
 $string['iplookupgeoplugin'] = '<a href="http://www.geoplugin.com">geoPlugin</a> service is currently being used to look up geographical information. For more accurate results we recommend installing a local copy of the MaxMind GeoLite database.';
 $string['iplookupinfo'] = 'By default Moodle uses the free online NetGeo (The Internet Geographic Database) server to lookup location of IP addresses, unfortunately this database is not maintained anymore and may return <em>wildly incorrect</em> data.
@@ -1173,6 +1173,7 @@ $string['userquota'] = 'User quota';
 $string['usesitenameforsitepages'] = 'Use site name for site pages';
 $string['usetags'] = 'Enable tags functionality';
 $string['validateerror'] = 'This value is not valid';
+$string['validateiperror'] = 'These IP addresses are invalid: {$a}';
 $string['verifychangedemail'] = 'Restrict domains when changing email';
 $string['warningcurrentsetting'] = 'Invalid current value: {$a}';
 $string['warningiconvbuggy'] = 'Your version of the iconv library does not support the //IGNORE modifier. You should install the mbstring extension which can be used instead for cleaning strings containing invalid UTF-8 characters.';
index b5217e5..8a2b6db 100644 (file)
@@ -3526,21 +3526,24 @@ class admin_setting_configiplist extends admin_setting_configtextarea {
             return true;
         }
         $result = true;
+        $badips = array();
         foreach($ips as $ip) {
             $ip = trim($ip);
+            if (empty($ip)) {
+                continue;
+            }
             if (preg_match('#^(\d{1,3})(\.\d{1,3}){0,3}$#', $ip, $match) ||
                 preg_match('#^(\d{1,3})(\.\d{1,3}){0,3}(\/\d{1,2})$#', $ip, $match) ||
                 preg_match('#^(\d{1,3})(\.\d{1,3}){3}(-\d{1,3})$#', $ip, $match)) {
-                $result = true;
             } else {
                 $result = false;
-                break;
+                $badips[] = $ip;
             }
         }
         if($result) {
             return true;
         } else {
-            return get_string('validateerror', 'admin');
+            return get_string('validateiperror', 'admin', join(', ', $badips));
         }
     }
 }
index 7c6049b..15e89fb 100644 (file)
@@ -138,8 +138,10 @@ class ADORecordset_oci8po extends ADORecordset_oci8 {
        // 10% speedup to move MoveNext to child class
        function MoveNext()
        {
-               if(@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode)) {
+               $ret = @oci_fetch_array($this->_queryID,$this->fetchMode);
+               if($ret !== false) {
                global $ADODB_ANSI_PADDING_OFF;
+                       $this->fields = $ret;
                        $this->_currentRow++;
                        $this->_updatefields();
 
@@ -169,10 +171,12 @@ class ADORecordset_oci8po extends ADORecordset_oci8 {
                                $arr = array();
                                return $arr;
                        }
-               if (!@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode)) {
+               $ret = @oci_fetch_array($this->_queryID,$this->fetchMode);
+               if ($ret === false) {
                        $arr = array();
                        return $arr;
                }
+               $this->fields = $ret;
                $this->_updatefields();
                $results = array();
                $cnt = 0;
@@ -188,8 +192,9 @@ class ADORecordset_oci8po extends ADORecordset_oci8 {
        {
                global $ADODB_ANSI_PADDING_OFF;
 
-               $ret = @OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode);
+               $ret = @oci_fetch_array($this->_queryID,$this->fetchMode);
                if ($ret) {
+                       $this->fields = $ret;
                        $this->_updatefields();
 
                        if (!empty($ADODB_ANSI_PADDING_OFF)) {
@@ -198,7 +203,7 @@ class ADORecordset_oci8po extends ADORecordset_oci8 {
                                }
                        }
                }
-               return $ret;
+               return $ret !== false;
        }
 
 }
index a088b63..a2590c6 100644 (file)
@@ -27,5 +27,6 @@ Our changes:
  * Removed random seed initialization from lib/adodb/adodb.inc.php:216 (see 038f546 and MDL-41198).
  * MDL-52286 Added muting erros in ADORecordSet::__destruct().
    Check if fixed upstream during the next upgrade and remove this note.
+ * MDL-52544 Pull upstream patch for php7 and ocipo.
 
 skodak, iarenaza, moodler, stronk7, abgreeve
index 0ffe642..f95b3a4 100644 (file)
Binary files a/lib/amd/build/templates.min.js and b/lib/amd/build/templates.min.js differ
index 503c351..6eeeea5 100644 (file)
@@ -37,22 +37,33 @@ define(['core/mustache',
        ],
        function(mustache, $, ajax, str, notification, coreurl, config, storage, event, Y, Log) {
 
-    // Private variables and functions.
+    // Module variables.
+    /** @var {Number} uniqInstances Count of times this constructor has been called. */
+    var uniqInstances = 0;
 
     /** @var {string[]} templateCache - Cache of already loaded templates */
     var templateCache = {};
 
+    /**
+     * Constructor
+     *
+     * Each call to templates.render gets it's own instance of this class.
+     */
+    var Renderer = function() {
+        this.requiredStrings = [];
+        this.requiredJS = [];
+        this.currentThemeName = '';
+    };
+    // Class variables and functions.
+
     /** @var {string[]} requiredStrings - Collection of strings found during the rendering of one template */
-    var requiredStrings = [];
+    Renderer.prototype.requiredStrings = null;
 
     /** @var {string[]} requiredJS - Collection of js blocks found during the rendering of one template */
-    var requiredJS = [];
-
-    /** @var {Number} uniqid Incrementing value that is changed for every call to render */
-    var uniqid = 1;
+    Renderer.prototype.requiredJS = null;
 
     /** @var {String} themeName for the current render */
-    var currentThemeName = '';
+    Renderer.prototype.currentThemeName = '';
 
     /**
      * Load a template from the cache or local storage or ajax request.
@@ -65,13 +76,13 @@ define(['core/mustache',
      * @param {Boolean} async If false - this function will not return until the promises are resolved.
      * @return {Promise} JQuery promise object resolved when the template has been fetched.
      */
-    var getTemplate = function(templateName, async) {
+    Renderer.prototype.getTemplate = function(templateName, async) {
         var deferred = $.Deferred();
         var parts = templateName.split('/');
         var component = parts.shift();
         var name = parts.shift();
 
-        var searchKey = currentThemeName + '/' + templateName;
+        var searchKey = this.currentThemeName + '/' + templateName;
 
         // First try request variables.
         if (searchKey in templateCache) {
@@ -94,7 +105,7 @@ define(['core/mustache',
             args: {
                 component: component,
                 template: name,
-                themename: currentThemeName
+                themename: this.currentThemeName
             }
         }], async, false);
 
@@ -120,10 +131,10 @@ define(['core/mustache',
      * @param {string} name The partial name to load.
      * @return {string}
      */
-    var partialHelper = function(name) {
+    Renderer.prototype.partialHelper = function(name) {
         var template = '';
 
-        getTemplate(name, false).done(
+        this.getTemplate(name, false).done(
             function(source) {
                 template = source;
             }
@@ -137,11 +148,12 @@ define(['core/mustache',
      *
      * @method pixHelper
      * @private
+     * @param {object} context The mustache context
      * @param {string} sectionText The text to parse arguments from.
      * @param {function} helper Used to render the alt attribute of the text.
      * @return {string}
      */
-    var pixHelper = function(sectionText, helper) {
+    Renderer.prototype.pixHelper = function(context, sectionText, helper) {
         var parts = sectionText.split(',');
         var key = '';
         var component = '';
@@ -167,8 +179,8 @@ define(['core/mustache',
             ]
         };
         // We forced loading of this early, so it will be in the cache.
-        var template = templateCache[currentThemeName + '/core/pix_icon'];
-        result = mustache.render(template, templatecontext, partialHelper);
+        var template = templateCache[this.currentThemeName + '/core/pix_icon'];
+        result = mustache.render(template, templatecontext, this.partialHelper.bind(this));
         return result.trim();
     };
 
@@ -177,12 +189,13 @@ define(['core/mustache',
      *
      * @method jsHelper
      * @private
+     * @param {object} context The current mustache context.
      * @param {string} sectionText The text to save as a js block.
      * @param {function} helper Used to render the block.
      * @return {string}
      */
-    var jsHelper = function(sectionText, helper) {
-        requiredJS.push(helper(sectionText, this));
+    Renderer.prototype.jsHelper = function(context, sectionText, helper) {
+        this.requiredJS.push(helper(sectionText, context));
         return '';
     };
 
@@ -192,11 +205,12 @@ define(['core/mustache',
      *
      * @method stringHelper
      * @private
+     * @param {object} context The current mustache context.
      * @param {string} sectionText The text to parse the arguments from.
      * @param {function} helper Used to render subsections of the text.
      * @return {string}
      */
-    var stringHelper = function(sectionText, helper) {
+    Renderer.prototype.stringHelper = function(context, sectionText, helper) {
         var parts = sectionText.split(',');
         var key = '';
         var component = '';
@@ -213,15 +227,15 @@ define(['core/mustache',
 
         if (param !== '') {
             // Allow variable expansion in the param part only.
-            param = helper(param, this);
+            param = helper(param, context);
         }
         // Allow json formatted $a arguments.
         if ((param.indexOf('{') === 0) && (param.indexOf('{{') !== 0)) {
             param = JSON.parse(param);
         }
 
-        var index = requiredStrings.length;
-        requiredStrings.push({key: key, component: component, param: param});
+        var index = this.requiredStrings.length;
+        this.requiredStrings.push({key: key, component: component, param: param});
         return '{{_s' + index + '}}';
     };
 
@@ -230,12 +244,13 @@ define(['core/mustache',
      *
      * @method quoteHelper
      * @private
+     * @param {object} context The current mustache context.
      * @param {string} sectionText The text to parse the arguments from.
      * @param {function} helper Used to render subsections of the text.
      * @return {string}
      */
-    var quoteHelper = function(sectionText, helper) {
-        var content = helper(sectionText.trim(), this);
+    Renderer.prototype.quoteHelper = function(context, sectionText, helper) {
+        var content = helper(sectionText.trim(), context);
 
         // Escape the {{ and the ".
         // This involves wrapping {{, and }} in change delimeter tags.
@@ -255,23 +270,23 @@ define(['core/mustache',
      * @param {Object} context Simple types used as the context for the template.
      * @param {String} themeName We set this multiple times, because there are async calls.
      */
-    var addHelpers = function(context, themeName) {
-        currentThemeName = themeName;
-        requiredStrings = [];
-        requiredJS = [];
-        context.uniqid = uniqid++;
+    Renderer.prototype.addHelpers = function(context, themeName) {
+        this.currentThemeName = themeName;
+        this.requiredStrings = [];
+        this.requiredJS = [];
+        context.uniqid = (uniqInstances++);
         context.str = function() {
-          return stringHelper;
-        };
+          return this.stringHelper.bind(this, context);
+        }.bind(this);
         context.pix = function() {
-          return pixHelper;
-        };
+          return this.pixHelper.bind(this, context);
+        }.bind(this);
         context.js = function() {
-          return jsHelper;
-        };
+          return this.jsHelper.bind(this, context);
+        }.bind(this);
         context.quote = function() {
-          return quoteHelper;
-        };
+          return this.quoteHelper.bind(this, context);
+        }.bind(this);
         context.globals = {config: config};
         context.currentTheme = themeName;
     };
@@ -284,14 +299,14 @@ define(['core/mustache',
      * @param {string[]} strings Replacement strings.
      * @return {string}
      */
-    var getJS = function(strings) {
+    Renderer.prototype.getJS = function(strings) {
         var js = '';
-        if (requiredJS.length > 0) {
-            js = requiredJS.join(";\n");
+        if (this.requiredJS.length > 0) {
+            js = this.requiredJS.join(";\n");
         }
 
         // Re-render to get the final strings.
-        return treatStringsInContent(js, strings);
+        return this.treatStringsInContent(js, strings);
     };
 
     /**
@@ -311,7 +326,7 @@ define(['core/mustache',
      * @param {Array} strings The strings to replace with.
      * @return {String} The treated content.
      */
-    var treatStringsInContent = function(content, strings) {
+    Renderer.prototype.treatStringsInContent = function(content, strings) {
         var pattern = /{{_s\d+}}/,
             treated,
             index,
@@ -373,26 +388,26 @@ define(['core/mustache',
      * @param {String} themeName Name of the current theme.
      * @return {Promise} object
      */
-    var doRender = function(templateSource, context, themeName) {
+    Renderer.prototype.doRender = function(templateSource, context, themeName) {
         var deferred = $.Deferred();
 
-        currentThemeName = themeName;
+        this.currentThemeName = themeName;
 
         // Make sure we fetch this first.
-        var loadPixTemplate = getTemplate('core/pix_icon', true);
+        var loadPixTemplate = this.getTemplate('core/pix_icon', true);
 
         loadPixTemplate.done(
             function() {
-                addHelpers(context, themeName);
+                this.addHelpers(context, themeName);
                 var result = '';
                 try {
-                    result = mustache.render(templateSource, context, partialHelper);
+                    result = mustache.render(templateSource, context, this.partialHelper.bind(this));
                 } catch (ex) {
                     deferred.reject(ex);
                 }
 
-                if (requiredStrings.length > 0) {
-                    str.get_strings(requiredStrings)
+                if (this.requiredStrings.length > 0) {
+                    str.get_strings(this.requiredStrings)
                     .then(function(strings) {
 
                         // Why do we not do another call the render here?
@@ -402,14 +417,14 @@ define(['core/mustache',
                         // would get inserted in the template in the first pass
                         // and cause the template to die on the second pass (unbalanced).
 
-                        result = treatStringsInContent(result, strings);
-                        deferred.resolve(result, getJS(strings));
-                    })
+                        result = this.treatStringsInContent(result, strings);
+                        deferred.resolve(result, this.getJS(strings));
+                    }.bind(this))
                     .fail(deferred.reject);
                 } else {
-                    deferred.resolve(result.trim(), getJS([]));
+                    deferred.resolve(result.trim(), this.getJS([]));
                 }
-            }
+            }.bind(this)
         ).fail(deferred.reject);
         return deferred.promise();
     };
@@ -468,11 +483,58 @@ define(['core/mustache',
         }
     };
 
+    /**
+     * Load a template and call doRender on it.
+     *
+     * @method render
+     * @private
+     * @param {string} templateName - should consist of the component and the name of the template like this:
+     *                              core/menu (lib/templates/menu.mustache) or
+     *                              tool_bananas/yellow (admin/tool/bananas/templates/yellow.mustache)
+     * @param {Object} context - Could be array, string or simple value for the context of the template.
+     * @param {string} themeName - Name of the current theme.
+     * @return {Promise} JQuery promise object resolved when the template has been rendered.
+     */
+    Renderer.prototype.render = function(templateName, context, themeName) {
+        var deferred = $.Deferred();
+
+        if (typeof (themeName) === "undefined") {
+            // System context by default.
+            themeName = config.theme;
+        }
+
+        this.currentThemeName = themeName;
+
+        var loadTemplate = this.getTemplate(templateName, true);
+
+        loadTemplate.done(
+            function(templateSource) {
+                var renderPromise = this.doRender(templateSource, context, themeName);
+
+                renderPromise.done(
+                    function(result, js) {
+                        deferred.resolve(result, js);
+                    }
+                ).fail(
+                    function(ex) {
+                        deferred.reject(ex);
+                    }
+                );
+            }.bind(this)
+        ).fail(
+            function(ex) {
+                deferred.reject(ex);
+            }
+        );
+        return deferred.promise();
+    };
+
 
     return /** @alias module:core/templates */ {
         // Public variables and functions.
         /**
-         * Load a template and call doRender on it.
+         * Every call to render creates a new instance of the class and calls render on it. This
+         * means each render call has it's own class variables.
          *
          * @method render
          * @private
@@ -484,37 +546,8 @@ define(['core/mustache',
          * @return {Promise} JQuery promise object resolved when the template has been rendered.
          */
         render: function(templateName, context, themeName) {
-            var deferred = $.Deferred();
-
-            if (typeof (themeName) === "undefined") {
-                // System context by default.
-                themeName = config.theme;
-            }
-
-            currentThemeName = themeName;
-
-            var loadTemplate = getTemplate(templateName, true);
-
-            loadTemplate.done(
-                function(templateSource) {
-                    var renderPromise = doRender(templateSource, context, themeName);
-
-                    renderPromise.done(
-                        function(result, js) {
-                            deferred.resolve(result, js);
-                        }
-                    ).fail(
-                        function(ex) {
-                            deferred.reject(ex);
-                        }
-                    );
-                }
-            ).fail(
-                function(ex) {
-                    deferred.reject(ex);
-                }
-            );
-            return deferred.promise();
+            var renderer = new Renderer();
+            return renderer.render(templateName, context, themeName);
         },
 
         /**
index 79e62c1..5e03c41 100644 (file)
@@ -258,656 +258,11 @@ class boxnet_client extends oauth2_client {
 }
 
 /**
- * Box REST Client Library for PHP5 Developers.
- *
- * Deprecation note: As of the 14th of December 2013 Box.net APIv1, used by this class,
- * is reaching its end of life. Please use boxnet_client() instead.
- *
- * @package core
- * @author James Levy <james@box.net>
- * @link http://enabled.box.net
- * @access public
- * @version 1.0
- * @copyright copyright Box.net 2007
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  * @deprecated since 2.6, 2.5.3, 2.4.7
  */
 class boxclient {
-    /** @var string */
-    public $auth_token = '';
-    /** @var string */
-    private $_box_api_url = 'https://www.box.com/api/1.0/rest';
-    private $_box_api_upload_url = 'http://upload.box.com/api/1.0/upload';
-    private $_box_api_download_url = 'http://www.box.com/api/1.0/download';
-    private $_box_api_auth_url = 'http://www.box.com/api/1.0/auth';
-    private $_error_code = '';
-    private $_error_msg = '';
-    /** @var bool */
-    private $debug = false;
-
-    /**
-     * @param string $api_key
-     * @param string $auth_token
-     * @param bool $debug
-     */
-    public function __construct($api_key, $auth_token = '', $debug = false) {
-        $this->api_key    = $api_key;
-        $this->auth_token = $auth_token;
-        if (!empty($debug)) {
-            $this->debug = true;
-        } else {
-            $this->debug = false;
-        }
-    }
-    /**
-     * Setup for Functions
-     *
-     * @param string $method
-     * @param array $params
-     * @return array
-     */
-    function makeRequest($method, $params = array()) {
-        $this->_clearErrors();
-        $c = new curl(array('debug'=>$this->debug, 'cache'=>true, 'module_cache'=>'repository'));
-        $c->setopt(array('CURLOPT_FOLLOWLOCATION'=>1));
-        try {
-            if ($method == 'upload'){
-                $request = $this->_box_api_upload_url.'/'.
-                    $this->auth_token.'/'.$params['folder_id'];
-                $xml = $c->post($request, $params);
-            }else{
-                $args = array();
-                $xml = $c->get($this->_box_api_url, $params);
-            }
-            $xml_parser = xml_parser_create();
-            // set $data here
-            xml_parse_into_struct($xml_parser, $xml, $data);
-            xml_parser_free($xml_parser);
-        } catch (moodle_exception $e) {
-            $this->setError(0, 'connection time-out or invalid url');
-            return false;
-        }
-        return $data;
-    }
-    /**
-     * @param array $params
-     * @return array
-     */
-    function getTicket($params = array()) {
-        $params['api_key'] = $this->api_key;
-        $params['action']  = 'get_ticket';
-        $ret_array = array();
-        $data = $this->makeRequest('action=get_ticket', $params);
-        if ($this->_checkForError($data)) {
-            return false;
-        }
-        foreach ($data as $a) {
-            switch ($a['tag']) {
-            case 'STATUS':
-                $ret_array['status'] = $a['value'];
-                break;
-            case 'TICKET':
-                $ret_array['ticket'] = $a['value'];
-                break;
-            }
-        }
-        return $ret_array;
-    }
-
-    /**
-     * $options['username'] and $options['password'] must be
-     * given, we  will use them to obtain a valid auth_token
-     * To get a token, you should use following code:
-     *
-     * <code>
-     * $box = new boxclient('dmls97d8j3i9tn7av8y71m9eb55vrtj4');
-     * Get a ticket
-     * $t = $box->getTicket();
-     * $box->getAuthToken($t['ticket'], array(
-     *              'username'=>'dongsheng@moodle.com',
-     *              'password'=>'xxx'));
-     * </code>
-     *
-     * @param string $ticket
-     * @param string $username
-     * @param string $password
-     * @return mixed
-     */
-    function getAuthToken($ticket, $username, $password) {
-        $c = new curl(array('debug'=>$this->debug));
-        $c->setopt(array('CURLOPT_FOLLOWLOCATION'=>0));
-        $param =  array(
-            'login_form1'=>'',
-            'login'=>$username,
-            'password'=>$password,
-            'dologin'=>1,
-            '__login'=>1
-            );
-        try {
-            $ret = $c->post($this->_box_api_auth_url.'/'.$ticket, $param);
-        } catch (moodle_exception $e) {
-            $this->setError(0, 'connection time-out or invalid url');
-            return false;
-        }
-        $header = $c->getResponse();
-        if(empty($header['location'])) {
-            throw new repository_exception('invalidpassword', 'repository_boxnet');
-        }
-        $location = $header['location'];
-        preg_match('#auth_token=(.*)$#i', $location, $matches);
-        $auth_token = $matches[1];
-        if(!empty($auth_token)) {
-            $this->auth_token = $auth_token;
-            return $auth_token;
-        } else {
-            throw new repository_exception('invalidtoken', 'repository_boxnet');
-        }
-    }
-    /**
-     * @param string $path Unused
-     * @param array $params
-     * @return array
-     */
-    function getfiletree($path, $params = array()) {
-        $this->_clearErrors();
-        $params['auth_token'] = $this->auth_token;
-        $params['folder_id']  = 0;
-        $params['api_key']    = $this->api_key;
-        $params['action']     = 'get_account_tree';
-        $params['onelevel']   = 1;
-        $params['params[]']   = 'nozip';
-        $c = new curl(array('debug'=>$this->debug));
-        $c->setopt(array('CURLOPT_FOLLOWLOCATION'=>1));
-        try {
-            $args = array();
-            $xml = $c->get($this->_box_api_url, $params);
-        } catch (Exception $e){
-        }
-        $ret = array();
-        $o = simplexml_load_string(trim($xml));
-        if($o->status == 'listing_ok') {
-            $tree = $o->tree->folder;
-            $this->buildtree($tree, $ret);
-        }
-        return $ret;
-    }
-
-    /**
-     * Get box.net file info
-     *
-     * @param string $fileid
-     * @param int $timeout request timeout in seconds
-     * @return stdClass|null
-     */
-    function get_file_info($fileid, $timeout = 0) {
-        $this->_clearErrors();
-        $params = array();
-        $params['action']     = 'get_file_info';
-        $params['file_id']    = $fileid;
-        $params['auth_token'] = $this->auth_token;
-        $params['api_key']    = $this->api_key;
-        $http = new curl(array('debug'=>$this->debug));
-        $xml = $http->get($this->_box_api_url, $params, array('timeout' => $timeout));
-        if (!$http->get_errno()) {
-            $o = simplexml_load_string(trim($xml));
-            if ($o->status == 's_get_file_info') {
-                return $o->info;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * @param array $sax
-     * @param array $tree Passed by reference
-     */
-    function buildtree($sax, &$tree){
-        $sax = (array)$sax;
-        $count = 0;
-        foreach($sax as $k=>$v){
-            if($k == 'folders'){
-                $o = $sax[$k];
-                foreach($o->folder as $z){
-                    $tmp = array('title'=>(string)$z->attributes()->name,
-                        'size'=>0, 'date'=>userdate(time()),
-                        'thumbnail'=>'https://www.box.com/img/small_folder_icon.gif',
-                        'path'=>array('name'=>(string)$z->attributes()->name, 'path'=>(int)$z->attributes()->id));
-                    $tmp['children'] = array();
-                    $this->buildtree($z, $tmp['children']);
-                    $tree[] = $tmp;
-                }
-            } elseif ($k == 'files') {
-                $val = $sax[$k]->file;
-                foreach($val as $file){
-                    $thumbnail = (string)$file->attributes()->thumbnail;
-                    if (!preg_match('#^(?:http://)?([^/]+)#i', $thumbnail)) {
-                        $thumbnail =  'http://www.box.com'.$thumbnail;
-                    }
-                    $tmp = array('title'=>(string)$file->attributes()->file_name,
-                        'size'=>display_size((int)$file->attributes()->size),
-                        'thumbnail'=>$thumbnail,
-                        'date'=>userdate((int)$file->attributes()->updated),
-                        'source'=> $this->_box_api_download_url .'/'
-                            .$this->auth_token.'/'.(string)$file->attributes()->id,
-                        'url'=>(string)$file->attributes()->shared_link);
-                    $tree[] = $tmp;
-                }
-            }
-            $count++;
-        }
-    }
-    /**
-     * @param array $params
-     * @return bool|array Array or false
-     */
-    function getAccountTree($params = array()) {
-        $params['auth_token'] = $this->auth_token;
-        $params['folder_id']  = 0;
-        $params['api_key']    = $this->api_key;
-        $params['action']     = 'get_account_tree';
-        $params['onelevel']   = 1;
-        $params['params[]']   = 'nozip';
-        $ret_array = array();
-        $data = $this->makeRequest('action=get_account_tree', $params);
-        if ($this->_checkForError($data)) {
-            return false;
-        }
-        $tree_count=count($data);
-        $entry_count = 0;
-        for ($i=0; $i<$tree_count; $i++) {
-            $a = $data[$i];
-            switch ($a['tag'])
-            {
-            case 'FOLDER':
-                if (@is_array($a['attributes'])) {
-                    $ret_array['folder_id'][$i] = $a['attributes']['ID'];
-                    $ret_array['folder_name'][$i] = $a['attributes']['NAME'];
-                    $ret_array['shared'][$i] = $a['attributes']['SHARED'];
-                }
-                break;
-
-            case 'FILE':
-                if (@is_array($a['attributes'])) {
-                    $ret_array['file_id'][$i] = $a['attributes']['ID'];
-                    @$ret_array['file_name'][$i] = $a['attributes']['FILE_NAME'];
-                    @$ret_array['file_keyword'][$i] = $a['attributes']['KEYWORD'];
-                    @$ret_array['file_size'][$i] = display_size($a['attributes']['SIZE']);
-                    @$ret_array['file_date'][$i] = userdate($a['attributes']['UPDATED']);
-                    if (preg_match('#^(?:http://)?([^/]+)#i', $a['attributes']['THUMBNAIL'])) {
-                        @$ret_array['thumbnail'][$i] =  $a['attributes']['THUMBNAIL'];
-                    } else {
-                        @$ret_array['thumbnail'][$i] =  'http://www.box.com'.$a['attributes']['THUMBNAIL'];
-                    }
-                    $entry_count++;
-                }
-                break;
-            }
-        }
-        return $ret_array;
-    }
-
-    /**
-     * @param string $new_folder_name
-     * @param array $params
-     * @return bool|array Array or false
-     */
-    function CreateFolder($new_folder_name, $params = array()) {
-        $params['auth_token'] =  $this->auth_token;
-        $params['api_key']    = $this->api_key;
-        $params['action']     = 'create_folder';
-        $params['name']       = $new_folder_name;
-        $defaults = array(
-            'parent_id'  => 0, //Set to '0' by default. Change to create within sub-folder.
-            'share'     => 1, //Set to '1' by default. Set to '0' to make folder private.
-        );
-        foreach ($defaults as $key => $value) {
-            if (!array_key_exists($key, $params)) {
-                $params[$key] = $value;
-            }
-        }
-
-        $ret_array = array();
-        $data = $this->makeRequest('action=create_folder', $params);
-        if ($this->_checkForError($data)) {
-            return false;
-        }
-        foreach ($data as $a) {
-            if (!empty($a['value'])) {
-                switch ($a['tag']) {
-
-                case 'FOLDER_ID':
-                    $ret_array['folder_id'] = $a['value'];
-                    break;
-
-                case 'FOLDER_NAME':
-                    $ret_array['folder_name'] = $a['value'];
-                    break;
-
-                case 'FOLDER_TYPE_ID':
-                    $ret_array['folder_type_id'] = $a['value'];
-                    break;
-
-                case 'SHARED':
-                    $ret_array['shared'] = $a['value'];
-                    break;
-
-                case 'PASSWORD':
-                    $ret_array['password'] = $a['value'];
-                    break;
-                }
-            } else {
-                $ret_array[strtolower($a['tag'])] = null;
-            }
-        }
-        return $ret_array;
+    public function __construct() {
+        throw new coding_exception(__CLASS__ . ' has been removed. Please update your code to use boxnet_client.',
+            DEBUG_DEVELOPER);
     }
-
-    /**
-     * Upload a File
-     * @param array $params the file MUST be present in key 'file' and be a moodle stored_file object.
-     * @return array|bool Array or false
-     */
-    function UploadFile ($params = array()) {
-        $params['auth_token'] = $this->auth_token;
-        // this param should be the full path of the file
-        $params['new_file1']  = $params['file'];
-        unset($params['file']);
-        $defaults = array(
-            'folder_id' => 0, //Set to '0' by default. Change to create within sub-folder.
-            'share'     => 1, //Set to '1' by default. Set to '0' to make folder private.
-        );
-        foreach ($defaults as $key => $value) {
-            if (!array_key_exists($key, $params)) {
-                $params[$key] = $value;
-            }
-        }
-        $ret_array = array();
-        $entry_count = 0;
-        $data = $this->makeRequest('upload', $params);
-        if ($this->_checkForError($data)) {
-            return false;
-        }
-        for ($i=0, $tree_count=count($data); $i<$tree_count; $i++) {
-            $a = $data[$i];
-            switch ($a['tag']) {
-            case 'STATUS':
-                $ret_array['status'] = $a['value'];
-                break;
-
-            case 'FILE':
-                if (is_array($a['attributes'])) {
-                    @$ret_array['file_name'][$i] = $a['attributes']['FILE_NAME'];
-                    @$ret_array['id'][$i] = $a['attributes']['ID'];
-                    @$ret_array['folder_name'][$i] = $a['attributes']['FOLDER_NAME'];
-                    @$ret_array['error'][$i] = $a['attributes']['ERROR'];
-                    @$ret_array['public_name'][$i] = $a['attributes']['PUBLIC_NAME'];
-                    $entry_count++;
-                }
-                break;
-            }
-        }
-
-        return $ret_array;
-    }
-    /**
-     * @param string $fileid
-     * @param string $newname
-     * @return bool
-     */
-    function RenameFile($fileid, $newname) {
-        $params = array(
-            'api_key'    => $this->api_key,
-            'auth_token' => $this->auth_token,
-            'action'     => 'rename',
-            'target'     => 'file',
-            'target_id'  => $fileid,
-            'new_name'   => $newname,
-        );
-        $data = $this->makeRequest('action=rename', $params);
-        if ($this->_checkForError($data)) {
-            return false;
-        }
-        foreach ($data as $a) {
-            switch ($a['tag']) {
-                case 'STATUS':
-                    if ($a['value'] == 's_rename_node') {
-                        return true;
-                    }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Register New User
-     *
-     * @param array $params
-     * @return array|bool Outcome Array or false
-     */
-    function RegisterUser($params = array()) {
-        $params['api_key'] = $this->api_key;
-        $params['action']  = 'register_new_user';
-        $params['login']   = $_REQUEST['login'];
-        $params['password'] = $_REQUEST['password'];
-        $ret_array = array();
-        $data = $this->makeRequest('action=register_new_user', $params);
-        if ($this->_checkForError($data)) {
-            return false;
-        }
-        foreach ($data as $a) {
-            switch ($a['tag']) {
-            case 'STATUS':
-                $ret_array['status'] = $a['value'];
-                break;
-
-            case 'AUTH_TOKEN':
-                $ret_array['auth_token'] = $a['value'];
-                break;
-
-            case 'LOGIN':
-                $ret_array['login'] = $a['value'];
-                break;
-            case 'SPACE_AMOUNT':
-                $ret_array['space_amount'] = $a['value'];
-                break;
-            case 'SPACE_USED':
-                $ret_array['space_used'] = $a['value'];
-                break;
-            }
-        }
-
-        return $ret_array;
-    }
-
-    /**
-     * Add Tags  (http://enabled.box.net/docs/rest#add_to_tag)
-     *
-     * @param string $tag
-     * @param string $id Set to ID of file or folder
-     * @param string $target_type File or folder
-     * @param array $params
-     * @return array|bool Outcome Array or false
-     */
-    function AddTag($tag, $id, $target_type, $params = array()) {
-        $params['auth_token'] = $this->auth_token;
-        $params['api_key']    = $this->api_key;
-        $params['action']     = 'add_to_tag';
-        $params['target']     = $target_type; // File or folder
-        $params['target_id']  = $id; // Set to ID of file or folder
-        $params['tags[]']     = $tag;
-        $ret_array = array();
-        $data = $this->makeRequest('action=add_to_tag', $params);
-        if ($this->_checkForError($data)) {
-            return false;
-        }
-        foreach ($data as $a) {
-            switch ($a['tag']) {
-            case 'STATUS':
-                $ret_array['status'] = $a['value'];
-
-                break;
-            }
-        }
-        return $ret_array;
-    }
-
-    /**
-     * Public Share  (http://enabled.box.net/docs/rest#public_share)
-     *
-     * @param string $message
-     * @param string $emails
-     * @param string $id Set to ID of file or folder
-     * @param string $target_type File or folder
-     * @param string $password
-     * @param array $params
-     * @return array|bool Outcome Array or false
-     */
-    function PublicShare($message, $emails, $id, $target_type, $password, $params = array()) {
-        $params['auth_token'] = $this->auth_token;
-        $params['api_key']    = $this->api_key;
-        $params['action']     = 'public_share';
-        $params['target']     = $target_type;
-        $params['target_id']  = $id;
-        $params['password']   =  $password;
-        $params['message']    = $message;
-        $params['emails']     = $emails;
-        $ret_array = array();
-        $data = $this->makeRequest('action=public_share', $params);
-        if ($this->_checkForError($data)) {
-            return false;
-        }
-        foreach ($data as $a) {
-            switch ($a['tag']) {
-            case 'STATUS':
-                $ret_array['status'] = $a['value'];
-                break;
-            case 'PUBLIC_NAME':
-                $ret_array['public_name'] = $a['value'];
-                break;
-            }
-        }
-
-        return $ret_array;
-    }
-    /**
-     * Get Friends  (http://enabled.box.net/docs/rest#get_friends)
-     *
-     * @param array $params
-     * @return array|bool Outcome Array or false
-     */
-    function GetFriends ($params = array()) {
-        $params['auth_token'] = $this->auth_token;
-        $params['action']     = 'get_friends';
-        $params['api_key']    = $this->api_key;
-        $params['params[]']   = 'nozip';
-        $ret_array = array();
-        $data = $this->makeRequest('action=get_friends', $params);
-        if ($this->_checkForError($data)) {
-            return false;
-        }
-        foreach ($data as $a) {
-            switch ($a['tag']) {
-            case 'NAME':
-                $ret_array['name'] = $a['value'];
-                break;
-            case 'EMAIL':
-                $ret_array['email'] = $a['value'];
-                break;
-            case 'ACCEPTED':
-                $ret_array['accepted'] = $a['value'];
-                break;
-            case 'AVATAR_URL':
-                $ret_array['avatar_url'] = $a['value'];
-                break;
-            case 'ID':
-                $ret_array['id'] = $a['value'];
-                break;
-            case 'URL':
-                $ret_array['url'] = $a['value'];
-                break;
-            case 'STATUS':
-                $ret_array['status'] = $a['value'];
-                break;
-            }
-        }
-        return $ret_array;
-    }
-
-    /**
-     * Logout User  (http://enabled.box.net/docs/rest#get_friends)
-     *
-     * @param array $params
-     * @return array|bool Outcome Array or false
-     */
-    function Logout($params = array()) {
-        $params['auth_token'] = $this->auth_token;
-        $params['api_key']    = $this->api_key;
-        $params['action']     = 'logout';
-        $ret_array = array();
-        $data = $this->makeRequest('action=logout', $params);
-        if ($this->_checkForError($data)) {
-            return false;
-        }
-        foreach ($data as $a) {
-            switch ($a['tag']) {
-            case 'ACTION':
-                $ret_array['logout'] = $a['value'];
-
-                break;
-            }
-            return $ret_array;
-        }
-    }
-    /**
-     * @param array $data
-     * @return bool
-     */
-    function _checkForError($data) {
-        if ($this->_error_msg != '') {
-            return true;
-        }
-        if (@$data[0]['attributes']['STAT'] == 'fail') {
-            $this->_error_code = $data[1]['attributes']['CODE'];
-            $this->_error_msg = $data[1]['attributes']['MSG'];
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * @return bool
-     */
-    public function isError() {
-        if  ($this->_error_msg != '') {
-            return true;
-        }
-        return false;
-    }
-    /**
-     *
-     */
-    public function setError($code = 0, $msg){
-        $this->_error_code = $code;
-        $this->_error_msg  = $msg;
-    }
-    /**
-     * @return string
-     */
-    function getErrorMsg() {
-        return '<p>Error: (' . $this->_error_code . ') ' . $this->_error_msg . '</p>';
-    }
-    /**
-     * @return string
-     */
-    function getErrorCode() {
-        return $this->_error_code;
-    }
-    /**
-     *
-     */
-    function _clearErrors() {
-        $this->_error_code = '';
-        $this->_error_msg = '';
-    }
-
 }
index 3148646..34744dc 100644 (file)
@@ -294,6 +294,14 @@ $functions = array(
         'type' => 'write',
         'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
     ),
+    'core_course_get_activities_overview' => array(
+        'classname' => 'core_course_external',
+        'methodname' => 'get_activities_overview',
+        'classpath' => 'course/externallib.php',
+        'description' => 'Return activities overview for the given courses.',
+        'type' => 'read',
+        'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
+    ),
     'core_enrol_get_course_enrolment_methods' => array(
         'classname' => 'core_enrol_external',
         'methodname' => 'get_course_enrolment_methods',
index 8964b9b..e8acbde 100644 (file)
@@ -4490,3 +4490,134 @@ function external_function_info($function, $strictness=MUST_EXIST) {
     return external_api::external_function_info($function, $strictness);
 }
 
+/**
+ * Retrieves an array of records from a CSV file and places
+ * them into a given table structure
+ * This function is deprecated. Please use csv_import_reader() instead.
+ *
+ * @deprecated since Moodle 3.2 MDL-55126
+ * @todo MDL-55195 for final deprecation in Moodle 3.6
+ * @see csv_import_reader::load_csv_content()
+ * @global stdClass $CFG
+ * @global moodle_database $DB
+ * @param string $file The path to a CSV file
+ * @param string $table The table to retrieve columns from
+ * @return bool|array Returns an array of CSV records or false
+ */
+function get_records_csv($file, $table) {
+    global $CFG, $DB;
+
+    debugging('get_records_csv() is deprecated. Please use lib/csvlib.class.php csv_import_reader() instead.');
+
+    if (!$metacolumns = $DB->get_columns($table)) {
+        return false;
+    }
+
+    if(!($handle = @fopen($file, 'r'))) {
+        print_error('get_records_csv failed to open '.$file);
+    }
+
+    $fieldnames = fgetcsv($handle, 4096);
+    if(empty($fieldnames)) {
+        fclose($handle);
+        return false;
+    }
+
+    $columns = array();
+
+    foreach($metacolumns as $metacolumn) {
+        $ord = array_search($metacolumn->name, $fieldnames);
+        if(is_int($ord)) {
+            $columns[$metacolumn->name] = $ord;
+        }
+    }
+
+    $rows = array();
+
+    while (($data = fgetcsv($handle, 4096)) !== false) {
+        $item = new stdClass;
+        foreach($columns as $name => $ord) {
+            $item->$name = $data[$ord];
+        }
+        $rows[] = $item;
+    }
+
+    fclose($handle);
+    return $rows;
+}
+
+/**
+ * Create a file with CSV contents
+ * This function is deprecated. Please use download_as_dataformat() instead.
+ *
+ * @deprecated since Moodle 3.2 MDL-55126
+ * @todo MDL-55195 for final deprecation in Moodle 3.6
+ * @see download_as_dataformat (lib/dataformatlib.php)
+ * @global stdClass $CFG
+ * @global moodle_database $DB
+ * @param string $file The file to put the CSV content into
+ * @param array $records An array of records to write to a CSV file
+ * @param string $table The table to get columns from
+ * @return bool success
+ */
+function put_records_csv($file, $records, $table = NULL) {
+    global $CFG, $DB;
+
+    debugging('put_records_csv() is deprecated. Please use lib/dataformatlib.php download_as_dataformat()');
+
+    if (empty($records)) {
+        return true;
+    }
+
+    $metacolumns = NULL;
+    if ($table !== NULL && !$metacolumns = $DB->get_columns($table)) {
+        return false;
+    }
+
+    echo "x";
+
+    if(!($fp = @fopen($CFG->tempdir.'/'.$file, 'w'))) {
+        print_error('put_records_csv failed to open '.$file);
+    }
+
+    $proto = reset($records);
+    if(is_object($proto)) {
+        $fields_records = array_keys(get_object_vars($proto));
+    }
+    else if(is_array($proto)) {
+        $fields_records = array_keys($proto);
+    }
+    else {
+        return false;
+    }
+    echo "x";
+
+    if(!empty($metacolumns)) {
+        $fields_table = array_map(create_function('$a', 'return $a->name;'), $metacolumns);
+        $fields = array_intersect($fields_records, $fields_table);
+    }
+    else {
+        $fields = $fields_records;
+    }
+
+    fwrite($fp, implode(',', $fields));
+    fwrite($fp, "\r\n");
+
+    foreach($records as $record) {
+        $array  = (array)$record;
+        $values = array();
+        foreach($fields as $field) {
+            if(strpos($array[$field], ',')) {
+                $values[] = '"'.str_replace('"', '\"', $array[$field]).'"';
+            }
+            else {
+                $values[] = $array[$field];
+            }
+        }
+        fwrite($fp, implode(',', $values)."\r\n");
+    }
+
+    fclose($fp);
+    @chmod($CFG->tempdir.'/'.$file, $CFG->filepermissions);
+    return true;
+}
index 929ffbd..343663f 100644 (file)
@@ -922,8 +922,7 @@ class oci_native_moodle_database extends moodle_database {
         return true;
     }
 
-    protected function bind_params($stmt, array $params=null, $tablename=null) {
-        $descriptors = array();
+    protected function bind_params($stmt, array &$params=null, $tablename=null, array &$descriptors = null) {
         if ($params) {
             $columns = array();
             if ($tablename) {
@@ -943,15 +942,21 @@ class oci_native_moodle_database extends moodle_database {
                 if (is_array($value)) { // Let's go to bind special cases (lob descriptors)
                     if (isset($value['clob'])) {
                         $lob = oci_new_descriptor($this->oci, OCI_DTYPE_LOB);
+                        if ($descriptors === null) {
+                            throw new coding_exception('moodle_database::bind_params() $descriptors not specified for clob');
+                        }
+                        $descriptors[] = $lob;
                         oci_bind_by_name($stmt, $key, $lob, -1, SQLT_CLOB);
                         $lob->writeTemporary($this->oracle_dirty_hack($tablename, $columnname, $params[$key]['clob']), OCI_TEMP_CLOB);
-                        $descriptors[] = $lob;
                         continue; // Column binding finished, go to next one
                     } else if (isset($value['blob'])) {
                         $lob = oci_new_descriptor($this->oci, OCI_DTYPE_LOB);
+                        if ($descriptors === null) {
+                            throw new coding_exception('moodle_database::bind_params() $descriptors not specified for clob');
+                        }
+                        $descriptors[] = $lob;
                         oci_bind_by_name($stmt, $key, $lob, -1, SQLT_BLOB);
                         $lob->writeTemporary($params[$key]['blob'], OCI_TEMP_BLOB);
-                        $descriptors[] = $lob;
                         continue; // Column binding finished, go to next one
                     }
                 } else {
@@ -961,9 +966,12 @@ class oci_native_moodle_database extends moodle_database {
                     // conditions and other raw SQLs not covered by the above function.
                     if (strlen($value) > 4000) {
                         $lob = oci_new_descriptor($this->oci, OCI_DTYPE_LOB);
+                        if ($descriptors === null) {
+                            throw new coding_exception('moodle_database::bind_params() $descriptors not specified for clob');
+                        }
+                        $descriptors[] = $lob;
                         oci_bind_by_name($stmt, $key, $lob, -1, SQLT_CLOB);
                         $lob->writeTemporary($this->oracle_dirty_hack($tablename, $columnname, $params[$key]), OCI_TEMP_CLOB);
-                        $descriptors[] = $lob;
                         continue; // Param binding finished, go to next one.
                     }
                 }
@@ -1000,6 +1008,12 @@ class oci_native_moodle_database extends moodle_database {
                     default: // Bind as CHAR (applying dirty hack)
                         // TODO: Optimise
                         $params[$key] = $this->oracle_dirty_hack($tablename, $columnname, $params[$key]);
+                        // Because of PHP7 bug (https://bugs.php.net/bug.php?id=72524) it seems that it's
+                        // impossible to bind NULL values in a reliable way, let's use empty string
+                        // instead in the mean time.
+                        if ($params[$key] === null && version_compare(PHP_VERSION, '7.0.0', '>=')) {
+                            $params[$key] = '';
+                        }
                         oci_bind_by_name($stmt, $key, $params[$key]);
                 }
             }
@@ -1009,6 +1023,10 @@ class oci_native_moodle_database extends moodle_database {
 
     protected function free_descriptors($descriptors) {
         foreach ($descriptors as $descriptor) {
+            // Because all descriptors used in the driver come from LOB::writeTemporary() calls
+            // we can safely close them here unconditionally.
+            $descriptor->close();
+            // Free resources.
             oci_free_descriptor($descriptor);
         }
     }
@@ -1045,8 +1063,10 @@ class oci_native_moodle_database extends moodle_database {
         list($sql, $params) = $this->tweak_param_names($sql, $params);
         $this->query_start($sql, $params, SQL_QUERY_UPDATE);
         $stmt = $this->parse_query($sql);
-        $this->bind_params($stmt, $params);
+        $descriptors = array();
+        $this->bind_params($stmt, $params, null, $descriptors);
         $result = oci_execute($stmt, $this->commit_status);
+        $this->free_descriptors($descriptors);
         $this->query_end($result, $stmt);
         oci_free_statement($stmt);
 
@@ -1109,8 +1129,10 @@ class oci_native_moodle_database extends moodle_database {
         list($rawsql, $params) = $this->tweak_param_names($rawsql, $params);
         $this->query_start($rawsql, $params, SQL_QUERY_SELECT);
         $stmt = $this->parse_query($rawsql);
-        $this->bind_params($stmt, $params);
+        $descriptors = array();
+        $this->bind_params($stmt, $params, null, $descriptors);
         $result = oci_execute($stmt, $this->commit_status);
+        $this->free_descriptors($descriptors);
         $this->query_end($result, $stmt);
 
         return $this->create_recordset($stmt);
@@ -1144,8 +1166,10 @@ class oci_native_moodle_database extends moodle_database {
         list($rawsql, $params) = $this->tweak_param_names($rawsql, $params);
         $this->query_start($rawsql, $params, SQL_QUERY_SELECT);
         $stmt = $this->parse_query($rawsql);
-        $this->bind_params($stmt, $params);
+        $descriptors = array();
+        $this->bind_params($stmt, $params, null, $descriptors);
         $result = oci_execute($stmt, $this->commit_status);
+        $this->free_descriptors($descriptors);
         $this->query_end($result, $stmt);
 
         $records = null;
@@ -1183,8 +1207,10 @@ class oci_native_moodle_database extends moodle_database {
         list($sql, $params) = $this->tweak_param_names($sql, $params);
         $this->query_start($sql, $params, SQL_QUERY_SELECT);
         $stmt = $this->parse_query($sql);
-        $this->bind_params($stmt, $params);
+        $descriptors = array();
+        $this->bind_params($stmt, $params, null, $descriptors);
         $result = oci_execute($stmt, $this->commit_status);
+        $this->free_descriptors($descriptors);
         $this->query_end($result, $stmt);
 
         $records = null;
@@ -1241,16 +1267,17 @@ class oci_native_moodle_database extends moodle_database {
         list($sql, $params, $type) = $this->fix_sql_params($sql, $params);
         $sql .= $returning;
 
-        $id = null;
+        $id = 0;
 
         // note we don't need tweak_param_names() here. Placeholders are safe column names. MDL-28080
         // list($sql, $params) = $this->tweak_param_names($sql, $params);
         $this->query_start($sql, $params, SQL_QUERY_INSERT);
         $stmt = $this->parse_query($sql);
-        $descriptors = $this->bind_params($stmt, $params, $table);
         if ($returning) {
             oci_bind_by_name($stmt, ":oracle_id", $id, 10, SQLT_INT);
         }
+        $descriptors = array();
+        $this->bind_params($stmt, $params, $table, $descriptors);
         $result = oci_execute($stmt, $this->commit_status);
         $this->free_descriptors($descriptors);
         $this->query_end($result, $stmt);
@@ -1364,7 +1391,8 @@ class oci_native_moodle_database extends moodle_database {
         // list($sql, $params) = $this->tweak_param_names($sql, $params);
         $this->query_start($sql, $params, SQL_QUERY_UPDATE);
         $stmt = $this->parse_query($sql);
-        $descriptors = $this->bind_params($stmt, $params, $table);
+        $descriptors = array();
+        $this->bind_params($stmt, $params, $table, $descriptors);
         $result = oci_execute($stmt, $this->commit_status);
         $this->free_descriptors($descriptors);
         $this->query_end($result, $stmt);
@@ -1454,7 +1482,8 @@ class oci_native_moodle_database extends moodle_database {
         list($sql, $params) = $this->tweak_param_names($sql, $params);
         $this->query_start($sql, $params, SQL_QUERY_UPDATE);
         $stmt = $this->parse_query($sql);
-        $descriptors = $this->bind_params($stmt, $params, $table);
+        $descriptors = array();
+        $this->bind_params($stmt, $params, $table, $descriptors);
         $result = oci_execute($stmt, $this->commit_status);
         $this->free_descriptors($descriptors);
         $this->query_end($result, $stmt);
@@ -1485,8 +1514,10 @@ class oci_native_moodle_database extends moodle_database {
         list($sql, $params) = $this->tweak_param_names($sql, $params);
         $this->query_start($sql, $params, SQL_QUERY_UPDATE);
         $stmt = $this->parse_query($sql);
-        $this->bind_params($stmt, $params);
+        $descriptors = array();
+        $this->bind_params($stmt, $params, null, $descriptors);
         $result = oci_execute($stmt, $this->commit_status);
+        $this->free_descriptors($descriptors);
         $this->query_end($result, $stmt);
         oci_free_statement($stmt);
 
index 731161c..3320709 100644 (file)
Binary files a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js and b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js differ
index ebf6605..4a4d051 100644 (file)
Binary files a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js and b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js differ
index 0c89fae..4680aef 100644 (file)
Binary files a/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor.js and b/lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor.js differ
index cbcce69..79be551 100644 (file)
@@ -287,14 +287,6 @@ EditorClean.prototype = {
 
         // Run some more rules that care about quotes and whitespace.
         rules = [
-            // Get all style attributes so we can work on them.
-            {regex: /(<[^>]*?style\s*?=\s*?")([^>"]*)(")/gi, replace: function(match, group1, group2, group3) {
-                    // Remove MSO-blah, MSO:blah style attributes.
-                    group2 = group2.replace(/(?:^|;)[\s]*MSO[-:](?:&[\w]*;|[^;"])*/gi,"");
-                    // Remove backgroud color style.
-                    group2 = group2.replace(/background-color:.*?;/gi,"");
-                    return group1 + group2 + group3;
-                }},
             // Get all class attributes so we can work on them.
             {regex: /(<[^>]*?class\s*?=\s*?")([^>"]*)(")/gi, replace: function(match, group1, group2, group3) {
                     // Remove MSO classes.
@@ -307,6 +299,9 @@ EditorClean.prototype = {
             {regex: /<a [^>]*?name\s*?=\s*?"OLE_LINK\d*?"[^>]*?>\s*?<\/a>/gi, replace: ""}
         ];
 
+        // Clean all style attributes from the text.
+        content = this._cleanStyles(content);
+
         // Apply the rules.
         content = this._filterContentWithRules(content, rules);
 
@@ -319,6 +314,33 @@ EditorClean.prototype = {
         return content;
     },
 
+    /**
+     * Clean all inline styles from pasted text.
+     *
+     * This code intentionally doesn't use YUI Nodes. YUI was quite a bit slower at this, so using raw DOM objects instead.
+     *
+     * @method _cleanStyles
+     * @private
+     * @param {String} content The content to clean
+     * @return {String} The cleaned HTML
+     */
+    _cleanStyles: function(content) {
+        var holder = document.createElement('div');
+        holder.innerHTML = content;
+        var elementsWithStyle = holder.querySelectorAll('[style]');
+        var i = 0;
+
+        for (i = 0; i < elementsWithStyle.length; i++) {
+            elementsWithStyle[i].removeAttribute('style');
+        }
+
+        var elementsWithClass = holder.querySelectorAll('[class]');
+        for (i = 0; i < elementsWithClass.length; i++) {
+            elementsWithClass[i].removeAttribute('class');
+        }
+
+        return holder.innerHTML;
+    },
     /**
      * Clean empty or un-unused spans from passed HTML.
      *
index 63aed7b..6bd5a85 100644 (file)
@@ -2367,127 +2367,6 @@ function send_stored_file($stored_file, $lifetime=null, $filter=0, $forcedownloa
     die; //no more chars to output!!!
 }
 
-/**
- * Retrieves an array of records from a CSV file and places
- * them into a given table structure
- *
- * @global stdClass $CFG
- * @global moodle_database $DB
- * @param string $file The path to a CSV file
- * @param string $table The table to retrieve columns from
- * @return bool|array Returns an array of CSV records or false
- */
-function get_records_csv($file, $table) {
-    global $CFG, $DB;
-
-    if (!$metacolumns = $DB->get_columns($table)) {
-        return false;
-    }
-
-    if(!($handle = @fopen($file, 'r'))) {
-        print_error('get_records_csv failed to open '.$file);
-    }
-
-    $fieldnames = fgetcsv($handle, 4096);
-    if(empty($fieldnames)) {
-        fclose($handle);
-        return false;
-    }
-
-    $columns = array();
-
-    foreach($metacolumns as $metacolumn) {
-        $ord = array_search($metacolumn->name, $fieldnames);
-        if(is_int($ord)) {
-            $columns[$metacolumn->name] = $ord;
-        }
-    }
-
-    $rows = array();
-
-    while (($data = fgetcsv($handle, 4096)) !== false) {
-        $item = new stdClass;
-        foreach($columns as $name => $ord) {
-            $item->$name = $data[$ord];
-        }
-        $rows[] = $item;
-    }
-
-    fclose($handle);
-    return $rows;
-}
-
-/**
- * Create a file with CSV contents
- *
- * @global stdClass $CFG
- * @global moodle_database $DB
- * @param string $file The file to put the CSV content into
- * @param array $records An array of records to write to a CSV file
- * @param string $table The table to get columns from
- * @return bool success
- */
-function put_records_csv($file, $records, $table = NULL) {
-    global $CFG, $DB;
-
-    if (empty($records)) {
-        return true;
-    }
-
-    $metacolumns = NULL;
-    if ($table !== NULL && !$metacolumns = $DB->get_columns($table)) {
-        return false;
-    }
-
-    echo "x";
-
-    if(!($fp = @fopen($CFG->tempdir.'/'.$file, 'w'))) {
-        print_error('put_records_csv failed to open '.$file);
-    }
-
-    $proto = reset($records);
-    if(is_object($proto)) {
-        $fields_records = array_keys(get_object_vars($proto));
-    }
-    else if(is_array($proto)) {
-        $fields_records = array_keys($proto);
-    }
-    else {
-        return false;
-    }
-    echo "x";
-
-    if(!empty($metacolumns)) {
-        $fields_table = array_map(create_function('$a', 'return $a->name;'), $metacolumns);
-        $fields = array_intersect($fields_records, $fields_table);
-    }
-    else {
-        $fields = $fields_records;
-    }
-
-    fwrite($fp, implode(',', $fields));
-    fwrite($fp, "\r\n");
-
-    foreach($records as $record) {
-        $array  = (array)$record;
-        $values = array();
-        foreach($fields as $field) {
-            if(strpos($array[$field], ',')) {
-                $values[] = '"'.str_replace('"', '\"', $array[$field]).'"';
-            }
-            else {
-                $values[] = $array[$field];
-            }
-        }
-        fwrite($fp, implode(',', $values)."\r\n");
-    }
-
-    fclose($fp);
-    @chmod($CFG->tempdir.'/'.$file, $CFG->filepermissions);
-    return true;
-}
-
-
 /**
  * Recursively delete the file or folder with path $location. That is,
  * if it is a file delete it. If it is a folder, delete all its content
index 4a15f63..411499f 100644 (file)
@@ -4376,7 +4376,6 @@ function password_is_legacy_hash($password) {
  */
 function validate_internal_user_password($user, $password) {
     global $CFG;
-    require_once($CFG->libdir.'/password_compat/lib/password.php');
 
     if ($user->password === AUTH_PASSWORD_NOT_CACHED) {
         // Internal password is not used at all, it can not validate.
@@ -4437,7 +4436,6 @@ function validate_internal_user_password($user, $password) {
  */
 function hash_internal_user_password($password, $fasthash = false) {
     global $CFG;
-    require_once($CFG->libdir.'/password_compat/lib/password.php');
 
     // Set the cost factor to 4 for fast hashing, otherwise use default cost.
     $options = ($fasthash) ? array('cost' => 4) : array();
@@ -4473,7 +4471,6 @@ function hash_internal_user_password($password, $fasthash = false) {
  */
 function update_internal_user_password($user, $password, $fasthash = false) {
     global $CFG, $DB;
-    require_once($CFG->libdir.'/password_compat/lib/password.php');
 
     // Figure out what the hashed password should be.
     if (!isset($user->auth)) {
index cc6896c..d5b178e 100644 (file)
 <?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/>.
+
 /**
- * A Compatibility library with PHP 5.5's simplified password hashing API.
+ * Deprecation notice for password_compat.
  *
- * @author Anthony Ferrara <ircmaxell@php.net>
- * @license http://www.opensource.org/licenses/mit-license.html MIT License
- * @copyright 2012 The Authors
+ * @package    core
+ * @copyright  2016 Andrew Nicols <andrew@nicols.co.uk>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-namespace {
-
-    if (!defined('PASSWORD_BCRYPT')) {
-        /**
-         * PHPUnit Process isolation caches constants, but not function declarations.
-         * So we need to check if the constants are defined separately from 
-         * the functions to enable supporting process isolation in userland
-         * code.
-         */
-        define('PASSWORD_BCRYPT', 1);
-        define('PASSWORD_DEFAULT', PASSWORD_BCRYPT);
-        define('PASSWORD_BCRYPT_DEFAULT_COST', 10);
-    }
-
-    if (!function_exists('password_hash')) {
-
-        /**
-         * Hash the password using the specified algorithm
-         *
-         * @param string $password The password to hash
-         * @param int    $algo     The algorithm to use (Defined by PASSWORD_* constants)
-         * @param array  $options  The options for the algorithm to use
-         *
-         * @return string|false The hashed password, or false on error.
-         */
-        function password_hash($password, $algo, array $options = array()) {
-            if (!function_exists('crypt')) {
-                trigger_error("Crypt must be loaded for password_hash to function", E_USER_WARNING);
-                return null;
-            }
-            if (is_null($password) || is_int($password)) {
-                $password = (string) $password;
-            }
-            if (!is_string($password)) {
-                trigger_error("password_hash(): Password must be a string", E_USER_WARNING);
-                return null;
-            }
-            if (!is_int($algo)) {
-                trigger_error("password_hash() expects parameter 2 to be long, " . gettype($algo) . " given", E_USER_WARNING);
-                return null;
-            }
-            $resultLength = 0;
-            switch ($algo) {
-                case PASSWORD_BCRYPT:
-                    $cost = PASSWORD_BCRYPT_DEFAULT_COST;
-                    if (isset($options['cost'])) {
-                        $cost = $options['cost'];
-                        if ($cost < 4 || $cost > 31) {
-                            trigger_error(sprintf("password_hash(): Invalid bcrypt cost parameter specified: %d", $cost), E_USER_WARNING);
-                            return null;
-                        }
-                    }
-                    // The length of salt to generate
-                    $raw_salt_len = 16;
-                    // The length required in the final serialization
-                    $required_salt_len = 22;
-                    $hash_format = sprintf("$2y$%02d$", $cost);
-                    // The expected length of the final crypt() output
-                    $resultLength = 60;
-                    break;
-                default:
-                    trigger_error(sprintf("password_hash(): Unknown password hashing algorithm: %s", $algo), E_USER_WARNING);
-                    return null;
-            }
-            $salt_requires_encoding = false;
-            if (isset($options['salt'])) {
-                switch (gettype($options['salt'])) {
-                    case 'NULL':
-                    case 'boolean':
-                    case 'integer':
-                    case 'double':
-                    case 'string':
-                        $salt = (string) $options['salt'];
-                        break;
-                    case 'object':
-                        if (method_exists($options['salt'], '__tostring')) {
-                            $salt = (string) $options['salt'];
-                            break;
-                        }
-                    case 'array':
-                    case 'resource':
-                    default:
-                        trigger_error('password_hash(): Non-string salt parameter supplied', E_USER_WARNING);
-                        return null;
-                }
-                if (PasswordCompat\binary\_strlen($salt) < $required_salt_len) {
-                    trigger_error(sprintf("password_hash(): Provided salt is too short: %d expecting %d", PasswordCompat\binary\_strlen($salt), $required_salt_len), E_USER_WARNING);
-                    return null;
-                } elseif (0 == preg_match('#^[a-zA-Z0-9./]+$#D', $salt)) {
-                    $salt_requires_encoding = true;
-                }
-            } else {
-                $buffer = '';
-                $buffer_valid = false;
-                if (function_exists('mcrypt_create_iv') && !defined('PHALANGER')) {
-                    $buffer = mcrypt_create_iv($raw_salt_len, MCRYPT_DEV_URANDOM);
-                    if ($buffer) {
-                        $buffer_valid = true;
-                    }
-                }
-                if (!$buffer_valid && function_exists('openssl_random_pseudo_bytes')) {
-                    $buffer = openssl_random_pseudo_bytes($raw_salt_len);
-                    if ($buffer) {
-                        $buffer_valid = true;
-                    }
-                }
-                if (!$buffer_valid && @is_readable('/dev/urandom')) {
-                    $f = fopen('/dev/urandom', 'r');
-                    $read = PasswordCompat\binary\_strlen($buffer);
-                    while ($read < $raw_salt_len) {
-                        $buffer .= fread($f, $raw_salt_len - $read);
-                        $read = PasswordCompat\binary\_strlen($buffer);
-                    }
-                    fclose($f);
-                    if ($read >= $raw_salt_len) {
-                        $buffer_valid = true;
-                    }
-                }
-                if (!$buffer_valid || PasswordCompat\binary\_strlen($buffer) < $raw_salt_len) {
-                    $bl = PasswordCompat\binary\_strlen($buffer);
-                    for ($i = 0; $i < $raw_salt_len; $i++) {
-                        if ($i < $bl) {
-                            $buffer[$i] = $buffer[$i] ^ chr(mt_rand(0, 255));
-                        } else {
-                            $buffer .= chr(mt_rand(0, 255));
-                        }
-                    }
-                }
-                $salt = $buffer;
-                $salt_requires_encoding = true;
-            }
-            if ($salt_requires_encoding) {
-                // encode string with the Base64 variant used by crypt
-                $base64_digits =
-                    'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
-                $bcrypt64_digits =
-                    './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
-
-                $base64_string = base64_encode($salt);
-                $salt = strtr(rtrim($base64_string, '='), $base64_digits, $bcrypt64_digits);
-            }
-            $salt = PasswordCompat\binary\_substr($salt, 0, $required_salt_len);
-
-            $hash = $hash_format . $salt;
-
-            $ret = crypt($password, $hash);
-
-            if (!is_string($ret) || PasswordCompat\binary\_strlen($ret) != $resultLength) {
-                return false;
-            }
-
-            return $ret;
-        }
-
-        /**
-         * Get information about the password hash. Returns an array of the information
-         * that was used to generate the password hash.
-         *
-         * array(
-         *    'algo' => 1,
-         *    'algoName' => 'bcrypt',
-         *    'options' => array(
-         *        'cost' => PASSWORD_BCRYPT_DEFAULT_COST,
-         *    ),
-         * )
-         *
-         * @param string $hash The password hash to extract info from
-         *
-         * @return array The array of information about the hash.
-         */
-        function password_get_info($hash) {
-            $return = array(
-                'algo' => 0,
-                'algoName' => 'unknown',
-                'options' => array(),
-            );
-            if (PasswordCompat\binary\_substr($hash, 0, 4) == '$2y$' && PasswordCompat\binary\_strlen($hash) == 60) {
-                $return['algo'] = PASSWORD_BCRYPT;
-                $return['algoName'] = 'bcrypt';
-                list($cost) = sscanf($hash, "$2y$%d$");
-                $return['options']['cost'] = $cost;
-            }
-            return $return;
-        }
-
-        /**
-         * Determine if the password hash needs to be rehashed according to the options provided
-         *
-         * If the answer is true, after validating the password using password_verify, rehash it.
-         *
-         * @param string $hash    The hash to test
-         * @param int    $algo    The algorithm used for new password hashes
-         * @param array  $options The options array passed to password_hash
-         *
-         * @return boolean True if the password needs to be rehashed.
-         */
-        function password_needs_rehash($hash, $algo, array $options = array()) {
-            $info = password_get_info($hash);
-            if ($info['algo'] != $algo) {
-                return true;
-            }
-            switch ($algo) {
-                case PASSWORD_BCRYPT:
-                    $cost = isset($options['cost']) ? $options['cost'] : PASSWORD_BCRYPT_DEFAULT_COST;
-                    if ($cost != $info['options']['cost']) {
-                        return true;
-                    }
-                    break;
-            }
-            return false;
-        }
-
-        /**
-         * Verify a password against a hash using a timing attack resistant approach
-         *
-         * @param string $password The password to verify
-         * @param string $hash     The hash to verify against
-         *
-         * @return boolean If the password matches the hash
-         */
-        function password_verify($password, $hash) {
-            if (!function_exists('crypt')) {
-                trigger_error("Crypt must be loaded for password_verify to function", E_USER_WARNING);
-                return false;
-            }
-            $ret = crypt($password, $hash);
-            if (!is_string($ret) || PasswordCompat\binary\_strlen($ret) != PasswordCompat\binary\_strlen($hash) || PasswordCompat\binary\_strlen($ret) <= 13) {
-                return false;
-            }
-
-            $status = 0;
-            for ($i = 0; $i < PasswordCompat\binary\_strlen($ret); $i++) {
-                $status |= (ord($ret[$i]) ^ ord($hash[$i]));
-            }
-
-            return $status === 0;
-        }
-    }
-
-}
-
-namespace PasswordCompat\binary {
-
-    if (!function_exists('PasswordCompat\\binary\\_strlen')) {
-
-        /**
-         * Count the number of bytes in a string
-         *
-         * We cannot simply use strlen() for this, because it might be overwritten by the mbstring extension.
-         * In this case, strlen() will count the number of *characters* based on the internal encoding. A
-         * sequence of bytes might be regarded as a single multibyte character.
-         *
-         * @param string $binary_string The input string
-         *
-         * @internal
-         * @return int The number of bytes
-         */
-        function _strlen($binary_string) {
-            if (function_exists('mb_strlen')) {
-                return mb_strlen($binary_string, '8bit');
-            }
-            return strlen($binary_string);
-        }
-
-        /**
-         * Get a substring based on byte limits
-         *
-         * @see _strlen()
-         *
-         * @param string $binary_string The input string
-         * @param int    $start
-         * @param int    $length
-         *
-         * @internal
-         * @return string The substring
-         */
-        function _substr($binary_string, $start, $length) {
-            if (function_exists('mb_substr')) {
-                return mb_substr($binary_string, $start, $length, '8bit');
-            }
-            return substr($binary_string, $start, $length);
-        }
-
-        /**
-         * Check if current PHP version is compatible with the library
-         *
-         * @return boolean the check result
-         */
-        function check() {
-            static $pass = NULL;
-
-            if (is_null($pass)) {
-                if (function_exists('crypt')) {
-                    $hash = '$2y$04$usesomesillystringfore7hnbRJHxXVLeakoG8K30oukPsA.ztMG';
-                    $test = crypt("password", $hash);
-                    $pass = $test == $hash;
-                } else {
-                    $pass = false;
-                }
-            }
-            return $pass;
-        }
+defined('MOODLE_INTERNAL') || die();
 
-    }
-}
\ No newline at end of file
+debugging('password_compat is now standard in all versions of PHP that Moodle supports. '
+    . 'You no longer need to include the password_compat polyfill.',
+    DEBUG_DEVELOPER);
diff --git a/lib/password_compat/readme_moodle.txt b/lib/password_compat/readme_moodle.txt
deleted file mode 100644 (file)
index 4e07072..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-Description of password_compat import into Moodle:
-==================================================
-
-Imported from: https://github.com/ircmaxell/password_compat/releases/tag/v1.0.4
-Copyright: (c) 2012 Anthony Ferrara
-License: MIT License
-
-Files used from the library:
-* lib/password.php  > lib/password.php
-* test/Unit/*       > tests/
-
-Added:
-* None.
-
-Our changes:
-* Added the following require_once() to the test files:
-    global $CFG;
-    require_once($CFG->dirroot . '/lib/password_compat/lib/password.php');
-* tests/PasswordHashTest.php supresses debugging from using salt in password_hash()
-  see MDL-52283
-
-Library description:
-====================
-
-Compatibility with the password_* functions being worked on for PHP 5.5.
-
-This library requires PHP >= 5.3.7 due to a PHP security issue prior to that
-version.
-
-See the RFC (https://wiki.php.net/rfc/password_hash) for more information.
-
-Latest code available from https://github.com/ircmaxell/password_compat/
-under MIT license.
diff --git a/lib/password_compat/tests/PasswordGetInfoTest.php b/lib/password_compat/tests/PasswordGetInfoTest.php
deleted file mode 100644 (file)
index e745674..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-<?php
-
-global $CFG;
-require_once($CFG->dirroot . '/lib/password_compat/lib/password.php');
-
-class PasswordGetInfoTest extends PHPUnit_Framework_TestCase {
-    
-    public static function provideInfo() {
-        return array(
-            array('foo', array('algo' => 0, 'algoName' => 'unknown', 'options' => array())),
-            array('$2y$', array('algo' => 0, 'algoName' => 'unknown', 'options' => array())),
-            array('$2y$07$usesomesillystringfore2uDLvp1Ii2e./U9C8sBjqp8I90dH6hi', array('algo' => PASSWORD_BCRYPT, 'algoName' => 'bcrypt', 'options' => array('cost' => 7))),
-            array('$2y$10$usesomesillystringfore2uDLvp1Ii2e./U9C8sBjqp8I90dH6hi', array('algo' => PASSWORD_BCRYPT, 'algoName' => 'bcrypt', 'options' => array('cost' => 10))),
-
-        );
-    }
-
-    public function testFuncExists() {
-        $this->assertTrue(function_exists('password_get_info'));
-    }
-
-    /**
-     * @dataProvider provideInfo
-     */
-    public function testInfo($hash, $info) {
-        $this->assertEquals($info, password_get_info($hash));
-    }
-
-}
diff --git a/lib/password_compat/tests/PasswordHashTest.php b/lib/password_compat/tests/PasswordHashTest.php
deleted file mode 100644 (file)
index 261d6df..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-<?php
-
-global $CFG;
-require_once($CFG->dirroot . '/lib/password_compat/lib/password.php');
-
-class PasswordHashTest extends PHPUnit_Framework_TestCase {
-    
-    public function testFuncExists() {
-        $this->assertTrue(function_exists('password_hash'));
-    }
-
-    public function testStringLength() {
-        $this->assertEquals(60, strlen(password_hash('foo', PASSWORD_BCRYPT)));
-    }
-
-    public function testHash() {
-        $hash = password_hash('foo', PASSWORD_BCRYPT);
-        $this->assertEquals($hash, crypt('foo', $hash));
-    }
-
-    public function testKnownSalt() {
-        $hash = @password_hash("rasmuslerdorf", PASSWORD_BCRYPT, array("cost" => 7, "salt" => "usesomesillystringforsalt"));
-        $this->assertEquals('$2y$07$usesomesillystringfore2uDLvp1Ii2e./U9C8sBjqp8I90dH6hi', $hash);
-    }
-
-    public function testRawSalt() {
-        $hash = @password_hash("test", PASSWORD_BCRYPT, array("salt" => "123456789012345678901" . chr(0)));
-        if (version_compare(PHP_VERSION, '5.5.0', '<')) {
-            $this->assertEquals('$2y$10$KRGxLBS0Lxe3KBCwKxOzLexLDeu0ZfqJAKTubOfy7O/yL2hjimw3u', $hash);
-        } else {
-            $this->assertEquals('$2y$10$MTIzNDU2Nzg5MDEyMzQ1Nej0NmcAWSLR.oP7XOR9HD/vjUuOj100y', $hash);
-        }
-    }
-
-    public function testNullBehavior() {
-        $hash = @password_hash(null, PASSWORD_BCRYPT, array("salt" => "1234567890123456789012345678901234567890"));
-        $this->assertEquals('$2y$10$123456789012345678901uhihPb9QpE2n03zMu9TDdvO34jDn6mO.', $hash);
-    }
-
-    public function testIntegerBehavior() {
-        $hash = @password_hash(12345, PASSWORD_BCRYPT, array("salt" => "1234567890123456789012345678901234567890"));
-        $this->assertEquals('$2y$10$123456789012345678901ujczD5TiARVFtc68bZCAlbEg1fCIexfO', $hash);
-    }    
-
-    /**
-     * @expectedException PHPUnit_Framework_Error
-     */
-    public function testInvalidAlgo() {
-        password_hash('foo', array());
-    }
-
-    /**
-     * @expectedException PHPUnit_Framework_Error
-     */
-    public function testInvalidAlgo2() {
-        password_hash('foo', 2);
-    }
-
-    /**
-     * @expectedException PHPUnit_Framework_Error
-     */
-    public function testInvalidPassword() {
-        password_hash(array(), 1);
-    }
-
-    /**
-     * @expectedException PHPUnit_Framework_Error
-     */
-    public function testInvalidSalt() {
-        password_hash('foo', PASSWORD_BCRYPT, array('salt' => array()));
-    }
-
-    /**
-     * @expectedException PHPUnit_Framework_Error
-     */
-    public function testInvalidBcryptCostLow() {
-        password_hash('foo', PASSWORD_BCRYPT, array('cost' => 3));
-    }
-        
-    /**
-     * @expectedException PHPUnit_Framework_Error
-     */
-    public function testInvalidBcryptCostHigh() {
-        password_hash('foo', PASSWORD_BCRYPT, array('cost' => 32));
-    }
-
-    /**
-     * @expectedException PHPUnit_Framework_Error
-     */
-    public function testInvalidBcryptCostInvalid() {
-        password_hash('foo', PASSWORD_BCRYPT, array('cost' => 'foo'));
-    }
-
-    /**
-     * @expectedException PHPUnit_Framework_Error
-     */
-    public function testInvalidBcryptSaltShort() {
-        password_hash('foo', PASSWORD_BCRYPT, array('salt' => 'abc'));
-    }
-
-}
diff --git a/lib/password_compat/tests/PasswordNeedsRehashTest.php b/lib/password_compat/tests/PasswordNeedsRehashTest.php
deleted file mode 100644 (file)
index 1d00360..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-<?php
-
-global $CFG;
-require_once($CFG->dirroot . '/lib/password_compat/lib/password.php');
-
-class PasswordNeedsRehashTest extends PHPUnit_Framework_TestCase {
-    
-    public static function provideCases() {
-        return array(
-            array('foo', 0, array(), false),
-            array('foo', 1, array(), true),
-            array('$2y$07$usesomesillystringfore2uDLvp1Ii2e./U9C8sBjqp8I90dH6hi', PASSWORD_BCRYPT, array(), true),
-            array('$2y$07$usesomesillystringfore2udlvp1ii2e./u9c8sbjqp8i90dh6hi', PASSWORD_BCRYPT, array('cost' => 7), false),
-            array('$2y$07$usesomesillystringfore2udlvp1ii2e./u9c8sbjqp8i90dh6hi', PASSWORD_BCRYPT, array('cost' => 5), true),
-        );
-    }
-
-    public function testFuncExists() {
-        $this->assertTrue(function_exists('password_needs_rehash'));
-    }
-
-    /**
-     * @dataProvider provideCases
-     */
-    public function testCases($hash, $algo, $options, $valid) {
-        $this->assertEquals($valid, password_needs_rehash($hash, $algo, $options));
-    }
-
-}
diff --git a/lib/password_compat/tests/PasswordVerifyTest.php b/lib/password_compat/tests/PasswordVerifyTest.php
deleted file mode 100644 (file)
index 3175e89..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-<?php
-
-global $CFG;
-require_once($CFG->dirroot . '/lib/password_compat/lib/password.php');
-
-class PasswordVerifyTest extends PHPUnit_Framework_TestCase {
-    
-    public function testFuncExists() {
-        $this->assertTrue(function_exists('password_verify'));
-    }
-
-    public function testFailedType() {
-        $this->assertFalse(password_verify(123, 123));
-    }
-
-    public function testSaltOnly() {
-        $this->assertFalse(password_verify('foo', '$2a$07$usesomesillystringforsalt$'));
-    }
-
-    public function testInvalidPassword() {
-        $this->assertFalse(password_verify('rasmusler', '$2a$07$usesomesillystringfore2uDLvp1Ii2e./U9C8sBjqp8I90dH6hi'));
-    }
-
-    public function testValidPassword() {
-        $this->assertTrue(password_verify('rasmuslerdorf', '$2a$07$usesomesillystringfore2uDLvp1Ii2e./U9C8sBjqp8I90dH6hi'));
-    }
-
-    public function testInValidHash() {
-        $this->assertFalse(password_verify('rasmuslerdorf', '$2a$07$usesomesillystringfore2uDLvp1Ii2e./U9C8sBjqp8I90dH6hj'));
-    }
-
-}
index f51e166..a5b8b7c 100644 (file)
@@ -798,7 +798,7 @@ abstract class testing_util {
         if (file_exists(self::get_dataroot() . '/filedir')) {
             $handle = opendir(self::get_dataroot() . '/filedir');
             while (false !== ($item = readdir($handle))) {
-                if (in_array('filedir/' . $item, $childclassname::$datarootskiponreset)) {
+                if (in_array('filedir' . DIRECTORY_SEPARATOR . $item, $childclassname::$datarootskiponreset)) {
                     continue;
                 }
                 if (is_dir(self::get_dataroot()."/filedir/$item")) {
@@ -1054,8 +1054,10 @@ abstract class testing_util {
         if (!file_exists($jsonfilepath)) {
 
             $listfiles = array();
-            $listfiles['filedir/.'] = 'filedir/.';
-            $listfiles['filedir/..'] = 'filedir/..';
+            $currentdir = 'filedir' . DIRECTORY_SEPARATOR . '.';
+            $parentdir = 'filedir' . DIRECTORY_SEPARATOR . '..';
+            $listfiles[$currentdir] = $currentdir;
+            $listfiles[$parentdir] = $parentdir;
 
             $filedir = self::get_dataroot() . '/filedir';
             if (file_exists($filedir)) {
index a4a1faf..4b9a2bf 100644 (file)
@@ -1025,6 +1025,7 @@ EOF;
             'filename'  => $filename,
         );
         $file = self::create_draft_file($filerecord);
+        $draftitemid = $file->get_itemid();
 
         $maxbytes = $CFG->userquota;
         $maxareabytes = $CFG->userquota;
@@ -1034,7 +1035,7 @@ EOF;
                          'areamaxbytes' => $maxareabytes);
 
         // Add new file.
-        file_merge_files_from_draft_area_into_filearea($file->get_itemid(), $usercontext->id, 'user', 'private', 0, $options);
+        file_merge_files_from_draft_area_into_filearea($draftitemid, $usercontext->id, 'user', 'private', 0, $options);
 
         $files = $fs->get_area_files($usercontext->id, 'user', 'private', 0);
         // Directory and file.
@@ -1051,12 +1052,12 @@ EOF;
 
         // Add two more files.
         $filerecord = array(
-            'itemid'  => $file->get_itemid(),
+            'itemid'  => $draftitemid,
             'filename'  => 'second.txt',
         );
         self::create_draft_file($filerecord);
         $filerecord = array(
-            'itemid'  => $file->get_itemid(),
+            'itemid'  => $draftitemid,
             'filename'  => 'third.txt',
         );
         $file = self::create_draft_file($filerecord);
index 76ecc4e..22dd14a 100644 (file)
     <version>3.2.18</version>
     <licenseversion>3.0+</licenseversion>
   </library>
-  <library>
-    <location>password_compat</location>
-    <name>Compatible password hashing</name>
-    <license>MIT</license>
-    <version>1.0.4</version>
-    <licenseversion></licenseversion>
-  </library>
   <library>
     <location>pear/Auth/RADIUS.php</location>
     <name>Pear_Auth_Radius</name>
index 2c41422..4f353de 100644 (file)
@@ -14,12 +14,18 @@ information provided here is intended especially for developers.
 * The parameter $usepost of the following functions has been deprecated and is not used any more:
   - get_max_upload_file_size()
   - get_user_max_upload_file_size()
+* The following classes have been removed and should not be used any more:
+    - boxclient - See MDL-49599 for more information.
 * The following functions have been removed and should not be used any more:
     - file_modify_html_header() - See MDL-29738 for more information.
 * core_grades_external::get_grades has been deprecated. Please do not call this function any more.
   External function gradereport_user_external::get_grades_table can be used for retrieving the course grades table.
   Please note that the information returned from that WS is intended for displaying (not for data consumption).
 * New option 'escape' added to format_string. When true (default), escapes HTML entities from the string
+* The following functions have been deprecated and are not used any more:
+  - get_records_csv() Please use csv_import_reader::load_csv_content() instead.
+  - put_records_csv() Please use download_as_dataformat (lib/dataformatlib.php) instead.
+* The password_compat library was removed as it is no longer required.
 
 === 3.1 ===
 
index 068e80d..f4d1fec 100644 (file)
 
 $functions = array(
 
+        'mod_assign_copy_previous_attempt' => array(
+            'classname'     => 'mod_assign_external',
+            'methodname'    => 'copy_previous_attempt',
+            'classpath'     => 'mod/assign/externallib.php',
+            'description'   => 'Copy a students previous attempt to a new attempt.',
+            'type'          => 'write',
+            'capabilities'  => 'mod/assign:view, mod/assign:submit'
+        ),
+
         'mod_assign_get_grades' => array(
                 'classname'   => 'mod_assign_external',
                 'methodname'  => 'get_grades',
@@ -220,4 +229,14 @@ $functions = array(
                 'ajax'          => true,
                 'capabilities'  => 'mod/assign:view, mod/assign:viewgrades'
         ),
+        'mod_assign_view_assign' => array(
+            'classname'     => 'mod_assign_external',
+            'methodname'    => 'view_assign',
+            'classpath'     => 'mod/assign/externallib.php',
+            'description'   => 'Update the module completion status.',
+            'type'          => 'write',
+            'capabilities'  => 'mod/assign:view',
+            'services'      => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
+        ),
+
 );
index 9bdc7cc..910d7b5 100644 (file)
@@ -933,11 +933,8 @@ class mod_assign_external extends external_api {
                                                   'userflags' => $userflags));
 
         // Load assignment if it exists and if the user has the capability.
-        $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
-        $context = context_module::instance($cm->id);
-        self::validate_context($context);
+        list($assign, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
         require_capability('mod/assign:grade', $context);
-        $assign = new assign($context, null, null);
 
         $results = array();
         foreach ($params['userflags'] as $userflag) {
@@ -1363,11 +1360,7 @@ class mod_assign_external extends external_api {
                         array('assignmentid' => $assignmentid,
                               'userids' => $userids));
 
-        $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
-        $context = context_module::instance($cm->id);
-        self::validate_context($context);
-
-        $assignment = new assign($context, $cm, null);
+        list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
 
         $warnings = array();
         foreach ($params['userids'] as $userid) {
@@ -1424,11 +1417,7 @@ class mod_assign_external extends external_api {
                         array('assignmentid' => $assignmentid,
                               'userids' => $userids));
 
-        $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
-        $context = context_module::instance($cm->id);
-        self::validate_context($context);
-
-        $assignment = new assign($context, $cm, null);
+        list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
 
         $warnings = array();
         foreach ($params['userids'] as $userid) {
@@ -1485,11 +1474,7 @@ class mod_assign_external extends external_api {
                         array('assignmentid' => $assignmentid,
                               'userids' => $userids));
 
-        $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
-        $context = context_module::instance($cm->id);
-        self::validate_context($context);
-
-        $assignment = new assign($context, $cm, null);
+        list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
 
         $warnings = array();
         foreach ($params['userids'] as $userid) {
@@ -1551,11 +1536,7 @@ class mod_assign_external extends external_api {
                                                 'jsonformdata' => $jsonformdata
                                             ));
 
-        $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
-        $context = context_module::instance($cm->id);
-        self::validate_context($context);
-
-        $assignment = new assign($context, $cm, null);
+        list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
 
         $serialiseddata = json_decode($params['jsonformdata']);
 
@@ -1627,11 +1608,7 @@ class mod_assign_external extends external_api {
                                             array('assignmentid' => $assignmentid,
                                                   'acceptsubmissionstatement' => $acceptsubmissionstatement));
 
-        $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
-        $context = context_module::instance($cm->id);
-        self::validate_context($context);
-
-        $assignment = new assign($context, $cm, null);
+        list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
 
         $warnings = array();
         $data = new stdClass();
@@ -1705,11 +1682,7 @@ class mod_assign_external extends external_api {
             return $warnings;
         }
 
-        $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
-        $context = context_module::instance($cm->id);
-        self::validate_context($context);
-
-        $assignment = new assign($context, $cm, null);
+        list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
 
         $warnings = array();
         foreach ($params['userids'] as $idx => $userid) {
@@ -1761,11 +1734,7 @@ class mod_assign_external extends external_api {
         $params = self::validate_parameters(self::reveal_identities_parameters(),
                                             array('assignmentid' => $assignmentid));
 
-        $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
-        $context = context_module::instance($cm->id);
-        self::validate_context($context);
-
-        $assignment = new assign($context, $cm, null);
+        list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
 
         $warnings = array();
         if (!$assignment->reveal_identities()) {
@@ -1832,11 +1801,7 @@ class mod_assign_external extends external_api {
                                             array('assignmentid' => $assignmentid,
                                                   'plugindata' => $plugindata));
 
-        $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
-        $context = context_module::instance($cm->id);
-        self::validate_context($context);
-
-        $assignment = new assign($context, $cm, null);
+        list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
 
         $notices = array();
 
@@ -1963,11 +1928,7 @@ class mod_assign_external extends external_api {
                                                   'plugindata' => $plugindata,
                                                   'advancedgradingdata' => $advancedgradingdata));
 
-        $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
-        $context = context_module::instance($cm->id);
-        self::validate_context($context);
-
-        $assignment = new assign($context, $cm, null);
+        list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
 
         $gradedata = (object)$params['plugindata'];
 
@@ -2101,10 +2062,7 @@ class mod_assign_external extends external_api {
                                                   'applytoall' => $applytoall,
                                                   'grades' => $grades));
 
-        $cm = get_coursemodule_from_instance('assign', $params['assignmentid'], 0, false, MUST_EXIST);
-        $context = context_module::instance($cm->id);
-        self::validate_context($context);
-        $assignment = new assign($context, $cm, null);
+        list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
 
         if ($assignment->get_instance()->teamsubmission && $params['applytoall']) {
             // Check that only 1 user per submission group is provided.
@@ -2160,7 +2118,7 @@ class mod_assign_external extends external_api {
 
     /**
      * Describes the parameters for copy_previous_attempt
-     * @return external_external_function_parameters
+     * @return external_function_parameters
      * @since  Moodle 2.6
      */
     public static function copy_previous_attempt_parameters() {
@@ -2179,20 +2137,15 @@ class mod_assign_external extends external_api {
      * @since Moodle 2.6
      */
     public static function copy_previous_attempt($assignmentid) {
-        global $CFG, $USER;
 
         $params = self::validate_parameters(self::copy_previous_attempt_parameters(),
                                             array('assignmentid' => $assignmentid));
 
-        $cm = get_coursemodule_from_instance('assign', $assignmentid, 0, false, MUST_EXIST);
-        $context = context_module::instance($cm->id);
-        self::validate_context($context);
-
-        $assignment = new assign($context, $cm, null);
+        list($assignment, $course, $cm, $context) = self::validate_assign($params['assignmentid']);
 
         $notices = array();
 
-        $assignment->copy_previous_attempt($submissiondata, $notices);
+        $assignment->copy_previous_attempt($notices);
 
         $warnings = array();
         foreach ($notices as $notice) {
@@ -2237,7 +2190,6 @@ class mod_assign_external extends external_api {
      * @throws moodle_exception
      */
     public static function view_grading_table($assignid) {
-        global $DB, $CFG;
 
         $params = self::validate_parameters(self::view_grading_table_parameters(),
                                             array(
@@ -2245,16 +2197,8 @@ class mod_assign_external extends external_api {
                                             ));
         $warnings = array();
 
-        // Request and permission validation.
-        $assign = $DB->get_record('assign', array('id' => $params['assignid']), 'id', MUST_EXIST);
-        list($course, $cm) = get_course_and_cm_from_instance($assign, 'assign');
-
-        $context = context_module::instance($cm->id);
-        self::validate_context($context);
-
-        require_capability('mod/assign:view', $context);
+        list($assign, $course, $cm, $context) = self::validate_assign($params['assignid']);
 
-        $assign = new assign($context, null, null);
         $assign->require_view_grades();
         \mod_assign\event\grading_table_viewed::create_from_assign($assign)->trigger();
 
@@ -2301,7 +2245,6 @@ class mod_assign_external extends external_api {
      * @since Moodle 3.1
      */
     public static function view_submission_status($assignid) {
-        global $DB, $CFG;
 
         $warnings = array();
         $params = array(
@@ -2309,15 +2252,8 @@ class mod_assign_external extends external_api {
         );
         $params = self::validate_parameters(self::view_submission_status_parameters(), $params);
 
-        // Request and permission validation.
-        $assign = $DB->get_record('assign', array('id' => $params['assignid']), 'id', MUST_EXIST);
-        list($course, $cm) = get_course_and_cm_from_instance($assign, 'assign');
-
-        $context = context_module::instance($cm->id);
-        // Please, note that is not required to check mod/assign:view because is done by validate_context->require_login.
-        self::validate_context($context);
+        list($assign, $course, $cm, $context) = self::validate_assign($params['assignid']);
 
-        $assign = new assign($context, $cm, $course);
         \mod_assign\event\submission_status_viewed::create_from_assign($assign)->trigger();
 
         $result = array();
@@ -2366,7 +2302,7 @@ class mod_assign_external extends external_api {
      * @throws required_capability_exception
      */
     public static function get_submission_status($assignid, $userid = 0) {
-        global $USER, $DB;
+        global $USER;
 
         $warnings = array();
 
@@ -2376,14 +2312,7 @@ class mod_assign_external extends external_api {
         );
         $params = self::validate_parameters(self::get_submission_status_parameters(), $params);
 
-        // Request and permission validation.
-        $assign = $DB->get_record('assign', array('id' => $params['assignid']), 'id', MUST_EXIST);
-        list($course, $cm) = get_course_and_cm_from_instance($assign, 'assign');
-
-        $context = context_module::instance($cm->id);
-        self::validate_context($context);
-
-        $assign = new assign($context, $cm, $course);
+        list($assign, $course, $cm, $context) = self::validate_assign($params['assignid']);
 
         // Default value for userid.
         if (empty($params['userid'])) {
@@ -2629,16 +2558,10 @@ class mod_assign_external extends external_api {
                                             ));
         $warnings = array();
 
-        // Request and permission validation.
-        $assign = $DB->get_record('assign', array('id' => $params['assignid']), 'id', MUST_EXIST);
-        list($course, $cm) = get_course_and_cm_from_instance($assign, 'assign');
-
-        $context = context_module::instance($cm->id);
-        self::validate_context($context);
+        list($assign, $course, $cm, $context) = self::validate_assign($params['assignid']);
 
         require_capability('mod/assign:view', $context);
 
-        $assign = new assign($context, null, null);
         $assign->require_view_grades();
 
         $participants = $assign->list_participants_with_filter_status_and_group($params['groupid']);
@@ -2794,14 +2717,7 @@ class mod_assign_external extends external_api {
             'embeduser' => $embeduser
         ));
 
-        // Request and permission validation.
-        $assign = $DB->get_record('assign', array('id' => $params['assignid']), 'id', MUST_EXIST);
-        list($course, $cm) = get_course_and_cm_from_instance($assign, 'assign');
-
-        $context = context_module::instance($cm->id);
-        self::validate_context($context);
-
-        $assign = new assign($context, null, null);
+        list($assign, $course, $cm, $context) = self::validate_assign($params['assignid']);
         $assign->require_view_grades();
 
         $participant = $assign->get_participant($params['userid']);
@@ -2856,4 +2772,79 @@ class mod_assign_external extends external_api {
             'user' => $userdescription,
         ));
     }
+
+    /**
+     * Utility function for validating an assign.
+     *
+     * @param int $assignid assign instance id
+     * @return array array containing the assign, course, context and course module objects
+     * @since  Moodle 3.2
+     */
+    protected static function validate_assign($assignid) {
+        global $DB;
+
+        // Request and permission validation.
+        $assign = $DB->get_record('assign', array('id' => $assignid), 'id', MUST_EXIST);
+        list($course, $cm) = get_course_and_cm_from_instance($assign, 'assign');
+
+        $context = context_module::instance($cm->id);
+        // Please, note that is not required to check mod/assign:view because is done by validate_context->require_login.
+        self::validate_context($context);
+        $assign = new assign($context, $cm, $course);
+
+        return array($assign, $course, $cm, $context);
+    }
+
+    /**
+     * Describes the parameters for view_assign.
+     *
+     * @return external_external_function_parameters
+     * @since Moodle 3.2
+     */
+    public static function view_assign_parameters() {
+        return new external_function_parameters (
+            array(
+                'assignid' => new external_value(PARAM_INT, 'assign instance id'),
+            )
+        );
+    }
+
+    /**
+     * Update the module completion status.
+     *
+     * @param int $assignid assign instance id
+     * @return array of warnings and status result
+     * @since Moodle 3.2
+     */
+    public static function view_assign($assignid) {
+        $warnings = array();
+        $params = array(
+            'assignid' => $assignid,
+        );
+        $params = self::validate_parameters(self::view_assign_parameters(), $params);
+
+        list($assign, $course, $cm, $context) = self::validate_assign($params['assignid']);
+
+        $assign->set_module_viewed();
+
+        $result = array();
+        $result['status'] = true;
+        $result['warnings'] = $warnings;
+        return $result;
+    }
+
+    /**
+     * Describes the view_assign return value.
+     *
+     * @return external_single_structure
+     * @since Moodle 3.2
+     */
+    public static function view_assign_returns() {
+        return new external_single_structure(
+            array(
+                'status' => new external_value(PARAM_BOOL, 'status: true if success'),
+                'warnings' => new external_warnings(),
+            )
+        );
+    }
 }
index 3d815bd..ef79a9d 100644 (file)
Binary files a/mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor-debug.js and b/mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor-debug.js differ
index ebc14b8..984ffd6 100644 (file)
Binary files a/mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor-min.js and b/mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor-min.js differ
index 3d815bd..ef79a9d 100644 (file)
Binary files a/mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor.js and b/mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor.js differ
index 9d209c5..69dea5d 100644 (file)
@@ -43,7 +43,7 @@ Y.extend(ANNOTATIONSTAMP, M.assignfeedback_editpdf.annotation, {
      */
     draw : function() {
         var drawable = new M.assignfeedback_editpdf.drawable(this.editor),
-            drawingcanvas = this.editor.get_dialogue_element(SELECTOR.DRAWINGCANVAS),
+            drawingregion = this.editor.get_dialogue_element(SELECTOR.DRAWINGREGION),
             node,
             position;
 
@@ -59,10 +59,19 @@ Y.extend(ANNOTATIONSTAMP, M.assignfeedback_editpdf.annotation, {
             'zIndex': 50
         });
 
-        drawingcanvas.append(node);
+        drawingregion.append(node);
         node.setX(position.x);
         node.setY(position.y);
         drawable.store_position(node, position.x, position.y);
+
+        // Bind events only when editing.
+        if (!this.editor.get('readonly')) {
+            // Pass through the event handlers on the div.
+            node.on('gesturemovestart', this.editor.edit_start, null, this.editor);
+            node.on('gesturemove', this.editor.edit_move, null, this.editor);
+            node.on('gesturemoveend', this.editor.edit_end, null, this.editor);
+        }
+
         drawable.nodes.push(node);
 
         this.drawable = drawable;
index 0d5a965..c953de4 100644 (file)
@@ -160,7 +160,7 @@ var COMMENT = function(editor, gradeid, pageno, x, y, width, colour, rawtext) {
     this.draw = function(focus) {
         var drawable = new M.assignfeedback_editpdf.drawable(this.editor),
             node,
-            drawingcanvas = this.editor.get_dialogue_element(SELECTOR.DRAWINGCANVAS),
+            drawingregion = this.editor.get_dialogue_element(SELECTOR.DRAWINGREGION),
             container,
             menu,
             position,
@@ -190,7 +190,7 @@ var COMMENT = function(editor, gradeid, pageno, x, y, width, colour, rawtext) {
             color: COMMENTTEXTCOLOUR
         });
 
-        drawingcanvas.append(container);
+        drawingregion.append(container);
         container.setStyle('position', 'absolute');
         container.setX(position.x);
         container.setY(position.y);
index 2aa605d..4419562 100644 (file)
@@ -7978,6 +7978,10 @@ class assign {
         // Check if default gradebook feedback is visible and enabled.
         $gradebookfeedbackplugin = $this->get_feedback_plugin_by_type($gradebookplugin);
 
+        if (empty($gradebookfeedbackplugin)) {
+            return false;
+        }
+
         if ($gradebookfeedbackplugin->is_visible() && $gradebookfeedbackplugin->is_enabled()) {
             return true;
         }
@@ -8068,6 +8072,15 @@ class assign {
         return;
     }
 
+    /**
+     * Update the module completion status (set it viewed).
+     *
+     * @since Moodle 3.2
+     */
+    public function set_module_viewed() {
+        $completion = new completion_info($this->get_course());
+        $completion->set_module_viewed($this->get_course_module());
+    }
 }
 
 /**
index 22cd561..4f8e7ea 100644 (file)
@@ -2391,4 +2391,32 @@ class mod_assign_external_testcase extends externallib_advanced_testcase {
             'teacher' => $teacher
         );
     }
+
+    /**
+     * Test test_view_assign
+     */
+    public function test_view_assign() {
+        global $CFG;
+
+        $CFG->enablecompletion = 1;
+        $this->resetAfterTest();
+
+        $this->setAdminUser();
+        // Setup test data.
+        $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
+        $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id),
+                                                            array('completion' => 2, 'completionview' => 1));
+        $context = context_module::instance($assign->cmid);
+        $cm = get_coursemodule_from_instance('assign', $assign->id);
+
+        $result = mod_assign_external::view_assign($assign->id);
+        $result = external_api::clean_returnvalue(mod_assign_external::view_assign_returns(), $result);
+        $this->assertTrue($result['status']);
+        $this->assertEmpty($result['warnings']);
+
+        // Check completion status.
+        $completion = new completion_info($course);
+        $completiondata = $completion->get_data($cm);
+        $this->assertEquals(1, $completiondata->completionstate);
+    }
 }
index 23e28b2..1eea120 100644 (file)
@@ -25,6 +25,6 @@
 defined('MOODLE_INTERNAL') || die();
 
 $plugin->component = 'mod_assign'; // Full name of the plugin (used for diagnostics).
-$plugin->version  = 2016070400;    // The current module version (Date: YYYYMMDDXX).
+$plugin->version  = 2016070402;    // The current module version (Date: YYYYMMDDXX).
 $plugin->requires = 2016051900;    // Requires this Moodle version.
 $plugin->cron     = 60;
index 01d0edd..8c3ddfe 100644 (file)
@@ -44,8 +44,8 @@ $urlparams = array('id' => $id,
 $url = new moodle_url('/mod/assign/view.php', $urlparams);
 $PAGE->set_url($url);
 
-$completion=new completion_info($course);
-$completion->set_module_viewed($cm);
+// Update module completion status.
+$assign->set_module_viewed();
 
 // Get the assign class to
 // render the page.
index 842ef63..0fabbfe 100644 (file)
@@ -54,6 +54,8 @@ $string['availabletodatevalidation'] = 'The available to date cannot be before t
 $string['blank'] = 'Blank';
 $string['buttons'] = 'Actions';
 $string['bynameondate'] = 'by {$a->name} - {$a->date}';
+$string['calendarend'] = 'Database {$a} closes';
+$string['calendarstart'] = 'Database {$a} opens';
 $string['cancel'] = 'Cancel';
 $string['cannotaccesspresentsother'] = 'You are not allowed to access presets from other users';
 $string['cannotadd'] = 'Can not add entries!';
index d4da9cb..a9d2614 100644 (file)
@@ -904,7 +904,8 @@ function data_tags_check($dataid, $template) {
  * @return int intance id
  */
 function data_add_instance($data, $mform = null) {
-    global $DB;
+    global $DB, $CFG;
+    require_once($CFG->dirroot.'/mod/data/locallib.php');
 
     if (empty($data->assessed)) {
         $data->assessed = 0;
@@ -919,6 +920,9 @@ function data_add_instance($data, $mform = null) {
 
     $data->id = $DB->insert_record('data', $data);
 
+    // Add calendar events if necessary.
+    data_set_events($data);
+
     data_grade_item_update($data);
 
     return $data->id;
@@ -932,7 +936,8 @@ function data_add_instance($data, $mform = null) {
  * @return bool
  */
 function data_update_instance($data) {
-    global $DB, $OUTPUT;
+    global $DB, $CFG;
+    require_once($CFG->dirroot.'/mod/data/locallib.php');
 
     $data->timemodified = time();
     $data->id           = $data->instance;
@@ -952,6 +957,9 @@ function data_update_instance($data) {
 
     $DB->update_record('data', $data);
 
+    // Add calendar events if necessary.
+    data_set_events($data);
+
     data_grade_item_update($data);
 
     return true;
@@ -992,6 +1000,13 @@ function data_delete_instance($id) {    // takes the dataid
     $DB->delete_records('data_records', array('dataid'=>$id));
     $DB->delete_records('data_fields', array('dataid'=>$id));
 
+    // Remove old calendar events.
+    $events = $DB->get_records('event', array('modulename' => 'data', 'instance' => $id));
+    foreach ($events as $event) {
+        $event = calendar_event::load($event);
+        $event->delete();
+    }
+
     // Delete the instance itself
     $result = $DB->delete_records('data', array('id'=>$id));
 
@@ -3965,3 +3980,33 @@ function data_process_submission(stdClass $mod, $fields, stdClass $datarecord) {
 
     return $result;
 }
+
+/**
+ * This standard function will check all instances of this module
+ * and make sure there are up-to-date events created for each of them.
+ * If courseid = 0, then every data event in the site is checked, else
+ * only data events belonging to the course specified are checked.
+ * This function is used, in its new format, by restore_refresh_events()
+ *
+ * @param int $courseid
+ * @return bool
+ */
+function data_refresh_events($courseid = 0) {
+    global $DB, $CFG;
+    require_once($CFG->dirroot.'/mod/data/locallib.php');
+
+    if ($courseid) {
+        if (! $data = $DB->get_records("data", array("course" => $courseid))) {
+            return true;
+        }
+    } else {
+        if (! $data = $DB->get_records("data")) {
+            return true;
+        }
+    }
+
+    foreach ($data as $datum) {
+        data_set_events($datum);
+    }
+    return true;
+}
index 9d82963..c836e98 100644 (file)
@@ -578,3 +578,93 @@ class data_file_info_container extends file_info {
         return $this->browser->get_file_info($this->context);
     }
 }
+
+/**
+ * This creates new calendar events given as timeavailablefrom and timeclose by $data.
+ *
+ * @param stdClass $data
+ * @return void
+ */
+function data_set_events($data) {
+    global $DB, $CFG;
+
+    require_once($CFG->dirroot.'/calendar/lib.php');
+
+    // Get CMID if not sent as part of $data.
+    if (!isset($data->coursemodule)) {
+        $cm = get_coursemodule_from_instance('data', $data->id, $data->course);
+        $data->coursemodule = $cm->id;
+    }
+    // Data start calendar events.
+    $event = new stdClass();
+    if ($event->id = $DB->get_field('event', 'id',
+            array('modulename' => 'data', 'instance' => $data->id, 'eventtype' => 'open'))) {
+        if ($data->timeavailablefrom > 0) {
+            // Calendar event exists so update it.
+            $event->name         = get_string('calendarstart', 'data', $data->name);
+            $event->description  = format_module_intro('data', $data, $data->coursemodule);
+            $event->timestart    = $data->timeavailablefrom;
+            $event->visible      = instance_is_visible('data', $data);
+            $event->timeduration = 0;
+            $calendarevent = calendar_event::load($event->id);
+            $calendarevent->update($event);
+        } else {
+            // Calendar event is on longer needed.
+            $calendarevent = calendar_event::load($event->id);
+            $calendarevent->delete();
+        }
+    } else {
+        // Event doesn't exist so create one.
+        if (isset($data->timeavailablefrom) && $data->timeavailablefrom > 0) {
+            $event->name         = get_string('calendarstart', 'data', $data->name);
+            $event->description  = format_module_intro('data', $data, $data->coursemodule);
+            $event->courseid     = $data->course;
+            $event->groupid      = 0;
+            $event->userid       = 0;
+            $event->modulename   = 'data';
+            $event->instance     = $data->id;
+            $event->eventtype    = 'open';
+            $event->timestart    = $data->timeavailablefrom;
+            $event->visible      = instance_is_visible('data', $data);
+            $event->timeduration = 0;
+            calendar_event::create($event);
+        }
+    }
+
+    // Data end calendar events.
+    $event = new stdClass();
+    if ($event->id = $DB->get_field('event', 'id',
+            array('modulename' => 'data', 'instance' => $data->id, 'eventtype' => 'close'))) {
+        if ($data->timeavailableto > 0) {
+            // Calendar event exists so update it.
+            $event->name         = get_string('calendarend', 'data', $data->name);
+            $event->description  = format_module_intro('data', $data, $data->coursemodule);
+            $event->timestart    = $data->timeavailableto;
+            $event->visible      = instance_is_visible('data', $data);
+            $event->timeduration = 0;
+            $calendarevent = calendar_event::load($event->id);
+            $calendarevent->update($event);
+        } else {
+            // Calendar event is on longer needed.
+            $calendarevent = calendar_event::load($event->id);
+            $calendarevent->delete();
+        }
+    } else {
+        // Event doesn't exist so create one.
+        if (isset($data->timeavailableto) && $data->timeavailableto > 0) {
+            $event = new stdClass();
+            $event->name         = get_string('calendarend', 'data', $data->name);
+            $event->description  = format_module_intro('data', $data, $data->coursemodule);
+            $event->courseid     = $data->course;
+            $event->groupid      = 0;
+            $event->userid       = 0;
+            $event->modulename   = 'data';
+            $event->instance     = $data->id;
+            $event->eventtype    = 'close';
+            $event->timestart    = $data->timeavailableto;
+            $event->visible      = instance_is_visible('data', $data);
+            $event->timeduration = 0;
+            calendar_event::create($event);
+        }
+    }
+}
index 7d87cde..c9fc50e 100644 (file)
@@ -623,4 +623,47 @@ class mod_data_lib_testcase extends advanced_testcase {
         $this->assertTrue(mod_data_rating_can_see_item_ratings($params1));
 
     }
+
+    /**
+     * Tests for mod_data_refresh_events.
+     */
+    public function test_data_refresh_events() {
+        global $DB;
+        $this->resetAfterTest();
+        $this->setAdminUser();
+
+        $timeopen = time();
+        $timeclose = time() + 86400;
+
+        $course = $this->getDataGenerator()->create_course();
+        $generator = $this->getDataGenerator()->get_plugin_generator('mod_data');
+        $params['course'] = $course->id;
+        $params['timeavailablefrom'] = $timeopen;
+        $params['timeavailableto'] = $timeclose;
+        $data = $generator->create_instance($params);
+
+        // Normal case, with existing course.
+        $this->assertTrue(data_refresh_events($course->id));
+        $eventparams = array('modulename' => 'data', 'instance' => $data->id, 'eventtype' => 'open');
+        $openevent = $DB->get_record('event', $eventparams, '*', MUST_EXIST);
+        $this->assertEquals($openevent->timestart, $timeopen);
+
+        $eventparams = array('modulename' => 'data', 'instance' => $data->id, 'eventtype' => 'close');
+        $closeevent = $DB->get_record('event', $eventparams, '*', MUST_EXIST);
+        $this->assertEquals($closeevent->timestart, $timeclose);
+        // In case the course ID is passed as a numeric string.
+        $this->assertTrue(data_refresh_events('' . $course->id));
+        // Course ID not provided.
+        $this->assertTrue(data_refresh_events());
+        $eventparams = array('modulename' => 'data');
+        $events = $DB->get_records('event', $eventparams);
+        foreach ($events as $event) {
+            if ($event->modulename === 'data' && $event->instance === $data->id && $event->eventtype === 'open') {
+                $this->assertEquals($event->timestart, $timeopen);
+            }
+            if ($event->modulename === 'data' && $event->instance === $data->id && $event->eventtype === 'close') {
+                $this->assertEquals($event->timestart, $timeclose);
+            }
+        }
+    }
 }
index bddeea5..4a0d5ef 100644 (file)
@@ -23,6 +23,8 @@ relateditemsdeleted,mod_feedback
 separator_decimal,mod_feedback
 separator_thousand,mod_feedback
 saving_failed_because_missing_or_false_values,mod_feedback
+start,mod_feedback
+stop,mod_feedback
 switch_group,mod_feedback
 viewcompleted,mod_feedback
 viewcompleted_help,mod_feedback
index 6e2988e..c5291cb 100644 (file)
@@ -37,6 +37,8 @@ $string['autonumbering'] = 'Auto number questions';
 $string['autonumbering_help'] = 'Enables or disables automated numbers for each question';
 $string['average'] = 'Average';
 $string['bold'] = 'Bold';
+$string['calendarend'] = 'Feedback {$a} closes';
+$string['calendarstart'] = 'Feedback {$a} opens';
 $string['cannotaccess'] = 'You can only access this feedback from a course';
 $string['cannotsavetempl'] = 'saving templates is not allowed';
 $string['captcha'] = 'Captcha';
@@ -238,9 +240,7 @@ $string['show_entry'] = 'Show response';
 $string['show_nonrespondents'] = 'Show non-respondents';
 $string['site_after_submit'] = 'Site after submit';
 $string['sort_by_course'] = 'Sort by course';
-$string['start'] = 'Start';
 $string['started'] = 'started';
-$string['stop'] = 'End';
 $string['subject'] = 'Subject';
 $string['switch_item_to_not_required'] = 'Set as not required';
 $string['switch_item_to_required'] = 'Set as required';
@@ -296,3 +296,6 @@ $string['relateditemsdeleted'] = 'All responses for this question will also be d
 $string['radiorated'] = 'Radiobutton (rated)';
 $string['radiobutton'] = 'Multiple choice - single answer allowed (radio buttons)';
 $string['radiobutton_rated'] = 'Radiobutton (rated)';
+// Deprecated since Moodle 3.2.
+$string['start'] = 'Start';
+$string['stop'] = 'End';
index eff359a..a2a0b2e 100644 (file)
@@ -25,8 +25,6 @@
 
 /** Include eventslib.php */
 require_once($CFG->libdir.'/eventslib.php');
-/** Include calendar/lib.php */
-require_once($CFG->dirroot.'/calendar/lib.php');
 // Include forms lib.
 require_once($CFG->libdir.'/formslib.php');
 
@@ -633,6 +631,12 @@ function feedback_reset_userdata($data) {
                         'error'=>false);
     }
 
+    // Updating dates - shift may be negative too.
+    if ($data->timeshift) {
+        $shifterror = !shift_course_mod_dates('feedback', array('timeopen', 'timeclose'), $data->timeshift, $data->courseid);
+        $status[] = array('component' => $componentstr, 'item' => get_string('datechanged'), 'error' => $shifterror);
+    }
+
     return $status;
 }
 
@@ -731,55 +735,109 @@ function feedback_get_editor_options() {
  * @return void
  */
 function feedback_set_events($feedback) {
-    global $DB;
+    global $DB, $CFG;
 
-    // adding the feedback to the eventtable (I have seen this at quiz-module)
-    $DB->delete_records('event', array('modulename'=>'feedback', 'instance'=>$feedback->id));
+    // Include calendar/lib.php.
+    require_once($CFG->dirroot.'/calendar/lib.php');
 
+    // Get CMID if not sent as part of $feedback.
     if (!isset($feedback->coursemodule)) {
-        $cm = get_coursemodule_from_id('feedback', $feedback->id);
+        $cm = get_coursemodule_from_instance('feedback', $feedback->id, $feedback->course);
         $feedback->coursemodule = $cm->id;
     }
 
-    // the open-event
-    if ($feedback->timeopen > 0) {
+    // Feedback start calendar events.
+    $eventid = $DB->get_field('event', 'id',
+            array('modulename' => 'feedback', 'instance' => $feedback->id, 'eventtype' => 'open'));
+
+    if (isset($feedback->timeopen) && $feedback->timeopen > 0) {
         $event = new stdClass();
-        $event->name         = get_string('start', 'feedback').' '.$feedback->name;
+        $event->name         = get_string('calendarstart', 'feedback', $feedback->name);
         $event->description  = format_module_intro('feedback', $feedback, $feedback->coursemodule);
-        $event->courseid     = $feedback->course;
-        $event->groupid      = 0;
-        $event->userid       = 0;
-        $event->modulename   = 'feedback';
-        $event->instance     = $feedback->id;
-        $event->eventtype    = 'open';
         $event->timestart    = $feedback->timeopen;
         $event->visible      = instance_is_visible('feedback', $feedback);
-        if ($feedback->timeclose > 0) {
-            $event->timeduration = ($feedback->timeclose - $feedback->timeopen);
+        $event->timeduration = 0;
+        if ($eventid) {
+            // Calendar event exists so update it.
+            $event->id = $eventid;
+            $calendarevent = calendar_event::load($event->id);
+            $calendarevent->update($event);
         } else {
-            $event->timeduration = 0;
+            // Event doesn't exist so create one.
+            $event->courseid     = $feedback->course;
+            $event->groupid      = 0;
+            $event->userid       = 0;
+            $event->modulename   = 'feedback';
+            $event->instance     = $feedback->id;
+            $event->eventtype    = 'open';
+            calendar_event::create($event);
         }
-
-        calendar_event::create($event);
+    } else if ($eventid) {
+        // Calendar event is on longer needed.
+        $calendarevent = calendar_event::load($eventid);
+        $calendarevent->delete();
     }
 
-    // the close-event
-    if ($feedback->timeclose > 0) {
+    // Feedback close calendar events.
+    $eventid = $DB->get_field('event', 'id',
+            array('modulename' => 'feedback', 'instance' => $feedback->id, 'eventtype' => 'close'));
+
+    if (isset($feedback->timeclose) && $feedback->timeclose > 0) {
         $event = new stdClass();
-        $event->name         = get_string('stop', 'feedback').' '.$feedback->name;
+        $event->name         = get_string('calendarend', 'feedback', $feedback->name);
         $event->description  = format_module_intro('feedback', $feedback, $feedback->coursemodule);
-        $event->courseid     = $feedback->course;
-        $event->groupid      = 0;
-        $event->userid       = 0;
-        $event->modulename   = 'feedback';
-        $event->instance     = $feedback->id;
-        $event->eventtype    = 'close';
         $event->timestart    = $feedback->timeclose;
         $event->visible      = instance_is_visible('feedback', $feedback);
         $event->timeduration = 0;
+        if ($eventid) {
+            // Calendar event exists so update it.
+            $event->id = $eventid;
+            $calendarevent = calendar_event::load($event->id);
+            $calendarevent->update($event);
+        } else {
+            // Event doesn't exist so create one.
+            $event->courseid     = $feedback->course;
+            $event->groupid      = 0;
+            $event->userid       = 0;
+            $event->modulename   = 'feedback';
+            $event->instance     = $feedback->id;
+            $event->eventtype    = 'close';
+            calendar_event::create($event);
+        }
+    } else if ($eventid) {
+        // Calendar event is on longer needed.
+        $calendarevent = calendar_event::load($eventid);
+        $calendarevent->delete();
+    }
+}
+
+/**
+ * This standard function will check all instances of this module
+ * and make sure there are up-to-date events created for each of them.
+ * If courseid = 0, then every feedback event in the site is checked, else
+ * only feedback events belonging to the course specified are checked.
+ * This function is used, in its new format, by restore_refresh_events()
+ *
+ * @param int $courseid
+ * @return bool
+ */
+function feedback_refresh_events($courseid = 0) {
+    global $DB;
 
-        calendar_event::create($event);
+    if ($courseid) {
+        if (! $feedbacks = $DB->get_records("feedback", array("course" => $courseid))) {
+            return true;
+        }
+    } else {
+        if (! $feedbacks = $DB->get_records("feedback")) {
+            return true;
+        }
     }
+
+    foreach ($feedbacks as $feedback) {
+        feedback_set_events($feedback);
+    }
+    return true;
 }
 
 /**
diff --git a/mod/feedback/tests/lib_test.php b/mod/feedback/tests/lib_test.php
new file mode 100644 (file)
index 0000000..789b74d
--- /dev/null
@@ -0,0 +1,79 @@
+<?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/>.
+/**
+ * Unit tests for (some of) mod/feedback/lib.php.
+ *
+ * @package    mod_feedback
+ * @copyright  2016 Stephen Bourget
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+defined('MOODLE_INTERNAL') || die();
+global $CFG;
+require_once($CFG->dirroot . '/mod/feedback/lib.php');
+
+/**
+ * Unit tests for (some of) mod/feedback/lib.php.
+ *
+ * @copyright  2016 Stephen Bourget
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class mod_feedback_lib_testcase extends advanced_testcase {
+
+    /**
+     * Tests for mod_feedback_refresh_events.
+     */
+    public function test_feedback_refresh_events() {
+        global $DB;
+        $this->resetAfterTest();
+        $this->setAdminUser();
+
+        $timeopen = time();
+        $timeclose = time() + 86400;
+
+        $course = $this->getDataGenerator()->create_course();
+        $generator = $this->getDataGenerator()->get_plugin_generator('mod_feedback');
+        $params['course'] = $course->id;
+        $params['timeopen'] = $timeopen;
+        $params['timeclose'] = $timeclose;
+        $feedback = $generator->create_instance($params);
+        $cm = get_coursemodule_from_instance('feedback', $feedback->id);
+        $context = context_module::instance($cm->id);
+
+        // Normal case, with existing course.
+        $this->assertTrue(feedback_refresh_events($course->id));
+        $eventparams = array('modulename' => 'feedback', 'instance' => $feedback->id, 'eventtype' => 'open');
+        $openevent = $DB->get_record('event', $eventparams, '*', MUST_EXIST);
+        $this->assertEquals($openevent->timestart, $timeopen);
+
+        $eventparams = array('modulename' => 'feedback', 'instance' => $feedback->id, 'eventtype' => 'close');
+        $closeevent = $DB->get_record('event', $eventparams, '*', MUST_EXIST);
+        $this->assertEquals($closeevent->timestart, $timeclose);
+        // In case the course ID is passed as a numeric string.
+        $this->assertTrue(feedback_refresh_events('' . $course->id));
+        // Course ID not provided.
+        $this->assertTrue(feedback_refresh_events());
+        $eventparams = array('modulename' => 'feedback');
+        $events = $DB->get_records('event', $eventparams);
+        foreach ($events as $event) {
+            if ($event->modulename === 'feedback' && $event->instance === $feedback->id && $event->eventtype === 'open') {
+                $this->assertEquals($event->timestart, $timeopen);
+            }
+            if ($event->modulename === 'feedback' && $event->instance === $feedback->id && $event->eventtype === 'close') {
+                $this->assertEquals($event->timestart, $timeclose);
+            }
+        }
+    }
+}
index 3be31b9..be682bd 100644 (file)
@@ -25,474 +25,135 @@ defined('MOODLE_INTERNAL') || die();
 // Deprecated a very long time ago.
 
 /**
- * How many posts by other users are unrated by a given user in the given discussion?
- *
- * @param int $discussionid
- * @param int $userid
- * @return mixed
  * @deprecated since Moodle 1.1 - please do not use this function any more.
  */
-function forum_count_unrated_posts($discussionid, $userid) {
-    global $CFG, $DB;
-    debugging('forum_count_unrated_posts() is deprecated and will not be replaced.', DEBUG_DEVELOPER);
-
-    $sql = "SELECT COUNT(*) as num
-              FROM {forum_posts}
-             WHERE parent > 0
-               AND discussion = :discussionid
-               AND userid <> :userid";
-    $params = array('discussionid' => $discussionid, 'userid' => $userid);
-    $posts = $DB->get_record_sql($sql, $params);
-    if ($posts) {
-        $sql = "SELECT count(*) as num
-                  FROM {forum_posts} p,
-                       {rating} r
-                 WHERE p.discussion = :discussionid AND
-                       p.id = r.itemid AND
-                       r.userid = userid AND
-                       r.component = 'mod_forum' AND
-                       r.ratingarea = 'post'";
-        $rated = $DB->get_record_sql($sql, $params);
-        if ($rated) {
-            if ($posts->num > $rated->num) {
-                return $posts->num - $rated->num;
-            } else {
-                return 0;    // Just in case there was a counting error
-            }
-        } else {
-            return $posts->num;
-        }
-    } else {
-        return 0;
-    }
+function forum_count_unrated_posts() {
+    throw new coding_exception(__FUNCTION__ . '() can not be used any more.');
 }
 
 
 // Since Moodle 1.5.
 
 /**
- * Returns the count of records for the provided user and discussion.
- *
- * @global object
- * @global object
- * @param int $userid
- * @param int $discussionid
- * @return bool
  * @deprecated since Moodle 1.5 - please do not use this function any more.
  */
-function forum_tp_count_discussion_read_records($userid, $discussionid) {
-    debugging('forum_tp_count_discussion_read_records() is deprecated and will not be replaced.', DEBUG_DEVELOPER);
-
-    global $CFG, $DB;
-
-    $cutoffdate = isset($CFG->forum_oldpostdays) ? (time() - ($CFG->forum_oldpostdays*24*60*60)) : 0;
-
-    $sql = 'SELECT COUNT(DISTINCT p.id) '.
-           'FROM {forum_discussions} d '.
-           'LEFT JOIN {forum_read} r ON d.id = r.discussionid AND r.userid = ? '.
-           'LEFT JOIN {forum_posts} p ON p.discussion = d.id '.
-                'AND (p.modified < ? OR p.id = r.postid) '.
-           'WHERE d.id = ? ';
-
-    return ($DB->count_records_sql($sql, array($userid, $cutoffdate, $discussionid)));
+function forum_tp_count_discussion_read_records() {
+    throw new coding_exception(__FUNCTION__ . '() can not be used any more.');
 }
 
 /**
- * Get all discussions started by a particular user in a course (or group)
- *
- * @global object
- * @global object
- * @param int $courseid
- * @param int $userid
- * @param int $groupid
- * @return array
  * @deprecated since Moodle 1.5 - please do not use this function any more.
  */
-function forum_get_user_discussions($courseid, $userid, $groupid=0) {
-    debugging('forum_get_user_discussions() is deprecated and will not be replaced.', DEBUG_DEVELOPER);
-
-    global $CFG, $DB;
-    $params = array($courseid, $userid);
-    if ($groupid) {
-        $groupselect = " AND d.groupid = ? ";
-        $params[] = $groupid;
-    } else  {
-        $groupselect = "";
-    }
-
-    $allnames = get_all_user_name_fields(true, 'u');
-    return $DB->get_records_sql("SELECT p.*, d.groupid, $allnames, u.email, u.picture, u.imagealt,
-                                   f.type as forumtype, f.name as forumname, f.id as forumid
-                              FROM {forum_discussions} d,
-                                   {forum_posts} p,
-                                   {user} u,
-                                   {forum} f
-                             WHERE d.course = ?
-                               AND p.discussion = d.id
-                               AND p.parent = 0
-                               AND p.userid = u.id
-                               AND u.id = ?
-                               AND d.forum = f.id $groupselect
-                          ORDER BY p.created DESC", $params);
+function forum_get_user_discussions() {
+    throw new coding_exception(__FUNCTION__ . '() can not be used any more.');
 }
 
 
 // Since Moodle 1.6.
 
 /**
- * Returns the count of posts for the provided forum and [optionally] group.
- * @global object
- * @global object
- * @param int $forumid
- * @param int|bool $groupid
- * @return int
  * @deprecated since Moodle 1.6 - please do not use this function any more.
  */
-function forum_tp_count_forum_posts($forumid, $groupid=false) {
-    debugging('forum_tp_count_forum_posts() is deprecated and will not be replaced.', DEBUG_DEVELOPER);
-
-    global $CFG, $DB;
-    $params = array($forumid);
-    $sql = 'SELECT COUNT(*) '.
-           'FROM {forum_posts} fp,{forum_discussions} fd '.
-           'WHERE fd.forum = ? AND fp.discussion = fd.id';
-    if ($groupid !== false) {
-        $sql .= ' AND (fd.groupid = ? OR fd.groupid = -1)';
-        $params[] = $groupid;
-    }
-    $count = $DB->count_records_sql($sql, $params);
-
-
-    return $count;
+function forum_tp_count_forum_posts() {
+    throw new coding_exception(__FUNCTION__ . '() can not be used any more.');
 }
 
 /**
- * Returns the count of records for the provided user and forum and [optionally] group.
- * @global object
- * @global object
- * @param int $userid
- * @param int $forumid
- * @param int|bool $groupid
- * @return int
  * @deprecated since Moodle 1.6 - please do not use this function any more.
  */
-function forum_tp_count_forum_read_records($userid, $forumid, $groupid=false) {
-    debugging('forum_tp_count_forum_read_records() is deprecated and will not be replaced.', DEBUG_DEVELOPER);
-
-    global $CFG, $DB;
-
-    $cutoffdate = time() - ($CFG->forum_oldpostdays*24*60*60);
-
-    $groupsel = '';
-    $params = array($userid, $forumid, $cutoffdate);
-    if ($groupid !== false) {
-        $groupsel = "AND (d.groupid = ? OR d.groupid = -1)";
-        $params[] = $groupid;
-    }
-
-    $sql = "SELECT COUNT(p.id)
-              FROM  {forum_posts} p
-                    JOIN {forum_discussions} d ON d.id = p.discussion
-                    LEFT JOIN {forum_read} r   ON (r.postid = p.id AND r.userid= ?)
-              WHERE d.forum = ?
-                    AND (p.modified < $cutoffdate OR (p.modified >= ? AND r.id IS NOT NULL))
-                    $groupsel";
-
-    return $DB->get_field_sql($sql, $params);
+function forum_tp_count_forum_read_records() {
+    throw new coding_exception(__FUNCTION__ . '() can not be used any more.');
 }
 
 
 // Since Moodle 1.7.
 
 /**
- * Returns array of forum open modes.
- *
- * @return array
  * @deprecated since Moodle 1.7 - please do not use this function any more.
  */
 function forum_get_open_modes() {
-    debugging('forum_get_open_modes() is deprecated and will not be replaced.', DEBUG_DEVELOPER);
-    return array();
+    throw new coding_exception(__FUNCTION__ . '() can not be used any more.');
 }
 
 
 // Since Moodle 1.9.
 
 /**
- * Gets posts with all info ready for forum_print_post
- * We pass forumid in because we always know it so no need to make a
- * complicated join to find it out.
- *
- * @global object
- * @global object
- * @param int $parent
- * @param int $forumid
- * @return array
  * @deprecated since Moodle 1.9 MDL-13303 - please do not use this function any more.
  */
-function forum_get_child_posts($parent, $forumid) {
-    debugging('forum_get_child_posts() is deprecated.', DEBUG_DEVELOPER);
-
-    global $CFG, $DB;
-
-    $allnames = get_all_user_name_fields(true, 'u');
-    return $DB->get_records_sql("SELECT p.*, $forumid AS forum, $allnames, u.email, u.picture, u.imagealt
-                              FROM {forum_posts} p
-                         LEFT JOIN {user} u ON p.userid = u.id
-                             WHERE p.parent = ?
-                          ORDER BY p.created ASC", array($parent));
+function forum_get_child_posts() {
+    throw new coding_exception(__FUNCTION__ . '() can not be used any more.');
 }
 
 /**
- * Gets posts with all info ready for forum_print_post
- * We pass forumid in because we always know it so no need to make a
- * complicated join to find it out.
- *
- * @global object
- * @global object
- * @return mixed array of posts or false
  * @deprecated since Moodle 1.9 MDL-13303 - please do not use this function any more.
  */
-function forum_get_discussion_posts($discussion, $sort, $forumid) {
-    debugging('forum_get_discussion_posts() is deprecated.', DEBUG_DEVELOPER);
-
-    global $CFG, $DB;
-
-    $allnames = get_all_user_name_fields(true, 'u');
-    return $DB->get_records_sql("SELECT p.*, $forumid AS forum, $allnames, u.email, u.picture, u.imagealt
-                              FROM {forum_posts} p
-                         LEFT JOIN {user} u ON p.userid = u.id
-                             WHERE p.discussion = ?
-                               AND p.parent > 0 $sort", array($discussion));
+function forum_get_discussion_posts() {
+    throw new coding_exception(__FUNCTION__ . '() can not be used any more.');
 }
 
 
 // Since Moodle 2.0.
 
 /**
- * Returns a list of ratings for a particular post - sorted.
- *
- * @param stdClass $context
- * @param int $postid
- * @param string $sort
- * @return array Array of ratings or false
  * @deprecated since Moodle 2.0 MDL-21657 - please do not use this function any more.
  */
-function forum_get_ratings($context, $postid, $sort = "u.firstname ASC") {
-    debugging('forum_get_ratings() is deprecated.', DEBUG_DEVELOPER);
-    $options = new stdClass;
-    $options->context = $context;
-    $options->component = 'mod_forum';
-    $options->ratingarea = 'post';
-    $options->itemid = $postid;
-    $options->sort = "ORDER BY $sort";
-
-    $rm = new rating_manager();
-    return $rm->get_all_ratings_for_item($options);
+function forum_get_ratings() {
+    throw new coding_exception(__FUNCTION__ . '() can not be used any more.');
 }
 
 /**
- * Generate and return the track or no track link for a forum.
- *
- * @global object
- * @global object
- * @global object
- * @param object $forum the forum. Fields used are $forum->id and $forum->forcesubscribe.
- * @param array $messages
- * @param bool $fakelink
- * @return string
  * @deprecated since Moodle 2.0 MDL-14632 - please do not use this function any more.
  */
-function forum_get_tracking_link($forum, $messages=array(), $fakelink=true) {
-    debugging('forum_get_tracking_link() is deprecated.', DEBUG_DEVELOPER);
-
-    global $CFG, $USER, $PAGE, $OUTPUT;
-
-    static $strnotrackforum, $strtrackforum;
-
-    if (isset($messages['trackforum'])) {
-         $strtrackforum = $messages['trackforum'];
-    }
-    if (isset($messages['notrackforum'])) {
-         $strnotrackforum = $messages['notrackforum'];
-    }
-    if (empty($strtrackforum)) {
-        $strtrackforum = get_string('trackforum', 'forum');
-    }
-    if (empty($strnotrackforum)) {
-        $strnotrackforum = get_string('notrackforum', 'forum');
-    }
-
-    if (forum_tp_is_tracked($forum)) {
-        $linktitle = $strnotrackforum;
-        $linktext = $strnotrackforum;
-    } else {
-        $linktitle = $strtrackforum;
-        $linktext = $strtrackforum;
-    }
-
-    $link = '';
-    if ($fakelink) {
-        $PAGE->requires->js('/mod/forum/forum.js');
-        $PAGE->requires->js_function_call('forum_produce_tracking_link', Array($forum->id, $linktext, $linktitle));
-        // use <noscript> to print button in case javascript is not enabled
-        $link .= '<noscript>';
-    }
-    $url = new moodle_url('/mod/forum/settracking.php', array(
-            'id' => $forum->id,
-            'sesskey' => sesskey(),
-        ));
-    $link .= $OUTPUT->single_button($url, $linktext, 'get', array('title'=>$linktitle));
-
-    if ($fakelink) {
-        $link .= '</noscript>';
-    }
-
-    return $link;
+function forum_get_tracking_link() {
+    throw new coding_exception(__FUNCTION__ . '() can not be used any more.');
 }
 
 /**
- * Returns the count of records for the provided user and discussion.
- *
- * @global object
- * @global object
- * @param int $userid
- * @param int $discussionid
- * @return int
  * @deprecated since Moodle 2.0 MDL-14113 - please do not use this function any more.
  */
-function forum_tp_count_discussion_unread_posts($userid, $discussionid) {
-    debugging('forum_tp_count_discussion_unread_posts() is deprecated.', DEBUG_DEVELOPER);
-    global $CFG, $DB;
-
-    $cutoffdate = isset($CFG->forum_oldpostdays) ? (time() - ($CFG->forum_oldpostdays*24*60*60)) : 0;
-
-    $sql = 'SELECT COUNT(p.id) '.
-           'FROM {forum_posts} p '.
-           'LEFT JOIN {forum_read} r ON r.postid = p.id AND r.userid = ? '.
-           'WHERE p.discussion = ? '.
-                'AND p.modified >= ? AND r.id is NULL';
-
-    return $DB->count_records_sql($sql, array($userid, $discussionid, $cutoffdate));
+function forum_tp_count_discussion_unread_posts() {
+    throw new coding_exception(__FUNCTION__ . '() can not be used any more.');
 }
 
 /**
- * Converts a forum to use the Roles System
- *
  * @deprecated since Moodle 2.0 MDL-23479 - please do not use this function any more.
  */
 function forum_convert_to_roles() {
-    debugging('forum_convert_to_roles() is deprecated and will not be replaced.', DEBUG_DEVELOPER);
+    throw new coding_exception(__FUNCTION__ . '() can not be used any more.');
 }
 
 /**
- * Returns all records in the 'forum_read' table matching the passed keys, indexed
- * by userid.
- *
- * @global object
- * @param int $userid
- * @param int $postid
- * @param int $discussionid
- * @param int $forumid
- * @return array
  * @deprecated since Moodle 2.0 MDL-14113 - please do not use this function any more.
  */
-function forum_tp_get_read_records($userid=-1, $postid=-1, $discussionid=-1, $forumid=-1) {
-    debugging('forum_tp_get_read_records() is deprecated and will not be replaced.', DEBUG_DEVELOPER);
-
-    global $DB;
-    $select = '';
-    $params = array();
-
-    if ($userid > -1) {
-        if ($select != '') $select .= ' AND ';
-        $select .= 'userid = ?';
-        $params[] = $userid;
-    }
-    if ($postid > -1) {
-        if ($select != '') $select .= ' AND ';
-        $select .= 'postid = ?';
-        $params[] = $postid;
-    }
-    if ($discussionid > -1) {
-        if ($select != '') $select .= ' AND ';
-        $select .= 'discussionid = ?';
-        $params[] = $discussionid;
-    }
-    if ($forumid > -1) {
-        if ($select != '') $select .= ' AND ';
-        $select .= 'forumid = ?';
-        $params[] = $forumid;
-    }
-
-    return $DB->get_records_select('forum_read', $select, $params);
+function forum_tp_get_read_records() {
+    throw new coding_exception(__FUNCTION__ . '() can not be used any more.');
 }
 
 /**
- * Returns all read records for the provided user and discussion, indexed by postid.
- *
- * @global object
- * @param inti $userid
- * @param int $discussionid
  * @deprecated since Moodle 2.0 MDL-14113 - please do not use this function any more.
  */
-function forum_tp_get_discussion_read_records($userid, $discussionid) {
-    debugging('forum_tp_get_discussion_read_records() is deprecated and will not be replaced.', DEBUG_DEVELOPER);
-
-    global $DB;
-    $select = 'userid = ? AND discussionid = ?';
-    $fields = 'postid, firstread, lastread';
-    return $DB->get_records_select('forum_read', $select, array($userid, $discussionid), '', $fields);
+function forum_tp_get_discussion_read_records() {
+    throw new coding_exception(__FUNCTION__ . '() can not be used any more.');
 }
 
 // Deprecated in 2.3.
 
 /**
- * This function gets run whenever user is enrolled into course
- *
  * @deprecated since Moodle 2.3 MDL-33166 - please do not use this function any more.
- * @param stdClass $cp
- * @return void
  */
-function forum_user_enrolled($cp) {
-    debugging('forum_user_enrolled() is deprecated. Please use forum_user_role_assigned instead.', DEBUG_DEVELOPER);
-    global $DB;
-
-    // NOTE: this has to be as fast as possible - we do not want to slow down enrolments!
-    //       Originally there used to be 'mod/forum:initialsubscriptions' which was
-    //       introduced because we did not have enrolment information in earlier versions...
-
-    $sql = "SELECT f.id
-              FROM {forum} f
-         LEFT JOIN {forum_subscriptions} fs ON (fs.forum = f.id AND fs.userid = :userid)
-             WHERE f.course = :courseid AND f.forcesubscribe = :initial AND fs.id IS NULL";
-    $params = array('courseid'=>$cp->courseid, 'userid'=>$cp->userid, 'initial'=>FORUM_INITIALSUBSCRIBE);
-
-    $forums = $DB->get_records_sql($sql, $params);
-    foreach ($forums as $forum) {
-        \mod_forum\subscriptions::subscribe_user($cp->userid, $forum);
-    }
+function forum_user_enrolled() {
+    throw new coding_exception(__FUNCTION__ . '() can not be used any more.');
 }
 
 
 // Deprecated in 2.4.
 
 /**
- * Checks to see if a user can view a particular post.
- *
  * @deprecated since Moodle 2.4 use forum_user_can_see_post() instead
- *
- * @param object $post
- * @param object $course
- * @param object $cm
- * @param object $forum
- * @param object $discussion
- * @param object $user
- * @return boolean
  */
-function forum_user_can_view_post($post, $course, $cm, $forum, $discussion, $user=null){
-    debugging('forum_user_can_view_post() is deprecated. Please use forum_user_can_see_post() instead.', DEBUG_DEVELOPER);
-    return forum_user_can_see_post($forum, $discussion, $post, $user, $cm);
+function forum_user_can_view_post() {
+    throw new coding_exception(__FUNCTION__ . '() can not be used any more.');
 }
 
 
@@ -509,204 +170,93 @@ define('FORUM_TRACKING_ON', 2);
  * @see shorten_text()
  */
 function forum_shorten_post($message) {
-    throw new coding_exception('forum_shorten_post() can not be used any more. Please use shorten_text($message, $CFG->forum_shortpost) instead.');
+    throw new coding_exception(__FUNCTION__ . '() can not be used any more. '
+        . 'Please use shorten_text($message, $CFG->forum_shortpost) instead.');
 }
 
 // Deprecated in 2.8.
 
 /**
- * @global object
- * @param int $userid
- * @param object $forum
- * @return bool
  * @deprecated since Moodle 2.8 use \mod_forum\subscriptions::is_subscribed() instead
  */
-function forum_is_subscribed($userid, $forum) {
-    global $DB;
-    debugging("forum_is_subscribed() has been deprecated, please use \\mod_forum\\subscriptions::is_subscribed() instead.",
-            DEBUG_DEVELOPER);
-
-    // Note: The new function does not take an integer form of forum.
-    if (is_numeric($forum)) {
-        $forum = $DB->get_record('forum', array('id' => $forum));
-    }
-
-    return mod_forum\subscriptions::is_subscribed($userid, $forum);
+function forum_is_subscribed() {
+    throw new coding_exception(__FUNCTION__ . '() can not be used any more.');
 }
 
 /**
- * Adds user to the subscriber list
- *
- * @param int $userid
- * @param int $forumid
- * @param context_module|null $context Module context, may be omitted if not known or if called for the current module set in page.
- * @param boolean $userrequest Whether the user requested this change themselves. This has an effect on whether
- * discussion subscriptions are removed too.
  * @deprecated since Moodle 2.8 use \mod_forum\subscriptions::subscribe_user() instead
  */
-function forum_subscribe($userid, $forumid, $context = null, $userrequest = false) {
-    global $DB;
-    debugging("forum_subscribe() has been deprecated, please use \\mod_forum\\subscriptions::subscribe_user() instead.",
-            DEBUG_DEVELOPER);
-
-    // Note: The new function does not take an integer form of forum.
-    $forum = $DB->get_record('forum', array('id' => $forumid));
-    \mod_forum\subscriptions::subscribe_user($userid, $forum, $context, $userrequest);
+function forum_subscribe() {
+    throw new coding_exception(__FUNCTION__ . '() can not be used any more. Please use '
+        . \mod_forum\subscriptions::class . '::subscribe_user() instead');
 }
 
 /**
- * Removes user from the subscriber list
- *
- * @param int $userid
- * @param int $forumid
- * @param context_module|null $context Module context, may be omitted if not known or if called for the current module set in page.
- * @param boolean $userrequest Whether the user requested this change themselves. This has an effect on whether
- * discussion subscriptions are removed too.
  * @deprecated since Moodle 2.8 use \mod_forum\subscriptions::unsubscribe_user() instead
  */
-function forum_unsubscribe($userid, $forumid, $context = null, $userrequest = false) {
-    global $DB;
-    debugging("forum_unsubscribe() has been deprecated, please use \\mod_forum\\subscriptions::unsubscribe_user() instead.",
-            DEBUG_DEVELOPER);
-
-    // Note: The new function does not take an integer form of forum.
-    $forum = $DB->get_record('forum', array('id' => $forumid));
-    \mod_forum\subscriptions::unsubscribe_user($userid, $forum, $context, $userrequest);
+function forum_unsubscribe() {
+    throw new coding_exception(__FUNCTION__ . '() can not be used any more. Please use '
+        . \mod_forum\subscriptions::class . '::unsubscribe_user() instead');
 }
 
 /**
- * Returns list of user objects that are subscribed to this forum.
- *
- * @param stdClass $course the course
- * @param stdClass $forum the forum
- * @param int $groupid group id, or 0 for all.
- * @param context_module $context the forum context, to save re-fetching it where possible.
- * @param string $fields requested user fields (with "u." table prefix)
- * @param boolean $considerdiscussions Whether to take discussion subscriptions and unsubscriptions into consideration.
- * @return array list of users.
  * @deprecated since Moodle 2.8 use \mod_forum\subscriptions::fetch_subscribed_users() instead
   */
-function forum_subscribed_users($course, $forum, $groupid = 0, $context = null, $fields = null) {
-    debugging("forum_subscribed_users() has been deprecated, please use \\mod_forum\\subscriptions::fetch_subscribed_users() instead.",
-            DEBUG_DEVELOPER);
-
-    \mod_forum\subscriptions::fetch_subscribed_users($forum, $groupid, $context, $fields);
+function forum_subscribed_users() {
+    throw new coding_exception(__FUNCTION__ . '() can not be used any more. Please use '
+        . \mod_forum\subscriptions::class . '::fetch_subscribed_users() instead');
 }
 
 /**
  * Determine whether the forum is force subscribed.
  *
- * @param object $forum
- * @return bool
  * @deprecated since Moodle 2.8 use \mod_forum\subscriptions::is_forcesubscribed() instead
  */
 function forum_is_forcesubscribed($forum) {
-    debugging("forum_is_forcesubscribed() has been deprecated, please use \\mod_forum\\subscriptions::is_forcesubscribed() instead.",
-            DEBUG_DEVELOPER);
-
-    global $DB;
-    if (!isset($forum->forcesubscribe)) {
-       $forum = $DB->get_field('forum', 'forcesubscribe', array('id' => $forum));
-    }
-
-    return \mod_forum\subscriptions::is_forcesubscribed($forum);
+    throw new coding_exception(__FUNCTION__ . '() can not be used any more. Please use '
+        . \mod_forum\subscriptions::class . '::is_forcesubscribed() instead');
 }
 
 /**
- * Set the subscription mode for a forum.
- *
- * @param int $forumid
- * @param mixed $value
- * @return bool
  * @deprecated since Moodle 2.8 use \mod_forum\subscriptions::set_subscription_mode() instead
  */
 function forum_forcesubscribe($forumid, $value = 1) {
-    debugging("forum_forcesubscribe() has been deprecated, please use \\mod_forum\\subscriptions::set_subscription_mode() instead.",
-            DEBUG_DEVELOPER);
-
-    return \mod_forum\subscriptions::set_subscription_mode($forumid, $value);
+    throw new coding_exception(__FUNCTION__ . '() can not be used any more. Please use '
+        . \mod_forum\subscriptions::class . '::set_subscription_mode() instead');
 }
 
 /**
- * Get the current subscription mode for the forum.
- *
- * @param int|stdClass $forumid
- * @param mixed $value
- * @return bool
  * @deprecated since Moodle 2.8 use \mod_forum\subscriptions::get_subscription_mode() instead
  */
 function forum_get_forcesubscribed($forum) {
-    debugging("forum_get_forcesubscribed() has been deprecated, please use \\mod_forum\\subscriptions::get_subscription_mode() instead.",
-            DEBUG_DEVELOPER);
-
-    global $DB;
-    if (!isset($forum->forcesubscribe)) {
-       $forum = $DB->get_field('forum', 'forcesubscribe', array('id' => $forum));
-    }
-
-    return \mod_forum\subscriptions::get_subscription_mode($forumid, $value);
+    throw new coding_exception(__FUNCTION__ . '() can not be used any more. Please use '
+        . \mod_forum\subscriptions::class . '::set_subscription_mode() instead');
 }
 
 /**
- * Get a list of forums in the specified course in which a user can change
- * their subscription preferences.
- *
- * @param stdClass $course The course from which to find subscribable forums.
- * @return array
  * @deprecated since Moodle 2.8 use \mod_forum\subscriptions::is_subscribed in combination wtih
  * \mod_forum\subscriptions::fill_subscription_cache_for_course instead.
  */
-function forum_get_subscribed_forums($course) {
-    debugging("forum_get_subscribed_forums() has been deprecated, please see " .
-              "\\mod_forum\\subscriptions::is_subscribed::() " .
-              " and \\mod_forum\\subscriptions::fill_subscription_cache_for_course instead.",
-              DEBUG_DEVELOPER);
-
-    global $USER, $CFG, $DB;
-    $sql = "SELECT f.id
-              FROM {forum} f
-                   LEFT JOIN {forum_subscriptions} fs ON (fs.forum = f.id AND fs.userid = ?)
-             WHERE f.course = ?
-                   AND f.forcesubscribe <> ".FORUM_DISALLOWSUBSCRIBE."
-                   AND (f.forcesubscribe = ".FORUM_FORCESUBSCRIBE." OR fs.id IS NOT NULL)";
-    if ($subscribed = $DB->get_records_sql($sql, array($USER->id, $course->id))) {
-        foreach ($subscribed as $s) {
-            $subscribed[$s->id] = $s->id;
-        }
-        return $subscribed;
-    } else {
-        return array();
-    }
+function forum_get_subscribed_forums() {
+    throw new coding_exception(__FUNCTION__ . '() can not be used any more. Please use '
+        . \mod_forum\subscriptions::class . '::is_subscribed(), and '
+        . \mod_forum\subscriptions::class . '::fill_subscription_cache_for_course() instead');
 }
 
 /**
- * Returns an array of forums that the current user is subscribed to and is allowed to unsubscribe from
- *
- * @return array An array of unsubscribable forums
  * @deprecated since Moodle 2.8 use \mod_forum\subscriptions::get_unsubscribable_forums() instead
  */
 function forum_get_optional_subscribed_forums() {
-    debugging("forum_get_optional_subscribed_forums() has been deprecated, please use \\mod_forum\\subscriptions::get_unsubscribable_forums() instead.",
-            DEBUG_DEVELOPER);
-
-    return \mod_forum\subscriptions::get_unsubscribable_forums();
+    throw new coding_exception(__FUNCTION__ . '() can not be used any more. Please use '
+        . \mod_forum\subscriptions::class . '::get_unsubscribable_forums() instead');
 }
 
 /**
- * Get the list of potential subscribers to a forum.
- *
- * @param object $forumcontext the forum context.
- * @param integer $groupid the id of a group, or 0 for all groups.
- * @param string $fields the list of fields to return for each user. As for get_users_by_capability.
- * @param string $sort sort order. As for get_users_by_capability.
- * @return array list of users.
  * @deprecated since Moodle 2.8 use \mod_forum\subscriptions::get_potential_subscribers() instead
  */
-function forum_get_potential_subscribers($forumcontext, $groupid, $fields, $sort = '') {
-    debugging("forum_get_potential_subscribers() has been deprecated, please use \\mod_forum\\subscriptions::get_potential_subscribers() instead.",
-            DEBUG_DEVELOPER);
-
-    \mod_forum\subscriptions::get_potential_subscribers($forumcontext, $groupid, $fields, $sort);
+function forum_get_potential_subscribers() {
+    throw new coding_exception(__FUNCTION__ . '() can not be used any more. Please use '
+        . \mod_forum\subscriptions::class . '::get_potential_subscribers() instead');
 }
 
 /**
index 8b2d40b..cdd98ac 100644 (file)
@@ -220,11 +220,6 @@ class mod_forum_subscriptions_testcase extends advanced_testcase {
         // Check that the user is currently not subscribed to the forum.
         $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum));
 
-        // Check the deprecated function too.
-        $this->assertFalse(forum_is_subscribed($author->id, $forum));
-        $this->assertEquals(1, count($this->getDebuggingMessages()));
-        $this->resetDebugging();
-
         // Check that the user is unsubscribed from the discussion too.
         $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));
 
@@ -262,36 +257,6 @@ class mod_forum_subscriptions_testcase extends advanced_testcase {
             'discussion'    => $discussion->id,
         )));
 
-        // The same thing should happen calling the deprecated versions of
-        // these functions.
-        // Subscribing to the forum should create a record in the subscriptions table, but not the forum discussion
-        // subscriptions table.
-        forum_subscribe($author->id, $forum->id);
-        $this->assertEquals(1, count($this->getDebuggingMessages()));
-        $this->resetDebugging();
-        $this->assertEquals(1, $DB->count_records('forum_subscriptions', array(
-            'userid'        => $author->id,
-            'forum'         => $forum->id,
-        )));
-        $this->assertEquals(0, $DB->count_records('forum_discussion_subs', array(
-            'userid'        => $author->id,
-            'discussion'    => $discussion->id,
-        )));
-
-        // Unsubscribing should remove the record from the forum subscriptions table, and not modify the forum
-        // discussion subscriptions table.
-        forum_unsubscribe($author->id, $forum->id);
-        $this->assertEquals(1, count($this->getDebuggingMessages()));
-        $this->resetDebugging();
-        $this->assertEquals(0, $DB->count_records('forum_subscriptions', array(
-            'userid'        => $author->id,
-            'forum'         => $forum->id,
-        )));
-        $this->assertEquals(0, $DB->count_records('forum_discussion_subs', array(
-            'userid'        => $author->id,
-            'discussion'    => $discussion->id,
-        )));
-
         // Enroling the user in the discussion should add one record to the forum discussion table without modifying the
         // form subscriptions.
         \mod_forum\subscriptions::subscribe_user_to_discussion($author->id, $discussion);
@@ -439,11 +404,6 @@ class mod_forum_subscriptions_testcase extends advanced_testcase {
         // Check that the user is currently not subscribed to the forum.
         $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum));
 
-        // Check the deprecated function too.
-        $this->assertFalse(forum_is_subscribed($author->id, $forum));
-        $this->assertEquals(1, count($this->getDebuggingMessages()));
-        $this->resetDebugging();
-
         // Post a discussion to the forum.
         list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
 
@@ -476,11 +436,6 @@ class mod_forum_subscriptions_testcase extends advanced_testcase {
         // Check that the user is currently subscribed to the forum.
         $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum));
 
-        // Check the deprecated function too.
-        $this->assertTrue(forum_is_subscribed($author->id, $forum));
-        $this->assertEquals(1, count($this->getDebuggingMessages()));
-        $this->resetDebugging();
-
         // Post a discussion to the forum.
         list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
 
@@ -506,11 +461,6 @@ class mod_forum_subscriptions_testcase extends advanced_testcase {
         // Check that the user is currently not subscribed to the forum.
         $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum));
 
-        // Check the deprecated function too.
-        $this->assertFalse(forum_is_subscribed($author->id, $forum));
-        $this->assertEquals(1, count($this->getDebuggingMessages()));
-        $this->resetDebugging();
-
         // Post a discussion to the forum.
         list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
 
@@ -523,11 +473,6 @@ class mod_forum_subscriptions_testcase extends advanced_testcase {
         // Check that the user is still unsubscribed from the forum.
         $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum));
 
-        // Check the deprecated function too.
-        $this->assertFalse(forum_is_subscribed($author->id, $forum));
-        $this->assertEquals(1, count($this->getDebuggingMessages()));
-        $this->resetDebugging();
-
         // But subscribed to the discussion.
         $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));
     }
@@ -553,11 +498,6 @@ class mod_forum_subscriptions_testcase extends advanced_testcase {
         // Check that the user is currently subscribed to the forum.
         $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum));
 
-        // Check the deprecated function too.
-        $this->assertTrue(forum_is_subscribed($author->id, $forum));
-        $this->assertEquals(1, count($this->getDebuggingMessages()));
-        $this->resetDebugging();
-
         // Post a discussion to the forum.
         list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
 
@@ -567,11 +507,6 @@ class mod_forum_subscriptions_testcase extends advanced_testcase {
         // Check that the user is still subscribed to the forum.
         $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum));
 
-        // Check the deprecated function too.
-        $this->assertTrue(forum_is_subscribed($author->id, $forum));
-        $this->assertEquals(1, count($this->getDebuggingMessages()));
-        $this->resetDebugging();
-
         // But unsubscribed from the discussion.
         $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));
     }
@@ -599,11 +534,6 @@ class mod_forum_subscriptions_testcase extends advanced_testcase {
         // Check that the user is currently subscribed to the forum.
         $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum));
 
-        // Check the deprecated function too.
-        $this->assertTrue(forum_is_subscribed($author->id, $forum));
-        $this->assertEquals(1, count($this->getDebuggingMessages()));
-        $this->resetDebugging();
-
         // Post a discussion to the forum.
         list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
 
@@ -629,11 +559,6 @@ class mod_forum_subscriptions_testcase extends advanced_testcase {
         // Check that the user is still subscribed to the forum.
         $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum));
 
-        // Check the deprecated function too.
-        $this->assertTrue(forum_is_subscribed($author->id, $forum));
-        $this->assertEquals(1, count($this->getDebuggingMessages()));
-        $this->resetDebugging();
-
         // An attempt to unsubscribe again should result in a falsey return to indicate that no change was made.
         $this->assertFalse(\mod_forum\subscriptions::unsubscribe_user_from_discussion($author->id, $discussion));
 
@@ -668,16 +593,6 @@ class mod_forum_subscriptions_testcase extends advanced_testcase {
         // Check that the user is still subscribed to the forum.
         $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum));
 
-        // Check the deprecated function too.
-        $this->assertTrue(forum_is_subscribed($author->id, $forum));
-        $this->assertEquals(1, count($this->getDebuggingMessages()));
-        $this->resetDebugging();
-
-        // Check the deprecated function too.
-        $this->assertTrue(forum_is_subscribed($author->id, $forum));
-        $this->assertEquals(1, count($this->getDebuggingMessages()));
-        $this->resetDebugging();
-
         // And is subscribed to the discussion again.
         $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));
 
@@ -699,11 +614,6 @@ class mod_forum_subscriptions_testcase extends advanced_testcase {
         // Check that the user is still subscribed to the forum.
         $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum));
 
-        // Check the deprecated function too.
-        $this->assertTrue(forum_is_subscribed($author->id, $forum));
-        $this->assertEquals(1, count($this->getDebuggingMessages()));
-        $this->resetDebugging();
-
         // But unsubscribed from the discussion.
         $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));
 
@@ -747,11 +657,6 @@ class mod_forum_subscriptions_testcase extends advanced_testcase {
         // Check that the user is still subscribed to the forum.
         $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum));
 
-        // Check the deprecated function too.
-        $this->assertTrue(forum_is_subscribed($author->id, $forum));
-        $this->assertEquals(1, count($this->getDebuggingMessages()));
-        $this->resetDebugging();
-
         // But unsubscribed from the discussion.
         $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));
 
@@ -806,11 +711,6 @@ class mod_forum_subscriptions_testcase extends advanced_testcase {
         // Check that the user is currently unsubscribed to the forum.
         $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum));
 
-        // Check the deprecated function too.
-        $this->assertFalse(forum_is_subscribed($author->id, $forum));
-        $this->assertEquals(1, count($this->getDebuggingMessages()));
-        $this->resetDebugging();
-
         // Post a discussion to the forum.
         list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
 
@@ -826,11 +726,6 @@ class mod_forum_subscriptions_testcase extends advanced_testcase {
         // Check that the user is still unsubscribed from the forum.
         $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum));
 
-        // Check the deprecated function too.
-        $this->assertFalse(forum_is_subscribed($author->id, $forum));
-        $this->assertEquals(1, count($this->getDebuggingMessages()));
-        $this->resetDebugging();
-
         // But subscribed to the discussion.
         $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));
 
@@ -846,11 +741,6 @@ class mod_forum_subscriptions_testcase extends advanced_testcase {
         // Check that the user is still unsubscribed from the forum.
         $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum));
 
-        // Check the deprecated function too.
-        $this->assertFalse(forum_is_subscribed($author->id, $forum));
-        $this->assertEquals(1, count($this->getDebuggingMessages()));
-        $this->resetDebugging();
-
         // And is unsubscribed from the discussion again.
         $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));
 
@@ -866,11 +756,6 @@ class mod_forum_subscriptions_testcase extends advanced_testcase {
         // Check that the user is still unsubscribed from the forum.
         $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum));
 
-        // Check the deprecated function too.
-        $this->assertFalse(forum_is_subscribed($author->id, $forum));
-        $this->assertEquals(1, count($this->getDebuggingMessages()));
-        $this->resetDebugging();
-
         // And is subscribed to the discussion again.
         $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));
 
@@ -886,11 +771,6 @@ class mod_forum_subscriptions_testcase extends advanced_testcase {
         // Check that the user is still unsubscribed from the forum.
         $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum));
 
-        // Check the deprecated function too.
-        $this->assertFalse(forum_is_subscribed($author->id, $forum));
-        $this->assertEquals(1, count($this->getDebuggingMessages()));
-        $this->resetDebugging();
-
         // But unsubscribed from the discussion.
         $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));
 
@@ -901,46 +781,6 @@ class mod_forum_subscriptions_testcase extends advanced_testcase {
         )));
     }
 
-    /**
-     * Test that the deprecated forum_is_subscribed accepts numeric forum IDs.
-     */
-    public function test_forum_is_subscribed_numeric() {
-        global $DB;
-
-        $this->resetAfterTest(true);
-
-        // Create a course, with a forum.
-        $course = $this->getDataGenerator()->create_course();
-
-        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
-        $forum = $this->getDataGenerator()->create_module('forum', $options);
-
-        // Create a user enrolled in the course as a students.
-        list($author) = $this->helper_create_users($course, 1);
-
-        // Check that the user is currently unsubscribed to the forum.
-        $this->assertFalse(forum_is_subscribed($author->id, $forum->id));
-        $this->assertEquals(1, count($this->getDebuggingMessages()));
-        $this->resetDebugging();
-
-        // It should match the result of when it's called with the forum object.
-        $this->assertFalse(forum_is_subscribed($author->id, $forum));
-        $this->assertEquals(1, count($this->getDebuggingMessages()));
-        $this->resetDebugging();
-
-        // And when the user is subscribed, we should also get the correct result.
-        \mod_forum\subscriptions::subscribe_user($author->id, $forum);
-
-        $this->assertTrue(forum_is_subscribed($author->id, $forum->id));
-        $this->assertEquals(1, count($this->getDebuggingMessages()));
-        $this->resetDebugging();
-
-        // It should match the result of when it's called with the forum object.
-        $this->assertTrue(forum_is_subscribed($author->id, $forum));
-        $this->assertEquals(1, count($this->getDebuggingMessages()));
-        $this->resetDebugging();
-    }
-
     /**
      * Test that the correct users are returned when fetching subscribed users from a forum where users can choose to
      * subscribe and unsubscribe.
index 572ffe5..bbbe97a 100644 (file)
@@ -1,6 +1,36 @@
 This files describes API changes in /mod/forum/*,
 information provided here is intended especially for developers.
 
+=== 3.2 ===
+ * The following functions have been finally deprecated and should no longer be used.
+  - forum_count_unrated_posts
+  - forum_tp_count_discussion_read_records
+  - forum_get_user_discussions
+  - forum_tp_count_forum_posts
+  - forum_tp_count_forum_read_records
+  - forum_get_open_modes
+  - forum_get_child_posts
+  - forum_get_discussion_posts
+  - forum_get_ratings
+  - forum_get_tracking_link
+  - forum_tp_count_discussion_unread_posts
+  - forum_convert_to_roles
+  - forum_tp_get_read_records
+  - forum_tp_get_discussion_read_records
+  - forum_user_enrolled
+  - forum_user_can_view_post
+  - forum_shorten_post
+  - forum_is_subscribed
+  - forum_subscribe
+  - forum_unsubscribe
+  - forum_subscribed_users
+  - forum_is_forcesubscribed
+  - forum_forcesubscribe
+  - forum_get_forcesubscribed
+  - forum_get_subscribed_forums
+  - forum_get_optional_subscribed_forums
+  - forum_get_potential_subscribers
+
 === 3.1 ===
  * The inteface to forum_get_email_message_id() has changed and no longer needs the $host argument.
 
index 5f8d7f7..daa66ce 100644 (file)
@@ -96,4 +96,13 @@ $capabilities = array(
             'manager' => CAP_ALLOW
         )
     ),
+
+    'mod/lesson:view' => array(
+        'captype' => 'read',
+        'contextlevel' => CONTEXT_MODULE,
+        'archetypes' => array(
+            'user' => CAP_ALLOW,
+            'guest' => CAP_ALLOW
+        )
+    ),
 );
index f1d503b..155f404 100644 (file)
@@ -253,6 +253,7 @@ $string['lesson:edit'] = 'Edit a lesson activity';
 $string['lessonformating'] = 'Lesson formatting';
 $string['lesson:manage'] = 'Manage a lesson activity';
 $string['lesson:manageoverrides'] = 'Manage lesson overrides';
+$string['lesson:view'] = 'View lesson activity';
 $string['lesson:viewreports'] = 'View lesson reports';
 $string['lessonname'] = 'Lesson: {$a}';
 $string['lessonmenu'] = 'Lesson menu';
index 6993359..708c987 100644 (file)
@@ -24,7 +24,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2016052300;       // The current module version (Date: YYYYMMDDXX)
+$plugin->version   = 2016071300;     // The current module version (Date: YYYYMMDDXX)
 $plugin->requires  = 2016051900;    // Requires this Moodle version
 $plugin->component = 'mod_lesson'; // Full name of the plugin (used for diagnostics)
 $plugin->cron      = 0;
index 10712fe..0ddebcc 100644 (file)
@@ -198,6 +198,7 @@ $string['downloadinfo'] = 'You can download the complete raw data for this surve
 $string['downloadresults'] = 'Download results';
 $string['downloadtext'] = 'Download data as a plain text file';
 $string['editingasurvey'] = 'Editing a survey';
+$string['errorunabletosavenotes'] = 'An error occurred while saving your notes.';
 $string['eventreportdownloaded'] = 'Survey report downloaded';
 $string['eventreportviewed'] = 'Survey report viewed';
 $string['eventresponsesubmitted'] = 'Survey response submitted';
index dad4a8b..0ca661d 100644 (file)
          if ($notes != '' and confirm_sesskey()) {
              if (survey_get_analysis($survey->id, $user->id)) {
                  if (! survey_update_analysis($survey->id, $user->id, $notes)) {
-                     echo $OUTPUT->notification("An error occurred while saving your notes.  Sorry.");
+                     echo $OUTPUT->notification(get_string("errorunabletosavenotes", "survey"), "notifyproblem");
                  } else {
-                     echo $OUTPUT->notification(get_string("savednotes", "survey"));
+                     echo $OUTPUT->notification(get_string("savednotes", "survey"), "notifysuccess");
                  }
              } else {
                  if (! survey_add_analysis($survey->id, $user->id, $notes)) {
-                     echo $OUTPUT->notification("An error occurred while saving your notes.  Sorry.");
+                     echo $OUTPUT->notification(get_string("errorunabletosavenotes", "survey"), "notifyproblem");
                  } else {
-                     echo $OUTPUT->notification(get_string("savednotes", "survey"));
+                     echo $OUTPUT->notification(get_string("savednotes", "survey"), "notifysuccess");
                  }
              }
          }
index 9e305af..f0488d8 100644 (file)
@@ -44,7 +44,6 @@
         <testsuite name="core_testsuite">
             <directory suffix="_test.php">lib/tests</directory>
             <directory suffix="_test.php">lib/ajax/tests</directory>
-            <directory>lib/password_compat/tests</directory>
         </testsuite>
         <testsuite name="core_form_testsuite">
             <directory suffix="_test.php">lib/form/tests</directory>
index 13d6512..4d60ffa 100644 (file)
Binary files a/question/type/ddmarker/yui/build/moodle-qtype_ddmarker-form/moodle-qtype_ddmarker-form-debug.js and b/question/type/ddmarker/yui/build/moodle-qtype_ddmarker-form/moodle-qtype_ddmarker-form-debug.js differ
index 8247169..41fc7ca 100644 (file)
Binary files a/question/type/ddmarker/yui/build/moodle-qtype_ddmarker-form/moodle-qtype_ddmarker-form-min.js and b/question/type/ddmarker/yui/build/moodle-qtype_ddmarker-form/moodle-qtype_ddmarker-form-min.js differ
index 13d6512..4d60ffa 100644 (file)
Binary files a/question/type/ddmarker/yui/build/moodle-qtype_ddmarker-form/moodle-qtype_ddmarker-form.js and b/question/type/ddmarker/yui/build/moodle-qtype_ddmarker-form/moodle-qtype_ddmarker-form.js differ
index 339da2f..04897cb 100644 (file)
@@ -30,10 +30,14 @@ Y.extend(DDMARKER_FORM, M.qtype_ddmarker.dd_base_class, {
         var tn = Y.one(this.get('topnode'));
         tn.one('div.fcontainer').append(
                 '<div class="ddarea">' +
-                    '<div class="markertexts"></div>' +
-                    '<div class="droparea"></div>' +
-                    '<div class="dropzones"></div>' +
-                    '<div class="grid"></div>' +
+                '<div class="markertexts"></div>' +
+                '<div class="droparea"></div>' +
+                '<div class="dropzones"></div>' +
+                '<ul class="pager">' +
+                '<li><span id="xcoordpreview">X = </span></li>' +
+                '<li><span id="ycoordpreview">Y = </span></li>' +
+                '</ul>' +
+                '<div class="grid"></div>' +
                 '</div>');
         this.doc = this.doc_structure(this);
         this.stop_selector_events();
@@ -42,6 +46,15 @@ Y.extend(DDMARKER_FORM, M.qtype_ddmarker.dd_base_class, {
         Y.later(500, this, this.update_drop_zones, [pendingid], true);
         Y.after(this.load_bg_image, M.form_filepicker, 'callback', this);
         this.load_bg_image();
+
+        var topnode = Y.one(this.get('topnode'));
+        topnode.one('.grid').on('mousemove', function (e) {
+            var img = topnode.one('.dropbackground');
+            var x = Math.round(Number(e.pageX) - img.getX() - 1);
+            var y = Math.round(Number(e.pageY) - img.getY() - 1);
+            topnode.one('#xcoordpreview').setHTML("X = " + x);
+            topnode.one('#ycoordpreview').setHTML("Y = " + y);
+        });
     },
 
     load_bg_image : function() {
@@ -116,7 +129,7 @@ Y.extend(DDMARKER_FORM, M.qtype_ddmarker.dd_base_class, {
         }
     },
     set_options_for_drag_item_selectors : function () {
-        var dragitemsoptions = {'0': ''};
+        var dragitemsoptions = {0: ''};
         for (var i = 1; i <= this.form.get_form_value('noitems', []); i++) {
             var label = this.get_marker_text(i);
             if (label !== "") {
index 05a1ae6..ce2f722 100644 (file)
@@ -316,6 +316,9 @@ class engine extends \core_search\engine {
             $query->setGroupLimit(3);
             $query->setGroupNGroups(true);
             $query->addGroupField('solr_filegroupingid');
+        } else {
+            // Make sure we only get text files, in case the index has pre-existing files.
+            $query->addFilterQuery('type:'.\core_search\manager::TYPE_TEXT);
         }
 
         return $query;
index e3399ee..92a3a0c 100644 (file)
@@ -109,7 +109,7 @@ if ($data) {
 
 if ($errorstr = $search->get_engine()->get_query_error()) {
     echo $OUTPUT->notification(get_string('queryerror', 'search', $errorstr), 'notifyproblem');
-} else if (empty($results) && !empty($data)) {
+} else if (empty($results->totalcount) && !empty($data)) {
     echo $OUTPUT->notification(get_string('noresults', 'search'), 'notifymessage');
 }
 
index 0165535..910bf5c 100644 (file)
@@ -953,7 +953,6 @@ function user_get_user_navigation_info($user, $page, $options = array()) {
  */
 function user_add_password_history($userid, $password) {
     global $CFG, $DB;
-    require_once($CFG->libdir.'/password_compat/lib/password.php');
 
     if (empty($CFG->passwordreuselimit) or $CFG->passwordreuselimit < 0) {
         return;
@@ -992,7 +991,6 @@ function user_add_password_history($userid, $password) {
  */
 function user_is_previously_used_password($userid, $password) {
     global $CFG, $DB;
-    require_once($CFG->libdir.'/password_compat/lib/password.php');
 
     if (empty($CFG->passwordreuselimit) or $CFG->passwordreuselimit < 0) {
         return false;
index 3b3fb14..fae5fd2 100644 (file)
@@ -29,7 +29,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$version  = 2016071400.00;              // YYYYMMDD      = weekly release date of this DEV branch.
+$version  = 2016071400.01;              // YYYYMMDD      = weekly release date of this DEV branch.
                                         //         RR    = release increments - 00 in DEV branches.
                                         //           .XX = incremental changes.