externallib.php 59.7 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.

/**
 * External user API
 *
20
21
22
 * @package    core_user
 * @category   external
 * @copyright  2009 Petr Skodak
23
24
25
26
27
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */

require_once("$CFG->libdir/externallib.php");

28
/**
29
30
31
32
33
34
35
 * User external functions
 *
 * @package    core_user
 * @category   external
 * @copyright  2011 Jerome Mouneyrac
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 * @since Moodle 2.2
36
37
 */
class core_user_external extends external_api {
38

Petr Skoda's avatar
Petr Skoda committed
39
40
    /**
     * Returns description of method parameters
41
     *
Petr Skoda's avatar
Petr Skoda committed
42
     * @return external_function_parameters
43
     * @since Moodle 2.2
Petr Skoda's avatar
Petr Skoda committed
44
     */
skodak's avatar
skodak committed
45
    public static function create_users_parameters() {
Petr Skoda's avatar
Petr Skoda committed
46
47
        global $CFG;

48
49
50
51
52
        return new external_function_parameters(
            array(
                'users' => new external_multiple_structure(
                    new external_single_structure(
                        array(
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
                            'username' =>
                                new external_value(PARAM_USERNAME, 'Username policy is defined in Moodle security config.'),
                            'password' =>
                                new external_value(PARAM_RAW, 'Plain text password consisting of any characters'),
                            'firstname' =>
                                new external_value(PARAM_NOTAGS, 'The first name(s) of the user'),
                            'lastname' =>
                                new external_value(PARAM_NOTAGS, 'The family name of the user'),
                            'email' =>
                                new external_value(PARAM_EMAIL, 'A valid and unique email address'),
                            'auth' =>
                                new external_value(PARAM_PLUGIN, 'Auth plugins include manual, ldap, imap, etc', VALUE_DEFAULT,
                                    'manual', NULL_NOT_ALLOWED),
                            'idnumber' =>
                                new external_value(PARAM_RAW, 'An arbitrary ID code number perhaps from the institution',
                                    VALUE_DEFAULT, ''),
                            'lang' =>
                                new external_value(PARAM_SAFEDIR, 'Language code such as "en", must exist on server', VALUE_DEFAULT,
                                    $CFG->lang, NULL_NOT_ALLOWED),
                            'calendartype' =>
                                new external_value(PARAM_PLUGIN, 'Calendar type such as "gregorian", must exist on server',
                                    VALUE_DEFAULT, $CFG->calendartype, VALUE_OPTIONAL),
                            'theme' =>
                                new external_value(PARAM_PLUGIN, 'Theme name such as "standard", must exist on server',
                                    VALUE_OPTIONAL),
                            'timezone' =>
                                new external_value(PARAM_TIMEZONE, 'Timezone code such as Australia/Perth, or 99 for default',
                                    VALUE_OPTIONAL),
                            'mailformat' =>
                                new external_value(PARAM_INT, 'Mail format code is 0 for plain text, 1 for HTML etc',
                                    VALUE_OPTIONAL),
                            'description' =>
                                new external_value(PARAM_TEXT, 'User profile description, no HTML', VALUE_OPTIONAL),
                            'city' =>
                                new external_value(PARAM_NOTAGS, 'Home city of the user', VALUE_OPTIONAL),
                            'country' =>
                                new external_value(PARAM_ALPHA, 'Home country code of the user, such as AU or CZ', VALUE_OPTIONAL),
                            'firstnamephonetic' =>
                                new external_value(PARAM_NOTAGS, 'The first name(s) phonetically of the user', VALUE_OPTIONAL),
                            'lastnamephonetic' =>
                                new external_value(PARAM_NOTAGS, 'The family name phonetically of the user', VALUE_OPTIONAL),
                            'middlename' =>
                                new external_value(PARAM_NOTAGS, 'The middle name of the user', VALUE_OPTIONAL),
                            'alternatename' =>
                                new external_value(PARAM_NOTAGS, 'The alternate name of the user', VALUE_OPTIONAL),
                            'preferences' => new external_multiple_structure(
99
100
                                new external_single_structure(
                                    array(
Petr Skoda's avatar
Petr Skoda committed
101
                                        'type'  => new external_value(PARAM_ALPHANUMEXT, 'The name of the preference'),
102
103
                                        'value' => new external_value(PARAM_RAW, 'The value of the preference')
                                    )
104
                                ), 'User preferences', VALUE_OPTIONAL),
105
106
107
                            'customfields' => new external_multiple_structure(
                                new external_single_structure(
                                    array(
Petr Skoda's avatar
Petr Skoda committed
108
                                        'type'  => new external_value(PARAM_ALPHANUMEXT, 'The name of the custom field'),
109
110
                                        'value' => new external_value(PARAM_RAW, 'The value of the custom field')
                                    )
111
                                ), 'User custom fields (also known as user profil fields)', VALUE_OPTIONAL)
112
113
114
115
116
                        )
                    )
                )
            )
        );
117
118
    }

skodak's avatar
skodak committed
119
    /**
120
     * Create one or more users.
121
     *
122
     * @throws invalid_parameter_exception
123
     * @param array $users An array of users to create.
Petr Skoda's avatar
Petr Skoda committed
124
     * @return array An array of arrays
125
     * @since Moodle 2.2
126
     */
Petr Skoda's avatar
Petr Skoda committed
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/profile/lib.php"); // Required for customfields related function.
132

133
        // Ensure the current user is allowed to run this function.
134
        $context = context_system::instance();
135
        self::validate_context($context);
136
        require_capability('moodle/user:create', $context);
137

138
139
140
        // Do basic automatic PARAM checks on incoming data, using params description.
        // If any problems are found then exceptions are thrown with helpful error messages.
        $params = self::validate_parameters(self::create_users_parameters(), array('users' => $users));
141

142
        $availableauths  = core_component::get_plugin_list('auth');
143
144
        unset($availableauths['mnet']);       // These would need mnethostid too.
        unset($availableauths['webservice']); // We do not want new webservice users for now.
Petr Skoda's avatar
Petr Skoda committed
145

146
        $availablethemes = core_component::get_plugin_list('theme');
147
        $availablelangs  = get_string_manager()->get_list_of_translations();
148

149
        $transaction = $DB->start_delegated_transaction();
150

151
        $userids = array();
Petr Skoda's avatar
Petr Skoda committed
152
        foreach ($params['users'] as $user) {
153
154
            // Make sure that the username doesn't already exist.
            if ($DB->record_exists('user', array('username' => $user['username'], 'mnethostid' => $CFG->mnet_localhost_id))) {
Petr Skoda's avatar
Petr Skoda committed
155
                throw new invalid_parameter_exception('Username already exists: '.$user['username']);
156
157
            }

158
            // Make sure auth is valid.
Petr Skoda's avatar
Petr Skoda committed
159
160
            if (empty($availableauths[$user['auth']])) {
                throw new invalid_parameter_exception('Invalid authentication type: '.$user['auth']);
161
162
            }

163
            // Make sure lang is valid.
Petr Skoda's avatar
Petr Skoda committed
164
165
            if (empty($availablelangs[$user['lang']])) {
                throw new invalid_parameter_exception('Invalid language code: '.$user['lang']);
166
167
            }

168
169
170
            // Make sure lang is valid.
            if (!empty($user['theme']) && empty($availablethemes[$user['theme']])) { // Theme is VALUE_OPTIONAL,
                                                                                     // so no default value
171
                                                                                     // We need to test if the client sent it
172
                                                                                     // => !empty($user['theme']).
Petr Skoda's avatar
Petr Skoda committed
173
                throw new invalid_parameter_exception('Invalid theme: '.$user['theme']);
174
            }
175

176
            $user['confirmed'] = true;
177
            $user['mnethostid'] = $CFG->mnet_localhost_id;
178

179
            // Start of user info validation.
180
            // Make sure we validate current user info as handled by current GUI. See user/editadvanced_form.php func validation().
181
182
            if (!validate_email($user['email'])) {
                throw new invalid_parameter_exception('Email address is invalid: '.$user['email']);
183
            } else if ($DB->record_exists('user', array('email' => $user['email'], 'mnethostid' => $user['mnethostid']))) {
184
185
186
187
                throw new invalid_parameter_exception('Email address already exists: '.$user['email']);
            }
            // End of user info validation.

188
            // Create the user data now!
189
            $user['id'] = user_create_user($user);
190

191
192
193
194
195
196
            // Custom fields.
            if (!empty($user['customfields'])) {
                foreach ($user['customfields'] as $customfield) {
                    // Profile_save_data() saves profile file it's expecting a user with the correct id,
                    // and custom field to be named profile_field_"shortname".
                    $user["profile_field_".$customfield['type']] = $customfield['value'];
197
198
199
                }
                profile_save_data((object) $user);
            }
Petr Skoda's avatar
Petr Skoda committed
200

201
            // Preferences.
202
            if (!empty($user['preferences'])) {
203
204
                foreach ($user['preferences'] as $preference) {
                    set_user_preference($preference['type'], $preference['value'], $user['id']);
205
206
                }
            }
skodak's avatar
skodak committed
207

208
            $userids[] = array('id' => $user['id'], 'username' => $user['username']);
209
210
        }

211
        $transaction->allow_commit();
Petr Skoda's avatar
Petr Skoda committed
212

213
        return $userids;
214
215
    }

