ce0b580fcf6a0a0a835b8ae152e75b5fdacb1f74
[moodle.git] / user / externallib.php
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
17 /**
18  * External user API
19  *
20  * @package    core_user
21  * @category   external
22  * @copyright  2009 Petr Skodak
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
26 defined('MOODLE_INTERNAL') || die();
28 require_once("$CFG->libdir/externallib.php");
30 /**
31  * User external functions
32  *
33  * @package    core_user
34  * @category   external
35  * @copyright  2011 Jerome Mouneyrac
36  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37  * @since Moodle 2.2
38  */
39 class core_user_external extends external_api {
41     /**
42      * Returns description of method parameters
43      *
44      * @return external_function_parameters
45      * @since Moodle 2.2
46      */
47     public static function create_users_parameters() {
48         global $CFG;
50         return new external_function_parameters(
51             array(
52                 'users' => new external_multiple_structure(
53                     new external_single_structure(
54                         array(
55                             'username' =>
56                                 new external_value(core_user::get_property_type('username'), 'Username policy is defined in Moodle security config.'),
57                             'password' =>
58                                 new external_value(core_user::get_property_type('password'), 'Plain text password consisting of any characters', VALUE_OPTIONAL),
59                             'createpassword' =>
60                                 new external_value(PARAM_BOOL, 'True if password should be created and mailed to user.',
61                                     VALUE_OPTIONAL),
62                             'firstname' =>
63                                 new external_value(core_user::get_property_type('firstname'), 'The first name(s) of the user'),
64                             'lastname' =>
65                                 new external_value(core_user::get_property_type('lastname'), 'The family name of the user'),
66                             'email' =>
67                                 new external_value(core_user::get_property_type('email'), 'A valid and unique email address'),
68                             'auth' =>
69                                 new external_value(core_user::get_property_type('auth'), 'Auth plugins include manual, ldap, etc', VALUE_DEFAULT,
70                                     'manual', core_user::get_property_null('auth')),
71                             'idnumber' =>
72                                 new external_value(core_user::get_property_type('idnumber'), 'An arbitrary ID code number perhaps from the institution',
73                                     VALUE_DEFAULT, ''),
74                             'lang' =>
75                                 new external_value(core_user::get_property_type('lang'), 'Language code such as "en", must exist on server', VALUE_DEFAULT,
76                                     core_user::get_property_default('lang'), core_user::get_property_null('lang')),
77                             'calendartype' =>
78                                 new external_value(core_user::get_property_type('calendartype'), 'Calendar type such as "gregorian", must exist on server',
79                                     VALUE_DEFAULT, $CFG->calendartype, VALUE_OPTIONAL),
80                             'theme' =>
81                                 new external_value(core_user::get_property_type('theme'), 'Theme name such as "standard", must exist on server',
82                                     VALUE_OPTIONAL),
83                             'timezone' =>
84                                 new external_value(core_user::get_property_type('timezone'), 'Timezone code such as Australia/Perth, or 99 for default',
85                                     VALUE_OPTIONAL),
86                             'mailformat' =>
87                                 new external_value(core_user::get_property_type('mailformat'), 'Mail format code is 0 for plain text, 1 for HTML etc',
88                                     VALUE_OPTIONAL),
89                             'description' =>
90                                 new external_value(core_user::get_property_type('description'), 'User profile description, no HTML', VALUE_OPTIONAL),
91                             'city' =>
92                                 new external_value(core_user::get_property_type('city'), 'Home city of the user', VALUE_OPTIONAL),
93                             'country' =>
94                                 new external_value(core_user::get_property_type('country'), 'Home country code of the user, such as AU or CZ', VALUE_OPTIONAL),
95                             'firstnamephonetic' =>
96                                 new external_value(core_user::get_property_type('firstnamephonetic'), 'The first name(s) phonetically of the user', VALUE_OPTIONAL),
97                             'lastnamephonetic' =>
98                                 new external_value(core_user::get_property_type('lastnamephonetic'), 'The family name phonetically of the user', VALUE_OPTIONAL),
99                             'middlename' =>
100                                 new external_value(core_user::get_property_type('middlename'), 'The middle name of the user', VALUE_OPTIONAL),
101                             'alternatename' =>
102                                 new external_value(core_user::get_property_type('alternatename'), 'The alternate name of the user', VALUE_OPTIONAL),
103                             'preferences' => new external_multiple_structure(
104                                 new external_single_structure(
105                                     array(
106                                         'type'  => new external_value(PARAM_ALPHANUMEXT, 'The name of the preference'),
107                                         'value' => new external_value(PARAM_RAW, 'The value of the preference')
108                                     )
109                                 ), 'User preferences', VALUE_OPTIONAL),
110                             'customfields' => new external_multiple_structure(
111                                 new external_single_structure(
112                                     array(
113                                         'type'  => new external_value(PARAM_ALPHANUMEXT, 'The name of the custom field'),
114                                         'value' => new external_value(PARAM_RAW, 'The value of the custom field')
115                                     )
116                                 ), 'User custom fields (also known as user profil fields)', VALUE_OPTIONAL)
117                         )
118                     )
119                 )
120             )
121         );
122     }
124     /**
125      * Create one or more users.
126      *
127      * @throws invalid_parameter_exception
128      * @param array $users An array of users to create.
129      * @return array An array of arrays
130      * @since Moodle 2.2
131      */
132     public static function create_users($users) {
133         global $CFG, $DB;
134         require_once($CFG->dirroot."/lib/weblib.php");
135         require_once($CFG->dirroot."/user/lib.php");
136         require_once($CFG->dirroot."/user/profile/lib.php"); // Required for customfields related function.
138         // Ensure the current user is allowed to run this function.
139         $context = context_system::instance();
140         self::validate_context($context);
141         require_capability('moodle/user:create', $context);
143         // Do basic automatic PARAM checks on incoming data, using params description.
144         // If any problems are found then exceptions are thrown with helpful error messages.
145         $params = self::validate_parameters(self::create_users_parameters(), array('users' => $users));
147         $availableauths  = core_component::get_plugin_list('auth');
148         unset($availableauths['mnet']);       // These would need mnethostid too.
149         unset($availableauths['webservice']); // We do not want new webservice users for now.
151         $availablethemes = core_component::get_plugin_list('theme');
152         $availablelangs  = get_string_manager()->get_list_of_translations();
154         $transaction = $DB->start_delegated_transaction();
156         $userids = array();
157         $createpassword = false;
158         foreach ($params['users'] as $user) {
159             // Make sure that the username doesn't already exist.
160             if ($DB->record_exists('user', array('username' => $user['username'], 'mnethostid' => $CFG->mnet_localhost_id))) {
161                 throw new invalid_parameter_exception('Username already exists: '.$user['username']);
162             }
164             // Make sure auth is valid.
165             if (empty($availableauths[$user['auth']])) {
166                 throw new invalid_parameter_exception('Invalid authentication type: '.$user['auth']);
167             }
169             // Make sure lang is valid.
170             if (empty($availablelangs[$user['lang']])) {
171                 throw new invalid_parameter_exception('Invalid language code: '.$user['lang']);
172             }
174             // Make sure lang is valid.
175             if (!empty($user['theme']) && empty($availablethemes[$user['theme']])) { // Theme is VALUE_OPTIONAL,
176                                                                                      // so no default value
177                                                                                      // We need to test if the client sent it
178                                                                                      // => !empty($user['theme']).
179                 throw new invalid_parameter_exception('Invalid theme: '.$user['theme']);
180             }
182             // Make sure we have a password or have to create one.
183             if (empty($user['password']) && empty($user['createpassword'])) {
184                 throw new invalid_parameter_exception('Invalid password: you must provide a password, or set createpassword.');
185             }
187             $user['confirmed'] = true;
188             $user['mnethostid'] = $CFG->mnet_localhost_id;
190             // Start of user info validation.
191             // Make sure we validate current user info as handled by current GUI. See user/editadvanced_form.php func validation().
192             if (!validate_email($user['email'])) {
193                 throw new invalid_parameter_exception('Email address is invalid: '.$user['email']);
194             } else if (empty($CFG->allowaccountssameemail) &&
195                     $DB->record_exists('user', array('email' => $user['email'], 'mnethostid' => $user['mnethostid']))) {
196                 throw new invalid_parameter_exception('Email address already exists: '.$user['email']);
197             }
198             // End of user info validation.
200             $createpassword = !empty($user['createpassword']);
201             unset($user['createpassword']);
202             if ($createpassword) {
203                 $user['password'] = '';
204                 $updatepassword = false;
205             } else {
206                 $updatepassword = true;
207             }
209             // Create the user data now!
210             $user['id'] = user_create_user($user, $updatepassword, false);
212             // Custom fields.
213             if (!empty($user['customfields'])) {
214                 foreach ($user['customfields'] as $customfield) {
215                     // Profile_save_data() saves profile file it's expecting a user with the correct id,
216                     // and custom field to be named profile_field_"shortname".
217                     $user["profile_field_".$customfield['type']] = $customfield['value'];
218                 }
219                 profile_save_data((object) $user);
220             }
222             if ($createpassword) {
223                 $userobject = (object)$user;
224                 setnew_password_and_mail($userobject);
225                 unset_user_preference('create_password', $userobject);
226                 set_user_preference('auth_forcepasswordchange', 1, $userobject);
227             }
229             // Trigger event.
230             \core\event\user_created::create_from_userid($user['id'])->trigger();
232             // Preferences.
233             if (!empty($user['preferences'])) {
234                 $userpref = (object)$user;
235                 foreach ($user['preferences'] as $preference) {
236                     $userpref->{'preference_'.$preference['type']} = $preference['value'];
237                 }
238                 useredit_update_user_preference($userpref);
239             }
241             $userids[] = array('id' => $user['id'], 'username' => $user['username']);
242         }
244         $transaction->allow_commit();
246         return $userids;
247     }
249     /**
250      * Returns description of method result value
251      *
252      * @return external_description
253      * @since Moodle 2.2
254      */
255     public static function create_users_returns() {
256         return new external_multiple_structure(
257             new external_single_structure(
258                 array(
259                     'id'       => new external_value(core_user::get_property_type('id'), 'user id'),
260                     'username' => new external_value(core_user::get_property_type('username'), 'user name'),
261                 )
262             )
263         );
264     }
267     /**
268      * Returns description of method parameters
269      *
270      * @return external_function_parameters
271      * @since Moodle 2.2
272      */
273     public static function delete_users_parameters() {
274         return new external_function_parameters(
275             array(
276                 'userids' => new external_multiple_structure(new external_value(core_user::get_property_type('id'), 'user ID')),
277             )
278         );
279     }
281     /**
282      * Delete users
283      *
284      * @throws moodle_exception
285      * @param array $userids
286      * @return null
287      * @since Moodle 2.2
288      */
289     public static function delete_users($userids) {
290         global $CFG, $DB, $USER;
291         require_once($CFG->dirroot."/user/lib.php");
293         // Ensure the current user is allowed to run this function.
294         $context = context_system::instance();
295         require_capability('moodle/user:delete', $context);
296         self::validate_context($context);
298         $params = self::validate_parameters(self::delete_users_parameters(), array('userids' => $userids));
300         $transaction = $DB->start_delegated_transaction();
302         foreach ($params['userids'] as $userid) {
303             $user = $DB->get_record('user', array('id' => $userid, 'deleted' => 0), '*', MUST_EXIST);
304             // Must not allow deleting of admins or self!!!
305             if (is_siteadmin($user)) {
306                 throw new moodle_exception('useradminodelete', 'error');
307             }
308             if ($USER->id == $user->id) {
309                 throw new moodle_exception('usernotdeletederror', 'error');
310             }
311             user_delete_user($user);
312         }
314         $transaction->allow_commit();
316         return null;
317     }
319     /**
320      * Returns description of method result value
321      *
322      * @return null
323      * @since Moodle 2.2
324      */
325     public static function delete_users_returns() {
326         return null;
327     }
329     /**
330      * Returns description of method parameters.
331      *
332      * @return external_function_parameters
333      * @since Moodle 3.2
334      */
335     public static function update_user_preferences_parameters() {
336         return new external_function_parameters(
337             array(
338                 'userid' => new external_value(PARAM_INT, 'id of the user, default to current user', VALUE_DEFAULT, 0),
339                 'emailstop' => new external_value(core_user::get_property_type('emailstop'),
340                     'Enable or disable notifications for this user', VALUE_DEFAULT, null),
341                 'preferences' => new external_multiple_structure(
342                     new external_single_structure(
343                         array(
344                             'type'  => new external_value(PARAM_ALPHANUMEXT, 'The name of the preference'),
345                             'value' => new external_value(PARAM_RAW, 'The value of the preference')
346                         )
347                     ), 'User preferences', VALUE_DEFAULT, array()
348                 )
349             )
350         );
351     }
353     /**
354      * Update the user's preferences.
355      *
356      * @param int $userid
357      * @param bool|null $emailstop
358      * @param array $preferences
359      * @return null
360      * @since Moodle 3.2
361      */
362     public static function update_user_preferences($userid, $emailstop = null, $preferences = array()) {
363         global $USER, $CFG;
365         require_once($CFG->dirroot . '/user/lib.php');
366         require_once($CFG->dirroot . '/user/editlib.php');
367         require_once($CFG->dirroot . '/message/lib.php');
369         if (empty($userid)) {
370             $userid = $USER->id;
371         }
373         $systemcontext = context_system::instance();
374         self::validate_context($systemcontext);
375         $params = array(
376             'userid' => $userid,
377             'emailstop' => $emailstop,
378             'preferences' => $preferences
379         );
380         self::validate_parameters(self::update_user_preferences_parameters(), $params);
382         // Preferences.
383         if (!empty($preferences)) {
384             $userpref = ['id' => $userid];
385             foreach ($preferences as $preference) {
386                 $userpref['preference_' . $preference['type']] = $preference['value'];
387             }
388             useredit_update_user_preference($userpref);
389         }
391         // Check if they want to update the email.
392         if ($emailstop !== null) {
393             $otheruser = ($userid == $USER->id) ? $USER : core_user::get_user($userid, '*', MUST_EXIST);
394             core_user::require_active_user($otheruser);
395             if (core_message_can_edit_message_profile($otheruser) && $otheruser->emailstop != $emailstop) {
396                 $user = new stdClass();
397                 $user->id = $userid;
398                 $user->emailstop = $emailstop;
399                 user_update_user($user);
401                 // Update the $USER if we should.
402                 if ($userid == $USER->id) {
403                     $USER->emailstop = $emailstop;
404                 }
405             }
406         }
408         return null;
409     }
411     /**
412      * Returns description of method result value
413      *
414      * @return null
415      * @since Moodle 3.2
416      */
417     public static function update_user_preferences_returns() {
418         return null;
419     }
421     /**
422      * Returns description of method parameters
423      *
424      * @return external_function_parameters
425      * @since Moodle 2.2
426      */
427     public static function update_users_parameters() {
428         return new external_function_parameters(
429             array(
430                 'users' => new external_multiple_structure(
431                     new external_single_structure(
432                         array(
433                             'id' =>
434                                 new external_value(core_user::get_property_type('id'), 'ID of the user'),
435                             'username' =>
436                                 new external_value(core_user::get_property_type('username'), 'Username policy is defined in Moodle security config.',
437                                     VALUE_OPTIONAL, '', NULL_NOT_ALLOWED),
438                             'password' =>
439                                 new external_value(core_user::get_property_type('password'), 'Plain text password consisting of any characters', VALUE_OPTIONAL,
440                                     '', NULL_NOT_ALLOWED),
441                             'firstname' =>
442                                 new external_value(core_user::get_property_type('firstname'), 'The first name(s) of the user', VALUE_OPTIONAL, '',
443                                     NULL_NOT_ALLOWED),
444                             'lastname' =>
445                                 new external_value(core_user::get_property_type('lastname'), 'The family name of the user', VALUE_OPTIONAL),
446                             'email' =>
447                                 new external_value(core_user::get_property_type('email'), 'A valid and unique email address', VALUE_OPTIONAL, '',
448                                     NULL_NOT_ALLOWED),
449                             'auth' =>
450                                 new external_value(core_user::get_property_type('auth'), 'Auth plugins include manual, ldap, etc', VALUE_OPTIONAL, '',
451                                     NULL_NOT_ALLOWED),
452                             'suspended' =>
453                                 new external_value(core_user::get_property_type('suspended'), 'Suspend user account, either false to enable user login or true to disable it', VALUE_OPTIONAL),
454                             'idnumber' =>
455                                 new external_value(core_user::get_property_type('idnumber'), 'An arbitrary ID code number perhaps from the institution',
456                                     VALUE_OPTIONAL),
457                             'lang' =>
458                                 new external_value(core_user::get_property_type('lang'), 'Language code such as "en", must exist on server',
459                                     VALUE_OPTIONAL, '', NULL_NOT_ALLOWED),
460                             'calendartype' =>
461                                 new external_value(core_user::get_property_type('calendartype'), 'Calendar type such as "gregorian", must exist on server',
462                                     VALUE_OPTIONAL, '', NULL_NOT_ALLOWED),
463                             'theme' =>
464                                 new external_value(core_user::get_property_type('theme'), 'Theme name such as "standard", must exist on server',
465                                     VALUE_OPTIONAL),
466                             'timezone' =>
467                                 new external_value(core_user::get_property_type('timezone'), 'Timezone code such as Australia/Perth, or 99 for default',
468                                     VALUE_OPTIONAL),
469                             'mailformat' =>
470                                 new external_value(core_user::get_property_type('mailformat'), 'Mail format code is 0 for plain text, 1 for HTML etc',
471                                     VALUE_OPTIONAL),
472                             'description' =>
473                                 new external_value(core_user::get_property_type('description'), 'User profile description, no HTML', VALUE_OPTIONAL),
474                             'city' =>
475                                 new external_value(core_user::get_property_type('city'), 'Home city of the user', VALUE_OPTIONAL),
476                             'country' =>
477                                 new external_value(core_user::get_property_type('country'), 'Home country code of the user, such as AU or CZ', VALUE_OPTIONAL),
478                             'firstnamephonetic' =>
479                                 new external_value(core_user::get_property_type('firstnamephonetic'), 'The first name(s) phonetically of the user', VALUE_OPTIONAL),
480                             'lastnamephonetic' =>
481                                 new external_value(core_user::get_property_type('lastnamephonetic'), 'The family name phonetically of the user', VALUE_OPTIONAL),
482                             'middlename' =>
483                                 new external_value(core_user::get_property_type('middlename'), 'The middle name of the user', VALUE_OPTIONAL),
484                             'alternatename' =>
485                                 new external_value(core_user::get_property_type('alternatename'), 'The alternate name of the user', VALUE_OPTIONAL),
486                             'userpicture' =>
487                                 new external_value(PARAM_INT, 'The itemid where the new user picture '.
488                                     'has been uploaded to, 0 to delete', VALUE_OPTIONAL),
489                             'customfields' => new external_multiple_structure(
490                                 new external_single_structure(
491                                     array(
492                                         'type'  => new external_value(PARAM_ALPHANUMEXT, 'The name of the custom field'),
493                                         'value' => new external_value(PARAM_RAW, 'The value of the custom field')
494                                     )
495                                 ), 'User custom fields (also known as user profil fields)', VALUE_OPTIONAL),
496                             'preferences' => new external_multiple_structure(
497                                 new external_single_structure(
498                                     array(
499                                         'type'  => new external_value(PARAM_ALPHANUMEXT, 'The name of the preference'),
500                                         'value' => new external_value(PARAM_RAW, 'The value of the preference')
501                                     )
502                                 ), 'User preferences', VALUE_OPTIONAL),
503                         )
504                     )
505                 )
506             )
507         );
508     }
510     /**
511      * Update users
512      *
513      * @param array $users
514      * @return null
515      * @since Moodle 2.2
516      */
517     public static function update_users($users) {
518         global $CFG, $DB, $USER;
519         require_once($CFG->dirroot."/user/lib.php");
520         require_once($CFG->dirroot."/user/profile/lib.php"); // Required for customfields related function.
521         require_once($CFG->dirroot.'/user/editlib.php');
523         // Ensure the current user is allowed to run this function.
524         $context = context_system::instance();
525         require_capability('moodle/user:update', $context);
526         self::validate_context($context);
528         $params = self::validate_parameters(self::update_users_parameters(), array('users' => $users));
530         $filemanageroptions = array('maxbytes' => $CFG->maxbytes,
531                 'subdirs'        => 0,
532                 'maxfiles'       => 1,
533                 'accepted_types' => 'web_image');
535         $transaction = $DB->start_delegated_transaction();
537         foreach ($params['users'] as $user) {
538             // First check the user exists.
539             if (!$existinguser = core_user::get_user($user['id'])) {
540                 continue;
541             }
542             // Check if we are trying to update an admin.
543             if ($existinguser->id != $USER->id and is_siteadmin($existinguser) and !is_siteadmin($USER)) {
544                 continue;
545             }
546             // Other checks (deleted, remote or guest users).
547             if ($existinguser->deleted or is_mnet_remote_user($existinguser) or isguestuser($existinguser->id)) {
548                 continue;
549             }
550             // Check duplicated emails.
551             if (isset($user['email']) && $user['email'] !== $existinguser->email) {
552                 if (!validate_email($user['email'])) {
553                     continue;
554                 } else if (empty($CFG->allowaccountssameemail) &&
555                         $DB->record_exists('user', array('email' => $user['email'], 'mnethostid' => $CFG->mnet_localhost_id))) {
556                     continue;
557                 }
558             }
560             user_update_user($user, true, false);
562             // Update user picture if it was specified for this user.
563             if (empty($CFG->disableuserimages) && isset($user['userpicture'])) {
564                 $userobject = (object)$user;
566                 $userobject->deletepicture = null;
568                 if ($user['userpicture'] == 0) {
569                     $userobject->deletepicture = true;
570                 } else {
571                     $userobject->imagefile = $user['userpicture'];
572                 }
574                 core_user::update_picture($userobject, $filemanageroptions);
575             }
577             // Update user custom fields.
578             if (!empty($user['customfields'])) {
580                 foreach ($user['customfields'] as $customfield) {
581                     // Profile_save_data() saves profile file it's expecting a user with the correct id,
582                     // and custom field to be named profile_field_"shortname".
583                     $user["profile_field_".$customfield['type']] = $customfield['value'];
584                 }
585                 profile_save_data((object) $user);
586             }
588             // Trigger event.
589             \core\event\user_updated::create_from_userid($user['id'])->trigger();
591             // Preferences.
592             if (!empty($user['preferences'])) {
593                 $userpref = clone($existinguser);
594                 foreach ($user['preferences'] as $preference) {
595                     $userpref->{'preference_'.$preference['type']} = $preference['value'];
596                 }
597                 useredit_update_user_preference($userpref);
598             }
599             if (isset($user['suspended']) and $user['suspended']) {
600                 \core\session\manager::kill_user_sessions($user['id']);
601             }
602         }
604         $transaction->allow_commit();
606         return null;
607     }
609     /**
610      * Returns description of method result value
611      *
612      * @return null
613      * @since Moodle 2.2
614      */
615     public static function update_users_returns() {
616         return null;
617     }
619     /**
620      * Returns description of method parameters
621      *
622      * @return external_function_parameters
623      * @since Moodle 2.4
624      */
625     public static function get_users_by_field_parameters() {
626         return new external_function_parameters(
627             array(
628                 'field' => new external_value(PARAM_ALPHA, 'the search field can be
629                     \'id\' or \'idnumber\' or \'username\' or \'email\''),
630                 'values' => new external_multiple_structure(
631                         new external_value(PARAM_RAW, 'the value to match'))
632             )
633         );
634     }
636     /**
637      * Get user information for a unique field.
638      *
639      * @throws coding_exception
640      * @throws invalid_parameter_exception
641      * @param string $field
642      * @param array $values
643      * @return array An array of arrays containg user profiles.
644      * @since Moodle 2.4
645      */
646     public static function get_users_by_field($field, $values) {
647         global $CFG, $USER, $DB;
648         require_once($CFG->dirroot . "/user/lib.php");
650         $params = self::validate_parameters(self::get_users_by_field_parameters(),
651                 array('field' => $field, 'values' => $values));
653         // This array will keep all the users that are allowed to be searched,
654         // according to the current user's privileges.
655         $cleanedvalues = array();
657         switch ($field) {
658             case 'id':
659                 $paramtype = core_user::get_property_type('id');
660                 break;
661             case 'idnumber':
662                 $paramtype = core_user::get_property_type('idnumber');
663                 break;
664             case 'username':
665                 $paramtype = core_user::get_property_type('username');
666                 break;
667             case 'email':
668                 $paramtype = core_user::get_property_type('email');
669                 break;
670             default:
671                 throw new coding_exception('invalid field parameter',
672                         'The search field \'' . $field . '\' is not supported, look at the web service documentation');
673         }
675         // Clean the values.
676         foreach ($values as $value) {
677             $cleanedvalue = clean_param($value, $paramtype);
678             if ( $value != $cleanedvalue) {
679                 throw new invalid_parameter_exception('The field \'' . $field .
680                         '\' value is invalid: ' . $value . '(cleaned value: '.$cleanedvalue.')');
681             }
682             $cleanedvalues[] = $cleanedvalue;
683         }
685         // Retrieve the users.
686         $users = $DB->get_records_list('user', $field, $cleanedvalues, 'id');
688         $context = context_system::instance();
689         self::validate_context($context);
691         // Finally retrieve each users information.
692         $returnedusers = array();
693         foreach ($users as $user) {
694             $userdetails = user_get_user_details_courses($user);
696             // Return the user only if the searched field is returned.
697             // Otherwise it means that the $USER was not allowed to search the returned user.
698             if (!empty($userdetails) and !empty($userdetails[$field])) {
699                 $returnedusers[] = $userdetails;
700             }
701         }
703         return $returnedusers;
704     }
706     /**
707      * Returns description of method result value
708      *
709      * @return external_multiple_structure
710      * @since Moodle 2.4
711      */
712     public static function get_users_by_field_returns() {
713         return new external_multiple_structure(self::user_description());
714     }
717     /**
718      * Returns description of get_users() parameters.
719      *
720      * @return external_function_parameters
721      * @since Moodle 2.5
722      */
723     public static function get_users_parameters() {
724         return new external_function_parameters(
725             array(
726                 'criteria' => new external_multiple_structure(
727                     new external_single_structure(
728                         array(
729                             'key' => new external_value(PARAM_ALPHA, 'the user column to search, expected keys (value format) are:
730                                 "id" (int) matching user id,
731                                 "lastname" (string) user last name (Note: you can use % for searching but it may be considerably slower!),
732                                 "firstname" (string) user first name (Note: you can use % for searching but it may be considerably slower!),
733                                 "idnumber" (string) matching user idnumber,
734                                 "username" (string) matching user username,
735                                 "email" (string) user email (Note: you can use % for searching but it may be considerably slower!),
736                                 "auth" (string) matching user auth plugin'),
737                             'value' => new external_value(PARAM_RAW, 'the value to search')
738                         )
739                     ), 'the key/value pairs to be considered in user search. Values can not be empty.
740                         Specify different keys only once (fullname => \'user1\', auth => \'manual\', ...) -
741                         key occurences are forbidden.
742                         The search is executed with AND operator on the criterias. Invalid criterias (keys) are ignored,
743                         the search is still executed on the valid criterias.
744                         You can search without criteria, but the function is not designed for it.
745                         It could very slow or timeout. The function is designed to search some specific users.'
746                 )
747             )
748         );
749     }
751     /**
752      * Retrieve matching user.
753      *
754      * @throws moodle_exception
755      * @param array $criteria the allowed array keys are id/lastname/firstname/idnumber/username/email/auth.
756      * @return array An array of arrays containing user profiles.
757      * @since Moodle 2.5
758      */
759     public static function get_users($criteria = array()) {
760         global $CFG, $USER, $DB;
762         require_once($CFG->dirroot . "/user/lib.php");
764         $params = self::validate_parameters(self::get_users_parameters(),
765                 array('criteria' => $criteria));
767         // Validate the criteria and retrieve the users.
768         $users = array();
769         $warnings = array();
770         $sqlparams = array();
771         $usedkeys = array();
773         // Do not retrieve deleted users.
774         $sql = ' deleted = 0';
776         foreach ($params['criteria'] as $criteriaindex => $criteria) {
778             // Check that the criteria has never been used.
779             if (array_key_exists($criteria['key'], $usedkeys)) {
780                 throw new moodle_exception('keyalreadyset', '', '', null, 'The key ' . $criteria['key'] . ' can only be sent once');
781             } else {
782                 $usedkeys[$criteria['key']] = true;
783             }
785             $invalidcriteria = false;
786             // Clean the parameters.
787             $paramtype = PARAM_RAW;
788             switch ($criteria['key']) {
789                 case 'id':
790                     $paramtype = core_user::get_property_type('id');
791                     break;
792                 case 'idnumber':
793                     $paramtype = core_user::get_property_type('idnumber');
794                     break;
795                 case 'username':
796                     $paramtype = core_user::get_property_type('username');
797                     break;
798                 case 'email':
799                     // We use PARAM_RAW to allow searches with %.
800                     $paramtype = core_user::get_property_type('email');
801                     break;
802                 case 'auth':
803                     $paramtype = core_user::get_property_type('auth');
804                     break;
805                 case 'lastname':
806                 case 'firstname':
807                     $paramtype = core_user::get_property_type('firstname');
808                     break;
809                 default:
810                     // Send back a warning that this search key is not supported in this version.
811                     // This warning will make the function extandable without breaking clients.
812                     $warnings[] = array(
813                         'item' => $criteria['key'],
814                         'warningcode' => 'invalidfieldparameter',
815                         'message' =>
816                             'The search key \'' . $criteria['key'] . '\' is not supported, look at the web service documentation'
817                     );
818                     // Do not add this invalid criteria to the created SQL request.
819                     $invalidcriteria = true;
820                     unset($params['criteria'][$criteriaindex]);
821                     break;
822             }
824             if (!$invalidcriteria) {
825                 $cleanedvalue = clean_param($criteria['value'], $paramtype);
827                 $sql .= ' AND ';
829                 // Create the SQL.
830                 switch ($criteria['key']) {
831                     case 'id':
832                     case 'idnumber':
833                     case 'username':
834                     case 'auth':
835                         $sql .= $criteria['key'] . ' = :' . $criteria['key'];
836                         $sqlparams[$criteria['key']] = $cleanedvalue;
837                         break;
838                     case 'email':
839                     case 'lastname':
840                     case 'firstname':
841                         $sql .= $DB->sql_like($criteria['key'], ':' . $criteria['key'], false);
842                         $sqlparams[$criteria['key']] = $cleanedvalue;
843                         break;
844                     default:
845                         break;
846                 }
847             }
848         }
850         $users = $DB->get_records_select('user', $sql, $sqlparams, 'id ASC');
852         // Finally retrieve each users information.
853         $returnedusers = array();
854         foreach ($users as $user) {
855             $userdetails = user_get_user_details_courses($user);
857             // Return the user only if all the searched fields are returned.
858             // Otherwise it means that the $USER was not allowed to search the returned user.
859             if (!empty($userdetails)) {
860                 $validuser = true;
862                 foreach ($params['criteria'] as $criteria) {
863                     if (empty($userdetails[$criteria['key']])) {
864                         $validuser = false;
865                     }
866                 }
868                 if ($validuser) {
869                     $returnedusers[] = $userdetails;
870                 }
871             }
872         }
874         return array('users' => $returnedusers, 'warnings' => $warnings);
875     }
877     /**
878      * Returns description of get_users result value.
879      *
880      * @return external_description
881      * @since Moodle 2.5
882      */
883     public static function get_users_returns() {
884         return new external_single_structure(
885             array('users' => new external_multiple_structure(
886                                 self::user_description()
887                              ),
888                   'warnings' => new external_warnings('always set to \'key\'', 'faulty key name')
889             )
890         );
891     }
893     /**
894      * Returns description of method parameters
895      *
896      * @return external_function_parameters
897      * @since Moodle 2.2
898      */
899     public static function get_course_user_profiles_parameters() {
900         return new external_function_parameters(
901             array(
902                 'userlist' => new external_multiple_structure(
903                     new external_single_structure(
904                         array(
905                             'userid'    => new external_value(core_user::get_property_type('id'), 'userid'),
906                             'courseid'    => new external_value(PARAM_INT, 'courseid'),
907                         )
908                     )
909                 )
910             )
911         );
912     }
914     /**
915      * Get course participant's details
916      *
917      * @param array $userlist  array of user ids and according course ids
918      * @return array An array of arrays describing course participants
919      * @since Moodle 2.2
920      */
921     public static function get_course_user_profiles($userlist) {
922         global $CFG, $USER, $DB;
923         require_once($CFG->dirroot . "/user/lib.php");
924         $params = self::validate_parameters(self::get_course_user_profiles_parameters(), array('userlist' => $userlist));
926         $userids = array();
927         $courseids = array();
928         foreach ($params['userlist'] as $value) {
929             $userids[] = $value['userid'];
930             $courseids[$value['userid']] = $value['courseid'];
931         }
933         // Cache all courses.
934         $courses = array();
935         list($sqlcourseids, $params) = $DB->get_in_or_equal(array_unique($courseids), SQL_PARAMS_NAMED);
936         $cselect = ', ' . context_helper::get_preload_record_columns_sql('ctx');
937         $cjoin = "LEFT JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :contextlevel)";
938         $params['contextlevel'] = CONTEXT_COURSE;
939         $coursesql = "SELECT c.* $cselect
940                         FROM {course} c $cjoin
941                        WHERE c.id $sqlcourseids";
942         $rs = $DB->get_recordset_sql($coursesql, $params);
943         foreach ($rs as $course) {
944             // Adding course contexts to cache.
945             context_helper::preload_from_record($course);
946             // Cache courses.
947             $courses[$course->id] = $course;
948         }
949         $rs->close();
951         list($sqluserids, $params) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
952         $uselect = ', ' . context_helper::get_preload_record_columns_sql('ctx');
953         $ujoin = "LEFT JOIN {context} ctx ON (ctx.instanceid = u.id AND ctx.contextlevel = :contextlevel)";
954         $params['contextlevel'] = CONTEXT_USER;
955         $usersql = "SELECT u.* $uselect
956                       FROM {user} u $ujoin
957                      WHERE u.id $sqluserids";
958         $users = $DB->get_recordset_sql($usersql, $params);
959         $result = array();
960         foreach ($users as $user) {
961             if (!empty($user->deleted)) {
962                 continue;
963             }
964             context_helper::preload_from_record($user);
965             $course = $courses[$courseids[$user->id]];
966             $context = context_course::instance($courseids[$user->id], IGNORE_MISSING);
967             self::validate_context($context);
968             if ($userarray = user_get_user_details($user, $course)) {
969                 $result[] = $userarray;
970             }
971         }
973         $users->close();
975         return $result;
976     }
978     /**
979      * Returns description of method result value
980      *
981      * @return external_description
982      * @since Moodle 2.2
983      */
984     public static function get_course_user_profiles_returns() {
985         $additionalfields = array(
986             'groups' => new external_multiple_structure(
987                 new external_single_structure(
988                     array(
989                         'id'  => new external_value(PARAM_INT, 'group id'),
990                         'name' => new external_value(PARAM_RAW, 'group name'),
991                         'description' => new external_value(PARAM_RAW, 'group description'),
992                         'descriptionformat' => new external_format_value('description'),
993                     )
994                 ), 'user groups', VALUE_OPTIONAL),
995             'roles' => new external_multiple_structure(
996                 new external_single_structure(
997                     array(
998                         'roleid'       => new external_value(PARAM_INT, 'role id'),
999                         'name'         => new external_value(PARAM_RAW, 'role name'),
1000                         'shortname'    => new external_value(PARAM_ALPHANUMEXT, 'role shortname'),
1001                         'sortorder'    => new external_value(PARAM_INT, 'role sortorder')
1002                     )
1003                 ), 'user roles', VALUE_OPTIONAL),
1004             'enrolledcourses' => new external_multiple_structure(
1005                 new external_single_structure(
1006                     array(
1007                         'id'  => new external_value(PARAM_INT, 'Id of the course'),
1008                         'fullname'  => new external_value(PARAM_RAW, 'Fullname of the course'),
1009                         'shortname' => new external_value(PARAM_RAW, 'Shortname of the course')
1010                     )
1011                 ), 'Courses where the user is enrolled - limited by which courses the user is able to see', VALUE_OPTIONAL)
1012         );
1014         return new external_multiple_structure(self::user_description($additionalfields));
1015     }
1017     /**
1018      * Create user return value description.
1019      *
1020      * @param array $additionalfields some additional field
1021      * @return single_structure_description
1022      */
1023     public static function user_description($additionalfields = array()) {
1024         $userfields = array(
1025             'id'    => new external_value(core_user::get_property_type('id'), 'ID of the user'),
1026             'username'    => new external_value(core_user::get_property_type('username'), 'The username', VALUE_OPTIONAL),
1027             'firstname'   => new external_value(core_user::get_property_type('firstname'), 'The first name(s) of the user', VALUE_OPTIONAL),
1028             'lastname'    => new external_value(core_user::get_property_type('lastname'), 'The family name of the user', VALUE_OPTIONAL),
1029             'fullname'    => new external_value(core_user::get_property_type('firstname'), 'The fullname of the user'),
1030             'email'       => new external_value(core_user::get_property_type('email'), 'An email address - allow email as root@localhost', VALUE_OPTIONAL),
1031             'address'     => new external_value(core_user::get_property_type('address'), 'Postal address', VALUE_OPTIONAL),
1032             'phone1'      => new external_value(core_user::get_property_type('phone1'), 'Phone 1', VALUE_OPTIONAL),
1033             'phone2'      => new external_value(core_user::get_property_type('phone2'), 'Phone 2', VALUE_OPTIONAL),
1034             'icq'         => new external_value(core_user::get_property_type('icq'), 'icq number', VALUE_OPTIONAL),
1035             'skype'       => new external_value(core_user::get_property_type('skype'), 'skype id', VALUE_OPTIONAL),
1036             'yahoo'       => new external_value(core_user::get_property_type('yahoo'), 'yahoo id', VALUE_OPTIONAL),
1037             'aim'         => new external_value(core_user::get_property_type('aim'), 'aim id', VALUE_OPTIONAL),
1038             'msn'         => new external_value(core_user::get_property_type('msn'), 'msn number', VALUE_OPTIONAL),
1039             'department'  => new external_value(core_user::get_property_type('department'), 'department', VALUE_OPTIONAL),
1040             'institution' => new external_value(core_user::get_property_type('institution'), 'institution', VALUE_OPTIONAL),
1041             'idnumber'    => new external_value(core_user::get_property_type('idnumber'), 'An arbitrary ID code number perhaps from the institution', VALUE_OPTIONAL),
1042             'interests'   => new external_value(PARAM_TEXT, 'user interests (separated by commas)', VALUE_OPTIONAL),
1043             'firstaccess' => new external_value(core_user::get_property_type('firstaccess'), 'first access to the site (0 if never)', VALUE_OPTIONAL),
1044             'lastaccess'  => new external_value(core_user::get_property_type('lastaccess'), 'last access to the site (0 if never)', VALUE_OPTIONAL),
1045             'auth'        => new external_value(core_user::get_property_type('auth'), 'Auth plugins include manual, ldap, etc', VALUE_OPTIONAL),
1046             'suspended'   => new external_value(core_user::get_property_type('suspended'), 'Suspend user account, either false to enable user login or true to disable it', VALUE_OPTIONAL),
1047             'confirmed'   => new external_value(core_user::get_property_type('confirmed'), 'Active user: 1 if confirmed, 0 otherwise', VALUE_OPTIONAL),
1048             'lang'        => new external_value(core_user::get_property_type('lang'), 'Language code such as "en", must exist on server', VALUE_OPTIONAL),
1049             'calendartype' => new external_value(core_user::get_property_type('calendartype'), 'Calendar type such as "gregorian", must exist on server', VALUE_OPTIONAL),
1050             'theme'       => new external_value(core_user::get_property_type('theme'), 'Theme name such as "standard", must exist on server', VALUE_OPTIONAL),
1051             'timezone'    => new external_value(core_user::get_property_type('timezone'), 'Timezone code such as Australia/Perth, or 99 for default', VALUE_OPTIONAL),
1052             'mailformat'  => new external_value(core_user::get_property_type('mailformat'), 'Mail format code is 0 for plain text, 1 for HTML etc', VALUE_OPTIONAL),
1053             'description' => new external_value(core_user::get_property_type('description'), 'User profile description', VALUE_OPTIONAL),
1054             'descriptionformat' => new external_format_value(core_user::get_property_type('descriptionformat'), VALUE_OPTIONAL),
1055             'city'        => new external_value(core_user::get_property_type('city'), 'Home city of the user', VALUE_OPTIONAL),
1056             'url'         => new external_value(core_user::get_property_type('url'), 'URL of the user', VALUE_OPTIONAL),
1057             'country'     => new external_value(core_user::get_property_type('country'), 'Home country code of the user, such as AU or CZ', VALUE_OPTIONAL),
1058             'profileimageurlsmall' => new external_value(PARAM_URL, 'User image profile URL - small version'),
1059             'profileimageurl' => new external_value(PARAM_URL, 'User image profile URL - big version'),
1060             'customfields' => new external_multiple_structure(
1061                 new external_single_structure(
1062                     array(
1063                         'type'  => new external_value(PARAM_ALPHANUMEXT, 'The type of the custom field - text field, checkbox...'),
1064                         'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
1065                         'name' => new external_value(PARAM_RAW, 'The name of the custom field'),
1066                         'shortname' => new external_value(PARAM_RAW, 'The shortname of the custom field - to be able to build the field class in the code'),
1067                     )
1068                 ), 'User custom fields (also known as user profile fields)', VALUE_OPTIONAL),
1069             'preferences' => new external_multiple_structure(
1070                 new external_single_structure(
1071                     array(
1072                         'name'  => new external_value(PARAM_ALPHANUMEXT, 'The name of the preferences'),
1073                         'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
1074                     )
1075             ), 'Users preferences', VALUE_OPTIONAL)
1076         );
1077         if (!empty($additionalfields)) {
1078             $userfields = array_merge($userfields, $additionalfields);
1079         }
1080         return new external_single_structure($userfields);
1081     }
1083     /**
1084      * Returns description of method parameters
1085      *
1086      * @return external_function_parameters
1087      * @since Moodle 2.6
1088      */
1089     public static function add_user_private_files_parameters() {
1090         return new external_function_parameters(
1091             array(
1092                 'draftid' => new external_value(PARAM_INT, 'draft area id')
1093             )
1094         );
1095     }
1097     /**
1098      * Copy files from a draft area to users private files area.
1099      *
1100      * @throws invalid_parameter_exception
1101      * @param int $draftid Id of a draft area containing files.
1102      * @return array An array of warnings
1103      * @since Moodle 2.6
1104      */
1105     public static function add_user_private_files($draftid) {
1106         global $CFG, $USER;
1107         require_once($CFG->libdir . "/filelib.php");
1109         $params = self::validate_parameters(self::add_user_private_files_parameters(), array('draftid' => $draftid));
1111         if (isguestuser()) {
1112             throw new invalid_parameter_exception('Guest users cannot upload files');
1113         }
1115         $context = context_user::instance($USER->id);
1116         require_capability('moodle/user:manageownfiles', $context);
1118         $maxbytes = $CFG->userquota;
1119         $maxareabytes = $CFG->userquota;
1120         if (has_capability('moodle/user:ignoreuserquota', $context)) {
1121             $maxbytes = USER_CAN_IGNORE_FILE_SIZE_LIMITS;
1122             $maxareabytes = FILE_AREA_MAX_BYTES_UNLIMITED;
1123         }
1125         $options = array('subdirs' => 1,
1126                          'maxbytes' => $maxbytes,
1127                          'maxfiles' => -1,
1128                          'areamaxbytes' => $maxareabytes);
1130         file_merge_files_from_draft_area_into_filearea($draftid, $context->id, 'user', 'private', 0, $options);
1132         return null;
1133     }
1135     /**
1136      * Returns description of method result value
1137      *
1138      * @return external_description
1139      * @since Moodle 2.2
1140      */
1141     public static function add_user_private_files_returns() {
1142         return null;
1143     }
1145     /**
1146      * Returns description of method parameters.
1147      *
1148      * @return external_function_parameters
1149      * @since Moodle 2.6
1150      */
1151     public static function add_user_device_parameters() {
1152         return new external_function_parameters(
1153             array(
1154                 'appid'     => new external_value(PARAM_NOTAGS, 'the app id, usually something like com.moodle.moodlemobile'),
1155                 'name'      => new external_value(PARAM_NOTAGS, 'the device name, \'occam\' or \'iPhone\' etc.'),
1156                 'model'     => new external_value(PARAM_NOTAGS, 'the device model \'Nexus4\' or \'iPad1,1\' etc.'),
1157                 'platform'  => new external_value(PARAM_NOTAGS, 'the device platform \'iOS\' or \'Android\' etc.'),
1158                 'version'   => new external_value(PARAM_NOTAGS, 'the device version \'6.1.2\' or \'4.2.2\' etc.'),
1159                 'pushid'    => new external_value(PARAM_RAW, 'the device PUSH token/key/identifier/registration id'),
1160                 'uuid'      => new external_value(PARAM_RAW, 'the device UUID')
1161             )
1162         );
1163     }
1165     /**
1166      * Add a user device in Moodle database (for PUSH notifications usually).
1167      *
1168      * @throws moodle_exception
1169      * @param string $appid The app id, usually something like com.moodle.moodlemobile.
1170      * @param string $name The device name, occam or iPhone etc.
1171      * @param string $model The device model Nexus4 or iPad1.1 etc.
1172      * @param string $platform The device platform iOs or Android etc.
1173      * @param string $version The device version 6.1.2 or 4.2.2 etc.
1174      * @param string $pushid The device PUSH token/key/identifier/registration id.
1175      * @param string $uuid The device UUID.
1176      * @return array List of possible warnings.
1177      * @since Moodle 2.6
1178      */
1179     public static function add_user_device($appid, $name, $model, $platform, $version, $pushid, $uuid) {
1180         global $CFG, $USER, $DB;
1181         require_once($CFG->dirroot . "/user/lib.php");
1183         $params = self::validate_parameters(self::add_user_device_parameters(),
1184                 array('appid' => $appid,
1185                       'name' => $name,
1186                       'model' => $model,
1187                       'platform' => $platform,
1188                       'version' => $version,
1189                       'pushid' => $pushid,
1190                       'uuid' => $uuid
1191                       ));
1193         $warnings = array();
1195         // Prevent duplicate keys for users.
1196         if ($DB->get_record('user_devices', array('pushid' => $params['pushid'], 'userid' => $USER->id))) {
1197             $warnings['warning'][] = array(
1198                 'item' => $params['pushid'],
1199                 'warningcode' => 'existingkeyforthisuser',
1200                 'message' => 'This key is already stored for this user'
1201             );
1202             return $warnings;
1203         }
1205         // Notice that we can have multiple devices because previously it was allowed to have repeated ones.
1206         // Since we don't have a clear way to decide which one is the more appropiate, we update all.
1207         if ($userdevices = $DB->get_records('user_devices', array('uuid' => $params['uuid'],
1208                 'appid' => $params['appid'], 'userid' => $USER->id))) {
1210             foreach ($userdevices as $userdevice) {
1211                 $userdevice->version    = $params['version'];   // Maybe the user upgraded the device.
1212                 $userdevice->pushid     = $params['pushid'];
1213                 $userdevice->timemodified  = time();
1214                 $DB->update_record('user_devices', $userdevice);
1215             }
1217         } else {
1218             $userdevice = new stdclass;
1219             $userdevice->userid     = $USER->id;
1220             $userdevice->appid      = $params['appid'];
1221             $userdevice->name       = $params['name'];
1222             $userdevice->model      = $params['model'];
1223             $userdevice->platform   = $params['platform'];
1224             $userdevice->version    = $params['version'];
1225             $userdevice->pushid     = $params['pushid'];
1226             $userdevice->uuid       = $params['uuid'];
1227             $userdevice->timecreated  = time();
1228             $userdevice->timemodified = $userdevice->timecreated;
1230             if (!$DB->insert_record('user_devices', $userdevice)) {
1231                 throw new moodle_exception("There was a problem saving in the database the device with key: " . $params['pushid']);
1232             }
1233         }
1235         return $warnings;
1236     }
1238     /**
1239      * Returns description of method result value.
1240      *
1241      * @return external_multiple_structure
1242      * @since Moodle 2.6
1243      */
1244     public static function add_user_device_returns() {
1245         return new external_multiple_structure(
1246            new external_warnings()
1247         );
1248     }
1250     /**
1251      * Returns description of method parameters.
1252      *
1253      * @return external_function_parameters
1254      * @since Moodle 2.9
1255      */
1256     public static function remove_user_device_parameters() {
1257         return new external_function_parameters(
1258             array(
1259                 'uuid'  => new external_value(PARAM_RAW, 'the device UUID'),
1260                 'appid' => new external_value(PARAM_NOTAGS,
1261                                                 'the app id, if empty devices matching the UUID for the user will be removed',
1262                                                 VALUE_DEFAULT, ''),
1263             )
1264         );
1265     }
1267     /**
1268      * Remove a user device from the Moodle database (for PUSH notifications usually).
1269      *
1270      * @param string $uuid The device UUID.
1271      * @param string $appid The app id, opitonal parameter. If empty all the devices fmatching the UUID or the user will be removed.
1272      * @return array List of possible warnings and removal status.
1273      * @since Moodle 2.9
1274      */
1275     public static function remove_user_device($uuid, $appid = "") {
1276         global $CFG;
1277         require_once($CFG->dirroot . "/user/lib.php");
1279         $params = self::validate_parameters(self::remove_user_device_parameters(), array('uuid' => $uuid, 'appid' => $appid));
1281         $context = context_system::instance();
1282         self::validate_context($context);
1284         // Warnings array, it can be empty at the end but is mandatory.
1285         $warnings = array();
1287         $removed = user_remove_user_device($params['uuid'], $params['appid']);
1289         if (!$removed) {
1290             $warnings[] = array(
1291                 'item' => $params['uuid'],
1292                 'warningcode' => 'devicedoesnotexist',
1293                 'message' => 'The device doesn\'t exists in the database'
1294             );
1295         }
1297         $result = array(
1298             'removed' => $removed,
1299             'warnings' => $warnings
1300         );
1302         return $result;
1303     }
1305     /**
1306      * Returns description of method result value.
1307      *
1308      * @return external_multiple_structure
1309      * @since Moodle 2.9
1310      */
1311     public static function remove_user_device_returns() {
1312         return new external_single_structure(
1313             array(
1314                 'removed' => new external_value(PARAM_BOOL, 'True if removed, false if not removed because it doesn\'t exists'),
1315                 'warnings' => new external_warnings(),
1316             )
1317         );
1318     }
1320     /**
1321      * Returns description of method parameters
1322      *
1323      * @return external_function_parameters
1324      * @since Moodle 2.9
1325      */
1326     public static function view_user_list_parameters() {
1327         return new external_function_parameters(
1328             array(
1329                 'courseid' => new external_value(PARAM_INT, 'id of the course, 0 for site')
1330             )
1331         );
1332     }
1334     /**
1335      * Trigger the user_list_viewed event.
1336      *
1337      * @param int $courseid id of course
1338      * @return array of warnings and status result
1339      * @since Moodle 2.9
1340      * @throws moodle_exception
1341      */
1342     public static function view_user_list($courseid) {
1343         global $CFG;
1344         require_once($CFG->dirroot . "/user/lib.php");
1345         require_once($CFG->dirroot . '/course/lib.php');
1347         $params = self::validate_parameters(self::view_user_list_parameters(),
1348                                             array(
1349                                                 'courseid' => $courseid
1350                                             ));
1352         $warnings = array();
1354         if (empty($params['courseid'])) {
1355             $params['courseid'] = SITEID;
1356         }
1358         $course = get_course($params['courseid']);
1360         if ($course->id == SITEID) {
1361             $context = context_system::instance();
1362         } else {
1363             $context = context_course::instance($course->id);
1364         }
1365         self::validate_context($context);
1367         course_require_view_participants($context);
1369         user_list_view($course, $context);
1371         $result = array();
1372         $result['status'] = true;
1373         $result['warnings'] = $warnings;
1374         return $result;
1375     }
1377     /**
1378      * Returns description of method result value
1379      *
1380      * @return external_description
1381      * @since Moodle 2.9
1382      */
1383     public static function view_user_list_returns() {
1384         return new external_single_structure(
1385             array(
1386                 'status' => new external_value(PARAM_BOOL, 'status: true if success'),
1387                 'warnings' => new external_warnings()
1388             )
1389         );
1390     }
1392     /**
1393      * Returns description of method parameters
1394      *
1395      * @return external_function_parameters
1396      * @since Moodle 2.9
1397      */
1398     public static function view_user_profile_parameters() {
1399         return new external_function_parameters(
1400             array(
1401                 'userid' => new external_value(PARAM_INT, 'id of the user, 0 for current user', VALUE_REQUIRED),
1402                 'courseid' => new external_value(PARAM_INT, 'id of the course, default site course', VALUE_DEFAULT, 0)
1403             )
1404         );
1405     }
1407     /**
1408      * Trigger the user profile viewed event.
1409      *
1410      * @param int $userid id of user
1411      * @param int $courseid id of course
1412      * @return array of warnings and status result
1413      * @since Moodle 2.9
1414      * @throws moodle_exception
1415      */
1416     public static function view_user_profile($userid, $courseid = 0) {
1417         global $CFG, $USER;
1418         require_once($CFG->dirroot . "/user/profile/lib.php");
1420         $params = self::validate_parameters(self::view_user_profile_parameters(),
1421                                             array(
1422                                                 'userid' => $userid,
1423                                                 'courseid' => $courseid
1424                                             ));
1426         $warnings = array();
1428         if (empty($params['userid'])) {
1429             $params['userid'] = $USER->id;
1430         }
1432         if (empty($params['courseid'])) {
1433             $params['courseid'] = SITEID;
1434         }
1436         $course = get_course($params['courseid']);
1437         $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
1438         core_user::require_active_user($user);
1440         if ($course->id == SITEID) {
1441             $coursecontext = context_system::instance();;
1442         } else {
1443             $coursecontext = context_course::instance($course->id);
1444         }
1445         self::validate_context($coursecontext);
1447         $currentuser = $USER->id == $user->id;
1448         $usercontext = context_user::instance($user->id);
1450         if (!$currentuser and
1451                 !has_capability('moodle/user:viewdetails', $coursecontext) and
1452                 !has_capability('moodle/user:viewdetails', $usercontext)) {
1453             throw new moodle_exception('cannotviewprofile');
1454         }
1456         // Case like user/profile.php.
1457         if ($course->id == SITEID) {
1458             profile_view($user, $usercontext);
1459         } else {
1460             // Case like user/view.php.
1461             if (!$currentuser and !can_access_course($course, $user, '', true)) {
1462                 throw new moodle_exception('notenrolledprofile');
1463             }
1465             profile_view($user, $coursecontext, $course);
1466         }
1468         $result = array();
1469         $result['status'] = true;
1470         $result['warnings'] = $warnings;
1471         return $result;
1472     }
1474     /**
1475      * Returns description of method result value
1476      *
1477      * @return external_description
1478      * @since Moodle 2.9
1479      */
1480     public static function view_user_profile_returns() {
1481         return new external_single_structure(
1482             array(
1483                 'status' => new external_value(PARAM_BOOL, 'status: true if success'),
1484                 'warnings' => new external_warnings()
1485             )
1486         );
1487     }
1489     /**
1490      * Returns description of method parameters
1491      *
1492      * @return external_function_parameters
1493      * @since Moodle 3.2
1494      */
1495     public static function get_user_preferences_parameters() {
1496         return new external_function_parameters(
1497             array(
1498                 'name' => new external_value(PARAM_RAW, 'preference name, empty for all', VALUE_DEFAULT, ''),
1499                 'userid' => new external_value(PARAM_INT, 'id of the user, default to current user', VALUE_DEFAULT, 0)
1500             )
1501         );
1502     }
1504     /**
1505      * Return user preferences.
1506      *
1507      * @param string $name preference name, empty for all
1508      * @param int $userid id of the user, 0 for current user
1509      * @return array of warnings and preferences
1510      * @since Moodle 3.2
1511      * @throws moodle_exception
1512      */
1513     public static function get_user_preferences($name = '', $userid = 0) {
1514         global $USER;
1516         $params = self::validate_parameters(self::get_user_preferences_parameters(),
1517                                             array(
1518                                                 'name' => $name,
1519                                                 'userid' => $userid
1520                                             ));
1521         $preferences = array();
1522         $warnings = array();
1524         $context = context_system::instance();
1525         self::validate_context($context);
1527         if (empty($params['name'])) {
1528             $name = null;
1529         }
1530         if (empty($params['userid'])) {
1531             $user = null;
1532         } else {
1533             $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
1534             core_user::require_active_user($user);
1535             if ($user->id != $USER->id) {
1536                 // Only admins can retrieve other users preferences.
1537                 require_capability('moodle/site:config', $context);
1538             }
1539         }
1541         $userpreferences = get_user_preferences($name, null, $user);
1542         // Check if we received just one preference.
1543         if (!is_array($userpreferences)) {
1544             $userpreferences = array($name => $userpreferences);
1545         }
1547         foreach ($userpreferences as $name => $value) {
1548             $preferences[] = array(
1549                 'name' => $name,
1550                 'value' => $value,
1551             );
1552         }
1554         $result = array();
1555         $result['preferences'] = $preferences;
1556         $result['warnings'] = $warnings;
1557         return $result;
1558     }
1560     /**
1561      * Returns description of method result value
1562      *
1563      * @return external_description
1564      * @since Moodle 3.2
1565      */
1566     public static function get_user_preferences_returns() {
1567         return new external_single_structure(
1568             array(
1569                 'preferences' => new external_multiple_structure(
1570                     new external_single_structure(
1571                         array(
1572                             'name' => new external_value(PARAM_RAW, 'The name of the preference'),
1573                             'value' => new external_value(PARAM_RAW, 'The value of the preference'),
1574                         )
1575                     ),
1576                     'User custom fields (also known as user profile fields)'
1577                 ),
1578                 'warnings' => new external_warnings()
1579             )
1580         );
1581     }
1583     /**
1584      * Returns description of method parameters
1585      *
1586      * @return external_function_parameters
1587      * @since Moodle 3.2
1588      */
1589     public static function update_picture_parameters() {
1590         return new external_function_parameters(
1591             array(
1592                 'draftitemid' => new external_value(PARAM_INT, 'Id of the user draft file to use as image'),
1593                 'delete' => new external_value(PARAM_BOOL, 'If we should delete the user picture', VALUE_DEFAULT, false),
1594                 'userid' => new external_value(PARAM_INT, 'Id of the user, 0 for current user', VALUE_DEFAULT, 0)
1595             )
1596         );
1597     }
1599     /**
1600      * Update or delete the user picture in the site
1601      *
1602      * @param  int  $draftitemid id of the user draft file to use as image
1603      * @param  bool $delete      if we should delete the user picture
1604      * @param  int $userid       id of the user, 0 for current user
1605      * @return array warnings and success status
1606      * @since Moodle 3.2
1607      * @throws moodle_exception
1608      */
1609     public static function update_picture($draftitemid, $delete = false, $userid = 0) {
1610         global $CFG, $USER, $PAGE;
1612         $params = self::validate_parameters(
1613             self::update_picture_parameters(),
1614             array(
1615                 'draftitemid' => $draftitemid,
1616                 'delete' => $delete,
1617                 'userid' => $userid
1618             )
1619         );
1621         $context = context_system::instance();
1622         self::validate_context($context);
1624         if (!empty($CFG->disableuserimages)) {
1625             throw new moodle_exception('userimagesdisabled', 'admin');
1626         }
1628         if (empty($params['userid']) or $params['userid'] == $USER->id) {
1629             $user = $USER;
1630             require_capability('moodle/user:editownprofile', $context);
1631         } else {
1632             $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
1633             core_user::require_active_user($user);
1634             $personalcontext = context_user::instance($user->id);
1636             require_capability('moodle/user:editprofile', $personalcontext);
1637             if (is_siteadmin($user) and !is_siteadmin($USER)) {  // Only admins may edit other admins.
1638                 throw new moodle_exception('useradmineditadmin');
1639             }
1640         }
1642         // Load the appropriate auth plugin.
1643         $userauth = get_auth_plugin($user->auth);
1644         if (is_mnet_remote_user($user) or !$userauth->can_edit_profile() or $userauth->edit_profile_url()) {
1645             throw new moodle_exception('noprofileedit', 'auth');
1646         }
1648         $filemanageroptions = array('maxbytes' => $CFG->maxbytes, 'subdirs' => 0, 'maxfiles' => 1, 'accepted_types' => 'web_image');
1649         $user->deletepicture = $params['delete'];
1650         $user->imagefile = $params['draftitemid'];
1651         $success = core_user::update_picture($user, $filemanageroptions);
1653         $result = array(
1654             'success' => $success,
1655             'warnings' => array(),
1656         );
1657         if ($success) {
1658             $userpicture = new user_picture(core_user::get_user($user->id));
1659             $userpicture->size = 1; // Size f1.
1660             $result['profileimageurl'] = $userpicture->get_url($PAGE)->out(false);
1661         }
1662         return $result;
1663     }
1665     /**
1666      * Returns description of method result value
1667      *
1668      * @return external_description
1669      * @since Moodle 3.2
1670      */
1671     public static function update_picture_returns() {
1672         return new external_single_structure(
1673             array(
1674                 'success' => new external_value(PARAM_BOOL, 'True if the image was updated, false otherwise.'),
1675                 'profileimageurl' => new external_value(PARAM_URL, 'New profile user image url', VALUE_OPTIONAL),
1676                 'warnings' => new external_warnings()
1677             )
1678         );
1679     }
1681     /**
1682      * Returns description of method parameters
1683      *
1684      * @return external_function_parameters
1685      * @since Moodle 3.2
1686      */
1687     public static function set_user_preferences_parameters() {
1688         return new external_function_parameters(
1689             array(
1690                 'preferences' => new external_multiple_structure(
1691                     new external_single_structure(
1692                         array(
1693                             'name' => new external_value(PARAM_RAW, 'The name of the preference'),
1694                             'value' => new external_value(PARAM_RAW, 'The value of the preference'),
1695                             'userid' => new external_value(PARAM_INT, 'Id of the user to set the preference'),
1696                         )
1697                     )
1698                 )
1699             )
1700         );
1701     }
1703     /**
1704      * Set user preferences.
1705      *
1706      * @param array $preferences list of preferences including name, value and userid
1707      * @return array of warnings and preferences saved
1708      * @since Moodle 3.2
1709      * @throws moodle_exception
1710      */
1711     public static function set_user_preferences($preferences) {
1712         global $USER;
1714         $params = self::validate_parameters(self::set_user_preferences_parameters(), array('preferences' => $preferences));
1715         $warnings = array();
1716         $saved = array();
1718         $context = context_system::instance();
1719         self::validate_context($context);
1721         $userscache = array();
1722         foreach ($params['preferences'] as $pref) {
1723             // Check to which user set the preference.
1724             if (!empty($userscache[$pref['userid']])) {
1725                 $user = $userscache[$pref['userid']];
1726             } else {
1727                 try {
1728                     $user = core_user::get_user($pref['userid'], '*', MUST_EXIST);
1729                     core_user::require_active_user($user);
1730                     $userscache[$pref['userid']] = $user;
1731                 } catch (Exception $e) {
1732                     $warnings[] = array(
1733                         'item' => 'user',
1734                         'itemid' => $pref['userid'],
1735                         'warningcode' => 'invaliduser',
1736                         'message' => $e->getMessage()
1737                     );
1738                     continue;
1739                 }
1740             }
1742             try {
1743                 if (core_user::can_edit_preference($pref['name'], $user)) {
1744                     $value = core_user::clean_preference($pref['value'], $pref['name']);
1745                     set_user_preference($pref['name'], $value, $user->id);
1746                     $saved[] = array(
1747                         'name' => $pref['name'],
1748                         'userid' => $user->id,
1749                     );
1750                 } else {
1751                     $warnings[] = array(
1752                         'item' => 'user',
1753                         'itemid' => $user->id,
1754                         'warningcode' => 'nopermission',
1755                         'message' => 'You are not allowed to change the preference '.s($pref['name']).' for user '.$user->id
1756                     );
1757                 }
1758             } catch (Exception $e) {
1759                 $warnings[] = array(
1760                     'item' => 'user',
1761                     'itemid' => $user->id,
1762                     'warningcode' => 'errorsavingpreference',
1763                     'message' => $e->getMessage()
1764                 );
1765             }
1766         }
1768         $result = array();
1769         $result['saved'] = $saved;
1770         $result['warnings'] = $warnings;
1771         return $result;
1772     }
1774     /**
1775      * Returns description of method result value
1776      *
1777      * @return external_description
1778      * @since Moodle 3.2
1779      */
1780     public static function set_user_preferences_returns() {
1781         return new external_single_structure(
1782             array(
1783                 'saved' => new external_multiple_structure(
1784                     new external_single_structure(
1785                         array(
1786                             'name' => new external_value(PARAM_RAW, 'The name of the preference'),
1787                             'userid' => new external_value(PARAM_INT, 'The user the preference was set for'),
1788                         )
1789                     ), 'Preferences saved'
1790                 ),
1791                 'warnings' => new external_warnings()
1792             )
1793         );
1794     }
1796     /**
1797      * Returns description of method parameters.
1798      *
1799      * @return external_function_parameters
1800      * @since Moodle 3.2
1801      */
1802     public static function agree_site_policy_parameters() {
1803         return new external_function_parameters(array());
1804     }
1806     /**
1807      * Agree the site policy for the current user.
1808      *
1809      * @return array of warnings and status result
1810      * @since Moodle 3.2
1811      * @throws moodle_exception
1812      */
1813     public static function agree_site_policy() {
1814         global $CFG, $DB, $USER;
1816         $warnings = array();
1818         $context = context_system::instance();
1819         try {
1820             // We expect an exception here since the user didn't agree the site policy yet.
1821             self::validate_context($context);
1822         } catch (Exception $e) {
1823             // We are expecting only a sitepolicynotagreed exception.
1824             if (!($e instanceof moodle_exception) or $e->errorcode != 'sitepolicynotagreed') {
1825                 // In case we receive a different exception, throw it.
1826                 throw $e;
1827             }
1828         }
1830         if (empty($CFG->sitepolicy)) {
1831             $status = false;
1832             $warnings[] = array(
1833                 'item' => 'user',
1834                 'itemid' => $USER->id,
1835                 'warningcode' => 'nositepolicy',
1836                 'message' => 'The site does not have a site policy configured.'
1837             );
1838         } else if (!empty($USER->policyagreed)) {
1839             $status = false;
1840             $warnings[] = array(
1841                 'item' => 'user',
1842                 'itemid' => $USER->id,
1843                 'warningcode' => 'alreadyagreed',
1844                 'message' => 'The user already agreed the site policy.'
1845             );
1846         } else {
1847             $DB->set_field('user', 'policyagreed', 1, array('id' => $USER->id));
1848             $USER->policyagreed = 1;
1849             $status = true;
1850         }
1852         $result = array();
1853         $result['status'] = $status;
1854         $result['warnings'] = $warnings;
1855         return $result;
1856     }
1858     /**
1859      * Returns description of method result value.
1860      *
1861      * @return external_description
1862      * @since Moodle 3.2
1863      */
1864     public static function agree_site_policy_returns() {
1865         return new external_single_structure(
1866             array(
1867                 'status' => new external_value(PARAM_BOOL, 'Status: true only if we set the policyagreed to 1 for the user'),
1868                 'warnings' => new external_warnings()
1869             )
1870         );
1871     }
1873     /**
1874      * Returns description of method parameters.
1875      *
1876      * @return external_function_parameters
1877      * @since Moodle 3.4
1878      */
1879     public static function get_private_files_info_parameters() {
1880         return new external_function_parameters(
1881             array(
1882                 'userid' => new external_value(PARAM_INT, 'Id of the user, default to current user.', VALUE_DEFAULT, 0)
1883             )
1884         );
1885     }
1887     /**
1888      * Returns general information about files in the user private files area.
1889      *
1890      * @param int $userid Id of the user, default to current user.
1891      * @return array of warnings and file area information
1892      * @since Moodle 3.4
1893      * @throws moodle_exception
1894      */
1895     public static function get_private_files_info($userid = 0) {
1896         global $CFG, $USER;
1897         require_once($CFG->libdir . '/filelib.php');
1899         $params = self::validate_parameters(self::get_private_files_info_parameters(), array('userid' => $userid));
1900         $warnings = array();
1902         $context = context_system::instance();
1903         self::validate_context($context);
1905         if (empty($params['userid']) || $params['userid'] == $USER->id) {
1906             $usercontext = context_user::instance($USER->id);
1907             require_capability('moodle/user:manageownfiles', $usercontext);
1908         } else {
1909             $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
1910             core_user::require_active_user($user);
1911             // Only admins can retrieve other users information.
1912             require_capability('moodle/site:config', $context);
1913             $usercontext = context_user::instance($user->id);
1914         }
1916         $fileareainfo = file_get_file_area_info($usercontext->id, 'user', 'private');
1918         $result = array();
1919         $result['filecount'] = $fileareainfo['filecount'];
1920         $result['foldercount'] = $fileareainfo['foldercount'];
1921         $result['filesize'] = $fileareainfo['filesize'];
1922         $result['filesizewithoutreferences'] = $fileareainfo['filesize_without_references'];
1923         $result['warnings'] = $warnings;
1924         return $result;
1925     }
1927     /**
1928      * Returns description of method result value.
1929      *
1930      * @return external_description
1931      * @since Moodle 3.4
1932      */
1933     public static function get_private_files_info_returns() {
1934         return new external_single_structure(
1935             array(
1936                 'filecount' => new external_value(PARAM_INT, 'Number of files in the area.'),
1937                 'foldercount' => new external_value(PARAM_INT, 'Number of folders in the area.'),
1938                 'filesize' => new external_value(PARAM_INT, 'Total size of the files in the area.'),
1939                 'filesizewithoutreferences' => new external_value(PARAM_INT, 'Total size of the area excluding file references'),
1940                 'warnings' => new external_warnings()
1941             )
1942         );
1943     }