MDL-36088 questions: Add new events
authorStephen Bourget <steve.bourget@sau19.org>
Mon, 22 Aug 2016 01:41:16 +0000 (21:41 -0400)
committerVinhLe <Vinh.LeThe@Nashtechglobal.com>
Mon, 29 Apr 2019 04:15:36 +0000 (11:15 +0700)
25 files changed:
lang/en/question.php
lib/classes/event/question_base.php [new file with mode: 0644]
lib/classes/event/question_category_base.php [new file with mode: 0644]
lib/classes/event/question_category_created.php
lib/classes/event/question_category_deleted.php [new file with mode: 0644]
lib/classes/event/question_category_moved.php [new file with mode: 0644]
lib/classes/event/question_category_updated.php [new file with mode: 0644]
lib/classes/event/question_category_viewed.php [new file with mode: 0644]
lib/classes/event/question_created.php [new file with mode: 0644]
lib/classes/event/question_deleted.php [new file with mode: 0644]
lib/classes/event/question_moved.php [new file with mode: 0644]
lib/classes/event/question_previewed.php [new file with mode: 0644]
lib/classes/event/question_updated.php [new file with mode: 0644]
lib/classes/event/questions_exported.php [new file with mode: 0644]
lib/classes/event/questions_imported.php [new file with mode: 0644]
lib/questionlib.php
question/category.php
question/category_class.php
question/edit.php
question/export.php
question/import.php
question/preview.php
question/tests/events_test.php
question/type/questiontypebase.php
version.php

index d9df15e..25a204a 100644 (file)
@@ -142,6 +142,17 @@ $string['errorprocessingresponses'] = 'An error occurred while processing your r
 $string['errorsavingcomment'] = 'Error saving the comment for question {$a->name} in the database.';
 $string['errorupdatingattempt'] = 'Error updating attempt {$a->id} in the database.';
 $string['eventquestioncategorycreated'] = 'Question category created';
+$string['eventquestioncategorydeleted'] = 'Question category deleted';
+$string['eventquestioncategorymoved'] = 'Question category moved';
+$string['eventquestioncategoryupdated'] = 'Question category updated';
+$string['eventquestioncategoryviewed'] = 'Question category viewed';
+$string['eventquestioncreated'] = 'Question created';
+$string['eventquestiondeleted'] = 'Question deleted';
+$string['eventquestionmoved'] = 'Question moved';
+$string['eventquestionpreviewed'] = 'Question previewed';
+$string['eventquestionsexported'] = 'Questions exported';
+$string['eventquestionsimported'] = 'Questions imported';
+$string['eventquestionupdated'] = 'Question updated';
 $string['export'] = 'Export';
 $string['exportcategory'] = 'Export category';
 $string['exportcategory_help'] = 'This setting determines the category from which the exported questions will be taken.
