def render_cloud(env, req, cloud, renderer=None): """Render a tag cloud :cloud: Dictionary of {object: count} representing the cloud. :param renderer: A callable with signature (tag, count, percent) used to render the cloud objects. """ min_px = 10.0 max_px = 30.0 scale = 1.0 if renderer is None: def default_renderer(tag, count, percent): href = get_resource_url(env, Resource('tag', tag), req.href) return builder.a(tag, rel='tag', title='%i' % count, href=href, style='font-size: %ipx' % int(min_px + percent * (max_px - min_px))) renderer = default_renderer # A LUT from count to n/len(cloud) size_lut = dict([(c, float(i)) for i, c in enumerate(sorted(set([r for r in cloud.values()])))]) if size_lut: scale = 1.0 / len(size_lut) ul = builder.ul(class_='tagcloud') last = len(cloud) - 1 for i, (tag, count) in enumerate(sorted(cloud.iteritems())): percent = size_lut[count] * scale li = builder.li(renderer(tag, count, percent)) if i == last: li(class_='last') li() ul(li) return ul
def get_months_authors_categories(self, from_dt=None, to_dt=None, user=None, perm=None): """ Returns a structure of post metadata: ([ ((year1, month1), count), ((year1, month2), count) ], # newest first [ (author1, count), (author2, count) ], # alphabetical [ (category1, count), (category2, count) ], # alphabetical total) # num of posts * Use 'from_dt' and 'to_dt' (datetime objects) to restrict search to posts with a publish_time within the intervals (None means ignore). * If user and perm is provided, the list is also filtered for permissions. * Note also that it only fetches from most recent version. """ blog_posts = get_blog_posts(self.env, from_dt=from_dt, to_dt=to_dt) a_dict = {} c_dict = {} m_dict = {} total = 0 for post in blog_posts: if user and perm: # Check permissions bp = BlogPost(self.env, post[0], post[1]) if not 'BLOG_VIEW' in perm(bp.resource): continue # Skip this post post_time = post[2] m_dict[(post_time.year, post_time.month)] = m_dict.get( (post_time.year, post_time.month), 0) + 1 author = post[3] a_dict[author] = a_dict.get(author, 0) + 1 categories = post[6] # a list for category in set(categories): c_dict[category] = c_dict.get(category, 0) + 1 total += 1 return ([(m, m_dict.get(m, 0)) for m in sorted(m_dict.keys(), reverse=True)], [(a, a_dict.get(a, 0)) for a in sorted(a_dict.keys())], [(c, c_dict.get(c, 0)) for c in sorted(c_dict.keys())], total)
def post_process_request(self, req, template, data, content_type): if req.path_info.startswith('/ticket/'): # In case of an invalid ticket, the data is invalid if not data: return template, data, content_type tkt = data['ticket'] links = TicketLinks(self.env, tkt) for i in links.blocked_by: if Ticket(self.env, i)['status'] != 'closed': add_script(req, 'mastertickets/disable_resolve.js') break # Add link to depgraph if needed if links: add_ctxtnav(req, 'Depgraph', req.href.depgraph('ticket', tkt.id)) for change in data.get('changes', {}): if not change.has_key('fields'): continue for field, field_data in change['fields'].iteritems(): if field in self.fields: if field_data['new'].strip(): new = set( [int(n) for n in field_data['new'].split(',')]) else: new = set() if field_data['old'].strip(): old = set( [int(n) for n in field_data['old'].split(',')]) else: old = set() add = new - old sub = old - new elms = tag() if add: elms.append( tag.em(u', '.join( [unicode(n) for n in sorted(add)]))) elms.append(u' added') if add and sub: elms.append(u'; ') if sub: elms.append( tag.em(u', '.join( [unicode(n) for n in sorted(sub)]))) elms.append(u' removed') field_data['rendered'] = elms #add a link to generate a dependency graph for all the tickets in the milestone if req.path_info.startswith('/milestone/'): if not data: return template, data, content_type milestone = data['milestone'] add_ctxtnav(req, 'Depgraph', req.href.depgraph('milestone', milestone.name)) return template, data, content_type
def post_process_request(self, req, template, data, content_type): if req.path_info.startswith('/ticket/'): # In case of an invalid ticket, the data is invalid if not data: return template, data, content_type tkt = data['ticket'] links = TicketLinks(self.env, tkt) for i in links.blocked_by: if Ticket(self.env, i)['status'] != 'closed': add_script(req, 'mastertickets/disable_resolve.js') break # Add link to depgraph if needed if links: add_ctxtnav(req, 'Depgraph', req.href.depgraph(tkt.id)) for change in data.get('changes', {}): if not change.has_key('fields'): continue for field, field_data in change['fields'].iteritems(): if field in self.fields: if field_data['new'].strip(): new = set([int(n) for n in field_data['new'].split(',')]) else: new = set() if field_data['old'].strip(): old = set([int(n) for n in field_data['old'].split(',')]) else: old = set() add = new - old sub = old - new elms = tag() if add: elms.append( tag.em(u', '.join([unicode(n) for n in sorted(add)])) ) elms.append(u' added') if add and sub: elms.append(u'; ') if sub: elms.append( tag.em(u', '.join([unicode(n) for n in sorted(sub)])) ) elms.append(u' removed') field_data['rendered'] = elms #add a link to generate a dependency graph for all the tickets in the milestone if req.path_info.startswith('/milestone/'): if not data: return template, data, content_type milestone=data['milestone'] add_ctxtnav(req, 'Depgraph', req.href.depgraph('milestone', milestone.name)) return template, data, content_type
def render_admin_panel(self, req, cat, page, path_info): assert req.perm.has_permission('TRAC_ADMIN') excludes_match = self._patterns_match(self.excludes) if page not in self._get_sections_set(excludes_match): raise TracError("Invalid section %s" % page) options = sorted( [option for (section, name), option in Option.registry.iteritems() if section == page and \ not excludes_match('%s:%s' % (section, name))], key=lambda opt: opt.name) # Apply changes if req.method == 'POST': modified = False for name, value in req.args.iteritems(): if any(name == opt.name for opt in options): if self.config.get(page, name) != value: self.config.set(page, name, value) modified = True if modified: self.log.debug("Updating trac.ini") self.config.save() req.redirect(req.href.admin(cat, page)) add_stylesheet(req, 'iniadmin/css/iniadmin.css') password_match = self._patterns_match(self.passwords) options_data = [] for option in options: doc = self._get_doc(option) value = self.config.get(page, option.name) # We assume the classes all end in "Option" type = option.__class__.__name__.lower()[:-6] or 'text' if type == 'list' and not isinstance(value, basestring): value = unicode(option.sep).join(list(value)) option_data = { 'name': option.name, 'default': option.default, 'doc': doc, 'value': value, 'type': type } if type == 'extension': option_data['options'] = sorted( impl.__class__.__name__ for impl in option.xtnpt.extensions(self)) elif type == 'text' and \ password_match('%s:%s' % (option.section, option.name)): option_data['type'] = 'password' options_data.append(option_data) data = {'iniadmin': {'section': page, 'options': options_data}} return 'iniadmin.html', data
def render_cloud(self, req, cloud, renderer=None, caseless_sort=False, mincount=None): """Render a tag cloud. :cloud: Dictionary of {object: count} representing the cloud. :param renderer: A callable with signature (tag, count, percent) used to render the cloud objects. :param caseless_sort: Boolean, whether tag cloud should be sorted case-sensitive. :param mincount: Integer threshold to hide tags with smaller count. """ min_px = 10.0 max_px = 30.0 scale = 1.0 if renderer is None: def default_renderer(tag, count, percent): href = self.get_href(req, tag=Resource('tag', tag)) return builder.a(tag, rel='tag', title='%i' % count, href=href, style='font-size: %ipx' % int(min_px + percent * (max_px - min_px))) renderer = default_renderer # A LUT from count to n/len(cloud) size_lut = dict([(c, float(i)) for i, c in enumerate(sorted(set([r for r in cloud.values()])))]) if size_lut: scale = 1.0 / len(size_lut) if caseless_sort: # Preserve upper-case precedence within similar tags. items = reversed(sorted(cloud.iteritems(), key=lambda t: t[0].lower(), reverse=True)) else: items = sorted(cloud.iteritems()) ul = li = None for i, (tag, count) in enumerate(items): percent = size_lut[count] * scale if mincount and count < as_int(mincount, 1): # Tag count is too low. continue if ul: # Found new tag for cloud; now add previously prepared one. ul('\n', li) else: # Found first tag for cloud; now create the list. ul = builder.ul(class_='tagcloud') # Prepare current tag entry. li = builder.li(renderer(tag, count, percent)) if li: # All tags checked; mark latest tag as last one (no tailing colon). li(class_='last') ul('\n', li, '\n') return ul and ul or _("No tags found")
def render_cloud(self, req, cloud, renderer=None, caseless_sort=False, mincount=None, realms=()): """Render a tag cloud. :cloud: Dictionary of {object: count} representing the cloud. :param renderer: A callable with signature (tag, count, percent) used to render the cloud objects. :param caseless_sort: Boolean, whether tag cloud should be sorted case-sensitive. :param mincount: Integer threshold to hide tags with smaller count. """ min_px = 10.0 max_px = 30.0 scale = 1.0 if renderer is None: def default_renderer(tag, count, percent): href = self.get_href(req, realms, tag=Resource('tag', tag)) return builder.a(tag, rel='tag', title='%i' % count, href=href, style='font-size: %ipx' % int(min_px + percent * (max_px - min_px))) renderer = default_renderer # A LUT from count to n/len(cloud) size_lut = dict([(c, float(i)) for i, c in enumerate(sorted(set([r for r in cloud.values()])))]) if size_lut: scale = 1.0 / len(size_lut) if caseless_sort: # Preserve upper-case precedence within similar tags. items = reversed(sorted(cloud.iteritems(), key=lambda t: t[0].lower(), reverse=True)) else: items = sorted(cloud.iteritems()) ul = li = None for i, (tag, count) in enumerate(items): percent = size_lut[count] * scale if mincount and count < as_int(mincount, 1): # Tag count is too low. continue if ul: # Found new tag for cloud; now add previously prepared one. ul('\n', li) else: # Found first tag for cloud; now create the list. ul = builder.ul(class_='tagcloud') # Prepare current tag entry. li = builder.li(renderer(tag, count, percent)) if li: # All tags checked; mark latest tag as last one (no tailing colon). li(class_='last') ul('\n', li, '\n') return ul and ul or _("No tags found")
def post_process_request(self, req, template, data, content_type): if req.path_info.startswith('/ticket/'): tkt = data['ticket'] links = TicketLinks(self.env, tkt) for i in links.blocked_by: if Ticket(self.env, i)['status'] != 'closed': add_script(req, 'mastertickets/disable_resolve.js') break data['mastertickets'] = { 'field_values': { 'blocking': linkify_ids(self.env, req, links.blocking), 'blockedby': linkify_ids(self.env, req, links.blocked_by), }, } # Add link to depgraph if needed if links: add_ctxtnav(req, 'Depgraph', req.href.depgraph(tkt.id)) for change in data.get('changes', []): for field, field_data in change['fields'].iteritems(): if field in self.fields: if field_data['new'].strip(): new = set( [int(n) for n in field_data['new'].split(',')]) else: new = set() if field_data['old'].strip(): old = set( [int(n) for n in field_data['old'].split(',')]) else: old = set() add = new - old sub = old - new elms = tag() if add: elms.append( tag.em(u', '.join( [unicode(n) for n in sorted(add)]))) elms.append(u' added') if add and sub: elms.append(u'; ') if sub: elms.append( tag.em(u', '.join( [unicode(n) for n in sorted(sub)]))) elms.append(u' removed') field_data['rendered'] = elms return template, data, content_type
def get_months_authors_categories(self, from_dt=None, to_dt=None, user=None, perm=None): """ Returns a structure of post metadata: ([ ((year1, month1), count), ((year1, month2), count) ], # newest first [ (author1, count), (author2, count) ], # alphabetical [ (category1, count), (category2, count) ], # alphabetical total) # num of posts * Use 'from_dt' and 'to_dt' (datetime objects) to restrict search to posts with a publish_time within the intervals (None means ignore). * If user and perm is provided, the list is also filtered for permissions. * Note also that it only fetches from most recent version. """ # cache_months_authors_categories= self.env.project_name + "_months_authors_categories" cache_months_authors_categories = self.env.project_name + '_blog_posts_months' # if user: # cache_months_authors_categories += "_user_" + user if from_dt: s_from_dt = str(from_dt) cache_months_authors_categories += s_from_dt.replace(" ", "").replace(":", "").replace("-", "").replace("+", "") if to_dt: s_to_dt= str(to_dt) cache_months_authors_categories += s_to_dt.replace(" ", "").replace(":", "").replace("-", "").replace("+", "") if cache.c(cache_months_authors_categories): self.env.log.debug("%r found Cache ,return Cache...318......." % cache_months_authors_categories) return cache.c(cache_months_authors_categories) else: self.env.log.debug("%r not found. Cacheing.. 322." % cache_months_authors_categories) blog_posts = get_all_blog_posts(self.env, from_dt=from_dt, to_dt=to_dt) a_dict = {} c_dict = {} m_dict = {} total = 0 for post in blog_posts: if user and perm: # Check permissions bp = BlogPost(self.env, post[0], post[1]) if not 'BLOG_VIEW' in perm(bp.resource): continue # Skip this post post_time = post[2] m_dict[(post_time.year, post_time.month)] = m_dict.get( (post_time.year, post_time.month), 0) + 1 author = post[3] a_dict[author] = a_dict.get(author, 0) + 1 categories = post[6] # a list for category in set(categories): c_dict[category] = c_dict.get(category, 0) + 1 total += 1 return cache.c(cache_months_authors_categories,([(m, m_dict.get(m, 0)) for m in sorted(m_dict.keys(), reverse=True)], [(a, a_dict.get(a, 0)) for a in sorted(a_dict.keys())], [(c, c_dict.get(c, 0)) for c in sorted(c_dict.keys())], total))
def post_process_request(self, req, template, data, content_type, method=None): if req.path_info.startswith('/ticket/'): # In case of an invalid ticket, the data is invalid if not data: return template, data, content_type, method tkt = data['ticket'] with self.env.db_query as db: links = CrashDumpTicketLinks(self.env, tkt, db=db) for change in data.get('changes', {}): if not change.has_key('fields'): continue for field, field_data in change['fields'].iteritems(): if field in self.crashdump_link_fields: if field_data['new'].strip(): new = set([ CrashDumpSystem.get_crash_id(n) for n in field_data['new'].split(',') ]) else: new = set() if field_data['old'].strip(): old = set([ CrashDumpSystem.get_crash_id(n) for n in field_data['old'].split(',') ]) else: old = set() add = new - old sub = old - new elms = tag() if add: elms.append( tag.em(u', '.join( [unicode(n) for n in sorted(add)]))) elms.append(u' added') if add and sub: elms.append(u'; ') if sub: elms.append( tag.em(u', '.join( [unicode(n) for n in sorted(sub)]))) elms.append(u' removed') field_data['rendered'] = elms links.crashes = new return template, data, content_type, method
def render_admin_panel(self, req, cat, page, path_info): assert req.perm.has_permission('TRAC_ADMIN') excludes_match = self._patterns_match(self.excludes) if page not in self._get_sections_set(excludes_match): raise TracError("Invalid section %s" % page) options = sorted( [option for (section, name), option in Option.registry.iteritems() if section == page and \ not excludes_match('%s:%s' % (section, name))], key=lambda opt: opt.name) # Apply changes if req.method == 'POST': modified = False for name, value in req.args.iteritems(): if any(name == opt.name for opt in options): if self.config.get(page, name) != value: self.config.set(page, name, value) modified = True if modified: self.log.debug("Updating trac.ini") self.config.save() req.redirect(req.href.admin(cat, page)) add_stylesheet(req, 'iniadmin/css/iniadmin.css') password_match = self._patterns_match(self.passwords) options_data = [] for option in options: doc = self._get_doc(option) value = self.config.get(page, option.name) # We assume the classes all end in "Option" type = option.__class__.__name__.lower()[:-6] or 'text' if type == 'list' and not isinstance(value,basestring): value = unicode(option.sep).join(list(value)) option_data = {'name': option.name, 'default': option.default, 'doc': doc, 'value': value, 'type': type} if type == 'extension': option_data['options'] = sorted( impl.__class__.__name__ for impl in option.xtnpt.extensions(self)) elif type == 'text' and \ password_match('%s:%s' % (option.section, option.name)): option_data['type'] = 'password' options_data.append(option_data) data = {'iniadmin': {'section': page, 'options': options_data}} return 'iniadmin.html', data
def post_process_request(self, req, template, origData, content_type): if req.path_info.startswith('/newticket'): mode = 'new' elif req.path_info.startswith('/ticket/'): mode = 'view' else: return template, origData, content_type fieldData = {} fieldData['condfields'] = {} all_fields = [] standard_fields = set() for f in TicketSystem(self.env).get_ticket_fields(): all_fields.append(f['name']) if not f.get('custom'): standard_fields.add(f['name']) if 'owner' in all_fields: curr_idx = all_fields.index('owner') if 'cc' in all_fields: insert_idx = all_fields.index('cc') else: insert_idx = len(all_fields) if curr_idx < insert_idx: all_fields.insert(insert_idx, all_fields[curr_idx]) del all_fields[curr_idx] for t in self.types: fieldData['condfields'][t] = self.get_fields(t, all_fields, standard_fields) # fields = set(getattr(self, t+'_fields')) # if self.include_std: # fields.update(standard_fields) # fields.update(self.forced_fields) # fieldData['condfields'][t] = dict([ # (f, f in fields) for f in all_fields # ]) self.log.debug(all_fields) self.log.info(standard_fields) fieldData['mode'] = mode fieldData['all_fields'] = list(all_fields) fieldData['ok_view_fields'] = sorted(set(all_fields) - self.forced_fields, key=lambda x: all_fields.index(x)) fieldData['ok_new_fields'] = sorted((set(all_fields) - self.forced_fields) - set(['owner']), key=lambda x: all_fields.index(x)) add_script_data(req, fieldData) add_script(req, '/condfields.js') return template, origData, content_type
def post_process_request(self, req, template, data, content_type): if req.path_info.startswith('/ticket/'): tkt = data['ticket'] links = TicketLinks(self.env, tkt) for i in links.blocked_by: if Ticket(self.env, i)['status'] != 'closed': add_script(req, 'mastertickets/disable_resolve.js') break data['mastertickets'] = { 'field_values': { 'blocking': linkify_ids(self.env, req, links.blocking), 'blockedby': linkify_ids(self.env, req, links.blocked_by), }, } # Add link to depgraph if needed if links: add_ctxtnav(req, 'Depgraph', req.href.depgraph(tkt.id)) for change in data.get('changes', []): for field, field_data in change['fields'].iteritems(): if field in self.fields: if field_data['new'].strip(): new = set([int(n) for n in field_data['new'].split(',')]) else: new = set() if field_data['old'].strip(): old = set([int(n) for n in field_data['old'].split(',')]) else: old = set() add = new - old sub = old - new elms = tag() if add: elms.append( tag.em(u', '.join([unicode(n) for n in sorted(add)])) ) elms.append(u' added') if add and sub: elms.append(u'; ') if sub: elms.append( tag.em(u', '.join([unicode(n) for n in sorted(sub)])) ) elms.append(u' removed') field_data['rendered'] = elms return template, data, content_type
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
def _page_tags(self, req): pagename = req.args.get('page', 'WikiStart') tag_system = TagSystem(self.env) resource = Resource('wiki', pagename) tags = sorted(tag_system.get_tags(req, resource)) return tags
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([ WikiModule.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_str, template=req.args.get('template'))) return (template, data, content_type)
def _build_graph(self, req, tkt_id): links = TicketLinks(self.env, tkt_id) g = graphviz.Graph() node_default = g['node'] node_default['style'] = 'filled' edge_default = g['edge'] edge_default['style'] = '' # Force this to the top of the graph g[tkt_id] links = sorted(links.walk(), key=lambda link: link.tkt.id) for link in links: tkt = link.tkt node = g[tkt.id] node['label'] = u'#%s' % tkt.id node['fillcolor'] = tkt['status'] == 'closed' and 'green' or 'red' node['URL'] = req.href.ticket(tkt.id) node['alt'] = u'Ticket #%s' % tkt.id node['tooltip'] = tkt['summary'] for n in link.blocking: node > g[n] return g
def _build_graph(self, req, tkt_ids, label_summary=0): g = graphviz.Graph() g.label_summary = label_summary g.attributes['rankdir'] = self.graph_direction node_default = g['node'] node_default['style'] = 'filled' edge_default = g['edge'] edge_default['style'] = '' # Force this to the top of the graph for id in tkt_ids: g[id] links = TicketLinks.walk_tickets(self.env, tkt_ids) links = sorted(links, key=lambda link: link.tkt.id) for link in links: tkt = link.tkt node = g[tkt.id] if label_summary: node['label'] = u'#%s %s' % (tkt.id, tkt['summary']) else: node['label'] = u'#%s' % tkt.id node['fillcolor'] = tkt[ 'status'] == 'closed' and self.closed_color or self.opened_color node['URL'] = req.href.ticket(tkt.id) node['alt'] = u'Ticket #%s' % tkt.id node['tooltip'] = tkt['summary'] for n in link.blocking: node > g[n] return g
def render_table(items, colspec, render_item, colspace=1): try: columns = max(int(colspec), 1) except Exception: columns = 3 nbsp = Markup(' ') # hdr = [tag.th(nbsp, 'Display'), tag.th('Markup', nbsp)] spacer_style = 'width:%dem;border:none' % colspace # Find max width to make value cols equally wide width = 0 for i in items: if (isinstance(i, str) or isinstance(i, unicode)) and len(i) > width: width = len(i) value_style = 'border:none' #noinspection PyUnusedLocal value_style += ';width:%dem' % (width*2/3) if width else '' # empirical... def render_def(s): rendered = s and render_item(s) or None if isinstance(s, str): s = Markup(s.replace('&', '&')) return [tag.td(rendered, nbsp, style='border:none'), tag.td(tag.kbd(s), style=value_style)] return tag.table(#tag.tr((hdr + [tag.td(style=spacer_style)]) * # (columns-1) + hdr), [tag.tr([render_def(s) + [tag.td(style=spacer_style)] for s in row[:-1]] + render_def(row[-1])) for row in group_over(sorted(items), columns)], class_='wiki', style='border:none')
def prepare_to_cumulate(sorted_events): dhist = {} for date, date_events in groupby(sorted_events, lambda (t, events): to_datetime(t).date()): evset = {'Enter': set(), 'Leave': set(), 'Finish': set()} dhist[date] = evset date_events_list = list(date_events) for (t, events) in date_events_list: for k, ids in events.iteritems(): evset[k] |= ids # resolve Enter / Leave conflicts enter_leave_ids = evset['Enter'] & evset['Leave'] if enter_leave_ids: evs = {'Enter': None, 'Leave': None} last = {'Enter': None, 'Leave': None} for k in ('Enter', 'Leave'): evs[k] = sorted([(t, evs['Enter']) for (t, evs) in date_events_list], key=lambda (t, ids): t) for id in enter_leave_ids: for k in ('Enter', 'Leave'): last[k] = 0 for t, ids in reversed(evs[k]): if id in ids: last[k] = t break to_del = (last['Enter'] > last['Leave']) and 'Leave' or 'Enter' evset[to_del].remove(id)
def render_table(items, colspec, render_item, colspace=1): try: columns = max(int(colspec), 1) except Exception: columns = 3 nbsp = Markup(' ') # hdr = [tag.th(nbsp, 'Display'), tag.th('Markup', nbsp)] spacer_style = 'width:%dem;border:none' % colspace # Find max width to make value cols equally wide width = 0 for i in items: if (isinstance(i, str) or isinstance(i, unicode)) and len(i) > width: width = len(i) value_style = 'border:none' #noinspection PyUnusedLocal if width: # empirical... value_style = '%s;width:%dem' % (value_style, width*2/3) def render_def(s): rendered = s and render_item(s) or None if isinstance(s, str): s = Markup(s.replace('&', '&')) return [tag.td(rendered, nbsp, style='border:none'), tag.td(tag.kbd(s), style=value_style)] return tag.table(#tag.tr((hdr + [tag.td(style=spacer_style)]) * # (columns-1) + hdr), [tag.tr([render_def(s) + [tag.td(style=spacer_style)] for s in row[:-1]] + render_def(row[-1])) for row in group_over(sorted(items), columns)], class_='wiki', style='border:none')
def to_ranges(revs): """Converts a list of revisions to a minimal set of ranges. >>> to_ranges([2, 12, 3, 6, 9, 1, 5, 11]) '1-3,5-6,9,11-12' >>> to_ranges([]) '' """ ranges = [] begin = end = None def store(): if end == begin: ranges.append(str(begin)) else: ranges.append('%d-%d' % (begin, end)) for rev in sorted(revs): if begin is None: begin = end = rev elif rev == end + 1: end = rev else: store() begin = end = rev if begin is not None: store() return ','.join(ranges)
def _build_graph(self, req, tkt_ids, label_summary=0): g = graphviz.Graph() g.label_summary = label_summary g.attributes['rankdir'] = self.graph_direction node_default = g['node'] node_default['style'] = 'filled' edge_default = g['edge'] edge_default['style'] = '' # Force this to the top of the graph for id in tkt_ids: g[id] links = TicketLinks.walk_tickets(self.env, tkt_ids) links = sorted(links, key=lambda link: link.tkt.id) for link in links: tkt = link.tkt node = g[tkt.id] if label_summary: node['label'] = u'#%s %s' % (tkt.id, tkt['summary']) else: node['label'] = u'#%s'%tkt.id node['fillcolor'] = tkt['status'] == 'closed' and self.closed_color or self.opened_color node['URL'] = req.href.ticket(tkt.id) node['alt'] = u'Ticket #%s'%tkt.id node['tooltip'] = tkt['summary'] for n in link.blocking: node > g[n] return g
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
def test_attachments(self): # Note: Quite similar to the tracrpc.tests.json.JsonTestCase.test_binary image_url = os.path.join(rpc_testenv.trac_src, 'trac', 'htdocs', 'feed.png') image_in = StringIO(open(image_url, 'r').read()) # Create attachment self.admin.wiki.putAttachmentEx('TitleIndex', 'feed2.png', 'test image', xmlrpclib.Binary(image_in.getvalue())) self.assertEquals( image_in.getvalue(), self.admin.wiki.getAttachment('TitleIndex/feed2.png').data) # Update attachment (adding new) self.admin.wiki.putAttachmentEx('TitleIndex', 'feed2.png', 'test image', xmlrpclib.Binary(image_in.getvalue()), False) self.assertEquals( image_in.getvalue(), self.admin.wiki.getAttachment('TitleIndex/feed2.2.png').data) # List attachments self.assertEquals(['TitleIndex/feed2.2.png', 'TitleIndex/feed2.png'], sorted( self.admin.wiki.listAttachments('TitleIndex'))) # Delete both attachments self.admin.wiki.deleteAttachment('TitleIndex/feed2.png') self.admin.wiki.deleteAttachment('TitleIndex/feed2.2.png') # List attachments again self.assertEquals([], self.admin.wiki.listAttachments('TitleIndex'))
def get_all_status(self): """Returns a sorted list of all the states all of the action controllers know about.""" valid_states = set() for controller in self.action_controllers: valid_states.update(controller.get_all_status()) return sorted(valid_states)
def _build_graph(self, req, tkt_id): links = TicketLinks(self.env, tkt_id) g = graphviz.Graph() node_default = g['node'] node_default['style'] = 'filled' edge_default = g['edge'] edge_default['style'] = '' # Force this to the top of the graph g[tkt_id] links = sorted(links.walk(), key=lambda link: link.tkt.id) for link in links: tkt = link.tkt node = g[tkt.id] node['label'] = u'#%s'%tkt.id node['fillcolor'] = tkt['status'] == 'closed' and 'green' or 'red' node['URL'] = req.href.ticket(tkt.id) node['alt'] = u'Ticket #%s'%tkt.id node['tooltip'] = tkt['summary'] for n in link.blocking: node > g[n] return g
def get_timeline_events(self, req, start, stop, filters): if "blog" in filters: blog_realm = Resource("blog") if not "BLOG_VIEW" in req.perm(blog_realm): return add_stylesheet(req, "tracfullblog/css/fullblog.css") # Blog posts blog_posts = get_blog_posts(self.env, from_dt=start, to_dt=stop, all_versions=True) for name, version, time, author, title, body, category_list in blog_posts: bp_resource = blog_realm(id=name, version=version) if "BLOG_VIEW" not in req.perm(bp_resource): continue bp = BlogPost(self.env, name, version=version) yield ("blog", bp.version_time, bp.version_author, (bp_resource, bp, None)) # Attachments (will be rendered by attachment module) for event in AttachmentModule(self.env).get_timeline_events(req, blog_realm, start, stop): yield event # Blog comments blog_comments = get_blog_comments(self.env, from_dt=start, to_dt=stop) blog_comments = sorted(blog_comments, key=itemgetter(4), reverse=True) for post_name, number, comment, author, time in blog_comments: bp_resource = blog_realm(id=post_name) if "BLOG_VIEW" not in req.perm(bp_resource): continue bp = BlogPost(self.env, post_name) bc = BlogComment(self.env, post_name, number=number) yield ("blog", time, author, (bp_resource, bp, bc))
def test_mixed_case_group(self): db = self.env.get_db_cnx() cursor = db.cursor() cursor.executemany("INSERT INTO permission VALUES (%s,%s)", [ ('Dev', 'WIKI_MODIFY'), ('Dev', 'REPORT_ADMIN'), ('Admin', 'Dev'), ('john', 'Admin')]) self.assertEquals(['REPORT_ADMIN', 'WIKI_MODIFY'], sorted(self.store.get_user_permissions('john')))
def get_comments(self): """ Returns a list of used comment numbers attached to the post. It instantiates BlogComment objects for comments attached to the current BlogPost, and returns them in a list sorted by number. """ comments = sorted(get_blog_comments(self.env, post_name=self.name), key=itemgetter(1)) return [BlogComment(self.env, comment[0], comment[1]) for comment in comments]
def sections(self): """Return a list of section names.""" sections = set([to_unicode(s) for s in self.parser.sections()]) if self.parent: sections.update(self.parent.sections()) else: sections.update(self.defaults().keys()) return sorted(sections)
def set_resource_tags(self, req, resource, tags): req.perm.require('TICKET_MODIFY', resource) split_into_tags = TagSystem(self.env).split_into_tags ticket = Ticket(self.env, resource.id) all = self._ticket_tags(ticket) 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'')
def _create_ticket(self, tags, **kwargs): ticket = Ticket(self.env) ticket['keywords'] = u' '.join(sorted(map(to_unicode, tags))) ticket['summary'] = 'summary' ticket['reporter'] = 'admin' for name, value in kwargs.iteritems(): ticket[name] = value ticket.insert() return ticket
def get_versions(self): """ Returns a sorted list of versions stored for the blog post. Returns empty list ([]) if no versions exists. """ cnx = self.env.get_db_cnx() cursor = cnx.cursor() cursor.execute("SELECT version from fullblog_posts " "WHERE name=%s", (self.name, )) self.versions = sorted([row[0] for row in cursor]) return self.versions
def test_simple_actions(self): db = self.env.get_db_cnx() cursor = db.cursor() cursor.executemany("INSERT INTO permission VALUES (%s,%s)", [ ('john', 'WIKI_MODIFY'), ('john', 'REPORT_ADMIN'), ('kate', 'TICKET_CREATE')]) self.assertEquals(['REPORT_ADMIN', 'WIKI_MODIFY'], sorted(self.store.get_user_permissions('john'))) self.assertEquals(['TICKET_CREATE'], self.store.get_user_permissions('kate'))
def get_versions(self): """ Returns a sorted list of versions stored for the blog post. Returns empty list ([]) if no versions exists. """ cnx = self.env.get_db_cnx() cursor = cnx.cursor() cursor.execute("SELECT version from fullblog_posts " "WHERE name=%s", (self.name,) ) self.versions = sorted([row[0] for row in cursor]) return self.versions
def post_process_request(self, req, template, data, content_type): if req.path_info.startswith('/ticket/'): # In case of an invalid ticket, the data is invalid if not data: return template, data, content_type tkt = data['ticket'] links = TicketLinks(self.env, tkt) for i in links.blocked_by: if Ticket(self.env, i)['status'] != 'closed': add_script(req, 'ticketrelations/disable_resolve.js') break for change in data.get('changes', {}): if not change.has_key('fields'): continue for field, field_data in change['fields'].iteritems(): if field in self.fields: if field_data['new'].strip(): new = extract_ticket_ids(field_data['new']) else: new = set() if field_data['old'].strip(): old = extract_ticket_ids(field_data['old']) else: old = set() add = new - old sub = old - new elms = tag() if add: elms.append( tag.em(u', '.join([unicode(n) for n in sorted(add)])) ) elms.append(u' added') if add and sub: elms.append(u'; ') if sub: elms.append( tag.em(u', '.join([unicode(n) for n in sorted(sub)])) ) elms.append(u' removed') field_data['rendered'] = elms return template, data, content_type
def get_comments(self): """ Returns a list of used comment numbers attached to the post. It instantiates BlogComment objects for comments attached to the current BlogPost, and returns them in a list sorted by number. """ comments = sorted(get_blog_comments(self.env, post_name=self.name), key=itemgetter(1)) return [ BlogComment(self.env, comment[0], comment[1]) for comment in comments ]
def render_list(self, req, data, hacks): ul = builder.ul() for votes, rank, resource, tags, title in sorted(hacks, key=lambda h: h[2].id): li = builder.li(builder.a(resource.id, href=req.href.wiki(resource.id)), ' - ', title) ul(li) data['body'] = ul # TODO Top-n + sample return 'hacks_view.html', data, None
def render_cloud(env, req, cloud, renderer=None): """Render a tag cloud :cloud: Dictionary of {object: count} representing the cloud. :param renderer: A callable with signature (tag, count, percent) used to render the cloud objects. """ min_px = 10.0 max_px = 30.0 scale = 1.0 if renderer is None: def default_renderer(tag, count, percent): href = get_resource_url(env, Resource('tag', tag), req.href) return builder.a(tag, rel='tag', title='%i' % count, href=href, style='font-size: %ipx' % int(min_px + percent * (max_px - min_px))) renderer = default_renderer # A LUT from count to n/len(cloud) size_lut = dict([ (c, float(i)) for i, c in enumerate(sorted(set([r for r in cloud.values()]))) ]) if size_lut: scale = 1.0 / len(size_lut) ul = builder.ul(class_='tagcloud') last = len(cloud) - 1 for i, (tag, count) in enumerate(sorted(cloud.iteritems())): percent = size_lut[count] * scale li = builder.li(renderer(tag, count, percent)) if i == last: li(class_='last') li() ul(li) return ul
def test_builtin_groups(self): db = self.env.get_db_cnx() cursor = db.cursor() cursor.executemany("INSERT INTO permission VALUES (%s,%s)", [ ('authenticated', 'WIKI_MODIFY'), ('authenticated', 'REPORT_ADMIN'), ('anonymous', 'TICKET_CREATE')]) self.assertEquals(['REPORT_ADMIN', 'TICKET_CREATE', 'WIKI_MODIFY'], sorted(self.store.get_user_permissions('john'))) self.assertEquals(['TICKET_CREATE'], self.store.get_user_permissions('anonymous'))
def expand_macro(self, formatter, name, content): req = formatter.req query_result = TagSystem(self.env).query(req, content) def link(resource): return render_resource_link(self.env, formatter.context, resource, 'compact') ul = builder.ul(class_='taglist') # Beware: Resources can be Unicode strings. for resource, tags in sorted(query_result, key=lambda r: r[0].id): tags = sorted(tags) if tags: rendered_tags = [link(resource('tag', tag)) for tag in tags] li = builder.li(link(resource), ' (', rendered_tags[0], [(' ', tag) for tag in rendered_tags[1:]], ')') else: li = builder.li(link(resource)) ul(li, '\n') return ul
def post_process_request(self, req, template, data, content_type, method=None): if req.path_info.startswith('/ticket/'): # In case of an invalid ticket, the data is invalid if not data: return template, data, content_type tkt = data['ticket'] with self.env.db_query as db: links = CrashDumpTicketLinks(self.env, tkt, db=db) for change in data.get('changes', {}): if not change.has_key('fields'): continue for field, field_data in change['fields'].iteritems(): if field in self.crashdump_link_fields: if field_data['new'].strip(): new = set([CrashDumpSystem.get_crash_id(n) for n in field_data['new'].split(',')]) else: new = set() if field_data['old'].strip(): old = set([CrashDumpSystem.get_crash_id(n) for n in field_data['old'].split(',')]) else: old = set() add = new - old sub = old - new elms = tag() if add: elms.append( tag.em(u', '.join([unicode(n) for n in sorted(add)])) ) elms.append(u' added') if add and sub: elms.append(u'; ') if sub: elms.append( tag.em(u', '.join([unicode(n) for n in sorted(sub)])) ) elms.append(u' removed') field_data['rendered'] = elms links.crashes = new return template, data, content_type, method
def linkify_ids(env, req, ids): data = [] for id in sorted(ids, key=lambda x: int(x)): try: tkt = Ticket(env, id) data.append(tag.a('#%s'%tkt.id, href=req.href.ticket(tkt.id), class_='%s ticket'%tkt['status'], title=tkt['summary'])) except ResourceNotFound: data.append('#%s'%id) data.append(', ') if data: del data[-1] # Remove the last comma if needed return tag.span(*data)
def getUsers(self, next_action): allowed_group = getlist(next_action, 'owner_group', sep='|') if not allowed_group: allowed_group = self.owner_group users_dict = {} for name in self._get_users(allowed_group): u = GroupedSimpleUser() u.setUsername(name) u.setOptionValue(name) u.setOptionDisplay(name) users_dict.update({name: u}) return sorted(users_dict.values())
def _page_tags(self, req): pagename = req.args.get('page', 'WikiStart') version = req.args.get('version') tags_version = req.args.get('tags_version') page = WikiPage(self.env, pagename, version=version) resource = page.resource if version and not tags_version: tags_version = page.time tags = sorted( self.tag_system.get_tags(req, resource, when=tags_version)) return tags
def getUsers(self, next_action): allowed_group = getlist(next_action, 'owner_group', sep='|') if not allowed_group: allowed_group = self.owner_group users_dict = {} for name in self._get_users(allowed_group): u = GroupedSimpleUser() u.setUsername(name) u.setOptionValue(name) u.setOptionDisplay(name) users_dict.update({name:u}) return sorted(users_dict.values())
def get_versions(self): """ Returns a sorted list of versions stored for the blog post. Returns empty list ([]) if no versions exists. """ sql = "SELECT version from fullblog_posts WHERE name=%s" args = (self.name, ) if hasattr(self.env, 'db_query'): cursor = self.env.db_query(sql, args) else: db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute(sql, args) self.versions = sorted([row[0] for row in cursor]) return self.versions
def get_available_actions(self, req, ticket): """Returns a sorted list of available actions""" # The list should not have duplicates. actions = {} for controller in self.action_controllers: weighted_actions = controller.get_ticket_actions(req, ticket) for weight, action in weighted_actions: if action in actions: actions[action] = max(actions[action], weight) else: actions[action] = weight all_weighted_actions = [(weight, action) for action, weight in actions.items()] return [x[1] for x in sorted(all_weighted_actions, reverse=True)]
def get_available_actions(self, req, crashobj): """Returns a sorted list of available actions""" # The list should not have duplicates. actions = {} #for controller in self.action_controllers: #weighted_actions = controller.get_ticket_actions(req, crashobj) or [] #for weight, action in weighted_actions: #if action in actions: #actions[action] = max(actions[action], weight) #else: #actions[action] = weight all_weighted_actions = [(weight, action) for action, weight in actions.items()] return [x[1] for x in sorted(all_weighted_actions, reverse=True)]
def render_admin_panel(self, req, cat, page, version): req.perm.require('TAGS_ADMIN') realms = [p.get_taggable_realm() for p in self.tag_providers if (not hasattr(p, 'check_permission') or \ p.check_permission(req.perm, 'view'))] # Check request for enabled filters, or use default. if [r for r in realms if r in req.args] == []: for realm in realms: req.args[realm] = 'on' checked_realms = [r for r in realms if r in req.args] data = dict(checked_realms=checked_realms, tag_realms=list( dict(name=realm, checked=realm in checked_realms) for realm in realms)) 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, filter=checked_realms) data['selected'] = new_tag query = ' or '.join(['realm:%s' % r for r in checked_realms]) all_tags = sorted(tag_system.get_all_tags(req, query)) 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
def _post_process_request_history(self, req, data): history = [] page_histories = data.get('history', []) resource = data['resource'] tags_histories = tag_changes(self.env, resource) for page_history in page_histories: while tags_histories and \ tags_histories[0][0] >= page_history['date']: tags_history = tags_histories.pop(0) date = tags_history[0] author = tags_history[1] old_tags = split_into_tags(tags_history[2] or '') new_tags = split_into_tags(tags_history[3] or '') added = sorted(new_tags - old_tags) added = added and \ tagn_("%(tags)s added", "%(tags)s added", len(added), tags=tag.em(', '.join(added))) removed = sorted(old_tags - new_tags) removed = removed and \ tagn_("%(tags)s removed", "%(tags)s removed", len(removed), tags=tag.em(', '.join(removed))) # TRANSLATOR: How to delimit added and removed tags. delim = added and removed and _("; ") comment = tag(tag.strong(_("Tags")), ' ', added, delim, removed) url = req.href(resource.realm, resource.id, version=page_history['version'], tags_version=to_utimestamp(date)) history.append({'version': '*', 'url': url, 'date': date, 'author': author, 'comment': comment, 'ipnr': ''}) history.append(page_history) data.update(dict(history=history, wiki_to_oneliner=self._wiki_to_oneliner))
def linkify_ids(env, req, ids): data = [] for id in sorted(ids, key=lambda x: int(x)): try: tkt = Ticket(env, id) data.append( tag.a('#%s' % tkt.id, href=req.href.ticket(tkt.id), class_='%s ticket' % tkt['status'], title=tkt['summary'])) except ResourceNotFound: data.append('#%s' % id) data.append(', ') if data: del data[-1] # Remove the last comma if needed return tag.span(*data)
def _next_comment_number(self): """ Function that returns the next available comment number. If no blog post exists (can't attach comment), it returns 0. """ cnx = self.env.get_db_cnx() cursor = cnx.cursor() cursor.execute("SELECT number FROM fullblog_comments " "WHERE name=%s", (self.post_name, )) cmts = sorted([row[0] for row in cursor]) if cmts: return cmts[-1] + 1 # Add 1 for next free # No item found - need to double-check to find out why bp = BlogPost(self.env, self.post_name) if bp.get_versions(): return 1 else: return 0
def render_table(items, colspec, render_item): try: columns = max(int(colspec), 1) except: columns = 3 nbsp = Markup(' ') hdr = [tag.th('Markup', nbsp), tag.th(nbsp, 'Display')] def render_def(s): rendered = s and render_item(s) or None return [tag.td(s), tag.td(rendered)] return tag.table(tag.tr((hdr + [tag.th(nbsp)]) * (columns-1) + hdr), [tag.tr([render_def(s) + [tag.td(nbsp)] for s in row[:-1]] + render_def(row[-1])) for row in group(sorted(items), columns)], class_="wiki")