Merge branch 'event_monitor' of git://github.com/ankitagarwal/moodle
authorDamyon Wiese <damyon@moodle.com>
Thu, 16 Oct 2014 05:20:45 +0000 (13:20 +0800)
committerDamyon Wiese <damyon@moodle.com>
Thu, 16 Oct 2014 05:20:45 +0000 (13:20 +0800)
44 files changed:
admin/tool/monitor/classes/eventlist.php [new file with mode: 0644]
admin/tool/monitor/classes/eventobservers.php [new file with mode: 0644]
admin/tool/monitor/classes/notification_task.php [new file with mode: 0644]
admin/tool/monitor/classes/output/helpicon/renderable.php [new file with mode: 0644]
admin/tool/monitor/classes/output/helpicon/renderer.php [new file with mode: 0644]
admin/tool/monitor/classes/output/managerules/renderable.php [new file with mode: 0644]
admin/tool/monitor/classes/output/managerules/renderer.php [new file with mode: 0644]
admin/tool/monitor/classes/output/managesubs/renderer.php [new file with mode: 0644]
admin/tool/monitor/classes/output/managesubs/rules.php [new file with mode: 0644]
admin/tool/monitor/classes/output/managesubs/subs.php [new file with mode: 0644]
admin/tool/monitor/classes/rule.php [new file with mode: 0644]
admin/tool/monitor/classes/rule_form.php [new file with mode: 0644]
admin/tool/monitor/classes/rule_manager.php [new file with mode: 0644]
admin/tool/monitor/classes/subscription.php [new file with mode: 0644]
admin/tool/monitor/classes/subscription_manager.php [new file with mode: 0644]
admin/tool/monitor/db/access.php [new file with mode: 0644]
admin/tool/monitor/db/events.php [new file with mode: 0644]
admin/tool/monitor/db/install.xml [new file with mode: 0644]
admin/tool/monitor/db/messages.php [new file with mode: 0644]
admin/tool/monitor/edit.php [new file with mode: 0644]
admin/tool/monitor/help.php [new file with mode: 0644]
admin/tool/monitor/help_ajax.php [new file with mode: 0644]
admin/tool/monitor/index.php [new file with mode: 0644]
admin/tool/monitor/lang/en/tool_monitor.php [new file with mode: 0644]
admin/tool/monitor/lib.php [new file with mode: 0644]
admin/tool/monitor/managerules.php [new file with mode: 0644]
admin/tool/monitor/settings.php [new file with mode: 0644]
admin/tool/monitor/tests/behat/rule.feature [new file with mode: 0644]
admin/tool/monitor/tests/behat/subscription.feature [new file with mode: 0644]
admin/tool/monitor/tests/eventobservers_test.php [new file with mode: 0644]
admin/tool/monitor/tests/generator/lib.php [new file with mode: 0644]
admin/tool/monitor/tests/generator_test.php [new file with mode: 0644]
admin/tool/monitor/tests/rule_manager_test.php [new file with mode: 0644]
admin/tool/monitor/version.php [new file with mode: 0644]
admin/tool/monitor/yui/build/moodle-tool_monitor-dropdown/moodle-tool_monitor-dropdown-debug.js [new file with mode: 0644]
admin/tool/monitor/yui/build/moodle-tool_monitor-dropdown/moodle-tool_monitor-dropdown-min.js [new file with mode: 0644]
admin/tool/monitor/yui/build/moodle-tool_monitor-dropdown/moodle-tool_monitor-dropdown.js [new file with mode: 0644]
admin/tool/monitor/yui/src/dropdown/build.json [new file with mode: 0644]
admin/tool/monitor/yui/src/dropdown/js/dropdown.js [new file with mode: 0644]
admin/tool/monitor/yui/src/dropdown/meta/dropdown.json [new file with mode: 0644]
lib/classes/plugin_manager.php
lib/navigationlib.php
lib/upgrade.txt
lib/weblib.php

