MDL-51629 mod_survey: New WS mod_survey_submit_answers
authorJuan Leyva <juanleyvadelgado@gmail.com>
Thu, 1 Oct 2015 15:50:39 +0000 (17:50 +0200)
committerJuan Leyva <juanleyvadelgado@gmail.com>
Thu, 15 Oct 2015 14:17:23 +0000 (16:17 +0200)
lib/db/services.php
mod/survey/classes/external.php
mod/survey/db/services.php
mod/survey/lib.php
mod/survey/save.php
mod/survey/tests/externallib_test.php
mod/survey/tests/lib_test.php

index c998294..f6bf808 100644 (file)
@@ -1234,6 +1234,7 @@ $services = array(
             'mod_survey_get_surveys_by_courses',
             'mod_survey_view_survey',
             'mod_survey_get_questions',
+            'mod_survey_submit_answers',
             'mod_page_view_page',
             'mod_resource_view_resource',
             'mod_folder_view_folder',
index 79cbc61..7302bfe 100644 (file)
@@ -322,4 +322,87 @@ class mod_survey_external extends external_api {
         );
     }
 
+    /**
+     * Describes the parameters for submit_answers.
+     *
+     * @return external_function_parameters
+     * @since Moodle 3.0
+     */
+    public static function submit_answers_parameters() {
+        return new external_function_parameters(
+            array(
+                'surveyid' => new external_value(PARAM_INT, 'Survey id'),
+                'answers' => new external_multiple_structure(
+                    new external_single_structure(
+                        array(
+                            'key' => new external_value(PARAM_RAW, 'Answer key'),
+                            'value' => new external_value(PARAM_RAW, 'Answer value')
+                        )
+                    )
+                ),
+            )
+        );
+    }
+
+    /**
+     * Submit the answers for a given survey.
+     *
+     * @param int $surveyid the survey instance id
+     * @param array $answers the survey answers
+     * @return array of warnings and status result
+     * @since Moodle 3.0
+     * @throws moodle_exception
+     */
+    public static function submit_answers($surveyid, $answers) {
+        global $DB, $USER;
+
+        $params = self::validate_parameters(self::submit_answers_parameters(),
+                                            array(
+                                                'surveyid' => $surveyid,
+                                                'answers' => $answers
+                                            ));
+        $warnings = array();
+
+        // Request and permission validation.
+        $survey = $DB->get_record('survey', array('id' => $params['surveyid']), '*', MUST_EXIST);
+        list($course, $cm) = get_course_and_cm_from_instance($survey, 'survey');
+
+        $context = context_module::instance($cm->id);
+        self::validate_context($context);
+        require_capability('mod/survey:participate', $context);
+
+        if (survey_already_done($survey->id, $USER->id)) {
+            throw new moodle_exception("alreadysubmitted", "survey");
+        }
+
+        // Build the answers array. Data is cleaned inside the survey_save_answers function.
+        $answers = array();
+        foreach ($params['answers'] as $answer) {
+            $key = $answer['key'];
+            $answers[$key] = $answer['value'];
+        }
+
+        survey_save_answers($survey, $answers, $course, $context);
+
+        $result = array();
+        $result['status'] = true;
+        $result['warnings'] = $warnings;
+        return $result;
+    }
+
+    /**
+     * Returns description of method result value
+     *
+     * @return external_description
+     * @since Moodle 3.0
+     */
+    public static function submit_answers_returns() {
+        return new external_single_structure(
+            array(
+                'status' => new external_value(PARAM_BOOL, 'status: true if success'),
+                'warnings' => new external_warnings()
+            )
+        );
+    }
+
 }
index 5e96528..ccc16b2 100644 (file)
@@ -53,4 +53,12 @@ $functions = array(
         'capabilities'  => 'mod/survey:participate'
     ),
 
+    'mod_survey_submit_answers' => array(
+        'classname'     => 'mod_survey_external',
+        'methodname'    => 'submit_answers',
+        'description'   => 'Submit the answers for a given survey.',
+        'type'          => 'write',
+        'capabilities'  => 'mod/survey:participate'
+    ),
+
 );
index 46ff830..3acce42 100644 (file)
@@ -948,3 +948,71 @@ function survey_get_subquestions($question) {
 
     return survey_order_questions($questions, $questionids);
 }
