externallib.php 70.3 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
                            'username' =>
                                new external_value(PARAM_USERNAME, 'Username policy is defined in Moodle security config.'),
                            'password' =>
56
57
58
59
                                new external_value(PARAM_RAW, 'Plain text password consisting of any characters', VALUE_OPTIONAL),
                            'createpassword' =>
                                new external_value(PARAM_BOOL, 'True if password should be created and mailed to user.',
                                    VALUE_OPTIONAL),
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
99
100
101
                            '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(
102
103
                                new external_single_structure(
                                    array(
Petr Skoda's avatar
Petr Skoda committed
104
                                        'type'  => new external_value(PARAM_ALPHANUMEXT, 'The name of the preference'),
105
106
                                        'value' => new external_value(PARAM_RAW, 'The value of the preference')
                                    )
107
                                ), 'User preferences', VALUE_OPTIONAL),
108
109
110
                            'customfields' => new external_multiple_structure(
                                new external_single_structure(
                                    array(
Petr Skoda's avatar
Petr Skoda committed
111
                                        'type'  => new external_value(PARAM_ALPHANUMEXT, 'The name of the custom field'),
112
113
                                        'value' => new external_value(PARAM_RAW, 'The value of the custom field')
                                    )
114
                                ), 'User custom fields (also known as user profil fields)', VALUE_OPTIONAL)
115
116
117
118
119
                        )
                    )
                )
            )
        );
120
121
    }

skodak's avatar
skodak committed
122
    /**
123
     * Create one or more users.
124
     *
125
     * @throws invalid_parameter_exception
126
     * @param array $users An array of users to create.
Petr Skoda's avatar
Petr Skoda committed
127
     * @return array An array of arrays
128
     * @since Moodle 2.2
129
     */
Petr Skoda's avatar
Petr Skoda committed
130
    public static function create_users($users) {
131
        global $CFG, $DB;
132
        require_once($CFG->dirroot."/lib/weblib.php");
133
        require_once($CFG->dirroot."/user/lib.php");
134
        require_once($CFG->dirroot."/user/profile/lib.php"); // Required for customfields related function.
135

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

141
142
143
        // 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));
144

145
        $availableauths  = core_component::get_plugin_list('auth');
146
147
        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
148

149
        $availablethemes = core_component::get_plugin_list('theme');
150
        $availablelangs  = get_string_manager()->get_list_of_translations();
151

152
        $transaction = $DB->start_delegated_transaction();
153

154
        $userids = array();
155
        $createpassword = false;
Petr Skoda's avatar
Petr Skoda committed
156
        foreach ($params['users'] as $user) {
157
158
            // 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
159
                throw new invalid_parameter_exception('Username already exists: '.$user['username']);
160
161
            }

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

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

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

180
181
            // Make sure we have a password or have to create one.
            if (empty($user['password']) && empty($user['createpassword'])) {
182
                throw new invalid_parameter_exception('Invalid password: you must provide a password, or set createpassword.');
183
184
            }

185
            $user['confirmed'] = true;
186
            $user['mnethostid'] = $CFG->mnet_localhost_id;
187

188
            // Start of user info validation.
189
            // Make sure we validate current user info as handled by current GUI. See user/editadvanced_form.php func validation().
190
191
            if (!validate_email($user['email'])) {
                throw new invalid_parameter_exception('Email address is invalid: '.$user['email']);
192
            } else if ($DB->record_exists('user', array('email' => $user['email'], 'mnethostid' => $user['mnethostid']))) {
193
194
195
196
                throw new invalid_parameter_exception('Email address already exists: '.$user['email']);
            }
            // End of user info validation.

197
198
199
200
201
202
203
204
205
            $createpassword = !empty($user['createpassword']);
            unset($user['createpassword']);
            if ($createpassword) {
                $user['password'] = '';
                $updatepassword = false;
            } else {
                $updatepassword = true;
            }

206
            // Create the user data now!
207
            $user['id'] = user_create_user($user, $updatepassword, false);
208

209
210
211
212
213
214
            // 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'];
215
216
217
                }
                profile_save_data((object) $user);
            }
