Differences Between: [Versions 310 and 311] [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403] [Versions 39 and 311]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * Airnotifier manager class 19 * 20 * @package message_airnotifier 21 * @category external 22 * @copyright 2012 Jerome Mouneyrac <jerome@moodle.com> 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 * @since Moodle 2.7 25 */ 26 27 defined('MOODLE_INTERNAL') || die; 28 29 /** 30 * Airnotifier helper manager class 31 * 32 * @copyright 2012 Jerome Mouneyrac <jerome@moodle.com> 33 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 34 */ 35 class message_airnotifier_manager { 36 37 /** @var string The Airnotifier public instance URL */ 38 const AIRNOTIFIER_PUBLICURL = 'https://messages.moodle.net'; 39 40 /** 41 * Include the relevant javascript and language strings for the device 42 * toolbox YUI module 43 * 44 * @return bool 45 */ 46 public function include_device_ajax() { 47 global $PAGE, $CFG; 48 49 $config = new stdClass(); 50 $config->resturl = '/message/output/airnotifier/rest.php'; 51 $config->pageparams = array(); 52 53 // Include toolboxes. 54 $PAGE->requires->yui_module('moodle-message_airnotifier-toolboxes', 'M.message.init_device_toolbox', array(array( 55 'ajaxurl' => $config->resturl, 56 'config' => $config, 57 )) 58 ); 59 60 // Required strings for the javascript. 61 $PAGE->requires->strings_for_js(array('deletecheckdevicename'), 'message_airnotifier'); 62 $PAGE->requires->strings_for_js(array('show', 'hide'), 'moodle'); 63 64 return true; 65 } 66 67 /** 68 * Return the user devices for a specific app. 69 * 70 * @param string $appname the app name . 71 * @param int $userid if empty take the current user. 72 * @return array all the devices 73 */ 74 public function get_user_devices($appname, $userid = null) { 75 global $USER, $DB; 76 77 if (empty($userid)) { 78 $userid = $USER->id; 79 } 80 81 $devices = array(); 82 83 $params = array('appid' => $appname, 'userid' => $userid); 84 85 // First, we look all the devices registered for this user in the Moodle core. 86 // We are going to allow only ios devices (since these are the ones that supports PUSH notifications). 87 $userdevices = $DB->get_records('user_devices', $params); 88 foreach ($userdevices as $device) { 89 if (core_text::strtolower($device->platform)) { 90 // Check if the device is known by airnotifier. 91 if (!$airnotifierdev = $DB->get_record('message_airnotifier_devices', 92 array('userdeviceid' => $device->id))) { 93 94 // We have to create the device token in airnotifier. 95 if (! $this->create_token($device->pushid, $device->platform)) { 96 continue; 97 } 98 99 $airnotifierdev = new stdClass; 100 $airnotifierdev->userdeviceid = $device->id; 101 $airnotifierdev->enable = 1; 102 $airnotifierdev->id = $DB->insert_record('message_airnotifier_devices', $airnotifierdev); 103 } 104 $device->id = $airnotifierdev->id; 105 $device->enable = $airnotifierdev->enable; 106 $devices[] = $device; 107 } 108 } 109 110 return $devices; 111 } 112 113 /** 114 * Request and access key to Airnotifier 115 * 116 * @return mixed The access key or false in case of error 117 */ 118 public function request_accesskey() { 119 global $CFG, $USER; 120 121 require_once($CFG->libdir . '/filelib.php'); 122 123 // Sending the request access key request to Airnotifier. 124 $serverurl = $CFG->airnotifierurl . ':' . $CFG->airnotifierport . '/accesskeys/'; 125 // We use an APP Key "none", it can be anything. 126 $header = array('Accept: application/json', 'X-AN-APP-NAME: ' . $CFG->airnotifierappname, 127 'X-AN-APP-KEY: none'); 128 $curl = new curl(); 129 $curl->setHeader($header); 130 131 // Site ids are stored as secrets in md5 in the Moodle public hub. 132 $params = array( 133 'url' => $CFG->wwwroot, 134 'siteid' => md5($CFG->siteidentifier), 135 'contact' => $USER->email, 136 'description' => $CFG->wwwroot 137 ); 138 $resp = $curl->post($serverurl, $params); 139 140 if ($key = json_decode($resp, true)) { 141 if (!empty($key['accesskey'])) { 142 return $key['accesskey']; 143 } 144 } 145 debugging("Unexpected response from the Airnotifier server: $resp"); 146 return false; 147 } 148 149 /** 150 * Create a device token in the Airnotifier instance 151 * @param string $token The token to be created 152 * @param string $deviceplatform The device platform (Android, iOS, iOS-fcm, etc...) 153 * @return bool True if all was right 154 */ 155 private function create_token($token, $deviceplatform = '') { 156 global $CFG; 157 158 if (!$this->is_system_configured()) { 159 return false; 160 } 161 162 require_once($CFG->libdir . '/filelib.php'); 163 164 $serverurl = $CFG->airnotifierurl . ':' . $CFG->airnotifierport . '/tokens/' . $token; 165 $header = array('Accept: application/json', 'X-AN-APP-NAME: ' . $CFG->airnotifierappname, 166 'X-AN-APP-KEY: ' . $CFG->airnotifieraccesskey); 167 $curl = new curl; 168 $curl->setHeader($header); 169 $params = []; 170 if (!empty($deviceplatform)) { 171 $params["device"] = $deviceplatform; 172 } 173 $resp = $curl->post($serverurl, $params); 174 175 if ($token = json_decode($resp, true)) { 176 if (!empty($token['status'])) { 177 return $token['status'] == 'ok' || $token['status'] == 'token exists'; 178 } 179 } 180 debugging("Unexpected response from the Airnotifier server: $resp"); 181 return false; 182 } 183 184 /** 185 * Tests whether the airnotifier settings have been configured 186 * @return boolean true if airnotifier is configured 187 */ 188 public function is_system_configured() { 189 global $CFG; 190 191 return (!empty($CFG->airnotifierurl) && !empty($CFG->airnotifierport) && 192 !empty($CFG->airnotifieraccesskey) && !empty($CFG->airnotifierappname) && 193 !empty($CFG->airnotifiermobileappname)); 194 } 195 196 /** 197 * Enables or disables a registered user device so it can receive Push notifications 198 * 199 * @param int $deviceid the device id 200 * @param bool $enable true to enable it, false to disable it 201 * @return bool true if the device was enabled, false in case of error 202 * @since Moodle 3.2 203 */ 204 public static function enable_device($deviceid, $enable) { 205 global $DB, $USER; 206 207 if (!$device = $DB->get_record('message_airnotifier_devices', array('id' => $deviceid), '*')) { 208 return false; 209 } 210 211 // Check that the device belongs to the current user. 212 if (!$userdevice = $DB->get_record('user_devices', array('id' => $device->userdeviceid, 'userid' => $USER->id), '*')) { 213 return false; 214 } 215 216 $device->enable = $enable; 217 return $DB->update_record('message_airnotifier_devices', $device); 218 } 219 220 /** 221 * Check the system configuration to detect possible issues. 222 * 223 * @return array result checks 224 */ 225 public function check_configuration(): array { 226 global $CFG, $DB; 227 228 $results = []; 229 // Check Mobile services enabled. 230 $summary = html_writer::link((new moodle_url('/admin/settings.php', ['section' => 'mobilesettings'])), 231 get_string('enablemobilewebservice', 'admin')); 232 if (empty($CFG->enablewebservices) || empty($CFG->enablemobilewebservice)) { 233 $results[] = new core\check\result(core\check\result::CRITICAL, $summary, get_string('enablewsdescription', 'webservice')); 234 } else { 235 $results[] = new core\check\result(core\check\result::OK, $summary, get_string('enabled', 'admin')); 236 } 237 238 // Check Mobile notifications enabled. 239 require_once($CFG->dirroot . '/message/lib.php'); 240 $processors = get_message_processors(); 241 $enabled = false; 242 foreach ($processors as $processor => $status) { 243 if ($processor == 'airnotifier' && $status->enabled) { 244 $enabled = true; 245 } 246 } 247 248 $summary = html_writer::link((new moodle_url('/admin/message.php')), get_string('enableprocessor', 'message_airnotifier')); 249 if ($enabled) { 250 $results[] = new core\check\result(core\check\result::OK, $summary, get_string('enabled', 'admin')); 251 } else { 252 $results[] = new core\check\result(core\check\result::CRITICAL, $summary, 253 get_string('mobilenotificationsdisabledwarning', 'tool_mobile')); 254 } 255 256 // Check Mobile notifications configuration is ok. 257 $summary = html_writer::link((new moodle_url('/admin/settings.php', ['section' => 'messagesettingairnotifier'])), 258 get_string('notificationsserverconfiguration', 'message_airnotifier')); 259 if ($this->is_system_configured()) { 260 $results[] = new core\check\result(core\check\result::OK, $summary, get_string('configured', 'message_airnotifier')); 261 } else { 262 $results[] = new core\check\result(core\check\result::ERROR, $summary, get_string('notconfigured', 'message_airnotifier')); 263 } 264 265 // Check settings properly formatted. Only display in case of errors. 266 $settingstocheck = ['airnotifierappname', 'airnotifiermobileappname']; 267 if ($this->is_system_configured()) { 268 foreach ($settingstocheck as $setting) { 269 if ($CFG->$setting != trim($CFG->$setting)) { 270 $summary = html_writer::link((new moodle_url('/admin/settings.php', ['section' => 'messagesettingairnotifier'])), 271 get_string('notificationsserverconfiguration', 'message_airnotifier')); 272 273 $results[] = new core\check\result(core\check\result::ERROR, $summary, 274 get_string('airnotifierfielderror', 'message_airnotifier', get_string($setting, 'message_airnotifier'))); 275 } 276 } 277 } 278 279 // Check connectivity with Airnotifier. 280 $url = $CFG->airnotifierurl . ':' . $CFG->airnotifierport; 281 $curl = new \curl(); 282 $curl->setopt(['CURLOPT_TIMEOUT' => 5, 'CURLOPT_CONNECTTIMEOUT' => 5]); 283 $curl->get($url); 284 $info = $curl->get_info(); 285 286 $summary = html_writer::link($url, get_string('airnotifierurl', 'message_airnotifier')); 287 if (!empty($info['http_code']) && ($info['http_code'] == 200 || $info['http_code'] == 302)) { 288 $results[] = new core\check\result(core\check\result::OK, $summary, get_string('online', 'message')); 289 } else { 290 $details = get_string('serverconnectivityerror', 'message_airnotifier', $url); 291 $curlerrno = $curl->get_errno(); 292 if (!empty($curlerrno)) { 293 $details .= ' CURL ERROR: ' . $curlerrno . ' - ' . $curl->error; 294 } 295 $results[] = new core\check\result(core\check\result::ERROR, $summary, $details); 296 } 297 298 // Check access key by trying to create an invalid token. 299 $settingsurl = new moodle_url('/admin/settings.php', ['section' => 'messagesettingairnotifier']); 300 $summary = html_writer::link($settingsurl, get_string('airnotifieraccesskey', 'message_airnotifier')); 301 if (!empty($CFG->airnotifieraccesskey)) { 302 $url = $CFG->airnotifierurl . ':' . $CFG->airnotifierport . '/tokens/testtoken'; 303 $header = ['Accept: application/json', 'X-AN-APP-NAME: ' . $CFG->airnotifierappname, 304 'X-AN-APP-KEY: ' . $CFG->airnotifieraccesskey]; 305 $curl->setHeader($header); 306 $response = $curl->post($url); 307 $info = $curl->get_info(); 308 309 if ($curlerrno = $curl->get_errno()) { 310 $details = get_string('serverconnectivityerror', 'message_airnotifier', $url); 311 $details .= ' CURL ERROR: ' . $curlerrno . ' - ' . $curl->error; 312 $results[] = new core\check\result(core\check\result::ERROR, $summary, $details); 313 } else if (!empty($info['http_code']) && $info['http_code'] == 400 && $key = json_decode($response, true)) { 314 if ($key['error'] == 'Invalid access key') { 315 $results[] = new core\check\result(core\check\result::ERROR, $summary, $key['error']); 316 } else { 317 $results[] = new core\check\result(core\check\result::OK, $summary, get_string('enabled', 'admin')); 318 } 319 } 320 } else { 321 $results[] = new core\check\result(core\check\result::ERROR, $summary, 322 get_string('requestaccesskey', 'message_airnotifier')); 323 } 324 325 // Check default preferences. 326 $preferences = (array) get_message_output_default_preferences(); 327 $providerscount = 0; 328 $providersconfigured = 0; 329 foreach ($preferences as $prefname => $prefval) { 330 if (strpos($prefname, 'message_provider') === 0) { 331 $providerscount++; 332 if (strpos($prefval, 'airnotifier') !== false) { 333 $providersconfigured++; 334 } 335 } 336 } 337 338 $summary = html_writer::link((new moodle_url('/admin/message.php')), get_string('managemessageoutputs', 'message')); 339 if ($providersconfigured == 0) { 340 $results[] = new core\check\result(core\check\result::ERROR, $summary, 341 get_string('messageprovidersempty', 'message_airnotifier')); 342 } else if ($providersconfigured / $providerscount < 0.25) { 343 // Less than a 25% of the providers are enabled by default for users. 344 $results[] = new core\check\result(core\check\result::WARNING, $summary, 345 get_string('messageproviderslow', 'message_airnotifier')); 346 } else { 347 $results[] = new core\check\result(core\check\result::OK, $summary, get_string('configured', 'message_airnotifier')); 348 } 349 350 // Check user devices from last month. 351 $recentdevicescount = $DB->count_records_select('user_devices', 'appid = ? AND timemodified > ?', 352 [$CFG->airnotifiermobileappname, time() - (WEEKSECS * 4)]); 353 354 $summary = get_string('userdevices', 'message_airnotifier'); 355 if (!empty($recentdevicescount)) { 356 $results[] = new core\check\result(core\check\result::OK, $summary, get_string('configured', 'message_airnotifier')); 357 } else { 358 $results[] = new core\check\result(core\check\result::ERROR, $summary, get_string('nodevices', 'message_airnotifier')); 359 } 360 return $results; 361 } 362 363 /** 364 * Send a test notification to the given user. 365 * 366 * @param stdClass $user user object 367 */ 368 public function send_test_notification(stdClass $user): void { 369 global $CFG; 370 require_once($CFG->dirroot . '/message/output/airnotifier/message_output_airnotifier.php'); 371 372 $data = new stdClass; 373 $data->userto = clone $user; 374 $data->subject = 'Push Notification Test'; 375 $data->fullmessage = 'This is a test message send at: ' . userdate(time()); 376 $data->notification = 1; 377 378 // The send_message method always return true, so it does not make sense to return anything. 379 $airnotifier = new message_output_airnotifier(); 380 $airnotifier->send_message($data); 381 } 382 383 /** 384 * Check whether the given user has enabled devices or not for the given app. 385 * 386 * @param string $appname the app to check 387 * @param int $userid the user to check the devices for (empty for current user) 388 * @return bool true when the user has enabled devices, false otherwise 389 */ 390 public function has_enabled_devices(string $appname, int $userid = null): bool { 391 $enableddevices = false; 392 $devices = $this->get_user_devices($appname, $userid); 393 394 foreach ($devices as $device) { 395 if (!$device->enable) { 396 continue; 397 } 398 $enableddevices = true; 399 break; 400 } 401 return $enableddevices; 402 } 403 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body