216
    /**
Petr Skoda's avatar
Petr Skoda committed
217
     * Returns description of method result value
218
     *
Petr Skoda's avatar
Petr Skoda committed
219
     * @return external_description
220
     * @since Moodle 2.2
Petr Skoda's avatar
Petr Skoda committed
221
222
223
224
225
226
     */
    public static function create_users_returns() {
        return new external_multiple_structure(
            new external_single_structure(
                array(
                    'id'       => new external_value(PARAM_INT, 'user id'),
227
                    'username' => new external_value(PARAM_USERNAME, 'user name'),
Petr Skoda's avatar
Petr Skoda committed
228
229
230
                )
            )
        );
skodak's avatar
skodak committed
231
232
233
    }


Petr Skoda's avatar
Petr Skoda committed
234
235
    /**
     * Returns description of method parameters
236
     *
Petr Skoda's avatar
Petr Skoda committed
237
     * @return external_function_parameters
238
     * @since Moodle 2.2
Petr Skoda's avatar
Petr Skoda committed
239
     */
skodak's avatar
skodak committed
240
    public static function delete_users_parameters() {
Petr Skoda's avatar
Petr Skoda committed
241
242
243
244
245
        return new external_function_parameters(
            array(
                'userids' => new external_multiple_structure(new external_value(PARAM_INT, 'user ID')),
            )
        );
skodak's avatar
skodak committed
246
    }
Petr Skoda's avatar
Petr Skoda committed
247

248
249
    /**
     * Delete users
250
     *
251
     * @throws moodle_exception
252
     * @param array $userids
Sam Hemelryk's avatar
Sam Hemelryk committed
253
     * @return null
254
     * @since Moodle 2.2
255
     */
