Exemple #1
0
    def _update_tags(self, req, page):
        tag_system = TagSystem(self.env)
        newtags = tag_system.split_into_tags(req.args.get('tags', ''))
        oldtags = tag_system.get_tags(req, page.resource)

        if oldtags != newtags:
            tag_system.set_tags(req, page.resource, newtags)
            return True
        return False
Exemple #2
0
    def _update_tags(self, req, page):
        tag_system = TagSystem(self.env)
        newtags = tag_system.split_into_tags(req.args.get('tags', ''))
        oldtags = tag_system.get_tags(req, page.resource)

        if oldtags != newtags:
            tag_system.set_tags(req, page.resource, newtags)
            return True
        return False
Exemple #3
0
    def _update_tags(self, req, resource, new_tags):
        # Get old tags of the resource.
        tag_system = TagSystem(self.env)
        old_tags = self._get_stored_tags(req, resource)

        self.log.debug("setting tags: %s" % (new_tags,))

        # Replace with new tags if different.
        if old_tags != new_tags:
            tag_system.set_tags(req, resource, new_tags)
            return True
        return False
Exemple #4
0
class TagRPC(Component):
    """[extra] RPC interface for the tag system.

    Access Trac resource tagging system through methods provided by
    [https://trac-hacks.org/wiki/XmlRpcPlugin XmlRpcPlugin].
    """

    implements(IXMLRPCHandler)

    def __init__(self):
        self.tag_system = TagSystem(self.env)

    # IXMLRPCHandler methods

    def xmlrpc_namespace(self):
        return 'tags'

    def xmlrpc_methods(self):
        yield (None, ((list, str),), self.splitIntoTags)
        yield ('TAGS_VIEW', ((list,),), self.getTaggableRealms)
        yield ('TAGS_VIEW', ((dict,), (dict, list)), self.getAllTags)
        yield ('TAGS_VIEW', ((list, str, str),), self.getTags)
        yield ('TAGS_VIEW', ((list, str),), self.query)
        yield ('TAGS_MODIFY', ((list, str, str, list),
                               (list, str, str, list, str)), self.addTags)
        yield ('TAGS_MODIFY', ((list, str, str, list),
                               (list, str, str, list, str)), self.setTags)

    # Exported functions and TagSystem methods

    def addTags(self, req, realm, id, tags, comment=u''):
        """Add the supplied list of tags to a taggable Trac resource.

        Returns the updated list of resource tags.
        """
        resource = Resource(realm, id)
        # Replicate TagSystem.add_tags() method due to xmlrpclib issue.
        tags = set(tags)
        tags.update(self._get_tags(req, resource))
        self.tag_system.set_tags(req, resource, tags, comment)
        return self._get_tags(req, resource)

    def getAllTags(self, req, realms=[]):
        """Returns a dict of all tags as keys and occurrences as values.

        If a realm list is supplied, only tags from these taggable realms
        are shown.
        """
        # Type conversion needed for content transfer of Counter object.
        return dict(self.tag_system.get_all_tags(req, realms))

    def getTaggableRealms(self, req):
        """Returns the list of taggable Trac realms."""
        return list(self.tag_system.get_taggable_realms())

    def getTags(self, req, realm, id):
        """Returns the list of tags for a Trac resource."""
        return self._get_tags(req, Resource(realm, id))

    def query(self, req, query_str):
        """Returns a list of tagged Trac resources, whose tags match the
        supplied tag query expression.
        """
        # Type conversion needed for content transfer of Python set objects.
        return [(resource.realm, resource.id, list(tags))
                for resource, tags in self.tag_system.query(req, query_str)]

    def setTags(self, req, realm, id, tags, comment=u''):
        """Replace tags for a Trac resource with the supplied list of tags.

        Returns the updated list of resource tags.
        """
        resource = Resource(realm, id)
        self._get_tags(req, resource) # Trac resource exists?
        self.tag_system.set_tags(req, resource, tags, comment)
        return self._get_tags(req, resource)

    def splitIntoTags(self, req, tag_str):
        """Returns a list of tags from a string.

        Comma, whitespace and combinations of these characters are recognized
        as delimiter, that get stripped from the output.
        """
        return split_into_tags(tag_str)

    # Private methods

    def _get_tags(self, req, resource):
        if not resource_exists(self.env, resource):
            raise ResourceNotFound('Resource "%r" does not exists' % resource)
        # Workaround for ServiceException when calling TagSystem.get_tags().
        provider = [p for p in self.tag_system.tag_providers
                    if p.get_taggable_realm() == resource.realm][0]
        # Type conversion needed for content transfer of Python set objects.
        return list(provider.get_resource_tags(req, resource))
