���ѧۧݧ�ӧ�� �ާ֧ߧ֧էا֧� - ���֧էѧܧ�ڧ��ӧѧ�� - /home3/cpr76684/public_html/airnotifier.tar
���ѧ٧ѧ�
requestaccesskey.php 0000644 00000010053 15152360230 0010634 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * 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'); $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'); $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, message_airnotifier_manager::AIRNOTIFIER_PUBLICURL) !== false ) { $adminrenderer = $PAGE->get_renderer('core', 'admin'); $msg = $adminrenderer->warn_if_not_registered(); if ($msg) { $msg .= html_writer::div(get_string('sitemustberegistered', 'message_airnotifier')); echo $OUTPUT->header(); echo $OUTPUT->box($msg, 'generalbox'); echo $OUTPUT->footer(); die; } } echo $OUTPUT->header(); $manager = new message_airnotifier_manager(); $warnings = []; if ($key = $manager->request_accesskey()) { set_config('airnotifieraccesskey', $key); $msg = $OUTPUT->box(get_string('keyretrievedsuccessfully', 'message_airnotifier'), 'generalbox alert alert-success'); // Check mobile notifications. $processors = get_message_processors(); $enabled = false; foreach ($processors as $processor => $status) { if ($processor == 'airnotifier' && $status->enabled) { $enabled = true; } } if (!$enabled) { // Airnotifier processor isn't enabled. Warn the user. $warnings[] = [ 'msg' => get_string('mobilenotificationsdisabledwarning', 'tool_mobile'), 'linkmsg' => get_string('enableprocessor', 'message_airnotifier'), 'linkurl' => new moodle_url('/admin/message.php'), ]; } if (empty($CFG->enablemobilewebservice)) { // Mobile web services not enabled. Warn the user. $warnings[] = [ 'msg' => get_string('mobilenotconfiguredwarning', 'admin'), 'linkmsg' => get_string('enablemobilewebservice', 'admin'), 'linkurl' => new moodle_url('/admin/settings.php', ['section' => 'mobilesettings']), ]; } } else { $msg = $OUTPUT->box(get_string('errorretrievingkey', 'message_airnotifier'), 'generalbox alert alert-danger'); } // Display the warnings. foreach ($warnings as $warning) { if (!empty($warning['linkurl'])) { $warning['msg'] = $warning['msg'] . ' ' . html_writer::tag('a', $warning['linkmsg'], ['href' => $warning['linkurl']]); } $msg .= $OUTPUT->box($warning['msg'], 'generalbox alert alert-warning'); } $msg .= $OUTPUT->continue_button($returl); echo $OUTPUT->box($msg, 'generalbox '); echo $OUTPUT->footer(); db/install.php 0000644 00000002246 15152360230 0007311 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * 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 $CFG, $DB; $result = true; $provider = new stdClass(); $provider->name = 'airnotifier'; $DB->insert_record('message_processors', $provider); return $result; } db/services.php 0000644 00000005352 15152360230 0007467 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * 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_is_system_configured' => array( 'classname' => 'message_airnotifier_external', 'methodname' => 'is_system_configured', 'classpath' => 'message/output/airnotifier/externallib.php', 'description' => 'Check whether the airnotifier settings have been configured', 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'message_airnotifier_are_notification_preferences_configured' => array( 'classname' => 'message_airnotifier_external', 'methodname' => 'are_notification_preferences_configured', 'classpath' => 'message/output/airnotifier/externallib.php', 'description' => 'Check if the users have notification preferences configured yet', 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'message_airnotifier_get_user_devices' => array( 'classname' => 'message_airnotifier_external', 'methodname' => 'get_user_devices', 'classpath' => 'message/output/airnotifier/externallib.php', 'description' => 'Return the list of mobile devices that are registered in Moodle for the given user', 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'message_airnotifier_enable_device' => array( 'classname' => 'message_airnotifier_external', 'methodname' => 'enable_device', 'classpath' => 'message/output/airnotifier/externallib.php', 'description' => 'Enables or disables a registered user device so it can receive Push notifications', 'type' => 'write', 'capabilities' => 'message/airnotifier:managedevice', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), ); db/access.php 0000644 00000002224 15152360230 0007100 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * 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_SYSTEM, 'archetypes' => array( 'user' => CAP_ALLOW ) ) ); db/install.xml 0000644 00000002075 15152360230 0007322 0 ustar 00 <?xml version="1.0" encoding="UTF-8" ?> <XMLDB PATH="message/output/airnotifier/db" VERSION="20140129" COMMENT="XMLDB file for Moodle message/output/airnotifier" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../lib/xmldb/xmldb.xsd" > <TABLES> <TABLE NAME="message_airnotifier_devices" COMMENT="Store information about the devices registered in Airnotifier for PUSH notifications"> <FIELDS> <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/> <FIELD NAME="userdeviceid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="The user device id in the user_devices table"/> <FIELD NAME="enable" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="1" SEQUENCE="false" COMMENT="The user can enable/disable his devices"/> </FIELDS> <KEYS> <KEY NAME="primary" TYPE="primary" FIELDS="id"/> <KEY NAME="userdeviceid" TYPE="foreign-unique" FIELDS="userdeviceid" REFTABLE="user_devices" REFFIELDS="id"/> </KEYS> </TABLE> </TABLES> </XMLDB> rest.php 0000644 00000003213 15152360230 0006226 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * 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 */ define('AJAX_SCRIPT', true); require_once(__DIR__ . '/../../../config.php'); // Initialise ALL the incoming parameters here, up front. $id = required_param('id', PARAM_INT); $enable = required_param('enable', PARAM_BOOL); require_login(); require_sesskey(); $systemcontext = context_system::instance(); $PAGE->set_url('/message/output/airnotifier/rest.php'); $PAGE->set_context($systemcontext); require_capability('message/airnotifier:managedevice', $systemcontext); echo $OUTPUT->header(); // Response class to be converted to json string. $response = new stdClass(); if (!message_airnotifier_manager::enable_device($id, $enable)) { throw new moodle_exception('unknowndevice', 'message_airnotifier'); } $response->success = true; echo json_encode($response); die; tests/privacy/provider_test.php 0000644 00000022674 15152360230 0012775 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * Base class for unit tests for message_airnotifier. * * @package message_airnotifier * @copyright 2018 Adrian Greeve <adrian@moodle.com> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace message_airnotifier\privacy; defined('MOODLE_INTERNAL') || die(); use core_privacy\tests\provider_testcase; use message_airnotifier\privacy\provider; use core_privacy\local\request\approved_userlist; /** * Unit tests for message\output\airnotifier\classes\privacy\provider.php * * @copyright 2018 Adrian Greeve <adrian@moodle.com> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class provider_test extends provider_testcase { /** * Basic setup for these tests. */ public function setUp(): void { $this->resetAfterTest(true); } /** * / * @param object $user User object * @param string $pushid unique string */ protected function add_device($user, $pushid) { global $DB; // Add fake core device. $device = array( 'appid' => 'com.moodle.moodlemobile', 'name' => 'occam', 'model' => 'Nexus 4', 'platform' => 'Android', 'version' => '4.2.2', 'pushid' => $pushid, 'uuid' => 'asdnfl348qlksfaasef859', 'userid' => $user->id, 'timecreated' => time(), 'timemodified' => time(), ); $coredeviceid = $DB->insert_record('user_devices', (object) $device); $airnotifierdev = array( 'userdeviceid' => $coredeviceid, 'enable' => 1 ); $airnotifierdevid = $DB->insert_record('message_airnotifier_devices', (object) $airnotifierdev); } /** * Test returning metadata. */ public function test_get_metadata() { $collection = new \core_privacy\local\metadata\collection('message_airnotifier'); $collection = \message_airnotifier\privacy\provider::get_metadata($collection); $this->assertNotEmpty($collection); } /** * Test getting the context for the user ID related to this plugin. */ public function test_get_contexts_for_userid() { $user = $this->getDataGenerator()->create_user(); $context = \context_user::instance($user->id); $this->add_device($user, 'apuJih874kj'); $this->add_device($user, 'bdu09Ikjjsu'); $contextlist = \message_airnotifier\privacy\provider::get_contexts_for_userid($user->id); $this->assertEquals($context->id, $contextlist->current()->id); } /** * Test that data is exported correctly for this plugin. */ public function test_export_user_data() { $user = $this->getDataGenerator()->create_user(); $context = \context_user::instance($user->id); $this->add_device($user, 'apuJih874kj'); $this->add_device($user, 'bdu09Ikjjsu'); $writer = \core_privacy\local\request\writer::with_context($context); $this->assertFalse($writer->has_any_data()); $this->export_context_data_for_user($user->id, $context, 'message_airnotifier'); // First device. $data = $writer->get_data([get_string('privacy:subcontext', 'message_airnotifier'), 'Nexus 4_apuJih874kj']); $this->assertEquals('com.moodle.moodlemobile', $data->appid); // Second device. $data = $writer->get_data([get_string('privacy:subcontext', 'message_airnotifier'), 'Nexus 4_bdu09Ikjjsu']); $this->assertEquals('bdu09Ikjjsu', $data->pushid); } /** * Test that user data is deleted using the context. */ public function test_delete_data_for_all_users_in_context() { global $DB; $user = $this->getDataGenerator()->create_user(); $context = \context_user::instance($user->id); $this->add_device($user, 'apuJih874kj'); // Check that we have an entry. $devices = $DB->get_records('message_airnotifier_devices'); $this->assertCount(1, $devices); \message_airnotifier\privacy\provider::delete_data_for_all_users_in_context($context); // Check that it has now been deleted. $devices = $DB->get_records('message_airnotifier_devices'); $this->assertCount(0, $devices); } /** * Test that user data is deleted for this user. */ public function test_delete_data_for_user() { global $DB; $user = $this->getDataGenerator()->create_user(); $context = \context_user::instance($user->id); $this->add_device($user, 'apuJih874kj'); // Check that we have an entry. $devices = $DB->get_records('message_airnotifier_devices'); $this->assertCount(1, $devices); $approvedlist = new \core_privacy\local\request\approved_contextlist($user, 'message_airnotifier', [$context->id]); \message_airnotifier\privacy\provider::delete_data_for_user($approvedlist); // Check that it has now been deleted. $devices = $DB->get_records('message_airnotifier_devices'); $this->assertCount(0, $devices); } /** * Test that only users with a user context are fetched. */ public function test_get_users_in_context() { $component = 'message_airnotifier'; // Create user. $user = $this->getDataGenerator()->create_user(); $usercontext = \context_user::instance($user->id); // The lists of users for the user context should be empty. // Related user data have not been created yet. $userlist = new \core_privacy\local\request\userlist($usercontext, $component); provider::get_users_in_context($userlist); $this->assertCount(0, $userlist); $this->add_device($user, 'apuJih874kj'); $this->add_device($user, 'bdu09Ikjjsu'); // The list of users for userlist should return one user (user). provider::get_users_in_context($userlist); $this->assertCount(1, $userlist); $expected = [$user->id]; $actual = $userlist->get_userids(); $this->assertEquals($expected, $actual); // The list of users should only return users in the user context. $systemcontext = \context_system::instance(); $userlist1 = new \core_privacy\local\request\userlist($systemcontext, $component); provider::get_users_in_context($userlist1); $this->assertCount(0, $userlist1); } /** * Test that data for users in approved userlist is deleted. */ public function test_delete_data_for_users() { $component = 'message_airnotifier'; // Create user1. $user1 = $this->getDataGenerator()->create_user(); $usercontext1 = \context_user::instance($user1->id); // Create user2. $user2 = $this->getDataGenerator()->create_user(); $usercontext2 = \context_user::instance($user2->id); $this->add_device($user1, 'apuJih874kj'); $this->add_device($user1, 'cpuJih874kp'); $this->add_device($user2, 'bdu09Ikjjsu'); // The list of users for usercontext1 should return one user (user1). $userlist1 = new \core_privacy\local\request\userlist($usercontext1, $component); provider::get_users_in_context($userlist1); $this->assertCount(1, $userlist1); // The list of users for usercontext2 should return one user (user2). $userlist2 = new \core_privacy\local\request\userlist($usercontext2, $component); provider::get_users_in_context($userlist2); $this->assertCount(1, $userlist2); $approvedlist = new approved_userlist($usercontext1, $component, $userlist1->get_userids()); // Delete using delete_data_for_user. provider::delete_data_for_users($approvedlist); // Re-fetch users in usercontext1 - the user data should now be empty. $userlist1 = new \core_privacy\local\request\userlist($usercontext1, $component); provider::get_users_in_context($userlist1); $this->assertCount(0, $userlist1); // The list of users for usercontext2 should still return one user (user2). $userlist2 = new \core_privacy\local\request\userlist($usercontext2, $component); provider::get_users_in_context($userlist2); $this->assertCount(1, $userlist2); // User data should only be removed in the user context. $systemcontext = \context_system::instance(); $approvedlist = new approved_userlist($systemcontext, $component, $userlist2->get_userids()); // Delete using delete_data_for_user. provider::delete_data_for_users($approvedlist); // Re-fetch users in usercontext2 - the user data should still be present. $userlist2 = new \core_privacy\local\request\userlist($usercontext2, $component); provider::get_users_in_context($userlist2); $this->assertCount(1, $userlist2); } } tests/externallib_test.php 0000644 00000020201 15152360230 0011757 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. namespace message_airnotifier; use externallib_advanced_testcase; use message_airnotifier_external; 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 externallib_test extends externallib_advanced_testcase { /** * Tests set up */ protected function setUp(): void { global $CFG; require_once($CFG->dirroot . '/message/output/airnotifier/externallib.php'); } /** * Test is_system_configured */ public function test_is_system_configured() { global $DB; $this->resetAfterTest(true); $user = self::getDataGenerator()->create_user(); self::setUser($user); // In a clean installation, it should be not configured. $configured = message_airnotifier_external::is_system_configured(); $configured = \external_api::clean_returnvalue(message_airnotifier_external::is_system_configured_returns(), $configured); $this->assertEquals(0, $configured); // Fake configuration. set_config('airnotifieraccesskey', random_string()); // Enable the plugin. $DB->set_field('message_processors', 'enabled', 1, array('name' => 'airnotifier')); $configured = message_airnotifier_external::is_system_configured(); $configured = \external_api::clean_returnvalue(message_airnotifier_external::is_system_configured_returns(), $configured); $this->assertEquals(1, $configured); } /** * 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_enabled', 'airnotifier', $user1); set_user_preference('message_provider_moodle_instantmessage_enabled', 'airnotifier', $user2); $params = array($user1->id, $user2->id, $user3->id); $preferences = message_airnotifier_external::are_notification_preferences_configured($params); $returnsdescription = message_airnotifier_external::are_notification_preferences_configured_returns(); $preferences = \external_api::clean_returnvalue($returnsdescription, $preferences); $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); $preferences = \external_api::clean_returnvalue($returnsdescription, $preferences); $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 preference for airnotifier). unset_user_preference('message_provider_moodle_instantmessage_enabled', $user1); $preferences = message_airnotifier_external::are_notification_preferences_configured($params); $preferences = \external_api::clean_returnvalue($returnsdescription, $preferences); $this->assertEquals($expected, $preferences['users']); } /** * Test get_user_devices */ public function test_get_user_devices() { global $CFG, $DB; require_once($CFG->dirroot . '/user/externallib.php'); $this->resetAfterTest(true); $this->setAdminUser(); // System not configured. $devices = message_airnotifier_external::get_user_devices(''); $devices = \external_api::clean_returnvalue(message_airnotifier_external::get_user_devices_returns(), $devices); $this->assertCount(1, $devices['warnings']); $this->assertEquals('systemnotconfigured', $devices['warnings'][0]['warningcode']); // Fake configuration. set_config('airnotifieraccesskey', random_string()); // Enable the plugin. $DB->set_field('message_processors', 'enabled', 1, array('name' => 'airnotifier')); // Get devices. $devices = message_airnotifier_external::get_user_devices(''); $devices = \external_api::clean_returnvalue(message_airnotifier_external::get_user_devices_returns(), $devices); $this->assertCount(0, $devices['warnings']); // No devices, unfortunatelly we cannot create devices (we can't mock airnotifier server). $this->assertCount(0, $devices['devices']); } /** * Test get_user_devices permissions */ public function test_get_user_devices_permissions() { global $CFG, $DB; require_once($CFG->dirroot . '/user/externallib.php'); $this->resetAfterTest(true); $user = self::getDataGenerator()->create_user(); $otheruser = self::getDataGenerator()->create_user(); self::setUser($user); // No permission to get other users devices. $this->expectException('required_capability_exception'); $devices = message_airnotifier_external::get_user_devices('', $otheruser->id); } /** * Test enable_device */ public function test_enable_device() { global $USER, $DB; $this->resetAfterTest(true); $this->setAdminUser(); // Add fake core device. $device = array( 'appid' => 'com.moodle.moodlemobile', 'name' => 'occam', 'model' => 'Nexus 4', 'platform' => 'Android', 'version' => '4.2.2', 'pushid' => 'apushdkasdfj4835', 'uuid' => 'asdnfl348qlksfaasef859', 'userid' => $USER->id, 'timecreated' => time(), 'timemodified' => time(), ); $coredeviceid = $DB->insert_record('user_devices', (object) $device); $airnotifierdev = array( 'userdeviceid' => $coredeviceid, 'enable' => 1 ); $airnotifierdevid = $DB->insert_record('message_airnotifier_devices', (object) $airnotifierdev); // Disable and enable. $result = message_airnotifier_external::enable_device($airnotifierdevid, false); $result = \external_api::clean_returnvalue(message_airnotifier_external::enable_device_returns(), $result); $this->assertCount(0, $result['warnings']); $this->assertTrue($result['success']); $this->assertEquals(0, $DB->get_field('message_airnotifier_devices', 'enable', array('id' => $airnotifierdevid))); $result = message_airnotifier_external::enable_device($airnotifierdevid, true); $result = \external_api::clean_returnvalue(message_airnotifier_external::enable_device_returns(), $result); $this->assertCount(0, $result['warnings']); $this->assertTrue($result['success']); $this->assertEquals(1, $DB->get_field('message_airnotifier_devices', 'enable', array('id' => $airnotifierdevid))); } } tests/manager_test.php 0000644 00000014162 15152360230 0011071 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. namespace message_airnotifier; use message_airnotifier_manager; /** * Unit tests for message_airnotifier_manager. * * @package message_airnotifier * @category test * @copyright 2020 Juan Leyva <juan@moodle.com> * @license http://www.gnu.org/copyleft/gpl.html GNU Public License */ class manager_test extends \advanced_testcase { /** Test check_configuration by default **/ public function test_check_configuration_default() { global $CFG; $this->resetAfterTest(true); $manager = new message_airnotifier_manager(); // Mock server responses. $CFG->airnotifierurl = 'localhost'; \curl::mock_response(json_encode(['error' => 'Invalid access key'])); // Mock request to check access key. $checks = $manager->check_configuration(); $this->assertEquals(\core\check\result::OK, $checks[0]->get_status()); // Mobile service enabled. $this->assertEquals(\core\check\result::OK, $checks[1]->get_status()); // Message output not disabled in config.php. $this->assertEquals(\core\check\result::OK, $checks[2]->get_status()); // Mobile notifications enabled. $this->assertEquals(\core\check\result::ERROR, $checks[3]->get_status()); // Airnotifier NOT configured, missing key. $this->assertEquals(\core\check\result::OK, $checks[4]->get_status()); // Airnotifier URL available. $this->assertEquals(\core\check\result::ERROR, $checks[5]->get_status()); // Missing access key. $this->assertEquals(\core\check\result::ERROR, $checks[7]->get_status()); // No registered devices yet. } /** Test check_configuration with token **/ public function test_check_configuration_with_token() { global $CFG; $this->resetAfterTest(true); $manager = new message_airnotifier_manager(); // Mock server responses. $CFG->airnotifierurl = 'localhost'; \curl::mock_response(json_encode(['status' => 'ok'])); // Mock first request to check URL. \curl::mock_response(json_encode(['error' => 'Invalid access key'])); // Mock second request to check acces key. $CFG->airnotifieraccesskey = 'test'; // For enabling Airnotifier. $checks = $manager->check_configuration(); $this->assertEquals(\core\check\result::OK, $checks[0]->get_status()); // Mobile service enabled. $this->assertEquals(\core\check\result::OK, $checks[1]->get_status()); // Message output not disabled in config.php. $this->assertEquals(\core\check\result::OK, $checks[2]->get_status()); // Mobile notifications enabled. $this->assertEquals(\core\check\result::OK, $checks[3]->get_status()); // Airnotifier configured. $this->assertEquals(\core\check\result::OK, $checks[4]->get_status()); // Airnotifier URL available. // The original function fourth check (access key valid in the remote Airnotifier server) is not mockable. $this->assertEquals(\core\check\result::ERROR, $checks[6]->get_status()); // No registered devices yet. } /** Test check_configuration bad settings **/ public function test_check_configuration_incorrect_settings() { global $CFG; $this->resetAfterTest(true); $manager = new message_airnotifier_manager(); // Mock server responses. $CFG->airnotifierurl = 'localhost'; \curl::mock_response(json_encode(['status' => 'ok'])); // Mock first request to check URL. \curl::mock_response(json_encode(['error' => 'Invalid access key'])); // Mock second request to check acces key. $CFG->airnotifieraccesskey = 'test'; // For enabling Airnotifier. $CFG->airnotifierappname .= ' '; $CFG->noemailever = true; $checks = $manager->check_configuration(); $this->assertEquals(\core\check\result::OK, $checks[0]->get_status()); // Mobile service enabled. $this->assertEquals(\core\check\result::CRITICAL, $checks[1]->get_status()); // Message output disabled in config.php. $this->assertEquals(\core\check\result::OK, $checks[2]->get_status()); // Mobile notifications enabled. $this->assertEquals(\core\check\result::OK, $checks[3]->get_status()); // Airnotifier configured. $this->assertEquals(\core\check\result::ERROR, $checks[4]->get_status()); // Airnotifier URL available. $this->assertEquals(\core\check\result::OK, $checks[5]->get_status()); // Invalid setting (empty space). // The original function fifth check (access key valid in the remote Airnotifier server) is not mockable. $this->assertEquals(\core\check\result::ERROR, $checks[7]->get_status()); // No registered devices yet. } /** Test has_enabled_devices **/ public function test_has_enabled_devices() { global $CFG, $DB, $USER; $this->resetAfterTest(true); $CFG->airnotifieraccesskey = 'test'; // For mocking the request. $manager = new message_airnotifier_manager(); // No devices yet for current user. $this->assertFalse($manager->has_enabled_devices($CFG->airnotifiermobileappname)); // Add devices. \curl::mock_response(json_encode(['status' => 'ok'])); $DB->insert_record('user_devices', ['userid' => $USER->id, 'appid' => $CFG->airnotifiermobileappname, 'platform' => 'ios', 'timecreated' => time(), 'timemodified' => time()]); $this->assertTrue($manager->has_enabled_devices($CFG->airnotifiermobileappname)); } } message_output_airnotifier.php 0000644 00000021507 15152360230 0012716 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * Airnotifier message processor to send messages to the APNS provider: airnotfier. (https://github.com/dcai/airnotifier) * * @package message_airnotifier * @category external * @copyright 2012 Jerome Mouneyrac <jerome@moodle.com> * @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'); /** * Message processor class * * @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 */ public function send_message($eventdata) { global $CFG, $DB; require_once($CFG->libdir . '/filelib.php'); 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; } // If username is empty we try to retrieve it, since it's required to generate the siteid. if (empty($eventdata->userto->username)) { $eventdata->userto->username = $DB->get_field('user', 'username', array('id' => $eventdata->userto->id)); } // Site id, to map with Moodle Mobile stored sites. $siteid = md5($CFG->wwwroot . $eventdata->userto->username); // Airnotifier can handle custom requests using processors (that are Airnotifier plugins). // In the extra parameter we indicate which processor to use and also additional data to be handled by the processor. // Here we clone the eventdata object because will be deleting/adding attributes. $extra = clone $eventdata; // Delete attributes that may content private information. if (!empty($eventdata->userfrom)) { $extra->userfromid = $eventdata->userfrom->id; $extra->userfromfullname = fullname($eventdata->userfrom); unset($extra->userfrom); } $extra->usertoid = $eventdata->userto->id; unset($extra->userto); $extra->processor = 'moodle'; $extra->site = $siteid; $extra->date = (!empty($eventdata->timecreated)) ? $eventdata->timecreated : time(); $extra->notification = (!empty($eventdata->notification)) ? 1 : 0; // Site name. $site = get_site(); $extra->sitefullname = clean_param(format_string($site->fullname), PARAM_NOTAGS); $extra->siteshortname = clean_param(format_string($site->shortname), PARAM_NOTAGS); // Clean HTML, push notifications must arrive clean. if (!empty($extra->smallmessage)) { $extra->smallmessage = clean_param($extra->smallmessage, PARAM_NOTAGS); } if (!empty($extra->fullmessage)) { $extra->fullmessage = clean_param($extra->fullmessage, PARAM_NOTAGS); } if (!empty($extra->fullmessagehtml)) { $extra->fullmessagehtml = clean_param($extra->fullmessagehtml, PARAM_NOTAGS); } // Send wwwroot to airnotifier. $extra->wwwroot = $CFG->wwwroot; // 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) { if (!$devicetoken->enable) { continue; } // Sending the message to the device. $serverurl = $CFG->airnotifierurl . ':' . $CFG->airnotifierport . '/api/v2/push/'; $header = array('Accept: application/json', 'X-AN-APP-NAME: ' . $CFG->airnotifierappname, 'X-AN-APP-KEY: ' . $CFG->airnotifieraccesskey); $curl = new curl; // Push notifications are supposed to be instant, do not wait to long blocking the execution. $curl->setopt(array('CURLOPT_TIMEOUT' => 2, 'CURLOPT_CONNECTTIMEOUT' => 2)); $curl->setHeader($header); $params = array( 'device' => $devicetoken->platform, 'token' => $devicetoken->pushid, 'extra' => $extra ); // JSON POST raw body request. $resp = $curl->post($serverurl, json_encode($params)); } return true; } /** * Creates necessary fields in the messaging config form. * * @param array $preferences An array of user preferences */ 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 { $airnotifiermanager = new message_airnotifier_manager(); $devicetokens = $airnotifiermanager->get_user_devices($CFG->airnotifiermobileappname, $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')); $name = "{$devicetoken->name} {$devicetoken->model} {$devicetoken->platform} {$devicetoken->version}"; $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('#', $hideshowicon, array('class' => 'hidedevice', 'alt' => 'show/hide')) . "\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('class' => 'list-unstyled unstyled', '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 */ public function process_form($form, &$preferences) { return true; } /** * 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 */ public function load_data(&$preferences, $userid) { return true; } /** * Tests whether the airnotifier settings have been configured * @return boolean true if airnotifier is configured */ public function is_system_configured() { $airnotifiermanager = new message_airnotifier_manager(); return $airnotifiermanager->is_system_configured(); } } settings.php 0000644 00000006373 15152360230 0007123 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * Airnotifier configuration page * * @package message_airnotifier * @copyright 2012 Jerome Mouneyrac, 2014 Juan Leyva * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die; if ($ADMIN->fulltree) { $notify = new \core\output\notification( get_string('moodleappsportallimitswarning', 'message_airnotifier', (new moodle_url('https://apps.moodle.com'))->out()), \core\output\notification::NOTIFY_WARNING); $settings->add(new admin_setting_heading('tool_mobile/moodleappsportalfeaturesappearance', '', $OUTPUT->render($notify))); // 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'), message_airnotifier_manager::AIRNOTIFIER_PUBLICURL, PARAM_URL)); $settings->add(new admin_setting_configtext('airnotifierport', get_string('airnotifierport', 'message_airnotifier'), get_string('configairnotifierport', 'message_airnotifier'), 443, 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'), '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', array('sesskey' => sesskey())); $link = html_writer::link($url, get_string('requestaccesskey', 'message_airnotifier')); $settings->add(new admin_setting_heading('requestaccesskey', '', $link)); // Check configuration. $url = new moodle_url('/message/output/airnotifier/checkconfiguration.php'); $link = html_writer::link($url, get_string('checkconfiguration', 'message_airnotifier')); $settings->add(new admin_setting_heading('checkconfiguration', '', $link)); } externallib.php 0000644 00000033220 15152360230 0007563 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * External functions * * @package message_airnotifier * @category external * @copyright 2012 Jerome Mouneyrac <jerome@moodle.com> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 2.7 */ defined('MOODLE_INTERNAL') || die; require_once("$CFG->libdir/externallib.php"); /** * External API for airnotifier web services * * @package message_airnotifier * @category external * @copyright 2012 Jerome Mouneyrac <jerome@moodle.com> * @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 method parameters * * @since Moodle 2.7 */ 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() { 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(); } /** * 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( 'userids' => new external_multiple_structure(new external_value(PARAM_INT, 'user ID')), ) ); } /** * Check if the users have notification preferences configured for the airnotifier plugin * * @param array $userids Array of user ids * @since Moodle 2.7 */ public static function are_notification_preferences_configured($userids) { global $CFG, $USER, $DB; require_once($CFG->dirroot . '/message/lib.php'); $params = self::validate_parameters(self::are_notification_preferences_configured_parameters(), array('userids' => $userids)); 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; $usersql = "SELECT u.* $uselect FROM {user} u $ujoin WHERE u.id $sqluserids"; $users = $DB->get_recordset_sql($usersql, $params); $result = array( 'users' => [], 'warnings' => [] ); $hasuserupdatecap = has_capability('moodle/user:update', context_system::instance()); foreach ($users as $user) { $currentuser = ($user->id == $USER->id); if ($currentuser or $hasuserupdatecap) { if (!empty($user->deleted)) { $warning = []; $warning['item'] = 'user'; $warning['itemid'] = $user->id; $warning['warningcode'] = '1'; $warning['message'] = "User $user->id was deleted"; $result['warnings'][] = $warning; continue; } $preferences = []; $preferences['userid'] = $user->id; $preferences['configured'] = 0; // 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; } $prefstocheck = []; $prefname = 'message_provider_'.$provider->component.'_'.$provider->name.'_enabled'; // First get forced settings. if ($forcedpref = get_config('message', $prefname)) { $prefstocheck = array_merge($prefstocheck, explode(',', $forcedpref)); } // Then get user settings. if ($userpref = get_user_preferences($prefname, '', $user->id)) { $prefstocheck = array_merge($prefstocheck, explode(',', $userpref)); } if (in_array('airnotifier', $prefstocheck)) { $preferences['configured'] = 1; $configured = true; break; } } $result['users'][] = $preferences; } else if (!$hasuserupdatecap) { $warning = []; $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; } } $users->close(); return $result; } /** * Returns description of method result value * * @return external_single_structure * @since Moodle 2.7 */ 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 if the user preferences have been configured and 0 if not') ) ), 'list of preferences by user'), 'warnings' => new external_warnings() ) ); } /** * Returns description of method parameters * * @since Moodle 3.2 */ public static function get_user_devices_parameters() { return new external_function_parameters( array( 'appid' => new external_value(PARAM_NOTAGS, 'App unique id (usually a reversed domain)'), 'userid' => new external_value(PARAM_INT, 'User id, 0 for current user', VALUE_DEFAULT, 0) ) ); } /** * Return the list of mobile devices that are registered in Moodle for the given user. * * @param string $appid app unique id (usually a reversed domain) * @param integer $userid the user id, 0 for current user * @return array warnings and devices * @throws moodle_exception * @since Moodle 3.2 */ public static function get_user_devices($appid, $userid = 0) { global $USER; $params = self::validate_parameters( self::get_user_devices_parameters(), array( 'appid' => $appid, 'userid' => $userid, ) ); $context = context_system::instance(); self::validate_context($context); if (empty($params['userid'])) { $user = $USER; } else { $user = core_user::get_user($params['userid'], '*', MUST_EXIST); core_user::require_active_user($user); // Allow only admins to retrieve other users devices. if ($user->id != $USER->id) { require_capability('moodle/site:config', $context); } } $warnings = array(); $devices = array(); // Check if mobile notifications are enabled. if (!self::is_system_configured()) { $warnings[] = array( 'item' => 'user', 'itemid' => $user->id, 'warningcode' => 'systemnotconfigured', 'message' => 'Mobile notifications are not configured' ); } else { // We catch exceptions here because get_user_devices may try to connect to Airnotifier. try { $manager = new message_airnotifier_manager(); $devices = $manager->get_user_devices($appid, $user->id); } catch (Exception $e) { $warnings[] = array( 'item' => 'user', 'itemid' => $user->id, 'warningcode' => 'errorgettingdevices', 'message' => $e->getMessage() ); } } return array( 'devices' => $devices, 'warnings' => $warnings ); } /** * Returns description of method result value * * @return external_single_structure * @since Moodle 3.2 */ public static function get_user_devices_returns() { return new external_single_structure( array( 'devices' => new external_multiple_structure( new external_single_structure( array ( 'id' => new external_value(PARAM_INT, 'Device id (in the message_airnotifier table)'), 'appid' => new external_value(PARAM_NOTAGS, 'The app id, something like com.moodle.moodlemobile'), 'name' => new external_value(PARAM_NOTAGS, 'The device name, \'occam\' or \'iPhone\' etc.'), 'model' => new external_value(PARAM_NOTAGS, 'The device model \'Nexus4\' or \'iPad1,1\' etc.'), 'platform' => new external_value(PARAM_NOTAGS, 'The device platform \'iOS\' or \'Android\' etc.'), 'version' => new external_value(PARAM_NOTAGS, 'The device version \'6.1.2\' or \'4.2.2\' etc.'), 'pushid' => new external_value(PARAM_RAW, 'The device PUSH token/key/identifier/registration id'), 'uuid' => new external_value(PARAM_RAW, 'The device UUID'), 'enable' => new external_value(PARAM_INT, 'Whether the device is enabled or not'), 'timecreated' => new external_value(PARAM_INT, 'Time created'), 'timemodified' => new external_value(PARAM_INT, 'Time modified'), ) ), 'List of devices' ), 'warnings' => new external_warnings() ) ); } /** * Returns description of method parameters * * @since Moodle 3.2 */ public static function enable_device_parameters() { return new external_function_parameters( array( 'deviceid' => new external_value(PARAM_INT, 'The device id'), 'enable' => new external_value(PARAM_BOOL, 'True for enable the device, false otherwise') ) ); } /** * Enables or disables a registered user device so it can receive Push notifications * * @param integer $deviceid the device id * @param bool $enable whether to enable the device * @return array warnings and success status * @throws moodle_exception * @since Moodle 3.2 */ public static function enable_device($deviceid, $enable) { global $USER; $params = self::validate_parameters( self::enable_device_parameters(), array( 'deviceid' => $deviceid, 'enable' => $enable, ) ); $context = context_system::instance(); self::validate_context($context); require_capability('message/airnotifier:managedevice', $context); if (!message_airnotifier_manager::enable_device($params['deviceid'], $params['enable'])) { throw new moodle_exception('unknowndevice', 'message_airnotifier'); } return array( 'success' => true, 'warnings' => array() ); } /** * Returns description of method result value * * @return external_single_structure * @since Moodle 3.2 */ public static function enable_device_returns() { return new external_single_structure( array( 'success' => new external_value(PARAM_BOOL, 'True if success'), 'warnings' => new external_warnings() ) ); } } checkconfiguration.php 0000644 00000007557 15152360230 0011135 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * Check and test Push notifications configuration. * * @package message_airnotifier * @copyright 2020 Juan Leyva * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ require('../../../config.php'); require_once($CFG->libdir . '/filelib.php'); $pageurl = new moodle_url('/message/output/airnotifier/checkconfiguration.php'); $PAGE->set_url($pageurl); $PAGE->set_context(context_system::instance()); require_login(); require_capability('moodle/site:config', context_system::instance()); // Build a path. $strheading = get_string('checkconfiguration', 'message_airnotifier'); $PAGE->navbar->add(get_string('administrationsite')); $returl = new moodle_url('/admin/category.php', ['category' => 'messaging']); $PAGE->navbar->add(get_string('messagingcategory', 'admin'), $returl); $returl = new moodle_url('/admin/settings.php', ['section' => 'messagesettingairnotifier']); $PAGE->navbar->add(get_string('pluginname', 'message_airnotifier'), $returl); $PAGE->navbar->add($strheading); $PAGE->set_heading($SITE->fullname); $PAGE->set_title($strheading); $manager = new message_airnotifier_manager(); // Sending a test Push notification. if (data_submitted()) { require_sesskey(); if (optional_param('confirm', 0, PARAM_INT)) { $manager->send_test_notification($USER); redirect($pageurl, get_string('eventnotificationsent', 'message'), 5); } else { if (!$manager->has_enabled_devices($CFG->airnotifiermobileappname)) { // The user has not connected to the site with the app yet. redirect($pageurl, get_string('nodevices', 'message_airnotifier'), 5, \core\output\notification::NOTIFY_ERROR); } echo $OUTPUT->header(); $message = get_string('sendtestconfirmation', 'message_airnotifier'); $confirmurl = new moodle_url($pageurl->out(false), ['confirm' => 1]); $continueb = new single_button($confirmurl, get_string('continue'), 'post'); $cancelb = new single_button($pageurl, get_string('cancel'), 'get'); echo $OUTPUT->confirm($message, $continueb, $cancelb); echo $OUTPUT->footer(); } die; } $checkresults = $manager->check_configuration(); $table = new \html_table(); $table->data = []; $table->head = [ get_string('status'), get_string('check'), get_string('summary'), ]; $table->colclasses = [ 'rightalign status', 'leftalign check', 'leftalign summary', ]; $table->id = 'message_airnotifier_checkconfiguration'; $table->attributes = ['class' => 'admintable generaltable']; $table->data = []; $senddisabled = false; foreach ($checkresults as $result) { if ($result->get_status() == core\check\result::CRITICAL || $result->get_status() == core\check\result::ERROR) { $senddisabled = true; } $table->data[] = [$OUTPUT->check_result($result), $result->get_summary(), $result->get_details()]; } echo $OUTPUT->header(); echo $OUTPUT->heading($strheading); // Check table. echo \html_writer::table($table); // Test notification button. $button = $OUTPUT->single_button($PAGE->url, get_string('sendtest', 'message_airnotifier'), 'post', ['disabled' => $senddisabled]); echo $OUTPUT->box($button, 'clearfix mdl-align'); echo $OUTPUT->footer(); lib.php 0000644 00000003270 15152360230 0006022 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * Callbacks for message_airnotifier. * * @package message_airnotifier * @category external * @copyright 2020 Moodle Pty Ltd <juan@moodle.com> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 3.9 */ /** * Callback for when a site is first registered. The function generates an Airnotifier accesskey for the new site. * * @param int $registrationid the new registration id (registration_hubs table) */ function message_airnotifier_post_site_registration_confirmed(int $registrationid) { global $CFG; // Do nothing if the site already has an Airnotifier access key configured. if (!empty($CFG->airnotifieraccesskey)) { return; } $manager = new message_airnotifier_manager(); // Do nothing for custom Airnotifier instances. if (strpos($CFG->airnotifierurl, $manager::AIRNOTIFIER_PUBLICURL) === false ) { return; } if ($key = $manager->request_accesskey()) { set_config('airnotifieraccesskey', $key); } } version.php 0000644 00000002266 15152360230 0006745 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * 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 = 2022112800; // The current plugin version (Date: YYYYMMDDXX). $plugin->requires = 2022111800; // Requires this Moodle version. $plugin->component = 'message_airnotifier'; // Full name of the plugin (used for diagnostics). classes/privacy/provider.php 0000644 00000017753 15152360230 0012233 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * Privacy class for requesting user data. * * @package message_airnotifier * @copyright 2018 Adrian Greeve <adrian@moodle.com> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace message_airnotifier\privacy; defined('MOODLE_INTERNAL') || die(); use \core_privacy\local\metadata\collection; use \core_privacy\local\request\contextlist; use \core_privacy\local\request\approved_contextlist; use \core_privacy\local\request\transform; use core_privacy\local\request\userlist; use \core_privacy\local\request\approved_userlist; /** * Privacy class for requesting user data. * * @package message_airnotifier * @copyright 2018 Adrian Greeve <adrian@moodle.com> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class provider implements \core_privacy\local\metadata\provider, \core_privacy\local\request\core_userlist_provider, \core_privacy\local\request\plugin\provider { /** * Returns meta data about this system. * * @param collection $collection The initialised collection to add items to. * @return collection A listing of user data stored through this system. */ public static function get_metadata(collection $collection) : collection { $collection->link_subsystem('core_user', 'privacy:metadata:usersubsystem'); $collection->add_database_table('message_airnotifier_devices', [ 'userdeviceid' => 'privacy:metadata:userdeviceid', 'enabled' => 'privacy:metadata:enabled' ], 'privacy:metadata:tableexplanation'); $collection->link_external_location('External airnotifier site.', [ 'userid' => 'privacy:metadata:userid', 'username' => 'privacy:metadata:username', 'userfromid' => 'privacy:metadata:userfromid', 'userfromfullname' => 'privacy:metadata:userfromfullname', 'date' => 'privacy:metadata:date', 'subject' => 'privacy:metadata:subject', 'notification' => 'privacy:metadata:notification', 'smallmessage' => 'privacy:metadata:smallmessage', 'fullmessage' => 'privacy:metadata:fullmessage' ], 'privacy:metadata:externalpurpose'); // This system is unaware of user preferences such as message_provider_moodle_instantmessage_enabled. return $collection; } /** * Get the list of contexts that contain user information for the specified user. * * @param int $userid The user to search. * @return contextlist $contextlist The contextlist containing the list of contexts used in this plugin. */ public static function get_contexts_for_userid(int $userid) : contextlist { $sql = "SELECT ctx.id FROM {message_airnotifier_devices} mad JOIN {user_devices} ud ON ud.id = mad.userdeviceid JOIN {user} u ON ud.userid = u.id JOIN {context} ctx ON ctx.instanceid = u.id AND ctx.contextlevel = :contextlevel WHERE ud.userid = :userid"; $params = ['userid' => $userid, 'contextlevel' => CONTEXT_USER]; $contextlist = new contextlist(); $contextlist->add_from_sql($sql, $params); return $contextlist; } /** * Get the list of users within a specific context. * * @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination. */ public static function get_users_in_context(userlist $userlist) { $context = $userlist->get_context(); if (!$context instanceof \context_user) { return; } $sql = "SELECT ud.userid FROM {message_airnotifier_devices} mad JOIN {user_devices} ud ON ud.id = mad.userdeviceid WHERE ud.userid = ?"; $params = [$context->instanceid]; $userlist->add_from_sql('userid', $sql, $params); } /** * Export all user data for the specified user, in the specified contexts. * * @param approved_contextlist $contextlist The approved contexts to export information for. */ public static function export_user_data(approved_contextlist $contextlist) { $results = static::get_records($contextlist->get_user()->id); $context = $contextlist->current(); foreach ($results as $result) { $data = (object)[ 'appid' => $result->appid, 'pushid' => $result->pushid, 'name' => $result->name, 'model' => $result->model, 'platform' => $result->platform, 'version' => $result->version, 'timecreated' => transform::datetime($result->timecreated), 'timemodified' => transform::datetime($result->timemodified), 'enabled' => transform::yesno($result->enable) ]; \core_privacy\local\request\writer::with_context($context)->export_data([ get_string('privacy:subcontext', 'message_airnotifier'), $result->model . '_' . $result->pushid ], $data); } } /** * Delete all use data which matches the specified deletion_criteria. * * @param context $context A context. */ public static function delete_data_for_all_users_in_context(\context $context) { if (!$context instanceof \context_user) { return; } static::delete_data($context->instanceid); } /** * Delete multiple users within a single context. * * @param approved_userlist $userlist The approved context and user information to delete information for. */ public static function delete_data_for_users(approved_userlist $userlist) { $context = $userlist->get_context(); if ($context instanceof \context_user) { static::delete_data($context->instanceid); } } /** * Delete all user data for the specified user, in the specified contexts. * * @param approved_contextlist $contextlist The approved contexts and user information to delete information for. */ public static function delete_data_for_user(approved_contextlist $contextlist) { static::delete_data($contextlist->get_user()->id); } /** * Delete data related to a userid. * * @param int $userid The user ID */ protected static function delete_data(int $userid) { global $DB; foreach (static::get_records($userid) as $record) { $DB->delete_records('message_airnotifier_devices', ['id' => $record->id]); } } /** * Get records related to this plugin and user. * * @param int $userid The user ID * @return array An array of records. */ protected static function get_records(int $userid) : array { global $DB; $sql = "SELECT mad.id, mad.enable, ud.appid, ud.name, ud.model, ud.platform, ud.version, ud.timecreated, ud.timemodified, ud.pushid FROM {message_airnotifier_devices} mad JOIN {user_devices} ud ON mad.userdeviceid = ud.id WHERE ud.userid = :userid"; $params = ['userid' => $userid]; return $DB->get_records_sql($sql, $params); } } classes/manager.php 0000644 00000042046 15152360230 0010327 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * Airnotifier manager class * * @package message_airnotifier * @category external * @copyright 2012 Jerome Mouneyrac <jerome@moodle.com> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 2.7 */ defined('MOODLE_INTERNAL') || die; /** * Airnotifier helper manager class * * @copyright 2012 Jerome Mouneyrac <jerome@moodle.com> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class message_airnotifier_manager { /** @var string The Airnotifier public instance URL */ const AIRNOTIFIER_PUBLICURL = 'https://messages.moodle.net'; /** * Include the relevant javascript and language strings for the device * toolbox YUI module * * @return bool */ public function include_device_ajax() { global $PAGE, $CFG; $config = new stdClass(); $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( '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). $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))) { // We have to create the device token in airnotifier. if (! $this->create_token($device->pushid, $device->platform)) { 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']; } } debugging("Unexpected response from the Airnotifier server: $resp"); return false; } /** * Create a device token in the Airnotifier instance * @param string $token The token to be created * @param string $deviceplatform The device platform (Android, iOS, iOS-fcm, etc...) * @return bool True if all was right */ private function create_token($token, $deviceplatform = '') { 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 = []; if (!empty($deviceplatform)) { $params["device"] = $deviceplatform; } $resp = $curl->post($serverurl, $params); if ($token = json_decode($resp, true)) { if (!empty($token['status'])) { return $token['status'] == 'ok' || $token['status'] == 'token exists'; } } debugging("Unexpected response from the Airnotifier server: $resp"); 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)); } /** * Enables or disables a registered user device so it can receive Push notifications * * @param int $deviceid the device id * @param bool $enable true to enable it, false to disable it * @return bool true if the device was enabled, false in case of error * @since Moodle 3.2 */ public static function enable_device($deviceid, $enable) { global $DB, $USER; if (!$device = $DB->get_record('message_airnotifier_devices', array('id' => $deviceid), '*')) { return false; } // Check that the device belongs to the current user. if (!$userdevice = $DB->get_record('user_devices', array('id' => $device->userdeviceid, 'userid' => $USER->id), '*')) { return false; } $device->enable = $enable; return $DB->update_record('message_airnotifier_devices', $device); } /** * Check the system configuration to detect possible issues. * * @return array result checks */ public function check_configuration(): array { global $CFG, $DB; $results = []; // Check Mobile services enabled. $summary = html_writer::link((new moodle_url('/admin/settings.php', ['section' => 'mobilesettings'])), get_string('enablemobilewebservice', 'admin')); if (empty($CFG->enablewebservices) || empty($CFG->enablemobilewebservice)) { $results[] = new core\check\result(core\check\result::CRITICAL, $summary, get_string('enablewsdescription', 'webservice')); } else { $results[] = new core\check\result(core\check\result::OK, $summary, get_string('enabled', 'admin')); } // Check message outputs are not disabled in config.php. $summary = get_string('noemailevernotset', 'message_airnotifier'); if (!empty($CFG->noemailever)) { $results[] = new core\check\result(core\check\result::CRITICAL, $summary, get_string('noemaileverset', 'message_airnotifier')); } else { $results[] = new core\check\result(core\check\result::OK, $summary, get_string('disabled', 'admin')); } // Check Mobile notifications enabled. require_once($CFG->dirroot . '/message/lib.php'); $processors = get_message_processors(); $enabled = false; foreach ($processors as $processor => $status) { if ($processor == 'airnotifier' && $status->enabled) { $enabled = true; } } $summary = html_writer::link((new moodle_url('/admin/message.php')), get_string('enableprocessor', 'message_airnotifier')); if ($enabled) { $results[] = new core\check\result(core\check\result::OK, $summary, get_string('enabled', 'admin')); } else { $results[] = new core\check\result(core\check\result::CRITICAL, $summary, get_string('mobilenotificationsdisabledwarning', 'tool_mobile')); } // Check Mobile notifications configuration is ok. $summary = html_writer::link((new moodle_url('/admin/settings.php', ['section' => 'messagesettingairnotifier'])), get_string('notificationsserverconfiguration', 'message_airnotifier')); if ($this->is_system_configured()) { $results[] = new core\check\result(core\check\result::OK, $summary, get_string('configured', 'message_airnotifier')); } else { $results[] = new core\check\result(core\check\result::ERROR, $summary, get_string('notconfigured', 'message_airnotifier')); } // Check settings properly formatted. Only display in case of errors. $settingstocheck = ['airnotifierappname', 'airnotifiermobileappname']; if ($this->is_system_configured()) { foreach ($settingstocheck as $setting) { if ($CFG->$setting != trim($CFG->$setting)) { $summary = html_writer::link((new moodle_url('/admin/settings.php', ['section' => 'messagesettingairnotifier'])), get_string('notificationsserverconfiguration', 'message_airnotifier')); $results[] = new core\check\result(core\check\result::ERROR, $summary, get_string('airnotifierfielderror', 'message_airnotifier', get_string($setting, 'message_airnotifier'))); } } } // Check connectivity with Airnotifier. $url = $CFG->airnotifierurl . ':' . $CFG->airnotifierport; $curl = new \curl(); $curl->setopt(['CURLOPT_TIMEOUT' => 5, 'CURLOPT_CONNECTTIMEOUT' => 5]); $curl->get($url); $info = $curl->get_info(); $summary = html_writer::link($url, get_string('airnotifierurl', 'message_airnotifier')); if (!empty($info['http_code']) && ($info['http_code'] == 200 || $info['http_code'] == 302)) { $results[] = new core\check\result(core\check\result::OK, $summary, get_string('online', 'message')); } else { $details = get_string('serverconnectivityerror', 'message_airnotifier', $url); $curlerrno = $curl->get_errno(); if (!empty($curlerrno)) { $details .= ' CURL ERROR: ' . $curlerrno . ' - ' . $curl->error; } $results[] = new core\check\result(core\check\result::ERROR, $summary, $details); } // Check access key by trying to create an invalid token. $settingsurl = new moodle_url('/admin/settings.php', ['section' => 'messagesettingairnotifier']); $summary = html_writer::link($settingsurl, get_string('airnotifieraccesskey', 'message_airnotifier')); if (!empty($CFG->airnotifieraccesskey)) { $url = $CFG->airnotifierurl . ':' . $CFG->airnotifierport . '/tokens/testtoken'; $header = ['Accept: application/json', 'X-AN-APP-NAME: ' . $CFG->airnotifierappname, 'X-AN-APP-KEY: ' . $CFG->airnotifieraccesskey]; $curl->setHeader($header); $response = $curl->post($url); $info = $curl->get_info(); if ($curlerrno = $curl->get_errno()) { $details = get_string('serverconnectivityerror', 'message_airnotifier', $url); $details .= ' CURL ERROR: ' . $curlerrno . ' - ' . $curl->error; $results[] = new core\check\result(core\check\result::ERROR, $summary, $details); } else if (!empty($info['http_code']) && $info['http_code'] == 400 && $key = json_decode($response, true)) { if ($key['error'] == 'Invalid access key') { $results[] = new core\check\result(core\check\result::ERROR, $summary, $key['error']); } else { $results[] = new core\check\result(core\check\result::OK, $summary, get_string('enabled', 'admin')); } } } else { $results[] = new core\check\result(core\check\result::ERROR, $summary, get_string('requestaccesskey', 'message_airnotifier')); } // Check default preferences. $preferences = (array) get_message_output_default_preferences(); $providerscount = 0; $providersconfigured = 0; foreach ($preferences as $prefname => $prefval) { if (strpos($prefname, 'message_provider') === 0) { $providerscount++; if (strpos($prefval, 'airnotifier') !== false) { $providersconfigured++; } } } $summary = html_writer::link((new moodle_url('/admin/message.php')), get_string('managemessageoutputs', 'message')); if ($providersconfigured == 0) { $results[] = new core\check\result(core\check\result::ERROR, $summary, get_string('messageprovidersempty', 'message_airnotifier')); } else if ($providersconfigured / $providerscount < 0.25) { // Less than a 25% of the providers are enabled by default for users. $results[] = new core\check\result(core\check\result::WARNING, $summary, get_string('messageproviderslow', 'message_airnotifier')); } else { $results[] = new core\check\result(core\check\result::OK, $summary, get_string('configured', 'message_airnotifier')); } // Check user devices from last month. $recentdevicescount = $DB->count_records_select('user_devices', 'appid = ? AND timemodified > ?', [$CFG->airnotifiermobileappname, time() - (WEEKSECS * 4)]); $summary = get_string('userdevices', 'message_airnotifier'); if (!empty($recentdevicescount)) { $results[] = new core\check\result(core\check\result::OK, $summary, get_string('configured', 'message_airnotifier')); } else { $results[] = new core\check\result(core\check\result::ERROR, $summary, get_string('nodevices', 'message_airnotifier')); } return $results; } /** * Send a test notification to the given user. * * @param stdClass $user user object */ public function send_test_notification(stdClass $user): void { global $CFG; require_once($CFG->dirroot . '/message/output/airnotifier/message_output_airnotifier.php'); $data = new stdClass; $data->userto = clone $user; $data->subject = 'Push Notification Test'; $data->fullmessage = 'This is a test message send at: ' . userdate(time()); $data->notification = 1; // The send_message method always return true, so it does not make sense to return anything. $airnotifier = new message_output_airnotifier(); $airnotifier->send_message($data); } /** * Check whether the given user has enabled devices or not for the given app. * * @param string $appname the app to check * @param int $userid the user to check the devices for (empty for current user) * @return bool true when the user has enabled devices, false otherwise */ public function has_enabled_devices(string $appname, int $userid = null): bool { $enableddevices = false; $devices = $this->get_user_devices($appname, $userid); foreach ($devices as $device) { if (!$device->enable) { continue; } $enableddevices = true; break; } return $enableddevices; } } yui/build/moodle-message_airnotifier-toolboxes/moodle-message_airnotifier-toolboxes.js 0000644 00000016245 15152360230 0025634 0 ustar 00 YUI.add('moodle-message_airnotifier-toolboxes', function (Y, NAME) { /** * Provides a tool for enabling/disabling elements using AJAX/REST. * * @module moodle-message_airnotifier-toolboxes */ // The CSS selectors we use. var CSS = { AIRNOTIFIERCONTENT: 'div[data-processor-name="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, varname; 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) { // Ignore. } 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 <li> 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() { 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) { baseselector = CSS.AIRNOTIFIERCONTENT; } Y.all(baseselector).each(this._setup_for_device, this); }, _setup_for_device: function(toolboxtarget) { // Show/Hide. 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"]}); yui/build/moodle-message_airnotifier-toolboxes/moodle-message_airnotifier-toolboxes-min.js 0000644 00000004047 15152360230 0026412 0 ustar 00 YUI.add("moodle-message_airnotifier-toolboxes",function(c,e){var t,i='div[data-processor-name="airnotifier"]',o="a.hidedevice",n="li.airnotifierdevice",a="dimmed_text",s="deviceid-",r=function(){r.superclass.constructor.apply(this,arguments)};c.extend(r,c.Base,{replace_button:function(e,t,i,o){o=o||"pointer";e=c.one(e).all(t).setStyle("cursor",o);return e.on("click",i,this),e},toggle_hide_device_ui:function(e){var t=e.ancestor(n),i=e.one("img"),o=a,s="",s=t.hasClass(o)?"hide":"show";t.toggleClass(o),o=M.util.get_string(s,"moodle"),i.setAttrs({alt:o,title:o,src:M.util.image_url("t/"+s)}),e.set("title",o),e.set("className","editing_"+s)},send_request:function(e,o,s){var t,i,n,a;for(i in e=e||{},t=this.get("config").pageparams)e[i]=t[i];return o&&o.show(),e.sesskey=M.cfg.sesskey,n=M.cfg.wwwroot+this.get("ajaxurl"),a=[],c.io(n,{method:"POST",data:e,on:{success:function(e,t){try{(a=c.JSON.parse(t.responseText)).error?c.use("moodle-core-notification-ajaxexception",function(){return new M.core.ajaxException(a).show()}):a.success&&s()}catch(i){}o&&o.hide()},failure:function(e,t){o&&o.hide(),c.use("moodle-core-notification-ajaxexception",function(){return new M.core.ajaxException(t).show()})}},context:this,sync:!1}),a},get_element_id:function(e){return e.get("id").replace(s,"")}},{NAME:"device-toolbox",ATTRS:{ajaxurl:{value:0},config:{value:0}}}),c.extend(t=function(){t.superclass.constructor.apply(this,arguments)},r,{initializer:function(){this.setup_for_device()},setup_for_device:function(e){e=e||i,c.all(e).each(this._setup_for_device,this)},_setup_for_device:function(e){this.replace_button(e,o,this.toggle_hide_device)},toggle_hide_device:function(e){var t,i,o;e.preventDefault(),i=e.target.ancestor(n),t=e.target.ancestor("a",!0),e={field:"enable",enable:i.hasClass(a)?1:0,id:this.get_element_id(i)},i=M.util.add_spinner(c,i),(o=this).send_request(e,i,function(){o.toggle_hide_device_ui(t)})}},{NAME:"message-device-toolbox",ATTRS:{}}),M.message=M.message||{},M.message.init_device_toolbox=function(e){return new t(e)}},"@VERSION@",{requires:["base","node","io"]}); yui/build/moodle-message_airnotifier-toolboxes/moodle-message_airnotifier-toolboxes-debug.js 0000644 00000016245 15152360230 0026720 0 ustar 00 YUI.add('moodle-message_airnotifier-toolboxes', function (Y, NAME) { /** * Provides a tool for enabling/disabling elements using AJAX/REST. * * @module moodle-message_airnotifier-toolboxes */ // The CSS selectors we use. var CSS = { AIRNOTIFIERCONTENT: 'div[data-processor-name="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, varname; 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) { // Ignore. } 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 <li> 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() { 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) { baseselector = CSS.AIRNOTIFIERCONTENT; } Y.all(baseselector).each(this._setup_for_device, this); }, _setup_for_device: function(toolboxtarget) { // Show/Hide. 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"]}); yui/src/toolboxes/meta/toolboxes.json 0000644 00000000171 15152360230 0014012 0 ustar 00 { "moodle-message_airnotifier-toolboxes": { "requires": [ "base", "node", "io" ] } } yui/src/toolboxes/build.json 0000644 00000000256 15152360230 0012151 0 ustar 00 { "name": "moodle-message_airnotifier-toolboxes", "builds": { "moodle-message_airnotifier-toolboxes": { "jsfiles": [ "toolboxes.js" ] } } } yui/src/toolboxes/js/toolboxes.js 0000644 00000016046 15152360230 0013153 0 ustar 00 /** * Provides a tool for enabling/disabling elements using AJAX/REST. * * @module moodle-message_airnotifier-toolboxes */ // The CSS selectors we use. var CSS = { AIRNOTIFIERCONTENT: 'div[data-processor-name="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, varname; 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) { // Ignore. } 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 <li> 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() { 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) { baseselector = CSS.AIRNOTIFIERCONTENT; } Y.all(baseselector).each(this._setup_for_device, this); }, _setup_for_device: function(toolboxtarget) { // Show/Hide. 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); }; lang/en/message_airnotifier.php 0000644 00000014620 15152360230 0012617 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * 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['airnotifierfielderror'] = 'Please remove any empty spaces or unnecessary characters from the following field: {$a}'; $string['airnotifiermobileappname'] = 'Mobile app name'; $string['airnotifierport'] = 'Airnotifier port'; $string['airnotifierurl'] = 'Airnotifier URL'; $string['checkconfiguration'] = 'Check and test push notification configuration'; $string['configairnotifierurl'] = 'The server URL to connect to for sending push notifications.'; $string['configairnotifierport'] = 'The port to use when connecting to the airnotifier server.'; $string['configairnotifieraccesskey'] = 'The access key for connecting to the Airnotifier server. You can obtain an access key by clicking the "Request access key" link below (registered sites only) or by creating an account on the <a href="https://apps.moodle.com">Moodle Apps Portal</a>.'; $string['configairnotifierappname'] = 'The app name identifier in Airnotifier.'; $string['configairnotifiermobileappname'] = 'The Mobile app unique identifier (usually something like com.moodle.moodlemobile).'; $string['configured'] = 'Configured'; $string['deletecheckdevicename'] = 'Delete your device: {$a->name}'; $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['enableprocessor'] = 'Enable mobile notifications'; $string['errorretrievingkey'] = 'An error occurred while retrieving the access key. Your site must be registered to use this service. If your site is already registered, please try updating your registration. Alternatively, you can obtain an access key by creating an account on the <a href="https://apps.moodle.com">Moodle Apps Portal</a>.'; $string['keyretrievedsuccessfully'] = 'The access key was retrieved successfully. To access Moodle app usage statistics, please create an account on the <a href="https://apps.moodle.com">Moodle Apps Portal</a>.'; $string['messageprovidersempty'] = 'There are no mobile notifications enabled in default notification preferences.'; $string['messageproviderslow'] = 'Only a few mobile notifications are enabled in default notification preferences.'; $string['moodleappsportallimitswarning'] = 'Please note that the number of user devices allowed to receive notifications depends on your Moodle app subscription. For details, visit the <a href="{$a}" target="_blank">Moodle Apps Portal</a>.'; $string['nodevices'] = 'No registered devices. Devices will automatically appear after you install the Moodle app and add this site.'; $string['noemailevernotset'] = '$CFG->noemailever disabled'; $string['noemaileverset'] = '$CFG->noemailever is enabled in config.php. You need to set this setting to false or remove it.'; $string['nopermissiontomanagedevices'] = 'You don\'t have permission to manage devices.'; $string['notconfigured'] = 'The Airnotifier server has not been configured so push notifications cannot be sent.'; $string['notificationsserverconfiguration'] = 'Notifications server (Airnotifier) configuration'; $string['pluginname'] = 'Mobile'; $string['privacy:appiddescription'] = 'This is an identifier to the application being used.'; $string['privacy:enableddescription'] = 'If this device is enabled for airnotifier.'; $string['privacy:metadata:enabled'] = 'Whether the airnotifier device is enabled.'; $string['privacy:metadata:date'] = 'The date that the message was sent.'; $string['privacy:metadata:externalpurpose'] = 'This information is sent to an external site to be ultimately delivered to the mobile device of the user.'; $string['privacy:metadata:fullmessage'] = 'The full message.'; $string['privacy:metadata:notification'] = 'If this message is a notification.'; $string['privacy:metadata:smallmessage'] = 'A section of the message.'; $string['privacy:metadata:subject'] = 'The subject line of the message.'; $string['privacy:metadata:tableexplanation'] = 'Airnotifier device information is stored here.'; $string['privacy:metadata:userdeviceid'] = 'The ID linking to the user\'s mobile device'; $string['privacy:metadata:userfromfullname'] = 'The full name of the user who sent the message.'; $string['privacy:metadata:userfromid'] = 'The user ID of the author of the message.'; $string['privacy:metadata:userid'] = 'The ID of the user who sent the message.'; $string['privacy:metadata:username'] = 'The username of the user.'; $string['privacy:metadata:usersubsystem'] = 'This plugin is connected to the user subsystem.'; $string['privacy:subcontext'] = 'Message Airnotifier'; $string['sitemustberegistered'] = 'In order to use the public Airnotifier instance, your site must be registered. Alternatively, you can obtain an access key by creating an account on the <a href="https://apps.moodle.com">Moodle Apps Portal</a>.'; $string['showhide'] = 'Enable/disable the device.'; $string['requestaccesskey'] = 'Request access key'; $string['sendtest'] = 'Send test push notification to my devices'; $string['sendtestconfirmation'] = 'A test push notification will be sent to the devices you use to connect to this site. Please ensure that your devices are connected to the Internet and that the mobile app is not open (since push notifications are only displayed when received in the background).'; $string['serverconnectivityerror'] = 'This site is not able to connect to the notifications server {$a}'; $string['unknowndevice'] = 'Unknown device'; $string['userdevices'] = 'User devices'; $string['airnotifier:managedevice'] = 'Manage devices';
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | ���֧ߧ֧�ѧ�ڧ� ����ѧߧڧ��: 0 |
proxy
|
phpinfo
|
���ѧ����ۧܧ