externallib.php 58.3 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?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/>.

17

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

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

29
/**
30
31
32
33
34
35
36
 * 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
37
38
 */
class core_user_external extends external_api {
39

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

49
50
51
52
53
        return new external_function_parameters(
            array(
                'users' => new external_multiple_structure(
                    new external_single_structure(
                        array(
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
                            'username'            => new external_value(PARAM_USERNAME, 'Username policy is defined in Moodle security config.'),
                            'password'            => new external_value(PARAM_RAW, 'Plain text password consisting of any characters'),
                            'firstname'           => new external_value(PARAM_NOTAGS, 'The first name(s) of the user'),
                            'lastname'            => new external_value(PARAM_NOTAGS, 'The family name of the user'),
                            'email'               => new external_value(PARAM_EMAIL, 'A valid and unique email address'),
                            'auth'                => new external_value(PARAM_PLUGIN, 'Auth plugins include manual, ldap, imap, etc', VALUE_DEFAULT, 'manual', NULL_NOT_ALLOWED),
                            'idnumber'            => new external_value(PARAM_RAW, 'An arbitrary ID code number perhaps from the institution', VALUE_DEFAULT, ''),
                            'lang'                => new external_value(PARAM_SAFEDIR, 'Language code such as "en", must exist on server', VALUE_DEFAULT, $CFG->lang, NULL_NOT_ALLOWED),
                            '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(
73
74
                                new external_single_structure(
                                    array(
Petr Skoda's avatar
Petr Skoda committed
75
                                        'type'  => new external_value(PARAM_ALPHANUMEXT, 'The name of the preference'),
76
77
                                        'value' => new external_value(PARAM_RAW, 'The value of the preference')
                                    )
78
                                ), 'User preferences', VALUE_OPTIONAL),
79
80
81
                            'customfields' => new external_multiple_structure(
                                new external_single_structure(
                                    array(
Petr Skoda's avatar
Petr Skoda committed
82
                                        'type'  => new external_value(PARAM_ALPHANUMEXT, 'The name of the custom field'),
83
84
                                        'value' => new external_value(PARAM_RAW, 'The value of the custom field')
                                    )
85
                                ), 'User custom fields (also known as user profil fields)', VALUE_OPTIONAL)
86
87
88
89
90
                        )
                    )
                )
            )
        );
91
92
    }

skodak's avatar
skodak committed
93
    /**
94
95
     * Create one or more users
     *
96
     * @param array $users An array of users to create.
Petr Skoda's avatar
Petr Skoda committed
97
     * @return array An array of arrays
98
     * @since Moodle 2.2
99
     */
