externallib.php 63.1 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, true, false);
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
202
203
            // Trigger event.
            \core\event\user_created::create_from_userid($user['id'])->trigger();

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

211
            $userids[] = array('id' => $user['id'], 'username' => $user['username']);
212
213
        }

214
        $transaction->allow_commit();
Petr Skoda's avatar
Petr Skoda committed
215

216
        return $userids;
217
218
    }

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


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

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

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

268
        $params = self::validate_parameters(self::delete_users_parameters(), array('userids' => $userids));
269
270
271
272

        $transaction = $DB->start_delegated_transaction();

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

        $transaction->allow_commit();

        return null;
287
    }
Petr Skoda's avatar
Petr Skoda committed
288

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


Petr Skoda's avatar
Petr Skoda committed
300
301
    /**
     * Returns description of method parameters
302
     *
Petr Skoda's avatar
Petr Skoda committed
303
     * @return external_function_parameters
304
     * @since Moodle 2.2
Petr Skoda's avatar
Petr Skoda committed
305
     */
skodak's avatar
skodak committed
306
    public static function update_users_parameters() {
307
        return new external_function_parameters(
308
309
310
311
            array(
                'users' => new external_multiple_structure(
                    new external_single_structure(
                        array(
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
361
362
363
                            '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(
364
365
366
367
368
                                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')
                                    )
369
                                ), 'User custom fields (also known as user profil fields)', VALUE_OPTIONAL),
370
371
372
373
374
375
376
                            '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),
377
378
379
380
381
                        )
                    )
                )
            )
        );
skodak's avatar
skodak committed
382
    }
383

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

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

401
        $params = self::validate_parameters(self::update_users_parameters(), array('users' => $users));
402
403
404
405

        $transaction = $DB->start_delegated_transaction();

        foreach ($params['users'] as $user) {
406
            user_update_user($user, true, false);
407
408
            // Update user custom fields.
            if (!empty($user['customfields'])) {
409

410
411
412
413
                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'];
414
415
416
                }
                profile_save_data((object) $user);
            }
417

418
419
420
            // Trigger event.
            \core\event\user_updated::create_from_userid($user['id'])->trigger();

421
            // Preferences.
422
            if (!empty($user['preferences'])) {
423
424
                foreach ($user['preferences'] as $preference) {
                    set_user_preference($preference['type'], $preference['value'], $user['id']);
425
426
                }
            }
427
428
429
430
431
        }

        $transaction->allow_commit();

        return null;
432
    }
Petr Skoda's avatar
Petr Skoda committed
433

434
    /**
Petr Skoda's avatar
Petr Skoda committed
435
     * Returns description of method result value
436
437
438
     *
     * @return null
     * @since Moodle 2.2
Petr Skoda's avatar
Petr Skoda committed
439
     */
skodak's avatar
skodak committed
440
    public static function update_users_returns() {
Petr Skoda's avatar
Petr Skoda committed
441
        return null;
skodak's avatar
skodak committed
442
443
    }

444
445
446
447
448
449
    /**
     * Returns description of method parameters
     *
     * @return external_function_parameters
     * @since Moodle 2.4
     */
450
    public static function get_users_by_field_parameters() {
451
452
        return new external_function_parameters(
            array(
453
454
455
456
                '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'))
457
458
459
460
461
            )
        );
    }

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

475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
        $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':
490
                $paramtype = PARAM_RAW;
491
492
493
494
495
496
497
                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');
498
499
        }

500
        // Clean the values.
501
502
503
504
505
506
507
        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;
508
509
        }

510
        // Retrieve the users.
511
        $users = $DB->get_records_list('user', $field, $cleanedvalues, 'id');
512

513
        // Finally retrieve each users information.
514
        $returnedusers = array();
515
516
517
        foreach ($users as $user) {
            $userdetails = user_get_user_details_courses($user);

518
519
            // Return the user only if the searched field is returned.
            // Otherwise it means that the $USER was not allowed to search the returned user.
520
521
            if (!empty($userdetails) and !empty($userdetails[$field])) {
                $returnedusers[] = $userdetails;
522
523
524
            }
        }

525
        return $returnedusers;
526
527
528
529
530
    }

    /**
     * Returns description of method result value
     *
531
532
     * @return external_multiple_structure
     * @since Moodle 2.4
533
     */
