| [13858] | 1 | # -*- coding: utf-8 -*- |
|---|
| 2 | # |
|---|
| 3 | # Copyright (C) 2014 Steffen Hoffmann <hoff.st@web.de> |
|---|
| 4 | # |
|---|
| 5 | # This software is licensed as described in the file COPYING, which |
|---|
| 6 | # you should have received as part of this distribution. |
|---|
| 7 | # |
|---|
| 8 | |
|---|
| 9 | from trac.core import Component, implements |
|---|
| 10 | from trac.resource import Resource, ResourceNotFound, resource_exists |
|---|
| 11 | |
|---|
| 12 | from tracrpc.api import IXMLRPCHandler |
|---|
| 13 | |
|---|
| 14 | from tractags.api import TagSystem |
|---|
| 15 | from tractags.util import split_into_tags |
|---|
| 16 | |
|---|
| 17 | |
|---|
| 18 | class TagRPC(Component): |
|---|
| [13865] | 19 | """[extra] RPC interface for the tag system. |
|---|
| 20 | |
|---|
| [13867] | 21 | Access Trac resource tagging system through methods provided by |
|---|
| [13865] | 22 | [https://trac-hacks.org/wiki/XmlRpcPlugin XmlRpcPlugin]. |
|---|
| [13858] | 23 | """ |
|---|
| 24 | |
|---|
| 25 | implements(IXMLRPCHandler) |
|---|
| 26 | |
|---|
| 27 | def __init__(self): |
|---|
| [14154] | 28 | self.tag_system = TagSystem(self.env) |
|---|
| [13858] | 29 | |
|---|
| 30 | # IXMLRPCHandler methods |
|---|
| 31 | |
|---|
| 32 | def xmlrpc_namespace(self): |
|---|
| 33 | return 'tags' |
|---|
| 34 | |
|---|
| 35 | def xmlrpc_methods(self): |
|---|
| 36 | yield (None, ((list, str),), self.splitIntoTags) |
|---|
| 37 | yield ('TAGS_VIEW', ((list,),), self.getTaggableRealms) |
|---|
| 38 | yield ('TAGS_VIEW', ((dict,), (dict, list)), self.getAllTags) |
|---|
| 39 | yield ('TAGS_VIEW', ((list, str, str),), self.getTags) |
|---|
| 40 | yield ('TAGS_VIEW', ((list, str),), self.query) |
|---|
| 41 | yield ('TAGS_MODIFY', ((list, str, str, list), |
|---|
| 42 | (list, str, str, list, str)), self.addTags) |
|---|
| 43 | yield ('TAGS_MODIFY', ((list, str, str, list), |
|---|
| 44 | (list, str, str, list, str)), self.setTags) |
|---|
| 45 | |
|---|
| 46 | # Exported functions and TagSystem methods |
|---|
| 47 | |
|---|
| 48 | def addTags(self, req, realm, id, tags, comment=u''): |
|---|
| 49 | """Add the supplied list of tags to a taggable Trac resource. |
|---|
| 50 | |
|---|
| 51 | Returns the updated list of resource tags. |
|---|
| 52 | """ |
|---|
| 53 | resource = Resource(realm, id) |
|---|
| 54 | # Replicate TagSystem.add_tags() method due to xmlrpclib issue. |
|---|
| 55 | tags = set(tags) |
|---|
| 56 | tags.update(self._get_tags(req, resource)) |
|---|
| [14154] | 57 | self.tag_system.set_tags(req, resource, tags, comment) |
|---|
| [13858] | 58 | return self._get_tags(req, resource) |
|---|
| 59 | |
|---|
| 60 | def getAllTags(self, req, realms=[]): |
|---|
| 61 | """Returns a dict of all tags as keys and occurrences as values. |
|---|
| 62 | |
|---|
| 63 | If a realm list is supplied, only tags from these taggable realms |
|---|
| 64 | are shown. |
|---|
| 65 | """ |
|---|
| 66 | # Type conversion needed for content transfer of Counter object. |
|---|
| [14154] | 67 | return dict(self.tag_system.get_all_tags(req, realms)) |
|---|
| [13858] | 68 | |
|---|
| 69 | def getTaggableRealms(self, req): |
|---|
| 70 | """Returns the list of taggable Trac realms.""" |
|---|
| [14154] | 71 | return list(self.tag_system.get_taggable_realms()) |
|---|
| [13858] | 72 | |
|---|
| 73 | def getTags(self, req, realm, id): |
|---|
| 74 | """Returns the list of tags for a Trac resource.""" |
|---|
| 75 | return self._get_tags(req, Resource(realm, id)) |
|---|
| 76 | |
|---|
| 77 | def query(self, req, query_str): |
|---|
| 78 | """Returns a list of tagged Trac resources, whose tags match the |
|---|
| 79 | supplied tag query expression. |
|---|
| 80 | """ |
|---|
| 81 | # Type conversion needed for content transfer of Python set objects. |
|---|
| 82 | return [(resource.realm, resource.id, list(tags)) |
|---|
| [14154] | 83 | for resource, tags in self.tag_system.query(req, query_str)] |
|---|
| [13858] | 84 | |
|---|
| 85 | def setTags(self, req, realm, id, tags, comment=u''): |
|---|
| 86 | """Replace tags for a Trac resource with the supplied list of tags. |
|---|
| 87 | |
|---|
| 88 | Returns the updated list of resource tags. |
|---|
| 89 | """ |
|---|
| 90 | resource = Resource(realm, id) |
|---|
| 91 | self._get_tags(req, resource) # Trac resource exists? |
|---|
| [14154] | 92 | self.tag_system.set_tags(req, resource, tags, comment) |
|---|
| [13858] | 93 | return self._get_tags(req, resource) |
|---|
| 94 | |
|---|
| 95 | def splitIntoTags(self, req, tag_str): |
|---|
| 96 | """Returns a list of tags from a string. |
|---|
| 97 | |
|---|
| 98 | Comma, whitespace and combinations of these characters are recognized |
|---|
| 99 | as delimiter, that get stripped from the output. |
|---|
| 100 | """ |
|---|
| 101 | return split_into_tags(tag_str) |
|---|
| 102 | |
|---|
| 103 | # Private methods |
|---|
| 104 | |
|---|
| 105 | def _get_tags(self, req, resource): |
|---|
| 106 | if not resource_exists(self.env, resource): |
|---|
| 107 | raise ResourceNotFound('Resource "%r" does not exists' % resource) |
|---|
| 108 | # Workaround for ServiceException when calling TagSystem.get_tags(). |
|---|
| [14154] | 109 | provider = [p for p in self.tag_system.tag_providers |
|---|
| [13858] | 110 | if p.get_taggable_realm() == resource.realm][0] |
|---|
| 111 | # Type conversion needed for content transfer of Python set objects. |
|---|
| 112 | return list(provider.get_resource_tags(req, resource)) |
|---|