Exemple #5
0
class WikiTagInterface(TagTemplateProvider):
    """[main] Implements the user interface for tagging Wiki pages."""

    implements(IRequestFilter, ITemplateStreamFilter, IWikiChangeListener,
               IWikiPageManipulator)

    def __init__(self):
        self.tag_system = TagSystem(self.env)

    # IRequestFilter methods

    def pre_process_request(self, req, handler):
        return handler

    def post_process_request(self, req, template, data, content_type):
        if template is not None:
            if req.method == 'GET' and req.path_info.startswith('/wiki/'):
                if req.args.get('action') == 'edit' and \
                        req.args.get('template') and 'tags' not in req.args:
                    self._post_process_request_edit(req)
                if req.args.get('action') == 'history' and \
                        data and 'history' in data:
                    self._post_process_request_history(req, data)
            elif req.method == 'POST' and \
                    req.path_info.startswith('/wiki/') and \
                    'save' in req.args:
                requests.reset()
        return template, data, content_type

    # ITemplateStreamFilter methods
    def filter_stream(self, req, method, filename, stream, data):
        page_name = req.args.get('page', 'WikiStart')
        resource = Resource('wiki', page_name)
        if filename == 'wiki_view.html' and 'TAGS_VIEW' in req.perm(resource):
            return self._wiki_view(req, stream)
        elif filename == 'wiki_edit.html' and \
                         'TAGS_MODIFY' in req.perm(resource):
            return self._wiki_edit(req, stream)
        elif filename == 'history_view.html' and \
                         'TAGS_VIEW' in req.perm(resource):
            return self._wiki_history(req, stream)
        return stream

    # IWikiPageManipulator methods
    def prepare_wiki_page(self, req, page, fields):
        pass

    def validate_wiki_page(self, req, page):
        # If we're saving the wiki page, and can modify tags, do so.
        if req and 'TAGS_MODIFY' in req.perm(page.resource) \
                and req.path_info.startswith('/wiki') and 'save' in req.args:
            page_modified = req.args.get('text') != page.old_text or \
                    page.readonly != int('readonly' in req.args)
            if page_modified:
                requests.set(req)
                req.add_redirect_listener(self._redirect_listener)
            elif page.version > 0:
                # If the page hasn't been otherwise modified, save tags and
                # redirect to avoid the "page has not been modified" warning.
                if self._update_tags(req, page):
                    req.redirect(
                        get_resource_url(self.env,
                                         page.resource,
                                         req.href,
                                         version=None))
        return []

    # IWikiChangeListener methods
    def wiki_page_added(self, page):
        req = requests.get()
        if req:
            self._update_tags(req, page, page.time)

    def wiki_page_changed(self, page, version, t, comment, author, ipnr):
        req = requests.get()
        if req:
            self._update_tags(req, page, page.time)

    def wiki_page_renamed(self, page, old_name):
        """Called when a page has been renamed (since Trac 0.12)."""
        self.log.debug("Moving wiki page tags from %s to %s", old_name,
                       page.name)
        req = MockReq()
        self.tag_system.reparent_tags(req, Resource('wiki', page.name),
                                      old_name)

    def wiki_page_deleted(self, page):
        # Page gone, so remove all records on it.
        delete_tags(self.env, page.resource, purge=True)

    def wiki_page_version_deleted(self, page):
        pass

    # Internal methods
    def _page_tags(self, req):
        pagename = req.args.get('page', 'WikiStart')
        version = req.args.get('version')
        tags_version = req.args.get('tags_version')

        page = WikiPage(self.env, pagename, version=version)
        resource = page.resource
        if version and not tags_version:
            tags_version = page.time
        tags = sorted(
            self.tag_system.get_tags(req, resource, when=tags_version))
        return tags

    def _redirect_listener(self, req, url, permanent):
        requests.reset()

    def _post_process_request_edit(self, req):
        # Retrieve template resource to be queried for tags.
        template_pagename = ''.join(
            [WikiModule.PAGE_TEMPLATES_PREFIX,
             req.args.get('template')])
        template_page = WikiPage(self.env, template_pagename)
        if template_page.exists and \
                'TAGS_VIEW' in req.perm(template_page.resource):
            tags = sorted(self.tag_system.get_tags(req,
                                                   template_page.resource))
            # Prepare tags as content for the editor field.
            tags_str = ' '.join(tags)
            self.env.log.debug("Tags retrieved from template: '%s'",
                               unicode(tags_str).encode('utf-8'))
            # DEVEL: More arguments need to be propagated here?
            req.redirect(
                req.href(req.path_info,
                         action='edit',
                         tags=tags_str,
                         template=req.args.get('template')))

    def _post_process_request_history(self, req, data):
        history = []
        page_histories = data.get('history', [])
        resource = data['resource']
        tags_histories = tag_changes(self.env, resource)

        for page_history in page_histories:
            while tags_histories and \
                    tags_histories[0][0] >= page_history['date']:
                tags_history = tags_histories.pop(0)
                date = tags_history[0]
                author = tags_history[1]
                comment = render_tag_changes(tags_history[2], tags_history[3])
                url = req.href(resource.realm,
                               resource.id,
                               version=page_history['version'],
                               tags_version=to_utimestamp(date))
                history.append({
                    'version': '*',
                    'url': url,
                    'date': date,
                    'author': author,
                    'comment': comment,
                    'ipnr': ''
                })
            history.append(page_history)

        data.update(
            dict(history=history, wiki_to_oneliner=self._wiki_to_oneliner))

    def _wiki_view(self, req, stream):
        add_stylesheet(req, 'tags/css/tractags.css')
        tags = self._page_tags(req)
        if not tags:
            return stream
        li = []
        for tag_ in tags:
            resource = Resource('tag', tag_)
            anchor = render_resource_link(self.env, web_context(req, resource),
                                          resource)
            anchor = anchor(rel='tag')
            li.append(tag.li(anchor, ' '))

        # TRANSLATOR: Header label text for tag list at wiki page bottom.
        insert = tag.ul(class_='tags')(tag.li(_("Tags"), class_='header'), li)
        return stream | (
            Transformer('//div[contains(@class,"wikipage")]').after(insert))

    def _update_tags(self, req, page, when=None):
        newtags = split_into_tags(req.args.get('tags', ''))
        oldtags = self.tag_system.get_tags(req, page.resource)

        if oldtags != newtags:
            self.tag_system.set_tags(req, page.resource, newtags, when=when)
            return True
        return False

    def _wiki_edit(self, req, stream):
        # TRANSLATOR: Label text for link to '/tags'.
        link = tag.a(_("view all tags"), href=req.href.tags())
        # TRANSLATOR: ... (view all tags)
        insert = tag(Markup(_("Tag under: (%(tags_link)s)", tags_link=link)))
        insert(
            tag.br(),
            tag.input(id='tags',
                      type='text',
                      name='tags',
                      size='50',
                      value=req.args.get('tags',
                                         ' '.join(self._page_tags(req)))))
        insert = tag.div(tag.label(insert), class_='field')
        return stream | Transformer('//div[@id="changeinfo1"]').append(insert)

    def _wiki_history(self, req, stream):
        xpath = '//input[@type="radio" and @value="*"]'
        stream = stream | Transformer(xpath).remove()
        # Remove invalid links to wiki page revisions (fix for Trac < 0.12).
        xpath = '//a[contains(@href,"%2A")]'
        return stream | Transformer(xpath).remove()

    def _wiki_to_oneliner(self, context, wiki, shorten=None):
        if isinstance(wiki, Fragment):
            return wiki
        return format_to_oneliner(self.env, context, wiki, shorten=shorten)
