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
use core_external\external_description;
18
use core_external\external_value;
19
use core_external\external_format_value;
20
use core_external\external_single_structure;
21
use core_external\external_multiple_structure;
22
use core_external\external_function_parameters;
23
use core_external\external_warnings;
24
 
25
/**
26
 * User external functions
27
 *
28
 * @package    core_user
29
 * @category   external
30
 * @copyright  2011 Jerome Mouneyrac
31
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
32
 * @since Moodle 2.2
33
 */
34
class core_user_external extends \core_external\external_api {
35
 
36
    /**
37
     * Returns description of method parameters
38
     *
39
     * @return external_function_parameters
40
     * @since Moodle 2.2
41
     */
42
    public static function create_users_parameters() {
43
        global $CFG;
44
        $userfields = [
45
            'createpassword' => new external_value(PARAM_BOOL, 'True if password should be created and mailed to user.',
46
                VALUE_OPTIONAL),
47
            // General.
48
            'username' => new external_value(core_user::get_property_type('username'),
49
                'Username policy is defined in Moodle security config.'),
50
            'auth' => new external_value(core_user::get_property_type('auth'), 'Auth plugins include manual, ldap, etc',
51
                VALUE_DEFAULT, 'manual', core_user::get_property_null('auth')),
52
            'password' => new external_value(core_user::get_property_type('password'),
53
                'Plain text password consisting of any characters', VALUE_OPTIONAL),
54
            'firstname' => new external_value(core_user::get_property_type('firstname'), 'The first name(s) of the user'),
55
            'lastname' => new external_value(core_user::get_property_type('lastname'), 'The family name of the user'),
56
            'email' => new external_value(core_user::get_property_type('email'), 'A valid and unique email address'),
57
            'maildisplay' => new external_value(core_user::get_property_type('maildisplay'), 'Email visibility', VALUE_OPTIONAL),
58
            'city' => new external_value(core_user::get_property_type('city'), 'Home city of the user', VALUE_OPTIONAL),
59
            'country' => new external_value(core_user::get_property_type('country'),
60
                'Home country code of the user, such as AU or CZ', VALUE_OPTIONAL),
61
            'timezone' => new external_value(core_user::get_property_type('timezone'),
62
                'Timezone code such as Australia/Perth, or 99 for default', VALUE_OPTIONAL),
63
            'description' => new external_value(core_user::get_property_type('description'), 'User profile description, no HTML',
64
                VALUE_OPTIONAL),
65
            // Additional names.
66
            'firstnamephonetic' => new external_value(core_user::get_property_type('firstnamephonetic'),
67
                'The first name(s) phonetically of the user', VALUE_OPTIONAL),
68
            'lastnamephonetic' => new external_value(core_user::get_property_type('lastnamephonetic'),
69
                'The family name phonetically of the user', VALUE_OPTIONAL),
70
            'middlename' => new external_value(core_user::get_property_type('middlename'), 'The middle name of the user',
71
                VALUE_OPTIONAL),
72
            'alternatename' => new external_value(core_user::get_property_type('alternatename'), 'The alternate name of the user',
73
                VALUE_OPTIONAL),
74
            // Interests.
75
            'interests' => new external_value(PARAM_TEXT, 'User interests (separated by commas)', VALUE_OPTIONAL),
76
            // Optional.
77
            'idnumber' => new external_value(core_user::get_property_type('idnumber'),
78
                'An arbitrary ID code number perhaps from the institution', VALUE_DEFAULT, ''),
79
            'institution' => new external_value(core_user::get_property_type('institution'), 'institution', VALUE_OPTIONAL),
80
            'department' => new external_value(core_user::get_property_type('department'), 'department', VALUE_OPTIONAL),
81
            'phone1' => new external_value(core_user::get_property_type('phone1'), 'Phone 1', VALUE_OPTIONAL),
82
            'phone2' => new external_value(core_user::get_property_type('phone2'), 'Phone 2', VALUE_OPTIONAL),
83
            'address' => new external_value(core_user::get_property_type('address'), 'Postal address', VALUE_OPTIONAL),
84
            // Other user preferences stored in the user table.
85
            'lang' => new external_value(core_user::get_property_type('lang'), 'Language code such as "en", must exist on server',
86
                VALUE_DEFAULT, core_user::get_property_default('lang'), core_user::get_property_null('lang')),
87
            'calendartype' => new external_value(core_user::get_property_type('calendartype'),
88
                'Calendar type such as "gregorian", must exist on server', VALUE_DEFAULT, $CFG->calendartype, VALUE_OPTIONAL),
89
            'theme' => new external_value(core_user::get_property_type('theme'),
90
                'Theme name such as "standard", must exist on server', VALUE_OPTIONAL),
91
            'mailformat' => new external_value(core_user::get_property_type('mailformat'),
92
                'Mail format code is 0 for plain text, 1 for HTML etc', VALUE_OPTIONAL),
93
            // Custom user profile fields.
94
            'customfields' => new external_multiple_structure(
95
                new external_single_structure(
96
                    [
97
                        'type'  => new external_value(PARAM_ALPHANUMEXT, 'The name of the custom field'),
98
                        'value' => new external_value(PARAM_RAW, 'The value of the custom field')
99
                    ]
100
                ), 'User custom fields (also known as user profil fields)', VALUE_OPTIONAL),
101
            // User preferences.
102
            'preferences' => new external_multiple_structure(
103
            new external_single_structure(
104
                [
105
                    'type'  => new external_value(PARAM_RAW, 'The name of the preference'),
106
                    'value' => new external_value(PARAM_RAW, 'The value of the preference')
107
                ]
108
            ), 'User preferences', VALUE_OPTIONAL),
109
        ];
110
        return new external_function_parameters(
111
            [
112
                'users' => new external_multiple_structure(
113
                    new external_single_structure($userfields)
114
                )
115
            ]
116
        );
117
    }
118
 
119
    /**
120
     * Create one or more users.
121
     *
122
     * @throws invalid_parameter_exception
123
     * @param array $users An array of users to create.
124
     * @return array An array of arrays
125
     * @since Moodle 2.2
126
     */
127
    public static function create_users($users) {
128
        global $CFG, $DB;
129
        require_once($CFG->dirroot."/lib/weblib.php");
130
        require_once($CFG->dirroot."/user/lib.php");
131
        require_once($CFG->dirroot."/user/editlib.php");
132
        require_once($CFG->dirroot."/user/profile/lib.php"); // Required for customfields related function.
133
 
134
        // Ensure the current user is allowed to run this function.
135
        $context = context_system::instance();
136
        self::validate_context($context);
137
        require_capability('moodle/user:create', $context);
138
 
139
        // Do basic automatic PARAM checks on incoming data, using params description.
140
        // If any problems are found then exceptions are thrown with helpful error messages.
141
        $params = self::validate_parameters(self::create_users_parameters(), array('users' => $users));
142
 
143
        $availableauths  = core_component::get_plugin_list('auth');
144
        unset($availableauths['mnet']);       // These would need mnethostid too.
145
        unset($availableauths['webservice']); // We do not want new webservice users for now.
146
 
147
        $availablethemes = core_component::get_plugin_list('theme');
148
        $availablelangs  = get_string_manager()->get_list_of_translations();
149
 
150
        $transaction = $DB->start_delegated_transaction();
151
 
152
        $userids = array();
153
        foreach ($params['users'] as $user) {
154
            // Make sure that the username, firstname and lastname are not blank.
155
            foreach (array('username', 'firstname', 'lastname') as $fieldname) {
156
                if (trim($user[$fieldname]) === '') {
157
                    throw new invalid_parameter_exception('The field '.$fieldname.' cannot be blank');
158
                }
159
            }
160
 
161
            // Make sure that the username doesn't already exist.
162
            if ($DB->record_exists('user', array('username' => $user['username'], 'mnethostid' => $CFG->mnet_localhost_id))) {
163
                throw new invalid_parameter_exception('Username already exists: '.$user['username']);
164
            }
165
 
166
            // Make sure auth is valid.
167
            if (empty($availableauths[$user['auth']])) {
168
                throw new invalid_parameter_exception('Invalid authentication type: '.$user['auth']);
169
            }
170
 
171
            // Make sure lang is valid.
172
            if (empty($availablelangs[$user['lang']])) {
173
                throw new invalid_parameter_exception('Invalid language code: '.$user['lang']);
174
            }
175
 
176
            // Make sure lang is valid.
177
            if (!empty($user['theme']) && empty($availablethemes[$user['theme']])) { // Theme is VALUE_OPTIONAL,
178
                                                                                     // so no default value
179
                                                                                     // We need to test if the client sent it
180
                                                                                     // => !empty($user['theme']).
181
                throw new invalid_parameter_exception('Invalid theme: '.$user['theme']);
182
            }
183
 
184
            // Make sure we have a password or have to create one.
185
            $authplugin = get_auth_plugin($user['auth']);
186
            if ($authplugin->is_internal() && empty($user['password']) && empty($user['createpassword'])) {
187
                throw new invalid_parameter_exception('Invalid password: you must provide a password, or set createpassword.');
188
            }
189
 
190
            $user['confirmed'] = true;
191
            $user['mnethostid'] = $CFG->mnet_localhost_id;
192
 
193
            // Start of user info validation.
194
            // Make sure we validate current user info as handled by current GUI. See user/editadvanced_form.php func validation().
195
            if (!validate_email($user['email'])) {
196
                throw new invalid_parameter_exception('Email address is invalid: '.$user['email']);
197
            } else if (empty($CFG->allowaccountssameemail)) {
198
                // Make a case-insensitive query for the given email address.
199
                $select = $DB->sql_equal('email', ':email', false) . ' AND mnethostid = :mnethostid';
200
                $params = array(
201
                    'email' => $user['email'],
202
                    'mnethostid' => $user['mnethostid']
203
                );
204
                // If there are other user(s) that already have the same email, throw an error.
205
                if ($DB->record_exists_select('user', $select, $params)) {
206
                    throw new invalid_parameter_exception('Email address already exists: '.$user['email']);
207
                }
208
            }
209
            // End of user info validation.
210
 
211
            $createpassword = !empty($user['createpassword']);
212
            unset($user['createpassword']);
213
            $updatepassword = false;
214
            if ($authplugin->is_internal()) {
215
                if ($createpassword) {
216
                    $user['password'] = '';
217
                } else {
218
                    $updatepassword = true;
219
                }
220
            } else {
221
                $user['password'] = AUTH_PASSWORD_NOT_CACHED;
222
            }
223
 
224
            // Create the user data now!
225
            $user['id'] = user_create_user($user, $updatepassword, false);
226
 
227
            $userobject = (object)$user;
228
 
229
            // Set user interests.
230
            if (!empty($user['interests'])) {
231
                $trimmedinterests = array_map('trim', explode(',', $user['interests']));
232
                $interests = array_filter($trimmedinterests, function($value) {
233
                    return !empty($value);
234
                });
235
                useredit_update_interests($userobject, $interests);
236
            }
237
 
238
            // Custom fields.
239
            if (!empty($user['customfields'])) {
240
                foreach ($user['customfields'] as $customfield) {
241
                    // Profile_save_data() saves profile file it's expecting a user with the correct id,
242
                    // and custom field to be named profile_field_"shortname".
243
                    $user["profile_field_".$customfield['type']] = $customfield['value'];
244
                }
245
                profile_save_data((object) $user);
246
            }
247
 
248
            if ($createpassword) {
249
                setnew_password_and_mail($userobject);
250
                unset_user_preference('create_password', $userobject);
251
                set_user_preference('auth_forcepasswordchange', 1, $userobject);
252
            }
253
 
254
            // Trigger event.
255
            \core\event\user_created::create_from_userid($user['id'])->trigger();
256
 
257
            // Preferences.
258
            if (!empty($user['preferences'])) {
259
                $userpref = (object)$user;
260
                foreach ($user['preferences'] as $preference) {
261
                    $userpref->{'preference_'.$preference['type']} = $preference['value'];
262
                }
263
                useredit_update_user_preference($userpref);
264
            }
265
 
266
            $userids[] = array('id' => $user['id'], 'username' => $user['username']);
267
        }
268
 
269
        $transaction->allow_commit();
270
 
271
        return $userids;
272
    }
273
 
274
    /**
275
     * Returns description of method result value
276
     *
277
     * @return external_description
278
     * @since Moodle 2.2
279
     */
280
    public static function create_users_returns() {
281
        return new external_multiple_structure(
282
            new external_single_structure(
283
                array(
284
                    'id'       => new external_value(core_user::get_property_type('id'), 'user id'),
285
                    'username' => new external_value(core_user::get_property_type('username'), 'user name'),
286
                )
287
            )
288
        );
289
    }
290
 
291
 
292
    /**
293
     * Returns description of method parameters
294
     *
295
     * @return external_function_parameters
296
     * @since Moodle 2.2
297
     */
298
    public static function delete_users_parameters() {
299
        return new external_function_parameters(
300
            array(
301
                'userids' => new external_multiple_structure(new external_value(core_user::get_property_type('id'), 'user ID')),
302
            )
303
        );
304
    }
305
 
306
    /**
307
     * Delete users
308
     *
309
     * @throws moodle_exception
310
     * @param array $userids
311
     * @return null
312
     * @since Moodle 2.2
313
     */
314
    public static function delete_users($userids) {
315
        global $CFG, $DB, $USER;
316
        require_once($CFG->dirroot."/user/lib.php");
317
 
318
        // Ensure the current user is allowed to run this function.
319
        $context = context_system::instance();
320
        require_capability('moodle/user:delete', $context);
321
        self::validate_context($context);
322
 
323
        $params = self::validate_parameters(self::delete_users_parameters(), array('userids' => $userids));
324
 
325
        $transaction = $DB->start_delegated_transaction();
326
 
327
        foreach ($params['userids'] as $userid) {
328
            $user = $DB->get_record('user', array('id' => $userid, 'deleted' => 0), '*', MUST_EXIST);
329
            // Must not allow deleting of admins or self!!!
330
            if (is_siteadmin($user)) {
331
                throw new moodle_exception('useradminodelete', 'error');
332
            }
333
            if ($USER->id == $user->id) {
334
                throw new moodle_exception('usernotdeletederror', 'error');
335
            }
336
            user_delete_user($user);
337
        }
338
 
339
        $transaction->allow_commit();
340
 
341
        return null;
342
    }
343
 
344
    /**
345
     * Returns description of method result value
346
     *
347
     * @return null
348
     * @since Moodle 2.2
349
     */
350
    public static function delete_users_returns() {
351
        return null;
352
    }
353
 
354
    /**
355
     * Returns description of method parameters.
356
     *
357
     * @return external_function_parameters
358
     * @since Moodle 3.2
359
     */
360
    public static function update_user_preferences_parameters() {
361
        return new external_function_parameters(
362
            array(
363
                'userid' => new external_value(PARAM_INT, 'id of the user, default to current user', VALUE_DEFAULT, 0),
364
                'emailstop' => new external_value(core_user::get_property_type('emailstop'),
365
                    'Enable or disable notifications for this user', VALUE_DEFAULT, null),
366
                'preferences' => new external_multiple_structure(
367
                    new external_single_structure(
368
                        array(
369
                            'type'  => new external_value(PARAM_RAW, 'The name of the preference'),
370
                            'value' => new external_value(PARAM_RAW, 'The value of the preference, do not set this field if you
371
                                want to remove (unset) the current value.', VALUE_DEFAULT, null),
372
                        )
373
                    ), 'User preferences', VALUE_DEFAULT, array()
374
                )
375
            )
376
        );
377
    }
378
 
379
    /**
380
     * Update the user's preferences.
381
     *
382
     * @param int $userid
383
     * @param bool|null $emailstop
384
     * @param array $preferences
385
     * @return null
386
     * @since Moodle 3.2
387
     */
388
    public static function update_user_preferences($userid = 0, $emailstop = null, $preferences = array()) {
389
        global $USER, $CFG;
390
 
391
        require_once($CFG->dirroot . '/user/lib.php');
392
        require_once($CFG->dirroot . '/user/editlib.php');
393
        require_once($CFG->dirroot . '/message/lib.php');
394
 
395
        if (empty($userid)) {
396
            $userid = $USER->id;
397
        }
398
 
399
        $systemcontext = context_system::instance();
400
        self::validate_context($systemcontext);
401
        $params = array(
402
            'userid' => $userid,
403
            'emailstop' => $emailstop,
404
            'preferences' => $preferences
405
        );
406
        $params = self::validate_parameters(self::update_user_preferences_parameters(), $params);
407
        $preferences = $params['preferences'];
408
 
409
        // Preferences.
410
        if (!empty($preferences)) {
411
            $userpref = ['id' => $userid];
412
            foreach ($preferences as $preference) {
413
 
414
                /*
415
                 * Rename user message provider preferences to avoid orphan settings on old app versions.
416
                 * @todo Remove this "translation" block on MDL-73284.
417
                 */
418
                if (preg_match('/message_provider_.*_loggedin/', $preference['type']) ||
419
                        preg_match('/message_provider_.*_loggedoff/', $preference['type'])) {
420
                    $nameparts = explode('_', $preference['type']);
421
                    array_pop($nameparts);
422
                    $preference['type'] = implode('_', $nameparts).'_enabled';
423
                }
424
 
425
                $userpref['preference_' . $preference['type']] = $preference['value'];
426
            }
427
            useredit_update_user_preference($userpref);
428
        }
429
 
430
        // Check if they want to update the email.
431
        if ($emailstop !== null) {
432
            $otheruser = ($userid == $USER->id) ? $USER : core_user::get_user($userid, '*', MUST_EXIST);
433
            core_user::require_active_user($otheruser);
434
            if (core_message_can_edit_message_profile($otheruser) && $otheruser->emailstop != $emailstop) {
435
                $user = new stdClass();
436
                $user->id = $userid;
437
                $user->emailstop = $emailstop;
438
                user_update_user($user);
439
 
440
                // Update the $USER if we should.
441
                if ($userid == $USER->id) {
442
                    $USER->emailstop = $emailstop;
443
                }
444
            }
445
        }
446
 
447
        return null;
448
    }
449
 
450
    /**
451
     * Returns description of method result value
452
     *
453
     * @return null
454
     * @since Moodle 3.2
455
     */
456
    public static function update_user_preferences_returns() {
457
        return null;
458
    }
459
 
460
    /**
461
     * Returns description of method parameters
462
     *
463
     * @return external_function_parameters
464
     * @since Moodle 2.2
465
     */
466
    public static function update_users_parameters() {
467
        $userfields = [
468
            'id' => new external_value(core_user::get_property_type('id'), 'ID of the user'),
469
            // General.
470
            'username' => new external_value(core_user::get_property_type('username'),
471
                'Username policy is defined in Moodle security config.', VALUE_OPTIONAL, '', NULL_NOT_ALLOWED),
472
            'auth' => new external_value(core_user::get_property_type('auth'), 'Auth plugins include manual, ldap, etc',
473
                VALUE_OPTIONAL, '', NULL_NOT_ALLOWED),
474
            'suspended' => new external_value(core_user::get_property_type('suspended'),
475
                'Suspend user account, either false to enable user login or true to disable it', VALUE_OPTIONAL),
476
            'password' => new external_value(core_user::get_property_type('password'),
477
                'Plain text password consisting of any characters', VALUE_OPTIONAL, '', NULL_NOT_ALLOWED),
478
            'firstname' => new external_value(core_user::get_property_type('firstname'), 'The first name(s) of the user',
479
                VALUE_OPTIONAL, '', NULL_NOT_ALLOWED),
480
            'lastname' => new external_value(core_user::get_property_type('lastname'), 'The family name of the user',
481
                VALUE_OPTIONAL),
482
            'email' => new external_value(core_user::get_property_type('email'), 'A valid and unique email address', VALUE_OPTIONAL,
483
                '', NULL_NOT_ALLOWED),
484
            'maildisplay' => new external_value(core_user::get_property_type('maildisplay'), 'Email visibility', VALUE_OPTIONAL),
485
            'city' => new external_value(core_user::get_property_type('city'), 'Home city of the user', VALUE_OPTIONAL),
486
            'country' => new external_value(core_user::get_property_type('country'),
487
                'Home country code of the user, such as AU or CZ', VALUE_OPTIONAL),
488
            'timezone' => new external_value(core_user::get_property_type('timezone'),
489
                'Timezone code such as Australia/Perth, or 99 for default', VALUE_OPTIONAL),
490
            'description' => new external_value(core_user::get_property_type('description'), 'User profile description, no HTML',
491
                VALUE_OPTIONAL),
492
            // User picture.
493
            'userpicture' => new external_value(PARAM_INT,
494
                'The itemid where the new user picture has been uploaded to, 0 to delete', VALUE_OPTIONAL),
495
            // Additional names.
496
            'firstnamephonetic' => new external_value(core_user::get_property_type('firstnamephonetic'),
497
                'The first name(s) phonetically of the user', VALUE_OPTIONAL),
498
            'lastnamephonetic' => new external_value(core_user::get_property_type('lastnamephonetic'),
499
                'The family name phonetically of the user', VALUE_OPTIONAL),
500
            'middlename' => new external_value(core_user::get_property_type('middlename'), 'The middle name of the user',
501
                VALUE_OPTIONAL),
502
            'alternatename' => new external_value(core_user::get_property_type('alternatename'), 'The alternate name of the user',
503
                VALUE_OPTIONAL),
504
            // Interests.
505
            'interests' => new external_value(PARAM_TEXT, 'User interests (separated by commas)', VALUE_OPTIONAL),
506
            // Optional.
507
            'idnumber' => new external_value(core_user::get_property_type('idnumber'),
508
                'An arbitrary ID code number perhaps from the institution', VALUE_OPTIONAL),
509
            'institution' => new external_value(core_user::get_property_type('institution'), 'Institution', VALUE_OPTIONAL),
510
            'department' => new external_value(core_user::get_property_type('department'), 'Department', VALUE_OPTIONAL),
511
            'phone1' => new external_value(core_user::get_property_type('phone1'), 'Phone', VALUE_OPTIONAL),
512
            'phone2' => new external_value(core_user::get_property_type('phone2'), 'Mobile phone', VALUE_OPTIONAL),
513
            'address' => new external_value(core_user::get_property_type('address'), 'Postal address', VALUE_OPTIONAL),
514
            // Other user preferences stored in the user table.
515
            'lang' => new external_value(core_user::get_property_type('lang'), 'Language code such as "en", must exist on server',
516
                VALUE_OPTIONAL, '', NULL_NOT_ALLOWED),
517
            'calendartype' => new external_value(core_user::get_property_type('calendartype'),
518
                'Calendar type such as "gregorian", must exist on server', VALUE_OPTIONAL, '', NULL_NOT_ALLOWED),
519
            'theme' => new external_value(core_user::get_property_type('theme'),
520
                'Theme name such as "standard", must exist on server', VALUE_OPTIONAL),
521
            'mailformat' => new external_value(core_user::get_property_type('mailformat'),
522
                'Mail format code is 0 for plain text, 1 for HTML etc', VALUE_OPTIONAL),
523
            // Custom user profile fields.
524
            'customfields' => new external_multiple_structure(
525
                new external_single_structure(
526
                    [
527
                        'type'  => new external_value(PARAM_ALPHANUMEXT, 'The name of the custom field'),
528
                        'value' => new external_value(PARAM_RAW, 'The value of the custom field')
529
                    ]
530
                ), 'User custom fields (also known as user profil fields)', VALUE_OPTIONAL),
531
            // User preferences.
532
            'preferences' => new external_multiple_structure(
533
                new external_single_structure(
534
                    [
535
                        'type'  => new external_value(PARAM_RAW, 'The name of the preference'),
536
                        'value' => new external_value(PARAM_RAW, 'The value of the preference')
537
                    ]
538
                ), 'User preferences', VALUE_OPTIONAL),
539
        ];
540
        return new external_function_parameters(
541
            [
542
                'users' => new external_multiple_structure(
543
                    new external_single_structure($userfields)
544
                )
545
            ]
546
        );
547
    }
548
 
549
    /**
550
     * Update users
551
     *
552
     * @param array $users
553
     * @return null
554
     * @since Moodle 2.2
555
     */
556
    public static function update_users($users) {
557
        global $CFG, $DB, $USER;
558
        require_once($CFG->dirroot."/user/lib.php");
559
        require_once($CFG->dirroot."/user/profile/lib.php"); // Required for customfields related function.
560
        require_once($CFG->dirroot.'/user/editlib.php');
561
 
562
        // Ensure the current user is allowed to run this function.
563
        $context = context_system::instance();
564
        require_capability('moodle/user:update', $context);
565
        self::validate_context($context);
566
 
567
        $params = self::validate_parameters(self::update_users_parameters(), array('users' => $users));
568
 
569
        $filemanageroptions = array('maxbytes' => $CFG->maxbytes,
570
                'subdirs'        => 0,
571
                'maxfiles'       => 1,
572
                'accepted_types' => 'optimised_image');
573
 
574
        $warnings = array();
575
        foreach ($params['users'] as $user) {
576
            // Catch any exception while updating a user and return it as a warning.
577
            try {
578
                $transaction = $DB->start_delegated_transaction();
579
 
580
                // First check the user exists.
581
                if (!$existinguser = core_user::get_user($user['id'])) {
582
                    throw new moodle_exception('invaliduserid', '', '', null,
583
                            'Invalid user ID');
584
                }
585
                // Check if we are trying to update an admin.
586
                if ($existinguser->id != $USER->id and is_siteadmin($existinguser) and !is_siteadmin($USER)) {
587
                    throw new moodle_exception('usernotupdatedadmin', '', '', null,
588
                            'Cannot update admin accounts');
589
                }
590
                // Other checks (deleted, remote or guest users).
591
                if ($existinguser->deleted) {
592
                    throw new moodle_exception('usernotupdateddeleted', '', '', null,
593
                            'User is a deleted user');
594
                }
595
                if (is_mnet_remote_user($existinguser)) {
596
                    throw new moodle_exception('usernotupdatedremote', '', '', null,
597
                            'User is a remote user');
598
                }
599
                if (isguestuser($existinguser->id)) {
600
                    throw new moodle_exception('usernotupdatedguest', '', '', null,
601
                            'Cannot update guest account');
602
                }
603
                // Check duplicated emails.
604
                if (isset($user['email']) && $user['email'] !== $existinguser->email) {
605
                    if (!validate_email($user['email'])) {
606
                        throw new moodle_exception('useremailinvalid', '', '', null,
607
                                'Invalid email address');
608
                    } else if (empty($CFG->allowaccountssameemail)) {
609
                        // Make a case-insensitive query for the given email address
610
                        // and make sure to exclude the user being updated.
611
                        $select = $DB->sql_equal('email', ':email', false) . ' AND mnethostid = :mnethostid AND id <> :userid';
612
                        $params = array(
613
                            'email' => $user['email'],
614
                            'mnethostid' => $CFG->mnet_localhost_id,
615
                            'userid' => $user['id']
616
                        );
617
                        // Skip if there are other user(s) that already have the same email.
618
                        if ($DB->record_exists_select('user', $select, $params)) {
619
                            throw new moodle_exception('useremailduplicate', '', '', null,
620
                                    'Duplicate email address');
621
                        }
622
                    }
623
                }
624
 
625
                user_update_user($user, true, false);
626
 
627
                $userobject = (object)$user;
628
 
629
                // Update user picture if it was specified for this user.
630
                if (empty($CFG->disableuserimages) && isset($user['userpicture'])) {
631
                    $userobject->deletepicture = null;
632
 
633
                    if ($user['userpicture'] == 0) {
634
                        $userobject->deletepicture = true;
635
                    } else {
636
                        $userobject->imagefile = $user['userpicture'];
637
                    }
638
 
639
                    core_user::update_picture($userobject, $filemanageroptions);
640
                }
641
 
642
                // Update user interests.
643
                if (!empty($user['interests'])) {
644
                    $trimmedinterests = array_map('trim', explode(',', $user['interests']));
645
                    $interests = array_filter($trimmedinterests, function($value) {
646
                        return !empty($value);
647
                    });
648
                    useredit_update_interests($userobject, $interests);
649
                }
650
 
651
                // Update user custom fields.
652
                if (!empty($user['customfields'])) {
653
 
654
                    foreach ($user['customfields'] as $customfield) {
655
                        // Profile_save_data() saves profile file it's expecting a user with the correct id,
656
                        // and custom field to be named profile_field_"shortname".
657
                        $user["profile_field_".$customfield['type']] = $customfield['value'];
658
                    }
659
                    profile_save_data((object) $user);
660
                }
661
 
662
                // Trigger event.
663
                \core\event\user_updated::create_from_userid($user['id'])->trigger();
664
 
665
                // Preferences.
666
                if (!empty($user['preferences'])) {
667
                    $userpref = clone($existinguser);
668
                    foreach ($user['preferences'] as $preference) {
669
                        $userpref->{'preference_'.$preference['type']} = $preference['value'];
670
                    }
671
                    useredit_update_user_preference($userpref);
672
                }
673
                if (isset($user['suspended']) and $user['suspended']) {
674
                    \core\session\manager::kill_user_sessions($user['id']);
675
                }
676
 
677
                $transaction->allow_commit();
678
            } catch (Exception $e) {
679
                try {
680
                    $transaction->rollback($e);
681
                } catch (Exception $e) {
682
                    $warning = [];
683
                    $warning['item'] = 'user';
684
                    $warning['itemid'] = $user['id'];
685
                    if ($e instanceof moodle_exception) {
686
                        $warning['warningcode'] = $e->errorcode;
687
                    } else {
688
                        $warning['warningcode'] = $e->getCode();
689
                    }
690
                    $warning['message'] = $e->getMessage();
691
                    $warnings[] = $warning;
692
                }
693
            }
694
        }
695
 
696
        return ['warnings' => $warnings];
697
    }
698
 
699
    /**
700
     * Returns description of method result value
701
     *
702
     * @return external_description
703
     * @since Moodle 2.2
704
     */
705
    public static function update_users_returns() {
706
        return new external_single_structure(
707
            array(
708
                'warnings' => new external_warnings()
709
            )
710
        );
711
    }
712
 
713
    /**
714
     * Returns description of method parameters
715
     *
716
     * @return external_function_parameters
717
     * @since Moodle 2.4
718
     */
719
    public static function get_users_by_field_parameters() {
720
        return new external_function_parameters(
721
            array(
722
                'field' => new external_value(PARAM_ALPHA, 'the search field can be
723
                    \'id\' or \'idnumber\' or \'username\' or \'email\''),
724
                'values' => new external_multiple_structure(
725
                        new external_value(PARAM_RAW, 'the value to match'))
726
            )
727
        );
728
    }
729
 
730
    /**
731
     * Get user information for a unique field.
732
     *
733
     * @throws coding_exception
734
     * @throws invalid_parameter_exception
735
     * @param string $field
736
     * @param array $values
737
     * @return array An array of arrays containg user profiles.
738
     * @since Moodle 2.4
739
     */
740
    public static function get_users_by_field($field, $values) {
741
        global $CFG, $USER, $DB;
742
        require_once($CFG->dirroot . "/user/lib.php");
743
 
744
        $params = self::validate_parameters(self::get_users_by_field_parameters(),
745
                array('field' => $field, 'values' => $values));
746
 
747
        // This array will keep all the users that are allowed to be searched,
748
        // according to the current user's privileges.
749
        $cleanedvalues = array();
750
 
751
        switch ($field) {
752
            case 'id':
753
                $paramtype = core_user::get_property_type('id');
754
                break;
755
            case 'idnumber':
756
                $paramtype = core_user::get_property_type('idnumber');
757
                break;
758
            case 'username':
759
                $paramtype = core_user::get_property_type('username');
760
                break;
761
            case 'email':
762
                $paramtype = core_user::get_property_type('email');
763
                break;
764
            default:
765
                throw new coding_exception('invalid field parameter',
766
                        'The search field \'' . $field . '\' is not supported, look at the web service documentation');
767
        }
768
 
769
        // Clean the values.
770
        foreach ($values as $value) {
771
            $cleanedvalue = clean_param($value, $paramtype);
772
            if ( $value != $cleanedvalue) {
773
                throw new invalid_parameter_exception('The field \'' . $field .
774
                        '\' value is invalid: ' . $value . '(cleaned value: '.$cleanedvalue.')');
775
            }
776
            $cleanedvalues[] = $cleanedvalue;
777
        }
778
 
779
        // Retrieve the users.
780
        $users = $DB->get_records_list('user', $field, $cleanedvalues, 'id');
781
 
782
        $context = context_system::instance();
783
        self::validate_context($context);
784
 
785
        // Finally retrieve each users information.
786
        $returnedusers = array();
787
        foreach ($users as $user) {
788
            $userdetails = user_get_user_details_courses($user);
789
 
790
            // Return the user only if the searched field is returned.
791
            // Otherwise it means that the $USER was not allowed to search the returned user.
792
            if (!empty($userdetails) and !empty($userdetails[$field])) {
793
                $returnedusers[] = $userdetails;
794
            }
795
        }
796
 
797
        return $returnedusers;
798
    }
799
 
800
    /**
801
     * Returns description of method result value
802
     *
803
     * @return external_multiple_structure
804
     * @since Moodle 2.4
805
     */
806
    public static function get_users_by_field_returns() {
807
        return new external_multiple_structure(self::user_description());
808
    }
809
 
810
 
811
    /**
812
     * Returns description of get_users() parameters.
813
     *
814
     * @return external_function_parameters
815
     * @since Moodle 2.5
816
     */
817
    public static function get_users_parameters() {
818
        return new external_function_parameters(
819
            array(
820
                'criteria' => new external_multiple_structure(
821
                    new external_single_structure(
822
                        array(
823
                            'key' => new external_value(PARAM_ALPHA, 'the user column to search, expected keys (value format) are:
824
                                "id" (int) matching user id,
825
                                "lastname" (string) user last name (Note: you can use % for searching but it may be considerably slower!),
826
                                "firstname" (string) user first name (Note: you can use % for searching but it may be considerably slower!),
827
                                "idnumber" (string) matching user idnumber,
828
                                "username" (string) matching user username,
829
                                "email" (string) user email (Note: you can use % for searching but it may be considerably slower!),
830
                                "auth" (string) matching user auth plugin'),
831
                            'value' => new external_value(PARAM_RAW, 'the value to search')
832
                        )
833
                    ), 'the key/value pairs to be considered in user search. Values can not be empty.
834
                        Specify different keys only once (fullname => \'user1\', auth => \'manual\', ...) -
835
                        key occurences are forbidden.
836
                        The search is executed with AND operator on the criterias. Invalid criterias (keys) are ignored,
837
                        the search is still executed on the valid criterias.
838
                        You can search without criteria, but the function is not designed for it.
839
                        It could very slow or timeout. The function is designed to search some specific users.'
840
                )
841
            )
842
        );
843
    }
844
 
845
    /**
846
     * Retrieve matching user.
847
     *
848
     * @throws moodle_exception
849
     * @param array $criteria the allowed array keys are id/lastname/firstname/idnumber/username/email/auth.
850
     * @return array An array of arrays containing user profiles.
851
     * @since Moodle 2.5
852
     */
853
    public static function get_users($criteria = array()) {
854
        global $CFG, $USER, $DB;
855
 
856
        require_once($CFG->dirroot . "/user/lib.php");
857
 
858
        $params = self::validate_parameters(self::get_users_parameters(),
859
                array('criteria' => $criteria));
860
 
861
        // Validate the criteria and retrieve the users.
862
        $users = array();
863
        $warnings = array();
864
        $sqlparams = array();
865
        $usedkeys = array();
866
 
867
        // Do not retrieve deleted users.
868
        $sql = ' deleted = 0';
869
 
870
        foreach ($params['criteria'] as $criteriaindex => $criteria) {
871
 
872
            // Check that the criteria has never been used.
873
            if (array_key_exists($criteria['key'], $usedkeys)) {
874
                throw new moodle_exception('keyalreadyset', '', '', null, 'The key ' . $criteria['key'] . ' can only be sent once');
875
            } else {
876
                $usedkeys[$criteria['key']] = true;
877
            }
878
 
879
            $invalidcriteria = false;
880
            // Clean the parameters.
881
            $paramtype = PARAM_RAW;
882
            switch ($criteria['key']) {
883
                case 'id':
884
                    $paramtype = core_user::get_property_type('id');
885
                    break;
886
                case 'idnumber':
887
                    $paramtype = core_user::get_property_type('idnumber');
888
                    break;
889
                case 'username':
890
                    $paramtype = core_user::get_property_type('username');
891
                    break;
892
                case 'email':
893
                    // We use PARAM_RAW to allow searches with %.
894
                    $paramtype = core_user::get_property_type('email');
895
                    break;
896
                case 'auth':
897
                    $paramtype = core_user::get_property_type('auth');
898
                    break;
899
                case 'lastname':
900
                case 'firstname':
901
                    $paramtype = core_user::get_property_type('firstname');
902
                    break;
903
                default:
904
                    // Send back a warning that this search key is not supported in this version.
905
                    // This warning will make the function extandable without breaking clients.
906
                    $warnings[] = array(
907
                        'item' => $criteria['key'],
908
                        'warningcode' => 'invalidfieldparameter',
909
                        'message' =>
910
                            'The search key \'' . $criteria['key'] . '\' is not supported, look at the web service documentation'
911
                    );
912
                    // Do not add this invalid criteria to the created SQL request.
913
                    $invalidcriteria = true;
914
                    unset($params['criteria'][$criteriaindex]);
915
                    break;
916
            }
917
 
918
            if (!$invalidcriteria) {
919
                $cleanedvalue = clean_param($criteria['value'], $paramtype);
920
 
921
                $sql .= ' AND ';
922
 
923
                // Create the SQL.
924
                switch ($criteria['key']) {
925
                    case 'id':
926
                    case 'idnumber':
927
                    case 'username':
928
                    case 'auth':
929
                        $sql .= $criteria['key'] . ' = :' . $criteria['key'];
930
                        $sqlparams[$criteria['key']] = $cleanedvalue;
931
                        break;
932
                    case 'email':
933
                    case 'lastname':
934
                    case 'firstname':
935
                        $sql .= $DB->sql_like($criteria['key'], ':' . $criteria['key'], false);
936
                        $sqlparams[$criteria['key']] = $cleanedvalue;
937
                        break;
938
                    default:
939
                        break;
940
                }
941
            }
942
        }
943
 
944
        $users = $DB->get_records_select('user', $sql, $sqlparams, 'id ASC');
945
 
946
        // Finally retrieve each users information.
947
        $returnedusers = array();
948
        foreach ($users as $user) {
949
            $userdetails = user_get_user_details_courses($user);
950
 
951
            // Return the user only if all the searched fields are returned.
952
            // Otherwise it means that the $USER was not allowed to search the returned user.
953
            if (!empty($userdetails)) {
954
                $validuser = true;
955
 
956
                foreach ($params['criteria'] as $criteria) {
957
                    if (empty($userdetails[$criteria['key']])) {
958
                        $validuser = false;
959
                    }
960
                }
961
 
962
                if ($validuser) {
963
                    $returnedusers[] = $userdetails;
964
                }
965
            }
966
        }
967
 
968
        return array('users' => $returnedusers, 'warnings' => $warnings);
969
    }
970
 
971
    /**
972
     * Returns description of get_users result value.
973
     *
974
     * @return external_description
975
     * @since Moodle 2.5
976
     */
977
    public static function get_users_returns() {
978
        return new external_single_structure(
979
            array('users' => new external_multiple_structure(
980
                                self::user_description()
981
                             ),
982
                  'warnings' => new external_warnings('always set to \'key\'', 'faulty key name')
983
            )
984
        );
985
    }
986
 
987
    /**
988
     * Returns description of method parameters
989
     *
990
     * @return external_function_parameters
991
     * @since Moodle 2.2
992
     */
993
    public static function get_course_user_profiles_parameters() {
994
        return new external_function_parameters(
995
            array(
996
                'userlist' => new external_multiple_structure(
997
                    new external_single_structure(
998
                        array(
999
                            'userid'    => new external_value(core_user::get_property_type('id'), 'userid'),
1000
                            'courseid'    => new external_value(PARAM_INT, 'courseid'),
1001
                        )
1002
                    )
1003
                )
1004
            )
1005
        );
1006
    }
1007
 
1008
    /**
1009
     * Get course participant's details
1010
     *
1011
     * @param array $userlist  array of user ids and according course ids
1012
     * @return array An array of arrays describing course participants
1013
     * @since Moodle 2.2
1014
     */
1015
    public static function get_course_user_profiles($userlist) {
1016
        global $CFG, $USER, $DB;
1017
        require_once($CFG->dirroot . "/user/lib.php");
1018
        $params = self::validate_parameters(self::get_course_user_profiles_parameters(), array('userlist' => $userlist));
1019
 
1020
        $userids = array();
1021
        $courseids = array();
1022
        foreach ($params['userlist'] as $value) {
1023
            $userids[] = $value['userid'];
1024
            $courseids[$value['userid']] = $value['courseid'];
1025
        }
1026
 
1027
        // Cache all courses.
1028
        $courses = array();
1029
        list($sqlcourseids, $params) = $DB->get_in_or_equal(array_unique($courseids), SQL_PARAMS_NAMED);
1030
        $cselect = ', ' . context_helper::get_preload_record_columns_sql('ctx');
1031
        $cjoin = "LEFT JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :contextlevel)";
1032
        $params['contextlevel'] = CONTEXT_COURSE;
1033
        $coursesql = "SELECT c.* $cselect
1034
                        FROM {course} c $cjoin
1035
                       WHERE c.id $sqlcourseids";
1036
        $rs = $DB->get_recordset_sql($coursesql, $params);
1037
        foreach ($rs as $course) {
1038
            // Adding course contexts to cache.
1039
            context_helper::preload_from_record($course);
1040
            // Cache courses.
1041
            $courses[$course->id] = $course;
1042
        }
1043
        $rs->close();
1044
 
1045
        list($sqluserids, $params) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
1046
        $uselect = ', ' . context_helper::get_preload_record_columns_sql('ctx');
1047
        $ujoin = "LEFT JOIN {context} ctx ON (ctx.instanceid = u.id AND ctx.contextlevel = :contextlevel)";
1048
        $params['contextlevel'] = CONTEXT_USER;
1049
        $usersql = "SELECT u.* $uselect
1050
                      FROM {user} u $ujoin
1051
                     WHERE u.id $sqluserids";
1052
        $users = $DB->get_recordset_sql($usersql, $params);
1053
        $result = array();
1054
        foreach ($users as $user) {
1055
            if (!empty($user->deleted)) {
1056
                continue;
1057
            }
1058
            context_helper::preload_from_record($user);
1059
            $course = $courses[$courseids[$user->id]];
1060
            $context = context_course::instance($courseids[$user->id], IGNORE_MISSING);
1061
            self::validate_context($context);
1062
            if ($userarray = user_get_user_details($user, $course)) {
1063
                $result[] = $userarray;
1064
            }
1065
        }
1066
 
1067
        $users->close();
1068
 
1069
        return $result;
1070
    }
1071
 
1072
    /**
1073
     * Returns description of method result value
1074
     *
1075
     * @return external_description
1076
     * @since Moodle 2.2
1077
     */
1078
    public static function get_course_user_profiles_returns() {
1079
        $additionalfields = array(
1080
            'groups' => new external_multiple_structure(
1081
                new external_single_structure(
1082
                    array(
1083
                        'id'  => new external_value(PARAM_INT, 'group id'),
1084
                        'name' => new external_value(PARAM_RAW, 'group name'),
1085
                        'description' => new external_value(PARAM_RAW, 'group description'),
1086
                        'descriptionformat' => new external_format_value('description'),
1087
                    )
1088
                ), 'user groups', VALUE_OPTIONAL),
1089
            'roles' => new external_multiple_structure(
1090
                new external_single_structure(
1091
                    array(
1092
                        'roleid'       => new external_value(PARAM_INT, 'role id'),
1093
                        'name'         => new external_value(PARAM_RAW, 'role name'),
1094
                        'shortname'    => new external_value(PARAM_ALPHANUMEXT, 'role shortname'),
1095
                        'sortorder'    => new external_value(PARAM_INT, 'role sortorder')
1096
                    )
1097
                ), 'user roles', VALUE_OPTIONAL),
1098
            'enrolledcourses' => new external_multiple_structure(
1099
                new external_single_structure(
1100
                    array(
1101
                        'id'  => new external_value(PARAM_INT, 'Id of the course'),
1102
                        'fullname'  => new external_value(PARAM_RAW, 'Fullname of the course'),
1103
                        'shortname' => new external_value(PARAM_RAW, 'Shortname of the course')
1104
                    )
1105
                ), 'Courses where the user is enrolled - limited by which courses the user is able to see', VALUE_OPTIONAL)
1106
        );
1107
 
1108
        return new external_multiple_structure(self::user_description($additionalfields));
1109
    }
1110
 
1111
    /**
1112
     * Create user return value description.
1113
     *
1114
     * @param array $additionalfields some additional field
1115
     * @return external_description
1116
     */
1117
    public static function user_description($additionalfields = array()) {
1118
        $userfields = array(
1119
            'id'    => new external_value(core_user::get_property_type('id'), 'ID of the user'),
1120
            'username'    => new external_value(core_user::get_property_type('username'), 'The username', VALUE_OPTIONAL),
1121
            'firstname'   => new external_value(core_user::get_property_type('firstname'), 'The first name(s) of the user', VALUE_OPTIONAL),
1122
            'lastname'    => new external_value(core_user::get_property_type('lastname'), 'The family name of the user', VALUE_OPTIONAL),
1123
            'fullname'    => new external_value(core_user::get_property_type('firstname'), 'The fullname of the user'),
1124
            'email'       => new external_value(core_user::get_property_type('email'), 'An email address - allow email as root@localhost', VALUE_OPTIONAL),
1125
            'address'     => new external_value(core_user::get_property_type('address'), 'Postal address', VALUE_OPTIONAL),
1126
            'phone1'      => new external_value(core_user::get_property_type('phone1'), 'Phone 1', VALUE_OPTIONAL),
1127
            'phone2'      => new external_value(core_user::get_property_type('phone2'), 'Phone 2', VALUE_OPTIONAL),
1128
            'department'  => new external_value(core_user::get_property_type('department'), 'department', VALUE_OPTIONAL),
1129
            'institution' => new external_value(core_user::get_property_type('institution'), 'institution', VALUE_OPTIONAL),
1130
            'idnumber'    => new external_value(core_user::get_property_type('idnumber'), 'An arbitrary ID code number perhaps from the institution', VALUE_OPTIONAL),
1131
            'interests'   => new external_value(PARAM_TEXT, 'user interests (separated by commas)', VALUE_OPTIONAL),
1132
            'firstaccess' => new external_value(core_user::get_property_type('firstaccess'), 'first access to the site (0 if never)', VALUE_OPTIONAL),
1133
            'lastaccess'  => new external_value(core_user::get_property_type('lastaccess'), 'last access to the site (0 if never)', VALUE_OPTIONAL),
1134
            'auth'        => new external_value(core_user::get_property_type('auth'), 'Auth plugins include manual, ldap, etc', VALUE_OPTIONAL),
1135
            'suspended'   => new external_value(core_user::get_property_type('suspended'), 'Suspend user account, either false to enable user login or true to disable it', VALUE_OPTIONAL),
1136
            'confirmed'   => new external_value(core_user::get_property_type('confirmed'), 'Active user: 1 if confirmed, 0 otherwise', VALUE_OPTIONAL),
1137
            'lang'        => new external_value(core_user::get_property_type('lang'), 'Language code such as "en", must exist on server', VALUE_OPTIONAL),
1138
            'calendartype' => new external_value(core_user::get_property_type('calendartype'), 'Calendar type such as "gregorian", must exist on server', VALUE_OPTIONAL),
1139
            'theme'       => new external_value(core_user::get_property_type('theme'), 'Theme name such as "standard", must exist on server', VALUE_OPTIONAL),
1140
            'timezone'    => new external_value(core_user::get_property_type('timezone'), 'Timezone code such as Australia/Perth, or 99 for default', VALUE_OPTIONAL),
1141
            'mailformat'  => new external_value(core_user::get_property_type('mailformat'), 'Mail format code is 0 for plain text, 1 for HTML etc', VALUE_OPTIONAL),
1142
            'trackforums'  => new external_value(core_user::get_property_type('trackforums'),
1143
                'Whether the user is tracking forums.', VALUE_OPTIONAL),
1144
            'description' => new external_value(core_user::get_property_type('description'), 'User profile description', VALUE_OPTIONAL),
1145
            'descriptionformat' => new external_format_value(core_user::get_property_type('descriptionformat'), VALUE_OPTIONAL),
1146
            'city'        => new external_value(core_user::get_property_type('city'), 'Home city of the user', VALUE_OPTIONAL),
1147
            'country'     => new external_value(core_user::get_property_type('country'), 'Home country code of the user, such as AU or CZ', VALUE_OPTIONAL),
1148
            'profileimageurlsmall' => new external_value(PARAM_URL, 'User image profile URL - small version'),
1149
            'profileimageurl' => new external_value(PARAM_URL, 'User image profile URL - big version'),
1150
            'customfields' => new external_multiple_structure(
1151
                new external_single_structure(
1152
                    array(
1153
                        'type'  => new external_value(PARAM_ALPHANUMEXT, 'The type of the custom field - text field, checkbox...'),
1154
                        'value' => new external_value(PARAM_RAW, 'The value of the custom field (as stored in the database)'),
1155
                        'displayvalue' => new external_value(PARAM_RAW, 'The value of the custom field for display',
1156
                            VALUE_OPTIONAL),
1157
                        'name' => new external_value(PARAM_RAW, 'The name of the custom field'),
1158
                        'shortname' => new external_value(PARAM_RAW, 'The shortname of the custom field - to be able to build the field class in the code'),
1159
                    )
1160
                ), 'User custom fields (also known as user profile fields)', VALUE_OPTIONAL),
1161
            'preferences' => new external_multiple_structure(
1162
                new external_single_structure(
1163
                    array(
1164
                        'name'  => new external_value(PARAM_RAW, 'The name of the preferences'),
1165
                        'value' => new external_value(PARAM_RAW, 'The value of the preference'),
1166
                    )
1167
            ), 'Users preferences', VALUE_OPTIONAL)
1168
        );
1169
        if (!empty($additionalfields)) {
1170
            $userfields = array_merge($userfields, $additionalfields);
1171
        }
1172
        return new external_single_structure($userfields);
1173
    }
1174
 
1175
    /**
1176
     * Returns description of method parameters
1177
     *
1178
     * @return external_function_parameters
1179
     * @since Moodle 2.6
1180
     */
1181
    public static function add_user_private_files_parameters() {
1182
        return new external_function_parameters(
1183
            array(
1184
                'draftid' => new external_value(PARAM_INT, 'draft area id')
1185
            )
1186
        );
1187
    }
1188
 
1189
    /**
1190
     * Copy files from a draft area to users private files area.
1191
     *
1192
     * @throws invalid_parameter_exception
1193
     * @throws moodle_exception
1194
     * @param int $draftid Id of a draft area containing files.
1195
     * @return array An array of warnings
1196
     * @since Moodle 2.6
1197
     */
1198
    public static function add_user_private_files($draftid) {
1199
        global $CFG, $USER;
1200
        require_once($CFG->libdir . "/filelib.php");
1201
 
1202
        $params = self::validate_parameters(self::add_user_private_files_parameters(), array('draftid' => $draftid));
1203
 
1204
        if (isguestuser()) {
1205
            throw new invalid_parameter_exception('Guest users cannot upload files');
1206
        }
1207
 
1208
        $context = context_user::instance($USER->id);
1209
        require_capability('moodle/user:manageownfiles', $context);
1210
 
1211
        $maxbytes = $CFG->userquota;
1212
        $maxareabytes = $CFG->userquota;
1213
        if (has_capability('moodle/user:ignoreuserquota', $context)) {
1214
            $maxbytes = USER_CAN_IGNORE_FILE_SIZE_LIMITS;
1215
            $maxareabytes = FILE_AREA_MAX_BYTES_UNLIMITED;
1216
        } else {
1217
            // Get current used space for this user (private files only).
1218
            $fileareainfo = file_get_file_area_info($context->id, 'user', 'private');
1219
            $usedspace = $fileareainfo['filesize_without_references'];
1220
 
1221
            // Get the total size of the new files we want to add to private files.
1222
            $newfilesinfo = file_get_draft_area_info($params['draftid']);
1223
 
1224
            if (($newfilesinfo['filesize_without_references'] + $usedspace) > $maxareabytes) {
1225
                throw new moodle_exception('maxareabytes');
1226
            }
1227
        }
1228
 
1229
        $options = array('subdirs' => 1,
1230
                         'maxbytes' => $maxbytes,
1231
                         'maxfiles' => -1,
1232
                         'areamaxbytes' => $maxareabytes);
1233
 
1234
        file_merge_files_from_draft_area_into_filearea($draftid, $context->id, 'user', 'private', 0, $options);
1235
 
1236
        return null;
1237
    }
1238
 
1239
    /**
1240
     * Returns description of method result value
1241
     *
1242
     * @return external_description
1243
     * @since Moodle 2.2
1244
     */
1245
    public static function add_user_private_files_returns() {
1246
        return null;
1247
    }
1248
 
1249
    /**
1250
     * Returns description of method parameters.
1251
     *
1252
     * @return external_function_parameters
1253
     * @since Moodle 2.6
1254
     */
1255
    public static function add_user_device_parameters() {
1256
        return new external_function_parameters(
1257
            array(
1258
                'appid'     => new external_value(PARAM_NOTAGS, 'the app id, usually something like com.moodle.moodlemobile'),
1259
                'name'      => new external_value(PARAM_NOTAGS, 'the device name, \'occam\' or \'iPhone\' etc.'),
1260
                'model'     => new external_value(PARAM_NOTAGS, 'the device model \'Nexus4\' or \'iPad1,1\' etc.'),
1261
                'platform'  => new external_value(PARAM_NOTAGS, 'the device platform \'iOS\' or \'Android\' etc.'),
1262
                'version'   => new external_value(PARAM_NOTAGS, 'the device version \'6.1.2\' or \'4.2.2\' etc.'),
1263
                'pushid'    => new external_value(PARAM_RAW, 'the device PUSH token/key/identifier/registration id'),
1264
                'uuid'      => new external_value(PARAM_RAW, 'the device UUID'),
1265
                'publickey' => new external_value(PARAM_RAW, 'the app generated public key', VALUE_DEFAULT, null),
1266
            )
1267
        );
1268
    }
1269
 
1270
    /**
1271
     * Add a user device in Moodle database (for PUSH notifications usually).
1272
     *
1273
     * @throws moodle_exception
1274
     * @param string $appid The app id, usually something like com.moodle.moodlemobile.
1275
     * @param string $name The device name, occam or iPhone etc.
1276
     * @param string $model The device model Nexus4 or iPad1.1 etc.
1277
     * @param string $platform The device platform iOs or Android etc.
1278
     * @param string $version The device version 6.1.2 or 4.2.2 etc.
1279
     * @param string $pushid The device PUSH token/key/identifier/registration id.
1280
     * @param string $uuid The device UUID.
1281
     * @param string $publickey The app generated public key
1282
     * @return array List of possible warnings.
1283
     * @since Moodle 2.6
1284
     */
1285
    public static function add_user_device($appid, $name, $model, $platform, $version, $pushid, $uuid, $publickey = null) {
1286
        global $CFG, $USER, $DB;
1287
        require_once($CFG->dirroot . "/user/lib.php");
1288
 
1289
        $params = self::validate_parameters(self::add_user_device_parameters(),
1290
                array('appid' => $appid,
1291
                      'name' => $name,
1292
                      'model' => $model,
1293
                      'platform' => $platform,
1294
                      'version' => $version,
1295
                      'pushid' => $pushid,
1296
                      'uuid' => $uuid,
1297
                      'publickey' => $publickey,
1298
                      ));
1299
 
1300
        $warnings = array();
1301
 
1302
        // Prevent duplicate keys for users.
1303
        if ($DB->get_record('user_devices', array('pushid' => $params['pushid'], 'userid' => $USER->id))) {
1304
            $warnings['warning'][] = array(
1305
                'item' => $params['pushid'],
1306
                'warningcode' => 'existingkeyforthisuser',
1307
                'message' => 'This key is already stored for this user'
1308
            );
1309
            return $warnings;
1310
        }
1311
 
1312
        // Notice that we can have multiple devices because previously it was allowed to have repeated ones.
1313
        // Since we don't have a clear way to decide which one is the more appropiate, we update all.
1314
        if ($userdevices = $DB->get_records('user_devices', array('uuid' => $params['uuid'],
1315
                'appid' => $params['appid'], 'userid' => $USER->id))) {
1316
 
1317
            foreach ($userdevices as $userdevice) {
1318
                $userdevice->version    = $params['version'];   // Maybe the user upgraded the device.
1319
                $userdevice->pushid     = $params['pushid'];
1320
                $userdevice->publickey  = $params['publickey'];
1321
                $userdevice->timemodified  = time();
1322
                $DB->update_record('user_devices', $userdevice);
1323
            }
1324
 
1325
        } else {
1326
            $userdevice = new stdclass;
1327
            $userdevice->userid     = $USER->id;
1328
            $userdevice->appid      = $params['appid'];
1329
            $userdevice->name       = $params['name'];
1330
            $userdevice->model      = $params['model'];
1331
            $userdevice->platform   = $params['platform'];
1332
            $userdevice->version    = $params['version'];
1333
            $userdevice->pushid     = $params['pushid'];
1334
            $userdevice->uuid       = $params['uuid'];
1335
            $userdevice->publickey  = $params['publickey'];
1336
            $userdevice->timecreated  = time();
1337
            $userdevice->timemodified = $userdevice->timecreated;
1338
 
1339
            if (!$DB->insert_record('user_devices', $userdevice)) {
1340
                throw new moodle_exception("There was a problem saving in the database the device with key: " . $params['pushid']);
1341
            }
1342
        }
1343
 
1344
        return $warnings;
1345
    }
1346
 
1347
    /**
1348
     * Returns description of method result value.
1349
     *
1350
     * @return external_multiple_structure
1351
     * @since Moodle 2.6
1352
     */
1353
    public static function add_user_device_returns() {
1354
        return new external_multiple_structure(
1355
           new external_warnings()
1356
        );
1357
    }
1358
 
1359
    /**
1360
     * Returns description of method parameters.
1361
     *
1362
     * @return external_function_parameters
1363
     * @since Moodle 2.9
1364
     */
1365
    public static function remove_user_device_parameters() {
1366
        return new external_function_parameters(
1367
            array(
1368
                'uuid'  => new external_value(PARAM_RAW, 'the device UUID'),
1369
                'appid' => new external_value(PARAM_NOTAGS,
1370
                                                'the app id, if empty devices matching the UUID for the user will be removed',
1371
                                                VALUE_DEFAULT, ''),
1372
            )
1373
        );
1374
    }
1375
 
1376
    /**
1377
     * Remove a user device from the Moodle database (for PUSH notifications usually).
1378
     *
1379
     * @param string $uuid The device UUID.
1380
     * @param string $appid The app id, opitonal parameter. If empty all the devices fmatching the UUID or the user will be removed.
1381
     * @return array List of possible warnings and removal status.
1382
     * @since Moodle 2.9
1383
     */
1384
    public static function remove_user_device($uuid, $appid = "") {
1385
        global $CFG;
1386
        require_once($CFG->dirroot . "/user/lib.php");
1387
 
1388
        $params = self::validate_parameters(self::remove_user_device_parameters(), array('uuid' => $uuid, 'appid' => $appid));
1389
 
1390
        $context = context_system::instance();
1391
        self::validate_context($context);
1392
 
1393
        // Warnings array, it can be empty at the end but is mandatory.
1394
        $warnings = array();
1395
 
1396
        $removed = user_remove_user_device($params['uuid'], $params['appid']);
1397
 
1398
        if (!$removed) {
1399
            $warnings[] = array(
1400
                'item' => $params['uuid'],
1401
                'warningcode' => 'devicedoesnotexist',
1402
                'message' => 'The device doesn\'t exists in the database'
1403
            );
1404
        }
1405
 
1406
        $result = array(
1407
            'removed' => $removed,
1408
            'warnings' => $warnings
1409
        );
1410
 
1411
        return $result;
1412
    }
1413
 
1414
    /**
1415
     * Returns description of method result value.
1416
     *
1417
     * @return external_multiple_structure
1418
     * @since Moodle 2.9
1419
     */
1420
    public static function remove_user_device_returns() {
1421
        return new external_single_structure(
1422
            array(
1423
                'removed' => new external_value(PARAM_BOOL, 'True if removed, false if not removed because it doesn\'t exists'),
1424
                'warnings' => new external_warnings(),
1425
            )
1426
        );
1427
    }
1428
 
1429
    /**
1430
     * Returns description of method parameters
1431
     *
1432
     * @return external_function_parameters
1433
     * @since Moodle 2.9
1434
     */
1435
    public static function view_user_list_parameters() {
1436
        return new external_function_parameters(
1437
            array(
1438
                'courseid' => new external_value(PARAM_INT, 'id of the course, 0 for site')
1439
            )
1440
        );
1441
    }
1442
 
1443
    /**
1444
     * Trigger the user_list_viewed event.
1445
     *
1446
     * @param int $courseid id of course
1447
     * @return array of warnings and status result
1448
     * @since Moodle 2.9
1449
     * @throws moodle_exception
1450
     */
1451
    public static function view_user_list($courseid) {
1452
        global $CFG;
1453
        require_once($CFG->dirroot . "/user/lib.php");
1454
        require_once($CFG->dirroot . '/course/lib.php');
1455
 
1456
        $params = self::validate_parameters(self::view_user_list_parameters(),
1457
                                            array(
1458
                                                'courseid' => $courseid
1459
                                            ));
1460
 
1461
        $warnings = array();
1462
 
1463
        if (empty($params['courseid'])) {
1464
            $params['courseid'] = SITEID;
1465
        }
1466
 
1467
        $course = get_course($params['courseid']);
1468
 
1469
        if ($course->id == SITEID) {
1470
            $context = context_system::instance();
1471
        } else {
1472
            $context = context_course::instance($course->id);
1473
        }
1474
        self::validate_context($context);
1475
 
1476
        course_require_view_participants($context);
1477
 
1478
        user_list_view($course, $context);
1479
 
1480
        $result = array();
1481
        $result['status'] = true;
1482
        $result['warnings'] = $warnings;
1483
        return $result;
1484
    }
1485
 
1486
    /**
1487
     * Returns description of method result value
1488
     *
1489
     * @return external_description
1490
     * @since Moodle 2.9
1491
     */
1492
    public static function view_user_list_returns() {
1493
        return new external_single_structure(
1494
            array(
1495
                'status' => new external_value(PARAM_BOOL, 'status: true if success'),
1496
                'warnings' => new external_warnings()
1497
            )
1498
        );
1499
    }
1500
 
1501
    /**
1502
     * Returns description of method parameters
1503
     *
1504
     * @return external_function_parameters
1505
     * @since Moodle 2.9
1506
     */
1507
    public static function view_user_profile_parameters() {
1508
        return new external_function_parameters(
1509
            array(
1510
                'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_REQUIRED),
1511
                'courseid' => new external_value(PARAM_INT, 'id of the course, default site course', VALUE_DEFAULT, 0)
1512
            )
1513
        );
1514
    }
1515
 
1516
    /**
1517
     * Trigger the user profile viewed event.
1518
     *
1519
     * @param int $userid id of user
1520
     * @param int $courseid id of course
1521
     * @return array of warnings and status result
1522
     * @since Moodle 2.9
1523
     * @throws moodle_exception
1524
     */
1525
    public static function view_user_profile($userid, $courseid = 0) {
1526
        global $CFG, $USER;
1527
        require_once($CFG->dirroot . "/user/profile/lib.php");
1528
 
1529
        $params = self::validate_parameters(self::view_user_profile_parameters(),
1530
                                            array(
1531
                                                'userid' => $userid,
1532
                                                'courseid' => $courseid
1533
                                            ));
1534
 
1535
        $warnings = array();
1536
 
1537
        if (empty($params['userid'])) {
1538
            $params['userid'] = $USER->id;
1539
        }
1540
 
1541
        if (empty($params['courseid'])) {
1542
            $params['courseid'] = SITEID;
1543
        }
1544
 
1545
        $course = get_course($params['courseid']);
1546
        $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
1547
        core_user::require_active_user($user);
1548
 
1549
        if ($course->id == SITEID) {
1550
            $coursecontext = context_system::instance();;
1551
        } else {
1552
            $coursecontext = context_course::instance($course->id);
1553
        }
1554
        self::validate_context($coursecontext);
1555
 
1556
        $currentuser = $USER->id == $user->id;
1557
        $usercontext = context_user::instance($user->id);
1558
 
1559
        if (!$currentuser and
1560
                !has_capability('moodle/user:viewdetails', $coursecontext) and
1561
                !has_capability('moodle/user:viewdetails', $usercontext)) {
1562
            throw new moodle_exception('cannotviewprofile');
1563
        }
1564
 
1565
        // Case like user/profile.php.
1566
        if ($course->id == SITEID) {
1567
            profile_view($user, $usercontext);
1568
        } else {
1569
            // Case like user/view.php.
1570
            if (!$currentuser and !can_access_course($course, $user, '', true)) {
1571
                throw new moodle_exception('notenrolledprofile');
1572
            }
1573
 
1574
            profile_view($user, $coursecontext, $course);
1575
        }
1576
 
1577
        $result = array();
1578
        $result['status'] = true;
1579
        $result['warnings'] = $warnings;
1580
        return $result;
1581
    }
1582
 
1583
    /**
1584
     * Returns description of method result value
1585
     *
1586
     * @return external_description
1587
     * @since Moodle 2.9
1588
     */
1589
    public static function view_user_profile_returns() {
1590
        return new external_single_structure(
1591
            array(
1592
                'status' => new external_value(PARAM_BOOL, 'status: true if success'),
1593
                'warnings' => new external_warnings()
1594
            )
1595
        );
1596
    }
1597
 
1598
    /**
1599
     * Returns description of method parameters
1600
     *
1601
     * @return external_function_parameters
1602
     * @since Moodle 3.2
1603
     */
1604
    public static function get_user_preferences_parameters() {
1605
        return new external_function_parameters(
1606
            array(
1607
                'name' => new external_value(PARAM_RAW, 'preference name, empty for all', VALUE_DEFAULT, ''),
1608
                'userid' => new external_value(PARAM_INT, 'id of the user, default to current user', VALUE_DEFAULT, 0)
1609
            )
1610
        );
1611
    }
1612
 
1613
    /**
1614
     * Return user preferences.
1615
     *
1616
     * @param string $name preference name, empty for all
1617
     * @param int $userid id of the user, 0 for current user
1618
     * @return array of warnings and preferences
1619
     * @since Moodle 3.2
1620
     * @throws moodle_exception
1621
     */
1622
    public static function get_user_preferences($name = '', $userid = 0) {
1623
        global $USER;
1624
 
1625
        $params = self::validate_parameters(self::get_user_preferences_parameters(),
1626
                                            array(
1627
                                                'name' => $name,
1628
                                                'userid' => $userid
1629
                                            ));
1630
        $preferences = array();
1631
        $warnings = array();
1632
 
1633
        $context = context_system::instance();
1634
        self::validate_context($context);
1635
 
1636
        if (empty($params['name'])) {
1637
            $name = null;
1638
        }
1639
        if (empty($params['userid'])) {
1640
            $user = null;
1641
        } else {
1642
            $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
1643
            core_user::require_active_user($user);
1644
            if ($user->id != $USER->id) {
1645
                // Only admins can retrieve other users preferences.
1646
                require_capability('moodle/site:config', $context);
1647
            }
1648
        }
1649
 
1650
        $userpreferences = get_user_preferences($name, null, $user);
1651
        // Check if we received just one preference.
1652
        if (!is_array($userpreferences)) {
1653
            $userpreferences = array($name => $userpreferences);
1654
        }
1655
 
1656
        foreach ($userpreferences as $name => $value) {
1657
            $preferences[] = array(
1658
                'name' => $name,
1659
                'value' => $value,
1660
            );
1661
        }
1662
 
1663
        $result = array();
1664
        $result['preferences'] = $preferences;
1665
        $result['warnings'] = $warnings;
1666
        return $result;
1667
    }
1668
 
1669
    /**
1670
     * Returns description of method result value
1671
     *
1672
     * @return external_description
1673
     * @since Moodle 3.2
1674
     */
1675
    public static function get_user_preferences_returns() {
1676
        return new external_single_structure(
1677
            array(
1678
                'preferences' => new external_multiple_structure(
1679
                    new external_single_structure(
1680
                        array(
1681
                            'name' => new external_value(PARAM_RAW, 'The name of the preference'),
1682
                            'value' => new external_value(PARAM_RAW, 'The value of the preference'),
1683
                        )
1684
                    ),
1685
                    'User custom fields (also known as user profile fields)'
1686
                ),
1687
                'warnings' => new external_warnings()
1688
            )
1689
        );
1690
    }
1691
 
1692
    /**
1693
     * Returns description of method parameters
1694
     *
1695
     * @return external_function_parameters
1696
     * @since Moodle 3.2
1697
     */
1698
    public static function update_picture_parameters() {
1699
        return new external_function_parameters(
1700
            array(
1701
                'draftitemid' => new external_value(PARAM_INT, 'Id of the user draft file to use as image'),
1702
                'delete' => new external_value(PARAM_BOOL, 'If we should delete the user picture', VALUE_DEFAULT, false),
1703
                'userid' => new external_value(PARAM_INT, 'Id of the user, 0 for current user', VALUE_DEFAULT, 0)
1704
            )
1705
        );
1706
    }
1707
 
1708
    /**
1709
     * Update or delete the user picture in the site
1710
     *
1711
     * @param  int  $draftitemid id of the user draft file to use as image
1712
     * @param  bool $delete      if we should delete the user picture
1713
     * @param  int $userid       id of the user, 0 for current user
1714
     * @return array warnings and success status
1715
     * @since Moodle 3.2
1716
     * @throws moodle_exception
1717
     */
1718
    public static function update_picture($draftitemid, $delete = false, $userid = 0) {
1719
        global $CFG, $USER, $PAGE;
1720
 
1721
        $params = self::validate_parameters(
1722
            self::update_picture_parameters(),
1723
            array(
1724
                'draftitemid' => $draftitemid,
1725
                'delete' => $delete,
1726
                'userid' => $userid
1727
            )
1728
        );
1729
 
1730
        $context = context_system::instance();
1731
        self::validate_context($context);
1732
 
1733
        if (!empty($CFG->disableuserimages)) {
1734
            throw new moodle_exception('userimagesdisabled', 'admin');
1735
        }
1736
 
1737
        if (empty($params['userid']) or $params['userid'] == $USER->id) {
1738
            $user = $USER;
1739
            require_capability('moodle/user:editownprofile', $context);
1740
        } else {
1741
            $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
1742
            core_user::require_active_user($user);
1743
            $personalcontext = context_user::instance($user->id);
1744
 
1745
            require_capability('moodle/user:editprofile', $personalcontext);
1746
            if (is_siteadmin($user) and !is_siteadmin($USER)) {  // Only admins may edit other admins.
1747
                throw new moodle_exception('useradmineditadmin');
1748
            }
1749
        }
1750
 
1751
        // Load the appropriate auth plugin.
1752
        $userauth = get_auth_plugin($user->auth);
1753
        if (is_mnet_remote_user($user) or !$userauth->can_edit_profile() or $userauth->edit_profile_url()) {
1754
            throw new moodle_exception('noprofileedit', 'auth');
1755
        }
1756
 
1757
        $filemanageroptions = array(
1758
            'maxbytes' => $CFG->maxbytes,
1759
            'subdirs' => 0,
1760
            'maxfiles' => 1,
1761
            'accepted_types' => 'optimised_image'
1762
        );
1763
        $user->deletepicture = $params['delete'];
1764
        $user->imagefile = $params['draftitemid'];
1765
        $success = core_user::update_picture($user, $filemanageroptions);
1766
 
1767
        $result = array(
1768
            'success' => $success,
1769
            'warnings' => array(),
1770
        );
1771
        if ($success) {
1772
            $userpicture = new user_picture(core_user::get_user($user->id));
1773
            $userpicture->size = 1; // Size f1.
1774
            $result['profileimageurl'] = $userpicture->get_url($PAGE)->out(false);
1775
        }
1776
        return $result;
1777
    }
1778
 
1779
    /**
1780
     * Returns description of method result value
1781
     *
1782
     * @return external_description
1783
     * @since Moodle 3.2
1784
     */
1785
    public static function update_picture_returns() {
1786
        return new external_single_structure(
1787
            array(
1788
                'success' => new external_value(PARAM_BOOL, 'True if the image was updated, false otherwise.'),
1789
                'profileimageurl' => new external_value(PARAM_URL, 'New profile user image url', VALUE_OPTIONAL),
1790
                'warnings' => new external_warnings()
1791
            )
1792
        );
1793
    }
1794
 
1795
    /**
1796
     * Returns description of method parameters
1797
     *
1798
     * @return external_function_parameters
1799
     * @since Moodle 3.2
1800
     */
1801
    public static function set_user_preferences_parameters() {
1802
        return new external_function_parameters(
1803
            array(
1804
                'preferences' => new external_multiple_structure(
1805
                    new external_single_structure(
1806
                        array(
1807
                            'name' => new external_value(PARAM_RAW, 'The name of the preference'),
1808
                            'value' => new external_value(PARAM_RAW, 'The value of the preference'),
1809
                            'userid' => new external_value(PARAM_INT,
1810
                                'Id of the user to set the preference (default to current user)', VALUE_DEFAULT, 0),
1811
                        )
1812
                    )
1813
                )
1814
            )
1815
        );
1816
    }
1817
 
1818
    /**
1819
     * Set user preferences.
1820
     *
1821
     * @param array $preferences list of preferences including name, value and userid
1822
     * @return array of warnings and preferences saved
1823
     * @since Moodle 3.2
1824
     * @throws moodle_exception
1825
     */
1826
    public static function set_user_preferences($preferences) {
1827
        global $PAGE, $USER;
1828
 
1829
        $params = self::validate_parameters(self::set_user_preferences_parameters(), array('preferences' => $preferences));
1830
        $warnings = array();
1831
        $saved = array();
1832
 
1833
        $context = context_system::instance();
1834
        $PAGE->set_context($context);
1835
 
1836
        $userscache = array();
1837
        foreach ($params['preferences'] as $pref) {
1838
            $userid = $pref['userid'] ?: $USER->id;
1839
 
1840
            // Check to which user set the preference.
1841
            if (!empty($userscache[$userid])) {
1842
                $user = $userscache[$userid];
1843
            } else {
1844
                try {
1845
                    $user = core_user::get_user($userid, '*', MUST_EXIST);
1846
                    core_user::require_active_user($user);
1847
                    $userscache[$userid] = $user;
1848
                } catch (Exception $e) {
1849
                    $warnings[] = array(
1850
                        'item' => 'user',
1851
                        'itemid' => $userid,
1852
                        'warningcode' => 'invaliduser',
1853
                        'message' => $e->getMessage()
1854
                    );
1855
                    continue;
1856
                }
1857
            }
1858
 
1859
            try {
1860
 
1861
                // Support legacy preferences from the old M.util.set_user_preference API (always using the current user).
1862
                if (isset($USER->ajax_updatable_user_prefs[$pref['name']])) {
1863
                    debugging('Updating preferences via ajax_updatable_user_prefs is deprecated. ' .
1864
                        'Please use the "core_user/repository" module instead.', DEBUG_DEVELOPER);
1865
 
1866
                    set_user_preference($pref['name'], $pref['value']);
1867
                    $saved[] = array(
1868
                        'name' => $pref['name'],
1869
                        'userid' => $USER->id,
1870
                    );
1871
                } else if (core_user::can_edit_preference($pref['name'], $user)) {
1872
                    $value = core_user::clean_preference($pref['value'], $pref['name']);
1873
                    set_user_preference($pref['name'], $value, $user->id);
1874
                    $saved[] = array(
1875
                        'name' => $pref['name'],
1876
                        'userid' => $user->id,
1877
                    );
1878
                } else {
1879
                    $warnings[] = array(
1880
                        'item' => 'user',
1881
                        'itemid' => $user->id,
1882
                        'warningcode' => 'nopermission',
1883
                        'message' => 'You are not allowed to change the preference '.s($pref['name']).' for user '.$user->id
1884
                    );
1885
                }
1886
            } catch (Exception $e) {
1887
                $warnings[] = array(
1888
                    'item' => 'user',
1889
                    'itemid' => $user->id,
1890
                    'warningcode' => 'errorsavingpreference',
1891
                    'message' => $e->getMessage()
1892
                );
1893
            }
1894
        }
1895
 
1896
        $result = array();
1897
        $result['saved'] = $saved;
1898
        $result['warnings'] = $warnings;
1899
        return $result;
1900
    }
1901
 
1902
    /**
1903
     * Returns description of method result value
1904
     *
1905
     * @return external_description
1906
     * @since Moodle 3.2
1907
     */
1908
    public static function set_user_preferences_returns() {
1909
        return new external_single_structure(
1910
            array(
1911
                'saved' => new external_multiple_structure(
1912
                    new external_single_structure(
1913
                        array(
1914
                            'name' => new external_value(PARAM_RAW, 'The name of the preference'),
1915
                            'userid' => new external_value(PARAM_INT, 'The user the preference was set for'),
1916
                        )
1917
                    ), 'Preferences saved'
1918
                ),
1919
                'warnings' => new external_warnings()
1920
            )
1921
        );
1922
    }
1923
 
1924
    /**
1925
     * Returns description of method parameters.
1926
     *
1927
     * @return external_function_parameters
1928
     * @since Moodle 3.2
1929
     */
1930
    public static function agree_site_policy_parameters() {
1931
        return new external_function_parameters(array());
1932
    }
1933
 
1934
    /**
1935
     * Agree the site policy for the current user.
1936
     *
1937
     * @return array of warnings and status result
1938
     * @since Moodle 3.2
1939
     * @throws moodle_exception
1940
     */
1941
    public static function agree_site_policy() {
1942
        global $CFG, $DB, $USER;
1943
 
1944
        $warnings = array();
1945
 
1946
        $context = context_system::instance();
1947
        try {
1948
            // We expect an exception here since the user didn't agree the site policy yet.
1949
            self::validate_context($context);
1950
        } catch (Exception $e) {
1951
            // We are expecting only a sitepolicynotagreed exception.
1952
            if (!($e instanceof moodle_exception) or $e->errorcode != 'sitepolicynotagreed') {
1953
                // In case we receive a different exception, throw it.
1954
                throw $e;
1955
            }
1956
        }
1957
 
1958
        $manager = new \core_privacy\local\sitepolicy\manager();
1959
        if (!empty($USER->policyagreed)) {
1960
            $status = false;
1961
            $warnings[] = array(
1962
                'item' => 'user',
1963
                'itemid' => $USER->id,
1964
                'warningcode' => 'alreadyagreed',
1965
                'message' => 'The user already agreed the site policy.'
1966
            );
1967
        } else if (!$manager->is_defined()) {
1968
            $status = false;
1969
            $warnings[] = array(
1970
                'item' => 'user',
1971
                'itemid' => $USER->id,
1972
                'warningcode' => 'nositepolicy',
1973
                'message' => 'The site does not have a site policy configured.'
1974
            );
1975
        } else {
1976
            $status = $manager->accept();
1977
        }
1978
 
1979
        $result = array();
1980
        $result['status'] = $status;
1981
        $result['warnings'] = $warnings;
1982
        return $result;
1983
    }
1984
 
1985
    /**
1986
     * Returns description of method result value.
1987
     *
1988
     * @return external_description
1989
     * @since Moodle 3.2
1990
     */
1991
    public static function agree_site_policy_returns() {
1992
        return new external_single_structure(
1993
            array(
1994
                'status' => new external_value(PARAM_BOOL, 'Status: true only if we set the policyagreed to 1 for the user'),
1995
                'warnings' => new external_warnings()
1996
            )
1997
        );
1998
    }
1999
 
2000
    /**
2001
     * Returns description of method parameters.
2002
     *
2003
     * @return external_function_parameters
2004
     * @since Moodle 3.4
2005
     */
2006
    public static function get_private_files_info_parameters() {
2007
        return new external_function_parameters(
2008
            array(
2009
                'userid' => new external_value(PARAM_INT, 'Id of the user, default to current user.', VALUE_DEFAULT, 0)
2010
            )
2011
        );
2012
    }
2013
 
2014
    /**
2015
     * Returns general information about files in the user private files area.
2016
     *
2017
     * @param int $userid Id of the user, default to current user.
2018
     * @return array of warnings and file area information
2019
     * @since Moodle 3.4
2020
     * @throws moodle_exception
2021
     */
2022
    public static function get_private_files_info($userid = 0) {
2023
        global $CFG, $USER;
2024
        require_once($CFG->libdir . '/filelib.php');
2025
 
2026
        $params = self::validate_parameters(self::get_private_files_info_parameters(), array('userid' => $userid));
2027
        $warnings = array();
2028
 
2029
        $context = context_system::instance();
2030
        self::validate_context($context);
2031
 
2032
        if (empty($params['userid']) || $params['userid'] == $USER->id) {
2033
            $usercontext = context_user::instance($USER->id);
2034
            require_capability('moodle/user:manageownfiles', $usercontext);
2035
        } else {
2036
            $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
2037
            core_user::require_active_user($user);
2038
            // Only admins can retrieve other users information.
2039
            require_capability('moodle/site:config', $context);
2040
            $usercontext = context_user::instance($user->id);
2041
        }
2042
 
2043
        $fileareainfo = file_get_file_area_info($usercontext->id, 'user', 'private');
2044
 
2045
        $result = array();
2046
        $result['filecount'] = $fileareainfo['filecount'];
2047
        $result['foldercount'] = $fileareainfo['foldercount'];
2048
        $result['filesize'] = $fileareainfo['filesize'];
2049
        $result['filesizewithoutreferences'] = $fileareainfo['filesize_without_references'];
2050
        $result['warnings'] = $warnings;
2051
        return $result;
2052
    }
2053
 
2054
    /**
2055
     * Returns description of method result value.
2056
     *
2057
     * @return external_description
2058
     * @since Moodle 3.4
2059
     */
2060
    public static function get_private_files_info_returns() {
2061
        return new external_single_structure(
2062
            array(
2063
                'filecount' => new external_value(PARAM_INT, 'Number of files in the area.'),
2064
                'foldercount' => new external_value(PARAM_INT, 'Number of folders in the area.'),
2065
                'filesize' => new external_value(PARAM_INT, 'Total size of the files in the area.'),
2066
                'filesizewithoutreferences' => new external_value(PARAM_INT, 'Total size of the area excluding file references'),
2067
                'warnings' => new external_warnings()
2068
            )
2069
        );
2070
    }
2071
}