def _link_crash(self, req, uuid, show_uuid=False, sysinfo=False): ret = None try: crash = CrashDump(env=self.env, uuid=uuid) if sysinfo: href = req.href('crash', crash.uuid, 'sysinfo_report') title = 'CrashId#%i (%s)' % (crash.id, crash.uuid) else: if show_uuid: title = str(crash.uuid) else: title = 'CrashId#%i (%s)' % (crash.id, crash.uuid) href = req.href('crash', crash.uuid) if show_uuid: ret = \ tag.a( str(crash.uuid), class_=crash['status'], href=href, title=title, style="white-space: nowrap" ) else: ret = \ tag.a( 'CrashId#%i' % crash.id, class_=crash['status'], href=href, title=crash.uuid ) except ResourceNotFound: pass return ret
def filter_stream(self, req, method, filename, stream, data): project_id = self.env.config.get('por-dashboard', 'project-id') if project_id: project = DBSession().query(Project).get(project_id) # XXX se project is None, 404 stream |= Transformer(".//div[@id='trac-before-subnav']").prepend(tag.ul( tag.li(tag.a("Home", href="/")), tag.li( tag.span(" / ", class_="divider"), tag.a(project.customer.name, href="/admin/Customer/%s" % project.customer.id) ), tag.li( tag.span(" / ", class_="divider"), tag.a(project.name, href="/admin/Project/%s" % project.id) ), tag.li( tag.span(" / ", class_="divider"), tag.a('Trac', href="/trac/%s" % project.id), class_='active' ), class_="breadcrumb noprint", )) return stream
def render_timeline_event(self, context, field, event): """Display the title of the event in the given context. Full description here http://trac.edgewall.org/browser/trunk/trac/timeline/api.py """ if field == 'url': return event[3]['url'] elif field == 'title': return "Build %s #%s was %s" % (event[3]['builder'], event[3]['num'], event[0]) elif field == 'description': data = event[3] msg = tag.span() if data['source'] and data["rev"]: rev_msg = tag.div("rev: ", tag.a(data['rev'][:7], href=context.href("/browser/%s" % data['source'], rev=data['rev'])), " ", tag.a(tag.img(src=context.href("/chrome/common/changeset.png")), href=context.href("/changeset/%s/%s" % (data['rev'], data['source']))) ) msg.append(rev_msg) if 'error' in event[3] and event[3]['error']: error_msg = tag.div(event[3]['error'], " ") if 'error_log' in event[3] and event[3]['error_log']: error_msg.append(tag.a("Log", href=event[3]['error_log'])) msg.append(error_msg) return msg
def _ticket_links(env, formatter, t, a_class=""): """Build links to tickets.""" tkt_id = str(t.get("id")) status = t.get("status") summary = to_unicode(t.get("summary")) owner = to_unicode(t.get("owner")) description = to_unicode(t.get("description")) url = t.get("href") if status == "closed": a_class = a_class + "closed" else: a_class = a_class + "open" # Reduce content for tooltips. markup = format_to_html(env, formatter.context, description) extractor = TextExtractor() extractor.feed(markup) tip = tag.span(shorten_line(extractor.getvalue())) ticket = tag.a("#" + tkt_id, href=url) ticket(tip, class_="tip", target="_blank") ticket = tag.div(ticket, class_=a_class, align="left") # Fix stripping of regular leading space in IE. blank = " " ticket(Markup(blank), summary, " (", owner, ")") summary = tag(summary, " (", owner, ")") ticket_short = tag.span(tag.a("#" + tkt_id, href=url, target="_blank", title_=summary), class_=a_class) return ticket, ticket_short
def _format_link(self, formatter, ns, pagename, label, ignore_missing, original_label=None): pagename, query, fragment = formatter.split_link(pagename) version = None if '@' in pagename: pagename, version = pagename.split('@', 1) if version and query: query = '&' + query[1:] pagename = pagename.strip('/') or 'WikiStart' if 'WIKI_VIEW' in formatter.perm('wiki', pagename, version): href = formatter.href.wiki(pagename, version=version) + query \ + fragment if self.has_page(pagename): return tag.a(label, href=href, class_='wiki') else: if ignore_missing: return original_label or label if 'WIKI_CREATE' in formatter.perm('wiki', pagename, version): return tag.a(label + '?', class_='missing wiki', href=href, rel='nofollow') else: return tag.a(label + '?', class_='missing wiki') elif ignore_missing and not self.has_page(pagename): return label else: return tag.a(label, class_='forbidden wiki', title=_("no permission to view this wiki page"))
def render_bookmarker(self, req): if 'action' in req.args and \ req.args['action'] in self.nonbookmarkable_actions: return resource = self._get_resource_uri(req) bookmark = self.get_bookmark(req, resource) if bookmark: class_ = 'bookmark_on' title = 'Delete Bookmark' href = req.href.bookmark('delete', resource) else: class_ = 'bookmark_off' title = 'Bookmark this page' href = req.href.bookmark('add', resource) anchor = tag.a(u'\u200b', id='bookmark_this', class_=class_, title=title, href=href, data_list=req.href.bookmark()) req.chrome.setdefault('ctxtnav', []).insert(0, anchor) add_script(req, 'bookmark/js/tracbookmark.js') add_stylesheet(req, 'bookmark/css/tracbookmark.css') menu = self._get_bookmarks_menu(req) item = tag.span(tag.a('Bookmarks', href=req.href.bookmark()), menu, id='bookmark_menu') add_ctxtnav(req, item)
def expand_macro(self, formatter, name, content): env = formatter.env req = formatter.req if not 'VOTE_VIEW' in req.perm: return # Simplify function calls. format_author = partial(Chrome(self.env).format_author, req) if not content: args = [] compact = None kw = {} top = 5 else: args, kw = parse_args(content) compact = 'compact' in args and True top = as_int(kw.get('top'), 5, min=0) if name == 'LastVoted': lst = tag.ul() for i in self.get_votes(req, top=top): resource = Resource(i[0], i[1]) # Anotate who and when. voted = ('by %s at %s' % (format_author(i[3]), format_datetime(to_datetime(i[4])))) lst(tag.li(tag.a( get_resource_description(env, resource, compact and 'compact' or 'default'), href=get_resource_url(env, resource, formatter.href), title=(compact and '%+i %s' % (i[2], voted) or None)), (not compact and Markup(' %s %s' % (tag.b('%+i' % i[2]), voted)) or ''))) return lst elif name == 'TopVoted': realm = kw.get('realm') lst = tag.ul() for i in self.get_top_voted(req, realm=realm, top=top): if 'up-only' in args and i[2] < 1: break resource = Resource(i[0], i[1]) lst(tag.li(tag.a( get_resource_description(env, resource, compact and 'compact' or 'default'), href=get_resource_url(env, resource, formatter.href), title=(compact and '%+i' % i[2] or None)), (not compact and ' (%+i)' % i[2] or ''))) return lst elif name == 'VoteList': lst = tag.ul() resource = resource_from_path(env, req.path_info) for i in self.get_votes(req, resource, top=top): vote = ('at %s' % format_datetime(to_datetime(i[4]))) lst(tag.li( compact and format_author(i[3]) or Markup(u'%s by %s %s' % (tag.b('%+i' % i[2]), tag(format_author(i[3])), vote)), title=(compact and '%+i %s' % (i[2], vote) or None))) return lst
def get_content(self, req, entry, data): # Make sure that repository is a Subversion repository, since # we're relying for now on Subversion to provide WebDAV # support. if not is_subversion_repository(data.get('repos')): return None # don't show up in the menu if the user is browsing a non-HEAD revision if data.get('stickyrev'): return None reponame = data['reponame'] or '' ext = os.path.splitext(entry.name)[1][1:] if not entry.isdir and ext in self.office_file_extensions: path = self.get_subversion_path(entry) href = self.get_subversion_href(data, path) if re.search('(MSIE |Trident/)', req.environ.get('HTTP_USER_AGENT', '')): # for IE, we'll use ActiveX as this may work with older Office installations return tag.a(tag.i(class_="fa fa-edit"), ' %s with Microsoft Office' % self._verb, href=href, class_=self._link_class) else: # otherwise, let's try https://msdn.microsoft.com/en-us/library/office/dn906146.aspx # which is Office 2010 SP2 and above application = self.office_file_extensions[ext] return tag.a(tag.i(class_="fa fa-edit"), ' %s with Microsoft %s' % (self._verb, application.title()), href="ms-%s:%s|u|%s" % (application, self._href_mode, href))
def get_navigation_items(self, req): if req.authname and req.authname != 'anonymous': yield ('metanav', 'openidlogin', 'logged in as %s' % (req.session.get('name') or req.authname)) yield ('metanav', 'openidlogout', tag.a('Logout', href=req.href.openidlogout())) else: yield ('metanav', 'openidlogin', tag.a(('OpenID Login'), href=req.href.openidlogin()))
def nav_pager(d): return tag.span( tag.a(u'\u25c4', href=nav_href(d - timedelta(days=1))), tag.a(_("Current month"), href=nav_href(get_today(req.tz))), tag.a(u'\u25ba', href=nav_href(d + timedelta(days=31))), class_='ticketcalendar-pager')
def prevnext_ctxnav(self, req, prev_label, next_label, up_label=None): links = req.chrome["links"] prev_link = next_link = None if not any(lnk in links for lnk in ("prev", "up", "next")): # Short circuit return if "prev" in links: prev = links["prev"][0] prev_link = tag.a(prev_label, href=prev["href"], title=prev["title"], class_="prev") add_ctxtnav( req, tag.span("", prev_link or prev_label, id="leftarrow", class_=not prev_link and "missing" or None) ) if up_label and "up" in links: up = links["up"][0] add_ctxtnav(req, tag.a(up_label, href=up["href"], title=up["title"])) if "next" in links: next_ = links["next"][0] next_link = tag.a(next_label, href=next_["href"], title=next_["title"], class_="next") add_ctxtnav( req, tag.span(next_link or next_label, "", id="rightarrow", class_=not next_link and "missing" or None) )
def expand_macro(self,formatter,name,content): citelist = getattr(formatter, CITELIST,[]) columns = ['itemTypeID','firstCreator','year', 'publicationTitle','volume','issue','pages','title','url'] model = ZoteroModelProvider(self.env) item_ids = model.get_items_ids_by_keys( citelist ) item_data = model.get_item_columns_by_iids(item_ids,columns) refs = [] for itemID, itemTypeID, firstCreator, year, publicationTitle, volume, issue, pages, title, url in item_data: prefix = '' if firstCreator and firstCreator != 'None': prefix += firstCreator if year and year != 'None': prefix += ' ' + year + '. ' titlehref = '' if title and title != 'None': titlehref = tag.a(title, href = formatter.req.href.zotero('item/' + str(itemID))) suffix = '' if publicationTitle and publicationTitle != 'None': suffix += '. ' + publicationTitle if volume and volume != 'None': suffix += ', ' + str(volume) if pages and pages != 'None': suffix += ': ' + pages + '.' ref = [] if url and url != 'None': ref = tag.li( tag.span( prefix ), tag.span(titlehref), tag.span(suffix), tag.br(),tag.span(tag.a(url, href=url ),style = 'font-size:x-small;') ) else: ref = tag.li( tag.span( prefix ), tag.span(titlehref), tag.span(suffix) ) refs.append(ref) return tag.div(tag.ol(refs),id='References')
def post_process_request(self, req, template, data, content_type): if 'BLOG_VIEW' not in req.perm: return (template, data, content_type) if '_blog_watch_message_' in req.session: add_notice(req, req.session['_blog_watch_message_']) del req.session['_blog_watch_message_'] if req.authname == "anonymous": return (template, data, content_type) # FullBlogPlugin sets the blog_path arg in pre_process_request name = req.args.get('blog_path') if not name: return (template, data, content_type) klass = self.__class__.__name__ attrs = SubscriptionAttribute.find_by_sid_class_and_target( self.env, req.session.sid, req.session.authenticated, klass, name) if attrs: add_ctxtnav(req, tag.a(_('Unwatch This'), href=req.href.blog_watch(name))) else: add_ctxtnav(req, tag.a(_('Watch This'), href=req.href.blog_watch(name))) return (template, data, content_type)
def render_voter(self, req): resource = self.normalise_resource(req.path_info) vote = self.get_vote(req, resource) up = tag.img(src=req.href.chrome('vote/' + self.image_map[vote][0]), alt='Up-vote') down = tag.img(src=req.href.chrome('vote/' + self.image_map[vote][1]), alt='Down-vote') if 'VOTE_MODIFY' in req.perm and get_reporter_id(req) != 'anonymous': down = tag.a(down, id='downvote', href=req.href.vote('down', resource), title='Down-vote') up = tag.a(up, id='upvote', href=req.href.vote('up', resource), title='Up-vote') add_script(req, 'vote/js/tracvote.js') shown = req.session.get('shown_vote_message') if not shown: add_notice(req, 'You can vote for resources on this Trac ' 'install by clicking the up-vote/down-vote arrows ' 'in the context navigation bar.') req.session['shown_vote_message'] = '1' body, title = self.format_votes(resource) votes = tag.span(body, id='votes') add_stylesheet(req, 'vote/css/tracvote.css') elm = tag.span(up, votes, down, id='vote', title=title) req.chrome.setdefault('ctxtnav', []).insert(0, elm)
def filter_stream(self, req, method, filename, stream, data): """ filter project fields to have it correctly display on the ticket.html """ if filename == 'ticket.html': project = [ field for field in data['fields'] if field['name'] == 'project' ][0] ticket_id = data['ticket'].id if ticket_id is None: # new ticket field = 'none' else: proj = self.getProjectByTicketId(ticket_id) if proj == 'none': field = 'none' else: field = tag.a(proj['name'], href=req.href('c_projects', proj['id']), title="Project for ticket %s" % data['ticket'].id) project['rendered'] = field stream |= Transformer("//input[@id='field-project']").replace(field) customer = [ c_field for c_field in data['fields'] if c_field['name'] == 'customer' ][0] if ticket_id is None: # new ticket c_field = 'none' else: if proj != 'none': cust = self.getCustomerByProjectId(proj['id']) c_field = tag.a(cust['name'], href=req.href('cust','projects', cust['id']), title="Customer for ticket %s" % data['ticket'].id) else: c_field = 'none' customer['rendered'] = c_field stream |= Transformer("//input[@id='field-customer']").replace(c_field) return stream
def test_attributes_preserved_in_navigation_item(self): class TestNavigationContributor1(Component): implements(INavigationContributor) def get_active_navigation_item(self, req): return None def get_navigation_items(self, req): yield 'mainnav', 'test1', \ tag.a('Test 1', href='test1', target='blank') class TestNavigationContributor2(Component): implements(INavigationContributor) def get_active_navigation_item(self, req): return None def get_navigation_items(self, req): yield 'mainnav', 'test2', \ tag.a('Test 2', href='test2', target='blank') req = Request(abs_href=Href('http://example.org/trac.cgi'), href=Href('/trac.cgi'), base_path='/trac.cgi', path_info='/', add_redirect_listener=lambda listener: None) self.env.config.set('mainnav', 'test1.label', 'Test One') self.env.config.set('mainnav', 'test2.label', 'Test Two') self.env.config.set('mainnav', 'test2.href', 'testtwo') chrome = Chrome(self.env) items = chrome.prepare_request(req)['nav']['mainnav'] item = self._get_navigation_item(items, 'test1') self.assertEqual(str(tag.a('Test One', href='test1', target='blank')), str(item['label'])) item = self._get_navigation_item(items, 'test2') self.assertEqual(str(tag.a('Test Two', href='testtwo', target='blank')), str(item['label']))
def _format_link(self, formatter, ns, target, label, match=None): try: paste = Paste(self.env, id=target) return tag.a(label, title=paste.title, href=formatter.href.pastebin(paste.id), class_='paste') except Exception, e: return tag.a(label, class_='missing paste')
def _format_exomine_link(self, formatter, ns, target, label): # Wiki support for short exomine links: # exomine:3234 -> https://secure.exocad.com/exomine/issues/3234 # exomine:3234:12 -> https://secure.exocad.com/exomine/issues/3234#note-12 def get_url(ticket, comment=None): base_exomine_url = "" href = "https://secure.exocad.com/exomine/issues/{0}".format(ticket) if comment is not None: href += "#note-{0}".format(comment) return href try: if ":" in target: arr = target.split(":") if len(arr) == 2: href = get_url(arr[0], arr[1]) title = _("Comment %(cnum)s for exomine ticket #%(id)s", cnum=arr[1], id=arr[0]) return tag.a(tag.span(class_="icon") + label, href=href, title=title, class_="ext-link") else: href = get_url(target) title = _("exomine ticket #%(id)s", id=target) return tag.a(tag.span(class_="icon") + label, href=href, title=title, class_="ext-link") except ValueError: pass return tag.a(label, class_="unable to parse") # Todo: Add this class
def _format_comment_link(self, formatter, ns, target, label): resource = None if ':' in target: elts = target.split(':') if len(elts) == 3: cnum, realm, id = elts if cnum != 'description' and cnum and not cnum[0].isdigit(): realm, id, cnum = elts # support old comment: style resource = formatter.resource(realm, id) else: resource = formatter.resource cnum = target if resource and resource.realm == 'ticket': id = as_int(resource.id, None) if id is not None: href = "%s#comment:%s" % (formatter.href.ticket(resource.id), cnum) title = _("Comment %(cnum)s for Ticket #%(id)s", cnum=cnum, id=resource.id) if 'TICKET_VIEW' in formatter.perm(resource): for status, in self.env.db_query( "SELECT status FROM ticket WHERE id=%s", (id,)): return tag.a(label, href=href, title=title, class_=status) return tag.a(label, href=href, title=title) return label
def expand_macro(self, formatter, name, content): attachment_type = "" if content: argv = [arg.strip() for arg in content.split(',')] if len(argv) > 0: attachment_type = argv[0] db = self.env.get_db_cnx() if db == None: return "No DB connection" attachmentFormattedList="" cursor = db.cursor() if attachment_type == None or attachment_type == "": cursor.execute("SELECT type,id,filename,size,time," "description,author,ipnr FROM attachment") else: cursor.execute("SELECT type,id,filename,size,time," "description,author,ipnr FROM attachment " "WHERE type=%s", (attachment_type, )) formatters={"wiki": formatter.href.wiki, "ticket": formatter.href.ticket} types={"wiki": "", "ticket": "ticket "} return tag.ul( [tag.li( tag.a(filename, href=formatter.href.attachment(type + "/" + id + "/" + filename)), " (", tag.span(pretty_size(size), title=size), ") - added by ", tag.em(author), " to ", tag.a(types[type] + " " + id, href=formatters[type](id)), " ") for type,id,filename,size,time,description,author,ipnr in cursor if self._has_perm(type, id, filename, formatter.context)]) return attachmentFormattedList
def _format_link(self, formatter, ns, target, label, fullmatch=None): intertrac = formatter.shorthand_intertrac_helper(ns, target, label, fullmatch) if intertrac: return intertrac try: link, params, fragment = formatter.split_link(target) r = Ranges(link) if len(r) == 1: num = r.a ticket = formatter.resource('ticket', num) from trac.ticket.model import Ticket if Ticket.id_is_valid(num) and \ 'TICKET_VIEW' in formatter.perm(ticket): # TODO: watch #6436 and when done, attempt to retrieve # ticket directly (try: Ticket(self.env, num) ...) cursor = formatter.db.cursor() cursor.execute("SELECT type,summary,status,resolution " "FROM ticket WHERE id=%s", (str(num),)) for type, summary, status, resolution in cursor: title = self.format_summary(summary, status, resolution, type) href = formatter.href.ticket(num) + params + fragment return tag.a(label, class_='%s ticket' % status, title=title, href=href) else: ranges = str(r) if params: params = '&' + params[1:] return tag.a(label, title='Tickets '+ranges, href=formatter.href.query(id=ranges) + params) except ValueError: pass return tag.a(label, class_='missing ticket')
def expand_macro(self, formatter, name, content): interwikis = [] for k in sorted(self.keys()): prefix, url, title = self[k] interwikis.append({ 'prefix': prefix, 'url': url, 'title': title, 'rc_url': self._expand_or_append(url, ['RecentChanges']), 'description': url if title == prefix else title }) return tag.table( tag.tr(tag.th(tag.em(_("Prefix"))), tag.th(tag.em(_("Site")))), [ tag.tr( tag.td(tag.a(w['prefix'], href=w['rc_url'])), tag.td(tag.a(w['description'], href=w['url']))) for w in interwikis ], class_="wiki interwiki")
def _format_sha_link(self, formatter, sha, label): # FIXME: this function needs serious rethinking... reponame = '' context = formatter.context while context: if context.resource.realm in ('source', 'changeset'): reponame = context.resource.parent.id break context = context.parent try: repos = self.env.get_repository(reponame) if not repos: raise Exception("Repository '%s' not found" % reponame) sha = repos.normalize_rev(sha) # in case it was abbreviated changeset = repos.get_changeset(sha) return tag.a(label, class_='changeset', title=shorten_line(changeset.message), href=formatter.href.changeset(sha, repos.reponame)) except Exception, e: return tag.a(label, class_='missing changeset', title=to_unicode(e), rel='nofollow')
def prevnext_ctxnav(self, req, prev_label, next_label, up_label=None): links = req.chrome['links'] prev_link = next_link = None if not any(lnk in links for lnk in ('prev', 'up', 'next')): # Short circuit return if 'prev' in links: prev = links['prev'][0] prev_link = tag.a(prev_label, href=prev['href'], title=prev['title'], class_='prev') add_ctxtnav(req, tag.span('', prev_link or prev_label, id='leftarrow', class_=not prev_link and 'missing' or None)) if up_label and 'up' in links: up = links['up'][0] add_ctxtnav(req, tag.a(up_label, href=up['href'], title=up['title'])) if 'next' in links: next_ = links['next'][0] next_link = tag.a(next_label, href=next_['href'], title=next_['title'], class_='next') add_ctxtnav(req, tag.span(next_link or next_label, '', id='rightarrow', class_=not next_link and 'missing' or None))
def get_navigation_items(self, req): if req.authname and req.authname != "anonymous": if not self.env.is_component_enabled(LoginModule): yield ("metanav", "openidlogin", "logged in as %s" % (req.session.get("name") or req.authname)) yield ("metanav", "openidlogout", tag.a("Logout", href=req.href.openidlogout())) else: yield ("metanav", "openidlogin", tag.a(("OpenID Login"), href=req.href.openidlogin()))
def _link_refs(self, req, refs_text, verbose_link=False): items_tag = None items, verbose_items = [], [] elem = verbose_elem = "#%s" % refs_text try: c = self.cursor c.execute("SELECT id,name FROM assets_feature WHERE id ='%s'" % refs_text) row = c.fetchone() if row: title = shorten_line(row[1]) attr = { "class_": "assigned", "href": "/splice/tractab/SPLICE/projects/feature/" + str(row[0]), "title": title, } elem = tag.a("#%s %s" % (refs_text, title), **attr) verbose_elem = tag.a("#%s %s" % (refs_text, title), **attr) except ResourceNotFound: pass # not supposed to happen, just in case items.extend([elem, ", "]) verbose_items.extend([verbose_elem, tag.br()]) if items: items_tag = [tag.span(items[:-1], id="tref_ticketid")] if verbose_link: vattr = {"id": "tref_summary", "class_": "tref-display-none"} items_tag.append(tag.span(verbose_items[:-1], **vattr)) return tag(items_tag)
def get_navigation_items(self, req): if 'WIKI_VIEW' in req.perm('wiki'): yield ('mainnav', 'wiki', tag.a(_('Wiki'), href=req.href.wiki(), accesskey=1)) yield ('metanav', 'help', tag.a(_('Help/Guide'), href=req.href.wiki('TracGuide'), accesskey=6))
def expand_macro(self, formatter, name, content): offset = +1 label = None if content is not None: if ',' in content: offset, label = content.split(',', 1) elif content and content[0] in '+-': offset = content else: label = content try: offset = int(offset) except ValueError: offset = 0 mp = MyPageModule(self.env) base = mp.get_mypage_base(formatter.perm.username) all_mypages = mp.get_all_mypages(base) r = formatter.resource if r.realm == 'wiki' and r.id.startswith(base): mypage = r.id else: tzinfo = getattr(formatter.context.req, 'tz', None) now = datetime.now(tzinfo or localtz) today = format_date(now, 'iso8601', tzinfo) mypage = '/'.join([base, today]) selected = base idx = bisect(all_mypages, mypage) # adjust to actual position if mypage exists if 0 <= idx - 1 < len(all_mypages) and all_mypages[idx -1] == mypage: idx -= 1 self.log.debug("Reference is %s, pos %d in %r", mypage, idx, all_mypages) # Special cases: at the beginning or at the end, the # predecessors resp. successors are "missing" missing = False if idx >= len(all_mypages) - 1 and offset > 0: missing, tooltip = True, _("(at the end)") elif idx < 1 and offset < 0: missing, tooltip = True, _("(at the beginning)") if missing: if not label: label, tooltip = tooltip, None return tag.a(label, title=tooltip, class_='missing') # Link to the targeted `MyPage` page idx += offset selected = all_mypages[max(0, min(idx, len(all_mypages) - 1))] self.log.debug("With offset %d, going to %d (adjusted to %d)", offset, idx, max(0, min(idx, len(all_mypages) - 1))) selected_day = selected.split('/')[-1] try: tooltip = _("MyPage for %(day)s", day=format_date(parse_date(selected_day))) except TracError: tooltip = _("non-day page '%(special)'", special=selected_day) return tag.a(label if label is not None else selected, title=tooltip, href=formatter.href.wiki(selected))
def get_navigation_items(self, req): if req.perm.has_permission('SCREENSHOTS_VIEW'): if self.mainnav_title: yield ('mainnav', 'screenshots', tag.a(self.mainnav_title, href = req.href.screenshots())) if self.metanav_title: yield ('metanav', 'screenshots', tag.a(self.metanav_title, href = req.href.screenshots()))
def _format_link(self, formatter, ns, target, label): try: link, params, fragment = formatter.split_link(target) review = formatter.resource('review', link) return tag.a(label, href=self.get_resource_url(review, formatter.href)) except ValueError: pass return tag.a(label, class_='missing review')
def get_navigation_items(self, req): if 'TAGS_VIEW' in req.perm: label = tag_("Tags") yield ('mainnav', 'tags', builder.a(label, href=req.href.tags(), accesskey='T'))
def render_widget(self, name, context, options): """Gather timeline events and render data in compact view """ data = None req = context.req try: timemdl = self.env[TimelineModule] admin_page = tag.a(_("administration page."), title=_("Plugin Administration Page"), href=req.href.admin('general/plugin')) if timemdl is None: return 'widget_alert.html', { 'title': _("Activity"), 'data': { 'msglabel': _("Warning"), 'msgbody': tag_( "The TimelineWidget is disabled because the " "Timeline component is not available. " "Is the component disabled? " "You can enable from the %(page)s", page=admin_page), 'dismiss': False, } }, context params = ('from', 'daysback', 'doneby', 'precision', 'filters', 'max', 'realm', 'id') start, days, user, precision, filters, count, realm, rid = \ self.bind_params(name, options, *params) if context.resource.realm == 'ticket': if days is None: # calculate a long enough time daysback ticket = Ticket(self.env, context.resource.id) ticket_age = datetime.now(utc) - ticket.time_created days = ticket_age.days + 1 if count is None: # ignore short count for ticket feeds count = 0 if count is None: count = self.default_count fakereq = dummy_request(self.env, req.authname) fakereq.args = { 'author': user or '', 'daysback': days or '', 'max': count, 'precision': precision, 'user': user } if filters: fakereq.args.update(dict((k, True) for k in filters)) if start is not None: fakereq.args['from'] = start.strftime('%x %X') wcontext = context.child() if (realm, rid) != (None, None): # Override rendering context resource = Resource(realm, rid) if resource_exists(self.env, resource) or \ realm == rid == '': wcontext = context.child(resource) wcontext.req = req else: self.log.warning("TimelineWidget: Resource %s not found", resource) # FIXME: Filter also if existence check is not conclusive ? if resource_exists(self.env, wcontext.resource): module = FilteredTimeline(self.env, wcontext) self.log.debug('Filtering timeline events for %s', wcontext.resource) else: module = timemdl data = module.process_request(fakereq)[1] except TracError, exc: if data is not None: exc.title = data.get('title', _('Activity')) raise
if trac_version < trac_tags[0]: args = fakereq, self.env.get_db_cnx(), rptid else: args = fakereq, rptid data = rptmdl._render_view(*args)[1] except ResourceNotFound, exc: raise InvalidIdentifier(unicode(exc)) except RequestDone: raise TracError('Cannot execute report. Redirection needed') except TracError, exc: if data is not None: exc.title = data.get('title', 'TracReports') raise else: title = data.get('title', '%s {%s}' % (_('Report'), rptid)) rptctx = Context.from_request(fakereq, 'report', rptid) return 'widget_grid.html', \ { 'title' : title, 'data' : data, 'ctxtnav' : [ tag.a(_('More'), href=req.href('report', rptid)), ('REPORT_MODIFY' in req.perm(rptctx.resource)) and \ tag.a(_('Edit'), href=req.href('report', rptid, action='edit')) or None, ], 'altlinks' : fakereq.chrome.get('links', {}).get('alternate') }, \ rptctx render_widget = pretty_wrapper(render_widget, check_widget_name)
def render(self, ticketset): return_div = tag.div(class_=self.cssclass + ' projectplanrender') # check for missing parameters missingparameter = False if self.rows == [] or self.rows == None: return_div( tag.div( 'Missing parameter "rows": use a semicolon-separated list to input the "' + self.rowtype + '".', class_='ppwarning')) missingparameter = True if self.rowtype == None or str(self.rowtype).strip() == '': return_div( tag.div( 'Missing parameter "rowtype": specifies the ticket attribute that should be showed at the rows.', class_='ppwarning')) missingparameter = True if self.cols == [] or self.cols == None: return_div( tag.div( 'Missing parameter: use a semicolon-separated list to input the "cols".', class_='ppwarning')) missingparameter = True if self.coltype == None or str(self.coltype).strip() == '': return_div( tag.div( 'Missing parameter "coltype": specifies the ticket attribute that should be showed in the columns.', class_='ppwarning')) missingparameter = True if missingparameter: return return_div #ul = tag.ul() #for tid in ticketset.getIDSortedList(): #ticket = ticketset.getTicket(tid) #ul( tag.li(tid, " ",ticket.getfield('component') , " ", ticket.getfield('owner') )) #return_div(ul) def getstatistictitle(statusdict): mytitle = '' mysum = 0 for status in statusdict: mytitle += "%s: %s\n" % (status, str(statusdict[status])) mysum += int(statusdict[status]) mytitle += "%s: %s" % ('number', mysum) return mytitle def setKV(myStruct, myKey, newValue): ''' shortcut to set the values correctly used to reduce the code needed while using a list as key of a dict ''' myStruct[str(myKey)] = newValue def tableKeyPrettyPrint(mylist): ''' transform a list of keys to a user readable string in: ['a','b'] --> out: 'a|b' ''' return '|'.join(mylist) def tableKeyQueryParameter(parameter, mylist): ''' transform a list of keys to a Trac query string parameter (OR) in: x, ['a','b'] --> out: 'x=a&x=b' ''' return '&'.join(["%s=%s" % (parameter, s) for s in mylist]) chartheight = 80 chartwidth = 170 data = {} statistics = {} # init table data for row in self.rows: colstatistics = {} colkeys = {} for col in self.cols: # colkeys[col] = [] setKV(colkeys, col, []) # colstatistics[col] = {} setKV(colstatistics, col, {}) # data[row] = colkeys setKV(data, row, colkeys) # statistics[row] = colstatistics setKV(statistics, row, colstatistics) for tid in ticketset.getIDSortedList(): ticket = ticketset.getTicket(tid) ticket_rowtype = ticket.getfield(self.rowtype) ticket_coltype = ticket.getfield(self.coltype) # determine the data cell where the ticket has to be added, keep in mind that rows and cols are list of lists for row in self.rows: for col in self.cols: if ticket_rowtype in row and ticket_coltype in col: data[str(row)][str(col)].append( ticket ) # save tickets at precise values of row and col self.log_debug('row:%s col:%s append:%s' % (row, col, tid)) # if ticket_rowtype in self.rows and ticket_coltype in self.cols : # create HTML table table = tag.table(class_="data pptableticketperday", border="1", style='width:auto;') # create HTML table head thead = tag.thead() tr = tag.tr() tr(tag.th("%s vs %s" % (self.rowtype, self.coltype))) for colkey in self.cols: tr( tag.th(tag.h4( tag.a(tableKeyPrettyPrint(colkey), href=self.macroenv.tracenv.href() + ('/query?%s&order=%s' % (tableKeyQueryParameter( self.coltype, colkey), self.rowtype)))), title="%s is %s" % (self.coltype, tableKeyPrettyPrint(colkey))) ) # first line with all colkeys if self.showsummarypiechart: tr(tag.th(tag.h4("Ticket Overview"))) thead(tr) table(thead) # create HTML table body tbody = tag.tbody() counter = 0 for rowkey in self.rows: # switch line color if counter % 2 == 1: class_ = 'odd' else: class_ = 'even' counter += 1 tr = tag.tr(class_=class_) # new line td = tag.td() # new cell td(tag.h5( tag.a(tableKeyPrettyPrint(rowkey), href=self.macroenv.tracenv.href() + ('/query?%s&order=%s' % (tableKeyQueryParameter( self.rowtype, rowkey), self.coltype)))), title="%s is %s" % (self.rowtype, tableKeyPrettyPrint(rowkey))) # first cell contains row key tr(td) for colkey in self.cols: td = tag.td() for ticket in data[str(rowkey)][str(colkey)]: td(tag.span(self.createTicketLink(ticket), class_='ticket_inner'), " ", mytitle="%s is %s and %s is %s" % (self.rowtype, rowkey, self.coltype, colkey)) # mytitle might be used later by javascript if not statistics[str(rowkey)][str(colkey)].has_key( ticket.getstatus()): statistics[str(rowkey)][str(colkey)][ ticket.getstatus()] = 0 statistics[str(rowkey)][str(colkey)][ ticket.getstatus()] += 1 tr(td) # compute statistics rowstatistics = {} count = 0 for colkey in statistics[str(rowkey)]: for status in statistics[str(rowkey)][str(colkey)]: if not rowstatistics.has_key(status): rowstatistics[status] = 0 try: rowstatistics[status] += statistics[str(rowkey)][str( colkey)][status] count += statistics[str(rowkey)][str(colkey)][status] except: pass if self.showsummarypiechart: tr( tag.td(tag.img(src=self.createGoogleChartFromDict( 'ColorForStatus', rowstatistics, '%s tickets' % (count, ), height=chartheight)), class_='ppstatistics', title=getstatistictitle(rowstatistics))) # Summary tbody(tr) table(tbody) # create HTML table foot if self.showsummarypiechart: fullstatistics = {} tfoot = tag.tfoot() tr = tag.tr() tr(tag.td(tag.h5('Ticket Overview'))) # create statistics for col fullcount = 0 for colkey in self.cols: colstatistics = {} colcount = 0 for rowkey in self.rows: for status in statistics[str(rowkey)][str(colkey)]: if not fullstatistics.has_key(status): fullstatistics[status] = 0 if not colstatistics.has_key(status): colstatistics[status] = 0 try: colstatistics[status] += statistics[str(rowkey)][ str(colkey)][status] colcount += statistics[str(rowkey)][str( colkey)][status] fullstatistics[status] += statistics[str(rowkey)][ str(colkey)][status] fullcount += statistics[str(rowkey)][str( colkey)][status] except: pass tr( tag.td( tag.img(src=self.createGoogleChartFromDict( 'ColorForStatus', colstatistics, '%s tickets' % (colcount, ), height=chartheight)), title=getstatistictitle(colstatistics))) # Col Summary tr( tag.td( tag.img(src=self.createGoogleChartFromDict( 'ColorForStatus', fullstatistics, '%s tickets' % (fullcount, ), height=chartheight)), class_='ppstatistics', title=getstatistictitle(fullstatistics))) # Full Summary tfoot(tr) table(tfoot) return_div(table) return return_div
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 = set([p.get_taggable_realm() for p in tag_system.tag_providers]) 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])) env.log.debug('LISTTAGGED_QUERY: %s', 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, 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) # Fallback to resource provider method. desc = desc or get_resource_description(env, resource, context=context) 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 __call__(self, obj, depth=0, dontRender=False): from genshi.builder import tag if depth > self.maxDepth: raise RuntimeError("Maximum nesting depth %d exceeded" % self.maxDepth) kw = self.padding.copy() if depth > 0: kw.update(width='100%') # was [1:] to omit leading woo./wooExtra., but that is not desirable objInExtra = obj.__class__.__module__.startswith('wooExtra.') if self.hideWooExtra and objInExtra: head = tag.span(tag.b(obj.__class__.__name__), title=_ensureUnicode(obj.__class__.__doc__)) else: head = tag.b('.'.join(obj.__class__.__module__.split('.')[0:]) + '.' + obj.__class__.__name__) head = tag.a(head, href=woo.document.makeObjectUrl(obj), title=_ensureUnicode(obj.__class__.__doc__)) ret = tag.table(tag.th(head, colspan=3, align='left'), frame='box', rules='all', **kw) # get all attribute traits first traits = obj._getAllTraits() for trait in traits: if trait.hidden or (self.hideNoGui and trait.noGui ) or trait.noDump or (trait.hideIf and eval( trait.hideIf, globals(), {'self': obj})): continue # start new group (additional line) if trait.startGroup: ret.append( tag.tr( tag.td(tag.i(u'▸ %s' % _ensureUnicode(trait.startGroup)), colspan=3))) attr = getattr(obj, trait.name) if self.showDoc: tr = tag.tr(tag.td(_ensureUnicode(trait.doc))) else: try: if self.hideWooExtra and objInExtra: label = tag.span(tag.b(trait.name), title=_ensureUnicode(trait.doc)) else: label = tag.a(trait.name, href=woo.document.makeObjectUrl( obj, trait.name), title=_ensureUnicode(trait.doc)) tr = tag.tr(tag.td(label)) except UnicodeEncodeError: print( 'ERROR: UnicodeEncodeError while formatting the attribute ', obj.__class__.__name__ + '.' + trait.name) print('ERROR: the docstring is', trait.doc) raise # tr=tag.tr(tag.td(trait.name if not self.showDoc else trait.doc.decode('utf-8'))) # nested object if isinstance(attr, woo.core.Object): tr.append( [tag.td(self(attr, depth + 1), align='justify'), tag.td()]) # sequence of objects (no units here) elif hasattr(attr, '__len__') and len(attr) > 0 and isinstance( attr[0], woo.core.Object): tr.append( tag.td(tag.ol([tag.li(self(o, depth + 1)) for o in attr]))) else: # !! make deepcopy so that the original object is not modified !! import copy attr = copy.deepcopy(attr) if not trait.multiUnit: # the easier case if not trait.prefUnit: unit = u'−' else: unit = _ensureUnicode(trait.prefUnit[0][0]) # create new list, where entries are multiplied by the multiplier if type(attr) == list: attr = [a * trait.prefUnit[0][1] for a in attr] else: attr = attr * trait.prefUnit[0][1] else: # multiple units unit = [] wasList = isinstance(attr, list) if not wasList: attr = [attr] # handle uniformly for i in range(len(attr)): attr[i] = [ attr[i][j] * trait.prefUnit[j][1] for j in range(len(attr[i])) ] for pu in trait.prefUnit: unit.append(_ensureUnicode(pu[0])) if not wasList: attr = attr[0] unit = ', '.join(unit) # sequence type, or something similar if hasattr(attr, '__len__') and not isinstance(attr, (str, bytes)): if len(attr) > 0: tr.append( tag.td(self.htmlSeq(attr, insideTable=False), align='right')) else: tr.append(tag.td(tag.i('[empty]'), align='right')) else: tr.append( tag.td(float2str(attr) if isinstance(attr, float) else str(attr), align='right')) if unit: tr.append(tag.td(unit, align='right')) ret.append(tr) if depth > 0 or dontRender: return ret r1 = ret.generate().render('xhtml', encoding='ascii') if isinstance(r1, bytes): r1 = r1.decode('ascii') return r1 + u'\n'
def get_navigation_items(self, req): if XMailPermissions.checkPermissions(self,req): yield ('mainnav', self.MY_PATH, tag.a('XMail', href=req.href.xmail()))
def expand_macro(self, formatter, name, content): args, kwargs = parse_args(content, strict=False) if len(args) == 1: hat = args[0].lower() title = TITLES.get(hat, '') elif len(args) == 2: hat = args[0].lower() title = args[1] # An simple Exception would probabl;y be better, see http://../ for examples. if len(args) == 0 or hat not in ('black', 'blue', 'green', 'red', 'white', 'yellow', 'intro', 'cite'): raise TracError( 'Invalid parameters, see http://trac-hacks.org/wiki/SixhatsMacro#Examples for example uses.' ) #tags = [tag.strong()('Error: Invalid parameters, see the following examples:')] #tags.extend([tag.pre()(i) for i in HELP.splitlines()]) #return tag.div(class_='system-message')(*tags) if hat == 'cite': if not title: title = "The best way to learn the Six Hats method is to read Edward de Bono's " url = get_absolute_url( formatter.href.base, 'htdocs://sixhats/pdf/six_thinking_hats.pdf') return tag.p()(title, tag.a(href=url)('Six Thinking Hats'), '.') # Not too sure if a plugin should be self-documenting. if hat == 'intro': hide = kwargs.get('hide') tags = [] tags.append( tag.h1(id='SixHatsIntroduction')('Six Hats Introduction')) tags.append( tag.blockquote( tag. p("There is nothing more sad and wasteful than a roomful of intelligent and highly paid people waiting for a chance to attack something the speaker has said. With the Six Hats method the fullest use is made of everyone's intelligence, experience and information. The Six Hats also removes all 'ego' from the discussion process." ))) tags.append( tag.p( 'The Six Thinking Hats represent directions of thought. They are used to request thinking in a paticular direction, ', tag.strong('not'), ' as a description or label to classify your thinking afterwards. They are ', tag.strong('not'), ' used to characterize people. A person is not a black hat, but he or she might prefer to think with the black hat on. It is desirable for everyone to become skilled in the use of all the hats.' )) tags.append(self.expand_macro(formatter, name, 'cite')) tags.append(tag.h1(id='Summary')('Summary')) tags.append(self.expand_macro(formatter, name, 'white,size=m')) li = [ tag.li('The white hat is neutral and objective.'), tag.li('It focusses on facts and figures.'), ] sub_li = [ tag.li('What information do we have?'), tag.li('What information do we need?'), tag.li('What information is missing?'), tag.li('What questions do we need to ask?'), tag.li('How are we going to get the information we need?') ] li.append( tag.li('Questions to ask with the white hat on:', tag.ul(*sub_li))) li.extend([ tag. li('The information can range from hard verifiable facts and figures to soft information such as 3rd party opinions and feelings. Your own opinions and feelings are placed under the red hat.' ), tag.li('Whose fact is it?'), tag.li('Is the information a fact, a likelyhood or a believe?') ]) sub_li = [ tag.li('Always true'), tag.li('Usually true'), tag.li('Generally true'), tag.li('By and large'), tag.li('More often than not'), tag.li('About half the time'), tag.li('Often'), tag.li('Sometimes true'), tag.li('Occastionally true'), tag.li('Been known to happen'), tag.li('Never true'), tag.li('Cannot be true (contradictory)') ] li.append( tag. li('How true is the fact? Frame the information appropriatly:', tag.ul(*sub_li))) li.append( tag. li('Split the facts into two groups: checked facts and believed facts.' )) tags.append(tag.ul(*li)) tags.append(self.expand_macro(formatter, name, 'red,size=m')) li = [ tag.li( 'The red hat is subjective and generally non-rational.'), tag.li('It exposes and legitimizes emotions and feelings.'), tag. li('It allows people to express their opinions, hunches, intuitions and impressions. (a function of their experience)' ), tag. li("Resist the temptation to justify your emotions. You don't need to give a reason or a logical basis." ), tag. li('If emotions and feelings are not permitted as inputs in the thinking process, they will lurk in the background and affect all the thinking in a hidden way.' ), tag. li('The red hat makes feelings visible so that they can become part of the thinking map and also part of the value system that chooses the route on the map.' ) ] sub_li = [ tag. li('Ordinary emotions such as fear and dislike to more subtle ones such as suspicion.' ), tag. li('Complex judgements that go into such type of feelings as a hunch, intuition, sense, taste, aesthetic feeling and other not visibly justified types of feeling.' ) ] li.append( tag.li('The red hat covers two broad types of feelings:', tag.ol(*sub_li))) tags.append(tag.ul(*li)) tags.append(self.expand_macro(formatter, name, 'black,size=m')) li = [ tag. li('The black hat is critical and logical (negative judgements).' ), tag. li("It's perhaps the most important hat, the hat of survival, of caution and of being careful." ), tag. li('It points out how something does not fit our experience, our resources, our policy, our stragegy, our ethics, our values, etc.' ), tag. li('It protects us from wasting energy and money, it seeks to avoid dangers, problems, obstacles and difficulties.' ), tag. li('There must be a logical basis for the criticism and reasons must be capable of standing on their own.' ), tag. li('It focuses on why something may not work or may not be the right thing to do.' ), ] sub_li = [ tag.li('Be as causious and as fiercely critical as possible.'), tag.li( 'Point out errors or deficiencies in the thinking process.' ), tag.li('Question the strength of the evidence.'), tag.li('What are the risks?'), ] li.append( tag.li( 'In order to get the full value from any suggestion or idea, it is important that the black hat be done thoroughly:', tag.ul(*sub_li))) li.extend([ tag. li('Black hat thinking is not argument and must not be allowed to degenerate into argument.' ), tag.li( tag.strong('Caution:'), ' There are people who overuse the black hat and who spend all their time trying to find fault. The fault is not in the black hat but in the abuse, overuse or misuse of the black hat.' ) ]) tags.append(tag.ul(*li)) tags.append(self.expand_macro(formatter, name, 'yellow,size=m')) li = [ tag. li('The yellow hat is optimistic and logical (positive judgements).' ) ] sub_li = [ tag.li('The generation of proposals.'), tag.li('The positive assessment of proposals.'), tag.li("Developing or 'building up' of proposals.") ] li.append( tag.li('The yellow hat is concerned with:', tag.ol(*sub_li))) li.extend([ tag. li('Under the yellow hat a thinker deliberatly sets out to find whatever benefit there may be in a suggestion.' ), tag. li('It is a waste of time setting out to be creative if you are not going to recognize a good idea.' ), tag.li('Value and benefit are by no means always obvious.'), tag. li('Since the yellow hat is logical you should give a reason for the value you put forward.' ), tag. li('The emphasis of yellow hat thinking is on exploration and positive speculation, oppertunity seeking.' ), tag. li("Yellow hat thinking is concerned with the positive attitude of getting the job done, let's make it happen." ) ]) tags.append(tag.ul(*li)) tags.append(self.expand_macro(formatter, name, 'green,size=m')) li = [ tag.li('The green hat is creative, sometimes illogical.'), tag. li('The green hat is concerned with new ideas and new ways of looking at things, new perceptions.' ), tag. li("Under the green hat we lay out options, alternatives and 'possibilities'." ), tag. li('It corrects the faults found under the black hat and find noval ways the exploit the opportunities identified under the yellow hat.' ), tag. li('You use the green hat to deliberately go after new ideas instead of waiting for them to come to you.' ), tag. li('Sometimes you may be required to put forward provocations, illogical ideas, in order to provoke new concepts.' ), tag. li('Under the green hat we use ideas as stepping stones, for their movement value, moving us to new ideas.' ), tag.li('Use random words and ', tag.em('Po'), '.') ] tags.append(tag.ul(*li)) tags.append(self.expand_macro(formatter, name, 'blue,size=m')) li = [ tag. li('The blue hat is for meta-thinking, thinking about thinking.' ), tag. li('Wearing the blue hat we are no longer thinking about the subject; instead, we are thinking about the thinking needed to explore the subject.' ), tag. li('The blue hat thinker is like the conductor of an orchestra.' ), tag. li("Generally the blue hat is worn by the facilitator, chairperson or leader of the session, it's a permanent role." ), tag. li('The blue hat is for the organization and management of thinking, it also controls the process of thinking.' ), tag. li('The initial blue hat defines the problem, list constraints and sets the agenda, the sequence of the other hats to be used.' ), tag. li('Blue hat thinking stops arguments, enforces the discipline and keep people focussed on map making.' ), tag. li('The final blue hat is responsible for summaries, overviews and conclusions.' ) ] tags.append(tag.ul(*li)) fedoras = tag.a('fedoras', href=get_absolute_url( formatter.href.base, 'htdocs://sixhats/img/source/fedoras.svg')) hardhats = tag.a('hardhats', href=get_absolute_url( formatter.href.base, 'htdocs://sixhats/img/source/hardhats.svg')) tophats = tag.a('tophats', href=get_absolute_url( formatter.href.base, 'htdocs://sixhats/img/source/tophats.svg')) tags.append( tag.p( 'PS. If you wish to use any of these images in your own documents rather use the high quality vectors: ', fedoras, ', ', hardhats, ' and ', tophats, '.')) if hide: add_script(formatter.req, 'sixhats/js/hide.js') if hide.lower() == 'hide': return ''.join([ str(i) for i in ('- ', tag.a(id='hide', href='#', style='font-size: 8pt;')( 'show six hats introduction'), ' -', tag.div( id='introduction', style='display: none;', *tags)) ]) else: return ''.join([ str(i) for i in ( '- ', tag.a(id='hide', href='#', style='font-size: 8pt;') ('hide six hats introduction'), ' -', tag.div(id='introduction', *tags)) ]) else: return tag.div(id='introduction', *tags) id = kwargs.get('id', squish_string(title)) size = kwargs.get('size', 'large') type = kwargs.get('type', 'hardhat').lower() h = kwargs.get('h') if type in ('fedora', 'hardhat', 'tophat'): if size.endswith('%'): percentage = float(size.strip('%')) / 100.0 width, height = SIZES[type]['l'] else: percentage = 1 width, height = SIZES[type][size[0]] width = int(width * percentage) height = int(height * percentage) if h: if h == '1': root = tag.h1 elif h == '2': root = tag.h2 elif h == '3': root = tag.h3 elif h == '4': root = tag.h4 else: root = tag.h5 else: s0 = size[0] if s0 == 'l': root = tag.h1 elif s0 == 'm': root = tag.h2 elif s0 == 's': root = tag.h3 else: root = tag.h1 url = get_absolute_url( formatter.href.base, 'htdocs://sixhats/img/%(type)s/%(hat)s.jpg' % { 'type': type, 'hat': hat }) if id: return root(id=id)(tag.img(src=url, width=width, height=height), title) else: return root()(tag.img(src=url, width=width, height=height), title)
def get_navigation_items(self, req): if 'TIMELINE_VIEW' in req.perm: yield ('mainnav', 'timeline', tag.a(_("Timeline"), href=req.href.timeline(), accesskey=2))
def get_navigation_items(self, req): if 'TICKET_GRAPH' in req.perm: yield ('mainnav', 'ticketgraph', tag.a(_('Ticket Graph'), href=req.href.ticketgraph()))
def annotate_row(self, context, row, lineno, line, data): lineno += data['offset'] id = data['id'] % lineno if data['marks'] and lineno in data['marks']: row(class_='hilite') row.append(tag.th(id=id)(tag.a(lineno, href='#' + id)))
def get_navigation_items(self, req): if 'REPORT_VIEW' in req.perm: yield ('mainnav', 'tickets', tag.a(_('View Tickets'), href=req.href.report()))
def annotate_row(self, context, row, lineno, line, data): row.append( tag.th(id='L%s' % lineno)(tag.a(lineno, href='javascript:setLineNum(%s)' % lineno)))
def filter_stream(self, req, method, filename, stream, data): """ Quick and dirty solution - modify page on the fly to inject special field. It would be nicer if we can do it by creating custom field as this depends on page structure. """ #embed(header='Ticket Stream Filter') if filename == 'ticket.html': # Disable any direct bounty input filter = Transformer('.//input[@id="field-bounty"]') stream |= filter.attr("disabled", "disabled") ticket = data.get('ticket') if ticket and ticket.exists: identifier = ticket.id user = req.authname if req.authname != 'anonymous' else None request = self.call_api('GET', '/issue/%s' % identifier) fragment = tag() sponsorships = {} status = self.convert_status(ticket.values['status']) owner = ticket.values['owner'] tooltip = None if request != None and (request.status_code == 200 or request.status_code == 404): sponsorships = self.get_sponsorships(identifier) pledged_amount = sum_amounts(sponsorships.values()) user_sponsorship = sponsorships.get(user, Sponsorship()) # Bounty tooltip = u"Pledged: %d\u20ac" % pledged_amount if status == 'STARTED' or status == 'COMPLETED': confirmed_amount = sum_amounts( sponsorships.values(), ('CONFIRMED', 'VALIDATED', 'REJECTED', 'TRANSFERRED', 'REFUNDED')) tooltip += u" \nConfirmed: %d\u20ac" % confirmed_amount if status == 'COMPLETED': validated_amount = sum_amounts(sponsorships.values(), 'VALIDATED') tooltip += u" \nValidated: %d\u20ac" % validated_amount # Action action = None if (((status == 'STARTED' or status == 'COMPLETED') and user_sponsorship.status == 'PLEDGED') or (status == 'STARTED' and user != None and user != owner and user_sponsorship.status == None)): response = self.call_api('GET', '/config/payment_gateways') gateways = response.json().get('gateways') gateway_tags = [] if 'DUMMY' in gateways: gateway_tags.append( tag.input(type="submit", value="Payment Card", name='DUMMY')) if 'PAYPAL_STANDARD' in gateways: gateway_tags.append( tag.input(type="submit", value="PayPal", name='PAYPAL_STANDARD')) if 'PAYPAL_ADAPTIVE' in gateways: gateway_tags.append( tag.input(type="submit", value="PayPal", name='PAYPAL_ADAPTIVE')) if user_sponsorship.status == 'PLEDGED': action = tag.form( tag.input(type="button", name="confirm", value=u"Confirm %d\u20ac" % user_sponsorship.amount, id="confirm-button"), tag.span(gateway_tags, id="confirm-options"), tag.input(type="submit", name="delete", value="Delete"), method="post", action=req.href.ticket(identifier, "confirm")) else: #TODO: should be separate action action = tag.form( tag.input(name="amount", type="text", size="3", value="0", pattern="[0-9]*", title="money amount"), tag.input(type="button", value="Pledge & Confirm", id="confirm-button"), tag.span(gateway_tags, id="confirm-options"), method="post", action=req.href.ticket(identifier, "confirm")) elif status == 'COMPLETED' and user_sponsorship.status in ( 'CONFIRMED', 'REJECTED', 'VALIDATED'): action = tag.form(method="post", action=req.href.ticket( identifier, "validate")) if user_sponsorship.status == 'CONFIRMED' or user_sponsorship.status == 'REJECTED': action.append( tag.input(type="submit", name='validate', value=u"Validate %d\u20ac" % user_sponsorship.amount)) if user_sponsorship.status == 'CONFIRMED' or user_sponsorship.status == 'VALIDATED': action.append( tag.input(type="submit", name='reject', value="Reject")) elif (status == 'READY' and user != None): if user_sponsorship.status == None: action = tag.form( tag.input(name="amount", type="text", size="3", value=user_sponsorship.amount, pattern="[0-9]*", title="money amount"), tag.input(type="submit", value="Pledge"), method="post", action=req.href.ticket(identifier, "sponsor")) elif user_sponsorship.status == 'PLEDGED': action = tag.form( tag.input(name="amount", type="text", size=3, value=user_sponsorship.amount, pattern="[0-9]*", title="money amount"), tag.input(type="submit", name="update", value="Update"), tag.input(type="submit", name="delete", value="Delete"), method="post", action=req.href.ticket(identifier, "update_sponsorship")) elif (user == None): action = tag.span( u"\u00A0", tag.a("Login", href=req.href.login()), " or ", tag.a("Register", href=req.href.register()), " to sponsor") if action != None: fragment.append(" ") fragment.append(action) else: error = "Connection error" if request: error = request.json().get("error", "Unknown error") fragment.append( tag.span("[BountyFunding Error]", title=error)) #chrome = Chrome(self.env) #chrome.add_jquery_ui(req) add_stylesheet(req, 'htdocs/styles/bountyfunding.css') add_script(req, 'htdocs/scripts/bountyfunding.js') if tooltip != None: filter = Transformer('.//td[@headers="h_bounty"]/text()') stream |= filter.wrap(tag.span(title=tooltip)) filter = Transformer('.//td[@headers="h_bounty"]') stream |= filter.attr("class", "bountyfunding") stream |= filter.append(fragment) return stream
def get_navigation_items(self, req): if req.perm.has_permission('BLOG_VIEW') and self.nav_bar: yield 'mainnav', 'blog', tag.a(self.nav_bar_text, href=req.href.blog())
def annotate_row(self, context, row, lineno, line, data): row.append( tag.th(id='L%s' % lineno)(tag.a(lineno, href='#L%s' % lineno)))
def render_property(self, name, mode, context, props): def sha_link(sha, label=None): # sha is assumed to be a non-abbreviated 40-chars sha id try: reponame = context.resource.parent.id repos = self.env.get_repository(reponame) cset = repos.get_changeset(sha) if label is None: label = repos.display_rev(sha) return tag.a(label, class_='changeset', title=shorten_line(cset.message), href=context.href.changeset(sha, repos.reponame)) except Exception as e: return tag.a(sha, class_='missing changeset', title=to_unicode(e), rel='nofollow') if name == 'Branches': branches = props[name] # simple non-merge commit return tag(*intersperse(', ', (sha_link(rev, label) for label, rev in branches))) elif name in ('Parents', 'Children'): revs = props[name] # list of commit ids if name == 'Parents' and len(revs) > 1: # we got a merge... current_sha = context.resource.id reponame = context.resource.parent.id parent_links = intersperse(', ', \ ((sha_link(rev), ' (', tag.a('diff', title="Diff against this parent (show the " \ "changes merged from the other parents)", href=context.href.changeset(current_sha, reponame, old=rev)), ')') for rev in revs)) return tag( list(parent_links), tag.br(), tag.span(tag( "Note: this is a ", tag.strong("merge"), " changeset, " "the changes displayed below " "correspond to the merge itself."), class_='hint'), tag.br(), tag.span(tag( "Use the ", tag.code("(diff)"), " links above to see all the changes " "relative to each parent."), class_='hint')) # simple non-merge commit return tag(*intersperse(', ', map(sha_link, revs))) elif name in ('git-committer', 'git-author'): user_, time_ = props[name] _str = "%s (%s)" % (Chrome(self.env).format_author( context.req, user_), format_datetime(time_, tzinfo=context.req.tz)) return unicode(_str) raise TracError("Internal error")
def get_navigation_items(self, req): rm = RepositoryManager(self.env) if any(repos.is_viewable(req.perm) for repos in rm.get_real_repositories()): yield ('mainnav', 'browser', tag.a(_('Browse Source'), href=req.href.browser()))
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)))
def process_request(self, req): presel = req.args.get('preselected') if presel and (presel + '/').startswith(req.href.browser() + '/'): req.redirect(presel) path = req.args.get('path', '/') rev = req.args.get('rev', '') if rev.lower() in ('', 'head'): rev = None format = req.args.get('format') order = req.args.get('order', 'name').lower() desc = 'desc' in req.args xhr = req.get_header('X-Requested-With') == 'XMLHttpRequest' rm = RepositoryManager(self.env) all_repositories = rm.get_all_repositories() reponame, repos, path = rm.get_repository_by_path(path) # Repository index show_index = not reponame and path == '/' if show_index: if repos and (as_bool(all_repositories[''].get('hidden')) or not repos.is_viewable(req.perm)): repos = None if not repos and reponame: raise ResourceNotFound(_("Repository '%(repo)s' not found", repo=reponame)) if reponame and reponame != repos.reponame: # Redirect alias qs = req.query_string req.redirect(req.href.browser(repos.reponame or None, path) + ('?' + qs if qs else '')) reponame = repos.reponame if repos else None # Find node for the requested path/rev context = web_context(req) node = None changeset = None display_rev = lambda rev: rev if repos: try: if rev: rev = repos.normalize_rev(rev) # If `rev` is `None`, we'll try to reuse `None` consistently, # as a special shortcut to the latest revision. rev_or_latest = rev or repos.youngest_rev node = get_existing_node(req, repos, path, rev_or_latest) except NoSuchChangeset as e: raise ResourceNotFound(e.message, _('Invalid changeset number')) if node: try: # use changeset instance to retrieve branches and tags changeset = repos.get_changeset(node.rev) except NoSuchChangeset: pass context = context.child(repos.resource.child('source', path, version=rev_or_latest)) display_rev = repos.display_rev # Prepare template data path_links = get_path_links(req.href, reponame, path, rev, order, desc) repo_data = dir_data = file_data = None if show_index: repo_data = self._render_repository_index( context, all_repositories, order, desc) if node: if not node.is_viewable(req.perm): raise PermissionError('BROWSER_VIEW' if node.isdir else 'FILE_VIEW', node.resource, self.env) if node.isdir: if format in ('zip',): # extension point here... self._render_zip(req, context, repos, node, rev) # not reached dir_data = self._render_dir(req, repos, node, rev, order, desc) elif node.isfile: file_data = self._render_file(req, context, repos, node, rev) if not repos and not (repo_data and repo_data['repositories']): # If no viewable repositories, check permission instead of # repos.is_viewable() req.perm.require('BROWSER_VIEW') raise ResourceNotFound(_("No node %(path)s", path=path)) quickjump_data = properties_data = None if node and not xhr: properties_data = self.render_properties( 'browser', context, node.get_properties()) quickjump_data = list(repos.get_quickjump_entries(rev)) data = { 'context': context, 'reponame': reponame, 'repos': repos, 'repoinfo': all_repositories.get(reponame or ''), 'path': path, 'rev': node and node.rev, 'stickyrev': rev, 'display_rev': display_rev, 'changeset': changeset, 'created_path': node and node.created_path, 'created_rev': node and node.created_rev, 'properties': properties_data, 'path_links': path_links, 'order': order, 'desc': 1 if desc else None, 'repo': repo_data, 'dir': dir_data, 'file': file_data, 'quickjump_entries': quickjump_data, 'wiki_format_messages': \ self.config['changeset'].getbool('wiki_format_messages'), 'xhr': xhr, } if xhr: # render and return the content only return 'dir_entries.html', data, None if dir_data or repo_data: add_script(req, 'common/js/expand_dir.js') add_script(req, 'common/js/keyboard_nav.js') # Links for contextual navigation if node: if node.isfile: prev_rev = repos.previous_rev(rev=node.created_rev, path=node.created_path) if prev_rev: href = req.href.browser(reponame, node.created_path, rev=prev_rev) add_link(req, 'prev', href, _('Revision %(num)s', num=display_rev(prev_rev))) if rev is not None: add_link(req, 'up', req.href.browser(reponame, node.created_path)) next_rev = repos.next_rev(rev=node.created_rev, path=node.created_path) if next_rev: href = req.href.browser(reponame, node.created_path, rev=next_rev) add_link(req, 'next', href, _('Revision %(num)s', num=display_rev(next_rev))) prevnext_nav(req, _('Previous Revision'), _('Next Revision'), _('Latest Revision')) else: if path != '/': add_link(req, 'up', path_links[-2]['href'], _('Parent directory')) add_ctxtnav(req, tag.a(_('Last Change'), href=req.href.changeset(node.created_rev, reponame, node.created_path))) if node.isfile: annotate = data['file']['annotate'] if annotate: add_ctxtnav(req, _('Normal'), title=_('View file without annotations'), href=req.href.browser(reponame, node.created_path, rev=rev)) if annotate != 'blame': add_ctxtnav(req, _('Blame'), title=_('Annotate each line with the last ' 'changed revision ' '(this can be time consuming...)'), href=req.href.browser(reponame, node.created_path, rev=rev, annotate='blame')) add_ctxtnav(req, _('Revision Log'), href=req.href.log(reponame, path, rev=rev)) path_url = repos.get_path_url(path, rev) if path_url: if path_url.startswith('//'): path_url = req.scheme + ':' + path_url add_ctxtnav(req, _('Repository URL'), href=path_url) add_stylesheet(req, 'common/css/browser.css') return 'browser.html', data, None
def _do_login(self, req): """Log the remote user in. This function expects to be called when the remote user name is available. The user name is inserted into the `auth_cookie` table and a cookie identifying the user on subsequent requests is sent back to the client. If the Authenticator was created with `ignore_case` set to true, then the authentication name passed from the web server in req.remote_user will be converted to lower case before being used. This is to avoid problems on installations authenticating against Windows which is not case sensitive regarding user names and domain names """ if not req.remote_user: # TRANSLATOR: ... refer to the 'installation documentation'. (link) inst_doc = tag.a(_('installation documentation'), title=_("Configuring Authentication"), href=req.href.wiki('TracInstall') + "#ConfiguringAuthentication") raise TracError( tag_( "Authentication information not available. " "Please refer to the %(inst_doc)s.", inst_doc=inst_doc)) remote_user = req.remote_user if self.ignore_case: remote_user = remote_user.lower() if req.authname not in ('anonymous', remote_user): raise TracError( _('Already logged in as %(user)s.', user=req.authname)) with self.env.db_transaction as db: # Delete cookies older than 10 days db("DELETE FROM auth_cookie WHERE time < %s", (int(time.time()) - 86400 * 10, )) # Insert a new cookie if we haven't already got one cookie = None trac_auth = req.incookie.get('trac_auth') if trac_auth is not None: name = self._cookie_to_name(req, trac_auth) cookie = trac_auth.value if name == remote_user else None if cookie is None: cookie = hex_entropy() db( """ INSERT INTO auth_cookie (cookie, name, ipnr, time) VALUES (%s, %s, %s, %s) """, (cookie, remote_user, req.remote_addr, int(time.time()))) req.authname = remote_user req.outcookie['trac_auth'] = cookie req.outcookie['trac_auth']['path'] = self.auth_cookie_path \ or req.base_path or '/' if self.env.secure_cookies: req.outcookie['trac_auth']['secure'] = True req.outcookie['trac_auth']['httponly'] = True if self.auth_cookie_lifetime > 0: req.outcookie['trac_auth']['expires'] = self.auth_cookie_lifetime
def repolink(reponame, repos): label = reponame or _('(default)') return Markup(tag.a(label, title=_('View repository %(repo)s', repo=label), href=formatter.href.browser(repos.reponame or None)))
counter = 0 for o in self.rows: if counter % 2 == 1: class_ = 'odd' else: class_ = 'even' counter += 1 tr = tag.tr(class_=class_) tr( tag.td( tag.h5( tag.a(o, href=self.macroenv.tracenv.href() + ('/query?%s=%s&order=status' % ( self.rowtype, o, )))))) # query owner's tickets countStatus = {} # summarize the status of a ticket for segment in self.segments: class_ = '' if calendar[segment]['date'] == currentDate: # Today class_ = 'today' elif calendar[segment]['isocalendar'][2] == 6: # Saturday class_ = 'saturday' elif calendar[segment]['isocalendar'][2] == 7: # Sunday class_ = 'sunday' td_div = tag.div() # only show the highlight, if the date is in the past color_class = ''
def expand_macro(self, formatter, name, content): args, kw = parse_args(content) prefix = args[0].strip() if args else None hideprefix = args and len(args) > 1 and args[1].strip() == 'hideprefix' minsize = max(int(kw.get('min', 2)), 2) depth = int(kw.get('depth', -1)) start = prefix.count('/') if prefix else 0 format = kw.get('format', '') def parse_list(name): return [inc.strip() for inc in kw.get(name, '').split(':') if inc.strip()] includes = parse_list('include') or ['*'] excludes = parse_list('exclude') if hideprefix: omitprefix = lambda page: page[len(prefix):] else: omitprefix = lambda page: page wiki = formatter.wiki pages = sorted(page for page in wiki.get_pages(prefix) if (depth < 0 or depth >= page.count('/') - start) and 'WIKI_VIEW' in formatter.perm('wiki', page) and any(fnmatchcase(page, inc) for inc in includes) and not any(fnmatchcase(page, exc) for exc in excludes)) if format == 'compact': return tag( separated((tag.a(wiki.format_page_name(omitprefix(p)), href=formatter.href.wiki(p)) for p in pages), ', ')) # the function definitions for the different format styles # the different page split formats, each corresponding to its rendering def split_pages_group(pages): """Return a list of (path elements, page_name) pairs, where path elements correspond to the page name (without prefix) splitted at Camel Case word boundaries, numbers and '/'. """ page_paths = [] for page in pages: path = [elt.rstrip('/').strip() for elt in self.SPLIT_RE.split( wiki.format_page_name(omitprefix(page), split=True))] page_paths.append(([elt for elt in path if elt], page)) return page_paths def split_pages_hierarchy(pages): """Return a list of (path elements, page_name) pairs, where path elements correspond to the page name (without prefix) splitted according to the '/' hierarchy. """ return [(wiki.format_page_name(omitprefix(page)).split("/"), page) for page in pages] # create the group hierarchy (same for group and hierarchy formats) def split_in_groups(entries): """Transform a flat list of entries into a tree structure. `entries` is a list of `(path_elements, page_name)` pairs Return a list organized in a tree structure, in which: - a leaf is a page name - a node is a `(key, nodes)` pairs, where: - `key` is the leftmost of the path elements, common to the grouped (path element, page_name) entries - `nodes` is a list of nodes or leaves """ groups = [] for key, grouper in groupby(entries, lambda (elts, name): elts[0] if elts else ''): # remove key from path_elements in grouped entries for further # grouping grouped_entries = [(path_elements[1:], page_name) for path_elements, page_name in grouper] if key and len(grouped_entries) >= minsize: subnodes = split_in_groups(sorted(grouped_entries)) if len(subnodes) == 1: subkey, subnodes = subnodes[0] node = (key + subkey, subnodes) # FIXME else: node = (key, subnodes) groups.append(node) else: for path_elements, page_name in grouped_entries: groups.append(page_name) return groups
def get_navigation_items(self, req): if 'ROADMAP_VIEW' in req.perm: yield ('mainnav', 'pdashboard', tag.a('Metrics', href=req.href.pdashboard()))
def expand_macro(self, formatter, name, text, args): try: comment = Comments(formatter.req, formatter.env).by_id(text) return tag.a(comment.link_text(), href=comment.href()) except: return ''
def get_navigation_items(self, req): yield ('metanav', 'prefs', tag.a(_('Preferences'), href=req.href.prefs()))
def get_navigation_items(self, req): if 'ROADMAP_VIEW' in req.perm: yield ('mainnav', 'roadmap', tag.a(_('Roadmap'), href=req.href.roadmap(), accesskey=3))
def process_request(self, req): #ok, what are we about. #db = self.env.get_db_cnx() #ticketlist = {} # dict of ticket->??? #revlist = {} # dict of revision-> repos = self.env.get_repository() new_path = req.args.get('new_path') new_rev = req.args.get('new') old_path = req.args.get('old_path') old_rev = req.args.get('old') new_path = repos.normalize_path(new_path) new_rev = repos.normalize_rev(new_rev) old_path = repos.normalize_path(old_path) old_rev = repos.normalize_rev(old_rev) # if not req.perm.has_permission('TICKET_MODIFY'): # return req.redirect(req.href.browser()) old_rev = int(old_rev) new_rev = int(new_rev) ticket = req.args.get('ticket') try: ticket = int(ticket) except Exception: ticket = 0 # req.hdf['review.ticket'] = ticket # req.hdf['review.tickethtml'] = tag.a(ticket, req.href.ticket(ticket)) data = {} data['overall_y'] = 0 data['ticket_id'] = req.args['ticket'] data['ticket_href'] = req.href.ticket(req.args['ticket']) ticket_mgr = TicketManager(self.compmgr) db = self.env.get_db_cnx() repos = self.env.get_repository() revs = ticket_mgr.tkt2revs(self.log, db, repos, req, req.args['ticket']) if (not revs or len(revs) == 0): # nothing to review. shouldn't happen return ('nothing.html', data, 'text/html') elif (len(revs) == 1): # only one change - just do a changeset view return req.redirect(req.href.changeset(revs[0])) revcount = 0 branches = {} files = {} # may be 0 revs. revisions = [] for rev in revs: chgset = repos.get_changeset(rev) # q: (u'trunk/Locale.java', 'file', 'add', None, u'-1') from r3 # q: (u'trunk/util.c', 'file', 'edit', u'trunk/util.c', u'2') from r4 message = chgset.message or '--' revcount = revcount + 1 revision = {} revision['rev'] = tag.a(rev, req.href.changeset(rev)) revision['author'] = chgset.author revision['num'] = rev revision[ 'comment'] = message #wiki_to_oneliner( message, self.env, db, shorten=False ) rbranches = revision['branches'] = [] for chg in chgset.get_changes(): path = chg[0] if path in files: item = files[path] else: item = [] files[path] = item item.append(self.changeToRange(rev, chg)) branch_name = self.pathToBranchName(path) if branch_name not in rbranches: rbranches.append(branch_name) revisions.append(revision) data['revisions'] = revisions if (revcount > 0): data['revcount'] = revcount # print "files: %d" % len(files) # go throuhg each file and calculate its minimum range filelist = files.keys() filelist.sort() # print 'bar to %d len of %s' % (len(filelist),str(filelist)) for file in filelist: changes = files[file] i = 0 # print " looping from %d to %d over %d " % (i,len(changes)-1,len(changes)) while len(changes) > 1 and i < (len(changes) - 1): if changes[i][1] == changes[i + 1][0]: if changes[i][0] == -1: changes[i + 1] = (changes[i][0], changes[i + 1][1], 'add+commits') # retain 'first' rev else: changes[i + 1] = (changes[i][0], changes[i + 1][1], 'multiple commits' ) # retain 'first' rev changes = changes[:i] + changes[i + 1:] # and shift down # print "merged: %s" % str(changes) files[file] = changes else: i = i + 1 # now, write 'em out sera = 0 #files_data = [] for file in filelist: sera = sera + 1 file_data = {} file_data['name'] = Markup('<a href="%s">%s</a>' % (req.href.browser(file), file)) branch_name = self.pathToBranchName(file) #print "branch is: (%s)" % (branch_name) branches_data = branches.get(branch_name, {}) files_data = branches_data.get('files', []) changes = files[file] cha = 0 changes_data = [] for change in changes: cha = cha + 1 # print "%s output %s " % (file, str(change)) changes_data.append(self.describeChange(file, change, req, db)) file_data['changes'] = changes_data if (len(changes) > 1): whathtml = self.describeChange( file, (changes[0][0], changes[len(changes) - 1][1], 'overall'), req, db) file_data['overall'] = whathtml file_data['overall_y'] = 1 data['overall_y'] = 1 else: file_data['overall_y'] = 0 files_data.append(file_data) # sets branches_data['files'] = files_data branches_data['len'] = len(files_data) branches_data['name'] = branch_name branches[branch_name] = branches_data # .. convert dict to array. branch_list = [] for branch in branches: branch_list.append(branches[branch]) data['branches'] = branch_list data['lastbranch'] = branch data['branchcount'] = len(branches) content_type = "text/html" add_stylesheet(req, 'icucodetools/css/icuxtn.css') add_script(req, 'icucodetools/js/review.js') return 'review.html', data, content_type
def render(self, ticketset): ''' Generate HTML List ''' (counttickets,ticketgrouping) = self.getTicketGrouping( ticketset ) if counttickets == 0: return self.divWarning('No tickets available.') maxTicketsToShow = 20 maxWidth = 400 statuskeys = ticketgrouping.keys() (width,height) = self.getDimensions() framewrapper = tag.div(style="width:%spx;" % (width+10+(2*len(statuskeys)),) ) # ensures the width if the browser window is to small framebar = tag.div(class_="clearfix",style="height:%spx; background-color: #333; box-shadow: 4px 4px 4px #999; display: inline-block; white-space:nowrap; border: 1px solid #333; float: left;" % (height,) ) js = tag.script() framebox = tag.div() currentPosition = 0 # offset myId = self.getStrongKey() orderedStatusKeys = self.getOrderedStatusKeys(self.orderOrg, statuskeys) for statuskey in orderedStatusKeys : groupingCount = len(ticketgrouping[statuskey]) groupingPercent = int(round(float(groupingCount) / float(counttickets) * 100)) groupingWidth = int(round(float(groupingCount) / float(counttickets) * width )) groupingColor = self.macroenv.conf.get_map_val('ColorForStatus', statuskey ) barId = myId+'_bar_'+statuskey boxId = myId+'_box_'+statuskey statusTicketsWidth = max(groupingWidth,maxWidth)-10 statusTickets = tag.div(id=boxId, style='width:%spx; border:1px solid %s;display:none;position:absolute;left:%spx;background-color:#FFF;padding:0.5ex;margin:0.5ex;box-shadow:4px 4px 4px #AAA;' % (statusTicketsWidth,groupingColor,currentPosition)) ticketids = ticketgrouping[statuskey].keys() ticketids.sort(reverse=True) ticketTable = tag.table() for tid in ticketids[0:maxTicketsToShow]: ticketTable(tag.tr(tag.td(self.createTicketLink(ticketset.getTicket(tid))), tag.td(ticketgrouping[statuskey][tid], style="max-width:%spx"%(min(max(maxWidth,groupingWidth),width),)) )) # show the number of left out tickets if len(ticketids) > maxTicketsToShow : ticketTable(tag.tr(tag.td('...'),tag.td('%s more tickets' % (len(ticketids)-maxTicketsToShow,)))) if groupingCount==1: ticketString = 'ticket' else: ticketString = 'tickets' statusTickets(tag.div(tag.div('%s %s: %s' % (groupingCount,ticketString,statuskey),style="float:left;font-weight:bold;"),tag.div(tag.a('X',href="javascript: return false;"),id="%s%s" % (boxId,"close"),style="float:right",class_="ppinsetshadow"),class_="clearFix"),ticketTable) framebar( tag.div( '', id=barId, style="float:left;border:1px solid #333;width:%spx; height:%spx; %s;" % (groupingWidth,height,self.getCSSgradient(groupingColor)) ) ) framebox( statusTickets, style="position:absolute;" ) # if clicked, show permanently; until clicked again; ppStore stores the current click state js('''$("#%s").mouseover(function() { $("#%s").slideDown(200, function() {}); });''' % (barId,boxId)) js('''$("#%s").mouseout(function() { if(ppStore["%s"] == 0 || isNaN(ppStore["%s"])){ $("#%s").slideUp(400, function() {}); } }); ''' % (barId,barId,barId,boxId)) js('''$("#%s").mouseup(function() { if ( isNaN(ppStore["%s"]) ) { ppStore["%s"]=0; } ppStore["%s"] = (ppStore["%s"]+1) %% 2 if( ppStore["%s"] == 1 ){ $("#%s").slideDown(100, function() {}); } else { $("#%s").slideUp(100, function() {}); } });''' % (barId,barId,barId,barId,barId,barId,boxId,boxId)) js('''$("#%sclose").click(function() { $("#%s").slideUp(100, function() {}); });''' % (boxId,boxId)) #js('$("#%s").mouseenter().mouseleave(function() {$("#%s").hide(); });' % (boxId,boxId)) currentPosition += groupingWidth + 2 # offset is 2px return tag.div(framewrapper(framebar),tag.div(style="clear:both;"),framebox,js)