From 08ca1a5bfdb22d916460b5508785582b1bfceac2 Mon Sep 17 00:00:00 2001 From: mouneyrac Date: Mon, 24 Mar 2014 11:28:08 +0100 Subject: [PATCH 1/9] MDL-44342 airnotifier: Initial work, first version of the plugin --- message/output/airnotifier/db/access.php | 37 +++ message/output/airnotifier/db/install.php | 38 +++ message/output/airnotifier/db/install.xml | 26 ++ message/output/airnotifier/db/services.php | 44 +++ message/output/airnotifier/db/upgrade.php | 68 +++++ message/output/airnotifier/externallib.php | 150 ++++++++++ .../lang/en/message_airnotifier.php | 43 +++ message/output/airnotifier/lib.php | 139 +++++++++ .../message_output_airnotifier.php | 195 ++++++++++++ message/output/airnotifier/rest.php | 72 +++++ message/output/airnotifier/settings.php | 45 +++ message/output/airnotifier/style.css | 29 ++ .../airnotifier/tests/externallib_test.php | 86 ++++++ message/output/airnotifier/version.php | 29 ++ .../airnotifier/yui/toolboxes/toolboxes.js | 277 ++++++++++++++++++ 15 files changed, 1278 insertions(+) create mode 100755 message/output/airnotifier/db/access.php create mode 100755 message/output/airnotifier/db/install.php create mode 100755 message/output/airnotifier/db/install.xml create mode 100755 message/output/airnotifier/db/services.php create mode 100755 message/output/airnotifier/db/upgrade.php create mode 100755 message/output/airnotifier/externallib.php create mode 100755 message/output/airnotifier/lang/en/message_airnotifier.php create mode 100755 message/output/airnotifier/lib.php create mode 100755 message/output/airnotifier/message_output_airnotifier.php create mode 100755 message/output/airnotifier/rest.php create mode 100755 message/output/airnotifier/settings.php create mode 100755 message/output/airnotifier/style.css create mode 100755 message/output/airnotifier/tests/externallib_test.php create mode 100755 message/output/airnotifier/version.php create mode 100755 message/output/airnotifier/yui/toolboxes/toolboxes.js diff --git a/message/output/airnotifier/db/access.php b/message/output/airnotifier/db/access.php new file mode 100755 index 00000000000..e890372b3b5 --- /dev/null +++ b/message/output/airnotifier/db/access.php @@ -0,0 +1,37 @@ +. + +/** + * Capability definitions for airnotifier. + * + * @package message_airnotifier + * @category access + * @copyright 2012 Jerome Mouneyrac + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$capabilities = array( + + 'message/airnotifier:managedevice' => array( + 'captype' => 'write', + 'contextlevel' => CONTEXT_USER, + 'archetypes' => array( + 'user' => CAP_ALLOW + ) + ) +); diff --git a/message/output/airnotifier/db/install.php b/message/output/airnotifier/db/install.php new file mode 100755 index 00000000000..6e439ba4cdd --- /dev/null +++ b/message/output/airnotifier/db/install.php @@ -0,0 +1,38 @@ +. + +/** + * Airnotifier message processor installation code + * + * @package message_airnotifier + * @copyright 2012 Jerome Mouneyrac + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +/** + * Install the Airnotifier message processor + */ +function xmldb_message_airnotifier_install() { + global $DB; + + $result = true; + + $provider = new stdClass(); + $provider->name = 'airnotifier'; + $DB->insert_record('message_processors', $provider); + return $result; +} diff --git a/message/output/airnotifier/db/install.xml b/message/output/airnotifier/db/install.xml new file mode 100755 index 00000000000..e4df629034a --- /dev/null +++ b/message/output/airnotifier/db/install.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + +
+
+
\ No newline at end of file diff --git a/message/output/airnotifier/db/services.php b/message/output/airnotifier/db/services.php new file mode 100755 index 00000000000..47c3c45aaca --- /dev/null +++ b/message/output/airnotifier/db/services.php @@ -0,0 +1,44 @@ +. + + +/** + * Airnotifier external functions and service definitions. + * + * @package message_airnotifier + * @category webservice + * @copyright 2012 Jerome Mouneyrac + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +$functions = array( + 'message_airnotifier_add_user_device' => array( + 'classname' => 'message_airnotifier_external', + 'methodname' => 'add_user_device', + 'classpath' => 'message/output/airnotifier/externallib.php', + 'description' => 'Add device to user device list', + 'type' => 'write', + ), + + 'message_airnotifier_get_access_key' => array( + 'classname' => 'message_airnotifier_external', + 'methodname' => 'get_access_key', + 'classpath' => 'message/output/airnotifier/externallib.php', + 'description' => 'Get the mobile device access key with specified permissions', + 'type' => 'read', + ), +); + diff --git a/message/output/airnotifier/db/upgrade.php b/message/output/airnotifier/db/upgrade.php new file mode 100755 index 00000000000..acee7891e9a --- /dev/null +++ b/message/output/airnotifier/db/upgrade.php @@ -0,0 +1,68 @@ +. + +/** + * Upgrade code for airnotifier message processor + * + * @package message_airnotifier + * @copyright 2012 Jerome Mouneyrac + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +/** + * Upgrade code for the airnotifier message processor + * + * @param int $oldversion The version that we are upgrading from + */ +function xmldb_message_airnotifier_upgrade($oldversion) { + global $CFG, $DB; + + $dbman = $DB->get_manager(); + + + if ($oldversion < 2012070500.05) { + + // Define table user_devices to be created + $table = new xmldb_table('airnotifier_user_devices'); + + // Adding fields to table user_devices + $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); + $table->add_field('appname', XMLDB_TYPE_CHAR, '100', null, null, null, null); + $table->add_field('devicename', XMLDB_TYPE_CHAR, '255', null, null, null, null); + $table->add_field('devicetype', XMLDB_TYPE_CHAR, '100', null, null, null, null); + $table->add_field('deviceos', XMLDB_TYPE_CHAR, '100', null, null, null, null); + $table->add_field('deviceosversion', XMLDB_TYPE_CHAR, '100', null, null, null, null); + $table->add_field('devicebrand', XMLDB_TYPE_CHAR, '100', null, null, null, null); + $table->add_field('devicenotificationtoken', XMLDB_TYPE_CHAR, '255', null, null, null, null); + $table->add_field('deviceuid', XMLDB_TYPE_CHAR, '255', null, null, null, null); + $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); + $table->add_field('enable', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '1'); + + // Adding keys to table user_devices + $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); + + // Conditionally launch create table for user_devices + if (!$dbman->table_exists($table)) { + $dbman->create_table($table); + } + // Main savepoint reached + upgrade_plugin_savepoint(true, 2012070500.05); + } + + return true; +} + diff --git a/message/output/airnotifier/externallib.php b/message/output/airnotifier/externallib.php new file mode 100755 index 00000000000..6f52b8a2425 --- /dev/null +++ b/message/output/airnotifier/externallib.php @@ -0,0 +1,150 @@ +. + +/** + * external API for airnotifier web services + * + * @package message_airnotifier + * @category external + * @copyright 2012 Jerome Mouneyrac + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @since Moodle 2.4 + */ +class message_airnotifier_external extends external_api { + + /** + * Returns description of add_user_device() parameters + * + * @return external_function_parameters + * @since Moodle 2.4 + */ + public static function add_user_device_parameters() { + return new external_function_parameters( + array('device' => new external_single_structure( + array( + 'appname' => new external_value( PARAM_TEXT, 'the app name'), + 'devicename' => new external_value( PARAM_TEXT, 'Device name: "Jerome\'s iPhone"', VALUE_OPTIONAL), + 'devicetype' => new external_value( PARAM_TEXT, 'iPhone 3GS, Google Nexus S, ...', VALUE_OPTIONAL), + 'deviceos' => new external_value( PARAM_TEXT, 'iOS, Android, ...', VALUE_OPTIONAL), + 'deviceosversion' => new external_value( PARAM_TEXT, 'OS version number', VALUE_OPTIONAL), + 'devicebrand' => new external_value( PARAM_TEXT, 'the device brand (Apple, Samsung, ...)', VALUE_OPTIONAL), + 'devicenotificationtoken' => new external_value( PARAM_RAW, 'the device token used to send notification for the specified app'), + 'deviceuid' => new external_value( PARAM_RAW, 'the device unique device id if it exists', VALUE_OPTIONAL), + ), 'the device information - Important: type, os, osversion and brand will be saved in lowercase for fast searching' + ) + )); + } + + /** + * Add a device to the user device list + * + * @param array $device + * @return int device id + * @since Moodle 2.4 + */ + public static function add_user_device($device) { + global $USER, $CFG; + + $params = self::validate_parameters(self::add_user_device_parameters(), + array('device'=>$device)); + + // Ensure the current user is allowed to run this function + $context = context_user::instance($USER->id); + self::validate_context($context); + require_capability('message/airnotifier:managedevice', $context); + + $device['userid'] = $USER->id; + + require_once($CFG->dirroot . "/message/output/airnotifier/lib.php"); + $airnotifiermanager = new airnotifier_manager(); + $device['id'] = $airnotifiermanager->add_user_device($device); + + return $device['id']; + } + + /** + * Returns description of add_user_device() result value + * + * @return external_single_structure + * @since Moodle 2.4 + */ + public static function add_user_device_returns() { + return new external_value( PARAM_INT, 'Device id in the Moodle database'); + } + + /** + * Returns description of get_access_key() parameters + * + * @return external_function_parameters + * @since Moodle 2.4 + */ + public static function get_access_key_parameters() { + return new external_function_parameters( + array('permissions' => new external_multiple_structure( + new external_value( PARAM_ALPHA, 'the permission'), + 'Allowed permissions: createtoken (not yet implemented: deletetoken, accessobjects, + sendnotification, sendbroadcast)', + VALUE_DEFAULT, array() + ) + )); + } + + /** + * Get access key with specified permissions + * + * @param array $permissions the permission that the access key should + * @return string access key + * @since Moodle 2.4 + */ + public static function get_access_key($permissions = array()) { + global $CFG; + + $params = self::validate_parameters(self::get_access_key_parameters(), + array('permissions'=>$permissions)); + + // Check that user can use the requested permission. + foreach ($params['permissions'] as $perm) { + switch ($perm) { + case 'createtoken': + // Any mobile device / user should have this permission. + // No need to check anything for this permission. + + break; + + default: + throw new moodle_exception('permissionnotimplemented'); + break; + } + } + + // Look for access key that have exactly the same permissions. + // TODO: This mobile device access key should be retrieve by web service from + // moodle.org or airnotifer when the admin enables mobile on Moodle. + $accesskey = $CFG->airnotifierdeviceaccesskey; + + return $accesskey; + } + + /** + * Returns description of add_user_device() result value + * + * @return external_single_structure + * @since Moodle 2.4 + */ + public static function get_access_key_returns() { + return new external_value( PARAM_ALPHANUMEXT, 'access key'); + } +} diff --git a/message/output/airnotifier/lang/en/message_airnotifier.php b/message/output/airnotifier/lang/en/message_airnotifier.php new file mode 100755 index 00000000000..9f94a3599fe --- /dev/null +++ b/message/output/airnotifier/lang/en/message_airnotifier.php @@ -0,0 +1,43 @@ +. + +/** + * Strings for component 'message_airnotifier', language 'en' + * + * @package message_airnotifier + * @copyright 2012 Jerome mouneyrac + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +$string['airnotifieraccesskey'] = 'Airnotifier access key'; +$string['airnotifierappname'] = 'Airnotifier app name'; +$string['airnotifierdeviceaccesskey'] = 'Airnotifier device access key'; +$string['airnotifierport'] = 'Airnotifier port'; +$string['airnotifierurl'] = 'Airnotifier URL'; +$string['configairnotifierurl'] = 'The server url to connect to to send push notifications.'; +$string['configairnotifierport'] = 'The port to use when connecting to the airnotifier server.'; +$string['configairnotifieraccesskey'] = 'The access key to use when connecting to the airnotifier server.'; +$string['configairnotifierappname'] = 'The app name where Airnotifier will send notification.'; +$string['configairnotifierdeviceaccesskey'] = 'The access key used by mobile devices when connecting to the airnotifier server.'; +$string['deletecheckdevicename'] = 'Delete your device: {$a->name}'; +$string['deletedevice'] = 'Delete the device. Note that it will not forbid an app to register it again. If the device keep reappearing, disable it.'; +$string['devicetoken'] = 'Device token'; +$string['nodevices'] = 'No registered devices. Devices are automatically registered when you allow a Moodle iOS app to receive push notifications.'; +$string['notconfigured'] = 'The Airnotifier server hasn\'t been configured so Airnotifier messages cannot be sent'; +$string['pluginname'] = 'iPhone/iPad'; +$string['showhide'] = 'Enable/disable the device.'; +$string['unknowndevice'] = 'Unknown device'; diff --git a/message/output/airnotifier/lib.php b/message/output/airnotifier/lib.php new file mode 100755 index 00000000000..57c2d41348d --- /dev/null +++ b/message/output/airnotifier/lib.php @@ -0,0 +1,139 @@ +. + +/** + * Airnotifier related functions + * + * @package message_airnotifier + * @category external + * @copyright 2012 Jerome Mouneyrac + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @since Moodle 2.4 + */ +class airnotifier_manager { + + /** + * Include the relevant javascript and language strings for the device + * toolbox YUI module + * + * @return bool + */ + function include_device_ajax() { + global $PAGE, $CFG; + + if (!$CFG->enableajax) { + return false; + } + + $config = new stdClass(); + + // The URL to use for resource changes + if (!isset($config->resturl)) { + $config->resturl = '/message/output/airnotifier/rest.php'; + } + + // Any additional parameters which need to be included on page submission + if (!isset($config->pageparams)) { + $config->pageparams = array(); + } + + // Include toolboxes + $PAGE->requires->yui_module('moodle-message_airnotifier-toolboxes', 'M.message.init_device_toolbox', array(array( + 'ajaxurl' => $config->resturl, + 'config' => $config, + )) + ); + + // Required strings for the javascript + $PAGE->requires->strings_for_js(array('deletecheckdevicename'), 'message_airnotifier'); + $PAGE->requires->strings_for_js(array('show','hide'), 'moodle'); + + return true; + } + + /** + * Add device to the user device list. + * + * @param object $device + * @param int $userid if empty take the current user + * @return int device id (in Moodle database) + */ + public function add_user_device($device, $userid = null) { + global $DB, $USER; + + if (empty($user)) { + $userid = $USER->id; + } + + $device = (object) $device; + + // Check if the device token already exist - Note that we look for user in case several users use the same device + $existingdevice = $DB->get_record('airnotifier_user_devices', array('appname' => $device->appname, + 'devicenotificationtoken' => $device->devicenotificationtoken, 'userid' => $userid)); + + // trim the data (some field should be in lowercase to make the search on them quickly) + $attributstolower = array('devicetype', 'deviceos', 'deviceosversion', 'devicebrand'); + if ($existingdevice) { + foreach ($device as $attributname => $value) { + if (in_array($attributname, $attributstolower)) { + $existingdevice->{$attributname} = strtolower($value); + } else { + $existingdevice->{$attributname} = $value; + } + } + } else { + foreach ($attributstolower as $attribut) { + if (!empty($device->{$attribut})) { + $device->{$attribut} = strtolower($device->{$attribut}); + } + } + } + + if ($existingdevice) { + $DB->update_record('airnotifier_user_devices', $existingdevice); + return $existingdevice->id; + } else { + return $DB->insert_record('airnotifier_user_devices', $device); + } + } + + /** + * Return the user devices for a specific app. + * + * @param string $appname the app name . + * @param int $userid if empty take the current user. + * @param int $enabled if 1 returned only enabled devices, if 0 only disabled devices, if null all devices. + * @return array all the devices + */ + public function get_user_devices($appname, $userid = null, $enabled = null) { + global $USER, $DB; + + if (empty($userid)) { + $userid = $USER->id; + } + + $params = array('appname' => $appname, 'userid' => $userid); + if ($enabled !== null) { + $params['enable'] = $enabled; + } + + return $DB->get_records('airnotifier_user_devices', $params); + } + +} + +?> diff --git a/message/output/airnotifier/message_output_airnotifier.php b/message/output/airnotifier/message_output_airnotifier.php new file mode 100755 index 00000000000..202cfcaaf48 --- /dev/null +++ b/message/output/airnotifier/message_output_airnotifier.php @@ -0,0 +1,195 @@ +. + +require_once($CFG->dirroot . '/message/output/lib.php'); +require_once($CFG->dirroot . '/message/output/airnotifier/lib.php'); + +/** + * Airnotifier message processor to send messages to the APNS provider: airnotfier. + * (https://github.com/dongsheng/airnotifier) + * + * @package message_airnotifier + * @copyright 2012 Jerome Mouneyrac + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class message_output_airnotifier extends message_output { + + /** + * Processes the message and sends a notification via airnotifier + * + * @param stdClass $eventdata the event data submitted by the message sender plus $eventdata->savedmessageid + * @return true if ok, false if error + */ + function send_message($eventdata) { + global $CFG; + + if (!empty($CFG->noemailever)) { + // hidden setting for development sites, set in config.php if needed + debugging('$CFG->noemailever active, no airnotifier message sent.', DEBUG_MINIMAL); + return true; + } + + // skip any messaging suspended and deleted users + if ($eventdata->userto->auth === 'nologin' or $eventdata->userto->suspended or + $eventdata->userto->deleted) { + return true; + } + + // Mandatory notification data that need to be sent in the payload. They have variable length. + // We need to take them in consideration to calculate the maximum message size. + $notificationdata = array("urlparams" => $eventdata->urlparams, "id" => $eventdata->savedmessageid, + "type" => $eventdata->component . '_' . $eventdata->name, "userfrom" => fullname($eventdata->userfrom)); + + // Calculate the size of the message knowing Apple payload must be lower than 256 bytes. + // Airnotifier using few bytes of the payload, we must limit our message to even less characters. + $maxmsgsize = 205 - strlen(json_encode($notificationdata)); + $message = $eventdata->smallmessage; + // If the message size is too big make it shorter + if (strlen($message) >= $maxmsgsize) { + + // Cut the message to the maximum possible size. -4 for the the ending 3 dots (...). + $message = substr($message, 0 , $maxmsgsize - 4); + + // We need to check when the message is "escaped" then the message is not too long. + $encodedmsgsize = strlen(json_encode($message)); + if ($encodedmsgsize > $maxmsgsize) { + $totalescapedchar = $encodedmsgsize - strlen($message); + // Cut the message to the maximum possible size (taking the escaped character in account) + $message = substr($message, 0 , $maxmsgsize - 4 - $totalescapedchar); + } + + $message = $message . '...'; + } + + // We are sending to message to all devices + $airnotifiermanager = new airnotifier_manager(); + $devicetokens = $airnotifiermanager->get_user_devices($CFG->airnotifierappname, $eventdata->userto->id, true); + + foreach ($devicetokens as $devicetoken) { + + // Sending the message to the device + $serverurl = $CFG->airnotifierurl . ':' . $CFG->airnotifierport . '/notification/'; + $header = array('Accept: application/json', 'X-AN-APP-NAME: ' . $CFG->airnotifierappname, + 'X-AN-APP-KEY: ' . $CFG->airnotifieraccesskey); + $curl = new curl; + $curl->setHeader($header); + $params = array('alert' => $message, + 'date' => $eventdata->timecreated, + 'urlparams' => json_encode($eventdata->urlparams), + 'type' => $eventdata->component . '_' . $eventdata->name, + 'userfrom' => fullname($eventdata->userfrom), + 'id' => $eventdata->savedmessageid, + 'token' => $devicetoken->devicenotificationtoken); + $resp = $curl->post($serverurl, $params); + } + + return true; + } + + /** + * Creates necessary fields in the messaging config form. + * + * @param array $preferences An array of user preferences + */ + function config_form($preferences) { + global $CFG, $OUTPUT, $USER, $PAGE; + + if (!$this->is_system_configured()) { + return get_string('notconfigured', 'message_airnotifier'); + } else { + + $PAGE->requires->css('/message/output/airnotifier/style.css'); + + $airnotifiermanager = new airnotifier_manager(); + $devicetokens = $airnotifiermanager->get_user_devices($CFG->airnotifierappname, $USER->id); + + if (!empty($devicetokens)) { + $output = ''; + + foreach ($devicetokens as $devicetoken) { + + if ($devicetoken->enable) { + $hideshowiconname = 't/hide'; + $dimmed = ''; + } else { + $hideshowiconname = 't/show'; + $dimmed = 'dimmed_text'; + } + + $hideshowicon = $OUTPUT->pix_icon($hideshowiconname, get_string('showhide', 'message_airnotifier')); + $deleteicon = $OUTPUT->pix_icon('t/delete', get_string('deletedevice', 'message_airnotifier')); + $devicename = empty($devicetoken->devicename) ? get_string('unknowndevice', 'message_airnotifier') : s($devicetoken->devicename); + $hideurl = new moodle_url('message/output/airnotifier/action.php', + array('hide' => !$devicetoken->enable, 'deviceid' => $devicetoken->id, + 'sesskey' => sesskey())); + $deleteurl = new moodle_url('message/output/airnotifier/action.php', + array('delete' => true, 'deviceid' => $devicetoken->id, + 'sesskey' => sesskey())); + + $output .= html_writer::start_tag('li', array('id' => $devicetoken->id, 'class' => 'airnotifierdevice ' . $dimmed)) . "\n"; + $output .= html_writer::label($devicename, 'deviceid-' . $devicetoken->id, array('class' => 'devicelabel ')) . ' ' . + html_writer::link($hideurl, $hideshowicon, array('class' => 'hidedevice', 'alt' => 'show/hide')) . '' . + html_writer::link($deleteurl, $deleteicon, array('class' => 'deletedevice', 'alt' => 'delete')) . "\n"; + $output .= html_writer::end_tag('li') . "\n"; + } + + // Include the AJAX script to automatically trigger the action + $airnotifiermanager->include_device_ajax(); + + $output = html_writer::tag('ul', $output, array('id' => 'airnotifierdevices')); + } else { + $output = get_string('nodevices', 'message_airnotifier'); + } + return $output; + } + } + + /** + * Parses the submitted form data and saves it into preferences array. + * + * @param stdClass $form preferences form class + * @param array $preferences preferences array + */ + function process_form($form, &$preferences) { + if (isset($form->airnotifier_devicetoken) && !empty($form->airnotifier_devicetoken)) { + $preferences['message_processor_airnotifier_devicetoken'] = $form->airnotifier_devicetoken; + } + } + + /** + * Loads the config data from database to put on the form during initial form display + * + * @param array $preferences preferences array + * @param int $userid the user id + */ + function load_data(&$preferences, $userid) { + $preferences->airnotifier_devicetoken = + get_user_preferences('message_processor_airnotifier_devicetoken', '', $userid); + } + + /** + * Tests whether the airnotifier settings have been configured + * @return boolean true if airnotifier is configured + */ + function is_system_configured() { + global $CFG; + return (!empty($CFG->airnotifierurl) && !empty($CFG->airnotifierport) && + !empty($CFG->airnotifieraccesskey) && !empty($CFG->airnotifierdeviceaccesskey) && + !empty($CFG->airnotifierappname)); + } +} + diff --git a/message/output/airnotifier/rest.php b/message/output/airnotifier/rest.php new file mode 100755 index 00000000000..982830648f9 --- /dev/null +++ b/message/output/airnotifier/rest.php @@ -0,0 +1,72 @@ +. + +/** + * Provide interface for AJAX device actions + * + * @copyright 2012 Jerome Mouneyrac + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @package message_airnotifier + */ + +if (!defined('AJAX_SCRIPT')) { + define('AJAX_SCRIPT', true); +} +require_once(dirname(__FILE__) . '/../../../config.php'); +require_once($CFG->dirroot.'/message/output/airnotifier/lib.php'); + +// Initialise ALL the incoming parameters here, up front. +$field = optional_param('field', '', PARAM_ALPHA); +$id = required_param('id', PARAM_INT); +$pageaction = optional_param('action', '', PARAM_ALPHA); // Used to simulate a DELETE command + +$usercontext = context_user::instance($USER->id); + +$PAGE->set_url('/message/output/airnotifier/rest.php'); +$PAGE->set_context($usercontext); +require_login(); +require_sesskey(); + +echo $OUTPUT->header(); // send headers + +// OK, now let's process the parameters and do stuff +// MDL-10221 the DELETE method is not allowed on some web servers, so we simulate it with the action URL param +$requestmethod = $_SERVER['REQUEST_METHOD']; +if ($pageaction == 'DELETE') { + $requestmethod = 'DELETE'; +} + +$device = $DB->get_record('airnotifier_user_devices', array('id' => $id), '*', MUST_EXIST); + +$airnotifiermanager = new airnotifier_manager(); + +switch($requestmethod) { + case 'POST': + switch ($field) { + case 'enable': + require_capability('message/airnotifier:managedevice', $usercontext); + $device->enable = required_param('enable', PARAM_BOOL); + $DB->update_record('airnotifier_user_devices', $device); + break; + } + break; + + case 'DELETE': + require_capability('message/airnotifier:managedevice', $usercontext); + $DB->delete_records('airnotifier_user_devices', array('id' => $id)); + break; +} diff --git a/message/output/airnotifier/settings.php b/message/output/airnotifier/settings.php new file mode 100755 index 00000000000..f9a8c0191d5 --- /dev/null +++ b/message/output/airnotifier/settings.php @@ -0,0 +1,45 @@ +. + +/** + * Airnotifier configuration page + * + * @package message_airnotifier + * @copyright 2012 Jerome Mouneyrac + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +defined('MOODLE_INTERNAL') || die; + +if ($ADMIN->fulltree) { + //TODO: It will be better for these settings to be automatically retrieved by web service from moodle.org when enabling mobile. + // The processor should be enabled by the same enable mobile setting. + $settings->add(new admin_setting_configtext('airnotifierurl', + get_string('airnotifierurl', 'message_airnotifier'), + get_string('configairnotifierurl', 'message_airnotifier'), 'http://notify.moodle.net', PARAM_URL)); + $settings->add(new admin_setting_configtext('airnotifierport', + get_string('airnotifierport', 'message_airnotifier'), + get_string('configairnotifierport', 'message_airnotifier'), '8801', PARAM_INT)); + $settings->add(new admin_setting_configtext('airnotifieraccesskey', + get_string('airnotifieraccesskey', 'message_airnotifier'), + get_string('configairnotifieraccesskey', 'message_airnotifier'), '34220428981805c610cacc22aa6635fb', PARAM_ALPHANUMEXT)); + $settings->add(new admin_setting_configtext('airnotifierdeviceaccesskey', + get_string('airnotifierdeviceaccesskey', 'message_airnotifier'), + get_string('configairnotifierdeviceaccesskey', 'message_airnotifier'), 'e6b15c1ac0606980c46f87c476c9f28e', PARAM_ALPHANUMEXT)); + $settings->add(new admin_setting_configtext('airnotifierappname', + get_string('airnotifierappname', 'message_airnotifier'), + get_string('configairnotifierappname', 'message_airnotifier'), 'moodlehtml5app', PARAM_TEXT)); +} diff --git a/message/output/airnotifier/style.css b/message/output/airnotifier/style.css new file mode 100755 index 00000000000..cb030449778 --- /dev/null +++ b/message/output/airnotifier/style.css @@ -0,0 +1,29 @@ +/** +// 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 . + + + Airnotifier (message output plugin) CSS + + package message_airnotifier + category css + copyright 2012 Jerome Mouneyrac + license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +ul#airnotifierdevices { + list-style: none; + margin: 0em; +} \ No newline at end of file diff --git a/message/output/airnotifier/tests/externallib_test.php b/message/output/airnotifier/tests/externallib_test.php new file mode 100755 index 00000000000..5ced13822c3 --- /dev/null +++ b/message/output/airnotifier/tests/externallib_test.php @@ -0,0 +1,86 @@ +. + +/** + * External airnotifier functions unit tests + * + * @package message_airnotifier + * @category external + * @copyright 2012 Jerome Mouneyrac + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; + +require_once($CFG->dirroot . '/webservice/tests/helpers.php'); + +/** + * External airnotifier functions unit tests + * + * @package message_airnotifier + * @category external + * @copyright 2012 Jerome Mouneyrac + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class message_airnotifier_external_testcase extends externallib_testcase { + + /** + * Tests set up + */ + protected function setUp() { + global $CFG; + require_once($CFG->dirroot . '/lib/externallib.php'); + require_once($CFG->dirroot . '/webservice/externallib.php'); + } + + /** + * Test add_user_device + */ + public function test_add_user_device() { + + global $DB, $USER; + + $this->resetAfterTest(true); + + $user = self::getDataGenerator()->create_user(); + self::setUser($user); + + $device = array(); + $device['appname'] = 'mymoodle'; + $device['devicename'] = 'Jerome\'s Android'; + $device['devicetype'] = 'galaxy nexus'; + $device['deviceos'] = 'android'; + $device['deviceosversion'] = '4.0.3'; + $device['devicebrand'] = 'samsung'; + $device['devicenotificationtoken'] = 'jhg576576sesgy98sd7g87sdg697sfg576df'; + $device['deviceuid'] = 'is87fs64g2vuf84g378gbh378ehg98h875'; + + $deviceid = message_airnotifier_external::add_user_device($device); + + $devicedb = $DB->get_record('user_devices', array('id' => $deviceid)); + $this->assertEquals($devicedb->devicename, $device['devicename']); + $this->assertEquals($devicedb->devicetype, $device['devicetype']); + $this->assertEquals($devicedb->deviceos, $device['deviceos']); + $this->assertEquals($devicedb->deviceosversion, $device['deviceosversion']); + $this->assertEquals($devicedb->devicebrand, $device['devicebrand']); + $this->assertEquals($devicedb->devicenotificationtoken, $device['devicenotificationtoken']); + $this->assertEquals($devicedb->deviceuid, $device['deviceuid']); + $this->assertEquals($devicedb->userid, $USER->id); + } + +} diff --git a/message/output/airnotifier/version.php b/message/output/airnotifier/version.php new file mode 100755 index 00000000000..64545d1a2af --- /dev/null +++ b/message/output/airnotifier/version.php @@ -0,0 +1,29 @@ +. + +/** + * Airnotifier message processor version information + * + * @package message_airnotifier + * @copyright 2012 Jerome Mouneyrac + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +defined('MOODLE_INTERNAL') || die(); + +$plugin->version = 2012070500.05; // The current plugin version (Date: YYYYMMDDXX) +$plugin->requires = 2012110101.01; // Requires this Moodle version +$plugin->component = 'message_airnotifier'; // Full name of the plugin (used for diagnostics) diff --git a/message/output/airnotifier/yui/toolboxes/toolboxes.js b/message/output/airnotifier/yui/toolboxes/toolboxes.js new file mode 100755 index 00000000000..8a6e824974c --- /dev/null +++ b/message/output/airnotifier/yui/toolboxes/toolboxes.js @@ -0,0 +1,277 @@ +YUI.add('moodle-message_airnotifier-toolboxes', function(Y) { + WAITICON = { + 'pix':"i/loading_small", + 'component':'moodle' + }; + // The CSS selectors we use + var CSS = { + AIRNOTIFIERCONTENT : 'fieldset#messageprocessor_airnotifier', + HIDEDEVICE : 'a.hidedevice', + DELETEDEVICE : 'a.deletedevice', + DEVICELI : 'li.airnotifierdevice', + DIMCLASS : 'dimmed', + DIMMEDTEXT : 'dimmed_text', + DEVICEIDPREFIX : 'deviceid-' + }; + + /** + * The toolbox classes + * + * TOOLBOX is a generic class which should never be directly instantiated + * DEVICETOOLBOX is a class extending TOOLBOX containing code specific to devices + */ + var TOOLBOX = function() { + TOOLBOX.superclass.constructor.apply(this, arguments); + } + + Y.extend(TOOLBOX, Y.Base, { + /** + * Replace the button click at the selector with the specified + * callback + * + * @param toolboxtarget The selector of the working area + * @param selector The 'button' to replace + * @param callback The callback to apply + * @param cursor An optional cursor style to apply + */ + replace_button : function(toolboxtarget, selector, callback, cursor) { + if (!cursor) { + // Set the default cursor type to pointer to match the + // anchor + cursor = 'pointer'; + } + var button = Y.one(toolboxtarget).all(selector) + .setStyle('cursor', cursor); + + // on isn't chainable and will return an event + button.on('click', callback, this); + + return button; + }, + /** + * Toggle the visibility and availability for the specified + * device show/hide button + */ + toggle_hide_device_ui : function(button) { + + var element = button.ancestor(CSS.DEVICELI); + var hideicon = button.one('img'); + + var dimarea; + var toggle_class = CSS.DIMMEDTEXT; + + var status = ''; + var value; + if (element.hasClass(toggle_class)) { + status = 'hide'; + value = 1; + } else { + status = 'show'; + value = 0; + } + + // Change the UI + element.toggleClass(toggle_class); + // We need to toggle dimming on the description too + // element.all(CSS.CONTENTAFTERLINK).toggleClass(CSS.DIMMEDTEXT); + var newstring = M.util.get_string(status, 'moodle'); + hideicon.setAttrs({ + 'alt' : newstring, + 'title' : newstring, + 'src' : M.util.image_url('t/' + status) + }); + button.set('title', newstring); + button.set('className', 'editing_'+status); + + return value; + }, + /** + * Send a request using the REST API + * + * @param data The data to submit + * @param statusspinner (optional) A statusspinner which may contain a section loader + * @param optionalconfig (optional) Any additional configuration to submit + * @return response responseText field from responce + */ + send_request : function(data, statusspinner, optionalconfig) { + // Default data structure + if (!data) { + data = {}; + } + // Handle any variables which we must pass back through to + var pageparams = this.get('config').pageparams; + for (varname in pageparams) { + data[varname] = pageparams[varname]; + } + + data.sesskey = M.cfg.sesskey; + + var uri = M.cfg.wwwroot + this.get('ajaxurl'); + + // Define the configuration to send with the request + var responsetext = []; + var config = { + method: 'POST', + data: data, + on: { + success: function(tid, response) { + try { + responsetext = Y.JSON.parse(response.responseText); + if (responsetext.error) { + new M.core.ajaxException(responsetext); + } + } catch (e) {} + if (statusspinner) { + window.setTimeout(function(e) { + statusspinner.hide(); + }, 400); + } + }, + failure : function(tid, response) { + if (statusspinner) { + statusspinner.hide(); + } + new M.core.ajaxException(response); + } + }, + context: this, + sync: true + } + + // Apply optional config + if (optionalconfig) { + for (varname in optionalconfig) { + config[varname] = optionalconfig[varname]; + } + } + + if (statusspinner) { + statusspinner.show(); + } + + // Send the request + Y.io(uri, config); + return responsetext; + }, + /** + * Return the module ID for the specified element + * + * @param element The
  • element to determine a module-id number for + * @return string The module ID + */ + get_element_id : function(element) { + return element.get('id').replace(CSS.DEVICEIDPREFIX, ''); + } + }, + { + NAME : 'device-toolbox', + ATTRS : { + ajaxurl : { + 'value' : 0 + }, + config : { + 'value' : 0 + } + } + } + ); + + var DEVICETOOLBOX = function() { + DEVICETOOLBOX.superclass.constructor.apply(this, arguments); + } + + Y.extend(DEVICETOOLBOX, TOOLBOX, { + + /** + * Initialize the device toolbox + * + * Updates all span.commands with relevant handlers and other required changes + */ + initializer : function(config) { + this.setup_for_device(); + }, + /** + * Update any span.commands within the scope of the specified + * selector with AJAX equivelants + * + * @param baseselector The selector to limit scope to + * @return void + */ + setup_for_device : function(baseselector) { + if (!baseselector) { + var baseselector = CSS.AIRNOTIFIERCONTENT; + } + + Y.all(baseselector).each(this._setup_for_device, this); + }, + _setup_for_device : function(toolboxtarget) { + // Delete + this.replace_button(toolboxtarget, CSS.DELETEDEVICE, this.delete_device); + + // Show/Hide + var showhide = this.replace_button(toolboxtarget, CSS.HIDEDEVICE, this.toggle_hide_device); + }, + delete_device : function(e) { + // Prevent the default button action + e.preventDefault(); + + // Get the element we're working on + var element = e.target.ancestor(CSS.DEVICELI); + + var confirmstring = ''; + var plugindata = { + name : element.one('*').getHTML() //get the label + } + confirmstring = M.util.get_string('deletecheckdevicename', 'message_airnotifier', plugindata); + + // Confirm element removal + if (!confirm(confirmstring)) { + return false; + } + + // Actually remove the element + element.remove(); + var data = { + 'class' : 'device', + 'action' : 'DELETE', + 'id' : this.get_element_id(element) + }; + this.send_request(data); + }, + toggle_hide_device : function(e) { + // Prevent the default button action + e.preventDefault(); + + // Get the element we're working on + var element = e.target.ancestor(CSS.DEVICELI); + + var button = e.target.ancestor('a', true); + + var value = this.toggle_hide_device_ui(button); + + // Send the request + var data = { + 'field' : 'enable', + 'enable' : value, + 'id' : this.get_element_id(element) + }; + var spinner = M.util.add_spinner(Y, element); + this.send_request(data, spinner); + } + }, { + NAME : 'message-device-toolbox', + ATTRS : { + } + }); + + M.message = M.message || {}; + + M.message.init_device_toolbox = function(config) { + return new DEVICETOOLBOX(config); + }; + +}, +'@VERSION@', { + requires : ['base', 'node', 'io'] +} +); From 3570f5e287f7ba4b29f7afd3155da92fef9e6173 Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Mon, 24 Mar 2014 11:48:00 +0100 Subject: [PATCH 2/9] MDL-44342 airnotifier: Added support for the new workflow of registering devices And also for request access keys to Airnotifier --- .../output/airnotifier/classes/manager.php | 198 ++++++++++++++++ message/output/airnotifier/db/install.php | 4 +- message/output/airnotifier/db/install.xml | 19 +- message/output/airnotifier/db/services.php | 15 +- message/output/airnotifier/db/upgrade.php | 68 ------ message/output/airnotifier/externallib.php | 217 ++++++++++-------- .../lang/en/message_airnotifier.php | 19 +- message/output/airnotifier/lib.php | 139 ----------- .../message_output_airnotifier.php | 121 +++++----- .../output/airnotifier/requestaccesskey.php | 77 +++++++ message/output/airnotifier/rest.php | 46 +--- message/output/airnotifier/settings.php | 28 +-- message/output/airnotifier/style.css | 2 +- message/output/airnotifier/version.php | 7 +- .../airnotifier/yui/toolboxes/toolboxes.js | 42 ++-- 15 files changed, 539 insertions(+), 463 deletions(-) create mode 100755 message/output/airnotifier/classes/manager.php delete mode 100755 message/output/airnotifier/db/upgrade.php delete mode 100755 message/output/airnotifier/lib.php create mode 100755 message/output/airnotifier/requestaccesskey.php diff --git a/message/output/airnotifier/classes/manager.php b/message/output/airnotifier/classes/manager.php new file mode 100755 index 00000000000..bbb7b0f77a5 --- /dev/null +++ b/message/output/airnotifier/classes/manager.php @@ -0,0 +1,198 @@ +. + +/** + * Airnotifier manager class + * + * @package message_airnotifier + * @category external + * @copyright 2012 Jerome Mouneyrac + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @since Moodle 2.7 + */ + + +/** + * Airnotifier helper manager class + * + * @copyright 2012 Jerome Mouneyrac + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class message_airnotifier_manager { + + /** + * Include the relevant javascript and language strings for the device + * toolbox YUI module + * + * @return bool + */ + public function include_device_ajax() { + global $PAGE, $CFG; + + if (!$CFG->enableajax) { + return false; + } + + $config = new stdClass(); + + // The URL to use for resource changes. + if (!isset($config->resturl)) { + $config->resturl = '/message/output/airnotifier/rest.php'; + } + + // Any additional parameters which need to be included on page submission. + if (!isset($config->pageparams)) { + $config->pageparams = array(); + } + + // Include toolboxes. + $PAGE->requires->yui_module('moodle-message_airnotifier-toolboxes', 'M.message.init_device_toolbox', array(array( + 'ajaxurl' => $config->resturl, + 'config' => $config, + )) + ); + + // Required strings for the javascript. + $PAGE->requires->strings_for_js(array('deletecheckdevicename'), 'message_airnotifier'); + $PAGE->requires->strings_for_js(array('show', 'hide'), 'moodle'); + + return true; + } + + /** + * Return the user devices for a specific app. + * + * @param string $appname the app name . + * @param int $userid if empty take the current user. + * @return array all the devices + */ + public function get_user_devices($appname, $userid = null) { + global $USER, $DB; + + if (empty($userid)) { + $userid = $USER->id; + } + + $devices = array(); + + $params = array('appid' => $appname, 'userid' => $userid); + + // First, we look all the devices registered for this user in the Moodle core. + // We are going to allow only ios devices (since these are the ones that supports PUSH notifications). + if ($userdevices = $DB->get_records('user_devices', $params)) { + foreach ($userdevices as $device) { + if (core_text::strtolower($device->platform) == 'ios') { + // Check if the device is known by airnotifier. + if (!$airnotifierdev = $DB->get_record('message_airnotifier_devices', array('userdeviceid' => $device->id))) { + // We have to create the device token in airnotifier. + + if (! $this->create_token($device->pushid)) { + continue; + } + + $airnotifierdev = new stdClass; + $airnotifierdev->userdeviceid = $device->id; + $airnotifierdev->enable = 1; + $airnotifierdev->id = $DB->insert_record('message_airnotifier_devices', $airnotifierdev); + } + $device->id = $airnotifierdev->id; + $device->enable = $airnotifierdev->enable; + $devices[] = $device; + } + } + } + return $devices; + } + + /** + * Request and access key to Airnotifier + * + * @return mixed The access key or false in case of error + */ + public function request_accesskey() { + global $CFG, $USER; + + require_once($CFG->libdir . '/filelib.php'); + + // Sending the request access key request to Airnotifier. + $serverurl = $CFG->airnotifierurl . ':' . $CFG->airnotifierport . '/accesskeys/'; + // We use an APP Key "none", it can be anything. + $header = array('Accept: application/json', 'X-AN-APP-NAME: ' . $CFG->airnotifierappname, + 'X-AN-APP-KEY: none'); + $curl = new curl(); + $curl->setHeader($header); + + // Site ids are stored as secrets in md5 in the Moodle public hub. + $params = array( + 'url' => $CFG->wwwroot, + 'siteid' => md5($CFG->siteidentifier), + 'contact' => $USER->email, + 'description' => $CFG->wwwroot + ); + $resp = $curl->post($serverurl, $params); + + if ($key = json_decode($resp, true)) { + if (!empty($key['accesskey'])) { + return $key['accesskey']; + } + } + return false; + } + + /** + * Create a device token in the Airnotifier instance + * @param string $token The token to be created + * @return bool True if all was right + */ + private function create_token($token) { + global $CFG; + + if (!$this->is_system_configured()) { + return false; + } + + require_once($CFG->libdir . '/filelib.php'); + + $serverurl = $CFG->airnotifierurl . ':' . $CFG->airnotifierport . '/tokens/' . $token; + $header = array('Accept: application/json', 'X-AN-APP-NAME: ' . $CFG->airnotifierappname, + 'X-AN-APP-KEY: ' . $CFG->airnotifieraccesskey); + $curl = new curl; + $curl->setHeader($header); + $params = array(); + $resp = $curl->post($serverurl, $params); + + if ($resp = json_decode($resp, true)) { + if (!empty($resp['status'])) { + return $resp['status'] == 'ok' || $resp['status'] == 'token exists'; + } + } + return false; + } + + /** + * Tests whether the airnotifier settings have been configured + * @return boolean true if airnotifier is configured + */ + public function is_system_configured() { + global $CFG; + + return (!empty($CFG->airnotifierurl) && !empty($CFG->airnotifierport) && + !empty($CFG->airnotifieraccesskey) && !empty($CFG->airnotifierappname) && + !empty($CFG->airnotifiermobileappname)); + } + +} + diff --git a/message/output/airnotifier/db/install.php b/message/output/airnotifier/db/install.php index 6e439ba4cdd..433698b5fd1 100755 --- a/message/output/airnotifier/db/install.php +++ b/message/output/airnotifier/db/install.php @@ -1,5 +1,4 @@ name = 'airnotifier'; $DB->insert_record('message_processors', $provider); + return $result; } diff --git a/message/output/airnotifier/db/install.xml b/message/output/airnotifier/db/install.xml index e4df629034a..ec529ff0924 100755 --- a/message/output/airnotifier/db/install.xml +++ b/message/output/airnotifier/db/install.xml @@ -1,25 +1,18 @@ - - +
    - - - - - - - - - - - + + + +
    diff --git a/message/output/airnotifier/db/services.php b/message/output/airnotifier/db/services.php index 47c3c45aaca..f7fa49ec9c1 100755 --- a/message/output/airnotifier/db/services.php +++ b/message/output/airnotifier/db/services.php @@ -25,20 +25,19 @@ */ $functions = array( - 'message_airnotifier_add_user_device' => array( + 'message_airnotifier_is_system_configured' => array( 'classname' => 'message_airnotifier_external', - 'methodname' => 'add_user_device', + 'methodname' => 'is_system_configured', 'classpath' => 'message/output/airnotifier/externallib.php', - 'description' => 'Add device to user device list', - 'type' => 'write', + 'description' => 'Check whether the airnotifier settings have been configured', + 'type' => 'read', ), - 'message_airnotifier_get_access_key' => array( + 'message_airnotifier_are_notification_preferences_configured' => array( 'classname' => 'message_airnotifier_external', - 'methodname' => 'get_access_key', + 'methodname' => 'are_notification_preferences_configured', 'classpath' => 'message/output/airnotifier/externallib.php', - 'description' => 'Get the mobile device access key with specified permissions', + 'description' => 'Check if the users have notification preferences configured yet', 'type' => 'read', ), ); - diff --git a/message/output/airnotifier/db/upgrade.php b/message/output/airnotifier/db/upgrade.php deleted file mode 100755 index acee7891e9a..00000000000 --- a/message/output/airnotifier/db/upgrade.php +++ /dev/null @@ -1,68 +0,0 @@ -. - -/** - * Upgrade code for airnotifier message processor - * - * @package message_airnotifier - * @copyright 2012 Jerome Mouneyrac - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -/** - * Upgrade code for the airnotifier message processor - * - * @param int $oldversion The version that we are upgrading from - */ -function xmldb_message_airnotifier_upgrade($oldversion) { - global $CFG, $DB; - - $dbman = $DB->get_manager(); - - - if ($oldversion < 2012070500.05) { - - // Define table user_devices to be created - $table = new xmldb_table('airnotifier_user_devices'); - - // Adding fields to table user_devices - $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); - $table->add_field('appname', XMLDB_TYPE_CHAR, '100', null, null, null, null); - $table->add_field('devicename', XMLDB_TYPE_CHAR, '255', null, null, null, null); - $table->add_field('devicetype', XMLDB_TYPE_CHAR, '100', null, null, null, null); - $table->add_field('deviceos', XMLDB_TYPE_CHAR, '100', null, null, null, null); - $table->add_field('deviceosversion', XMLDB_TYPE_CHAR, '100', null, null, null, null); - $table->add_field('devicebrand', XMLDB_TYPE_CHAR, '100', null, null, null, null); - $table->add_field('devicenotificationtoken', XMLDB_TYPE_CHAR, '255', null, null, null, null); - $table->add_field('deviceuid', XMLDB_TYPE_CHAR, '255', null, null, null, null); - $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); - $table->add_field('enable', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '1'); - - // Adding keys to table user_devices - $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); - - // Conditionally launch create table for user_devices - if (!$dbman->table_exists($table)) { - $dbman->create_table($table); - } - // Main savepoint reached - upgrade_plugin_savepoint(true, 2012070500.05); - } - - return true; -} - diff --git a/message/output/airnotifier/externallib.php b/message/output/airnotifier/externallib.php index 6f52b8a2425..3be2313e71e 100755 --- a/message/output/airnotifier/externallib.php +++ b/message/output/airnotifier/externallib.php @@ -15,136 +15,163 @@ // along with Moodle. If not, see . /** - * external API for airnotifier web services + * External functions * * @package message_airnotifier * @category external * @copyright 2012 Jerome Mouneyrac * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - * @since Moodle 2.4 + * @since Moodle 2.7 + */ + + +/** + * External API for airnotifier web services + * + * @package message_airnotifier + * @category external + * @copyright 2012 Jerome Mouneyrac + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @since Moodle 2.7 */ class message_airnotifier_external extends external_api { /** - * Returns description of add_user_device() parameters + * Returns description of method parameters * - * @return external_function_parameters - * @since Moodle 2.4 + * @since Moodle 2.7 */ - public static function add_user_device_parameters() { + public static function is_system_configured_parameters() { + return new external_function_parameters( + array() + ); + } + + /** + * Tests whether the airnotifier settings have been configured + * + * @since Moodle 2.7 + */ + public static function is_system_configured() { + + $manager = new message_airnotifier_manager(); + return (int) $manager->is_system_configured(); + } + + /** + * Returns description of method result value + * + * @return external_single_structure + * @since Moodle 2.7 + */ + public static function is_system_configured_returns() { + return new external_value( PARAM_INT, '0 if the system is not configured, 1 otherwise'); + } + + /** + * Returns description of method parameters + * + * @since Moodle 2.7 + */ + public static function are_notification_preferences_configured_parameters() { return new external_function_parameters( - array('device' => new external_single_structure( array( - 'appname' => new external_value( PARAM_TEXT, 'the app name'), - 'devicename' => new external_value( PARAM_TEXT, 'Device name: "Jerome\'s iPhone"', VALUE_OPTIONAL), - 'devicetype' => new external_value( PARAM_TEXT, 'iPhone 3GS, Google Nexus S, ...', VALUE_OPTIONAL), - 'deviceos' => new external_value( PARAM_TEXT, 'iOS, Android, ...', VALUE_OPTIONAL), - 'deviceosversion' => new external_value( PARAM_TEXT, 'OS version number', VALUE_OPTIONAL), - 'devicebrand' => new external_value( PARAM_TEXT, 'the device brand (Apple, Samsung, ...)', VALUE_OPTIONAL), - 'devicenotificationtoken' => new external_value( PARAM_RAW, 'the device token used to send notification for the specified app'), - 'deviceuid' => new external_value( PARAM_RAW, 'the device unique device id if it exists', VALUE_OPTIONAL), - ), 'the device information - Important: type, os, osversion and brand will be saved in lowercase for fast searching' - ) - )); + 'userids' => new external_multiple_structure(new external_value(PARAM_INT, 'user ID')), + ) + ); } /** - * Add a device to the user device list + * Check if the users have notification preferences configured for the airnotifier plugin * - * @param array $device - * @return int device id - * @since Moodle 2.4 + * @param array $userids Array of user ids + * @since Moodle 2.7 */ - public static function add_user_device($device) { - global $USER, $CFG; + public static function are_notification_preferences_configured($userids) { + global $CFG, $USER, $DB; - $params = self::validate_parameters(self::add_user_device_parameters(), - array('device'=>$device)); + require_once($CFG->dirroot . '/message/lib.php'); + $params = self::validate_parameters(self::are_notification_preferences_configured_parameters(), + array('userids' => $userids)); - // Ensure the current user is allowed to run this function - $context = context_user::instance($USER->id); - self::validate_context($context); - require_capability('message/airnotifier:managedevice', $context); + list($sqluserids, $params) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED); + $uselect = ', ' . context_helper::get_preload_record_columns_sql('ctx'); + $ujoin = "LEFT JOIN {context} ctx ON (ctx.instanceid = u.id AND ctx.contextlevel = :contextlevel)"; + $params['contextlevel'] = CONTEXT_USER; + $usersql = "SELECT u.* $uselect + FROM {user} u $ujoin + WHERE u.id $sqluserids"; + $users = $DB->get_recordset_sql($usersql, $params); - $device['userid'] = $USER->id; + $result = array(); + $hasuserupdatecap = has_capability('moodle/user:update', context_system::instance()); + foreach ($users as $user) { - require_once($CFG->dirroot . "/message/output/airnotifier/lib.php"); - $airnotifiermanager = new airnotifier_manager(); - $device['id'] = $airnotifiermanager->add_user_device($device); + $currentuser = ($user->id == $USER->id); - return $device['id']; - } + if ($currentuser or $hasuserupdatecap) { - /** - * Returns description of add_user_device() result value - * - * @return external_single_structure - * @since Moodle 2.4 - */ - public static function add_user_device_returns() { - return new external_value( PARAM_INT, 'Device id in the Moodle database'); - } + if (!empty($user->deleted)) { + $result['warnings'][] = "User $user->id was deleted"; + continue; + } - /** - * Returns description of get_access_key() parameters - * - * @return external_function_parameters - * @since Moodle 2.4 - */ - public static function get_access_key_parameters() { - return new external_function_parameters( - array('permissions' => new external_multiple_structure( - new external_value( PARAM_ALPHA, 'the permission'), - 'Allowed permissions: createtoken (not yet implemented: deletetoken, accessobjects, - sendnotification, sendbroadcast)', - VALUE_DEFAULT, array() - ) - )); - } + $preferences = array(); + $preferences['userid'] = $user->id; + $preferences['configured'] = 0; - /** - * Get access key with specified permissions - * - * @param array $permissions the permission that the access key should - * @return string access key - * @since Moodle 2.4 - */ - public static function get_access_key($permissions = array()) { - global $CFG; + // Now we get for all the providers and all the states + // the user preferences to check if at least one is enabled for airnotifier plugin. + $providers = message_get_providers_for_user($user->id); + foreach ($providers as $provider) { + foreach (array('loggedin', 'loggedoff') as $state) { + $prefname = 'message_provider_'.$provider->component.'_'.$provider->name.'_'.$state; + $linepref = get_user_preferences($prefname, '', $user->id); + if ($linepref == '') { + continue; + } + $lineprefarray = explode(',', $linepref); - $params = self::validate_parameters(self::get_access_key_parameters(), - array('permissions'=>$permissions)); + foreach ($lineprefarray as $pref) { + if ($pref == 'airnotifier') { + $preferences['configured'] = 1; + break 2; + } + } + } + } - // Check that user can use the requested permission. - foreach ($params['permissions'] as $perm) { - switch ($perm) { - case 'createtoken': - // Any mobile device / user should have this permission. - // No need to check anything for this permission. - - break; - - default: - throw new moodle_exception('permissionnotimplemented'); - break; + $result['users'][] = $preferences; + } else if (!$hasuserupdatecap) { + $result['warnings'][] = "You don't have permissions for view user $user->id preferences"; } + } + $users->close(); - // Look for access key that have exactly the same permissions. - // TODO: This mobile device access key should be retrieve by web service from - // moodle.org or airnotifer when the admin enables mobile on Moodle. - $accesskey = $CFG->airnotifierdeviceaccesskey; - - return $accesskey; + return $result; } /** - * Returns description of add_user_device() result value + * Returns description of method result value * * @return external_single_structure - * @since Moodle 2.4 + * @since Moodle 2.7 */ - public static function get_access_key_returns() { - return new external_value( PARAM_ALPHANUMEXT, 'access key'); + public static function are_notification_preferences_configured_returns() { + return new external_single_structure( + array( + 'users' => new external_multiple_structure( + new external_single_structure( + array ( + 'userid' => new external_value(PARAM_INT, 'userid id'), + 'configured' => new external_value(PARAM_INT, '1 is the user preferences have been configured and 0 if not') + ) + ), + 'list of preferences by user'), + 'warnings' => new external_warnings() + ) + ); } -} + +} \ No newline at end of file diff --git a/message/output/airnotifier/lang/en/message_airnotifier.php b/message/output/airnotifier/lang/en/message_airnotifier.php index 9f94a3599fe..2c8c5c019ad 100755 --- a/message/output/airnotifier/lang/en/message_airnotifier.php +++ b/message/output/airnotifier/lang/en/message_airnotifier.php @@ -1,5 +1,4 @@ name}'; -$string['deletedevice'] = 'Delete the device. Note that it will not forbid an app to register it again. If the device keep reappearing, disable it.'; +$string['deletedevice'] = 'Delete the device. Note that an app can register the device again. If the device keeps reappearing, disable it.'; $string['devicetoken'] = 'Device token'; -$string['nodevices'] = 'No registered devices. Devices are automatically registered when you allow a Moodle iOS app to receive push notifications.'; +$string['errorretrievingkey'] = 'An error occurred while retrieving key. Your Moodle site must be registered with Moodle.org to use this service. You may need to re-register to update your details there.'; +$string['keyretrievedsuccessfully'] = 'Key retrieved successfully'; +$string['nodevices'] = 'No registered devices. Devices will automatically register after you allow a Moodle iOS app to receive push notifications.'; $string['notconfigured'] = 'The Airnotifier server hasn\'t been configured so Airnotifier messages cannot be sent'; -$string['pluginname'] = 'iPhone/iPad'; +$string['pluginname'] = 'Mobile notifications'; +$string['sitemustberegistered'] = 'In order to use the public Airnotifier instance you must register your site with Moodle.org'; $string['showhide'] = 'Enable/disable the device.'; +$string['requestaccesskey'] = 'Request access key'; $string['unknowndevice'] = 'Unknown device'; diff --git a/message/output/airnotifier/lib.php b/message/output/airnotifier/lib.php deleted file mode 100755 index 57c2d41348d..00000000000 --- a/message/output/airnotifier/lib.php +++ /dev/null @@ -1,139 +0,0 @@ -. - -/** - * Airnotifier related functions - * - * @package message_airnotifier - * @category external - * @copyright 2012 Jerome Mouneyrac - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - * @since Moodle 2.4 - */ -class airnotifier_manager { - - /** - * Include the relevant javascript and language strings for the device - * toolbox YUI module - * - * @return bool - */ - function include_device_ajax() { - global $PAGE, $CFG; - - if (!$CFG->enableajax) { - return false; - } - - $config = new stdClass(); - - // The URL to use for resource changes - if (!isset($config->resturl)) { - $config->resturl = '/message/output/airnotifier/rest.php'; - } - - // Any additional parameters which need to be included on page submission - if (!isset($config->pageparams)) { - $config->pageparams = array(); - } - - // Include toolboxes - $PAGE->requires->yui_module('moodle-message_airnotifier-toolboxes', 'M.message.init_device_toolbox', array(array( - 'ajaxurl' => $config->resturl, - 'config' => $config, - )) - ); - - // Required strings for the javascript - $PAGE->requires->strings_for_js(array('deletecheckdevicename'), 'message_airnotifier'); - $PAGE->requires->strings_for_js(array('show','hide'), 'moodle'); - - return true; - } - - /** - * Add device to the user device list. - * - * @param object $device - * @param int $userid if empty take the current user - * @return int device id (in Moodle database) - */ - public function add_user_device($device, $userid = null) { - global $DB, $USER; - - if (empty($user)) { - $userid = $USER->id; - } - - $device = (object) $device; - - // Check if the device token already exist - Note that we look for user in case several users use the same device - $existingdevice = $DB->get_record('airnotifier_user_devices', array('appname' => $device->appname, - 'devicenotificationtoken' => $device->devicenotificationtoken, 'userid' => $userid)); - - // trim the data (some field should be in lowercase to make the search on them quickly) - $attributstolower = array('devicetype', 'deviceos', 'deviceosversion', 'devicebrand'); - if ($existingdevice) { - foreach ($device as $attributname => $value) { - if (in_array($attributname, $attributstolower)) { - $existingdevice->{$attributname} = strtolower($value); - } else { - $existingdevice->{$attributname} = $value; - } - } - } else { - foreach ($attributstolower as $attribut) { - if (!empty($device->{$attribut})) { - $device->{$attribut} = strtolower($device->{$attribut}); - } - } - } - - if ($existingdevice) { - $DB->update_record('airnotifier_user_devices', $existingdevice); - return $existingdevice->id; - } else { - return $DB->insert_record('airnotifier_user_devices', $device); - } - } - - /** - * Return the user devices for a specific app. - * - * @param string $appname the app name . - * @param int $userid if empty take the current user. - * @param int $enabled if 1 returned only enabled devices, if 0 only disabled devices, if null all devices. - * @return array all the devices - */ - public function get_user_devices($appname, $userid = null, $enabled = null) { - global $USER, $DB; - - if (empty($userid)) { - $userid = $USER->id; - } - - $params = array('appname' => $appname, 'userid' => $userid); - if ($enabled !== null) { - $params['enable'] = $enabled; - } - - return $DB->get_records('airnotifier_user_devices', $params); - } - -} - -?> diff --git a/message/output/airnotifier/message_output_airnotifier.php b/message/output/airnotifier/message_output_airnotifier.php index 202cfcaaf48..299e4ad6397 100755 --- a/message/output/airnotifier/message_output_airnotifier.php +++ b/message/output/airnotifier/message_output_airnotifier.php @@ -1,5 +1,4 @@ . +/** + * Airnotifier message processor to send messages to the APNS provider: airnotfier. (https://github.com/dongsheng/airnotifier) + * + * @package message_airnotifier + * @category external + * @copyright 2012 Jerome Mouneyrac + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @since Moodle 2.7 + */ + + require_once($CFG->dirroot . '/message/output/lib.php'); -require_once($CFG->dirroot . '/message/output/airnotifier/lib.php'); /** - * Airnotifier message processor to send messages to the APNS provider: airnotfier. - * (https://github.com/dongsheng/airnotifier) + * Message processor class * * @package message_airnotifier * @copyright 2012 Jerome Mouneyrac @@ -34,66 +42,76 @@ class message_output_airnotifier extends message_output { * @param stdClass $eventdata the event data submitted by the message sender plus $eventdata->savedmessageid * @return true if ok, false if error */ - function send_message($eventdata) { + public function send_message($eventdata) { global $CFG; if (!empty($CFG->noemailever)) { - // hidden setting for development sites, set in config.php if needed + // Hidden setting for development sites, set in config.php if needed. debugging('$CFG->noemailever active, no airnotifier message sent.', DEBUG_MINIMAL); return true; } - // skip any messaging suspended and deleted users - if ($eventdata->userto->auth === 'nologin' or $eventdata->userto->suspended or - $eventdata->userto->deleted) { + // Skip any messaging suspended and deleted users. + if ($eventdata->userto->auth === 'nologin' or + $eventdata->userto->suspended or + $eventdata->userto->deleted) { return true; } + // Site id, to map with Moodle Mobile stored sites. + $siteid = md5($CFG->wwwroot . $eventdata->userto->username); + // Mandatory notification data that need to be sent in the payload. They have variable length. // We need to take them in consideration to calculate the maximum message size. - $notificationdata = array("urlparams" => $eventdata->urlparams, "id" => $eventdata->savedmessageid, - "type" => $eventdata->component . '_' . $eventdata->name, "userfrom" => fullname($eventdata->userfrom)); + $notificationdata = array( + "site" => $siteid, + "type" => $eventdata->component . '_' . $eventdata->name, + "userfrom" => fullname($eventdata->userfrom)); // Calculate the size of the message knowing Apple payload must be lower than 256 bytes. // Airnotifier using few bytes of the payload, we must limit our message to even less characters. - $maxmsgsize = 205 - strlen(json_encode($notificationdata)); - $message = $eventdata->smallmessage; - // If the message size is too big make it shorter - if (strlen($message) >= $maxmsgsize) { + $maxmsgsize = 205 - core_text::strlen(json_encode($notificationdata)); + $message = s($eventdata->smallmessage); + // If the message size is too big make it shorter. + if (core_text::strlen($message) >= $maxmsgsize) { // Cut the message to the maximum possible size. -4 for the the ending 3 dots (...). - $message = substr($message, 0 , $maxmsgsize - 4); + $message = core_text::substr($message, 0 , $maxmsgsize - 4); // We need to check when the message is "escaped" then the message is not too long. - $encodedmsgsize = strlen(json_encode($message)); + $encodedmsgsize = core_text::strlen(json_encode($message)); if ($encodedmsgsize > $maxmsgsize) { - $totalescapedchar = $encodedmsgsize - strlen($message); - // Cut the message to the maximum possible size (taking the escaped character in account) - $message = substr($message, 0 , $maxmsgsize - 4 - $totalescapedchar); + $totalescapedchar = $encodedmsgsize - core_text::strlen($message); + // Cut the message to the maximum possible size (taking the escaped character in account). + $message = core_text::substr($message, 0 , $maxmsgsize - 4 - $totalescapedchar); } $message = $message . '...'; } - // We are sending to message to all devices - $airnotifiermanager = new airnotifier_manager(); - $devicetokens = $airnotifiermanager->get_user_devices($CFG->airnotifierappname, $eventdata->userto->id, true); + // We are sending to message to all devices. + $airnotifiermanager = new message_airnotifier_manager(); + $devicetokens = $airnotifiermanager->get_user_devices($CFG->airnotifiermobileappname, $eventdata->userto->id); foreach ($devicetokens as $devicetoken) { - // Sending the message to the device + if (!$devicetoken->enable) { + continue; + } + + // Sending the message to the device. $serverurl = $CFG->airnotifierurl . ':' . $CFG->airnotifierport . '/notification/'; $header = array('Accept: application/json', 'X-AN-APP-NAME: ' . $CFG->airnotifierappname, 'X-AN-APP-KEY: ' . $CFG->airnotifieraccesskey); $curl = new curl; $curl->setHeader($header); - $params = array('alert' => $message, - 'date' => $eventdata->timecreated, - 'urlparams' => json_encode($eventdata->urlparams), - 'type' => $eventdata->component . '_' . $eventdata->name, - 'userfrom' => fullname($eventdata->userfrom), - 'id' => $eventdata->savedmessageid, - 'token' => $devicetoken->devicenotificationtoken); + $params = array( + 'alert' => $message, + 'date' => $eventdata->timecreated, + 'site' => $siteid, + 'type' => $eventdata->component . '_' . $eventdata->name, + 'userfrom' => fullname($eventdata->userfrom), + 'token' => $devicetoken->pushid); $resp = $curl->post($serverurl, $params); } @@ -105,7 +123,7 @@ class message_output_airnotifier extends message_output { * * @param array $preferences An array of user preferences */ - function config_form($preferences) { + public function config_form($preferences) { global $CFG, $OUTPUT, $USER, $PAGE; if (!$this->is_system_configured()) { @@ -114,8 +132,8 @@ class message_output_airnotifier extends message_output { $PAGE->requires->css('/message/output/airnotifier/style.css'); - $airnotifiermanager = new airnotifier_manager(); - $devicetokens = $airnotifiermanager->get_user_devices($CFG->airnotifierappname, $USER->id); + $airnotifiermanager = new message_airnotifier_manager(); + $devicetokens = $airnotifiermanager->get_user_devices($CFG->airnotifiermobileappname, $USER->id); if (!empty($devicetokens)) { $output = ''; @@ -131,23 +149,19 @@ class message_output_airnotifier extends message_output { } $hideshowicon = $OUTPUT->pix_icon($hideshowiconname, get_string('showhide', 'message_airnotifier')); - $deleteicon = $OUTPUT->pix_icon('t/delete', get_string('deletedevice', 'message_airnotifier')); - $devicename = empty($devicetoken->devicename) ? get_string('unknowndevice', 'message_airnotifier') : s($devicetoken->devicename); + $name = "{$devicetoken->name} {$devicetoken->model} {$devicetoken->platform} {$devicetoken->version}"; $hideurl = new moodle_url('message/output/airnotifier/action.php', array('hide' => !$devicetoken->enable, 'deviceid' => $devicetoken->id, 'sesskey' => sesskey())); - $deleteurl = new moodle_url('message/output/airnotifier/action.php', - array('delete' => true, 'deviceid' => $devicetoken->id, - 'sesskey' => sesskey())); - $output .= html_writer::start_tag('li', array('id' => $devicetoken->id, 'class' => 'airnotifierdevice ' . $dimmed)) . "\n"; - $output .= html_writer::label($devicename, 'deviceid-' . $devicetoken->id, array('class' => 'devicelabel ')) . ' ' . - html_writer::link($hideurl, $hideshowicon, array('class' => 'hidedevice', 'alt' => 'show/hide')) . '' . - html_writer::link($deleteurl, $deleteicon, array('class' => 'deletedevice', 'alt' => 'delete')) . "\n"; + $output .= html_writer::start_tag('li', array('id' => $devicetoken->id, + 'class' => 'airnotifierdevice ' . $dimmed)) . "\n"; + $output .= html_writer::label($name, 'deviceid-' . $devicetoken->id, array('class' => 'devicelabel ')) . ' ' . + html_writer::link($hideurl, $hideshowicon, array('class' => 'hidedevice', 'alt' => 'show/hide')) . "\n"; $output .= html_writer::end_tag('li') . "\n"; } - // Include the AJAX script to automatically trigger the action + // Include the AJAX script to automatically trigger the action. $airnotifiermanager->include_device_ajax(); $output = html_writer::tag('ul', $output, array('id' => 'airnotifierdevices')); @@ -164,10 +178,8 @@ class message_output_airnotifier extends message_output { * @param stdClass $form preferences form class * @param array $preferences preferences array */ - function process_form($form, &$preferences) { - if (isset($form->airnotifier_devicetoken) && !empty($form->airnotifier_devicetoken)) { - $preferences['message_processor_airnotifier_devicetoken'] = $form->airnotifier_devicetoken; - } + public function process_form($form, &$preferences) { + return true; } /** @@ -176,20 +188,17 @@ class message_output_airnotifier extends message_output { * @param array $preferences preferences array * @param int $userid the user id */ - function load_data(&$preferences, $userid) { - $preferences->airnotifier_devicetoken = - get_user_preferences('message_processor_airnotifier_devicetoken', '', $userid); + public function load_data(&$preferences, $userid) { + return true; } /** * Tests whether the airnotifier settings have been configured * @return boolean true if airnotifier is configured */ - function is_system_configured() { - global $CFG; - return (!empty($CFG->airnotifierurl) && !empty($CFG->airnotifierport) && - !empty($CFG->airnotifieraccesskey) && !empty($CFG->airnotifierdeviceaccesskey) && - !empty($CFG->airnotifierappname)); + public function is_system_configured() { + $airnotifiermanager = new message_airnotifier_manager(); + return $airnotifiermanager->is_system_configured(); } } diff --git a/message/output/airnotifier/requestaccesskey.php b/message/output/airnotifier/requestaccesskey.php new file mode 100755 index 00000000000..96cf544a688 --- /dev/null +++ b/message/output/airnotifier/requestaccesskey.php @@ -0,0 +1,77 @@ +. + +/** + * Request access key to AirNotifier + * + * @package message_airnotifier + * @copyright 2012 Jerome Mouneyrac + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require('../../../config.php'); +require_once($CFG->dirroot . '/' . $CFG->admin . '/registration/lib.php'); + +define('AIRNOTIFIER_PUBLICURL', 'http://messages.moodle.net'); + +$PAGE->set_url(new moodle_url('/message/output/airnotifier/requestaccesskey.php')); +$PAGE->set_context(context_system::instance()); + +require_login(); +require_capability('moodle/site:config', context_system::instance()); + +$strheading = get_string('requestaccesskey', 'message_airnotifier'); +$PAGE->navbar->add(get_string('administrationsite')); +$PAGE->navbar->add(get_string('plugins', 'admin')); +$PAGE->navbar->add(get_string('messageoutputs', 'message')); +$returl = new moodle_url('/admin/settings.php', array('section' => 'messagesettingairnotifier')); +$PAGE->navbar->add(get_string('pluginname', 'message_airnotifier'), $returl); +$PAGE->navbar->add($strheading); + +$PAGE->set_heading($strheading); +$PAGE->set_title($strheading); + +$msg = ""; + +// If we are requesting a key to the official message system, verify first that this site is registered. +// This check is also done in Airnotifier. +if (strpos($CFG->airnotifierurl, AIRNOTIFIER_PUBLICURL) !== false ) { + $registrationmanager = new registration_manager(); + if (!$registrationmanager->get_registeredhub(HUB_MOODLEORGHUBURL)) { + $msg = get_string('sitemustberegistered', 'message_airnotifier'); + $msg .= $OUTPUT->continue_button($returl); + + echo $OUTPUT->header(); + echo $OUTPUT->box($msg, 'generalbox'); + echo $OUTPUT->footer(); + die; + } +} + +$manager = new message_airnotifier_manager(); + +if ($key = $manager->request_accesskey()) { + set_config('airnotifieraccesskey', $key); + $msg = get_string('keyretrievedsuccessfully', 'message_airnotifier'); +} else { + $msg = get_string('errorretrievingkey', 'message_airnotifier'); +} + +$msg .= $OUTPUT->continue_button($returl); + +echo $OUTPUT->header(); +echo $OUTPUT->box($msg, 'generalbox '); +echo $OUTPUT->footer(); diff --git a/message/output/airnotifier/rest.php b/message/output/airnotifier/rest.php index 982830648f9..9a981f8951b 100755 --- a/message/output/airnotifier/rest.php +++ b/message/output/airnotifier/rest.php @@ -1,5 +1,4 @@ dirroot.'/message/output/airnotifier/lib.php'); // Initialise ALL the incoming parameters here, up front. -$field = optional_param('field', '', PARAM_ALPHA); $id = required_param('id', PARAM_INT); -$pageaction = optional_param('action', '', PARAM_ALPHA); // Used to simulate a DELETE command +$enable = required_param('enable', PARAM_BOOL); + +require_login(); +require_sesskey(); $usercontext = context_user::instance($USER->id); $PAGE->set_url('/message/output/airnotifier/rest.php'); $PAGE->set_context($usercontext); -require_login(); -require_sesskey(); -echo $OUTPUT->header(); // send headers +require_capability('message/airnotifier:managedevice', $usercontext); -// OK, now let's process the parameters and do stuff -// MDL-10221 the DELETE method is not allowed on some web servers, so we simulate it with the action URL param -$requestmethod = $_SERVER['REQUEST_METHOD']; -if ($pageaction == 'DELETE') { - $requestmethod = 'DELETE'; -} +echo $OUTPUT->header(); -$device = $DB->get_record('airnotifier_user_devices', array('id' => $id), '*', MUST_EXIST); +$device = $DB->get_record('message_airnotifier_devices', array('id' => $id), '*', MUST_EXIST); -$airnotifiermanager = new airnotifier_manager(); +$device->enable = required_param('enable', PARAM_BOOL); +$DB->update_record('message_airnotifier_devices', $device); -switch($requestmethod) { - case 'POST': - switch ($field) { - case 'enable': - require_capability('message/airnotifier:managedevice', $usercontext); - $device->enable = required_param('enable', PARAM_BOOL); - $DB->update_record('airnotifier_user_devices', $device); - break; - } - break; - - case 'DELETE': - require_capability('message/airnotifier:managedevice', $usercontext); - $DB->delete_records('airnotifier_user_devices', array('id' => $id)); - break; -} diff --git a/message/output/airnotifier/settings.php b/message/output/airnotifier/settings.php index f9a8c0191d5..977a17be2c1 100755 --- a/message/output/airnotifier/settings.php +++ b/message/output/airnotifier/settings.php @@ -1,5 +1,4 @@ fulltree) { - //TODO: It will be better for these settings to be automatically retrieved by web service from moodle.org when enabling mobile. - // The processor should be enabled by the same enable mobile setting. + // The processor should be enabled by the same enable mobile setting. $settings->add(new admin_setting_configtext('airnotifierurl', get_string('airnotifierurl', 'message_airnotifier'), - get_string('configairnotifierurl', 'message_airnotifier'), 'http://notify.moodle.net', PARAM_URL)); + get_string('configairnotifierurl', 'message_airnotifier'), 'http://messages.moodle.net', PARAM_URL)); $settings->add(new admin_setting_configtext('airnotifierport', get_string('airnotifierport', 'message_airnotifier'), - get_string('configairnotifierport', 'message_airnotifier'), '8801', PARAM_INT)); - $settings->add(new admin_setting_configtext('airnotifieraccesskey', - get_string('airnotifieraccesskey', 'message_airnotifier'), - get_string('configairnotifieraccesskey', 'message_airnotifier'), '34220428981805c610cacc22aa6635fb', PARAM_ALPHANUMEXT)); - $settings->add(new admin_setting_configtext('airnotifierdeviceaccesskey', - get_string('airnotifierdeviceaccesskey', 'message_airnotifier'), - get_string('configairnotifierdeviceaccesskey', 'message_airnotifier'), 'e6b15c1ac0606980c46f87c476c9f28e', PARAM_ALPHANUMEXT)); + get_string('configairnotifierport', 'message_airnotifier'), '80', PARAM_INT)); + $settings->add(new admin_setting_configtext('airnotifiermobileappname', + get_string('airnotifiermobileappname', 'message_airnotifier'), + get_string('configairnotifiermobileappname', 'message_airnotifier'), 'com.moodle.moodlemobile', PARAM_TEXT)); $settings->add(new admin_setting_configtext('airnotifierappname', get_string('airnotifierappname', 'message_airnotifier'), - get_string('configairnotifierappname', 'message_airnotifier'), 'moodlehtml5app', PARAM_TEXT)); + get_string('configairnotifierappname', 'message_airnotifier'), 'commoodlemoodlemobile', PARAM_TEXT)); + $settings->add(new admin_setting_configtext('airnotifieraccesskey', + get_string('airnotifieraccesskey', 'message_airnotifier'), + get_string('configairnotifieraccesskey', 'message_airnotifier'), '', PARAM_ALPHANUMEXT)); + + $url = new moodle_url('/message/output/airnotifier/requestaccesskey.php'); + $link = html_writer::link($url, get_string('requestaccesskey', 'message_airnotifier')); + $settings->add(new admin_setting_heading('requestaccesskey', '', $link)); } diff --git a/message/output/airnotifier/style.css b/message/output/airnotifier/style.css index cb030449778..46b41d16a33 100755 --- a/message/output/airnotifier/style.css +++ b/message/output/airnotifier/style.css @@ -19,7 +19,7 @@ package message_airnotifier category css - copyright 2012 Jerome Mouneyrac + copyright 2012/2014 Jerome Mouneyrac / Juan Leyva license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ diff --git a/message/output/airnotifier/version.php b/message/output/airnotifier/version.php index 64545d1a2af..ef1b91cc05a 100755 --- a/message/output/airnotifier/version.php +++ b/message/output/airnotifier/version.php @@ -1,5 +1,4 @@ version = 2012070500.05; // The current plugin version (Date: YYYYMMDDXX) -$plugin->requires = 2012110101.01; // Requires this Moodle version -$plugin->component = 'message_airnotifier'; // Full name of the plugin (used for diagnostics) +$plugin->version = 2014012800; // The current plugin version (Date: YYYYMMDDXX). +$plugin->requires = 2013111800; // Requires this Moodle version. +$plugin->component = 'message_airnotifier'; // Full name of the plugin (used for diagnostics). diff --git a/message/output/airnotifier/yui/toolboxes/toolboxes.js b/message/output/airnotifier/yui/toolboxes/toolboxes.js index 8a6e824974c..a3650da9aba 100755 --- a/message/output/airnotifier/yui/toolboxes/toolboxes.js +++ b/message/output/airnotifier/yui/toolboxes/toolboxes.js @@ -3,7 +3,7 @@ YUI.add('moodle-message_airnotifier-toolboxes', function(Y) { 'pix':"i/loading_small", 'component':'moodle' }; - // The CSS selectors we use + // The CSS selectors we use. var CSS = { AIRNOTIFIERCONTENT : 'fieldset#messageprocessor_airnotifier', HIDEDEVICE : 'a.hidedevice', @@ -36,14 +36,13 @@ YUI.add('moodle-message_airnotifier-toolboxes', function(Y) { */ replace_button : function(toolboxtarget, selector, callback, cursor) { if (!cursor) { - // Set the default cursor type to pointer to match the - // anchor + // Set the default cursor type to pointer to match the anchor. cursor = 'pointer'; } var button = Y.one(toolboxtarget).all(selector) .setStyle('cursor', cursor); - // on isn't chainable and will return an event + // On isn't chainable and will return an event. button.on('click', callback, this); return button; @@ -70,10 +69,9 @@ YUI.add('moodle-message_airnotifier-toolboxes', function(Y) { value = 0; } - // Change the UI + // Change the UI. element.toggleClass(toggle_class); - // We need to toggle dimming on the description too - // element.all(CSS.CONTENTAFTERLINK).toggleClass(CSS.DIMMEDTEXT); + // We need to toggle dimming on the description too element.all(CSS.CONTENTAFTERLINK).toggleClass(CSS.DIMMEDTEXT);. var newstring = M.util.get_string(status, 'moodle'); hideicon.setAttrs({ 'alt' : newstring, @@ -81,7 +79,7 @@ YUI.add('moodle-message_airnotifier-toolboxes', function(Y) { 'src' : M.util.image_url('t/' + status) }); button.set('title', newstring); - button.set('className', 'editing_'+status); + button.set('className', 'editing_' + status); return value; }, @@ -98,7 +96,7 @@ YUI.add('moodle-message_airnotifier-toolboxes', function(Y) { if (!data) { data = {}; } - // Handle any variables which we must pass back through to + // Handle any variables which we must pass back through to. var pageparams = this.get('config').pageparams; for (varname in pageparams) { data[varname] = pageparams[varname]; @@ -108,7 +106,7 @@ YUI.add('moodle-message_airnotifier-toolboxes', function(Y) { var uri = M.cfg.wwwroot + this.get('ajaxurl'); - // Define the configuration to send with the request + // Define the configuration to send with the request. var responsetext = []; var config = { method: 'POST', @@ -138,7 +136,7 @@ YUI.add('moodle-message_airnotifier-toolboxes', function(Y) { sync: true } - // Apply optional config + // Apply optional config. if (optionalconfig) { for (varname in optionalconfig) { config[varname] = optionalconfig[varname]; @@ -149,7 +147,7 @@ YUI.add('moodle-message_airnotifier-toolboxes', function(Y) { statusspinner.show(); } - // Send the request + // Send the request. Y.io(uri, config); return responsetext; }, @@ -205,31 +203,31 @@ YUI.add('moodle-message_airnotifier-toolboxes', function(Y) { Y.all(baseselector).each(this._setup_for_device, this); }, _setup_for_device : function(toolboxtarget) { - // Delete + // Delete. this.replace_button(toolboxtarget, CSS.DELETEDEVICE, this.delete_device); - // Show/Hide + // Show/Hide. var showhide = this.replace_button(toolboxtarget, CSS.HIDEDEVICE, this.toggle_hide_device); }, delete_device : function(e) { - // Prevent the default button action + // Prevent the default button action. e.preventDefault(); - // Get the element we're working on + // Get the element we're working on. var element = e.target.ancestor(CSS.DEVICELI); var confirmstring = ''; var plugindata = { - name : element.one('*').getHTML() //get the label + name : element.one('*').getHTML() // Get the label. } confirmstring = M.util.get_string('deletecheckdevicename', 'message_airnotifier', plugindata); - // Confirm element removal + // Confirm element removal. if (!confirm(confirmstring)) { return false; } - // Actually remove the element + // Actually remove the element. element.remove(); var data = { 'class' : 'device', @@ -239,17 +237,17 @@ YUI.add('moodle-message_airnotifier-toolboxes', function(Y) { this.send_request(data); }, toggle_hide_device : function(e) { - // Prevent the default button action + // Prevent the default button action. e.preventDefault(); - // Get the element we're working on + // Get the element we're working on. var element = e.target.ancestor(CSS.DEVICELI); var button = e.target.ancestor('a', true); var value = this.toggle_hide_device_ui(button); - // Send the request + // Send the request. var data = { 'field' : 'enable', 'enable' : value, From a8f111aaf25bc50e783cb4b7aa30a91b0e68c091 Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Mon, 24 Mar 2014 15:44:35 +0100 Subject: [PATCH 3/9] MDL-44342 airnotifier: rest.php should return success information Javascript should handle properly errors, also, the eye and spinner should be synchronized with the remote operation --- message/output/airnotifier/externallib.php | 3 +- message/output/airnotifier/rest.php | 9 ++ .../airnotifier/yui/toolboxes/toolboxes.js | 86 +++++++------------ 3 files changed, 40 insertions(+), 58 deletions(-) diff --git a/message/output/airnotifier/externallib.php b/message/output/airnotifier/externallib.php index 3be2313e71e..cc95b89894f 100755 --- a/message/output/airnotifier/externallib.php +++ b/message/output/airnotifier/externallib.php @@ -165,7 +165,8 @@ class message_airnotifier_external extends external_api { new external_single_structure( array ( 'userid' => new external_value(PARAM_INT, 'userid id'), - 'configured' => new external_value(PARAM_INT, '1 is the user preferences have been configured and 0 if not') + 'configured' => new external_value(PARAM_INT, + '1 if the user preferences have been configured and 0 if not') ) ), 'list of preferences by user'), diff --git a/message/output/airnotifier/rest.php b/message/output/airnotifier/rest.php index 9a981f8951b..87e25ec7040 100755 --- a/message/output/airnotifier/rest.php +++ b/message/output/airnotifier/rest.php @@ -43,8 +43,17 @@ require_capability('message/airnotifier:managedevice', $usercontext); echo $OUTPUT->header(); +// Response class to be converted to json string. +$response = new stdClass(); + $device = $DB->get_record('message_airnotifier_devices', array('id' => $id), '*', MUST_EXIST); +// Check that the device belongs to the current user. +$userdevice = $DB->get_record('user_devices', array('id' => $device->userdeviceid, 'userid' => $USER->id), '*', MUST_EXIST); + $device->enable = required_param('enable', PARAM_BOOL); $DB->update_record('message_airnotifier_devices', $device); +$response->success = true; +echo json_encode($response); +die; \ No newline at end of file diff --git a/message/output/airnotifier/yui/toolboxes/toolboxes.js b/message/output/airnotifier/yui/toolboxes/toolboxes.js index a3650da9aba..aded3e7f157 100755 --- a/message/output/airnotifier/yui/toolboxes/toolboxes.js +++ b/message/output/airnotifier/yui/toolboxes/toolboxes.js @@ -7,7 +7,6 @@ YUI.add('moodle-message_airnotifier-toolboxes', function(Y) { var CSS = { AIRNOTIFIERCONTENT : 'fieldset#messageprocessor_airnotifier', HIDEDEVICE : 'a.hidedevice', - DELETEDEVICE : 'a.deletedevice', DEVICELI : 'li.airnotifierdevice', DIMCLASS : 'dimmed', DIMMEDTEXT : 'dimmed_text', @@ -56,17 +55,13 @@ YUI.add('moodle-message_airnotifier-toolboxes', function(Y) { var element = button.ancestor(CSS.DEVICELI); var hideicon = button.one('img'); - var dimarea; var toggle_class = CSS.DIMMEDTEXT; var status = ''; - var value; if (element.hasClass(toggle_class)) { status = 'hide'; - value = 1; } else { status = 'show'; - value = 0; } // Change the UI. @@ -80,18 +75,16 @@ YUI.add('moodle-message_airnotifier-toolboxes', function(Y) { }); button.set('title', newstring); button.set('className', 'editing_' + status); - - return value; }, /** * Send a request using the REST API * * @param data The data to submit * @param statusspinner (optional) A statusspinner which may contain a section loader - * @param optionalconfig (optional) Any additional configuration to submit + * @param callbacksuccess Call back on success * @return response responseText field from responce */ - send_request : function(data, statusspinner, optionalconfig) { + send_request : function(data, statusspinner, callbacksuccess) { // Default data structure if (!data) { data = {}; @@ -102,6 +95,10 @@ YUI.add('moodle-message_airnotifier-toolboxes', function(Y) { data[varname] = pageparams[varname]; } + if (statusspinner) { + statusspinner.show(); + } + data.sesskey = M.cfg.sesskey; var uri = M.cfg.wwwroot + this.get('ajaxurl'); @@ -116,35 +113,28 @@ YUI.add('moodle-message_airnotifier-toolboxes', function(Y) { try { responsetext = Y.JSON.parse(response.responseText); if (responsetext.error) { - new M.core.ajaxException(responsetext); + Y.use('moodle-core-notification-ajaxexception', function() { + return new M.core.ajaxException(responsetext).show(); + }); + } else if (responsetext.success) { + callbacksuccess(); } } catch (e) {} if (statusspinner) { - window.setTimeout(function(e) { - statusspinner.hide(); - }, 400); + statusspinner.hide(); } }, failure : function(tid, response) { if (statusspinner) { statusspinner.hide(); } - new M.core.ajaxException(response); + Y.use('moodle-core-notification-ajaxexception', function() { + return new M.core.ajaxException(response).show(); + }); } }, context: this, - sync: true - } - - // Apply optional config. - if (optionalconfig) { - for (varname in optionalconfig) { - config[varname] = optionalconfig[varname]; - } - } - - if (statusspinner) { - statusspinner.show(); + sync: false } // Send the request. @@ -203,39 +193,10 @@ YUI.add('moodle-message_airnotifier-toolboxes', function(Y) { Y.all(baseselector).each(this._setup_for_device, this); }, _setup_for_device : function(toolboxtarget) { - // Delete. - this.replace_button(toolboxtarget, CSS.DELETEDEVICE, this.delete_device); // Show/Hide. var showhide = this.replace_button(toolboxtarget, CSS.HIDEDEVICE, this.toggle_hide_device); }, - delete_device : function(e) { - // Prevent the default button action. - e.preventDefault(); - - // Get the element we're working on. - var element = e.target.ancestor(CSS.DEVICELI); - - var confirmstring = ''; - var plugindata = { - name : element.one('*').getHTML() // Get the label. - } - confirmstring = M.util.get_string('deletecheckdevicename', 'message_airnotifier', plugindata); - - // Confirm element removal. - if (!confirm(confirmstring)) { - return false; - } - - // Actually remove the element. - element.remove(); - var data = { - 'class' : 'device', - 'action' : 'DELETE', - 'id' : this.get_element_id(element) - }; - this.send_request(data); - }, toggle_hide_device : function(e) { // Prevent the default button action. e.preventDefault(); @@ -245,7 +206,13 @@ YUI.add('moodle-message_airnotifier-toolboxes', function(Y) { var button = e.target.ancestor('a', true); - var value = this.toggle_hide_device_ui(button); + var value; + // Enable the device in case the CSS is dimmed. + if (element.hasClass(CSS.DIMMEDTEXT)) { + value = 1; + } else { + value = 0; + } // Send the request. var data = { @@ -254,7 +221,12 @@ YUI.add('moodle-message_airnotifier-toolboxes', function(Y) { 'id' : this.get_element_id(element) }; var spinner = M.util.add_spinner(Y, element); - this.send_request(data, spinner); + + var context = this; + var callback = function() { + context.toggle_hide_device_ui(button); + }; + this.send_request(data, spinner, callback); } }, { NAME : 'message-device-toolbox', From 98fb4d413b621b325e2aebf5de91179e02b87973 Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Thu, 3 Apr 2014 15:13:56 +0200 Subject: [PATCH 4/9] MDL-44342 airnotifier: Add support for Android notifications --- .../output/airnotifier/classes/manager.php | 49 ++++++++----------- .../message_output_airnotifier.php | 8 +++ 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/message/output/airnotifier/classes/manager.php b/message/output/airnotifier/classes/manager.php index bbb7b0f77a5..e08b5cfdef3 100755 --- a/message/output/airnotifier/classes/manager.php +++ b/message/output/airnotifier/classes/manager.php @@ -24,6 +24,7 @@ * @since Moodle 2.7 */ +defined('MOODLE_INTERNAL') || die; /** * Airnotifier helper manager class @@ -47,16 +48,8 @@ class message_airnotifier_manager { } $config = new stdClass(); - - // The URL to use for resource changes. - if (!isset($config->resturl)) { - $config->resturl = '/message/output/airnotifier/rest.php'; - } - - // Any additional parameters which need to be included on page submission. - if (!isset($config->pageparams)) { - $config->pageparams = array(); - } + $config->resturl = '/message/output/airnotifier/rest.php'; + $config->pageparams = array(); // Include toolboxes. $PAGE->requires->yui_module('moodle-message_airnotifier-toolboxes', 'M.message.init_device_toolbox', array(array( @@ -92,28 +85,29 @@ class message_airnotifier_manager { // First, we look all the devices registered for this user in the Moodle core. // We are going to allow only ios devices (since these are the ones that supports PUSH notifications). - if ($userdevices = $DB->get_records('user_devices', $params)) { - foreach ($userdevices as $device) { - if (core_text::strtolower($device->platform) == 'ios') { - // Check if the device is known by airnotifier. - if (!$airnotifierdev = $DB->get_record('message_airnotifier_devices', array('userdeviceid' => $device->id))) { - // We have to create the device token in airnotifier. + $userdevices = $DB->get_records('user_devices', $params); + foreach ($userdevices as $device) { + if (core_text::strtolower($device->platform)) { + // Check if the device is known by airnotifier. + if (!$airnotifierdev = $DB->get_record('message_airnotifier_devices', + array('userdeviceid' => $device->id))) { - if (! $this->create_token($device->pushid)) { - continue; - } - - $airnotifierdev = new stdClass; - $airnotifierdev->userdeviceid = $device->id; - $airnotifierdev->enable = 1; - $airnotifierdev->id = $DB->insert_record('message_airnotifier_devices', $airnotifierdev); + // We have to create the device token in airnotifier. + if (! $this->create_token($device->pushid)) { + continue; } - $device->id = $airnotifierdev->id; - $device->enable = $airnotifierdev->enable; - $devices[] = $device; + + $airnotifierdev = new stdClass; + $airnotifierdev->userdeviceid = $device->id; + $airnotifierdev->enable = 1; + $airnotifierdev->id = $DB->insert_record('message_airnotifier_devices', $airnotifierdev); } + $device->id = $airnotifierdev->id; + $device->enable = $airnotifierdev->enable; + $devices[] = $device; } } + return $devices; } @@ -195,4 +189,3 @@ class message_airnotifier_manager { } } - diff --git a/message/output/airnotifier/message_output_airnotifier.php b/message/output/airnotifier/message_output_airnotifier.php index 299e4ad6397..bd0b9e908e5 100755 --- a/message/output/airnotifier/message_output_airnotifier.php +++ b/message/output/airnotifier/message_output_airnotifier.php @@ -63,9 +63,11 @@ class message_output_airnotifier extends message_output { // Mandatory notification data that need to be sent in the payload. They have variable length. // We need to take them in consideration to calculate the maximum message size. + // Since at this point we don't know the device, we use a 10 chars device platform. $notificationdata = array( "site" => $siteid, "type" => $eventdata->component . '_' . $eventdata->name, + "device" => "xxxxxxxxxx", "userfrom" => fullname($eventdata->userfrom)); // Calculate the size of the message knowing Apple payload must be lower than 256 bytes. @@ -111,6 +113,7 @@ class message_output_airnotifier extends message_output { 'site' => $siteid, 'type' => $eventdata->component . '_' . $eventdata->name, 'userfrom' => fullname($eventdata->userfrom), + 'device' => $devicetoken->platform, 'token' => $devicetoken->pushid); $resp = $curl->post($serverurl, $params); } @@ -126,6 +129,11 @@ class message_output_airnotifier extends message_output { public function config_form($preferences) { global $CFG, $OUTPUT, $USER, $PAGE; + $systemcontext = context_system::instance(); + if (!has_capability('message/airnotifier:managedevice', $systemcontext)) { + return get_string('nopermissiontomanagedevices', 'message_airnotifier'); + } + if (!$this->is_system_configured()) { return get_string('notconfigured', 'message_airnotifier'); } else { From 324facf4576902cdb307e3ea6a1c5fe29cbc6546 Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Thu, 3 Apr 2014 15:15:47 +0200 Subject: [PATCH 5/9] MDL-44342 airnotifier: Use system context instead user. Added missing capabilities and language strings --- lib/classes/plugin_manager.php | 2 +- message/output/airnotifier/db/access.php | 2 +- message/output/airnotifier/lang/en/message_airnotifier.php | 2 ++ message/output/airnotifier/rest.php | 6 +++--- message/output/airnotifier/yui/toolboxes/toolboxes.js | 2 +- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/classes/plugin_manager.php b/lib/classes/plugin_manager.php index d06464b6435..2e103b40192 100644 --- a/lib/classes/plugin_manager.php +++ b/lib/classes/plugin_manager.php @@ -1044,7 +1044,7 @@ class core_plugin_manager { ), 'message' => array( - 'email', 'jabber', 'popup' + 'airnotifier', 'email', 'jabber', 'popup' ), 'mnetservice' => array( diff --git a/message/output/airnotifier/db/access.php b/message/output/airnotifier/db/access.php index e890372b3b5..ac0fc11a0ab 100755 --- a/message/output/airnotifier/db/access.php +++ b/message/output/airnotifier/db/access.php @@ -29,7 +29,7 @@ $capabilities = array( 'message/airnotifier:managedevice' => array( 'captype' => 'write', - 'contextlevel' => CONTEXT_USER, + 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'user' => CAP_ALLOW ) diff --git a/message/output/airnotifier/lang/en/message_airnotifier.php b/message/output/airnotifier/lang/en/message_airnotifier.php index 2c8c5c019ad..4edc66d739c 100755 --- a/message/output/airnotifier/lang/en/message_airnotifier.php +++ b/message/output/airnotifier/lang/en/message_airnotifier.php @@ -38,9 +38,11 @@ $string['devicetoken'] = 'Device token'; $string['errorretrievingkey'] = 'An error occurred while retrieving key. Your Moodle site must be registered with Moodle.org to use this service. You may need to re-register to update your details there.'; $string['keyretrievedsuccessfully'] = 'Key retrieved successfully'; $string['nodevices'] = 'No registered devices. Devices will automatically register after you allow a Moodle iOS app to receive push notifications.'; +$string['nopermissiontomanagedevices'] = 'You don\'t have permission to manage devices.'; $string['notconfigured'] = 'The Airnotifier server hasn\'t been configured so Airnotifier messages cannot be sent'; $string['pluginname'] = 'Mobile notifications'; $string['sitemustberegistered'] = 'In order to use the public Airnotifier instance you must register your site with Moodle.org'; $string['showhide'] = 'Enable/disable the device.'; $string['requestaccesskey'] = 'Request access key'; $string['unknowndevice'] = 'Unknown device'; +$string['airnotifier:managedevice'] = 'Mange devices'; diff --git a/message/output/airnotifier/rest.php b/message/output/airnotifier/rest.php index 87e25ec7040..bf3712e5fe9 100755 --- a/message/output/airnotifier/rest.php +++ b/message/output/airnotifier/rest.php @@ -34,12 +34,12 @@ $enable = required_param('enable', PARAM_BOOL); require_login(); require_sesskey(); -$usercontext = context_user::instance($USER->id); +$systemcontext = context_system::instance(); $PAGE->set_url('/message/output/airnotifier/rest.php'); -$PAGE->set_context($usercontext); +$PAGE->set_context($systemcontext); -require_capability('message/airnotifier:managedevice', $usercontext); +require_capability('message/airnotifier:managedevice', $systemcontext); echo $OUTPUT->header(); diff --git a/message/output/airnotifier/yui/toolboxes/toolboxes.js b/message/output/airnotifier/yui/toolboxes/toolboxes.js index aded3e7f157..6d46fe90965 100755 --- a/message/output/airnotifier/yui/toolboxes/toolboxes.js +++ b/message/output/airnotifier/yui/toolboxes/toolboxes.js @@ -135,7 +135,7 @@ YUI.add('moodle-message_airnotifier-toolboxes', function(Y) { }, context: this, sync: false - } + }, // Send the request. Y.io(uri, config); From 1bcb18e7f9fff77eacd8bca150c8102943ece80d Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Thu, 3 Apr 2014 16:01:01 +0200 Subject: [PATCH 6/9] MDL-44342 airnotifier: Unit tests for external functions (web services) --- message/output/airnotifier/externallib.php | 19 +++- .../airnotifier/tests/externallib_test.php | 94 ++++++++++++++----- 2 files changed, 84 insertions(+), 29 deletions(-) diff --git a/message/output/airnotifier/externallib.php b/message/output/airnotifier/externallib.php index cc95b89894f..97abeb2ffc1 100755 --- a/message/output/airnotifier/externallib.php +++ b/message/output/airnotifier/externallib.php @@ -103,7 +103,10 @@ class message_airnotifier_external extends external_api { WHERE u.id $sqluserids"; $users = $DB->get_recordset_sql($usersql, $params); - $result = array(); + $result = array( + 'users' => array(), + 'warnings' => array() + ); $hasuserupdatecap = has_capability('moodle/user:update', context_system::instance()); foreach ($users as $user) { @@ -112,7 +115,12 @@ class message_airnotifier_external extends external_api { if ($currentuser or $hasuserupdatecap) { if (!empty($user->deleted)) { - $result['warnings'][] = "User $user->id was deleted"; + $warning = array(); + $warning['item'] = 'user'; + $warning['itemid'] = $user->id; + $warning['warningcode'] = '1'; + $warning['message'] = "User $user->id was deleted"; + $result['warnings'][] = $warning; continue; } @@ -143,7 +151,12 @@ class message_airnotifier_external extends external_api { $result['users'][] = $preferences; } else if (!$hasuserupdatecap) { - $result['warnings'][] = "You don't have permissions for view user $user->id preferences"; + $warning = array(); + $warning['item'] = 'user'; + $warning['itemid'] = $user->id; + $warning['warningcode'] = '2'; + $warning['message'] = "You don't have permissions for view user $user->id preferences"; + $result['warnings'][] = $warning; } } diff --git a/message/output/airnotifier/tests/externallib_test.php b/message/output/airnotifier/tests/externallib_test.php index 5ced13822c3..375f71f6b1f 100755 --- a/message/output/airnotifier/tests/externallib_test.php +++ b/message/output/airnotifier/tests/externallib_test.php @@ -27,6 +27,7 @@ defined('MOODLE_INTERNAL') || die(); global $CFG; +require_once($CFG->libdir . '/externallib.php'); require_once($CFG->dirroot . '/webservice/tests/helpers.php'); /** @@ -37,50 +38,91 @@ require_once($CFG->dirroot . '/webservice/tests/helpers.php'); * @copyright 2012 Jerome Mouneyrac * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class message_airnotifier_external_testcase extends externallib_testcase { +class message_airnotifier_external_testcase extends externallib_advanced_testcase { /** * Tests set up */ protected function setUp() { global $CFG; - require_once($CFG->dirroot . '/lib/externallib.php'); - require_once($CFG->dirroot . '/webservice/externallib.php'); + require_once($CFG->dirroot . '/message/output/airnotifier/externallib.php'); } /** - * Test add_user_device + * Test is_system_configured */ - public function test_add_user_device() { - - global $DB, $USER; + public function test_is_system_configured() { $this->resetAfterTest(true); $user = self::getDataGenerator()->create_user(); self::setUser($user); - $device = array(); - $device['appname'] = 'mymoodle'; - $device['devicename'] = 'Jerome\'s Android'; - $device['devicetype'] = 'galaxy nexus'; - $device['deviceos'] = 'android'; - $device['deviceosversion'] = '4.0.3'; - $device['devicebrand'] = 'samsung'; - $device['devicenotificationtoken'] = 'jhg576576sesgy98sd7g87sdg697sfg576df'; - $device['deviceuid'] = 'is87fs64g2vuf84g378gbh378ehg98h875'; + // In a clean installation, it should be not configured. + $configured = message_airnotifier_external::is_system_configured(); + $this->assertEquals(0, $configured); - $deviceid = message_airnotifier_external::add_user_device($device); + // Fake configuration. + set_config('airnotifieraccesskey', random_string()); + $configured = message_airnotifier_external::is_system_configured(); + $this->assertEquals(1, $configured); + } - $devicedb = $DB->get_record('user_devices', array('id' => $deviceid)); - $this->assertEquals($devicedb->devicename, $device['devicename']); - $this->assertEquals($devicedb->devicetype, $device['devicetype']); - $this->assertEquals($devicedb->deviceos, $device['deviceos']); - $this->assertEquals($devicedb->deviceosversion, $device['deviceosversion']); - $this->assertEquals($devicedb->devicebrand, $device['devicebrand']); - $this->assertEquals($devicedb->devicenotificationtoken, $device['devicenotificationtoken']); - $this->assertEquals($devicedb->deviceuid, $device['deviceuid']); - $this->assertEquals($devicedb->userid, $USER->id); + /** + * Test are_notification_preferences_configured + */ + public function test_are_notification_preferences_configured() { + + $this->resetAfterTest(true); + + $user1 = self::getDataGenerator()->create_user(); + $user2 = self::getDataGenerator()->create_user(); + $user3 = self::getDataGenerator()->create_user(); + + self::setUser($user1); + + set_user_preference('message_provider_moodle_instantmessage_loggedin', 'airnotifier', $user1); + set_user_preference('message_provider_moodle_instantmessage_loggedoff', 'airnotifier', $user1); + set_user_preference('message_provider_moodle_instantmessage_loggedin', 'airnotifier', $user2); + set_user_preference('message_provider_moodle_instantmessage_loggedin', 'airnotifier', $user3); + + $params = array($user1->id, $user2->id, $user3->id); + + $preferences = message_airnotifier_external::are_notification_preferences_configured($params); + + $expected = array( + array( + 'userid' => $user1->id, + 'configured' => 1 + ) + ); + + $this->assertEquals(1, count($preferences['users'])); + $this->assertEquals($expected, $preferences['users']); + $this->assertEquals(2, count($preferences['warnings'])); + + // Now, remove one user. + delete_user($user2); + $preferences = message_airnotifier_external::are_notification_preferences_configured($params); + $this->assertEquals(1, count($preferences['users'])); + $this->assertEquals($expected, $preferences['users']); + $this->assertEquals(2, count($preferences['warnings'])); + + // Now, remove one user1 preference (the user still has one prefernce for airnotifier). + unset_user_preference('message_provider_moodle_instantmessage_loggedin', $user1); + $preferences = message_airnotifier_external::are_notification_preferences_configured($params); + $this->assertEquals($expected, $preferences['users']); + + // Delete the last user1 preference. + unset_user_preference('message_provider_moodle_instantmessage_loggedoff', $user1); + $preferences = message_airnotifier_external::are_notification_preferences_configured($params); + $expected = array( + array( + 'userid' => $user1->id, + 'configured' => 0 + ) + ); + $this->assertEquals($expected, $preferences['users']); } } From b310aeecd4bc1bad0b941d2fb9b4fa70c185c0a3 Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Fri, 4 Apr 2014 11:26:21 +0200 Subject: [PATCH 7/9] MDL-44342 airnotifier: Add information about the message in the payload. In order to check if is a notification or just a private message --- message/output/airnotifier/message_output_airnotifier.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/message/output/airnotifier/message_output_airnotifier.php b/message/output/airnotifier/message_output_airnotifier.php index bd0b9e908e5..b1fe4cf5a6c 100755 --- a/message/output/airnotifier/message_output_airnotifier.php +++ b/message/output/airnotifier/message_output_airnotifier.php @@ -63,11 +63,11 @@ class message_output_airnotifier extends message_output { // Mandatory notification data that need to be sent in the payload. They have variable length. // We need to take them in consideration to calculate the maximum message size. - // Since at this point we don't know the device, we use a 10 chars device platform. $notificationdata = array( "site" => $siteid, "type" => $eventdata->component . '_' . $eventdata->name, - "device" => "xxxxxxxxxx", + "device" => "xxxxxxxxxx", // Since at this point we don't know the device, we use a 10 chars device platform. + "notif" => "x", // 1 or 0 wheter is a notification or not (it may be a private message). "userfrom" => fullname($eventdata->userfrom)); // Calculate the size of the message knowing Apple payload must be lower than 256 bytes. @@ -114,6 +114,7 @@ class message_output_airnotifier extends message_output { 'type' => $eventdata->component . '_' . $eventdata->name, 'userfrom' => fullname($eventdata->userfrom), 'device' => $devicetoken->platform, + 'notif' => (!empty($eventdata->notification)) ? '1' : '0', 'token' => $devicetoken->pushid); $resp = $curl->post($serverurl, $params); } From 438c7020c2c6f608c2a2fa11ab7a1c8732c8b759 Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Fri, 4 Apr 2014 11:37:39 +0200 Subject: [PATCH 8/9] MDL-44342 airnotifier: Include missing defined and required lib in externallib.php --- message/output/airnotifier/externallib.php | 3 +++ message/output/airnotifier/tests/externallib_test.php | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/message/output/airnotifier/externallib.php b/message/output/airnotifier/externallib.php index 97abeb2ffc1..b61f70457b8 100755 --- a/message/output/airnotifier/externallib.php +++ b/message/output/airnotifier/externallib.php @@ -24,6 +24,9 @@ * @since Moodle 2.7 */ +defined('MOODLE_INTERNAL') || die; + +require_once("$CFG->libdir/externallib.php"); /** * External API for airnotifier web services diff --git a/message/output/airnotifier/tests/externallib_test.php b/message/output/airnotifier/tests/externallib_test.php index 375f71f6b1f..906bc42a99c 100755 --- a/message/output/airnotifier/tests/externallib_test.php +++ b/message/output/airnotifier/tests/externallib_test.php @@ -27,7 +27,6 @@ defined('MOODLE_INTERNAL') || die(); global $CFG; -require_once($CFG->libdir . '/externallib.php'); require_once($CFG->dirroot . '/webservice/tests/helpers.php'); /** From d62c3c6ea8e7e5c0e4a8917cb4c7f23f054d8b96 Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Mon, 7 Apr 2014 11:52:10 +0200 Subject: [PATCH 9/9] MDL-44342 airnotifier: Add support for shifter in the YUI Module This commit includes also some integrator review related fixes --- message/output/airnotifier/db/install.php | 1 + message/output/airnotifier/externallib.php | 23 +- .../output/airnotifier/requestaccesskey.php | 1 + message/output/airnotifier/settings.php | 2 +- ...dle-message_airnotifier-toolboxes-debug.js | 252 ++++++++++++++++++ ...oodle-message_airnotifier-toolboxes-min.js | 1 + .../moodle-message_airnotifier-toolboxes.js | 252 ++++++++++++++++++ .../airnotifier/yui/src/toolboxes/build.json | 10 + .../yui/src/toolboxes/js/toolboxes.js | 247 +++++++++++++++++ .../yui/src/toolboxes/meta/toolboxes.json | 9 + .../airnotifier/yui/toolboxes/toolboxes.js | 247 ----------------- 11 files changed, 795 insertions(+), 250 deletions(-) create mode 100644 message/output/airnotifier/yui/build/moodle-message_airnotifier-toolboxes/moodle-message_airnotifier-toolboxes-debug.js create mode 100644 message/output/airnotifier/yui/build/moodle-message_airnotifier-toolboxes/moodle-message_airnotifier-toolboxes-min.js create mode 100644 message/output/airnotifier/yui/build/moodle-message_airnotifier-toolboxes/moodle-message_airnotifier-toolboxes.js create mode 100644 message/output/airnotifier/yui/src/toolboxes/build.json create mode 100755 message/output/airnotifier/yui/src/toolboxes/js/toolboxes.js create mode 100644 message/output/airnotifier/yui/src/toolboxes/meta/toolboxes.json delete mode 100755 message/output/airnotifier/yui/toolboxes/toolboxes.js diff --git a/message/output/airnotifier/db/install.php b/message/output/airnotifier/db/install.php index 433698b5fd1..81c0bf4d6d8 100755 --- a/message/output/airnotifier/db/install.php +++ b/message/output/airnotifier/db/install.php @@ -32,6 +32,7 @@ function xmldb_message_airnotifier_install() { $provider = new stdClass(); $provider->name = 'airnotifier'; + $provider->enabled = 0; $DB->insert_record('message_processors', $provider); return $result; diff --git a/message/output/airnotifier/externallib.php b/message/output/airnotifier/externallib.php index b61f70457b8..dbe89d59eb9 100755 --- a/message/output/airnotifier/externallib.php +++ b/message/output/airnotifier/externallib.php @@ -56,7 +56,15 @@ class message_airnotifier_external extends external_api { * @since Moodle 2.7 */ public static function is_system_configured() { + global $DB; + // First, check if the plugin is disabled. + $processor = $DB->get_record('message_processors', array('name' => 'airnotifier'), '*', MUST_EXIST); + if (!$processor->enabled) { + return 0; + } + + // Then, check if the plugin is completly configured. $manager = new message_airnotifier_manager(); return (int) $manager->is_system_configured(); } @@ -97,7 +105,7 @@ class message_airnotifier_external extends external_api { $params = self::validate_parameters(self::are_notification_preferences_configured_parameters(), array('userids' => $userids)); - list($sqluserids, $params) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED); + list($sqluserids, $params) = $DB->get_in_or_equal($params['userids'], SQL_PARAMS_NAMED); $uselect = ', ' . context_helper::get_preload_record_columns_sql('ctx'); $ujoin = "LEFT JOIN {context} ctx ON (ctx.instanceid = u.id AND ctx.contextlevel = :contextlevel)"; $params['contextlevel'] = CONTEXT_USER; @@ -134,8 +142,18 @@ class message_airnotifier_external extends external_api { // Now we get for all the providers and all the states // the user preferences to check if at least one is enabled for airnotifier plugin. $providers = message_get_providers_for_user($user->id); + $configured = false; + foreach ($providers as $provider) { + if ($configured) { + break; + } + foreach (array('loggedin', 'loggedoff') as $state) { + if ($configured) { + break; + } + $prefname = 'message_provider_'.$provider->component.'_'.$provider->name.'_'.$state; $linepref = get_user_preferences($prefname, '', $user->id); if ($linepref == '') { @@ -146,7 +164,8 @@ class message_airnotifier_external extends external_api { foreach ($lineprefarray as $pref) { if ($pref == 'airnotifier') { $preferences['configured'] = 1; - break 2; + $configured = true; + break; } } } diff --git a/message/output/airnotifier/requestaccesskey.php b/message/output/airnotifier/requestaccesskey.php index 96cf544a688..457832d97cc 100755 --- a/message/output/airnotifier/requestaccesskey.php +++ b/message/output/airnotifier/requestaccesskey.php @@ -31,6 +31,7 @@ $PAGE->set_url(new moodle_url('/message/output/airnotifier/requestaccesskey.php' $PAGE->set_context(context_system::instance()); require_login(); +require_sesskey(); require_capability('moodle/site:config', context_system::instance()); $strheading = get_string('requestaccesskey', 'message_airnotifier'); diff --git a/message/output/airnotifier/settings.php b/message/output/airnotifier/settings.php index 977a17be2c1..d788f75371c 100755 --- a/message/output/airnotifier/settings.php +++ b/message/output/airnotifier/settings.php @@ -41,7 +41,7 @@ if ($ADMIN->fulltree) { get_string('airnotifieraccesskey', 'message_airnotifier'), get_string('configairnotifieraccesskey', 'message_airnotifier'), '', PARAM_ALPHANUMEXT)); - $url = new moodle_url('/message/output/airnotifier/requestaccesskey.php'); + $url = new moodle_url('/message/output/airnotifier/requestaccesskey.php', array('sesskey' => sesskey())); $link = html_writer::link($url, get_string('requestaccesskey', 'message_airnotifier')); $settings->add(new admin_setting_heading('requestaccesskey', '', $link)); } diff --git a/message/output/airnotifier/yui/build/moodle-message_airnotifier-toolboxes/moodle-message_airnotifier-toolboxes-debug.js b/message/output/airnotifier/yui/build/moodle-message_airnotifier-toolboxes/moodle-message_airnotifier-toolboxes-debug.js new file mode 100644 index 00000000000..acd18da77eb --- /dev/null +++ b/message/output/airnotifier/yui/build/moodle-message_airnotifier-toolboxes/moodle-message_airnotifier-toolboxes-debug.js @@ -0,0 +1,252 @@ +YUI.add('moodle-message_airnotifier-toolboxes', function (Y, NAME) { + +/** + * Provides a tool for enabling/disabling elements using AJAX/REST. + * + * @module moodle-message_airnotifier-toolboxes + */ + +WAITICON = { + 'pix':"i/loading_small", + 'component':'moodle' +}; +// The CSS selectors we use. +var CSS = { + AIRNOTIFIERCONTENT : 'fieldset#messageprocessor_airnotifier', + HIDEDEVICE : 'a.hidedevice', + DEVICELI : 'li.airnotifierdevice', + DIMCLASS : 'dimmed', + DIMMEDTEXT : 'dimmed_text', + DEVICEIDPREFIX : 'deviceid-' +}; + +/** + * The toolbox classes + * + * TOOLBOX is a generic class which should never be directly instantiated + * DEVICETOOLBOX is a class extending TOOLBOX containing code specific to devices + */ +var TOOLBOX = function() { + TOOLBOX.superclass.constructor.apply(this, arguments); +}; + +Y.extend(TOOLBOX, Y.Base, { + /** + * Replace the button click at the selector with the specified + * callback + * + * @param toolboxtarget The selector of the working area + * @param selector The 'button' to replace + * @param callback The callback to apply + * @param cursor An optional cursor style to apply + */ + replace_button : function(toolboxtarget, selector, callback, cursor) { + if (!cursor) { + // Set the default cursor type to pointer to match the anchor. + cursor = 'pointer'; + } + var button = Y.one(toolboxtarget).all(selector) + .setStyle('cursor', cursor); + + // On isn't chainable and will return an event. + button.on('click', callback, this); + + return button; + }, + /** + * Toggle the visibility and availability for the specified + * device show/hide button + */ + toggle_hide_device_ui : function(button) { + + var element = button.ancestor(CSS.DEVICELI); + var hideicon = button.one('img'); + + var toggle_class = CSS.DIMMEDTEXT; + + var status = ''; + if (element.hasClass(toggle_class)) { + status = 'hide'; + } else { + status = 'show'; + } + + // Change the UI. + element.toggleClass(toggle_class); + // We need to toggle dimming on the description too element.all(CSS.CONTENTAFTERLINK).toggleClass(CSS.DIMMEDTEXT);. + var newstring = M.util.get_string(status, 'moodle'); + hideicon.setAttrs({ + 'alt' : newstring, + 'title' : newstring, + 'src' : M.util.image_url('t/' + status) + }); + button.set('title', newstring); + button.set('className', 'editing_' + status); + }, + /** + * Send a request using the REST API + * + * @param data The data to submit + * @param statusspinner (optional) A statusspinner which may contain a section loader + * @param callbacksuccess Call back on success + * @return response responseText field from responce + */ + send_request : function(data, statusspinner, callbacksuccess) { + // Default data structure + if (!data) { + data = {}; + } + // Handle any variables which we must pass back through to. + var pageparams = this.get('config').pageparams; + for (varname in pageparams) { + data[varname] = pageparams[varname]; + } + + if (statusspinner) { + statusspinner.show(); + } + + data.sesskey = M.cfg.sesskey; + + var uri = M.cfg.wwwroot + this.get('ajaxurl'); + + // Define the configuration to send with the request. + var responsetext = []; + var config = { + method: 'POST', + data: data, + on: { + success: function(tid, response) { + try { + responsetext = Y.JSON.parse(response.responseText); + if (responsetext.error) { + Y.use('moodle-core-notification-ajaxexception', function() { + return new M.core.ajaxException(responsetext).show(); + }); + } else if (responsetext.success) { + callbacksuccess(); + } + } catch (e) {} + if (statusspinner) { + statusspinner.hide(); + } + }, + failure : function(tid, response) { + if (statusspinner) { + statusspinner.hide(); + } + Y.use('moodle-core-notification-ajaxexception', function() { + return new M.core.ajaxException(response).show(); + }); + } + }, + context: this, + sync: false + }; + + // Send the request. + Y.io(uri, config); + return responsetext; + }, + /** + * Return the module ID for the specified element + * + * @param element The
  • element to determine a module-id number for + * @return string The module ID + */ + get_element_id : function(element) { + return element.get('id').replace(CSS.DEVICEIDPREFIX, ''); + } +}, +{ + NAME : 'device-toolbox', + ATTRS : { + ajaxurl : { + 'value' : 0 + }, + config : { + 'value' : 0 + } + } +} +); + +var DEVICETOOLBOX = function() { + DEVICETOOLBOX.superclass.constructor.apply(this, arguments); +}; + +Y.extend(DEVICETOOLBOX, TOOLBOX, { + + /** + * Initialize the device toolbox + * + * Updates all span.commands with relevant handlers and other required changes + */ + initializer : function(config) { + this.setup_for_device(); + }, + /** + * Update any span.commands within the scope of the specified + * selector with AJAX equivelants + * + * @param baseselector The selector to limit scope to + * @return void + */ + setup_for_device : function(baseselector) { + if (!baseselector) { + var baseselector = CSS.AIRNOTIFIERCONTENT; + } + + Y.all(baseselector).each(this._setup_for_device, this); + }, + _setup_for_device : function(toolboxtarget) { + + // Show/Hide. + var showhide = this.replace_button(toolboxtarget, CSS.HIDEDEVICE, this.toggle_hide_device); + }, + toggle_hide_device : function(e) { + // Prevent the default button action. + e.preventDefault(); + + // Get the element we're working on. + var element = e.target.ancestor(CSS.DEVICELI); + + var button = e.target.ancestor('a', true); + + var value; + // Enable the device in case the CSS is dimmed. + if (element.hasClass(CSS.DIMMEDTEXT)) { + value = 1; + } else { + value = 0; + } + + // Send the request. + var data = { + 'field' : 'enable', + 'enable' : value, + 'id' : this.get_element_id(element) + }; + var spinner = M.util.add_spinner(Y, element); + + var context = this; + var callback = function() { + context.toggle_hide_device_ui(button); + }; + this.send_request(data, spinner, callback); + } +}, { + NAME : 'message-device-toolbox', + ATTRS : { +} +}); + +M.message = M.message || {}; + +M.message.init_device_toolbox = function(config) { + return new DEVICETOOLBOX(config); +}; + + + +}, '@VERSION@', {"requires": ["base", "node", "io"]}); diff --git a/message/output/airnotifier/yui/build/moodle-message_airnotifier-toolboxes/moodle-message_airnotifier-toolboxes-min.js b/message/output/airnotifier/yui/build/moodle-message_airnotifier-toolboxes/moodle-message_airnotifier-toolboxes-min.js new file mode 100644 index 00000000000..63e59ca7052 --- /dev/null +++ b/message/output/airnotifier/yui/build/moodle-message_airnotifier-toolboxes/moodle-message_airnotifier-toolboxes-min.js @@ -0,0 +1 @@ +YUI.add("moodle-message_airnotifier-toolboxes",function(e,t){WAITICON={pix:"i/loading_small",component:"moodle"};var n={AIRNOTIFIERCONTENT:"fieldset#messageprocessor_airnotifier",HIDEDEVICE:"a.hidedevice",DEVICELI:"li.airnotifierdevice",DIMCLASS:"dimmed",DIMMEDTEXT:"dimmed_text",DEVICEIDPREFIX:"deviceid-"},r=function(){r.superclass.constructor.apply(this,arguments)};e.extend(r,e.Base,{replace_button:function(t,n,r,i){i||(i="pointer");var s=e.one(t).all(n).setStyle("cursor",i);return s.on("click",r,this),s},toggle_hide_device_ui:function(e){var t=e.ancestor(n.DEVICELI),r=e.one("img"),i=n.DIMMEDTEXT,s="";t.hasClass(i)?s="hide":s="show",t.toggleClass(i);var o=M.util.get_string(s,"moodle");r.setAttrs({alt:o,title:o,src:M.util.image_url("t/"+s)}),e.set("title",o),e.set("className","editing_"+s)},send_request:function(t,n,r){t||(t={});var i=this.get("config").pageparams;for(varname in i)t[varname]=i[varname];n&&n.show(),t.sesskey=M.cfg.sesskey;var s=M.cfg.wwwroot+this.get("ajaxurl"),o=[],u={method:"POST",data:t,on:{success:function(t,i){try{o=e.JSON.parse(i.responseText),o.error?e.use("moodle-core-notification-ajaxexception",function(){return(new M.core.ajaxException(o)).show()}):o.success&&r()}catch(s){}n&&n.hide()},failure:function(t,r){n&&n.hide(),e.use("moodle-core-notification-ajaxexception",function(){return(new M.core.ajaxException(r)).show()})}},context:this,sync:!1};return e.io(s,u),o},get_element_id:function(e){return e.get("id").replace(n.DEVICEIDPREFIX,"")}},{NAME:"device-toolbox",ATTRS:{ajaxurl:{value:0},config:{value:0}}});var i=function(){i.superclass.constructor.apply(this,arguments)};e.extend(i,r,{initializer:function(e){this.setup_for_device()},setup_for_device:function(t){if(!t)var t=n.AIRNOTIFIERCONTENT;e.all(t).each(this._setup_for_device,this)},_setup_for_device:function(e){var t=this.replace_button(e,n.HIDEDEVICE,this.toggle_hide_device)},toggle_hide_device:function(t){t.preventDefault();var r=t.target.ancestor(n.DEVICELI),i=t.target.ancestor("a",!0),s;r.hasClass(n.DIMMEDTEXT)?s=1:s=0;var o={field:"enable",enable:s,id:this.get_element_id(r)},u=M.util.add_spinner(e,r),a=this,f=function(){a.toggle_hide_device_ui(i)};this.send_request(o,u,f)}},{NAME:"message-device-toolbox",ATTRS:{}}),M.message=M.message||{},M.message.init_device_toolbox=function(e){return new i(e)}},"@VERSION@",{requires:["base","node","io"]}); diff --git a/message/output/airnotifier/yui/build/moodle-message_airnotifier-toolboxes/moodle-message_airnotifier-toolboxes.js b/message/output/airnotifier/yui/build/moodle-message_airnotifier-toolboxes/moodle-message_airnotifier-toolboxes.js new file mode 100644 index 00000000000..acd18da77eb --- /dev/null +++ b/message/output/airnotifier/yui/build/moodle-message_airnotifier-toolboxes/moodle-message_airnotifier-toolboxes.js @@ -0,0 +1,252 @@ +YUI.add('moodle-message_airnotifier-toolboxes', function (Y, NAME) { + +/** + * Provides a tool for enabling/disabling elements using AJAX/REST. + * + * @module moodle-message_airnotifier-toolboxes + */ + +WAITICON = { + 'pix':"i/loading_small", + 'component':'moodle' +}; +// The CSS selectors we use. +var CSS = { + AIRNOTIFIERCONTENT : 'fieldset#messageprocessor_airnotifier', + HIDEDEVICE : 'a.hidedevice', + DEVICELI : 'li.airnotifierdevice', + DIMCLASS : 'dimmed', + DIMMEDTEXT : 'dimmed_text', + DEVICEIDPREFIX : 'deviceid-' +}; + +/** + * The toolbox classes + * + * TOOLBOX is a generic class which should never be directly instantiated + * DEVICETOOLBOX is a class extending TOOLBOX containing code specific to devices + */ +var TOOLBOX = function() { + TOOLBOX.superclass.constructor.apply(this, arguments); +}; + +Y.extend(TOOLBOX, Y.Base, { + /** + * Replace the button click at the selector with the specified + * callback + * + * @param toolboxtarget The selector of the working area + * @param selector The 'button' to replace + * @param callback The callback to apply + * @param cursor An optional cursor style to apply + */ + replace_button : function(toolboxtarget, selector, callback, cursor) { + if (!cursor) { + // Set the default cursor type to pointer to match the anchor. + cursor = 'pointer'; + } + var button = Y.one(toolboxtarget).all(selector) + .setStyle('cursor', cursor); + + // On isn't chainable and will return an event. + button.on('click', callback, this); + + return button; + }, + /** + * Toggle the visibility and availability for the specified + * device show/hide button + */ + toggle_hide_device_ui : function(button) { + + var element = button.ancestor(CSS.DEVICELI); + var hideicon = button.one('img'); + + var toggle_class = CSS.DIMMEDTEXT; + + var status = ''; + if (element.hasClass(toggle_class)) { + status = 'hide'; + } else { + status = 'show'; + } + + // Change the UI. + element.toggleClass(toggle_class); + // We need to toggle dimming on the description too element.all(CSS.CONTENTAFTERLINK).toggleClass(CSS.DIMMEDTEXT);. + var newstring = M.util.get_string(status, 'moodle'); + hideicon.setAttrs({ + 'alt' : newstring, + 'title' : newstring, + 'src' : M.util.image_url('t/' + status) + }); + button.set('title', newstring); + button.set('className', 'editing_' + status); + }, + /** + * Send a request using the REST API + * + * @param data The data to submit + * @param statusspinner (optional) A statusspinner which may contain a section loader + * @param callbacksuccess Call back on success + * @return response responseText field from responce + */ + send_request : function(data, statusspinner, callbacksuccess) { + // Default data structure + if (!data) { + data = {}; + } + // Handle any variables which we must pass back through to. + var pageparams = this.get('config').pageparams; + for (varname in pageparams) { + data[varname] = pageparams[varname]; + } + + if (statusspinner) { + statusspinner.show(); + } + + data.sesskey = M.cfg.sesskey; + + var uri = M.cfg.wwwroot + this.get('ajaxurl'); + + // Define the configuration to send with the request. + var responsetext = []; + var config = { + method: 'POST', + data: data, + on: { + success: function(tid, response) { + try { + responsetext = Y.JSON.parse(response.responseText); + if (responsetext.error) { + Y.use('moodle-core-notification-ajaxexception', function() { + return new M.core.ajaxException(responsetext).show(); + }); + } else if (responsetext.success) { + callbacksuccess(); + } + } catch (e) {} + if (statusspinner) { + statusspinner.hide(); + } + }, + failure : function(tid, response) { + if (statusspinner) { + statusspinner.hide(); + } + Y.use('moodle-core-notification-ajaxexception', function() { + return new M.core.ajaxException(response).show(); + }); + } + }, + context: this, + sync: false + }; + + // Send the request. + Y.io(uri, config); + return responsetext; + }, + /** + * Return the module ID for the specified element + * + * @param element The
  • element to determine a module-id number for + * @return string The module ID + */ + get_element_id : function(element) { + return element.get('id').replace(CSS.DEVICEIDPREFIX, ''); + } +}, +{ + NAME : 'device-toolbox', + ATTRS : { + ajaxurl : { + 'value' : 0 + }, + config : { + 'value' : 0 + } + } +} +); + +var DEVICETOOLBOX = function() { + DEVICETOOLBOX.superclass.constructor.apply(this, arguments); +}; + +Y.extend(DEVICETOOLBOX, TOOLBOX, { + + /** + * Initialize the device toolbox + * + * Updates all span.commands with relevant handlers and other required changes + */ + initializer : function(config) { + this.setup_for_device(); + }, + /** + * Update any span.commands within the scope of the specified + * selector with AJAX equivelants + * + * @param baseselector The selector to limit scope to + * @return void + */ + setup_for_device : function(baseselector) { + if (!baseselector) { + var baseselector = CSS.AIRNOTIFIERCONTENT; + } + + Y.all(baseselector).each(this._setup_for_device, this); + }, + _setup_for_device : function(toolboxtarget) { + + // Show/Hide. + var showhide = this.replace_button(toolboxtarget, CSS.HIDEDEVICE, this.toggle_hide_device); + }, + toggle_hide_device : function(e) { + // Prevent the default button action. + e.preventDefault(); + + // Get the element we're working on. + var element = e.target.ancestor(CSS.DEVICELI); + + var button = e.target.ancestor('a', true); + + var value; + // Enable the device in case the CSS is dimmed. + if (element.hasClass(CSS.DIMMEDTEXT)) { + value = 1; + } else { + value = 0; + } + + // Send the request. + var data = { + 'field' : 'enable', + 'enable' : value, + 'id' : this.get_element_id(element) + }; + var spinner = M.util.add_spinner(Y, element); + + var context = this; + var callback = function() { + context.toggle_hide_device_ui(button); + }; + this.send_request(data, spinner, callback); + } +}, { + NAME : 'message-device-toolbox', + ATTRS : { +} +}); + +M.message = M.message || {}; + +M.message.init_device_toolbox = function(config) { + return new DEVICETOOLBOX(config); +}; + + + +}, '@VERSION@', {"requires": ["base", "node", "io"]}); diff --git a/message/output/airnotifier/yui/src/toolboxes/build.json b/message/output/airnotifier/yui/src/toolboxes/build.json new file mode 100644 index 00000000000..6a1b51af7a3 --- /dev/null +++ b/message/output/airnotifier/yui/src/toolboxes/build.json @@ -0,0 +1,10 @@ +{ + "name": "moodle-message_airnotifier-toolboxes", + "builds": { + "moodle-message_airnotifier-toolboxes": { + "jsfiles": [ + "toolboxes.js" + ] + } + } +} diff --git a/message/output/airnotifier/yui/src/toolboxes/js/toolboxes.js b/message/output/airnotifier/yui/src/toolboxes/js/toolboxes.js new file mode 100755 index 00000000000..fcb362a5c45 --- /dev/null +++ b/message/output/airnotifier/yui/src/toolboxes/js/toolboxes.js @@ -0,0 +1,247 @@ +/** + * Provides a tool for enabling/disabling elements using AJAX/REST. + * + * @module moodle-message_airnotifier-toolboxes + */ + +WAITICON = { + 'pix':"i/loading_small", + 'component':'moodle' +}; +// The CSS selectors we use. +var CSS = { + AIRNOTIFIERCONTENT : 'fieldset#messageprocessor_airnotifier', + HIDEDEVICE : 'a.hidedevice', + DEVICELI : 'li.airnotifierdevice', + DIMCLASS : 'dimmed', + DIMMEDTEXT : 'dimmed_text', + DEVICEIDPREFIX : 'deviceid-' +}; + +/** + * The toolbox classes + * + * TOOLBOX is a generic class which should never be directly instantiated + * DEVICETOOLBOX is a class extending TOOLBOX containing code specific to devices + */ +var TOOLBOX = function() { + TOOLBOX.superclass.constructor.apply(this, arguments); +}; + +Y.extend(TOOLBOX, Y.Base, { + /** + * Replace the button click at the selector with the specified + * callback + * + * @param toolboxtarget The selector of the working area + * @param selector The 'button' to replace + * @param callback The callback to apply + * @param cursor An optional cursor style to apply + */ + replace_button : function(toolboxtarget, selector, callback, cursor) { + if (!cursor) { + // Set the default cursor type to pointer to match the anchor. + cursor = 'pointer'; + } + var button = Y.one(toolboxtarget).all(selector) + .setStyle('cursor', cursor); + + // On isn't chainable and will return an event. + button.on('click', callback, this); + + return button; + }, + /** + * Toggle the visibility and availability for the specified + * device show/hide button + */ + toggle_hide_device_ui : function(button) { + + var element = button.ancestor(CSS.DEVICELI); + var hideicon = button.one('img'); + + var toggle_class = CSS.DIMMEDTEXT; + + var status = ''; + if (element.hasClass(toggle_class)) { + status = 'hide'; + } else { + status = 'show'; + } + + // Change the UI. + element.toggleClass(toggle_class); + // We need to toggle dimming on the description too element.all(CSS.CONTENTAFTERLINK).toggleClass(CSS.DIMMEDTEXT);. + var newstring = M.util.get_string(status, 'moodle'); + hideicon.setAttrs({ + 'alt' : newstring, + 'title' : newstring, + 'src' : M.util.image_url('t/' + status) + }); + button.set('title', newstring); + button.set('className', 'editing_' + status); + }, + /** + * Send a request using the REST API + * + * @param data The data to submit + * @param statusspinner (optional) A statusspinner which may contain a section loader + * @param callbacksuccess Call back on success + * @return response responseText field from responce + */ + send_request : function(data, statusspinner, callbacksuccess) { + // Default data structure + if (!data) { + data = {}; + } + // Handle any variables which we must pass back through to. + var pageparams = this.get('config').pageparams; + for (varname in pageparams) { + data[varname] = pageparams[varname]; + } + + if (statusspinner) { + statusspinner.show(); + } + + data.sesskey = M.cfg.sesskey; + + var uri = M.cfg.wwwroot + this.get('ajaxurl'); + + // Define the configuration to send with the request. + var responsetext = []; + var config = { + method: 'POST', + data: data, + on: { + success: function(tid, response) { + try { + responsetext = Y.JSON.parse(response.responseText); + if (responsetext.error) { + Y.use('moodle-core-notification-ajaxexception', function() { + return new M.core.ajaxException(responsetext).show(); + }); + } else if (responsetext.success) { + callbacksuccess(); + } + } catch (e) {} + if (statusspinner) { + statusspinner.hide(); + } + }, + failure : function(tid, response) { + if (statusspinner) { + statusspinner.hide(); + } + Y.use('moodle-core-notification-ajaxexception', function() { + return new M.core.ajaxException(response).show(); + }); + } + }, + context: this, + sync: false + }; + + // Send the request. + Y.io(uri, config); + return responsetext; + }, + /** + * Return the module ID for the specified element + * + * @param element The
  • element to determine a module-id number for + * @return string The module ID + */ + get_element_id : function(element) { + return element.get('id').replace(CSS.DEVICEIDPREFIX, ''); + } +}, +{ + NAME : 'device-toolbox', + ATTRS : { + ajaxurl : { + 'value' : 0 + }, + config : { + 'value' : 0 + } + } +} +); + +var DEVICETOOLBOX = function() { + DEVICETOOLBOX.superclass.constructor.apply(this, arguments); +}; + +Y.extend(DEVICETOOLBOX, TOOLBOX, { + + /** + * Initialize the device toolbox + * + * Updates all span.commands with relevant handlers and other required changes + */ + initializer : function(config) { + this.setup_for_device(); + }, + /** + * Update any span.commands within the scope of the specified + * selector with AJAX equivelants + * + * @param baseselector The selector to limit scope to + * @return void + */ + setup_for_device : function(baseselector) { + if (!baseselector) { + var baseselector = CSS.AIRNOTIFIERCONTENT; + } + + Y.all(baseselector).each(this._setup_for_device, this); + }, + _setup_for_device : function(toolboxtarget) { + + // Show/Hide. + var showhide = this.replace_button(toolboxtarget, CSS.HIDEDEVICE, this.toggle_hide_device); + }, + toggle_hide_device : function(e) { + // Prevent the default button action. + e.preventDefault(); + + // Get the element we're working on. + var element = e.target.ancestor(CSS.DEVICELI); + + var button = e.target.ancestor('a', true); + + var value; + // Enable the device in case the CSS is dimmed. + if (element.hasClass(CSS.DIMMEDTEXT)) { + value = 1; + } else { + value = 0; + } + + // Send the request. + var data = { + 'field' : 'enable', + 'enable' : value, + 'id' : this.get_element_id(element) + }; + var spinner = M.util.add_spinner(Y, element); + + var context = this; + var callback = function() { + context.toggle_hide_device_ui(button); + }; + this.send_request(data, spinner, callback); + } +}, { + NAME : 'message-device-toolbox', + ATTRS : { +} +}); + +M.message = M.message || {}; + +M.message.init_device_toolbox = function(config) { + return new DEVICETOOLBOX(config); +}; + diff --git a/message/output/airnotifier/yui/src/toolboxes/meta/toolboxes.json b/message/output/airnotifier/yui/src/toolboxes/meta/toolboxes.json new file mode 100644 index 00000000000..402d910c089 --- /dev/null +++ b/message/output/airnotifier/yui/src/toolboxes/meta/toolboxes.json @@ -0,0 +1,9 @@ +{ + "moodle-message_airnotifier-toolboxes": { + "requires": [ + "base", + "node", + "io" + ] + } +} diff --git a/message/output/airnotifier/yui/toolboxes/toolboxes.js b/message/output/airnotifier/yui/toolboxes/toolboxes.js deleted file mode 100755 index 6d46fe90965..00000000000 --- a/message/output/airnotifier/yui/toolboxes/toolboxes.js +++ /dev/null @@ -1,247 +0,0 @@ -YUI.add('moodle-message_airnotifier-toolboxes', function(Y) { - WAITICON = { - 'pix':"i/loading_small", - 'component':'moodle' - }; - // The CSS selectors we use. - var CSS = { - AIRNOTIFIERCONTENT : 'fieldset#messageprocessor_airnotifier', - HIDEDEVICE : 'a.hidedevice', - DEVICELI : 'li.airnotifierdevice', - DIMCLASS : 'dimmed', - DIMMEDTEXT : 'dimmed_text', - DEVICEIDPREFIX : 'deviceid-' - }; - - /** - * The toolbox classes - * - * TOOLBOX is a generic class which should never be directly instantiated - * DEVICETOOLBOX is a class extending TOOLBOX containing code specific to devices - */ - var TOOLBOX = function() { - TOOLBOX.superclass.constructor.apply(this, arguments); - } - - Y.extend(TOOLBOX, Y.Base, { - /** - * Replace the button click at the selector with the specified - * callback - * - * @param toolboxtarget The selector of the working area - * @param selector The 'button' to replace - * @param callback The callback to apply - * @param cursor An optional cursor style to apply - */ - replace_button : function(toolboxtarget, selector, callback, cursor) { - if (!cursor) { - // Set the default cursor type to pointer to match the anchor. - cursor = 'pointer'; - } - var button = Y.one(toolboxtarget).all(selector) - .setStyle('cursor', cursor); - - // On isn't chainable and will return an event. - button.on('click', callback, this); - - return button; - }, - /** - * Toggle the visibility and availability for the specified - * device show/hide button - */ - toggle_hide_device_ui : function(button) { - - var element = button.ancestor(CSS.DEVICELI); - var hideicon = button.one('img'); - - var toggle_class = CSS.DIMMEDTEXT; - - var status = ''; - if (element.hasClass(toggle_class)) { - status = 'hide'; - } else { - status = 'show'; - } - - // Change the UI. - element.toggleClass(toggle_class); - // We need to toggle dimming on the description too element.all(CSS.CONTENTAFTERLINK).toggleClass(CSS.DIMMEDTEXT);. - var newstring = M.util.get_string(status, 'moodle'); - hideicon.setAttrs({ - 'alt' : newstring, - 'title' : newstring, - 'src' : M.util.image_url('t/' + status) - }); - button.set('title', newstring); - button.set('className', 'editing_' + status); - }, - /** - * Send a request using the REST API - * - * @param data The data to submit - * @param statusspinner (optional) A statusspinner which may contain a section loader - * @param callbacksuccess Call back on success - * @return response responseText field from responce - */ - send_request : function(data, statusspinner, callbacksuccess) { - // Default data structure - if (!data) { - data = {}; - } - // Handle any variables which we must pass back through to. - var pageparams = this.get('config').pageparams; - for (varname in pageparams) { - data[varname] = pageparams[varname]; - } - - if (statusspinner) { - statusspinner.show(); - } - - data.sesskey = M.cfg.sesskey; - - var uri = M.cfg.wwwroot + this.get('ajaxurl'); - - // Define the configuration to send with the request. - var responsetext = []; - var config = { - method: 'POST', - data: data, - on: { - success: function(tid, response) { - try { - responsetext = Y.JSON.parse(response.responseText); - if (responsetext.error) { - Y.use('moodle-core-notification-ajaxexception', function() { - return new M.core.ajaxException(responsetext).show(); - }); - } else if (responsetext.success) { - callbacksuccess(); - } - } catch (e) {} - if (statusspinner) { - statusspinner.hide(); - } - }, - failure : function(tid, response) { - if (statusspinner) { - statusspinner.hide(); - } - Y.use('moodle-core-notification-ajaxexception', function() { - return new M.core.ajaxException(response).show(); - }); - } - }, - context: this, - sync: false - }, - - // Send the request. - Y.io(uri, config); - return responsetext; - }, - /** - * Return the module ID for the specified element - * - * @param element The
  • element to determine a module-id number for - * @return string The module ID - */ - get_element_id : function(element) { - return element.get('id').replace(CSS.DEVICEIDPREFIX, ''); - } - }, - { - NAME : 'device-toolbox', - ATTRS : { - ajaxurl : { - 'value' : 0 - }, - config : { - 'value' : 0 - } - } - } - ); - - var DEVICETOOLBOX = function() { - DEVICETOOLBOX.superclass.constructor.apply(this, arguments); - } - - Y.extend(DEVICETOOLBOX, TOOLBOX, { - - /** - * Initialize the device toolbox - * - * Updates all span.commands with relevant handlers and other required changes - */ - initializer : function(config) { - this.setup_for_device(); - }, - /** - * Update any span.commands within the scope of the specified - * selector with AJAX equivelants - * - * @param baseselector The selector to limit scope to - * @return void - */ - setup_for_device : function(baseselector) { - if (!baseselector) { - var baseselector = CSS.AIRNOTIFIERCONTENT; - } - - Y.all(baseselector).each(this._setup_for_device, this); - }, - _setup_for_device : function(toolboxtarget) { - - // Show/Hide. - var showhide = this.replace_button(toolboxtarget, CSS.HIDEDEVICE, this.toggle_hide_device); - }, - toggle_hide_device : function(e) { - // Prevent the default button action. - e.preventDefault(); - - // Get the element we're working on. - var element = e.target.ancestor(CSS.DEVICELI); - - var button = e.target.ancestor('a', true); - - var value; - // Enable the device in case the CSS is dimmed. - if (element.hasClass(CSS.DIMMEDTEXT)) { - value = 1; - } else { - value = 0; - } - - // Send the request. - var data = { - 'field' : 'enable', - 'enable' : value, - 'id' : this.get_element_id(element) - }; - var spinner = M.util.add_spinner(Y, element); - - var context = this; - var callback = function() { - context.toggle_hide_device_ui(button); - }; - this.send_request(data, spinner, callback); - } - }, { - NAME : 'message-device-toolbox', - ATTRS : { - } - }); - - M.message = M.message || {}; - - M.message.init_device_toolbox = function(config) { - return new DEVICETOOLBOX(config); - }; - -}, -'@VERSION@', { - requires : ['base', 'node', 'io'] -} -);