MDL-57923 mod_data: New WS mod_data_add_entry
authorJuan Leyva <juanleyvadelgado@gmail.com>
Mon, 13 Feb 2017 16:22:54 +0000 (17:22 +0100)
committerEloy Lafuente (stronk7) <stronk7@moodle.org>
Thu, 30 Mar 2017 00:28:54 +0000 (02:28 +0200)
mod/data/classes/external.php
mod/data/db/services.php
mod/data/tests/externallib_test.php
mod/data/version.php

index 62e4b74..972c340 100644 (file)
@@ -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()
+            )
+        );
+    }
 }
index dafc468..dc30d4c 100644 (file)
@@ -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)
+    ),
 );
index cffdef9..0719475 100644 (file)
@@ -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, []);
+    }
 }
index e052917..d3b7b81 100644 (file)
@@ -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;