Petr Skoda's avatar
Petr Skoda committed
100
    public static function create_users($users) {
101
        global $CFG, $DB;
102
        require_once($CFG->dirroot."/lib/weblib.php");
103
        require_once($CFG->dirroot."/user/lib.php");
104
        require_once($CFG->dirroot."/user/profile/lib.php"); //required for customfields related function
105

106
        // Ensure the current user is allowed to run this function
107
        $context = context_system::instance();
108
        self::validate_context($context);
109
        require_capability('moodle/user:create', $context);
110

111
112
        // Do basic automatic PARAM checks on incoming data, using params description
        // If any problems are found then exceptions are thrown with helpful error messages
Petr Skoda's avatar
Petr Skoda committed
113
        $params = self::validate_parameters(self::create_users_parameters(), array('users'=>$users));
114

115
        $availableauths  = core_component::get_plugin_list('auth');
Petr Skoda's avatar
Petr Skoda committed
116
117
118
        unset($availableauths['mnet']);       // these would need mnethostid too
        unset($availableauths['webservice']); // we do not want new webservice users for now

119
        $availablethemes = core_component::get_plugin_list('theme');
120
        $availablelangs  = get_string_manager()->get_list_of_translations();
121

122
        $transaction = $DB->start_delegated_transaction();
123

124
        $userids = array();
Petr Skoda's avatar
Petr Skoda committed
125
        foreach ($params['users'] as $user) {
Petr Skoda's avatar
Petr Skoda committed
126
127
128
            // Make sure that the username doesn't already exist
            if ($DB->record_exists('user', array('username'=>$user['username'], 'mnethostid'=>$CFG->mnet_localhost_id))) {
                throw new invalid_parameter_exception('Username already exists: '.$user['username']);
129
130
            }

Petr Skoda's avatar
Petr Skoda committed
131
132
133
            // Make sure auth is valid
            if (empty($availableauths[$user['auth']])) {
                throw new invalid_parameter_exception('Invalid authentication type: '.$user['auth']);
134
135
            }

Petr Skoda's avatar
Petr Skoda committed
136
137
138
            // Make sure lang is valid
            if (empty($availablelangs[$user['lang']])) {
                throw new invalid_parameter_exception('Invalid language code: '.$user['lang']);
139
140
            }

Petr Skoda's avatar
Petr Skoda committed
141
            // Make sure lang is valid
142
143
144
145
            if (!empty($user['theme']) && empty($availablethemes[$user['theme']])) { //theme is VALUE_OPTIONAL,
                                                                                     // so no default value.
                                                                                     // We need to test if the client sent it
                                                                                     // => !empty($user['theme'])
Petr Skoda's avatar
Petr Skoda committed
146
                throw new invalid_parameter_exception('Invalid theme: '.$user['theme']);
147
            }
148

149
            $user['confirmed'] = true;
150
            $user['mnethostid'] = $CFG->mnet_localhost_id;
151

152
153
154
155
156
157
158
159
160
            // Start of user info validation.
            // Lets make sure we validate current user info as handled by current GUI. see user/editadvanced_form.php function validation()
            if (!validate_email($user['email'])) {
                throw new invalid_parameter_exception('Email address is invalid: '.$user['email']);
            } else if ($DB->record_exists('user', array('email'=>$user['email'], 'mnethostid'=>$user['mnethostid']))) {
                throw new invalid_parameter_exception('Email address already exists: '.$user['email']);
            }
            // End of user info validation.

161
162
            // create the user data now!
            $user['id'] = user_create_user($user);
163

164
165
166
167
168
169
170
171
172
            // custom fields
            if(!empty($user['customfields'])) {
                foreach($user['customfields'] as $customfield) {
                    $user["profile_field_".$customfield['type']] = $customfield['value']; //profile_save_data() saves profile file
                                                                                            //it's expecting a user with the correct id,
                                                                                            //and custom field to be named profile_field_"shortname"
                }
                profile_save_data((object) $user);
            }
Petr Skoda's avatar
Petr Skoda committed
173

174
175
176
177
178
179
            //preferences
            if (!empty($user['preferences'])) {
                foreach($user['preferences'] as $preference) {
                    set_user_preference($preference['type'], $preference['value'],$user['id']);
                }
            }
skodak's avatar
skodak committed
180

181
            $userids[] = array('id'=>$user['id'], 'username'=>$user['username']);
182
183
        }

184
        $transaction->allow_commit();
Petr Skoda's avatar
Petr Skoda committed
185

186
        return $userids;
187
188
    }

Petr Skoda's avatar
Petr Skoda committed
189
190
   /**
     * Returns description of method result value
191
     *
Petr Skoda's avatar
Petr Skoda committed
192
     * @return external_description
193
     * @since Moodle 2.2
Petr Skoda's avatar
Petr Skoda committed
194
195
196
197
198
199
     */
    public static function create_users_returns() {
        return new external_multiple_structure(
            new external_single_structure(
                array(
                    'id'       => new external_value(PARAM_INT, 'user id'),
200
                    'username' => new external_value(PARAM_USERNAME, 'user name'),
Petr Skoda's avatar
Petr Skoda committed
201
202
203
                )
            )
        );
skodak's avatar
skodak committed
204
205
206
    }