Petr Skoda's avatar
Petr Skoda committed
218

219
220
221
222
223
224
225
            if ($createpassword) {
                $userobject = (object)$user;
                setnew_password_and_mail($userobject);
                unset_user_preference('create_password', $userobject);
                set_user_preference('auth_forcepasswordchange', 1, $userobject);
            }

226
227
228
            // Trigger event.
            \core\event\user_created::create_from_userid($user['id'])->trigger();

229
            // Preferences.
230
            if (!empty($user['preferences'])) {
231
232
                foreach ($user['preferences'] as $preference) {
                    set_user_preference($preference['type'], $preference['value'], $user['id']);
233
234
                }
            }
skodak's avatar
skodak committed
235

236
            $userids[] = array('id' => $user['id'], 'username' => $user['username']);
237
238
        }

239
        $transaction->allow_commit();
Petr Skoda's avatar
Petr Skoda committed
240

241
        return $userids;
242
243
    }

244
    /**
Petr Skoda's avatar
Petr Skoda committed
245
     * Returns description of method result value
246
     *
Petr Skoda's avatar
Petr Skoda committed
247
     * @return external_description
248
     * @since Moodle 2.2
Petr Skoda's avatar
Petr Skoda committed
249
250
251
252
253
254
     */
    public static function create_users_returns() {
        return new external_multiple_structure(
            new external_single_structure(
                array(
                    'id'       => new external_value(PARAM_INT, 'user id'),
255
                    'username' => new external_value(PARAM_USERNAME, 'user name'),
Petr Skoda's avatar
Petr Skoda committed
256
257
258
                )
            )
        );
skodak's avatar
skodak committed
259
260
261
    }


Petr Skoda's avatar
Petr Skoda committed
262
263
    /**
     * Returns description of method parameters
264
     *
Petr Skoda's avatar
Petr Skoda committed
265
     * @return external_function_parameters
266
     * @since Moodle 2.2
Petr Skoda's avatar
Petr Skoda committed
267
     */
skodak's avatar
skodak committed
268
    public static function delete_users_parameters() {
Petr Skoda's avatar
Petr Skoda committed
269
270
271
272
273
        return new external_function_parameters(
            array(
                'userids' => new external_multiple_structure(new external_value(PARAM_INT, 'user ID')),
            )
        );
skodak's avatar
skodak committed
274
    }
Petr Skoda's avatar
Petr Skoda committed
275

276
277
    /**
     * Delete users
278
     *
279
     * @throws moodle_exception
280
     * @param array $userids
Sam Hemelryk's avatar
Sam Hemelryk committed
281
     * @return null
282
     * @since Moodle 2.2
283
     */
284
    public static function delete_users($userids) {
285
        global $CFG, $DB, $USER;
286
        require_once($CFG->dirroot."/user/lib.php");
287

288
        // Ensure the current user is allowed to run this function.
289
        $context = context_system::instance();
290
291
292
        require_capability('moodle/user:delete', $context);
        self::validate_context($context);

293
        $params = self::validate_parameters(self::delete_users_parameters(), array('userids' => $userids));
294
295
296
297

        $transaction = $DB->start_delegated_transaction();

        foreach ($params['userids'] as $userid) {
298
299
            $user = $DB->get_record('user', array('id' => $userid, 'deleted' => 0), '*', MUST_EXIST);
            // Must not allow deleting of admins or self!!!
300
301
302
303
304
            if (is_siteadmin($user)) {
                throw new moodle_exception('useradminodelete', 'error');
            }
            if ($USER->id == $user->id) {
                throw new moodle_exception('usernotdeletederror', 'error');
305
            }
306
            user_delete_user($user);
307
308
309
310
311
        }

        $transaction->allow_commit();

        return null;
312
    }
Petr Skoda's avatar
Petr Skoda committed
313

314
    /**
Petr Skoda's avatar
Petr Skoda committed
315
     * Returns description of method result value
316
317
318
     *
     * @return null
     * @since Moodle 2.2
Petr Skoda's avatar
Petr Skoda committed
319
     */