Exemple #6
0
class WikiTagInterface(TagTemplateProvider):
    """[main] Implements the user interface for tagging Wiki pages."""

    implements(IRequestFilter, ITemplateStreamFilter,
               IWikiChangeListener, IWikiPageManipulator)

    def __init__(self):
        self.tag_system = TagSystem(self.env)

    # IRequestFilter methods

    def pre_process_request(self, req, handler):
        return handler

    def post_process_request(self, req, template, data, content_type):
        if template is not None:
            if req.method == 'GET' and req.path_info.startswith('/wiki/'):
                if req.args.get('action') == 'edit' and \
                        req.args.get('template') and 'tags' not in req.args:
                    self._post_process_request_edit(req)
                if req.args.get('action') == 'history' and \
                        data and 'history' in data:
                    self._post_process_request_history(req, data)
            elif req.method == 'POST' and \
                    req.path_info.startswith('/wiki/') and \
                    'save' in req.args:
                requests.reset()
        return template, data, content_type

    # ITemplateStreamFilter methods
    def filter_stream(self, req, method, filename, stream, data):
        page_name = req.args.get('page', 'WikiStart')
        resource = Resource('wiki', page_name)
        if filename == 'wiki_view.html' and 'TAGS_VIEW' in req.perm(resource):
            return self._wiki_view(req, stream)
        elif filename == 'wiki_edit.html' and \
                         'TAGS_MODIFY' in req.perm(resource):
            return self._wiki_edit(req, stream)
        elif filename == 'history_view.html' and \
                         'TAGS_VIEW' in req.perm(resource):
            return self._wiki_history(req, stream)
        return stream

    # IWikiPageManipulator methods
    def prepare_wiki_page(self, req, page, fields):
        pass

    def validate_wiki_page(self, req, page):
        # If we're saving the wiki page, and can modify tags, do so.
        if req and 'TAGS_MODIFY' in req.perm(page.resource) \
                and req.path_info.startswith('/wiki') and 'save' in req.args:
            page_modified = req.args.get('text') != page.old_text or \
                    page.readonly != int('readonly' in req.args)
            if page_modified:
                requests.set(req)
                req.add_redirect_listener(self._redirect_listener)
            elif page.version > 0:
                # If the page hasn't been otherwise modified, save tags and
                # redirect to avoid the "page has not been modified" warning.
                if self._update_tags(req, page):
                    req.redirect(get_resource_url(self.env, page.resource,
                                                  req.href, version=None))
        return []

    # IWikiChangeListener methods
    def wiki_page_added(self, page):
        req = requests.get()
        if req:
            self._update_tags(req, page, page.time)

    def wiki_page_changed(self, page, version, t, comment, author, ipnr):
        req = requests.get()
        if req:
            self._update_tags(req, page, page.time)

    def wiki_page_renamed(self, page, old_name):
        """Called when a page has been renamed (since Trac 0.12)."""
        self.log.debug("Moving wiki page tags from %s to %s",
                       old_name, page.name)
        req = MockReq()
        self.tag_system.reparent_tags(req, Resource('wiki', page.name),
                                      old_name)

    def wiki_page_deleted(self, page):
        # Page gone, so remove all records on it.
        delete_tags(self.env, page.resource, purge=True)

    def wiki_page_version_deleted(self, page):
        pass

    # Internal methods
    def _page_tags(self, req):
        pagename = req.args.get('page', 'WikiStart')
        version = req.args.get('version')
        tags_version = req.args.get('tags_version')

        page = WikiPage(self.env, pagename, version=version)
        resource = page.resource
        if version and not tags_version:
            tags_version = page.time
        tags = sorted(self.tag_system.get_tags(req, resource,
                                               when=tags_version))
        return tags

    def _redirect_listener(self, req, url, permanent):
        requests.reset()

    def _post_process_request_edit(self, req):
        # Retrieve template resource to be queried for tags.
        template_pagename = ''.join([WikiModule.PAGE_TEMPLATES_PREFIX,
                                     req.args.get('template')])
        template_page = WikiPage(self.env, template_pagename)
        if template_page.exists and \
                'TAGS_VIEW' in req.perm(template_page.resource):
            tags = sorted(self.tag_system.get_tags(req,
                                                   template_page.resource))
            # Prepare tags as content for the editor field.
            tags_str = ' '.join(tags)
            self.env.log.debug("Tags retrieved from template: '%s'",
                               unicode(tags_str).encode('utf-8'))
            # DEVEL: More arguments need to be propagated here?
            req.redirect(req.href(req.path_info,
                                  action='edit', tags=tags_str,
                                  template=req.args.get('template')))

    def _post_process_request_history(self, req, data):
        history = []
        page_histories = data.get('history', [])
        resource = data['resource']
        tags_histories = tag_changes(self.env, resource)

        for page_history in page_histories:
            while tags_histories and \
                    tags_histories[0][0] >= page_history['date']:
                tags_history = tags_histories.pop(0)
                date = tags_history[0]
                author = tags_history[1]
                comment = render_tag_changes(tags_history[2], tags_history[3])
                url = req.href(resource.realm, resource.id,
                               version=page_history['version'],
                               tags_version=to_utimestamp(date))
                history.append({'version': '*', 'url': url, 'date': date,
                                'author': author, 'comment': comment,
                                'ipnr': ''})
            history.append(page_history)

        data.update(dict(history=history,
                         wiki_to_oneliner=self._wiki_to_oneliner))

    def _wiki_view(self, req, stream):
        add_stylesheet(req, 'tags/css/tractags.css')
        tags = self._page_tags(req)
        if not tags:
            return stream
        li = []
        for tag_ in tags:
            resource = Resource('tag', tag_)
            anchor = render_resource_link(self.env,
                                          web_context(req, resource),
                                          resource)
            anchor = anchor(rel='tag')
            li.append(tag.li(anchor, ' '))

        # TRANSLATOR: Header label text for tag list at wiki page bottom.
        insert = tag.ul(class_='tags')(tag.li(_("Tags"), class_='header'), li)
        return stream | (Transformer('//div[contains(@class,"wikipage")]')
                         .after(insert))

    def _update_tags(self, req, page, when=None):
        newtags = split_into_tags(req.args.get('tags', ''))
        oldtags = self.tag_system.get_tags(req, page.resource)

        if oldtags != newtags:
            self.tag_system.set_tags(req, page.resource, newtags, when=when)
            return True
        return False

    def _wiki_edit(self, req, stream):
        # TRANSLATOR: Label text for link to '/tags'.
        link = tag.a(_("view all tags"), href=req.href.tags())
        # TRANSLATOR: ... (view all tags)
        insert = tag(Markup(_("Tag under: (%(tags_link)s)", tags_link=link)))
        insert(
            tag.br(),
            tag.input(id='tags', type='text', name='tags', size='50',
                value=req.args.get('tags', ' '.join(self._page_tags(req))))
        )
        insert = tag.div(tag.label(insert), class_='field')
        return stream | Transformer('//div[@id="changeinfo1"]').append(insert)

    def _wiki_history(self, req, stream):
        xpath = '//input[@type="radio" and @value="*"]'
        stream = stream | Transformer(xpath).remove()
        # Remove invalid links to wiki page revisions (fix for Trac < 0.12).
        xpath = '//a[contains(@href,"%2A")]'
        return stream | Transformer(xpath).remove()

    def _wiki_to_oneliner(self, context, wiki, shorten=None):
        if isinstance(wiki, Fragment):
            return wiki
        return format_to_oneliner(self.env, context, wiki, shorten=shorten)
