def filter_stream(self, req, method, filename, stream, data): if filename != 'query.html' and filename != 'report_list.html' and \ filename != 'report_view.html': return stream has_query_permission = self.query_permission in \ PermissionSystem(self.env).get_user_permissions(req.authname) buffer = StreamBuffer() def replace_query_link(): if has_query_permission: return buffer else: return HTML('<div id="ctxtnav" class="nav"></div>') def replace_filter_box(): if has_query_permission: return buffer else: return HTML('') return stream | Transformer('//div[@id="ctxtnav" and @class="nav"]') \ .copy(buffer) \ .replace(replace_query_link).end() \ .select('//form[@id="query" and @method="post" and @action]') \ .copy(buffer) \ .replace(replace_filter_box)
def filter_stream(self, req, method, filename, stream, data): if not filename == 'report_list.html': return stream user = req.authname buffer = StreamBuffer() def check_report_permission(): delimiter = '</tr>' reportstream = str(buffer) reports_raw = reportstream.split(delimiter) reportstream = '' for report in reports_raw: if report != None and len(report) != 0: # determine the report id s = report.find('/report/') if s == -1: continue e = report.find('\"', s) if e == -1: continue report_id = report[s + len('/report/'):e] if self._has_permission(user, report_id): reportstream += report return HTML(reportstream) return stream | Transformer('//tbody/tr') \ .copy(buffer) \ .replace(check_report_permission)
def filter_stream(self, req, method, filename, stream, data): """ filter the stream for the roadmap (/roadmap) and milestones /milestone/<milestone> """ if filename in ('roadmap.html', 'milestone_view.html'): trachours = TracHoursPlugin(self.env) hours = {} milestones = data.get('milestones') this_milestone = None if milestones is None: # /milestone view : only one milestone milestones = [ data['milestone'] ] this_milestone = milestones[0].name find_xpath = "//div[@class='milestone']//h1" xpath = "//div[@class='milestone']//div[@class='info']" else: # /roadmap view find_xpath = "//li[@class='milestone']//h2/a" xpath = "//li[@class='milestone']//div[@class='info']" for milestone in milestones: hours[milestone.name] = dict(totalhours=0., estimatedhours=0.,) db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute("select id from ticket where milestone=%s", (milestone.name,)) tickets = [i[0] for i in cursor.fetchall()] if tickets: hours[milestone.name]['date'] = Ticket(self.env, tickets[0]).time_created for ticket in tickets: ticket = Ticket(self.env, ticket) # estimated hours for the ticket try: estimatedhours = float(ticket['estimatedhours']) except (ValueError, TypeError): estimatedhours = 0. hours[milestone.name]['estimatedhours'] += estimatedhours # total hours for the ticket (seconds -> hours) totalhours = trachours.get_total_hours(ticket.id) / 3600.0 hours[milestone.name]['totalhours'] += totalhours # update date for oldest ticket if ticket.time_created < hours[milestone.name]['date']: hours[milestone.name]['date'] = ticket.time_created b = StreamBuffer() stream |= Transformer(find_xpath).copy(b).end().select(xpath).append(self.MilestoneMarkup(b, hours, req.href, this_milestone)) return stream
def _milestone_versions(self, stream, req): buffer = StreamBuffer() def apply_version(): return self._version_display(req, buffer.events[1][1]) filter = Transformer('//li[@class="milestone"]/div/h2/a/em').copy(buffer).end() \ .select('//li[@class="milestone"]//p[@class="date"]').append(apply_version) return stream | filter
def filter_stream(self, req, method, filename, stream, data): if filename not in ('ticket.html', 'ticket_preview.html'): return stream ticket = data.get('ticket') if not (ticket and ticket.exists and 'TICKET_ADMIN' in req.perm(ticket.resource)): return stream # Insert "Delete" buttons for ticket description and each comment def delete_ticket(): return tag.form( tag.div( tag.input(type='hidden', name='action', value='delete'), tag.input( type='submit', value=captioned_button( req, u'–', # 'EN DASH' _("Delete")), title=_('Delete ticket'), class_="trac-delete"), class_="inlinebuttons"), action='#', method='get') def delete_comment(): for event in buffer: cnum, cdate = event[1][1].get('id')[12:].split('-', 1) return tag.form( tag.div( tag.input(type='hidden', name='action', value='delete-comment'), tag.input(type='hidden', name='cnum', value=cnum), tag.input(type='hidden', name='cdate', value=cdate), tag.input( type='submit', value=captioned_button( req, u'–', # 'EN DASH' _("Delete")), title=_('Delete comment %(num)s', num=cnum), class_="trac-delete"), class_="inlinebuttons"), action='#', method='get') buffer = StreamBuffer() return stream | Transformer('//div[@class="description"]' '/h3[@id="comment:description"]') \ .after(delete_ticket).end() \ .select('//div[starts-with(@class, "change")]/@id') \ .copy(buffer).end() \ .select('//div[starts-with(@class, "change") and @id]' '/div[@class="trac-ticket-buttons"]') \ .prepend(delete_comment)
def dir_entries(self, req, stream, data, xpath_prefix=''): # add table cells b = StreamBuffer() xpath = "//td[@class='%s']" stream |= Transformer(xpath_prefix + (xpath % 'name') + "/a/@href").copy(b).end().select(xpath_prefix + ( xpath % self.element_class)).after( self.GenerateSVNUrl( b, self.svn_base_url, self.link_text, data['path_links'][0]['href'])) return stream
def filter_stream(self, req, method, filename, stream, data): if filename in ('milestone_view.html', ): buffer = StreamBuffer() t = Transformer('//div[@class="milestone"]/h1/text()[2]') t = t.copy(buffer).end() t = t.select('//div[@class="milestone"]/div[@class="info"]/dl') t = t.append(GenerateMetrixLink(buffer, req.href)) stream |= t return stream
def filter_stream(self, req, method, filename, stream, data): if filename == "attachment.html": if data["mode"] == "new" and data[ "attachment"].parent_realm == "ticket": stream |= Transformer("//fieldset").after( self._generate_attachmentflags_fieldset(readonly=False)) elif data["mode"] == "list" and data["attachments"] and data[ "attachments"]["parent"].realm == "ticket": stream = self._filter_obsolete_attachments_from_stream( stream, data["attachments"]["attachments"]) elif data["mode"] == "view" and data[ "attachment"].parent_realm == "ticket": flags = AttachmentFlags(self.env, data["attachment"]) if 'TICKET_MODIFY' in req.perm or get_reporter_id( req) == data["attachment"].author: stream |= Transformer("//div[@id='preview']").after( self._generate_attachmentflags_fieldset( readonly=False, current_flags=flags, form=True)) else: stream |= Transformer("//div[@id='preview']").after( self._generate_attachmentflags_fieldset( current_flags=flags)) if filename == "ticket.html": if "attachments" in data: stream = self._filter_obsolete_attachments_from_stream( stream, data["attachments"]["attachments"]) stream |= Transformer("//label[@for='field-patch']").wrap('strike') buffer = StreamBuffer() #stream |= Transformer("//input[@id='field-patch']").attr('disabled','disabled') # Copy a checked box to buffer then disable original stream |= Transformer('//input[@id="field-patch" and (@checked)]')\ .copy(buffer).after(buffer).attr("disabled","disabled") # Change new element to hidden field instead of checkbox and # remove check stream |= Transformer('//input[@id="field-patch" and (@checked) \ and not (@disabled)]' ).attr("type","hidden") \ .attr("checked", None).attr("id", None) # Disable any unchecked fields # NOTE: if the box was checked and copied, the id is removed so the # hidden field will not be disabled here. stream |= Transformer('//input[@id="field-patch" \ and not (@checked)]').attr("disabled", "disabled") if filename == "query.html": # Filter the patch field from the Trac 1.0 batch modify utility stream |= Transformer( "//select[@id='add_batchmod_field']/option[@value='patch']" ).remove() return stream
def _customize_View(self, stream): filter = Transformer('.//div [@id="banner"]') stream = stream | filter.wrap(self.css_banner_top2) buffer = StreamBuffer() stream = stream | Transformer('.//div [@id="banner"]').copy(buffer) \ .end().select('.//div [@id="top2"]') \ .after(tag.div(id_='top1')(buffer)) filter = Transformer('.//div [@id="mainnav"]') stream = stream | filter.wrap(self.css_banner_top4) buffer = StreamBuffer() stream = stream | Transformer('.//div [@id="mainnav"]').copy(buffer) \ .end().select('.//div [@id="top4"]') \ .after(tag.div(id_='top3')(buffer)) filter = Transformer('.//div [@id="top3"]') stream = stream | filter.after(tag.div(id_='right')(tag.p())) filter = Transformer('.//div [@id="right"]') stream = stream | filter. \ append(tag.div(class_='wiki-toc')(tag.h4(_('Table of Contents')))) # just for the menu / TOC filter = Transformer('.//div [@class="wiki-toc"]') if self.anchors and self.keylist: for key in self.keylist: stream = stream | filter.append( tag.a(key, href='#' + self.anchors.get(key), onclick="scrollup();") + tag.br()) filter = Transformer('.//div [@id="main"]') stream = stream | filter.wrap(self.css_left) return stream
def filter_stream(self, req, method, filename, stream, data): if filename != 'ticket.html': return stream ticket = data.get('ticket') if not (ticket and ticket.exists and 'TICKET_ADMIN' in req.perm(ticket.resource)): return stream # Insert "Delete" buttons for ticket description and each comment def delete_ticket(): return tag.form(tag.div(tag.input(type='hidden', name='action', value='delete'), tag.input(type='submit', value=_('Delete'), title=_('Delete ticket')), class_='inlinebuttons'), action='#', method='get') def delete_comment(): for event in buffer: cnum = event[1][1].get('id')[12:] return tag.form(tag.div(tag.input(type='hidden', name='action', value='delete-comment'), tag.input(type='hidden', name='cnum', value=cnum), tag.input(type='submit', value=_('Delete'), title=_( 'Delete comment %(num)s', num=cnum)), class_='inlinebuttons'), action='#', method='get') buffer = StreamBuffer() return stream | Transformer('//div[@class="description"]' '/h3[@id="comment:description"]') \ .after(delete_ticket).end() \ .select('//div[@class="change"]/@id') \ .copy(buffer).end() \ .select('//div[@class="change" and @id]/h3[@class="change"]') \ .after(delete_comment)
def filter_stream(self, req, method, filename, stream, data): if not filename == 'report_list.html': return stream stream_buffer = StreamBuffer() from pkg_resources import parse_version if parse_version(trac_version) < parse_version('1.0'): delimiter = '</tr>' selector = '//tbody/tr' else: delimiter = '</div>' selector = '//div[@class="reports"]/div' def check_report_permission(): report_stream = str(stream_buffer) reports_raw = report_stream.split(delimiter) report_stream = '' for row in reports_raw: if row is not None and len(row) != 0 and 'View report' in row: # determine the report id s = row.find('/report/') if s == -1: continue e = row.find('\"', s) if e == -1: continue report_id = row[s+len('/report/'):e] if self._has_permission(req.authname, report_id): report_stream += row else: self.log.debug("Removing report %s from list because " "%s doesn't have permission to view" % (report_id, req.authname)) elif 'View report' in row: report_stream += row return HTML(report_stream) return stream | Transformer(selector) \ .copy(stream_buffer) \ .replace(check_report_permission)
def _apply(self, select, with_attrs=False): buffer = StreamBuffer() events = buffer.events class Trace(object): last = None trace = [] def __call__(self, stream): for event in stream: if events and hash(tuple(events)) != self.last: self.last = hash(tuple(events)) self.trace.append(list(events)) yield event trace = Trace() output = _transform(FOOBAR, getattr(Transformer(select), self.operation) (buffer).apply(trace), with_attrs=with_attrs) simplified = [] for interval in trace.trace: simplified.append(_simplify([(None, e) for e in interval], with_attrs=with_attrs)) return output, simplified
def filter_stream(self, req, method, filename, stream, data): if filename == 'ticket.html' and req.authname != 'anonymous': ticket = data.get('ticket') if req.perm.has_permission('TICKET_ADMIN'): self.log.debug( "TicketChangePlugin adding 'Change' links for ticket %s" % ticket.id) buffer = StreamBuffer() def insert_change_link(): cnum = list(buffer)[0][1][1][0][1] return tag( " ", tag.a("Change", href=("../ticketchangecomment/%s?cnum=%s" % (ticket.id, cnum)))) filter = Transformer( "//div[@class='change']/div[@class='inlinebuttons']/input[@name='replyto']/@value" ) return stream | filter.copy(buffer).end() \ .select("//div[@class='change']/div[@class='inlinebuttons']/input[@value='Reply']") \ .after(insert_change_link) return stream
def filter_stream(self, req, method, filename, stream, data): """ filter the stream for the roadmap (/roadmap) and milestones /milestone/<milestone> """ if filename in ('roadmap.html', 'milestone_view.html') and \ 'TICKET_VIEW_HOURS' in req.perm: trac_hours = TracHoursPlugin(self.env) hours = {} milestones = data.get('milestones') this_milestone = None if milestones is None: # /milestone view : only one milestone milestones = [data['milestone']] this_milestone = milestones[0].name find_xpath = "//div[@class='milestone']/h1" xpath = "//div[@class='milestone']/div[1]" else: # /roadmap view find_xpath = "//*[@class='milestone']//h2/a" xpath = "//*[@class='milestone']/div[1]" for milestone in milestones: hours[milestone.name] = dict( totalhours=0., estimatedhours=0., ) tickets = [ tid for tid, in self.env.db_query( """ SELECT id FROM ticket WHERE milestone=%s """, (milestone.name, )) ] if tickets: hours[milestone.name]['date'] = \ Ticket(self.env, tickets[0])['time'] for ticket in tickets: ticket = Ticket(self.env, ticket) # estimated hours for the ticket try: estimated_hours = float(ticket['estimatedhours']) except (ValueError, TypeError): estimated_hours = 0. hours[milestone.name]['estimatedhours'] += estimated_hours # total hours for the ticket (seconds -> hours) total_hours = trac_hours.get_total_hours( ticket.id) / 3600.0 hours[milestone.name]['totalhours'] += total_hours # update date for oldest ticket if ticket['time'] < hours[milestone.name]['date']: hours[milestone.name]['date'] = ticket['time'] b = StreamBuffer() stream |= Transformer(find_xpath).copy(b).end().select(xpath). \ append( self.MilestoneMarkup(b, hours, req.href, this_milestone)) return stream
def filter_stream(self, req, method, filename, stream, data): #remove matches from custom queries due to the fact ticket permissions are checked after this stream is manipulated so the count cannot be updated. if filename == "query.html": stream |= Transformer( '//div[@class="query"]/h1/span[@class="numrows"]/text()' ).replace("") if filename == "ticket.html": for field, e in self.enchants.items(): disabled = e["disable"] hidden = e["hide"] #permissions are set for field if e["permission"] != "" and not hidden and not ( disabled or disabled and e["ondenial"] == "hide"): self.env.log.debug("Permissions %s" % e["permission"]) #default set to denied denied = True #iterate through permissions for perm in (x.strip() for x in e["permission"].split(',')): self.env.log.debug("Checking permission %s" % perm) #user has permission no denied if perm and perm in req.perm( data.get("ticket").resource): self.env.log.debug("Has %s permission" % perm) denied = False #if denied is true hide/disable dpending on denial setting if denied: denial = self.config.get('blackmagic', '%s.ondenial' % field, None) if denial: if denial == "disable": disabled = True elif denial == "hide": hidden = True else: disabled = True else: disabled = True #hide fields if hidden: #replace th and td in previews with empty tags stream = stream | Transformer( '//th[@id="h_%s"]' % field).replace(tag.th(" ")) stream = stream | Transformer( '//td[@headers="h_%s"]' % field).replace(tag.td(" ")) #replace labels and fields with blank space stream = stream | Transformer( '//label[@for="field-%s"]' % field).replace(" ") stream = stream | Transformer( '//*[@id="field-%s"]' % field).replace(" ") #change label if e["label"] is not None: stream |= Transformer('//th[@id="h_%s"]/text()' % field).replace(e["label"] + ":") stream = stream | Transformer( '//label[@for="field-%s"]/text()' % field).replace(e["label"] + ":") if disabled: buffer = StreamBuffer() #copy input to buffer then disable original stream |= Transformer( '//*[@id="field-%s" and (@checked) and @type="checkbox"]' % field).copy(buffer).after(buffer).attr( "disabled", "disabled") #change new element to hidden field instead of checkbox and remove check stream |= Transformer( '//*[@id="field-%s" and not (@disabled) and (@checked) and @type="checkbox"]' % field).attr("type", "hidden").attr("checked", None).attr("id", None) #disable non-check boxes / unchecked check boxes stream = stream | Transformer( '//*[@id="field-%s" and not (@checked)]' % field).attr( "disabled", "disabled") if not self.gray_disabled: #cut label content into buffer then append it into the label with a strike tag around it stream = stream | Transformer( '//label[@for="field-%s"]/text()' % field).cut(buffer).end().select( '//label[@for="field-%s"]/' % field).append( tag.strike(buffer)) else: #cut label and replace with coloured span stream = stream | Transformer( '//label[@for="field-%s"]/text()' % field).cut(buffer).end().select( '//label[@for="field-%s"]/' % field).append( tag.span( buffer, style="color:%s" % self.gray_disabled)) if self.config.get('blackmagic', '%s.notice' % field, None): stream = stream | Transformer( '//*[@id="field-%s"]' % field ).after(tag.br() + tag.small()(tag.em()(Markup( self.config.get('blackmagic', '%s.notice' % field))))) tip = self.config.get('blackmagic', '%s.tip' % field, None) if tip: stream = stream | Transformer( '//div[@id="banner"]').before( tag.script(type="text/javascript", src=req.href.chrome( "blackmagic", "js", "wz_tooltip.js"))()) stream = stream | Transformer( '//*[@id="field-%s"]' % field).attr( "onmouseover", "Tip('%s')" % tip.replace(r"'", r"\'")) return stream
def filter_stream(self, req, method, filename, stream, data): if filename not in ('ticket.html', 'ticket.rss'): return stream ticket_id = req.args.get('id') if not ticket_id: return stream # determine the username of the current user user = req.authname # determine if the user has the permission to see private comments perms = PermissionSystem(self.env) has_private_permission = \ self.private_comment_permission in perms.get_user_permissions(user) # Remove private comments from Ticket Page if filename == 'ticket.html': buf = StreamBuffer() def check_comments(): delimiter = '<div xmlns="http://www.w3.org/1999/xhtml" ' + \ 'class="change" id="trac-change-' comment_stream = str(buf) # split the comment_stream to get single comments comments_raw = comment_stream.split(delimiter) comment_stream = '' for comment in comments_raw: if comment is None or len(comment) < 1: continue # determine comment id find = comment.find('">') if find == -1: continue comment_id = comment[:find] # concat the delimiter and the comment again comment_code = delimiter + comment # if the user has the permission to see the comment # the comment_code will be appended to the comment_stream comment_private = self._is_comment_private( ticket_id, comment_id) if comment_private: comment_code = comment_code.replace( '<span class="threading">', '<span class="threading"> <span class="%s">' 'this comment is private</span>' % str(self.css_class_private_comment_marker)) if has_private_permission or not comment_private: comment_stream += comment_code return HTML(comment_stream) # filter all comments stream |= Transformer('//div[@class="change" and @id]') \ .copy(buf).replace(check_comments) # if the user has the private comment permission the checkboxes # to change the private value will be added if has_private_permission: comment_box = tag.label( _("Private Comment:"), tag.input(type='checkbox', name='private_comment')) stream |= Transformer('//h2[@id="trac-add-comment"]') \ .after(comment_box) # Trac 1.0 and later: # stream |= Transformer( # '//div[@id="trac-add-comment"]//fieldset').prepend(input) # Remove private comments from ticket RSS feed if filename == 'ticket.rss': comments = self._get_all_private_comments(ticket_id) self.log.debug("Private Comments for Ticket %d: %s" % (ticket_id, comments)) for comment_id in comments: stream |= Transformer('//item[%d]' % comment_id).remove() return stream