skodak's avatar
skodak committed
320
    public static function delete_users_returns() {
Petr Skoda's avatar
Petr Skoda committed
321
        return null;
skodak's avatar
skodak committed
322
    }
323
324


Petr Skoda's avatar
Petr Skoda committed
325
326
    /**
     * Returns description of method parameters
327
     *
Petr Skoda's avatar
Petr Skoda committed
328
     * @return external_function_parameters
329
     * @since Moodle 2.2
Petr Skoda's avatar
Petr Skoda committed
330
     */
skodak's avatar
skodak committed
331
    public static function update_users_parameters() {
332
        return new external_function_parameters(
333
334
335
336
            array(
                'users' => new external_multiple_structure(
                    new external_single_structure(
                        array(
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
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
                            '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(
389
390
391
392
393
                                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')
                                    )
394
                                ), 'User custom fields (also known as user profil fields)', VALUE_OPTIONAL),
395
396
397
398
399
400
401
                            '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),
402
403
404
405
406
                        )
                    )
                )
            )
        );
skodak's avatar
skodak committed
407
    }
408

409
410
    /**
     * Update users
411
     *
412
     * @param array $users
Sam Hemelryk's avatar
Sam Hemelryk committed
413
     * @return null
414
     * @since Moodle 2.2
415
     */
416
417
    public static function update_users($users) {
        global $CFG, $DB;
418
        require_once($CFG->dirroot."/user/lib.php");
419
        require_once($CFG->dirroot."/user/profile/lib.php"); // Required for customfields related function.
420

421
        // Ensure the current user is allowed to run this function.
422
        $context = context_system::instance();
423
424
425
        require_capability('moodle/user:update', $context);
        self::validate_context($context);

426
        $params = self::validate_parameters(self::update_users_parameters(), array('users' => $users));
427
428
429
430

        $transaction = $DB->start_delegated_transaction();

        foreach ($params['users'] as $user) {
431
            user_update_user($user, true, false);
432
433
            // Update user custom fields.
            if (!empty($user['customfields'])) {
434

435
436
437
438
                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'];
439
440
441
                }
                profile_save_data((object) $user);
            }
442

443
444
445
            // Trigger event.
            \core\event\user_updated::create_from_userid($user['id'])->trigger();

446
            // Preferences.
447
            if (!empty($user['preferences'])) {
448
449
                foreach ($user['preferences'] as $preference) {
                    set_user_preference($preference['type'], $preference['value'], $user['id']);
450
451
                }
            }
452
453
454
455
456
        }

        $transaction->allow_commit();

        return null;
457
    }
Petr Skoda's avatar
Petr Skoda committed
458

459
    /**
Petr Skoda's avatar
Petr Skoda committed
460
     * Returns description of method result value
461
462
463
     *
     * @return null
     * @since Moodle 2.2
Petr Skoda's avatar
Petr Skoda committed
464
     */
skodak's avatar
skodak committed
465
    public static function update_users_returns() {
Petr Skoda's avatar
Petr Skoda committed
466
        return null;
skodak's avatar
skodak committed
467
468
    }

469
470
471
472
473
474
    /**
     * Returns description of method parameters
     *
     * @return external_function_parameters
     * @since Moodle 2.4
     */
475
    public static function get_users_by_field_parameters() {
476
477
        return new external_function_parameters(
            array(
478
479
480
481
                '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'))
482
483
484
485
486
            )
        );
    }

    /**
487
     * Get user information for a unique field.
488
     *
489
490
     * @throws coding_exception
     * @throws invalid_parameter_exception
491
492
493
494
     * @param string $field
     * @param array $values
     * @return array An array of arrays containg user profiles.
     * @since Moodle 2.4
495
     */
496
    public static function get_users_by_field($field, $values) {
497
498
499
        global $CFG, $USER, $DB;
        require_once($CFG->dirroot . "/user/lib.php");

500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
        $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':
515
                $paramtype = PARAM_RAW;
516
517
518
519
520
521
522
                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');
523
524
        }

525
        // Clean the values.
