Merge branch 'MDL-49823-master' of git://github.com/jleyva/moodle
[moodle.git] / mod / choice / classes / external.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  * Choice module external API
19  *
20  * @package    mod_choice
21  * @category   external
22  * @copyright  2015 Costantino Cito <ccito@cvaconsulting.com>
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  * @since      Moodle 3.0
25  */
27 defined('MOODLE_INTERNAL') || die;
28 require_once($CFG->libdir . '/externallib.php');
29 require_once($CFG->dirroot . '/mod/choice/lib.php');
31 /**
32  * Choice module external functions
33  *
34  * @package    mod_choice
35  * @category   external
36  * @copyright  2015 Costantino Cito <ccito@cvaconsulting.com>
37  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
38  * @since      Moodle 3.0
39  */
40 class mod_choice_external extends external_api {
42     /**
43      * Describes the parameters for get_choices_by_courses.
44      *
45      * @return external_external_function_parameters
46      * @since Moodle 3.0
47      */
48     public static function get_choice_results_parameters() {
49         return new external_function_parameters (array('choiceid' => new external_value(PARAM_INT, 'choice instance id')));
50     }
51     /**
52      * Returns user's results for a specific choice
53      * and a list of those users that did not answered yet.
54      *
55      * @param int $choiceid the choice instance id
56      * @return array of responses details
57      * @since Moodle 3.0
58      */
59     public static function get_choice_results($choiceid) {
60         global $USER, $PAGE;
62         $params = self::validate_parameters(self::get_choice_results_parameters(), array('choiceid' => $choiceid));
64         if (!$choice = choice_get_choice($params['choiceid'])) {
65             throw new moodle_exception("invalidcoursemodule", "error");
66         }
67         list($course, $cm) = get_course_and_cm_from_instance($choice, 'choice');
69         $context = context_module::instance($cm->id);
70         self::validate_context($context);
72         $groupmode = groups_get_activity_groupmode($cm);
73         // Check if we have to include responses from inactive users.
74         $onlyactive = $choice->includeinactive ? false : true;
75         $users = choice_get_response_data($choice, $cm, $groupmode, $onlyactive);
76         // Show those who haven't answered the question.
77         if (!empty($choice->showunanswered)) {
78             $choice->option[0] = get_string('notanswered', 'choice');
79             $choice->maxanswers[0] = 0;
80         }
81         $results = prepare_choice_show_results($choice, $course, $cm, $users);
83         $options = array();
84         $fullnamecap = has_capability('moodle/site:viewfullnames', $context);
85         foreach ($results->options as $optionid => $option) {
87             $userresponses = array();
88             $numberofuser = 0;
89             $percentageamount = 0;
90             if (property_exists($option, 'user') and
91                 (has_capability('mod/choice:readresponses', $context) or choice_can_view_results($choice))) {
92                 $numberofuser = count($option->user);
93                 $percentageamount = ((float)$numberofuser / (float)$results->numberofuser) * 100.0;
94                 if ($choice->publish) {
95                     foreach ($option->user as $userresponse) {
96                         $response = array();
97                         $response['userid'] = $userresponse->id;
98                         $response['fullname'] = fullname($userresponse, $fullnamecap);
100                         $userpicture = new user_picture($userresponse);
101                         $userpicture->size = 1; // Size f1.
102                         $response['profileimageurl'] = $userpicture->get_url($PAGE)->out(false);
104                         // Add optional properties.
105                         foreach (array('answerid', 'timemodified') as $field) {
106                             if (property_exists($userresponse, 'answerid')) {
107                                 $response[$field] = $userresponse->$field;
108                             }
109                         }
110                         $userresponses[] = $response;
111                     }
112                 }
113             }
115             $options[] = array('id'               => $optionid,
116                                'text'             => external_format_string($option->text, $context->id),
117                                'maxanswer'        => $option->maxanswer,
118                                'userresponses'    => $userresponses,
119                                'numberofuser'     => $numberofuser,
120                                'percentageamount' => $percentageamount
121                               );
122         }
124         $warnings = array();
125         return array(
126             'options' => $options,
127             'warnings' => $warnings
128         );
129     }
131     /**
132      * Describes the get_choice_results return value.
133      *
134      * @return external_single_structure
135      * @since Moodle 3.0
136      */
137     public static function get_choice_results_returns() {
138         return new external_single_structure(
139             array(
140                 'options' => new external_multiple_structure(
141                     new external_single_structure(
142                         array(
143                             'id' => new external_value(PARAM_INT, 'choice instance id'),
144                             'text' => new external_value(PARAM_RAW, 'text of the choice'),
145                             'maxanswer' => new external_value(PARAM_INT, 'maximum number of answers'),
146                             'userresponses' => new external_multiple_structure(
147                                  new external_single_structure(
148                                      array(
149                                         'userid' => new external_value(PARAM_INT, 'user id'),
150                                         'fullname' => new external_value(PARAM_NOTAGS, 'user full name'),
151                                         'profileimageurl' => new external_value(PARAM_URL, 'profile user image url'),
152                                         'answerid' => new external_value(PARAM_INT, 'answer id', VALUE_OPTIONAL),
153                                         'timemodified' => new external_value(PARAM_INT, 'time of modification', VALUE_OPTIONAL),
154                                      ), 'User responses'
155                                  )
156                             ),
157                             'numberofuser' => new external_value(PARAM_INT, 'number of users answers'),
158                             'percentageamount' => new external_value(PARAM_FLOAT, 'percentage of users answers')
159                         ), 'Options'
160                     )
161                 ),
162                 'warnings' => new external_warnings(),
163             )
164         );
165     }
167     /**
168      * Describes the parameters for mod_choice_get_choice_options.
169      *
170      * @return external_external_function_parameters
171      * @since Moodle 3.0
172      */
173     public static function get_choice_options_parameters() {
174         return new external_function_parameters (array('choiceid' => new external_value(PARAM_INT, 'choice instance id')));
175     }
177     /**
178      * Returns options for a specific choice
179      *
180      * @param int $choiceid the choice instance id
181      * @return array of options details
182      * @since Moodle 3.0
183      */
184     public static function get_choice_options($choiceid) {
185         global $USER;
186         $warnings = array();
187         $params = self::validate_parameters(self::get_choice_options_parameters(), array('choiceid' => $choiceid));
189         if (!$choice = choice_get_choice($params['choiceid'])) {
190             throw new moodle_exception("invalidcoursemodule", "error");
191         }
192         list($course, $cm) = get_course_and_cm_from_instance($choice, 'choice');
194         $context = context_module::instance($cm->id);
195         self::validate_context($context);
197         require_capability('mod/choice:choose', $context);
199         $groupmode = groups_get_activity_groupmode($cm);
200         $onlyactive = $choice->includeinactive ? false : true;
201         $allresponses = choice_get_response_data($choice, $cm, $groupmode, $onlyactive);
203         $timenow = time();
204         $choiceopen = true;
205         $showpreview = false;
207         if ($choice->timeclose != 0) {
208             if ($choice->timeopen > $timenow) {
209                 $choiceopen = false;
210                 $warnings[1] = get_string("notopenyet", "choice", userdate($choice->timeopen));
211                 if ($choice->showpreview) {
212                     $warnings[2] = get_string('previewonly', 'choice', userdate($choice->timeopen));
213                     $showpreview = true;
214                 }
215             }
216             if ($timenow > $choice->timeclose) {
217                 $choiceopen = false;
218                 $warnings[3] = get_string("expired", "choice", userdate($choice->timeclose));
219             }
220         }
221         $optionsarray = array();
223         if ($choiceopen or $showpreview) {
225             $options = choice_prepare_options($choice, $USER, $cm, $allresponses);
227             foreach ($options['options'] as $option) {
228                 $optionarr = array();
229                 $optionarr['id']            = $option->attributes->value;
230                 $optionarr['text']          = external_format_string($option->text, $context->id);
231                 $optionarr['maxanswers']    = $option->maxanswers;
232                 $optionarr['displaylayout'] = $option->displaylayout;
233                 $optionarr['countanswers']  = $option->countanswers;
234                 foreach (array('checked', 'disabled') as $field) {
235                     if (property_exists($option->attributes, $field) and $option->attributes->$field == 1) {
236                         $optionarr[$field] = 1;
237                     } else {
238                         $optionarr[$field] = 0;
239                     }
240                 }
241                 // When showpreview is active, we show options as disabled.
242                 if ($showpreview or ($optionarr['checked'] == 1 and !$choice->allowupdate)) {
243                     $optionarr['disabled'] = 1;
244                 }
245                 $optionsarray[] = $optionarr;
246             }
247         }
248         foreach ($warnings as $key => $message) {
249             $warnings[$key] = array(
250                 'item' => 'choice',
251                 'itemid' => $cm->id,
252                 'warningcode' => $key,
253                 'message' => $message
254             );
255         }
256         return array(
257             'options' => $optionsarray,
258             'warnings' => $warnings
259         );
260     }
262     /**
263      * Describes the get_choice_results return value.
264      *
265      * @return external_multiple_structure
266      * @since Moodle 3.0
267      */
268     public static function get_choice_options_returns() {
269         return new external_single_structure(
270             array(
271                 'options' => new external_multiple_structure(
272                     new external_single_structure(
273                         array(
274                             'id' => new external_value(PARAM_INT, 'option id'),
275                             'text' => new external_value(PARAM_RAW, 'text of the choice'),
276                             'maxanswers' => new external_value(PARAM_INT, 'maximum number of answers'),
277                             'displaylayout' => new external_value(PARAM_BOOL, 'true for orizontal, otherwise vertical'),
278                             'countanswers' => new external_value(PARAM_INT, 'number of answers'),
279                             'checked' => new external_value(PARAM_BOOL, 'we already answered'),
280                             'disabled' => new external_value(PARAM_BOOL, 'option disabled'),
281                             )
282                     ), 'Options'
283                 ),
284                 'warnings' => new external_warnings(),
285             )
286         );
287     }
289     /**
290      * Describes the parameters for submit_choice_response.
291      *
292      * @return external_external_function_parameters
293      * @since Moodle 3.0
294      */
295     public static function submit_choice_response_parameters() {
296         return new external_function_parameters (
297             array(
298                 'choiceid' => new external_value(PARAM_INT, 'choice instance id'),
299                 'responses' => new external_multiple_structure(
300                     new external_value(PARAM_INT, 'answer id'),
301                     'Array of response ids'
302                 ),
303             )
304         );
305     }
307     /**
308      * Submit choice responses
309      *
310      * @param int $choiceid the choice instance id
311      * @param array $responses the response ids
312      * @return array ansers informatinon and warnings
313      * @since Moodle 3.0
314      */
315     public static function submit_choice_response($choiceid, $responses) {
316         global $USER;
318         $warnings = array();
319         $params = self::validate_parameters(self::submit_choice_response_parameters(),
320                                             array(
321                                                 'choiceid' => $choiceid,
322                                                 'responses' => $responses
323                                             ));
325         if (!$choice = choice_get_choice($params['choiceid'])) {
326             throw new moodle_exception("invalidcoursemodule", "error");
327         }
328         list($course, $cm) = get_course_and_cm_from_instance($choice, 'choice');
330         $context = context_module::instance($cm->id);
331         self::validate_context($context);
333         require_capability('mod/choice:choose', $context);
335         $timenow = time();
336         if ($choice->timeclose != 0) {
337             if ($choice->timeopen > $timenow) {
338                 throw new moodle_exception("notopenyet", "choice", '', userdate($choice->timeopen));
339             } else if ($timenow > $choice->timeclose) {
340                 throw new moodle_exception("expired", "choice", '', userdate($choice->timeclose));
341             }
342         }
343         if (!choice_get_my_response($choice) or $choice->allowupdate) {
344             // When a single response is given, we convert the array to a simple variable
345             // in order to avoid choice_user_submit_response to check with allowmultiple even
346             // for a single response.
347             if (count($params['responses']) == 1) {
348                 $params['responses'] = reset($params['responses']);
349             }
350             choice_user_submit_response($params['responses'], $choice, $USER->id, $course, $cm);
351         } else {
352             throw new moodle_exception('missingrequiredcapability', 'webservice', '', 'allowupdate');
353         }
354         $answers = choice_get_my_response($choice);
356         return array(
357             'answers' => $answers,
358             'warnings' => $warnings
359         );
360     }
362     /**
363      * Describes the submit_choice_response return value.
364      *
365      * @return external_multiple_structure
366      * @since Moodle 3.0
367      */
368     public static function submit_choice_response_returns() {
369         return new external_single_structure(
370             array(
371                 'answers' => new external_multiple_structure(
372                      new external_single_structure(
373                          array(
374                              'id'           => new external_value(PARAM_INT, 'answer id'),
375                              'choiceid'     => new external_value(PARAM_INT, 'choiceid'),
376                              'userid'       => new external_value(PARAM_INT, 'user id'),
377                              'optionid'     => new external_value(PARAM_INT, 'optionid'),
378                              'timemodified' => new external_value(PARAM_INT, 'time of last modification')
379                          ), 'Answers'
380                      )
381                 ),
382                 'warnings' => new external_warnings(),
383             )
384         );
385     }
387     /**
388      * Returns description of method parameters
389      *
390      * @return external_function_parameters
391      * @since Moodle 3.0
392      */
393     public static function view_choice_parameters() {
394         return new external_function_parameters(
395             array(
396                 'choiceid' => new external_value(PARAM_INT, 'choice instance id')
397             )
398         );
399     }
401     /**
402      * Trigger the course module viewed event and update the module completion status.
403      *
404      * @param int $choiceid the choice instance id
405      * @return array of warnings and status result
406      * @since Moodle 3.0
407      * @throws moodle_exception
408      */
409     public static function view_choice($choiceid) {
410         global $CFG;
412         $params = self::validate_parameters(self::view_choice_parameters(),
413                                             array(
414                                                 'choiceid' => $choiceid
415                                             ));
416         $warnings = array();
418         // Request and permission validation.
419         if (!$choice = choice_get_choice($params['choiceid'])) {
420             throw new moodle_exception("invalidcoursemodule", "error");
421         }
422         list($course, $cm) = get_course_and_cm_from_instance($choice, 'choice');
424         $context = context_module::instance($cm->id);
425         self::validate_context($context);
427         // Trigger course_module_viewed event and completion.
428         choice_view($choice, $course, $cm, $context);
430         $result = array();
431         $result['status'] = true;
432         $result['warnings'] = $warnings;
433         return $result;
434     }
436     /**
437      * Returns description of method result value
438      *
439      * @return external_description
440      * @since Moodle 3.0
441      */
442     public static function view_choice_returns() {
443         return new external_single_structure(
444             array(
445                 'status' => new external_value(PARAM_BOOL, 'status: true if success'),
446                 'warnings' => new external_warnings()
447             )
448         );
449     }
451     /**
452      * Describes the parameters for get_choices_by_courses.
453      *
454      * @return external_external_function_parameters
455      * @since Moodle 3.0
456      */
457     public static function get_choices_by_courses_parameters() {
458         return new external_function_parameters (
459             array(
460                 'courseids' => new external_multiple_structure(
461                     new external_value(PARAM_INT, 'course id'), 'Array of course ids', VALUE_DEFAULT, array()
462                 ),
463             )
464         );
465     }
467     /**
468      * Returns a list of choices in a provided list of courses,
469      * if no list is provided all choices that the user can view will be returned.
470      *
471      * @param array $courseids the course ids
472      * @return array of choices details
473      * @since Moodle 3.0
474      */
475     public static function get_choices_by_courses($courseids = array()) {
476         global $CFG;
478         $returnedchoices = array();
479         $warnings = array();
481         $params = self::validate_parameters(self::get_choices_by_courses_parameters(), array('courseids' => $courseids));
483         if (empty($params['courseids'])) {
484             $params['courseids'] = array_keys(enrol_get_my_courses());
485         }
487         // Ensure there are courseids to loop through.
488         if (!empty($params['courseids'])) {
490             list($courses, $warnings) = external_util::validate_courses($params['courseids']);
492             // Get the choices in this course, this function checks users visibility permissions.
493             // We can avoid then additional validate_context calls.
494             $choices = get_all_instances_in_courses("choice", $courses);
495             foreach ($choices as $choice) {
496                 $context = context_module::instance($choice->coursemodule);
497                 // Entry to return.
498                 $choicedetails = array();
499                 // First, we return information that any user can see in the web interface.
500                 $choicedetails['id'] = $choice->id;
501                 $choicedetails['coursemodule'] = $choice->coursemodule;
502                 $choicedetails['course'] = $choice->course;
503                 $choicedetails['name']  = external_format_string($choice->name, $context->id);
504                 // Format intro.
505                 list($choicedetails['intro'], $choicedetails['introformat']) =
506                     external_format_text($choice->intro, $choice->introformat,
507                                             $context->id, 'mod_choice', 'intro', null);
509                 if (has_capability('mod/choice:choose', $context)) {
510                     $choicedetails['publish']  = $choice->publish;
511                     $choicedetails['showresults']  = $choice->showresults;
512                     $choicedetails['showpreview']  = $choice->showpreview;
513                     $choicedetails['timeopen']  = $choice->timeopen;
514                     $choicedetails['timeclose']  = $choice->timeclose;
515                     $choicedetails['display']  = $choice->display;
516                     $choicedetails['allowupdate']  = $choice->allowupdate;
517                     $choicedetails['allowmultiple']  = $choice->allowmultiple;
518                     $choicedetails['limitanswers']  = $choice->limitanswers;
519                     $choicedetails['showunanswered']  = $choice->showunanswered;
520                     $choicedetails['includeinactive']  = $choice->includeinactive;
521                 }
523                 if (has_capability('moodle/course:manageactivities', $context)) {
524                     $choicedetails['timemodified']  = $choice->timemodified;
525                     $choicedetails['completionsubmit']  = $choice->completionsubmit;
526                     $choicedetails['section']  = $choice->section;
527                     $choicedetails['visible']  = $choice->visible;
528                     $choicedetails['groupmode']  = $choice->groupmode;
529                     $choicedetails['groupingid']  = $choice->groupingid;
530                 }
531                 $returnedchoices[] = $choicedetails;
532             }
533         }
534         $result = array();
535         $result['choices'] = $returnedchoices;
536         $result['warnings'] = $warnings;
537         return $result;
538     }
540     /**
541      * Describes the mod_choice_get_choices_by_courses return value.
542      *
543      * @return external_single_structure
544      * @since Moodle 3.0
545      */
546     public static function get_choices_by_courses_returns() {
547         return new external_single_structure(
548             array(
549                 'choices' => new external_multiple_structure(
550                     new external_single_structure(
551                         array(
552                             'id' => new external_value(PARAM_INT, 'Choice instance id'),
553                             'coursemodule' => new external_value(PARAM_INT, 'Course module id'),
554                             'course' => new external_value(PARAM_INT, 'Course id'),
555                             'name' => new external_value(PARAM_RAW, 'Choice name'),
556                             'intro' => new external_value(PARAM_RAW, 'The choice intro'),
557                             'introformat' => new external_format_value('intro'),
558                             'publish' => new external_value(PARAM_BOOL, 'If choice is published', VALUE_OPTIONAL),
559                             'showresults' => new external_value(PARAM_INT, '0 never, 1 after answer, 2 after close, 3 always',
560                                                                 VALUE_OPTIONAL),
561                             'display' => new external_value(PARAM_INT, 'Display mode (vertical, horizontal)', VALUE_OPTIONAL),
562                             'allowupdate' => new external_value(PARAM_BOOL, 'Allow update', VALUE_OPTIONAL),
563                             'allowmultiple' => new external_value(PARAM_BOOL, 'Allow multiple choices', VALUE_OPTIONAL),
564                             'showunanswered' => new external_value(PARAM_BOOL, 'Show users who not answered yet', VALUE_OPTIONAL),
565                             'includeinactive' => new external_value(PARAM_BOOL, 'Include inactive users', VALUE_OPTIONAL),
566                             'limitanswers' => new external_value(PARAM_BOOL, 'Limit unswers', VALUE_OPTIONAL),
567                             'timeopen' => new external_value(PARAM_INT, 'Date of opening validity', VALUE_OPTIONAL),
568                             'timeclose' => new external_value(PARAM_INT, 'Date of closing validity', VALUE_OPTIONAL),
569                             'showpreview' => new external_value(PARAM_BOOL, 'Show preview before timeopen', VALUE_OPTIONAL),
570                             'timemodified' => new external_value(PARAM_INT, 'Time of last modification', VALUE_OPTIONAL),
571                             'completionsubmit' => new external_value(PARAM_BOOL, 'Completion on user submission', VALUE_OPTIONAL),
572                             'section' => new external_value(PARAM_INT, 'Course section id', VALUE_OPTIONAL),
573                             'visible' => new external_value(PARAM_BOOL, 'Visible', VALUE_OPTIONAL),
574                             'groupmode' => new external_value(PARAM_INT, 'Group mode', VALUE_OPTIONAL),
575                             'groupingid' => new external_value(PARAM_INT, 'Group id', VALUE_OPTIONAL),
576                         ), 'Choices'
577                     )
578                 ),
579                 'warnings' => new external_warnings(),
580             )
581         );
582     }