256
    public static function delete_users($userids) {
257
        global $CFG, $DB, $USER;
258
        require_once($CFG->dirroot."/user/lib.php");
259

260
        // Ensure the current user is allowed to run this function.
261
        $context = context_system::instance();
262
263
264
        require_capability('moodle/user:delete', $context);
        self::validate_context($context);

265
        $params = self::validate_parameters(self::delete_users_parameters(), array('userids' => $userids));
266
267
268
269

        $transaction = $DB->start_delegated_transaction();

        foreach ($params['userids'] as $userid) {
270
271
            $user = $DB->get_record('user', array('id' => $userid, 'deleted' => 0), '*', MUST_EXIST);
            // Must not allow deleting of admins or self!!!
272
273
274
275
276
            if (is_siteadmin($user)) {
                throw new moodle_exception('useradminodelete', 'error');
            }
            if ($USER->id == $user->id) {
                throw new moodle_exception('usernotdeletederror', 'error');
277
            }
278
            user_delete_user($user);
279
280
281
282
283
        }

        $transaction->allow_commit();

        return null;
284
    }
Petr Skoda's avatar
Petr Skoda committed
285

286
    /**
Petr Skoda's avatar
Petr Skoda committed
287
     * Returns description of method result value
288
289
290
     *
     * @return null
     * @since Moodle 2.2
Petr Skoda's avatar
Petr Skoda committed
291
     */
skodak's avatar
skodak committed
292
    public static function delete_users_returns() {
Petr Skoda's avatar
Petr Skoda committed
293
        return null;
skodak's avatar
skodak committed
294
    }
295
296


Petr Skoda's avatar
Petr Skoda committed
297
298
    /**
     * Returns description of method parameters
299
     *
Petr Skoda's avatar
Petr Skoda committed
300
     * @return external_function_parameters
301
     * @since Moodle 2.2
Petr Skoda's avatar
Petr Skoda committed
302
     */
skodak's avatar
skodak committed
303
    public static function update_users_parameters() {
304
        return new external_function_parameters(
305
306
307
308
            array(
                'users' => new external_multiple_structure(
                    new external_single_structure(
                        array(
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
                            'id' =>
                                new external_value(PARAM_INT, 'ID of the user'),
                            'username' =>
                                new external_value(PARAM_USERNAME, 'Username policy is defined in Moodle security config.',
                                    VALUE_OPTIONAL, '', NULL_NOT_ALLOWED),
                            'password' =>
                                new external_value(PARAM_RAW, 'Plain text password consisting of any characters', VALUE_OPTIONAL,
                                    '', NULL_NOT_ALLOWED),
                            'firstname' =>
                                new external_value(PARAM_NOTAGS, 'The first name(s) of the user', VALUE_OPTIONAL, '',
                                    NULL_NOT_ALLOWED),
                            'lastname' =>
                                new external_value(PARAM_NOTAGS, 'The family name of the user', VALUE_OPTIONAL),
                            'email' =>
                                new external_value(PARAM_EMAIL, 'A valid and unique email address', VALUE_OPTIONAL, '',
                                    NULL_NOT_ALLOWED),
                            'auth' =>
                                new external_value(PARAM_PLUGIN, 'Auth plugins include manual, ldap, imap, etc', VALUE_OPTIONAL, '',
                                    NULL_NOT_ALLOWED),
                            'idnumber' =>
                                new external_value(PARAM_RAW, 'An arbitrary ID code number perhaps from the institution',
                                    VALUE_OPTIONAL),
                            'lang' =>
                                new external_value(PARAM_SAFEDIR, 'Language code such as "en", must exist on server',
                                    VALUE_OPTIONAL, '', NULL_NOT_ALLOWED),
                            'calendartype' =>
                                new external_value(PARAM_PLUGIN, 'Calendar type such as "gregorian", must exist on server',
                                    VALUE_OPTIONAL, '', NULL_NOT_ALLOWED),
                            'theme' =>
                                new external_value(PARAM_PLUGIN, 'Theme name such as "standard", must exist on server',
                                    VALUE_OPTIONAL),
                            'timezone' =>
                                new external_value(PARAM_TIMEZONE, 'Timezone code such as Australia/Perth, or 99 for default',
                                    VALUE_OPTIONAL),
                            'mailformat' =>
                                new external_value(PARAM_INT, 'Mail format code is 0 for plain text, 1 for HTML etc',
                                    VALUE_OPTIONAL),
                            'description' =>
                                new external_value(PARAM_TEXT, 'User profile description, no HTML', VALUE_OPTIONAL),
                            'city' =>
                                new external_value(PARAM_NOTAGS, 'Home city of the user', VALUE_OPTIONAL),
                            'country' =>
                                new external_value(PARAM_ALPHA, 'Home country code of the user, such as AU or CZ', VALUE_OPTIONAL),
                            'firstnamephonetic' =>
                                new external_value(PARAM_NOTAGS, 'The first name(s) phonetically of the user', VALUE_OPTIONAL),
                            'lastnamephonetic' =>
                                new external_value(PARAM_NOTAGS, 'The family name phonetically of the user', VALUE_OPTIONAL),
                            'middlename' =>
                                new external_value(PARAM_NOTAGS, 'The middle name of the user', VALUE_OPTIONAL),
                            'alternatename' =>
                                new external_value(PARAM_NOTAGS, 'The alternate name of the user', VALUE_OPTIONAL),
                            'customfields' => new external_multiple_structure(
361
362
363
364
365
                                new external_single_structure(
                                    array(
                                        'type'  => new external_value(PARAM_ALPHANUMEXT, 'The name of the custom field'),
                                        'value' => new external_value(PARAM_RAW, 'The value of the custom field')
                                    )
366
                                ), 'User custom fields (also known as user profil fields)', VALUE_OPTIONAL),
367
368
369
370
371
372
373
                            'preferences' => new external_multiple_structure(
                                new external_single_structure(
                                    array(
                                        'type'  => new external_value(PARAM_ALPHANUMEXT, 'The name of the preference'),
                                        'value' => new external_value(PARAM_RAW, 'The value of the preference')
                                    )
                                ), 'User preferences', VALUE_OPTIONAL),
374
375
376
377
378
                        )
                    )
                )
            )
        );
skodak's avatar
skodak committed
379
    }