526
527
528
529
530
531
532
        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;
533
534
        }

535
        // Retrieve the users.
536
        $users = $DB->get_records_list('user', $field, $cleanedvalues, 'id');
537

538
        // Finally retrieve each users information.
539
        $returnedusers = array();
540
541
542
        foreach ($users as $user) {
            $userdetails = user_get_user_details_courses($user);

543
544
            // Return the user only if the searched field is returned.
            // Otherwise it means that the $USER was not allowed to search the returned user.
545
546
            if (!empty($userdetails) and !empty($userdetails[$field])) {
                $returnedusers[] = $userdetails;
547
548
549
            }
        }

550
        return $returnedusers;
551
552
553
554
555
    }

    /**
     * Returns description of method result value
     *
556
557
     * @return external_multiple_structure
     * @since Moodle 2.4
558
     */
559
    public static function get_users_by_field_returns() {
560
        return new external_multiple_structure(self::user_description());
561
562
563
564
    }


    /**
565
     * Returns description of get_users() parameters.
566
567
     *
     * @return external_function_parameters
568
     * @since Moodle 2.5
569
570
571
572
573
574
575
     */
    public static function get_users_parameters() {
        return new external_function_parameters(
            array(
                'criteria' => new external_multiple_structure(
                    new external_single_structure(
                        array(
576
577
578
579
580
581
582
583
584
                            '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')
585
586
                        )
                    ), 'the key/value pairs to be considered in user search. Values can not be empty.
587
                        Specify different keys only once (fullname => \'user1\', auth => \'manual\', ...) -
588
                        key occurences are forbidden.
589
                        The search is executed with AND operator on the criterias. Invalid criterias (keys) are ignored,
590
591
592
                        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.'
593
594
595
596
597
                )
            )
        );
    }

598
    /**
599
     * Retrieve matching user.
600
     *
601
     * @throws moodle_exception
602
603
604
     * @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
605
606
607
608
609
610
611
612
613
     */
    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));

614
        // Validate the criteria and retrieve the users.
615
616
617
        $users = array();
        $warnings = array();
        $sqlparams = array();
618
619
620
621
        $usedkeys = array();

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

623
        foreach ($params['criteria'] as $criteriaindex => $criteria) {
624
625
626
627
628
629
630
631

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

632
            $invalidcriteria = false;
633
            // Clean the parameters.
634
635
636
637
638
639
640
641
642
            $paramtype = PARAM_RAW;
            switch ($criteria['key']) {
                case 'id':
                    $paramtype = PARAM_INT;
                    break;
                case 'idnumber':
                    $paramtype = PARAM_RAW;
                    break;
                case 'username':
643
                    $paramtype = PARAM_RAW;
644
645
                    break;
                case 'email':
646
                    // We use PARAM_RAW to allow searches with %.
647
648
649
650
651
652
653
654
655
656
                    $paramtype = PARAM_RAW;
                    break;
                case 'auth':
                    $paramtype = PARAM_AUTH;
                    break;
                case 'lastname':
                case 'firstname':
                    $paramtype = PARAM_TEXT;
                    break;
                default:
657
658
                    // Send back a warning that this search key is not supported in this version.
                    // This warning will make the function extandable without breaking clients.
659
                    $warnings[] = array(
660
                        'item' => $criteria['key'],
661
                        'warningcode' => 'invalidfieldparameter',
662
663
                        'message' =>
                            'The search key \'' . $criteria['key'] . '\' is not supported, look at the web service documentation'
664
                    );
665
666
667
668
                    // Do not add this invalid criteria to the created SQL request.
                    $invalidcriteria = true;
                    unset($params['criteria'][$criteriaindex]);
                    break;
669
670
            }

671
672
            if (!$invalidcriteria) {
                $cleanedvalue = clean_param($criteria['value'], $paramtype);
673

674
                $sql .= ' AND ';
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693

                // 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;
                }
694
695
696
697
698
            }
        }

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

699
        // Finally retrieve each users information.
700
701
702
703
704
705
706
707
708
        $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;

