From 61c640c13a863e4c267d6df6c2fe794639bfbfb4 Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Mon, 13 Feb 2017 17:22:54 +0100 Subject: [PATCH] MDL-57923 mod_data: New WS mod_data_add_entry --- mod/data/classes/external.php | 136 ++++++++++++++++++++ mod/data/db/services.php | 8 ++ mod/data/tests/externallib_test.php | 185 ++++++++++++++++++++++++++++ mod/data/version.php | 2 +- 4 files changed, 330 insertions(+), 1 deletion(-) diff --git a/mod/data/classes/external.php b/mod/data/classes/external.php index 62e4b7494a0..972c340151e 100644 --- a/mod/data/classes/external.php +++ b/mod/data/classes/external.php @@ -933,4 +933,140 @@ class mod_data_external extends external_api { ) ); } + + /** + * Returns description of method parameters + * + * @return external_function_parameters + * @since Moodle 3.3 + */ + public static function add_entry_parameters() { + return new external_function_parameters( + array( + 'databaseid' => new external_value(PARAM_INT, 'data instance id'), + 'groupid' => new external_value(PARAM_INT, 'Group id, 0 means that the function will determine the user group', + VALUE_DEFAULT, 0), + 'data' => new external_multiple_structure( + new external_single_structure( + array( + 'fieldid' => new external_value(PARAM_INT, 'The field id.'), + 'subfield' => new external_value(PARAM_NOTAGS, 'The subfield name (if required).', VALUE_DEFAULT, ''), + 'value' => new external_value(PARAM_RAW, 'The contents for the field always JSON encoded.'), + ) + ), 'The fields data to be created' + ), + ) + ); + } + + /** + * Adds a new entry to a database + * + * @param int $databaseid the data instance id + * @param int $groupid (optional) group id, 0 means that the function will determine the user group + * @param array $data the fields data to be created + * @return array of warnings and status result + * @since Moodle 3.3 + * @throws moodle_exception + */ + public static function add_entry($databaseid, $groupid, $data) { + global $DB; + + $params = array('databaseid' => $databaseid, 'groupid' => $groupid, 'data' => $data); + $params = self::validate_parameters(self::add_entry_parameters(), $params); + $warnings = array(); + $fieldnotifications = array(); + + list($database, $course, $cm, $context) = self::validate_database($params['databaseid']); + // Check database is open in time. + data_require_time_available($database, null, $context); + + $groupmode = groups_get_activity_groupmode($cm); + if (!empty($params['groupid'])) { + $groupid = $params['groupid']; + // Determine is the group is visible to user. + if (!groups_group_visible($groupid, $course, $cm)) { + throw new moodle_exception('notingroup'); + } + } else { + // Check to see if groups are being used here. + if ($groupmode) { + $groupid = groups_get_activity_group($cm); + // Determine is the group is visible to user (this is particullary for the group 0 -> all groups). + if (!groups_group_visible($groupid, $course, $cm)) { + throw new moodle_exception('notingroup'); + } + } else { + $groupid = 0; + } + } + + if (!data_user_can_add_entry($database, $groupid, $groupmode, $context)) { + throw new moodle_exception('noaccess', 'data'); + } + + // Prepare the data as is expected by the API. + $datarecord = new stdClass; + foreach ($params['data'] as $data) { + $subfield = ($data['subfield'] !== '') ? '_' . $data['subfield'] : ''; + // We ask for JSON encoded values because of multiple choice forms or checkboxes that use array parameters. + $datarecord->{'field_' . $data['fieldid'] . $subfield} = json_decode($data['value']); + } + // Validate to ensure that enough data was submitted. + $fields = $DB->get_records('data_fields', array('dataid' => $database->id)); + $processeddata = data_process_submission($database, $fields, $datarecord); + + // Format notifications. + if (!empty($processeddata->fieldnotifications)) { + foreach ($processeddata->fieldnotifications as $field => $notififications) { + foreach ($notififications as $notif) { + $fieldnotifications[] = [ + 'fieldname' => $field, + 'notification' => $notif, + ]; + } + } + } + + // Create a new (empty) record. + $newentryid = 0; + if ($processeddata->validated && $recordid = data_add_record($database, $groupid)) { + $newentryid = $recordid; + // Now populate the fields contents of the new record. + data_add_fields_contents_to_new_record($database, $context, $recordid, $fields, $datarecord, $processeddata); + } + + $result = array( + 'newentryid' => $newentryid, + 'generalnotifications' => $processeddata->generalnotifications, + 'fieldnotifications' => $fieldnotifications, + ); + return $result; + } + + /** + * Returns description of method result value + * + * @return external_description + * @since Moodle 3.3 + */ + public static function add_entry_returns() { + return new external_single_structure( + array( + 'newentryid' => new external_value(PARAM_INT, 'True new created entry id. 0 if the entry was not created.'), + 'generalnotifications' => new external_multiple_structure( + new external_value(PARAM_RAW, 'General notifications') + ), + 'fieldnotifications' => new external_multiple_structure( + new external_single_structure( + array( + 'fieldname' => new external_value(PARAM_TEXT, 'The field name.'), + 'notification' => new external_value(PARAM_RAW, 'The notification for the field.'), + ) + ) + ), + 'warnings' => new external_warnings() + ) + ); + } } diff --git a/mod/data/db/services.php b/mod/data/db/services.php index dafc468dcb5..dc30d4c44a6 100644 --- a/mod/data/db/services.php +++ b/mod/data/db/services.php @@ -99,4 +99,12 @@ $functions = array( 'capabilities' => 'mod/data:manageentries', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE) ), + 'mod_data_add_entry' => array( + 'classname' => 'mod_data_external', + 'methodname' => 'add_entry', + 'description' => 'Adds a new entry.', + 'type' => 'write', + 'capabilities' => 'mod/data:writeentry', + 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE) + ), ); diff --git a/mod/data/tests/externallib_test.php b/mod/data/tests/externallib_test.php index cffdef96eed..07194755453 100644 --- a/mod/data/tests/externallib_test.php +++ b/mod/data/tests/externallib_test.php @@ -808,4 +808,189 @@ class mod_data_external_testcase extends externallib_advanced_testcase { $this->expectException('moodle_exception'); mod_data_external::delete_entry($entry21); } + + /** + * Test add_entry. + */ + public function test_add_entry() { + global $DB; + // First create the record structure and add some entries. + list($entry11, $entry12, $entry13, $entry21) = self::populate_database_with_entries(); + + $this->setUser($this->student1); + $newentrydata = []; + $fields = $DB->get_records('data_fields', array('dataid' => $this->data->id), 'id'); + // Prepare the new entry data. + foreach ($fields as $field) { + $subfield = $value = ''; + + switch ($field->type) { + case 'checkbox': + $value = ['opt1', 'opt2']; + break; + case 'date': + // Add two extra. + $newentrydata[] = [ + 'fieldid' => $field->id, + 'subfield' => 'day', + 'value' => json_encode('5') + ]; + $newentrydata[] = [ + 'fieldid' => $field->id, + 'subfield' => 'month', + 'value' => json_encode('1') + ]; + $subfield = 'year'; + $value = '1981'; + break; + case 'menu': + $value = 'menu1'; + break; + case 'multimenu': + $value = ['multimenu1', 'multimenu4']; + break; + case 'number': + $value = 6; + break; + case 'radiobutton': + $value = 'radioopt1'; + break; + case 'text': + $value = 'some text'; + break; + case 'textarea': + $newentrydata[] = [ + 'fieldid' => $field->id, + 'subfield' => 'content1', + 'value' => json_encode(FORMAT_MOODLE) + ]; + $newentrydata[] = [ + 'fieldid' => $field->id, + 'subfield' => 'itemid', + 'value' => json_encode(0) + ]; + $value = 'more text'; + break; + case 'url': + $value = 'https://moodle.org'; + $subfield = 0; + break; + } + + $newentrydata[] = [ + 'fieldid' => $field->id, + 'subfield' => $subfield, + 'value' => json_encode($value) + ]; + } + $result = mod_data_external::add_entry($this->data->id, 0, $newentrydata); + $result = external_api::clean_returnvalue(mod_data_external::add_entry_returns(), $result); + + $newentryid = $result['newentryid']; + $result = mod_data_external::get_entry($newentryid, 0, true); + $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result); + $this->assertEquals($this->student1->id, $result['entry']['userid']); + $this->assertCount(9, $result['entry']['contents']); + foreach ($result['entry']['contents'] as $content) { + $field = $fields[$content['fieldid']]; + // Stored content same that the one retrieved by WS. + $dbcontent = $DB->get_record('data_content', array('fieldid' => $field->id, 'recordid' => $newentryid)); + $this->assertEquals($dbcontent->content, $content['content']); + + // Now double check everything stored is correct. + if ($field->type == 'checkbox') { + $this->assertEquals('opt1##opt2', $content['content']); + continue; + } + if ($field->type == 'date') { + $this->assertEquals(347500800, $content['content']); // Date in gregorian format. + continue; + } + if ($field->type == 'menu') { + $this->assertEquals('menu1', $content['content']); + continue; + } + if ($field->type == 'multimenu') { + $this->assertEquals('multimenu1##multimenu4', $content['content']); + continue; + } + if ($field->type == 'number') { + $this->assertEquals(6, $content['content']); + continue; + } + if ($field->type == 'radiobutton') { + $this->assertEquals('radioopt1', $content['content']); + continue; + } + if ($field->type == 'text') { + $this->assertEquals('some text', $content['content']); + continue; + } + if ($field->type == 'textarea') { + $this->assertEquals('more text', $content['content']); + $this->assertEquals(FORMAT_MOODLE, $content['content1']); + continue; + } + if ($field->type == 'url') { + $this->assertEquals('https://moodle.org', $content['content']); + continue; + } + $this->assertEquals('multimenu1##multimenu4', $content['content']); + } + + // Now, try to add another entry but removing some required data. + unset($newentrydata[0]); + $result = mod_data_external::add_entry($this->data->id, 0, $newentrydata); + $result = external_api::clean_returnvalue(mod_data_external::add_entry_returns(), $result); + $this->assertEquals(0, $result['newentryid']); + $this->assertCount(0, $result['generalnotifications']); + $this->assertCount(1, $result['fieldnotifications']); + $this->assertEquals('field-1', $result['fieldnotifications'][0]['fieldname']); + $this->assertEquals(get_string('errormustsupplyvalue', 'data'), $result['fieldnotifications'][0]['notification']); + } + + /** + * Test add_entry empty_form. + */ + public function test_add_entry_empty_form() { + $result = mod_data_external::add_entry($this->data->id, 0, []); + $result = external_api::clean_returnvalue(mod_data_external::add_entry_returns(), $result); + $this->assertEquals(0, $result['newentryid']); + $this->assertCount(1, $result['generalnotifications']); + $this->assertCount(0, $result['fieldnotifications']); + $this->assertEquals(get_string('emptyaddform', 'data'), $result['generalnotifications'][0]); + } + + /** + * Test add_entry read_only_period. + */ + public function test_add_entry_read_only_period() { + global $DB; + list($entry11, $entry12, $entry13, $entry21) = self::populate_database_with_entries(); + // Set a time period. + $this->data->timeviewfrom = time() - HOURSECS; + $this->data->timeviewto = time() + HOURSECS; + $DB->update_record('data', $this->data); + + $this->setUser($this->student1); + $this->expectExceptionMessage(get_string('noaccess', 'data')); + $this->expectException('moodle_exception'); + mod_data_external::add_entry($this->data->id, 0, []); + } + + /** + * Test add_entry max_num_entries. + */ + public function test_add_entry_max_num_entries() { + global $DB; + list($entry11, $entry12, $entry13, $entry21) = self::populate_database_with_entries(); + // Set a time period. + $this->data->maxentries = 1; + $DB->update_record('data', $this->data); + + $this->setUser($this->student1); + $this->expectExceptionMessage(get_string('noaccess', 'data')); + $this->expectException('moodle_exception'); + mod_data_external::add_entry($this->data->id, 0, []); + } } diff --git a/mod/data/version.php b/mod/data/version.php index e05291706a4..d3b7b813400 100644 --- a/mod/data/version.php +++ b/mod/data/version.php @@ -24,7 +24,7 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2016120508; // The current module version (Date: YYYYMMDDXX) +$plugin->version = 2016120509; // The current module version (Date: YYYYMMDDXX) $plugin->requires = 2016112900; // Requires this Moodle version $plugin->component = 'mod_data'; // Full name of the plugin (used for diagnostics) $plugin->cron = 0;