def filter_stream(self, req, method, filename, stream, data): if filename == 'ticket.html' \ and req.path_info.startswith('/newticket'): # common js files add_script(req, 'tt/json2.js') stream = stream | Transformer('body').append( tag.script("preview = %s;" % ('true' if 'preview' in req.args else 'false')) + tag.script(type='text/javascript', src=req.href('tt', 'tt_newticket.js'))() ) return stream
def filter_stream(self, req, method, filename, stream, data): if filename == 'ticket.html' \ and req.path_info.startswith('/newticket'): # common js files add_script(req, 'tt/json2.js') stream = stream | Transformer('body').append( tag.script("preview = %s;" % ('true' if 'preview' in req.args else 'false')) + tag.script(type='text/javascript', src=req.href('tt', 'tt_newticket.js'))()) return stream
def filter_stream(self, req, method, filename, stream, data): # Add Stylesheet here, so that the ticket page gets it too :) add_stylesheet(req, "clients/clients.css") newticket = req.path_info.startswith('/newticket') if req.path_info.startswith('/ticket/') or newticket: setdefaultrate = '' if newticket: setdefaultrate = "$('#field-client').trigger('change');" script = StringIO() script.write(""" $(document).ready(function() { $('#field-client').change(function() { """) script.write('var clientrates = new Array();') for client in Client.select(self.env): script.write('clientrates["%s"] = %s;' % (client.name, client.default_rate or '""')) script.write(""" try { $('#field-clientrate').attr('value', clientrates[this.options[this.selectedIndex].value]); } catch (er) { } }); %s });""" % setdefaultrate) stream |= Transformer('.//head').append( tag.script(script.getvalue(), type_='text/javascript')) return stream
def filter_stream(self, req, method, filename, stream, data): if req.path_info.startswith('/ticket'): button = tag.div( tag.input(type="submit", title=_("Translate to %s") % req.locale.get_display_name(), value=_("Translate"), forward=_("Translate"), backward=_("Untranslate"), working=_("Working"), name="translate", class_="translate")) button(class_="inlinebuttons") script = tag.script('') script(src='https://www.google.com/jsapi?key=' + self.googleApiKey) script(type='text/javascript') stream |= Transformer('//head').prepend(script) stream |= Transformer( '//div[@id="content"]/div[@id="ticket"]/div[@class="description"]/h3' ).after(button) stream |= Transformer( '//div[@id="content"]/div/div[@id="changelog"]/div[@class="change"]/h3' ).after(button) add_stylesheet(req, 'translate/translate.css') add_script_data(req, {'googleApiKey': self.googleApiKey}) add_script_data(req, {'sessionLanguage': req.locale.language}) add_script(req, 'translate/translate.js') return stream
def _make_graph(self, req, repos, info): # Generate graph data graph = {} threads, vertices, columns = \ make_log_graph(repos, (item['rev'] for item in info)) graph.update(threads=threads, vertices=vertices, columns=columns, colors=self.graph_colors, line_width=0.04, dot_radius=0.1) add_script(req, 'revisiongraph/js/excanvas.js') add_script(req, 'revisiongraph/js/log_graph.js') add_script_data(req, {'graph': graph}) script = Markup("//<![CDATA[") + """ jQuery(document).ready(function($) { $('th.trac-graph, td.trac-graph').show(); var canvas = $('""" + Markup("<canvas>") + """').css({width: '%dem', height: '%dem'}) .appendTo('td.trac-graph')[0]; canvas.width = $(canvas).width(); canvas.height = $(canvas).height(); if (typeof(G_vmlCanvasManager) != 'undefined') canvas = G_vmlCanvasManager.initElement(canvas); $.paintLogGraph(graph, canvas); }); """ % (graph['columns'] * 2, len(info) * 2) + Markup("//]]>") th = tag.th('Graph', class_='trac-graph') td = tag.td(class_='trac-graph', rowspan='%d' % len(info)) script_filter = Transformer('//head').append(tag.script(script, type="text/javascript")) table_filter = Transformer('//table[@class="listing chglist"]').attr('class', 'listing chglist trac-graph') th_filter = Transformer('//table/thead/tr/th[@class="diff"]').before(th) td_filter = Transformer('//table/tbody/tr[1]/td[@class="diff"]').before(td) return [script_filter, table_filter, th_filter, td_filter]
def script(): from genshi.builder import tag data = getattr(req, '_tracdragdrop_data') text = '\n'.join([ 'var %s = %s;' % (name, to_json(val)) for name, val in data.iteritems()]) return tag.script(text, type='text/javascript')
def filter_stream(self, req, method, filename, stream, data): # Add Stylesheet here, so that the ticket page gets it too :) add_stylesheet(req, "clients/clients.css") newticket = req.path_info.startswith('/newticket') if req.path_info.startswith('/ticket/') or newticket: setdefaultrate = '' if newticket: setdefaultrate = "$('#field-client').trigger('change');" script = StringIO() script.write(""" $(document).ready(function() { $('#field-client').change(function() { """); script.write('var clientrates = new Array();') for client in Client.select(self.env): script.write('clientrates["%s"] = %s;' % (client.name, client.default_rate or '""')) script.write(""" try { $('#field-clientrate').attr('value', clientrates[this.options[this.selectedIndex].value]); } catch (er) { } }); %s });""" % setdefaultrate); stream |= Transformer('.//head').append(tag.script(script.getvalue(), type_='text/javascript')) return stream
def filter_stream(self, req, method, filename, stream, data): if filename == 'ticket.html': ticket = data.get('ticket') if ticket and ticket.exists and \ req.session.get(self.session_field, 'True') == 'True': filter_ = Transformer( '//script[contains(@src, "jquery.js")]') return stream | filter_.after( tag.script( type="text/javascript", src=self.env.href('chrome', 'ac', 'js', 'jquery.form.js'))) \ | filter_.after( tag.script( type="text/javascript", src=self.env.href('chrome', 'ac', 'js', 'comments.js'))) return stream
def embed_player(self, formatter, url, query, style): query_dict = xform_query(query) set_default_parameters( query_dict, _EMBED_FLOWPLAYER_DEFAULT_PARAMETERS ) player_id = self._generate_player_id() swf = pathjoin(formatter.href.chrome(), EMBED_PATH_FLOWPLAYER['swf']) style.pop('width') # use adaptiveRatio for player-size style.pop('height') # use adaptiveRatio for player-size attrs = { 'id': player_id, 'data-swf': swf, 'style': xform_style(style), } return tag.div( tag.video([ tag.source(type=mimetypes.guess_type(url)[0], src=url), tag.script(""" $(function() { $('#%s').flowplayer(%s); }); """ % (player_id, to_json(query_dict)) ), ]), **attrs )
def expand_macro(self, formatter, name, content): content = content.strip() if not content.isdigit(): return system_message('Invalid Ohloh project ID', '%s is not a number' % content) return tag.script('', src=self.SCRIPT_LOCATION % content, type='text/javascript')
def script(): from genshi.builder import tag data = getattr(req, '_tracdragdrop_data') text = '\n'.join([ 'var %s = %s;' % (name, to_json(val)) for name, val in data.iteritems() ]) return tag.script(text, type='text/javascript')
def expand_macro(self, formatter, name, content): if content and content.strip(): def repl(match): return self._quote[match.group(0)] return tag.script(self._quote_re.sub(repl, content), type='WaveDrom')
def load_map(self, locations, kml=None): """JS map for issues""" options = '{kml: %s}' % (kml and '"%s"' % kml or 'null') script = """$(document).ready(function() { var locations = %s; map_locations(locations, %s); })""" % (simplejson.dumps(locations), options) return tag.script(Markup(script), **{'type': 'text/javascript'})
def filter_stream(self, req, method, filename, stream, data): self.log.debug("TicketWebUiAddon executing") if not filename == 'ticket.html': #self.log.debug("TicketWebUiAddon not emitting ticket javascript because we are not on ticket.html") return stream stream = stream | Transformer('//div[@id="banner"]').before( tag.script(type="text/javascript", src=req.href.chrome("Billing", "ticket.js"))()) return stream
def _expand_view_topic(self, formatter, name, content): self.log.debug("Rendering ViewTopic macro...") # Check permission if not formatter.perm.has_permission('DISCUSSION_VIEW'): return # Determine topic subject page_name = formatter.req.path_info[6:] or 'WikiStart' subject = content or page_name # Create request context. context = Context.from_request(formatter.req) context.realm = 'discussion-wiki' # Get database access. db = self.env.get_db_cnx() context.cursor = db.cursor() # Get API component. api = self.env[DiscussionApi] # Get topic by subject try: id = int(subject) topic = api.get_topic(context, id) except: topic = api.get_topic_by_subject(context, subject) self.log.debug('subject: %s' % (subject,)) self.log.debug('topic: %s' % (topic,)) # Prepare request and resource object. if topic: context.req.args['topic'] = topic['id'] context.resource = Resource('discussion', 'topic/%s' % (topic['id'] ,)) # Process discussion request. template, data = api.process_discussion(context) # Return rendered template. data['discussion']['mode'] = 'message-list' data['discussion']['page_name'] = page_name if context.redirect_url: # Generate HTML elements for redirection. href = context.req.href(context.redirect_url[0]) + \ context.redirect_url[1] self.log.debug("Redirecting to %s" % (href)) return tag.div(tag.strong('Redirect: '), ' This page redirects to ', tag.a(href, href = href), tag.script("window.location = '" + context.req.href('discussion', 'redirect', redirect_url = href) + "'", language = "JavaScript"), class_ = "system-message") else: # Render template. return to_unicode(Chrome(self.env).render_template(formatter.req, template, data, 'text/html', True))
def filter_stream(self, req, method, filename, stream, data): self.log.debug("TicketWebUiAddon executing") if not filename == 'ticket.html': return stream stream = stream | Transformer('//div[@id="banner"]').before( tag.script(type="text/javascript", src=req.href.chrome("timingandestimation", "ticket.js"))() ) return stream
def filter_stream(self, req, method, filename, stream, formdata): if (filename == 'ticket.html'): add_stylesheet(req, 'multiselectlist/css/jquery-ui.css') add_stylesheet(req, 'multiselectlist/css/jquery.multiselect.css') add_script(req, 'multiselectlist/js/jquery-ui-1.8.16.custom.min.js') add_script(req, 'multiselectlist/js/jquery.multiselect.min.js') for item in self.multilist: values = self.env.config.get('multiselectlist', '%s.values' % item) if values: key = 'field_%s' % unicode(item) # 既存のチケットの場合はDBに格納されている値を取得する inputvalues = [] if key in req.args: # チケット登録時のバリデーションで引っかかった場合 # なお、DBに保管されている値より優先しなければならない inputvalues = req.args.get(key) elif req.path_info.startswith('/ticket'): ticketno = req.path_info[8:] db = self.env.get_db_cnx() cursor = db.cursor() sql = "select value from ticket_custom where ticket=%s and name='%s'" % ( ticketno, item) cursor.execute(sql) row = cursor.fetchone() if row and row[0]: inputvalues = row[0].split(',') self.env.log.info(inputvalues) value = values.split(',') xpath = '//input[@id="field-%s"]' % item # input要素をselect/option要素に置き換える。 # タグの繰り返しを行う場合は、配列で指定すればいいようだ。 script = """ jQuery(function(){ jQuery("#field-%s").multiselect({ selectedList: 3 }); }); """ % item stream |= Transformer(xpath).replace( tag.select([ tag.option(v, selected=(v in inputvalues or None)) for v in value ], id='field-%s' % item, name='field_%s' % item, size='%d' % len(value), multiple='true')) stream |= Transformer('//head').append( tag.script(script, type="text/javascript")) return stream
def filter_stream(self, req, method, filename, stream, data): self.log.debug("TicketWebUiAddon executing") if not filename == 'ticket.html': #self.log.debug("TicketWebUiAddon not emitting ticket javascript because we are not on ticket.html") return stream stream = stream | Transformer('//div[@id="banner"]').before( tag.script(type="text/javascript", src=req.href.chrome("Billing", "ticket.js"))() ) return stream
def filter_stream(self, req, method, filename, stream, data): if filename == 'query.html': filter_script = Transformer('//script[contains(@src, "jquery.js")]') filter_query = Transformer('table[@class="listing tickets"]/.') return stream | filter_script.after( tag.script( type="text/javascript", src=self.env.href('chrome', 'aq', 'js', 'query.js'))) \ | filter_query.prepend('<!-- Hello -->') return stream
def filter_stream(self, req, method, filename, stream, data): if filename == 'query.html': filter_script = Transformer( '//script[contains(@src, "jquery.js")]') filter_query = Transformer('table[@class="listing tickets"]/.') return stream | filter_script.after( tag.script( type="text/javascript", src=self.env.href('chrome', 'aq', 'js', 'query.js'))) \ | filter_query.prepend('<!-- Hello -->') return stream
def _create_twitter_button(self, url, locale): return [ tag.a('Tweet', href='//twitter.com/share', class_='twitter-share-button', data_url=url, data_counturl=url, data_lang=locale or None, data_count=self._twitter_data_count), tag.script(type='text/javascript', src='//platform.twitter.com/widgets.js'), ]
def expand_macro(self, formatter, name, content): req = formatter.req query_string = TicketQueryMacro.parse_args(content)[0] kwargs = dict([item.split('=') for item in content.split(',')[1:]]) try: query = Query.from_string(self.env, query_string) except QuerySyntaxError as e: raise MacroError(e) try: tickets = query.execute(req) except QueryValueError as e: raise MacroError(e) # Formats above had their own permission checks, here we need to # do it explicitly: tickets = [t for t in tickets if 'TICKET_VIEW' in req.perm(self.realm, t['id'])] tickets = map(lambda x: Ticket(self.env, x['id']), tickets) schedule_info = {'test': TicketScheduleSystem(self.env).get_schedule(tickets)} add_script(req, 'ticketrelation/js/bundle.js') add_stylesheet(req, 'ticketrelation/css/schedule.css') random_id = str(random.randint(0, 10000)) config = { 'url': req.base_url, 'startDate': kwargs.get('startdate', None), 'finishDate': kwargs.get('finishdate', None), 'showUnavailable': kwargs.get('showunavailable', 1) } return tag.div(tag.div( tag.schedule(**{':schedule': 'schedule', ':config': 'config'}), class_='schedule_container', id='schedule_container_' + random_id), tag.script(""" $(window).load(function() { var data = %s; var config = %s; var app = new window.Vue({ el: '#schedule_container_%s', data: { schedule: data, config: config, } }); });""" % (json.dumps(schedule_info, cls=DateTimeEncoder), json.dumps(config, cls=DateTimeEncoder), random_id)) )
def filter_stream(self, req, method, filename, stream, data): if not _is_wysiwyg_enabled(filename, self.templates): return stream options = {} if filename == 'ticket.html': options['escapeNewlines'] = _preserve_newlines(self.env) if options: text = 'var _tracwysiwyg = %s' % _to_json(options) stream |= Transformer('//head').append(tag.script(text, type='text/javascript')) return stream
def _create_hatena_bookmark_button(self, url, locale): href = '//b.hatena.ne.jp/entry/' if url: href += url.replace('#', '%23') return [ tag.a(tag.img(src='//b.st-hatena.com/images/entry-button/button-only.gif', alt=u'このエントリーをはてなブックマークに追加', width='20', height='20', style='border:none'), href=href, class_='hatena-bookmark-button', data_hatena_bookmark_layout=self._hatena_bookmark_layout, title=u'このエントリーをはてなブックマークに追加'), tag.script(type='text/javascript', charset='utf-8', async='async', src='//b.st-hatena.com/js/bookmark_button_wo_al.js'), ]
def filter_stream(self, req, method, filename, stream, data): if not _is_wysiwyg_enabled(filename, self.templates): return stream options = {} if filename == 'ticket.html': options['escapeNewlines'] = _preserve_newlines(self.env) if options: text = 'var _tracwysiwyg = %s' % _to_json(options) stream |= Transformer('//head').append( tag.script(text, type='text/javascript')) return stream
def _parse_params(self): self.metatags = [tag.meta(charset=self.document.settings.output_encoding)] self.stylesheets = [] stylesheets = self.document.settings.stylesheet or [] for href in stylesheets: self.stylesheets.append(tag.link(rel='stylesheet', href=href)) self.scripts = [] scripts = self.document.settings.script or [] for src, attributes in scripts: script = tag.script(src=src) if attributes: script = script(**{attributes: attributes}) self.scripts.append(script) return
def expand_macro(self, formatter, name, content, args=None): add_script(formatter.req, 'mathjax/update.js', 'text/javascript') # We access this internals directly because it is not possible to use add_script with full/absolute URL # http://trac.edgewall.org/ticket/10369 # We know scripts and scriptset elements are initialized because we called add_script before if MATHJAX_URL not in formatter.req.chrome.get('scriptset'): formatter.req.chrome.get('scripts').append({ 'href': MATHJAX_URL + '?delayStartupUntil=configured', 'type': 'text/javascript', }) formatter.req.chrome.get('scriptset').add(MATHJAX_URL) # We load configuration afterwards, as we have delay it with delayStartupUntil and we call MathJax.Hub.Configured here # We do this because having text/x-mathjax-config config blocks outside the head does not seem to work add_script(formatter.req, 'mathjax/config.js', 'text/javascript') if args is None: # Called as macro return tag.script(Markup(content), type_="math/tex") else: # Called as processor return tag.script(Markup(content), type_="math/tex; mode=display")
def jsconfig_tag(self): if self.js_config is None: return None if json is None: if 'theme' in self.js_config: msg = 'simplejson is not available, can not set reCAPTCHA ' + \ 'theme. Please install simplejson.' self.log.error(msg) else: msg = 'simplejson is not available so the reCAPTCHA widget ' + \ 'can not switch to the user\'s language.' self.log.debug(msg) return None js_string = 'RecaptchaOptions = %s;' % json.dumps(self.js_config) return tag.script(js_string, type='text/javascript')
def _enable_treesearch_for_page(self, req, stream, fields): project_name = req.environ.get('SCRIPT_NAME') add_script(req, 'treesearch/js/treesearch.js') add_stylesheet(req, 'treesearch/css/treesearch.css') add_script_data(req,(('selector',','.join(fields)),('params',{'SERVER_URL': project_name + '/diff'}))) stream = stream | Transformer('head').append(tag.script(''' jQuery(document).ready(function() { jQuery(selector).each(function() { $(this).addClass('treesearch'); $(this).tree(params); }); });''')) return stream
def filter_stream(self, req, method, filename, stream, data): if req.path_info.startswith('/ticket'): button = tag.div(tag.input(type="submit", title=_("Translate to %s") % req.locale.get_display_name(), value=_("Translate"), forward=_("Translate"), backward=_("Untranslate"), working=_("Working"), name="translate", class_="translate")) button(class_="inlinebuttons") script = tag.script(''); script(src = 'https://www.google.com/jsapi?key='+self.googleApiKey) script(type = 'text/javascript') stream |= Transformer('//head').prepend(script) stream |= Transformer('//div[@id="content"]/div[@id="ticket"]/div[@class="description"]/h3').after(button) stream |= Transformer('//div[@id="content"]/div/div[@id="changelog"]/div[@class="change"]/h3').after(button) add_stylesheet(req, 'translate/translate.css') add_script_data(req, {'googleApiKey': self.googleApiKey}) add_script_data(req, {'sessionLanguage': req.locale.language}) add_script(req, 'translate/translate.js') return stream
def _parse_params(self): self.metatags = [ tag.meta(charset=self.document.settings.output_encoding) ] self.stylesheets = [] stylesheets = self.document.settings.stylesheet or [] for href in stylesheets: self.stylesheets.append(tag.link(rel='stylesheet', href=href)) self.scripts = [] scripts = self.document.settings.script or [] for src, attributes in scripts: script = tag.script(src=src) if attributes: script = script(**{attributes: attributes}) self.scripts.append(script) return
def filter_stream(self, req, method, filename, stream, formdata): if (filename == 'ticket.html'): add_stylesheet(req, 'multiselectlist/css/jquery-ui.css') add_stylesheet(req, 'multiselectlist/css/jquery.multiselect.css') add_script(req, 'multiselectlist/js/jquery-ui-1.8.16.custom.min.js') add_script(req, 'multiselectlist/js/jquery.multiselect.min.js') for item in self.multilist: values = self.env.config.get('multiselectlist', '%s.values' % item) if values: key = 'field_%s' % unicode(item) # 既存のチケットの場合はDBに格納されている値を取得する inputvalues = [] if key in req.args: # チケット登録時のバリデーションで引っかかった場合 # なお、DBに保管されている値より優先しなければならない inputvalues = req.args.get(key) elif req.path_info.startswith('/ticket'): ticketno = req.path_info[8:] db = self.env.get_db_cnx() cursor = db.cursor() sql = "select value from ticket_custom where ticket=%s and name='%s'" % (ticketno, item) cursor.execute(sql) row = cursor.fetchone() if row and row[0]: inputvalues = row[0].split(',') self.env.log.info(inputvalues) value = values.split(',') xpath = '//input[@id="field-%s"]' % item # input要素をselect/option要素に置き換える。 # タグの繰り返しを行う場合は、配列で指定すればいいようだ。 script = """ jQuery(function(){ jQuery("#field-%s").multiselect({ selectedList: 3 }); }); """ % item stream |= Transformer(xpath).replace( tag.select( [tag.option(v, selected=(v in inputvalues or None)) for v in value], id='field-%s' % item, name='field_%s' % item, size='%d' % len(value), multiple='true')) stream |= Transformer('//head').append(tag.script(script, type="text/javascript")) return stream
def widget_fix(self, tag_id): "" js = ''' var factoid = jQuery('#%(tag_id)s'); var factoidDivElements = factoid.find('div'); // remove background color var factoidWithBackgroundColor = function(index){ return 'transparent' !== jQuery(this).css('background-color'); }; factoidDivElements.filter(factoidWithBackgroundColor).css('background-color', 'transparent'); // remove round corners factoidDivElements.filter(function() { return "none" !== $(this).css('background-image') }).remove() // ensure all links have the right color factoid.find('a').css({'color': null}); ''' % dict(tag_id=tag_id) return tag.script(js, type='text/javascript')
def filter_stream(self, req, method, filename, stream, data): if (filename <> 'ticket.html'): return stream keywords = self.config.getlist('keywordsuggest','keywords') if not keywords: self.log.debug('List of keywords not found in trac.ini. '\ 'Plugin keywordsuggest disabled.') return stream keywords = ','.join(("'%s'" % keyword for keyword in keywords)) mustmatch = 'mustMatch: true,' if not self.config.getbool('keywordsuggest','mustmatch'): mustmatch = "" sep = self.config.get('keywordsuggest','multipleseparator', '')[1:-1] or ', ' matchcontains = 'matchContains: true,' if not self.config.getbool('keywordsuggest','matchcontains',True): matchcontains = "" # inject transient part of javascript directly into ticket.html template js = """ $(function($) { var sep = '%s' $('#field-keywords').autocomplete([%s], {multiple: true, %s %s multipleSeparator: sep, autoFill: true}); $("form").submit(function() { // remove trail separator if any keywords = $("input#field-keywords").attr('value') if (keywords.lastIndexOf(sep) + sep.length == keywords.length) { $("input#field-keywords").attr('value', keywords.substring(0, keywords.length-sep.length)) } }); });""" % (sep, keywords, matchcontains, mustmatch) stream = stream | Transformer('.//head').append \ (tag.script(Markup(js), type='text/javascript')) # turn keywords field label into link to wiki page helppage = self.config.get('keywordsuggest','helppage') if helppage: link = tag.a(href='wiki/%s' % helppage, target='blank') if not self.config.getbool('keywordsuggest','helppage.newwindow', 'false'): link.attrib -= 'target' stream = stream | Transformer\ ('//label[@for="field-keywords"]/text()').wrap(link) return stream
def get_ajax(self, macroenv, req, content): macroenv.tracenv.log.debug("AJAX: %s" % (content,) ) myhash = hashlib.md5(content).hexdigest() ajax_url = "/projectplan_service/%s" % (urllib.quote_plus(content),) return tag.div( tag.div( tag.div( tag.a(tag.img( src="%s/%s" % (macroenv.tracreq.href.chrome( 'projectplan', macroenv.PPConstant.RelDocPath ), 'loading.gif' ), style='display:none;margin-right:2ex;margin-top:3ex;', title='computing projectplan report' ), tag.span("project report is loading"), href=ajax_url, style='padding:3ex;text-align:center;' ), id="%s_inner" % myhash, style="height:8ex;" ), id=myhash, style="height:8ex" ), tag.script(''' $(document).ready(function(){ $.ajax({ url: "%s", cache: false, beforeSend: function(){ $("#%s img").show(); }, success: function(data){ $("#%s").hide().after(data); ppAddTooltipWrapper("#%s"); }, error: function(jqXHR, textStatus, errorThrown){ $("#%s a").first().html("Error: report (of ProjectPlan) could not be rendered.").css({padding:"0",margin:"auto"}); $("#%s").addClass("system-message").css({height:"auto"}); $("#%s_inner").css({height:"auto"}); } }); }); ''' % (ajax_url, myhash, myhash, myhash, myhash, myhash, myhash )) )
def visit_math_block(self, node): ''' Only MathJax support ''' math_code = node.astext() math_env = pick_math_environment(math_code) if 'align' in math_env: template = '\\begin{%s}\n%s\n\\end{%s}' % (math_env, math_code, math_env) elem = tag.div(template) else: # equation template = '\(%s\)' % math_code elem = tag.span(template) elem(class_='math') self.context.append(elem) if not getattr(self, 'already_has_math_script', None): src = "http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML" self.scripts.append(tag.script(src=src)) self.already_has_math_script = True raise nodes.SkipNode
def obfuscate(self, clearText, format='<a href="mailto:%s">%s</a>', noscript='%s', at = u' § ', dot = u' · '): humanText = clearText.replace('@', at).replace('.', dot) valid, ignore = self.randomlyPartition() obfuText = self.buildObfuscatedString(humanText, valid, ignore) expr = '"' + obfuText + '"' expr += '.replace(/([%s](.)|[%s].)/ig,"$2")' % (valid, ignore) expr += '.replace(/%s/g, "@")' % at expr += '.replace(/%s/g, ".")' % dot var = 's%06x' % random.randrange(0x1000000) format = self.jsQuote(format, var) t = tag(tag.script('var ', var, ' = ', expr, ';\n', 'document.write(', Markup(format), ');\n', type='text/javascript')) if noscript: t = t(tag.noscript(noscript.replace('%s', humanText))) return t
def widget_fix(self, tag_id): "" js = ''' var widget = jQuery('#%(tag_id)s'); // trac's wiki page/h3 has a negative margin-left widget.find('h3').css({'margin-left': '-10px'}); // #div.gadget div.main' gets an additional margin-left which looks // bad - however it's done with an addition <style> tag so we can't // remove the rule itself. Instead this will make sure that the rule // does not apply anymore widget.find('div.gadget div.main').removeClass('main'); // the Ohloh logo floats all the way to the right, so we just set // a width widget.find('div.gadget').css({'width': '20em'}); ''' % dict(tag_id=tag_id) return tag.script(js, type='text/javascript')
def _parse_params(self): self.metatags = [tag.meta(charset=self.document.settings.output_encoding)] self.stylesheets = [] stylesheets = self.document.settings.stylesheet or [] for href in stylesheets: self.stylesheets.append(tag.link(rel='stylesheet', href=href)) stylesheets_inline = [] for path in (self.document.settings.stylesheet_inline or []): with open(path) as f: stylesheets_inline.append(f.read()) if stylesheets_inline: self.stylesheets.append(tag.style(Markup(''.join(stylesheets_inline)))) self.scripts = [] scripts = self.document.settings.script or [] for src, attributes in scripts: script = tag.script(src=src) if attributes: script = script(**{attributes: attributes}) self.scripts.append(script) return
def _create_hatena_bookmark_button(self, url, locale): href = '//b.hatena.ne.jp/entry/' if url: href += url.replace('#', '%23') return [ tag.a(tag.img( src='//b.st-hatena.com/images/entry-button/button-only.gif', alt=u'このエントリーをはてなブックマークに追加', width='20', height='20', style='border:none'), href=href, class_='hatena-bookmark-button', data_hatena_bookmark_layout=self._hatena_bookmark_layout, title=u'このエントリーをはてなブックマークに追加'), tag.script(type='text/javascript', charset='utf-8', async='async', src='//b.st-hatena.com/js/bookmark_button_wo_al.js'), ]
def filter_stream(self, req, method, filename, stream, data): if filename == "ticket.html" and 'ticket' in data: ticket = data['ticket'] if ticket.id > 0 and self._have_schedule(ticket): add_stylesheet(req, 'ticketrelation/css/schedule.css') schedule = self._get_schedule_info(ticket) stream |= Transformer('//div[@id="ticket"]').after( tag.div( tag.h3( tag.a('Schedule', id='schedule_label', href='#schedule_label'), class_='foldable'), tag.div(tag.schedule(**{':schedule': 'schedule', ':config': 'config'}), class_='schedule_container', id='schedule_container'), id='schedule') ) config = { 'url': req.base_url, 'startDate': None, 'finishDate': None, 'showUnavailable': 1 } stream |= Transformer('//body').append(tag.script(""" $(window).load(function() { var data = %s; var config = %s; var app = new Vue({ el: '#schedule_container', data: { schedule: data, config: config, } }); }); """ % (json.dumps(schedule, cls=DateTimeEncoder), json.dumps(config, cls=DateTimeEncoder)))) return stream
def filter_stream(self, req, method, filename, stream, data): """Add javascript to the ticket pages that makes an ajax request whenever a field changes so that the rules for hidden fields can be processed at the server side without and be effected without a page reload.""" if not req.path_info.startswith(('/newticket', '/ticket')): return stream address = tag.script(type="text/javascript") address.append("function getTracURL() { return '") address.append(req.base_url + AJAX_URL + "'; }") stream |= Transformer('.//head').append(address) # For some reason there isn't a status field on the ticket page, only the newticket page # Add it in so validation rules involving the status work if req.path_info.startswith('/ticket') and 'ticket' in data: status = tag.input(type="hidden", name="field_status", value=data['ticket'].values['status']) stream |= Transformer(".//form[@id='propertyform']").append(status) add_script(req, "ticketvalidation/js/RuleUpdate.js") return stream
def get_ajax(self, macroenv, req, content): macroenv.tracenv.log.debug("AJAX: %s" % (content, )) myhash = hashlib.md5(content).hexdigest() ajax_url = "/projectplan_service/%s" % (urllib.quote_plus(content), ) return tag.div( tag.div(tag.div(tag.a(tag.img( src="%s/%s" % (macroenv.tracreq.href.chrome( 'projectplan', macroenv.PPConstant.RelDocPath), 'loading.gif'), style='display:none;margin-right:2ex;margin-top:3ex;', title='computing projectplan report'), tag.span("project report is loading"), href=ajax_url, style='padding:3ex;text-align:center;'), id="%s_inner" % myhash, style="height:8ex;"), id=myhash, style="height:8ex"), tag.script(''' $(document).ready(function(){ $.ajax({ url: "%s", cache: false, beforeSend: function(){ $("#%s img").show(); }, success: function(data){ $("#%s").hide().after(data); ppAddTooltipWrapper("#%s"); }, error: function(jqXHR, textStatus, errorThrown){ $("#%s a").first().html("Error: report (of ProjectPlan) could not be rendered.").css({padding:"0",margin:"auto"}); $("#%s").addClass("system-message").css({height:"auto"}); $("#%s_inner").css({height:"auto"}); } }); }); ''' % (ajax_url, myhash, myhash, myhash, myhash, myhash, myhash)))
def filter_stream(self, req, method, filename, stream, data): if req.path_info == '/select_tickets': stream |= Transformer('//div[@id="banner"]').remove() stream |= Transformer('//div[@id="mainnav"]').remove() stream |= Transformer('//div[@id="ctxtnav"]').remove() if (filename == "ticket.html" or filename == 'ticket_preview.html') and 'ticket' in data: ticket = data['ticket'] trs = TicketRelationSystem(self.env) data = {} for relation in trs.build_relations().values(): if relation.ticket_type_a == ticket['type']: stream = self._generate_html(relation, relation.relation_type_a, 'a', stream, ticket, data) elif relation.ticket_type_b == ticket['type']: stream = self._generate_html(relation, relation.relation_type_b, 'b', stream, ticket, data) add_script(req, 'ticketrelation/js/bundle.js') stream |= Transformer('//body').append( tag.script(""" (function () { var data = %s; var app = new Vue({ el: '#properties', data: { relation: data, } }); })(); """ % json.dumps(data))) return stream
def _enable_autocomplete_for_page(self, req, method, filename, stream, data, inputs): add_stylesheet(req, 'autocomplete/css/autocomplete.css') add_script(req, 'autocomplete/js/jquery.tracautocomplete.js') username_completers = list(self._username_completers(req)) add_script_data(req, {'username_completers': username_completers}) # we could put this into some other URL which the browser could cache? #show users from all groups, shown or not, on the members page add_script_data(req, {'project_users': self._project_users}) controls = (self._split_input(input_) for input_ in inputs) js = ''.join('$("%s").makeAutocompleteSearch("%s", %s);\n' % (selector, method_ or 'select', options if isinstance(options, basestring) else to_json(options)) for selector, method_, options in controls) stream = stream | Transformer('//head').append(tag.script(""" jQuery(document).ready(function($) { %s }); """ % js,type="text/javascript")) return stream
def filter_stream(self, req, method, template, stream, data): if not (template == 'browser.html' and data.get('dir')): if ((not data.get('dir')) and (data.get('path')) and (data.get('path').endswith('.md'))): # Rendering single markdown file preview stream = stream | Transformer("//head/script[not(@src)][1]").after( tag.script( Markup( "jQuery(document).ready(function($) {" " $('#preview').each(function() {" " $(this).html(marked( $(this).children('pre').first().text() ));" " });" "});" ), type = "text/javascript" ) ) return stream add_stylesheet(req, 'common/css/code.css') repos = data.get('repos') or self.env.get_repository() rev = req.args.get('rev', None) for entry in data['dir']['entries']: # Rendering all READMEs in a directory preview try: if not entry.isdir and entry.name.lower().startswith('readme'): node = repos.get_node(entry.path, rev) req.perm(data['context'].resource).require('FILE_VIEW') mimeview = Mimeview(self.env) content = node.get_content() mimetype = node.content_type divclass = 'searchable' if entry.name.lower().endswith('.wiki'): mimetype = 'text/x-trac-wiki' divclass = 'searchable wiki' elif entry.name.lower().endswith('.md'): mimetype = 'text/x-markdown' divclass = 'searchable markdown' if not mimetype or mimetype == 'application/octet-stream': mimetype = mimeview.get_mimetype(node.name, content.read(4096)) or mimetype or 'text/plain' del content self.log.debug("ReadmeRenderer: rendering node %s@%s as %s" % (node.name, str(rev), mimetype)) output = mimeview.preview_data( data['context'], node.get_content(), node.get_content_length(), mimetype, node.created_path, '', annotations = [], force_source = False ) if output: if isinstance(output['rendered'], Stream): #content = output['rendered'].select('./pre/node()') #content = output['rendered'].select('./pre') content = output['rendered'].select('.') else: self.log.debug("GOT THERE") content = output['rendered'] insert = tag.div( tag.h1(entry.name, tag.a(Markup(' ¶'), class_ = "anchor", href = '#' + entry.name, title = 'Link to file' ), id_ = entry.name ), tag.div( content, #xml:space = "preserve", class_ = divclass, title = entry.name ), class_ = "readme", style = "padding-top: 1em;" ) stream = stream | Transformer("//div[@id='content']/div[@id='help']").before(insert) except Exception, e: self.log.debug(to_unicode(e))
def filter_stream(self, req, method, filename, stream, data): if not (filename == 'ticket.html' or (self.tags_enabled and filename == 'wiki_edit.html')): return stream keywords = self._get_keywords_string(req) if not keywords: self.log.debug( "No keywords found. TagInputAutoComplete is disabled.") return stream matchfromstart = '"^" +' if self.matchcontains_opt: matchfromstart = '' js = """ jQuery(document).ready(function($) { var keywords = [ %(keywords)s ] var sep = '%(separator)s'.trim() + ' ' function split( val ) { return val.split( /%(separator)s\s*|\s+/ ); } function extractLast( term ) { return split( term ).pop(); } $('%(field)s') // don't navigate away from field on tab when selecting // an item .bind( "keydown", function( event ) { if ( event.keyCode === $.ui.keyCode.TAB && $( this ).data( "autocomplete" ).menu.active ) { event.preventDefault(); } }) .autocomplete({ delay: 0, minLength: 0, source: function( request, response ) { // delegate back to autocomplete, but extract // the last term response( $.ui.autocomplete.filter( keywords, extractLast( request.term ) ) ); }, focus: function() { // prevent value inserted on focus return false; }, select: function( event, ui ) { var terms = split( this.value ); // remove the current input terms.pop(); // add the selected item terms.push( ui.item.value ); // add placeholder to get the comma-and-space at // the end terms.push( "" ); this.value = terms.join( sep ); return false; } }); });""" # Inject transient part of JavaScript into ticket.html template. if req.path_info.startswith('/ticket/') or \ req.path_info.startswith('/newticket'): js_ticket = js % { 'field': '#field-' + self.field_opt, 'keywords': keywords, 'matchfromstart': matchfromstart, 'separator': self.separator } stream = stream | Transformer('.//head').append\ (builder.script(Markup(js_ticket), type='text/javascript')) # Turn keywords field label into link to an arbitrary resource. if self.help_opt: link = self._get_help_link(req) if self.helpnewwindow_opt: link = builder.a(href=link, target='blank') else: link = builder.a(href=link) stream = stream | Transformer\ ('//label[@for="field-keywords"]/text()').wrap(link) # Inject transient part of JavaScript into wiki.html template. elif self.tags_enabled and req.path_info.startswith('/wiki/'): js_wiki = js % { 'field': '#tags', 'keywords': keywords, 'matchfromstart': matchfromstart, 'separator': self.separator } stream = stream | Transformer('.//head').append \ (builder.script(Markup(js_wiki), type='text/javascript')) return stream
def expand_macro(self, formatter, name, content): args, kwargs = parse_args(content, strict=True) try: kwargs = string_keys(kwargs) except: raise TracError('Error #3922, content: %s, args: %s, kwargs: %s', (str(content), str(args), kwargs)) if len(args) >= 1: url = args[0] elif len(args) == 0: raise TracError('URL to the mindmap at least required.') embed_count = getattr(formatter, EMBED_COUNT, 0) embed_count += 1 setattr(formatter, EMBED_COUNT, embed_count) if embed_count == 1: add_script(formatter.req, 'freemind/js/flashobject.js') url = get_absolute_url(url, formatter) base = url[:url.rfind('/') + 1] #script = '''\ # $(document).ready(function() { # $("#flashcontent%(count)02d").mouseover(function() { # document.visorFreeMind%(count)02d.focus(); # }); # # var fo = new FlashObject("%(visor)s", "visorFreeMind%(count)02d", "100%%", "100%%", 6, "#9999ff"); # # fo.addParam("quality","high"); # fo.addParam("bgcolor","#a0a0f0"); # fo.addVariable("openUrl","_blank"); # fo.addVariable("startCollapsedToLevel","3"); # fo.addVariable("maxNodeWidth","200"); # fo.addVariable("mainNodeShape","elipse"); # fo.addVariable("justMap","false"); # fo.addVariable("initLoadFile","%(file)s"); # fo.addVariable("defaultToolTipWordWrap",200); # fo.addVariable("offsetX","left"); # fo.addVariable("offsetY","top"); # fo.addVariable("buttonsPos","top"); # fo.addVariable("min_alpha_buttons",20); # fo.addVariable("max_alpha_buttons",100); # fo.addVariable("scaleTooltips","false"); # fo.addVariable("baseImagePath","%(base)s"); # fo.addVariable("CSSFile","%(css)s"); # //fo.addVariable("toolTipsBgColor","0xa0a0f0"); # //fo.addVariable("genAllShots","true"); # //fo.addVariable("unfoldAll","true"); # fo.write("flashcontent%(count)02d"); # }); #''' % { # 'count': embed_count, # 'visor': get_absolute_url('htdocs://freemind/swf/visorFreemind.swf', formatter), # 'file': url, # 'base': base, # 'css': get_absolute_url('htdocs://freemind/css/flashfreemind.css', formatter), #} script = '''\ $(document).ready(function() { $("#flashcontent%(count)02d").mouseover(function() { document.visorFreeMind%(count)02d.focus(); }); }); // url: %(url)s // base: %(base)s ''' % { 'count': embed_count, 'url': url, 'base': base } flash_dict = { 'openUrl': '_blank', 'initLoadFile': url, 'startCollapsedToLevel': '3', 'defaultToolTipWordWrap': '200', 'baseImagePath': base, 'min_alpha_buttons': '20', 'max_alpha_buttons': '100' } flash_vars = '&'.join( ['%s=%s' % (k, v) for k, v in flash_dict.items()]) embed = tag.embed(type='application/x-shockwave-flash', src=get_absolute_url( 'htdocs://freemind/swf/visorFreemind.swf', formatter), id='visorFreeMind%02d' % embed_count, bgcolor='#ffffff', quality='high', flashvars=flash_vars, align='middle', height='100%', width='100%') # Debugging. if 'debug' in args: import os import datetime output = "FreemindMacro Debug Log\n"\ "=======================\n\n"\ "time: %(time)s\n"\ "version: %(version)s\n"\ "content: %(content)s\n"\ "args: %(args)s\n"\ "kwargs: %(kwargs)s\n"\ "formatter.href.base: %(base)s\n"\ "script: \n\n"\ "%(script)s" % { 'time': datetime.datetime.utcnow().strftime('%Y%m%dT%H%M%SZ'), 'version': __version__, 'content': content, 'args': str(args), 'kwargs': str(kwargs), 'base': str(formatter.href.base), 'script': script } return tag.pre( output, style='border: 2px dashed red; padding: 5px; background: #eee;' ) style_dict = xform_style(kwargs.get('style', '')) width = kwargs.pop('width', style_dict.get('width', '800px')) height = kwargs.pop('height', style_dict.get('height', '600px')) style_dict['border'] = style_dict.get('border', '1px solid #cccccc;') style_dict['margin'] = style_dict.get('margin', '0 auto') style_dict['width'] = width style_dict['height'] = height kwargs['style'] = xform_style(style_dict) # You can't touch this... kwargs.pop('id', None) if 'class' in kwargs: kwargs['class_'] = kwargs.pop('class') tags = [] #tags.append(tag.div('Flash plugin or JavaScript are turned off. Activate both and reload to view the mindmap.', # id='flashcontent%02d' % embed_count, **kwargs)) tags.append( tag.div(embed, id='flashcontent%02d' % embed_count, **kwargs)) tags.append(tag.script(script)) return ''.join([str(i) for i in tags])
def _enable_autocomplete_for_page(self, req, stream, inputs): add_stylesheet(req, 'autocomplete/css/select2_autocomplete.css') #There should never be multiple implementations of IADLDSAutoCompleteProvider endpoint = self.autocompleter[0].get_endpoint() if endpoint.get('permission') is None or req.perm.has_permission( endpoint.get('permission')): #Js to initialize select2 js = '' selectors = [str(element + class_) for class_, element in inputs] if selectors: js += '$("%s").select2({' % ', '.join(selectors) js += '''width: "500px", dropdownCssClass: "ui-dialog",''' js += 'placeholder: "%s",' % (_('Search known users within' + ' this project and users' + ' in external sources.')) js += 'multiple: true,' js += 'minimumInputLength: 2,' js += 'tokenSeparators: [",", ";"],' js += 'ajax: {' js += 'url: "%s",' % req.href(endpoint.get('url')) js += ''' dataType: 'json', data: function (term, page) { return { q: term }; }, results: function (data, page) { return { results: data }; } }, createSearchChoice: function(term, data) { return { id:term, text:term } }, formatResult: userFormatResult, formatSelection: userFormatSelection, escapeMarkup: function (m) { return m; }''' js += '});' #Add formatting functions stream = stream | Transformer('//head').append(tag.script(Markup(''' function userFormatResult(user) { var markup = ''; if (user.text !== undefined) { if (user.id == user.text) { return '<div class="manual">' + user.text + '</div>'; } else { return '<div class="header"><h5>' + user.text + '</h5></div>'; } } markup = '<div class="result">'; if (user.id !== undefined) { markup += '<span class="username"><p>' + user.id + '</p></span>'; } if(user.name !== undefined || user.email !== undefined) { markup += '<span class="info">'; if (user.name !== undefined) { markup += '<p>' + user.name + '</p>'; } if (user.email !== undefined) { markup += '<p><' + user.email + '></p>'; } markup += '</span>'; } markup += '</div>'; return markup; } function userFormatSelection(user) { return user.id; } '''), type="text/javascript")) stream = stream | Transformer('//head').append(tag.script(''' jQuery(document).ready( function($) { %s }); ''' % js, type="text/javascript")) return stream
def filter_stream(self, req, method, filename, stream, data): if filename == 'ticket.html': # setup variables javascript = [self.javascript()] onload = [] onsubmit = [] policy_dict = self.policy_dict() # add JS functions to the head block for policy in self.policies: policy_javascript = policy.javascript() if policy_javascript: add_script(req, policy_javascript) # javascript.append(policy_javascript) policies = self.parse() for name, policy in policies.items(): # insert the condition into the JS conditions = policy['condition'] _conditions = [] for condition in conditions: _condition = {} _condition['field'] = condition['field'] _condition['comparitor'] = camelCase(condition['comparitor']) comp_type = self.comparitors[condition['comparitor']] value = condition['value'] if comp_type == 'Array': _condition['value'] = '[ %s ]' % ', '.join(["'%s'" % v for v in value]) else: _condition['value'] = "'%s'" % value _conditions.append("{field: '%(field)s', comparitor: %(comparitor)s, value: %(value)s}" % _condition) condition = '%s = [ %s ];' % (name, ', '.join(_conditions)) javascript.append(condition) # find the correct handler for the policy for action in policy['actions']: handler = policy_dict.get(action['name']) if handler is None: self.log.error('No ITicketSubmitPolicy found for "%s"' % action['name']) continue # filter the stream stream = handler.filter_stream(stream, name, policy['condition'], *action['args']) # add other necessary JS to the page policy_onload = handler.onload(name, policy['condition'], *action['args']) if policy_onload: onload.append(policy_onload) policy_onsubmit = handler.onsubmit(name, policy['condition'], *action['args']) if policy_onsubmit: onsubmit.append(policy_onsubmit) # insert onload, onsubmit hooks if supplied if onload: javascript.append(self.onload(onload)) if onsubmit: javascript.append(self.onsubmit(onsubmit)) stream |= Transformer("//form[@id='propertyform']").attr('onsubmit', 'validate()') # insert head javascript if javascript: javascript = '\n%s\n' % '\n'.join(javascript) javascript = tag.script(Markup(javascript), **{ "type": "text/javascript"}) stream |= Transformer("head").append(javascript) return stream
def filter_stream(self, req, method, filename, stream, data): if filename == "ticket.html": ##Check Permissions enchants = self.config.get('blackmagic', 'tweaks', '') for field in (x.strip() for x in enchants.split(',')): self.env.log.debug("Checking %s:" % field) disabled = False hidden = False #Get a list of the permissions from the config, split them on commas and remove spaces perms = self.config.get('blackmagic', '%s.permission' % field, '').upper() #Default to not having permissions hasPerm = True if perms: hasPerm = False #If there are permissions self.env.log.debug("perm: %s" % len(perms)) perms = perms.split(',') #walk the list we got back and check the request for the permission for perm in perms: perm = perm.strip() self.env.log.debug("Checking perm: %s" % perm) if perm in req.perm: self.env.log.debug("User has perm: %s" % perm) hasPerm = True if hasPerm == False: 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 self.env.log.debug('hasPerm: %s' % hasPerm) if hidden == False: if self.config.get('blackmagic', '%s.label' % field, None): labelTXT = self.config.get('blackmagic', '%s.label' % field) label = tag.label("%s:" %labelTXT, for_="field-%s" % field) stream = stream | Transformer('//label[@for="field-%s"]' % field).replace(label) if hasPerm == False: if istrue(self.config.get('blackmagic', '%s.hide' % field, None)): hidden = True if disabled or istrue(self.config.get('blackmagic', '%s.disable' % field, False)): stream = stream | Transformer('//*[@id="field-%s"]' % field).attr("disabled", "disabled") label = self.config.get('blackmagic', '%s.label' % field) if not label: label = field.capitalize() if not self.gray_disabled: stream = stream | Transformer('//label[@for="field-%s"]' % field).replace( tag.strike()('%s:' % label) ) else: stream = stream | Transformer('//label[@for="field-%s"]' % field).replace( tag.span(style="color:%s" % self.gray_disabled)('%s:' % label) ) if hidden or istrue(self.config.get('blackmagic', '%s.hide' % field, None)): stream = stream | Transformer('//th[@id="h_%s"]' % field).replace(" ") stream = stream | Transformer('//td[@headers="h_%s"]' % field).replace(" ") stream = stream | Transformer('//label[@for="field-%s"]' % field).replace(" ") stream = stream | Transformer('//*[@id="field-%s"]' % field).replace(" ") if hidden == False: if self.config.get('blackmagic', '%s.notice' % field, None): stream = stream | Transformer('//*[@id="field-%s"]' % field).after( tag.br() + tag.small(class_="notice-%s" %field)( 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 script_tag(path=None, content=None): kw = { 'type' : "text/javascript"} if path: kw['src'] = link_builder(map_script(path, debug)) return tag.script(content , **kw)
def expand_macro(self, formatter, name, content, args=None): # Utility methods def lte_ie8(req): user_agent = formatter.req.get_header('user-agent') msie = user_agent.find('MSIE ') return (msie != -1) and user_agent[msie + 5:msie + 6] in ['6', '7', '8'] def after_AS(string): index = string.find(' AS ') return index > 0 and string[index + 4:] or string def before_AS(string): index = string.find(' AS ') return index > 0 and string[:index] or string # add scripts if lte_ie8(formatter.req): add_script(formatter.req, "statushistorychart/js/flot/excanvas.js") add_script(formatter.req, "statushistorychart/js/flot/jquery.flot.js") add_script(formatter.req, "statushistorychart/js/flot/jquery.flot.time.js") add_script(formatter.req, "statushistorychart/js/enabler.js") # from macro parameters query = None query_href = None status_list = ['new', 'assigned', 'accepted', 'closed'] # default if(content): # replace parameters for match in reversed([match for match in re_param.finditer(content)]): param_name = match.group(0) if param_name == '$USER': authname = formatter.req.authname if authname == 'anonymous': authname = 'anonymous||somebody' content = content.replace(param_name, authname) else: param_value = formatter.req.args.get(param_name[1:]) if param_value: content = content.replace(param_name, param_value) # execute query query = Query.from_string(formatter.env, content) field = query and query.id or 'status' # stopgap implementation; I use 'report' for y-axis parameter field = filter(lambda x: x['name'] == field, TicketSystem(self.env).fields) if len(field) <= 0: return tag.div(tag.strong('Error: Macro %s failed' % name), tag.pre("%s is not field name" % query.id), class_="system-message") field = field[0] custom = 'custom' in field and field['custom'] or None status_list = query and query.format and query.format.split('/') or \ 'options' in field and copy.copy(field['options']) or status_list if field['name'] == 'status' and not (query and query.format): def isprime(item): primaries = ['new', 'assigned', 'accepted', 'closed'] return (item in primaries and [primaries.index(item)] or [len(primaries)])[0] status_list.sort(key=isprime) if '*' not in status_list: status_list.append('*') default_status = status_list.index('*') # execute query for ids of ticket if(query and len(query.constraint_cols) > 0): result = query.execute() or [{'id':-1}] # Sentinel for no result cond = "ticket.id in (%s)" % ', '.join([str(t['id']) for t in result]) elif formatter.resource.realm == 'milestone': cond = "ticket.milestone='%s'" % formatter.resource.id elif('query_tickets' in formatter.req.session): # You Feeling Lucky query_tickets = formatter.req.session.get('query_tickets', '') query_href = formatter.req.session.get('query_href', '') tickets = len(query_tickets) and query_tickets.split(' ') or ['-1'] # Sentinel for no result cond = "ticket.id in (%s)" % ', '.join(tickets) else: raise TracError("%sMacro: Empty. There are no content and no context." % name) # execute query for value changes of each ticket join_clause_dummy = '' join_clause = "JOIN ticket_custom ON ticket.id = ticket_custom.ticket and ticket_custom.name = '%s'" cursor = formatter.env.get_read_db().cursor() cursor.execute(""" SELECT id, time, null, %s FROM ticket %s WHERE %s UNION SELECT id, ticket_change.time, oldvalue, newvalue FROM ticket JOIN ticket_change ON ticket.id = ticket_change.ticket WHERE %s AND field='%s' ORDER BY id, time """ % (custom and "'\uFEFF'" or field['name'], # ZERO WIDTH NO-BREAK SPACE; uses for mark of invalid data custom and (join_clause % field['name']) or join_clause_dummy, cond, cond, field['name'])) changes = [row for row in cursor.fetchall()] # transform (ticket, time, status)* ==> (ticket <>- status)* tid = 0 # position of ticket id tickets = {} for change in changes: ticket = tickets.get(change[tid]) # slot is exist, or if ticket is None: ticket = tickets[change[tid]] = [] # create new slot and use it ticket.append(list(change)) # generate status_list splitted, {a, b+c, d} => {a:0, b+c:1, b:1, c:1, d:2} status_list_splitted = {} for index, status in enumerate(map(before_AS, status_list)): status_list_splitted[status] = index if status.find('+') >= 0: for each in status.split('+'): status_list_splitted[each] = index # groupstats = self.compmgr[DefaultTicketGroupStatsProvider] # ticket_groups = groupstats._get_ticket_groups() # for group in ticket_groups: # pass for tid in tickets: if len(tickets[tid]) > 1: tickets[tid][0][3] = tickets[tid][1][2] # override last value with initial value # generate data data = [] # points too_many_tickets = len(tickets) > 200 for no, tid in enumerate(sorted(tickets)): if not too_many_tickets or tickets[tid][-1][3] != 'closed': void, time, void, state = tickets[tid][-1] # @UnusedVariable index = status_list_splitted.get(state, default_status) data.append({'points': {'show': True, 'radius': 8, 'color': no}, 'label': tid, 'data': [[time / 1000, index]]}) # lines for no, tid in enumerate(sorted(tickets)): data.append({'color': no, 'label': tid, 'data': [[time / 1000, status_list_splitted.get(state, default_status)] for void, time, void, state in tickets[tid]]}) # @UnusedVariable from trac import __version__ as VERSION if VERSION[0:1] != '0': # render for trac 1.0 or later add_script_data(formatter.req, {'statushistorychart_yaxis': map(after_AS, status_list)}) add_script_data(formatter.req, {'statushistorychart_data': data}) return tag.a(_("Return to Last Query"), href=query_href) \ + tag.div(id="statushistorychart", style="width: 800px; height: 400px;") else: # if trac < 1.0 or earlier from trac.util.presentation import to_json return tag.script("var statushistorychart_yaxis = %s; var statushistorychart_data = %s" \ % (to_json(map(after_AS, status_list)), to_json(data)), type="text/javascript") \ + tag.a(_("Return to Last Query"), href=query_href) \ + tag.div(id="statushistorychart", style="width: 800px; height: 400px;")