709
                foreach ($params['criteria'] as $criteria) {
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
                    if (empty($userdetails[$criteria['key']])) {
                        $validuser = false;
                    }
                }

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

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

    /**
725
     * Returns description of get_users result value.
726
727
     *
     * @return external_description
728
     * @since Moodle 2.5
729
730
731
732
     */
    public static function get_users_returns() {
        return new external_single_structure(
            array('users' => new external_multiple_structure(
733
                                self::user_description()
734
                             ),
735
                  'warnings' => new external_warnings('always set to \'key\'', 'faulty key name')
736
737
738
739
            )
        );
    }

Petr Skoda's avatar
Petr Skoda committed
740
741
    /**
     * Returns description of method parameters
742
     *
Petr Skoda's avatar
Petr Skoda committed
743
     * @return external_function_parameters
744
     * @since Moodle 2.2
745
746
     * @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
747
     */
748
    public static function get_users_by_id_parameters() {
Petr Skoda's avatar
Petr Skoda committed
749
        return new external_function_parameters(
750
751
752
            array(
                'userids' => new external_multiple_structure(new external_value(PARAM_INT, 'user ID')),
            )
Petr Skoda's avatar
Petr Skoda committed
753
        );
skodak's avatar
skodak committed
754
    }
Petr Skoda's avatar
Petr Skoda committed
755

Petr Skoda's avatar
Petr Skoda committed
756
757
    /**
     * Get user information
758
759
760
     * - 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
761
     *
Petr Skoda's avatar
Petr Skoda committed
762
763
     * @param array $userids  array of user ids
     * @return array An array of arrays describing users
764
     * @since Moodle 2.2
765
766
     * @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
767
     */
768
    public static function get_users_by_id($userids) {
769
        global $CFG, $USER, $DB;
770
        require_once($CFG->dirroot . "/user/lib.php");
771

772
        $params = self::validate_parameters(self::get_users_by_id_parameters(),
773
                array('userids' => $userids));
774

775
        list($sqluserids, $params) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
776
777
778
        $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
779
780
781
782
        $usersql = "SELECT u.* $uselect
                      FROM {user} u $ujoin
                     WHERE u.id $sqluserids";
        $users = $DB->get_recordset_sql($usersql, $params);
skodak's avatar
skodak committed
783

784
        $result = array();
785
        $hasuserupdatecap = has_capability('moodle/user:update', context_system::instance());
skodak's avatar
skodak committed
786
        foreach ($users as $user) {
Dongsheng Cai's avatar
Dongsheng Cai committed
787
788
789
            if (!empty($user->deleted)) {
                continue;
            }
790
            context_helper::preload_from_record($user);
791
            $usercontext = context_user::instance($user->id, IGNORE_MISSING);
792
            self::validate_context($usercontext);
793
794
            $currentuser = ($user->id == $USER->id);

795
            if ($userarray  = user_get_user_details($user)) {
796
                // Fields matching permissions from /user/editadvanced.php.
797
798
799
800
801
802
803
804
                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;
805
                }
806
                $result[] = $userarray;
Dongsheng Cai's avatar
Dongsheng Cai committed
807
            }
808
        }
Dongsheng Cai's avatar
Dongsheng Cai committed
809
        $users->close();
Petr Skoda's avatar
Petr Skoda committed
810
811

        return $result;
skodak's avatar
skodak committed
812
    }
Petr Skoda's avatar
Petr Skoda committed
813

814
    /**
Petr Skoda's avatar
Petr Skoda committed
815
     * Returns description of method result value
816
     *
Petr Skoda's avatar
Petr Skoda committed
817
     * @return external_description
818
     * @since Moodle 2.2
819
820
     * @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
821
     */
822
    public static function get_users_by_id_returns() {
823
824
        $additionalfields = array (
            'enrolledcourses' => new external_multiple_structure(
Damyon Wiese's avatar
Damyon Wiese committed
825
826
827
828
829
830
831
            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));
832
        return new external_multiple_structure(self::user_description($additionalfields));
Dongsheng Cai's avatar
Dongsheng Cai committed
833
    }