Petr Skoda's avatar
Petr Skoda committed
207
208
    /**
     * Returns description of method parameters
209
     *
Petr Skoda's avatar
Petr Skoda committed
210
     * @return external_function_parameters
211
     * @since Moodle 2.2
Petr Skoda's avatar
Petr Skoda committed
212
     */
skodak's avatar
skodak committed
213
    public static function delete_users_parameters() {
Petr Skoda's avatar
Petr Skoda committed
214
215
216
217
218
        return new external_function_parameters(
            array(
                'userids' => new external_multiple_structure(new external_value(PARAM_INT, 'user ID')),
            )
        );
skodak's avatar
skodak committed
219
    }
Petr Skoda's avatar
Petr Skoda committed
220

221
222
    /**
     * Delete users
223
     *
224
     * @param array $userids
Sam Hemelryk's avatar
Sam Hemelryk committed
225
     * @return null
226
     * @since Moodle 2.2
227
     */
228
    public static function delete_users($userids) {
229
        global $CFG, $DB, $USER;
230
        require_once($CFG->dirroot."/user/lib.php");
231
232

        // Ensure the current user is allowed to run this function
233
        $context = context_system::instance();
234
235
236
        require_capability('moodle/user:delete', $context);
        self::validate_context($context);

237
        $params = self::validate_parameters(self::delete_users_parameters(), array('userids'=>$userids));
238
239
240
241
242

        $transaction = $DB->start_delegated_transaction();

        foreach ($params['userids'] as $userid) {
            $user = $DB->get_record('user', array('id'=>$userid, 'deleted'=>0), '*', MUST_EXIST);
243
            // must not allow deleting of admins or self!!!
244
245
246
247
248
            if (is_siteadmin($user)) {
                throw new moodle_exception('useradminodelete', 'error');
            }
            if ($USER->id == $user->id) {
                throw new moodle_exception('usernotdeletederror', 'error');
249
            }
250
            user_delete_user($user);
251
252
253
254
255
        }

        $transaction->allow_commit();

        return null;
256
    }
Petr Skoda's avatar
Petr Skoda committed
257
258
259

   /**
     * Returns description of method result value
260
261
262
     *
     * @return null
     * @since Moodle 2.2
Petr Skoda's avatar
Petr Skoda committed
263
     */
skodak's avatar
skodak committed
264
    public static function delete_users_returns() {
Petr Skoda's avatar
Petr Skoda committed
265
        return null;
skodak's avatar
skodak committed
266
    }
267
268


Petr Skoda's avatar
Petr Skoda committed
269
270
    /**
     * Returns description of method parameters
271
     *
Petr Skoda's avatar
Petr Skoda committed
272
     * @return external_function_parameters
273
     * @since Moodle 2.2
Petr Skoda's avatar
Petr Skoda committed
274
     */
skodak's avatar
skodak committed
275
    public static function update_users_parameters() {
276
        global $CFG;
277
        return new external_function_parameters(
278
279
280
281
            array(
                'users' => new external_multiple_structure(
                    new external_single_structure(
                        array(
282
                            'id'    => new external_value(PARAM_INT, 'ID of the user'),
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
                            '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),
                            '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(
302
303
304
305
306
                                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')
                                    )
307
                                ), 'User custom fields (also known as user profil fields)', VALUE_OPTIONAL),
308
309
310
311
312
313
314
                            '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),
315
316
317
318
319
                        )
                    )
                )
            )
        );
skodak's avatar
skodak committed
320
    }
321

322
323
    /**
     * Update users
324
     *
325
     * @param array $users
Sam Hemelryk's avatar
Sam Hemelryk committed
326
     * @return null
327
     * @since Moodle 2.2
328
     */