+
+/**
+ * Save the answer for the given survey
+ *
+ * @param  stdClass $survey   a survey object
+ * @param  array $answersrawdata the answers to be saved
+ * @param  stdClass $course   a course object (required for trigger the submitted event)
+ * @param  stdClass $context  a context object (required for trigger the submitted event)
+ * @since Moodle 3.0
+ */
+function survey_save_answers($survey, $answersrawdata, $course, $context) {
+    global $DB, $USER;
+
+    $answers = array();
+
+    // Sort through the data and arrange it.
+    // This is necessary because some of the questions may have two answers, eg Question 1 -> 1 and P1.
+    foreach ($answersrawdata as $key => $val) {
+        if ($key <> "userid" && $key <> "id") {
+            if (substr($key, 0, 1) == "q") {
+                $key = clean_param(substr($key, 1), PARAM_ALPHANUM);   // Keep everything but the 'q', number or P number.
+            }
+            if (substr($key, 0, 1) == "P") {
+                $realkey = (int) substr($key, 1);
+                $answers[$realkey][1] = $val;
+            } else {
+                $answers[$key][0] = $val;
+            }
+        }
+    }
+
+    // Now store the data.
+    $timenow = time();
+    $answerstoinsert = array();
+    foreach ($answers as $key => $val) {
+        if ($key != 'sesskey') {
+            $newdata = new stdClass();
+            $newdata->time = $timenow;
+            $newdata->userid = $USER->id;
+            $newdata->survey = $survey->id;
+            $newdata->question = $key;
+            if (!empty($val[0])) {
+                $newdata->answer1 = $val[0];
+            } else {
+                $newdata->answer1 = "";
+            }
+            if (!empty($val[1])) {
+                $newdata->answer2 = $val[1];
+            } else {
+                $newdata->answer2 = "";
+            }
+
+            $answerstoinsert[] = $newdata;
+        }
+    }
+
+    if (!empty($answerstoinsert)) {
+        $DB->insert_records("survey_answers", $answerstoinsert);
+    }
+
+    $params = array(
+        'context' => $context,
+        'courseid' => $course->id,
+        'other' => array('surveyid' => $survey->id)
+    );
+    $event = \mod_survey\event\response_submitted::create($params);
+    $event->trigger();
+}
index 4b3580e..0fff2c7 100644 (file)
         exit;
     }
 
-
-// Sort through the data and arrange it
-// This is necessary because some of the questions
-// may have two answers, eg Question 1 -> 1 and P1
-
-    $answers = array();
-
-    foreach ($formdata as $key => $val) {
-        if ($key <> "userid" && $key <> "id") {
-            if ( substr($key,0,1) == "q") {
-                $key = clean_param(substr($key,1), PARAM_ALPHANUM);   // keep everything but the 'q', number or Pnumber
-            }
-            if ( substr($key,0,1) == "P") {
-                $realkey = (int) substr($key,1);
-                $answers[$realkey][1] = $val;
-            } else {
-                $answers[$key][0] = $val;
-            }
-        }
-    }
-
-
-// Now store the data.
-
-    $timenow = time();
-    foreach ($answers as $key => $val) {
-        if ($key != 'sesskey') {
-            $newdata = new stdClass();
-            $newdata->time = $timenow;
-            $newdata->userid = $USER->id;
-            $newdata->survey = $survey->id;
-            $newdata->question = $key;
-            if (!empty($val[0])) {
-                $newdata->answer1 = $val[0];
-            } else {
-                $newdata->answer1 = "";
-            }
-            if (!empty($val[1])) {
-                $newdata->answer2 = $val[1];
-            } else {
-                $newdata->answer2 = "";
-            }
-
-            $DB->insert_record("survey_answers", $newdata);
-        }
-    }
+    survey_save_answers($survey, $formdata, $course, $context);
 
     $params = array(
         'context' => $context,
index 836c866..5b9976d 100644 (file)
@@ -305,4 +305,81 @@ class mod_survey_external_testcase extends externallib_advanced_testcase {
         }
     }
 