834

835
836
837
838
839
840
841
842
843
    /**
     * 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
844
845
    /**
     * Returns description of method parameters
846
     *
Dongsheng Cai's avatar
Dongsheng Cai committed
847
     * @return external_function_parameters
848
     * @since Moodle 2.2
Dongsheng Cai's avatar
Dongsheng Cai committed
849
     */
850
    public static function get_course_user_profiles_parameters() {
Dongsheng Cai's avatar
Dongsheng Cai committed
851
852
853
854
855
856
857
        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'),
858
                        )
Dongsheng Cai's avatar
Dongsheng Cai committed
859
                    )
Petr Skoda's avatar
Petr Skoda committed
860
                )
Dongsheng Cai's avatar
Dongsheng Cai committed
861
862
863
864
865
866
            )
        );
    }

    /**
     * Get course participant's details
867
     *
Dongsheng Cai's avatar
Dongsheng Cai committed
868
869
     * @param array $userlist  array of user ids and according course ids
     * @return array An array of arrays describing course participants
870
     * @since Moodle 2.2
Dongsheng Cai's avatar
Dongsheng Cai committed
871
     */
872
    public static function get_course_user_profiles($userlist) {
Dongsheng Cai's avatar
Dongsheng Cai committed
873
874
        global $CFG, $USER, $DB;
        require_once($CFG->dirroot . "/user/lib.php");
875
        $params = self::validate_parameters(self::get_course_user_profiles_parameters(), array('userlist' => $userlist));
Dongsheng Cai's avatar
Dongsheng Cai committed
876
877
878
879
880
881
882
883

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

884
        // Cache all courses.
Dongsheng Cai's avatar
Dongsheng Cai committed
885
        $courses = array();
886
        list($sqlcourseids, $params) = $DB->get_in_or_equal(array_unique($courseids), SQL_PARAMS_NAMED);
887
888
889
        $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;
890
        $coursesql = "SELECT c.* $cselect
Dongsheng Cai's avatar
Dongsheng Cai committed
891
892
893
894
                        FROM {course} c $cjoin
                       WHERE c.id $sqlcourseids";
        $rs = $DB->get_recordset_sql($coursesql, $params);
        foreach ($rs as $course) {
895
            // Adding course contexts to cache.
896
            context_helper::preload_from_record($course);
897
            // Cache courses.
Dongsheng Cai's avatar
Dongsheng Cai committed
898
899
900
901
            $courses[$course->id] = $course;
        }
        $rs->close();

902
        list($sqluserids, $params) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
903
904
905
        $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
906
907
908
909
910
911
912
913
914
        $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;
            }
915
            context_helper::preload_from_record($user);
Dongsheng Cai's avatar
Dongsheng Cai committed
916
            $course = $courses[$courseids[$user->id]];
917
            $context = context_course::instance($courseids[$user->id], IGNORE_MISSING);
Dongsheng Cai's avatar
Dongsheng Cai committed
918
            self::validate_context($context);
919
920
            if ($userarray = user_get_user_details($user, $course)) {
                $result[] = $userarray;
Dongsheng Cai's avatar
Dongsheng Cai committed
921
            }
922
        }
Dongsheng Cai's avatar
Dongsheng Cai committed
923

924
        $users->close();
Dongsheng Cai's avatar
Dongsheng Cai committed
925

926
927
        return $result;
    }
Dongsheng Cai's avatar
Dongsheng Cai committed
928

929
930
    /**
     * Returns description of method result value
931
     *
932
     * @return external_description
933
     * @since Moodle 2.2
934
     */
935
    public static function get_course_user_profiles_returns() {
936
        $additionalfields = array(
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
            '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)
        );
964

965
        return new external_multiple_structure(self::user_description($additionalfields));
966
967
968
969
970
    }

    /**
     * Create user return value description.
     *
971
     * @param array $additionalfields some additional field
972
973
     * @return single_structure_description
     */
974
    public static function user_description($additionalfields = array()) {
975
        $userfields = array(
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),
For faster browsing, not all history is shown. View entire blame