Merge branch 'MDL-50714-master' of git://github.com/andrewnicols/moodle
authorEloy Lafuente (stronk7) <stronk7@moodle.org>
Mon, 24 Aug 2015 22:06:01 +0000 (00:06 +0200)
committerEloy Lafuente (stronk7) <stronk7@moodle.org>
Mon, 24 Aug 2015 22:06:01 +0000 (00:06 +0200)
15 files changed:
auth/cas/auth.php
blocks/messages/block_messages.php
grade/report/singleview/lib.php
lib/ajax/blocks.php
lib/amd/build/templates.min.js
lib/amd/src/templates.js
lib/db/install.xml
lib/db/upgrade.php
lib/outputlib.php
lib/upgrade.txt
login/signup_form.php
mod/lesson/mod_form.php
tag/lib.php
tag/tests/taglib_test.php
version.php

index eaf3f6c..22733b2 100644 (file)
@@ -187,7 +187,7 @@ class auth_plugin_cas extends auth_plugin_ldap {
         }
 
         // If Moodle is configured to use a proxy, phpCAS needs some curl options set.
-        if (!empty($CFG->proxyhost) && !is_proxybypass($this->config->hostname)) {
+        if (!empty($CFG->proxyhost) && !is_proxybypass(phpCAS::getServerLoginURL())) {
             phpCAS::setExtraCurlOption(CURLOPT_PROXY, $CFG->proxyhost);
             if (!empty($CFG->proxyport)) {
                 phpCAS::setExtraCurlOption(CURLOPT_PROXYPORT, $CFG->proxyport);
index c9f93b2..f0ed9b2 100644 (file)
@@ -31,7 +31,9 @@ class block_messages extends block_base {
         global $USER, $CFG, $DB, $OUTPUT;
 
         if (!$CFG->messaging) {
+            $this->content = new stdClass;
             $this->content->text = '';
+            $this->content->footer = '';
             if ($this->page->user_is_editing()) {
                 $this->content->text = get_string('disabled', 'message');
             }
index 5ecb430..cd4fead 100644 (file)
@@ -52,7 +52,7 @@ class gradereport_singleview extends grade_report {
      * @return array List of warnings
      */
     public function process_data($data) {
-        if (has_capability('moodle/grade:manage', $this->context)) {
+        if (has_capability('moodle/grade:edit', $this->context)) {
             return $this->screen->process($data);
         }
     }
index 1cfd346..83625f7 100644 (file)
@@ -63,13 +63,12 @@ switch ($pagetype[0]) {
         $PAGE->set_blocks_editing_capability('moodle/my:manageblocks');
         break;
     case 'user':
-        if ($pagelayout == 'mydashboard') {
-            // If it's not the current user's profile, we need a different capability.
-            if ($PAGE->context->contextlevel == CONTEXT_USER && $PAGE->context->instanceid != $USER->id) {
-                $PAGE->set_blocks_editing_capability('moodle/user:manageblocks');
-            } else {
-                $PAGE->set_blocks_editing_capability('moodle/user:manageownblocks');
-            }
+        if ($pagetype[1] === 'profile' && $PAGE->context->contextlevel == CONTEXT_USER
+                && $PAGE->context->instanceid == $USER->id) {
+            // A user can only move blocks on their own site profile.
+            $PAGE->set_blocks_editing_capability('moodle/user:manageownblocks');
+        } else {
+            $PAGE->set_blocks_editing_capability('moodle/user:manageblocks');
         }
         break;
 }
index 62d34b1..ed4aa32 100644 (file)
Binary files a/lib/amd/build/templates.min.js and b/lib/amd/build/templates.min.js differ
index d03738d..93ed7d1 100644 (file)
@@ -298,6 +298,7 @@ define([ 'core/mustache',
 
         if (cached) {
             deferred.resolve(cached);
+            templateCache[searchKey] = cached;
             return deferred.promise();
         }
 
index fcc226d..b077fb3 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<XMLDB PATH="lib/db" VERSION="20150608" COMMENT="XMLDB file for core Moodle tables"
+<XMLDB PATH="lib/db" VERSION="20150824" COMMENT="XMLDB file for core Moodle tables"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:noNamespaceSchemaLocation="../../lib/xmldb/xmldb.xsd"
 >
         <INDEX NAME="courseid-name" UNIQUE="true" FIELDS="courseid, name"/>
       </INDEXES>
     </TABLE>
-    <TABLE NAME="webdav_locks" COMMENT="Resource locks for WebDAV users">
-      <FIELDS>
-        <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
-        <FIELD NAME="token" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
-        <FIELD NAME="path" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
-        <FIELD NAME="expiry" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
-        <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
-        <FIELD NAME="recursive" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
-        <FIELD NAME="exclusivelock" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
-        <FIELD NAME="created" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
-        <FIELD NAME="modified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
-        <FIELD NAME="owner" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false"/>
-      </FIELDS>
-      <KEYS>
-        <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
-        <KEY NAME="token" TYPE="unique" FIELDS="token"/>
-      </KEYS>
-      <INDEXES>
-        <INDEX NAME="path" UNIQUE="false" FIELDS="path"/>
-        <INDEX NAME="expiry" UNIQUE="false" FIELDS="expiry"/>
-      </INDEXES>
-    </TABLE>
     <TABLE NAME="portfolio_instance" COMMENT="base table (not including config data) for instances of portfolio plugins.">
       <FIELDS>
         <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
index 0adf125..9dcce10 100644 (file)
@@ -4456,5 +4456,19 @@ function xmldb_main_upgrade($oldversion) {
         upgrade_main_savepoint(true, 2015081300.01);
     }
 
+    if ($oldversion < 2015082400.00) {
+
+        // Define table webdav_locks to be dropped.
+        $table = new xmldb_table('webdav_locks');
+
+        // Conditionally launch drop table for webdav_locks.
+        if ($dbman->table_exists($table)) {
+            $dbman->drop_table($table);
+        }
+
+        // Main savepoint reached.
+        upgrade_main_savepoint(true, 2015082400.00);
+    }
+
     return true;
 }
index 35e2f34..87e5c73 100644 (file)
@@ -1660,11 +1660,11 @@ class theme_config {
         global $CFG;
         if ($this->usesvg === null) {
 
-            if (!isset($CFG->svgicons) || !is_bool($CFG->svgicons)) {
+            if (!isset($CFG->svgicons)) {
                 $this->usesvg = core_useragent::supports_svg();
             } else {
                 // Force them on/off depending upon the setting.
-                $this->usesvg = $CFG->svgicons;
+                $this->usesvg = (bool)$CFG->svgicons;
             }
         }
         return $this->usesvg;
index a842ea7..218c03b 100644 (file)
@@ -125,6 +125,7 @@ information provided here is intended especially for developers.
     cohort_get_visible_list()
     enrol_cohort_enrol_all_users()
     enrol_cohort_search_cohorts()
+* The never unused webdav_locks table was dropped.
 
 === 2.9.1 ===
 
index 83ce9fc..281c76d 100644 (file)
@@ -41,24 +41,24 @@ class login_signup_form extends moodleform {
 
         $mform->addElement('text', 'username', get_string('username'), 'maxlength="100" size="12"');
         $mform->setType('username', PARAM_NOTAGS);
-        $mform->addRule('username', get_string('missingusername'), 'required', null, 'server');
+        $mform->addRule('username', get_string('missingusername'), 'required', null, 'client');
 
         if (!empty($CFG->passwordpolicy)){
             $mform->addElement('static', 'passwordpolicyinfo', '', print_password_policy());
         }
         $mform->addElement('passwordunmask', 'password', get_string('password'), 'maxlength="32" size="12"');
         $mform->setType('password', PARAM_RAW);
-        $mform->addRule('password', get_string('missingpassword'), 'required', null, 'server');
+        $mform->addRule('password', get_string('missingpassword'), 'required', null, 'client');
 
         $mform->addElement('header', 'supplyinfo', get_string('supplyinfo'),'');
 
         $mform->addElement('text', 'email', get_string('email'), 'maxlength="100" size="25"');
         $mform->setType('email', PARAM_RAW_TRIMMED);
-        $mform->addRule('email', get_string('missingemail'), 'required', null, 'server');
+        $mform->addRule('email', get_string('missingemail'), 'required', null, 'client');
 
         $mform->addElement('text', 'email2', get_string('emailagain'), 'maxlength="100" size="25"');
         $mform->setType('email2', PARAM_RAW_TRIMMED);
-        $mform->addRule('email2', get_string('missingemail'), 'required', null, 'server');
+        $mform->addRule('email2', get_string('missingemail'), 'required', null, 'client');
 
         $namefields = useredit_get_required_name_fields();
         foreach ($namefields as $field) {
@@ -68,7 +68,7 @@ class login_signup_form extends moodleform {
             if (!get_string_manager()->string_exists($stringid, 'moodle')) {
                 $stringid = 'required';
             }
-            $mform->addRule($field, get_string($stringid), 'required', null, 'server');
+            $mform->addRule($field, get_string($stringid), 'required', null, 'client');
         }
 
         $mform->addElement('text', 'city', get_string('city'), 'maxlength="120" size="20"');
@@ -101,7 +101,7 @@ class login_signup_form extends moodleform {
             $mform->setExpanded('policyagreement');
             $mform->addElement('static', 'policylink', '', '<a href="'.$CFG->sitepolicy.'" onclick="this.target=\'_blank\'">'.get_String('policyagreementclick').'</a>');
             $mform->addElement('checkbox', 'policyagreed', get_string('policyaccept'));
-            $mform->addRule('policyagreed', get_string('policyagree'), 'required', null, 'server');
+            $mform->addRule('policyagreed', get_string('policyagree'), 'required', null, 'client');
         }
 
         // buttons
index 7385e88..5d3eb3a 100644 (file)
@@ -336,6 +336,12 @@ class mod_lesson_mod_form extends moodleform_mod {
     function validation($data, $files) {
         $errors = parent::validation($data, $files);
 
+        // Check open and close times are consistent.
+        if ($data['available'] != 0 && $data['deadline'] != 0 &&
+                $data['deadline'] < $data['available']) {
+            $errors['deadline'] = get_string('closebeforeopen', 'lesson');
+        }
+
         if (!empty($data['usepassword']) && empty($data['password'])) {
             $errors['password'] = get_string('emptypassword', 'lesson');
         }
index 102704c..2ab484b 100644 (file)
@@ -546,10 +546,8 @@ function tag_get_related_tags($tagid, $type=TAG_RELATED_ALL, $limitnum=10) {
 
     if ( $type == TAG_RELATED_ALL || $type == TAG_RELATED_CORRELATED ) {
         //gets the correlated tags
-        $automatic_related_tags = tag_get_correlated($tagid, $limitnum);
-        if (is_array($automatic_related_tags)) {
-            $related_tags = array_merge($related_tags, $automatic_related_tags);
-        }
+        $automatic_related_tags = tag_get_correlated($tagid);
+        $related_tags = array_merge($related_tags, $automatic_related_tags);
     }
 
     // Remove duplicated tags (multiple instances of the same tag).
@@ -1378,13 +1376,21 @@ function tag_get_name($tagids) {
  * Returns the correlated tags of a tag, retrieved from the tag_correlation table. Make sure cron runs, otherwise the table will be
  * empty and this function won't return anything.
  *
+ * Correlated tags are calculated in cron based on existing tag instances.
+ *
+ * This function will return as many entries as there are existing tag instances,
+ * which means that there will be duplicates for each tag.
+ *
+ * If you need only one record for each correlated tag please call:
+ *      tag_get_related_tags($tag_id, TAG_RELATED_CORRELATED);
+ *
  * @package core_tag
  * @access  private
  * @param   int      $tag_id   is a single tag id
- * @param   int      $limitnum this parameter does not appear to have any function???
+ * @param   int      $notused  this argument is no longer used
  * @return  array    an array of tag objects or an empty if no correlated tags are found
  */
-function tag_get_correlated($tag_id, $limitnum=null) {
+function tag_get_correlated($tag_id, $notused = null) {
     global $DB;
 
     $tag_correlation = $DB->get_record('tag_correlation', array('tagid'=>$tag_id));
@@ -1399,12 +1405,7 @@ function tag_get_correlated($tag_id, $limitnum=null) {
         INNER JOIN {tag_instance} ti ON tg.id = ti.tagid
              WHERE tg.id IN ({$tag_correlation->correlatedtags})
           ORDER BY ti.ordering ASC";
-    $result = $DB->get_records_sql($sql);
-    if (!$result) {
-        return array();
-    }
-
-    return $result;
+    return $DB->get_records_sql($sql);
 }
 
 /**
index 1e0bc74..24afde7 100644 (file)
@@ -256,4 +256,169 @@ class core_tag_taglib_testcase extends advanced_testcase {
         $instancecount = $DB->count_records('tag_instance');
         $this->assertEquals(0, $instancecount);
     }
+
+    /**
+     * Test for function tag_compute_correlations() that is part of tag cron
+     */
+    public function test_correlations() {
+        global $DB;
+        $user = $this->getDataGenerator()->create_user();
+        $this->setUser($user);
+
+        $user1 = $this->getDataGenerator()->create_user();
+        $user2 = $this->getDataGenerator()->create_user();
+        $user3 = $this->getDataGenerator()->create_user();
+        $user4 = $this->getDataGenerator()->create_user();
+        $user5 = $this->getDataGenerator()->create_user();
+        $user6 = $this->getDataGenerator()->create_user();
+
+        // Several records have both 'cat' and 'cats' tags attached to them.
+        // This will make those tags automatically correlated.
+        // Same with 'dog', 'dogs' and 'puppy.
+        tag_set('user', $user1->id, array('cat', 'cats'),
+                'core', context_user::instance($user1->id)->id);
+        tag_set('user', $user2->id, array('cat', 'cats', 'kitten'),
+                'core', context_user::instance($user2->id)->id);
+        tag_set('user', $user3->id, array('cat', 'cats'),
+                'core', context_user::instance($user3->id)->id);
+        tag_set('user', $user4->id, array('dog', 'dogs', 'puppy'),
+                'core', context_user::instance($user4->id)->id);
+        tag_set('user', $user5->id, array('dog', 'dogs', 'puppy'),
+                'core', context_user::instance($user5->id)->id);
+        tag_set('user', $user6->id, array('dog', 'dogs', 'puppy'),
+                'core', context_user::instance($user6->id)->id);
+
+        $tags = tag_get_id(array('cat', 'cats', 'dog', 'dogs', 'kitten', 'puppy'));
+
+        // Add manual relation between tags 'cat' and 'kitten'.
+        tag_set('tag', $tags['cat'], array('kitten'), 'core', context_system::instance()->id);
+
+        tag_compute_correlations();
+
+        $this->assertEquals($tags['cats'],
+                $DB->get_field_select('tag_correlation', 'correlatedtags',
+                'tagid = ?', array($tags['cat'])));
+        $this->assertEquals($tags['cat'],
+                $DB->get_field_select('tag_correlation', 'correlatedtags',
+                'tagid = ?', array($tags['cats'])));
+        $this->assertEquals($tags['dogs'] . ',' . $tags['puppy'],
+                $DB->get_field_select('tag_correlation', 'correlatedtags',
+                'tagid = ?', array($tags['dog'])));
+        $this->assertEquals($tags['dog'] . ',' . $tags['puppy'],
+                $DB->get_field_select('tag_correlation', 'correlatedtags',
+                'tagid = ?', array($tags['dogs'])));
+        $this->assertEquals($tags['dog'] . ',' . $tags['dogs'],
+                $DB->get_field_select('tag_correlation', 'correlatedtags',
+                'tagid = ?', array($tags['puppy'])));
+
+        // Make sure tag_get_correlated() returns 'cats' as the only correlated tag to the 'cat'.
+        $correlatedtags = array_values(tag_get_correlated($tags['cat']));
+        $this->assertCount(3, $correlatedtags); // This will return all existing instances but they all point to the same tag.
+        $this->assertEquals('cats', $correlatedtags[0]->rawname);
+        $this->assertEquals('cats', $correlatedtags[1]->rawname);
+        $this->assertEquals('cats', $correlatedtags[2]->rawname);
+
+        $correlatedtags = array_values(tag_get_related_tags($tags['cat'], TAG_RELATED_CORRELATED));
+        $this->assertCount(1, $correlatedtags); // Duplicates are filtered out here.
+        $this->assertEquals('cats', $correlatedtags[0]->rawname);
+
+        // Make sure tag_get_correlated() returns 'dogs' and 'puppy' as the correlated tags to the 'dog'.
+        $correlatedtags = array_values(tag_get_correlated($tags['dog']));
+        $this->assertCount(6, $correlatedtags); // 2 tags times 3 instances.
+
+        $correlatedtags = array_values(tag_get_related_tags($tags['dog'], TAG_RELATED_CORRELATED));
+        $this->assertCount(2, $correlatedtags);
+        $this->assertEquals('dogs', $correlatedtags[0]->rawname);
+        $this->assertEquals('puppy', $correlatedtags[1]->rawname);
+
+        // Function tag_get_related_tags() with default argument will return both related and correlated tags.
+        $relatedtags = array_values(tag_get_related_tags($tags['cat']));
+        $this->assertCount(2, $relatedtags);
+        $this->assertEquals('kitten', $relatedtags[0]->rawname);
+        $this->assertEquals('cats', $relatedtags[1]->rawname);
+
+        // If we then manually set 'cat' and 'cats' as related, tag_get_related_tags() will filter out duplicates.
+        tag_set('tag', $tags['cat'], array('kitten', 'cats'), 'core', context_system::instance()->id);
+
+        $relatedtags = array_values(tag_get_related_tags($tags['cat']));
+        $this->assertCount(2, $relatedtags);
+        $this->assertEquals('kitten', $relatedtags[0]->rawname);
+        $this->assertEquals('cats', $relatedtags[1]->rawname);
+
+        // Make sure tag_get_correlated() and tag_get_tags() return the same set of fields.
+        $relatedtags = tag_get_tags('tag', $tags['cat']);
+        $relatedtag = reset($relatedtags);
+        $correlatedtags = tag_get_correlated($tags['cat']);
+        $correlatedtag = reset($correlatedtags);
+        $this->assertEquals(array_keys((array)$relatedtag), array_keys((array)$correlatedtag));
+    }
+
+    /**
+     * Test for function tag_cleanup() that is part of tag cron
+     */
+    public function test_cleanup() {
+        global $DB;
+        $user = $this->getDataGenerator()->create_user();
+
+        // Setting tags will create non-official tags 'cat', 'dog' and 'fish'.
+        tag_set('user', $user->id, array('cat', 'dog', 'fish'),
+                'core', context_user::instance($user->id)->id);
+
+        $this->assertTrue($DB->record_exists('tag', array('name' => 'cat')));
+        $this->assertTrue($DB->record_exists('tag', array('name' => 'dog')));
+        $this->assertTrue($DB->record_exists('tag', array('name' => 'fish')));
+
+        // Make tag 'dog' official.
+        $dogtag = tag_get('name', 'dog');
+        $fishtag = tag_get('name', 'fish');
+        tag_type_set($dogtag->id, 'official');
+
+        // Manually remove the instances pointing on tags 'dog' and 'fish'.
+        $DB->execute('DELETE FROM {tag_instance} WHERE tagid in (?,?)', array($dogtag->id, $fishtag->id));
+
+        // Call tag_cleanup().
+        tag_cleanup();
+
+        // Tag 'cat' is still present because it's used. Tag 'dog' is present because it's official.
+        // Tag 'fish' was removed because it is not official and it is no longer used by anybody.
+        $this->assertTrue($DB->record_exists('tag', array('name' => 'cat')));
+        $this->assertTrue($DB->record_exists('tag', array('name' => 'dog')));
+        $this->assertFalse($DB->record_exists('tag', array('name' => 'fish')));
+
+        // Delete user without using API function.
+        $DB->update_record('user', array('id' => $user->id, 'deleted' => 1));
+
+        // Call tag_cleanup().
+        tag_cleanup();
+
+        // Tag 'cat' was now deleted too.
+        $this->assertFalse($DB->record_exists('tag', array('name' => 'cat')));
+
+        // Assign tag to non-existing record. Make sure tag was created in the DB.
+        tag_set('course', 1231231, array('bird'), 'core', context_system::instance()->id);
+        $this->assertTrue($DB->record_exists('tag', array('name' => 'bird')));
+
+        // Call tag_cleanup().
+        tag_cleanup();
+
+        // Tag 'bird' was now deleted because the related record does not exist in the DB.
+        $this->assertFalse($DB->record_exists('tag', array('name' => 'bird')));
+
+        // Now we have a tag instance pointing on 'sometag' tag.
+        $user = $this->getDataGenerator()->create_user();
+        tag_set('user', $user->id, array('sometag'),
+                'core', context_user::instance($user->id)->id);
+        $sometag = tag_get('name', 'sometag');
+
+        $this->assertTrue($DB->record_exists('tag_instance', array('tagid' => $sometag->id)));
+
+        // Some hacker removes the tag without using API.
+        $DB->delete_records('tag', array('id' => $sometag->id));
+
+        // Call tag_cleanup().
+        tag_cleanup();
+
+        // The tag instances were also removed.
+        $this->assertFalse($DB->record_exists('tag_instance', array('tagid' => $sometag->id)));
+    }
 }
index 822072b..048e9c2 100644 (file)
@@ -29,7 +29,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$version  = 2015082000.00;              // YYYYMMDD      = weekly release date of this DEV branch.
+$version  = 2015082400.00;              // YYYYMMDD      = weekly release date of this DEV branch.
                                         //         RR    = release increments - 00 in DEV branches.
                                         //           .XX = incremental changes.