def _format_tagged(self, formatter, ns, target, label, fullmatch=None): """Tag and tag query expression link formatter.""" def unquote(text): """Strip all matching pairs of outer quotes from string.""" while re.match(WikiParser.QUOTED_STRING, text): # Remove outer whitespace after stripped quotation too. text = text[1:-1].strip() return text label = label and unquote(label.strip()) or '' target = unquote(target.strip()) tag_res = Resource('tag', target) if 'TAGS_VIEW' in formatter.perm(tag_res): context = formatter.context href = get_resource_url(self.env, tag_res, context.href) tag_sys = TagSystem(self.env) # Tag exists or tags query yields at least one match. if target in tag_sys.get_all_tags(formatter.req) or \ [(res, tags) for res, tags in tag_sys.query(formatter.req, target)]: if label: return tag.a(label, href=href) return render_resource_link(self.env, context, tag_res) else: return tag.a(label+'?', href=href, class_='missing tags', rel='nofollow') else: return tag.span(label, class_='forbidden tags', title=_("no permission to view tags"))
def process_request(self, req): """Process the request. For ClearSilver, return a (template_name, content_type) tuple, where `template` is the ClearSilver template to use (either a `neo_cs.CS` object, or the file name of the template), and `content_type` is the MIME type of the content. For Genshi, return a (template_name, data, content_type) tuple, where `data` is a dictionary of substitutions for the template. For both templating systems, "text/html" is assumed if `content_type` is `None`. Note that if template processing should not occur, this method can simply send the response itself and not return anything. """ query = req.args.get('q', '').lower() tagsystem = TagSystem(self.env) alltags = tagsystem.query(req) tags = {} for resource, _tags in alltags: for tag in _tags: if query in tag.lower(): tags[tag] = tags.setdefault(tag, 0) + 1 tags = sorted(tags.items(), key=lambda x: x[1], reverse=True) writeOut = str('\n'.join( ['%s|%d' % (name, number) for name, number in tags])) req.send_header('Content-length', str(len(writeOut))) req.end_headers() req.write(writeOut)
def process_request(self, req): """Process the request. For ClearSilver, return a (template_name, content_type) tuple, where `template` is the ClearSilver template to use (either a `neo_cs.CS` object, or the file name of the template), and `content_type` is the MIME type of the content. For Genshi, return a (template_name, data, content_type) tuple, where `data` is a dictionary of substitutions for the template. For both templating systems, "text/html" is assumed if `content_type` is `None`. Note that if template processing should not occur, this method can simply send the response itself and not return anything. """ query = req.args.get('q', '').lower() tagsystem = TagSystem(self.env) alltags = tagsystem.query(req) tags = {} for resource, _tags in alltags: for tag in _tags: if query in tag.lower(): tags[tag] = tags.setdefault(tag, 0) + 1 tags = sorted(tags.items(), key=lambda x: x[1], reverse=True) writeOut=str('\n'.join(['%s|%d' % (name, number) for name, number in tags])) req.send_header('Content-length', str(len(writeOut))) req.end_headers() req.write(writeOut)
def expand_macro(self, formatter, name, args): req = formatter.req add_stylesheet(req, 'hacks/css/trachacks.css') tag_system = TagSystem(self.env) releases = natural_sort([r.id for r, _ in tag_system.query(req, 'realm:wiki release')]) def link(resource): return render_resource_link(self.env, formatter.context, resource, 'compact') dl = builder.dl(class_='tracreleasesmacro') for release in releases: page = WikiPage(self.env, release) match = self.title_extract.search(page.text) if match: rel_title = '%s' % match.group(1).strip() else: rel_title = '%s' % release dl(builder.dt(link(Resource('wiki', release)))) dl(builder.dd(wiki_to_html(rel_title, self.env, req))) return dl
def load_pages_with_tags(env, req, tags): tag_system = TagSystem(env) pages = [] blog_resources = [i for i in tag_system.query(req, tags)] for resource, ignored in blog_resources: pages.append(WikiPage(env, resource)) return pages
def expand_macro(self, formatter, name, args): req = formatter.req add_stylesheet(req, 'hacks/css/trachacks.css') tag_system = TagSystem(self.env) categories = natural_sort([r.id for r, _ in tag_system.query(req, 'realm:wiki type')]) def link(resource): return render_resource_link(self.env, formatter.context, resource, 'compact') dl = builder.dl(class_='hacktypesmacro') for category in categories: page = WikiPage(self.env, category) match = self.title_extract.search(page.text) if match: cat_title = '%s' % match.group(1).strip() cat_body = self.title_extract.sub('', page.text, 1) else: cat_title = '%s' % category cat_body = page.text cat_body = self.self_extract.sub('', cat_body).strip() dl(builder.dt(link(Resource('wiki', category)))) dl(builder.dd(wiki_to_html(cat_body, self.env, req))) return dl
def Main(opts): """ Cross your fingers and pray """ env = Environment(opts.envpath) from tractags.api import TagSystem tlist = opts.tags or split_tags(env.config.get('blog', 'default_tag', 'blog')) tags = TagSystem(env) req = Mock(perm=MockPerm()) blog = tags.query(req, ' '.join(tlist + ['realm:wiki'])) cnx = env.get_db_cnx() for resource, page_tags in list(blog): try: page = WikiPage(env, version=1, name=resource.id) _, publish_time, author, _, _ = page.get_history().next() if opts.deleteonly: page.delete() continue categories = ' '.join([t for t in page_tags if t not in tlist]) page = WikiPage(env, name=resource.id) for version, version_time, version_author, version_comment, \ _ in page.get_history(): # Currently the basename of the post url is used due to # http://trac-hacks.org/ticket/2956 #name = resource.id.replace('/', '_') name = resource.id # extract title from text: fulltext = page.text match = _title_split_match(fulltext) if match: title = match.group(1) fulltext = match.group(2) else: title = name body = fulltext print "Adding post %s, v%s: %s" % (name, version, title) insert_blog_post(cnx, name, version, title, body, publish_time, version_time, version_comment, version_author, author, categories) reparent_blog_attachments(env, resource.id, name) continue cnx.commit() if opts.delete: page.delete() continue except: env.log.debug("Error loading wiki page %s" % resource.id, exc_info=True) print "Failed to add post %s, v%s: %s" % (name, version, title) cnx.rollback() cnx.close() return 1 cnx.close() return 0
def __call__(self, context, type_): if not (type_ and isinstance(type_, basestring) and len(type_)): raise ValidationError('No type selected?!') tags = TagSystem(self.env) req = FakeRequest(self.env) types = [r.id for r, _ in tags.query(req, 'realm:wiki type')] if type_ not in types: raise ValidationError('Selected type "%s" invalid?!' % str(type_)) return type_
def Main(opts): """ Cross your fingers and pray """ env = Environment(opts.envpath) from tractags.api import TagSystem tlist = opts.tags or split_tags( env.config.get('blog', 'default_tag', 'blog')) tags = TagSystem(env) req = Mock(perm=MockPerm()) blog = tags.query(req, ' '.join(tlist + ['realm:wiki'])) cnx = env.get_db_cnx() for resource, page_tags in list(blog): try: page = WikiPage(env, version=1, name=resource.id) _, publish_time, author, _, _ = page.get_history().next() if opts.deleteonly: page.delete() continue categories = ' '.join([t for t in page_tags if t not in tlist]) page = WikiPage(env, name=resource.id) for version, version_time, version_author, version_comment, \ _ in page.get_history(): # Currently the basename of the post url is used due to # http://trac-hacks.org/ticket/2956 #name = resource.id.replace('/', '_') name = resource.id # extract title from text: fulltext = page.text match = _title_split_match(fulltext) if match: title = match.group(1) fulltext = match.group(2) else: title = name body = fulltext print "Adding post %s, v%s: %s" % (name, version, title) insert_blog_post(cnx, name, version, title, body, publish_time, version_time, version_comment, version_author, author, categories) reparent_blog_attachments(env, resource.id, name) continue cnx.commit() if opts.delete: page.delete() continue except: env.log.debug("Error loading wiki page %s" % resource.id, exc_info=True) print "Failed to add post %s, v%s: %s" % (name, version, title) cnx.rollback() cnx.close() return 1 cnx.close() return 0
def process_request(self, req): data = {} tag_system = TagSystem(self.env) #match = self.path_match.match(req.path_info) #view = 'cloud' #if match.group(1): # view = match.group(1) # Hack types and their description types = [] for category in sorted([r.id for r, _ in tag_system.query(req, 'realm:wiki type')]): page = WikiPage(self.env, category) match = self.title_extract.search(page.text) if match: title = '%s' % match.group(1).strip() else: title = '%s' % category types.append((category, title)) # Trac releases releases = natural_sort([r.id for r, _ in tag_system.query(req, 'realm:wiki release')]) data['types'] = types data['releases'] = releases selected_releases = req.args.get('release', set(['0.10', '0.11', 'anyrelease'])) data['selected_releases'] = selected_releases hacks = self.fetch_hacks(req, data, [ t[0] for t in types ], selected_releases) add_stylesheet(req, 'tags/css/tractags.css') add_stylesheet(req, 'hacks/css/trachacks.css') add_script(req, 'hacks/js/trachacks.js') if req.path_info == '/newhack': return self.render_new(req, data, hacks)
def __call__(self, context, selected): if selected: tags = TagSystem(self.env) req = FakeRequest(self.env) releases = [r.id for r, _ in tags.query(req, 'realm:wiki release')] if isinstance(selected, (basestring, unicode)): selected = [ selected ] for s in selected: if s not in releases: hack = context.data.get('name', '') + \ context.data.get('type', '').title() self.env.log.error( "Invalid release %s selected for new hack %s" % (s, hack) ) raise ValidationError('Selected release "%s" invalid?!' % str(s)) return selected
class TagWikiSyntaxProvider(Component): """[opt] Provides tag:<expr> links. This extends TracLinks via WikiFormatting to point at tag queries or at specific tags. """ implements(IWikiSyntaxProvider) def __init__(self): self.tag_system = TagSystem(self.env) # IWikiSyntaxProvider methods def get_wiki_syntax(self): """Additional syntax for quoted tags or tag expression.""" tag_expr = ( r"(%s)" % (WikiParser.QUOTED_STRING) ) # Simple (tag|tagged):link syntax yield (r'''(?P<qualifier>tag(?:ged)?):(?P<tag_expr>%s)''' % tag_expr, lambda f, ns, match: self._format_tagged( f, match.group('qualifier'), match.group('tag_expr'), '%s:%s' % (match.group('qualifier'), match.group('tag_expr')))) # [(tag|tagged):link with label] yield (r'''\[tag(?:ged)?:''' r'''(?P<ltag_expr>%s)\s*(?P<tag_title>[^\]]+)?\]''' % tag_expr, lambda f, ns, match: self._format_tagged(f, 'tag', match.group('ltag_expr'), match.group('tag_title'))) def get_link_resolvers(self): return [('tag', self._format_tagged), ('tagged', self._format_tagged)] def _format_tagged(self, formatter, ns, target, label, fullmatch=None): """Tag and tag query expression link formatter.""" def unquote(text): """Strip all matching pairs of outer quotes from string.""" while re.match(WikiParser.QUOTED_STRING, text): # Remove outer whitespace after stripped quotation too. text = text[1:-1].strip() return text label = label and unquote(label.strip()) or '' target = unquote(target.strip()) query = target # Pop realms from query expression. all_realms = self.tag_system.get_taggable_realms(formatter.perm) realms = query_realms(target, all_realms) if realms: kwargs = dict((realm, 'on') for realm in realms) target = re.sub('(^|\W)realm:\S+(\W|$)', ' ', target).strip() else: kwargs = {} tag_res = Resource('tag', target) if 'TAGS_VIEW' not in formatter.perm(tag_res): return tag.span(label, class_='forbidden tags', title=_("no permission to view tags")) context = formatter.context href = self.tag_system.get_resource_url(tag_res, context.href, kwargs) if all_realms and ( target in self.tag_system.get_all_tags(formatter.req) or not iter_is_empty(self.tag_system.query(formatter.req, query))): # At least one tag provider is available and tag exists or # tags query yields at least one match. if label: return tag.a(label, href=href) return render_resource_link(self.env, context, tag_res) return tag.a(label+'?', href=href, class_='missing tags', rel='nofollow')
def expand_macro(self, formatter, name, content): env = self.env req = formatter.req args, kw = parse_args(content) # Use macro arguments (most likely wiki macro calls). realms = 'realm' in kw and kw['realm'].split('|') or [] tag_system = TagSystem(env) all_realms = [p.get_taggable_realm() for p in tag_system.tag_providers] self.all_realms = all_realms self.realms = realms if name == 'TagCloud': args.append(' or '.join(['realm:%s' % r for r in realms])) all_tags = tag_system.get_all_tags(req, ' '.join(args)) mincount = 'mincount' in kw and kw['mincount'] or None return self.render_cloud(req, all_tags, caseless_sort=self.caseless_sort, mincount=mincount) elif name == 'ListTagged': if _OBSOLETE_ARGS_RE.search(content): data = {'warning': 'obsolete_args'} else: data = {'warning': None} context=formatter.context # Use TagsQuery arguments (most likely wiki macro calls). cols = 'cols' in kw and kw['cols'] or self.default_cols format = 'format' in kw and kw['format'] or self.default_format query = args and args[0].strip() or None if query and not realms: # First read query arguments (most likely a web-UI call). for realm in all_realms: if re.search('(^|\W)realm:%s(\W|$)' % (realm), query): realms = realms and realms.append(realm) or [realm] if not realms: # Apply ListTagged defaults to macro call w/o realm. realms = list(set(all_realms)-set(self.exclude_realms)) if not realms: return '' else: self.query = query self.realms = realms query = '(%s) (%s)' % (query or '', ' or '.join(['realm:%s' % (r) for r in realms])) env.log.debug('LISTTAGGED_QUERY: ' + query) query_result = tag_system.query(req, query) if not query_result: return '' def _link(resource): if resource.realm == 'tag': # Keep realm selection in tag links. return builder.a(resource.id, href=self.get_href(req, tag=resource)) elif resource.realm == 'ticket': # Return resource link including ticket status dependend # class to allow for common Trac ticket link style. ticket = Ticket(env, resource.id) return builder.a('#%s' % ticket.id, class_=ticket['status'], href=formatter.href.ticket(ticket.id), title=shorten_line(ticket['summary'])) return render_resource_link(env, context, resource, 'compact') if format == 'table': cols = [col for col in cols.split('|') if col in self.supported_cols] # Use available translations from Trac core. try: labels = TicketSystem(env).get_ticket_field_labels() labels['id'] = _('Id') except AttributeError: # Trac 0.11 neither has the attribute nor uses i18n. labels = {'id': 'Id', 'description': 'Description'} labels['realm'] = _('Realm') labels['tags'] = _('Tags') headers = [{'label': labels.get(col)} for col in cols] data.update({'cols': cols, 'headers': headers}) results = sorted(query_result, key=lambda r: \ embedded_numbers(to_unicode(r[0].id))) results = self._paginate(req, results) rows = [] for resource, tags in results: desc = tag_system.describe_tagged_resource(req, resource) tags = sorted(tags) if tags: rendered_tags = [_link(Resource('tag', tag)) for tag in tags] if 'oldlist' == format: resource_link = _link(resource) else: desc = desc or \ get_resource_description(env, resource, context=context) resource_link = builder.a(desc, href=get_resource_url( env, resource, context.href)) if 'table' == format: cells = [] for col in cols: if col == 'id': cells.append(_link(resource)) # Don't duplicate links to resource in both. elif col == 'description' and 'id' in cols: cells.append(desc) elif col == 'description': cells.append(resource_link) elif col == 'realm': cells.append(resource.realm) elif col == 'tags': cells.append( builder([(tag, ' ') for tag in rendered_tags])) rows.append({'cells': cells}) continue rows.append({'desc': desc, 'rendered_tags': None, 'resource_link': _link(resource)}) data.update({'format': format, 'paginator': results, 'results': rows, 'tags_url': req.href('tags')}) # Work around a bug in trac/templates/layout.html, that causes a # TypeError for the wiki macro call, if we use add_link() alone. add_stylesheet(req, 'common/css/search.css') return Chrome(env).render_template( req, 'listtagged_results.html', data, 'text/html', True)
class HacksCache(Borg): _lock = threading.RLock() _initialized = False _hacks = dict() _types = dict() _releases = dict() def __init__(self, env): if self._initialized: self._log('already initialized') return else: env.log.debug('HacksCache %x: initializing' % id(self.__dict__)) self._lock.acquire() self._env = env self._tag_system = TagSystem(env) self._req = FakeRequest(env) self.id = id(self.__dict__) self._rebuild_cache() self._initialized = True self._lock.release() self._log('initialization finished') def __del__(self): self._log('Aiiieeeee') def _rebuild_cache(self): """ Rebuild the cache. Caller MUST hold the lock! """ self._log('rebuilding cache (initialized=%s)' % self._initialized) self._hacks = dict() self._types = dict() self._releases = dict() self._update_types() self._update_releases() query = 'realm:wiki (%s) (%s)' % \ (' or '.join(self._types), ' or '.join(self._releases)) for h in self._query_tags(query): self._update_hack(h, False, False) def _log(self, message): self._env.log.debug('HacksCache %x: %s' % (self.id, message)) def _query_tags(self, query): """ Helper to perform queries on cached tags system """ return [ r.id for r, _ in self._tag_system.query(self._req, query) ] def _update_types(self): """ Get/update list of hack types """ types = self._query_tags('realm:wiki type') keys = self._types.keys() # add types that have been added since last update for t in types: if t not in keys: self._types[t] = Tag(t, self._env, self._req, self._tag_system) # remove types that no longer exist for k in keys: if k not in types: del self._types[k] def _update_releases(self): """ Get/update list of Trac releases """ releases = self._query_tags('realm:wiki release') keys = self._releases.keys() # add releases that have been added since last update for r in releases: if r not in keys: self._releases[r] = Tag(r, self._env, self._req, self._tag_system) # remove releases that no longer exist for k in keys: if k not in releases: del self._releases[k] def _update_hack(self, name, full_update = True, check_delete = True): """ Get/update/delete hack properties """ if full_update: self._update_types() self._update_releases() delete = False if check_delete: page = WikiPage(self._env, name) if page.exists: tags = set(self._tag_system.get_tags(self._req, page.resource)) if not (tags.intersection(self._types) and tags.intersection(self._releases)): delete = True else: delete = True if delete: try: del self._hacks[name] self._log('deleted hack %s' % name) except: self._log('%s is no hack, skipped' % name) else: try: self._hacks[name].update() self._log('updated hack %s' % name) except: self._hacks[name] = Hack(name, self._env, self._req, self._tag_system) self._log('learned hack %s' % name) def _get(self, where, what): try: return where[what] except: return None def _get_all(self, where, sorted = False): v = where.values() if sorted: return natural_sort(v) else: return v # Public API def update(self, hack = None): """ Update cache for all or just the given hack """ if hack: self._update_hack(hack, True, True) else: self._lock.acquire() self._rebuild_cache() self._lock.release() def get_type(self, name): return self._get(self._types, name) def get_all_types(self, sorted = False): return self._get_all(self._types, sorted) def get_release(self, name): return self._get(self._releases, name) def get_all_releases(self, sorted = False): return self._get_all(self._releases, sorted) def get_hack(self, name): return self._get(self._hacks, name) def get_all_hacks(self, sorted = False): return self._get_all(self._hacks, sorted)
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))
class HacksCache(Borg): _lock = threading.RLock() _initialized = False _hacks = dict() _types = dict() _releases = dict() def __init__(self, env): if self._initialized: self._log('already initialized') return else: env.log.debug('HacksCache %x: initializing' % id(self.__dict__)) self._lock.acquire() self._env = env self._tag_system = TagSystem(env) self._req = FakeRequest(env) self.id = id(self.__dict__) self._rebuild_cache() self._initialized = True self._lock.release() self._log('initialization finished') def __del__(self): self._log('Aiiieeeee') def _rebuild_cache(self): """ Rebuild the cache. Caller MUST hold the lock! """ self._log('rebuilding cache (initialized=%s)' % self._initialized) self._hacks = dict() self._types = dict() self._releases = dict() self._update_types() self._update_releases() query = 'realm:wiki (%s) (%s)' % \ (' or '.join(self._types), ' or '.join(self._releases)) for h in self._query_tags(query): self._update_hack(h, False, False) def _log(self, message): self._env.log.debug('HacksCache %x: %s' % (self.id, message)) def _query_tags(self, query): """ Helper to perform queries on cached tags system """ return [r.id for r, _ in self._tag_system.query(self._req, query)] def _update_types(self): """ Get/update list of hack types """ types = self._query_tags('realm:wiki type') keys = self._types.keys() # add types that have been added since last update for t in types: if t not in keys: self._types[t] = Tag(t, self._env, self._req, self._tag_system) # remove types that no longer exist for k in keys: if k not in types: del self._types[k] def _update_releases(self): """ Get/update list of Trac releases """ releases = self._query_tags('realm:wiki release') keys = self._releases.keys() # add releases that have been added since last update for r in releases: if r not in keys: self._releases[r] = Tag(r, self._env, self._req, self._tag_system) # remove releases that no longer exist for k in keys: if k not in releases: del self._releases[k] def _update_hack(self, name, full_update=True, check_delete=True): """ Get/update/delete hack properties """ if full_update: self._update_types() self._update_releases() delete = False if check_delete: page = WikiPage(self._env, name) if page.exists: tags = set(self._tag_system.get_tags(self._req, page.resource)) if not (tags.intersection(self._types) and tags.intersection(self._releases)): delete = True else: delete = True if delete: try: del self._hacks[name] self._log('deleted hack %s' % name) except: self._log('%s is no hack, skipped' % name) else: try: self._hacks[name].update() self._log('updated hack %s' % name) except: self._hacks[name] = Hack(name, self._env, self._req, self._tag_system) self._log('learned hack %s' % name) def _get(self, where, what): try: return where[what] except: return None def _get_all(self, where, sorted=False): v = where.values() if sorted: return natural_sort(v) else: return v # Public API def update(self, hack=None): """ Update cache for all or just the given hack """ if hack: self._update_hack(hack, True, True) else: self._lock.acquire() self._rebuild_cache() self._lock.release() def get_type(self, name): return self._get(self._types, name) def get_all_types(self, sorted=False): return self._get_all(self._types, sorted) def get_release(self, name): return self._get(self._releases, name) def get_all_releases(self, sorted=False): return self._get_all(self._releases, sorted) def get_hack(self, name): return self._get(self._hacks, name) def get_all_hacks(self, sorted=False): return self._get_all(self._hacks, sorted)
def expand_macro(self, formatter, name, content, realms=[]): """Evaluate macro call and render results. Calls from web-UI come with pre-processed realm selection. """ env = self.env req = formatter.req tag_system = TagSystem(env) all_realms = tag_system.get_taggable_realms() if not all_realms: # Tag providers are required, no result without at least one. return '' args, kw = parse_args(content) query = args and args[0].strip() or None if not realms: # Check macro arguments for realms (typical wiki macro call). realms = 'realm' in kw and kw['realm'].split('|') or [] if query: # Add realms from query expression. realms.extend(query_realms(query, all_realms)) # Remove redundant realm selection for performance. if set(realms) == all_realms: query = re.sub('(^|\W)realm:\S+(\W|$)', ' ', query).strip() if name == 'TagCloud': # Set implicit 'all tagged realms' as default. if not realms: realms = all_realms if query: all_tags = Counter() # Require per resource query including view permission checks. for resource, tags in tag_system.query(req, query): all_tags.update(tags) else: # Allow faster per tag query, side steps permission checks. all_tags = tag_system.get_all_tags(req, realms=realms) mincount = 'mincount' in kw and kw['mincount'] or None return self.render_cloud(req, all_tags, caseless_sort=self.caseless_sort, mincount=mincount, realms=realms) elif name == 'ListTagged': if content and _OBSOLETE_ARGS_RE.search(content): data = {'warning': 'obsolete_args'} else: data = {'warning': None} context = formatter.context # Use TagsQuery arguments (most likely wiki macro calls). cols = 'cols' in kw and kw['cols'] or self.default_cols format = 'format' in kw and kw['format'] or self.default_format if not realms: # Apply ListTagged defaults to macro call w/o realm. realms = list(set(all_realms) - set(self.exclude_realms)) if not realms: return '' query = '(%s) (%s)' % (query or '', ' or '.join( ['realm:%s' % (r) for r in realms])) query_result = tag_system.query(req, query) excludes = [ exc.strip() for exc in kw.get('exclude', '').split(':') if exc.strip() ] if excludes and query_result: filtered_result = [(resource, tags) for resource, tags in query_result if not any( fnmatchcase(resource.id, exc) for exc in excludes)] query_result = filtered_result if not query_result: return '' def _link(resource): if resource.realm == 'tag': # Keep realm selection in tag links. return builder.a(resource.id, href=self.get_href(req, realms, tag=resource)) elif resource.realm == 'ticket': # Return resource link including ticket status dependend # class to allow for common Trac ticket link style. ticket = Ticket(env, resource.id) return builder.a('#%s' % ticket.id, class_=ticket['status'], href=formatter.href.ticket(ticket.id), title=shorten_line(ticket['summary'])) return render_resource_link(env, context, resource, 'compact') if format == 'table': cols = [ col for col in cols.split('|') if col in self.supported_cols ] # Use available translations from Trac core. try: labels = TicketSystem(env).get_ticket_field_labels() labels['id'] = _('Id') except AttributeError: # Trac 0.11 neither has the attribute nor uses i18n. labels = {'id': 'Id', 'description': 'Description'} labels['realm'] = _('Realm') labels['tags'] = _('Tags') headers = [{'label': labels.get(col)} for col in cols] data.update({'cols': cols, 'headers': headers}) try: results = sorted( query_result, key=lambda r: embedded_numbers(to_unicode(r[0].id))) except (InvalidQuery, InvalidTagRealm), e: return system_message(_("ListTagged macro error"), e) results = self._paginate(req, results, realms) rows = [] for resource, tags in results: desc = tag_system.describe_tagged_resource(req, resource) tags = sorted(tags) wiki_desc = format_to_oneliner(env, context, desc) if tags: rendered_tags = [ _link(Resource('tag', tag)) for tag in tags ] if 'oldlist' == format: resource_link = _link(resource) else: resource_link = builder.a(wiki_desc, href=get_resource_url( env, resource, context.href)) if 'table' == format: cells = [] for col in cols: if col == 'id': cells.append(_link(resource)) # Don't duplicate links to resource in both. elif col == 'description' and 'id' in cols: cells.append(wiki_desc) elif col == 'description': cells.append(resource_link) elif col == 'realm': cells.append(resource.realm) elif col == 'tags': cells.append( builder([(tag, ' ') for tag in rendered_tags])) rows.append({'cells': cells}) continue rows.append({ 'desc': wiki_desc, 'rendered_tags': None, 'resource_link': _link(resource) }) data.update({ 'format': format, 'paginator': results, 'results': rows, 'tags_url': req.href('tags') }) # Work around a bug in trac/templates/layout.html, that causes a # TypeError for the wiki macro call, if we use add_link() alone. add_stylesheet(req, 'common/css/search.css') return Chrome(env).render_template(req, 'listtagged_results.html', data, 'text/html', True)
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='')], [])
def expand_macro(self, formatter, name, content, realms=[]): """Evaluate macro call and render results. Calls from web-UI come with pre-processed realm selection. """ env = self.env req = formatter.req tag_system = TagSystem(env) all_realms = tag_system.get_taggable_realms() if not all_realms: # Tag providers are required, no result without at least one. return '' args, kw = parse_args(content) query = args and args[0].strip() or None if not realms: # Check macro arguments for realms (typical wiki macro call). realms = 'realm' in kw and kw['realm'].split('|') or [] if query: # Add realms from query expression. realms.extend(query_realms(query, all_realms)) # Remove redundant realm selection for performance. if set(realms) == all_realms: query = re.sub('(^|\W)realm:\S+(\W|$)', ' ', query).strip() if name == 'TagCloud': # Set implicit 'all tagged realms' as default. if not realms: realms = all_realms if query: all_tags = Counter() # Require per resource query including view permission checks. for resource, tags in tag_system.query(req, query): all_tags.update(tags) else: # Allow faster per tag query, side steps permission checks. all_tags = tag_system.get_all_tags(req, realms=realms) mincount = 'mincount' in kw and kw['mincount'] or None return self.render_cloud(req, all_tags, caseless_sort=self.caseless_sort, mincount=mincount, realms=realms) elif name == 'ListTagged': if content and _OBSOLETE_ARGS_RE.search(content): data = {'warning': 'obsolete_args'} else: data = {'warning': None} context = formatter.context # Use TagsQuery arguments (most likely wiki macro calls). cols = 'cols' in kw and kw['cols'] or self.default_cols format = 'format' in kw and kw['format'] or self.default_format if not realms: # Apply ListTagged defaults to macro call w/o realm. realms = list(set(all_realms)-set(self.exclude_realms)) if not realms: return '' query = '(%s) (%s)' % (query or '', ' or '.join(['realm:%s' % (r) for r in realms])) query_result = tag_system.query(req, query) excludes = [exc.strip() for exc in kw.get('exclude', '' ).split(':') if exc.strip()] if excludes and query_result: filtered_result = [(resource, tags) for resource, tags in query_result if not any(fnmatchcase(resource.id, exc) for exc in excludes)] query_result = filtered_result if not query_result: return '' def _link(resource): if resource.realm == 'tag': # Keep realm selection in tag links. return builder.a(resource.id, href=self.get_href(req, realms, tag=resource)) elif resource.realm == 'ticket': # Return resource link including ticket status dependend # class to allow for common Trac ticket link style. ticket = Ticket(env, resource.id) return builder.a('#%s' % ticket.id, class_=ticket['status'], href=formatter.href.ticket(ticket.id), title=shorten_line(ticket['summary'])) return render_resource_link(env, context, resource, 'compact') if format == 'table': cols = [col for col in cols.split('|') if col in self.supported_cols] # Use available translations from Trac core. try: labels = TicketSystem(env).get_ticket_field_labels() labels['id'] = _('Id') except AttributeError: # Trac 0.11 neither has the attribute nor uses i18n. labels = {'id': 'Id', 'description': 'Description'} labels['realm'] = _('Realm') labels['tags'] = _('Tags') headers = [{'label': labels.get(col)} for col in cols] data.update({'cols': cols, 'headers': headers}) results = sorted(query_result, key=lambda r: \ embedded_numbers(to_unicode(r[0].id))) results = self._paginate(req, results, realms) rows = [] for resource, tags in results: desc = tag_system.describe_tagged_resource(req, resource) tags = sorted(tags) wiki_desc = format_to_oneliner(env, context, desc) if tags: rendered_tags = [_link(Resource('tag', tag)) for tag in tags] if 'oldlist' == format: resource_link = _link(resource) else: resource_link = builder.a(wiki_desc, href=get_resource_url( env, resource, context.href)) if 'table' == format: cells = [] for col in cols: if col == 'id': cells.append(_link(resource)) # Don't duplicate links to resource in both. elif col == 'description' and 'id' in cols: cells.append(wiki_desc) elif col == 'description': cells.append(resource_link) elif col == 'realm': cells.append(resource.realm) elif col == 'tags': cells.append( builder([(tag, ' ') for tag in rendered_tags])) rows.append({'cells': cells}) continue rows.append({'desc': wiki_desc, 'rendered_tags': None, 'resource_link': _link(resource)}) data.update({'format': format, 'paginator': results, 'results': rows, 'tags_url': req.href('tags')}) # Work around a bug in trac/templates/layout.html, that causes a # TypeError for the wiki macro call, if we use add_link() alone. add_stylesheet(req, 'common/css/search.css') return Chrome(env).render_template( req, 'listtagged_results.html', data, 'text/html', True)
def expand_macro(self, formatter, name, content): env = self.env req = formatter.req args, kw = parse_args(content) # Use macro arguments (most likely wiki macro calls). realms = 'realm' in kw and kw['realm'].split('|') or [] tag_system = TagSystem(env) all_realms = [p.get_taggable_realm() for p in tag_system.tag_providers] self.all_realms = all_realms self.realms = realms if name == 'TagCloud': args.append(' or '.join(['realm:%s' % r for r in realms])) all_tags = tag_system.get_all_tags(req, ' '.join(args)) mincount = 'mincount' in kw and kw['mincount'] or None return self.render_cloud(req, all_tags, caseless_sort=self.caseless_sort, mincount=mincount) elif name == 'ListTagged': if _OBSOLETE_ARGS_RE.search(content): data = {'warning': 'obsolete_args'} else: data = {'warning': None} context = formatter.context # Use TagsQuery arguments (most likely wiki macro calls). cols = 'cols' in kw and kw['cols'] or self.default_cols format = 'format' in kw and kw['format'] or self.default_format query = args and args[0].strip() or None if query and not realms: # First read query arguments (most likely a web-UI call). for realm in all_realms: if re.search('(^|\W)realm:%s(\W|$)' % (realm), query): realms = realms and realms.append(realm) or [realm] if not realms: # Apply ListTagged defaults to macro call w/o realm. realms = list(set(all_realms) - set(self.exclude_realms)) if not realms: return '' else: self.query = query self.realms = realms query = '(%s) (%s)' % (query or '', ' or '.join( ['realm:%s' % (r) for r in realms])) env.log.debug('LISTTAGGED_QUERY: ' + query) query_result = tag_system.query(req, query) if not query_result: return '' def _link(resource): if resource.realm == 'tag': # Keep realm selection in tag links. return builder.a(resource.id, href=self.get_href(req, tag=resource)) elif resource.realm == 'ticket': # Return resource link including ticket status dependend # class to allow for common Trac ticket link style. ticket = Ticket(env, resource.id) return builder.a('#%s' % ticket.id, class_=ticket['status'], href=formatter.href.ticket(ticket.id), title=shorten_line(ticket['summary'])) return render_resource_link(env, context, resource, 'compact') if format == 'table': cols = [ col for col in cols.split('|') if col in self.supported_cols ] # Use available translations from Trac core. try: labels = TicketSystem(env).get_ticket_field_labels() labels['id'] = _('Id') except AttributeError: # Trac 0.11 neither has the attribute nor uses i18n. labels = {'id': 'Id', 'description': 'Description'} labels['realm'] = _('Realm') labels['tags'] = _('Tags') headers = [{'label': labels.get(col)} for col in cols] data.update({'cols': cols, 'headers': headers}) results = sorted(query_result, key=lambda r: \ embedded_numbers(to_unicode(r[0].id))) results = self._paginate(req, results) rows = [] for resource, tags in results: desc = tag_system.describe_tagged_resource(req, resource) tags = sorted(tags) if tags: rendered_tags = [ _link(Resource('tag', tag)) for tag in tags ] if 'oldlist' == format: resource_link = _link(resource) else: desc = desc or \ get_resource_description(env, resource, context=context) resource_link = builder.a(desc, href=get_resource_url( env, resource, context.href)) if 'table' == format: cells = [] for col in cols: if col == 'id': cells.append(_link(resource)) # Don't duplicate links to resource in both. elif col == 'description' and 'id' in cols: cells.append(desc) elif col == 'description': cells.append(resource_link) elif col == 'realm': cells.append(resource.realm) elif col == 'tags': cells.append( builder([(tag, ' ') for tag in rendered_tags])) rows.append({'cells': cells}) continue rows.append({ 'desc': desc, 'rendered_tags': None, 'resource_link': _link(resource) }) data.update({ 'format': format, 'paginator': results, 'results': rows, 'tags_url': req.href('tags') }) # Work around a bug in trac/templates/layout.html, that causes a # TypeError for the wiki macro call, if we use add_link() alone. add_stylesheet(req, 'common/css/search.css') return Chrome(env).render_template(req, 'listtagged_results.html', data, 'text/html', True)
def fetch_hacks(self, req, data, types, releases): """Return a list of hacks in the form [votes, rank, resource, tags, title] """ tag_system = TagSystem(self.env) vote_system = VoteSystem(self.env) query = 'realm:wiki (%s) (%s)' % \ (' or '.join(releases), ' or '.join(types)) self.env.log.debug(query) tagged = tag_system.query(req, query) # Limit try: limit = int(req.args.get('limit', self.limit)) data['limit_message'] = 'top %s' % limit except ValueError: data['limit_message'] = 'all' limit = 9999 data['limit'] = limit # Query q = req.args.get('q', '') data['query'] = q query = Query(q.lower()) # Build hacks list hacks = [] for resource, tags in tagged: page = WikiPage(self.env, resource.id) if q: text = page.name.lower() + page.text.lower() + ' '.join(tags) if not query(text): continue _, count, _ = vote_system.get_vote_counts(resource) match = self.title_extract.search(page.text) count_string = pluralise(count, 'vote') if match: title = '%s (%s)' % (match.group(1).strip(), count_string) else: title = '%s' % count_string hacks.append([count, None, resource, tags, title]) # Rank total_hack_count = len(hacks) hacks = sorted(hacks, key=lambda i: -i[0]) remainder = hacks[limit:] hacks = hacks[:limit] + random.sample(remainder, min(limit, len(remainder))) # Navigation #if len(hacks) >= limit: # add_ctxtnav(req, builder.a('More', href='?action=more')) # limit = len(hacks) # data['limit'] = data['limit_message'] = limit #else: # add_ctxtnav(req, 'More') #if q or limit != self.limit: # add_ctxtnav(req, builder.a('Default', href='?action=default')) #else: # add_ctxtnav(req, 'Default') #if total_hack_count > limit: # add_ctxtnav(req, builder.a('All', href='?action=all')) #else: # add_ctxtnav(req, 'All') #if limit > 10: # limit = min(limit, len(hacks)) # add_ctxtnav(req, builder.a('Less', href='?action=less')) #else: # add_ctxtnav(req, 'Less') #for i, hack in enumerate(hacks): # hack[1] = i return hacks
class TagWikiSyntaxProvider(Component): """[opt] Provides tag:<expr> links. This extends TracLinks via WikiFormatting to point at tag queries or at specific tags. """ implements(IWikiSyntaxProvider) def __init__(self): self.tag_system = TagSystem(self.env) # IWikiSyntaxProvider methods def get_wiki_syntax(self): """Additional syntax for quoted tags or tag expression.""" tag_expr = (r"(%s)" % (WikiParser.QUOTED_STRING)) # Simple (tag|tagged):link syntax yield ( r'''(?P<qualifier>tag(?:ged)?):(?P<tag_expr>%s)''' % tag_expr, lambda f, ns, match: self._format_tagged( f, match.group('qualifier'), match.group('tag_expr'), '%s:%s' % (match.group('qualifier'), match.group('tag_expr')))) # [(tag|tagged):link with label] yield ( r'''\[tag(?:ged)?:''' r'''(?P<ltag_expr>%s)\s*(?P<tag_title>[^\]]+)?\]''' % tag_expr, lambda f, ns, match: self._format_tagged( f, 'tag', match.group('ltag_expr'), match.group('tag_title'))) def get_link_resolvers(self): return [('tag', self._format_tagged), ('tagged', self._format_tagged)] def _format_tagged(self, formatter, ns, target, label, fullmatch=None): """Tag and tag query expression link formatter.""" def unquote(text): """Strip all matching pairs of outer quotes from string.""" while re.match(WikiParser.QUOTED_STRING, text): # Remove outer whitespace after stripped quotation too. text = text[1:-1].strip() return text label = label and unquote(label.strip()) or '' target = unquote(target.strip()) query = target # Pop realms from query expression. all_realms = self.tag_system.get_taggable_realms(formatter.perm) realms = query_realms(target, all_realms) if realms: kwargs = dict((realm, 'on') for realm in realms) target = re.sub('(^|\W)realm:\S+(\W|$)', ' ', target).strip() else: kwargs = {} tag_res = Resource('tag', target) if 'TAGS_VIEW' not in formatter.perm(tag_res): return tag.span(label, class_='forbidden tags', title=_("no permission to view tags")) context = formatter.context href = self.tag_system.get_resource_url(tag_res, context.href, kwargs) if all_realms and (target in self.tag_system.get_all_tags( formatter.req) or not iter_is_empty( self.tag_system.query(formatter.req, query))): # At least one tag provider is available and tag exists or # tags query yields at least one match. if label: return tag.a(label, href=href) return render_resource_link(self.env, context, tag_res) return tag.a(label + '?', href=href, class_='missing tags', rel='nofollow')
def expand_macro(self, formatter, name, args): req = formatter.req tag_system = TagSystem(self.env) all_releases = natural_sort([r.id for r, _ in tag_system.query(req, 'realm:wiki release')]) all_categories = sorted([r.id for r, _ in tag_system.query(req, 'realm:wiki type')]) hide_release_picker = False hide_fieldset_legend = False hide_fieldset_description = False other = [] if args: categories = [] releases = [] for arg in args.split(): if arg in all_releases: hide_release_picker = True releases.append(arg) elif arg in all_categories: categories.append(arg) else: other.append(arg) if len(categories) or len(releases): hide_fieldset_description = True if not len(categories): categories = all_categories elif len(categories) == 1: hide_fieldset_legend = True if not len(releases): releases = all_releases else: categories = all_categories releases = all_releases if 'update_th_filter' in req.args: show_releases = req.args.get('release', ['0.11']) if isinstance(show_releases, basestring): show_releases = [show_releases] req.session['th_release_filter'] = ','.join(show_releases) else: show_releases = req.session.get('th_release_filter', '0.11').split(',') output = "" if not hide_release_picker: style = "text-align:right; padding-top:1em; margin-right:5em;" form = builder.form('\n', style=style, method="get") style = "font-size:xx-small;" span = builder.span("Show hacks for releases:", style=style) for version in releases: inp = builder.input(version, type_="checkbox", name="release", value=version) if version in show_releases: inp(checked="checked") span(inp, '\n') style = "font-size:xx-small; padding:0; border:solid 1px black;" span(builder.input(name="update_th_filter", type_="submit", style=style, value="Update"), '\n') form('\n', span, '\n') output = "%s%s\n" % (output, form) def link(resource): return render_resource_link(self.env, formatter.context, resource, 'compact') for category in categories: page = WikiPage(self.env, category) match = self.title_extract.search(page.text) if match: cat_title = '%s' % match.group(1).strip() cat_body = self.title_extract.sub('', page.text, 1) else: cat_title = '%s' % category cat_body = page.text cat_body = self.self_extract.sub('', cat_body).strip() style = "padding:1em; margin:0em 5em 2em 5em; border:1px solid #999;" fieldset = builder.fieldset('\n', style=style) if not hide_fieldset_legend: legend = builder.legend(style="color: #999;") legend(builder.a(cat_title, href=self.env.href.wiki(category))) fieldset(legend, '\n') if not hide_fieldset_description: fieldset(builder.p(wiki_to_html(cat_body, self.env, req))) ul = builder.ul('\n', class_="listtagged") query = 'realm:wiki (%s) %s %s' % \ (' or '.join(show_releases), category, ' '.join(other)) lines = 0 for resource, tags in tag_system.query(req, query): # filter out the page used to make important tags # persistent if resource.id == "tags/persistent": continue lines += 1 li = builder.li(link(resource), ': ') page = WikiPage(self.env, resource) match = self.title_extract.search(page.text) description = "''no description available''" if match: if match.group(1): description = match.group(1).strip() li(wiki_to_oneliner(description, self.env, req=req)) if tags: if hide_fieldset_legend == False and category in tags: tags.remove(category) self.log.debug("hide %s: no legend" % category) for o in other: if o in tags: tags.remove(o) rendered_tags = [ link(resource('tag', tag)) for tag in natural_sort(tags) ] span = builder.span(style="font-size:xx-small;") span(' (tags: ', rendered_tags[0], [(', ', tag) for tag in rendered_tags[1:]], ')') li(span) ul(li, '\n') if lines: fieldset(ul, '\n') else: message = "No results for %s." % \ (hide_release_picker and "this version" or "your selection") fieldset(builder.p(builder.em(message)), '\n') output = "%s%s\n" % (output, fieldset) return output
def render_new(self, req, data, hacks): req.perm.require('HACK_CREATE') tag_system = TagSystem(self.env) hacks = list(hacks) hack_names = set(r[2].id for r in hacks) users = set(u.id for u, _ in tag_system.query(req, 'realm:wiki user')) exclude = hack_names.union(users).union(data['types']).union(data['releases']) cloud = {} for votes, rank, resource, tags, title in hacks: for tag in tags: if tag in exclude: continue try: cloud[tag] += 1 except KeyError: cloud[tag] = 1 # Pick the top 25 tags + a random sample of 10 from the rest. cloud = sorted(cloud.items(), key=lambda i: -i[1]) remainder = cloud[25:] cloud = dict(cloud[:25] + random.sample(remainder, min(10, len(remainder)))) # Render the cloud min_px = 8 max_px = 20 def cloud_renderer(tag, count, percent): self.env.log.debug("cloud: %s = %2.2f%%" % (tag, percent * 100)) return builder.a(tag, href='#', style='font-size: %ipx' % int(min_px + percent * (max_px - min_px))) data['cloud'] = render_cloud(self.env, req, cloud, cloud_renderer) add_script(req, 'common/js/wikitoolbar.js') add_script(req, 'common/js/folding.js') data['focus'] = 'name' # Populate data with form submission if req.method == 'POST' and 'create' in req.args or 'preview' in req.args: data.update(req.args) context = self.form.validate(data) data['form_context'] = context vars = {} vars['OWNER'] = req.authname vars['WIKINAME'] = get_page_name(data['name'], data['type']) vars['TYPE'] = data.setdefault('type', 'plugin') vars['TITLE'] = data.setdefault('title', 'No title available') vars['LCNAME'] = vars['WIKINAME'].lower() vars['SOURCEURL'] = '%s/%s' % (self.svnbase, vars['LCNAME']) vars['DESCRIPTION'] = data.setdefault('description', 'No description available') vars['EXAMPLE'] = data.setdefault('example', 'No example available') if 'create' in req.args and not context.errors: success, message = self.create_hack(req, data, vars) if success: target = '%s?%s' % \ (req.href.wiki(vars['WIKINAME']), 'hack=created') req.redirect(target) raise RequestDone else: add_warning(req, message) elif 'preview' in req.args and not context.errors: page = WikiPage(self.env, self.template) if not page.exists: raise TracError('New hack template %s does not exist.' % \ self.template) template = Template(page.text).substitute(vars) template = re.sub(r'\[\[ChangeLog[^\]]*\]\]', 'No changes yet', template) data['page_preview'] = wiki_to_html(template, self.env, req) else: data['form_context'] = None data['type'] = 'plugin' data['release'] = ['0.11'] self.env.log.debug('MUPPETS AHOY') return 'hacks_new.html', data, None
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='')], [])