from trac.core import Component, implements, TracError from trac.config import Option, IntOption, ListOption, BoolOption from trac.web.chrome import ITemplateProvider, add_stylesheet, add_script from pkg_resources import resource_filename from trac.web.api import ITemplateStreamFilter from genshi.builder import tag from genshi.core import Markup, Stream from genshi.filters.transform import Transformer import re, cPickle from trac.perm import IPermissionRequestor from genshi.filters import HTMLSanitizer from genshi.output import TextSerializer def textOf(self, **keys): return self.render('text', None, **keys) Stream.textOf = textOf #@staticmethod def disable_field(stream, field): def select_helper(stream): s = Stream(stream) name = s.select('@name').textOf() opt = s.select('//option[@selected]') if not opt: s.select('//option[position()=1]') text = opt.select("text()").textOf() value = s.select('@value').textOf() if not value: value = text for kind,data,pos in tag.span(text, id=("field-%s"%field)).generate(): yield kind,data,pos for kind,data,pos in tag.input(value=value, name=name, type="hidden").generate(): yield kind,data,pos def helper(field_stream): s = Stream(field_stream) value = s.select('@value').textOf() name = s.select('@name').textOf() for kind,data,pos in tag.span(value, id=("field-%s"%field)).generate(): yield kind,data,pos for kind,data,pos in tag.input(value=value, name=name, type="hidden").generate(): yield kind,data,pos stream = stream | Transformer( '//select[@id="field-%s"]' % field ).filter(select_helper) stream = stream | Transformer( '//input[@id="field-%s"]' % field ).filter(helper) return stream def remove_header(stream, field): """ Removes the display from the ticket properties """ stream = stream | \ Transformer('//th[@id="h_%s"]' % field).replace(tag.th(id="h_%s" % field)) stream = stream | \ Transformer('//td[@headers="h_%s"]' % field).replace(tag.th(id="h_%s" % field)) return stream def remove_changelog(self, stream, field): """ Removes entries from the visible changelog""" #self.log.debug('Begin ChangeLog Filter') check = self.env.config.get('ticket-custom',field+'.label', field).lower().strip() def helper(field_stream): try: s = Stream(field_stream) self.log.debug('ChangeLog Pre') # without None as the second value we get str instead of unicode # and that causes things to break sometimes f = s.select('//strong/text()').textOf(strip_markup=True).lower() # self.log.debug(u'ChangeLog Pre 2 : %s: %r', type(f), f) self.log.debug('ChangeLog Filter: field:%s, label:%s, we are looking at:%r, skip?%s', field, check, f, check == f ) if check != f: #if we are the field just skip it #identity stream filter for kind, data, pos in s: yield kind, data, pos except Exception, e: self.log.exception('ChangeLog: Stream Filter Exception'); raise e stream = stream | Transformer('//ul[@class="changes"]/li').filter(helper) return stream def hide_field(self, stream , field): """ Replaces a field from the form area with an input type=hidden""" def helper (field_stream): type = Stream(field_stream).select('@type').textOf() if type == 'checkbox': if Stream(field_stream).select('@checked').textOf() == "checked": value = 1 else: value = 0 else: value = Stream(field_stream).select('@value').textOf() name = Stream(field_stream).select('@name').textOf() for kind,data,pos in tag.input( value=value, type="hidden", name=name).generate(): yield kind,data,pos def select_helper(stream): s = Stream(stream) name = s.select('@name').textOf() opt = s.select('//option[@selected]') if not opt: s.select('//option[position()=1]') text = opt.select("text()").textOf() value = s.select('@value').textOf() if not value: value = text for kind,data,pos in tag.input(value=value, name=name, type="hidden").generate(): yield kind,data,pos stream = stream | Transformer('//label[@for="field-%s"]' % field).replace(" ") stream = stream | Transformer('//input[@id="field-%s"]' % field).filter(helper) stream = stream | Transformer('//select[@id="field-%s"]' % field).filter(select_helper) return remove_changelog(self, remove_header(stream , field), field) def remove_field(self, stream , field): """ Removes a field from the form area""" stream = stream | Transformer('//label[@for="field-%s"]' % field).replace(" ") stream = stream | Transformer('//*[@id="field-%s"]' % field).replace(" ") return remove_changelog(self,remove_header(stream , field), field) def istrue(v, otherwise=None): if isinstance(v, bool): return v if str(v).lower() in ('yes', 'true', '1', 'on'): return True if not otherwise: return False return otherwise csection = 'field settings' class TicketTweaks(Component): implements(ITemplateStreamFilter, ITemplateProvider) ## ITemplateStreamFilter def filter_stream(self, req, method, filename, stream, data): self.log.debug('IN BlackMagic') if not filename == "ticket.html": self.log.debug('Not a ticket returning') return stream fields = self.config.getlist(csection, 'fields', []) self.log.debug('read enchants = %r' % fields) for field in fields: self.log.debug('starting : %s' % field) disabled = False hidden = False hide_summary = False remove = False perms = self.config.getlist(csection, '%s.permission' % field, []) self.log.debug('read permission config: %s has %s' % (field, perms)) for (perm, denial) in [s.split(":") for s in perms] : perm = perm.upper() self.log.debug('testing permission: %s:%s should act= %s' % (field, perm, (not req.perm.has_permission(perm) or perm == "ALWAYS"))) if (not req.perm.has_permission(perm) or perm == "ALWAYS"): if denial: denial = denial.lower() if denial == "disable": disabled = True elif denial == "hide": hidden = True elif denial == "remove": remove = True else: disabled = True else: disabled = True if disabled or istrue(self.config.get(csection, '%s.disable' % field, False)): self.log.debug('disabling: %s' % field) stream = disable_field(stream, field) if self.config.get(csection, '%s.label' % field, None): self.log.debug('labeling: %s' % field) stream = stream | Transformer('//label[@for="field-%s"]' % field).replace( self.config.get(csection, '%s.label' % field) ) if self.config.get(csection, '%s.notice' % field, None): self.log.debug('noticing: %s' % field) stream = stream | Transformer('//*[@id="field-%s"]' % field).after( tag.br() + tag.small()( tag.em()( Markup(self.config.get(csection, '%s.notice' % field)) ) ) ) tip = self.config.get(csection, '%s.tip' % field, None) if tip: self.log.debug('tipping: %s' % field) 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"\'") ) if remove or istrue(self.config.get(csection, '%s.remove' % field, None)): self.log.debug('removing: %s' % field) stream = remove_field(self, stream, field) if hidden or istrue(self.config.get(csection, '%s.hide' % field, None)): self.log.debug('hiding: %s' % field) stream = hide_field(self, stream, field) return stream ## ITemplateProvider def get_htdocs_dirs(self): from pkg_resources import resource_filename return [('blackmagic', resource_filename(__name__, 'htdocs'))] def get_templates_dirs(self): return []