externallib.php 70.5 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
193
            } else if (empty($CFG->allowaccountssameemail) &&
                    $DB->record_exists('user', array('email' => $user['email'], 'mnethostid' => $user['mnethostid']))) {
194
195
196
197
                throw new invalid_parameter_exception('Email address already exists: '.$user['email']);
            }
            // End of user info validation.

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

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

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

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

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

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

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

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

242
        return $userids;
243
244
    }

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


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

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

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

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

        $transaction = $DB->start_delegated_transaction();

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

        $transaction->allow_commit();

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

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


Petr Skoda's avatar
Petr Skoda committed
326
327
    /**
     * Returns description of method parameters
328
     *
Petr Skoda's avatar
Petr Skoda committed
329
     * @return external_function_parameters
330
     * @since Moodle 2.2
Petr Skoda's avatar
Petr Skoda committed
331
     */
skodak's avatar
skodak committed
332
    public static function update_users_parameters() {
333
        return new external_function_parameters(
334
335
336
337
            array(
                'users' => new external_multiple_structure(
                    new external_single_structure(
                        array(
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
389
                            '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(
390
391
392
393
394
                                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')
                                    )
395
                                ), 'User custom fields (also known as user profil fields)', VALUE_OPTIONAL),
396
397
398
399
400
401
402
                            '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),
403
404
405
406
407
                        )
                    )
                )
            )
        );
skodak's avatar
skodak committed
408
    }
409

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

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

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

        $transaction = $DB->start_delegated_transaction();

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

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

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

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

        $transaction->allow_commit();

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

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

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

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

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

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

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

539
540
541
        $context = context_system::instance();
        self::validate_context($context);

542
        // Finally retrieve each users information.
543
        $returnedusers = array();
544
545
546
        foreach ($users as $user) {
            $userdetails = user_get_user_details_courses($user);

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

554
        return $returnedusers;
555
556
557
558
559
    }

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


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

602
    /**
603
     * Retrieve matching user.
604
     *
605
     * @throws moodle_exception
606
607
608
     * @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
609
610
611
612
613
614
615
616
617
     */
    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));

618
        // Validate the criteria and retrieve the users.
619
620
621
        $users = array();
        $warnings = array();
        $sqlparams = array();
622
623
624
625
        $usedkeys = array();

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

627
        foreach ($params['criteria'] as $criteriaindex => $criteria) {
628
629
630
631
632
633
634
635

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

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

675
676
            if (!$invalidcriteria) {
                $cleanedvalue = clean_param($criteria['value'], $paramtype);
677

678
                $sql .= ' AND ';
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697

                // 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;
                }
698
699
700
701
702
            }
        }

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

703
        // Finally retrieve each users information.
704
705
706
707
708
709
710
711
712
        $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;

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

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

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

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

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

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

776
        $params = self::validate_parameters(self::get_users_by_id_parameters(),
777
                array('userids' => $userids));
778

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

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

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

        return $result;
skodak's avatar
skodak committed
816
    }
Petr Skoda's avatar
Petr Skoda committed
817

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

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

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

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

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

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

928
        $users->close();
Dongsheng Cai's avatar
Dongsheng Cai committed
929

930
931
        return $result;
    }
Dongsheng Cai's avatar
Dongsheng Cai committed
932

933
934
    /**
     * Returns description of method result value
935
     *
936
     * @return external_description
937
     * @since Moodle 2.2
938
     */
939
    public static function get_course_user_profiles_returns() {
940
        $additionalfields = array(
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
            '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)
        );
968

969
        return new external_multiple_structure(self::user_description($additionalfields));
970
971
972
973
974
    }

    /**
     * Create user return value description.
     *
975
     * @param array $additionalfields some additional field
976
977
     * @return single_structure_description
     */
978
    public static function user_description($additionalfields = array()) {
979
        $userfields = array(
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),