380

381
382
    /**
     * Update users
383
     *
384
     * @param array $users
Sam Hemelryk's avatar
Sam Hemelryk committed
385
     * @return null
386
     * @since Moodle 2.2
387
     */
388
389
    public static function update_users($users) {
        global $CFG, $DB;
390
        require_once($CFG->dirroot."/user/lib.php");
391
        require_once($CFG->dirroot."/user/profile/lib.php"); // Required for customfields related function.
392

393
        // Ensure the current user is allowed to run this function.
394
        $context = context_system::instance();
395
396
397
        require_capability('moodle/user:update', $context);
        self::validate_context($context);

398
        $params = self::validate_parameters(self::update_users_parameters(), array('users' => $users));
399
400
401
402

        $transaction = $DB->start_delegated_transaction();

        foreach ($params['users'] as $user) {
403
            user_update_user($user);
404
405
            // Update user custom fields.
            if (!empty($user['customfields'])) {
406

407
408
409
410
                foreach ($user['customfields'] as $customfield) {
                    // Profile_save_data() saves profile file it's expecting a user with the correct id,
                    // and custom field to be named profile_field_"shortname".
                    $user["profile_field_".$customfield['type']] = $customfield['value'];
411
412
413
                }
                profile_save_data((object) $user);
            }
414

415
            // Preferences.
416
            if (!empty($user['preferences'])) {
417
418
                foreach ($user['preferences'] as $preference) {
                    set_user_preference($preference['type'], $preference['value'], $user['id']);
419
420
                }
            }
421
422
423
424
425
        }

        $transaction->allow_commit();

        return null;
426
    }
Petr Skoda's avatar
Petr Skoda committed
427

428
    /**
Petr Skoda's avatar
Petr Skoda committed
429
     * Returns description of method result value
430
431
432
     *
     * @return null
     * @since Moodle 2.2
Petr Skoda's avatar
Petr Skoda committed
433
     */
skodak's avatar
skodak committed
434
    public static function update_users_returns() {
Petr Skoda's avatar
Petr Skoda committed
435
        return null;
skodak's avatar
skodak committed
436
437
    }

438
439
440
441
442
443
    /**
     * Returns description of method parameters
     *
     * @return external_function_parameters
     * @since Moodle 2.4
     */
444
    public static function get_users_by_field_parameters() {
445
446
        return new external_function_parameters(
            array(
447
448
449
450
                'field' => new external_value(PARAM_ALPHA, 'the search field can be
                    \'id\' or \'idnumber\' or \'username\' or \'email\''),
                'values' => new external_multiple_structure(
                        new external_value(PARAM_RAW, 'the value to match'))
451
452
453
454
455
            )
        );
    }

    /**
456
     * Get user information for a unique field.
457
     *
458
459
     * @throws coding_exception
     * @throws invalid_parameter_exception
460
461
462
463
     * @param string $field
     * @param array $values
     * @return array An array of arrays containg user profiles.
     * @since Moodle 2.4
464
     */
465
    public static function get_users_by_field($field, $values) {
466
467
468
        global $CFG, $USER, $DB;
        require_once($CFG->dirroot . "/user/lib.php");

469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
        $params = self::validate_parameters(self::get_users_by_field_parameters(),
                array('field' => $field, 'values' => $values));

        // This array will keep all the users that are allowed to be searched,
        // according to the current user's privileges.
        $cleanedvalues = array();

        switch ($field) {
            case 'id':
                $paramtype = PARAM_INT;
                break;
            case 'idnumber':
                $paramtype = PARAM_RAW;
                break;
            case 'username':
484
                $paramtype = PARAM_RAW;
485
486
487
488
489
490
491
                break;
            case 'email':
                $paramtype = PARAM_EMAIL;
                break;
            default:
                throw new coding_exception('invalid field parameter',
                        'The search field \'' . $field . '\' is not supported, look at the web service documentation');
492
493
        }

494
        // Clean the values.
495
496
497
498
499
500
501
        foreach ($values as $value) {
            $cleanedvalue = clean_param($value, $paramtype);
            if ( $value != $cleanedvalue) {
                throw new invalid_parameter_exception('The field \'' . $field .
                        '\' value is invalid: ' . $value . '(cleaned value: '.$cleanedvalue.')');
            }
            $cleanedvalues[] = $cleanedvalue;
502
503
        }

504
        // Retrieve the users.
505
        $users = $DB->get_records_list('user', $field, $cleanedvalues, 'id');
506

507
        // Finally retrieve each users information.
508
        $returnedusers = array();
509
510
511
        foreach ($users as $user) {
            $userdetails = user_get_user_details_courses($user);

512
513
            // Return the user only if the searched field is returned.
            // Otherwise it means that the $USER was not allowed to search the returned user.
514
515
            if (!empty($userdetails) and !empty($userdetails[$field])) {
                $returnedusers[] = $userdetails;
516
517
518
            }
        }

519
        return $returnedusers;
520
521
522
523
524
    }

    /**
     * Returns description of method result value
     *
525
526
     * @return external_multiple_structure
     * @since Moodle 2.4
527
     */
