first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
@@ -0,0 +1,82 @@
<?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 tool_monitor rule created event.
*
* @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\event;
defined('MOODLE_INTERNAL') || die();
/**
* The tool_monitor rule created event class.
*
* @package tool_monitor
* @since Moodle 2.8
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class rule_created extends \core\event\base {
/**
* Init method.
*
* @return void
*/
protected function init() {
$this->data['objecttable'] = 'tool_monitor_rules';
$this->data['crud'] = 'c';
$this->data['edulevel'] = self::LEVEL_OTHER;
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventrulecreated', 'tool_monitor');
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' created the event monitor rule with id '$this->objectid'.";
}
/**
* Get URL related to the action
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/admin/tool/monitor/edit.php', array('ruleid' => $this->objectid,
'courseid' => $this->courseid));
}
public static function get_objectid_mapping() {
// No mapping required for this event because event monitor rules are not backed up.
return array('db' => 'tool_monitor_rules', 'restore' => \core\event\base::NOT_MAPPED);
}
}
@@ -0,0 +1,81 @@
<?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 tool_monitor rule deleted event.
*
* @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\event;
defined('MOODLE_INTERNAL') || die();
/**
* The tool_monitor rule deleted event class.
*
* @package tool_monitor
* @since Moodle 2.8
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class rule_deleted extends \core\event\base {
/**
* Init method.
*
* @return void
*/
protected function init() {
$this->data['objecttable'] = 'tool_monitor_rules';
$this->data['crud'] = 'd';
$this->data['edulevel'] = self::LEVEL_OTHER;
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventruledeleted', 'tool_monitor');
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' deleted the event monitor rule with id '$this->objectid'.";
}
/**
* Get URL related to the action
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/admin/tool/monitor/managerules.php', array('courseid' => $this->courseid));
}
public static function get_objectid_mapping() {
// No mapping required for this event because event monitor rules are not backed up.
return array('db' => 'tool_monitor_rules', 'restore' => \core\event\base::NOT_MAPPED);
}
}
@@ -0,0 +1,82 @@
<?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 tool_monitor rule updated event.
*
* @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\event;
defined('MOODLE_INTERNAL') || die();
/**
* The tool_monitor rule updated event class.
*
* @package tool_monitor
* @since Moodle 2.8
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class rule_updated extends \core\event\base {
/**
* Init method.
*
* @return void
*/
protected function init() {
$this->data['objecttable'] = 'tool_monitor_rules';
$this->data['crud'] = 'u';
$this->data['edulevel'] = self::LEVEL_OTHER;
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventruleupdated', 'tool_monitor');
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' updated the event monitor rule with id '$this->objectid'.";
}
/**
* Get URL related to the action
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/admin/tool/monitor/edit.php', array('ruleid' => $this->objectid,
'courseid' => $this->courseid));
}
public static function get_objectid_mapping() {
// No mapping required for this event because event monitor rules are not backed up.
return array('db' => 'tool_monitor_rules', 'restore' => \core\event\base::NOT_MAPPED);
}
}
@@ -0,0 +1,72 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* The tool_monitor subscription created event.
*
* @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\event;
defined('MOODLE_INTERNAL') || die();
/**
* The tool_monitor subscription created event class.
*
* @package tool_monitor
* @since Moodle 2.8
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class subscription_created extends \core\event\base {
/**
* Init method.
*
* @return void
*/
protected function init() {
$this->data['objecttable'] = 'tool_monitor_subscriptions';
$this->data['crud'] = 'c';
$this->data['edulevel'] = self::LEVEL_TEACHING;
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventsubcreated', 'tool_monitor');
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' created the event monitor subscription with id '$this->objectid'.";
}
public static function get_objectid_mapping() {
// No mapping required for this event because event monitor subscriptions are not backed up.
return array('db' => 'tool_monitor_subscriptions', 'restore' => \core\event\base::NOT_MAPPED);
}
}
@@ -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/>.
/**
* The tool_monitor subscription criteria met event.
*
* @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\event;
defined('MOODLE_INTERNAL') || die();
/**
* The tool_monitor subscription criteria met event class.
*
* @property-read array $other {
* Extra information about event.
*
* - string subscriptionid: id of the subscription.
* }
*
* @package tool_monitor
* @since Moodle 2.8
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class subscription_criteria_met extends \core\event\base {
/**
* Init method.
*
* @return void
*/
protected function init() {
$this->data['crud'] = 'c';
$this->data['edulevel'] = self::LEVEL_TEACHING;
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventsubcriteriamet', 'tool_monitor');
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The criteria for the subscription with id '{$this->other['subscriptionid']}' was met.";
}
/**
* Custom validation.
*
* @throws \coding_exception
* @return void
*/
protected function validate_data() {
parent::validate_data();
if (!isset($this->other['subscriptionid'])) {
throw new \coding_exception('The \'subscriptionid\' value must be set in other.');
}
}
public static function get_other_mapping() {
// No mapping required for this event because event monitor subscriptions are not backed up.
return false;
}
}
@@ -0,0 +1,72 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* The tool_monitor subscription deleted event.
*
* @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\event;
defined('MOODLE_INTERNAL') || die();
/**
* The tool_monitor subscription deleted event class.
*
* @package tool_monitor
* @since Moodle 2.8
* @copyright 2014 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class subscription_deleted extends \core\event\base {
/**
* Init method.
*
* @return void
*/
protected function init() {
$this->data['objecttable'] = 'tool_monitor_subscriptions';
$this->data['crud'] = 'd';
$this->data['edulevel'] = self::LEVEL_TEACHING;
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventsubdeleted', 'tool_monitor');
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' deleted the event monitor subscription with id '$this->objectid'.";
}
public static function get_objectid_mapping() {
// No mapping required for this event because event monitor subscriptions are not backed up.
return array('db' => 'tool_monitor_subscriptions', 'restore' => \core\event\base::NOT_MAPPED);
}
}
+155
View File
@@ -0,0 +1,155 @@
<?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/>.
namespace tool_monitor;
use core_collator;
use core_component;
use core_plugin_manager;
use ReflectionClass;
/**
* 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 {
/**
* 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) {
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;
// List of exceptional events that will cause problems if displayed.
$eventsignore = [
\core\event\unknown_logged::class,
\logstore_legacy\event\legacy_logged::class,
];
$return = [];
$events = core_component::get_component_classes_in_namespace(null, 'event');
foreach (array_keys($events) as $event) {
// We need to filter all classes that extend event base, or the base class itself.
if (is_a($event, \core\event\base::class, true) && !in_array($event, $eventsignore)) {
$reflectionclass = new ReflectionClass($event);
if ($reflectionclass->isAbstract()) {
continue;
}
// We can't choose this component's own events.
[$component] = explode('\\', $event);
if ($component === 'tool_monitor') {
continue;
}
if ($withoutcomponent) {
$return["\\{$event}"] = $event::get_name_with_info();
} else {
$return[$component]["\\{$event}"] = $event::get_name_with_info();
}
}
}
// Now enable developer debugging as event information has been retrieved.
$CFG->debug = $debuglevel;
$CFG->debugdisplay = $debugdisplay;
$CFG->debugdeveloper = $debugdeveloper;
if ($withoutcomponent) {
array_multisort($return, SORT_NATURAL);
}
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, grouped by their type
*/
public static function get_plugin_list($eventlist = array()) {
$pluginmanager = core_plugin_manager::instance();
if (empty($eventlist)) {
$eventlist = self::get_all_eventlist();
}
$plugins = array_keys($eventlist);
$return = array();
foreach ($plugins as $plugin) {
// Core sub-systems are grouped together and are denoted by a distinct lang string.
if (strpos($plugin, 'core') === 0) {
$plugintype = get_string('core', 'tool_monitor');
$pluginname = get_string('coresubsystem', 'tool_monitor', $plugin);
} else {
[$type] = core_component::normalize_component($plugin);
$plugintype = $pluginmanager->plugintype_name_plural($type);
$pluginname = $pluginmanager->plugin_name($plugin);
}
$return[$plugintype][$plugin] = $pluginname;
}
// Sort returned components according to their type, followed by name.
core_collator::ksort($return);
array_walk($return, function(array &$componenttype) {
core_collator::asort($componenttype);
});
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;
}
}
@@ -0,0 +1,260 @@
<?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) {
// Delete rules defined inside this course and associated subscriptions.
$rules = rule_manager::get_rules_by_courseid($event->courseid, 0, 0, false);
foreach ($rules as $rule) {
rule_manager::delete_rule($rule->id, $event->get_context());
}
// Delete remaining subscriptions inside this course (from site-wide rules).
subscription_manager::remove_all_subscriptions_in_course($event->get_context());
}
/**
* 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 (!get_config('tool_monitor', 'enablemonitor')) {
return; // The tool is disabled. Nothing to do.
}
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) {
// If there are no subscriptions for this event do not buffer it.
if (!\tool_monitor\subscription_manager::event_has_subscriptions($event->eventname, $event->courseid)) {
return;
}
$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();
$allsubids = 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) {
// Only proceed to fire events and notifications if the subscription is active.
if (!subscription_manager::subscription_is_active($subscription)) {
continue;
}
$starttime = $now - $subscription->timewindow;
$starttime = ($starttime > $subscription->lastnotificationsent) ? $starttime : $subscription->lastnotificationsent;
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;
// Trigger a subscription_criteria_met event.
// It's possible that the course has been deleted since the criteria was met, so in that case use
// the system context. Set it here and change later if needed.
$context = \context_system::instance();
// We can't perform if (!empty($subscription->courseid)) below as it uses the magic method
// __get to return the variable, which will always result in being empty.
$courseid = $subscription->courseid;
if (!empty($courseid)) {
if ($coursecontext = \context_course::instance($courseid, IGNORE_MISSING)) {
$context = $coursecontext;
}
}
$params = array(
'userid' => $subscription->userid,
'courseid' => $subscription->courseid,
'context' => $context,
'other' => array(
'subscriptionid' => $subscription->id
)
);
$event = \tool_monitor\event\subscription_criteria_met::create($params);
$event->trigger();
}
}
if (!empty($idstosend)) {
$messagestosend[] = array('subscriptionids' => $idstosend, 'event' => $eventobj);
$allsubids = array_merge($allsubids, $idstosend);
}
}
if (!empty($allsubids)) {
// Update the last trigger flag.
list($sql, $params) = $DB->get_in_or_equal($allsubids, SQL_PARAMS_NAMED);
$params['now'] = $now;
$sql = "UPDATE {tool_monitor_subscriptions} SET lastnotificationsent = :now WHERE id $sql";
$DB->execute($sql, $params);
}
// 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);
}
}
@@ -0,0 +1,142 @@
<?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)) {
mtrace("Sending message to the user with id " . $message->userto->id . " for the subscription with id $id...");
message_send($message);
mtrace("Sent.");
}
}
}
}
/**
* 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);
if (empty($user)) {
// User doesn't exist. Should never happen, nothing to do return.
return false;
}
$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);
$htmlmessage = format_text($template, $subscription->templateformat, array('context' => $context));
$msgdata = new \core\message\message();
$msgdata->courseid = empty($subscription->courseid) ? SITEID : $subscription->courseid;
$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 = html_to_text($htmlmessage);
$msgdata->fullmessageformat = FORMAT_PLAIN;
$msgdata->fullmessagehtml = $htmlmessage;
$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) {
$replacements = [
'{link}' => $eventobj->link,
'{rulename}' => $subscription->get_name($context),
'{description}' => $subscription->get_description($context),
'{eventname}' => $subscription->get_event_name(),
];
if ($eventobj->contextlevel >= CONTEXT_COURSE && !empty($eventobj->courseid)) {
$iscoursetemplate = str_contains($template, '{course');
$ismodtemplate = str_contains($template, '{module');
if ($iscoursetemplate || $ismodtemplate) {
$modinfo = get_fast_modinfo($eventobj->courseid);
$course = $modinfo->get_course();
$replacements['{coursefullname}'] = $course->fullname;
$replacements['{courseshortname}'] = $course->shortname;
if ($eventobj->contextlevel == CONTEXT_MODULE && !empty($eventobj->contextinstanceid) && $ismodtemplate) {
$cm = $modinfo->get_cm($eventobj->contextinstanceid);
$replacements['{modulelink}'] = $cm->url;
$replacements['{modulename}'] = $cm->get_name();
}
}
}
return str_replace(
search: array_keys($replacements),
replace: array_values($replacements),
subject: $template,
);
}
}
@@ -0,0 +1,214 @@
<?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('id', 'toolmonitorrules_table');
$this->set_attribute('class', 'toolmonitor managerules generaltable generalbox');
$this->define_columns(array('name', 'description', 'course', 'plugin', 'eventname', 'filters', 'manage'));
$this->define_headers(array(
get_string('rulename', 'tool_monitor'),
get_string('description'),
get_string('course'),
get_string('area', 'tool_monitor'),
get_string('event', 'tool_monitor'),
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 course column.
*
* @param \tool_monitor\rule $rule rule object
* @return string html used to display the context column field.
*/
public function col_course(\tool_monitor\rule $rule) {
$coursename = $rule->get_course_name($this->context);
$courseid = $rule->courseid;
if (empty($courseid)) {
return $coursename;
} else {
return \html_writer::link(new \moodle_url('/course/view.php', array('id' => $courseid)), $coursename);
}
}
/**
* 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 = '';
// Do not allow the user to edit the rule unless they have the system capability, or we are viewing the rules
// for a course, and not the site. Note - we don't need to check for the capability at a course level since
// the user is never shown this page otherwise.
if ($this->hassystemcap || ($rule->courseid != 0)) {
$editurl = new \moodle_url($CFG->wwwroot. '/admin/tool/monitor/edit.php', array('ruleid' => $rule->id,
'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'));
}
// The user should always be able to copy the rule if they are able to view the page.
$copyurl = new \moodle_url($CFG->wwwroot. '/admin/tool/monitor/managerules.php',
array('ruleid' => $rule->id, 'action' => 'copy', 'courseid' => $this->courseid, 'sesskey' => sesskey()));
$icon = $OUTPUT->render(new \pix_icon('t/copy', get_string('duplicaterule', 'tool_monitor')));
$manage .= \html_writer::link($copyurl, $icon, array('class' => 'action-icon'));
if ($this->hassystemcap || ($rule->courseid != 0)) {
$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/delete', get_string('deleterule', 'tool_monitor')));
$manage .= \html_writer::link($deleteurl, $icon, array('class' => 'action-icon'));
}
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);
}
}
}
@@ -0,0 +1,99 @@
<?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 rule.
*
* @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'), ['class' => 'btn btn-primary']);
$addurl = new \moodle_url($CFG->wwwroot. '/admin/tool/monitor/edit.php', array('courseid' => $courseid));
return \html_writer::link($addurl, $button);
}
/**
* Html to add a link to go to the subscription page.
*
* @param moodle_url $manageurl The url of the subscription page.
*
* @return string html for the link to the subscription page.
*/
public function render_subscriptions_link($manageurl) {
echo \html_writer::start_div();
$a = \html_writer::link($manageurl, get_string('managesubscriptions', 'tool_monitor'));
$link = \html_writer::tag('span', get_string('managesubscriptionslink', 'tool_monitor', $a));
echo $link;
echo \html_writer::end_div();
}
}
@@ -0,0 +1,110 @@
<?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 = '';
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) {
if ($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;
}
/**
* Html to add a link to go to the rule manager page.
*
* @param moodle_url $ruleurl The url of the rule manager page.
*
* @return string html for the link to the rule manager page.
*/
public function render_rules_link($ruleurl) {
echo \html_writer::start_div();
$a = \html_writer::link($ruleurl, get_string('managerules', 'tool_monitor'));
$link = \html_writer::tag('span', get_string('manageruleslink', 'tool_monitor', $a));
echo $link;
echo \html_writer::end_div();
}
}
@@ -0,0 +1,227 @@
<?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;
/**
* 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 subscriberules generaltable generalbox');
$this->define_columns(array('name', 'description', 'course', 'plugin', 'eventname', 'filters', 'select'));
$this->define_headers(array(
get_string('rulename', 'tool_monitor'),
get_string('description'),
get_string('course'),
get_string('area', 'tool_monitor'),
get_string('event', 'tool_monitor'),
get_string('frequency', '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);
$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 rule name.
*/
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 description.
*/
public function col_description(\tool_monitor\rule $rule) {
return $rule->get_description($this->context);
}
/**
* Generate content for course column.
*
* @param \tool_monitor\rule $rule rule object
* @return string html used to display the course name.
*/
public function col_course(\tool_monitor\rule $rule) {
$coursename = $rule->get_course_name($this->context);
$courseid = $rule->courseid;
if (empty($courseid)) {
return $coursename;
} else {
return \html_writer::link(new \moodle_url('/course/view.php', array('id' => $this->courseid)), $coursename);
}
}
/**
* Generate content for plugin column.
*
* @param \tool_monitor\rule $rule rule object
* @return string html used to display the plugin name.
*/
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 event name.
*/
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.
*/
public function col_filters(\tool_monitor\rule $rule) {
return $rule->get_filters_description();
}
/**
* Generate content for select column.
*
* @param \tool_monitor\rule $rule rule object
* @return string html used to display the select field.
*/
public function col_select(\tool_monitor\rule $rule) {
global $OUTPUT;
$options = $rule->get_subscribe_options($this->courseid);
$text = get_string('subscribeto', 'tool_monitor', $rule->get_name($this->context));
if ($options instanceof \single_select) {
$options->set_label($text, array('class' => 'accesshide'));
return $OUTPUT->render($options);
} else if ($options instanceof \moodle_url) {
// A \moodle_url to subscribe.
$icon = $OUTPUT->pix_icon('t/add', $text);
$link = new \action_link($options, $icon);
return $OUTPUT->render($link);
} else {
return $options;
}
}
/**
* 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.
*
* @param bool $choose A flag for whether to show the 'choose...' option in the select box.
* @return \single_select|bool returns the list of courses, or false if the select box
* should not be displayed.
*/
public function get_user_courses_select($choose = false) {
$options = tool_monitor_get_user_courses();
// If we have no options then don't create a select element.
if (!$options) {
return false;
}
$selected = $this->courseid;
$nothing = array();
if ($choose) {
$selected = null;
$nothing = array('choosedots');
}
$url = new \moodle_url('/admin/tool/monitor/index.php');
$select = new \single_select($url, 'courseid', $options, $selected, $nothing);
$select->set_label(get_string('selectacourse', 'tool_monitor'));
return $select;
}
}
@@ -0,0 +1,197 @@
<?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;
/**
* 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 subscriptions generaltable generalbox');
$this->define_columns(array('name', 'description', 'course', 'plugin', 'instance', 'eventname',
'filters', 'unsubscribe'));
$this->define_headers(array(
get_string('rulename', 'tool_monitor'),
get_string('description'),
get_string('course'),
get_string('area', 'tool_monitor'),
get_string('moduleinstance', 'tool_monitor'),
get_string('event', 'tool_monitor'),
get_string('frequency', 'tool_monitor'),
get_string('unsubscribe', '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);
}
/**
* Generate content for name column.
*
* @param \tool_monitor\subscription $sub subscription object
* @return string html used to display the rule name.
*/
public function col_name(\tool_monitor\subscription $sub) {
return $sub->get_name($this->context);
}
/**
* Generate content for description column.
*
* @param \tool_monitor\subscription $sub subscription object
* @return string html used to display the description.
*/
public function col_description(\tool_monitor\subscription $sub) {
return $sub->get_description($this->context);
}
/**
* Generate content for course column.
*
* @param \tool_monitor\subscription $sub subscription object
* @return string html used to display the course name.
*/
public function col_course(\tool_monitor\subscription $sub) {
$coursename = $sub->get_course_name($this->context);
$courseid = $sub->courseid;
if (empty($courseid)) {
return $coursename;
} else {
return \html_writer::link(new \moodle_url('/course/view.php', array('id' => $courseid)), $coursename);
}
}
/**
* Generate content for plugin column.
*
* @param \tool_monitor\subscription $sub subscription object
* @return string html used to display the plugin name.
*/
public function col_plugin(\tool_monitor\subscription $sub) {
return $sub->get_plugin_name();
}
/**
* Generate content for instance column.
*
* @param \tool_monitor\subscription $sub subscription object
* @return string html used to display the instance name.
*/
public function col_instance(\tool_monitor\subscription $sub) {
return $sub->get_instance_name();
}
/**
* Generate content for eventname column.
*
* @param \tool_monitor\subscription $sub subscription object
* @return string html used to display the event name.
*/
public function col_eventname(\tool_monitor\subscription $sub) {
return $sub->get_event_name();
}
/**
* Generate content for filters column.
*
* @param \tool_monitor\subscription $sub subscription object
* @return string html used to display the filters.
*/
public function col_filters(\tool_monitor\subscription $sub) {
return $sub->get_filters_description();
}
/**
* Generate content for unsubscribe column.
*
* @param \tool_monitor\subscription $sub subscription object
* @return string html used to display the unsubscribe field.
*/
public function col_unsubscribe(\tool_monitor\subscription $sub) {
global $OUTPUT, $CFG;
$deleteurl = new \moodle_url($CFG->wwwroot. '/admin/tool/monitor/index.php', array('subscriptionid' => $sub->id,
'action' => 'unsubscribe', 'courseid' => $this->courseid, 'sesskey' => sesskey()));
$icon = $OUTPUT->render(new \pix_icon('t/delete', get_string('deletesubscription', 'tool_monitor')));
return \html_writer::link($deleteurl, $icon, array('class' => 'action-icon'));
}
/**
* 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);
}
}
}
@@ -0,0 +1,255 @@
<?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/>.
/**
* Privacy class for requesting user data.
*
* @package tool_monitor
* @copyright 2018 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_monitor\privacy;
defined('MOODLE_INTERNAL') || die();
use \core_privacy\local\metadata\collection;
use \core_privacy\local\request\contextlist;
use \core_privacy\local\request\approved_contextlist;
use \core_privacy\local\request\approved_userlist;
use \core_privacy\local\request\transform;
use \core_privacy\local\request\userlist;
use \core_privacy\local\request\writer;
use \tool_monitor\subscription_manager;
use \tool_monitor\rule_manager;
/**
* Privacy provider for tool_monitor
*
* @package tool_monitor
* @copyright 2018 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements
\core_privacy\local\metadata\provider,
\core_privacy\local\request\core_userlist_provider,
\core_privacy\local\request\plugin\provider {
/**
* Get information about the user data stored by this plugin.
*
* @param collection $collection An object for storing metadata.
* @return collection The metadata.
*/
public static function get_metadata(collection $collection): collection {
$toolmonitorrules = [
'description' => 'privacy:metadata:description',
'name' => 'privacy:metadata:name',
'userid' => 'privacy:metadata:userid',
'plugin' => 'privacy:metadata:plugin',
'eventname' => 'privacy:metadata:eventname',
'template' => 'privacy:metadata:template',
'frequency' => 'privacy:metadata:frequency',
'timewindow' => 'privacy:metadata:timewindow',
'timemodified' => 'privacy:metadata:timemodifiedrule',
'timecreated' => 'privacy:metadata:timecreatedrule'
];
$toolmonitorsubscriptions = [
'userid' => 'privacy:metadata:useridsub',
'timecreated' => 'privacy:metadata:timecreatedsub',
'lastnotificationsent' => 'privacy:metadata:lastnotificationsent',
'inactivedate' => 'privacy:metadata:inactivedate'
];
// Tool monitor history doesn't look like it is used at all.
$toolmonitorhistory = [
'userid' => 'privacy:metadata:useridhistory',
'timesent' => 'privacy:metadata:timesent'
];
$collection->add_database_table('tool_monitor_rules', $toolmonitorrules, 'privacy:metadata:rulessummary');
$collection->add_database_table('tool_monitor_subscriptions', $toolmonitorsubscriptions,
'privacy:metadata:subscriptionssummary');
$collection->add_database_table('tool_monitor_history', $toolmonitorhistory, 'privacy:metadata:historysummary');
$collection->link_subsystem('core_message', 'privacy:metadata:messagesummary');
return $collection;
}
/**
* Return all contexts for this userid. In this situation the user context.
*
* @param int $userid The user ID.
* @return contextlist The list of context IDs.
*/
public static function get_contexts_for_userid(int $userid): contextlist {
$params = ['useridrules' => $userid, 'useridsubscriptions' => $userid, 'contextuserrule' => CONTEXT_USER,
'contextusersub' => CONTEXT_USER];
$sql = "SELECT DISTINCT ctx.id
FROM {context} ctx
LEFT JOIN {tool_monitor_rules} mr ON ctx.instanceid = mr.userid AND ctx.contextlevel = :contextuserrule
AND mr.userid = :useridsubscriptions
LEFT JOIN {tool_monitor_subscriptions} ms ON ctx.instanceid = ms.userid AND ctx.contextlevel = :contextusersub
AND ms.userid = :useridrules
WHERE ms.id IS NOT NULL OR mr.id IS NOT NULL";
$contextlist = new contextlist();
$contextlist->add_from_sql($sql, $params);
return $contextlist;
}
/**
* Get the list of users who have data within a context.
*
* @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination.
*/
public static function get_users_in_context(userlist $userlist) {
$context = $userlist->get_context();
if (!$context instanceof \context_user) {
return;
}
$params = ['userid' => $context->instanceid];
$sql = "SELECT userid FROM {tool_monitor_rules} WHERE userid = :userid";
$userlist->add_from_sql('userid', $sql, $params);
$sql = "SELECT userid FROM {tool_monitor_subscriptions} WHERE userid = :userid";
$userlist->add_from_sql('userid', $sql, $params);
}
/**
* Export all event monitor information for the list of contexts and this user.
*
* @param approved_contextlist $contextlist The list of approved contexts for a user.
*/
public static function export_user_data(approved_contextlist $contextlist) {
global $DB;
// Export rules.
$context = \context_user::instance($contextlist->get_user()->id);
$rules = $DB->get_records('tool_monitor_rules', ['userid' => $contextlist->get_user()->id]);
if ($rules) {
static::export_monitor_rules($rules, $context);
}
// Export subscriptions.
$subscriptions = subscription_manager::get_user_subscriptions(0, 0, $contextlist->get_user()->id);
if ($subscriptions) {
static::export_monitor_subscriptions($subscriptions, $context);
}
}
/**
* Delete all user data for this context.
*
* @param \context $context The context to delete data for.
*/
public static function delete_data_for_all_users_in_context(\context $context) {
// Only delete data for user contexts.
if ($context->contextlevel == CONTEXT_USER) {
static::delete_user_data($context->instanceid);
}
}
/**
* Delete all user data for this user only.
*
* @param approved_contextlist $contextlist The list of approved contexts for a user.
*/
public static function delete_data_for_user(approved_contextlist $contextlist) {
static::delete_user_data($contextlist->get_user()->id);
}
/**
* Delete multiple users within a single context.
*
* @param approved_userlist $userlist The approved context and user information to delete information for.
*/
public static function delete_data_for_users(approved_userlist $userlist) {
$context = $userlist->get_context();
$userids = $userlist->get_userids();
$userid = reset($userids);
// Only delete data for user context, which should be a single user.
if ($context->contextlevel == CONTEXT_USER && count($userids) == 1 && $userid == $context->instanceid) {
static::delete_user_data($userid);
}
}
/**
* This does the deletion of user data for the event monitor.
*
* @param int $userid The user ID
*/
protected static function delete_user_data(int $userid) {
global $DB;
// Delete this user's subscriptions first.
subscription_manager::delete_user_subscriptions($userid);
// Because we only use user contexts the instance ID is the user ID.
// Get the rules and check if this user has the capability to delete them.
$rules = $DB->get_records('tool_monitor_rules', ['userid' => $userid]);
foreach ($rules as $ruledata) {
$rule = rule_manager::get_rule($ruledata);
// If no-one is suscribed to the rule then it is safe to delete.
if ($rule->can_manage_rule($userid) && subscription_manager::count_rule_subscriptions($rule->id) == 0) {
$rule->delete_rule();
}
}
}
/**
* This formats and then exports the monitor rules.
*
* @param array $rules The monitor rules.
* @param context_user $context The user context
*/
protected static function export_monitor_rules(array $rules, \context_user $context) {
foreach ($rules as $rule) {
$rule = rule_manager::get_rule($rule);
$ruledata = new \stdClass();
$ruledata->name = $rule->name;
$ruledata->eventname = $rule->get_event_name();
$ruledata->description = $rule->get_description($context);
$ruledata->plugin = $rule->get_plugin_name();
$ruledata->template = $rule->template;
$ruledata->frequency = $rule->get_filters_description();
$ruledata->course = $rule->get_course_name($context);
$ruledata->timecreated = transform::datetime($rule->timecreated);
$ruledata->timemodified = transform::datetime($rule->timemodified);
writer::with_context($context)->export_data([get_string('privacy:createdrules', 'tool_monitor'),
$rule->name . '_' . $rule->id], $ruledata);
}
}
/**
* This formats and then exports the event monitor subscriptions.
*
* @param array $subscriptions Subscriptions
* @param \context_user $context The user context
*/
protected static function export_monitor_subscriptions(array $subscriptions, \context_user $context) {
foreach ($subscriptions as $subscription) {
$subscriptiondata = new \stdClass();
$subscriptiondata->instancename = $subscription->get_instance_name();
$subscriptiondata->eventname = $subscription->get_event_name();
$subscriptiondata->frequency = $subscription->get_filters_description();
$subscriptiondata->name = $subscription->get_name($context);
$subscriptiondata->description = $subscription->get_description($context);
$subscriptiondata->pluginname = $subscription->get_plugin_name();
$subscriptiondata->course = $subscription->get_course_name($context);
$subscriptiondata->timecreated = transform::datetime($subscription->timecreated);
$subscriptiondata->lastnotificationsent = transform::datetime($subscription->lastnotificationsent);
writer::with_context($context)->export_data([get_string('privacy:subscriptions', 'tool_monitor'),
$subscriptiondata->name . '_' . $subscription->id, $subscriptiondata->course, $subscriptiondata->instancename],
$subscriptiondata);
}
}
}
+275
View File
@@ -0,0 +1,275 @@
<?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 user manage this rule? Defaults to $USER.
*
* @param int $userid Check against this userid.
* @return bool true if the current user can manage this rule, else false.
*/
public function can_manage_rule($userid = null) {
$courseid = $this->courseid;
$context = empty($courseid) ? \context_system::instance() : \context_course::instance($this->courseid);
return has_capability('tool/monitor:managerules', $context, $userid);
}
/**
* 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);
}
/**
* Gets the rule subscribe options for a given course and rule.
*
* Could be a select drop down with a list of possible module
* instances or a single link to subscribe if the rule plugin
* is not a module.
*
* @param int $courseid course id
*
* @return \single_select|\moodle_url|string
* @throws \coding_exception
*/
public function get_subscribe_options($courseid) {
global $CFG;
$url = new \moodle_url($CFG->wwwroot. '/admin/tool/monitor/index.php', array(
'courseid' => $courseid,
'ruleid' => $this->id,
'action' => 'subscribe',
'sesskey' => sesskey()
));
if (strpos($this->plugin, 'mod_') !== 0) {
return $url;
} else {
// Single select when the plugin is an activity.
$options = array();
$options[0] = get_string('allmodules', 'tool_monitor');
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();
}
}
return new \single_select($url, 'cmid', $options);
}
}
/**
* 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_with_info();
}
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 course associated.
*
* @param \context $context context where this name would be displayed.
* @return string The course fullname.
*/
public function get_course_name($context) {
$courseid = $this->courseid;
if (empty($courseid)) {
return get_string('site');
} else {
$course = get_course($courseid);
return format_string($course->fullname, true, array('context' => $context));
}
}
/**
* 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;
}
}
+162
View File
@@ -0,0 +1,162 @@
<?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() {
global $PAGE;
$mform = $this->_form;
$eventlist = $this->_customdata['eventlist'];
$pluginlist = $this->_customdata['pluginlist'];
$rule = $this->_customdata['rule'];
$courseid = $this->_customdata['courseid'];
$subscriptioncount = $this->_customdata['subscriptioncount'];
// 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('rulename', 'tool_monitor'), 'size="50"');
$mform->addRule('name', get_string('required'), 'required');
$mform->setType('name', PARAM_TEXT);
// Plugin field.
$mform->addElement('selectgroups', 'plugin', get_string('areatomonitor', 'tool_monitor'), $pluginlist, [
'data-field' => 'component',
]);
$mform->addRule('plugin', get_string('required'), 'required');
// Event field.
$mform->addElement('select', 'eventname', get_string('event', 'tool_monitor'), $eventlist, [
'data-field' => 'eventname',
'data-eventlist' => json_encode($eventlist),
]);
$mform->addRule('eventname', get_string('required'), 'required');
// Set up the client-side dropdown handler.
$PAGE->requires->js_call_amd('tool_monitor/dropdown', 'init');
// Freeze plugin and event fields for editing if there's a subscription for this rule.
if ($subscriptioncount > 0) {
$mform->freeze('plugin');
$mform->setConstant('plugin', $rule->plugin);
$mform->freeze('eventname');
$mform->setConstant('eventname', $rule->eventname);
}
// Description field.
$mform->addElement('editor', 'description', get_string('description'), $editoroptions);
// Filters.
$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('frequency', 'tool_monitor'), $freq);
$mform->addRule('frequency', get_string('required'), 'required');
$mform->addHelpButton('frequency', 'frequency', '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('inminutes', 'tool_monitor'), $mins);
$mform->addRule('minutes', get_string('required'), 'required');
// Message template.
$mform->addElement('editor', 'template', get_string('messagetemplate', 'tool_monitor'), $editoroptions);
$mform->setDefault('template', array('text' => get_string('defaultmessagetemplate', 'tool_monitor'),
'format' => FORMAT_HTML));
$mform->addRule('template', get_string('required'), 'required');
$mform->addHelpButton('template', 'messagetemplate', 'tool_monitor');
// Action buttons.
$this->add_action_buttons(true, 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;
}
}
+290
View File
@@ -0,0 +1,290 @@
<?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);
// Trigger a rule created event.
if ($ruledata->id) {
if (!empty($ruledata->courseid)) {
$courseid = $ruledata->courseid;
$context = \context_course::instance($ruledata->courseid);
} else {
$courseid = 0;
$context = \context_system::instance();
}
$params = array(
'objectid' => $ruledata->id,
'courseid' => $courseid,
'context' => $context
);
$event = \tool_monitor\event\rule_created::create($params);
$event->trigger();
}
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.
* @param \context|null $coursecontext the context of the course - this is passed when we
* can not get the context via \context_course as the course has been deleted.
*
* @return bool
*/
public static function delete_rule($ruleid, $coursecontext = null) {
global $DB;
subscription_manager::remove_all_subscriptions_for_rule($ruleid, $coursecontext);
// Retrieve the rule from the DB before we delete it, so we have a record when we trigger a rule deleted event.
$rule = $DB->get_record('tool_monitor_rules', array('id' => $ruleid));
$success = $DB->delete_records('tool_monitor_rules', array('id' => $ruleid));
// If successful trigger a rule deleted event.
if ($success) {
// It is possible that we are deleting rules associated with a deleted course, so we should be
// passing the context as the second parameter.
if (!is_null($coursecontext)) {
$context = $coursecontext;
$courseid = $rule->courseid;
} else if (!empty($rule->courseid) && ($context = \context_course::instance($rule->courseid,
IGNORE_MISSING))) {
$courseid = $rule->courseid;
} else {
$courseid = 0;
$context = \context_system::instance();
}
$params = array(
'objectid' => $rule->id,
'courseid' => $courseid,
'context' => $context
);
$event = \tool_monitor\event\rule_deleted::create($params);
$event->add_record_snapshot('tool_monitor_rules', $rule);
$event->trigger();
}
return $success;
}
/**
* 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();
$success = $DB->update_record('tool_monitor_rules', $ruledata);
// If successful trigger a rule updated event.
if ($success) {
// If we do not have the course id we need to retrieve it.
if (!isset($ruledata->courseid)) {
$courseid = $DB->get_field('tool_monitor_rules', 'courseid', array('id' => $ruledata->id), MUST_EXIST);
} else {
$courseid = $ruledata->courseid;
}
if (!empty($courseid)) {
$context = \context_course::instance($courseid);
} else {
$context = \context_system::instance();
}
$params = array(
'objectid' => $ruledata->id,
'courseid' => $courseid,
'context' => $context
);
$event = \tool_monitor\event\rule_updated::create($params);
$event->trigger();
}
return $success;
}
/**
* 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.
* @param bool $includesite Determines whether we return site wide rules or not.
*
* @return array List of rules for the given course id, if specified will also include site rules.
*/
public static function get_rules_by_courseid($courseid, $limitfrom = 0, $limitto = 0, $includesite = true) {
global $DB;
$select = 'courseid = ?';
$params = array();
$params[] = $courseid;
if ($includesite) {
$select .= ' OR courseid = ?';
$params[] = 0;
}
$orderby = 'courseid DESC, name ASC';
return self::get_instances($DB->get_records_select('tool_monitor_rules', $select, $params, $orderby,
'*', $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;
}
}
+198
View File
@@ -0,0 +1,198 @@
<?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 (isset($this->subscription->$prop)) {
return $this->subscription->$prop;
}
throw new \coding_exception('Property "' . $prop . '" doesn\'t exist');
}
/**
* Magic isset method.
*
* @param string $prop the property to get.
* @return bool true if the property is set, false otherwise.
*/
public function __isset($prop) {
return property_exists($this->subscription, $prop);
}
/**
* 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_with_info();
}
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) {
$courseid = $this->courseid;
if (empty($courseid)) {
return get_string('site');
} else {
try {
$course = get_course($courseid);
return format_string($course->fullname, true, array('context' => $context));
} catch (\dml_exception $e) {
return '-';
}
}
}
/**
* 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);
}
}
@@ -0,0 +1,562 @@
<?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 {
/** @var Period of time, in days, after which an inactive subscription will be removed completely.*/
const INACTIVE_SUBSCRIPTION_LIFESPAN_IN_DAYS = 30;
/**
* 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();
$subscription->id = $DB->insert_record('tool_monitor_subscriptions', $subscription);
// Trigger a subscription created event.
if ($subscription->id) {
if (!empty($subscription->courseid)) {
$courseid = $subscription->courseid;
$context = \context_course::instance($subscription->courseid);
} else {
$courseid = 0;
$context = \context_system::instance();
}
$params = array(
'objectid' => $subscription->id,
'courseid' => $courseid,
'context' => $context
);
$event = \tool_monitor\event\subscription_created::create($params);
$event->trigger();
// Let's invalidate the cache.
$cache = \cache::make('tool_monitor', 'eventsubscriptions');
$cache->delete($courseid);
}
return $subscription->id;
}
/**
* 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');
}
// Store the subscription before we delete it.
$subscription = $DB->get_record('tool_monitor_subscriptions', array('id' => $subscription->id));
$success = $DB->delete_records('tool_monitor_subscriptions', array('id' => $subscription->id));
// If successful trigger a subscription_deleted event.
if ($success) {
if (!empty($subscription->courseid) &&
($coursecontext = \context_course::instance($subscription->courseid, IGNORE_MISSING))) {
$courseid = $subscription->courseid;
$context = $coursecontext;
} else {
$courseid = 0;
$context = \context_system::instance();
}
$params = array(
'objectid' => $subscription->id,
'courseid' => $courseid,
'context' => $context
);
$event = \tool_monitor\event\subscription_deleted::create($params);
$event->add_record_snapshot('tool_monitor_subscriptions', $subscription);
$event->trigger();
// Let's invalidate the cache.
$cache = \cache::make('tool_monitor', 'eventsubscriptions');
$cache->delete($courseid);
}
return $success;
}
/**
* 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.
* @param \context|null $coursecontext the context of the course - this is passed when we
* can not get the context via \context_course as the course has been deleted.
*
* @return bool
*/
public static function remove_all_subscriptions_for_rule($ruleid, $coursecontext = null) {
global $DB;
// Store all the subscriptions we have to delete.
$subscriptions = $DB->get_recordset('tool_monitor_subscriptions', array('ruleid' => $ruleid));
// Now delete them.
$success = $DB->delete_records('tool_monitor_subscriptions', array('ruleid' => $ruleid));
// If successful and there were subscriptions that were deleted trigger a subscription deleted event.
if ($success && $subscriptions) {
foreach ($subscriptions as $subscription) {
// It is possible that we are deleting rules associated with a deleted course, so we should be
// passing the context as the second parameter.
if (!is_null($coursecontext)) {
$context = $coursecontext;
$courseid = $subscription->courseid;
} else if (!empty($subscription->courseid) && ($context =
\context_course::instance($subscription->courseid, IGNORE_MISSING))) {
$courseid = $subscription->courseid;
} else {
$courseid = 0;
$context = \context_system::instance();
}
$params = array(
'objectid' => $subscription->id,
'courseid' => $courseid,
'context' => $context
);
$event = \tool_monitor\event\subscription_deleted::create($params);
$event->add_record_snapshot('tool_monitor_subscriptions', $subscription);
$event->trigger();
// Let's invalidate the cache.
$cache = \cache::make('tool_monitor', 'eventsubscriptions');
$cache->delete($courseid);
}
}
$subscriptions->close();
return $success;
}
/**
* Delete all subscriptions in a course.
*
* This is called after a course was deleted, context no longer exists but we kept the object
*
* @param \context_course $coursecontext the context of the course
*/
public static function remove_all_subscriptions_in_course($coursecontext) {
global $DB;
// Store all the subscriptions we have to delete.
if ($subscriptions = $DB->get_records('tool_monitor_subscriptions', ['courseid' => $coursecontext->instanceid])) {
// Delete subscriptions in bulk.
$DB->delete_records('tool_monitor_subscriptions', ['courseid' => $coursecontext->instanceid]);
// Trigger events one by one.
foreach ($subscriptions as $subscription) {
$params = ['objectid' => $subscription->id, 'context' => $coursecontext];
$event = \tool_monitor\event\subscription_deleted::create($params);
$event->add_record_snapshot('tool_monitor_subscriptions', $subscription);
$event->trigger();
}
}
}
/**
* 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.courseid ASC, r.name' ) {
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;
}
/**
* Get count of subscriptions for a given rule.
*
* @param int $ruleid rule id of the subscription.
*
* @return int number of subscriptions
*/
public static function count_rule_subscriptions($ruleid) {
global $DB;
$sql = self::get_subscription_join_rule_sql(true);
$sql .= "WHERE s.ruleid = :ruleid";
return $DB->count_records_sql($sql, array('ruleid' => $ruleid));
}
/**
* Returns true if an event in a particular course has a subscription.
*
* @param string $eventname the name of the event
* @param int $courseid the course id
* @return bool returns true if the event has subscriptions in a given course, false otherwise.
*/
public static function event_has_subscriptions($eventname, $courseid) {
global $DB;
// Check if we can return these from cache.
$cache = \cache::make('tool_monitor', 'eventsubscriptions');
// The SQL we will be using to fill the cache if it is empty.
$sql = "SELECT DISTINCT(r.eventname)
FROM {tool_monitor_subscriptions} s
INNER JOIN {tool_monitor_rules} r
ON s.ruleid = r.id
WHERE s.courseid = :courseid";
$sitesubscriptions = $cache->get(0);
// If we do not have the site subscriptions in the cache then return them from the DB.
if ($sitesubscriptions === false) {
// Set the array for the cache.
$sitesubscriptions = array();
if ($subscriptions = $DB->get_records_sql($sql, array('courseid' => 0))) {
foreach ($subscriptions as $subscription) {
$sitesubscriptions[$subscription->eventname] = true;
}
}
$cache->set(0, $sitesubscriptions);
}
// Check if a subscription exists for this event site wide.
if (isset($sitesubscriptions[$eventname])) {
return true;
}
// If the course id is for the site, and we reached here then there is no site wide subscription for this event.
if (empty($courseid)) {
return false;
}
$coursesubscriptions = $cache->get($courseid);
// If we do not have the course subscriptions in the cache then return them from the DB.
if ($coursesubscriptions === false) {
// Set the array for the cache.
$coursesubscriptions = array();
if ($subscriptions = $DB->get_records_sql($sql, array('courseid' => $courseid))) {
foreach ($subscriptions as $subscription) {
$coursesubscriptions[$subscription->eventname] = true;
}
}
$cache->set($courseid, $coursesubscriptions);
}
// Check if a subscription exists for this event in this course.
if (isset($coursesubscriptions[$eventname])) {
return true;
}
return false;
}
/**
* Activates a group of subscriptions based on an input array of ids.
*
* @since 3.2.0
* @param array $ids of subscription ids.
* @return bool true if the operation was successful, false otherwise.
*/
public static function activate_subscriptions(array $ids) {
global $DB;
if (!empty($ids)) {
list($sql, $params) = $DB->get_in_or_equal($ids);
$success = $DB->set_field_select('tool_monitor_subscriptions', 'inactivedate', '0', 'id ' . $sql, $params);
return $success;
}
return false;
}
/**
* Deactivates a group of subscriptions based on an input array of ids.
*
* @since 3.2.0
* @param array $ids of subscription ids.
* @return bool true if the operation was successful, false otherwise.
*/
public static function deactivate_subscriptions(array $ids) {
global $DB;
if (!empty($ids)) {
$inactivedate = time();
list($sql, $params) = $DB->get_in_or_equal($ids);
$success = $DB->set_field_select('tool_monitor_subscriptions', 'inactivedate', $inactivedate, 'id ' . $sql,
$params);
return $success;
}
return false;
}
/**
* Deletes subscriptions which have been inactive for a period of time.
*
* @since 3.2.0
* @param int $userid if provided, only this user's stale subscriptions will be deleted.
* @return bool true if the operation was successful, false otherwise.
*/
public static function delete_stale_subscriptions($userid = 0) {
global $DB;
// Get the expiry duration, in days.
$cutofftime = strtotime("-" . self::INACTIVE_SUBSCRIPTION_LIFESPAN_IN_DAYS . " days", time());
if (!empty($userid)) {
// Remove any stale subscriptions for the desired user only.
$success = $DB->delete_records_select('tool_monitor_subscriptions',
'userid = ? AND inactivedate < ? AND inactivedate <> 0',
array($userid, $cutofftime));
} else {
// Remove all stale subscriptions.
$success = $DB->delete_records_select('tool_monitor_subscriptions',
'inactivedate < ? AND inactivedate <> 0',
array($cutofftime));
}
return $success;
}
/**
* Check whether a subscription is active.
*
* @since 3.2.0
* @param \tool_monitor\subscription $subscription instance.
* @return bool true if the subscription is active, false otherwise.
*/
public static function subscription_is_active(subscription $subscription) {
return empty($subscription->inactivedate);
}
}
@@ -0,0 +1,274 @@
<?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/>.
namespace tool_monitor\task;
use tool_monitor\subscription;
use tool_monitor\subscription_manager;
/**
* Simple task class responsible for activating, deactivating and removing subscriptions.
*
* Activation/deactivation is managed by looking at the same access rules used to determine whether a user can
* subscribe to the rule in the first place.
*
* Removal occurs when a subscription has been inactive for a period of time exceeding the lifespan, as set by
* subscription_manager::get_inactive_subscription_lifespan().
*
* I.e.
* - Activation: If a user can subscribe currently, then an existing subscription should be made active.
* - Deactivation: If a user cannot subscribe currently, then an existing subscription should be made inactive.
* - Removal: If a user has a subscription that has been inactive for longer than the prescribed period, then
* delete the subscription entirely.
*
* @since 3.2.0
* @package tool_monitor
* @copyright 2016 Jake Dallimore <jrhdallimore@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class check_subscriptions extends \core\task\scheduled_task {
/** @var array 1d static cache, indexed by userid, storing whether or not the user has been fully set up.*/
protected $userssetupcache = array();
/** @var array 2d static cache, indexed by courseid and userid, storing whether a user can access the course with
* the 'tool/monitor:subscribe' capability.
*/
protected $courseaccesscache = array();
/**
* Get a descriptive name for this task.
*
* @since 3.2.0
* @return string name of the task.
*/
public function get_name() {
return get_string('taskchecksubscriptions', 'tool_monitor');
}
/**
* Checks all course-level rule subscriptions and activates/deactivates based on current course access.
*
* The ordering of checks within the task is important for optimisation purposes. The aim is to be able to make a decision
* about whether to activate/deactivate each subscription without making unnecessary checks. The ordering roughly follows the
* context model, starting with system and user checks and moving down to course and course-module only when necessary.
*
* For example, if the user is suspended, then any active subscription is made inactive right away. I.e. there is no need to
* check site-level, course-level or course-module-level permissions. Likewise, if a subscriptions is site-level, there is no
* need to check course-level and course-module-level permissions.
*
* The task performs the following checks, in this order:
* 1. Check for a suspended user, breaking if suspended.
* 2. Check for an incomplete (not set up) user, breaking if not fully set up.
* 3. Check for the required capability in the relevant context, breaking if the capability is not found.
* 4. Check whether the subscription is site-context, breaking if true.
* 5. Check whether the user has course access, breaking only if the subscription is not also course-module-level.
* 6. Check whether the user has course-module access.
*
* @since 3.2.0
*/
public function execute() {
global $DB;
if (!get_config('tool_monitor', 'enablemonitor')) {
return; // The tool is disabled. Nothing to do.
}
$toactivate = array(); // Store the ids of subscriptions to be activated upon completion.
$todeactivate = array(); // Store the ids of subscriptions to be deactivated upon completion.
// Resultset rows are ordered by userid and courseid to work nicely with get_fast_modinfo() caching.
$sql = "SELECT u.id AS userid, u.firstname AS userfirstname, u.lastname AS userlastname, u.suspended AS usersuspended,
u.email AS useremail, c.visible as coursevisible, c.cacherev as coursecacherev, s.courseid AS subcourseid,
s.userid AS subuserid, s.cmid AS subcmid, s.inactivedate AS subinactivedate, s.id AS subid
FROM {user} u
JOIN {tool_monitor_subscriptions} s ON (s.userid = u.id)
LEFT JOIN {course} c ON (c.id = s.courseid)
WHERE u.id = s.userid
ORDER BY s.userid, s.courseid";
$rs = $DB->get_recordset_sql($sql);
foreach ($rs as $row) {
// Create skeleton records from the result. This should be enough to use in subsequent access calls and avoids DB hits.
$sub = $this->get_subscription_from_rowdata($row);
$sub = new subscription($sub);
if (!isset($user) || $user->id != $sub->userid) {
$user= $this->get_user_from_rowdata($row);
}
if ((!isset($course) || $course->id != $sub->courseid) && !empty($sub->courseid)) {
$course = $this->get_course_from_rowdata($row);
}
// The user is suspended at site level, so deactivate any active subscriptions.
if ($user->suspended) {
if (subscription_manager::subscription_is_active($sub)) {
$todeactivate[] = $sub->id;
}
continue;
}
// Is the user fully set up? As per require_login on the subscriptions page.
if (!$this->is_user_setup($user)) {
if (subscription_manager::subscription_is_active($sub)) {
$todeactivate[] = $sub->id;
}
continue;
}
// Determine the context, based on the subscription course id.
$sitelevelsubscription = false;
if (empty($sub->courseid)) {
$context = \context_system::instance();
$sitelevelsubscription = true;
} else {
$context = \context_course::instance($sub->courseid);
}
// Check capability in the context.
if (!has_capability('tool/monitor:subscribe', $context, $user)) {
if (subscription_manager::subscription_is_active($sub)) {
$todeactivate[] = $sub->id;
}
continue;
}
// If the subscription is site-level, then we've run all the checks required to make an access decision.
if ($sitelevelsubscription) {
if (!subscription_manager::subscription_is_active($sub)) {
$toactivate[] = $sub->id;
}
continue;
}
// Check course access.
if (!$this->user_can_access_course($user, $course, 'tool/monitor:subscribe')) {
if (subscription_manager::subscription_is_active($sub)) {
$todeactivate[] = $sub->id;
}
continue;
}
// If the subscription has no course module relationship.
if (empty($sub->cmid)) {
if (!subscription_manager::subscription_is_active($sub)) {
$toactivate[] = $sub->id;
}
continue;
}
// Otherwise, check the course module info. We use the same checks as on the subscription page.
$modinfo = get_fast_modinfo($course, $sub->userid);
$cm = $modinfo->get_cm($sub->cmid);
if (!$cm || !$cm->uservisible || !$cm->available) {
if (subscription_manager::subscription_is_active($sub)) {
$todeactivate[] = $sub->id;
}
continue;
}
// The course module is available and visible, so make a decision.
if (!subscription_manager::subscription_is_active($sub)) {
$toactivate[] = $sub->id;
}
}
$rs->close();
// Activate/deactivate/delete relevant subscriptions.
subscription_manager::activate_subscriptions($toactivate);
subscription_manager::deactivate_subscriptions($todeactivate);
subscription_manager::delete_stale_subscriptions();
}
/**
* Determines whether a user is fully set up, using cached results where possible.
*
* @since 3.2.0
* @param \stdClass $user the user record.
* @return bool true if the user is fully set up, false otherwise.
*/
protected function is_user_setup($user) {
if (!isset($this->userssetupcache[$user->id])) {
$this->userssetupcache[$user->id] = !user_not_fully_set_up($user, true);
}
return $this->userssetupcache[$user->id];
}
/**
* Determines a user's access to a course with a given capability, using cached results where possible.
*
* @since 3.2.0
* @param \stdClass $user the user record.
* @param \stdClass $course the course record.
* @param string $capability the capability to check.
* @return bool true if the user can access the course with the specified capability, false otherwise.
*/
protected function user_can_access_course($user, $course, $capability) {
if (!isset($this->courseaccesscache[$course->id][$user->id][$capability])) {
$this->courseaccesscache[$course->id][$user->id][$capability] = can_access_course($course, $user, $capability, true);
}
return $this->courseaccesscache[$course->id][$user->id][$capability];
}
/**
* Returns a partial subscription record, created from properties of the supplied recordset row object.
* Intended to return a minimal record for specific use within this class and in subsequent access control calls only.
*
* @since 3.2.0
* @param \stdClass $rowdata the row object.
* @return \stdClass a partial subscription record.
*/
protected function get_subscription_from_rowdata($rowdata) {
$sub = new \stdClass();
$sub->id = $rowdata->subid;
$sub->userid = $rowdata->subuserid;
$sub->courseid = $rowdata->subcourseid;
$sub->cmid = $rowdata->subcmid;
$sub->inactivedate = $rowdata->subinactivedate;
return $sub;
}
/**
* Returns a partial course record, created from properties of the supplied recordset row object.
* Intended to return a minimal record for specific use within this class and in subsequent access control calls only.
*
* @since 3.2.0
* @param \stdClass $rowdata the row object.
* @return \stdClass a partial course record.
*/
protected function get_course_from_rowdata($rowdata) {
$course = new \stdClass();
$course->id = $rowdata->subcourseid;
$course->visible = $rowdata->coursevisible;
$course->cacherev = $rowdata->coursecacherev;
return $course;
}
/**
* Returns a partial user record, created from properties of the supplied recordset row object.
* Intended to return a minimal record for specific use within this class and in subsequent access control calls only.
*
* @since 3.2.0
* @param \stdClass $rowdata the row object.
* @return \stdClass a partial user record.
*/
protected function get_user_from_rowdata($rowdata) {
$user = new \stdClass();
$user->id = $rowdata->userid;
$user->firstname = $rowdata->userfirstname;
$user->lastname = $rowdata->userlastname;
$user->email = $rowdata->useremail;
$user->suspended = $rowdata->usersuspended;
return $user;
}
}
@@ -0,0 +1,146 @@
<?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/>.
/**
* Clean the tool_monitor_events table.
*
* @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\task;
/**
* Simple task to clean the tool_monitor_events table.
*/
class clean_events extends \core\task\scheduled_task {
/**
* Get a descriptive name for this task.
*
* @return string
*/
public function get_name() {
return get_string('taskcleanevents', 'tool_monitor');
}
/**
* Performs the cleaning of events.
*/
public function execute() {
global $DB;
if (!get_config('tool_monitor', 'enablemonitor')) {
return; // The tool is disabled. Nothing to do.
}
// Array to store which events have been triggered in which course.
$courses = array();
// Firstly, let's go through the site wide rules. There may be multiple rules for the site that rely on
// the same event being triggered, so we only remove the events when they reach the max timewindow.
if ($siterules = $DB->get_recordset('tool_monitor_rules', array('courseid' => 0), 'timewindow DESC')) {
// Go through each rule and check if there are any events we can remove.
foreach ($siterules as $rule) {
// Check if we have already processed this event.
if (isset($courses[$rule->courseid][$rule->eventname])) {
continue;
}
// Store the timewindow for this event.
$courses[$rule->courseid][$rule->eventname] = $rule->timewindow;
// Delete any events that may exist that have exceeded the timewindow.
$DB->delete_records_select('tool_monitor_events', 'eventname = :eventname AND
courseid = :courseid AND timecreated <= :timewindow',
array('eventname' => $rule->eventname, 'courseid' => $rule->courseid,
'timewindow' => time() - $rule->timewindow));
}
// Free resources.
$siterules->close();
}
// Now, get the course rules. The same applies here - there may be multiple rules for the course that rely on
// the same event being triggered, so we only remove the events when they reach the max timewindow.
if ($rules = $DB->get_recordset_select('tool_monitor_rules', 'courseid != 0', array(), 'timewindow DESC')) {
// Go through each rule and check if there are any events we can remove.
foreach ($rules as $rule) {
// Check if we have already processed this event for this particular course.
if (isset($courses[$rule->courseid][$rule->eventname])) {
continue;
}
// Add the course and event to the list.
$courses[$rule->courseid][$rule->eventname] = $rule->timewindow;
// If there is a site wide rule listening for this event do not remove it unless the maximum
// timewindow between the two has exceeded.
$timewindow = $rule->timewindow;
if (isset($courses[0][$rule->eventname]) && ($courses[0][$rule->eventname] > $timewindow)) {
$timewindow = $courses[0][$rule->eventname];
}
// Delete any events that may exist that have exceeded the timewindow.
$DB->delete_records_select('tool_monitor_events', 'eventname = :eventname AND
courseid = :courseid AND timecreated <= :timewindow',
array('eventname' => $rule->eventname, 'courseid' => $rule->courseid,
'timewindow' => time() - $timewindow));
}
// Free resources.
$rules->close();
}
if ($siterules || $rules) { // Check that there are rules present.
// Get a list of all the events we have been through.
$allevents = array();
foreach ($courses as $key => $value) {
foreach ($courses[$key] as $event => $notused) {
$allevents[] = $event;
}
}
// Remove all the events in the table that are not applicable to any rule. There may be a rule in one course
// listening for a certain event, but not in another course, so we can delete the event from the course
// where there is no rule. We also have to consider site wide rules. We may have an event that is triggered
// in a course we can't remove because there is a site wide rule for this event.
if ($events = $DB->get_recordset('tool_monitor_events')) {
// Array to store which events we need to remove.
$eventstodelete = array();
// Store the current time.
$now = time();
foreach ($events as $event) {
// If the event is not required for a course rule and there is no site wide rule for it, or
// it has extended past or equal to the timewindow for the site rule - it can be deleted.
if (!isset($courses[$event->courseid][$event->eventname]) && (!isset($courses[0][$event->eventname])
|| $courses[0][$event->eventname] <= ($now - $event->timecreated))) {
$eventstodelete[] = $event->id;
}
}
// Free resources.
$events->close();
// Remove the events.
if (!empty($eventstodelete)) {
list($eventidsql, $params) = $DB->get_in_or_equal($eventstodelete);
$DB->delete_records_select('tool_monitor_events', "id $eventidsql", $params);
}
}
// Remove all the events in the table that are not used by any rule.
if (!empty($allevents)) {
list($eventnamesql, $params) = $DB->get_in_or_equal($allevents, SQL_PARAMS_QM, 'param', false);
$DB->delete_records_select('tool_monitor_events', "eventname $eventnamesql", $params);
}
} else { // No rules, just remove everything.
$DB->delete_records('tool_monitor_events');
}
}
}