Merge branch 'MDL-39607-master' of git://github.com/FMCorz/moodle
authorDan Poltawski <dan@moodle.com>
Fri, 18 Apr 2014 03:10:39 +0000 (11:10 +0800)
committerDan Poltawski <dan@moodle.com>
Fri, 18 Apr 2014 03:10:39 +0000 (11:10 +0800)
75 files changed:
admin/tool/log/classes/helper/reader.php
admin/tool/log/store/legacy/db/access.php [deleted file]
admin/tool/log/store/legacy/lang/en/logstore_legacy.php
admin/tool/log/store/legacy/version.php
admin/tool/log/store/standard/db/access.php [deleted file]
admin/tool/log/store/standard/lang/en/logstore_standard.php
admin/tool/log/store/standard/version.php
availability/classes/frontend.php
availability/classes/info.php
availability/condition/group/classes/frontend.php
availability/condition/grouping/classes/frontend.php
availability/yui/build/moodle-core_availability-form/moodle-core_availability-form-debug.js
availability/yui/build/moodle-core_availability-form/moodle-core_availability-form-min.js
availability/yui/build/moodle-core_availability-form/moodle-core_availability-form.js
availability/yui/src/form/js/form.js
backup/util/ui/tests/behat/behat_backup.php
blocks/mentees/block_mentees.php
lib/alfresco/README_MOODLE.txt
lib/alfresco/Service/WebService/AlfrescoWebService.php
lib/behat/behat_field_manager.php
lib/behat/classes/behat_context_helper.php [new file with mode: 0644]
lib/behat/classes/behat_selectors.php
lib/behat/form_field/behat_form_field.php
lib/behat/form_field/behat_form_filemanager.php [new file with mode: 0644]
lib/classes/log/reader.php
lib/db/install.xml
lib/db/upgrade.php
lib/outputrenderers.php
lib/tests/behat/behat_forms.php
lib/tests/behat/behat_hooks.php
lib/yui/build/moodle-core-notification-exception/moodle-core-notification-exception-debug.js
lib/yui/build/moodle-core-notification-exception/moodle-core-notification-exception-min.js
lib/yui/build/moodle-core-notification-exception/moodle-core-notification-exception.js
lib/yui/src/notification/js/exception.js
mod/assign/classes/event/assessable_submitted.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/editor.js
mod/assign/submission/file/locallib.php
mod/assign/submission/onlinetext/locallib.php
mod/book/classes/event/chapter_created.php
mod/book/classes/event/chapter_deleted.php
mod/book/classes/event/chapter_updated.php
mod/book/classes/event/chapter_viewed.php
mod/book/classes/event/course_module_instance_list_viewed.php
mod/book/classes/event/course_module_viewed.php
mod/book/delete.php
mod/book/edit.php
mod/book/index.php
mod/book/move.php
mod/book/show.php
mod/book/tests/events_test.php
mod/book/tool/exportimscp/classes/event/book_exported.php
mod/book/tool/exportimscp/index.php
mod/book/tool/exportimscp/tests/events_test.php
mod/book/tool/importhtml/locallib.php
mod/book/tool/print/classes/event/book_printed.php
mod/book/tool/print/classes/event/chapter_printed.php
mod/book/tool/print/index.php
mod/book/tool/print/tests/events_test.php
mod/book/upgrade.txt [new file with mode: 0644]
mod/book/view.php
mod/forum/tests/behat/add_forum.feature
mod/quiz/overridedelete.php
mod/survey/lib.php
report/log/classes/table_log.php
report/log/locallib.php
repository/alfresco/lang/en/repository_alfresco.php
repository/tests/behat/behat_filepicker.php
repository/upload/tests/behat/behat_repository_upload.php
user/filters/courserole.php
user/profile/definelib.php
user/profile/index.php
version.php

index 036f874..4d8b4bf 100644 (file)
@@ -61,15 +61,4 @@ trait reader {
         }
         return $this->store;
     }
-
-    /**
-     * If the current user can access current store or not.
-     *
-     * @param \context $context
-     *
-     * @return bool
-     */
-    public function can_access(\context $context) {
-        return has_capability('logstore/' . $this->store . ':read', $context);
-    }
 }
