From 93e122e0dcf3dbebfe4c21662bf910ce8c0bbb6b Mon Sep 17 00:00:00 2001 From: Frederic Massart Date: Tue, 13 Oct 2015 21:12:47 +0800 Subject: [PATCH] MDL-49231 mod_glossary: External function get_entries_by_term --- mod/glossary/classes/entry_query_builder.php | 20 +++++ mod/glossary/classes/external.php | 83 ++++++++++++++++++++ mod/glossary/lib.php | 41 ++++++++++ mod/glossary/tests/external_test.php | 59 ++++++++++++++ 4 files changed, 203 insertions(+) diff --git a/mod/glossary/classes/entry_query_builder.php b/mod/glossary/classes/entry_query_builder.php index 7de6d51e266..a48033abdde 100644 --- a/mod/glossary/classes/entry_query_builder.php +++ b/mod/glossary/classes/entry_query_builder.php @@ -272,6 +272,21 @@ class mod_glossary_entry_query_builder { } } + /** + * Filter by concept or alias. + * + * This requires the alias table to be joined in the query. See {@link self::join_alias()}. + * + * @param string $term What the concept or aliases should be. + */ + public function filter_by_term($term) { + $this->where[] = sprintf("(%s = :filterterma OR %s = :filtertermb)", + self::resolve_field('concept', 'entries'), + self::resolve_field('alias', 'alias')); + $this->params['filterterma'] = $term; + $this->params['filtertermb'] = $term; + } + /** * Filter by search terms. * @@ -335,6 +350,11 @@ class mod_glossary_entry_query_builder { } } + // When there are no conditions we add a negative one to ensure that we don't return anything. + if (empty($conditions)) { + $conditions[] = '1 = 2'; + } + $this->where[] = implode(' AND ', $conditions); $this->params = array_merge($this->params, $params); } diff --git a/mod/glossary/classes/external.php b/mod/glossary/classes/external.php index 0b18d4bfa8d..33c96b57219 100644 --- a/mod/glossary/classes/external.php +++ b/mod/glossary/classes/external.php @@ -1200,6 +1200,89 @@ class mod_glossary_external extends external_api { )); } + /** + * Returns the description of the external function parameters. + * + * @return external_function_parameters + * @since Moodle 3.1 + */ + public static function get_entries_by_term_parameters() { + return new external_function_parameters(array( + 'id' => new external_value(PARAM_INT, 'Glossary entry ID'), + 'term' => new external_value(PARAM_NOTAGS, 'The entry concept, or alias'), + 'from' => new external_value(PARAM_INT, 'Start returning records from here', VALUE_DEFAULT, 0), + 'limit' => new external_value(PARAM_INT, 'Number of records to return', VALUE_DEFAULT, 20), + 'options' => new external_single_structure(array( + 'includenotapproved' => new external_value(PARAM_BOOL, 'When false, includes the non-approved entries created by' . + ' the user. When true, also includes the ones that the user has the permission to approve.', VALUE_DEFAULT, 0) + ), 'An array of options', VALUE_DEFAULT, array()) + )); + } + + /** + * Browse a glossary entries using a term matching the concept or alias. + * + * @param int $id The glossary ID. + * @param string $term The term. + * @param int $from Start returning records from here. + * @param int $limit Number of records to return. + * @param array $options Array of options. + * @return array of warnings and status result + * @since Moodle 3.1 + * @throws moodle_exception + */ + public static function get_entries_by_term($id, $term, $from = 0, $limit = 20, $options = array()) { + global $DB, $USER; + + $params = self::validate_parameters(self::get_entries_by_term_parameters(), array( + 'id' => $id, + 'term' => $term, + 'from' => $from, + 'limit' => $limit, + 'options' => $options, + )); + $id = $params['id']; + $term = $params['term']; + $from = $params['from']; + $limit = $params['limit']; + $options = $params['options']; + $warnings = array(); + + // Get and validate the glossary. + list($glossary, $context) = self::validate_glossary($id); + + // Fetching the entries. + $entries = array(); + list($records, $count) = glossary_get_entries_by_term($glossary, $context, $term, $from, $limit, $options); + foreach ($records as $key => $record) { + self::fill_entry_details($record, $context); + $entries[] = $record; + } + $records->close(); + + return array( + 'count' => $count, + 'entries' => $entries, + 'warnings' => $warnings + ); + } + + /** + * Returns the description of the external function return value. + * + * @return external_description + * @since Moodle 3.1 + */ + public static function get_entries_by_term_returns() { + return new external_single_structure(array( + 'count' => new external_value(PARAM_INT, 'The total number of records matching the request.'), + 'entries' => new external_multiple_structure( + self::get_entry_return_structure() + ), + 'warnings' => new external_warnings() + )); + } + /** * Returns the description of the external function parameters. * diff --git a/mod/glossary/lib.php b/mod/glossary/lib.php index a6c53ac0b95..2bafd1ccf36 100644 --- a/mod/glossary/lib.php +++ b/mod/glossary/lib.php @@ -3694,6 +3694,47 @@ function glossary_get_entries_by_search($glossary, $context, $query, $fullsearch return array($entries, $count); } +/** + * Returns the entries of a glossary by term. + * + * @param object $glossary The glossary. + * @param context $context The context of the glossary. + * @param string $term The term we are searching for, a concept or alias. + * @param int $from Fetch records from. + * @param int $limit Number of records to fetch. + * @param array $options Accepts: + * - (bool) includenotapproved. When false, includes the non-approved entries created by + * the current user. When true, also includes the ones that the user has the permission to approve. + * @return array The first element being the recordset, the second the number of entries. + * @since Moodle 3.1 + */ +function glossary_get_entries_by_term($glossary, $context, $term, $from, $limit, $options = array()) { + + // Build the query. + $qb = new mod_glossary_entry_query_builder($glossary); + if (!empty($options['includenotapproved']) && has_capability('mod/glossary:approve', $context)) { + $qb->filter_by_non_approved(mod_glossary_entry_query_builder::NON_APPROVED_ALL); + } else { + $qb->filter_by_non_approved(mod_glossary_entry_query_builder::NON_APPROVED_SELF); + } + + $qb->add_field('*', 'entries'); + $qb->join_alias(); + $qb->distinct('id', 'entries'); + $qb->join_user(); + $qb->add_user_fields(); + $qb->filter_by_term($term); + + $qb->order_by('concept', 'entries'); + $qb->limit($from, $limit); + + // Fetching the entries. + $count = $qb->count_records(); + $entries = $qb->get_recordset(); + + return array($entries, $count); +} + /** * Fetch an entry. * diff --git a/mod/glossary/tests/external_test.php b/mod/glossary/tests/external_test.php index 6c536df8a6f..e09b6022049 100644 --- a/mod/glossary/tests/external_test.php +++ b/mod/glossary/tests/external_test.php @@ -868,6 +868,65 @@ class mod_glossary_external_testcase extends externallib_advanced_testcase { $this->assertEquals($e8->id, $return['entries'][1]['id']); } + public function test_get_entries_by_term() { + $this->resetAfterTest(true); + + // Generate all the things. + $gg = $this->getDataGenerator()->get_plugin_generator('mod_glossary'); + $c1 = $this->getDataGenerator()->create_course(); + $g1 = $this->getDataGenerator()->create_module('glossary', array('course' => $c1->id)); + $g2 = $this->getDataGenerator()->create_module('glossary', array('course' => $c1->id)); + $u1 = $this->getDataGenerator()->create_user(); + $ctx = context_module::instance($g1->cmid); + $this->getDataGenerator()->enrol_user($u1->id, $c1->id); + + $this->setAdminUser(); + + $e1 = $gg->create_content($g1, array('userid' => $u1->id, 'approved' => 1, 'concept' => 'cat')); + $e2 = $gg->create_content($g1, array('userid' => $u1->id, 'approved' => 1), array('cat', 'dog')); + $e3 = $gg->create_content($g1, array('userid' => $u1->id, 'approved' => 1), array('dog')); + $e4 = $gg->create_content($g1, array('userid' => $u1->id, 'approved' => 0, 'concept' => 'dog')); + $e5 = $gg->create_content($g2, array('userid' => $u1->id, 'approved' => 1, 'concept' => 'dog'), array('cat')); + + // Search concept + alias. + $return = mod_glossary_external::get_entries_by_term($g1->id, 'cat', 0, 20, array('includenotapproved' => false)); + $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_term_returns(), $return); + $this->assertCount(2, $return['entries']); + $this->assertEquals(2, $return['count']); + $this->assertEquals($e1->id, $return['entries'][0]['id']); + $this->assertEquals($e2->id, $return['entries'][1]['id']); + + // Search alias. + $return = mod_glossary_external::get_entries_by_term($g1->id, 'dog', 0, 20, array('includenotapproved' => false)); + $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_term_returns(), $return); + + $this->assertCount(2, $return['entries']); + $this->assertEquals(2, $return['count']); + $this->assertEquals($e2->id, $return['entries'][0]['id']); + $this->assertEquals($e3->id, $return['entries'][1]['id']); + + // Search including not approved. + $return = mod_glossary_external::get_entries_by_term($g1->id, 'dog', 0, 20, array('includenotapproved' => true)); + $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_term_returns(), $return); + $this->assertCount(3, $return['entries']); + $this->assertEquals(3, $return['count']); + $this->assertEquals($e4->id, $return['entries'][0]['id']); + $this->assertEquals($e2->id, $return['entries'][1]['id']); + $this->assertEquals($e3->id, $return['entries'][2]['id']); + + // Pagination. + $return = mod_glossary_external::get_entries_by_term($g1->id, 'dog', 0, 1, array('includenotapproved' => true)); + $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_term_returns(), $return); + $this->assertCount(1, $return['entries']); + $this->assertEquals(3, $return['count']); + $this->assertEquals($e4->id, $return['entries'][0]['id']); + $return = mod_glossary_external::get_entries_by_term($g1->id, 'dog', 1, 1, array('includenotapproved' => true)); + $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_term_returns(), $return); + $this->assertCount(1, $return['entries']); + $this->assertEquals(3, $return['count']); + $this->assertEquals($e2->id, $return['entries'][0]['id']); + } + public function test_get_entry_by_id() { $this->resetAfterTest(true);