528
    public static function get_users_by_field_returns() {
529
        return new external_multiple_structure(self::user_description());
530
531
532
533
    }


    /**
534
     * Returns description of get_users() parameters.
535
536
     *
     * @return external_function_parameters
537
     * @since Moodle 2.5
538
539
540
541
542
543
544
     */
    public static function get_users_parameters() {
        return new external_function_parameters(
            array(
                'criteria' => new external_multiple_structure(
                    new external_single_structure(
                        array(
545
546
547
548
549
550
551
552
553
                            'key' => new external_value(PARAM_ALPHA, 'the user column to search, expected keys (value format) are:
                                "id" (int) matching user id,
                                "lastname" (string) user last name (Note: you can use % for searching but it may be considerably slower!),
                                "firstname" (string) user first name (Note: you can use % for searching but it may be considerably slower!),
                                "idnumber" (string) matching user idnumber,
                                "username" (string) matching user username,
                                "email" (string) user email (Note: you can use % for searching but it may be considerably slower!),
                                "auth" (string) matching user auth plugin'),
                            'value' => new external_value(PARAM_RAW, 'the value to search')
554
555
                        )
                    ), 'the key/value pairs to be considered in user search. Values can not be empty.
556
                        Specify different keys only once (fullname => \'user1\', auth => \'manual\', ...) -
557
                        key occurences are forbidden.
558
                        The search is executed with AND operator on the criterias. Invalid criterias (keys) are ignored,
559
560
561
                        the search is still executed on the valid criterias.
                        You can search without criteria, but the function is not designed for it.
                        It could very slow or timeout. The function is designed to search some specific users.'
562
563
564
565
566
                )
            )
        );
    }

567
    /**
568
     * Retrieve matching user.
569
     *
570
     * @throws moodle_exception
571
572
573
     * @param array $criteria the allowed array keys are id/lastname/firstname/idnumber/username/email/auth.
     * @return array An array of arrays containing user profiles.
     * @since Moodle 2.5
574
575
576
577
578
579
580
581
582
     */
    public static function get_users($criteria = array()) {
        global $CFG, $USER, $DB;

        require_once($CFG->dirroot . "/user/lib.php");

        $params = self::validate_parameters(self::get_users_parameters(),
                array('criteria' => $criteria));

583
        // Validate the criteria and retrieve the users.
584
585
586
        $users = array();
        $warnings = array();
        $sqlparams = array();
587
588
589
590
        $usedkeys = array();

        // Do not retrieve deleted users.
        $sql = ' deleted = 0';
591

592
        foreach ($params['criteria'] as $criteriaindex => $criteria) {
593
594
595
596
597
598
599
600

            // Check that the criteria has never been used.
            if (array_key_exists($criteria['key'], $usedkeys)) {
                throw new moodle_exception('keyalreadyset', '', '', null, 'The key ' . $criteria['key'] . ' can only be sent once');
            } else {
                $usedkeys[$criteria['key']] = true;
            }

601
            $invalidcriteria = false;
602
            // Clean the parameters.
603
604
605
606
607
608
609
610
611
            $paramtype = PARAM_RAW;
            switch ($criteria['key']) {
                case 'id':
                    $paramtype = PARAM_INT;
                    break;
                case 'idnumber':
                    $paramtype = PARAM_RAW;
                    break;
                case 'username':
612
                    $paramtype = PARAM_RAW;
613
614
                    break;
                case 'email':
615
                    // We use PARAM_RAW to allow searches with %.
616
617
618
619
620
621
622
623
624
625
                    $paramtype = PARAM_RAW;
                    break;
                case 'auth':
                    $paramtype = PARAM_AUTH;
                    break;
                case 'lastname':
                case 'firstname':
                    $paramtype = PARAM_TEXT;
                    break;
                default:
626
627
                    // Send back a warning that this search key is not supported in this version.
                    // This warning will make the function extandable without breaking clients.
628
                    $warnings[] = array(
629
                        'item' => $criteria['key'],
630
                        'warningcode' => 'invalidfieldparameter',
631
632
                        'message' =>
                            'The search key \'' . $criteria['key'] . '\' is not supported, look at the web service documentation'
633
                    );
634
635
636
637
                    // Do not add this invalid criteria to the created SQL request.
                    $invalidcriteria = true;
                    unset($params['criteria'][$criteriaindex]);
                    break;
638
639
            }

640
641
            if (!$invalidcriteria) {
                $cleanedvalue = clean_param($criteria['value'], $paramtype);
642

643
                $sql .= ' AND ';
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662

                // Create the SQL.
                switch ($criteria['key']) {
                    case 'id':
                    case 'idnumber':
                    case 'username':
                    case 'auth':
                        $sql .= $criteria['key'] . ' = :' . $criteria['key'];
                        $sqlparams[$criteria['key']] = $cleanedvalue;
                        break;
                    case 'email':
                    case 'lastname':
                    case 'firstname':
                        $sql .= $DB->sql_like($criteria['key'], ':' . $criteria['key'], false);
                        $sqlparams[$criteria['key']] = $cleanedvalue;
                        break;
                    default:
                        break;
                }
663
664
665
666
667
            }
        }

        $users = $DB->get_records_select('user', $sql, $sqlparams, 'id ASC');

668
        // Finally retrieve each users information.
669
670
671
672
673
674
675
676
677
        $returnedusers = array();
        foreach ($users as $user) {
            $userdetails = user_get_user_details_courses($user);

            // Return the user only if all the searched fields are returned.
            // Otherwise it means that the $USER was not allowed to search the returned user.
            if (!empty($userdetails)) {
                $validuser = true;

678
                foreach ($params['criteria'] as $criteria) {
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
                    if (empty($userdetails[$criteria['key']])) {
                        $validuser = false;
                    }
                }

                if ($validuser) {
                    $returnedusers[] = $userdetails;
                }
            }
        }

        return array('users' => $returnedusers, 'warnings' => $warnings);
    }

    /**
694
     * Returns description of get_users result value.
695
696
     *
     * @return external_description
697
     * @since Moodle 2.5
698
699
700
701
     */
    public static function get_users_returns() {
        return new external_single_structure(
            array('users' => new external_multiple_structure(
702
                                self::user_description()
703
                             ),
704
                  'warnings' => new external_warnings('always set to \'key\'', 'faulty key name')
705
706
707
708
            )
        );
    }

Petr Skoda's avatar
Petr Skoda committed
709
710
    /**
     * Returns description of method parameters
711
     *
Petr Skoda's avatar
Petr Skoda committed
712
     * @return external_function_parameters
713
     * @since Moodle 2.2
714
715
     * @deprecated Moodle 2.5 MDL-38030 - Please do not call this function any more.
     * @see core_user_external::get_users_by_field_parameters()
Petr Skoda's avatar
Petr Skoda committed
716
     */
717
    public static function get_users_by_id_parameters() {
Petr Skoda's avatar
Petr Skoda committed
718
        return new external_function_parameters(
719
720
721
            array(
                'userids' => new external_multiple_structure(new external_value(PARAM_INT, 'user ID')),
            )
Petr Skoda's avatar
Petr Skoda committed
722
        );
skodak's avatar
skodak committed
723
    }
Petr Skoda's avatar
Petr Skoda committed
724

Petr Skoda's avatar
Petr Skoda committed
725
726
    /**
     * Get user information
727
728
729
     * - This function is matching the permissions of /user/profil.php
     * - It is also matching some permissions from /user/editadvanced.php for the following fields:
     *   auth, confirmed, idnumber, lang, theme, timezone, mailformat
730
     *
Petr Skoda's avatar
Petr Skoda committed
731
732
     * @param array $userids  array of user ids
     * @return array An array of arrays describing users
733
     * @since Moodle 2.2
734
735
     * @deprecated Moodle 2.5 MDL-38030 - Please do not call this function any more.
     * @see core_user_external::get_users_by_field()
Petr Skoda's avatar
Petr Skoda committed
736
     */
737
    public static function get_users_by_id($userids) {
738
        global $CFG, $USER, $DB;
739
        require_once($CFG->dirroot . "/user/lib.php");
740

741
        $params = self::validate_parameters(self::get_users_by_id_parameters(),
742
                array('userids' => $userids));
743

744
        list($sqluserids, $params) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
745
746
747
        $uselect = ', ' . context_helper::get_preload_record_columns_sql('ctx');
        $ujoin = "LEFT JOIN {context} ctx ON (ctx.instanceid = u.id AND ctx.contextlevel = :contextlevel)";
        $params['contextlevel'] = CONTEXT_USER;
Dongsheng Cai's avatar
Dongsheng Cai committed
748
749
750
751
        $usersql = "SELECT u.* $uselect
                      FROM {user} u $ujoin
                     WHERE u.id $sqluserids";
        $users = $DB->get_recordset_sql($usersql, $params);
skodak's avatar
skodak committed
752

753
        $result = array();
754
        $hasuserupdatecap = has_capability('moodle/user:update', context_system::instance());
skodak's avatar
skodak committed
755
        foreach ($users as $user) {
Dongsheng Cai's avatar
Dongsheng Cai committed
756
757
758
            if (!empty($user->deleted)) {
                continue;
            }
759
            context_helper::preload_from_record($user);
760
            $usercontext = context_user::instance($user->id, IGNORE_MISSING);
761
            self::validate_context($usercontext);
762
763
            $currentuser = ($user->id == $USER->id);

764
            if ($userarray  = user_get_user_details($user)) {
765
                // Fields matching permissions from /user/editadvanced.php.
766
767
768
769
770
771
772
773
                if ($currentuser or $hasuserupdatecap) {
                    $userarray['auth']       = $user->auth;
                    $userarray['confirmed']  = $user->confirmed;
                    $userarray['idnumber']   = $user->idnumber;
                    $userarray['lang']       = $user->lang;
                    $userarray['theme']      = $user->theme;
                    $userarray['timezone']   = $user->timezone;
                    $userarray['mailformat'] = $user->mailformat;
774
                }
775
                $result[] = $userarray;
Dongsheng Cai's avatar
Dongsheng Cai committed
776
            }
777
        }
Dongsheng Cai's avatar
Dongsheng Cai committed
778
        $users->close();
Petr Skoda's avatar
Petr Skoda committed
779
780

        return $result;
skodak's avatar
skodak committed
781
    }
Petr Skoda's avatar
Petr Skoda committed
782

783
    /**
Petr Skoda's avatar
Petr Skoda committed
784
     * Returns description of method result value
785
     *
Petr Skoda's avatar
Petr Skoda committed
786
     * @return external_description
787
     * @since Moodle 2.2
788
789
     * @deprecated Moodle 2.5 MDL-38030 - Please do not call this function any more.
     * @see core_user_external::get_users_by_field_returns()
Petr Skoda's avatar
Petr Skoda committed
790
     */
791
    public static function get_users_by_id_returns() {
792
793
        $additionalfields = array (
            'enrolledcourses' => new external_multiple_structure(
Damyon Wiese's avatar
Damyon Wiese committed
794
795
796
797
798
799
800
            new external_single_structure(
                array(
                    'id'  => new external_value(PARAM_INT, 'Id of the course'),
                    'fullname'  => new external_value(PARAM_RAW, 'Fullname of the course'),
                    'shortname' => new external_value(PARAM_RAW, 'Shortname of the course')
                )
            ), 'Courses where the user is enrolled - limited by which courses the user is able to see', VALUE_OPTIONAL));
801
        return new external_multiple_structure(self::user_description($additionalfields));
Dongsheng Cai's avatar
Dongsheng Cai committed
802
    }
803

Dongsheng Cai's avatar
Dongsheng Cai committed
804
805
    /**
     * Returns description of method parameters
806
     *
Dongsheng Cai's avatar
Dongsheng Cai committed
807
     * @return external_function_parameters
808
     * @since Moodle 2.2
Dongsheng Cai's avatar
Dongsheng Cai committed
809
     */
810
    public static function get_course_user_profiles_parameters() {
Dongsheng Cai's avatar
Dongsheng Cai committed
811
812
813
814
815
816
817
        return new external_function_parameters(
            array(
                'userlist' => new external_multiple_structure(
                    new external_single_structure(
                        array(
                            'userid'    => new external_value(PARAM_INT, 'userid'),
                            'courseid'    => new external_value(PARAM_INT, 'courseid'),
818
                        )
Dongsheng Cai's avatar
Dongsheng Cai committed
819
                    )
Petr Skoda's avatar
Petr Skoda committed
820
                )
Dongsheng Cai's avatar
Dongsheng Cai committed
821
822
823
824
825
826
            )
        );
    }

    /**
     * Get course participant's details
827
     *
Dongsheng Cai's avatar
Dongsheng Cai committed
828
829
     * @param array $userlist  array of user ids and according course ids
     * @return array An array of arrays describing course participants
830
     * @since Moodle 2.2
Dongsheng Cai's avatar
Dongsheng Cai committed
831
     */
832
    public static function get_course_user_profiles($userlist) {
Dongsheng Cai's avatar
Dongsheng Cai committed
833
834
        global $CFG, $USER, $DB;
        require_once($CFG->dirroot . "/user/lib.php");
835
        $params = self::validate_parameters(self::get_course_user_profiles_parameters(), array('userlist' => $userlist));
Dongsheng Cai's avatar
Dongsheng Cai committed
836
837
838
839
840
841
842
843

        $userids = array();
        $courseids = array();
        foreach ($params['userlist'] as $value) {
            $userids[] = $value['userid'];
            $courseids[$value['userid']] = $value['courseid'];
        }

844
        // Cache all courses.
Dongsheng Cai's avatar
Dongsheng Cai committed
845
        $courses = array();
846
        list($sqlcourseids, $params) = $DB->get_in_or_equal(array_unique($courseids), SQL_PARAMS_NAMED);
847
848
849
        $cselect = ', ' . context_helper::get_preload_record_columns_sql('ctx');
        $cjoin = "LEFT JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :contextlevel)";
        $params['contextlevel'] = CONTEXT_COURSE;
850
        $coursesql = "SELECT c.* $cselect
Dongsheng Cai's avatar
Dongsheng Cai committed
851
852
853
854
                        FROM {course} c $cjoin
                       WHERE c.id $sqlcourseids";
        $rs = $DB->get_recordset_sql($coursesql, $params);
        foreach ($rs as $course) {
855
            // Adding course contexts to cache.
856
            context_helper::preload_from_record($course);
857
            // Cache courses.
Dongsheng Cai's avatar
Dongsheng Cai committed
858
859
860
861
            $courses[$course->id] = $course;
        }
        $rs->close();

862
        list($sqluserids, $params) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
863
864
865
        $uselect = ', ' . context_helper::get_preload_record_columns_sql('ctx');
        $ujoin = "LEFT JOIN {context} ctx ON (ctx.instanceid = u.id AND ctx.contextlevel = :contextlevel)";
        $params['contextlevel'] = CONTEXT_USER;
Dongsheng Cai's avatar
Dongsheng Cai committed
866
867
868
869
870
871
872
873
874
        $usersql = "SELECT u.* $uselect
                      FROM {user} u $ujoin
                     WHERE u.id $sqluserids";
        $users = $DB->get_recordset_sql($usersql, $params);
        $result = array();
        foreach ($users as $user) {
            if (!empty($user->deleted)) {
                continue;
            }
875
            context_helper::preload_from_record($user);
Dongsheng Cai's avatar
Dongsheng Cai committed
876
            $course = $courses[$courseids[$user->id]];
877
            $context = context_course::instance($courseids[$user->id], IGNORE_MISSING);
Dongsheng Cai's avatar
Dongsheng Cai committed
878
            self::validate_context($context);
879
880
            if ($userarray = user_get_user_details($user, $course)) {
                $result[] = $userarray;
Dongsheng Cai's avatar
Dongsheng Cai committed
881
            }
882
        }
Dongsheng Cai's avatar
Dongsheng Cai committed
883

884
        $users->close();
Dongsheng Cai's avatar
Dongsheng Cai committed
885

886
887
        return $result;
    }
Dongsheng Cai's avatar
Dongsheng Cai committed
888

889
890
    /**
     * Returns description of method result value
891
     *
892
     * @return external_description
893
     * @since Moodle 2.2
894
     */
895
    public static function get_course_user_profiles_returns() {
896
        $additionalfields = array(
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
            'groups' => new external_multiple_structure(
                new external_single_structure(
                    array(
                        'id'  => new external_value(PARAM_INT, 'group id'),
                        'name' => new external_value(PARAM_RAW, 'group name'),
                        'description' => new external_value(PARAM_RAW, 'group description'),
                        'descriptionformat' => new external_format_value('description'),
                    )
                ), 'user groups', VALUE_OPTIONAL),
            'roles' => new external_multiple_structure(
                new external_single_structure(
                    array(
                        'roleid'       => new external_value(PARAM_INT, 'role id'),
                        'name'         => new external_value(PARAM_RAW, 'role name'),
                        'shortname'    => new external_value(PARAM_ALPHANUMEXT, 'role shortname'),
                        'sortorder'    => new external_value(PARAM_INT, 'role sortorder')
                    )
                ), 'user roles', VALUE_OPTIONAL),
            'enrolledcourses' => new external_multiple_structure(
                new external_single_structure(
                    array(
                        'id'  => new external_value(PARAM_INT, 'Id of the course'),
                        'fullname'  => new external_value(PARAM_RAW, 'Fullname of the course'),
                        'shortname' => new external_value(PARAM_RAW, 'Shortname of the course')
                    )
                ), 'Courses where the user is enrolled - limited by which courses the user is able to see', VALUE_OPTIONAL)
        );
924

925
        return new external_multiple_structure(self::user_description($additionalfields));
926
927
928
929
930
    }

    /**
     * Create user return value description.
     *
931
     * @param array $additionalfields some additional field
932
933
     * @return single_structure_description
     */
934
    public static function user_description($additionalfields = array()) {
935
        $userfields = array(
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
            'id'    => new external_value(PARAM_INT, 'ID of the user'),
            'username'    => new external_value(PARAM_RAW, 'The username', VALUE_OPTIONAL),
            'firstname'   => new external_value(PARAM_NOTAGS, 'The first name(s) of the user', VALUE_OPTIONAL),
            'lastname'    => new external_value(PARAM_NOTAGS, 'The family name of the user', VALUE_OPTIONAL),
            'fullname'    => new external_value(PARAM_NOTAGS, 'The fullname of the user'),
            'email'       => new external_value(PARAM_TEXT, 'An email address - allow email as root@localhost', VALUE_OPTIONAL),
            'address'     => new external_value(PARAM_TEXT, 'Postal address', VALUE_OPTIONAL),
            'phone1'      => new external_value(PARAM_NOTAGS, 'Phone 1', VALUE_OPTIONAL),
            'phone2'      => new external_value(PARAM_NOTAGS, 'Phone 2', VALUE_OPTIONAL),
            'icq'         => new external_value(PARAM_NOTAGS, 'icq number', VALUE_OPTIONAL),
            'skype'       => new external_value(PARAM_NOTAGS, 'skype id', VALUE_OPTIONAL),
            'yahoo'       => new external_value(PARAM_NOTAGS, 'yahoo id', VALUE_OPTIONAL),
            'aim'         => new external_value(PARAM_NOTAGS, 'aim id', VALUE_OPTIONAL),
            'msn'         => new external_value(PARAM_NOTAGS, 'msn number', VALUE_OPTIONAL),
            'department'  => new external_value(PARAM_TEXT, 'department', VALUE_OPTIONAL),
            'institution' => new external_value(PARAM_TEXT, 'institution', VALUE_OPTIONAL),
            'idnumber'    => new external_value(PARAM_RAW, 'An arbitrary ID code number perhaps from the institution', VALUE_OPTIONAL),
            'interests'   => new external_value(PARAM_TEXT, 'user interests (separated by commas)', VALUE_OPTIONAL),
            'firstaccess' => new external_value(PARAM_INT, 'first access to the site (0 if never)', VALUE_OPTIONAL),
            'lastaccess'  => new external_value(PARAM_INT, 'last access to the site (0 if never)', VALUE_OPTIONAL),
            'auth'        => new external_value(PARAM_PLUGIN, 'Auth plugins include manual, ldap, imap, etc', VALUE_OPTIONAL),
            'confirmed'   => new external_value(PARAM_INT, 'Active user: 1 if confirmed, 0 otherwise', VALUE_OPTIONAL),
            'lang'        => new external_value(PARAM_SAFEDIR, 'Language code such as "en", must exist on server', VALUE_OPTIONAL),
            'calendartype' => new external_value(PARAM_PLUGIN, 'Calendar type such as "gregorian", must exist on server', VALUE_OPTIONAL),
            'theme'       => new external_value(PARAM_PLUGIN, 'Theme name such as "standard", must exist on server', VALUE_OPTIONAL),
            'timezone'    => new external_value(PARAM_TIMEZONE, 'Timezone code such as Australia/Perth, or 99 for default', VALUE_OPTIONAL),
            'mailformat'  => new external_value(PARAM_INT, 'Mail format code is 0 for plain text, 1 for HTML etc', VALUE_OPTIONAL),
            'description' => new external_value(PARAM_RAW, 'User profile description', VALUE_OPTIONAL),
            'descriptionformat' => new external_format_value('description', VALUE_OPTIONAL),
            'city'        => new external_value(PARAM_NOTAGS, 'Home city of the user', VALUE_OPTIONAL),
            'url'         => new external_value(PARAM_URL, 'URL of the user', VALUE_OPTIONAL),
            'country'     => new external_value(PARAM_ALPHA, 'Home country code of the user, such as AU or CZ', VALUE_OPTIONAL),
            'profileimageurlsmall' => new external_value(PARAM_URL, 'User image profile URL - small version'),
            'profileimageurl' => new external_value(PARAM_URL, 'User image profile URL - big version'),
            'customfields' => new external_multiple_structure(
                new external_single_structure(
                    array(
                        'type'  => new external_value(PARAM_ALPHANUMEXT, 'The type of the custom field - text field, checkbox...'),
                        'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
                        'name' => new external_value(PARAM_RAW, 'The name of the custom field'),
                        'shortname' => new external_value(PARAM_RAW, 'The shortname of the custom field - to be able to build the field class in the code'),
                    )
                ), 'User custom fields (also known as user profile fields)', VALUE_OPTIONAL),
            'preferences' => new external_multiple_structure(
                new external_single_structure(
                    array(
                        'name'  => new external_value(PARAM_ALPHANUMEXT, 'The name of the preferences'),
                        'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
                    )
            ), 'Users preferences', VALUE_OPTIONAL)
        );
987
988
989
990
        if (!empty($additionalfields)) {
            $userfields = array_merge($userfields, $additionalfields);
        }
        return new external_single_structure($userfields);
991
    }
992

993
994
995
996
997
998
999
1000
    /**
     * Returns description of method parameters
     *
     * @return external_function_parameters
     * @since Moodle 2.6
     */
    public static function add_user_private_files_parameters() {
        return new external_function_parameters(
For faster browsing, not all history is shown. View entire blame