diff --git a/admin/tool/log/store/legacy/db/access.php b/admin/tool/log/store/legacy/db/access.php
deleted file mode 100644 (file)
index 1d32982..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Defines the capabilities used by standard log store.
- *
- * @package    logstore_legacy
- * @copyright  2013 Petr Skoda {@link http://skodak.org}
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die();
-
-$capabilities = array(
-    'logstore/legacy:read' => array(
-        'riskbitmask'  => RISK_PERSONAL,
-        'captype'      => 'read',
-        'contextlevel' => CONTEXT_MODULE,
-        'archetypes'   => array(
-            'manager'        => CAP_ALLOW,
-            'editingteacher' => CAP_ALLOW,
-            'teacher'        => CAP_ALLOW,
-        ),
-    ),
-);
index 58a54d4..9e39406 100644 (file)
@@ -23,7 +23,6 @@
  */
 
 $string['event_legacy_logged'] = 'Legacy event logged';
-$string['legacy:read'] = 'Read logs';
 $string['loglegacy'] = 'Log legacy data';
 $string['loglegacy_help'] = 'This plugin records log data to the legacy log table (mdl_log). This functionality has been replaced by newer, richer and more efficient logging plugins, so you should only run this plugin if you have old custom reports that directly query the old log table. Writing to the legacy logs will increase load, so it is recommended that you disable this plugin for performance reasons when it is not needed.';
 $string['pluginname'] = 'Legacy log';
index 95f1d42..f9b48d3 100644 (file)
@@ -24,6 +24,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version = 2014031300; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->version = 2014041700; // The current plugin version (Date: YYYYMMDDXX).
 $plugin->requires = 2014031200; // Requires this Moodle version.
 $plugin->component = 'logstore_legacy'; // Full name of the plugin (used for diagnostics).
diff --git a/admin/tool/log/store/standard/db/access.php b/admin/tool/log/store/standard/db/access.php
deleted file mode 100644 (file)
index 774489b..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Defines the capabilities used by standard log store.
- *
- * @package    logstore_standard
- * @copyright  2013 Petr Skoda {@link http://skodak.org}
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die();
-
-$capabilities = array(
-    'logstore/standard:read' => array(
-        'riskbitmask'  => RISK_PERSONAL,
-        'captype'      => 'read',
-        'contextlevel' => CONTEXT_MODULE,
-        'archetypes'   => array(
-            'manager'        => CAP_ALLOW,
-            'editingteacher' => CAP_ALLOW,
-            'teacher'        => CAP_ALLOW,
-        ),
-    ),
-);
index c81fac5..2e2c2a7 100644 (file)
@@ -25,5 +25,4 @@
 $string['buffersize'] = 'Write buffer size';
 $string['pluginname'] = 'Standard log';
 $string['pluginname_desc'] = 'A log plugin stores log entries in a Moodle database table.';
-$string['standard:read'] = 'Read logs';
 $string['taskcleanup'] = 'Log table cleanup';
index 32ba411..9fb2180 100644 (file)
@@ -24,6 +24,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version = 2014041500; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->version = 2014041700; // The current plugin version (Date: YYYYMMDDXX).
 $plugin->requires = 2014031200; // Requires this Moodle version.
 $plugin->component = 'logstore_standard'; // Full name of the plugin (used for diagnostics).
index f2a7276..ea7b012 100644 (file)
@@ -172,7 +172,7 @@ abstract class frontend {
         $decoded = json_decode($data['availabilityconditionsjson']);
         if (!$decoded) {
             // This shouldn't be possible.
-            throw new coding_exception('Invalid JSON from availabilityconditionsjson field');
+            throw new \coding_exception('Invalid JSON from availabilityconditionsjson field');
         }
         if (!empty($decoded->errors)) {
             $error = '';
index 965e552..a335d38 100644 (file)
@@ -193,12 +193,7 @@ abstract class info {
             $tree = $this->get_availability_tree();
             $result = $tree->check_available(false, $this, $grabthelot, $userid);
         } catch (\coding_exception $e) {
-            // We catch the message because it causes fatal problems in most of
-            // the GUI if this exception gets thrown (you can't edit the
-            // activity to fix it). Obviously it should never happen anyway, but
-            // just in case.
-            debugging('Error processing availability data for &lsquo;' .
-                    $this->get_thing_name() . '&rsquo;: ' . s($e->a), DEBUG_DEVELOPER);
+            $this->warn_about_invalid_availability($e);
             $this->modinfo = null;
             return false;
         }
@@ -237,7 +232,12 @@ abstract class info {
         if (is_null($this->availability)) {
             return true;
         } else {
-            return $this->get_availability_tree()->is_available_for_all();
+            try {
+                return $this->get_availability_tree()->is_available_for_all();
+            } catch (\coding_exception $e) {
+                $this->warn_about_invalid_availability($e);
+                return false;
+            }
         }
     }
 
@@ -273,13 +273,32 @@ abstract class info {
             $this->modinfo = null;
             return $result;
         } catch (\coding_exception $e) {
-            // Again we catch the message to avoid problems in GUI.
-            debugging('Error processing availability data for &lsquo;' .
-                    $this->get_thing_name() . '&rsquo;: ' . s($e->a), DEBUG_DEVELOPER);
+            $this->warn_about_invalid_availability($e);
             return false;
         }
     }
 
+    /**
+     * In some places we catch coding_exception because if a bug happens, it
+     * would be fatal for the course page GUI; instead we just show a developer
+     * debug message.
+     *
+     * @param \coding_exception $e Exception that occurred
+     */
+    protected function warn_about_invalid_availability(\coding_exception $e) {
+        $name = $this->get_thing_name();
+        // If it occurs while building modinfo based on somebody calling $cm->name,
+        // we can't get $cm->name, and this line will cause a warning.
+        $htmlname = @$this->format_info($name, $this->course);
+        if ($htmlname === '') {
+            // So instead use the numbers (cmid) from the tag.
+            $htmlname = preg_replace('~[^0-9]~', '', $name);
+        }
+        $info = 'Error processing availability data for &lsquo;' . $htmlname
+                 . '&rsquo;: ' . s($e->a);
+        debugging($info, DEBUG_DEVELOPER);
+    }
+
     /**
      * Called during restore (near end of restore). Updates any necessary ids
      * and writes the updated tree to the database. May output warnings if
index 31f1167..499d1a4 100644 (file)
@@ -79,9 +79,11 @@ class frontend extends \core_availability\frontend {
             \section_info $section = null) {
         global $CFG;
 
-        // Group option can be used on sections, and on modules but only
-        // if groupmembersonly is turned off. (To avoid confusion.)
-        if (!is_null($cm) && $CFG->enablegroupmembersonly) {
+        // If groupmembersonly is turned on, then you can only add group
+        // restrictions on sections (which don't use groupmembersonly) and
+        // not on modules. This is to avoid confusion - otherwise
+        // there would be two ways to add restrictions based on groups.
+        if (is_null($section) && $CFG->enablegroupmembersonly) {
             return false;
         }
 
index 9dfb5e5..456fb26 100644 (file)
@@ -74,9 +74,11 @@ class frontend extends \core_availability\frontend {
             \section_info $section = null) {
         global $CFG, $DB;
 
-        // Grouping option can be used on sections, and on modules but only
-        // if groupmembersonly is turned off. (To avoid confusion.)
-        if (!is_null($cm) && $CFG->enablegroupmembersonly) {
+        // If groupmembersonly is turned on, then you can only add group
+        // restrictions on sections (which don't use groupmembersonly) and
+        // not on modules. This is to avoid confusion - otherwise
+        // there would be two ways to add restrictions based on groups.
+        if (is_null($section) && $CFG->enablegroupmembersonly) {
             return false;
         }
 
index 4fec1b2..229f79a 100644 (file)
Binary files a/availability/yui/build/moodle-core_availability-form/moodle-core_availability-form-debug.js and b/availability/yui/build/moodle-core_availability-form/moodle-core_availability-form-debug.js differ
index aeb5c1e..60ede80 100644 (file)
Binary files a/availability/yui/build/moodle-core_availability-form/moodle-core_availability-form-min.js and b/availability/yui/build/moodle-core_availability-form/moodle-core_availability-form-min.js differ
index 4fec1b2..229f79a 100644 (file)
Binary files a/availability/yui/build/moodle-core_availability-form/moodle-core_availability-form.js and b/availability/yui/build/moodle-core_availability-form/moodle-core_availability-form.js differ
index 051c2c9..85fc922 100644 (file)
@@ -94,12 +94,16 @@ M.core_availability.form = {
 
         // Get top-level tree as JSON.
         var value = this.field.get('value');
-        if (value === '') {
-            this.rootList = new M.core_availability.List(null, true);
-        } else {
-            var data = Y.JSON.parse(value);
-            this.rootList = new M.core_availability.List(data, true);
+        var data = null;
+        if (value !== '') {
+            try {
+                data = Y.JSON.parse(value);
+            } catch(x) {
+                // If the JSON data is not valid, treat it as empty.
+                this.field.set('value', '');
+            }
         }
+        this.rootList = new M.core_availability.List(data, true);
         this.mainDiv.appendChild(this.rootList.node);
 
         // Update JSON value after loading (to reflect any changes that need
index 463f278..21f82f8 100644 (file)
@@ -349,9 +349,7 @@ class behat_backup extends behat_base {
         foreach ($datahash as $locator => $value) {
 
             try {
-                // Using $this->find* to enforce stability over speed.
-                $fieldnode = $this->find_field($locator);
-                $field = behat_field_manager::get_form_field($fieldnode, $this->getSession());
+                $field = behat_field_manager::get_form_field_from_label($locator, $this);
                 $field->set_value($value);
 
             } catch (ElementNotFoundException $e) {
index bbb76a1..0c2ff91 100644 (file)
@@ -50,7 +50,8 @@ class block_mentees extends block_base {
         $this->content = new stdClass();
 
         // get all the mentees, i.e. users you have a direct assignment to
-        if ($usercontexts = $DB->get_records_sql("SELECT c.instanceid, c.instanceid, u.firstname, u.lastname
+        $allusernames = get_all_user_name_fields(true, 'u');
+        if ($usercontexts = $DB->get_records_sql("SELECT c.instanceid, c.instanceid, $allusernames
                                                     FROM {role_assignments} ra, {context} c, {user} u
                                                    WHERE ra.userid = ?
                                                          AND ra.contextid = c.id
index 83179f0..aaf6400 100644 (file)
@@ -34,6 +34,8 @@
     - AlfrescoWebService::__soapCall() arguments do not match SoapClient::__soapCall()
     - AlfrescoWebService::__doRequest() arguments do not match SoapClient::__soapCall()
 
+6. Apply the changes from MDL-41975 in regard with the timestamp
+
 == Alfresco PHP Library ==
 
 Installation and developer documentation for the Alfresco PHP Library can be found on the Alfresco Wiki.
index b549755..898dcdf 100644 (file)
@@ -79,8 +79,8 @@ class AlfrescoWebService extends SoapClient
 \r
          // Construct Timestamp Header\r
          $timeStamp = $dom->createElementNS($this->wsUtilityNS, "Timestamp");\r
-         $createdDate = date("Y-m-d\TH:i:s\Z", mktime(date("H")+24, date("i"), date("s"), date("m"), date("d"), date("Y")));\r
-         $expiresDate = date("Y-m-d\TH:i:s\Z", mktime(date("H")+25, date("i"), date("s"), date("m"), date("d"), date("Y")));\r
+         $createdDate = gmdate("Y-m-d\TH:i:s\Z", gmmktime(gmdate("H"), gmdate("i"), gmdate("s"), gmdate("m"), gmdate("d"), gmdate("Y")));\r
+         $expiresDate = gmdate("Y-m-d\TH:i:s\Z", gmmktime(gmdate("H")+1, gmdate("i"), gmdate("s"), gmdate("m"), gmdate("d"), gmdate("Y")));\r
          $created = new DOMElement("Created", $createdDate, $this->wsUtilityNS);\r
          $expires = new DOMElement("Expires", $expiresDate, $this->wsUtilityNS);\r
          $timeStamp->appendChild($created);\r
index 6bbc39e..d2e8c7f 100644 (file)
@@ -26,7 +26,9 @@
 // NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
 
 use Behat\Mink\Session as Session,
-    Behat\Mink\Element\NodeElement as NodeElement;
+    Behat\Mink\Element\NodeElement as NodeElement,
+    Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException,
+    Behat\MinkExtension\Context\RawMinkContext as RawMinkContext;
 
 /**
  * Helper to interact with form fields.
@@ -38,6 +40,30 @@ use Behat\Mink\Session as Session,
  */
 class behat_field_manager {
 
+    /**
+     * Gets an instance of the form field from it's label
+     *
+     * @param string $label
+     * @param RawMinkContext $context
+     * @return behat_form_field
+     */
+    public static function get_form_field_from_label($label, RawMinkContext $context) {
+
+        // There are moodle form elements that are not directly related with
+        // a basic HTML form field, we should also take care of them.
+        try {
+            // The DOM node.
+            $fieldnode = $context->find_field($label);
+        } catch (ElementNotFoundException $e) {
+
+            // Looking for labels that points to filemanagers.
+            $fieldnode = $context->find_filemanager($label);
+        }
+
+        // The behat field manager.
+        return self::get_form_field($fieldnode, $context->getSession());
+    }
+
     /**
      * Gets an instance of the form field.
      *
@@ -217,6 +243,7 @@ class behat_field_manager {
      * @todo MDL-XXXXX This will be deleted in Moodle 2.8
      * @see behat_field_manager::get_form_field()
      * @param NodeElement $fieldnode
+     * @param string $locator
      * @param Session $session The behat browser session
      * @return behat_form_field
      */
@@ -237,6 +264,7 @@ class behat_field_manager {
      * @todo MDL-XXXXX This will be deleted in Moodle 2.8
      * @see behat_field_manager::get_field_node_type()
      * @param NodeElement $fieldnode The current node.
+     * @param string $locator
      * @param Session $session The behat browser session
      * @return mixed A NodeElement if we continue looking for the element type and String or false when we are done.
      */
diff --git a/lib/behat/classes/behat_context_helper.php b/lib/behat/classes/behat_context_helper.php
new file mode 100644 (file)
index 0000000..cdeea57
--- /dev/null
@@ -0,0 +1,111 @@
+<?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/>.
+
+/**
+ * Helper to initialise behat contexts from moodle code.
+ *
+ * @package    core
+ * @category   test
+ * @copyright  2014 David Monllaó
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
+
+use Behat\Mink\Session as Session,
+    Behat\Mink\Mink as Mink;
+
+/**
+ * Helper to get behat contexts.
+ *
+ * @package    core
+ * @category   test
+ * @copyright  2014 David Monllaó
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class behat_context_helper {
+
+    /**
+     * List of already initialized contexts.
+     *
+     * @var array
+     */
+    protected static $contexts = array();
+
+    /**
+     * @var Mink.
+     */
+    protected static $mink = false;
+
+    /**
+     * Sets the browser session.
+     *
+     * @param Session $session
+     * @return void
+     */
+    public static function set_session(Session $session) {
+
+        // Set mink to be able to init a context.
+        self::$mink = new Mink(array('mink' => $session));
+        self::$mink->setDefaultSessionName('mink');
+    }
+
+    /**
+     * Gets the required context.
+     *
+     * Getting a context you get access to all the steps
+     * that uses direct API calls; steps returning step chains
+     * can not be executed like this.
+     *
+     * @throws coding_exception
+     * @param string $classname Context identifier (the class name).
+     * @return behat_base
+     */
+    public static function get($classname) {
+
+        if (!self::init_context($classname)) {
+            throw coding_exception('The required "' . $classname . '" class does not exist');
+        }
+
+        return self::$contexts[$classname];
+    }
+
+    /**
+     * Initializes the required context.
+     *
+     * @throws coding_exception
+     * @param string $classname
+     * @return bool
+     */
+    protected static function init_context($classname) {
+
+        if (!empty(self::$contexts[$classname])) {
+            return true;
+        }
+
+        if (!class_exists($classname)) {
+            return false;
+        }
+
+        $instance = new $classname();
+        $instance->setMink(self::$mink);
+
+        self::$contexts[$classname] = $instance;
+
+        return true;
+    }
+
+}
index a46930e..ddacab4 100644 (file)
@@ -64,6 +64,7 @@ class behat_selectors {
         'checkbox' => 'checkbox',
         'radio' => 'radio',
         'file' => 'file',
+        'filemanager' => 'filemanager',
         'optgroup' => 'optgroup',
         'option' => 'option',
         'table' => 'table',
@@ -102,6 +103,9 @@ XPATH
 XPATH
         , 'table_row' => <<<XPATH
 .//tr[contains(normalize-space(.), %locator%)]
+XPATH
+        , 'filemanager' => <<<XPATH
+//div[contains(concat(' ', normalize-space(@class), ' '), ' ffilemanager ')]/descendant::input[@id = //label[contains(normalize-space(string(.)), %locator%)]/@for]
 XPATH
     );
 
index 4d3f1b4..c1abb1a 100644 (file)
@@ -50,6 +50,12 @@ class behat_form_field {
      */
     protected $field;
 
+    /**
+     * @var string The field's locator.
+     */
+    protected $fieldlocator = false;
+
+
     /**
      * General constructor with the node and the session to interact with.
      *
@@ -171,4 +177,63 @@ class behat_form_field {
         }
         return true;
     }
+
+    /**
+     * Gets the field locator.
+     *
+     * Defaults to the field label but you can
+     * specify other locators if you are interested.
+     *
+     * Public visibility as in most cases will be hard to
+     * use this method in a generic way, as fields can
+     * be selected using multiple ways (label, id, name...).
+     *
+     * @throws coding_exception
+     * @param string $locatortype
+     * @return string
+     */
+    protected function get_field_locator($locatortype = false) {
+
+        if (!empty($this->fieldlocator)) {
+            return $this->fieldlocator;
+        }
+
+        $fieldid = $this->field->getAttribute('id');
+
+        // Defaults to label.
+        if ($locatortype == 'label' || $locatortype == false) {
+
+            $labelnode = $this->session->getPage()->find('xpath', '//label[@for="' . $fieldid . '"]');
+
+            // Exception only if $locatortype was specified.
+            if (!$labelnode && $locatortype == 'label') {
+                throw new coding_exception('Field with "' . $fieldid . '" id does not have a label.');
+            }
+
+            $this->fieldlocator = $labelnode->getText();
+        }
+
+        // Let's look for the name as a second option (more popular than
+        // id's when pointing to fields).
+        if (($locatortype == 'name' || $locatortype == false) &&
+                empty($this->fieldlocator)) {
+
+            $name = $this->field->getAttribute('name');
+
+            // Exception only if $locatortype was specified.
+            if (!$name && $locatortype == 'name') {
+                throw new coding_exception('Field with "' . $fieldid . '" id does not have a name attribute.');
+            }
+
+            $this->fieldlocator = $name;
+        }
+
+        // Otherwise returns the id if no specific locator type was provided.
+        if (empty($this->fieldlocator)) {
+            $this->fieldlocator = $fieldid;
+        }
+
+        return $this->fieldlocator;
+    }
+
 }
diff --git a/lib/behat/form_field/behat_form_filemanager.php b/lib/behat/form_field/behat_form_filemanager.php
new file mode 100644 (file)
index 0000000..055efbf
--- /dev/null
@@ -0,0 +1,122 @@
+<?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/>.
+
+/**
+ * File manager form element.
+ *
+ * @package    core_form
+ * @category   test
+ * @copyright  2014 David Monllaó
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
+
+require_once(__DIR__  . '/behat_form_field.php');
+
+/**
+ * File manager form field.
+ *
+ * Simple filemanager field manager to allow
+ * forms to be filled using TableNodes. It only
+ * adds files and checks the field contents in the
+ * root directory. If you want to run complex actions
+ * that involves subdirectories or other repositories
+ * than 'Upload a file' you should use steps related with
+ * behat_filepicker::i_add_file_from_repository_to_filemanager
+ * this is intended to be used with multi-field
+ *
+ * This field manager allows you to:
+ * - Get: A comma-separated list of the root directory
+ *   file names, including folders.
+ * - Set: Add a file, in case you want to add more than
+ *     one file you can always set two table rows using
+ *     the same locator.
+ * - Match: A comma-separated list of file names.
+ *
+ * @package    core_form
+ * @category   test
+ * @copyright  2014 David Monllaó
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class behat_form_filemanager extends behat_form_field {
+
+    /**
+     * Gets the value.
+     *
+     * @return string A comma-separated list of the root directory file names.
+     */
+    public function get_value() {
+
+        // Wait until DOM and JS is ready.
+        $this->session->wait(behat_base::TIMEOUT, behat_base::PAGE_READY_JS);
+
+        // Get the label to restrict the files to this single form field.
+        $fieldlabel = $this->get_field_locator();
+
+        // Get the name of the current directory elements.
+        $xpath = "//label[contains(., '" . $fieldlabel . "')]" .
+            "/ancestor::div[contains(concat(' ', normalize-space(@class), ' '), ' fitemtitle ')]" .
+            "/following-sibling::div[contains(concat(' ', normalize-space(@class), ' '), ' ffilemanager ')]" .
+            "/descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' fp-filename ')]";
+
+        // We don't need to wait here, also we don't have access to protected
+        // contexts find* methods.
+        $files = $this->session->getPage()->findAll('xpath', $xpath);
+
+        if (!$files) {
+            return '';
+        }
+
+        $filenames = array();
+        foreach ($files as $filenode) {
+            $filenames[] = $filenode->getText();
+        }
+
+        return implode(',', $filenames);
+    }
+
+    /**
+     * Sets the field value.
+     *
+     * @param string $value
+     * @return void
+     */
+    public function set_value($value) {
+
+        // Getting the filemanager label from the DOM.
+        $fieldlabel = $this->get_field_locator();
+
+        // Getting the filepicker context and using the step definition
+        // to upload the requested file.
+        $uploadcontext = behat_context_helper::get('behat_repository_upload');
+        $uploadcontext->i_upload_file_to_filemanager($value, $fieldlabel);
+    }
+
+    /**
+     * Matches the provided filename/s against the current field value.
+     *
+     * If the filemanager contains more than one file the $expectedvalue
+     * value should include all the file names separating them by comma.
+     *
+     * @param string $expectedvalue
+     * @return bool The provided value matches the field value?
+     */
+    public function matches($expectedvalue) {
+        return $this->text_matches($expectedvalue);
+    }
+
+}
index 53a093c..4693358 100644 (file)
@@ -42,13 +42,6 @@ interface reader {
      */
     public function get_description();
 
-    /**
-     * Can the current user access this store?
-     * @param \context $context
-     * @return bool
-     */
-    public function can_access(\context $context);
-
     /**
      * Are the new events appearing in the reader?
      *
index f47c8ce..518e851 100644 (file)
         <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
       </KEYS>
       <INDEXES>
-        <INDEX NAME="userfieldidx" UNIQUE="false" FIELDS="userid, fieldid"/>
+        <INDEX NAME="userfieldidx" UNIQUE="true" FIELDS="userid, fieldid"/>
       </INDEXES>
     </TABLE>
     <TABLE NAME="question_categories" COMMENT="Categories are for grouping questions">
index d753199..4443c9b 100644 (file)
@@ -3592,5 +3592,37 @@ function xmldb_main_upgrade($oldversion) {
         upgrade_main_savepoint(true, 2014040800.00);
     }
 
+    if ($oldversion < 2014041500.01) {
+
+        $table = new xmldb_table('user_info_data');
+
+        $sql = 'SELECT DISTINCT info.id
+                  FROM {user_info_data} info
+            INNER JOIN {user_info_data} older
+                    ON info.fieldid = older.fieldid
+                   AND info.userid = older.userid
+                   AND older.id < info.id';
+        $transaction = $DB->start_delegated_transaction();
+        $rs = $DB->get_recordset_sql($sql);
+        foreach ($rs as $rec) {
+            $DB->delete_records('user_info_data', array('id' => $rec->id));
+        }
+        $transaction->allow_commit();
+
+        $oldindex = new xmldb_index('userid_fieldid', XMLDB_INDEX_NOTUNIQUE, array('userid', 'fieldid'));
+        if ($dbman->index_exists($table, $oldindex)) {
+            $dbman->drop_index($table, $oldindex);
+        }
+
+        $newindex = new xmldb_index('userid_fieldid', XMLDB_INDEX_UNIQUE, array('userid', 'fieldid'));
+
+        if (!$dbman->index_exists($table, $newindex)) {
+            $dbman->add_index($table, $newindex);
+        }
+
+        // Main savepoint reached.
+        upgrade_main_savepoint(true, 2014041500.01);
+    }
+
     return true;
 }
index 341f317..cf1aaa9 100644 (file)
@@ -3514,7 +3514,10 @@ class core_renderer_ajax extends core_renderer {
         $e->debuginfo  = NULL;
         $e->reproductionlink = NULL;
         if (!empty($CFG->debug) and $CFG->debug >= DEBUG_DEVELOPER) {
-            $e->reproductionlink = $link;
+            $link = (string) $link;
+            if ($link) {
+                $e->reproductionlink = $link;
+            }
             if (!empty($debuginfo)) {
                 $e->debuginfo = $debuginfo;
             }
index 127b74f..b686efb 100644 (file)
@@ -166,10 +166,8 @@ class behat_forms extends behat_base {
      */
     public function the_field_matches_value($field, $value) {
 
-        $fieldnode = $this->find_field($field);
-
         // Get the field.
-        $formfield = behat_field_manager::get_form_field($fieldnode, $this->getSession());
+        $formfield = behat_field_manager::get_form_field_from_label($field, $this);
 
         // Checks if the provided value matches the current field value.
         if (!$formfield->matches($value)) {
@@ -193,10 +191,8 @@ class behat_forms extends behat_base {
      */
     public function the_field_does_not_match_value($field, $value) {
 
-        $fieldnode = $this->find_field($field);
-
         // Get the field.
-        $formfield = behat_field_manager::get_form_field($fieldnode, $this->getSession());
+        $formfield = behat_field_manager::get_form_field_from_label($field, $this);
 
         // Checks if the provided value matches the current field value.
         if ($formfield->matches($value)) {
@@ -348,11 +344,9 @@ class behat_forms extends behat_base {
      */
     protected function set_field_value($fieldlocator, $value) {
 
-        $node = $this->find_field($fieldlocator);
-
         // We delegate to behat_form_field class, it will
         // guess the type properly as it is a select tag.
-        $field = behat_field_manager::get_form_field($node, $this->getSession());
+        $field = behat_field_manager::get_form_field_from_label($fieldlocator, $this);
         $field->set_value($value);
     }
 
index 0f3d560..b6f0779 100644 (file)
@@ -110,6 +110,7 @@ class behat_hooks extends behat_base {
         // Now that we are MOODLE_INTERNAL.
         require_once(__DIR__ . '/../../behat/classes/behat_command.php');
         require_once(__DIR__ . '/../../behat/classes/behat_selectors.php');
+        require_once(__DIR__ . '/../../behat/classes/behat_context_helper.php');
         require_once(__DIR__ . '/../../behat/classes/util.php');
         require_once(__DIR__ . '/../../testing/classes/test_lock.php');
         require_once(__DIR__ . '/../../testing/classes/nasty_strings.php');
@@ -184,6 +185,7 @@ class behat_hooks extends behat_base {
         // We need the Mink session to do it and we do it only before the first scenario.
         if (self::is_first_scenario()) {
             behat_selectors::register_moodle_selectors($session);
+            behat_context_helper::set_session($session);
         }
 
         // Reset $SESSION.
index ad7ae33..9ca3e81 100644 (file)
Binary files a/lib/yui/build/moodle-core-notification-exception/moodle-core-notification-exception-debug.js and b/lib/yui/build/moodle-core-notification-exception/moodle-core-notification-exception-debug.js differ
index dd9e075..ba222b3 100644 (file)
Binary files a/lib/yui/build/moodle-core-notification-exception/moodle-core-notification-exception-min.js and b/lib/yui/build/moodle-core-notification-exception/moodle-core-notification-exception-min.js differ
index ad7ae33..9ca3e81 100644 (file)
Binary files a/lib/yui/build/moodle-core-notification-exception/moodle-core-notification-exception.js and b/lib/yui/build/moodle-core-notification-exception/moodle-core-notification-exception.js differ
index 618800a..482b861 100644 (file)
@@ -31,7 +31,7 @@ Y.extend(EXCEPTION, M.core.notification.info, {
             delay = this.get('hideTimeoutDelay');
         this.get(BASE).addClass('moodle-dialogue-exception');
         this.setStdModContent(Y.WidgetStdMod.HEADER,
-                '<h1 id="moodle-dialogue-'+COUNT+'-header-text">' + config.name + '</h1>', Y.WidgetStdMod.REPLACE);
+                '<h1 id="moodle-dialogue-'+config.COUNT+'-header-text">' + config.name + '</h1>', Y.WidgetStdMod.REPLACE);
         content = Y.Node.create('<div class="moodle-exception"></div>')
                 .append(Y.Node.create('<div class="moodle-exception-message">'+this.get('message')+'</div>'))
                 .append(Y.Node.create('<div class="moodle-exception-param hidden param-filename"><label>File:</label> ' +
index cb04214..2ea5a23 100644 (file)
@@ -52,6 +52,8 @@ class assessable_submitted extends base {
      * @return assessable_submitted
      */
     public static function create_from_submission(\assign $assign, \stdClass $submission, $editable) {
+        global $USER;
+
         $data = array(
             'context' => $assign->get_context(),
             'objectid' => $submission->id,
@@ -59,6 +61,9 @@ class assessable_submitted extends base {
                 'submission_editable' => $editable,
             ),
         );
+        if (!empty($submission->userid) && ($submission->userid != $USER->id)) {
+            $data['relateduserid'] = $submission->userid;
+        }
         /** @var assessable_submitted $event */
         $event = self::create($data);
         $event->set_assign($assign);
index fdfcc10..05b1dc5 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 651bbd9..e03ddc0 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 fdfcc10..05b1dc5 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 67f7af2..898179b 100644 (file)
@@ -205,7 +205,9 @@ EDITOR.prototype = {
 
             this.currentedit.start = false;
             this.currentedit.end = false;
-            this.quicklist = new M.assignfeedback_editpdf.quickcommentlist(this);
+            if (!this.get('readonly')) {
+                this.quicklist = new M.assignfeedback_editpdf.quickcommentlist(this);
+            }
         }
     },
 
@@ -462,7 +464,9 @@ EDITOR.prototype = {
         }
 
         // Update the ui.
-        this.quicklist.load();
+        if (this.quicklist) {
+            this.quicklist.load();
+        }
         this.setup_navigation();
         this.setup_toolbar();
         this.change_page();
index 981d5ea..2c1b5c5 100644 (file)
@@ -227,6 +227,9 @@ class assign_submission_file extends assign_submission_plugin {
                 'pathnamehashes' => array_keys($files)
             )
         );
+        if (!empty($submission->userid) && ($submission->userid != $USER->id)) {
+            $params->relateduserid = $submission->userid;
+        }
         $event = \assignsubmission_file\event\assessable_uploaded::create($params);
         $event->set_legacy_files($files);
         $event->trigger();
index 47ee159..627c6a1 100644 (file)
@@ -228,6 +228,9 @@ class assign_submission_onlinetext extends assign_submission_plugin {
                 'format' => $data->onlinetext_editor['format']
             )
         );
+        if (!empty($submission->userid) && ($submission->userid != $USER->id)) {
+            $params['relateduserid'] = $submission->userid;
+        }
         $event = \assignsubmission_onlinetext\event\assessable_uploaded::create($params);
         $event->trigger();
 
index 5377973..71e6491 100644 (file)
@@ -34,6 +34,27 @@ defined('MOODLE_INTERNAL') || die();
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 class chapter_created extends \core\event\base {
+    /**
+     * Create instance of event.
+     *
+     * @since Moodle 2.7
+     *
+     * @param \stdClass $book
+     * @param \context_module $context
+     * @param \stdClass $chapter
+     * @return chapter_created
+     */
+    public static function create_from_chapter(\stdClass $book, \context_module $context, \stdClass $chapter) {
+        $data = array(
+            'context' => $context,
+            'objectid' => $chapter->id,
+        );
+        /** @var chapter_created $event */
+        $event = self::create($data);
+        $event->add_record_snapshot('book', $book);
+        $event->add_record_snapshot('book_chapters', $chapter);
+        return $event;
+    }
 
     /**
      * Returns description of what happened.
index d882a6b..80ddf71 100644 (file)
@@ -34,13 +34,27 @@ defined('MOODLE_INTERNAL') || die();
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 class chapter_deleted extends \core\event\base {
-
     /**
-     * Legacy log data.
+     * Create instance of event.
+     *
+     * @since Moodle 2.7
      *
-     * @var array
+     * @param \stdClass $book
+     * @param \context_module $context
+     * @param \stdClass $chapter
+     * @return chapter_deleted
      */
-    protected $legacylogdata;
+    public static function create_from_chapter(\stdClass $book, \context_module $context, \stdClass $chapter) {
+        $data = array(
+            'context' => $context,
+            'objectid' => $chapter->id,
+        );
+        /** @var chapter_deleted $event */
+        $event = self::create($data);
+        $event->add_record_snapshot('book', $book);
+        $event->add_record_snapshot('book_chapters', $chapter);
+        return $event;
+    }
 
     /**
      * Returns description of what happened.
@@ -57,7 +71,8 @@ class chapter_deleted extends \core\event\base {
      * @return array|null
      */
     protected function get_legacy_logdata() {
-        return $this->legacylogdata;
+        $chapter = $this->get_record_snapshot('book_chapters', $this->objectid);
+        return array($this->courseid, 'book', 'update', 'view.php?id='.$this->contextinstanceid, $chapter->bookid, $this->contextinstanceid);
     }
 
     /**
@@ -88,14 +103,4 @@ class chapter_deleted extends \core\event\base {
         $this->data['edulevel'] = self::LEVEL_TEACHING;
         $this->data['objecttable'] = 'book_chapters';
     }
-
-    /**
-     * Set the legacy event log data.
-     *
-     * @return array|null
-     */
-    public function set_legacy_logdata($legacydata) {
-        $this->legacylogdata = $legacydata;
-    }
-
 }
index 7293eb4..586d5ae 100644 (file)
@@ -34,6 +34,27 @@ defined('MOODLE_INTERNAL') || die();
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 class chapter_updated extends \core\event\base {
+    /**
+     * Create instance of event.
+     *
+     * @since Moodle 2.7
+     *
+     * @param \stdClass $book
+     * @param \context_module $context
+     * @param \stdClass $chapter
+     * @return chapter_updated
+     */
+    public static function create_from_chapter(\stdClass $book, \context_module $context, \stdClass $chapter) {
+        $data = array(
+            'context' => $context,
+            'objectid' => $chapter->id,
+        );
+        /** @var chapter_updated $event */
+        $event = self::create($data);
+        $event->add_record_snapshot('book', $book);
+        $event->add_record_snapshot('book_chapters', $chapter);
+        return $event;
+    }
 
     /**
      * Returns description of what happened.
index d6d0f8b..8a8dee0 100644 (file)
@@ -34,6 +34,27 @@ defined('MOODLE_INTERNAL') || die();
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 class chapter_viewed extends \core\event\base {
+    /**
+     * Create instance of event.
+     *
+     * @since Moodle 2.7
+     *
+     * @param \stdClass $book
+     * @param \context_module $context
+     * @param \stdClass $chapter
+     * @return chapter_viewed
+     */
+    public static function create_from_chapter(\stdClass $book, \context_module $context, \stdClass $chapter) {
+        $data = array(
+            'context' => $context,
+            'objectid' => $chapter->id,
+        );
+        /** @var chapter_viewed $event */
+        $event = self::create($data);
+        $event->add_record_snapshot('book', $book);
+        $event->add_record_snapshot('book_chapters', $chapter);
+        return $event;
+    }
 
     /**
      * Returns description of what happened.
index a1ad6d7..2216d31 100644 (file)
@@ -34,5 +34,18 @@ defined('MOODLE_INTERNAL') || die();
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 class course_module_instance_list_viewed extends \core\event\course_module_instance_list_viewed {
-}
+    /**
+     * Create the event from course record.
+     *
+     * @param \stdClass $course
+     * @return course_module_instance_list_viewed
+     */
+    public static function create_from_course(\stdClass $course) {
+        $params = array(
+            'context' => \context_course::instance($course->id)
+        );
+        $event = \mod_book\event\course_module_instance_list_viewed::create($params);
+        $event->add_record_snapshot('course', $course);
+        return $event;
+    }}
 
index 19dd67b..af02c7c 100644 (file)
@@ -34,6 +34,25 @@ defined('MOODLE_INTERNAL') || die();
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 class course_module_viewed extends \core\event\course_module_viewed {
+    /**
+     * Create instance of event.
+     *
+     * @since Moodle 2.7
+     *
+     * @param \stdClass $book
+     * @param \context_module $context
+     * @return course_module_viewed
+     */
+    public static function create_from_book(\stdClass $book, \context_module $context) {
+        $data = array(
+            'context' => $context,
+            'objectid' => $book->id
+        );
+        /** @var course_module_viewed $event */
+        $event = self::create($data);
+        $event->add_record_snapshot('book', $book);
+        return $event;
+    }
 
     /**
      * Init method.
index 02ce6a0..1706147 100644 (file)
@@ -60,14 +60,7 @@ if ($confirm) {  // the operation was confirmed.
             } else if ($found and $ch->subchapter) {
                 $fs->delete_area_files($context->id, 'mod_book', 'chapter', $ch->id);
                 $DB->delete_records('book_chapters', array('id'=>$ch->id));
-
-                $params = array(
-                    'context' => $context,
-                    'objectid' => $ch->id
-                );
-                $event = \mod_book\event\chapter_deleted::create($params);
-                $event->add_record_snapshot('book_chapters', $ch);
-                $event->trigger();
+                \mod_book\event\chapter_deleted::create_from_chapter($book, $context, $ch)->trigger();
             } else if ($found) {
                 break;
             }
@@ -77,15 +70,7 @@ if ($confirm) {  // the operation was confirmed.
     $fs->delete_area_files($context->id, 'mod_book', 'chapter', $chapter->id);
     $DB->delete_records('book_chapters', array('id'=>$chapter->id));
 
-    add_to_log($course->id, 'course', 'update mod', '../mod/book/view.php?id='.$cm->id, 'book '.$book->id);
-    $params = array(
-        'context' => $context,
-        'objectid' => $chapter->id
-    );
-    $event = \mod_book\event\chapter_deleted::create($params);
-    $event->add_record_snapshot('book_chapters', $chapter);
-    $event->set_legacy_logdata(array($course->id, 'book', 'update', 'view.php?id='.$cm->id, $book->id, $cm->id));
-    $event->trigger();
+    \mod_book\event\chapter_deleted::create_from_chapter($book, $context, $chapter)->trigger();
 
     book_preload_chapters($book); // Fix structure.
     $DB->set_field('book', 'revision', $book->revision+1, array('id'=>$book->id));
index b6ca863..0d0a9af 100644 (file)
@@ -74,18 +74,9 @@ if ($mform->is_cancelled()) {
         $data = file_postupdate_standard_editor($data, 'content', $options, $context, 'mod_book', 'chapter', $data->id);
         $DB->update_record('book_chapters', $data);
         $DB->set_field('book', 'revision', $book->revision+1, array('id'=>$book->id));
+        $chapter = $DB->get_record('book_chapters', array('id' => $data->id));
 
-        add_to_log($course->id, 'course', 'update mod', '../mod/book/view.php?id='.$cm->id, 'book '.$book->id);
-        $params = array(
-            'context' => $context,
-            'objectid' => $data->id
-        );
-        $event = \mod_book\event\chapter_updated::create($params);
-        foreach ($data as $key => $value) {
-            $chapter->$key = $value;
-        }
-        $event->add_record_snapshot('book_chapters', $chapter);
-        $event->trigger();
+        \mod_book\event\chapter_updated::create_from_chapter($book, $context, $chapter)->trigger();
 
     } else {
         // adding new chapter
@@ -109,15 +100,9 @@ if ($mform->is_cancelled()) {
         $data = file_postupdate_standard_editor($data, 'content', $options, $context, 'mod_book', 'chapter', $data->id);
         $DB->update_record('book_chapters', $data);
         $DB->set_field('book', 'revision', $book->revision+1, array('id'=>$book->id));
+        $chapter = $DB->get_record('book_chapters', array('id' => $data->id));
 
-        add_to_log($course->id, 'course', 'update mod', '../mod/book/view.php?id='.$cm->id, 'book '.$book->id);
-        $params = array(
-            'context' => $context,
-            'objectid' => $data->id
-        );
-        $event = \mod_book\event\chapter_created::create($params);
-        $event->add_record_snapshot('book_chapters', $data);
-        $event->trigger();
+        \mod_book\event\chapter_created::create_from_chapter($book, $context, $chapter)->trigger();
     }
 
     book_preload_chapters($book); // fix structure
index f58f358..359ecbd 100644 (file)
@@ -47,12 +47,7 @@ $PAGE->set_heading($course->fullname);
 $PAGE->navbar->add($strbooks);
 echo $OUTPUT->header();
 
-$params = array(
-    'context' => context_course::instance($course->id)
-);
-$event = \mod_book\event\course_module_instance_list_viewed::create($params);
-$event->add_record_snapshot('course', $course);
-$event->trigger();
+\mod_book\event\course_module_instance_list_viewed::create_from_course($course)->trigger();
 
 // Get all the appropriate data
 if (!$books = get_all_instances_in_course('book', $course)) {
index dc6a05f..1da5ef7 100644 (file)
@@ -173,22 +173,14 @@ if (!$nothing) {
     foreach ($newchapters as $ch) {
         $ch->pagenum = $i;
         $DB->update_record('book_chapters', $ch);
+        $ch = $DB->get_record('book_chapters', array('id' => $ch->id));
 
-        $params = array(
-            'context' => $context,
-            'objectid' => $ch->id
-        );
-        $event = \mod_book\event\chapter_updated::create($params);
-        $event->trigger();
+        \mod_book\event\chapter_updated::create_from_chapter($book, $context, $ch)->trigger();
 
         $i++;
     }
 }
 
-// MDL-39963 Decide what to do with those logs.
-add_to_log($course->id, 'course', 'update mod', '../mod/book/view.php?id='.$cm->id, 'book '.$book->id);
-add_to_log($course->id, 'book', 'update', 'view.php?id='.$cm->id, $book->id, $cm->id);
-
 book_preload_chapters($book); // fix structure
 $DB->set_field('book', 'revision', $book->revision+1, array('id'=>$book->id));
 
index 21372f8..d842a46 100644 (file)
@@ -47,42 +47,28 @@ $chapter->hidden = $chapter->hidden ? 0 : 1;
 
 // Update record.
 $DB->update_record('book_chapters', $chapter);
-$params = array(
-    'context' => $context,
-    'objectid' => $chapter->id
-);
-$event = \mod_book\event\chapter_updated::create($params);
-$event->add_record_snapshot('book_chapters', $chapter);
-$event->trigger();
+\mod_book\event\chapter_updated::create_from_chapter($book, $context, $chapter)->trigger();
 
 // Change visibility of subchapters too.
 if (!$chapter->subchapter) {
-    $chapters = $DB->get_records('book_chapters', array('bookid'=>$book->id), 'pagenum', 'id, subchapter, hidden');
+    $chapters = $DB->get_recordset('book_chapters', array('bookid'=>$book->id), 'pagenum ASC');
     $found = 0;
     foreach ($chapters as $ch) {
         if ($ch->id == $chapter->id) {
             $found = 1;
+
         } else if ($found and $ch->subchapter) {
             $ch->hidden = $chapter->hidden;
             $DB->update_record('book_chapters', $ch);
-
-            $params = array(
-                'context' => $context,
-                'objectid' => $ch->id
-            );
-            $event = \mod_book\event\chapter_updated::create($params);
-            $event->trigger();
+            \mod_book\event\chapter_updated::create_from_chapter($book, $context, $ch)->trigger();
 
         } else if ($found) {
             break;
         }
     }
+    $chapters->close();
 }
 
-// MDL-39963 Decide what to do with those logs.
-add_to_log($course->id, 'course', 'update mod', '../mod/book/view.php?id='.$cm->id, 'book '.$book->id);
-add_to_log($course->id, 'book', 'update', 'view.php?id='.$cm->id, $book->id, $cm->id);
-
 book_preload_chapters($book); // fix structure
 $DB->set_field('book', 'revision', $book->revision+1, array('id'=>$book->id));
 
index 2992884..cd95aea 100644 (file)
@@ -47,14 +47,11 @@ class mod_book_events_testcase extends advanced_testcase {
         $course = $this->getDataGenerator()->create_course();
         $book = $this->getDataGenerator()->create_module('book', array('course' => $course->id));
         $bookgenerator = $this->getDataGenerator()->get_plugin_generator('mod_book');
+        $context = context_module::instance($book->cmid);
 
         $chapter = $bookgenerator->create_chapter(array('bookid' => $book->id));
 
-        $params = array(
-            'context' => context_module::instance($book->cmid),
-            'objectid' => $chapter->id
-        );
-        $event = \mod_book\event\chapter_created::create($params);
+        $event = \mod_book\event\chapter_created::create_from_chapter($book, $context, $chapter);
 
         // Triggering and capturing the event.
         $sink = $this->redirectEvents();
@@ -80,14 +77,11 @@ class mod_book_events_testcase extends advanced_testcase {
         $course = $this->getDataGenerator()->create_course();
         $book = $this->getDataGenerator()->create_module('book', array('course' => $course->id));
         $bookgenerator = $this->getDataGenerator()->get_plugin_generator('mod_book');
+        $context = context_module::instance($book->cmid);
 
         $chapter = $bookgenerator->create_chapter(array('bookid' => $book->id));
 
-        $params = array(
-            'context' => context_module::instance($book->cmid),
-            'objectid' => $chapter->id
-        );
-        $event = \mod_book\event\chapter_updated::create($params);
+        $event = \mod_book\event\chapter_updated::create_from_chapter($book, $context, $chapter);
 
         // Triggering and capturing the event.
         $sink = $this->redirectEvents();
@@ -113,16 +107,12 @@ class mod_book_events_testcase extends advanced_testcase {
         $course = $this->getDataGenerator()->create_course();
         $book = $this->getDataGenerator()->create_module('book', array('course' => $course->id));
         $bookgenerator = $this->getDataGenerator()->get_plugin_generator('mod_book');
+        $context = context_module::instance($book->cmid);
 
         $chapter = $bookgenerator->create_chapter(array('bookid' => $book->id));
 
-        $params = array(
-            'context' => context_module::instance($book->cmid),
-            'objectid' => $chapter->id
-        );
-        $event = \mod_book\event\chapter_deleted::create($params);
-        $event->add_record_snapshot('book_chapters', $chapter);
-        $event->set_legacy_logdata(array('1', 2, false));
+        $event = \mod_book\event\chapter_deleted::create_from_chapter($book, $context, $chapter);
+        $legacy = array($course->id, 'book', 'update', 'view.php?id='.$book->cmid, $book->id, $book->cmid);
 
         // Triggering and capturing the event.
         $sink = $this->redirectEvents();
@@ -136,7 +126,7 @@ class mod_book_events_testcase extends advanced_testcase {
         $this->assertEquals(context_module::instance($book->cmid), $event->get_context());
         $this->assertEquals($chapter->id, $event->objectid);
         $this->assertEquals($chapter, $event->get_record_snapshot('book_chapters', $chapter->id));
-        $this->assertEventLegacyLogData(array('1', 2, false), $event);
+        $this->assertEventLegacyLogData($legacy, $event);
         $this->assertEventContextNotUsed($event);
     }
 
@@ -201,14 +191,11 @@ class mod_book_events_testcase extends advanced_testcase {
         $course = $this->getDataGenerator()->create_course();
         $book = $this->getDataGenerator()->create_module('book', array('course' => $course->id));
         $bookgenerator = $this->getDataGenerator()->get_plugin_generator('mod_book');
+        $context = context_module::instance($book->cmid);
 
         $chapter = $bookgenerator->create_chapter(array('bookid' => $book->id));
 
-        $params = array(
-            'context' => context_module::instance($book->cmid),
-            'objectid' => $chapter->id
-        );
-        $event = \mod_book\event\chapter_viewed::create($params);
+        $event = \mod_book\event\chapter_viewed::create_from_chapter($book, $context, $chapter);
 
         // Triggering and capturing the event.
         $sink = $this->redirectEvents();
index 6f7dd63..3108576 100644 (file)
@@ -34,6 +34,25 @@ defined('MOODLE_INTERNAL') || die();
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 class book_exported extends \core\event\base {
+    /**
+     * Create instance of event.
+     *
+     * @since Moodle 2.7
+     *
+     * @param \stdClass $book
+     * @param \context_module $context
+     * @return book_exported
+     */
+    public static function create_from_book(\stdClass $book, \context_module $context) {
+        $data = array(
+            'context' => $context,
+            'objectid' => $book->id
+        );
+        /** @var book_exported $event */
+        $event = self::create($data);
+        $event->add_record_snapshot('book', $book);
+        return $event;
+    }
 
     /**
      * Returns description of what happened.
index d12aa42..f3c4b0d 100644 (file)
@@ -43,13 +43,7 @@ $context = context_module::instance($cm->id);
 require_capability('mod/book:read', $context);
 require_capability('booktool/exportimscp:export', $context);
 
-$params = array(
-    'context' => $context,
-    'objectid' => $book->id
-);
-$event = \booktool_exportimscp\event\book_exported::create($params);
-$event->add_record_snapshot('book', $book);
-$event->trigger();
+\booktool_exportimscp\event\book_exported::create_from_book($book, $context)->trigger();
 
 $file = booktool_exportimscp_build_package($book, $context);
 
index e83d2f1..3132a41 100644 (file)
@@ -46,12 +46,9 @@ class booktool_exportimscp_events_testcase extends advanced_testcase {
 
         $course = $this->getDataGenerator()->create_course();
         $book = $this->getDataGenerator()->create_module('book', array('course' => $course->id));
+        $context = context_module::instance($book->cmid);
 
-        $params = array(
-            'context' => context_module::instance($book->cmid),
-            'objectid' => $book->id
-        );
-        $event = \booktool_exportimscp\event\book_exported::create($params);
+        $event = \booktool_exportimscp\event\book_exported::create_from_book($book, $context);
 
         // Triggering and capturing the event.
         $sink = $this->redirectEvents();
index c34bd30..5ca3e47 100644 (file)
@@ -82,15 +82,10 @@ function toolbook_importhtml_import_chapters($package, $type, $book, $context, $
                 }
 
                 $chapter->id = $DB->insert_record('book_chapters', $chapter);
+                $chapter = $DB->get_record('book_chapters', array('id' => $chapter->id));
                 $chapters[$chapter->id] = $chapter;
 
-                $params = array(
-                    'context' => $context,
-                    'objectid' => $chapter->id
-                );
-                $event = \mod_book\event\chapter_created::create($params);
-                $event->add_record_snapshot('book_chapters', $chapter);
-                $event->trigger();
+                \mod_book\event\chapter_created::create_from_chapter($book, $context, $chapter)->trigger();
             }
         }
     }
@@ -154,7 +149,6 @@ function toolbook_importhtml_import_chapters($package, $type, $book, $context, $
         }
     }
 
-    add_to_log($book->course, 'course', 'update mod', '../mod/book/view.php?id='.$context->instanceid, 'book '.$book->id);
     $fs->delete_area_files($context->id, 'mod_book', 'importhtmltemp', 0);
 
     // update the revision flag - this takes a long time, better to refetch the current value
index ca77d3a..f397d75 100644 (file)
@@ -34,6 +34,25 @@ defined('MOODLE_INTERNAL') || die();
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 class book_printed extends \core\event\base {
+    /**
+     * Create instance of event.
+     *
+     * @since Moodle 2.7
+     *
+     * @param \stdClass $book
+     * @param \context_module $context
+     * @return book_printed
+     */
+    public static function create_from_book(\stdClass $book, \context_module $context) {
+        $data = array(
+            'context' => $context,
+            'objectid' => $book->id
+        );
+        /** @var book_printed $event */
+        $event = self::create($data);
+        $event->add_record_snapshot('book', $book);
+        return $event;
+    }
 
     /**
      * Returns description of what happened.
index ed1c79f..22cd988 100644 (file)
@@ -34,6 +34,27 @@ defined('MOODLE_INTERNAL') || die();
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 class chapter_printed extends \core\event\base {
+    /**
+     * Create instance of event.
+     *
+     * @since Moodle 2.7
+     *
+     * @param \stdClass $book
+     * @param \context_module $context
+     * @param \stdClass $chapter
+     * @return chapter_printed
+     */
+    public static function create_from_chapter(\stdClass $book, \context_module $context, \stdClass $chapter) {
+        $data = array(
+            'context' => $context,
+            'objectid' => $chapter->id,
+        );
+        /** @var chapter_printed $event */
+        $event = self::create($data);
+        $event->add_record_snapshot('book', $book);
+        $event->add_record_snapshot('book_chapters', $chapter);
+        return $event;
+    }
 
     /**
      * Returns description of what happened.
index 9997117..fb83d71 100644 (file)
@@ -76,14 +76,7 @@ if ($chapter) {
     if ($chapter->hidden) {
         require_capability('mod/book:viewhiddenchapters', $context);
     }
-
-    $params = array(
-        'context' => $context,
-        'objectid' => $chapter->id
-    );
-    $event = \booktool_print\event\chapter_printed::create($params);
-    $event->add_record_snapshot('book_chapters', $chapter);
-    $event->trigger();
+    \booktool_print\event\chapter_printed::create_from_chapter($book, $context, $chapter)->trigger();
 
     // page header
     ?>
@@ -129,13 +122,7 @@ if ($chapter) {
     echo '</body> </html>';
 
 } else {
-    $params = array(
-        'context' => $context,
-        'objectid' => $book->id
-    );
-    $event = \booktool_print\event\book_printed::create($params);
-    $event->add_record_snapshot('book', $book);
-    $event->trigger();
+    \booktool_print\event\book_printed::create_from_book($book, $context)->trigger();
 
     $allchapters = $DB->get_records('book_chapters', array('bookid'=>$book->id), 'pagenum');
     $book->intro = file_rewrite_pluginfile_urls($book->intro, 'pluginfile.php', $context->id, 'mod_book', 'intro', null);
index bd3fd7d..da071e5 100644 (file)
@@ -46,12 +46,9 @@ class booktool_print_events_testcase extends advanced_testcase {
 
         $course = $this->getDataGenerator()->create_course();
         $book = $this->getDataGenerator()->create_module('book', array('course' => $course->id));
+        $context = context_module::instance($book->cmid);
 
-        $params = array(
-            'context' => context_module::instance($book->cmid),
-            'objectid' => $book->id
-        );
-        $event = \booktool_print\event\book_printed::create($params);
+        $event = \booktool_print\event\book_printed::create_from_book($book, $context);
 
         // Triggering and capturing the event.
         $sink = $this->redirectEvents();
@@ -78,12 +75,9 @@ class booktool_print_events_testcase extends advanced_testcase {
         $book = $this->getDataGenerator()->create_module('book', array('course' => $course->id));
         $bookgenerator = $this->getDataGenerator()->get_plugin_generator('mod_book');
         $chapter = $bookgenerator->create_chapter(array('bookid' => $book->id));
+        $context = context_module::instance($book->cmid);
 
-        $params = array(
-            'context' => context_module::instance($book->cmid),
-            'objectid' => $chapter->id
-        );
-        $event = \booktool_print\event\chapter_printed::create($params);
+        $event = \booktool_print\event\chapter_printed::create_from_chapter($book, $context, $chapter);
 
         // Triggering and capturing the event.
         $sink = $this->redirectEvents();
diff --git a/mod/book/upgrade.txt b/mod/book/upgrade.txt
new file mode 100644 (file)
index 0000000..27db349
--- /dev/null
@@ -0,0 +1,6 @@
+This files describes API changes in the book code.
+
+=== 2.7 ===
+
+* bogus legacy log calls were removed
+* \mod_book\event\chapter_deleted::set_legacy_logdata() was removed
\ No newline at end of file
index acb7f00..3fd9b45 100644 (file)
@@ -75,15 +75,7 @@ if ($allowedit and !$chapters) {
 }
 // Check chapterid and read chapter data
 if ($chapterid == '0') { // Go to first chapter if no given.
-    $params = array(
-        'context' => $context,
-        'objectid' => $book->id
-    );
-    $event = \mod_book\event\course_module_viewed::create($params);
-    $event->add_record_snapshot('course_modules', $cm);
-    $event->add_record_snapshot('course', $course);
-    $event->add_record_snapshot('book', $book);
-    $event->trigger();
+    \mod_book\event\course_module_viewed::create_from_book($book, $context)->trigger();
 
     foreach ($chapters as $ch) {
         if ($edit) {
@@ -119,13 +111,7 @@ unset($chapterid);
 
 // Security checks END.
 
-$params = array(
-    'context' => $context,
-    'objectid' => $chapter->id
-);
-$event = \mod_book\event\chapter_viewed::create($params);
-$event->add_record_snapshot('book_chapters', $chapter);
-$event->trigger();
+\mod_book\event\chapter_viewed::create_from_chapter($book, $context, $chapter)->trigger();
 
 // Read standard strings.
 $strbooks = get_string('modulenameplural', 'mod_book');
index c8515f0..690ddaf 100644 (file)
@@ -1,20 +1,22 @@
-@mod @mod_forum
+@mod @mod_forum @_file_upload
 Feature: Add forum activities and discussions
   In order to discuss topics with other users
   As a teacher
   I need to add forum activities to moodle courses
 
   @javascript
-  Scenario: Add a forum and a discussion
+  Scenario: Add a forum and a discussion attaching files
     Given the following "users" exist:
       | username | firstname | lastname | email |
       | teacher1 | Teacher | 1 | teacher1@asd.com |
+      | student1 | Student | 1 | student1@asd.com |
     And the following "courses" exist:
       | fullname | shortname | category |
       | Course 1 | C1 | 0 |
     And the following "course enrolments" exist:
       | user | course | role |
       | teacher1 | C1 | editingteacher |
+      | student1 | C1 | student |
     And I log in as "teacher1"
     And I follow "Course 1"
     And I turn editing mode on
@@ -22,7 +24,24 @@ Feature: Add forum activities and discussions
       | Forum name | Test forum name |
       | Forum type | Standard forum for general use |
       | Description | Test forum description |
-    When I add a new discussion to "Test forum name" forum with:
+    And I add a new discussion to "Test forum name" forum with:
       | Subject | Forum post 1 |
       | Message | This is the body |
-    Then I should see "Test forum name"
+    And I log out
+    And I log in as "student1"
+    And I follow "Course 1"
+    When I add a new discussion to "Test forum name" forum with:
+      | Subject | Post with attachment |
+      | Message | This is the body |
+      | Attachment | lib/tests/fixtures/empty.txt |
+    And I reply "Forum post 1" post from "Test forum name" forum with:
+      | Subject | Reply with attachment |
+      | Message | This is the body |
+      | Attachment | lib/tests/fixtures/upload_users.csv |
+    Then I should see "Reply with attachment"
+    And I should see "upload_users.csv"
+    And I follow "Test forum name"
+    And I follow "Post with attachment"
+    And I should see "empty.txt"
+    And I follow "Edit"
+    And the field "Attachment" matches value "empty.txt"
index 1f26b63..b33d9e3 100644 (file)
@@ -85,8 +85,9 @@ if ($override->groupid) {
     $group = $DB->get_record('groups', array('id' => $override->groupid), 'id, name');
     $confirmstr = get_string("overridedeletegroupsure", "quiz", $group->name);
 } else {
+    $namefields = get_all_user_name_fields(true);
     $user = $DB->get_record('user', array('id' => $override->userid),
-            'id, firstname, lastname');
+            'id, ' . $namefields);
     $confirmstr = get_string("overridedeleteusersure", "quiz", fullname($user));
 }
 
index ae3d171..5c374a7 100644 (file)
@@ -232,12 +232,13 @@ function survey_print_recent_activity($course, $viewfullnames, $timestart) {
 
     $slist = implode(',', $ids); // there should not be hundreds of glossaries in one course, right?
 
+    $allusernames = user_picture::fields('u');
     $rs = $DB->get_recordset_sql("SELECT sa.userid, sa.survey, MAX(sa.time) AS time,
-                                         u.firstname, u.lastname, u.email, u.picture
+                                         $allusernames
                                     FROM {survey_answers} sa
                                     JOIN {user} u ON u.id = sa.userid
                                    WHERE sa.survey IN ($slist) AND sa.time > ?
-                                GROUP BY sa.userid, sa.survey, u.firstname, u.lastname, u.email, u.picture
+                                GROUP BY sa.userid, sa.survey, $allusernames
                                 ORDER BY time ASC", array($timestart));
     if (!$rs->valid()) {
         $rs->close(); // Not going to iterate (but exit), close rs
index cdf25c5..a41c0da 100644 (file)
@@ -328,9 +328,14 @@ class report_log_table_log extends table_sql {
                 $sql = $DB->sql_like('action', ':action', false);
                 $params['action'] = '%'.$action.'%';
             }
-        } else {
+        } else if (!empty($this->filterparams->action)) {
             $sql = "crud = :crud";
             $params['crud'] = $this->filterparams->action;
+        } else {
+            // Add condition for all possible values of crud (to use db index).
+            list($sql, $params) = $DB->get_in_or_equal(array('c', 'r', 'u', 'd'),
+                    SQL_PARAMS_NAMED, 'crud');
+            $sql = "crud ".$sql;
         }
         return array($sql, $params);
     }
@@ -342,10 +347,15 @@ class report_log_table_log extends table_sql {
      * @param bool $useinitialsbar do you want to use the initials bar.
      */
     public function query_db($pagesize, $useinitialsbar = true) {
+        global $DB;
 
         $joins = array();
         $params = array();
 
+        // If we filter by userid and module id we also need to filter by crud and edulevel to ensure DB index is engaged.
+        $useextendeddbindex = !($this->filterparams->logreader instanceof logstore_legacy\log\store)
+                && !empty($this->filterparams->userid) && !empty($this->filterparams->modid);
+
         $groupid = 0;
         if (!empty($this->filterparams->courseid)) {
             if (!empty($this->filterparams->groupid)) {
@@ -362,10 +372,12 @@ class report_log_table_log extends table_sql {
 
         if (!empty($this->filterparams->modid)) {
             $joins[] = "contextinstanceid = :contextinstanceid";
+            $joins[] = "contextlevel = :contextmodule";
             $params['contextinstanceid'] = $this->filterparams->modid;
+            $params['contextmodule'] = CONTEXT_MODULE;
         }
 
-        if (!empty($this->filterparams->action)) {
+        if (!empty($this->filterparams->action) || $useextendeddbindex) {
             list($actionsql, $actionparams) = $this->get_action_sql();
             $joins[] = $actionsql;
             $params = array_merge($params, $actionparams);
@@ -392,6 +404,16 @@ class report_log_table_log extends table_sql {
         if (isset($this->filterparams->edulevel) && ($this->filterparams->edulevel >= 0)) {
             $joins[] = "edulevel = :edulevel";
             $params['edulevel'] = $this->filterparams->edulevel;
+        } else if ($useextendeddbindex) {
+            list($edulevelsql, $edulevelparams) = $DB->get_in_or_equal(array(\core\event\base::LEVEL_OTHER,
+                \core\event\base::LEVEL_PARTICIPATING, \core\event\base::LEVEL_TEACHING), SQL_PARAMS_NAMED, 'edulevel');
+            $joins[] = "edulevel ".$edulevelsql;
+            $params = array_merge($params, $edulevelparams);
+        }
+
+        if (!($this->filterparams->logreader instanceof logstore_legacy\log\store)) {
+            // Filter out anonymous actions, this is N/A for legacy log because it never stores them.
+            $joins[] = "anonymous = 0";
         }
 
         $selector = implode(' AND ', $joins);
index 8ddb193..8d2d37b 100644 (file)
@@ -92,10 +92,13 @@ function report_log_usercourse($userid, $courseid, $coursestart, $logreader = ''
         $logtable = 'log';
         $timefield = 'time';
         $coursefield = 'course';
+        // Anonymous actions are never logged in legacy log.
+        $nonanonymous = '';
     } else {
         $logtable = $reader->get_internal_log_table_name();
         $timefield = 'timecreated';
         $coursefield = 'courseid';
+        $nonanonymous = 'AND anonymous = 0';
     }
 
     $params = array();
@@ -108,7 +111,7 @@ function report_log_usercourse($userid, $courseid, $coursestart, $logreader = ''
     return $DB->get_records_sql("SELECT FLOOR(($timefield - $coursestart)/" . DAYSECS . ") AS day, COUNT(*) AS num
                                    FROM {" . $logtable . "}
                                   WHERE userid = :userid
-                                        AND $timefield > $coursestart $courseselect
+                                        AND $timefield > $coursestart $courseselect $nonanonymous
                                GROUP BY FLOOR(($timefield - $coursestart)/" . DAYSECS .")", $params);
 }
 
@@ -142,10 +145,13 @@ function report_log_userday($userid, $courseid, $daystart, $logreader = '') {
         $logtable = 'log';
         $timefield = 'time';
         $coursefield = 'course';
+        // Anonymous actions are never logged in legacy log.
+        $nonanonymous = '';
     } else {
         $logtable = $reader->get_internal_log_table_name();
         $timefield = 'timecreated';
         $coursefield = 'courseid';
+        $nonanonymous = 'AND anonymous = 0';
     }
     $params = array('userid' => $userid);
 
@@ -157,7 +163,7 @@ function report_log_userday($userid, $courseid, $daystart, $logreader = '') {
     return $DB->get_records_sql("SELECT FLOOR(($timefield - $daystart)/" . HOURSECS . ") AS hour, COUNT(*) AS num
                                    FROM {" . $logtable . "}
                                   WHERE userid = :userid
-                                        AND $timefield > $daystart $courseselect
+                                        AND $timefield > $daystart $courseselect $nonanonymous
                                GROUP BY FLOOR(($timefield - $daystart)/" . HOURSECS . ") ", $params);
 }
 
index fe75bd5..1bcc0e0 100644 (file)
@@ -24,7 +24,7 @@
  */
 
 $string['alfresco_url'] = 'Alfresco URL';
-$string['alfrescourltext'] = 'Afresco API url should be: http://yoursite.com/alfresco/api';
+$string['alfrescourltext'] = 'Afresco API URL should be: http://yoursite.com/alfresco/api or http://yoursite.com/alfresco/soapapi for Alfresco 4.2.d or greater.';
 $string['alfresco:view'] = 'View alfresco repository';
 $string['configplugin'] = 'Alfresco configuration';
 $string['notitle'] = 'notitle';
index ced8bff..0203d73 100644 (file)
@@ -281,11 +281,8 @@ class behat_filepicker extends behat_files {
 
         // The action depends on the field type.
         foreach ($datahash as $locator => $value) {
-            // Getting the node element pointed by the label.
-            $fieldnode = $this->find_field($locator);
 
-            // Gets the field type from a parent node.
-            $field = behat_field_manager::get_form_field($fieldnode, $this->getSession());
+            $field = behat_field_manager::get_form_field_from_label($locator, $this);
 
             // Delegates to the field class.
             $field->set_value($value);
index 48bc155..d5197fd 100644 (file)
@@ -143,11 +143,8 @@ class behat_repository_upload extends behat_files {
 
         // The action depends on the field type.
         foreach ($datahash as $locator => $value) {
-            // Getting the node element pointed by the label.
-            $fieldnode = $this->find_field($locator);
 
-            // Gets the field type from a parent node.
-            $field = behat_field_manager::get_form_field($fieldnode, $this->getSession());
+            $field = behat_field_manager::get_form_field_from_label($locator, $this);
 
             // Delegates to the field class.
             $field->set_value($value);
index 043df03..eb8dd88 100644 (file)
@@ -71,7 +71,7 @@ class user_filter_courserole extends user_filter_type {
         $objs[] = $mform->createElement('select', $this->_name .'_ct', null, $this->get_course_categories());
         $objs[] = $mform->createElement('text', $this->_name, null);
         $grp =& $mform->addElement('group', $this->_name.'_grp', $this->_label, $objs, '', false);
-        $mform->setType($this->_name, PARAM_ALPHANUMEXT);
+        $mform->setType($this->_name, PARAM_TEXT);
         if ($this->_advanced) {
             $mform->setAdvanced($this->_name.'_grp');
         }
index 32d2e55..b60d5ad 100644 (file)
@@ -451,9 +451,9 @@ function profile_list_categories() {
  * @param string $redirect
  */
 function profile_edit_category($id, $redirect) {
-    global $DB, $OUTPUT;
+    global $DB, $OUTPUT, $CFG;
 
-    require_once('index_category_form.php');
+    require_once($CFG->dirroot.'/user/profile/index_category_form.php');
     $categoryform = new category_form();
 
     if ($category = $DB->get_record('user_info_category', array('id' => $id))) {
@@ -515,7 +515,7 @@ function profile_edit_field($id, $datatype, $redirect) {
     $field->description = clean_text($field->description, $field->descriptionformat);
     $field->description = array('text' => $field->description, 'format' => $field->descriptionformat, 'itemid' => 0);
 
-    require_once('index_field_form.php');
+    require_once($CFG->dirroot.'/user/profile/index_field_form.php');
     $fieldform = new field_form(null, $field->datatype);
 
     // Convert the data format for.
index c688bb2..6bb4be3 100644 (file)
@@ -108,12 +108,11 @@ switch ($action) {
         // Normal form.
 }
 
-// Print the header.
-echo $OUTPUT->header();
-echo $OUTPUT->heading(get_string('profilefields', 'admin'));
+// Show all categories.
+$categories = $DB->get_records('user_info_category', null, 'sortorder ASC');
 
 // Check that we have at least one category defined.
-if ($DB->count_records('user_info_category') == 0) {
+if (empty($categories)) {
     $defaultcategory = new stdClass();
     $defaultcategory->name = $strdefaultcategory;
     $defaultcategory->sortorder = 1;
@@ -121,8 +120,9 @@ if ($DB->count_records('user_info_category') == 0) {
     redirect($redirect);
 }
 
-// Show all categories.
-$categories = $DB->get_records('user_info_category', null, 'sortorder ASC');
+// Print the header.
+echo $OUTPUT->header();
+echo $OUTPUT->heading(get_string('profilefields', 'admin'));
 
 foreach ($categories as $category) {
     $table = new html_table();
index 9dfdfab..cbdeed8 100644 (file)
 
 defined('MOODLE_INTERNAL') || die();
 
-$version  = 2014041500.00;              // YYYYMMDD      = weekly release date of this DEV branch.
+$version  = 2014041700.00;              // YYYYMMDD      = weekly release date of this DEV branch.
                                         //         RR    = release increments - 00 in DEV branches.
                                         //           .XX = incremental changes.
 
-$release  = '2.7beta+ (Build: 20140415)'; // Human-friendly version name
+$release  = '2.7beta+ (Build: 20140417)'; // Human-friendly version name
 
 $branch   = '27';                       // This version's branch.
 $maturity = MATURITY_BETA;             // This version's maturity level.