329
330
    public static function update_users($users) {
        global $CFG, $DB;
331
        require_once($CFG->dirroot."/user/lib.php");
332
        require_once($CFG->dirroot."/user/profile/lib.php"); //required for customfields related function
333
334

        // Ensure the current user is allowed to run this function
335
        $context = context_system::instance();
336
337
338
339
340
341
342
343
        require_capability('moodle/user:update', $context);
        self::validate_context($context);

        $params = self::validate_parameters(self::update_users_parameters(), array('users'=>$users));

        $transaction = $DB->start_delegated_transaction();

        foreach ($params['users'] as $user) {
344
            user_update_user($user);
345
346
347
348
349
350
351
352
353
354
            //update user custom fields
            if(!empty($user['customfields'])) {

                foreach($user['customfields'] as $customfield) {
                    $user["profile_field_".$customfield['type']] = $customfield['value']; //profile_save_data() saves profile file
                                                                                            //it's expecting a user with the correct id,
                                                                                            //and custom field to be named profile_field_"shortname"
                }
                profile_save_data((object) $user);
            }
355
356
357
358
359
360
361

            //preferences
            if (!empty($user['preferences'])) {
                foreach($user['preferences'] as $preference) {
                    set_user_preference($preference['type'], $preference['value'],$user['id']);
                }
            }
362
363
364
365
366
        }

        $transaction->allow_commit();

        return null;
367
    }
Petr Skoda's avatar
Petr Skoda committed
368
369
370

   /**
     * Returns description of method result value
371
372
373
     *
     * @return null
     * @since Moodle 2.2
Petr Skoda's avatar
Petr Skoda committed
374
     */
skodak's avatar
skodak committed
375
    public static function update_users_returns() {
Petr Skoda's avatar
Petr Skoda committed
376
        return null;
skodak's avatar
skodak committed
377
378
    }

379
380
381
382
   /**
   * Returns description of method parameters
   *
   * @return external_function_parameters
383
   * @since Moodle 2.4
384
   */
385
    public static function get_users_by_field_parameters() {
386
387
        return new external_function_parameters(
            array(
388
389
390
391
                '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'))
392
393
394
395
396
            )
        );
    }

    /**
397
     * Get user information for a unique field.
398
     *
399
400
401
402
     * @param string $field
     * @param array $values
     * @return array An array of arrays containg user profiles.
     * @since Moodle 2.4
403
     */
404
    public static function get_users_by_field($field, $values) {
405
406
407
        global $CFG, $USER, $DB;
        require_once($CFG->dirroot . "/user/lib.php");

408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
        $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':
423
                $paramtype = PARAM_RAW;
424
425
426
427
428
429
430
                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');
431
432
        }

433
434
435
436
437
438
439
440
        // Clean the values
        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;
441
442
        }

443
444
        // Retrieve the users
        $users = $DB->get_records_list('user', $field, $cleanedvalues, 'id');
445

446
447
        // Finally retrieve each users information
        $returnedusers = array();
448
449
450
        foreach ($users as $user) {
            $userdetails = user_get_user_details_courses($user);

451
452
453
454
            // Return the user only if the searched field is returned
            // Otherwise it means that the $USER was not allowed to search the returned user
            if (!empty($userdetails) and !empty($userdetails[$field])) {
                $returnedusers[] = $userdetails;
455
456
457
            }
        }

458
        return $returnedusers;
459
460
461
462
463
    }

    /**
     * Returns description of method result value
     *
464
465
     * @return external_multiple_structure
     * @since Moodle 2.4
466
     */
