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'] +} +);