diff --git a/admin/tool/monitor/classes/eventlist.php b/admin/tool/monitor/classes/eventlist.php
new file mode 100644 (file)
index 0000000..989bc3c
--- /dev/null
@@ -0,0 +1,221 @@
+<?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/>.
+
+/**
+ * Event documentation
+ *
+ * @package    tool_monitor
+ * @copyright  2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_monitor;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Class for returning event information.
+ *
+ * @since      Moodle 2.8
+ * @package    tool_monitor
+ * @copyright  2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class eventlist {
+    /**
+     * Return all of the core event files.
+     *
+     * @return array Core events.
+     */
+    protected static function get_core_eventlist() {
+        global $CFG;
+
+        // Disable developer debugging as deprecated events will fire warnings.
+        // Setup backup variables to restore the following settings back to what they were when we are finished.
+        $debuglevel          = $CFG->debug;
+        $debugdisplay        = $CFG->debugdisplay;
+        $debugdeveloper      = $CFG->debugdeveloper;
+        $CFG->debug          = 0;
+        $CFG->debugdisplay   = false;
+        $CFG->debugdeveloper = false;
+
+        $eventinformation = array();
+        $directory = $CFG->libdir . '/classes/event';
+        $files = self::get_file_list($directory);
+
+        // Remove exceptional events that will cause problems being displayed.
+        if (isset($files['unknown_logged'])) {
+            unset($files['unknown_logged']);
+        }
+        foreach ($files as $file => $location) {
+            $classname = '\\core\\event\\' . $file;
+            // Check to see if this is actually a valid event.
+            if (method_exists($classname, 'get_static_info')) {
+                $ref = new \ReflectionClass($classname);
+                // Ignore abstracts.
+                if (!$ref->isAbstract() && $file != 'manager') {
+                    $eventinformation[$classname] = $classname::get_name();
+                }
+            }
+        }
+        // Now enable developer debugging as event information has been retrieved.
+        $CFG->debug          = $debuglevel;
+        $CFG->debugdisplay   = $debugdisplay;
+        $CFG->debugdeveloper = $debugdeveloper;
+        return $eventinformation;
+    }
+
+    /**
+     * This function returns an array of all events for the plugins of the system.
+     *
+     * @param bool $withoutcomponent Return an eventlist without associated components.
+     *
+     * @return array A list of events from all plug-ins.
+     */
+    protected static function get_non_core_eventlist($withoutcomponent = false) {
+        global $CFG;
+        // Disable developer debugging as deprecated events will fire warnings.
+        // Setup backup variables to restore the following settings back to what they were when we are finished.
+        $debuglevel          = $CFG->debug;
+        $debugdisplay        = $CFG->debugdisplay;
+        $debugdeveloper      = $CFG->debugdeveloper;
+        $CFG->debug          = 0;
+        $CFG->debugdisplay   = false;
+        $CFG->debugdeveloper = false;
+
+        $noncorepluginlist = array();
+        $plugintypes = \core_component::get_plugin_types();
+        foreach ($plugintypes as $plugintype => $notused) {
+            $pluginlist = \core_component::get_plugin_list($plugintype);
+            foreach ($pluginlist as $plugin => $directory) {
+                $plugindirectory = $directory . '/classes/event';
+                foreach (self::get_file_list($plugindirectory) as $eventname => $notused) {
+                    $plugineventname = '\\' . $plugintype . '_' . $plugin . '\\event\\' . $eventname;
+                    // Check that this is actually an event.
+                    if (method_exists($plugineventname, 'get_static_info')  && $plugin != 'monitor') { // No selfie here.
+                        $ref = new \ReflectionClass($plugineventname);
+                        if (!$ref->isAbstract() && $plugin != 'legacy') {
+                            if ($withoutcomponent) {
+                                $noncorepluginlist[$plugineventname] = $plugineventname::get_name();
+                            } else {
+                                $noncorepluginlist[$plugintype . '_' . $plugin][$plugineventname] = $plugineventname::get_name();
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        // Now enable developer debugging as event information has been retrieved.
+        $CFG->debug          = $debuglevel;
+        $CFG->debugdisplay   = $debugdisplay;
+        $CFG->debugdeveloper = $debugdeveloper;
+
+        return $noncorepluginlist;
+    }
+
+    /**
+     * Returns a list of files with a full directory path in a specified directory.
+     *
+     * @param string $directory location of files.
+     * @return array full location of files from the specified directory.
+     */
+    protected static function get_file_list($directory) {
+        global $CFG;
+        $directoryroot = $CFG->dirroot;
+        $finalfiles = array();
+        if (is_dir($directory)) {
+            if ($handle = opendir($directory)) {
+                $files = scandir($directory);
+                foreach ($files as $file) {
+                    if ($file != '.' && $file != '..') {
+                        // Ignore the file if it is external to the system.
+                        if (strrpos($directory, $directoryroot) !== false) {
+                            $location = substr($directory, strlen($directoryroot));
+                            $name = substr($file, 0, -4);
+                            $finalfiles[$name] = $location  . '/' . $file;
+                        }
+                    }
+                }
+            }
+        }
+        return $finalfiles;
+    }
+
+    /**
+     * Get a list of events present in the system.
+     *
+     * @param bool $withoutcomponent Return an eventlist without associated components.
+     *
+     * @return array list of events present in the system.
+     */
+    public static function get_all_eventlist($withoutcomponent = false) {
+        if ($withoutcomponent) {
+            $return = array_merge(self::get_core_eventlist(), self::get_non_core_eventlist($withoutcomponent));
+            array_multisort($return, SORT_NATURAL);
+        } else {
+            $return = array_merge(array('core' => self::get_core_eventlist()),
+                    self::get_non_core_eventlist($withoutcomponent = false));
+        }
+        return $return;
+    }
+
+    /**
+     * Return list of plugins that have events.
+     *
+     * @param array $eventlist a list of events present in the system {@link eventlist::get_all_eventlist}.
+     *
+     * @return array list of plugins with human readable name.
+     */
+    public static function get_plugin_list($eventlist = array()) {
+        if (empty($eventlist)) {
+            $eventlist = self::get_all_eventlist();
+        }
+        $plugins = array_keys($eventlist);
+        $return = array();
+        foreach ($plugins as $plugin) {
+            if ($plugin === 'core') {
+                $return[$plugin] = get_string('core', 'tool_monitor');
+            } else if (get_string_manager()->string_exists('pluginname', $plugin)) {
+                $return[$plugin] = get_string('pluginname', $plugin);
+            } else {
+                $return[$plugin] = $plugin;
+            }
+        }
+
+        return $return;
+    }
+
+    /**
+     * validate if the given event belongs to the given plugin.
+     *
+     * @param string $plugin Frankenstyle name of the plugin.
+     * @param string $eventname Full qualified event name.
+     * @param array $eventlist List of events generated by {@link eventlist::get_all_eventlist}
+     *
+     * @return bool Returns true if the selected event belongs to the selected plugin, false otherwise.
+     */
+    public static function validate_event_plugin($plugin, $eventname, $eventlist = array()) {
+        if (empty($eventlist)) {
+            $eventlist = self::get_all_eventlist();
+        }
+        if (isset($eventlist[$plugin][$eventname])) {
+            return true;
+        }
+
+        return false;
+    }
+}
diff --git a/admin/tool/monitor/classes/eventobservers.php b/admin/tool/monitor/classes/eventobservers.php
new file mode 100644 (file)
index 0000000..27d3d29
--- /dev/null
@@ -0,0 +1,210 @@
+<?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/>.
+
+/**
+ * Observer class containing methods monitoring various events.
+ *
+ * @package    tool_monitor
+ * @copyright  2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_monitor;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Observer class containing methods monitoring various events.
+ *
+ * @since      Moodle 2.8
+ * @package    tool_monitor
+ * @copyright  2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class eventobservers {
+
+    /** @var array $buffer buffer of events. */
+    protected $buffer = array();
+
+    /** @var int Number of entries in the buffer. */
+    protected $count = 0;
+
+    /** @var  eventobservers a reference to a self instance. */
+    protected static $instance;
+
+    /**
+     * Course delete event observer.
+     * This observer monitors course delete event, and when a course is deleted it deletes any rules and subscriptions associated
+     * with it, so no orphan data is left behind.
+     *
+     * @param \core\event\course_deleted $event The course deleted event.
+     */
+    public static function course_deleted(\core\event\course_deleted $event) {
+        $rules = rule_manager::get_rules_by_courseid($event->courseid);
+        foreach ($rules as $rule) {
+            rule_manager::delete_rule($rule->id);
+        }
+    }
+
+    /**
+     * The observer monitoring all the events.
+     *
+     * This observers puts small event objects in buffer for later writing to the database. At the end of the request the buffer
+     * is cleaned up and all data dumped into the tool_monitor_events table.
+     *
+     * @param \core\event\base $event event object
+     */
+    public static function process_event(\core\event\base $event) {
+
+        if (empty(self::$instance)) {
+            self::$instance = new static();
+            // Register shutdown handler - this is useful for buffering, processing events, etc.
+            \core_shutdown_manager::register_function(array(self::$instance, 'process_buffer'));
+        }
+
+        self::$instance->buffer_event($event);
+
+        if (PHPUNIT_TEST) {
+            // Process buffer after every event when unit testing.
+            self::$instance->process_buffer();
+
+        }
+    }
+
+    /**
+     * Api to buffer events to store, to reduce db queries.
+     *
+     * @param \core\event\base $event
+     */
+    protected function buffer_event(\core\event\base $event) {
+
+        $eventdata = $event->get_data();
+        $eventobj = new \stdClass();
+        $eventobj->eventname = $eventdata['eventname'];
+        $eventobj->contextid = $eventdata['contextid'];
+        $eventobj->contextlevel = $eventdata['contextlevel'];
+        $eventobj->contextinstanceid = $eventdata['contextinstanceid'];
+        if ($event->get_url()) {
+            // Get link url if exists.
+            $eventobj->link = $event->get_url()->out();
+        } else {
+            $eventobj->link = '';
+        }
+        $eventobj->courseid = $eventdata['courseid'];
+        $eventobj->timecreated = $eventdata['timecreated'];
+
+        $this->buffer[] = $eventobj;
+        $this->count++;
+    }
+
+    /**
+     * This method process all events stored in the buffer.
+     *
+     * This is a multi purpose api. It does the following:-
+     * 1. Write event data to tool_monitor_events
+     * 2. Find out users that need to be notified about rule completion and schedule a task to send them messages.
+     */
+    public function process_buffer() {
+        global $DB;
+
+        $events = $this->flush(); // Flush data.
+
+        $select = "SELECT COUNT(id) FROM {tool_monitor_events} ";
+        $now = time();
+        $messagestosend = array();
+
+        // Let us now process the events and check for subscriptions.
+        foreach ($events as $eventobj) {
+            $subscriptions = subscription_manager::get_subscriptions_by_event($eventobj);
+            $idstosend = array();
+            foreach ($subscriptions as $subscription) {
+                $starttime = $now - $subscription->timewindow;
+                if ($subscription->courseid == 0) {
+                    // Site level subscription. Count all events.
+                    $where = "eventname = :eventname AND timecreated >  :starttime";
+                    $params = array('eventname' => $eventobj->eventname, 'starttime' => $starttime);
+                } else {
+                    // Course level subscription.
+                    if ($subscription->cmid == 0) {
+                        // All modules.
+                        $where = "eventname = :eventname AND courseid = :courseid AND timecreated > :starttime";
+                        $params = array('eventname' => $eventobj->eventname, 'courseid' => $eventobj->courseid,
+                                'starttime' => $starttime);
+                    } else {
+                        // Specific module.
+                        $where = "eventname = :eventname AND courseid = :courseid AND contextinstanceid = :cmid
+                                AND timecreated > :starttime";
+                        $params = array('eventname' => $eventobj->eventname, 'courseid' => $eventobj->courseid,
+                                'cmid' => $eventobj->contextinstanceid, 'starttime' => $starttime);
+
+                    }
+                }
+                $sql = $select . "WHERE " . $where;
+                $count = $DB->count_records_sql($sql, $params);
+                if (!empty($count) && $count >= $subscription->frequency) {
+                    $idstosend[] = $subscription->id;
+                }
+            }
+            if (!empty($idstosend)) {
+                $messagestosend[] = array('subscriptionids' => $idstosend, 'event' => $eventobj);
+            }
+        }
+
+        // Schedule a task to send notification.
+        if (!empty($messagestosend)) {
+            $adhocktask = new notification_task();
+            $adhocktask->set_custom_data($messagestosend);
+            $adhocktask->set_component('tool_monitor');
+            \core\task\manager::queue_adhoc_task($adhocktask);
+        }
+    }
+
+    /**
+     * Protected method that flushes the buffer of events and writes them to the database.
+     *
+     * @return array a copy of the events buffer.
+     */
+    protected function flush() {
+        global $DB;
+
+        // Flush the buffer to the db.
+        $events = $this->buffer;
+        $DB->insert_records('tool_monitor_events', $events); // Insert the whole chunk into the database.
+        $this->buffer = array();
+        $this->count = 0;
+        return $events;
+    }
+
+    /**
+     * Observer that monitors user deleted event and delete user subscriptions.
+     *
+     * @param \core\event\user_deleted $event the event object.
+     */
+    public static function user_deleted(\core\event\user_deleted $event) {
+        $userid = $event->objectid;
+        subscription_manager::delete_user_subscriptions($userid);
+    }
+
+    /**
+     * Observer that monitors course module deleted event and delete user subscriptions.
+     *
+     * @param \core\event\course_module_deleted $event the event object.
+     */
+    public static function course_module_deleted(\core\event\course_module_deleted $event) {
+        $cmid = $event->contextinstanceid;
+        subscription_manager::delete_cm_subscriptions($cmid);
+    }
+}
diff --git a/admin/tool/monitor/classes/notification_task.php b/admin/tool/monitor/classes/notification_task.php
new file mode 100644 (file)
index 0000000..ffb1f4a
--- /dev/null
@@ -0,0 +1,118 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This file defines an adhoc task to send notifications.
+ *
+ * @package    tool_monitor
+ * @copyright  2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_monitor;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Adhock class, used to send notifications to users.
+ *
+ * @since      Moodle 2.8
+ * @package    tool_monitor
+ * @copyright  2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class notification_task extends \core\task\adhoc_task {
+
+    /**
+     * Send out messages.
+     */
+    public function execute() {
+        foreach ($this->get_custom_data() as $data) {
+            $eventobj = $data->event;
+            $subscriptionids = $data->subscriptionids;
+            foreach ($subscriptionids as $id) {
+                if ($message = $this->generate_message($id, $eventobj)) {
+                    message_send($message);
+                }
+            }
+        }
+    }
+
+    /**
+     * Generates the message object for a give subscription and event.
+     *
+     * @param int $subscriptionid Subscription instance
+     * @param \stdClass $eventobj Event data
+     *
+     * @return false|\stdClass message object
+     */
+    protected function generate_message($subscriptionid, \stdClass $eventobj) {
+
+        try {
+            $subscription = subscription_manager::get_subscription($subscriptionid);
+        } catch (\dml_exception $e) {
+            // Race condition, someone deleted the subscription.
+            return false;
+        }
+        $user = \core_user::get_user($subscription->userid);
+        $context = \context_user::instance($user->id, IGNORE_MISSING);
+        if ($context === false) {
+            // User context doesn't exist. Should never happen, nothing to do return.
+            return false;
+        }
+
+        $template = $subscription->template;
+        $template = $this->replace_placeholders($template, $subscription, $eventobj, $context);
+        $msgdata = new \stdClass();
+        $msgdata->component         = 'tool_monitor'; // Your component name.
+        $msgdata->name              = 'notification'; // This is the message name from messages.php.
+        $msgdata->userfrom          = \core_user::get_noreply_user();
+        $msgdata->userto            = $user;
+        $msgdata->subject           = $subscription->get_name($context);
+        $msgdata->fullmessage       = format_text($template, $subscription->templateformat, array('context' => $context));
+        $msgdata->fullmessageformat = $subscription->templateformat;
+        $msgdata->fullmessagehtml   = format_text($template, $subscription->templateformat, array('context' => $context));
+        $msgdata->smallmessage      = '';
+        $msgdata->notification      = 1; // This is only set to 0 for personal messages between users.
+
+        return $msgdata;
+    }
+
+    /**
+     * Replace place holders in the template with respective content.
+     *
+     * @param string $template Message template.
+     * @param subscription $subscription subscription instance
+     * @param \stdclass $eventobj Event data
+     * @param \context $context context object
+     *
+     * @return mixed final template string.
+     */
+    protected function replace_placeholders($template, subscription $subscription, $eventobj, $context) {
+        $template = str_replace('{link}', $eventobj->link, $template);
+        if ($eventobj->contextlevel == CONTEXT_MODULE && !empty($eventobj->contextinstanceid)
+            && (strpos($template, '{modulelink}') !== false)) {
+            $cm = get_fast_modinfo($eventobj->courseid)->get_cm($eventobj->contextinstanceid);
+            $modulelink = $cm->url;
+            $template = str_replace('{modulelink}', $modulelink, $template);
+        }
+        $template = str_replace('{rulename}', $subscription->get_name($context), $template);
+        $template = str_replace('{description}', $subscription->get_description($context), $template);
+        $template = str_replace('{eventname}', $subscription->get_event_name(), $template);
+
+        return $template;
+    }
+}
diff --git a/admin/tool/monitor/classes/output/helpicon/renderable.php b/admin/tool/monitor/classes/output/helpicon/renderable.php
new file mode 100644 (file)
index 0000000..3cb0395
--- /dev/null
@@ -0,0 +1,93 @@
+<?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/>.
+
+/**
+ * The file for the renderable class for the tool_monitor help icon.
+ *
+ * @package    tool_monitor
+ * @copyright  2014 Mark Nelson <markn@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_monitor\output\helpicon;
+
+defined('MOODLE_INTERNAL') || die;
+
+/**
+ * Renderable class for the tool_monitor help icon.
+ *
+ * @since      Moodle 2.8
+ * @package    tool_monitor
+ * @copyright  2014 Mark Nelson <markn@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class renderable implements \renderable {
+
+    /**
+     * @var string $type the type we are displaying the help icon for (either rule or subscription).
+     */
+    public $type;
+
+    /**
+     * @var int $id the id of the type.
+     */
+    public $id;
+
+    /**
+     * The constructor.
+     *
+     * @param string $type the type we are displaying the help icon for (either rule or subscription).
+     * @param int $id the id of the type.
+     */
+    public function __construct($type, $id) {
+        $this->type = $type;
+        $this->id = $id;
+    }
+
+    /**
+     * Returns the string to display for the help icon.
+     *
+     * @param string $type the type we are displaying the help icon for (either rule or subscription).
+     * @param int $id the id of the type.
+     * @param boolean $ajax Whether this help is called from an AJAX script.
+     *      This is used to influence text formatting and determines which format to output the doclink in.
+     * @return string|object|array $a An object, string or number that can be used within translation strings
+     */
+    public static function get_help_string_parameters($type, $id, $ajax = false) {
+        if ($type == 'rule') {
+            $rule = \tool_monitor\rule_manager::get_rule($id);
+
+            $langstring = new \stdClass();
+            $langstring->eventname = $rule->get_event_name();
+            $langstring->eventcomponent = $rule->get_plugin_name();
+            $langstring->frequency = $rule->frequency;
+            $langstring->minutes = $rule->timewindow / MINSECS;
+
+            return get_formatted_help_string('rulehelp', 'tool_monitor', $ajax, $langstring);
+        }
+
+        // Must be a subscription.
+        $sub = \tool_monitor\subscription_manager::get_subscription($id);
+
+        $langstring = new \stdClass();
+        $langstring->eventname = $sub->get_event_name();
+        $langstring->moduleinstance = $sub->get_instance_name();
+        $langstring->frequency = $sub->frequency;
+        $langstring->minutes = $sub->timewindow / MINSECS;
+
+        return get_formatted_help_string('subhelp', 'tool_monitor', $ajax, $langstring);
+    }
+}
diff --git a/admin/tool/monitor/classes/output/helpicon/renderer.php b/admin/tool/monitor/classes/output/helpicon/renderer.php
new file mode 100644 (file)
index 0000000..99025f2
--- /dev/null
@@ -0,0 +1,79 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * The file for the renderer class for the tool_monitor help icon.
+ *
+ * @package    tool_monitor
+ * @copyright  2014 Mark Nelson <markn@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_monitor\output\helpicon;
+
+defined('MOODLE_INTERNAL') || die;
+
+/**
+ * Renderer class for tool_monitor help icons.
+ *
+ * @since      Moodle 2.8
+ * @package    tool_monitor
+ * @copyright  2014 Mark Nelson <markn@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class renderer extends \plugin_renderer_base {
+
+    /**
+     * Get the HTML for the help icon.
+     *
+     * @param renderable $renderable renderable widget
+     *
+     * @return string the HTML of the help icon to display.
+     */
+    protected function render_renderable(renderable $renderable) {
+        global $CFG;
+
+        // First get the help image icon.
+        $src = $this->pix_url('help');
+
+        if ($renderable->type == 'rule') {
+            $title = get_string('rulehelp', 'tool_monitor');
+        } else { // Must be a subscription.
+            $title = get_string('subhelp', 'tool_monitor');
+        }
+
+        $alt = get_string('helpwiththis');
+
+        $attributes = array('src' => $src, 'alt' => $alt, 'class' => 'iconhelp');
+        $output = \html_writer::empty_tag('img', $attributes);
+
+        // Now create the link around it - we need https on loginhttps pages.
+        $urlparams = array();
+        $urlparams['type'] = $renderable->type;
+        $urlparams['id'] = $renderable->id;
+        $urlparams['lang'] = current_language();
+        $url = new \moodle_url($CFG->httpswwwroot . '/admin/tool/monitor/help.php', $urlparams);
+
+        // Note: this title is displayed only if JS is disabled, otherwise the link will have the new ajax tooltip.
+        $title = get_string('helpprefix2', '', trim($title, ". \t"));
+
+        $attributes = array('href' => $url, 'title' => $title, 'aria-haspopup' => 'true', 'target' => '_blank');
+        $output = \html_writer::tag('a', $output, $attributes);
+
+        // Now, finally the span.
+        return \html_writer::tag('span', $output, array('class' => 'helptooltip'));
+    }
+}
diff --git a/admin/tool/monitor/classes/output/managerules/renderable.php b/admin/tool/monitor/classes/output/managerules/renderable.php
new file mode 100644 (file)
index 0000000..2c74245
--- /dev/null
@@ -0,0 +1,201 @@
+<?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/>.
+
+/**
+ * Renderable class for manage rules page.
+ *
+ * @package    tool_monitor
+ * @copyright  2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_monitor\output\managerules;
+
+defined('MOODLE_INTERNAL') || die;
+
+require_once($CFG->libdir . '/tablelib.php');
+
+/**
+ * Renderable class for manage rules page.
+ *
+ * @since      Moodle 2.8
+ * @package    tool_monitor
+ * @copyright  2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class renderable extends \table_sql implements \renderable {
+
+    /**
+     * @var int course id.
+     */
+    public $courseid;
+
+    /**
+     * @var \context_course|\context_system context of the page to be rendered.
+     */
+    protected $context;
+
+    /**
+     * @var bool Does the user have capability to manage rules at site context.
+     */
+    protected $hassystemcap;
+
+    /**
+     * Sets up the table_log parameters.
+     *
+     * @param string $uniqueid unique id of form.
+     * @param \moodle_url $url url where this table is displayed.
+     * @param int $courseid course id.
+     * @param int $perpage Number of rules to display per page.
+     */
+    public function __construct($uniqueid, \moodle_url $url, $courseid = 0, $perpage = 100) {
+        parent::__construct($uniqueid);
+
+        $this->set_attribute('class', 'toolmonitor managerules generaltable generalbox');
+        $this->define_columns(array('name', 'description', 'plugin', 'eventname', 'filters', 'manage'));
+        $this->define_headers(array(
+                get_string('name'),
+                get_string('description'),
+                get_string('plugin'),
+                get_string('eventname'),
+                get_string('frequency', 'tool_monitor'),
+                get_string('manage', 'tool_monitor'),
+            )
+        );
+        $this->courseid = $courseid;
+        $this->pagesize = $perpage;
+        $systemcontext = \context_system::instance();
+        $this->context = empty($courseid) ? $systemcontext : \context_course::instance($courseid);
+        $this->hassystemcap = has_capability('tool/monitor:managerules', $systemcontext);
+        $this->collapsible(false);
+        $this->sortable(false);
+        $this->pageable(true);
+        $this->is_downloadable(false);
+        $this->define_baseurl($url);
+    }
+
+    /**
+     * Generate content for name column.
+     *
+     * @param \tool_monitor\rule $rule rule object
+     *
+     * @return string html used to display the column field.
+     */
+    public function col_name(\tool_monitor\rule $rule) {
+        return $rule->get_name($this->context);
+    }
+
+    /**
+     * Generate content for description column.
+     *
+     * @param \tool_monitor\rule $rule rule object
+     *
+     * @return string html used to display the column field.
+     */
+    public function col_description(\tool_monitor\rule $rule) {
+        return $rule->get_description($this->context);
+    }
+
+    /**
+     * Generate content for plugin column.
+     *
+     * @param \tool_monitor\rule $rule rule object
+     *
+     * @return string html used to display the column field.
+     */
+    public function col_plugin(\tool_monitor\rule $rule) {
+        return $rule->get_plugin_name();
+    }
+
+    /**
+     * Generate content for eventname column.
+     *
+     * @param \tool_monitor\rule $rule rule object
+     *
+     * @return string html used to display the column field.
+     */
+    public function col_eventname(\tool_monitor\rule $rule) {
+        return $rule->get_event_name();
+    }
+
+    /**
+     * Generate content for filters column.
+     *
+     * @param \tool_monitor\rule $rule rule object
+     *
+     * @return string html used to display the filters column field.
+     */
+    public function col_filters(\tool_monitor\rule $rule) {
+        return $rule->get_filters_description();
+    }
+
+    /**
+     * Generate content for manage column.
+     *
+     * @param \tool_monitor\rule $rule rule object
+     *
+     * @return string html used to display the manage column field.
+     */
+    public function col_manage(\tool_monitor\rule $rule) {
+        global $OUTPUT, $CFG;
+        $manage = '';
+        // We don't need to check for capability at course level since, user is never shown this page,
+        // if he doesn't have the capability.
+        if ($this->hassystemcap || ($rule->courseid !== 0)) {
+            // There might be site rules which the user can not manage.
+            $editurl = new \moodle_url($CFG->wwwroot. '/admin/tool/monitor/edit.php', array('ruleid' => $rule->id,
+                    'courseid' => $rule->courseid, 'sesskey' => sesskey()));
+            $copyurl = new \moodle_url($CFG->wwwroot. '/admin/tool/monitor/managerules.php',
+                    array('ruleid' => $rule->id, 'action' => 'copy', 'courseid' => $this->courseid, 'sesskey' => sesskey()));
+            $deleteurl = new \moodle_url($CFG->wwwroot. '/admin/tool/monitor/managerules.php', array('ruleid' => $rule->id,
+                    'action' => 'delete', 'courseid' => $rule->courseid, 'sesskey' => sesskey()));
+
+            $icon = $OUTPUT->render(new \pix_icon('t/edit', get_string('editrule', 'tool_monitor')));
+            $manage .= \html_writer::link($editurl, $icon, array('class' => 'action-icon'));
+
+            $icon = $OUTPUT->render(new \pix_icon('t/copy', get_string('duplicaterule', 'tool_monitor')));
+            $manage .= \html_writer::link($copyurl, $icon, array('class' => 'action-icon'));
+
+            $a = $rule->get_name($this->context);
+            $action = new \component_action('click', 'M.util.show_confirm_dialog', array('message' => get_string('ruleareyousure',
+                    'tool_monitor', $a)));
+            $icon = $OUTPUT->action_link($deleteurl, new \pix_icon('t/delete', get_string('deleterule', 'tool_monitor')), $action);
+            $manage .= $icon;
+        } else {
+            $manage = '-';
+        }
+        return $manage;
+    }
+
+    /**
+     * Query the reader. Store results in the object for use by build_table.
+     *
+     * @param int $pagesize size of page for paginated displayed table.
+     * @param bool $useinitialsbar do you want to use the initials bar.
+     */
+    public function query_db($pagesize, $useinitialsbar = true) {
+
+        $total = \tool_monitor\rule_manager::count_rules_by_courseid($this->courseid);
+        $this->pagesize($pagesize, $total);
+        $rules = \tool_monitor\rule_manager::get_rules_by_courseid($this->courseid, $this->get_page_start(),
+                $this->get_page_size());
+        $this->rawdata = $rules;
+        // Set initial bars.
+        if ($useinitialsbar) {
+            $this->initialbars($total > $pagesize);
+        }
+    }
+}
diff --git a/admin/tool/monitor/classes/output/managerules/renderer.php b/admin/tool/monitor/classes/output/managerules/renderer.php
new file mode 100644 (file)
index 0000000..889817d
--- /dev/null
@@ -0,0 +1,84 @@
+<?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/>.
+
+/**
+ * Renderer class for manage rules page.
+ *
+ * @package    tool_monitor
+ * @copyright  2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_monitor\output\managerules;
+
+defined('MOODLE_INTERNAL') || die;
+
+/**
+ * Renderer class for manage rules page.
+ *
+ * @since      Moodle 2.8
+ * @package    tool_monitor
+ * @copyright  2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class renderer extends \plugin_renderer_base {
+
+    /**
+     * Get html to display on the page.
+     *
+     * @param renderable $renderable renderable widget
+     *
+     * @return string to display on the mangerules page.
+     */
+    protected function render_renderable(renderable $renderable) {
+        $o = $this->render_table($renderable);
+        $o .= $this->render_add_button($renderable->courseid);
+
+        return $o;
+    }
+
+    /**
+     * Get html to display on the page.
+     *
+     * @param renderable $renderable renderable widget
+     *
+     * @return string to display on the mangerules page.
+     */
+    protected function render_table(renderable $renderable) {
+        $o = '';
+        ob_start();
+        $renderable->out($renderable->pagesize, true);
+        $o = ob_get_contents();
+        ob_end_clean();
+
+        return $o;
+    }
+
+    /**
+     * Html to add a button for adding a new rul.
+     *
+     * @param int $courseid course id.
+     *
+     * @return string html for the button.
+     */
+    protected function render_add_button($courseid) {
+        global $CFG;
+
+        $button = \html_writer::tag('button', get_string('addrule', 'tool_monitor'));
+        $addurl = new \moodle_url($CFG->wwwroot. '/admin/tool/monitor/edit.php', array('courseid' => $courseid));
+        return \html_writer::link($addurl, $button);
+    }
+}
diff --git a/admin/tool/monitor/classes/output/managesubs/renderer.php b/admin/tool/monitor/classes/output/managesubs/renderer.php
new file mode 100644 (file)
index 0000000..fe3f8e0
--- /dev/null
@@ -0,0 +1,94 @@
+<?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/>.
+
+/**
+ * Renderer class for manage subscriptions page.
+ *
+ * @package    tool_monitor
+ * @copyright  2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_monitor\output\managesubs;
+
+defined('MOODLE_INTERNAL') || die;
+
+/**
+ * Renderer class for manage subscriptions page.
+ *
+ * @since      Moodle 2.8
+ * @package    tool_monitor
+ * @copyright  2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class renderer extends \plugin_renderer_base {
+
+    /**
+     * Get html to display on the page.
+     *
+     * @param subs $renderable renderable widget
+     *
+     * @return string to display on the mangesubs page.
+     */
+    protected function render_subs(subs $renderable) {
+        $o = $this->render_table($renderable);
+        return $o;
+    }
+
+    /**
+     * Get html to display on the page.
+     *
+     * @param rules $renderable renderable widget
+     *
+     * @return string to display on the mangesubs page.
+     */
+    protected function render_rules(rules $renderable) {
+        $o = $this->render_course_select($renderable);
+        if (!empty($renderable->totalcount)) {
+            $o .= $this->render_table($renderable);
+        }
+        return $o;
+    }
+
+    /**
+     * Get html to display on the page for select dropdown..
+     *
+     * @param rules $renderable renderable widget
+     *
+     * @return string to display on the mangesubs page.
+     */
+    protected function render_course_select(rules $renderable) {
+        $select = $renderable->get_user_courses_select();
+        return $this->render($select);;
+    }
+
+    /**
+     * Get html to display on the page.
+     *
+     * @param rules|subs $renderable renderable widget
+     *
+     * @return string to display on the mangesubs page.
+     */
+    protected function render_table($renderable) {
+        $o = '';
+        ob_start();
+        $renderable->out($renderable->pagesize, true);
+        $o = ob_get_contents();
+        ob_end_clean();
+
+        return $o;
+    }
+}
diff --git a/admin/tool/monitor/classes/output/managesubs/rules.php b/admin/tool/monitor/classes/output/managesubs/rules.php
new file mode 100644 (file)
index 0000000..f891e90
--- /dev/null
@@ -0,0 +1,171 @@
+<?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/>.
+
+/**
+ * Renderable class to display a set of rules in the manage subscriptions page.
+ *
+ * @package    tool_monitor
+ * @copyright  2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_monitor\output\managesubs;
+
+defined('MOODLE_INTERNAL') || die;
+
+require_once($CFG->libdir . '/tablelib.php');
+
+/**
+ * Renderable class to display a set of rules in the manage subscriptions page.
+ *
+ * @since      Moodle 2.8
+ * @package    tool_monitor
+ * @copyright  2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class rules extends \table_sql implements \renderable {
+
+    /**
+     * @var int course id.
+     */
+    public $courseid;
+
+    /**
+     * @var int total rules present.
+     */
+    public $totalcount = 0;
+
+    /**
+     * @var \context_course|\context_system context of the page to be rendered.
+     */
+    protected $context;
+
+    /**
+     * @var \tool_monitor\output\helpicon\renderer the help icon renderer.
+     */
+    protected $helpiconrenderer;
+
+    /**
+     * Sets up the table_log parameters.
+     *
+     * @param string $uniqueid unique id of form.
+     * @param \moodle_url $url url where this table is displayed.
+     * @param int $courseid course id.
+     * @param int $perpage Number of rules to display per page.
+     */
+    public function __construct($uniqueid, \moodle_url $url, $courseid = 0, $perpage = 100) {
+        global $PAGE;
+
+        parent::__construct($uniqueid);
+
+        $this->set_attribute('class', 'toolmonitor subscriberules generaltable generalbox');
+        $this->define_columns(array('name', 'description', 'select'));
+        $this->define_headers(array(
+                get_string('name'),
+                get_string('description'),
+                get_string('select')
+            )
+        );
+        $this->courseid = $courseid;
+        $this->pagesize = $perpage;
+        $systemcontext = \context_system::instance();
+        $this->context = empty($courseid) ? $systemcontext : \context_course::instance($courseid);
+        $this->collapsible(false);
+        $this->sortable(false);
+        $this->pageable(true);
+        $this->is_downloadable(false);
+        $this->define_baseurl($url);
+        $this->helpiconrenderer = $PAGE->get_renderer('tool_monitor', 'helpicon');
+        $total = \tool_monitor\rule_manager::count_rules_by_courseid($this->courseid);
+        $this->totalcount = $total;
+    }
+
+    /**
+     * Generate content for name column.
+     *
+     * @param \tool_monitor\rule $rule rule object
+     *
+     * @return string html used to display the column field.
+     */
+    public function col_name(\tool_monitor\rule $rule) {
+        $name = $rule->get_name($this->context);
+        $helpicon = new \tool_monitor\output\helpicon\renderable('rule', $rule->id);
+        $helpicon = $this->helpiconrenderer->render($helpicon);
+
+        return $name . $helpicon;
+    }
+
+    /**
+     * Generate content for description column.
+     *
+     * @param \tool_monitor\rule $rule rule object
+     *
+     * @return string html used to display the column field.
+     */
+    public function col_description(\tool_monitor\rule $rule) {
+        return $rule->get_description($this->context);
+    }
+
+    /**
+     * Generate content for plugin column.
+     *
+     * @param \tool_monitor\rule $rule rule object
+     *
+     * @return string html used to display the column field.
+     */
+    public function col_select(\tool_monitor\rule $rule) {
+        global $OUTPUT;
+        $select = $rule->get_module_select($this->courseid);
+        return is_object($select) ? $OUTPUT->render($select) : $select;
+    }
+
+    /**
+     * Query the reader. Store results in the object for use by build_table.
+     *
+     * @param int $pagesize size of page for paginated displayed table.
+     * @param bool $useinitialsbar do you want to use the initials bar.
+     */
+    public function query_db($pagesize, $useinitialsbar = true) {
+
+        $total = \tool_monitor\rule_manager::count_rules_by_courseid($this->courseid);
+        $this->pagesize($pagesize, $total);
+        $rules = \tool_monitor\rule_manager::get_rules_by_courseid($this->courseid, $this->get_page_start(),
+                $this->get_page_size());
+        $this->rawdata = $rules;
+        // Set initial bars.
+        if ($useinitialsbar) {
+            $this->initialbars($total > $pagesize);
+        }
+    }
+
+    /**
+     * Gets a list of courses where the current user can subscribe to rules as a dropdown.
+     *
+     * @return \single_select list of courses.
+     */
+    public function get_user_courses_select() {
+        $courses = get_user_capability_course('tool/monitor:subscribe', null, true, 'fullname');
+        $options = array(0 => get_string('site'));
+        $systemcontext = \context_system::instance();
+        foreach ($courses as $course) {
+            $options[$course->id] = format_text($course->fullname, array('context' => $systemcontext));
+        }
+        $url = new \moodle_url('/admin/tool/monitor/index.php');
+        $select = new \single_select($url, 'courseid', $options, $this->courseid);
+        $select->set_label(get_string('selectacourse', 'tool_monitor'));
+        return $select;
+    }
+}
diff --git a/admin/tool/monitor/classes/output/managesubs/subs.php b/admin/tool/monitor/classes/output/managesubs/subs.php
new file mode 100644 (file)
index 0000000..8ba616d
--- /dev/null
@@ -0,0 +1,182 @@
+<?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/>.
+
+/**
+ * Renderable class to display a set of subscriptions in the manage subscriptions page.
+ *
+ * @package    tool_monitor
+ * @copyright  2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_monitor\output\managesubs;
+
+defined('MOODLE_INTERNAL') || die;
+
+require_once($CFG->libdir . '/tablelib.php');
+
+/**
+ * Renderable class to display a set of subscriptions in the manage subscriptions page.
+ *
+ * @since      Moodle 2.8
+ * @package    tool_monitor
+ * @copyright  2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class subs extends \table_sql implements \renderable {
+
+    /**
+     * @var int course id.
+     */
+    public $courseid;
+
+    /**
+     * @var \context_course|\context_system context of the page to be rendered.
+     */
+    protected $context;
+
+    /**
+     * @var \tool_monitor\output\helpicon\renderer the help icon renderer.
+     */
+    protected $helpiconrenderer;
+
+    /**
+     * Sets up the table_log parameters.
+     *
+     * @param string $uniqueid unique id of form.
+     * @param \moodle_url $url url where this table is displayed.
+     * @param int $courseid course id.
+     * @param int $perpage Number of rules to display per page.
+     */
+    public function __construct($uniqueid, \moodle_url $url, $courseid = 0, $perpage = 100) {
+        global $PAGE;
+
+        parent::__construct($uniqueid);
+
+        $this->set_attribute('class', 'toolmonitor subscriptions generaltable generalbox');
+        $this->define_columns(array('name', 'course', 'instance', 'unsubscribe', 'editrule'));
+        $this->define_headers(array(
+                get_string('name'),
+                get_string('course'),
+                get_string('moduleinstance', 'tool_monitor'),
+                get_string('unsubscribe', 'tool_monitor'),
+                get_string('editrule', 'tool_monitor')
+            )
+        );
+        $this->courseid = $courseid;
+        $this->pagesize = $perpage;
+        $systemcontext = \context_system::instance();
+        $this->context = empty($courseid) ? $systemcontext : \context_course::instance($courseid);
+        $this->collapsible(false);
+        $this->sortable(false);
+        $this->pageable(true);
+        $this->is_downloadable(false);
+        $this->define_baseurl($url);
+        $this->helpiconrenderer = $PAGE->get_renderer('tool_monitor', 'helpicon');
+    }
+
+    /**
+     * Generate content for name column.
+     *
+     * @param \tool_monitor\subscription $sub subscription object
+     *
+     * @return string html used to display the column field.
+     */
+    public function col_name(\tool_monitor\subscription $sub) {
+        $name = $sub->get_name($this->context);
+        $helpicon = new \tool_monitor\output\helpicon\renderable('subscription', $sub->id);
+        $helpicon = $this->helpiconrenderer->render($helpicon);
+
+        return $name . $helpicon;
+    }
+
+    /**
+     * Generate content for course column.
+     *
+     * @param \tool_monitor\subscription $sub subscription object
+     *
+     * @return string html used to display the column field.
+     */
+    public function col_course(\tool_monitor\subscription $sub) {
+       return $sub->get_course_name($this->context);
+    }
+
+    /**
+     * Generate content for description column.
+     *
+     * @param \tool_monitor\subscription $sub subscription object
+     *
+     * @return string html used to display the column field.
+     */
+    public function col_instance(\tool_monitor\subscription $sub) {
+        return $sub->get_instance_name();
+    }
+
+    /**
+     * Generate content for manage column.
+     *
+     * @param \tool_monitor\subscription $sub subscription object
+     *
+     * @return string html used to display the column field.
+     */
+    public function col_unsubscribe(\tool_monitor\subscription $sub) {
+        global $OUTPUT, $CFG;
+
+        $a = $sub->get_name($this->context);
+        $deleteurl = new \moodle_url($CFG->wwwroot. '/admin/tool/monitor/index.php', array('subscriptionid' => $sub->id,
+                'action' => 'unsubscribe', 'courseid' => $this->courseid, 'sesskey' => sesskey()));
+        $action = new \component_action('click', 'M.util.show_confirm_dialog', array('message' => get_string('subareyousure',
+            'tool_monitor', $a)));
+        $icon = $OUTPUT->action_link($deleteurl,
+                new \pix_icon('t/delete', get_string('deletesubscription', 'tool_monitor')), $action);
+        return $icon;
+    }
+
+    /**
+     * Generate content for edit rule column.
+     *
+     * @param \tool_monitor\subscription $sub subscription object
+     *
+     * @return string html used to display the column field.
+     */
+    public function col_editrule(\tool_monitor\subscription $sub) {
+        if ($sub->can_manage_rule()) {
+            // User can manage rule.
+            $editurl = new \moodle_url('/admin/tool/monitor/edit.php', array('ruleid' => $sub->ruleid,
+                    'courseid' => $sub->rulecourseid));
+            return \html_writer::link($editurl, get_string('editrule', 'tool_monitor'));
+        }
+        return '-';
+    }
+
+    /**
+     * Query the reader. Store results in the object for use by build_table.
+     *
+     * @param int $pagesize size of page for paginated displayed table.
+     * @param bool $useinitialsbar do you want to use the initials bar.
+     */
+    public function query_db($pagesize, $useinitialsbar = true) {
+
+        $total = \tool_monitor\subscription_manager::count_user_subscriptions();
+        $this->pagesize($pagesize, $total);
+        $subs = \tool_monitor\subscription_manager::get_user_subscriptions($this->get_page_start(), $this->get_page_size());
+        $this->rawdata = $subs;
+        // Set initial bars.
+        if ($useinitialsbar) {
+            $this->initialbars($total > $pagesize);
+        }
+    }
+}
diff --git a/admin/tool/monitor/classes/rule.php b/admin/tool/monitor/classes/rule.php
new file mode 100644 (file)
index 0000000..81b3aef
--- /dev/null
@@ -0,0 +1,247 @@
+<?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/>.
+
+/**
+ * Class represents a single rule.
+ *
+ * @package    tool_monitor
+ * @copyright  2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_monitor;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Class represents a single rule.
+ *
+ * @since      Moodle 2.8
+ * @package    tool_monitor
+ * @copyright  2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class rule {
+
+    /**
+     * @var \stdClass The rule object form database.
+     */
+    protected $rule;
+
+    /**
+     * Constructor.
+     *
+     * @param \stdClass $rule A rule object from database.
+     */
+    public function __construct($rule) {
+        $this->rule = $rule;
+    }
+
+    /**
+     * Can the current user manage this rule?
+     *
+     * @return bool true if the current user can manage this rule, else false.
+     */
+    public function can_manage_rule() {
+        $courseid = $this->courseid;
+        $context = empty($courseid) ? \context_system::instance() : \context_course::instance($this->courseid);
+        return has_capability('tool/monitor:managerules', $context);
+    }
+
+    /**
+     * Api to duplicate a rule in a given courseid.
+     *
+     * @param int $finalcourseid Final course id.
+     */
+    public function duplicate_rule($finalcourseid) {
+        $rule = fullclone($this->rule);
+        unset($rule->id);
+        $rule->courseid = $finalcourseid;
+        $time = time();
+        $rule->timecreated = $time;
+        $rule->timemodified = $time;
+        rule_manager::add_rule($rule);
+    }
+
+    /**
+     * Delete this rule.
+     *
+     * Note: It also removes all associated subscriptions.
+     */
+    public function delete_rule() {
+        rule_manager::delete_rule($this->id);
+    }
+
+    /**
+     * Generate a select drop down with list of possible modules for a given course and rule.
+     *
+     * @param int $courseid course id
+     *
+     * @return \single_select a single select object
+     * @throws \coding_exception
+     */
+    public function get_module_select($courseid) {
+        global $CFG;
+        $options = array();
+        if (strpos($this->plugin, 'mod_') === 0) {
+            $options[0] = get_string('allmodules', 'tool_monitor');
+        } else {
+            $options[0] = get_string('allevents', 'tool_monitor');
+        }
+        if (strpos($this->plugin, 'mod_') === 0) {
+            if ($courseid == 0) {
+                // They need to be in a course to select module instance.
+                return get_string('selectcourse', 'tool_monitor');
+            }
+            // Let them select an instance.
+            $cms = get_fast_modinfo($courseid);
+            $instances = $cms->get_instances_of(str_replace('mod_', '',  $this->plugin));
+            foreach ($instances as $cminfo) {
+                // Don't list instances that are not visible or available to the user.
+                if ($cminfo->uservisible && $cminfo->available) {
+                    $options[$cminfo->id] = $cminfo->get_formatted_name();
+                }
+            }
+        }
+        $url = new \moodle_url($CFG->wwwroot. '/admin/tool/monitor/index.php', array('courseid' => $courseid, 'ruleid' => $this->id,
+                'action' => 'subscribe', 'sesskey' => sesskey()));
+        return new \single_select($url, 'cmid', $options, '', $nothing = array('' => 'choosedots'));
+    }
+
+    /**
+     * Subscribe an user to this rule.
+     *
+     * @param int $courseid Course id.
+     * @param int $cmid Course module id.
+     * @param int $userid User id.
+     *
+     * @throws \coding_exception
+     */
+    public function subscribe_user($courseid, $cmid, $userid = 0) {
+        global $USER;
+
+        if ($this->courseid != $courseid && $this->courseid != 0) {
+            // Trying to subscribe to a rule that belongs to a different course. Should never happen.
+            throw new \coding_exception('Can not subscribe to rules from a different course');
+        }
+        if ($cmid !== 0) {
+            $cms = get_fast_modinfo($courseid);
+            $cminfo = $cms->get_cm($cmid);
+            if (!$cminfo->uservisible || !$cminfo->available) {
+                // Trying to subscribe to a hidden or restricted cm. Should never happen.
+                throw new \coding_exception('You cannot do that');
+            }
+        }
+        $userid = empty($userid) ? $USER->id : $userid;
+
+        subscription_manager::create_subscription($this->id, $courseid, $cmid, $userid);
+    }
+
+    /**
+     * Magic get method.
+     *
+     * @param string $prop property to get.
+     *
+     * @return mixed
+     * @throws \coding_exception
+     */
+    public function __get($prop) {
+        if (property_exists($this->rule, $prop)) {
+            return $this->rule->$prop;
+        }
+        throw new \coding_exception('Property "' . $prop . '" doesn\'t exist');
+    }
+
+    /**
+     * Return the rule data to be used while setting mform.
+     *
+     * @throws \coding_exception
+     */
+    public function get_mform_set_data() {
+        if (!empty($this->rule)) {
+            $rule = fullclone($this->rule);
+            $rule->description = array('text' => $rule->description, 'format' => $rule->descriptionformat);
+            $rule->template = array('text' => $rule->template, 'format' => $rule->templateformat);
+            return $rule;
+        }
+        throw new \coding_exception('Invalid call to get_mform_set_data.');
+    }
+
+    /**
+     * Method to get event name.
+     *
+     * @return string
+     * @throws \coding_exception
+     */
+    public function get_event_name() {
+        $eventclass = $this->eventname;
+        if (class_exists($eventclass)) {
+            return $eventclass::get_name();
+        }
+        return get_string('eventnotfound', 'tool_monitor');
+    }
+
+    /**
+     * Get filter description.
+     *
+     * @return string
+     */
+    public function get_filters_description() {
+        $a = new \stdClass();
+        $a->freq = $this->frequency;
+        $mins = $this->timewindow / MINSECS; // Convert seconds to minutes.
+        $a->mins = $mins;
+        return get_string('freqdesc', 'tool_monitor', $a);
+    }
+
+    /**
+     * Get properly formatted name of the rule associated.
+     *
+     * @param \context $context context where this name would be displayed.
+     *
+     * @return string Formatted name of the rule.
+     */
+    public function get_name(\context $context) {
+        return format_text($this->name, FORMAT_HTML, array('context' => $context));
+    }
+
+    /**
+     * Get properly formatted description of the rule associated.
+     *
+     * @param \context $context context where this description would be displayed.
+     *
+     * @return string Formatted description of the rule.
+     */
+    public function get_description(\context $context) {
+        return format_text($this->description, $this->descriptionformat, array('context' => $context));
+    }
+
+    /**
+     * Get name of the plugin associated with this rule
+     *
+     * @return string Plugin name.
+     */
+    public function get_plugin_name() {
+        if ($this->plugin === 'core') {
+            $string = get_string('core', 'tool_monitor');
+        } else if (get_string_manager()->string_exists('pluginname', $this->plugin)) {
+            $string = get_string('pluginname', $this->plugin);
+        } else {
+            $string = $this->plugin;
+        }
+        return $string;
+    }
+}
diff --git a/admin/tool/monitor/classes/rule_form.php b/admin/tool/monitor/classes/rule_form.php
new file mode 100644 (file)
index 0000000..a069779
--- /dev/null
@@ -0,0 +1,148 @@
+<?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/>.
+
+/**
+ * The mform for creating and editing a rule.
+ *
+ * @copyright 2014 onwards Simey Lameze <lameze@gmail.com>
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @package   tool_monitor
+ */
+
+namespace tool_monitor;
+
+require_once($CFG->dirroot.'/lib/formslib.php');
+
+/**
+ * The mform for creating and editing a rule.
+ *
+ * @since     Moodle 2.8
+ * @copyright 2014 onwards Simey Lameze <lameze@gmail.com>
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @package   tool_monitor
+ */
+class rule_form extends \moodleform {
+
+    /**
+     * Mform class definition
+     *
+     */
+    public function definition () {
+        $mform = $this->_form;
+        $eventlist = $this->_customdata['eventlist'];
+        $pluginlist = $this->_customdata['pluginlist'];
+        $rule = $this->_customdata['rule'];
+        $courseid = $this->_customdata['courseid'];
+
+        // General section header.
+        $mform->addElement('header', 'general', get_string('general'));
+
+        // Hidden course ID.
+        $mform->addElement('hidden', 'courseid');
+        $mform->setType('courseid', PARAM_INT);
+
+        // We are editing a existing rule.
+        if (!empty($rule->id)) {
+            // Hidden rule id.
+            $mform->addElement('hidden', 'ruleid');
+            $mform->setType('ruleid', PARAM_INT);
+            $mform->setConstant('ruleid', $rule->id);
+
+            // Force course id.
+            $courseid = $rule->courseid;
+        }
+
+        // Make course id a constant.
+        $mform->setConstant('courseid', $courseid);
+
+        if (empty($courseid)) {
+            $context = \context_system::instance();
+        } else {
+            $context = \context_course::instance($courseid);
+        }
+
+        $editoroptions = array(
+            'subdirs' => 0,
+            'maxbytes' => 0,
+            'maxfiles' => 0,
+            'changeformat' => 0,
+            'context' => $context,
+            'noclean' => 0,
+            'trusttext' => 0
+        );
+
+        // Name field.
+        $mform->addElement('text', 'name', get_string('name', 'tool_monitor'), 'size="50"');
+        $mform->addRule('name', get_string('required'), 'required');
+        $mform->setType('name', PARAM_TEXT);
+        $mform->addHelpButton('name', 'name', 'tool_monitor');
+
+        // Plugin field.
+        $mform->addElement('select', 'plugin', get_string('selectplugin', 'tool_monitor'), $pluginlist);
+        $mform->addRule('plugin', get_string('required'), 'required');
+        $mform->addHelpButton('plugin', 'selectplugin', 'tool_monitor');
+
+        // Event field.
+        $mform->addElement('select', 'eventname', get_string('selectevent', 'tool_monitor'), $eventlist);
+        $mform->addRule('eventname', get_string('required'), 'required');
+        $mform->addHelpButton('eventname', 'selectevent', 'tool_monitor');
+
+        // Description field.
+        $mform->addElement('editor', 'description', get_string('description', 'tool_monitor'), $editoroptions);
+        $mform->addHelpButton('description', 'description', 'tool_monitor');
+
+        // Filters.
+        $mform->addElement('header', 'customisefilters', get_string('customisefilters', 'tool_monitor'));
+        $freq = array(1 => 1, 5 => 5, 10 => 10, 20 => 20, 30 => 30, 40 => 40, 50 => 50, 60 => 60, 70 => 70, 80 => 80, 90 => 90,
+                100 => 100, 1000 => 1000);
+        $mform->addElement('select', 'frequency', get_string('selectfrequency', 'tool_monitor'), $freq);
+        $mform->addRule('frequency', get_string('required'), 'required');
+        $mform->addHelpButton('frequency', 'selectfrequency', 'tool_monitor');
+
+        $mins = array(1 => 1, 5 => 5, 10 => 10, 15 => 15, 20 => 20, 25 => 25, 30 => 30, 35 => 35, 40 => 40, 45 => 45, 50 => 50,
+                55 => 55,  60 => 60);
+        $mform->addElement('select', 'minutes', get_string('selectminutes', 'tool_monitor'), $mins);
+        $mform->addRule('minutes', get_string('required'), 'required');
+
+        // Message template.
+        $mform->addElement('header', 'customisemessage', get_string('customisemessage', 'tool_monitor'));
+        $mform->addElement('editor', 'template', get_string('messagetemplate', 'tool_monitor'), $editoroptions);
+        $mform->setDefault('template', get_string('defaultmessagetpl', 'tool_monitor'));
+        $mform->addRule('template', get_string('required'), 'required');
+        $mform->addHelpButton('template', 'messagetemplate', 'tool_monitor');
+
+        // Action buttons.
+        $this->add_action_buttons(false, get_string('savechanges'));
+    }
+
+    /**
+     * Form validation
+     *
+     * @param array $data data from the form.
+     * @param array $files files uploaded.
+     *
+     * @return array of errors.
+     */
+    public function validation($data, $files) {
+        $errors = parent::validation($data, $files);
+
+        if (!eventlist::validate_event_plugin($data['plugin'], $data['eventname'])) {
+            $errors['eventname'] = get_string('errorincorrectevent', 'tool_monitor');
+        }
+
+        return $errors;
+    }
+}
\ No newline at end of file
diff --git a/admin/tool/monitor/classes/rule_manager.php b/admin/tool/monitor/classes/rule_manager.php
new file mode 100644 (file)
index 0000000..864451c
--- /dev/null
@@ -0,0 +1,200 @@
+<?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/>.
+
+/**
+ * Rule manager class.
+ *
+ * @package    tool_monitor
+ * @copyright  2014 onwards Simey Lameze <lameze@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace tool_monitor;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Rule manager class.
+ *
+ * @package    tool_monitor
+ * @copyright  2014 onwards Simey Lameze <lameze@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class rule_manager {
+
+    /**
+     * Create a new rule.
+     *
+     * @param \stdClass $ruledata data to insert as new rule entry.
+     *
+     * @return rule An instance of rule class.
+     */
+    public static function add_rule($ruledata) {
+        global $DB;
+
+        $now = time();
+        $ruledata->timecreated = $now;
+        $ruledata->timemodified = $now;
+
+        $ruledata->id = $DB->insert_record('tool_monitor_rules', $ruledata);
+        return new rule($ruledata);
+    }
+
+    /**
+     * Clean data submitted by mform.
+     *
+     * @param \stdClass $mformdata data to insert as new rule entry.
+     *
+     * @return \stdClass Cleaned rule data.
+     */
+    public static function clean_ruledata_form($mformdata) {
+        global $USER;
+
+        $rule = new \stdClass();
+        if (!empty($mformdata->ruleid)) {
+            $rule->id = $mformdata->ruleid;
+        }
+        $rule->userid = empty($mformdata->userid) ? $USER->id : $mformdata->userid;
+        $rule->courseid = $mformdata->courseid;
+        $rule->name = $mformdata->name;
+        $rule->plugin = $mformdata->plugin;
+        $rule->eventname = $mformdata->eventname;
+        $rule->description = $mformdata->description['text'];
+        $rule->descriptionformat = $mformdata->description['format'];
+        $rule->frequency = $mformdata->frequency;
+        $rule->timewindow = $mformdata->minutes * MINSECS;
+        $rule->template = $mformdata->template['text'];
+        $rule->templateformat = $mformdata->template['format'];
+
+        return $rule;
+    }
+
+    /**
+     * Delete a rule and associated subscriptions, by rule id.
+     *
+     * @param int $ruleid id of rule to be deleted.
+     *
+     * @return bool
+     */
+    public static function delete_rule($ruleid) {
+        global $DB;
+
+        subscription_manager::remove_all_subscriptions_for_rule($ruleid);
+        return $DB->delete_records('tool_monitor_rules', array('id' => $ruleid));
+    }
+
+    /**
+     * Get an instance of rule class.
+     *
+     * @param \stdClass|int $ruleorid A rule object from database or rule id.
+     *
+     * @return rule object with rule id.
+     */
+    public static function get_rule($ruleorid) {
+        global $DB;
+        if (!is_object($ruleorid)) {
+            $rule = $DB->get_record('tool_monitor_rules', array('id' => $ruleorid), '*', MUST_EXIST);
+        } else {
+            $rule = $ruleorid;
+        }
+
+        return new rule($rule);
+    }
+
+    /**
+     * Update rule data.
+     *
+     * @throws \coding_exception if $record->ruleid is invalid.
+     * @param object $ruledata rule data to be updated.
+     *
+     * @return bool
+     */
+    public static function update_rule($ruledata) {
+        global $DB;
+        if (!self::get_rule($ruledata->id)) {
+            throw new \coding_exception('Invalid rule ID.');
+        }
+        $ruledata->timemodified = time();
+        return $DB->update_record('tool_monitor_rules', $ruledata);
+    }
+
+    /**
+     * Get rules by course id.
+     *
+     * @param int $courseid course id of the rule.
+     * @param int $limitfrom Limit from which to fetch rules.
+     * @param int $limitto  Limit to which rules need to be fetched.
+     *
+     * @return array List of rules for the given course id, also includes system wide rules.
+     */
+    public static function get_rules_by_courseid($courseid, $limitfrom = 0, $limitto = 0) {
+        global $DB;
+        $select = "courseid = ? OR courseid = ?";
+        return self::get_instances($DB->get_records_select('tool_monitor_rules', $select, array(0, $courseid), null, '*',
+                $limitfrom, $limitto));
+    }
+
+    /**
+     * Get rule count by course id.
+     *
+     * @param int $courseid course id of the rule.
+     *
+     * @return int count of rules present in system visible in the given course id.
+     */
+    public static function count_rules_by_courseid($courseid) {
+        global $DB;
+        $select = "courseid = ? OR courseid = ?";
+        return $DB->count_records_select('tool_monitor_rules', $select, array(0, $courseid));
+    }
+
+    /**
+     * Get rules by plugin name.
+     *
+     * @param string $plugin plugin name of the rule.
+     *
+     * @return array List of rules for the given plugin name.
+     */
+    public static function get_rules_by_plugin($plugin) {
+        global $DB;
+        return self::get_instances($DB->get_records('tool_monitor_rules', array('plugin' => $plugin)));
+    }
+
+    /**
+     * Get rules by event name.
+     *
+     * @param string $eventname event name of the rule.
+     *
+     * @return array List of rules for the given event.
+     */
+    public static function get_rules_by_event($eventname) {
+        global $DB;
+        return self::get_instances($DB->get_records('tool_monitor_rules', array('eventname' => $eventname)));
+    }
+
+    /**
+     * Helper method to convert db records to instances.
+     *
+     * @param array $arr of rules.
+     *
+     * @return array of rules as instances.
+     */
+    protected static function get_instances($arr) {
+        $result = array();
+        foreach ($arr as $key => $sub) {
+            $result[$key] = new rule($sub);
+        }
+        return $result;
+    }
+}
diff --git a/admin/tool/monitor/classes/subscription.php b/admin/tool/monitor/classes/subscription.php
new file mode 100644 (file)
index 0000000..a729dc8
--- /dev/null
@@ -0,0 +1,192 @@
+<?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/>.
+
+/**
+ * Class represents a single subscription.
+ *
+ * @package    tool_monitor
+ * @copyright  2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_monitor;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Class represents a single subscription instance (i.e with all the subscription info).
+ *
+ * @since      Moodle 2.8
+ * @package    tool_monitor
+ * @copyright  2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class subscription {
+    /**
+     * @var \stdClass
+     */
+    protected $subscription;
+
+    /**
+     * Constructor.
+     *
+     * use {@link \tool_monitor\subscription_manager::get_subscription} to get an instance instead of directly calling this method.
+     *
+     * @param \stdClass $subscription
+     */
+    public function __construct($subscription) {
+        $this->subscription = $subscription;
+    }
+
+    /**
+     * Magic get method.
+     *
+     * @param string $prop property to get.
+     *
+     * @return mixed
+     * @throws \coding_exception
+     */
+    public function __get($prop) {
+        if (property_exists($this->subscription, $prop)) {
+            return $this->subscription->$prop;
+        }
+        throw new \coding_exception('Property "' . $prop . '" doesn\'t exist');
+    }
+
+    /**
+     * Get a human readable name for instances associated with this subscription.
+     *
+     * @return string
+     * @throws \coding_exception
+     */
+    public function get_instance_name() {
+        if ($this->plugin === 'core') {
+            $string = get_string('allevents', 'tool_monitor');
+        } else {
+            if ($this->cmid == 0) {
+                $string = get_string('allmodules', 'tool_monitor');
+            } else {
+                $cms = get_fast_modinfo($this->courseid);
+                $cms = $cms->get_cms();
+                if (isset($cms[$this->cmid])) {
+                    $string = $cms[$this->cmid]->get_formatted_name(); // Instance name.
+                } else {
+                    // Something is wrong, instance is not present anymore.
+                    $string = get_string('invalidmodule', 'tool_monitor');
+                }
+            }
+        }
+
+        return $string;
+    }
+
+    /**
+     * Method to get event name.
+     *
+     * @return string
+     * @throws \coding_exception
+     */
+    public function get_event_name() {
+        $eventclass = $this->eventname;
+        if (class_exists($eventclass)) {
+            return $eventclass::get_name();
+        }
+        return get_string('eventnotfound', 'tool_monitor');
+    }
+
+    /**
+     * Get filter description.
+     *
+     * @return string
+     */
+    public function get_filters_description() {
+        $a = new \stdClass();
+        $a->freq = $this->frequency;
+        $mins = $this->timewindow / MINSECS; // Convert seconds to minutes.
+        $a->mins = $mins;
+        return get_string('freqdesc', 'tool_monitor', $a);
+    }
+
+    /**
+     * Get properly formatted name of the rule associated.
+     *
+     * @param \context $context context where this name would be displayed.
+     *
+     * @return string Formatted name of the rule.
+     */
+    public function get_name(\context $context) {
+        return format_text($this->name, FORMAT_HTML, array('context' => $context));
+    }
+
+    /**
+     * Get properly formatted description of the rule associated.
+     *
+     * @param \context $context context where this description would be displayed.
+     *
+     * @return string Formatted description of the rule.
+     */
+    public function get_description(\context $context) {
+        return format_text($this->description, $this->descriptionformat, array('context' => $context));
+    }
+
+    /**
+     * Get name of the plugin associated with this rule
+     *
+     * @return string Plugin name.
+     */
+    public function get_plugin_name() {
+        if ($this->plugin === 'core') {
+            $string = get_string('core', 'tool_monitor');
+        } else if (get_string_manager()->string_exists('pluginname', $this->plugin)) {
+            $string = get_string('pluginname', $this->plugin);
+        } else {
+            $string = $this->plugin;
+        }
+        return $string;
+    }
+
+    /**
+     * Get properly formatted name of the course associated.
+     *
+     * @param \context $context context where this name would be displayed.
+     *
+     * @return string Formatted name of the rule.
+     */
+    public function get_course_name(\context $context) {
+        global $SITE;
+        $courseid = $this->courseid;
+        if (empty($courseid)) {
+            $coursename = format_string($SITE->fullname, true, array('context' => $context));
+        } else {
+            $course = get_course($this->courseid);
+            $link = new \moodle_url('/course/view.php', array('id' => $course->id));
+            $coursename = format_string($course->fullname, true, array('context' => $context));
+            $coursename = \html_writer::link($link, $coursename);
+        }
+        return $coursename;
+    }
+
+    /**
+     * Can the current user manage the rule associate with this subscription?
+     *
+     * @return bool true if the current user can manage this rule, else false.
+     */
+    public function can_manage_rule() {
+        $courseid = $this->rulecourseid;
+        $context = empty($courseid) ? \context_system::instance() : \context_course::instance($courseid);
+        return has_capability('tool/monitor:managerules', $context);
+    }
+}
diff --git a/admin/tool/monitor/classes/subscription_manager.php b/admin/tool/monitor/classes/subscription_manager.php
new file mode 100644 (file)
index 0000000..82796a3
--- /dev/null
@@ -0,0 +1,282 @@
+<?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/>.
+
+/**
+ * Class to manage subscriptions.
+ *
+ * @package    tool_monitor
+ * @copyright  2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_monitor;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Class to manage subscriptions.
+ *
+ * @since      Moodle 2.8
+ * @package    tool_monitor
+ * @copyright  2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class subscription_manager {
+    /**
+     * Subscribe a user to a given rule.
+     *
+     * @param int $ruleid  Rule id.
+     * @param int $courseid Course id.
+     * @param int $cmid Course module id.
+     * @param int $userid User who is subscribing, defaults to $USER.
+     *
+     * @return bool|int returns id of the created subscription.
+     */
+    public static function create_subscription($ruleid, $courseid, $cmid, $userid = 0) {
+        global $DB, $USER;
+
+        $subscription = new \stdClass();
+        $subscription->ruleid = $ruleid;
+        $subscription->courseid = $courseid;
+        $subscription->cmid = $cmid;
+        $subscription->userid = empty($userid) ? $USER->id : $userid;
+        if ($DB->record_exists('tool_monitor_subscriptions', (array)$subscription)) {
+            // Subscription already exists.
+            return false;
+        }
+
+        $subscription->timecreated = time();
+        return $DB->insert_record('tool_monitor_subscriptions', $subscription);
+    }
+
+    /**
+     * Delete a subscription.
+     *
+     * @param subscription|int $subscriptionorid an instance of subscription class or id.
+     * @param bool $checkuser Check if the subscription belongs to current user before deleting.
+     *
+     * @return bool
+     * @throws \coding_exception if $checkuser is true and the subscription doesn't belong to the current user.
+     */
+    public static function delete_subscription($subscriptionorid, $checkuser = true) {
+        global $DB, $USER;
+        if (is_object($subscriptionorid)) {
+            $subscription = $subscriptionorid;
+        } else {
+            $subscription = self::get_subscription($subscriptionorid);
+        }
+        if ($checkuser && $subscription->userid != $USER->id) {
+            throw new \coding_exception('Invalid subscription supplied');
+        }
+        return $DB->delete_records('tool_monitor_subscriptions', array('id' => $subscription->id));
+    }
+
+    /**
+     * Delete all subscriptions for a user.
+     *
+     * @param int $userid user id.
+     *
+     * @return mixed
+     */
+    public static function delete_user_subscriptions($userid) {
+        global $DB;
+        return $DB->delete_records('tool_monitor_subscriptions', array('userid' => $userid));
+    }
+
+    /**
+     * Delete all subscriptions for a course module.
+     *
+     * @param int $cmid cm id.
+     *
+     * @return mixed
+     */
+    public static function delete_cm_subscriptions($cmid) {
+        global $DB;
+        return $DB->delete_records('tool_monitor_subscriptions', array('cmid' => $cmid));
+    }
+
+    /**
+     * Delete all subscribers for a given rule.
+     *
+     * @param int $ruleid rule id.
+     *
+     * @return bool
+     */
+    public static function remove_all_subscriptions_for_rule($ruleid) {
+        global $DB;
+        return $DB->delete_records('tool_monitor_subscriptions', array('ruleid' => $ruleid));
+    }
+
+    /**
+     * Get a subscription instance for an given subscription id.
+     *
+     * @param subscription|int $subscriptionorid an instance of subscription class or id.
+     *
+     * @return subscription returns a instance of subscription class.
+     */
+    public static function get_subscription($subscriptionorid) {
+        global $DB;
+
+        if (is_object($subscriptionorid)) {
+            return new subscription($subscriptionorid);
+        }
+
+        $sql = self::get_subscription_join_rule_sql();
+        $sql .= "WHERE s.id = :id";
+        $sub = $DB->get_record_sql($sql, array('id' => $subscriptionorid), MUST_EXIST);
+        return new subscription($sub);
+    }
+
+    /**
+     * Get an array of subscriptions for a given user in a given course.
+     *
+     * @param int $courseid course id.
+     * @param int $limitfrom Limit from which to fetch rules.
+     * @param int $limitto  Limit to which rules need to be fetched.
+     * @param int $userid Id of the user for which the subscription needs to be fetched. Defaults to $USER;
+     * @param string $order Order to sort the subscriptions.
+     *
+     * @return array list of subscriptions
+     */
+    public static function get_user_subscriptions_for_course($courseid, $limitfrom = 0, $limitto = 0, $userid = 0,
+            $order = 's.timecreated DESC' ) {
+        global $DB, $USER;
+        if ($userid == 0) {
+            $userid = $USER->id;
+        }
+        $sql = self::get_subscription_join_rule_sql();
+        $sql .= "WHERE s.courseid = :courseid AND s.userid = :userid ORDER BY $order";
+
+        return self::get_instances($DB->get_records_sql($sql, array('courseid' => $courseid, 'userid' => $userid), $limitfrom,
+                $limitto));
+    }
+
+    /**
+     * Get count of subscriptions for a given user in a given course.
+     *
+     * @param int $courseid course id.
+     * @param int $userid Id of the user for which the subscription needs to be fetched. Defaults to $USER;
+     *
+     * @return int number of subscriptions
+     */
+    public static function count_user_subscriptions_for_course($courseid, $userid = 0) {
+        global $DB, $USER;
+        if ($userid == 0) {
+            $userid = $USER->id;
+        }
+        $sql = self::get_subscription_join_rule_sql(true);
+        $sql .= "WHERE s.courseid = :courseid AND s.userid = :userid";
+
+        return $DB->count_records_sql($sql, array('courseid' => $courseid, 'userid' => $userid));
+    }
+
+    /**
+     * Get an array of subscriptions for a given user.
+     *
+     * @param int $limitfrom Limit from which to fetch rules.
+     * @param int $limitto  Limit to which rules need to be fetched.
+     * @param int $userid Id of the user for which the subscription needs to be fetched. Defaults to $USER;
+     * @param string $order Order to sort the subscriptions.
+     *
+     * @return array list of subscriptions
+     */
+    public static function get_user_subscriptions($limitfrom = 0, $limitto = 0, $userid = 0,
+                                                             $order = 's.timecreated DESC' ) {
+        global $DB, $USER;
+        if ($userid == 0) {
+            $userid = $USER->id;
+        }
+        $sql = self::get_subscription_join_rule_sql();
+        $sql .= "WHERE s.userid = :userid ORDER BY $order";
+
+        return self::get_instances($DB->get_records_sql($sql, array('userid' => $userid), $limitfrom, $limitto));
+    }
+
+    /**
+     * Get count of subscriptions for a given user.
+     *
+     * @param int $userid Id of the user for which the subscription needs to be fetched. Defaults to $USER;
+     *
+     * @return int number of subscriptions
+     */
+    public static function count_user_subscriptions($userid = 0) {
+        global $DB, $USER;;
+        if ($userid == 0) {
+            $userid = $USER->id;
+        }
+        $sql = self::get_subscription_join_rule_sql(true);
+        $sql .= "WHERE s.userid = :userid";
+
+        return $DB->count_records_sql($sql, array('userid' => $userid));
+    }
+
+    /**
+     * Return a list of subscriptions for a given event.
+     *
+     * @param \stdClass $event the event object.
+     *
+     * @return array
+     */
+    public static function get_subscriptions_by_event(\stdClass $event) {
+        global $DB;
+
+        $sql = self::get_subscription_join_rule_sql();
+        if ($event->contextlevel == CONTEXT_MODULE && $event->contextinstanceid != 0) {
+            $sql .= "WHERE r.eventname = :eventname AND s.courseid = :courseid AND (s.cmid = :cmid OR s.cmid = 0)";
+            $params = array('eventname' => $event->eventname, 'courseid' => $event->courseid, 'cmid' => $event->contextinstanceid);
+        } else {
+            $sql .= "WHERE r.eventname = :eventname AND (s.courseid = :courseid OR s.courseid = 0)";
+            $params = array('eventname' => $event->eventname, 'courseid' => $event->courseid);
+        }
+        return self::get_instances($DB->get_records_sql($sql, $params));
+    }
+
+    /**
+     * Return sql to join rule and subscription table.
+     *
+     * @param bool $count Weather if this is a count query or not.
+     *
+     * @return string the sql.
+     */
+    protected static function get_subscription_join_rule_sql($count = false) {
+        if ($count) {
+            $select = "SELECT COUNT(s.id) ";
+        } else {
+            $select = "SELECT s.*, r.description, r.descriptionformat, r.name, r.userid as ruleuserid, r.courseid as rulecourseid,
+            r.plugin, r.eventname, r.template, r.templateformat, r.frequency, r.timewindow";
+        }
+        $sql = $select . "
+                  FROM {tool_monitor_rules} r
+                  JOIN {tool_monitor_subscriptions} s
+                        ON r.id = s.ruleid ";
+        return $sql;
+    }
+
+    /**
+     * Helper method to convert db records to instances.
+     *
+     * @param array $arr of subscriptions.
+     *
+     * @return array of subscriptions as instances.
+     */
+    protected static function get_instances($arr) {
+        $result = array();
+        foreach ($arr as $key => $sub) {
+            $result[$key] = new subscription($sub);
+        }
+        return $result;
+    }
+}
diff --git a/admin/tool/monitor/db/access.php b/admin/tool/monitor/db/access.php
new file mode 100644 (file)
index 0000000..d1b687c
--- /dev/null
@@ -0,0 +1,53 @@
+<?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/>.
+
+/**
+ * Capabilities.
+ *
+ * This files lists capabilities related to tool_monitor.
+ *
+ * @package    tool_monitor
+ * @copyright  2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$capabilities = array(
+
+    'tool/monitor:subscribe' => array(
+        'riskbitmask' => RISK_PERSONAL,
+        'captype' => 'read',
+        'contextlevel' => CONTEXT_COURSE,
+        'archetypes' => array(
+            'teacher' => CAP_ALLOW,
+            'editingteacher' => CAP_ALLOW,
+            'manager' => CAP_ALLOW
+        ),
+    ),
+
+    'tool/monitor:managerules' => array(
+        'riskbitmask' => RISK_XSS,
+        'captype' => 'write',
+        'contextlevel' => CONTEXT_COURSE,
+        'archetypes' => array(
+            'teacher' => CAP_ALLOW,
+            'editingteacher' => CAP_ALLOW,
+            'manager' => CAP_ALLOW
+        ),
+    ),
+);
+
diff --git a/admin/tool/monitor/db/events.php b/admin/tool/monitor/db/events.php
new file mode 100644 (file)
index 0000000..0b1859f
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This file definies observers needed by the tool.
+ *
+ * @package    tool_monitor
+ * @copyright  2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+// List of observers.
+$observers = array(
+    array(
+        'eventname'   => '\core\event\course_deleted',
+        'priority'    => 1,
+        'callback'    => '\tool_monitor\eventobservers::course_deleted',
+    ),
+    array(
+        'eventname'   => '*',
+        'callback'    => '\tool_monitor\eventobservers::process_event',
+    ),
+    array(
+        'eventname'   => '\core\event\user_deleted',
+        'callback'    => '\tool_monitor\eventobservers::user_deleted',
+    ),
+    array(
+        'eventname'   => '\core\event\course_module_deleted',
+        'callback'    => '\tool_monitor\eventobservers::course_module_deleted',
+    )
+);
diff --git a/admin/tool/monitor/db/install.xml b/admin/tool/monitor/db/install.xml
new file mode 100644 (file)
index 0000000..39d2bb9
--- /dev/null
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<XMLDB PATH="tool/monitor/db" VERSION="20140708" COMMENT="XMLDB file for Moodle tool/monitor"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
+>
+  <TABLES>
+    <TABLE NAME="tool_monitor_rules" COMMENT="Table to store rules">
+      <FIELDS>
+        <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
+        <FIELD NAME="description" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="Description of the rule"/>
+        <FIELD NAME="descriptionformat" TYPE="int" LENGTH="1" NOTNULL="true" SEQUENCE="false" COMMENT="Description format"/>
+        <FIELD NAME="name" TYPE="char" LENGTH="254" NOTNULL="true" SEQUENCE="false" COMMENT="Name of the rule"/>
+        <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Id of user who created the rule"/>
+        <FIELD NAME="courseid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Id of course to which this rule belongs."/>
+        <FIELD NAME="plugin" TYPE="char" LENGTH="254" NOTNULL="true" SEQUENCE="false" COMMENT="Frankenstlye name of the plguin"/>
+        <FIELD NAME="eventname" TYPE="char" LENGTH="254" NOTNULL="true" SEQUENCE="false" COMMENT="Fully qualified name of the event"/>
+        <FIELD NAME="template" TYPE="text" NOTNULL="true" SEQUENCE="false" COMMENT="Message template"/>
+        <FIELD NAME="templateformat" TYPE="int" LENGTH="1" NOTNULL="true" SEQUENCE="false" COMMENT="Template format"/>
+        <FIELD NAME="frequency" TYPE="int" LENGTH="4" NOTNULL="true" SEQUENCE="false" COMMENT="Frequency"/>
+        <FIELD NAME="timewindow" TYPE="int" LENGTH="5" NOTNULL="true" SEQUENCE="false" COMMENT="Time window in seconds"/>
+        <FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Timestamp when this rule was last modified"/>
+        <FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Time stamp of when this rule was created"/>
+      </FIELDS>
+      <KEYS>
+        <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
+      </KEYS>
+      <INDEXES>
+        <INDEX NAME="courseanduser" UNIQUE="false" FIELDS="courseid, userid" COMMENT="Index on courseid and userid"/>
+        <INDEX NAME="eventname" UNIQUE="false" FIELDS="eventname" COMMENT="eventname"/>
+      </INDEXES>
+    </TABLE>
+    <TABLE NAME="tool_monitor_subscriptions" COMMENT="Table to store user subscriptions to various rules">
+      <FIELDS>
+        <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
+        <FIELD NAME="courseid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Course id of the subscription"/>
+        <FIELD NAME="ruleid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Rule id"/>
+        <FIELD NAME="cmid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Course module id"/>
+        <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="User id of the subscriber"/>
+        <FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Timestamp of when this subscription was created"/>
+      </FIELDS>
+      <KEYS>
+        <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
+        <KEY NAME="rulekey" TYPE="foreign" FIELDS="ruleid" REFTABLE="tool_monitor_rules" REFFIELDS="id" COMMENT="Foreign key"/>
+      </KEYS>
+      <INDEXES>
+        <INDEX NAME="courseanduser" UNIQUE="false" FIELDS="courseid, userid" COMMENT="Course and user"/>
+      </INDEXES>
+    </TABLE>
+    <TABLE NAME="tool_monitor_history" COMMENT="Table to store history of message notifications sent">
+      <FIELDS>
+        <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
+        <FIELD NAME="sid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Subscription id"/>
+        <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="User to whom this notification was sent"/>
+        <FIELD NAME="timesent" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Timestamp of when the message was sent."/>
+      </FIELDS>
+      <KEYS>
+        <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
+        <KEY NAME="subscrptionid" TYPE="foreign" FIELDS="sid" REFTABLE="tool_monitor_subscriptions" REFFIELDS="id"/>
+      </KEYS>
+      <INDEXES>
+        <INDEX NAME="sid_userid_timesent" UNIQUE="true" FIELDS="sid, userid, timesent"/>
+      </INDEXES>
+    </TABLE>
+    <TABLE NAME="tool_monitor_events" COMMENT="A table that keeps a log of events related to subscriptions">
+      <FIELDS>
+        <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
+        <FIELD NAME="eventname" TYPE="char" LENGTH="254" NOTNULL="true" SEQUENCE="false" COMMENT="Event name"/>
+        <FIELD NAME="contextid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Context id"/>
+        <FIELD NAME="contextlevel" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Context level"/>
+        <FIELD NAME="contextinstanceid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Context instance id"/>
+        <FIELD NAME="link" TYPE="char" LENGTH="254" NOTNULL="true" SEQUENCE="false" COMMENT="Link to the event location"/>
+        <FIELD NAME="courseid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="course id"/>
+        <FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Time created"/>
+      </FIELDS>
+      <KEYS>
+        <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
+      </KEYS>
+    </TABLE>
+  </TABLES>
+</XMLDB>
\ No newline at end of file
diff --git a/admin/tool/monitor/db/messages.php b/admin/tool/monitor/db/messages.php
new file mode 100644 (file)
index 0000000..e80f6ef
--- /dev/null
@@ -0,0 +1,32 @@
+<?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/>.
+
+/**
+ * Message providers list.
+ *
+ * @package    tool_monitor
+ * @copyright  2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$messageproviders = array (
+    // Notify a user that a rule has happened.
+    'notification' => array (
+        'capability'  => 'tool/monitor:subscribe'
+    )
+);
diff --git a/admin/tool/monitor/edit.php b/admin/tool/monitor/edit.php
new file mode 100644 (file)
index 0000000..c69e92e
--- /dev/null
@@ -0,0 +1,104 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This file gives an overview of the monitors present in site.
+ *
+ * @package    tool_monitor
+ * @copyright  2014 onwards Simey Lameze <simey@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+require(__DIR__ . '/../../../config.php');
+require_once($CFG->libdir.'/adminlib.php');
+
+$ruleid = optional_param('ruleid', 0, PARAM_INT);
+$courseid = optional_param('courseid', 0, PARAM_INT);
+
+// Validate course id.
+if (empty($courseid)) {
+    require_login();
+    $context = context_system::instance();
+    $coursename = format_string($SITE->fullname, true, array('context' => $context));
+    $PAGE->set_context($context);
+} else {
+    $course = get_course($courseid);
+    require_login($course);
+    $context = context_course::instance($course->id);
+    $coursename = format_string($course->fullname, true, array('context' => $context));
+}
+
+// Check for caps.
+require_capability('tool/monitor:managerules', $context);
+
+// Set up the page.
+$a = new stdClass();
+$a->coursename = $coursename;
+$a->reportname = get_string('pluginname', 'tool_monitor');
+$title = get_string('title', 'tool_monitor', $a);
+$url = new moodle_url("/admin/tool/monitor/edit.php", array('courseid' => $courseid, 'ruleid' => $ruleid));
+$manageurl = new moodle_url("/admin/tool/monitor/managerules.php", array('courseid' => $courseid));
+
+$PAGE->set_url($url);
+$PAGE->set_pagelayout('report');
+$PAGE->set_title($title);
+$PAGE->set_heading($title);
+
+// Get data ready for mform.
+$eventlist = tool_monitor\eventlist::get_all_eventlist(true);
+$pluginlist = tool_monitor\eventlist::get_plugin_list();
+$eventlist = array_merge(array('' => get_string('choosedots')), $eventlist);
+$pluginlist = array_merge(array('' => get_string('choosedots')), $pluginlist);
+
+// Set up the yui module.
+$PAGE->requires->yui_module('moodle-tool_monitor-dropdown', 'Y.M.tool_monitor.DropDown.init',
+        array(array('eventlist' => $eventlist)));
+
+// Site level report.
+if (empty($courseid)) {
+    admin_externalpage_setup('toolmonitorrules', '', null, '', array('pagelayout' => 'report'));
+} else {
+    // Course level report.
+    $PAGE->navigation->override_active_url($manageurl);
+}
+
+// Mform setup.
+if (!empty($ruleid)) {
+    $rule = \tool_monitor\rule_manager::get_rule($ruleid)->get_mform_set_data();
+    $rule->minutes = $rule->timewindow / MINSECS;
+} else {
+    $rule = new stdClass();
+}
+
+$mform = new tool_monitor\rule_form(null, array('eventlist' => $eventlist, 'pluginlist' => $pluginlist, 'rule' => $rule,
+        'courseid' => $courseid));
+
+if ($mformdata = $mform->get_data()) {
+    $rule = \tool_monitor\rule_manager::clean_ruledata_form($mformdata);
+
+    if (empty($rule->id)) {
+        \tool_monitor\rule_manager::add_rule($rule);
+    } else {
+        \tool_monitor\rule_manager::update_rule($rule);
+    }
+
+    redirect($manageurl);
+} else {
+    echo $OUTPUT->header();
+    $mform->set_data($rule);
+    $mform->display();
+    echo $OUTPUT->footer();
+}
+
diff --git a/admin/tool/monitor/help.php b/admin/tool/monitor/help.php
new file mode 100644 (file)
index 0000000..572b4ba
--- /dev/null
@@ -0,0 +1,62 @@
+<?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/>.
+
+/**
+ * Displays help on a new page.
+ *
+ * @copyright 2014 Mark Nelson <markn@moodle.com>
+ * @package tool_monitor
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+define('NO_MOODLE_COOKIES', true);
+
+require_once('../../../config.php');
+
+$type = required_param('type', PARAM_ALPHA);
+$id = required_param('id', PARAM_INT);
+$lang = optional_param('lang', 'en', PARAM_LANG);
+
+// We don't actually modify the session here as we have NO_MOODLE_COOKIES set.
+$SESSION->lang = $lang;
+
+$PAGE->set_url('/admin/tool/monitor/help.php');
+$PAGE->set_pagelayout('popup');
+
+if ($type == 'rule') {
+    $item = \tool_monitor\rule_manager::get_rule($id);
+} else { // Must be a subscription.
+    $item = \tool_monitor\subscription_manager::get_subscription($id);
+}
+
+if ($item->courseid) {
+    $PAGE->set_context(context_course::instance($item->courseid));
+} else { // Must be system context.
+    $PAGE->set_context(context_system::instance());
+}
+
+// Get the help string data.
+$data = tool_monitor\output\helpicon\renderable::get_help_string_parameters($type, $id);
+
+echo $OUTPUT->header();
+if (!empty($data->heading)) {
+    echo $OUTPUT->heading($data->heading, 1, 'helpheading');
+}
+echo $data->text;
+if (isset($data->completedoclink)) {
+    echo $data->completedoclink;
+}
+echo $OUTPUT->footer();
diff --git a/admin/tool/monitor/help_ajax.php b/admin/tool/monitor/help_ajax.php
new file mode 100644 (file)
index 0000000..429ff3b
--- /dev/null
@@ -0,0 +1,50 @@
+<?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/>.
+
+/**
+ * Displays help via AJAX call.
+ *
+ * @copyright 2014 Mark Nelson <markn@moodle.com>
+ * @package tool_monitor
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+define('NO_MOODLE_COOKIES', true);
+define('AJAX_SCRIPT', true);
+
+require_once('../../../config.php');
+
+$type = required_param('type', PARAM_ALPHA);
+$id = required_param('id', PARAM_INT);
+$lang = optional_param('lang', 'en', PARAM_LANG);
+
+// We don't actually modify the session here as we have NO_MOODLE_COOKIES set.
+$SESSION->lang = $lang;
+$PAGE->set_url('/admin/tool/monitor/help_ajax.php');
+
+if ($type == 'rule') {
+    $item = \tool_monitor\rule_manager::get_rule($id);
+} else { // Must be a subscription.
+    $item = \tool_monitor\subscription_manager::get_subscription($id);
+}
+
+if ($item->courseid) {
+    $PAGE->set_context(context_course::instance($item->courseid));
+} else { // Must be system context.
+    $PAGE->set_context(context_system::instance());
+}
+
+echo json_encode(tool_monitor\output\helpicon\renderable::get_help_string_parameters($type, $id, true));
diff --git a/admin/tool/monitor/index.php b/admin/tool/monitor/index.php
new file mode 100644 (file)
index 0000000..45c7309
--- /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/>.
+
+/**
+ * This page lets users to manage rules for a given course.
+ *
+ * @package    tool_monitor
+ * @copyright  2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once(__DIR__ . '/../../../config.php');
+require_once($CFG->libdir.'/adminlib.php');
+
+$courseid = optional_param('courseid', 0, PARAM_INT);
+$action = optional_param('action', '', PARAM_ALPHA);
+$cmid = optional_param('cmid', 0, PARAM_INT);
+$ruleid = optional_param('ruleid', 0, PARAM_INT);
+$subscriptionid = optional_param('subscriptionid', 0, PARAM_INT);
+
+// Validate course id.
+if (empty($courseid)) {
+    require_login();
+} else {
+    // They might want to see rules for this course.
+    $course = get_course($courseid);
+    require_login($course);
+    $coursecontext = context_course::instance($course->id);
+    // Check for caps.
+    require_capability('tool/monitor:subscribe', $coursecontext);
+    $coursename = format_string($course->fullname, true, array('context' => $coursecontext));
+}
+
+// Always build the page in site context.
+$context = context_system::instance();
+$sitename = format_string($SITE->fullname, true, array('context' => $context));
+$PAGE->set_context($context);
+
+// Set up the page.
+$a = new stdClass();
+$a->coursename = $sitename;
+$a->reportname = get_string('pluginname', 'tool_monitor');
+$title = get_string('title', 'tool_monitor', $a);
+$indexurl = new moodle_url("/admin/tool/monitor/index.php", array('courseid' => $courseid));
+
+$PAGE->set_url($indexurl);
+$PAGE->set_pagelayout('report');
+$PAGE->set_title($title);
+$PAGE->set_heading($title);
+
+echo $OUTPUT->header();
+
+// Create/delete subscription if needed.
+if (!empty($action)) {
+    require_sesskey();
+    switch ($action) {
+        case 'subscribe' :
+            $rule = \tool_monitor\rule_manager::get_rule($ruleid);
+            $rule->subscribe_user($courseid, $cmid);
+            echo $OUTPUT->notification(get_string('subcreatesuccess', 'tool_monitor'), 'notifysuccess');
+            break;
+        case 'unsubscribe' :
+            \tool_monitor\subscription_manager::delete_subscription($subscriptionid);
+            echo $OUTPUT->notification(get_string('subdeletesuccess', 'tool_monitor'), 'notifysuccess');
+            break;
+        default:
+    }
+}
+
+// Render the current subscriptions list.
+$totalsubs = \tool_monitor\subscription_manager::count_user_subscriptions();
+$renderer = $PAGE->get_renderer('tool_monitor', 'managesubs');
+if (!empty($totalsubs)) {
+    // Show the subscriptions section only if there are subscriptions.
+    $subs = new \tool_monitor\output\managesubs\subs('toolmonitorsubs', $indexurl, $courseid);
+    echo $OUTPUT->heading(get_string('currentsubscriptions', 'tool_monitor'));
+    echo $renderer->render($subs);
+}
+
+// Render the potential rules list.
+$totalrules = \tool_monitor\rule_manager::count_rules_by_courseid($courseid);
+echo $OUTPUT->heading(get_string('rulescansubscribe', 'tool_monitor'));
+$rules = new \tool_monitor\output\managesubs\rules('toolmonitorrules', $indexurl, $courseid);
+echo $renderer->render($rules);
+if (empty($totalrules)) {
+    // No rules present. Show a link to manage rules page if permissions permit.
+    echo html_writer::start_div();
+    echo html_writer::tag('span', get_string('norules', 'tool_monitor'));
+    if (has_capability('tool/monitor:managerules', $context)) {
+        $manageurl = new moodle_url("/admin/tool/monitor/managerules.php", array('courseid' => $courseid));
+        $a = html_writer::link($manageurl, get_string('managerules', 'tool_monitor'));
+        $link = "&nbsp;";
+        $link .= html_writer::tag('span', get_string('manageruleslink', 'tool_monitor', $a));
+        echo $link;
+    }
+    echo html_writer::end_div();
+}
+echo $OUTPUT->footer();
diff --git a/admin/tool/monitor/lang/en/tool_monitor.php b/admin/tool/monitor/lang/en/tool_monitor.php
new file mode 100644 (file)
index 0000000..8547f8d
--- /dev/null
@@ -0,0 +1,91 @@
+<?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/>.
+
+/**
+ * Lang strings.
+ *
+ * This files lists lang strings related to tool_monitor.
+ *
+ * @package    tool_monitor
+ * @copyright  2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['addrule'] = 'Add a new rule';
+$string['allevents'] = 'All events';
+$string['allmodules'] = 'All modules';
+$string['core'] = 'Core';
+$string['customisefilters'] = 'Select the frequency of the events';
+$string['customisemessage'] = 'Customise the notification message';
+$string['currentsubscriptions'] = 'Your current subscriptions';
+$string['description_help'] = "Description is displayed to users when they want to subscribe to this rule. This helps them understand what the rule is about.";
+$string['defaultmessagetpl'] = 'Rule "{rulename}" has happened. You can find further details at {link}';
+$string['deleterule'] = 'Delete rule';
+$string['deletesubscription'] = 'Delete subscription';
+$string['description'] = 'Description:';
+$string['duplicaterule'] = 'Duplicate rule';
+$string['editrule'] = 'Edit rule';
+$string['eventnotfound'] = 'Event not found';
+$string['errorincorrectevent'] = 'Please select an event related to the selected plugin';
+$string['freqdesc'] = '{$a->freq} times in {$a->mins} minutes';
+$string['frequency'] = 'Frequency';
+$string['invalidmodule'] = 'Invalid module';
+$string['norules'] = 'There are no rules you can subscribe to.';
+$string['manageruleslink'] = 'You can manage rules from {$a} page.';
+$string['moduleinstance'] = 'Module instance';
+$string['manage'] = 'Manage';
+$string['managesubscriptions'] = 'Event monitoring';
+$string['managerules'] = 'Event monitoring rules';
+$string['messageheader'] = 'Customise your notification message';
+$string['messageprovider:notification'] = 'Notifications of rule subscriptions';
+$string['messagetemplate'] = 'Message template';
+$string['messagetemplate_help'] = 'This is the content of the message that will be sent to users, when the given conditions of the rule are met. You are allowed to use following templates in this.
+<br /> {link} - Link to the location where the event happened.
+<br /> {modulelink} - Link to the module where the event has happened.
+<br /> {rulename} - Name of this rule.
+<br /> {description} - Rule description.
+<br /> {eventname} - Name of the event associated with the rule.';
+$string['minutes'] = 'in minutes:';
+$string['name'] = 'Name of the rule: ';
+$string['name_help'] = "Choose a name for the rule.";
+$string['pluginname'] = 'Event monitor';
+$string['processevents'] = 'Process events';
+$string['ruleareyousure'] = 'Are you sure you want to delete rule "{$a}"?';
+$string['rulecopysuccess'] = 'Rule successfully duplicated';
+$string['ruledeletesuccess'] = 'Rule successfully deleted';
+$string['rulehelp'] = 'Rule details';
+$string['rulehelp_help'] = 'This rule listens for when the event \'{$a->eventname}\' in \'{$a->eventcomponent}\' has been triggered {$a->frequency} time(s) in {$a->minutes} minute(s).';
+$string['rulenopermissions'] = 'You do not have permissions to "{$a} a rule"';
+$string['rulescansubscribe'] = 'Rules you can subscribe to';
+$string['selectacourse'] = 'Select a course';
+$string['selectcourse'] = 'Visit this report at course level to get a list of possible modules';
+$string['selectevent'] = 'Select an event:';
+$string['selectevent_help'] = "Select an event to monitor. Please note that certain event can happen only at certain given context. For example, a rule based on 'course created' event inside a course can never be triggered.";
+$string['selectfrequency'] = 'Frequency of events:';
+$string['selectfrequency_help'] = "Frequency defines the denisty of the event occurrence. Select criterias to define how frequently the event should happen to trigger the notification.";
+$string['selectminutes'] = 'in minutes:';
+$string['selectplugin'] = 'Select the plugin type:';
+$string['selectplugin_help'] = "Select a plugin that you are interested in monitoring. The event list below would be updated to display events from the selected plugin.";
+$string['subareyousure'] = 'Are you sure you want to delete this subscription for the rule "{$a}"?';
+$string['subcreatesuccess'] = "Subscription successfully created";
+$string['subdeletesuccess'] = "Subscription successfully removed";
+$string['subhelp'] = 'Subscription details';
+$string['subhelp_help'] = 'This subscription listens for when the event \'{$a->eventname}\' has been triggered in \'{$a->moduleinstance}\' {$a->frequency} time(s) in {$a->minutes} minute(s).';
+$string['title'] = '{$a->coursename} : {$a->reportname}';
+$string['monitor:managerules'] = 'Manage event monitor rules';
+$string['monitor:subscribe'] = 'Subscribe to event monitor rules';
+$string['unsubscribe'] = 'Unsubscribe';
+
diff --git a/admin/tool/monitor/lib.php b/admin/tool/monitor/lib.php
new file mode 100644 (file)
index 0000000..88d2df9
--- /dev/null
@@ -0,0 +1,68 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This page lists public api for tool_monitor plugin.
+ *
+ * @package    tool_monitor
+ * @copyright  2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+/**
+ * This function extends the navigation with the tool items
+ *
+ * @param navigation_node $navigation The navigation node to extend
+ * @param stdClass        $course     The course to object for the tool
+ * @param context         $context    The context of the course
+ */
+function tool_monitor_extend_navigation_course($navigation, $course, $context) {
+
+    if (has_capability('tool/monitor:managerules', $context)) {
+        $url = new moodle_url('/admin/tool/monitor/managerules.php', array('courseid' => $course->id));
+        $settingsnode = navigation_node::create(get_string('managerules', 'tool_monitor'), $url, navigation_node::TYPE_SETTING,
+                null, null, new pix_icon('i/settings', ''));
+        $reportnode = $navigation->get('coursereports');
+
+        if (isset($settingsnode) && !empty($reportnode)) {
+            $reportnode->add_node($settingsnode);
+        }
+    }
+}
+
+/**
+ * This function extends the navigation with the tool items for user settings node.
+ *
+ * @param navigation_node $navigation  The navigation node to extend
+ * @param stdClass        $user        The user object
+ * @param context         $usercontext The context of the user
+ * @param stdClass        $course      The course to object for the tool
+ * @param context         $coursecontext     The context of the course
+ */
+function tool_monitor_extend_navigation_user_settings($navigation, $user, $usercontext, $course, $coursecontext) {
+    global $USER;
+    if (($USER->id == $user->id)) {
+        $url = new moodle_url('/admin/tool/monitor/index.php', array('courseid' => $course->id));
+        $subsnode = navigation_node::create(get_string('managesubscriptions', 'tool_monitor'), $url,
+                navigation_node::TYPE_SETTING, null, null, new pix_icon('i/settings', ''));
+
+        if (isset($subsnode) && !empty($navigation)) {
+            $navigation->add_node($subsnode, 'changepassword');
+        }
+    }
+}
\ No newline at end of file
diff --git a/admin/tool/monitor/managerules.php b/admin/tool/monitor/managerules.php
new file mode 100644 (file)
index 0000000..8259fec
--- /dev/null
@@ -0,0 +1,93 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This page lets users to manage rules for a given course.
+ *
+ * @package    tool_monitor
+ * @copyright  2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once(__DIR__ . '/../../../config.php');
+require_once($CFG->libdir.'/adminlib.php');
+
+$courseid = optional_param('courseid', 0, PARAM_INT);
+$ruleid = optional_param('ruleid', 0, PARAM_INT);
+$action = optional_param('action', '', PARAM_ALPHA);
+
+// Validate course id.
+if (empty($courseid)) {
+    require_login();
+    $context = context_system::instance();
+    $coursename = format_string($SITE->fullname, true, array('context' => $context));
+    $PAGE->set_context($context);
+} else {
+    $course = get_course($courseid);
+    require_login($course);
+    $context = context_course::instance($course->id);
+    $coursename = format_string($course->fullname, true, array('context' => $context));
+}
+
+// Check for caps.
+require_capability('tool/monitor:managerules', $context);
+
+// Set up the page.
+$a = new stdClass();
+$a->coursename = $coursename;
+$a->reportname = get_string('pluginname', 'tool_monitor');
+$title = get_string('title', 'tool_monitor', $a);
+$manageurl = new moodle_url("/admin/tool/monitor/managerules.php", array('courseid' => $courseid));
+
+$PAGE->set_url($manageurl);
+$PAGE->set_pagelayout('report');
+$PAGE->set_title($title);
+$PAGE->set_heading($title);
+
+// Site level report.
+if (empty($courseid)) {
+    admin_externalpage_setup('toolmonitorrules', '', null, '', array('pagelayout' => 'report'));
+}
+
+echo $OUTPUT->header();
+
+// Copy/delete rule if needed.
+if (!empty($action) && $ruleid) {
+    require_sesskey();
+    $rule = \tool_monitor\rule_manager::get_rule($ruleid);
+    if ($rule->can_manage_rule()) {
+        switch ($action) {
+            case 'copy' :
+                $rule->duplicate_rule($courseid);
+                echo $OUTPUT->notification(get_string('rulecopysuccess', 'tool_monitor'), 'notifysuccess');
+                break;
+            case 'delete' :
+                $rule->delete_rule();
+                echo $OUTPUT->notification(get_string('ruledeletesuccess', 'tool_monitor'), 'notifysuccess');
+                break;
+            default:
+        }
+    } else {
+        // User doesn't have permissions. Should never happen for real users.
+        throw new moodle_exception('rulenopermissions', 'tool_monitor', $manageurl, $action);
+    }
+}
+
+// Render the rule list.
+$renderable = new \tool_monitor\output\managerules\renderable('toolmonitorrules', $manageurl, $courseid);
+$renderer = $PAGE->get_renderer('tool_monitor', 'managerules');
+echo $renderer->render($renderable);
+echo $OUTPUT->footer();
diff --git a/admin/tool/monitor/settings.php b/admin/tool/monitor/settings.php
new file mode 100644 (file)
index 0000000..9e32468
--- /dev/null
@@ -0,0 +1,35 @@
+<?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/>.
+
+/**
+ * Links and settings
+ *
+ * This file contains links and settings used by tool_monitor
+ *
+ * @package    tool_monitor
+ * @copyright  2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+defined('MOODLE_INTERNAL') || die;
+
+if ($hassiteconfig) {
+
+    // Manage rules page.
+    $url = new moodle_url('/admin/tool/monitor/managerules.php', array('courseid' => 0));
+    $temp = new admin_externalpage('toolmonitorrules', get_string('managerules', 'tool_monitor'), $url,
+        'tool/monitor:managerules');
+    $ADMIN->add('reports', $temp);
+}
diff --git a/admin/tool/monitor/tests/behat/rule.feature b/admin/tool/monitor/tests/behat/rule.feature
new file mode 100644 (file)
index 0000000..80f74d3
--- /dev/null
@@ -0,0 +1,161 @@
+@javascript @tool @tool_monitor @tool_monitor_rules
+Feature: tool_monitor_rule
+  In order to manage rules
+  As an admin
+  I need to create a rule, edit a rule, duplicate a rule and delete a rule
+
+  Background:
+    Given the following "courses" exist:
+      | fullname | shortname |
+      | Course 1 | C1        |
+    And the following "users" exist:
+      | username | firstname | lastname | email |
+      | teacher1 | Teacher | 1 | teacher1@asd.com |
+    And the following "course enrolments" exist:
+      | user | course | role |
+      | teacher1 | C1 | editingteacher |
+    And   I log in as "admin"
+    And   I follow "Course 1"
+    And   I navigate to "Event monitoring rules" node in "Course administration > Reports"
+    And   I press "Add a new rule"
+    And   I set the following fields to these values:
+      | name              | New rule course level                             |
+      | plugin            | Forum                                             |
+      | eventname         | Post created                                      |
+      | id_description    | I want a rule to monitor posts created on a forum |
+      | frequency         | 1                                                 |
+      | minutes           | 1                                                 |
+      | Message template  | The forum post was created. {modulelink}          |
+    And   I press "Save changes"
+    And   I navigate to "Event monitoring rules" node in "Site administration > Reports"
+    And   I press "Add a new rule"
+    And   I set the following fields to these values:
+      | name              | New rule site level                               |
+      | plugin            | Forum                                             |
+      | eventname         | Post created                                      |
+      | id_description    | I want a rule to monitor posts created on a forum |
+      | frequency         | 1                                                 |
+      | minutes           | 1                                                 |
+      | Message template  | The forum post was created. {modulelink}          |
+    And  I press "Save changes"
+    And  I log out
+
+  Scenario: Add a rule on course level
+    Given I log in as "teacher1"
+    And   I am on homepage
+    And   I follow "Course 1"
+    And   I navigate to "Event monitoring rules" node in "Course administration > Reports"
+    When  I press "Add a new rule"
+    And   I set the following fields to these values:
+      | name              | New rule                                          |
+      | plugin            | Forum                                             |
+      | eventname         | Post created                                      |
+      | id_description    | I want a rule to monitor posts created on a forum |
+      | frequency         | 1                                                 |
+      | minutes           | 1                                                 |
+      | Message template  | The forum post was created. {modulelink}          |
+    And   I press "Save changes"
+    Then  I should see "New rule"
+    And   I should see "I want a rule to monitor posts created on a forum"
+    And   I should see "Forum"
+    And   I should see "Post created"
+    And   I should see "1 times in 1 minutes"
+
+  Scenario: Delete a rule on course level
+    Given I log in as "teacher1"
+    And   I follow "Course 1"
+    And   I navigate to "Event monitoring rules" node in "Course administration > Reports"
+    When  I click on "Delete rule" "link"
+    Then  I should see "Are you sure you want to delete rule \"New rule course level\"?"
+    And   I press "Yes"
+    And   I should see "Rule successfully deleted"
+    And   I should not see "New rule course level"
+
+  Scenario: Edit a rule on course level
+    Given I log in as "teacher1"
+    And   I follow "Course 1"
+    And   I navigate to "Event monitoring rules" node in "Course administration > Reports"
+    When  I click on "Edit rule" "link"
+    And   I set the following fields to these values:
+      | name              | New rule quiz                                  |
+      | plugin            | Quiz                                           |
+      | eventname         | Quiz attempt deleted                           |
+      | id_description    | I want a rule to monitor quiz attempts deleted |
+      | frequency         | 5                                              |
+      | minutes           | 5                                              |
+      | Message template  | Quiz attempt deleted. {modulelink}             |
+    And   I press "Save changes"
+    Then  I should see "New rule quiz"
+    And   I should see "I want a rule to monitor quiz attempts deleted"
+    And   I should see "Quiz attempt deleted"
+    And   I should see "5 times in 5 minutes"
+
+  Scenario: Duplicate a rule on course level
+    Given I log in as "teacher1"
+    And   I follow "Course 1"
+    And   I navigate to "Event monitoring rules" node in "Course administration > Reports"
+    When  I click on "Duplicate rule" "link"
+    Then  I should see "Rule successfully duplicated"
+    And   "#toolmonitorrules_r1" "css_element" should appear before "#toolmonitorrules_r2" "css_element"
+    And   I should see "New rule"
+    And   I should see "I want a rule to monitor posts created on a forum"
+    And   I should see "Forum"
+    And   I should see "Post created"
+    And   I should see "1 times in 1 minutes"
+
+  Scenario: Add a rule on site level
+    Given I log in as "admin"
+    And   I navigate to "Event monitoring rules" node in "Site administration > Reports"
+    When  I press "Add a new rule"
+    And   I set the following fields to these values:
+      | name              | New rule                                          |
+      | plugin            | Forum                                             |
+      | eventname         | Post created                                      |
+      | id_description    | I want a rule to monitor posts created on a forum |
+      | frequency         | 1                                                 |
+      | minutes           | 1                                                 |
+      | Message template  | The forum post was created. {modulelink}          |
+    And   I press "Save changes"
+    Then  I should see "New rule"
+    And   I should see "I want a rule to monitor posts created on a forum"
+    And   I should see "Forum"
+    And   I should see "Post created"
+    And   I should see "1 times in 1 minutes"
+
+  Scenario: Delete a rule on site level
+    Given I log in as "admin"
+    And   I navigate to "Event monitoring rules" node in "Site administration > Reports"
+    When  I click on "Delete rule" "link"
+    Then  I should see "Are you sure you want to delete rule \"New rule site level\"?"
+    And   I press "Yes"
+    And   I should see "Rule successfully deleted"
+    And   I should not see "New rule site level"
+
+  Scenario: Edit a rule on site level
+    Given I log in as "admin"
+    And   I navigate to "Event monitoring rules" node in "Site administration > Reports"
+    When  I click on "Edit rule" "link"
+    And   I set the following fields to these values:
+      | name              | New Rule Quiz                                  |
+      | plugin            | Quiz                                           |
+      | eventname         | Quiz attempt deleted                           |
+      | id_description    | I want a rule to monitor quiz attempts deleted |
+      | frequency         | 5                                              |
+      | minutes           | 5                                              |
+      | Message template  | Quiz attempt deleted. {modulelink}             |
+    And   I press "Save changes"
+    Then  I should see "New Rule Quiz"
+    And   I should see "I want a rule to monitor quiz attempts deleted"
+    And   I should see "Quiz attempt deleted"
+    And   I should see "5 times in 5 minutes"
+
+  Scenario: Duplicate a rule on site level
+    Given I log in as "admin"
+    And   I navigate to "Event monitoring rules" node in "Site administration > Reports"
+    When  I click on "Duplicate rule" "link"
+    Then  I should see "Rule successfully duplicated"
+    And   "#toolmonitorrules_r2" "css_element" should appear after "#toolmonitorrules_r1" "css_element"
+    And   I should see "I want a rule to monitor posts created on a forum"
+    And   I should see "Forum"
+    And   I should see "Post created"
+    And   I should see "1 times in 1 minutes"
diff --git a/admin/tool/monitor/tests/behat/subscription.feature b/admin/tool/monitor/tests/behat/subscription.feature
new file mode 100644 (file)
index 0000000..8d78a0a
--- /dev/null
@@ -0,0 +1,125 @@
+@javascript @tool @tool_monitor @tool_monitor_subscriptions
+Feature: tool_monitor_subscriptions
+  In order to monitor events and receive notifications
+  As an user
+  I need to create a new rule, subscribe to it, receive notification and delete subscription
+
+  Background:
+    Given the following "courses" exist:
+      | fullname | shortname |
+      | Course 1 | C1        |
+    And the following "users" exist:
+      | username | firstname | lastname | email |
+      | teacher1 | Teacher | 1 | teacher1@asd.com |
+    And the following "course enrolments" exist:
+      | user | course | role |
+      | teacher1 | C1 | editingteacher |
+    And   I log in as "admin"
+    And   I follow "Course 1"
+    And   I navigate to "Event monitoring rules" node in "Course administration > Reports"
+    And   I press "Add a new rule"
+    And   I set the following fields to these values:
+      | name              | New rule course level                             |
+      | plugin            | Core                                              |
+      | eventname         | Course viewed                                     |
+      | id_description    | I want a rule to monitor when a course is viewed. |
+      | frequency         | 1                                                 |
+      | minutes           | 1                                                 |
+      | Message template  | The course was viewed. {modulelink}               |
+    And   I press "Save changes"
+    And   I navigate to "Event monitoring rules" node in "Site administration > Reports"
+    And   I press "Add a new rule"
+    And   I set the following fields to these values:
+      | name              | New rule site level                               |
+      | plugin            | Core                                              |
+      | eventname         | Course viewed                                     |
+      | id_description    | I want a rule to monitor when a course is viewed. |
+      | frequency         | 1                                                 |
+      | minutes           | 1                                                 |
+      | Message template  | The course was viewed. {modulelink}               |
+    And  I press "Save changes"
+    And  I log out
+
+  Scenario: Subscribe to a rule on course level
+    Given I log in as "teacher1"
+    And   I follow "Course 1"
+    And   I navigate to "Event monitoring" node in "My profile settings"
+    And   I set the field "courseid" to "Course 1"
+    When  I set the field "cmid" to "All events"
+    Then  I should see "Subscription successfully created"
+    And   "#toolmonitorsubs_r0" "css_element" should exist
+
+  Scenario: Delete a subscription on course level
+    Given I log in as "teacher1"
+    And   I follow "Course 1"
+    And   I navigate to "Event monitoring" node in "My profile settings"
+    And   I set the field "courseid" to "Course 1"
+    And   I set the field "cmid" to "All events"
+    And   I should see "Subscription successfully created"
+    When  I click on "Delete subscription" "link"
+    And   I should see "Are you sure you want to delete this subscription for the rule \"New rule course level\"?"
+    And   I press "Yes"
+    Then  I should see "Subscription successfully removed"
+    And   "#toolmonitorsubs_r0" "css_element" should not exist
+
+  Scenario: Subscribe to a rule on site level
+    Given I log in as "admin"
+    And   I navigate to "Event monitoring" node in "My profile settings"
+    And   I set the field "courseid" to "Site"
+    When  I set the field "cmid" to "All events"
+    Then  I should see "Subscription successfully created"
+    And   "#toolmonitorsubs_r0" "css_element" should exist
+
+  Scenario: Delete a subscription on site level
+    Given I log in as "admin"
+    And   I navigate to "Event monitoring" node in "My profile settings"
+    And   I set the field "courseid" to "Site"
+    And   I set the field "cmid" to "All events"
+    And   I should see "Subscription successfully created"
+    And   "#toolmonitorsubs_r0" "css_element" should exist
+    When  I click on "Delete subscription" "link"
+    And   I should see "Are you sure you want to delete this subscription for the rule \"New rule site level\"?"
+    And   I press "Yes"
+    Then  I should see "Subscription successfully removed"
+    And   "#toolmonitorsubs_r0" "css_element" should not exist
+
+  Scenario: Receiving notification on site level
+    Given I log in as "admin"
+    And   I navigate to "Messaging" node in "My profile settings"
+    And   I click on "input[name^=tool_monitor_notification_loggedin]" "css_element"
+    And   I press "Update profile"
+    And   I am on homepage
+    And   I follow "Course 1"
+    And   I navigate to "Event monitoring" node in "My profile settings"
+    And   I set the field "courseid" to "Site"
+    And   I set the field "cmid" to "All events"
+    And   I should see "Subscription successfully created"
+    And   "#toolmonitorsubs_r0" "css_element" should exist
+    And   I am on homepage
+    And   I trigger cron
+    And   I am on homepage
+    And   I expand "My profile" node
+    When  I follow "Messages"
+    And   I follow "Do not reply to this email (1)"
+    Then  I should see "The course was viewed."
+
+  Scenario: Receiving notification on course level
+    Given I log in as "teacher1"
+    And   I navigate to "Messaging" node in "My profile settings"
+    And   I click on "input[name^=tool_monitor_notification_loggedin]" "css_element"
+    And   I press "Update profile"
+    And   I am on homepage
+    And   I follow "Course 1"
+    And   I navigate to "Event monitoring" node in "My profile settings"
+    And   I set the field "courseid" to "Course 1"
+    And   I set the field "cmid" to "All events"
+    And   I should see "Subscription successfully created"
+    And   "#toolmonitorsubs_r0" "css_element" should exist
+    And   I am on homepage
+    And   I follow "Course 1"
+    And   I trigger cron
+    And   I am on homepage
+    And   I expand "My profile" node
+    When  I follow "Messages"
+    And   I follow "Do not reply to this email (1)"
+    Then  I should see "The course was viewed."
diff --git a/admin/tool/monitor/tests/eventobservers_test.php b/admin/tool/monitor/tests/eventobservers_test.php
new file mode 100644 (file)
index 0000000..b8d178a
--- /dev/null
@@ -0,0 +1,482 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Unit tests for event observers.
+ *
+ * @package    tool_monitor
+ * @category   test
+ * @copyright  2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+require_once($CFG->dirroot . '/blog/locallib.php');
+require_once($CFG->dirroot . '/blog/lib.php');
+
+/**
+ * Class tool_monitor_eventobservers_testcase
+ *
+ * Tests for event observers
+ */
+class tool_monitor_eventobservers_testcase extends advanced_testcase {
+
+    /**
+     * Test observer for course delete event.
+     */
+    public function test_course_deleted() {
+        global $DB;
+
+        $this->setAdminUser();
+        $this->resetAfterTest(true);
+
+        $user = $this->getDataGenerator()->create_user();
+        $course = $this->getDataGenerator()->create_course();
+        $monitorgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor');
+
+        $rule = new stdClass();
+        $rule->userid = $user->id;
+        $rule->courseid = $course->id;
+        $rule->plugin = 'test';
+
+        $sub = new stdClass();
+        $sub->courseid = $course->id;
+        $sub->userid = $user->id;
+
+        // Add 10 rules for this course with subscriptions.
+        for ($i = 0; $i < 10; $i++) {
+            $createdrule = $monitorgenerator->create_rule($rule);
+            $sub->ruleid = $createdrule->id;
+            $monitorgenerator->create_subscription($sub);
+        }
+
+        // Add 10 random rules for random courses.
+        for ($i = 0; $i < 10; $i++) {
+            $rule->courseid = rand(10000000, 50000000);
+            $createdrule = $monitorgenerator->create_rule($rule);
+            $sub->courseid = $rule->courseid;
+            $sub->ruleid = $createdrule->id;
+            $monitorgenerator->create_subscription($sub);
+        }
+
+        // Verify data before course delete.
+        $totalrules = \tool_monitor\rule_manager::get_rules_by_plugin('test');
+        $this->assertCount(20, $totalrules);
+        $courserules = \tool_monitor\rule_manager::get_rules_by_courseid($course->id);
+        $this->assertCount(10, $courserules);
+        $totalsubs = $DB->get_records('tool_monitor_subscriptions');
+        $this->assertCount(20, $totalsubs);
+        $coursesubs = \tool_monitor\subscription_manager::get_user_subscriptions_for_course($course->id, 0, 0, $user->id);
+        $this->assertCount(10, $coursesubs);
+
+        // Let us delete the course now.
+        delete_course($course->id, false);
+
+        // Verify data after course delete.
+        $totalrules = \tool_monitor\rule_manager::get_rules_by_plugin('test');
+        $this->assertCount(10, $totalrules);
+        $courserules = \tool_monitor\rule_manager::get_rules_by_courseid($course->id);
+        $this->assertCount(0, $courserules); // Making sure all rules are deleted.
+        $totalsubs = $DB->get_records('tool_monitor_subscriptions');
+        $this->assertCount(10, $totalsubs);
+        $coursesubs = \tool_monitor\subscription_manager::get_user_subscriptions_for_course($course->id, 0, 0, $user->id);
+        $this->assertCount(0, $coursesubs); // Making sure all subscriptions are deleted.
+    }
+
+    /**
+     * This tests if writing of the events to the table tool_monitor_events is working fine.
+     */
+    public function test_flush() {
+        global $DB;
+
+        $this->resetAfterTest();
+
+        // Create events and verify data is fine.
+        $course = $this->getDataGenerator()->create_course();
+
+        $initialevents = $DB->get_records('tool_monitor_events');
+        $initalcount = count($initialevents);
+        $event = \mod_book\event\course_module_instance_list_viewed::create_from_course($course);
+        $event->trigger();
+
+        $events = $DB->get_records('tool_monitor_events');
+        $count = count($events);
+        $this->assertEquals($initalcount + 1, $count);
+        $monitorevent = array_pop($events);
+
+        // Match values.
+        $this->assertEquals($event->eventname, $monitorevent->eventname);
+        $this->assertEquals($event->contextid, $monitorevent->contextid);
+        $this->assertEquals($event->contextlevel, $monitorevent->contextlevel);
+        $this->assertEquals($event->get_url()->out(), $monitorevent->link);
+        $this->assertEquals($event->courseid, $monitorevent->courseid);
+        $this->assertEquals($event->timecreated, $monitorevent->timecreated);
+    }
+
+    /**
+     * Test the notification sending features.
+     */
+    public function test_process_event() {
+
+        global $DB, $USER;
+
+        $this->resetAfterTest();
+        $this->setAdminUser();
+        $msgsink = $this->redirectMessages();
+
+        // Generate data.
+        $course = $this->getDataGenerator()->create_course();
+        $toolgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor');
+
+        $rulerecord = new stdClass();
+        $rulerecord->courseid = $course->id;
+        $rulerecord->eventname = '\mod_book\event\course_module_instance_list_viewed';
+        $rulerecord->frequency = 1;
+
+        $rule = $toolgenerator->create_rule($rulerecord);
+
+        $subrecord = new stdClass();
+        $subrecord->courseid = $course->id;
+        $subrecord->ruleid = $rule->id;
+        $subrecord->userid = $USER->id;
+        $toolgenerator->create_subscription($subrecord);
+
+        $recordexists = $DB->record_exists('task_adhoc', array('component' => 'tool_monitor'));
+        $this->assertFalse($recordexists);
+
+        // Now let us trigger the event.
+        $event = \mod_book\event\course_module_instance_list_viewed::create_from_course($course);
+        $event->trigger();
+
+        $this->verify_processed_data($msgsink);
+
+        // Clean up.
+        \tool_monitor\rule_manager::delete_rule($rule->id);
+        $DB->delete_records('tool_monitor_events');
+
+        // Let us create a rule with more than 1 frequency.
+        $rulerecord->frequency = 5;
+        $rule = $toolgenerator->create_rule($rulerecord);
+        $subrecord->ruleid = $rule->id;
+        $toolgenerator->create_subscription($subrecord);
+
+        // Let us trigger events.
+        for ($i = 0; $i < 5; $i++) {
+            $event = \mod_book\event\course_module_instance_list_viewed::create_from_course($course);
+            $event->trigger();
+            if ($i != 4) {
+                $this->verify_message_not_sent_yet($msgsink);
+            }
+        }
+
+        $this->verify_processed_data($msgsink);
+
+        // Clean up.
+        \tool_monitor\rule_manager::delete_rule($rule->id);
+        $DB->delete_records('tool_monitor_events');
+
+        // Now let us create a rule specific to a module instance.
+        $cm = new stdClass();
+        $cm->course = $course->id;
+        $book = $this->getDataGenerator()->create_module('book', $cm);
+        $rulerecord->eventname = '\mod_book\event\course_module_viewed';
+        $rulerecord->cmid = $book->cmid;
+        $rule = $toolgenerator->create_rule($rulerecord);
+        $subrecord->ruleid = $rule->id;
+        $toolgenerator->create_subscription($subrecord);
+
+        // Let us trigger events.
+        $params = array(
+            'context' => context_module::instance($book->cmid),
+            'objectid' => $book->id
+        );
+        for ($i = 0; $i < 5; $i++) {
+            $event = \mod_book\event\course_module_viewed::create($params);
+            $event->trigger();
+            if ($i != 4) {
+                $this->verify_message_not_sent_yet($msgsink);
+            }
+        }
+
+        $this->verify_processed_data($msgsink);
+
+        // Clean up.
+        \tool_monitor\rule_manager::delete_rule($rule->id);
+        $DB->delete_records('tool_monitor_events');
+
+        // Now let us create a rule for event that happens in category context events.
+        $rulerecord->eventname = '\core\event\course_category_created';
+        $rulerecord->courseid = 0;
+        $rule = $toolgenerator->create_rule($rulerecord);
+        $subrecord->courseid = 0;
+        $subrecord->ruleid = $rule->id;
+        $toolgenerator->create_subscription($subrecord);
+
+        // Let us trigger events.
+        for ($i = 0; $i < 5; $i++) {
+            $this->getDataGenerator()->create_category();
+            if ($i != 4) {
+                $this->verify_message_not_sent_yet($msgsink);
+            }
+        }
+        $this->verify_processed_data($msgsink);
+
+        // Clean up.
+        \tool_monitor\rule_manager::delete_rule($rule->id);
+        $DB->delete_records('tool_monitor_events');
+
+        // Now let us create a rule at site level.
+        $rulerecord->eventname = '\core\event\blog_entry_created';
+        $rulerecord->courseid = 0;
+        $rule = $toolgenerator->create_rule($rulerecord);
+        $subrecord->courseid = 0;
+        $subrecord->ruleid = $rule->id;
+        $toolgenerator->create_subscription($subrecord);
+
+        // Let us trigger events.
+        $blog = new blog_entry();
+        $blog->subject = "Subject of blog";
+        $blog->userid = $USER->id;
+        $states = blog_entry::get_applicable_publish_states();
+        $blog->publishstate = reset($states);
+        for ($i = 0; $i < 5; $i++) {
+            $newblog = fullclone($blog);
+            $newblog->add();
+            if ($i != 4) {
+                $this->verify_message_not_sent_yet($msgsink);
+            }
+        }
+
+        $this->verify_processed_data($msgsink);
+    }
+
+    /**
+     * Run adhoc tasks.
+     */
+    protected function run_adhock_tasks() {
+        while ($task = \core\task\manager::get_next_adhoc_task(time())) {
+            $task->execute();
+            \core\task\manager::adhoc_task_complete($task);
+        }
+    }
+
+    /**
+     * Verify that task was scheduled and a message was sent as expected.
+     *
+     * @param phpunit_message_sink $msgsink Message sink
+     */
+    protected function verify_processed_data(phpunit_message_sink $msgsink) {
+        global $DB, $USER;
+
+        $recordexists = $DB->count_records('task_adhoc', array('component' => 'tool_monitor'));
+        $this->assertEquals(1, $recordexists); // We should have an adhock task now to send notifications.
+        $this->run_adhock_tasks();
+        $this->assertEquals(1, $msgsink->count());
+        $msgs = $msgsink->get_messages();
+        $msg = array_pop($msgs);
+        $this->assertEquals($USER->id, $msg->useridto);
+        $this->assertEquals(1, $msg->notification);
+        $msgsink->clear();
+    }
+
+    /**
+     * Verify that a message was not sent.
+     *
+     * @param phpunit_message_sink $msgsink Message sink
+     */
+    protected function verify_message_not_sent_yet(phpunit_message_sink $msgsink) {
+        $msgs = $msgsink->get_messages();
+        $this->assertCount(0, $msgs);
+        $msgsink->clear();
+    }
+
+    /**
+     * Tests for replace_placeholders method.
+     */
+    public function test_replace_placeholders() {
+        global $USER;
+
+        $this->resetAfterTest();
+        $this->setAdminUser();
+        $msgsink = $this->redirectMessages();
+
+        // Generate data.
+        $course = $this->getDataGenerator()->create_course();
+        $toolgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor');
+        $context = \context_user::instance($USER->id, IGNORE_MISSING);
+
+        // Creating book.
+        $cm = new stdClass();
+        $cm->course = $course->id;
+        $book = $this->getDataGenerator()->create_module('book', $cm);
+
+        // Creating rule.
+        $rulerecord = new stdClass();
+        $rulerecord->courseid = $course->id;
+        $rulerecord->eventname = '\mod_book\event\course_module_viewed';
+        $rulerecord->cmid = $book->cmid;
+        $rulerecord->frequency = 1;
+        $rulerecord->template = '{link} {modulelink} {rulename} {description} {eventname}';
+
+        $rule = $toolgenerator->create_rule($rulerecord);
+
+        // Creating subscription.
+        $subrecord = new stdClass();
+        $subrecord->courseid = $course->id;
+        $subrecord->ruleid = $rule->id;
+        $subrecord->userid = $USER->id;
+        $toolgenerator->create_subscription($subrecord);
+
+        // Now let us trigger the event.
+        $params = array(
+            'context' => context_module::instance($book->cmid),
+            'objectid' => $book->id
+        );
+
+        $event = \mod_book\event\course_module_viewed::create($params);
+        $event->trigger();
+        $this->run_adhock_tasks();
+        $msgs = $msgsink->get_messages();
+        $msg = array_pop($msgs);
+
+        $modurl = new moodle_url('/mod/book/view.php', array('id' => $book->cmid));
+        $expectedmsg = $event->get_url()->out() . ' ' .
+                        $modurl->out()  . ' ' .
+                        $rule->get_name($context) . ' ' .
+                        $rule->get_description($context) . ' ' .
+                        $rule->get_event_name();
+
+        $this->assertEquals($expectedmsg, $msg->fullmessage);
+    }
+
+    /**
+     * Test observer for user delete event.
+     */
+    public function test_user_deleted() {
+        global $DB;
+
+        $this->setAdminUser();
+        $this->resetAfterTest(true);
+
+        $user = $this->getDataGenerator()->create_user();
+        $course = $this->getDataGenerator()->create_course();
+        $monitorgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor');
+
+        $rule = new stdClass();
+        $rule->userid = $user->id;
+        $rule->courseid = $course->id;
+        $rule->plugin = 'test';
+
+        $sub = new stdClass();
+        $sub->courseid = $course->id;
+        $sub->userid = $user->id;
+
+        // Add 10 rules for this course with subscriptions.
+        for ($i = 0; $i < 10; $i++) {
+            $createdrule = $monitorgenerator->create_rule($rule);
+            $sub->ruleid = $createdrule->id;
+            $monitorgenerator->create_subscription($sub);
+        }
+
+        // Add 10 random rules for random courses.
+        for ($i = 0; $i < 10; $i++) {
+            $rule->courseid = rand(10000000, 50000000);
+            $createdrule = $monitorgenerator->create_rule($rule);
+            $sub->courseid = $rule->courseid;
+            $sub->ruleid = $createdrule->id;
+            $monitorgenerator->create_subscription($sub);
+        }
+
+        // Verify data before user delete.
+        $totalrules = \tool_monitor\rule_manager::get_rules_by_plugin('test');
+        $this->assertCount(20, $totalrules);
+        $totalsubs = $DB->get_records('tool_monitor_subscriptions');
+        $this->assertCount(20, $totalsubs);
+
+        // Let us delete the user now.
+        delete_user($user);
+
+        // Verify data after course delete.
+        $totalrules = \tool_monitor\rule_manager::get_rules_by_plugin('test');
+        $this->assertCount(20, $totalrules);
+        $totalsubs = $DB->get_records('tool_monitor_subscriptions');
+        $this->assertCount(0, $totalsubs); // Make sure all subscriptions are deleted.
+    }
+
+    /**
+     * Test observer for course module delete event.
+     */
+    public function test_course_module_deleted() {
+        global $DB;
+
+        $this->setAdminUser();
+        $this->resetAfterTest(true);
+
+        $user = $this->getDataGenerator()->create_user();
+        $course = $this->getDataGenerator()->create_course();
+        $monitorgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor');
+
+        // Now let us create a rule specific to a module instance.
+        $cm = new stdClass();
+        $cm->course = $course->id;
+        $book = $this->getDataGenerator()->create_module('book', $cm);
+
+        $rule = new stdClass();
+        $rule->userid = $user->id;
+        $rule->courseid = $course->id;
+        $rule->plugin = 'test';
+
+        $sub = new stdClass();
+        $sub->courseid = $course->id;
+        $sub->userid = $user->id;
+        $sub->cmid = $book->cmid;
+
+        // Add 10 rules for this course with subscriptions for this module.
+        for ($i = 0; $i < 10; $i++) {
+            $createdrule = $monitorgenerator->create_rule($rule);
+            $sub->ruleid = $createdrule->id;
+            $monitorgenerator->create_subscription($sub);
+        }
+
+        // Add 10 random rules for random courses.
+        for ($i = 0; $i < 10; $i++) {
+            $rule->courseid = rand(10000000, 50000000);
+            $createdrule = $monitorgenerator->create_rule($rule);
+            $sub->courseid = $rule->courseid;
+            $sub->ruleid = $createdrule->id;
+            $sub->cmid = 0;
+            $monitorgenerator->create_subscription($sub);
+        }
+
+        // Verify data before module delete.
+        $totalrules = \tool_monitor\rule_manager::get_rules_by_plugin('test');
+        $this->assertCount(20, $totalrules);
+        $totalsubs = $DB->get_records('tool_monitor_subscriptions');
+        $this->assertCount(20, $totalsubs);
+
+        // Let us delete the user now.
+        course_delete_module($book->cmid);
+
+        // Verify data after course delete.
+        $totalrules = \tool_monitor\rule_manager::get_rules_by_plugin('test');
+        $this->assertCount(20, $totalrules);
+        $totalsubs = $DB->get_records('tool_monitor_subscriptions');
+        $this->assertCount(10, $totalsubs); // Make sure only relevant subscriptions are deleted.
+    }
+
+}
diff --git a/admin/tool/monitor/tests/generator/lib.php b/admin/tool/monitor/tests/generator/lib.php
new file mode 100644 (file)
index 0000000..26c973d
--- /dev/null
@@ -0,0 +1,200 @@
+<?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/>.
+
+/**
+ * Event monitor data generator
+ *
+ * @package    tool_monitor
+ * @category   test
+ * @copyright  2014 onwards Simey Lameze <simey@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Event monitor data generator class
+ *
+ * @since       Moodle 2.8
+ * @package     tool_monitor
+ * @category    test
+ * @copyright   2014 onwards Simey Lameze <simey@moodle.com>
+ * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class tool_monitor_generator extends testing_module_generator {
+
+    /**
+     * @var int keep track of how many rules have been created.
+     */
+    protected $rulecount;
+
+    /**
+     * Function to generate rule data.
+     *
+     * @param \stdClass|array $record data to insert as rule entry.
+     *
+     * @return \tool_monitor\rule An instance of rule class.
+     */
+    public function create_rule($record = null) {
+        global $USER;
+
+        $this->rulecount++;
+        $i = $this->rulecount;
+        $now = time();
+        $record = (object)(array)$record;
+
+        if (!isset($record->userid)) {
+            $record->userid = $USER->id;
+        }
+        if (!isset($record->courseid)) {
+            $record->courseid = 0;
+        }
+        if (!isset($record->name)) {
+            $record->name = 'Test rule ' . $i;
+        }
+        if (!isset($record->description)) {
+            $record->description = 'Rule description ' . $i;
+        }
+        if (!isset($record->descriptionformat)) {
+            $record->descriptionformat = FORMAT_HTML;
+        }
+        if (!isset($record->frequency)) {
+            $record->frequency = 5;
+        }
+        if (!isset($record->minutes)) {
+            $record->minutes = 5;
+        }
+        if (!isset($record->template)) {
+            $record->template = 'Rule message template ' . $i;
+        }
+        if (!isset($record->templateformat)) {
+            $record->templateformat = FORMAT_HTML;
+        }
+        if (!isset($record->timewindow)) {
+            $record->timewindow = $record->minutes * 60;
+        }
+        if (!isset($record->timecreated)) {
+            $record->timecreated = $now;
+        }
+        if (!isset($record->timemodified)) {
+            $record->timemodified = $now;
+        }
+        if (!isset($record->plugin)) {
+            $record->plugin = 'core';
+        }
+        if (!isset($record->eventname)) {
+            $record->eventname = '\core\event\blog_entry_created';
+        }
+
+        unset($record->minutes); // Remove the minutes shortcut to the timewindow.
+        return \tool_monitor\rule_manager::add_rule($record);
+    }
+
+    /**
+     * Function to generate subscription data.
+     *
+     * @throws coding_exception if $record->ruleid or $record->userid not present.
+     * @param \stdClass|array $record data to insert as subscription entry.
+     *
+     * @return \tool_monitor\subscription An instance of the subscription class.
+     */
+    public function create_subscription($record = null) {
+
+        if (!isset($record->timecreated)) {
+            $record->timecreated = time();
+        }
+        if (!isset($record->courseid)) {
+            $record->courseid = 0;
+        }
+        if (!isset($record->ruleid)) {
+            throw new coding_exception('$record->ruleid must be present in tool_monitor_generator::create_subscription()');
+        }
+        if (!isset($record->cmid)) {
+            $record->cmid = 0;
+        }
+        if (!isset($record->userid)) {
+            throw new coding_exception('$record->userid must be present in tool_monitor_generator::create_subscription()');
+        }
+
+        $sid = \tool_monitor\subscription_manager::create_subscription($record->ruleid, $record->courseid,
+                $record->cmid, $record->userid);
+        return \tool_monitor\subscription_manager::get_subscription($sid);
+    }
+
+    /**
+     * Function to generate event entries.
+     *
+     * @param \stdClass|array $record data to insert as event entry.
+     *
+     * @return \stdClass $record An object representing the newly created event entry.
+     */
+    public function create_event_entries($record = null) {
+        global $DB, $CFG;
+
+        $record = (object)(array)$record;
+        $context = \context_system::instance();
+
+        if (!isset($record->eventname)) {
+            $record->eventname = '\core\event\user_loggedin';
+        }
+        if (!isset($record->contextid)) {
+            $record->contextid = $context->id;
+        }
+        if (!isset($record->contextlevel)) {
+            $record->contextlevel = $context->contextlevel;
+        }
+        if (!isset($record->contextinstanceid)) {
+            $record->contextinstanceid = $context->instanceid;
+        }
+        if (!isset($record->link)) {
+            $record->link = $CFG->wwwroot . '/user/profile.php';
+        }
+        if (!isset($record->courseid)) {
+            $record->courseid = 0;
+        }
+        if (!isset($record->timecreated)) {
+            $record->timecreated = time();
+        }
+        $record->id = $DB->insert_record('tool_monitor_events', $record, true);
+
+        return $record;
+    }
+
+    /**
+     * Function to generate history data.
+     *
+     * @throws coding_exception if $record->sid or $record->userid not present.
+     * @param \stdClass|array $record data to insert as history entry.
+     *
+     * @return \stdClass $record An object representing the newly created history entry.
+     */
+    public function create_history($record = null) {
+        global $DB;
+        $record = (object)(array)$record;
+        if (!isset($record->sid)) {
+            throw new coding_exception('subscription ID must be present in tool_monitor_generator::create_history() $record');
+        }
+        if (!isset($record->userid)) {
+            throw new coding_exception('user ID must be present in tool_monitor_generator::create_history() $record');
+        }
+        if (!isset($record->timesent)) {
+            $record->timesent = time();
+        }
+        $record->id = $DB->insert_record('tool_monitor_history', $record, true);
+
+        return $record;
+    }
+}
diff --git a/admin/tool/monitor/tests/generator_test.php b/admin/tool/monitor/tests/generator_test.php
new file mode 100644 (file)
index 0000000..7e6403e
--- /dev/null
@@ -0,0 +1,133 @@
+<?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/>.
+
+/**
+ * PHPUnit data generator tests.
+ *
+ * @package    tool_monitor
+ * @category   test
+ * @copyright  2014 onwards Simey Lameze <simey@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * PHPUnit data generator test case.
+ *
+ * @since      Moodle 2.8
+ * @package    tool_monitor
+ * @category   test
+ * @copyright  2014 onwards Simey Lameze <simey@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class tool_monitor_generator_testcase extends advanced_testcase {
+
+    /**
+     * Test create_rule data generator.
+     */
+    public function test_create_rule() {
+        $this->setAdminUser();
+        $this->resetAfterTest(true);
+        $course = $this->getDataGenerator()->create_course();
+        $user = $this->getDataGenerator()->create_user();
+
+        $rulegenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor');
+
+        $record = new stdClass();
+        $record->courseid = $course->id;
+        $record->userid = $user->id;
+
+        $rule = $rulegenerator->create_rule($record);
+        $this->assertInstanceOf('tool_monitor\rule', $rule);
+        $this->assertEquals($rule->userid, $record->userid);
+        $this->assertEquals($rule->courseid, $record->courseid);
+    }
+
+    /**
+     * Test create_subscription data generator.
+     */
+    public function test_create_subscription() {
+        $this->setAdminUser();
+        $this->resetAfterTest(true);
+
+        $user = $this->getDataGenerator()->create_user();
+        $course = $this->getDataGenerator()->create_course();
+        $monitorgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor');
+        $rule = $monitorgenerator->create_rule();
+
+        $record = new stdClass();
+        $record->courseid = $course->id;
+        $record->userid = $user->id;
+        $record->ruleid = $rule->id;
+
+        $subscription = $monitorgenerator->create_subscription($record);
+        $this->assertEquals($record->courseid, $subscription->courseid);
+        $this->assertEquals($record->ruleid, $subscription->ruleid);
+        $this->assertEquals($record->userid, $subscription->userid);
+        $this->assertEquals(0, $subscription->cmid);
+
+        // Make sure rule id is always required.
+        $this->setExpectedException('coding_exception');
+        unset($record->ruleid);
+        $monitorgenerator->create_subscription($record);
+    }
+
+    /**
+     * Test create_event data generator.
+     */
+    public function test_create_event_entries() {
+        $this->setAdminUser();
+        $this->resetAfterTest(true);
+        $context = \context_system::instance();
+
+        // Default data generator values.
+        $monitorgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor');
+
+        // First create and assertdata using default values.
+        $eventdata = $monitorgenerator->create_event_entries();
+        $this->assertEquals('\core\event\user_loggedin', $eventdata->eventname);
+        $this->assertEquals($context->id, $eventdata->contextid);
+        $this->assertEquals($context->contextlevel, $eventdata->contextlevel);
+    }
+
+    /**
+     * Test create_history data generator.
+     */
+    public function test_create_history() {
+        $this->setAdminUser();
+        $this->resetAfterTest(true);
+        $user = $this->getDataGenerator()->create_user();
+        $monitorgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor');
+        $rule = $monitorgenerator->create_rule();
+
+        $record = new \stdClass();
+        $record->userid = $user->id;
+        $record->ruleid = $rule->id;
+        $sid = $monitorgenerator->create_subscription($record)->id;
+        $record->sid = $sid;
+        $historydata = $monitorgenerator->create_history($record);
+        $this->assertEquals($record->userid, $historydata->userid);
+        $this->assertEquals($record->sid, $historydata->sid);
+
+        // Test using default values.
+        $record->userid = 1;
+        $record->sid = 1;
+        $historydata = $monitorgenerator->create_history($record);
+        $this->assertEquals(1, $historydata->userid);
+        $this->assertEquals(1, $historydata->sid);
+    }
+}
\ No newline at end of file
diff --git a/admin/tool/monitor/tests/rule_manager_test.php b/admin/tool/monitor/tests/rule_manager_test.php
new file mode 100644 (file)
index 0000000..f9e05dc
--- /dev/null
@@ -0,0 +1,184 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Unit tests for rule manager api.
+ *
+ * @package    tool_monitor
+ * @category   test
+ * @copyright  2014 onwards Simey Lameze <simey@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+
+/**
+ * Tests for rule manager.
+ *
+ * Class tool_monitor_rule_manager_testcase
+ */
+class tool_monitor_rule_manager_testcase extends advanced_testcase {
+
+    /**
+     * Test add_rule method.
+     */
+    public function test_add_rule() {
+        $this->setAdminUser();
+        $this->resetAfterTest(true);
+
+        $user = $this->getDataGenerator()->create_user();
+        $course = $this->getDataGenerator()->create_course();
+        $now = time();
+
+        $rule = new stdClass();
+        $rule->userid = $user->id;
+        $rule->courseid = $course->id;
+        $rule->name = 'test rule 1';
+        $rule->plugin = 'core';
+        $rule->eventname = '\core\event\course_updated';
+        $rule->description = 'test description 1';
+        $rule->descriptionformat = FORMAT_HTML;
+        $rule->frequency = 15;
+        $rule->template = 'test template message';
+        $rule->templateformat = FORMAT_HTML;
+        $rule->timewindow = 300;
+        $rule->timecreated = $now;
+        $rule->timemodified = $now;
+
+        $ruledata = \tool_monitor\rule_manager::add_rule($rule);
+        foreach ($rule as $prop => $value) {
+            $this->assertEquals($ruledata->$prop, $value);
+        }
+    }
+
+    /**
+     * Test get_rule method.
+     */
+    public function test_get_rule() {
+        $this->setAdminUser();
+        $this->resetAfterTest(true);
+
+        $monitorgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor');
+        $rule = $monitorgenerator->create_rule();
+        $rules1 = \tool_monitor\rule_manager::get_rule($rule->id);
+        $this->assertInstanceOf('tool_monitor\rule', $rules1);
+        $this->assertEquals($rules1, $rule);
+    }
+
+    /**
+     * Test update_rule method.
+     */
+    public function test_update_rule() {
+        $this->setAdminUser();
+        $this->resetAfterTest(true);
+
+        $monitorgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor');
+        $rule = $monitorgenerator->create_rule();
+
+        $ruledata = new stdClass;
+        $ruledata->id = $rule->id;
+        $ruledata->frequency = 25;
+
+        \tool_monitor\rule_manager::update_rule($ruledata);
+        $this->assertEquals(25, $ruledata->frequency);
+
+    }
+
+    /**
+     * Test get_rules_by_courseid method.
+     */
+    public function test_get_rules_by_courseid() {
+        $this->setAdminUser();
+        $this->resetAfterTest(true);
+
+        $monitorgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor');
+
+        $record = new stdClass();
+        $record->courseid = 3;
+
+        $record2 = new stdClass();
+        $record2->courseid = 4;
+
+        $ruleids = array();
+        for ($i = 0; $i < 10; $i++) {
+            $rule = $monitorgenerator->create_rule($record);
+            $ruleids[] = $rule->id;
+            $rule = $monitorgenerator->create_rule(); // Create some site level rules.
+            $ruleids[] = $rule->id;
+            $rule = $monitorgenerator->create_rule($record2); // Create rules in a different course.
+        }
+        $ruledata = \tool_monitor\rule_manager::get_rules_by_courseid(3);
+        $this->assertEquals($ruleids, array_keys($ruledata));
+        $this->assertCount(20, $ruledata);
+    }
+
+    /**
+     * Test get_rules_by_plugin method.
+     */
+    public function test_get_rules_by_plugin() {
+        $this->setAdminUser();
+        $this->resetAfterTest(true);
+
+        $monitorgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor');
+
+        $record = new stdClass();
+        $record->plugin = 'core';
+
+        $record2 = new stdClass();
+        $record2->plugin = 'mod_assign';
+
+        $ruleids = array();
+        for ($i = 0; $i < 10; $i++) {
+            $rule = $monitorgenerator->create_rule($record);
+            $ruleids[] = $rule->id;
+            $rule = $monitorgenerator->create_rule($record2); // Create rules in a different plugin.
+        }
+
+        $ruledata = \tool_monitor\rule_manager::get_rules_by_plugin('core');
+        $this->assertEquals($ruleids, array_keys($ruledata));
+        $this->assertCount(10, $ruledata);
+    }
+
+    /**
+     * Test get_rules_by_event method.
+     */
+    public function test_get_rules_by_event() {
+        $this->setAdminUser();
+        $this->resetAfterTest(true);
+
+        $monitorgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor');
+        $rule = $monitorgenerator->create_rule();
+
+        $record = new stdClass();
+        $record->eventname = '\core\event\calendar_event_created';
+
+        $record2 = new stdClass();
+        $record2->eventname = '\core\event\calendar_event_updated';
+
+        $ruleids = array();
+        for ($i = 0; $i < 10; $i++) {
+            $rule = $monitorgenerator->create_rule($record);
+            $ruleids[] = $rule->id;
+            $rule = $monitorgenerator->create_rule($record2); // Create rules in a different plugin.
+        }
+
+        $ruledata = \tool_monitor\rule_manager::get_rules_by_event('\core\event\calendar_event_created');
+        $this->assertEmpty(array_diff(array_keys($ruledata), $ruleids));
+        $this->assertCount(10, $ruledata);
+    }
+}
\ No newline at end of file
diff --git a/admin/tool/monitor/version.php b/admin/tool/monitor/version.php
new file mode 100644 (file)
index 0000000..dba3696
--- /dev/null
@@ -0,0 +1,31 @@
+<?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/>.
+
+/**
+ * Version info
+ *
+ * This file contains version information about tool_monitor.
+ *
+ * @package    tool_monitor
+ * @copyright  2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+$plugin->version   = 2014061900;       // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires  = 2014061900;       // Requires this Moodle version.
+$plugin->component = 'tool_monitor'; // Full name of the plugin (used for diagnostics).
diff --git a/admin/tool/monitor/yui/build/moodle-tool_monitor-dropdown/moodle-tool_monitor-dropdown-debug.js b/admin/tool/monitor/yui/build/moodle-tool_monitor-dropdown/moodle-tool_monitor-dropdown-debug.js
new file mode 100644 (file)
index 0000000..79184dd
Binary files /dev/null and b/admin/tool/monitor/yui/build/moodle-tool_monitor-dropdown/moodle-tool_monitor-dropdown-debug.js differ
diff --git a/admin/tool/monitor/yui/build/moodle-tool_monitor-dropdown/moodle-tool_monitor-dropdown-min.js b/admin/tool/monitor/yui/build/moodle-tool_monitor-dropdown/moodle-tool_monitor-dropdown-min.js
new file mode 100644 (file)
index 0000000..c62a013
Binary files /dev/null and b/admin/tool/monitor/yui/build/moodle-tool_monitor-dropdown/moodle-tool_monitor-dropdown-min.js differ
diff --git a/admin/tool/monitor/yui/build/moodle-tool_monitor-dropdown/moodle-tool_monitor-dropdown.js b/admin/tool/monitor/yui/build/moodle-tool_monitor-dropdown/moodle-tool_monitor-dropdown.js
new file mode 100644 (file)
index 0000000..79184dd
Binary files /dev/null and b/admin/tool/monitor/yui/build/moodle-tool_monitor-dropdown/moodle-tool_monitor-dropdown.js differ
diff --git a/admin/tool/monitor/yui/src/dropdown/build.json b/admin/tool/monitor/yui/src/dropdown/build.json
new file mode 100644 (file)
index 0000000..66630f6
--- /dev/null
@@ -0,0 +1,10 @@
+{
+    "name": "moodle-tool_monitor-dropdown",
+    "builds": {
+        "moodle-tool_monitor-dropdown": {
+            "jsfiles": [
+                "dropdown.js"
+            ]
+        }
+    }
+}
diff --git a/admin/tool/monitor/yui/src/dropdown/js/dropdown.js b/admin/tool/monitor/yui/src/dropdown/js/dropdown.js
new file mode 100644 (file)
index 0000000..ff839b6
--- /dev/null
@@ -0,0 +1,103 @@
+/**
+ * A module to manage dropdowns on the rule add/edit form.
+ *
+ * @module moodle-tool_monitor-dropdown
+ */
+
+/**
+ * A module to manage dependent selects on the edit page.
+ *
+ * @since Moodle 2.8
+ * @class moodle-tool_monitor.dropdown
+ * @extends Base
+ * @constructor
+ */
+function DropDown() {
+    DropDown.superclass.constructor.apply(this, arguments);
+}
+
+
+var SELECTORS = {
+        PLUGIN: '#id_plugin',
+        EVENTNAME: '#id_eventname',
+        OPTION: 'option',
+        CHOOSE: 'option[value=""]'
+    };
+
+Y.extend(DropDown, Y.Base, {
+
+    /**
+     * Reference to the plugin node.
+     *
+     * @property plugin
+     * @type Object
+     * @default null
+     * @protected
+     */
+    plugin: null,
+
+    /**
+     * Reference to the plugin node.
+     *
+     * @property eventname
+     * @type Object
+     * @default null
+     * @protected
+     */
+    eventname: null,
+
+    /**
+     * Initializer.
+     * Basic setup and delegations.
+     *
+     * @method initializer
+     */
+    initializer: function() {
+        this.plugin = Y.one(SELECTORS.PLUGIN);
+        this.eventname = Y.one(SELECTORS.EVENTNAME);
+        this.plugin.on('change', this.updateEventsList, this);
+    },
+
+    /**
+     * Method to update the events list drop down when plugin list drop down is changed.
+     *
+     * @method updateEventsList
+     */
+    updateEventsList: function() {
+        var node, options, choosenode;
+        var plugin = this.plugin.get('value'); // Get component name.
+        var namespace = '\\' + plugin + '\\';
+        this.eventname.all(SELECTORS.OPTION).remove(true); // Delete all nodes.
+        options = this.get('eventlist');
+
+        // Mark the default choose node as visible and selected.
+        choosenode = Y.Node.create('<option value="">' + options[''] + '</option>');
+        choosenode.set('selected', 'selected');
+        this.eventname.appendChild(choosenode);
+
+        Y.Object.each(options, function(value, key) {
+            // Make sure we highlight only nodes with correct namespace.
+            if (key.substring(0, namespace.length) === namespace) {
+                node = Y.Node.create('<option value="' + key + '">' + value + '</option>');
+                this.eventname.appendChild(node);
+            }
+        }, this);
+
+    }
+}, {
+    NAME: 'dropDown',
+    ATTRS: {
+        /**
+         * A list of events with components.
+         *
+         * @attribute eventlist
+         * @default null
+         * @type Object
+         */
+        eventlist: null
+    }
+});
+
+Y.namespace('M.tool_monitor.DropDown').init = function(config) {
+    return new DropDown(config);
+};
diff --git a/admin/tool/monitor/yui/src/dropdown/meta/dropdown.json b/admin/tool/monitor/yui/src/dropdown/meta/dropdown.json
new file mode 100644 (file)
index 0000000..34fa63f
--- /dev/null
@@ -0,0 +1,9 @@
+{
+  "moodle-tool_monitor-dropdown": {
+    "requires": [
+        "base",
+        "event",
+        "node"
+    ]
+  }
+}
index 22235f6..818832d 100644 (file)
@@ -1141,7 +1141,7 @@ class core_plugin_manager {
             'tool' => array(
                 'assignmentupgrade', 'availabilityconditions', 'behat', 'capability', 'customlang',
                 'dbtransfer', 'generator', 'health', 'innodb', 'installaddon',
-                'langimport', 'log', 'messageinbound', 'multilangupgrade', 'phpunit', 'profiling',
+                'langimport', 'log', 'messageinbound', 'multilangupgrade', 'monitor', 'phpunit', 'profiling',
                 'replace', 'spamcleaner', 'task', 'timezoneimport',
                 'unittest', 'uploadcourse', 'uploaduser', 'unsuproles', 'xmldb'
             ),
index 7f58d5a..d21c5b1 100644 (file)
@@ -3753,7 +3753,7 @@ class settings_navigation extends navigation_node {
 
         // View course reports.
         if (has_capability('moodle/site:viewreports', $coursecontext)) { // Basic capability for listing of reports.
-            $reportnav = $coursenode->add(get_string('reports'), null, self::TYPE_CONTAINER, null, null,
+            $reportnav = $coursenode->add(get_string('reports'), null, self::TYPE_CONTAINER, null, 'coursereports',
                     new pix_icon('i/stats', ''));
             $coursereports = core_component::get_plugin_list('coursereport');
             foreach ($coursereports as $report => $dir) {
@@ -3891,6 +3891,13 @@ class settings_navigation extends navigation_node {
                 $switchroles->add($name, $url, self::TYPE_SETTING, null, $key, new pix_icon('i/switchrole', ''));
             }
         }
+
+        // Let admin tools hook into course navigation.
+        $tools = get_plugin_list_with_function('tool', 'extend_navigation_course', 'lib.php');
+        foreach ($tools as $toolfunction) {
+            $toolfunction($coursenode, $course, $coursecontext);
+        }
+
         // Return we are done
         return $coursenode;
     }
@@ -4205,7 +4212,7 @@ class settings_navigation extends navigation_node {
             if (empty($passwordchangeurl)) {
                 $passwordchangeurl = new moodle_url('/login/change_password.php', array('id'=>$course->id));
             }
-            $usersetting->add(get_string("changepassword"), $passwordchangeurl, self::TYPE_SETTING);
+            $usersetting->add(get_string("changepassword"), $passwordchangeurl, self::TYPE_SETTING, null, 'changepassword');
         }
 
         // View the roles settings
@@ -4323,6 +4330,12 @@ class settings_navigation extends navigation_node {
             $usersetting->add(get_string('loginas'), $url, self::TYPE_SETTING);
         }
 
+        // Let admin tools hook into user settings navigation.
+        $tools = get_plugin_list_with_function('tool', 'extend_navigation_user_settings', 'lib.php');
+        foreach ($tools as $toolfunction) {
+            $toolfunction($usersetting, $user, $usercontext, $course, $coursecontext);
+        }
+
         return $usersetting;
     }
 
index 07ad74e..47bbe27 100644 (file)
@@ -22,6 +22,9 @@ information provided here is intended especially for developers.
 * Google APIs Client Library (lib/google/) has been upgraded to 1.0.5-beta and
   API has changed dramatically without backward compatibility. Any code accessing
   it must be amended. It does not apply to lib/googleapi.php. See MDL-47297
+* Added an extra parameter to the function get_formatted_help_string() (default null) which is used to specify
+  additional string parameters.
+* User settings node and course node in navigation now support callbacks from admin tools.
 
 DEPRECATIONS:
 * completion_info->get_incomplete_criteria() is deprecated and will be removed in Moodle 3.0.
index 7e56c1b..63e9b8c 100644 (file)
@@ -3487,6 +3487,8 @@ function print_password_policy() {
  * @param boolean $ajax Whether this help is called from an AJAX script.
  *                This is used to influence text formatting and determines
  *                which format to output the doclink in.
+ * @param string|object|array $a An object, string or number that can be used
+ *      within translation strings
  * @return Object An object containing:
  * - heading: Any heading that there may be for this help string.
  * - text: The wiki-formatted help string.
@@ -3494,7 +3496,7 @@ function print_password_policy() {
  *            CSS classes to apply to that link. Only present if $ajax = false.
  * - completedoclink: A text representation of the doclink. Only present if $ajax = true.
  */
-function get_formatted_help_string($identifier, $component, $ajax = false) {
+function get_formatted_help_string($identifier, $component, $ajax = false, $a = null) {
     global $CFG, $OUTPUT;
     $sm = get_string_manager();
 
@@ -3521,7 +3523,7 @@ function get_formatted_help_string($identifier, $component, $ajax = false) {
         $options->overflowdiv = !$ajax;
 
         // Should be simple wiki only MDL-21695.
-        $data->text =  format_text(get_string($identifier.'_help', $component), FORMAT_MARKDOWN, $options);
+        $data->text = format_text(get_string($identifier.'_help', $component, $a), FORMAT_MARKDOWN, $options);
 
         $helplink = $identifier . '_link';
         if ($sm->string_exists($helplink, $component)) {  // Link to further info in Moodle docs.