467
    public static function get_users_by_field_returns() {
468
        return new external_multiple_structure(self::user_description());
469
470
471
472
    }


    /**
473
     * Returns description of get_users() parameters.
474
475
     *
     * @return external_function_parameters
476
     * @since Moodle 2.5
477
478
479
480
481
482
483
     */
    public static function get_users_parameters() {
        return new external_function_parameters(
            array(
                'criteria' => new external_multiple_structure(
                    new external_single_structure(
                        array(
484
485
486
487
488
489
490
491
492
                            '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')
493
494
                        )
                    ), 'the key/value pairs to be considered in user search. Values can not be empty.
495
                        Specify different keys only once (fullname => \'user1\', auth => \'manual\', ...) -
496
                        key occurences are forbidden.
497
                        The search is executed with AND operator on the criterias. Invalid criterias (keys) are ignored,
498
499
500
                        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.'
501
502
503
504
505
                )
            )
        );
    }

506
    /**
507
     * Retrieve matching user.
508
     *
509
510
511
     * @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
512
513
514
515
516
517
518
519
520
     */
    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));

521
        // Validate the criteria and retrieve the users.
522
523
524
        $users = array();
        $warnings = array();
        $sqlparams = array();
525
526
527
528
        $usedkeys = array();

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

530
        foreach ($params['criteria'] as $criteriaindex => $criteria) {
531
532
533
534
535
536
537
538

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

539
            $invalidcriteria = false;
540
            // Clean the parameters.
541
542
543
544
545
546
547
548
549
            $paramtype = PARAM_RAW;
            switch ($criteria['key']) {
                case 'id':
                    $paramtype = PARAM_INT;
                    break;
                case 'idnumber':
                    $paramtype = PARAM_RAW;
                    break;
                case 'username':
550
                    $paramtype = PARAM_RAW;
551
552
                    break;
                case 'email':
553
                    // We use PARAM_RAW to allow searches with %.
554
555
556
557
558
559
560
561
562
563
                    $paramtype = PARAM_RAW;
                    break;
                case 'auth':
                    $paramtype = PARAM_AUTH;
                    break;
                case 'lastname':
                case 'firstname':
                    $paramtype = PARAM_TEXT;
                    break;
                default:
564
565
                    // Send back a warning that this search key is not supported in this version.
                    // This warning will make the function extandable without breaking clients.
566
                    $warnings[] = array(
567
                        'item' => $criteria['key'],
568
                        'warningcode' => 'invalidfieldparameter',
569
                        'message' => 'The search key \'' . $criteria['key'] . '\' is not supported, look at the web service documentation'
570
                    );
571
572
573
574
                    // Do not add this invalid criteria to the created SQL request.
                    $invalidcriteria = true;
                    unset($params['criteria'][$criteriaindex]);
                    break;
575
576
            }

577
578
            if (!$invalidcriteria) {
                $cleanedvalue = clean_param($criteria['value'], $paramtype);
579

580
                $sql .= ' AND ';
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599

                // 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;
                }
600
601
602
603
604
            }
        }

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

605
        // Finally retrieve each users information.
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
        $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;

                foreach($params['criteria'] as $criteria) {
                    if (empty($userdetails[$criteria['key']])) {
                        $validuser = false;
                    }
                }

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

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

    /**
631
     * Returns description of get_users result value.
632
633
     *
     * @return external_description
634
     * @since Moodle 2.5
635
636
637
638
     */
    public static function get_users_returns() {
        return new external_single_structure(
            array('users' => new external_multiple_structure(
639
                                self::user_description()
640
                             ),
641
                  'warnings' => new external_warnings('always set to \'key\'', 'faulty key name')
642
643
644
645
            )
        );
    }

Petr Skoda's avatar
Petr Skoda committed
646
647
    /**
     * Returns description of method parameters
648
     *
Petr Skoda's avatar
Petr Skoda committed
649
     * @return external_function_parameters
650
     * @since Moodle 2.2
651
652
     * @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
653
     */
654
    public static function get_users_by_id_parameters() {
Petr Skoda's avatar
Petr Skoda committed
655
        return new external_function_parameters(
656
657
658
                array(
                    'userids' => new external_multiple_structure(new external_value(PARAM_INT, 'user ID')),
                )
Petr Skoda's avatar
Petr Skoda committed
659
        );
skodak's avatar
skodak committed
660
    }
