diff -Nru trac-tags-0.6.0+svn9419/debian/changelog trac-tags-0.6.0+svn10671/debian/changelog --- trac-tags-0.6.0+svn9419/debian/changelog 2011-04-19 15:42:23.000000000 +0000 +++ trac-tags-0.6.0+svn10671/debian/changelog 2011-09-22 22:14:12.000000000 +0000 @@ -1,3 +1,14 @@ +trac-tags (0.6.0+svn10671-1) unstable; urgency=low + + * new upstream pull from SVN. This includes an API change by allowing + comments on tag changes (see + https://trac-hacks.org/ticket/9061#comment:12) + * correct README.Debian. Thanks, Libor Klepáč (Closes: #642201) + * drop fix-tags-link.patch; better fix upstream. + * transition to dh_python2 + + -- Daniel Kahn Gillmor Thu, 22 Sep 2011 18:01:06 -0400 + trac-tags (0.6.0+svn9419-2) unstable; urgency=low * added README.Debian diff -Nru trac-tags-0.6.0+svn9419/debian/control trac-tags-0.6.0+svn10671/debian/control --- trac-tags-0.6.0+svn9419/debian/control 2011-04-07 22:46:34.000000000 +0000 +++ trac-tags-0.6.0+svn10671/debian/control 2011-09-22 22:12:11.000000000 +0000 @@ -4,8 +4,8 @@ Maintainer: Daniel Kahn Gillmor Build-Depends: debhelper (>= 7.0.50~), - python-setuptools, - python-support + python (>= 2.6.6-3~), + python-setuptools Standards-Version: 3.9.2 Homepage: http://trac-hacks.org/wiki/TagsPlugin Vcs-Git: git://lair.fifthhorseman.net/~dkg/trac-tags @@ -18,7 +18,6 @@ trac, python-pkg-resources Provides: ${python:Provides} -XB-Python-Version: ${python:Versions} Description: Tagging plugin for Trac wiki and issue tracking system The Trac Tags plugin implements both a generic tagging engine, and frontends for the Wiki and ticket systems. An extra text entry box is diff -Nru trac-tags-0.6.0+svn9419/debian/patches/fix-tags-link.patch trac-tags-0.6.0+svn10671/debian/patches/fix-tags-link.patch --- trac-tags-0.6.0+svn9419/debian/patches/fix-tags-link.patch 2011-04-07 22:39:57.000000000 +0000 +++ trac-tags-0.6.0+svn10671/debian/patches/fix-tags-link.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -diff --git a/tractags/wiki.py b/tractags/wiki.py -index 52cbb61..5e3a565 100644 ---- a/tractags/wiki.py -+++ b/tractags/wiki.py -@@ -138,7 +138,7 @@ class WikiTagInterface(Component): - tags_link = tag.a(_("view all tags"), href=req.href.tags()) - insert = tag.div(class_='field')( - tag.label( -- tag_("Tag under: (%(tags_link)s)", tags_link=tags_link), -+ tag_("Tag under") + ": (" + tags_link + ")", - tag.br(), - tag.input(id='tags', type='text', name='tags', size='50', - value=req.args.get('tags', ' '.join(self._page_tags(req)))), diff -Nru trac-tags-0.6.0+svn9419/debian/patches/series trac-tags-0.6.0+svn10671/debian/patches/series --- trac-tags-0.6.0+svn9419/debian/patches/series 2011-04-07 22:41:44.000000000 +0000 +++ trac-tags-0.6.0+svn10671/debian/patches/series 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -fix-tags-link.patch diff -Nru trac-tags-0.6.0+svn9419/debian/README.Debian trac-tags-0.6.0+svn10671/debian/README.Debian --- trac-tags-0.6.0+svn9419/debian/README.Debian 2011-03-03 17:11:46.000000000 +0000 +++ trac-tags-0.6.0+svn10671/debian/README.Debian 2011-09-22 22:04:18.000000000 +0000 @@ -8,7 +8,7 @@ one line to the [components] section of trac.ini: [components] - tagsplugin.* = enabled + tractags.* = enabled Configuration @@ -19,7 +19,7 @@ [tags] ignore_closed_tickets = false - ticket_fields = component, keyword + ticket_fields = component, keywords Troubleshooting @@ -55,4 +55,4 @@ http://trac-hacks.org/wiki/TagsPlugin - -- Daniel Kahn Gillmor , Thu, 3 Mar 2011 12:11:46 -0500 + -- Daniel Kahn Gillmor , Thu, 22 Sep 2011 17:59:21 -0400 diff -Nru trac-tags-0.6.0+svn9419/debian/rules trac-tags-0.6.0+svn10671/debian/rules --- trac-tags-0.6.0+svn9419/debian/rules 2011-03-03 06:02:59.000000000 +0000 +++ trac-tags-0.6.0+svn10671/debian/rules 2011-09-22 22:13:40.000000000 +0000 @@ -1,6 +1,6 @@ #!/usr/bin/make -f %: - dh $@ + dh --with python2 $@ override_dh_clean: dh_clean diff -Nru trac-tags-0.6.0+svn9419/setup.cfg trac-tags-0.6.0+svn10671/setup.cfg --- trac-tags-0.6.0+svn9419/setup.cfg 2010-11-11 22:32:41.000000000 +0000 +++ trac-tags-0.6.0+svn10671/setup.cfg 2011-09-22 09:02:18.000000000 +0000 @@ -1,5 +1,6 @@ [egg_info] tag_build = dev +tag_svn_revision = true [extract_messages] add_comments = TRANSLATOR: diff -Nru trac-tags-0.6.0+svn9419/setup.py trac-tags-0.6.0+svn10671/setup.py --- trac-tags-0.6.0+svn9419/setup.py 2010-11-11 22:32:41.000000000 +0000 +++ trac-tags-0.6.0+svn10671/setup.py 2011-09-22 09:02:18.000000000 +0000 @@ -1,4 +1,4 @@ -from setuptools import setup +from setuptools import setup, find_packages extra = {} @@ -22,12 +22,13 @@ setup( name='TracTags', version='0.7', - packages=['tractags'], + packages=find_packages(exclude=['*.tests']), package_data={'tractags' : [ 'templates/*.html', 'htdocs/js/*.js', 'htdocs/css/*.css', 'locale/*/LC_MESSAGES/*.mo', 'locale/.placeholder']}, # With acknowledgement to Muness Albrae for the original idea :) author='Alec Thomas', + author_email='alec@swapoff.org', license='BSD', url='http://trac-hacks.org/wiki/TagsPlugin', description='Tags plugin for Trac', @@ -35,5 +36,7 @@ dependency_links=['http://svn.edgewall.org/repos/genshi/trunk#egg=Genshi-dev'], install_requires=['Genshi >= 0.5', 'Trac >= 0.11'], extras_require={'Babel': 'Babel>= 0.9.5', 'Trac': 'Trac >= 0.12'}, + test_suite = 'tractags.tests.test_suite', + tests_require = [], **extra ) diff -Nru trac-tags-0.6.0+svn9419/tractags/admin.py trac-tags-0.6.0+svn10671/tractags/admin.py --- trac-tags-0.6.0+svn9419/tractags/admin.py 1970-01-01 00:00:00.000000000 +0000 +++ trac-tags-0.6.0+svn10671/tractags/admin.py 2011-09-22 09:02:18.000000000 +0000 @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2011 Itamar Ostricher +# Copyright (C) 2011 Steffen Hoffmann +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. +# + + +from trac.admin import IAdminPanelProvider +from trac.core import Component, implements +from trac.util.compat import sorted +from trac.web.chrome import Chrome +from tractags.api import TagSystem, _ + + +class TagChangeAdminPanel(Component): + + implements(IAdminPanelProvider) + + # AdminPanelProvider methods + def get_admin_panels(self, req): + if 'TAGS_ADMIN' in req.perm: + yield ('tags', _('Tag System'), 'replace', _('Replace')) + + def render_admin_panel(self, req, cat, page, version): + req.perm.require('TAGS_ADMIN') + data = {} + tag_system = TagSystem(self.env) + if req.method == 'POST': + # Replace Tag + allow_delete = req.args.get('allow_delete') + new_tag = req.args.get('tag_new_name').strip() + new_tag = not new_tag == u'' and new_tag or None + if not (allow_delete or new_tag): + data['error'] = _("""Selected current tag(s) and either + new tag or delete approval are required""") + else: + comment = req.args.get('comment', u'') + old_tags = req.args.get('tag_name') + if old_tags: + # Provide list regardless of single or multiple selection. + old_tags = isinstance(old_tags, list) and old_tags or \ + [old_tags] + tag_system.replace_tag(req, old_tags, new_tag, comment, + allow_delete) + data['selected'] = new_tag + + all_tags = sorted(tag_system.get_all_tags(req, '-dummy')) + data['tags'] = all_tags + try: + Chrome(self.env).add_textarea_grips(req) + except AttributeError: + # Element modifiers unavailable before Trac 0.12, skip gracefully. + pass + return 'admin_tag_change.html', data + diff -Nru trac-tags-0.6.0+svn9419/tractags/api.py trac-tags-0.6.0+svn10671/tractags/api.py --- trac-tags-0.6.0+svn9419/tractags/api.py 2010-11-11 22:32:41.000000000 +0000 +++ trac-tags-0.6.0+svn10671/tractags/api.py 2011-09-22 09:02:18.000000000 +0000 @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # # Copyright (C) 2006 Alec Thomas +# Copyright (C) 2011 Steffen Hoffmann # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. @@ -9,7 +10,9 @@ import re from pkg_resources import resource_filename -from trac.core import * +from trac.config import BoolOption +from trac.core import Component, ExtensionPoint, Interface, TracError, \ + implements from trac.resource import Resource from trac.perm import IPermissionRequestor, PermissionError from trac.web.chrome import add_warning @@ -17,7 +20,7 @@ from trac.util.text import to_unicode from trac.util.compat import set, groupby from trac.resource import IResourceManager, get_resource_url, \ - get_resource_description + get_resource_description from genshi import Markup # Import translation functions. @@ -42,6 +45,11 @@ class ITagProvider(Interface): + """The interface for Components providing per-realm tag storage and + manipulation methods. + + Change comments and reparenting are supported since tags-0.7. + """ def get_taggable_realm(): """Return the realm this provider supports tags on.""" @@ -57,10 +65,13 @@ def get_resource_tags(req, resource): """Get tags for a Resource object.""" - def set_resource_tags(req, resource, tags): + def set_resource_tags(req, resource, tags, comment=u''): """Set tags for a resource.""" - def remove_resource_tags(req, resource): + def reparent_resource_tags(req, old_resource, new_resource, comment=u''): + """Move tags, typically when renaming an existing resource.""" + + def remove_resource_tags(req, resource, comment=u''): """Remove all tags from a resource.""" def describe_tagged_resource(req, resource): @@ -117,7 +128,7 @@ resources[resource.id] = resource if not resources: - return + return args = [self.realm] + list(resources) # XXX Is this going to be excruciatingly slow? @@ -140,7 +151,7 @@ for row in cursor: yield row[0] - def set_resource_tags(self, req, resource, tags): + def set_resource_tags(self, req, resource, tags, comment=u''): assert resource.realm == self.realm if not self.check_permission(req.perm(resource), 'modify'): raise PermissionError(resource=resource, env=self.env) @@ -158,7 +169,28 @@ db.rollback() raise - def remove_resource_tags(self, req, resource): + def reparent_resource_tags(self, req, old_resource, new_resource, + comment=u''): + assert old_resource.realm == self.realm + assert new_resource.realm == self.realm + if not self.check_permission(req.perm(old_resource), 'modify') or \ + not self.check_permission(req.perm(new_resource), 'modify'): + raise PermissionError(resource=resource, env=self.env) + db = self.env.get_db_cnx() + try: + cursor = db.cursor() + cursor.execute(""" + UPDATE tags + SET name=%s + WHERE tagspace=%s + AND name=%s + """, (new_resource.id, self.realm, old_resource.id)) + db.commit() + except: + db.rollback() + raise + + def remove_resource_tags(self, req, resource, comment=u''): assert resource.realm == self.realm if not self.check_permission(req.perm(resource), 'modify'): raise PermissionError(resource=resource, env=self.env) @@ -183,6 +215,9 @@ tag_providers = ExtensionPoint(ITagProvider) + wiki_page_link = BoolOption('tags', 'wiki_page_link', True, + doc="Link a tag to the wiki page with same name, if it exists.") + # Internal variables _realm = re.compile('realm:(\w+)', re.U | re.I) _tag_split = re.compile('[,\s]+') @@ -219,10 +254,10 @@ req.href('tags')) add_warning(req, message) query = Query(query, attribute_handlers=all_attribute_handlers) - providers = [] + providers = set() for m in self._realm.finditer(query.as_string()): realm = m.group(1) - providers.append(self._get_provider(realm)) + providers.add(self._get_provider(realm)) if not providers: providers = self.tag_providers @@ -252,33 +287,83 @@ return set(self._get_provider(resource.realm) \ .get_resource_tags(req, resource)) - def set_tags(self, req, resource, tags): + def set_tags(self, req, resource, tags, comment=u''): """Set tags on a resource. Existing tags are replaced. """ - return self._get_provider(resource.realm) \ - .set_resource_tags(req, resource, set(tags)) + try: + return self._get_provider(resource.realm) \ + .set_resource_tags(req, resource, set(tags), comment) + except TypeError: + # Handle old style tag providers gracefully. + return self._get_provider(resource.realm) \ + .set_resource_tags(req, resource, set(tags)) - def add_tags(self, req, resource, tags): + def add_tags(self, req, resource, tags, comment=u''): """Add to existing tags on a resource.""" tags = set(tags) tags.update(self.get_tags(req, resource)) - self.set_tags(req, resource, tags) + try: + self.set_tags(req, resource, tags, comment) + except TypeError: + # Handle old style tag providers gracefully. + self.set_tags(req, resource, tags) + + def reparent_tags(self, req, old_resource, new_resource, comment=u''): + """Move tags, typically when renaming an existing resource. + + Tags can't be moved between different tag realms with intention. + """ + provider = self._get_provider(old_resource.realm) + provider.reparent_resource_tags(req, old_resource, new_resource, + comment) + + def replace_tag(self, req, old_tags, new_tag=None, comment=u'', + allow_delete=False): + """Replace one or more tags in all resources it exists/they exist in. + + Tag deletion is optionally allowed for convenience as well. + """ + # Provide list regardless of attribute type. + for resource_provider in self.tag_providers: + for resource, tags in \ + resource_provider.get_tagged_resources(req, old_tags): + old_tags = set(old_tags) + tags = set(tags) + if old_tags >= tags and not new_tag: + if allow_delete: + self.delete_tags(req, resource, None, comment) + else: + eff_tags = tags - old_tags + if new_tag: + eff_tags.add(new_tag) + # Prevent to touch resources without effective change. + if not eff_tags == tags and (allow_delete or new_tag): + self.set_tags(req, resource, eff_tags, comment) - def delete_tags(self, req, resource, tags=None): + def delete_tags(self, req, resource, tags=None, comment=u''): """Delete tags on a resource. If tags is None, remove all tags on the resource. """ provider = self._get_provider(resource.realm) if tags is None: - provider.remove_resource_tags(req, resource) + try: + provider.remove_resource_tags(req, resource, comment) + except TypeError: + # Handle old style tag providers gracefully. + provider.remove_resource_tags(req, resource) else: tags = set(tags) current_tags = set(provider.get_resource_tags(req, resource)) current_tags.difference_update(tags) - provider.set_resource_tags(req, resource, current_tags) + try: + provider.set_resource_tags(req, resource, current_tags, + comment) + except TypeError: + # Handle old style tag providers gracefully. + provider.set_resource_tags(req, resource, current_tags) def split_into_tags(self, text): """Split plain text into tags.""" @@ -293,27 +378,34 @@ self.env.log.warning('ITagProvider %r does not implement ' 'describe_tagged_resource()' % provider) return '' - + # IPermissionRequestor methods def get_permission_actions(self): - return ['TAGS_VIEW', 'TAGS_MODIFY'] + action = ['TAGS_VIEW', 'TAGS_MODIFY'] + actions = [action[0], (action[1], [action[0]]), + ('TAGS_ADMIN', action)] + return actions # IResourceManager methods def get_resource_realms(self): yield 'tag' def get_resource_url(self, resource, href, **kwargs): - page = WikiPage(self.env, resource.id) - if page.exists: - return get_resource_url(self.env, page.resource, href, **kwargs) + if self.wiki_page_link: + page = WikiPage(self.env, resource.id) + if page.exists: + return get_resource_url(self.env, page.resource, href, + **kwargs) return href("tags/'%s'" % unicode(resource.id).replace("'", "\\'")) - def get_resource_description(self, resource, format='default', context=None, - **kwargs): - page = WikiPage(self.env, resource.id) - if page.exists: - return get_resource_description(self.env, page.resource, format, - **kwargs) + + def get_resource_description(self, resource, format='default', + context=None, **kwargs): + if self.wiki_page_link: + page = WikiPage(self.env, resource.id) + if page.exists: + return get_resource_description(self.env, page.resource, + format, **kwargs) rid = to_unicode(resource.id) if format in ('compact', 'default'): return rid diff -Nru trac-tags-0.6.0+svn9419/tractags/__init__.py trac-tags-0.6.0+svn10671/tractags/__init__.py --- trac-tags-0.6.0+svn9419/tractags/__init__.py 2010-11-11 22:32:41.000000000 +0000 +++ trac-tags-0.6.0+svn10671/tractags/__init__.py 2011-09-22 09:02:18.000000000 +0000 @@ -16,3 +16,4 @@ import web_ui import macros import model +import admin diff -Nru trac-tags-0.6.0+svn9419/tractags/macros.py trac-tags-0.6.0+svn10671/tractags/macros.py --- trac-tags-0.6.0+svn9419/tractags/macros.py 2010-11-11 22:32:41.000000000 +0000 +++ trac-tags-0.6.0+svn10671/tractags/macros.py 2011-09-22 09:02:18.000000000 +0000 @@ -1,14 +1,18 @@ # -*- coding: utf-8 -*- # # Copyright (C) 2006 Alec Thomas +# Copyright (C) 2011 Itamar Ostricher # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. # +import re + from genshi.builder import tag as builder -from trac.core import Component, implements +from trac.config import ListOption +from trac.core import implements from trac.resource import Resource, get_resource_url, render_resource_link from trac.util import embedded_numbers from trac.util.compat import sorted, set @@ -17,6 +21,7 @@ from trac.wiki import IWikiMacroProvider from tractags.api import TagSystem, _ +from tractags.web_ui import TagTemplateProvider def render_cloud(env, req, cloud, renderer=None): @@ -58,11 +63,16 @@ return ul -class TagWikiMacros(Component): +class TagWikiMacros(TagTemplateProvider): """Provides macros, that utilize the tagging system in wiki.""" implements(IWikiMacroProvider) + exclude_realms = ListOption('tags', 'listtagged_exclude_realms', [], + doc="""Comma-separated list of realms to exclude from tags queries + by default, unless specifically included using "realm:realm-name" + in a query.""") + def __init__(self): # TRANSLATOR: Keep macro doc style formatting here, please. self.doc_cloud = _("""Display a tag cloud. @@ -109,9 +119,19 @@ return render_cloud(self.env, req, all_tags) elif name == 'ListTagged': + query = content req = formatter.req tag_system = TagSystem(self.env) - query_result = tag_system.query(req, content) + realms = [p.get_taggable_realm() for p in tag_system.tag_providers] + for realm in self.exclude_realms: + if not re.search('(^|\W)realm:%s(\W|$)' % (realm), query): + realms.remove(realm) + if len(realms) == 0: + return '' + query = '(%s) (%s)' % (query, ' or '.join(['realm:%s' % (r) + for r in realms])) + self.env.log.debug('LISTTAGGED_QUERY: ' + query) + query_result = tag_system.query(req, query) add_stylesheet(req, 'tags/css/tractags.css') def _link(resource): @@ -138,3 +158,4 @@ li = builder.li(_link(resource), ' ', desc) ul(li, '\n') return ul + diff -Nru trac-tags-0.6.0+svn9419/tractags/model.py trac-tags-0.6.0+svn10671/tractags/model.py --- trac-tags-0.6.0+svn9419/tractags/model.py 2010-11-11 22:32:41.000000000 +0000 +++ trac-tags-0.6.0+svn10671/tractags/model.py 2011-09-22 09:02:18.000000000 +0000 @@ -22,12 +22,13 @@ def environment_needs_upgrade(self, db): if self._need_migration(db): return True - cursor = db.cursor() try: - cursor.execute("select count(*) from tags") + cursor = db.cursor() + cursor.execute("SELECT COUNT(*) FROM tags") cursor.fetchone() return False - except: + except Exception, e: + self.log.error("DatabaseError: %s", e) db.rollback() return True @@ -35,13 +36,14 @@ self._upgrade_db(db) def _need_migration(self, db): - cursor = db.cursor() try: - cursor.execute("select count(*) from wiki_namespace") + cursor = db.cursor() + cursor.execute("SELECT COUNT(*) FROM wiki_namespace") cursor.fetchone() self.env.log.debug("tractags needs to migrate old data") return True - except: + except Exception, e: + # The expected outcome for any new/updated installation. db.rollback() return False @@ -63,11 +65,16 @@ # Migrate old data if self._need_migration(db): cursor = db.cursor() - cursor.execute("INSERT INTO tags (tagspace, name, tag) SELECT " - "'wiki', name, namespace FROM wiki_namespace") + cursor.execute(""" + INSERT INTO tags + (tagspace, name, tag) + SELECT 'wiki', name, namespace + FROM wiki_namespace + """) cursor.execute("DROP TABLE wiki_namespace") db.commit() - except: + except Exception, e: + self.log.error("DatabaseError: %s", e) db.rollback() raise diff -Nru trac-tags-0.6.0+svn9419/tractags/templates/admin_tag_change.html trac-tags-0.6.0+svn10671/tractags/templates/admin_tag_change.html --- trac-tags-0.6.0+svn9419/tractags/templates/admin_tag_change.html 1970-01-01 00:00:00.000000000 +0000 +++ trac-tags-0.6.0+svn10671/tractags/templates/admin_tag_change.html 2011-09-22 09:02:18.000000000 +0000 @@ -0,0 +1,68 @@ + + + + + + Tags + + +

Manage Tags

+
+
+
+

${error}

+
+ Replace +

Select an existing tag to replace with another one; two or more + to consolidate into one. The required new tag may actually + be an existing tag or a new one. A change comment will be added + if supported by the tag storage for the resource's realm. +

+
+ +
+
+ +
+
+ +

Beware: Attempting to replace one or more + tags will always add the new tag, but removal could fail + for some tags, because tags might be immutable depending + on internals of the tag provider they've been derived from. +

+

You'll need to investigate further on how to + change/remove these remaining tags closer to the tagged resource, + i.e. in your trac.ini see ticket_fields + option that lists fields to expose as tags for tag realm:ticket. +

+
+
+
+ +
+
+ + diff -Nru trac-tags-0.6.0+svn9419/tractags/tests/admin.py trac-tags-0.6.0+svn10671/tractags/tests/admin.py --- trac-tags-0.6.0+svn9419/tractags/tests/admin.py 1970-01-01 00:00:00.000000000 +0000 +++ trac-tags-0.6.0+svn10671/tractags/tests/admin.py 2011-09-22 09:02:18.000000000 +0000 @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2011 Odd Simon Simonsen +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. +# + +import unittest +import shutil +import tempfile + +from trac.test import EnvironmentStub, Mock + +from tractags.model import TagModelProvider +from tractags.admin import TagChangeAdminPanel + + +class TagChangeAdminPanelTestCase(unittest.TestCase): + + def setUp(self): + self.env = EnvironmentStub( + enable=['trac.*', 'tractags.*']) + self.env.path = tempfile.mkdtemp() + TagModelProvider(self.env).environment_created() + + self.tag_cap = TagChangeAdminPanel(self.env) + + def tearDown(self): + shutil.rmtree(self.env.path) + + def test_init(self): + # Empty test just to confirm that setUp and tearDown works + pass + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(TagChangeAdminPanelTestCase, 'test')) + return suite + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff -Nru trac-tags-0.6.0+svn9419/tractags/tests/api.py trac-tags-0.6.0+svn10671/tractags/tests/api.py --- trac-tags-0.6.0+svn9419/tractags/tests/api.py 1970-01-01 00:00:00.000000000 +0000 +++ trac-tags-0.6.0+svn10671/tractags/tests/api.py 2011-09-22 09:02:18.000000000 +0000 @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2011 Odd Simon Simonsen +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. +# + +import unittest +import shutil +import tempfile + +from trac.test import EnvironmentStub, Mock + +from tractags.model import TagModelProvider +from tractags.api import TagSystem + + +class TagSystemTestCase(unittest.TestCase): + + def setUp(self): + self.env = EnvironmentStub( + enable=['trac.*', 'tractags.*']) + self.env.path = tempfile.mkdtemp() + TagModelProvider(self.env).environment_created() + + self.tag_s = TagSystem(self.env) + + def tearDown(self): + shutil.rmtree(self.env.path) + + def test_init(self): + # Empty test just to confirm that setUp and tearDown works + pass + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(TagSystemTestCase, 'test')) + return suite + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff -Nru trac-tags-0.6.0+svn9419/tractags/tests/__init__.py trac-tags-0.6.0+svn10671/tractags/tests/__init__.py --- trac-tags-0.6.0+svn9419/tractags/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ trac-tags-0.6.0+svn10671/tractags/tests/__init__.py 2011-09-22 09:02:18.000000000 +0000 @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2011 Odd Simon Simonsen +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. +# + +from unittest import TestSuite + + + +def test_suite(): + suite = TestSuite() + + import tractags.tests.admin + suite.addTest(tractags.tests.admin.test_suite()) + + import tractags.tests.api + suite.addTest(tractags.tests.api.test_suite()) + + import tractags.tests.macros + suite.addTest(tractags.tests.macros.test_suite()) + + import tractags.tests.model + suite.addTest(tractags.tests.model.test_suite()) + + import tractags.tests.query + suite.addTest(tractags.tests.query.test_suite()) + + import tractags.tests.ticket + suite.addTest(tractags.tests.ticket.test_suite()) + + import tractags.tests.web_ui + suite.addTest(tractags.tests.web_ui.test_suite()) + + import tractags.tests.wiki + suite.addTest(tractags.tests.wiki.test_suite()) + + return suite diff -Nru trac-tags-0.6.0+svn9419/tractags/tests/macros.py trac-tags-0.6.0+svn10671/tractags/tests/macros.py --- trac-tags-0.6.0+svn9419/tractags/tests/macros.py 1970-01-01 00:00:00.000000000 +0000 +++ trac-tags-0.6.0+svn10671/tractags/tests/macros.py 2011-09-22 09:02:18.000000000 +0000 @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2011 Odd Simon Simonsen +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. +# + +import unittest +import shutil +import tempfile + +from trac.test import EnvironmentStub, Mock + +from tractags.model import TagModelProvider +from tractags.macros import TagWikiMacros + + +class ListTaggedMacroTestCase(unittest.TestCase): + + def setUp(self): + self.env = EnvironmentStub( + enable=['trac.*', 'tractags.*']) + self.env.path = tempfile.mkdtemp() + TagModelProvider(self.env).environment_created() + + self.tag_twm = TagWikiMacros(self.env) + + def tearDown(self): + shutil.rmtree(self.env.path) + + def test_init(self): + # Empty test just to confirm that setUp and tearDown works + pass + + +class TagCloudMacroTestCase(unittest.TestCase): + + def setUp(self): + self.env = EnvironmentStub( + enable=['trac.*', 'tractags.*']) + self.env.path = tempfile.mkdtemp() + TagModelProvider(self.env).environment_created() + + self.tag_twm = TagWikiMacros(self.env) + + def tearDown(self): + shutil.rmtree(self.env.path) + + def test_init(self): + # Empty test just to confirm that setUp and tearDown works + pass + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(ListTaggedMacroTestCase, 'test')) + suite.addTest(unittest.makeSuite(TagCloudMacroTestCase, 'test')) + return suite + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff -Nru trac-tags-0.6.0+svn9419/tractags/tests/model.py trac-tags-0.6.0+svn10671/tractags/tests/model.py --- trac-tags-0.6.0+svn9419/tractags/tests/model.py 1970-01-01 00:00:00.000000000 +0000 +++ trac-tags-0.6.0+svn10671/tractags/tests/model.py 2011-09-22 09:02:18.000000000 +0000 @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2011 Odd Simon Simonsen +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. +# + +import unittest +import shutil +import tempfile + +from trac.test import EnvironmentStub, Mock + +from tractags.model import TagModelProvider + +class TagsProviderTestCase(unittest.TestCase): + + def setUp(self): + self.env = EnvironmentStub( + enable=['trac.*', 'tractags.*']) + self.env.path = tempfile.mkdtemp() + self.tag_mp = TagModelProvider(self.env) + self.tag_mp.environment_created() + + def tearDown(self): + shutil.rmtree(self.env.path) + + # Helpers + + def _get_cursor_description(self, cursor): + # Cursors don't look the same across Trac versions + from trac import __version__ as trac_version + if trac_version < '0.12': + return cursor.description + else: + return cursor.cursor.description + + # Tests + + def test_table_exists(self): + db = self.env.get_db_cnx() + cursor = db.cursor() + tags = cursor.execute("SELECT * FROM tags").fetchall() + self.assertEquals([], tags) + self.assertEquals(['tagspace', 'name', 'tag'], + [col[0] for col in self._get_cursor_description(cursor)]) + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(TagsProviderTestCase, 'test')) + return suite + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff -Nru trac-tags-0.6.0+svn9419/tractags/tests/query.py trac-tags-0.6.0+svn10671/tractags/tests/query.py --- trac-tags-0.6.0+svn9419/tractags/tests/query.py 1970-01-01 00:00:00.000000000 +0000 +++ trac-tags-0.6.0+svn10671/tractags/tests/query.py 2011-09-22 09:02:18.000000000 +0000 @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2011 Odd Simon Simonsen +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. +# + +import unittest +import doctest + +import tractags.query + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(doctest.DocTestSuite(module=tractags.query)) + return suite + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff -Nru trac-tags-0.6.0+svn9419/tractags/tests/ticket.py trac-tags-0.6.0+svn10671/tractags/tests/ticket.py --- trac-tags-0.6.0+svn9419/tractags/tests/ticket.py 1970-01-01 00:00:00.000000000 +0000 +++ trac-tags-0.6.0+svn10671/tractags/tests/ticket.py 2011-09-22 09:02:18.000000000 +0000 @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2011 Odd Simon Simonsen +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. +# + +import unittest +import shutil +import tempfile + +from trac.test import EnvironmentStub, Mock + +from tractags.model import TagModelProvider +from tractags.ticket import TicketTagProvider + + +class TicketTagProviderTestCase(unittest.TestCase): + + def setUp(self): + self.env = EnvironmentStub( + enable=['trac.*', 'tractags.*']) + self.env.path = tempfile.mkdtemp() + TagModelProvider(self.env).environment_created() + + self.tag_tp = TicketTagProvider(self.env) + + def tearDown(self): + shutil.rmtree(self.env.path) + + def test_init(self): + # Empty test just to confirm that setUp and tearDown works + pass + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(TicketTagProviderTestCase, 'test')) + return suite + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff -Nru trac-tags-0.6.0+svn9419/tractags/tests/web_ui.py trac-tags-0.6.0+svn10671/tractags/tests/web_ui.py --- trac-tags-0.6.0+svn9419/tractags/tests/web_ui.py 1970-01-01 00:00:00.000000000 +0000 +++ trac-tags-0.6.0+svn10671/tractags/tests/web_ui.py 2011-09-22 09:02:18.000000000 +0000 @@ -0,0 +1,115 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2011 Odd Simon Simonsen +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. +# + +import unittest +import shutil +import tempfile + +from trac.test import EnvironmentStub, Mock +from trac.perm import PermissionSystem, PermissionCache, PermissionError +from trac.web.href import Href +from trac.web.session import DetachedSession + +from tractags.api import TagSystem +from tractags.model import TagModelProvider +from tractags.web_ui import TagTemplateProvider, TagRequestHandler + + +class TagTemplateProviderTestCase(unittest.TestCase): + + def setUp(self): + self.env = EnvironmentStub( + enable=['trac.*', 'tractags.*']) + self.env.path = tempfile.mkdtemp() + TagModelProvider(self.env).environment_created() + + # TagTemplateProvider is abstract, test using a subclass + self.tag_rh = TagRequestHandler(self.env) + + def tearDown(self): + shutil.rmtree(self.env.path) + + def test_template_dirs_added(self): + from trac.web.chrome import Chrome + self.assertTrue(self.tag_rh in Chrome(self.env).template_providers) + + +class TagRequestHandlerTestCase(unittest.TestCase): + + def setUp(self): + self.env = EnvironmentStub( + enable=['trac.*', 'tractags.*']) + self.env.path = tempfile.mkdtemp() + TagModelProvider(self.env).environment_created() + + self.tag_s = TagSystem(self.env) + self.tag_rh = TagRequestHandler(self.env) + + perm_system = PermissionSystem(self.env) + self.anonymous = PermissionCache(self.env, 'anonymous') + self.reader = PermissionCache(self.env, 'reader') + perm_system.grant_permission('reader', 'TAGS_VIEW') + self.writer = PermissionCache(self.env, 'writer') + perm_system.grant_permission('writer', 'TAGS_MODIFY') + self.admin = PermissionCache(self.env, 'admin') + perm_system.grant_permission('admin', 'TAGS_ADMIN') + + self.href = Href('/trac') + self.abs_href = Href('http://example.org/trac') + + def tearDown(self): + shutil.rmtree(self.env.path) + + def test_matches(self): + req = Mock(path_info='/tags', + authname='reader', + perm=self.reader + ) + self.assertEquals(True, self.tag_rh.match_request(req)) + + def test_matches_no_permission(self): + req = Mock(path_info='/tags', + authname='anonymous', + perm=self.anonymous + ) + self.assertEquals(False, self.tag_rh.match_request(req)) + + def test_get_main_page(self): + req = Mock(path_info='/tags', + args={}, + authname='reader', + perm=self.reader, + href=self.href, + method='GET', + chrome={}, + session=DetachedSession(self.env, 'reader'), + locale='', + tz='' + ) + template, data, content_type = self.tag_rh.process_request(req) + self.assertEquals('tag_view.html', template) + self.assertEquals(None, content_type) + self.assertEquals(['tag_body', 'tag_query', 'tag_realms', 'title'], + sorted(data.keys())) + + def test_get_main_page_no_permission(self): + req = Mock(path_info='/tags', + authname='anonymous', + perm=self.anonymous + ) + self.assertRaises(PermissionError, self.tag_rh.process_request, req) + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(TagTemplateProviderTestCase, 'test')) + suite.addTest(unittest.makeSuite(TagRequestHandlerTestCase, 'test')) + return suite + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff -Nru trac-tags-0.6.0+svn9419/tractags/tests/wiki.py trac-tags-0.6.0+svn10671/tractags/tests/wiki.py --- trac-tags-0.6.0+svn9419/tractags/tests/wiki.py 1970-01-01 00:00:00.000000000 +0000 +++ trac-tags-0.6.0+svn10671/tractags/tests/wiki.py 2011-09-22 09:02:18.000000000 +0000 @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2011 Odd Simon Simonsen +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. +# + +import unittest +import shutil +import tempfile + +from trac.test import EnvironmentStub, Mock + +from tractags.model import TagModelProvider +from tractags.wiki import WikiTagProvider + + +class WikiTagProviderTestCase(unittest.TestCase): + + def setUp(self): + self.env = EnvironmentStub( + enable=['trac.*', 'tractags.*']) + self.env.path = tempfile.mkdtemp() + TagModelProvider(self.env).environment_created() + + self.tag_wp = WikiTagProvider(self.env) + + def tearDown(self): + shutil.rmtree(self.env.path) + + def test_init(self): + # Empty test just to confirm that setUp and tearDown works + pass + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(WikiTagProviderTestCase, 'test')) + return suite + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff -Nru trac-tags-0.6.0+svn9419/tractags/ticket.py trac-tags-0.6.0+svn10671/tractags/ticket.py --- trac-tags-0.6.0+svn9419/tractags/ticket.py 2010-11-11 22:32:41.000000000 +0000 +++ trac-tags-0.6.0+svn10671/tractags/ticket.py 2011-09-22 09:02:18.000000000 +0000 @@ -10,6 +10,7 @@ from trac.core import * from tractags.api import TagSystem, ITagProvider, _ from trac.ticket.model import Ticket +from trac.util import get_reporter_id from trac.util.text import to_unicode from trac.util.compat import set, sorted from trac.config import * @@ -81,7 +82,7 @@ ticket = Ticket(self.env, resource.id) return self._ticket_tags(ticket) - def set_resource_tags(self, req, resource, tags): + def set_resource_tags(self, req, resource, tags, comment=u''): req.perm.require('TICKET_MODIFY', resource) split_into_tags = TagSystem(self.env).split_into_tags ticket = Ticket(self.env, resource.id) @@ -89,13 +90,13 @@ keywords = split_into_tags(ticket['keywords']) tags.difference_update(all.difference(keywords)) ticket['keywords'] = u' '.join(sorted(map(to_unicode, tags))) - ticket.save_changes(req.username, u'') + ticket.save_changes(get_reporter_id(req), comment) - def remove_resource_tags(self, req, resource): + def remove_resource_tags(self, req, resource, comment=u''): req.perm.require('TICKET_MODIFY', resource) ticket = Ticket(self.env, resource.id) ticket['keywords'] = u'' - ticket.save_changes(req.username, u'') + ticket.save_changes(get_reporter_id(req), comment) def describe_tagged_resource(self, req, resource): if not 'TICKET_VIEW' in req.perm(resource): diff -Nru trac-tags-0.6.0+svn9419/tractags/web_ui.py trac-tags-0.6.0+svn10671/tractags/web_ui.py --- trac-tags-0.6.0+svn9419/tractags/web_ui.py 2010-11-11 22:32:41.000000000 +0000 +++ trac-tags-0.6.0+svn10671/tractags/web_ui.py 2011-09-22 09:02:18.000000000 +0000 @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- # # Copyright (C) 2006 Alec Thomas +# Copyright (C) 2011 Steffen Hoffmann +# Copyright (C) 2011 Itamar Ostricher # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. @@ -8,51 +10,59 @@ import re import math -from trac.core import * -from trac.web.api import IRequestHandler -from trac.web.chrome import ITemplateProvider, INavigationContributor, \ - add_stylesheet, add_ctxtnav + from genshi.builder import tag as builder +from pkg_resources import resource_filename +from trac.config import ListOption +from trac.core import Component, ExtensionPoint, implements +from trac.mimeview import Context +from trac.resource import Resource from trac.util import to_unicode -from trac.util.text import CRLF from trac.util.compat import sorted, set, any -from tractags.api import TagSystem, ITagProvider, _, tag_ -from tractags.query import InvalidQuery -from trac.resource import Resource -from trac.mimeview import Context +from trac.util.text import CRLF +from trac.web.api import IRequestHandler +from trac.web.chrome import ITemplateProvider, INavigationContributor, \ + add_stylesheet, add_ctxtnav from trac.wiki.formatter import Formatter from trac.wiki.model import WikiPage +from tractags.api import TagSystem, ITagProvider, _, tag_ +from tractags.query import InvalidQuery + class TagTemplateProvider(Component): """Provides templates and static resources for the tags plugin.""" implements(ITemplateProvider) + abstract = True + # ITemplateProvider methods def get_templates_dirs(self): + """Return the absolute path of the directory containing the provided + Genshi templates. """ - Return the absolute path of the directory containing the provided - ClearSilver templates. - """ - from pkg_resources import resource_filename return [resource_filename(__name__, 'templates')] def get_htdocs_dirs(self): """Return the absolute path of a directory containing additional static resources (such as images, style sheets, etc). """ - from pkg_resources import resource_filename return [('tags', resource_filename(__name__, 'htdocs'))] -class TagRequestHandler(Component): +class TagRequestHandler(TagTemplateProvider): """Implements the /tags handler.""" - implements(IRequestHandler, INavigationContributor) + implements(INavigationContributor, IRequestHandler) tag_providers = ExtensionPoint(ITagProvider) + exclude_realms = ListOption('tags', 'exclude_realms', [], + doc="""Comma-separated list of realms to exclude from tags queries + by default, unless specifically included using "realm:realm-name" + in a query.""") + # INavigationContributor methods def get_active_navigation_item(self, req): if 'TAGS_VIEW' in req.perm: @@ -83,8 +93,13 @@ ) realms = [p.get_taggable_realm() for p in self.tag_providers] - checked_realms = [r for r in realms if r in req.args] or realms - data['tag_realms'] = [{'name': realm, 'checked': realm in checked_realms} + if not 'q' in req.args or [r for r in realms if r in req.args] == []: + for realm in realms: + if not realm in self.exclude_realms: + req.args[realm] = 'on' + checked_realms = [r for r in realms if r in req.args] + data['tag_realms'] = [{'name': realm, + 'checked': realm in checked_realms} for realm in realms] single_page = re.match(r"""(['"]?)(\w+)\1$""", query) @@ -107,8 +122,12 @@ if r in checked_realms]), query) self.env.log.debug('Tag query: %s', query) try: - data['tag_body'] = macros.expand_macro(formatter, macro, query) + # Query string without realm throws 'NotImplementedError'. + data['tag_body'] = len(checked_realms) > 0 and \ + macros.expand_macro(formatter, macro, query) \ + or '' except InvalidQuery, e: data['tag_query_error'] = to_unicode(e) data['tag_body'] = macros.expand_macro(formatter, 'TagCloud', '') return 'tag_view.html', data, None + diff -Nru trac-tags-0.6.0+svn9419/tractags/wiki.py trac-tags-0.6.0+svn10671/tractags/wiki.py --- trac-tags-0.6.0+svn9419/tractags/wiki.py 2010-11-11 22:32:41.000000000 +0000 +++ trac-tags-0.6.0+svn10671/tractags/wiki.py 2011-09-22 09:02:18.000000000 +0000 @@ -1,31 +1,35 @@ # -*- coding: utf-8 -*- # # Copyright (C) 2006 Alec Thomas +# Copyright (C) 2011 Steffen Hoffmann # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. # import re -from trac.core import * -from tractags.api import DefaultTagProvider, TagSystem, _, tag_ -from trac.web.chrome import add_stylesheet, add_script -from trac.wiki.api import IWikiSyntaxProvider -from trac.resource import Resource, render_resource_link, get_resource_url + +from genshi.builder import Markup, tag +from genshi.filters.transform import Transformer +from trac.core import Component, ExtensionPoint, implements from trac.mimeview.api import Context -from trac.web.api import ITemplateStreamFilter -from trac.wiki.api import IWikiPageManipulator, IWikiChangeListener -from trac.wiki.model import WikiPage +from trac.resource import Resource, render_resource_link, get_resource_url from trac.util.compat import sorted -from genshi.builder import tag -from genshi.filters.transform import Transformer +from trac.web.api import IRequestFilter, ITemplateStreamFilter +from trac.web.chrome import add_stylesheet +from trac.wiki.api import IWikiChangeListener, IWikiPageManipulator, \ + IWikiSyntaxProvider +from trac.wiki.model import WikiPage + +from tractags.api import DefaultTagProvider, TagSystem, _ +from tractags.web_ui import TagTemplateProvider class WikiTagProvider(DefaultTagProvider): - """Tag provider for the Wiki.""" + """Tag provider for Trac wiki.""" realm = 'wiki' - first_head = re.compile('=\s+([^=]*)=') + first_head = re.compile('=\s+([^=\n]*)={0,1}') def check_permission(self, perm, operation): map = {'view': 'WIKI_VIEW', 'modify': 'WIKI_MODIFY'} @@ -42,10 +46,38 @@ return '' -class WikiTagInterface(Component): +class WikiTagInterface(TagTemplateProvider): """Implement the user interface for tagging Wiki pages.""" - implements(ITemplateStreamFilter, IWikiPageManipulator, - IWikiChangeListener) + implements(IRequestFilter, ITemplateStreamFilter, + IWikiChangeListener, IWikiPageManipulator) + + PAGE_TEMPLATES_PREFIX = 'PageTemplates/' + + # IRequestFilter methods + def pre_process_request(self, req, handler): + return handler + + def post_process_request(self, req, template, data, content_type): + if req.method == 'GET' and req.path_info.startswith('/wiki/') and \ + req.args.get('action') == 'edit' and \ + req.args.get('template') and 'tags' not in req.args: + # Retrieve template resource to be queried for tags. + template_page = WikiPage(self.env,''.join( + [self.PAGE_TEMPLATES_PREFIX, + req.args.get('template')])) + if template_page and template_page.exists and \ + 'TAGS_VIEW' in req.perm(template_page.resource): + ts = TagSystem(self.env) + tags = sorted(ts.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, + template=req.args.get('template'))) + return (template, data, content_type) # ITemplateStreamFilter methods def filter_stream(self, req, method, filename, stream, data): @@ -85,9 +117,21 @@ def wiki_page_changed(self, page, version, t, comment, author, ipnr): pass + def wiki_page_renamed(self, page, old_name): + """Called when a page has been renamed (since Trac 0.12).""" + new_resource = Resource('wiki', page.name) + old_resource = Resource('wiki', old_name) + self.log.debug("Moving tags from %s to %s", + old_resource.id, new_resource.id) + tag_system = TagSystem(self.env) + # XXX Ugh. Hopefully this will be sufficient to fool any endpoints. + from trac.test import Mock, MockPerm + req = Mock(authname='anonymous', perm=MockPerm()) + tag_system.reparent_resource_tags(req, old_resource, new_resource) + def wiki_page_deleted(self, page): tag_system = TagSystem(self.env) - # XXX Ugh. Hopefully this will be sufficient to full any endpoints. + # XXX Ugh. Hopefully this will be sufficient to fool any endpoints. from trac.test import Mock, MockPerm req = Mock(authname='anonymous', perm=MockPerm()) tag_system.delete_tags(req, page.resource) @@ -133,17 +177,16 @@ return False def _wiki_edit(self, req, stream): - # TRANSLATOR: Label text for link to '/tags'. - tags_link = tag.a(_("view all tags"), href=req.href.tags()) - insert = tag.div(class_='field')( - tag.label( - tag_("Tag under: (%(tags_link)s)", tags_link=tags_link), - tag.br(), - tag.input(id='tags', type='text', name='tags', size='50', - value=req.args.get('tags', ' '.join(self._page_tags(req)))), - ) - ) + 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)