| [4260] | 1 | from trac.core import Component, implements, TracError |
|---|
| 2 | from trac.config import Option, IntOption, ListOption, BoolOption |
|---|
| 3 | from trac.web.chrome import ITemplateProvider, add_stylesheet, add_script |
|---|
| 4 | from pkg_resources import resource_filename |
|---|
| 5 | from trac.web.api import ITemplateStreamFilter |
|---|
| 6 | from genshi.builder import tag |
|---|
| 7 | from genshi.core import Markup, Stream |
|---|
| 8 | from genshi.filters.transform import Transformer |
|---|
| 9 | import re, cPickle |
|---|
| 10 | from trac.perm import IPermissionRequestor |
|---|
| 11 | |
|---|
| [10266] | 12 | from genshi.filters import HTMLSanitizer |
|---|
| 13 | from genshi.output import TextSerializer |
|---|
| 14 | |
|---|
| 15 | def textOf(self, **keys): |
|---|
| 16 | return self.render('text', None, **keys) |
|---|
| 17 | |
|---|
| 18 | Stream.textOf = textOf |
|---|
| 19 | |
|---|
| [4260] | 20 | #@staticmethod |
|---|
| 21 | def disable_field(stream, field): |
|---|
| [7919] | 22 | def select_helper(stream): |
|---|
| 23 | s = Stream(stream) |
|---|
| [10266] | 24 | name = s.select('@name').textOf() |
|---|
| [7919] | 25 | opt = s.select('//option[@selected]') |
|---|
| 26 | if not opt: s.select('//option[position()=1]') |
|---|
| [10266] | 27 | text = opt.select("text()").textOf() |
|---|
| 28 | value = s.select('@value').textOf() |
|---|
| [7919] | 29 | if not value: value = text |
|---|
| 30 | |
|---|
| 31 | for kind,data,pos in tag.span(text, id=("field-%s"%field)).generate(): |
|---|
| 32 | yield kind,data,pos |
|---|
| 33 | for kind,data,pos in tag.input(value=value, name=name, type="hidden").generate(): |
|---|
| 34 | yield kind,data,pos |
|---|
| 35 | |
|---|
| 36 | |
|---|
| [4260] | 37 | def helper(field_stream): |
|---|
| [4270] | 38 | s = Stream(field_stream) |
|---|
| [10266] | 39 | value = s.select('@value').textOf() |
|---|
| 40 | name = s.select('@name').textOf() |
|---|
| [4260] | 41 | for kind,data,pos in tag.span(value, id=("field-%s"%field)).generate(): |
|---|
| 42 | yield kind,data,pos |
|---|
| [4270] | 43 | for kind,data,pos in tag.input(value=value, name=name, type="hidden").generate(): |
|---|
| 44 | yield kind,data,pos |
|---|
| [4260] | 45 | |
|---|
| [7919] | 46 | stream = stream | Transformer( '//select[@id="field-%s"]' % field ).filter(select_helper) |
|---|
| 47 | stream = stream | Transformer( '//input[@id="field-%s"]' % field ).filter(helper) |
|---|
| 48 | return stream |
|---|
| [4260] | 49 | |
|---|
| 50 | |
|---|
| 51 | def remove_header(stream, field): |
|---|
| 52 | """ Removes the display from the ticket properties """ |
|---|
| [4261] | 53 | stream = stream | \ |
|---|
| 54 | Transformer('//th[@id="h_%s"]' % field).replace(tag.th(id="h_%s" % field)) |
|---|
| 55 | stream = stream | \ |
|---|
| 56 | Transformer('//td[@headers="h_%s"]' % field).replace(tag.th(id="h_%s" % field)) |
|---|
| [4260] | 57 | return stream |
|---|
| 58 | |
|---|
| [9630] | 59 | def remove_changelog(self, stream, field): |
|---|
| [4261] | 60 | """ Removes entries from the visible changelog""" |
|---|
| [9630] | 61 | #self.log.debug('Begin ChangeLog Filter') |
|---|
| 62 | check = self.env.config.get('ticket-custom',field+'.label', field).lower().strip() |
|---|
| [4261] | 63 | def helper(field_stream): |
|---|
| [10266] | 64 | try: |
|---|
| 65 | s = Stream(field_stream) |
|---|
| 66 | self.log.debug('ChangeLog Pre') |
|---|
| 67 | # without None as the second value we get str instead of unicode |
|---|
| 68 | # and that causes things to break sometimes |
|---|
| 69 | f = s.select('//strong/text()').textOf(strip_markup=True).lower() |
|---|
| 70 | # self.log.debug(u'ChangeLog Pre 2 : %s: %r', type(f), f) |
|---|
| 71 | self.log.debug('ChangeLog Filter: field:%s, label:%s, we are looking at:%r, skip?%s', |
|---|
| 72 | field, check, f, check == f ) |
|---|
| 73 | if check != f: #if we are the field just skip it |
|---|
| [4261] | 74 | #identity stream filter |
|---|
| [10266] | 75 | for kind, data, pos in s: |
|---|
| 76 | yield kind, data, pos |
|---|
| 77 | except Exception, e: |
|---|
| 78 | self.log.exception('ChangeLog: Stream Filter Exception'); |
|---|
| 79 | raise e |
|---|
| [4261] | 80 | stream = stream | Transformer('//ul[@class="changes"]/li').filter(helper) |
|---|
| 81 | return stream |
|---|
| 82 | |
|---|
| 83 | |
|---|
| [9630] | 84 | def hide_field(self, stream , field): |
|---|
| [4261] | 85 | """ Replaces a field from the form area with an input type=hidden""" |
|---|
| 86 | def helper (field_stream): |
|---|
| [10266] | 87 | type = Stream(field_stream).select('@type').textOf() |
|---|
| [5348] | 88 | if type == 'checkbox': |
|---|
| [10266] | 89 | if Stream(field_stream).select('@checked').textOf() == "checked": |
|---|
| [5348] | 90 | value = 1 |
|---|
| 91 | else: |
|---|
| 92 | value = 0 |
|---|
| 93 | else: |
|---|
| [10266] | 94 | value = Stream(field_stream).select('@value').textOf() |
|---|
| 95 | name = Stream(field_stream).select('@name').textOf() |
|---|
| [4270] | 96 | for kind,data,pos in tag.input( value=value, |
|---|
| [4261] | 97 | type="hidden", name=name).generate(): |
|---|
| 98 | yield kind,data,pos |
|---|
| [7919] | 99 | |
|---|
| 100 | def select_helper(stream): |
|---|
| 101 | s = Stream(stream) |
|---|
| [10266] | 102 | name = s.select('@name').textOf() |
|---|
| [7919] | 103 | opt = s.select('//option[@selected]') |
|---|
| 104 | if not opt: s.select('//option[position()=1]') |
|---|
| [10266] | 105 | text = opt.select("text()").textOf() |
|---|
| 106 | value = s.select('@value').textOf() |
|---|
| [7919] | 107 | if not value: value = text |
|---|
| 108 | for kind,data,pos in tag.input(value=value, name=name, type="hidden").generate(): |
|---|
| 109 | yield kind,data,pos |
|---|
| 110 | |
|---|
| [4261] | 111 | stream = stream | Transformer('//label[@for="field-%s"]' % field).replace(" ") |
|---|
| [7919] | 112 | stream = stream | Transformer('//input[@id="field-%s"]' % field).filter(helper) |
|---|
| 113 | stream = stream | Transformer('//select[@id="field-%s"]' % field).filter(select_helper) |
|---|
| [4261] | 114 | |
|---|
| [9630] | 115 | return remove_changelog(self, remove_header(stream , field), field) |
|---|
| [4261] | 116 | |
|---|
| [9630] | 117 | def remove_field(self, stream , field): |
|---|
| [4260] | 118 | """ Removes a field from the form area""" |
|---|
| 119 | stream = stream | Transformer('//label[@for="field-%s"]' % field).replace(" ") |
|---|
| [7869] | 120 | stream = stream | Transformer('//*[@id="field-%s"]' % field).replace(" ") |
|---|
| [9630] | 121 | return remove_changelog(self,remove_header(stream , field), field) |
|---|
| [4260] | 122 | |
|---|
| 123 | def istrue(v, otherwise=None): |
|---|
| [7235] | 124 | if isinstance(v, bool): |
|---|
| 125 | return v |
|---|
| 126 | if str(v).lower() in ('yes', 'true', '1', 'on'): |
|---|
| [4260] | 127 | return True |
|---|
| [7235] | 128 | if not otherwise: |
|---|
| 129 | return False |
|---|
| 130 | return otherwise |
|---|
| [4260] | 131 | |
|---|
| [4261] | 132 | csection = 'field settings' |
|---|
| 133 | |
|---|
| [4260] | 134 | class TicketTweaks(Component): |
|---|
| [4270] | 135 | implements(ITemplateStreamFilter, ITemplateProvider) |
|---|
| [4260] | 136 | ## ITemplateStreamFilter |
|---|
| 137 | |
|---|
| 138 | def filter_stream(self, req, method, filename, stream, data): |
|---|
| [4261] | 139 | self.log.debug('IN BlackMagic') |
|---|
| [4260] | 140 | if not filename == "ticket.html": |
|---|
| [4261] | 141 | self.log.debug('Not a ticket returning') |
|---|
| [4260] | 142 | return stream |
|---|
| [4264] | 143 | fields = self.config.getlist(csection, 'fields', []) |
|---|
| 144 | self.log.debug('read enchants = %r' % fields) |
|---|
| 145 | for field in fields: |
|---|
| 146 | self.log.debug('starting : %s' % field) |
|---|
| [4260] | 147 | disabled = False |
|---|
| 148 | hidden = False |
|---|
| 149 | hide_summary = False |
|---|
| [4261] | 150 | remove = False |
|---|
| 151 | perms = self.config.getlist(csection, '%s.permission' % field, []) |
|---|
| [4264] | 152 | self.log.debug('read permission config: %s has %s' % (field, perms)) |
|---|
| [4260] | 153 | for (perm, denial) in [s.split(":") for s in perms] : |
|---|
| 154 | perm = perm.upper() |
|---|
| [4264] | 155 | self.log.debug('testing permission: %s:%s should act= %s' % |
|---|
| [4261] | 156 | (field, perm, (not req.perm.has_permission(perm) or perm == "ALWAYS"))) |
|---|
| 157 | if (not req.perm.has_permission(perm) or perm == "ALWAYS"): |
|---|
| [4260] | 158 | if denial: |
|---|
| 159 | denial = denial.lower() |
|---|
| 160 | if denial == "disable": |
|---|
| 161 | disabled = True |
|---|
| 162 | elif denial == "hide": |
|---|
| 163 | hidden = True |
|---|
| [4261] | 164 | elif denial == "remove": |
|---|
| 165 | remove = True |
|---|
| [4260] | 166 | else: |
|---|
| 167 | disabled = True |
|---|
| 168 | else: |
|---|
| 169 | disabled = True |
|---|
| 170 | |
|---|
| [4261] | 171 | if disabled or istrue(self.config.get(csection, '%s.disable' % field, False)): |
|---|
| [4264] | 172 | self.log.debug('disabling: %s' % field) |
|---|
| [4261] | 173 | stream = disable_field(stream, field) |
|---|
| [4260] | 174 | |
|---|
| [4261] | 175 | if self.config.get(csection, '%s.label' % field, None): |
|---|
| [4264] | 176 | self.log.debug('labeling: %s' % field) |
|---|
| [4260] | 177 | stream = stream | Transformer('//label[@for="field-%s"]' % field).replace( |
|---|
| [4261] | 178 | self.config.get(csection, '%s.label' % field) |
|---|
| [4260] | 179 | ) |
|---|
| 180 | |
|---|
| [4261] | 181 | if self.config.get(csection, '%s.notice' % field, None): |
|---|
| [4264] | 182 | self.log.debug('noticing: %s' % field) |
|---|
| [4260] | 183 | stream = stream | Transformer('//*[@id="field-%s"]' % field).after( |
|---|
| 184 | tag.br() + tag.small()( |
|---|
| 185 | tag.em()( |
|---|
| [4261] | 186 | Markup(self.config.get(csection, '%s.notice' % field)) |
|---|
| [4260] | 187 | ) |
|---|
| 188 | ) |
|---|
| 189 | ) |
|---|
| 190 | |
|---|
| [4261] | 191 | tip = self.config.get(csection, '%s.tip' % field, None) |
|---|
| [4260] | 192 | if tip: |
|---|
| [4264] | 193 | self.log.debug('tipping: %s' % field) |
|---|
| [4260] | 194 | stream = stream | Transformer('//div[@id="banner"]').before( |
|---|
| 195 | tag.script(type="text/javascript", |
|---|
| 196 | src=req.href.chrome("blackmagic", "js", "wz_tooltip.js"))() |
|---|
| 197 | ) |
|---|
| 198 | |
|---|
| 199 | stream = stream | Transformer('//*[@id="field-%s"]' % field).attr( |
|---|
| 200 | "onmouseover", "Tip('%s')" % tip.replace(r"'", r"\'") |
|---|
| 201 | ) |
|---|
| 202 | |
|---|
| [4261] | 203 | if remove or istrue(self.config.get(csection, '%s.remove' % field, None)): |
|---|
| [4264] | 204 | self.log.debug('removing: %s' % field) |
|---|
| [9630] | 205 | stream = remove_field(self, stream, field) |
|---|
| [4261] | 206 | |
|---|
| 207 | if hidden or istrue(self.config.get(csection, '%s.hide' % field, None)): |
|---|
| [4264] | 208 | self.log.debug('hiding: %s' % field) |
|---|
| [9630] | 209 | stream = hide_field(self, stream, field) |
|---|
| [4260] | 210 | |
|---|
| 211 | return stream |
|---|
| 212 | |
|---|
| 213 | ## ITemplateProvider |
|---|
| 214 | |
|---|
| 215 | def get_htdocs_dirs(self): |
|---|
| 216 | from pkg_resources import resource_filename |
|---|
| 217 | return [('blackmagic', resource_filename(__name__, 'htdocs'))] |
|---|
| 218 | |
|---|
| 219 | def get_templates_dirs(self): |
|---|
| 220 | return [] |
|---|