From 92c90b96ce2cb18f2ef890e05ee699532643a7f7 Mon Sep 17 00:00:00 2001 From: Marina Glancy Date: Mon, 7 Mar 2016 21:29:33 +0800 Subject: [PATCH] MDL-25742 mod_wiki: show wiki pages tagged with a tag --- mod/wiki/db/tag.php | 2 + mod/wiki/locallib.php | 115 ++++++++++++++++++++++++++++++ mod/wiki/tests/generator/lib.php | 20 ++++-- mod/wiki/tests/generator_test.php | 10 ++- mod/wiki/tests/lib_test.php | 84 ++++++++++++++++++++++ mod/wiki/version.php | 2 +- 6 files changed, 224 insertions(+), 9 deletions(-) diff --git a/mod/wiki/db/tag.php b/mod/wiki/db/tag.php index 11a44c6f37c..cfe6c75d567 100644 --- a/mod/wiki/db/tag.php +++ b/mod/wiki/db/tag.php @@ -29,5 +29,7 @@ $tagareas = array( array( 'itemtype' => 'wiki_pages', 'component' => 'mod_wiki', + 'callback' => 'mod_wiki_get_tagged_pages', + 'callbackfile' => '/mod/wiki/locallib.php', ), ); diff --git a/mod/wiki/locallib.php b/mod/wiki/locallib.php index d1c04cd2f92..74953b30933 100644 --- a/mod/wiki/locallib.php +++ b/mod/wiki/locallib.php @@ -1734,3 +1734,118 @@ function wiki_get_subwiki_by_group_and_user_with_validation($wiki, $groupid, $us return $subwiki; } + +/** + * Returns wiki pages tagged with a specified tag. + * + * This is a callback used by the tag area mod_wiki/wiki_pages to search for wiki pages + * tagged with a specific tag. + * + * @param core_tag_tag $tag + * @param bool $exclusivemode if set to true it means that no other entities tagged with this tag + * are displayed on the page and the per-page limit may be bigger + * @param int $fromctx context id where the link was displayed, may be used by callbacks + * to display items in the same context first + * @param int $ctx context id where to search for records + * @param bool $rec search in subcontexts as well + * @param int $page 0-based number of page being displayed + * @return \core_tag\output\tagindex + */ +function mod_wiki_get_tagged_pages($tag, $exclusivemode = false, $fromctx = 0, $ctx = 0, $rec = 1, $page = 0) { + global $OUTPUT; + $perpage = $exclusivemode ? 20 : 5; + + // Build the SQL query. + $ctxselect = context_helper::get_preload_record_columns_sql('ctx'); + $query = "SELECT wp.id, wp.title, ws.userid, ws.wikiid, ws.id AS subwikiid, ws.groupid, w.wikimode, + cm.id AS cmid, c.id AS courseid, c.shortname, c.fullname, $ctxselect + FROM {wiki_pages} wp + JOIN {wiki_subwikis} ws ON wp.subwikiid = ws.id + JOIN {wiki} w ON w.id = ws.wikiid + JOIN {modules} m ON m.name='wiki' + JOIN {course_modules} cm ON cm.module = m.id AND cm.instance = w.id + JOIN {tag_instance} tt ON wp.id = tt.itemid + JOIN {course} c ON cm.course = c.id + JOIN {context} ctx ON ctx.instanceid = cm.id AND ctx.contextlevel = :coursemodulecontextlevel + WHERE tt.itemtype = :itemtype AND tt.tagid = :tagid AND tt.component = :component + AND wp.id %ITEMFILTER% AND c.id %COURSEFILTER%"; + + $params = array('itemtype' => 'wiki_pages', 'tagid' => $tag->id, 'component' => 'mod_wiki', + 'coursemodulecontextlevel' => CONTEXT_MODULE); + + if ($ctx) { + $context = $ctx ? context::instance_by_id($ctx) : context_system::instance(); + $query .= $rec ? ' AND (ctx.id = :contextid OR ctx.path LIKE :path)' : ' AND ctx.id = :contextid'; + $params['contextid'] = $context->id; + $params['path'] = $context->path.'/%'; + } + + $query .= " ORDER BY "; + if ($fromctx) { + // In order-clause specify that modules from inside "fromctx" context should be returned first. + $fromcontext = context::instance_by_id($fromctx); + $query .= ' (CASE WHEN ctx.id = :fromcontextid OR ctx.path LIKE :frompath THEN 0 ELSE 1 END),'; + $params['fromcontextid'] = $fromcontext->id; + $params['frompath'] = $fromcontext->path.'/%'; + } + $query .= ' c.sortorder, cm.id, wp.id'; + + $totalpages = $page + 1; + + // Use core_tag_index_builder to build and filter the list of items. + $builder = new core_tag_index_builder('mod_wiki', 'wiki_pages', $query, $params, $page * $perpage, $perpage + 1); + while ($item = $builder->has_item_that_needs_access_check()) { + context_helper::preload_from_record($item); + $courseid = $item->courseid; + if (!$builder->can_access_course($courseid)) { + $builder->set_accessible($item, false); + continue; + } + $modinfo = get_fast_modinfo($builder->get_course($courseid)); + // Set accessibility of this item and all other items in the same course. + $builder->walk(function ($taggeditem) use ($courseid, $modinfo, $builder) { + if ($taggeditem->courseid == $courseid) { + $accessible = false; + if (($cm = $modinfo->get_cm($taggeditem->cmid)) && $cm->uservisible) { + $subwiki = (object)array('id' => $taggeditem->subwikiid, 'groupid' => $taggeditem->groupid, + 'userid' => $taggeditem->groupid, 'wikiid' => $taggeditem->wikiid); + $wiki = (object)array('id' => $taggeditem->wikiid, 'wikimode' => $taggeditem->wikimode, + 'course' => $cm->course); + $accessible = wiki_user_can_view($subwiki, $wiki); + } + $builder->set_accessible($taggeditem, $accessible); + } + }); + } + + $items = $builder->get_items(); + if (count($items) > $perpage) { + $totalpages = $page + 2; // We don't need exact page count, just indicate that the next page exists. + array_pop($items); + } + + // Build the display contents. + if ($items) { + $tagfeed = new core_tag\output\tagfeed(); + foreach ($items as $item) { + context_helper::preload_from_record($item); + $modinfo = get_fast_modinfo($item->courseid); + $cm = $modinfo->get_cm($item->cmid); + $pageurl = new moodle_url('/mod/wiki/view.php', array('pageid' => $item->id)); + $pagename = format_string($item->title, true, array('context' => context_module::instance($item->cmid))); + $pagename = html_writer::link($pageurl, $pagename); + $courseurl = course_get_url($item->courseid, $cm->sectionnum); + $cmname = html_writer::link($cm->url, $cm->get_formatted_name()); + $coursename = format_string($item->fullname, true, array('context' => context_course::instance($item->courseid))); + $coursename = html_writer::link($courseurl, $coursename); + $icon = html_writer::link($pageurl, html_writer::empty_tag('img', array('src' => $cm->get_icon_url()))); + $tagfeed->add($icon, $pagename, $cmname.'
'.$coursename); + } + + $content = $OUTPUT->render_from_template('core_tag/tagfeed', + $tagfeed->export_for_template($OUTPUT)); + + return new core_tag\output\tagindex($tag, 'mod_wiki', 'wiki_pages', $content, + $exclusivemode, $fromctx, $ctx, $rec, $page, $totalpages); + } +} diff --git a/mod/wiki/tests/generator/lib.php b/mod/wiki/tests/generator/lib.php index 0ad0105605b..401aebb6088 100644 --- a/mod/wiki/tests/generator/lib.php +++ b/mod/wiki/tests/generator/lib.php @@ -110,14 +110,22 @@ class mod_wiki_generator extends testing_module_generator { } } - if ($wikipage = wiki_get_page_by_title($record['subwikiid'], $record['title'])) { - $rv = wiki_save_page($wikipage, $record['content'], $USER->id); - return $rv['page']; + $wikipage = wiki_get_page_by_title($record['subwikiid'], $record['title']); + if (!$wikipage) { + $pageid = wiki_create_page($record['subwikiid'], $record['title'], $record['format'], $USER->id); + $wikipage = wiki_get_page($pageid); } - - $pageid = wiki_create_page($record['subwikiid'], $record['title'], $record['format'], $USER->id); - $wikipage = wiki_get_page($pageid); $rv = wiki_save_page($wikipage, $record['content'], $USER->id); + + if (array_key_exists('tags', $record)) { + $tags = is_array($record['tags']) ? $record['tags'] : preg_split('/,/', $record['tags']); + if (empty($wiki->cmid)) { + $cm = get_coursemodule_from_instance('wiki', $wiki->id, isset($wiki->course) ? $wiki->course : 0); + $wiki->cmid = $cm->id; + } + core_tag_tag::set_item_tags('mod_wiki', 'wiki_pages', $wikipage->id, + context_module::instance($wiki->cmid), $tags); + } return $rv['page']; } } diff --git a/mod/wiki/tests/generator_test.php b/mod/wiki/tests/generator_test.php index 4a0687e68d1..5fd05216009 100644 --- a/mod/wiki/tests/generator_test.php +++ b/mod/wiki/tests/generator_test.php @@ -64,16 +64,22 @@ class mod_wiki_generator_testcase extends advanced_testcase { $page1 = $wikigenerator->create_first_page($wiki); $page2 = $wikigenerator->create_content($wiki); - $page3 = $wikigenerator->create_content($wiki, array('title' => 'Custom title')); + $page3 = $wikigenerator->create_content($wiki, array('title' => 'Custom title', 'tags' => array('Cats', 'mice'))); + unset($wiki->cmid); + $page4 = $wikigenerator->create_content($wiki, array('tags' => 'Cats, dogs')); $subwikis = $DB->get_records('wiki_subwikis', array('wikiid' => $wiki->id), 'id'); $this->assertEquals(1, count($subwikis)); $subwikiid = key($subwikis); $records = $DB->get_records('wiki_pages', array('subwikiid' => $subwikiid), 'id'); - $this->assertEquals(3, count($records)); + $this->assertEquals(4, count($records)); $this->assertEquals($page1->id, $records[$page1->id]->id); $this->assertEquals($page2->id, $records[$page2->id]->id); $this->assertEquals($page3->id, $records[$page3->id]->id); $this->assertEquals('Custom title', $records[$page3->id]->title); + $this->assertEquals(array('Cats', 'mice'), + array_values(core_tag_tag::get_item_tags_array('mod_wiki', 'wiki_pages', $page3->id))); + $this->assertEquals(array('Cats', 'dogs'), + array_values(core_tag_tag::get_item_tags_array('mod_wiki', 'wiki_pages', $page4->id))); } public function test_create_content_individual() { diff --git a/mod/wiki/tests/lib_test.php b/mod/wiki/tests/lib_test.php index 8f01047c9ce..ea7a03af6c3 100644 --- a/mod/wiki/tests/lib_test.php +++ b/mod/wiki/tests/lib_test.php @@ -654,4 +654,88 @@ class mod_wiki_lib_testcase extends advanced_testcase { $result = wiki_get_visible_subwikis($wikisepind); $this->assertEquals($expectedsubwikis, $result, '', 0, 10, true); } + + public function test_mod_wiki_get_tagged_pages() { + global $DB; + + $this->resetAfterTest(); + $this->setAdminUser(); + + // Setup test data. + $wikigenerator = $this->getDataGenerator()->get_plugin_generator('mod_wiki'); + $course3 = $this->getDataGenerator()->create_course(); + $course2 = $this->getDataGenerator()->create_course(); + $course1 = $this->getDataGenerator()->create_course(); + $wiki1 = $this->getDataGenerator()->create_module('wiki', array('course' => $course1->id)); + $wiki2 = $this->getDataGenerator()->create_module('wiki', array('course' => $course2->id)); + $wiki3 = $this->getDataGenerator()->create_module('wiki', array('course' => $course3->id)); + $page11 = $wikigenerator->create_content($wiki1, array('tags' => array('Cats', 'Dogs'))); + $page12 = $wikigenerator->create_content($wiki1, array('tags' => array('Cats', 'mice'))); + $page13 = $wikigenerator->create_content($wiki1, array('tags' => array('Cats'))); + $page14 = $wikigenerator->create_content($wiki1); + $page15 = $wikigenerator->create_content($wiki1, array('tags' => array('Cats'))); + $page21 = $wikigenerator->create_content($wiki2, array('tags' => array('Cats'))); + $page22 = $wikigenerator->create_content($wiki2, array('tags' => array('Cats', 'Dogs'))); + $page23 = $wikigenerator->create_content($wiki2, array('tags' => array('mice', 'Cats'))); + $page31 = $wikigenerator->create_content($wiki3, array('tags' => array('mice', 'Cats'))); + + $tag = core_tag_tag::get_by_name(0, 'Cats'); + + // Admin can see everything. + $res = mod_wiki_get_tagged_pages($tag, /*$exclusivemode = */false, + /*$fromctx = */0, /*$ctx = */0, /*$rec = */1, /*$page = */0); + $this->assertRegExp('/'.$page11->title.'/', $res->content); + $this->assertRegExp('/'.$page12->title.'/', $res->content); + $this->assertRegExp('/'.$page13->title.'/', $res->content); + $this->assertNotRegExp('/'.$page14->title.'/', $res->content); + $this->assertRegExp('/'.$page15->title.'/', $res->content); + $this->assertRegExp('/'.$page21->title.'/', $res->content); + $this->assertNotRegExp('/'.$page22->title.'/', $res->content); + $this->assertNotRegExp('/'.$page23->title.'/', $res->content); + $this->assertNotRegExp('/'.$page31->title.'/', $res->content); + $this->assertEmpty($res->prevpageurl); + $this->assertNotEmpty($res->nextpageurl); + $res = mod_wiki_get_tagged_pages($tag, /*$exclusivemode = */false, + /*$fromctx = */0, /*$ctx = */0, /*$rec = */1, /*$page = */1); + $this->assertNotRegExp('/'.$page11->title.'/', $res->content); + $this->assertNotRegExp('/'.$page12->title.'/', $res->content); + $this->assertNotRegExp('/'.$page13->title.'/', $res->content); + $this->assertNotRegExp('/'.$page14->title.'/', $res->content); + $this->assertNotRegExp('/'.$page15->title.'/', $res->content); + $this->assertNotRegExp('/'.$page21->title.'/', $res->content); + $this->assertRegExp('/'.$page22->title.'/', $res->content); + $this->assertRegExp('/'.$page23->title.'/', $res->content); + $this->assertRegExp('/'.$page31->title.'/', $res->content); + $this->assertNotEmpty($res->prevpageurl); + $this->assertEmpty($res->nextpageurl); + + // Create and enrol a user. + $student = self::getDataGenerator()->create_user(); + $studentrole = $DB->get_record('role', array('shortname' => 'student')); + $this->getDataGenerator()->enrol_user($student->id, $course1->id, $studentrole->id, 'manual'); + $this->getDataGenerator()->enrol_user($student->id, $course2->id, $studentrole->id, 'manual'); + $this->setUser($student); + core_tag_index_builder::reset_caches(); + + // User can not see pages in course 3 because he is not enrolled. + $res = mod_wiki_get_tagged_pages($tag, /*$exclusivemode = */false, + /*$fromctx = */0, /*$ctx = */0, /*$rec = */1, /*$page = */1); + $this->assertRegExp('/'.$page22->title.'/', $res->content); + $this->assertRegExp('/'.$page23->title.'/', $res->content); + $this->assertNotRegExp('/'.$page31->title.'/', $res->content); + + // User can search wiki pages inside a course. + $coursecontext = context_course::instance($course1->id); + $res = mod_wiki_get_tagged_pages($tag, /*$exclusivemode = */false, + /*$fromctx = */0, /*$ctx = */$coursecontext->id, /*$rec = */1, /*$page = */0); + $this->assertRegExp('/'.$page11->title.'/', $res->content); + $this->assertRegExp('/'.$page12->title.'/', $res->content); + $this->assertRegExp('/'.$page13->title.'/', $res->content); + $this->assertNotRegExp('/'.$page14->title.'/', $res->content); + $this->assertRegExp('/'.$page15->title.'/', $res->content); + $this->assertNotRegExp('/'.$page21->title.'/', $res->content); + $this->assertNotRegExp('/'.$page22->title.'/', $res->content); + $this->assertNotRegExp('/'.$page23->title.'/', $res->content); + $this->assertEmpty($res->nextpageurl); + } } diff --git a/mod/wiki/version.php b/mod/wiki/version.php index f0472bd6972..1e6235d0560 100644 --- a/mod/wiki/version.php +++ b/mod/wiki/version.php @@ -33,7 +33,7 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2016011103; // The current module version (Date: YYYYMMDDXX) +$plugin->version = 2016032700; // The current module version (Date: YYYYMMDDXX) $plugin->requires = 2015111000; // Requires this Moodle version $plugin->component = 'mod_wiki'; // Full name of the plugin (used for diagnostics) $plugin->cron = 0; -- 2.17.1