diff --git a/mod/feedback/classes/external.php b/mod/feedback/classes/external.php index ad1b55d05d8..19d686a5c6a 100644 --- a/mod/feedback/classes/external.php +++ b/mod/feedback/classes/external.php @@ -592,4 +592,116 @@ class mod_feedback_external extends external_api { ) ); } + + /** + * Describes the parameters for process_page. + * + * @return external_function_parameters + * @since Moodle 3.3 + */ + public static function process_page_parameters() { + return new external_function_parameters ( + array( + 'feedbackid' => new external_value(PARAM_INT, 'Feedback instance id.'), + 'page' => new external_value(PARAM_INT, 'The page being processed.'), + 'responses' => new external_multiple_structure( + new external_single_structure( + array( + 'name' => new external_value(PARAM_NOTAGS, 'The response name (usually type[index]_id).'), + 'value' => new external_value(PARAM_RAW, 'The response value.'), + ) + ), 'The data to be processed.' + ), + 'goprevious' => new external_value(PARAM_BOOL, 'Whether we want to jump to previous page.', VALUE_DEFAULT, false), + ) + ); + } + + /** + * Process a jump between pages. + * + * @param array $feedbackid feedback instance id + * @param array $page the page being processed + * @param array $responses the responses to be processed + * @param bool $goprevious whether we want to jump to previous page + * @return array of warnings and launch information + * @since Moodle 3.3 + */ + public static function process_page($feedbackid, $page, $responses, $goprevious = false) { + global $USER, $SESSION; + + $params = array('feedbackid' => $feedbackid, 'page' => $page, 'responses' => $responses, 'goprevious' => $goprevious); + $params = self::validate_parameters(self::process_page_parameters(), $params); + $warnings = array(); + $siteaftersubmit = $completionpagecontents = ''; + + list($feedback, $course, $cm, $context) = self::validate_feedback($params['feedbackid']); + // Check we can do a new submission (or continue an existing). + $feedbackcompletion = self::validate_feedback_access($feedback, $course, $cm, $context, true); + + // Create the $_POST object required by the feedback question engine. + $_POST = array(); + foreach ($responses as $response) { + $_POST[$response['name']] = $response['value']; + } + // Force fields. + $_POST['id'] = $cm->id; + $_POST['courseid'] = $course->id; + $_POST['gopage'] = $params['page']; + $_POST['_qf__mod_feedback_complete_form'] = 1; + if (!$params['goprevious']) { + if ($feedbackcompletion->get_next_page($params['page'], false) === null) { + $_POST['savevalues'] = 1; // If there is no next page, it means we are finishing the feedback. + } else { + $_POST['gonextpage'] = 1; // If we are not going to previous page or finishing we are going forward. + } + } + + // Ignore sesskey (deep in some APIs), the request is already validated. + $USER->ignoresesskey = true; + feedback_init_feedback_session(); + $SESSION->feedback->is_started = true; + + $feedbackcompletion->process_page($params['page'], $params['goprevious']); + $completed = $feedbackcompletion->just_completed(); + if ($completed) { + $jumpto = 0; + if ($feedback->page_after_submit) { + $completionpagecontents = $feedbackcompletion->page_after_submit(); + } + + if ($feedback->site_after_submit) { + $siteaftersubmit = feedback_encode_target_url($feedback->site_after_submit); + } + } else { + $jumpto = $feedbackcompletion->get_jumpto(); + } + + $result = array( + 'jumpto' => $jumpto, + 'completed' => $completed, + 'completionpagecontents' => $completionpagecontents, + 'siteaftersubmit' => $siteaftersubmit, + 'warnings' => $warnings + ); + return $result; + } + + /** + * Describes the process_page return value. + * + * @return external_single_structure + * @since Moodle 3.3 + */ + public static function process_page_returns() { + return new external_single_structure( + array( + 'jumpto' => new external_value(PARAM_INT, 'The page to jump to.'), + 'completed' => new external_value(PARAM_BOOL, 'If the user completed the feedback.'), + 'completionpagecontents' => new external_value(PARAM_RAW, 'The completion page contents.'), + 'siteaftersubmit' => new external_value(PARAM_RAW, 'The link (could be relative) to show after submit.'), + 'warnings' => new external_warnings(), + ) + ); + } } diff --git a/mod/feedback/db/services.php b/mod/feedback/db/services.php index 971f6161109..bcfbdd82cac 100644 --- a/mod/feedback/db/services.php +++ b/mod/feedback/db/services.php @@ -85,4 +85,12 @@ $functions = array( 'capabilities' => 'mod/feedback:complete', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE) ), + 'mod_feedback_process_page' => array( + 'classname' => 'mod_feedback_external', + 'methodname' => 'process_page', + 'description' => 'Process a jump between pages.', + 'type' => 'write', + 'capabilities' => 'mod/feedback:complete', + 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE) + ), ); diff --git a/mod/feedback/tests/external_test.php b/mod/feedback/tests/external_test.php index 2a0e1c651c4..d095243ee8b 100644 --- a/mod/feedback/tests/external_test.php +++ b/mod/feedback/tests/external_test.php @@ -446,4 +446,89 @@ class mod_feedback_external_testcase extends externallib_advanced_testcase { $this->assertFalse($result['hasnextpage']); $this->assertTrue($result['hasprevpage']); } + + /** + * Test process_page. + */ + public function test_process_page() { + global $DB; + + // Test user with full capabilities. + $this->setUser($this->student); + $pagecontents = 'You finished it!'; + $DB->set_field('feedback', 'page_after_submit', $pagecontents, array('id' => $this->feedback->id)); + + // Add questions to the feedback, we are adding 2 pages of questions. + $itemscreated = self::populate_feedback($this->feedback, 2); + + $data = []; + foreach ($itemscreated as $item) { + + if (empty($item->hasvalue)) { + continue; + } + + switch ($item->typ) { + case 'textarea': + case 'textfield': + $value = 'Lorem ipsum'; + break; + case 'numeric': + $value = 5; + break; + case 'multichoice': + $value = '1'; + break; + case 'multichoicerated': + $value = '1'; + break; + case 'info': + $value = format_string($this->course->shortname, true, array('context' => $this->context)); + break; + default: + $value = ''; + } + $data[] = ['name' => $item->typ . '_' . $item->id, 'value' => $value]; + } + + // Process first page. + $firstpagedata = [$data[0], $data[1]]; + $result = mod_feedback_external::process_page($this->feedback->id, 0, $firstpagedata); + $result = external_api::clean_returnvalue(mod_feedback_external::process_page_returns(), $result); + $this->assertEquals(1, $result['jumpto']); + $this->assertFalse($result['completed']); + + // Now, process the second page. But first we are going back to the first page. + $secondpagedata = [$data[2], $data[3], $data[4], $data[5], $data[6]]; + $result = mod_feedback_external::process_page($this->feedback->id, 1, $secondpagedata, true); + $result = external_api::clean_returnvalue(mod_feedback_external::process_page_returns(), $result); + $this->assertFalse($result['completed']); + $this->assertEquals(0, $result['jumpto']); // We jumped to the first page. + // Check the values were correctly saved. + $tmpitems = $DB->get_records('feedback_valuetmp'); + $this->assertCount(7, $tmpitems); // 2 from the first page + 5 from the second page. + + // Go forward again (sending the same data). + $result = mod_feedback_external::process_page($this->feedback->id, 0, $firstpagedata); + $result = external_api::clean_returnvalue(mod_feedback_external::process_page_returns(), $result); + $this->assertEquals(1, $result['jumpto']); + $this->assertFalse($result['completed']); + $tmpitems = $DB->get_records('feedback_valuetmp'); + $this->assertCount(7, $tmpitems); // 2 from the first page + 5 from the second page. + + // And finally, save everything! We are going to modify one previous recorded value. + $data[2]['value'] = 'b'; + $secondpagedata = [$data[2], $data[3], $data[4], $data[5], $data[6]]; + $result = mod_feedback_external::process_page($this->feedback->id, 1, $secondpagedata); + $result = external_api::clean_returnvalue(mod_feedback_external::process_page_returns(), $result); + $this->assertTrue($result['completed']); + $this->assertTrue(strpos($result['completionpagecontents'], $pagecontents) !== false); + // Check all the items were saved. + $items = $DB->get_records('feedback_value'); + $this->assertCount(7, $items); + // Check if the one we modified was correctly saved. + $itemid = $itemscreated[4]->id; + $itemsaved = $DB->get_field('feedback_value', 'value', array('item' => $itemid)); + $this->assertEquals('b', $itemsaved); + } } diff --git a/mod/feedback/version.php b/mod/feedback/version.php index cdcf22143ef..9ef23d3d329 100644 --- a/mod/feedback/version.php +++ b/mod/feedback/version.php @@ -24,7 +24,7 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2016120507; // The current module version (Date: YYYYMMDDXX) +$plugin->version = 2016120508; // The current module version (Date: YYYYMMDDXX) $plugin->requires = 2016112900; // Requires this Moodle version $plugin->component = 'mod_feedback'; // Full name of the plugin (used for diagnostics) $plugin->cron = 0;