Petr Skoda's avatar
Petr Skoda committed
661

Petr Skoda's avatar
Petr Skoda committed
662
663
    /**
     * Get user information
664
665
666
     * - 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
667
     *
Petr Skoda's avatar
Petr Skoda committed
668
669
     * @param array $userids  array of user ids
     * @return array An array of arrays describing users
670
     * @since Moodle 2.2
671
672
     * @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
673
     */
674
    public static function get_users_by_id($userids) {
675
        global $CFG, $USER, $DB;
676
        require_once($CFG->dirroot . "/user/lib.php");
677

678
679
        $params = self::validate_parameters(self::get_users_by_id_parameters(),
                array('userids'=>$userids));
680

681
        list($sqluserids, $params) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
682
683
684
        $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
685
686
687
688
        $usersql = "SELECT u.* $uselect
                      FROM {user} u $ujoin
                     WHERE u.id $sqluserids";
        $users = $DB->get_recordset_sql($usersql, $params);
skodak's avatar
skodak committed
689

690
        $result = array();
691
        $hasuserupdatecap = has_capability('moodle/user:update', context_system::instance());
skodak's avatar
skodak committed
692
        foreach ($users as $user) {
Dongsheng Cai's avatar
Dongsheng Cai committed
693
694
695
            if (!empty($user->deleted)) {
                continue;
            }
696
            context_helper::preload_from_record($user);
697
            $usercontext = context_user::instance($user->id, IGNORE_MISSING);
698
            self::validate_context($usercontext);
699
700
            $currentuser = ($user->id == $USER->id);

701
702
703
704
705
706
707
708
709
710
            if ($userarray  = user_get_user_details($user)) {
                //fields matching permissions from /user/editadvanced.php
                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;
711
                }
712
                $result[] = $userarray;
Dongsheng Cai's avatar
Dongsheng Cai committed
713
            }
714
        }
Dongsheng Cai's avatar
Dongsheng Cai committed
715
        $users->close();
Petr Skoda's avatar
Petr Skoda committed
716
717

        return $result;
skodak's avatar
skodak committed
718
    }
Petr Skoda's avatar
Petr Skoda committed
719

720
    /**
Petr Skoda's avatar
Petr Skoda committed
721
     * Returns description of method result value
722
     *
Petr Skoda's avatar
Petr Skoda committed
723
     * @return external_description
724
     * @since Moodle 2.2
725
726
     * @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
727
     */
728
    public static function get_users_by_id_returns() {
729
730
        $additionalfields = array (
            'enrolledcourses' => new external_multiple_structure(
Damyon Wiese's avatar
Damyon Wiese committed
731
732
733
734
735
736
737
            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));
738
        return new external_multiple_structure(self::user_description($additionalfields));
Dongsheng Cai's avatar
Dongsheng Cai committed
739
    }
740

Dongsheng Cai's avatar
Dongsheng Cai committed
741
742
    /**
     * Returns description of method parameters
743
     *
Dongsheng Cai's avatar
Dongsheng Cai committed
744
     * @return external_function_parameters
745
     * @since Moodle 2.2
Dongsheng Cai's avatar
Dongsheng Cai committed
746
     */
747
    public static function get_course_user_profiles_parameters() {
Dongsheng Cai's avatar
Dongsheng Cai committed
748
749
750
751
752
753
754
        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'),
755
                        )
Dongsheng Cai's avatar
Dongsheng Cai committed
756
                    )
Petr Skoda's avatar
Petr Skoda committed
757
                )
Dongsheng Cai's avatar
Dongsheng Cai committed
758
759
760
761
762
763
            )
        );
    }

    /**
     * Get course participant's details
764
     *
Dongsheng Cai's avatar
Dongsheng Cai committed
765
766
     * @param array $userlist  array of user ids and according course ids
     * @return array An array of arrays describing course participants
767
     * @since Moodle 2.2
Dongsheng Cai's avatar
Dongsheng Cai committed
768
     */