diff --git a/lib/classes/event/question_base.php b/lib/classes/event/question_base.php
new file mode 100644 (file)
index 0000000..3690a4c
--- /dev/null
@@ -0,0 +1,98 @@
+<?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/>.
+
+/**
+ * Base class for question events.
+ *
+ * @package    core
+ * @copyright  2016 Stephen Bourget
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core\event;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Base class for question events.
+ *
+ * @package    core
+ * @since      Moodle 3.2
+ * @copyright  2016 Stephen Bourget
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class question_base extends base {
+
+    /**
+     * Init method.
+     */
+    protected function init() {
+        $this->data['edulevel'] = self::LEVEL_TEACHING;
+    }
+
+    /**
+     * Returns relevant URL.
+     *
+     * @return \moodle_url
+     */
+    public function get_url() {
+        if ($this->courseid) {
+            $cat = $this->other['categoryid'] . ',' . $this->contextid;
+            if ($this->contextlevel == CONTEXT_MODULE) {
+                return new \moodle_url('/question/edit.php', array('cmid' => $this->contextinstanceid, 'cat' => $cat, 'lastchanged' => $this->objectid));
+            }
+            return new \moodle_url('/question/edit.php', array('courseid' => $this->courseid, 'cat' => $cat, 'lastchanged' => $this->objectid));
+        }
+        // Lets try viewing from the frontpage for contexts above course.
+        return new \moodle_url('/question/category.php', array('courseid' => SITEID, 'edit' => $this->other['categoryid'], 'lastchanged' => $this->objectid));
+    }
+
+    /**
+     * Custom validations.
+     *
+     * @throws \coding_exception
+     * @return void
+     */
+    protected function validate_data() {
+        parent::validate_data();
+
+        if (!isset($this->other['categoryid'])) {
+            throw new \coding_exception('The \'categoryid\' must be set in \'other\'.');
+        }
+    }
+
+    /**
+     * Returns DB mappings used with backup / restore.
+     *
+     * @return array
+     */
+    public static function get_objectid_mapping() {
+        return array('db' => 'question', 'restore' => 'question');
+    }
+
+    /**
+     * Used for maping events on restore
+     *
+     * @return array
+     */
+    public static function get_other_mapping() {
+
+        $othermapped = array();
+        $othermapped['categoryid'] = array('db' => 'question_categories', 'restore' => 'question_categories');
+        return $othermapped;
+    }
+}
+
diff --git a/lib/classes/event/question_category_base.php b/lib/classes/event/question_category_base.php
new file mode 100644 (file)
index 0000000..dce12b0
--- /dev/null
@@ -0,0 +1,72 @@
+<?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/>.
+
+/**
+ * Base class for question category events.
+ *
+ * @package    core
+ * @copyright  2016 Stephen Bourget
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core\event;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Base class for question category events
+ *
+ * @package    core
+ * @since      Moodle 3.6
+ * @copyright  2016 Stephen Bourget
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class question_category_base extends base {
+
+    /**
+     * Init method.
+     */
+    protected function init() {
+        $this->data['objecttable'] = 'question_categories';
+        $this->data['edulevel'] = self::LEVEL_TEACHING;
+    }
+
+    /**
+     * Returns relevant URL.
+     *
+     * @return \moodle_url
+     */
+    public function get_url() {
+        if ($this->courseid) {
+            $cat = $this->objectid . ',' . $this->contextid;
+            if ($this->contextlevel == CONTEXT_MODULE) {
+                return new \moodle_url('/question/edit.php', array('cmid' => $this->contextinstanceid, 'cat' => $cat));
+            }
+            return new \moodle_url('/question/edit.php', array('courseid' => $this->courseid, 'cat' => $cat));
+        }
+        // Lets try viewing from the frontpage for contexts above course.
+        return new \moodle_url('/question/category.php', array('courseid' => SITEID, 'edit' => $this->objectid));
+    }
+
+    /**
+     * Returns DB mappings used with backup / restore.
+     * @return array
+     */
+    public static function get_objectid_mapping() {
+        return array('db' => 'question_categories', 'restore' => 'question_categories');
+    }
+}
+
index dd011ec..9838871 100644 (file)
@@ -34,7 +34,7 @@ defined('MOODLE_INTERNAL') || die();
  * @copyright  2014 Mark Nelson <markn@moodle.com>
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class question_category_created extends base {
+class question_category_created extends question_category_base {
 
     /**
      * Init method.
@@ -63,26 +63,6 @@ class question_category_created extends base {
         return "The user with id '$this->userid' created the question category with id '$this->objectid'.";
     }
 
-    /**
-     * Returns relevant URL.
-     *
-     * @return \moodle_url
-     */
-    public function get_url() {
-        if ($this->courseid) {
-            $cat = $this->objectid . ',' . $this->contextid;
-            if ($this->contextlevel == CONTEXT_MODULE) {
-                return new \moodle_url('/question/edit.php', array('cmid' => $this->contextinstanceid, 'cat' => $cat));
-            }
-            return new \moodle_url('/question/edit.php', array('courseid' => $this->courseid, 'cat' => $cat));
-        }
-
-        // Bad luck, there does not seem to be any simple intelligent way
-        // to go to specific question category in context above course,
-        // let's try to edit it from frontpage which may surprisingly work.
-        return new \moodle_url('/question/category.php', array('courseid' => SITEID, 'edit' => $this->objectid));
-    }
-
     /**
      * Return the legacy event log data.
      *
@@ -97,7 +77,4 @@ class question_category_created extends base {
         return null;
     }
 
-    public static function get_objectid_mapping() {
-        return array('db' => 'question_categories', 'restore' => 'question_category');
-    }
 }
diff --git a/lib/classes/event/question_category_deleted.php b/lib/classes/event/question_category_deleted.php
new file mode 100644 (file)
index 0000000..8e6571f
--- /dev/null
@@ -0,0 +1,66 @@
+<?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/>.
+
+/**
+ * Question category deleted event.
+ *
+ * @package    core
+ * @copyright  2016 Stephen Bourget
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core\event;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Question category deleted event class.
+ *
+ * @package    core
+ * @since      Moodle 3.2
+ * @copyright  2016 Stephen Bourget
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class question_category_deleted extends question_category_base {
+
+    /**
+     * Init method.
+     */
+    protected function init() {
+        $this->data['objecttable'] = 'question_categories';
+        $this->data['crud'] = 'd';
+        $this->data['edulevel'] = self::LEVEL_TEACHING;
+    }
+
+    /**
+     * Returns localised general event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('eventquestioncategorydeleted', 'question');
+    }
+
+    /**
+     * Returns description of what happened.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return "The user with id '$this->userid' deleted the question category with id '$this->objectid'.";
+    }
+
+}
diff --git a/lib/classes/event/question_category_moved.php b/lib/classes/event/question_category_moved.php
new file mode 100644 (file)
index 0000000..b3d603b
--- /dev/null
@@ -0,0 +1,66 @@
+<?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/>.
+
+/**
+ * Question category moved event.
+ *
+ * @package    core
+ * @copyright  2016 Stephen Bourget
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core\event;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Question category moved event class.
+ *
+ * @package    core
+ * @since      Moodle 3.2
+ * @copyright  2016 Stephen Bourget
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class question_category_moved extends question_category_base {
+
+    /**
+     * Init method.
+     */
+    protected function init() {
+        $this->data['objecttable'] = 'question_categories';
+        $this->data['crud'] = 'u';
+        $this->data['edulevel'] = self::LEVEL_TEACHING;
+    }
+
+    /**
+     * Returns localised general event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('eventquestioncategorymoved', 'question');
+    }
+
+    /**
+     * Returns description of what happened.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return "The user with id '$this->userid' moved the question category with id '$this->objectid'.";
+    }
+
+}
diff --git a/lib/classes/event/question_category_updated.php b/lib/classes/event/question_category_updated.php
new file mode 100644 (file)
index 0000000..fff098d
--- /dev/null
@@ -0,0 +1,65 @@
+<?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/>.
+
+/**
+ * Question category updated event.
+ *
+ * @package    core
+ * @copyright  2016 Stephen Bourget
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core\event;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Question category updated event class.
+ *
+ * @package    core
+ * @since      Moodle 3.6
+ * @copyright  2016 Stephen Bourget
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class question_category_updated extends question_category_base {
+
+    /**
+     * Init method.
+     */
+    protected function init() {
+        $this->data['objecttable'] = 'question_categories';
+        $this->data['crud'] = 'u';
+        $this->data['edulevel'] = self::LEVEL_TEACHING;
+    }
+
+    /**
+     * Returns localised general event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('eventquestioncategoryupdated', 'question');
+    }
+
+    /**
+     * Returns description of what happened.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return "The user with id '$this->userid' updated the question category with id '$this->objectid'.";
+    }
+}
diff --git a/lib/classes/event/question_category_viewed.php b/lib/classes/event/question_category_viewed.php
new file mode 100644 (file)
index 0000000..c5558fa
--- /dev/null
@@ -0,0 +1,66 @@
+<?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/>.
+
+/**
+ * Question category viewed event.
+ *
+ * @package    core
+ * @copyright  2016 Stephen Bourget
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core\event;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Question category viewed event class.
+ *
+ * @package    core
+ * @since      Moodle 3.2
+ * @copyright  2016 Stephen Bourget
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class question_category_viewed extends question_category_base {
+
+    /**
+     * Init method.
+     */
+    protected function init() {
+        $this->data['objecttable'] = 'question_categories';
+        $this->data['crud'] = 'r';
+        $this->data['edulevel'] = self::LEVEL_TEACHING;
+    }
+
+    /**
+     * Returns localised general event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('eventquestioncategoryviewed', 'question');
+    }
+
+    /**
+     * Returns description of what happened.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return "The user with id '$this->userid' viewed the question category with id '$this->objectid'.";
+    }
+
+}
diff --git a/lib/classes/event/question_created.php b/lib/classes/event/question_created.php
new file mode 100644 (file)
index 0000000..2b884de
--- /dev/null
@@ -0,0 +1,88 @@
+<?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/>.
+
+/**
+ * Question created event.
+ *
+ * @package    core
+ * @copyright  2016 Stephen Bourget
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core\event;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Question created event class.
+ *
+ * @property-read array $other {
+ *      Extra information about the event.
+ *
+ *      - int categoryid: The ID of the category where the question resides
+ * }
+ *
+ * @package    core
+ * @since      Moodle 3.2
+ * @copyright  2016 Stephen Bourget
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class question_created extends question_base {
+
+    /**
+     * Init method.
+     */
+    protected function init() {
+        $this->data['objecttable'] = 'question';
+        $this->data['crud'] = 'c';
+        $this->data['edulevel'] = self::LEVEL_TEACHING;
+    }
+
+    /**
+     * Returns localised general event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('eventquestioncreated', 'question');
+    }
+
+    /**
+     * Returns description of what happened.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return "The user with id '$this->userid' created a question with the id of '$this->objectid'".
+                " in the category with the id '".$this->other['categoryid']."'.";
+    }
+
+    /**
+     * Returns relevant URL.
+     *
+     * @return \moodle_url
+     */
+    public function get_url() {
+        if ($this->courseid) {
+            if ($this->contextlevel == CONTEXT_MODULE) {
+                return new \moodle_url('/question/preview.php', array('cmid' => $this->contextinstanceid, 'id' => $this->objectid));
+            }
+            return new \moodle_url('/question/preview.php', array('courseid' => $this->courseid, 'id' => $this->objectid));
+        }
+        // Lets try editing from the frontpage for contexts above course.
+        return new \moodle_url('/question/preview.php', array('courseid' => SITEID, 'id' => $this->objectid));
+    }
+}
diff --git a/lib/classes/event/question_deleted.php b/lib/classes/event/question_deleted.php
new file mode 100644 (file)
index 0000000..ba7c34d
--- /dev/null
@@ -0,0 +1,83 @@
+<?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/>.
+
+/**
+ * Question deleted event.
+ *
+ * @package    core
+ * @copyright  2016 Stephen Bourget
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core\event;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Question deleted event class.
+ *
+ * @property-read array $other {
+ *      Extra information about the event.
+ *
+ *      - int categoryid: The ID of the category where the question resides
+ * }
+ *
+ * @package    core
+ * @since      Moodle 3.2
+ * @copyright  2016 Stephen Bourget
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class question_deleted extends question_base {
+
+    /**
+     * Init method.
+     */
+    protected function init() {
+        $this->data['objecttable'] = 'question';
+        $this->data['crud'] = 'd';
+        $this->data['edulevel'] = self::LEVEL_TEACHING;
+    }
+
+    /**
+     * Returns localised general event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('eventquestiondeleted', 'question');
+    }
+
+    /**
+     * Returns description of what happened.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return "The user with id '$this->userid' deleted the question with id '$this->objectid'".
+                " from the category with the id '".$this->other['categoryid']."'.";
+    }
+
+    /**
+     * Returns relevant URL.
+     * This is needed to override the function in question_base
+     *
+     * @return \moodle_url
+     */
+    public function get_url() {
+        // No URL for delete.
+        return null;
+    }
+}
diff --git a/lib/classes/event/question_moved.php b/lib/classes/event/question_moved.php
new file mode 100644 (file)
index 0000000..6f1ea7a
--- /dev/null
@@ -0,0 +1,130 @@
+<?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/>.
+
+/**
+ * Question moved event.
+ *
+ * @package    core
+ * @copyright  2016 Stephen Bourget
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core\event;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Question moved event class.
+ *
+ * @property-read array $other {
+ *      Extra information about the event.
+ *
+ *      - int categoryid: The ID of the category where the question resides
+ * }
+ *
+ * @package    core
+ * @since      Moodle 3.6
+ * @copyright  2016 Stephen Bourget
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class question_moved extends base {
+
+    /**
+     * Init method.
+     */
+    protected function init() {
+        $this->data['objecttable'] = 'question';
+        $this->data['crud'] = 'u';
+        $this->data['edulevel'] = self::LEVEL_TEACHING;
+    }
+
+    /**
+     * Returns localised general event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('eventquestionmoved', 'question');
+    }
+
+    /**
+     * Returns description of what happened.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return "The user with id '$this->userid' moved the question with the id of '$this->objectid'".
+                " from the category with the id of '".$this->other['oldcategoryid'].
+                "' to the category with the id of '".$this->other['newcategoryid']."'.";
+    }
+
+    /**
+     * Returns relevant URL.
+     *
+     * @return \moodle_url
+     */
+    public function get_url() {
+        if ($this->courseid) {
+            $cat = $this->other['newcategoryid'] . ',' . $this->contextid;
+            if ($this->contextlevel == CONTEXT_MODULE) {
+                return new \moodle_url('/question/edit.php', array('cmid' => $this->contextinstanceid, 'cat' => $cat, 'lastchanged' => $this->objectid));
+            }
+            return new \moodle_url('/question/edit.php', array('courseid' => $this->courseid, 'cat' => $cat, 'lastchanged' => $this->objectid));
+        }
+        // Lets try viewing from the frontpage for contexts above course.
+        return new \moodle_url('/question/category.php', array('courseid' => SITEID, 'edit' => $this->other['newcategoryid'], 'lastchanged' => $this->objectid));
+    }
+
+    /**
+     * Custom validations.
+     *
+     * @throws \coding_exception
+     * @return void
+     */
+    protected function validate_data() {
+        parent::validate_data();
+
+        if (!isset($this->other['oldcategoryid'])) {
+            throw new \coding_exception('The \'oldcategoryid\' must be set in \'other\'.');
+        }
+        if (!isset($this->other['newcategoryid'])) {
+            throw new \coding_exception('The \'newcategoryid\' must be set in \'other\'.');
+        }
+    }
+
+    /**
+     * Returns DB mappings used with backup / restore.
+     *
+     * @return array
+     */
+    public static function get_objectid_mapping() {
+        return array('db' => 'question', 'restore' => 'question');
+    }
+
+    /**
+     * Used for maping events on restore
+     *
+     * @return array
+     */
+    public static function get_other_mapping() {
+
+        $othermapped = array();
+        $othermapped['newcategoryid'] = array('db' => 'question_categories', 'restore' => 'question_categories');
+        $othermapped['oldcategoryid'] = array('db' => 'question_categories', 'restore' => 'question_categories');
+
+        return $othermapped;
+    }
+}
diff --git a/lib/classes/event/question_previewed.php b/lib/classes/event/question_previewed.php
new file mode 100644 (file)
index 0000000..b36508c
--- /dev/null
@@ -0,0 +1,72 @@
+<?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/>.
+
+/**
+ * Question previewed event.
+ *
+ * @package    core
+ * @copyright  2016 Stephen Bourget
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core\event;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Question previewed event class.
+ *
+ * @property-read array $other {
+ *      Extra information about the event.
+ *
+ *      - int categoryid: The ID of the category where the question resides
+ * }
+ *
+ * @package    core
+ * @since      Moodle 3.2
+ * @copyright  2016 Stephen Bourget
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class question_previewed extends question_base {
+
+    /**
+     * Init method.
+     */
+    protected function init() {
+        $this->data['objecttable'] = 'question';
+        $this->data['crud'] = 'r';
+        $this->data['edulevel'] = self::LEVEL_TEACHING;
+    }
+
+    /**
+     * Returns localised general event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('eventquestionpreviewed', 'question');
+    }
+
+    /**
+     * Returns description of what happened.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return "The user with id '$this->userid' previewed the question with the id of '$this->objectid'.";
+    }
+
+}
diff --git a/lib/classes/event/question_updated.php b/lib/classes/event/question_updated.php
new file mode 100644 (file)
index 0000000..2006c81
--- /dev/null
@@ -0,0 +1,72 @@
+<?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/>.
+
+/**
+ * Question updated event.
+ *
+ * @package    core
+ * @copyright  2016 Stephen Bourget
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core\event;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Question updated event class.
+ *
+ * @property-read array $other {
+ *      Extra information about the event.
+ *
+ *      - int categoryid: The ID of the category where the question resides
+ * }
+ *
+ * @package    core
+ * @since      Moodle 3.6
+ * @copyright  2016 Stephen Bourget
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class question_updated extends question_base {
+
+    /**
+     * Init method.
+     */
+    protected function init() {
+        $this->data['objecttable'] = 'question';
+        $this->data['crud'] = 'u';
+        $this->data['edulevel'] = self::LEVEL_TEACHING;
+    }
+
+    /**
+     * Returns localised general event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('eventquestionupdated', 'question');
+    }
+
+    /**
+     * Returns description of what happened.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return "The user with id '$this->userid' updated the question with the id of '$this->objectid'.";
+    }
+
+}
diff --git a/lib/classes/event/questions_exported.php b/lib/classes/event/questions_exported.php
new file mode 100644 (file)
index 0000000..b427353
--- /dev/null
@@ -0,0 +1,107 @@
+<?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/>.
+
+/**
+ * Questions exported event.
+ *
+ * @package    core
+ * @copyright  2016 Stephen Bourget
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core\event;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Question category exported event class.
+ *
+ * @package    core
+ * @since      Moodle 3.2
+ * @copyright  2016 Stephen Bourget
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class questions_exported extends base {
+
+    /**
+     * Init method.
+     */
+    protected function init() {
+        $this->data['crud'] = 'r';
+        $this->data['edulevel'] = self::LEVEL_TEACHING;
+    }
+
+    /**
+     * Returns localised general event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('eventquestionsexported', 'question');
+    }
+
+    /**
+     * Returns description of what happened.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return "The user with id '$this->userid' exported questions in '". $this->other['format'].
+                "' format from the category with id '".$this->other['categoryid']."'.";
+    }
+
+    /**
+     * Returns relevant URL.
+     *
+     * @return \moodle_url
+     */
+    public function get_url() {
+        if ($this->courseid) {
+            $cat = $this->other['categoryid'] . ',' . $this->contextid;
+            if ($this->contextlevel == CONTEXT_MODULE) {
+                return new \moodle_url('/question/edit.php', array('cmid' => $this->contextinstanceid, 'cat' => $cat));
+            }
+            return new \moodle_url('/question/edit.php', array('courseid' => $this->courseid, 'cat' => $cat));
+        }
+        return new \moodle_url('/question/category.php', array('courseid' => SITEID, 'edit' => $this->other['categoryid']));
+    }
+
+    /**
+     * Custom validations.
+     *
+     * @throws \coding_exception
+     * @return void
+     */
+    protected function validate_data() {
+        parent::validate_data();
+
+        if (!isset($this->other['format'])) {
+            throw new \coding_exception('The \'format\' must be set in \'other\'.');
+        }
+    }
+
+    /**
+     * Returns DB mappings used with backup / restore.
+     * This is needed to override the function in question_base
+     *
+     * @return array
+     */
+    public static function get_objectid_mapping() {
+        // No mappings.
+        return array();
+    }
+
+}
diff --git a/lib/classes/event/questions_imported.php b/lib/classes/event/questions_imported.php
new file mode 100644 (file)
index 0000000..875aa46
--- /dev/null
@@ -0,0 +1,109 @@
+<?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/>.
+
+/**
+ * Questions imported event.
+ *
+ * @package    core
+ * @copyright  2016 Stephen Bourget
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core\event;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Question category imported event class.
+ *
+ * @package    core
+ * @since      Moodle 3.2
+ * @copyright  2016 Stephen Bourget
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class questions_imported extends question_base {
+
+    /**
+     * Init method.
+     */
+    protected function init() {
+        $this->data['crud'] = 'c';
+        $this->data['edulevel'] = self::LEVEL_TEACHING;
+    }
+
+    /**
+     * Returns localised general event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('eventquestionsimported', 'question');
+    }
+
+    /**
+     * Returns description of what happened.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return "The user with id '$this->userid' imported questions in '". $this->other['format'].
+                "' format into the category with id '".$this->other['categoryid']."'.";
+    }
+
+    /**
+     * Returns relevant URL.
+     *
+     * @return \moodle_url
+     */
+    public function get_url() {
+        if ($this->courseid) {
+            $cat = $this->other['categoryid'] . ',' . $this->contextid;
+            if ($this->contextlevel == CONTEXT_MODULE) {
+                return new \moodle_url('/question/edit.php', array('cmid' => $this->contextinstanceid, 'cat' => $cat));
+            }
+            return new \moodle_url('/question/edit.php', array('courseid' => $this->courseid, 'cat' => $cat));
+        }
+        return new \moodle_url('/question/category.php', array('courseid' => SITEID, 'edit' => $this->other['categoryid']));
+    }
+
+    /**
+     * Custom validations.
+     *
+     * @throws \coding_exception
+     * @return void
+     */
+    protected function validate_data() {
+        parent::validate_data();
+
+        if (!isset($this->other['categoryid'])) {
+            throw new \coding_exception('The \'categoryid\' must be set in \'other\'.');
+        }
+        if (!isset($this->other['format'])) {
+            throw new \coding_exception('The \'format\' must be set in \'other\'.');
+        }
+    }
+
+    /**
+     * Returns DB mappings used with backup / restore.
+     * This is needed to override the function in question_base
+     *
+     * @return array
+     */
+    public static function get_objectid_mapping() {
+        // No mappings.
+        return array();
+    }
+}
index a465c7d..2252758 100644 (file)
@@ -380,6 +380,16 @@ function question_delete_question($questionid) {
     // Finally delete the question record itself
     $DB->delete_records('question', array('id' => $questionid));
     question_bank::notify_question_edited($questionid);
+
+    // Log the deletion of this question.
+    $eventparams = array(
+        'contextid' => $question->contextid,
+        'objectid' => $question->id,
+        'other' => array('categoryid' => $question->category)
+    );
+
+    $event = \core\event\question_deleted::create($eventparams);
+    $event->trigger();
 }
 
 /**
@@ -673,7 +683,7 @@ function question_move_questions_to_category($questionids, $newcategoryid) {
             array('id' => $newcategoryid));
     list($questionidcondition, $params) = $DB->get_in_or_equal($questionids);
     $questions = $DB->get_records_sql("
-            SELECT q.id, q.qtype, qc.contextid, q.idnumber
+            SELECT q.id, q.qtype, qc.contextid, q.idnumber, q.category
               FROM {question} q
               JOIN {question_categories} qc ON q.category = qc.id
              WHERE  q.id $questionidcondition", $params);
@@ -703,6 +713,15 @@ function question_move_questions_to_category($questionids, $newcategoryid) {
             $q->idnumber = $question->idnumber . '_' . $unique;
             $DB->update_record('question', $q);
         }
+
+        // Log this question move.
+        $eventparams = array(
+            'contextid' => $question->contextid,
+            'objectid' => $question->id,
+            'other' => array('oldcategoryid' => $question->category, 'newcategoryid' => $newcategoryid)
+        );
+        $event = \core\event\question_moved::create($eventparams);
+        $event->trigger();
     }
 
     // Move the questions themselves.
index 8e4ae0e..60f3c3d 100644 (file)
@@ -81,6 +81,13 @@ if ($param->moveupcontext || $param->movedowncontext) {
         print_error('invalidcontext');
     }
     $oldcat = $DB->get_record('question_categories', array('id' => $catid), '*', MUST_EXIST);
+    // Log the move to another context.
+    $params = array(
+        'objectid' => explode(',',$pagevars['cat'], -1)[0],
+        'contextid' => $param->tocontext,
+    );
+    $event = \core\event\question_category_moved::create($params);
+    $event->trigger();
     $qcobject->update_category($catid, "{$newtopcat->id},{$param->tocontext}", $oldcat->name, $oldcat->info);
     // The previous line does a redirect().
 }
index a339462..f1193f3 100644 (file)
@@ -76,6 +76,27 @@ class question_category_list extends moodle_list {
         $topcategory = question_get_top_category($item->item->contextid, true);
         return $topcategory->id;
     }
+
+    public function process_actions($left, $right, $moveup, $movedown) {
+        if (!empty($left)) {
+            // Moved Left (In to another category).
+            $params = array(
+                'objectid' => $left,
+                'contextid' => $this->context->id
+            );
+            $event = \core\event\question_category_moved::create($params);
+            $event->trigger();
+        } else if (!empty($right)) {
+            // Moved Right (Out of the current category).
+            $params = array(
+                'objectid' => $right,
+                'contextid' => $this->context->id
+            );
+            $event = \core\event\question_category_moved::create($params);
+            $event->trigger();
+            }
+        parent::process_actions($left, $right, $moveup, $movedown);
+    }
 }
 
 
@@ -375,6 +396,15 @@ class question_category_object {
 
         /// Finally delete the category itself
         $DB->delete_records("question_categories", array("id" => $category->id));
+
+        // Log the deletion of this category.
+        $params = array(
+            'objectid' => $category->id,
+            'contextid' => $category->contextid
+        );
+        $event = \core\event\question_category_deleted::create($params);
+        $event->trigger();
+
     }
 
     public function move_questions_and_delete_category($oldcat, $newcat){
@@ -458,9 +488,18 @@ class question_category_object {
 
     /**
      * Updates an existing category with given params
+     *
+     * @param int $updateid
+     * @param int $newparent
+     * @param string $newname
+     * @param string $newinfo
+     * @param int $newinfoformat
+     * @param int $idnumber
+     * @param bool $redirect
+     * @return int
      */
     public function update_category($updateid, $newparent, $newname, $newinfo, $newinfoformat = FORMAT_HTML,
-            $idnumber = null) {
+            $idnumber = null, $redirect = true) {
         global $CFG, $DB;
         if (empty($newname)) {
             print_error('categorynamecantbeblank', 'question');
@@ -519,6 +558,14 @@ class question_category_object {
         }
         $DB->update_record('question_categories', $cat);
 
+        // Log the update of this category.
+        $params = array(
+            'objectid' => $cat->id,
+            'contextid' => $cat->contextid
+        );
+        $event = \core\event\question_category_updated::create($params);
+        $event->trigger();
+
         // If the category name has changed, rename any random questions in that category.
         if ($oldcat->name != $cat->name) {
             $where = "qtype = 'random' AND category = ? AND " . $DB->sql_compare_text('questiontext') . " = ?";
@@ -538,6 +585,8 @@ class question_category_object {
 
         // Cat param depends on the context id, so update it.
         $this->pageurl->param('cat', $updateid . ',' . $tocontextid);
-        redirect($this->pageurl);
+        if ($redirect) {
+            redirect($this->pageurl); // Always redirect after successful action.
+        }
     }
 }
index 9d3295e..ef8cff0 100644 (file)
@@ -39,8 +39,6 @@ $PAGE->set_url($url);
 $questionbank = new core_question\bank\view($contexts, $thispageurl, $COURSE, $cm);
 $questionbank->process_actions();
 
-// TODO log this page view.
-
 $context = $contexts->lowest();
 $streditingquestions = get_string('editquestions', 'question');
 $PAGE->set_title($streditingquestions);
@@ -57,4 +55,13 @@ $questionbank->display('questions', $pagevars['qpage'], $pagevars['qperpage'],
         $pagevars['qbshowtext'], $pagevars['qtagids']);
 echo "</div>\n";
 
+// Log the view of this category.
+$categoryid = explode(',' , urldecode($pagevars['cat']));
+$params = array(
+    'objectid' => $categoryid[0],
+    'context' => $context
+);
+$event = \core\event\question_category_viewed::create($params);
+$event->trigger();
+
 echo $OUTPUT->footer();
index 434526d..81b86fa 100644 (file)
@@ -77,6 +77,14 @@ if ($from_form = $export_form->get_data()) {
     echo get_string('yourfileshoulddownload', 'question', $export_url->out());
     echo $OUTPUT->box_end();
 
+        // Log the export of these questions.
+        $eventparams = array(
+            'contextid' => $category->contextid,
+            'other' => array('format' => $from_form->format, 'categoryid' => $category->id)
+        );
+        $event = \core\event\questions_exported::create($eventparams);
+        $event->trigger();
+
     // Don't allow force download for behat site, as pop-up can't be handled by selenium.
     if (!defined('BEHAT_SITE_RUNNING')) {
         $PAGE->requires->js_function_call('document.location.replace', array($export_url->out(false)), false, 1);
index c9dc091..56faf52 100644 (file)
@@ -129,6 +129,14 @@ if ($form = $import_form->get_data()) {
         print_error('cannotimport', '', $thispageurl->out());
     }
 
+        // Log the import into this category.
+        $eventparams = array(
+            'contextid' => $category->contextid,
+            'other' => array('format' => $form->format, 'categoryid' => $category->id)
+        );
+        $event = \core\event\questions_imported::create($eventparams);
+        $event->trigger();
+
     $params = $thispageurl->params() + array(
         'category' => $qformat->category->id . ',' . $qformat->category->contextid);
     echo $OUTPUT->continue_button(new moodle_url('edit.php', $params));
index 2f0908b..230ccc9 100644 (file)
@@ -280,6 +280,16 @@ if (question_has_capability_on($question, 'view')) {
             get_string('exportonequestion', 'question'));
 }
 
+// Log the preview of this question.
+$eventparams = array(
+    'context' => $context,
+    'objectid' => $question->id,
+    'other' => array('categoryid' => $question->category)
+);
+$newquestion = true;
+$event = \core\event\question_previewed::create($eventparams);
+$event->trigger();
+
 // Display the settings form.
 $optionsform->display();
 
index 357422e..1133738 100644 (file)
@@ -73,4 +73,398 @@ class core_question_events_testcase extends advanced_testcase {
         $this->assertEventLegacyLogData($expected, $event);
         $this->assertEventContextNotUsed($event);
     }
+
+    /**
+     * Test the question category deleted event.
+     */
+    public function test_question_category_deleted() {
+        $this->setAdminUser();
+        $course = $this->getDataGenerator()->create_course();
+        $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id));
+
+        $contexts = new question_edit_contexts(context_module::instance($quiz->cmid));
+
+        $defaultcategoryobj = question_make_default_categories(array($contexts->lowest()));
+        $defaultcategory = $defaultcategoryobj->id . ',' . $defaultcategoryobj->contextid;
+
+        $qcobject = new question_category_object(
+            1,
+            new moodle_url('/mod/quiz/edit.php', array('cmid' => $quiz->cmid)),
+            $contexts->having_one_edit_tab_cap('categories'),
+            $defaultcategoryobj->id,
+            $defaultcategory,
+            null,
+            $contexts->having_cap('moodle/question:add'));
+
+        // Create the category.
+        $categoryid = $qcobject->add_category($defaultcategory, 'newcategory', '', true);
+
+        // Trigger and capture the event.
+        $sink = $this->redirectEvents();
+        $qcobject->delete_category($categoryid);
+        $events = $sink->get_events();
+        $event = reset($events);
+
+        // Check that the event data is valid.
+        $this->assertInstanceOf('\core\event\question_category_deleted', $event);
+        $this->assertEquals(context_module::instance($quiz->cmid), $event->get_context());
+        $this->assertEquals($categoryid, $event->objectid);
+        $this->assertDebuggingNotCalled();
+    }
+
+    /**
+     * Test the question category updated event.
+     */
+    public function test_question_category_updated() {
+        $this->setAdminUser();
+        $course = $this->getDataGenerator()->create_course();
+        $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id));
+
+        $contexts = new question_edit_contexts(context_module::instance($quiz->cmid));
+
+        $defaultcategoryobj = question_make_default_categories(array($contexts->lowest()));
+        $defaultcategory = $defaultcategoryobj->id . ',' . $defaultcategoryobj->contextid;
+
+        $qcobject = new question_category_object(
+            1,
+            new moodle_url('/mod/quiz/edit.php', array('cmid' => $quiz->cmid)),
+            $contexts->having_one_edit_tab_cap('categories'),
+            $defaultcategoryobj->id,
+            $defaultcategory,
+            null,
+            $contexts->having_cap('moodle/question:add'));
+
+        // Create the category.
+        $categoryid = $qcobject->add_category($defaultcategory, 'newcategory', '', true);
+
+        // Trigger and capture the event.
+        $sink = $this->redirectEvents();
+        $qcobject->update_category($categoryid, $defaultcategory, 'updatedcategory', '', FORMAT_HTML, '', false);
+        $events = $sink->get_events();
+        $event = reset($events);
+
+        // Check that the event data is valid.
+        $this->assertInstanceOf('\core\event\question_category_updated', $event);
+        $this->assertEquals(context_module::instance($quiz->cmid), $event->get_context());
+        $this->assertEquals($categoryid, $event->objectid);
+        $this->assertDebuggingNotCalled();
+    }
+
+    /**
+     * Test the question category viewed event.
+     * There is no external API for viewing the category, so the unit test will simply
+     * create and trigger the event and ensure data is returned as expected.
+     */
+    public function test_question_category_viewed() {
+
+        $this->setAdminUser();
+        $course = $this->getDataGenerator()->create_course();
+        $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id));
+
+        $contexts = new question_edit_contexts(context_module::instance($quiz->cmid));
+
+        $defaultcategoryobj = question_make_default_categories(array($contexts->lowest()));
+        $defaultcategory = $defaultcategoryobj->id . ',' . $defaultcategoryobj->contextid;
+
+        $qcobject = new question_category_object(
+            1,
+            new moodle_url('/mod/quiz/edit.php', array('cmid' => $quiz->cmid)),
+            $contexts->having_one_edit_tab_cap('categories'),
+            $defaultcategoryobj->id,
+            $defaultcategory,
+            null,
+            $contexts->having_cap('moodle/question:add'));
+
+        // Create the category.
+        $categoryid = $qcobject->add_category($defaultcategory, 'newcategory', '', true);
+
+        // Log the view of this category.
+        $params = array(
+            'objectid' => $categoryid,
+            'context' => context_module::instance($quiz->cmid)
+        );
+
+        $event = \core\event\question_category_viewed::create($params);
+
+        // Trigger and capture the event.
+        $sink = $this->redirectEvents();
+        $event->trigger();
+        $events = $sink->get_events();
+        $event = reset($events);
+
+        // Check that the event data is valid.
+        $this->assertInstanceOf('\core\event\question_category_viewed', $event);
+        $this->assertEquals(context_module::instance($quiz->cmid), $event->get_context());
+        $this->assertEquals($categoryid, $event->objectid);
+        $this->assertDebuggingNotCalled();
+
+    }
+
+    /**
+     * Test the questions imported event.
+     * There is no easy way to trigger this event using the API, so the unit test will simply
+     * create and trigger the event and ensure data is returned as expected.
+     */
+    public function test_questions_imported() {
+
+        $this->setAdminUser();
+        $course = $this->getDataGenerator()->create_course();
+        $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id));
+
+        $contexts = new question_edit_contexts(context_module::instance($quiz->cmid));
+
+        $defaultcategoryobj = question_make_default_categories(array($contexts->lowest()));
+        $defaultcategory = $defaultcategoryobj->id . ',' . $defaultcategoryobj->contextid;
+
+        $qcobject = new question_category_object(
+            1,
+            new moodle_url('/mod/quiz/edit.php', array('cmid' => $quiz->cmid)),
+            $contexts->having_one_edit_tab_cap('categories'),
+            $defaultcategoryobj->id,
+            $defaultcategory,
+            null,
+            $contexts->having_cap('moodle/question:add'));
+
+        // Create the category.
+        $categoryid = $qcobject->add_category($defaultcategory, 'newcategory', '', true);
+
+        // Log the view of this category.
+        $params = array(
+            'context' => context_module::instance($quiz->cmid),
+            'other' => array('categoryid' => $categoryid, 'format' => 'testformat')
+        );
+
+        $event = \core\event\questions_imported::create($params);
+
+        // Trigger and capture the event.
+        $sink = $this->redirectEvents();
+        $event->trigger();
+        $events = $sink->get_events();
+        $event = reset($events);
+
+        // Check that the event data is valid.
+        $this->assertInstanceOf('\core\event\questions_imported', $event);
+        $this->assertEquals(context_module::instance($quiz->cmid), $event->get_context());
+        $this->assertEquals($categoryid, $event->other['categoryid']);
+        $this->assertEquals('testformat', $event->other['format']);
+        $this->assertDebuggingNotCalled();
+
+    }
+
+    /**
+     * Test the questions exported event.
+     * There is no easy way to trigger this event using the API, so the unit test will simply
+     * create and trigger the event and ensure data is returned as expected.
+     */
+    public function test_questions_exported() {
+
+        $this->setAdminUser();
+        $course = $this->getDataGenerator()->create_course();
+        $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id));
+
+        $contexts = new question_edit_contexts(context_module::instance($quiz->cmid));
+
+        $defaultcategoryobj = question_make_default_categories(array($contexts->lowest()));
+        $defaultcategory = $defaultcategoryobj->id . ',' . $defaultcategoryobj->contextid;
+
+        $qcobject = new question_category_object(
+            1,
+            new moodle_url('/mod/quiz/edit.php', array('cmid' => $quiz->cmid)),
+            $contexts->having_one_edit_tab_cap('categories'),
+            $defaultcategoryobj->id,
+            $defaultcategory,
+            null,
+            $contexts->having_cap('moodle/question:add'));
+
+        // Create the category.
+        $categoryid = $qcobject->add_category($defaultcategory, 'newcategory', '', true);
+
+        // Log the view of this category.
+        $params = array(
+            'context' => context_module::instance($quiz->cmid),
+            'other' => array('categoryid' => $categoryid, 'format' => 'testformat')
+        );
+
+        $event = \core\event\questions_exported::create($params);
+
+        // Trigger and capture the event.
+        $sink = $this->redirectEvents();
+        $event->trigger();
+        $events = $sink->get_events();
+        $event = reset($events);
+
+        // Check that the event data is valid.
+        $this->assertInstanceOf('\core\event\questions_exported', $event);
+        $this->assertEquals(context_module::instance($quiz->cmid), $event->get_context());
+        $this->assertEquals($categoryid, $event->other['categoryid']);
+        $this->assertEquals('testformat', $event->other['format']);
+        $this->assertDebuggingNotCalled();
+
+    }
+
+    /**
+     * Test the question created event.
+     */
+    public function test_question_created() {
+
+        $this->setAdminUser();
+        $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
+
+        $cat = $generator->create_question_category(array(
+                'name' => 'My category', 'sortorder' => 1));
+
+        // Trigger and capture the event.
+        $sink = $this->redirectEvents();
+        $questiondata = $generator->create_question('description', null, array('category' => $cat->id));
+        $question = question_bank::load_question($questiondata->id);
+
+        $events = $sink->get_events();
+        $event = reset($events);
+
+        // Check that the event data is valid.
+        $this->assertInstanceOf('\core\event\question_created', $event);
+        $this->assertEquals($question->id, $event->objectid);
+        $this->assertEquals($cat->id, $event->other['categoryid']);
+        $this->assertDebuggingNotCalled();
+
+    }
+
+    /**
+     * Test the question deleted event.
+     */
+    public function test_question_deleted() {
+
+        $this->setAdminUser();
+        $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
+
+        $cat = $generator->create_question_category(array(
+                'name' => 'My category', 'sortorder' => 1));
+
+        $questiondata = $generator->create_question('description', null, array('category' => $cat->id));
+        $question = question_bank::load_question($questiondata->id);
+
+        // Trigger and capture the event.
+        $sink = $this->redirectEvents();
+        question_delete_question($question->id);
+        $events = $sink->get_events();
+        $event = reset($events);
+
+        // Check that the event data is valid.
+        $this->assertInstanceOf('\core\event\question_deleted', $event);
+        $this->assertEquals($question->id, $event->objectid);
+        $this->assertEquals($cat->id, $event->other['categoryid']);
+        $this->assertDebuggingNotCalled();
+
+    }
+
+    /**
+     * Test the question updated event.
+     */
+    public function test_question_updated() {
+
+        global $CFG;
+        require_once($CFG->dirroot . '/question/type/description/questiontype.php');
+        require_once($CFG->dirroot . '/question/type/edit_question_form.php');
+        require_once($CFG->dirroot . '/question/type/description/edit_description_form.php');
+
+        $this->setAdminUser();
+        $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
+
+        $cat = $generator->create_question_category(array(
+                'name' => 'My category', 'sortorder' => 1));
+
+        $questiondata = $generator->create_question('description', null, array('category' => $cat->id));
+        $question = question_bank::load_question($questiondata->id);
+
+        $qtype = new qtype_description();
+        $formdata = test_question_maker::get_question_form_data('description');
+        $formdata->category = "{$cat->id},{$cat->contextid}";
+        qtype_description_edit_form::mock_submit((array)$formdata);
+
+        $form = qtype_description_test_helper::get_question_editing_form($cat, $questiondata);
+        $fromform = $form->get_data();
+
+        // Trigger and capture the event.
+        $sink = $this->redirectEvents();
+        $qtype->save_question($questiondata, $fromform);
+        $events = $sink->get_events();
+        $event = reset($events);
+
+        // Check that the event data is valid.
+        $this->assertInstanceOf('\core\event\question_updated', $event);
+        $this->assertEquals($question->id, $event->objectid);
+        $this->assertEquals($cat->id, $event->other['categoryid']);
+        $this->assertDebuggingNotCalled();
+
+    }
+
+    /**
+     * Test the question moved event.
+     */
+    public function test_question_moved() {
+
+        $this->setAdminUser();
+        $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
+
+        $cat1 = $generator->create_question_category(array(
+                'name' => 'My category 1', 'sortorder' => 1));
+
+        $cat2 = $generator->create_question_category(array(
+                'name' => 'My category 2', 'sortorder' => 2));
+
+        $questiondata = $generator->create_question('description', null, array('category' => $cat1->id));
+        $question = question_bank::load_question($questiondata->id);
+
+        // Trigger and capture the event.
+        $sink = $this->redirectEvents();
+        question_move_questions_to_category(array($question->id), $cat2->id);
+        $events = $sink->get_events();
+        $event = reset($events);
+
+        // Check that the event data is valid.
+        $this->assertInstanceOf('\core\event\question_moved', $event);
+        $this->assertEquals($question->id, $event->objectid);
+        $this->assertEquals($cat1->id, $event->other['oldcategoryid']);
+        $this->assertEquals($cat2->id, $event->other['newcategoryid']);
+        $this->assertDebuggingNotCalled();
+
+    }
+
+    /**
+     * Test the question viewed event.
+     * There is no external API for viewing the question, so the unit test will simply
+     * create and trigger the event and ensure data is returned as expected.
+     */
+    public function test_question_viewed() {
+
+        $this->setAdminUser();
+        $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
+
+        $cat = $generator->create_question_category(array(
+                'name' => 'My category', 'sortorder' => 1));
+
+        $questiondata = $generator->create_question('description', null, array('category' => $cat->id));
+        $question = question_bank::load_question($questiondata->id);
+
+        $params = array(
+            'objectid' => $question->id,
+            'context' => context::instance_by_id($cat->contextid),
+            'other' => array('categoryid' => $question->category)
+        );
+
+        $event = \core\event\question_previewed::create($params);
+
+        // Trigger and capture the event.
+        $sink = $this->redirectEvents();
+        $event->trigger();
+        $events = $sink->get_events();
+        $event = reset($events);
+
+        // Check that the event data is valid.
+        $this->assertInstanceOf('\core\event\question_previewed', $event);
+        $this->assertEquals($question->id, $event->objectid);
+        $this->assertEquals($cat->id, $event->other['categoryid']);
+        $this->assertDebuggingNotCalled();
+
+    }
 }
