MDL-25742 mod_wiki: show wiki pages tagged with a tag
authorMarina Glancy <marina@moodle.com>
Mon, 7 Mar 2016 13:29:33 +0000 (21:29 +0800)
committerMarina Glancy <marina@moodle.com>
Tue, 12 Apr 2016 23:57:58 +0000 (07:57 +0800)
mod/wiki/db/tag.php
mod/wiki/locallib.php
mod/wiki/tests/generator/lib.php
mod/wiki/tests/generator_test.php
mod/wiki/tests/lib_test.php
mod/wiki/version.php

index 11a44c6..cfe6c75 100644 (file)
@@ -29,5 +29,7 @@ $tagareas = array(
     array(
         'itemtype' => 'wiki_pages',
         'component' => 'mod_wiki',
+        'callback' => 'mod_wiki_get_tagged_pages',
+        'callbackfile' => '/mod/wiki/locallib.php',
     ),
 );
index d1c04cd..74953b3 100644 (file)
@@ -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.'<br>'.$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);
+    }
+}
index 0ad0105..401aebb 100644 (file)
@@ -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'];
     }
 }
index 4a0687e..5fd0521 100644 (file)
@@ -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() {
index 8f01047..ea7a03a 100644 (file)
@@ -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);
+    }
 }
index f0472bd..1e6235d 100644 (file)
@@ -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;