+    /**
+     * Test submit_answers
+     */
+    public function test_submit_answers() {
+        global $DB;
+
+        // Test user with full capabilities.
+        $this->setUser($this->student);
+
+        // Build our questions and responses array.
+        $realquestions = array();
+        $questions = survey_get_questions($this->survey);
+        $i = 5;
+        foreach ($questions as $q) {
+            if ($q->type >= 0) {
+                if ($q->multi) {
+                    $subquestions = survey_get_subquestions($q);
+                    foreach ($subquestions as $sq) {
+                        $realquestions[] = array(
+                            'key' => 'q' . $sq->id,
+                            'value' => $i % 5 + 1   // Values between 1 and 5.
+                        );
+                        $i++;
+                    }
+                } else {
+                    $realquestions[] = array(
+                        'key' => 'q' . $q->id,
+                        'value' => $i % 5 + 1
+                    );
+                    $i++;
+                }
+            }
+        }
+
+        $result = mod_survey_external::submit_answers($this->survey->id, $realquestions);
+        $result = external_api::clean_returnvalue(mod_survey_external::submit_answers_returns(), $result);
+
+        $this->assertTrue($result['status']);
+        $this->assertCount(0, $result['warnings']);
+
+        $dbanswers = $DB->get_records_menu('survey_answers', array('survey' => $this->survey->id), '', 'question, answer1');
+        foreach ($realquestions as $q) {
+            $id = str_replace('q', '', $q['key']);
+            $this->assertEquals($q['value'], $dbanswers[$id]);
+        }
+
+        // Submit again, we expect an error here.
+        try {
+            mod_survey_external::submit_answers($this->survey->id, $realquestions);
+            $this->fail('Exception expected due to answers already submitted.');
+        } catch (moodle_exception $e) {
+            $this->assertEquals('alreadysubmitted', $e->errorcode);
+        }
+
+        // Test user with no capabilities.
+        // We need a explicit prohibit since this capability is only defined in authenticated user and guest roles.
+        assign_capability('mod/survey:participate', CAP_PROHIBIT, $this->studentrole->id, $this->context->id);
+        accesslib_clear_all_caches_for_unit_testing();
+
+        try {
+            mod_survey_external::submit_answers($this->survey->id, $realquestions);
+            $this->fail('Exception expected due to missing capability.');
+        } catch (moodle_exception $e) {
+            $this->assertEquals('nopermissions', $e->errorcode);
+        }
+
+        // Test not-enrolled user.
+        $usernotenrolled = self::getDataGenerator()->create_user();
+        $this->setUser($usernotenrolled);
+        try {
+            mod_survey_external::submit_answers($this->survey->id, $realquestions);
+            $this->fail('Exception expected due to not enrolled user.');
+        } catch (moodle_exception $e) {
+            $this->assertEquals('requireloginerror', $e->errorcode);
+        }
+    }
+
 }
index 4b3576a..3a729cf 100644 (file)
@@ -111,4 +111,59 @@ class mod_survey_lib_testcase extends advanced_testcase {
         }
     }
 
+    /**
+     * Test survey_save_answers
+     */
+    public function test_survey_save_answers() {
+        global $DB;
+
+        $this->resetAfterTest();
+        $this->setAdminUser();
+
+        // Setup test data.
+        $course = $this->getDataGenerator()->create_course();
+        $survey = $this->getDataGenerator()->create_module('survey', array('course' => $course->id));
+        $context = context_module::instance($survey->cmid);
+
+        // Build our questions and responses array.
+        $realquestions = array();
+        $questions = survey_get_questions($survey);
+        $i = 5;
+        foreach ($questions as $q) {
+            if ($q->type > 0) {
+                if ($q->multi) {
+                    $subquestions = survey_get_subquestions($q);
+                    foreach ($subquestions as $sq) {
+                        $key = 'q' . $sq->id;
+                        $realquestions[$key] = $i % 5 + 1;
+                        $i++;
+                    }
+                } else {
+                    $key = 'q' . $q->id;
+                    $realquestions[$key] = $i % 5 + 1;
+                    $i++;
+                }
+            }
+        }
+
+        $sink = $this->redirectEvents();
+        survey_save_answers($survey, $realquestions, $course, $context);
+
+        // Check the stored answers, they must match.
+        $dbanswers = $DB->get_records_menu('survey_answers', array('survey' => $survey->id), '', 'question, answer1');
+        foreach ($realquestions as $key => $value) {
+            $id = str_replace('q', '', $key);
+            $this->assertEquals($value, $dbanswers[$id]);
+        }
+
+        // Check events.
+        $events = $sink->get_events();
+        $this->assertCount(1, $events);
+        $event = array_shift($events);
+
+        // Checking that the event contains the expected values.
+        $this->assertInstanceOf('\mod_survey\event\response_submitted', $event);
+        $this->assertEquals($context, $event->get_context());
+        $this->assertEquals($survey->id, $event->other['surveyid']);
+    }
 }