534
    public static function get_users_by_field_returns() {
535
        return new external_multiple_structure(self::user_description());
536
537
538
539
    }


    /**
540
     * Returns description of get_users() parameters.
541
542
     *
     * @return external_function_parameters
543
     * @since Moodle 2.5
544
545
546
547
548
549
550
     */
    public static function get_users_parameters() {
        return new external_function_parameters(
            array(
                'criteria' => new external_multiple_structure(
                    new external_single_structure(
                        array(
551
552
553
554
555
556
557
558
559
                            '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')
560
561
                        )
                    ), 'the key/value pairs to be considered in user search. Values can not be empty.
562
                        Specify different keys only once (fullname => \'user1\', auth => \'manual\', ...) -
563
                        key occurences are forbidden.
564
                        The search is executed with AND operator on the criterias. Invalid criterias (keys) are ignored,
565
566
567
                        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.'
568
569
570
571
572
                )
            )
        );
    }

573
    /**
574
     * Retrieve matching user.
575
     *
576
     * @throws moodle_exception
577
578
579
     * @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
580
581
582
583
584
585
586
587
588
     */
    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));

589
        // Validate the criteria and retrieve the users.
590
591
592
        $users = array();
        $warnings = array();
        $sqlparams = array();
593
594
595
596
        $usedkeys = array();

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

598
        foreach ($params['criteria'] as $criteriaindex => $criteria) {
599
600
601
602
603
604
605
606

            // 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;
            }

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

646
647
            if (!$invalidcriteria) {
                $cleanedvalue = clean_param($criteria['value'], $paramtype);
648

649
                $sql .= ' AND ';
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668

                // 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;
                }
669
670
671
672
673
            }
        }

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

674
        // Finally retrieve each users information.
675
676
677
678
679
680
681
682
683
        $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;

684
                foreach ($params['criteria'] as $criteria) {
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
                    if (empty($userdetails[$criteria['key']])) {
                        $validuser = false;
                    }
                }

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

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

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

Petr Skoda's avatar
Petr Skoda committed
715
716
    /**
     * Returns description of method parameters
717
     *
Petr Skoda's avatar
Petr Skoda committed
718
     * @return external_function_parameters
719
     * @since Moodle 2.2
720
721
     * @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
722
     */
723
    public static function get_users_by_id_parameters() {
Petr Skoda's avatar
Petr Skoda committed
724
        return new external_function_parameters(
725
726
727
            array(
                'userids' => new external_multiple_structure(new external_value(PARAM_INT, 'user ID')),
            )
Petr Skoda's avatar
Petr Skoda committed
728
        );
skodak's avatar
skodak committed
729
    }
Petr Skoda's avatar
Petr Skoda committed
730

Petr Skoda's avatar
Petr Skoda committed
731
732
    /**
     * Get user information
733
734
735
     * - 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
736
     *
Petr Skoda's avatar
Petr Skoda committed
737
738
     * @param array $userids  array of user ids
     * @return array An array of arrays describing users
739
     * @since Moodle 2.2
740
741
     * @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
742
     */
743
    public static function get_users_by_id($userids) {
744
        global $CFG, $USER, $DB;
745
        require_once($CFG->dirroot . "/user/lib.php");
746

747
        $params = self::validate_parameters(self::get_users_by_id_parameters(),
748
                array('userids' => $userids));
749

750
        list($sqluserids, $params) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
751
752
753
        $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
754
755
756
757
        $usersql = "SELECT u.* $uselect
                      FROM {user} u $ujoin
                     WHERE u.id $sqluserids";
        $users = $DB->get_recordset_sql($usersql, $params);
skodak's avatar
skodak committed
758

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

770
            if ($userarray  = user_get_user_details($user)) {
771
                // Fields matching permissions from /user/editadvanced.php.
772
773
774
775
776
777
778
779
                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;
780
                }
781
                $result[] = $userarray;
Dongsheng Cai's avatar
Dongsheng Cai committed
782
            }
783
        }
Dongsheng Cai's avatar
Dongsheng Cai committed
784
        $users->close();
Petr Skoda's avatar
Petr Skoda committed
785
786

        return $result;
skodak's avatar
skodak committed
787
    }
Petr Skoda's avatar
Petr Skoda committed
788

789
    /**
Petr Skoda's avatar
Petr Skoda committed
790
     * Returns description of method result value
791
     *
Petr Skoda's avatar
Petr Skoda committed
792
     * @return external_description
793
     * @since Moodle 2.2
794
795
     * @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
796
     */