Exemple #7
0
class TagSystemTestCase(unittest.TestCase):

    def setUp(self):
        self.env = EnvironmentStub(default_data=True,
                                   enable=['trac.*', 'tractags.*'])
        self.env.path = tempfile.mkdtemp()
        self.perms = PermissionSystem(self.env)
        self.req = Mock()

        self.actions = ['TAGS_ADMIN', 'TAGS_MODIFY', 'TAGS_VIEW']
        self.tag_s = TagSystem(self.env)
        self.db = self.env.get_db_cnx()
        setup = TagSetup(self.env)
        # Current tractags schema is setup with enabled component anyway.
        #   Revert these changes for getting default permissions inserted.
        self._revert_tractags_schema_init()
        setup.upgrade_environment(self.db)

    def tearDown(self):
        self.db.close()
        # Really close db connections.
        self.env.shutdown()
        shutil.rmtree(self.env.path)

    # Helpers

    def _revert_tractags_schema_init(self):
        cursor = self.db.cursor()
        cursor.execute("DROP TABLE IF EXISTS tags")
        cursor.execute("DELETE FROM system WHERE name='tags_version'")
        cursor.execute("DELETE FROM permission WHERE action %s"
                       % self.db.like(), ('TAGS_%',))

    # Tests

    def test_available_actions(self):
        for action in self.actions:
            self.failIf(action not in self.perms.get_actions())

    def test_available_providers(self):
        # Standard implementations of DefaultTagProvider should be registered.
        seen = []
        for provider in [TicketTagProvider(self.env),
                         WikiTagProvider(self.env)]:
            self.failIf(provider not in self.tag_s.tag_providers)
            # Ensure unique provider references, a possible bug in Trac-0.11.
            self.failIf(provider in seen)
            seen.append(provider)

    def test_set_tags_no_perms(self):
        resource = Resource('wiki', 'WikiStart')
        tags = ['tag1']
        # Mock an anonymous request.
        self.req.perm = PermissionCache(self.env)
        self.assertRaises(PermissionError, self.tag_s.set_tags, self.req,
                          resource, tags)

    def test_set_tags(self):
        resource = Resource('wiki', 'WikiStart')
        tags = ['tag1']
        self.req.perm = PermissionCache(self.env, username='******')
        # Shouldn't raise an error with appropriate permission.
        self.tag_s.set_tags(self.req, resource, tags)

    def test_query_no_args(self):
        # Regression test for query without argument,
        #   reported as th:ticket:7857.

        # Mock an anonymous request.
        self.req.perm = PermissionCache(self.env)
        self.assertEquals([(res, tags) for res, tags in
                           self.tag_s.query(self.req, query='')],
                          [])