index 294975d..a7806a1 100644 (file)
@@ -364,12 +364,14 @@ class question_type {
         }
 
         // If the question is new, create it.
+        $newquestion = false;
         if (empty($question->id)) {
             // Set the unique code.
             $question->stamp = make_unique_id_code();
             $question->createdby = $USER->id;
             $question->timecreated = time();
             $question->id = $DB->insert_record('question', $question);
+            $newquestion = true;
         }
 
         // Now, whether we are updating a existing question, or creating a new
@@ -391,6 +393,26 @@ class question_type {
         }
         $DB->update_record('question', $question);
 
+        if ($newquestion) {
+            // Log the creation of this question.
+            $eventparams = array(
+                'context' => $context,
+                'objectid' => $question->id,
+                'other' => array('categoryid' => $question->category)
+            );
+            $event = \core\event\question_created::create($eventparams);
+            $event->trigger();
+        } else {
+            // Log the update of this question.
+            $eventparams = array(
+                'context' => $context,
+                'objectid' => $question->id,
+                'other' => array('categoryid' => $question->category)
+            );
+            $event = \core\event\question_updated::create($eventparams);
+            $event->trigger();
+        }
+
         // Now to save all the answers and type-specific options.
         $form->id = $question->id;
         $form->qtype = $question->qtype;
index 0809475..af28588 100644 (file)
@@ -29,7 +29,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$version  = 2019042700.00;              // YYYYMMDD      = weekly release date of this DEV branch.
+$version  = 2019042700.01;              // YYYYMMDD      = weekly release date of this DEV branch.
                                         //         RR    = release increments - 00 in DEV branches.
                                         //           .XX = incremental changes.