797
    public static function get_users_by_id_returns() {
798
799
        $additionalfields = array (
            'enrolledcourses' => new external_multiple_structure(
Damyon Wiese's avatar
Damyon Wiese committed
800
801
802
803
804
805
806
            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));
807
        return new external_multiple_structure(self::user_description($additionalfields));
Dongsheng Cai's avatar
Dongsheng Cai committed
808
    }
809

810
811
812
813
814
815
816
817
818
    /**
     * Marking the method as deprecated.
     *
     * @return bool
     */
    public static function get_users_by_id_is_deprecated() {
        return true;
    }

Dongsheng Cai's avatar
Dongsheng Cai committed
819
820
    /**
     * Returns description of method parameters
821
     *
Dongsheng Cai's avatar
Dongsheng Cai committed
822
     * @return external_function_parameters
823
     * @since Moodle 2.2
Dongsheng Cai's avatar
Dongsheng Cai committed
824
     */
825
    public static function get_course_user_profiles_parameters() {
Dongsheng Cai's avatar
Dongsheng Cai committed
826
827
828
829
830
831
832
        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'),
833
                        )
Dongsheng Cai's avatar
Dongsheng Cai committed
834
                    )
Petr Skoda's avatar
Petr Skoda committed
835
                )
Dongsheng Cai's avatar
Dongsheng Cai committed
836
837
838
839
840
841
            )
        );
    }

    /**
     * Get course participant's details
842
     *
Dongsheng Cai's avatar
Dongsheng Cai committed
843
844
     * @param array $userlist  array of user ids and according course ids
     * @return array An array of arrays describing course participants
845
     * @since Moodle 2.2
Dongsheng Cai's avatar
Dongsheng Cai committed
846
     */
847
    public static function get_course_user_profiles($userlist) {
Dongsheng Cai's avatar
Dongsheng Cai committed
848
849
        global $CFG, $USER, $DB;
        require_once($CFG->dirroot . "/user/lib.php");
850
        $params = self::validate_parameters(self::get_course_user_profiles_parameters(), array('userlist' => $userlist));
Dongsheng Cai's avatar
Dongsheng Cai committed
851
852
853
854
855
856
857
858

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

859
        // Cache all courses.
Dongsheng Cai's avatar
Dongsheng Cai committed
860
        $courses = array();
861
        list($sqlcourseids, $params) = $DB->get_in_or_equal(array_unique($courseids), SQL_PARAMS_NAMED);
862
863
864
        $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;
865
        $coursesql = "SELECT c.* $cselect
Dongsheng Cai's avatar
Dongsheng Cai committed
866
867
868
869
                        FROM {course} c $cjoin
                       WHERE c.id $sqlcourseids";
        $rs = $DB->get_recordset_sql($coursesql, $params);
        foreach ($rs as $course) {
870
            // Adding course contexts to cache.
871
            context_helper::preload_from_record($course);
872
            // Cache courses.
Dongsheng Cai's avatar
Dongsheng Cai committed
873
874
875
876
            $courses[$course->id] = $course;
        }
        $rs->close();

877
        list($sqluserids, $params) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
878
879
880
        $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
881
882
883
884
885
886
887
888
889
        $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;
            }
890
            context_helper::preload_from_record($user);
Dongsheng Cai's avatar
Dongsheng Cai committed
891
            $course = $courses[$courseids[$user->id]];
892
            $context = context_course::instance($courseids[$user->id], IGNORE_MISSING);
Dongsheng Cai's avatar
Dongsheng Cai committed
893
            self::validate_context($context);
894
895
            if ($userarray = user_get_user_details($user, $course)) {
                $result[] = $userarray;
Dongsheng Cai's avatar
Dongsheng Cai committed
896
            }
897
        }
Dongsheng Cai's avatar
Dongsheng Cai committed
898

899
        $users->close();
Dongsheng Cai's avatar
Dongsheng Cai committed
900

901
902
        return $result;
    }
Dongsheng Cai's avatar
Dongsheng Cai committed
903

904
905
    /**
     * Returns description of method result value
906
     *
907
     * @return external_description
908
     * @since Moodle 2.2
909
     */
910
    public static function get_course_user_profiles_returns() {
911
        $additionalfields = array(
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
            '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)
        );
939

940
        return new external_multiple_structure(self::user_description($additionalfields));
941
942
943
944
945
    }

    /**
     * Create user return value description.
     *
946
     * @param array $additionalfields some additional field
947
948
     * @return single_structure_description
     */
949
    public static function user_description($additionalfields = array()) {
950
        $userfields = array(
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
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
            '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)
For faster browsing, not all history is shown. View entire blame