Exemple #8
0
    def _new_blog_post(self, req):
        """ Generate a new blog post """
        action = req.args.get('action', 'edit')
        wikitext = req.args.get('text', '')
        blogtitle = req.args.get('blogtitle', '')
        page_format = req.args.get('pagename', self.page_format) 
        tags = self._get_tags(req)
        referer = self._get_referer(req)

        author = req.authname
        pagename = self._generate_pagename(page_format, blogtitle, author) 
        titleline = ' '.join(["=", blogtitle, "=\n"])

        page = WikiPage(self.env, pagename, None)
        page.text = wikitext
        comment = req.args.get('comment', '')
        readonly = int(req.args.has_key('readonly'))
        edit_rows = req.args.get('edit_rows', '20')
        scroll_bar_pos = req.args.get('scroll_bar_pos', '')
        page_source = page.text

        if blogtitle:
            wikitext = ''.join([titleline, wikitext])

        if req.method == 'POST':
            if action == 'edit':
                if req.args.has_key('cancel'):
                    req.redirect(referer)
                page = WikiPage(self.env, pagename, None)
                tagsystem = TagSystem(self.env)
                # Add footer 
                page.text = ''.join([wikitext, "\n\n", self.var_subs(author, self.footer)]) 
                page.readonly = readonly
                if req.args.has_key('preview'):
                    action = 'preview'
                else:
                    page.save(author, comment, req.remote_addr)
                    tagsystem.set_tags(req, page.resource, tags)
                    req.redirect(referer)

        
        wiki = {'page_name' : pagename,
                'comment' : comment,
                'author' : author,
                'edit_rows' : edit_rows,
                'version' : 0,
                'read_only' : readonly,
                'scroll_bar_pos' : scroll_bar_pos,
                'page_source' : page_source,
                'action' : action,
               }

        data = {'page' : page,
                'action' : action,
                'title' : self.entry_page_title,
                'blog' : {'title' : blogtitle,
                          'pagename' : pagename,
                          'referer' : referer,
                         },
                'tags' : ', '.join(tags),
                'referer' : referer,
               }
        data.update(wiki)
        return data
