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