MDL-57920 mod_data: New WS mod_data_search_entries
authorJuan Leyva <juanleyvadelgado@gmail.com>
Mon, 13 Feb 2017 13:13:23 +0000 (14:13 +0100)
committerEloy Lafuente (stronk7) <stronk7@moodle.org>
Wed, 29 Mar 2017 23:53:46 +0000 (01:53 +0200)
mod/data/classes/external.php
mod/data/db/services.php
mod/data/tests/externallib_test.php
mod/data/version.php

index c2b67ac..6f6829e 100644 (file)
@@ -642,4 +642,172 @@ class mod_data_external extends external_api {
             )
         );
     }
+
+    /**
+     * Returns description of method parameters
+     *
+     * @return external_function_parameters
+     * @since Moodle 3.3
+     */
+    public static function search_entries_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),
+                'returncontents' => new external_value(PARAM_BOOL, 'Whether to return contents or not.', VALUE_DEFAULT, false),
+                'search' => new external_value(PARAM_NOTAGS, 'search string (empty when using advanced)', VALUE_DEFAULT, ''),
+                'advsearch' => new external_multiple_structure(
+                    new external_single_structure(
+                        array(
+                            'name' => new external_value(PARAM_ALPHANUMEXT, 'Field key for search.
+                                                            Use fn or ln for first or last name'),
+                            'value' => new external_value(PARAM_RAW, 'JSON encoded value for search'),
+                        )
+                    ), 'Advanced search', VALUE_DEFAULT, array()
+                ),
+                'sort' => new external_value(PARAM_INT, 'Sort the records by this field id, reserved ids are:
+                                                0: timeadded
+                                                -1: firstname
+                                                -2: lastname
+                                                -3: approved
+                                                -4: timemodified.
+                                                Empty for using the default database setting.', VALUE_DEFAULT, null),
+                'order' => new external_value(PARAM_ALPHA, 'The direction of the sorting: \'ASC\' or \'DESC\'.
+                                                Empty for using the default database setting.', VALUE_DEFAULT, null),
+                'page' => new external_value(PARAM_INT, 'The page of records to return.', VALUE_DEFAULT, 0),
+                'perpage' => new external_value(PARAM_INT, 'The number of records to return per page', VALUE_DEFAULT, 0),
+            )
+        );
+    }
+
+    /**
+     * Return access information for a given feedback
+     *
+     * @param int $databaseid       the data instance id
+     * @param int $groupid          (optional) group id, 0 means that the function will determine the user group
+     * @param bool $returncontents  whether to return contents or not
+     * @param str $search           search text
+     * @param array $advsearch      advanced search data
+     * @param str $sort             sort by this field
+     * @param int $order            the direction of the sorting
+     * @param int $page             page of records to return
+     * @param int $perpage          number of records to return per page
+     * @return array of warnings and the entries
+     * @since Moodle 3.3
+     * @throws moodle_exception
+     */
+    public static function search_entries($databaseid, $groupid = 0, $returncontents = false, $search = '', $advsearch = [],
+            $sort = null, $order = null, $page = 0, $perpage = 0) {
+        global $PAGE, $DB;
+
+        $params = array('databaseid' => $databaseid, 'groupid' => $groupid, 'returncontents' => $returncontents, 'search' => $search,
+                        'advsearch' => $advsearch, 'sort' => $sort, 'order' => $order, 'page' => $page, 'perpage' => $perpage);
+        $params = self::validate_parameters(self::search_entries_parameters(), $params);
+        $warnings = array();
+
+        if (!empty($params['order'])) {
+            $params['order'] = strtoupper($params['order']);
+            if ($params['order'] != 'ASC' && $params['order'] != 'DESC') {
+                throw new invalid_parameter_exception('Invalid value for sortdirection parameter (value: ' . $params['order'] . ')');
+            }
+        }
+
+        list($database, $course, $cm, $context) = self::validate_database($params['databaseid']);
+        // Check database is open in time.
+        data_require_time_available($database, null, $context);
+
+        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 = groups_get_activity_groupmode($cm)) {
+                $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 (!empty($params['advsearch'])) {
+            $advanced = true;
+            $defaults = [];
+            $fn = $ln = ''; // Defaults for first and last name.
+            // Force defaults for advanced search.
+            foreach ($params['advsearch'] as $adv) {
+                if ($adv['name'] == 'fn' || $adv['name'] == 'ln') {
+                    $$adv['name'] = json_decode($adv['value']);
+                    continue;
+                }
+                $defaults[$adv['name']] = json_decode($adv['value']);
+            }
+            list($searcharray, $params['search']) = data_build_search_array($database, false, [], $defaults, $fn, $ln);
+        } else {
+            $advanced = null;
+            $searcharray = null;
+        }
+
+        list($records, $maxcount, $totalcount, $page, $nowperpage, $sort, $mode) =
+            data_search_entries($database, $cm, $context, 'list', $groupid, $params['search'], $params['sort'], $params['order'],
+                $params['page'], $params['perpage'], $advanced, $searcharray);
+
+        $entries = [];
+        foreach ($records as $record) {
+            $user = user_picture::unalias($record, null, 'userid');
+            $related = array('context' => $context, 'database' => $database, 'user' => $user);
+            if ($params['returncontents']) {
+                $related['contents'] = $DB->get_records('data_content', array('recordid' => $record->id));
+            } else {
+                $related['contents'] = null;
+            }
+
+            $exporter = new record_exporter($record, $related);
+            $entries[] = $exporter->export($PAGE->get_renderer('core'));
+        }
+
+        $result = array(
+            'entries' => $entries,
+            'totalcount' => $totalcount,
+            'maxcount' => $maxcount,
+            'warnings' => $warnings
+        );
+
+        // Check if we should return the list rendered.
+        if ($params['returncontents']) {
+            ob_start();
+            // The return parameter stops the execution after the first record.
+            data_print_template('listtemplate', $records, $database, '', $page, false);
+            $result['listviewcontents'] = ob_get_contents();
+            ob_end_clean();
+        }
+
+        return $result;
+    }
+
+    /**
+     * Returns description of method result value
+     *
+     * @return external_description
+     * @since Moodle 3.3
+     */
+    public static function search_entries_returns() {
+        return new external_single_structure(
+            array(
+                'entries' => new external_multiple_structure(
+                    record_exporter::get_read_structure()
+                ),
+                'totalcount' => new external_value(PARAM_INT, 'Total count of records.'),
+                'listviewcontents' => new external_value(PARAM_RAW, 'The list view contents as is rendered in the site.',
+                                                            VALUE_OPTIONAL),
+                'warnings' => new external_warnings()
+            )
+        );
+    }
 }