Exemple #9
0
class TagSystemTestCase(unittest.TestCase):
    def setUp(self):
        self.env = EnvironmentStub(default_data=True,
                                   enable=['trac.*', 'tractags.*'])
        self.env.path = tempfile.mkdtemp()
        self.perms = PermissionSystem(self.env)
        self.req = Mock()

        self.actions = ['TAGS_ADMIN', 'TAGS_MODIFY', 'TAGS_VIEW']
        self.tag_s = TagSystem(self.env)
        self.db = self.env.get_db_cnx()
        setup = TagSetup(self.env)
        # Current tractags schema is setup with enabled component anyway.
        #   Revert these changes for getting default permissions inserted.
        self._revert_tractags_schema_init()
        setup.upgrade_environment(self.db)

    def tearDown(self):
        self.db.close()
        # Really close db connections.
        self.env.shutdown()
        shutil.rmtree(self.env.path)

    # Helpers

    def _revert_tractags_schema_init(self):
        cursor = self.db.cursor()
        cursor.execute("DROP TABLE IF EXISTS tags")
        cursor.execute("DELETE FROM system WHERE name='tags_version'")
        cursor.execute(
            "DELETE FROM permission WHERE action %s" % self.db.like(),
            ('TAGS_%', ))

    # Tests

    def test_available_actions(self):
        for action in self.actions:
            self.failIf(action not in self.perms.get_actions())

    def test_available_providers(self):
        # Standard implementations of DefaultTagProvider should be registered.
        seen = []
        for provider in [
                TicketTagProvider(self.env),
                WikiTagProvider(self.env)
        ]:
            self.failIf(provider not in self.tag_s.tag_providers)
            # Ensure unique provider references, a possible bug in Trac-0.11.
            self.failIf(provider in seen)
            seen.append(provider)

    def test_set_tags_no_perms(self):
        resource = Resource('wiki', 'WikiStart')
        tags = ['tag1']
        # Mock an anonymous request.
        self.req.perm = PermissionCache(self.env)
        self.assertRaises(PermissionError, self.tag_s.set_tags, self.req,
                          resource, tags)

    def test_set_tags(self):
        resource = Resource('wiki', 'WikiStart')
        tags = ['tag1']
        self.req.perm = PermissionCache(self.env, username='******')
        # Shouldn't raise an error with appropriate permission.
        self.tag_s.set_tags(self.req, resource, tags)

    def test_query_no_args(self):
        # Regression test for query without argument,
        #   reported as th:ticket:7857.

        # Mock an anonymous request.
        self.req.perm = PermissionCache(self.env)
        self.assertEquals(
            [(res, tags)
             for res, tags in self.tag_s.query(self.req, query='')], [])