769
    public static function get_course_user_profiles($userlist) {
Dongsheng Cai's avatar
Dongsheng Cai committed
770
771
        global $CFG, $USER, $DB;
        require_once($CFG->dirroot . "/user/lib.php");
772
        $params = self::validate_parameters(self::get_course_user_profiles_parameters(), array('userlist'=>$userlist));
Dongsheng Cai's avatar
Dongsheng Cai committed
773
774
775
776
777
778
779
780
781
782

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

        // cache all courses
        $courses = array();
783
        list($sqlcourseids, $params) = $DB->get_in_or_equal(array_unique($courseids), SQL_PARAMS_NAMED);
784
785
786
        $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;
787
        $coursesql = "SELECT c.* $cselect
Dongsheng Cai's avatar
Dongsheng Cai committed
788
789
790
791
792
                        FROM {course} c $cjoin
                       WHERE c.id $sqlcourseids";
        $rs = $DB->get_recordset_sql($coursesql, $params);
        foreach ($rs as $course) {
            // adding course contexts to cache
793
            context_helper::preload_from_record($course);
Dongsheng Cai's avatar
Dongsheng Cai committed
794
795
796
797
798
            // cache courses
            $courses[$course->id] = $course;
        }
        $rs->close();

799
        list($sqluserids, $params) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
800
801
802
        $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
803
804
805
806
807
808
809
810
811
        $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;
            }
812
            context_helper::preload_from_record($user);
Dongsheng Cai's avatar
Dongsheng Cai committed
813
            $course = $courses[$courseids[$user->id]];
814
            $context = context_course::instance($courseids[$user->id], IGNORE_MISSING);
Dongsheng Cai's avatar
Dongsheng Cai committed
815
            self::validate_context($context);
816
817
            if ($userarray = user_get_user_details($user, $course)) {
                $result[] = $userarray;
Dongsheng Cai's avatar
Dongsheng Cai committed
818
            }
819
        }
Dongsheng Cai's avatar
Dongsheng Cai committed
820

821
        $users->close();
Dongsheng Cai's avatar
Dongsheng Cai committed
822

823
824
        return $result;
    }
Dongsheng Cai's avatar
Dongsheng Cai committed
825

826
827
    /**
     * Returns description of method result value
828
     *
829
     * @return external_description
830
     * @since Moodle 2.2
831
     */
832
    public static function get_course_user_profiles_returns() {
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
        $additionalfields = array(
                    '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(
Damyon Wiese's avatar
Damyon Wiese committed
853
854
855
856
857
858
859
                        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)
860
861
                    );

862
        return new external_multiple_structure(self::user_description($additionalfields));
863
864
865
866
867
    }

    /**
     * Create user return value description.
     *
868
     * @param array $additionalfields some additional field
869
870
     * @return single_structure_description
     */