index c98e196..d95612d 100644 (file)
@@ -75,4 +75,12 @@ $functions = array(
         'capabilities'  => 'mod/data:viewentry',
         'services'      => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
     ),
+    'mod_data_search_entries' => array(
+        'classname'     => 'mod_data_external',
+        'methodname'    => 'search_entries',
+        'description'   => 'Search for entries in the given database.',
+        'type'          => 'read',
+        'capabilities'  => 'mod/data:viewentry',
+        'services'      => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
+    ),
 );
index 30ec6d8..99e549f 100644 (file)
@@ -619,4 +619,94 @@ class mod_data_external_testcase extends externallib_advanced_testcase {
             $this->assertEquals($field, (array) $fields[$field['id']]);
         }
     }
+
+    /**
+     * Test search_entries.
+     */
+    public function test_search_entries() {
+        global $DB;
+        list($entry11, $entry12, $entry13, $entry21) = self::populate_database_with_entries();
+
+        // First do a normal text search as student 1. I should see my two group entries.
+        $this->setUser($this->student1);
+        $result = mod_data_external::search_entries($this->data->id, 0, false, 'text');
+        $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
+        $this->assertCount(2, $result['entries']);
+        $this->assertEquals(2, $result['totalcount']);
+
+        // Now as the other student I should receive my not approved entry. Apply ordering here.
+        $this->setUser($this->student2);
+        $result = mod_data_external::search_entries($this->data->id, 0, false, 'text', [], DATA_APPROVED, 'ASC');
+        $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
+        $this->assertCount(3, $result['entries']);
+        $this->assertEquals(3, $result['totalcount']);
+        // The not approved one should be the first.
+        $this->assertEquals($entry13, $result['entries'][0]['id']);
+
+        // Now as the other group student.
+        $this->setUser($this->student3);
+        $result = mod_data_external::search_entries($this->data->id, 0, false, 'text');
+        $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
+        $this->assertCount(1, $result['entries']);
+        $this->assertEquals(1, $result['totalcount']);
+        $this->assertEquals($this->student3->id, $result['entries'][0]['userid']);
+
+        // Same normal text search as teacher.
+        $this->setUser($this->teacher);
+        $result = mod_data_external::search_entries($this->data->id, 0, false, 'text');
+        $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
+        $this->assertCount(4, $result['entries']);  // I can see all groups and non approved.
+        $this->assertEquals(4, $result['totalcount']);
+
+        // Pagination.
+        $this->setUser($this->teacher);
+        $result = mod_data_external::search_entries($this->data->id, 0, false, 'text', [], DATA_TIMEADDED, 'ASC', 0, 2);
+        $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
+        $this->assertCount(2, $result['entries']);  // Only 2 per page.
+        $this->assertEquals(4, $result['totalcount']);
+
+        // Now advanced search or not dinamic fields (user firstname for example).
+        $this->setUser($this->student1);
+        $advsearch = [
+            ['name' => 'fn', 'value' => json_encode($this->student2->firstname)]
+        ];
+        $result = mod_data_external::search_entries($this->data->id, 0, false, '', $advsearch);
+        $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
+        $this->assertCount(1, $result['entries']);
+        $this->assertEquals(1, $result['totalcount']);
+        $this->assertEquals($this->student2->id, $result['entries'][0]['userid']);  // I only found mine!
+
+        // Advanced search for fields.
+        $field = $DB->get_record('data_fields', array('type' => 'url'));
+        $advsearch = [
+            ['name' => 'f_' . $field->id , 'value' => 'sampleurl']
+        ];
+        $result = mod_data_external::search_entries($this->data->id, 0, false, '', $advsearch);
+        $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
+        $this->assertCount(2, $result['entries']);  // Found two entries matching this.
+        $this->assertEquals(2, $result['totalcount']);
+
+        // Combined search.
+        $field2 = $DB->get_record('data_fields', array('type' => 'number'));
+        $advsearch = [
+            ['name' => 'f_' . $field->id , 'value' => 'sampleurl'],
+            ['name' => 'f_' . $field2->id , 'value' => '12345'],
+            ['name' => 'ln', 'value' => json_encode($this->student2->lastname)]
+        ];
+        $result = mod_data_external::search_entries($this->data->id, 0, false, '', $advsearch);
+        $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
+        $this->assertCount(1, $result['entries']);  // Only one matching everything.
+        $this->assertEquals(1, $result['totalcount']);
+
+        // Combined search (no results).
+        $field2 = $DB->get_record('data_fields', array('type' => 'number'));
+        $advsearch = [
+            ['name' => 'f_' . $field->id , 'value' => 'sampleurl'],
+            ['name' => 'f_' . $field2->id , 'value' => '98780333'], // Non existent number.
+        ];
+        $result = mod_data_external::search_entries($this->data->id, 0, false, '', $advsearch);
+        $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
+        $this->assertCount(0, $result['entries']);  // Only one matching everything.
+        $this->assertEquals(0, $result['totalcount']);
+    }
 }
index f317280..bc85847 100644 (file)
@@ -24,7 +24,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2016120505;       // The current module version (Date: YYYYMMDDXX)
+$plugin->version   = 2016120506;       // 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;