From 52fcc0b827f3da7b4b20a28f12396095a0a40bdb Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Wed, 20 Feb 2019 15:23:03 +0100 Subject: [PATCH 1/6] MDL-64656 core_tag: Add existing WebServices to the mobile app service --- lib/db/services.php | 1 + version.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/db/services.php b/lib/db/services.php index ea5f502b64e..30fdec8c369 100644 --- a/lib/db/services.php +++ b/lib/db/services.php @@ -1498,6 +1498,7 @@ $functions = array( 'description' => 'Gets tag index page for one tag and one tag area', 'type' => 'read', 'ajax' => true, + 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_tag_get_tags' => array( 'classname' => 'core_tag_external', diff --git a/version.php b/version.php index 08fe4374e63..5017af85b91 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2019040600.00; // YYYYMMDD = weekly release date of this DEV branch. +$version = 2019040600.01; // YYYYMMDD = weekly release date of this DEV branch. // RR = release increments - 00 in DEV branches. // .XX = incremental changes. From 6c344ff282d5457bcdeaa7b6a2f85a97f04b2849 Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Wed, 20 Feb 2019 15:24:38 +0100 Subject: [PATCH 2/6] MDL-64656 core_tag: Return tags in modules and blog --- blog/classes/external/post_exporter.php | 13 +++ blog/tests/external_test.php | 11 +++ blog/upgrade.txt | 6 ++ course/externallib.php | 4 + course/upgrade.txt | 2 + mod/book/lib.php | 1 + mod/book/tests/lib_test.php | 21 ++++- mod/book/upgrade.txt | 4 + mod/data/classes/external/record_exporter.php | 10 ++ mod/data/tests/externallib_test.php | 7 +- mod/data/upgrade.txt | 3 + mod/forum/externallib.php | 5 + mod/forum/tests/externallib_test.php | 7 ++ mod/forum/upgrade.txt | 1 + mod/glossary/classes/external.php | 5 + mod/glossary/tests/external_test.php | 29 ++++-- mod/glossary/upgrade.txt | 3 + mod/wiki/classes/external.php | 10 +- mod/wiki/tests/externallib_test.php | 30 +++++- mod/wiki/upgrade.txt | 3 + tag/classes/external/tag_item_exporter.php | 92 +++++++++++++++++++ tag/classes/external/util.php | 70 ++++++++++++++ 22 files changed, 322 insertions(+), 15 deletions(-) create mode 100644 tag/classes/external/tag_item_exporter.php create mode 100644 tag/classes/external/util.php diff --git a/blog/classes/external/post_exporter.php b/blog/classes/external/post_exporter.php index 3d69299ef60..e608c32b754 100644 --- a/blog/classes/external/post_exporter.php +++ b/blog/classes/external/post_exporter.php @@ -29,6 +29,7 @@ use external_util; use external_files; use renderer_base; use context_system; +use core_tag\external\tag_item_exporter; /** * Class for exporting a blog post (entry). @@ -171,6 +172,12 @@ class post_exporter extends exporter { 'multiple' => true, 'optional' => true ), + 'tags' => array( + 'type' => tag_item_exporter::read_properties_definition(), + 'description' => 'Tags.', + 'multiple' => true, + 'optional' => true, + ), ); } @@ -179,6 +186,12 @@ class post_exporter extends exporter { $values['summaryfiles'] = external_util::get_area_files($context->id, 'blog', 'post', $this->data->id); $values['attachmentfiles'] = external_util::get_area_files($context->id, 'blog', 'attachment', $this->data->id); + if ($this->data->module == 'blog_external') { + // For external blogs, the content field has the external blog id. + $values['tags'] = \core_tag\external\util::get_item_tags('core', 'blog_external', $this->data->content); + } else { + $values['tags'] = \core_tag\external\util::get_item_tags('core', 'post', $this->data->id); + } return $values; } diff --git a/blog/tests/external_test.php b/blog/tests/external_test.php index f5822f4893b..5e23d5f827c 100644 --- a/blog/tests/external_test.php +++ b/blog/tests/external_test.php @@ -108,6 +108,9 @@ class core_blog_external_testcase extends advanced_testcase { $result = core_blog\external::get_entries(); $result = external_api::clean_returnvalue(core_blog\external::get_entries_returns(), $result); $this->assertCount(1, $result['entries']); + $this->assertCount(1, $result['entries'][0]['tags']); + $this->assertEquals('tag1', $result['entries'][0]['tags'][0]['rawname']); + $this->assertEquals($this->postid, $result['entries'][0]['id']); } @@ -141,6 +144,9 @@ class core_blog_external_testcase extends advanced_testcase { $result = core_blog\external::get_entries(); $result = external_api::clean_returnvalue(core_blog\external::get_entries_returns(), $result); $this->assertCount(1, $result['entries']); + $this->assertCount(1, $result['entries'][0]['tags']); + $this->assertEquals('tag1', $result['entries'][0]['tags'][0]['rawname']); + $this->assertEquals($this->postid, $result['entries'][0]['id']); } @@ -331,6 +337,9 @@ class core_blog_external_testcase extends advanced_testcase { $result = external_api::clean_returnvalue(core_blog\external::get_entries_returns(), $result); $this->assertCount(2, $result['entries']); $this->assertEquals(2, $result['totalentries']); + $this->assertCount(0, $result['entries'][0]['tags']); + $this->assertCount(1, $result['entries'][1]['tags']); + $this->assertEquals('tag1', $result['entries'][1]['tags'][0]['rawname']); $result = core_blog\external::get_entries(array(), 0, 1); $result = external_api::clean_returnvalue(core_blog\external::get_entries_returns(), $result); @@ -365,6 +374,8 @@ class core_blog_external_testcase extends advanced_testcase { $result = core_blog\external::get_entries(array(array('name' => 'courseid', 'value' => $this->courseid))); $result = external_api::clean_returnvalue(core_blog\external::get_entries_returns(), $result); $this->assertCount(1, $result['entries']); + $this->assertCount(1, $result['entries'][0]['tags']); + $this->assertEquals('tag1', $result['entries'][0]['tags'][0]['rawname']); // There is no entry associated with a wrong course. $result = core_blog\external::get_entries(array(array('name' => 'courseid', 'value' => $anothercourse->id))); diff --git a/blog/upgrade.txt b/blog/upgrade.txt index 598a27592cd..644ea08ae2a 100644 --- a/blog/upgrade.txt +++ b/blog/upgrade.txt @@ -1,3 +1,9 @@ +This files describes API changes in /blog/* , +information provided here is intended especially for developers. + +=== 3.7 === + * External function get_entries now returns an additional field "tags" returning the post tags. + === 2.7 === * blog_entry->add_associations() does not accept any params. diff --git a/course/externallib.php b/course/externallib.php index 97a42c233b7..dd9c77b6ab5 100644 --- a/course/externallib.php +++ b/course/externallib.php @@ -494,6 +494,10 @@ class core_course_external extends external_api { 'userid' => new external_value(PARAM_INT, 'User who added this content to moodle'), 'author' => new external_value(PARAM_TEXT, 'Content owner'), 'license' => new external_value(PARAM_TEXT, 'Content license'), + 'tags' => new external_multiple_structure( + \core_tag\external\tag_item_exporter::get_read_structure(), 'Tags', + VALUE_OPTIONAL + ), ) ), VALUE_DEFAULT, array() ), diff --git a/course/upgrade.txt b/course/upgrade.txt index efa1b2f604d..090b88a262d 100644 --- a/course/upgrade.txt +++ b/course/upgrade.txt @@ -6,6 +6,8 @@ information provided here is intended especially for developers. * External function core_course_external::get_course_contents new returns the following additional completiondata field: - valueused (indicates whether the completion state affects the availability of other content) * External function core_course_external::get_course_contents now returns a new contentsinfo field with summary files information. + * External function core_course_external::get_course_contents now returns an additional field "tags" returning the content tags. + === 3.6 === diff --git a/mod/book/lib.php b/mod/book/lib.php index f5bd20fd586..0b4887f80ba 100644 --- a/mod/book/lib.php +++ b/mod/book/lib.php @@ -611,6 +611,7 @@ function book_export_contents($cm, $baseurl) { $chapterindexfile['userid'] = null; $chapterindexfile['author'] = null; $chapterindexfile['license'] = null; + $chapterindexfile['tags'] = \core_tag\external\util::get_item_tags('mod_book', 'book_chapters', $chapter->id); $contents[] = $chapterindexfile; // Chapter files (images usually). diff --git a/mod/book/tests/lib_test.php b/mod/book/tests/lib_test.php index 51fe25f21d7..49f740a5b6d 100644 --- a/mod/book/tests/lib_test.php +++ b/mod/book/tests/lib_test.php @@ -45,7 +45,8 @@ class mod_book_lib_testcase extends advanced_testcase { } public function test_export_contents() { - global $DB; + global $DB, $CFG; + require_once($CFG->dirroot . '/course/externallib.php'); $user = $this->getDataGenerator()->create_user(); $course = $this->getDataGenerator()->create_course(array('enablecomment' => 1)); @@ -57,7 +58,10 @@ class mod_book_lib_testcase extends advanced_testcase { $cm = get_coursemodule_from_id('book', $book->cmid); $bookgenerator = $this->getDataGenerator()->get_plugin_generator('mod_book'); - $chapter1 = $bookgenerator->create_chapter(array('bookid' => $book->id, "pagenum" => 1)); + $chapter1 = $bookgenerator->create_chapter(array('bookid' => $book->id, "pagenum" => 1, + 'tags' => array('Cats', 'Dogs'))); + $tag = core_tag_tag::get_by_name(0, 'Cats'); + $chapter2 = $bookgenerator->create_chapter(array('bookid' => $book->id, "pagenum" => 2)); $subchapter = $bookgenerator->create_chapter(array('bookid' => $book->id, "pagenum" => 3, "subchapter" => 1)); $chapter3 = $bookgenerator->create_chapter(array('bookid' => $book->id, "pagenum" => 4, "hidden" => 1)); @@ -71,11 +75,24 @@ class mod_book_lib_testcase extends advanced_testcase { $this->assertEquals('structure', $contents[0]['filename']); $this->assertEquals('index.html', $contents[1]['filename']); $this->assertEquals('Chapter 1', $contents[1]['content']); + $this->assertCount(2, $contents[1]['tags']); + $this->assertEquals('Cats', $contents[1]['tags'][0]['rawname']); + $this->assertEquals($tag->id, $contents[1]['tags'][0]['id']); + $this->assertEquals('Dogs', $contents[1]['tags'][1]['rawname']); $this->assertEquals('index.html', $contents[2]['filename']); $this->assertEquals('Chapter 2', $contents[2]['content']); $this->assertEquals('index.html', $contents[3]['filename']); $this->assertEquals('Chapter 3', $contents[3]['content']); + // Now, test the function via the external API. + $contents = core_course_external::get_course_contents($course->id, array()); + $contents = external_api::clean_returnvalue(core_course_external::get_course_contents_returns(), $contents); + $this->assertEquals('book', $contents[0]['modules'][0]['modname']); + $this->assertEquals($cm->id, $contents[0]['modules'][0]['id']); + $this->assertCount(2, $contents[0]['modules'][0]['contents'][1]['tags']); + $this->assertEquals('Cats', $contents[0]['modules'][0]['contents'][1]['tags'][0]['rawname']); + $this->assertEquals('Dogs', $contents[0]['modules'][0]['contents'][1]['tags'][1]['rawname']); + // Test empty book. $emptybook = $this->getDataGenerator()->create_module('book', array('course' => $course->id)); $cm = get_coursemodule_from_id('book', $emptybook->cmid); diff --git a/mod/book/upgrade.txt b/mod/book/upgrade.txt index 53f766d96d4..1fb902803a8 100644 --- a/mod/book/upgrade.txt +++ b/mod/book/upgrade.txt @@ -1,5 +1,9 @@ This files describes API changes in the book code. +=== 3.7 === + +* book_export_contents() callback now returns tags information for every chapter. + === 3.1 === * The following functions, previously used (exclusively) by upgrade steps are not available diff --git a/mod/data/classes/external/record_exporter.php b/mod/data/classes/external/record_exporter.php index 7fecd44474a..428b44dab83 100644 --- a/mod/data/classes/external/record_exporter.php +++ b/mod/data/classes/external/record_exporter.php @@ -27,6 +27,7 @@ defined('MOODLE_INTERNAL') || die(); use core\external\exporter; use renderer_base; use core_user; +use core_tag\external\tag_item_exporter; /** * Class for exporting record data. @@ -102,6 +103,12 @@ class record_exporter extends exporter { 'multiple' => true, 'optional' => true, ), + 'tags' => array( + 'type' => tag_item_exporter::read_properties_definition(), + 'description' => 'Tags.', + 'multiple' => true, + 'optional' => true, + ), ); } @@ -128,6 +135,9 @@ class record_exporter extends exporter { } $values['contents'] = $contents; } + + $values['tags'] = \core_tag\external\util::get_item_tags('mod_data', 'data_records', $this->data->id); + return $values; } } diff --git a/mod/data/tests/externallib_test.php b/mod/data/tests/externallib_test.php index 51ad2d449fe..f34f0328b8d 100644 --- a/mod/data/tests/externallib_test.php +++ b/mod/data/tests/externallib_test.php @@ -412,9 +412,9 @@ class mod_data_external_testcase extends externallib_advanced_testcase { } $this->setUser($this->student1); - $entry11 = $generator->create_entry($this->database, $fieldcontents, $this->group1->id); + $entry11 = $generator->create_entry($this->database, $fieldcontents, $this->group1->id, ['Cats', 'Dogs']); $this->setUser($this->student2); - $entry12 = $generator->create_entry($this->database, $fieldcontents, $this->group1->id); + $entry12 = $generator->create_entry($this->database, $fieldcontents, $this->group1->id, ['Cats']); $entry13 = $generator->create_entry($this->database, $fieldcontents, $this->group1->id); // Entry not in group. $entry14 = $generator->create_entry($this->database, $fieldcontents, 0); @@ -447,10 +447,13 @@ class mod_data_external_testcase extends externallib_advanced_testcase { $this->assertCount(3, $result['entries']); $this->assertEquals(3, $result['totalcount']); $this->assertEquals($entry11, $result['entries'][0]['id']); + $this->assertCount(2, $result['entries'][0]['tags']); $this->assertEquals($this->student1->id, $result['entries'][0]['userid']); $this->assertEquals($this->group1->id, $result['entries'][0]['groupid']); $this->assertEquals($this->database->id, $result['entries'][0]['dataid']); $this->assertEquals($entry12, $result['entries'][1]['id']); + $this->assertCount(1, $result['entries'][1]['tags']); + $this->assertEquals('Cats', $result['entries'][1]['tags'][0]['rawname']); $this->assertEquals($this->student2->id, $result['entries'][1]['userid']); $this->assertEquals($this->group1->id, $result['entries'][1]['groupid']); $this->assertEquals($this->database->id, $result['entries'][1]['dataid']); diff --git a/mod/data/upgrade.txt b/mod/data/upgrade.txt index 28f7a44f24b..e776b4d0873 100644 --- a/mod/data/upgrade.txt +++ b/mod/data/upgrade.txt @@ -1,6 +1,9 @@ This files describes API changes in /mod/data - plugins, information provided here is intended especially for developers. +=== 3.7 === +* External functions get_entries, get_entry and search_entries now return an additional field "tags" containing the entry tags. + === 3.4 === * External function mod_data_external::search_entries() now returns the maxcount field: Total count of records that the user could see in the database (if all the search criterias were removed). diff --git a/mod/forum/externallib.php b/mod/forum/externallib.php index ba39260253d..a4d059172a1 100644 --- a/mod/forum/externallib.php +++ b/mod/forum/externallib.php @@ -419,6 +419,8 @@ class mod_forum_external extends external_api { if (!empty($messageinlinefiles)) { $post->messageinlinefiles = $messageinlinefiles; } + // Post tags. + $post->tags = \core_tag\external\util::get_item_tags('mod_forum', 'forum_posts', $post->id); $posts[] = $post; } @@ -465,6 +467,9 @@ class mod_forum_external extends external_api { 'userpictureurl' => new external_value(PARAM_URL, 'Post author picture.', VALUE_OPTIONAL), 'deleted' => new external_value(PARAM_BOOL, 'This post has been removed.'), 'isprivatereply' => new external_value(PARAM_BOOL, 'The post is a private reply'), + 'tags' => new external_multiple_structure( + \core_tag\external\tag_item_exporter::get_read_structure(), 'Tags', VALUE_OPTIONAL + ), ), 'post' ) ), diff --git a/mod/forum/tests/externallib_test.php b/mod/forum/tests/externallib_test.php index 8fa9dceb49e..be755662d89 100644 --- a/mod/forum/tests/externallib_test.php +++ b/mod/forum/tests/externallib_test.php @@ -260,6 +260,7 @@ class mod_forum_external_testcase extends externallib_advanced_testcase { $record->parent = $discussion1reply1->id; $record->userid = $user3->id; + $record->tags = array('Cats', 'Dogs'); $discussion1reply2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record); // Enrol the user in the course. @@ -311,7 +312,12 @@ class mod_forum_external_testcase extends externallib_advanced_testcase { 'userpictureurl' => '', 'deleted' => false, 'isprivatereply' => false, + 'tags' => \core_tag\external\util::get_item_tags('mod_forum', 'forum_posts', $discussion1reply2->id), ); + // Cast to expected. + $this->assertCount(2, $expectedposts['posts'][0]['tags']); + $expectedposts['posts'][0]['tags'][0]['isstandard'] = (bool) $expectedposts['posts'][0]['tags'][0]['isstandard']; + $expectedposts['posts'][0]['tags'][1]['isstandard'] = (bool) $expectedposts['posts'][0]['tags'][1]['isstandard']; $expectedposts['posts'][] = array( 'id' => $discussion1reply1->id, @@ -348,6 +354,7 @@ class mod_forum_external_testcase extends externallib_advanced_testcase { 'userpictureurl' => '', 'deleted' => false, 'isprivatereply' => false, + 'tags' => array(), ); // Test a discussion with two additional posts (total 3 posts). diff --git a/mod/forum/upgrade.txt b/mod/forum/upgrade.txt index 0eba55f6a05..ca4fa11fe17 100644 --- a/mod/forum/upgrade.txt +++ b/mod/forum/upgrade.txt @@ -9,6 +9,7 @@ information provided here is intended especially for developers. * The get_forum_discussion_posts web service has been deprecated in favour of get_discussion_posts. * The forum_count_replies function has been deprecated in favour of get_reply_count_for_post_id_in_discussion_id in the Post vault. + * External function get_forum_discussion_posts now returns an additional field "tags" returning the post tags. === 3.6 === diff --git a/mod/glossary/classes/external.php b/mod/glossary/classes/external.php index e900e04f1af..e60ef92a91f 100644 --- a/mod/glossary/classes/external.php +++ b/mod/glossary/classes/external.php @@ -101,6 +101,9 @@ class mod_glossary_external extends external_api { 'casesensitive' => new external_value(PARAM_BOOL, 'When true, the matching is case sensitive'), 'fullmatch' => new external_value(PARAM_BOOL, 'When true, the matching is done on full words only'), 'approved' => new external_value(PARAM_BOOL, 'Whether the entry was approved'), + 'tags' => new external_multiple_structure( + \core_tag\external\tag_item_exporter::get_read_structure(), 'Tags', VALUE_OPTIONAL + ), ); if ($includecat) { @@ -149,6 +152,8 @@ class mod_glossary_external extends external_api { if (!empty($definitioninlinefiles)) { $entry->definitioninlinefiles = $definitioninlinefiles; } + + $entry->tags = \core_tag\external\util::get_item_tags('mod_glossary', 'glossary_entries', $entry->id); } /** diff --git a/mod/glossary/tests/external_test.php b/mod/glossary/tests/external_test.php index 7320657a5e8..10e4e4432f0 100644 --- a/mod/glossary/tests/external_test.php +++ b/mod/glossary/tests/external_test.php @@ -203,7 +203,7 @@ class mod_glossary_external_testcase extends externallib_advanced_testcase { $ctx = context_module::instance($g1->cmid); $this->getDataGenerator()->enrol_user($u1->id, $c1->id); - $e1a = $gg->create_content($g1, array('approved' => 0, 'concept' => 'Bob', 'userid' => 2)); + $e1a = $gg->create_content($g1, array('approved' => 0, 'concept' => 'Bob', 'userid' => 2, 'tags' => array('Cats', 'Dogs'))); $e1b = $gg->create_content($g1, array('approved' => 1, 'concept' => 'Jane', 'userid' => 2)); $e1c = $gg->create_content($g1, array('approved' => 1, 'concept' => 'Alice', 'userid' => $u1->id)); $e1d = $gg->create_content($g1, array('approved' => 0, 'concept' => '0-day', 'userid' => $u1->id)); @@ -218,6 +218,8 @@ class mod_glossary_external_testcase extends externallib_advanced_testcase { $this->assertEquals(3, $return['count']); $this->assertEquals($e1c->id, $return['entries'][0]['id']); $this->assertEquals($e1a->id, $return['entries'][1]['id']); + $this->assertEquals('Cats', $return['entries'][1]['tags'][0]['rawname']); + $this->assertEquals('Dogs', $return['entries'][1]['tags'][1]['rawname']); $this->assertEquals($e1b->id, $return['entries'][2]['id']); // An admin user requesting all the entries. @@ -311,7 +313,7 @@ class mod_glossary_external_testcase extends externallib_advanced_testcase { $now = time(); $e1a = $gg->create_content($g1, array('approved' => 1, 'concept' => 'Bob', 'userid' => $u1->id, - 'timecreated' => 1, 'timemodified' => $now + 3600)); + 'timecreated' => 1, 'timemodified' => $now + 3600, 'tags' => array('Cats', 'Dogs'))); $e1b = $gg->create_content($g1, array('approved' => 1, 'concept' => 'Jane', 'userid' => $u1->id, 'timecreated' => $now + 3600, 'timemodified' => 1)); $e1c = $gg->create_content($g1, array('approved' => 1, 'concept' => 'Alice', 'userid' => $u1->id, @@ -328,6 +330,8 @@ class mod_glossary_external_testcase extends externallib_advanced_testcase { $this->assertCount(3, $return['entries']); $this->assertEquals(3, $return['count']); $this->assertEquals($e1a->id, $return['entries'][0]['id']); + $this->assertEquals('Cats', $return['entries'][0]['tags'][0]['rawname']); + $this->assertEquals('Dogs', $return['entries'][0]['tags'][1]['rawname']); $this->assertEquals($e1c->id, $return['entries'][1]['id']); $this->assertEquals($e1b->id, $return['entries'][2]['id']); @@ -425,7 +429,7 @@ class mod_glossary_external_testcase extends externallib_advanced_testcase { $u1 = $this->getDataGenerator()->create_user(); $ctx = context_module::instance($g1->cmid); - $e1a1 = $gg->create_content($g1, array('approved' => 1, 'userid' => $u1->id)); + $e1a1 = $gg->create_content($g1, array('approved' => 1, 'userid' => $u1->id, 'tags' => array('Cats', 'Dogs'))); $e1a2 = $gg->create_content($g1, array('approved' => 1, 'userid' => $u1->id)); $e1a3 = $gg->create_content($g1, array('approved' => 1, 'userid' => $u1->id)); $e1b1 = $gg->create_content($g1, array('approved' => 1, 'userid' => $u1->id)); @@ -448,6 +452,8 @@ class mod_glossary_external_testcase extends externallib_advanced_testcase { $this->assertCount(3, $return['entries']); $this->assertEquals(3, $return['count']); $this->assertEquals($e1a1->id, $return['entries'][0]['id']); + $this->assertEquals('Cats', $return['entries'][0]['tags'][0]['rawname']); + $this->assertEquals('Dogs', $return['entries'][0]['tags'][1]['rawname']); $this->assertEquals($e1a2->id, $return['entries'][1]['id']); $this->assertEquals($e1a3->id, $return['entries'][2]['id']); @@ -562,7 +568,7 @@ class mod_glossary_external_testcase extends externallib_advanced_testcase { $e1a2 = $gg->create_content($g1, array('approved' => 1, 'userid' => $u1->id)); $e1a3 = $gg->create_content($g1, array('approved' => 1, 'userid' => $u1->id)); $e1b1 = $gg->create_content($g1, array('approved' => 0, 'userid' => $u2->id)); - $e1b2 = $gg->create_content($g1, array('approved' => 1, 'userid' => $u2->id)); + $e1b2 = $gg->create_content($g1, array('approved' => 1, 'userid' => $u2->id, 'tags' => array('Cats', 'Dogs'))); $e1c1 = $gg->create_content($g1, array('approved' => 1, 'userid' => $u3->id)); $e1d1 = $gg->create_content($g1, array('approved' => 1, 'userid' => $u4->id)); $e2a = $gg->create_content($g2, array('approved' => 1, 'userid' => $u1->id)); @@ -575,6 +581,8 @@ class mod_glossary_external_testcase extends externallib_advanced_testcase { $this->assertCount(4, $return['entries']); $this->assertEquals(4, $return['count']); $this->assertEquals($e1b2->id, $return['entries'][0]['id']); + $this->assertEquals('Cats', $return['entries'][0]['tags'][0]['rawname']); + $this->assertEquals('Dogs', $return['entries'][0]['tags'][1]['rawname']); $this->assertEquals($e1a1->id, $return['entries'][1]['id']); $this->assertEquals($e1a2->id, $return['entries'][2]['id']); $this->assertEquals($e1a3->id, $return['entries'][3]['id']); @@ -780,7 +788,7 @@ class mod_glossary_external_testcase extends externallib_advanced_testcase { $e1 = $gg->create_content($g1, array('approved' => 1, 'concept' => 'House', 'timecreated' => time() + 3600)); $e2 = $gg->create_content($g1, array('approved' => 1, 'concept' => 'Mouse', 'timemodified' => 1)); - $e3 = $gg->create_content($g1, array('approved' => 1, 'concept' => 'Hero')); + $e3 = $gg->create_content($g1, array('approved' => 1, 'concept' => 'Hero', 'tags' => array('Cats', 'Dogs'))); $e4 = $gg->create_content($g1, array('approved' => 0, 'concept' => 'Toulouse')); $e5 = $gg->create_content($g1, array('approved' => 1, 'definition' => 'Heroes', 'concept' => 'Abcd')); $e6 = $gg->create_content($g1, array('approved' => 0, 'definition' => 'When used for Heroes')); @@ -798,6 +806,8 @@ class mod_glossary_external_testcase extends externallib_advanced_testcase { $this->assertCount(1, $return['entries']); $this->assertEquals(1, $return['count']); $this->assertEquals($e3->id, $return['entries'][0]['id']); + $this->assertEquals('Cats', $return['entries'][0]['tags'][0]['rawname']); + $this->assertEquals('Dogs', $return['entries'][0]['tags'][1]['rawname']); // Enabling full search. $query = 'hero'; @@ -893,7 +903,8 @@ class mod_glossary_external_testcase extends externallib_advanced_testcase { $this->setAdminUser(); - $e1 = $gg->create_content($g1, array('userid' => $u1->id, 'approved' => 1, 'concept' => 'cat')); + $e1 = $gg->create_content($g1, array('userid' => $u1->id, 'approved' => 1, 'concept' => 'cat', + 'tags' => array('Cats', 'Dogs'))); $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')); @@ -908,6 +919,8 @@ class mod_glossary_external_testcase extends externallib_advanced_testcase { $expected = array($e1->id, $e2->id); $actual = array($return['entries'][0]['id'], $return['entries'][1]['id']); $this->assertEquals($expected, $actual, '', 0.0, 10, true); + $this->assertEquals('Cats', $return['entries'][0]['tags'][0]['rawname']); + $this->assertEquals('Dogs', $return['entries'][0]['tags'][1]['rawname']); // Search alias. $return = mod_glossary_external::get_entries_by_term($g1->id, 'dog', 0, 20, array('includenotapproved' => false)); @@ -1063,7 +1076,7 @@ class mod_glossary_external_testcase extends externallib_advanced_testcase { $ctx = context_module::instance($g1->cmid); $this->getDataGenerator()->enrol_user($u1->id, $c1->id); - $e1 = $gg->create_content($g1, array('approved' => 1, 'userid' => $u1->id)); + $e1 = $gg->create_content($g1, array('approved' => 1, 'userid' => $u1->id, 'tags' => array('Cats', 'Dogs'))); // Add a fake inline image to the entry. $filename = 'shouldbeanimage.jpg'; $filerecordinline = array( @@ -1085,6 +1098,8 @@ class mod_glossary_external_testcase extends externallib_advanced_testcase { $return = mod_glossary_external::get_entry_by_id($e1->id); $return = external_api::clean_returnvalue(mod_glossary_external::get_entry_by_id_returns(), $return); $this->assertEquals($e1->id, $return['entry']['id']); + $this->assertEquals('Cats', $return['entry']['tags'][0]['rawname']); + $this->assertEquals('Dogs', $return['entry']['tags'][1]['rawname']); $this->assertEquals($filename, $return['entry']['definitioninlinefiles'][0]['filename']); $return = mod_glossary_external::get_entry_by_id($e2->id); diff --git a/mod/glossary/upgrade.txt b/mod/glossary/upgrade.txt index 6b298d2bbca..2ce13564b1a 100644 --- a/mod/glossary/upgrade.txt +++ b/mod/glossary/upgrade.txt @@ -1,6 +1,9 @@ This files describes API changes in /mod/glossary/*, information provided here is intended especially for developers. +=== 3.7 === +* External functions get_entries_by_* and get_entry now return an additional field "tags" containing the entry tags. + === 3.4 === * External functions returning entries now return an additional field "ratinginfo" containing the entry rating information. diff --git a/mod/wiki/classes/external.php b/mod/wiki/classes/external.php index 7195a66d9c9..34a91c3240a 100644 --- a/mod/wiki/classes/external.php +++ b/mod/wiki/classes/external.php @@ -492,7 +492,8 @@ class mod_wiki_external extends external_api { 'pageviews' => $page->pageviews, 'readonly' => $page->readonly, 'caneditpage' => $caneditpages, - 'firstpage' => $page->id == $firstpage->id + 'firstpage' => $page->id == $firstpage->id, + 'tags' => \core_tag\external\util::get_item_tags('mod_wiki', 'wiki_pages', $page->id), ); // Refresh page cached content if needed. @@ -555,6 +556,9 @@ class mod_wiki_external extends external_api { 'contentformat' => new external_format_value('cachedcontent', VALUE_OPTIONAL), 'contentsize' => new external_value(PARAM_INT, 'Size of page contents in bytes (doesn\'t include'. ' size of attached files).', VALUE_OPTIONAL), + 'tags' => new external_multiple_structure( + \core_tag\external\tag_item_exporter::get_read_structure(), 'Tags', VALUE_OPTIONAL + ), ), 'Pages' ) ), @@ -623,6 +627,7 @@ class mod_wiki_external extends external_api { $returnedpage['groupid'] = $subwiki->groupid; $returnedpage['userid'] = $subwiki->userid; $returnedpage['title'] = $page->title; + $returnedpage['tags'] = \core_tag\external\util::get_item_tags('mod_wiki', 'wiki_pages', $page->id); // Refresh page cached content if needed. if ($page->timerendered + WIKI_REFRESH_CACHE_TIME < time()) { @@ -668,6 +673,9 @@ class mod_wiki_external extends external_api { 'contentformat' => new external_format_value('cachedcontent', VALUE_OPTIONAL), 'caneditpage' => new external_value(PARAM_BOOL, 'True if user can edit the page.'), 'version' => new external_value(PARAM_INT, 'Latest version of the page.', VALUE_OPTIONAL), + 'tags' => new external_multiple_structure( + \core_tag\external\tag_item_exporter::get_read_structure(), 'Tags', VALUE_OPTIONAL + ), ), 'Page' ), 'warnings' => new external_warnings() diff --git a/mod/wiki/tests/externallib_test.php b/mod/wiki/tests/externallib_test.php index 7ecf190ba51..522cfbe44a7 100644 --- a/mod/wiki/tests/externallib_test.php +++ b/mod/wiki/tests/externallib_test.php @@ -69,7 +69,8 @@ class mod_wiki_external_testcase extends externallib_advanced_testcase { $this->getDataGenerator()->enrol_user($this->teacher->id, $this->course->id, $this->teacherrole->id, 'manual'); // Create first pages. - $this->firstpage = $this->getDataGenerator()->get_plugin_generator('mod_wiki')->create_first_page($this->wiki); + $this->firstpage = $this->getDataGenerator()->get_plugin_generator('mod_wiki')->create_first_page($this->wiki, + array('tags' => array('Cats', 'Dogs'))); } /** @@ -616,6 +617,10 @@ class mod_wiki_external_testcase extends externallib_advanced_testcase { $expectedfirstpage['caneditpage'] = true; // No groups and students have 'mod/wiki:editpage' capability. $expectedfirstpage['firstpage'] = true; $expectedfirstpage['contentformat'] = 1; + $expectedfirstpage['tags'] = \core_tag\external\util::get_item_tags('mod_wiki', 'wiki_pages', $this->firstpage->id); + // Cast to expected. + $expectedfirstpage['tags'][0]['isstandard'] = (bool) $expectedfirstpage['tags'][0]['isstandard']; + $expectedfirstpage['tags'][1]['isstandard'] = (bool) $expectedfirstpage['tags'][1]['isstandard']; $expectedpages[] = $expectedfirstpage; $result = mod_wiki_external::get_subwiki_pages($this->wiki->id); @@ -640,6 +645,7 @@ class mod_wiki_external_testcase extends externallib_advanced_testcase { $expectednewpage['caneditpage'] = true; // No groups and students have 'mod/wiki:editpage' capability. $expectednewpage['firstpage'] = false; $expectednewpage['contentformat'] = 1; + $expectednewpage['tags'] = array(); array_unshift($expectedpages, $expectednewpage); // Add page to the beginning since it orders by title by default. $result = mod_wiki_external::get_subwiki_pages($this->wiki->id); @@ -692,6 +698,7 @@ class mod_wiki_external_testcase extends externallib_advanced_testcase { $expectedteacherpage['caneditpage'] = true; $expectedteacherpage['firstpage'] = true; $expectedteacherpage['contentformat'] = 1; + $expectedteacherpage['tags'] = array(); $expectedpages = array($expectedteacherpage); $result = mod_wiki_external::get_subwiki_pages($indwiki->id, 0, $this->teacher->id); @@ -703,6 +710,7 @@ class mod_wiki_external_testcase extends externallib_advanced_testcase { $expectedstudentpage['caneditpage'] = true; $expectedstudentpage['firstpage'] = true; $expectedstudentpage['contentformat'] = 1; + $expectedstudentpage['tags'] = array(); $expectedpages = array($expectedstudentpage); $result = mod_wiki_external::get_subwiki_pages($indwiki->id, 0, $this->student->id); @@ -738,6 +746,7 @@ class mod_wiki_external_testcase extends externallib_advanced_testcase { $expectedpage['caneditpage'] = true; // User belongs to group and has 'mod/wiki:editpage' capability. $expectedpage['firstpage'] = true; $expectedpage['contentformat'] = 1; + $expectedpage['tags'] = array(); $expectedpages = array($expectedpage); $result = mod_wiki_external::get_subwiki_pages($this->wikisep->id, $this->group1->id); @@ -760,6 +769,7 @@ class mod_wiki_external_testcase extends externallib_advanced_testcase { $expectedpage['caneditpage'] = true; $expectedpage['firstpage'] = true; $expectedpage['contentformat'] = 1; + $expectedpage['tags'] = array(); $expectedpages = array($expectedpage); $result = mod_wiki_external::get_subwiki_pages($this->wikisep->id, 0); @@ -783,6 +793,7 @@ class mod_wiki_external_testcase extends externallib_advanced_testcase { $expectedpage['caneditpage'] = true; // User belongs to group and has 'mod/wiki:editpage' capability. $expectedpage['firstpage'] = true; $expectedpage['contentformat'] = 1; + $expectedpage['tags'] = array(); $expectedpages = array($expectedpage); $result = mod_wiki_external::get_subwiki_pages($this->wikivis->id, $this->group1->id); @@ -794,6 +805,7 @@ class mod_wiki_external_testcase extends externallib_advanced_testcase { $expectedpage['caneditpage'] = false; // User doesn't belong to group so he can't edit the page. $expectedpage['firstpage'] = true; $expectedpage['contentformat'] = 1; + $expectedpage['tags'] = array(); $expectedpages = array($expectedpage); $result = mod_wiki_external::get_subwiki_pages($this->wikivis->id, $this->group2->id); @@ -805,6 +817,7 @@ class mod_wiki_external_testcase extends externallib_advanced_testcase { $expectedpage['caneditpage'] = false; $expectedpage['firstpage'] = true; $expectedpage['contentformat'] = 1; + $expectedpage['tags'] = array(); $expectedpages = array($expectedpage); $result = mod_wiki_external::get_subwiki_pages($this->wikivis->id, 0); @@ -827,6 +840,7 @@ class mod_wiki_external_testcase extends externallib_advanced_testcase { $expectedpage['caneditpage'] = true; $expectedpage['firstpage'] = true; $expectedpage['contentformat'] = 1; + $expectedpage['tags'] = array(); $expectedpages = array($expectedpage); $result = mod_wiki_external::get_subwiki_pages($this->wikisepind->id, $this->group1->id, $this->student->id); @@ -850,6 +864,7 @@ class mod_wiki_external_testcase extends externallib_advanced_testcase { $expectedpage['caneditpage'] = false; $expectedpage['firstpage'] = true; $expectedpage['contentformat'] = 1; + $expectedpage['tags'] = array(); $expectedpages = array($expectedpage); $result = mod_wiki_external::get_subwiki_pages($this->wikisepind->id, $this->group1->id, $this->student2->id); @@ -872,6 +887,7 @@ class mod_wiki_external_testcase extends externallib_advanced_testcase { $expectedpage['caneditpage'] = true; $expectedpage['firstpage'] = true; $expectedpage['contentformat'] = 1; + $expectedpage['tags'] = array(); $expectedpages = array($expectedpage); $result = mod_wiki_external::get_subwiki_pages($this->wikivisind->id, $this->group1->id, $this->student->id); @@ -883,6 +899,7 @@ class mod_wiki_external_testcase extends externallib_advanced_testcase { $expectedpage['caneditpage'] = false; $expectedpage['firstpage'] = true; $expectedpage['contentformat'] = 1; + $expectedpage['tags'] = array(); $expectedpages = array($expectedpage); $result = mod_wiki_external::get_subwiki_pages($this->wikivisind->id, $this->group2->id, $this->teacher->id); @@ -894,6 +911,7 @@ class mod_wiki_external_testcase extends externallib_advanced_testcase { $expectedpage['caneditpage'] = false; $expectedpage['firstpage'] = true; $expectedpage['contentformat'] = 1; + $expectedpage['tags'] = array(); $expectedpages = array($expectedpage); $result = mod_wiki_external::get_subwiki_pages($this->wikivisind->id, 0, $this->teacher->id); @@ -987,8 +1005,12 @@ class mod_wiki_external_testcase extends externallib_advanced_testcase { 'cachedcontent' => $this->firstpage->cachedcontent, 'contentformat' => 1, 'caneditpage' => true, - 'version' => 1 + 'version' => 1, + 'tags' => \core_tag\external\util::get_item_tags('mod_wiki', 'wiki_pages', $this->firstpage->id), ); + // Cast to expected. + $expectedpage['tags'][0]['isstandard'] = (bool) $expectedpage['tags'][0]['isstandard']; + $expectedpage['tags'][1]['isstandard'] = (bool) $expectedpage['tags'][1]['isstandard']; $result = mod_wiki_external::get_page_contents($this->firstpage->id); $result = external_api::clean_returnvalue(mod_wiki_external::get_page_contents_returns(), $result); @@ -1000,6 +1022,7 @@ class mod_wiki_external_testcase extends externallib_advanced_testcase { $expectedpage['id'] = $newpage->id; $expectedpage['title'] = $newpage->title; $expectedpage['cachedcontent'] = $newpage->cachedcontent; + $expectedpage['tags'] = array(); $result = mod_wiki_external::get_page_contents($newpage->id); $result = external_api::clean_returnvalue(mod_wiki_external::get_page_contents_returns(), $result); @@ -1028,7 +1051,8 @@ class mod_wiki_external_testcase extends externallib_advanced_testcase { 'cachedcontent' => $this->fpsepg1indstu->cachedcontent, 'contentformat' => 1, 'caneditpage' => true, - 'version' => 1 + 'version' => 1, + 'tags' => array(), ); $result = mod_wiki_external::get_page_contents($this->fpsepg1indstu->id); diff --git a/mod/wiki/upgrade.txt b/mod/wiki/upgrade.txt index 0e4d30e182f..bc1bfd9cfa9 100644 --- a/mod/wiki/upgrade.txt +++ b/mod/wiki/upgrade.txt @@ -1,6 +1,9 @@ This files describes API changes in /mod/wiki/*, information provided here is intended especially for developers. +=== 3.7 === +* External functions get_subwiki_pages and get_page_contents now return an additional field "tags" returning the entry tags. + === 3.2 === * External functions that were returning file information now return the following file fields: filename, filepath, mimetype, filesize, timemodified and fileurl. diff --git a/tag/classes/external/tag_item_exporter.php b/tag/classes/external/tag_item_exporter.php new file mode 100644 index 00000000000..bbbe67e4b7a --- /dev/null +++ b/tag/classes/external/tag_item_exporter.php @@ -0,0 +1,92 @@ +. + +/** + * Contains related class for displaying information of a tag item. + * + * @package core_tag + * @copyright 2019 Juan Leyva + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core_tag\external; + +defined('MOODLE_INTERNAL') || die(); + +use core\external\exporter; + +/** + * Contains related class for displaying information of a tag item. + * + * @package core_tag + * @copyright 2019 Juan Leyva + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class tag_item_exporter extends exporter { + + /** + * Return the list of properties. + * + * @return array + */ + protected static function define_properties() { + return [ + 'id' => [ + 'type' => PARAM_INT, + 'description' => 'Tag id.', + ], + 'name' => [ + 'type' => PARAM_TAG, + 'description' => 'Tag name.', + ], + 'rawname' => [ + 'type' => PARAM_RAW, + 'description' => 'The raw, unnormalised name for the tag as entered by users.', + ], + 'isstandard' => [ + 'type' => PARAM_BOOL, + 'description' => 'Whether this tag is standard.', + 'default' => false, + ], + 'tagcollid' => [ + 'type' => PARAM_INT, + 'description' => 'Tag collection id.', + ], + 'taginstanceid' => [ + 'type' => PARAM_INT, + 'description' => 'Tag instance id.', + ], + 'taginstancecontextid' => [ + 'type' => PARAM_INT, + 'description' => 'Context the tag instance belongs to.', + ], + 'itemid' => [ + 'type' => PARAM_INT, + 'description' => 'Id of the record tagged.', + ], + 'ordering' => [ + 'type' => PARAM_INT, + 'description' => 'Tag ordering.', + ], + 'flag' => [ + 'type' => PARAM_INT, + 'description' => 'Whether the tag is flagged as inappropriate.', + 'default' => 0, + 'null' => NULL_ALLOWED, + ], + ]; + } +} diff --git a/tag/classes/external/util.php b/tag/classes/external/util.php new file mode 100644 index 00000000000..a4a1fed8506 --- /dev/null +++ b/tag/classes/external/util.php @@ -0,0 +1,70 @@ +. + +/** + * Tag external functions utility class. + * + * @package core_tag + * @copyright 2019 Juan Leyva + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core_tag\external; + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->libdir . '/externallib.php'); + +use core_tag\external\tag_item_exporter; +use core_tag_tag; + +/** + * Tag external functions utility class. + * + * @package core_tag + * @copyright 2019 Juan Leyva + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @since Moodle 3.7 + */ +class util { + + + /** + * Get the array of core_tag_tag objects for external functions associated with an item (instances). + * + * @param string $component component responsible for tagging. For BC it can be empty but in this case the + * query will be slow because DB index will not be used. + * @param string $itemtype type of the tagged item + * @param int $itemid + * @param int $standardonly wether to return only standard tags or any + * @param int $tiuserid tag instance user id, only needed for tag areas with user tagging + * @return array tags for external functions + */ + public static function get_item_tags($component, $itemtype, $itemid, $standardonly = core_tag_tag::BOTH_STANDARD_AND_NOT, + $tiuserid = 0) { + global $PAGE; + + $output = $PAGE->get_renderer('core'); + + $tagitems = core_tag_tag::get_item_tags($component, $itemtype, $itemid, $standardonly, $tiuserid); + $exportedtags = []; + foreach ($tagitems as $tagitem) { + $exporter = new tag_item_exporter($tagitem->to_object()); + $exportedtags[] = (array) $exporter->export($output); + } + return $exportedtags; + } +} From f12188993393219c7617aaad4782d8c2f9404d71 Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Wed, 20 Feb 2019 15:23:44 +0100 Subject: [PATCH 3/6] MDL-64656 core_tag: New WebService core_tag_get_tagindex_per_area --- lib/db/services.php | 7 +++ tag/classes/external.php | 118 ++++++++++++++++++++++++++++++++++++ tag/tests/external_test.php | 53 ++++++++++++++++ 3 files changed, 178 insertions(+) diff --git a/lib/db/services.php b/lib/db/services.php index 30fdec8c369..0ea1fd2cf43 100644 --- a/lib/db/services.php +++ b/lib/db/services.php @@ -1514,6 +1514,13 @@ $functions = array( 'type' => 'write', 'ajax' => true, ), + 'core_tag_get_tagindex_per_area' => array( + 'classname' => 'core_tag_external', + 'methodname' => 'get_tagindex_per_area', + 'description' => 'Gets tag index page per different areas.', + 'type' => 'read', + 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), + ), 'core_update_inplace_editable' => array( 'classname' => 'core_external', 'methodname' => 'update_inplace_editable', diff --git a/tag/classes/external.php b/tag/classes/external.php index 54fa2045bb3..86e82122490 100644 --- a/tag/classes/external.php +++ b/tag/classes/external.php @@ -335,4 +335,122 @@ class core_tag_external extends external_api { ), 'tag index' ); } + + + /** + * Parameters for function get_tagindex_per_area() + * + * @return external_function_parameters + * @since Moodle 3.7 + */ + public static function get_tagindex_per_area_parameters() { + return new external_function_parameters( + array( + 'tagindex' => new external_single_structure(array( + 'id' => new external_value(PARAM_INT, 'tag id', VALUE_OPTIONAL, 0), + 'tag' => new external_value(PARAM_TAG, 'tag name', VALUE_OPTIONAL, ''), + 'tc' => new external_value(PARAM_INT, 'tag collection id', VALUE_OPTIONAL, 0), + 'ta' => new external_value(PARAM_INT, 'tag area id', VALUE_OPTIONAL, 0), + 'excl' => new external_value(PARAM_BOOL, 'exlusive mode for this tag area', VALUE_OPTIONAL, 0), + 'from' => new external_value(PARAM_INT, 'context id where the link was displayed', VALUE_OPTIONAL, 0), + 'ctx' => new external_value(PARAM_INT, 'context id where to search for items', VALUE_OPTIONAL, 0), + 'rec' => new external_value(PARAM_INT, 'search in the context recursive', VALUE_OPTIONAL, 1), + 'page' => new external_value(PARAM_INT, 'page number (0-based)', VALUE_OPTIONAL, 0), + ), 'parameters') + ) + ); + } + + /** + * Returns the tag index per multiple areas if requested. + * + * @param array $params Tag index required information. + * @throws moodle_exception + * @since Moodle 3.7 + */ + public static function get_tagindex_per_area($params) { + global $CFG, $PAGE; + // Validate and normalize parameters. + $tagindex = self::validate_parameters( + self::get_tagindex_per_area_parameters(), array('tagindex' => $params)); + $params = $tagindex['tagindex'] + array( // Force defaults. + 'id' => 0, + 'tag' => '', + 'tc' => 0, + 'ta' => 0, + 'excl' => 0, + 'from' => 0, + 'ctx' => 0, + 'rec' => 1, + 'page' => 0, + ); + + if (empty($CFG->usetags)) { + throw new moodle_exception('tagsaredisabled', 'tag'); + } + + if (!empty($params['tag'])) { + if (empty($params['tc'])) { + // Tag name specified but tag collection was not. Try to guess it. + $tags = core_tag_tag::guess_by_name($params['tag'], '*'); + if (count($tags) > 1) { + // It is in more that one collection, do not display. + throw new moodle_exception('Tag is in more that one collection, please indicate one.'); + } else if (count($tags) == 1) { + $tag = reset($tags); + } + } else { + if (!$tag = core_tag_tag::get_by_name($params['tc'], $params['tag'], '*')) { + // Not found in collection. + throw new moodle_exception('notagsfound', 'tag'); + } + } + } else if (!empty($params['id'])) { + $tag = core_tag_tag::get($params['id'], '*'); + } + + if (empty($tag)) { + throw new moodle_exception('notagsfound', 'tag'); + } + + // Login to the course / module if applicable. + $context = !empty($params['ctx']) ? context::instance_by_id($params['ctx']) : context_system::instance(); + self::validate_context($context); + + $tag = core_tag_tag::get_by_name($params['tc'], $tag->name, '*', MUST_EXIST); + $tagareas = core_tag_collection::get_areas($params['tc']); + $tagareaid = $params['ta']; + + $exclusivemode = 0; + // Find all areas in this collection and their items tagged with this tag. + if ($tagareaid) { + $tagareas = array($tagareas[$tagareaid]); + } + if (!$tagareaid && count($tagareas) == 1) { + // Automatically set "exclusive" mode for tag collection with one tag area only. + $params['excl'] = 1; + } + + $renderer = $PAGE->get_renderer('core'); + $result = array(); + foreach ($tagareas as $ta) { + $tagindex = $tag->get_tag_index($ta, $params['excl'], $params['from'], $params['ctx'], $params['rec'], $params['page']); + if (!empty($tagindex->hascontent)) { + $result[] = $tagindex->export_for_template($renderer); + } + } + return $result; + } + + /** + * Return structure for get_tagindex_per_area + * + * @return external_description + * @since Moodle 3.7 + */ + public static function get_tagindex_per_area_returns() { + return new external_multiple_structure( + self::get_tagindex_returns() + ); + } } diff --git a/tag/tests/external_test.php b/tag/tests/external_test.php index c451d6987ec..cdf8a6293cb 100644 --- a/tag/tests/external_test.php +++ b/tag/tests/external_test.php @@ -181,4 +181,57 @@ class core_tag_external_testcase extends externallib_advanced_testcase { $this->assertEquals('Rename me again', $res['value']); $this->assertEquals('Rename me again', $DB->get_field('tag', 'rawname', array('id' => $tag->id))); } + + /** + * Test get_tagindex_per_area. + */ + public function test_get_tagindex_per_area() { + global $USER; + $this->resetAfterTest(true); + + // Create tags for two user profiles and one course. + $this->setAdminUser(); + $context = context_user::instance($USER->id); + core_tag_tag::set_item_tags('core', 'user', $USER->id, $context, array('test')); + + $this->setUser($this->getDataGenerator()->create_user()); + $context = context_user::instance($USER->id); + core_tag_tag::set_item_tags('core', 'user', $USER->id, $context, array('test')); + + $course = $this->getDataGenerator()->create_course(); + $context = context_course::instance($course->id); + core_tag_tag::set_item_tags('core', 'course', $course->id, $context, array('test')); + + $tag = core_tag_tag::get_by_name(0, 'test'); + + // First, search by id. + $result = core_tag_external::get_tagindex_per_area(array('id' => $tag->id)); + $result = external_api::clean_returnvalue(core_tag_external::get_tagindex_per_area_returns(), $result); + $this->assertCount(2, $result); // Two different areas: course and user. + $this->assertEquals($tag->id, $result[0]['tagid']); + $this->assertEquals('course', $result[0]['itemtype']); + $this->assertEquals($tag->id, $result[1]['tagid']); + $this->assertEquals('user', $result[1]['itemtype']); + + // Now, search by name. + $result = core_tag_external::get_tagindex_per_area(array('tag' => 'test')); + $result = external_api::clean_returnvalue(core_tag_external::get_tagindex_per_area_returns(), $result); + $this->assertCount(2, $result); // Two different areas: course and user. + $this->assertEquals($tag->id, $result[0]['tagid']); + $this->assertEquals('course', $result[0]['itemtype']); + $this->assertEquals($tag->id, $result[1]['tagid']); + $this->assertEquals('user', $result[1]['itemtype']); + + // Filter by tag area. + $result = core_tag_external::get_tagindex_per_area(array('tag' => 'test', 'ta' => $result[0]['ta'])); + $result = external_api::clean_returnvalue(core_tag_external::get_tagindex_per_area_returns(), $result); + $this->assertCount(1, $result); // Just the given area. + $this->assertEquals($tag->id, $result[0]['tagid']); + $this->assertEquals('course', $result[0]['itemtype']); + + // Now, search by tag collection (use default). + $result = core_tag_external::get_tagindex_per_area(array('id' => $tag->id, 'tc' => 1)); + $result = external_api::clean_returnvalue(core_tag_external::get_tagindex_per_area_returns(), $result); + $this->assertCount(2, $result); // Two different areas: course and user. + } } From 07a48837961274a9d088e2ea2799f54b4c128d93 Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Wed, 27 Feb 2019 12:35:15 +0100 Subject: [PATCH 4/6] MDL-64656 core_tag: New WebService core_tag_get_tag_areas --- lib/db/services.php | 7 ++ tag/classes/external.php | 67 ++++++++++++ tag/classes/external/tag_area_exporter.php | 116 +++++++++++++++++++++ tag/tests/external_test.php | 18 ++++ 4 files changed, 208 insertions(+) create mode 100644 tag/classes/external/tag_area_exporter.php diff --git a/lib/db/services.php b/lib/db/services.php index 0ea1fd2cf43..0681e836db8 100644 --- a/lib/db/services.php +++ b/lib/db/services.php @@ -1521,6 +1521,13 @@ $functions = array( 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), + 'core_tag_get_tag_areas' => array( + 'classname' => 'core_tag_external', + 'methodname' => 'get_tag_areas', + 'description' => 'Retrieves existing tag areas.', + 'type' => 'read', + 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), + ), 'core_update_inplace_editable' => array( 'classname' => 'core_external', 'methodname' => 'update_inplace_editable', diff --git a/tag/classes/external.php b/tag/classes/external.php index 86e82122490..cfe0008b434 100644 --- a/tag/classes/external.php +++ b/tag/classes/external.php @@ -453,4 +453,71 @@ class core_tag_external extends external_api { self::get_tagindex_returns() ); } + + /** + * Returns description of get_tag_areas() parameters. + * + * @return external_function_parameters + * @since Moodle 3.7 + */ + public static function get_tag_areas_parameters() { + return new external_function_parameters(array()); + } + + /** + * Retrieves existing tag areas. + * + * @return array an array of warnings and objects containing the plugin information + * @throws moodle_exception + * @since Moodle 3.7 + */ + public static function get_tag_areas() { + global $CFG, $PAGE; + + if (empty($CFG->usetags)) { + throw new moodle_exception('tagsaredisabled', 'tag'); + } + + $context = context_system::instance(); + self::validate_context($context); + $PAGE->set_context($context); // Needed by internal APIs. + $output = $PAGE->get_renderer('core'); + + $areas = core_tag_area::get_areas(); + $exportedareas = array(); + foreach ($areas as $itemtype => $component) { + foreach ($component as $area) { + // Move optional fields not part of the DB table to otherdata. + $locked = false; + if (isset($area->locked)) { + $locked = $area->locked; + unset($area->locked); + } + $exporter = new \core_tag\external\tag_area_exporter($area, array('locked' => $locked)); + $exportedareas[] = $exporter->export($output); + } + } + + return array( + 'areas' => $exportedareas, + 'warnings' => array(), + ); + } + + /** + * Returns description of get_tag_areas() result value. + * + * @return external_description + * @since Moodle 3.7 + */ + public static function get_tag_areas_returns() { + return new external_single_structure( + array( + 'areas' => new external_multiple_structure( + \core_tag\external\tag_area_exporter::get_read_structure() + ), + 'warnings' => new external_warnings(), + ) + ); + } } diff --git a/tag/classes/external/tag_area_exporter.php b/tag/classes/external/tag_area_exporter.php new file mode 100644 index 00000000000..20ec1e38ef1 --- /dev/null +++ b/tag/classes/external/tag_area_exporter.php @@ -0,0 +1,116 @@ +. + +/** + * Contains related class for displaying information of a tag area. + * + * @package core_tag + * @copyright 2019 Juan Leyva + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core_tag\external; + +defined('MOODLE_INTERNAL') || die(); + +use core\external\exporter; +use renderer_base; + +/** + * Contains related class for displaying information of a tag area. + * + * @package core_tag + * @copyright 2019 Juan Leyva + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class tag_area_exporter extends exporter { + + /** + * Return the list of properties. + * + * @return array + */ + protected static function define_properties() { + return [ + 'id' => [ + 'type' => PARAM_INT, + 'description' => 'Area id.', + ], + 'component' => [ + 'type' => PARAM_COMPONENT, + 'description' => 'Component the area is related to.', + ], + 'itemtype' => [ + 'type' => PARAM_ALPHANUMEXT, + 'description' => 'Type of item in the component.', + ], + 'enabled' => [ + 'type' => PARAM_BOOL, + 'description' => 'Whether this area is enabled.', + 'default' => true, + ], + 'tagcollid' => [ + 'type' => PARAM_INT, + 'description' => 'The tag collection this are belongs to.', + ], + 'callback' => [ + 'type' => PARAM_ALPHANUMEXT, + 'description' => 'Component callback for processing tags.', + 'null' => NULL_ALLOWED, + ], + 'callbackfile' => [ + 'type' => PARAM_RAW, + 'description' => 'Component callback file.', + 'null' => NULL_ALLOWED, + ], + 'showstandard' => [ + 'type' => PARAM_INT, + 'description' => 'Return whether to display only standard, only non-standard or both tags.', + 'default' => 0, + ], + 'multiplecontexts' => [ + 'type' => PARAM_BOOL, + 'description' => 'Whether the tag area allows tag instances to be created in multiple contexts. ', + 'default' => false, + ], + ]; + } + + protected static function define_related() { + return array( + 'locked' => 'bool?' + ); + } + + protected static function define_other_properties() { + return array( + 'locked' => [ + 'type' => PARAM_BOOL, + 'description' => 'Whether the area is locked.', + 'null' => NULL_ALLOWED, + 'default' => false, + 'optional' => true, + ] + ); + } + + protected function get_other_values(renderer_base $output) { + + $values['locked'] = $this->related['locked'] ? true : false; + + return $values; + } +} diff --git a/tag/tests/external_test.php b/tag/tests/external_test.php index cdf8a6293cb..0645541d7eb 100644 --- a/tag/tests/external_test.php +++ b/tag/tests/external_test.php @@ -234,4 +234,22 @@ class core_tag_external_testcase extends externallib_advanced_testcase { $result = external_api::clean_returnvalue(core_tag_external::get_tagindex_per_area_returns(), $result); $this->assertCount(2, $result); // Two different areas: course and user. } + + /** + * Test get_tag_areas. + */ + public function test_get_tag_areas() { + global $DB; + $this->resetAfterTest(true); + + $this->setAdminUser(); + $result = core_tag_external::get_tag_areas(); + $result = external_api::clean_returnvalue(core_tag_external::get_tag_areas_returns(), $result); + $areas = $DB->get_records('tag_area'); + $this->assertCount(count($areas), $result['areas']); + foreach ($result['areas'] as $area) { + $this->assertEquals($areas[$area['id']]->component, $area['component']); + $this->assertEquals($areas[$area['id']]->itemtype, $area['itemtype']); + } + } } From 87bdad57c40bc0f1a209e3f29c29f9f87989908d Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Wed, 27 Feb 2019 13:33:13 +0100 Subject: [PATCH 5/6] MDL-64656 core_tag: New WebService core_tag_get_tag_collections --- lib/db/services.php | 7 ++ tag/classes/external.php | 59 +++++++++++++ .../external/tag_collection_exporter.php | 82 +++++++++++++++++++ tag/tests/external_test.php | 23 ++++++ 4 files changed, 171 insertions(+) create mode 100644 tag/classes/external/tag_collection_exporter.php diff --git a/lib/db/services.php b/lib/db/services.php index 0681e836db8..4edebbcbf90 100644 --- a/lib/db/services.php +++ b/lib/db/services.php @@ -1528,6 +1528,13 @@ $functions = array( 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), + 'core_tag_get_tag_collections' => array( + 'classname' => 'core_tag_external', + 'methodname' => 'get_tag_collections', + 'description' => 'Retrieves existing tag collections.', + 'type' => 'read', + 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), + ), 'core_update_inplace_editable' => array( 'classname' => 'core_external', 'methodname' => 'update_inplace_editable', diff --git a/tag/classes/external.php b/tag/classes/external.php index cfe0008b434..40022a3eb72 100644 --- a/tag/classes/external.php +++ b/tag/classes/external.php @@ -520,4 +520,63 @@ class core_tag_external extends external_api { ) ); } + + /** + * Returns description of get_tag_collections() parameters. + * + * @return external_function_parameters + * @since Moodle 3.7 + */ + public static function get_tag_collections_parameters() { + return new external_function_parameters(array()); + } + + /** + * Retrieves existing tag collections. + * + * @return array an array of warnings and tag collections + * @throws moodle_exception + * @since Moodle 3.7 + */ + public static function get_tag_collections() { + global $CFG, $PAGE; + + if (empty($CFG->usetags)) { + throw new moodle_exception('tagsaredisabled', 'tag'); + } + + $context = context_system::instance(); + self::validate_context($context); + $PAGE->set_context($context); // Needed by internal APIs. + $output = $PAGE->get_renderer('core'); + + $collections = core_tag_collection::get_collections(); + $exportedcollections = array(); + foreach ($collections as $collection) { + $exporter = new \core_tag\external\tag_collection_exporter($collection); + $exportedcollections[] = $exporter->export($output); + } + + return array( + 'collections' => $exportedcollections, + 'warnings' => array(), + ); + } + + /** + * Returns description of get_tag_collections() result value. + * + * @return external_description + * @since Moodle 3.7 + */ + public static function get_tag_collections_returns() { + return new external_single_structure( + array( + 'collections' => new external_multiple_structure( + \core_tag\external\tag_collection_exporter::get_read_structure() + ), + 'warnings' => new external_warnings(), + ) + ); + } } diff --git a/tag/classes/external/tag_collection_exporter.php b/tag/classes/external/tag_collection_exporter.php new file mode 100644 index 00000000000..f49e70dc18e --- /dev/null +++ b/tag/classes/external/tag_collection_exporter.php @@ -0,0 +1,82 @@ +. + +/** + * Contains related class for displaying information of a tag collection. + * + * @package core_tag + * @copyright 2019 Juan Leyva + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core_tag\external; + +defined('MOODLE_INTERNAL') || die(); + +use core\external\exporter; + +/** + * Contains related class for displaying information of a tag collection. + * + * @package core_tag + * @copyright 2019 Juan Leyva + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class tag_collection_exporter extends exporter { + + /** + * Return the list of properties. + * + * @return array + */ + protected static function define_properties() { + return [ + 'id' => [ + 'type' => PARAM_INT, + 'description' => 'Collection id.', + ], + 'name' => [ + 'type' => PARAM_NOTAGS, + 'description' => 'Collection name.', + 'null' => NULL_ALLOWED, + ], + 'isdefault' => [ + 'type' => PARAM_BOOL, + 'description' => 'Whether is the default collection.', + 'default' => false, + ], + 'component' => [ + 'type' => PARAM_COMPONENT, + 'description' => 'Component the collection is related to.', + 'null' => NULL_ALLOWED, + ], + 'sortorder' => [ + 'type' => PARAM_INT, + 'description' => 'Collection ordering in the list.', + ], + 'searchable' => [ + 'type' => PARAM_BOOL, + 'description' => 'Whether the tag collection is searchable.', + 'default' => true, + ], + 'customurl' => [ + 'type' => PARAM_NOTAGS, + 'description' => 'Custom URL for the tag page instead of /tag/index.php.', + 'null' => NULL_ALLOWED, + ], + ]; + } +} diff --git a/tag/tests/external_test.php b/tag/tests/external_test.php index 0645541d7eb..8f557597bde 100644 --- a/tag/tests/external_test.php +++ b/tag/tests/external_test.php @@ -252,4 +252,27 @@ class core_tag_external_testcase extends externallib_advanced_testcase { $this->assertEquals($areas[$area['id']]->itemtype, $area['itemtype']); } } + + /** + * Test get_tag_collections. + */ + public function test_get_tag_collections() { + global $DB; + $this->resetAfterTest(true); + + // Create new tag collection. + $data = (object) array('name' => 'new tag coll'); + core_tag_collection::create($data); + + $this->setAdminUser(); + $result = core_tag_external::get_tag_collections(); + $result = external_api::clean_returnvalue(core_tag_external::get_tag_collections_returns(), $result); + + $collections = $DB->get_records('tag_coll'); + $this->assertCount(count($collections), $result['collections']); + foreach ($result['collections'] as $collection) { + $this->assertEquals($collections[$collection['id']]->component, $collection['component']); + $this->assertEquals($collections[$collection['id']]->name, $collection['name']); + } + } } From 86fd9277c2b5dbf2bf9cedcc49cd7f772dc72d3a Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Wed, 27 Feb 2019 16:40:16 +0100 Subject: [PATCH 6/6] MDL-64656 core_tag: New WebService core_tag_get_tag_cloud --- lib/db/services.php | 7 +++ tag/classes/external.php | 103 ++++++++++++++++++++++++++++++++++++ tag/tests/external_test.php | 99 ++++++++++++++++++++++++++++++++++ 3 files changed, 209 insertions(+) diff --git a/lib/db/services.php b/lib/db/services.php index 4edebbcbf90..7b2b093acf6 100644 --- a/lib/db/services.php +++ b/lib/db/services.php @@ -1535,6 +1535,13 @@ $functions = array( 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), + 'core_tag_get_tag_cloud' => array( + 'classname' => 'core_tag_external', + 'methodname' => 'get_tag_cloud', + 'description' => 'Retrieves a tag cloud for the given collection and/or query search.', + 'type' => 'read', + 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), + ), 'core_update_inplace_editable' => array( 'classname' => 'core_external', 'methodname' => 'update_inplace_editable', diff --git a/tag/classes/external.php b/tag/classes/external.php index 40022a3eb72..6214f86ac02 100644 --- a/tag/classes/external.php +++ b/tag/classes/external.php @@ -579,4 +579,107 @@ class core_tag_external extends external_api { ) ); } + + /** + * Returns description of get_tag_cloud() parameters. + * + * @return external_function_parameters + * @since Moodle 3.7 + */ + public static function get_tag_cloud_parameters() { + return new external_function_parameters( + array( + 'tagcollid' => new external_value(PARAM_INT, 'Tag collection id.', VALUE_DEFAULT, 0), + 'isstandard' => new external_value(PARAM_BOOL, 'Whether to return only standard tags.', VALUE_DEFAULT, false), + 'limit' => new external_value(PARAM_INT, 'Maximum number of tags to retrieve.', VALUE_DEFAULT, 150), + 'sort' => new external_value(PARAM_ALPHA, 'Sort order for display + (id, name, rawname, count, flag, isstandard, tagcollid).', VALUE_DEFAULT, 'name'), + 'search' => new external_value(PARAM_RAW, 'Search string.', VALUE_DEFAULT, ''), + 'fromctx' => new external_value(PARAM_INT, 'Context id where this tag cloud is displayed.', VALUE_DEFAULT, 0), + 'ctx' => new external_value(PARAM_INT, 'Only retrieve tag instances in this context.', VALUE_DEFAULT, 0), + 'rec' => new external_value(PARAM_INT, 'Retrieve tag instances in the $ctx context and it\'s children.', + VALUE_DEFAULT, 1), + ) + ); + } + + /** + * Retrieves a tag cloud for display. + * + * @param int $tagcollid tag collection id + * @param bool $isstandard return only standard tags + * @param int $limit maximum number of tags to retrieve, tags are sorted by the instance count + * descending here regardless of $sort parameter + * @param string $sort sort order for display, default 'name' - tags will be sorted after they are retrieved + * @param string $search search string + * @param int $fromctx context id where this tag cloud is displayed + * @param int $ctx only retrieve tag instances in this context + * @param int $rec retrieve tag instances in the $ctx context and it's children (default 1) + * @return array an array of warnings and tag cloud information and items + * @throws moodle_exception + * @since Moodle 3.7 + */ + public static function get_tag_cloud($tagcollid = 0, $isstandard = false, $limit = 150, $sort = 'name', + $search = '', $fromctx = 0, $ctx = 0, $rec = 1) { + global $CFG, $PAGE; + + $params = self::validate_parameters(self::get_tag_cloud_parameters(), + array( + 'tagcollid' => $tagcollid, + 'isstandard' => $isstandard, + 'limit' => $limit, + 'sort' => $sort, + 'search' => $search, + 'fromctx' => $fromctx, + 'ctx' => $ctx, + 'rec' => $rec, + ) + ); + + if (empty($CFG->usetags)) { + throw new moodle_exception('tagsaredisabled', 'tag'); + } + + $context = context_system::instance(); + self::validate_context($context); + $PAGE->set_context($context); // Needed by internal APIs. + $output = $PAGE->get_renderer('core'); + + $tagcloud = core_tag_collection::get_tag_cloud($params['tagcollid'], $params['isstandard'], $params['limit'], + $params['sort'], $params['search'], $params['fromctx'], $params['ctx'], $params['rec']); + + $result = $tagcloud->export_for_template($output); + $result->warnings = array(); + + return (array) $result; + } + + /** + * Returns description of get_tag_cloud() result value. + * + * @return external_description + * @since Moodle 3.7 + */ + public static function get_tag_cloud_returns() { + return new external_single_structure( + array( + 'tags' => new external_multiple_structure( + new external_single_structure( + array( + 'name' => new external_value(PARAM_TAG, 'Tag name.'), + 'viewurl' => new external_value(PARAM_RAW, 'URL to view the tag index.'), + 'flag' => new external_value(PARAM_BOOL, 'Whether the tag is flagged as inappropriate.', + VALUE_OPTIONAL), + 'isstandard' => new external_value(PARAM_BOOL, 'Whether is a standard tag or not.', VALUE_OPTIONAL), + 'count' => new external_value(PARAM_INT, 'Number of tag instances.', VALUE_OPTIONAL), + 'size' => new external_value(PARAM_INT, 'Proportional size to display the tag.', VALUE_OPTIONAL), + ), 'Tags.' + ) + ), + 'tagscount' => new external_value(PARAM_INT, 'Number of tags returned.'), + 'totalcount' => new external_value(PARAM_INT, 'Total count of tags.'), + 'warnings' => new external_warnings(), + ) + ); + } } diff --git a/tag/tests/external_test.php b/tag/tests/external_test.php index 8f557597bde..b6813a70125 100644 --- a/tag/tests/external_test.php +++ b/tag/tests/external_test.php @@ -275,4 +275,103 @@ class core_tag_external_testcase extends externallib_advanced_testcase { $this->assertEquals($collections[$collection['id']]->name, $collection['name']); } } + + /** + * Test get_tag_cloud. + */ + public function test_get_tag_cloud() { + global $USER, $DB; + $this->resetAfterTest(true); + + // Create tags for two user profiles, a post and one course. + $this->setAdminUser(); + $context = context_user::instance($USER->id); + core_tag_tag::set_item_tags('core', 'user', $USER->id, $context, array('Cats', 'Dogs')); + + $this->setUser($this->getDataGenerator()->create_user()); + $context = context_user::instance($USER->id); + core_tag_tag::set_item_tags('core', 'user', $USER->id, $context, array('Mice')); + + $course = $this->getDataGenerator()->create_course(); + $coursecontext = context_course::instance($course->id); + core_tag_tag::set_item_tags('core', 'course', $course->id, $coursecontext, array('Cats')); + + $post = new stdClass(); + $post->userid = $USER->id; + $post->content = 'test post content text'; + $post->id = $DB->insert_record('post', $post); + $context = context_system::instance(); + core_tag_tag::set_item_tags('core', 'post', $post->id, $context, array('Horses', 'Cats')); + + // First, retrieve complete cloud. + $result = core_tag_external::get_tag_cloud(); + $result = external_api::clean_returnvalue(core_tag_external::get_tag_cloud_returns(), $result); + $this->assertCount(4, $result['tags']); // Four different tags: Cats, Dogs, Mice, Horses. + $this->assertEquals(4, $result['tagscount']); + $this->assertEquals(4, $result['totalcount']); + + foreach ($result['tags'] as $tag) { + if ($tag['name'] == 'Cats') { + $this->assertEquals(3, $tag['count']); + } else { + $this->assertEquals(1, $tag['count']); + } + } + + // Test filter by collection, pagination and sorting. + $defaultcoll = core_tag_collection::get_default(); + $result = core_tag_external::get_tag_cloud($defaultcoll, false, 2, 'count'); + $result = external_api::clean_returnvalue(core_tag_external::get_tag_cloud_returns(), $result); + $this->assertCount(2, $result['tags']); // Only two tags. + $this->assertEquals(2, $result['tagscount']); + $this->assertEquals(4, $result['totalcount']); + $this->assertEquals('Dogs', $result['tags'][0]['name']); // Lower count first. + + // Test search. + $result = core_tag_external::get_tag_cloud(0, false, 150, 'name', 'Mice'); + $result = external_api::clean_returnvalue(core_tag_external::get_tag_cloud_returns(), $result); + $this->assertCount(1, $result['tags']); // Only the searched tags. + $this->assertEquals(1, $result['tagscount']); + $this->assertEquals(1, $result['totalcount']); // When searching, the total is always for the search. + $this->assertEquals('Mice', $result['tags'][0]['name']); + + $result = core_tag_external::get_tag_cloud(0, false, 150, 'name', 'Conejo'); + $result = external_api::clean_returnvalue(core_tag_external::get_tag_cloud_returns(), $result); + $this->assertCount(0, $result['tags']); // Nothing found. + $this->assertEquals(0, $result['tagscount']); + $this->assertEquals(0, $result['totalcount']); // When searching, the total is always for the search. + + // Test standard filtering. + $micetag = core_tag_tag::get_by_name($defaultcoll, 'Mice', '*'); + $micetag->update(array('isstandard' => 1)); + + $result = core_tag_external::get_tag_cloud(0, true); + $result = external_api::clean_returnvalue(core_tag_external::get_tag_cloud_returns(), $result); + $this->assertCount(1, $result['tags']); + $this->assertEquals(1, $result['tagscount']); + $this->assertEquals(1, $result['totalcount']); // When searching, the total is always for the search. + $this->assertEquals('Mice', $result['tags'][0]['name']); + + // Test course context filtering. + $result = core_tag_external::get_tag_cloud(0, false, 150, 'name', '', 0, $coursecontext->id); + $result = external_api::clean_returnvalue(core_tag_external::get_tag_cloud_returns(), $result); + $this->assertCount(1, $result['tags']); + $this->assertEquals(1, $result['tagscount']); + $this->assertEquals(1, $result['totalcount']); // When searching, the total is always for the search. + $this->assertEquals('Cats', $result['tags'][0]['name']); + + // Complete system context. + $result = core_tag_external::get_tag_cloud(0, false, 150, 'name', '', 0, context_system::instance()->id); + $result = external_api::clean_returnvalue(core_tag_external::get_tag_cloud_returns(), $result); + $this->assertCount(4, $result['tags']); + $this->assertEquals(4, $result['tagscount']); + + // Just system context - avoid children. + $result = core_tag_external::get_tag_cloud(0, false, 150, 'name', '', 0, context_system::instance()->id, 0); + $result = external_api::clean_returnvalue(core_tag_external::get_tag_cloud_returns(), $result); + $this->assertCount(2, $result['tags']); + $this->assertEquals(2, $result['tagscount']); // Horses and Cats. + $this->assertEquals('Cats', $result['tags'][0]['name']); + $this->assertEquals('Horses', $result['tags'][1]['name']); + } }