871
    public static function user_description($additionalfields = array()) {
872
        $userfields = array(
873
                    'id'    => new external_value(PARAM_INT, 'ID of the user'),
874
                    'username'    => new external_value(PARAM_RAW, 'The username', VALUE_OPTIONAL),
875
876
877
878
                    '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),
879
                    'address'     => new external_value(PARAM_TEXT, 'Postal address', VALUE_OPTIONAL),
880
881
882
883
884
885
886
887
888
                    '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),
889
                    'idnumber'    => new external_value(PARAM_RAW, 'An arbitrary ID code number perhaps from the institution', VALUE_OPTIONAL),
890
891
892
                    '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),
893
894
895
896
897
898
                    '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),
                    '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),
899
                    'description' => new external_value(PARAM_RAW, 'User profile description', VALUE_OPTIONAL),
900
                    'descriptionformat' => new external_format_value('description', VALUE_OPTIONAL),
901
902
903
904
905
906
907
908
909
910
911
912
913
                    'city'        => new external_value(PARAM_NOTAGS, 'Home city of the user', VALUE_OPTIONAL),
                    'url'         => new external_value(PARAM_URL, 'URL of the user', VALUE_OPTIONAL),
                    'country'     => new external_value(PARAM_ALPHA, 'Home country code of the user, such as AU or CZ', VALUE_OPTIONAL),
                    'profileimageurlsmall' => new external_value(PARAM_URL, 'User image profile URL - small version'),
                    'profileimageurl' => new external_value(PARAM_URL, 'User image profile URL - big version'),
                    'customfields' => new external_multiple_structure(
                        new external_single_structure(
                            array(
                                'type'  => new external_value(PARAM_ALPHANUMEXT, 'The type of the custom field - text field, checkbox...'),
                                'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
                                'name' => new external_value(PARAM_RAW, 'The name of the custom field'),
                                'shortname' => new external_value(PARAM_RAW, 'The shortname of the custom field - to be able to build the field class in the code'),
                            )
914
                        ), 'User custom fields (also known as user profile fields)', VALUE_OPTIONAL),
915
916
917
918
919
920
                    'preferences' => new external_multiple_structure(
                        new external_single_structure(
                            array(
                                'name'  => new external_value(PARAM_ALPHANUMEXT, 'The name of the preferences'),
                                'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
                            )
921
922
923
924
925
926
                    ), 'Users preferences', VALUE_OPTIONAL)
                );
        if (!empty($additionalfields)) {
            $userfields = array_merge($userfields, $additionalfields);
        }
        return new external_single_structure($userfields);
927
    }
928

929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
    /**
     * Returns description of method parameters
     *
     * @return external_function_parameters
     * @since Moodle 2.6
     */
    public static function add_user_private_files_parameters() {
        return new external_function_parameters(
            array(
                'draftid' => new external_value(PARAM_INT, 'draft area id')
            )
        );
    }

    /**
     * Copy files from a draft area to users private files area.
     *
     * @param int $draftid Id of a draft area containing files.
     * @return array An array of warnings
     * @since Moodle 2.6
     */
    public static function add_user_private_files($draftid) {
        global $CFG, $USER, $DB;

        require_once($CFG->dirroot . "/user/lib.php");
        $params = self::validate_parameters(self::add_user_private_files_parameters(), array('draftid'=>$draftid));

        if (isguestuser()) {
            throw new invalid_parameter_exception('Guest users cannot upload files');
        }

        $context = context_user::instance($USER->id);
        require_capability('moodle/user:manageownfiles', $context);

        $maxbytes = $CFG->userquota;
        $maxareabytes = $CFG->userquota;
        if (has_capability('moodle/user:ignoreuserquota', $context)) {
            $maxbytes = USER_CAN_IGNORE_FILE_SIZE_LIMITS;
            $maxareabytes = FILE_AREA_MAX_BYTES_UNLIMITED;
        }

        $options = array('subdirs' => 1,
                         'maxbytes' => $maxbytes,
                         'maxfiles' => -1,
                         'accepted_types' => '*',
                         'areamaxbytes' => $maxareabytes);

        file_save_draft_area_files($draftid, $context->id, 'user', 'private', 0, $options);

        return null;
    }

    /**
     * Returns description of method result value
     *
     * @return external_description
     * @since Moodle 2.2
     */
    public static function add_user_private_files_returns() {
        return null;
    }

991
992
993
994
995
996
997
998
999
1000
    /**
     * Returns description of method parameters.
     *
     * @return external_function_parameters
     * @since Moodle 2.6
     */
    public static function add_user_device_parameters() {
        return new external_function_parameters(
            array(
                'appid'     => new external_value(PARAM_NOTAGS, 'the app id, usually something like com.moodle.moodlemobile'),
For faster browsing, not all history is shown. View entire blame