Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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