def expand_macro(self, formatter, name, content): if not content: return '' args, kwargs = parse_args(content) if len(args) > 1: return system_message("Number of args can't be greater than 1") if args[0] == 'author': page = WikiPage(self.env, formatter.context.resource, 1) text = page.author elif args[0] == 'version': page = WikiPage(self.env, formatter.context.resource) text = str(page.version) elif args[0] == 'changed_by': page = WikiPage(self.env, formatter.context.resource) text = page.author elif args[0] == 'comment': page = WikiPage(self.env, formatter.context.resource) text = page.comment elif args[0] == 'changed_ts': page = WikiPage(self.env, formatter.context.resource) text = format_datetime(page.time) else: return system_message("Unkwown argument %s" % args[0]) return format_to_oneliner(self.env, formatter.context, text)
def expand_macro(self, formatter, name, content): args, kw = parse_args(content) utc_dt = args and args[0] or None if not utc_dt: return system_message('IrcLogQuote: Timestamp required') d = self.date_re.match(utc_dt) if not d: return system_message('IrcLogQuote: Invalid timestamp format') offset = int(args and len(args)>1 and args[1] or 10) irclogs = IrcLogsView(self.env) logfile = irclogs._get_filename(d.group('year'), d.group('month'), d.group('day')) if (not os.path.isfile(logfile)): return system_message('IrcLogQuote: No log file for this date') iterable = irclogs._to_unicode(file(logfile)) lines = irclogs._render_lines(iterable, formatter.req.tz) filtered_lines = [line for line in lines if unicode(line['utc_dt']) >= utc_dt and line['mode'] == 'channel' and line['hidden_user'] != 'hidden_user'] add_stylesheet(formatter.req, 'irclogs/style.css') data = Chrome(self.env).populate_data(formatter.req, {'lines':filtered_lines[:offset], 'excerpt_date':d.groupdict(), 'utc_dt':utc_dt}) return Chrome(self.env).load_template('macro_quote.html') \ .generate(**data)
def expand_macro(self, formatter, name, content): req = formatter.req if not content: return system_message("A title must be provided as the first argument to the poll macro") content = filter(None, [i.strip() for i in content.replace(';', '\n').split('\n')]) if len(content) < 2: return system_message("One or more options must be provided to vote on.") title = content.pop(0) return self.render_poll(req, title, content)
def expand_macro(self, formatter, name, content): req = formatter.req # Shortcut. safe_content = False # Whether or not to disable cleaning HTML. args = [x.strip() for x in content.split(',')] if len(args) == 1: args.append(None) elif len(args) == 3: return system_message('args == %s' % args) if not args[2].startswith('fragment='): msg = ('If three arguments are given, the last one must' ' start with fragment=, but tag content was %s') return system_message(msg % content) elif len(args) != 2: return system_message('Invalid arguments "%s"'%content) # Parse out fragment name. fragment_name = None if args[-1] and args[-1].startswith('fragment='): fragment_name = args[-1][len('fragment='):] args.pop() if len(args) == 1: args.append(None) # Pull out the arguments source, dest_format = args try: source_format, source_obj = source.split(':', 1) except ValueError: # If no : is present, assume its a wiki page source_format, source_obj = 'wiki', source # Apply a default format if needed if dest_format is None: try: dest_format = self.default_formats[source_format] except KeyError: pass if source_format in ('http', 'https', 'ftp'): # Since I can't really do recursion checking, and because this # could be a source of abuse allow selectively blocking it. # RFE: Allow blacklist/whitelist patterns for URLS. <NPK> # RFE: Track page edits and prevent unauthorized users from ever entering a URL include. <NPK> if not req.perm.has_permission('INCLUDE_URL'): self.log.info('IncludeMacro: Blocking attempt by %s to include URL %s on page %s', req.authname, source, req.path_info) return '' try: urlf = urllib2.urlopen(source) out = urlf.read() except urllib2.URLError, e: return system_message('Error while retrieving file', str(e)) except TracError, e: return system_message('Error while previewing', str(e))
def expand_macro(self, formatter, name, args): """Return value of a trac.ini [section].option as plain text.""" if args.find('.') == -1: return system_message('%s: Invalid parameter: "%s"' % (name, args)) section, option = re.sub('\s+', '', args).split('.', 1) if self.config.has_option(section, option): return (self.config.get(section, option)) else: return system_message('%s: No option "%s" in section [%s]' % (name, option, section))
def expand_macro(self, formatter, macro, args): args, kw = parse_args(args) try: source = args.pop(0).strip() except NameError: return system_message('%s: Missing HTML source argument.' % macro) try: stream = Stream(HTMLParser(StringIO(source))) return (stream | TracHTMLSanitizer()).render('xhtml', encoding=None) except ParseError, e: self.env.log.warn(e) return system_message('%s: HTML parse error: %s.' % (macro, escape(e.msg)))
def expand_macro(self, formatter, name, content): db = self.env.get_db_cnx() cursor = db.cursor() try: cursor.execute(content) except Exception, e: return system_message(_("Invalid SQL"), exception_to_unicode(e))
def expand_macro(self, formatter, macro, args): # dictionary file section = macro.lower() # [abbr] option = 'file' # file = /path/to/dictionary/file dict = {} if self.config.has_option(section, option): dict = build_dict(self.config.get(section, option), self.tags) args, kw = parse_args(args) try: for key in self.keys: kw[key] = kw[key].strip() except KeyError: if key == 'title' and kw['key'] in dict: kw['title'] = dict[kw['key']][0] kw['tag'] = dict[kw['key']][1] else: return system_message('%s: Missing required keyword "%s."' % (macro, key)) # tag keyword is optional if 'tag' in kw: tag = kw['tag'].strip() if tag not in self.tags: tag = self.default else: tag = self.default return Element(tag)(kw['key'], title="%s" % kw['title'])
def expand_macro(self, formatter, name, content): env = formatter.env abs = env.abs_href.base abs = abs[:len(abs) - len(env.href.base)] f = Formatter(formatter.env, formatter.context) def convert(m): pre, target, suf = filter(None, m.groups()) out = StringIO() f.format(target, out) url = search( HREF, out.getvalue(), I).groups()[0] # Trac creates relative links, which Markdown won't touch inside # <autolinks> because they look like HTML if pre == '<' and url != target: pre += abs return pre + str(url) + suf try: from markdown import markdown return markdown(sub(LINK, convert, content)) except ImportError: msg = 'Error importing Python Markdown, install it from ' url = 'http://www.freewisdom.org/projects/python-markdown/' return system_message(tag(msg, tag.a('here', href="%s" % url), '.'))
def get_macro_descr(): for macro_provider in formatter.wiki.macro_providers: names = list(macro_provider.get_macros() or []) if name_filter and not any(name.startswith(name_filter) for name in names): continue try: name_descriptions = [ (name, macro_provider.get_macro_description(name)) for name in names] except Exception, e: yield system_message( _("Error: Can't get description for macro %(name)s", name=names[0]), e), names else: for descr, pairs in groupby(name_descriptions, key=lambda p: p[1]): if descr: if isinstance(descr, (tuple, list)): descr = dgettext(descr[0], to_unicode(descr[1])) \ if descr[1] else '' else: descr = to_unicode(descr) or '' if content == '*': descr = format_to_oneliner( self.env, formatter.context, descr, shorten=True) else: descr = format_to_html( self.env, formatter.context, descr) yield descr, [name for name, descr in pairs]
def expand_macro(self, formatter, name, text, args): """Execute the macro """ self.env.log.debug("TestPlanMacro.expand_macro()") parser = TestcaseParser(self.env) self.env.log.debug( "name: %s", str(name)) self.env.log.debug( "args: %s", str(args)) self.env.log.debug( "text: %s", str(text)) errors = list() # Parse config and testcases attrs, tcnames_and_users = TestPlanMacroParser(self.env).parse_config(text) self.log.debug("attrs: %s" % attrs) # syntax check the testcases for tcname, users in tcnames_and_users: try: parser.parseTestcase(tcname) except TracError, e: # FIXME: commented because auf genshi unicode error - raised # instead, see other FIXME below! #""" error_message = safe_unicode("Parsing error in Testcase %s:" % tcname) errors.append(system_message(error_message, text= safe_unicode(e.message)))
def expand_macro(self, formatter, name, content): # Example: [[ASA(42)]] args, kw = parse_args(content) args = [arg.strip() for arg in args] if not args or not args[0].isdigit(): raise TracError('Custom artifact id not specified') args, kw = parse_args(content) if not args or not args[0].isdigit(): raise TracError('Custom artifact id not specified') artifact_id = int(args[0]) dbp = DBPool(self.env, InstancePool()) try: dbp.load_artifact(id=artifact_id) except ValueError: return system_message("Custom Artifact not found", "No custom artifact was found for id '{0}'.".format(artifact_id)) artifact = dbp.pool.get_item(id=artifact_id) artifact_url = formatter.req.href.customartifacts('artifact/{0}'.format(artifact.get_id())) res = Core._get_resource(artifact) if not artifact in (Entity, Instance, None) and not type(artifact)==unicode else None spec_name, spec_url, values = _get_artifact_details(artifact, formatter.req) tpl='view_artifact_dialog.html' data = { 'context': Context.from_request(formatter.req, res), 'spec_name': spec_name, 'spec_url': spec_url, 'artifact': artifact, 'artifact_url': artifact_url, 'artifacts_values': values, } return Chrome(self.env).render_template(formatter.req, tpl, data, None, fragment=True)
def expand_macro(self, formatter, name, content, args = None): if args is None: args = {} if name[-4:] in ('_svg', '_png'): name, type = name.split('_') else: type = (args.get('type') or self._default_type).lower() if type not in ('svg', 'png'): return system_message("Invalid type(%s). Type must be 'svg' or 'png'" % type) font = self._font # nonascii unicode can't be passed to hashlib. id = make_hash('%s,%s,%s,%r' % (name, type, font, content)).hexdigest() ## Create img tag. params = { "src": formatter.req.href("%s/%s.%s" % (name, id, type)) } for key, value in args.iteritems(): if key != "type": params[key] = value output = tag.img(**params) ## Generate image and cache it. def generate_image(): infile = mktemp(prefix='%s-' % name) outfile = mktemp(prefix='%s-' % name) try: try: f = codecs.open(infile, 'w', 'utf8') try: f.write(content) finally: f.close() cmd = [name, '-a', '-T', type, '-o', outfile, infile] if font: cmd.extend(['-f', font]) self.env.log.debug('(%s) command: %r' % (name, cmd)) try: proc = Popen(cmd, stderr=PIPE) stderr_value = proc.communicate()[1] except Exception, e: self.env.log.error('(%s) %r' % (name, e)) raise ImageGenerationError("Failed to generate diagram. (%s is not found.)" % name) if proc.returncode != 0 or not os.path.isfile(outfile): self.env.log.error('(%s) %s' % (name, stderr_value)) raise ImageGenerationError("Failed to generate diagram. (rc=%d)" % proc.returncode) f = open(outfile, 'rb') try: data = f.read() finally: f.close() return data except ImageGenerationError: raise except Exception, e: self.env.log.error('(%s) %r' % (name, e)) raise ImageGenerationError("Failed to generate diagram.")
def expand_macro(self, formatter, name, text, args): """Execute the macro """ parser = TestcaseParser(self.env) out = StringIO.StringIO() f = Formatter(self.env, formatter.context) try: parser.parseTestcase(text=text) except Exception, e: out.write(system_message("Parsing error", text=e))
def get_macro_descr(): for macro_provider in wiki.macro_providers: for macro_name in macro_provider.get_macros(): if content and macro_name != content: continue try: descr = macro_provider.get_macro_description(macro_name) descr = wiki_to_html(descr or "", self.env, req) except Exception, e: descr = Markup(system_message("Error: Can't get description for macro %s" % macro_name, e)) yield (macro_name, descr)
def expand_macro(self, formatter, name, content): _, kw = parse_args(content) channel_name = kw.get('channel') utc_dt = kw.get('datetime') if not utc_dt: return system_message('IrcLogQuote: arguments required '\ '(channel=channel, datetime=timestamp(UTCYYYY-MM-DDTHH:MM:SS), '\ 'offset=seonds)') d = self.date_re.match(utc_dt.strip()) if not d: return system_message('IrcLogQuote: Invalid timestamp format') offset = int(kw.get('offset', 10)) irclogs = IrcLogsView(self.env) ch_mgr = IRCChannelManager(self.env) start = datetime(*strptime(utc_dt, self.date_format)[:6]) start = UTC.localize(start) start = ch_mgr.to_user_tz(formatter.req, start) end = start + timedelta(seconds=offset) channel = ch_mgr.channel(channel_name) formatter.req.perm.assert_permission(channel.perm()) lines = channel.events_in_range(start, end) lines = filter(lambda x: not x.get('hidden'), map(irclogs._map_lines, lines)) rows = map(irclogs._render_line, lines) add_stylesheet(formatter.req, 'irclogs/css/irclogs.css') data = Chrome(self.env).populate_data( formatter.req, { 'channel': channel_name, 'lines': lines, 'year': '%04d'%(start.year), 'month': '%02d'%(start.month), 'day': '%02d'%(start.day), 'time': start.strftime("%H:%M:%S"), 'rows': rows } ) return Chrome(self.env).load_template('macro_quote.html') \ .generate(**data)
def expand_macro(self, formatter, name, content): args = [x.strip() for x in content.split(",")] if len(args) == 1: args.append(None) elif len(args) != 2: return system_message('Invalid arguments "%s"' % content) # Pull out the arguments source, dest_format = args try: source_format, source_obj = source.split(":", 1) except ValueError: # If no : is present, assume its a wiki page source_format, source_obj = "wiki", source # Apply a default format if needed if dest_format is None: try: dest_format = self.default_formats[source_format] except KeyError: pass if source_format in ("http", "https", "ftp"): # Since I can't really do recursion checking, and because this # could be a source of abuse allow selectively blocking it. # RFE: Allow blacklist/whitelist patterns for URLS. <NPK> # RFE: Track page edits and prevent unauthorized users from ever entering a URL include. <NPK> if not formatter.perm.has_permission("INCLUDE_URL"): self.log.info( "IncludeMacro: Blocking attempt by %s to include URL %s on page %s", formatter.req.authname, source, formatter.req.path_info, ) return "" try: urlf = urllib2.urlopen(source) out = urlf.read() except urllib2.URLError, e: return system_message("Error while retrieving file", str(e)) except TracError, e: return system_message("Error while previewing", str(e))
def _render_page_outline(self, formatter, ol, page_resource, active, params): page = params.get('root', '') + page_resource.id page_text, page_exists = self.get_page_text(formatter, page_resource) if page_exists: ctx = formatter.context(page_resource) fmt = OutlineFormatter(self.env, ctx) fmt.format(page_text, NullOut()) outline_tree(self.env, ol, fmt.outline, ctx, active and page_resource.id == formatter.context.resource.id, params['min_depth'], params['max_depth']) else: ol.append(system_message('Error: Page %s does not exist' % page_resource.id))
def get_macro_descr(): for macro_provider in formatter.wiki.macro_providers: for macro_name in macro_provider.get_macros(): if content and macro_name != content: continue try: descr = macro_provider.get_macro_description(macro_name) descr = format_to_html(self.env, formatter.context, to_unicode(descr) or '') except Exception, e: descr = system_message(_("Error: Can't get description " "for macro %(name)s", name=macro_name), e) yield (macro_name, descr)
def expand_macro(self, formatter, name, args): if args is None: return system_message("No UML text defined!") if not self.plantuml_jar: return system_message("plantuml_jar option not defined in .ini") if not os.path.exists(self.plantuml_jar): return system_message("plantuml.jar not found: %s" % self.plantuml_jar) source = args.encode('utf-8').strip() try: graphs = pickle.loads(base64.b64decode(formatter.req.session.get('plantuml',""))) except: graphs = {} img_id = hashlib.sha1(source).hexdigest() #Clean old images for key, graph in graphs.items(): if (datetime.now() - graph[1]).seconds > 3600: del graphs[key] if not graphs.has_key(img_id): cmd = "java -jar -Djava.awt.headless=true \"%s\" -charset UTF-8 -pipe" % (self.plantuml_jar) p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE) (stdout, stderr) = p.communicate(input=source) if p.returncode != 0: return system_message("Error running plantuml: %s" % stderr) graphs[img_id] = (stdout, datetime.now()) formatter.req.session['plantuml'] = base64.b64encode(pickle.dumps(graphs)) out = "{{{\n#!html\n<img src='%s' alt='PlantUML Diagram' />\n}}}\n" % formatter.href("plantuml", id=img_id) return wiki_to_html(out, self.env, formatter.req)
def process_request(self, req): req.perm.assert_permission('DOXYGEN_VIEW') # Get request arguments path = req.args.get('path') action = req.args.get('action') # Get config variables index = self.config.get('doxygen', 'index', 'main.html') wiki_index = self.config.get('doxygen', 'wiki_index', None) # Redirect search requests. if action == 'search': req.redirect('%s?q=%s&doxygen=on' % (self.env.href.search(), req.args.get('query'))) # Retrun apropriate content to type or search request elif action == 'index': if wiki_index: # Get access to database db = self.env.get_db_cnx() cursor = db.cursor() # Get wiki index sql = "SELECT text FROM wiki WHERE name = %s" cursor.execute(sql, (wiki_index,)) text = Markup(system_message('Error', 'Wiki page %s does not' \ ' exists' % (wiki_index))) for row in cursor: text = wiki_to_html(row[0], self.env, req) # Display wiki index page req.hdf['doxygen.text'] = text return 'doxygen.cs', 'text/html' else: add_stylesheet(req, 'doxygen/css/doxygen.css') req.hdf['doxygen.path'] = path + '/' + index return 'doxygen.cs', 'text/html' elif action == 'file': type = mimetypes.guess_type(path)[0] if type == 'text/html': add_stylesheet(req, 'doxygen/css/doxygen.css') req.hdf['doxygen.path'] = path return 'doxygen.cs', 'text/html' else: req.send_file(path, type)
def expand_macro(self, formatter, name, text, args): if not text: raw_actions = self.config.options('ticket-workflow') else: if args is None: text = '\n'.join([line.lstrip() for line in text.split(';')]) if '[ticket-workflow]' not in text: text = '[ticket-workflow]\n' + text parser = RawConfigParser() try: parser.readfp(StringIO(text)) except ParsingError as e: return system_message(_("Error parsing workflow."), unicode(e)) raw_actions = list(parser.items('ticket-workflow')) actions = parse_workflow_config(raw_actions) states = list(set( [state for action in actions.itervalues() for state in action['oldstates']] + [action['newstate'] for action in actions.itervalues()])) action_labels = [attrs['label'] for attrs in actions.values()] action_names = actions.keys() edges = [] for name, action in actions.items(): new_index = states.index(action['newstate']) name_index = action_names.index(name) for old_state in action['oldstates']: old_index = states.index(old_state) edges.append((old_index, new_index, name_index)) args = args or {} width = args.get('width', 800) height = args.get('height', 600) graph = {'nodes': states, 'actions': action_labels, 'edges': edges, 'width': width, 'height': height} graph_id = '%012x' % id(graph) req = formatter.req add_script(req, 'common/js/excanvas.js', ie_if='IE') add_script(req, 'common/js/workflow_graph.js') add_script_data(req, {'graph_%s' % graph_id: graph}) return tag( tag.div('', class_='trac-workflow-graph trac-noscript', id='trac-workflow-graph-%s' % graph_id, style="display:inline-block;width:%spx;height:%spx" % (width, height)), tag.noscript( tag.div(_("Enable JavaScript to display the workflow graph."), class_='system-message')))
def _render_title_index(self, formatter, ol, page_resource, active, show_title): page_text, page_exists = self.get_page_text(formatter, page_resource) if not page_exists: ol.append(system_message('Error: No page matching %s found' % page_resource.id)) return ctx = formatter.context(page_resource) fmt = OutlineFormatter(self.env, ctx) fmt.format(page_text, NullOut()) title = '' if show_title and fmt.outline: title = ': ' + fmt.outline[0][2] ol.append((tag.li(tag.a(page_resource.id, href=get_resource_url(self.env, page_resource, ctx.href)), Markup(title), class_= active and 'active' or None)))
def expand_macro(self, formatter, name, content): env = formatter.env abs = env.abs_href.base abs = abs[:len(abs) - len(env.href.base)] f = Formatter(formatter.env, formatter.context) def convert_links(m): pre, target, suf = filter(None, m.groups()) out = StringIO() f.format(target, out) url = re.search(HREF, out.getvalue()).groups()[0] # Trac creates relative links, which Markdown won't touch inside # <autolinks> because they look like HTML if pre == '<' and url != target: pre += abs return pre + str(url) + suf def emojify(html): pattern = ":([a-z0-9\\+\\-_]+):" link = "<img\ alt=\"\\1\"\ title=\":\\1:\"\ height=\"20\"\ style=\"vertical-align:middle\"\ width=\"20\"\ src=\"/chrome/markdown/emoji/\\1.png\" />" emojify_html = re.sub(pattern, link, html) return emojify_html try: # Import & convert import markdown2 # autolink http:// n stuff autolinked_content = re.sub(LINK, convert_links, content) # convert to markdown html = markdown2.markdown(autolinked_content, extras=MD_EXTRAS) # substitute emojis emojified_html = emojify(html) return emojified_html except ImportError: # no markdown2 package found? msg = 'Error importing python-markdown2, install it from ' url = 'https://github.com/trentm/python-markdown2' return system_message(tag(msg, tag.a('here', href="%s" % url), '.'))
def _format_link(self, formatter, ns, target, label): m = self.date_re.match(target) if not m: return system_message('Invalid IRC Log Link: ' 'Must be of the format channel-UTCYYYY-MM-DDTHH:MM:SS %s') if not m.group('datetime'): return html.a(label, title=label, href=formatter.href.irclogs( m.group('channel'))) else: ch_mgr = IRCChannelManager(self.env) t = strptime(m.group('datetime'), self.date_format) dt = UTC.localize(datetime(*t[:6])) dt = ch_mgr.to_user_tz(formatter.req, dt) timestr = dt.strftime(self.time_format) return html.a(label, title=label, href=formatter.href.irclogs( m.group('channel'), '%02d'%dt.year, '%02d'%dt.month, '%02d'%dt.day,) + '#%s'%timestr)
def expand_macro(self, formatter, name, content): if not self.plantuml_jar: return system_message(_("Installation error: plantuml_jar option not defined in trac.ini")) if not os.path.exists(self.plantuml_jar): return system_message(_("Installation error: plantuml.jar not found at '%s'") % self.plantuml_jar) # Trac 0.12 supports expand_macro(self, formatter, name, content, args) # which allows us to readily differentiate between a WikiProcess and WikiMacro # call. To support Trac 0.11, some additional work is required. try: args = formatter.code_processor.args except AttributeError: args = None path = None if not 'path' in args: #Could be WikiProcessor or WikiMacro call if content.strip().startswith("@startuml"): markup = content path = None else: path = content if not path: return system_message(_("Path not specified")) elif args: #WikiProcessor with args path = args.get('path') if not path: return system_message(_("Path not specified")) if path: markup, exists = self._read_source_from_repos(formatter, path) if not exists: return system_message(_("File not found in repository: " + path)) else: if not content: return system_message(_("No UML text defined")) markup = content.encode('utf-8').strip() img_id = hashlib.sha1(markup).hexdigest() if not self._is_img_existing(img_id): cmd = '%s -jar -Djava.awt.headless=true "%s" -charset UTF-8 -pipe' % (self.java_bin, self.plantuml_jar) p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE) (img_data, stderr) = p.communicate(input=markup) if p.returncode != 0: return system_message(_("Error running plantuml: '%s'") % stderr) self._write_img_to_file(img_id, img_data) link = formatter.href('plantuml', id=img_id) return tag.img(src=link)
def expand_macro(self, formatter, name, content): args, kw = parse_args(content) poll_frequency = args and args[0] or 60 count = args and args[1] or 10 if not (0==len(args) or 2==len(args)): return system_message('Incorrect arguments: ' 'Must be of the format (poll frequency, lines to display)') add_stylesheet(formatter.req, 'irclogs/style.css') add_script(formatter.req, 'irclogs/jquery.timer.js') data = Chrome(self.env).populate_data(formatter.req, {'poll_frequency':int(poll_frequency)*1000, 'count':count}) return Chrome(self.env).load_template('macro_live.html') \ .generate(**data)
def expand_macro(self, formatter, name, content): req = formatter.req args, options = parse_args(content, strict=False) if len(args) != 1: return system_message("TotalField macro error", "request and field_name are required.") # we have to add custom field to query so that field is added to # resulting ticket list field_name = args[0] options[field_name + "!"] = None tickets = execute_query(self.env, req, options) sum = 0.0 for t in tickets: try: sum += float(t[field_name]) except: pass return "%g" % round(sum, 2)
req.authname, source, req.path_info) return '' try: urlf = urllib2.urlopen(source) out = urlf.read() except urllib2.URLError, e: return system_message('Error while retrieving file', str(e)) except TracError, e: return system_message('Error while previewing', str(e)) elif source_format == 'wiki': # XXX: Check for recursion in page includes. <NPK> if not req.perm.has_permission('WIKI_VIEW'): return '' page = WikiPage(self.env, source_obj) if not page.exists: return system_message('Wiki page %s does not exist' % source_obj) out = page.text elif source_format == 'source': if not req.perm.has_permission('FILE_VIEW'): return '' repo = self.env.get_repository(req.authname) node = repo.get_node(source_obj) out = node.get_content().read() if dest_format is None: dest_format = node.content_type or get_mimetype( source_obj, out) # RFE: Add ticket: and comment: sources. <NPK> # RFE: Add attachment: source. <NPK> else: return system_message('Unsupported include source %s' % source)
except ImageGenerationError: raise except Exception, e: self.env.log.error('(%s) %r' % (name, e)) raise ImageGenerationError("Failed to generate diagram.") finally: for path in (infile, outfile): try: os.remove(path) except: pass try: self.cache.cache(id, type, generate_image) except ImageGenerationError, e: return system_message(str(e)) return output def match_request(self, req): return re.match(r'/([a-z]+diag)/.+$', req.path_info) def process_request(self, req): m = re.match(r'/([a-z]+diag)/(.+)\.(png|svg)$', req.path_info) if not m: return "" id, type = m.group(2), m.group(3) data = self.cache.get(id, type) if data: req.send(data, self.content_types[type], status=200) else:
elif page_name.startswith(('./', '../')) or \ page_name in ('.', '..'): try: # Trac 1.2 page_name = ws.resolve_relative_name( page_name, referrer) except AttributeError: page_name = _resolve_relative_name(page_name, referrer) else: page_name = _resolve_scoped_name(ws, page_name, referrer) try: page = WikiPage(self.env, page_name, page_version) except (TypeError, ValueError): # Trac < 1.2 (Trac:#11544) msg = _('"%(version)s" is not a valid wiki page version.', version=page_version) return system_message(msg) except TracError, e: return system_message(e) if 'WIKI_VIEW' not in formatter.perm(page.resource): return '' if not page.exists: if page_version: return system_message( 'No version "%s" for wiki page "%s"' % (page_version, page_name)) else: return system_message('Wiki page "%s" does not exist' % page_name) out = page.text if source_section: out = self._extract_section(out, source_section)
return '' try: urlf = urllib2.urlopen(source) out = urlf.read() except urllib2.URLError, e: return system_message('Error while retrieving file', str(e)) except TracError, e: return system_message('Error while previewing', str(e)) ctxt = Context.from_request(req) elif source_format == 'wiki': # XXX: Check for recursion in page includes. <NPK> if not req.perm.has_permission('WIKI_VIEW'): return '' page = WikiPage(self.env, source_obj) if not page.exists: return system_message('Wiki page %s does not exist'%source_obj) out = page.text ctxt = Context.from_request(req, 'wiki', source_obj) elif source_format == 'source': if not req.perm.has_permission('FILE_VIEW'): return '' repo = self.env.get_repository(authname=req.authname) node = repo.get_node(source_obj) out = node.get_content().read() if dest_format is None: dest_format = node.content_type or get_mimetype(source_obj, out) ctxt = Context.from_request(req, 'source', source_obj) # RFE: Add ticket: and comment: sources. <NPK> # RFE: Add attachment: source. <NPK> else: return system_message('Unsupported include source %s'%source)
def render_macro(self, req, name, content): args,kwargs = parse_args(content) if len(args) == 0: ul = tag.ul(class_="mailinglistlist") for mailinglist in Mailinglist.select(self.env): if "MAILINGLIST_VIEW" in req.perm(mailinglist.resource): ul.append(tag.li(tag.a(mailinglist.name, href=get_resource_url(self.env, mailinglist.resource, req.href)))) return ul if kwargs.has_key('limit'): limit = int(kwargs['limit']) elif len(args) > 1: limit = int(args[1]) else: limit = 10 resource = Resource("mailinglist",args[0]) instance = MailinglistSystem(self.env).get_instance_for_resource(resource) if isinstance(instance, Mailinglist): if not req.perm(instance.resource).has_permission('MAILINGLIST_VIEW'): return system_message("Permission denied viewing mailinglist: %s" % instance.name) ul = tag.ul() for message in instance.messages(limit=limit, insubject=kwargs.get('insubject', None),desc=True): ul.append(tag.li( tag.a(tag.span(message.subject, class_="messagesubject"), href=get_resource_url(self.env, message.resource, req.href)), " (", dateinfo(message.date), ")", )) ul.append(tag.li(tag.a("(%d messages...)" % instance.count_messages(insubject=kwargs.get('insubject', None)), href=get_resource_url(self.env, instance.resource, req.href)))) return tag.div("Mailinglist: ", tag.a(instance.name, href=get_resource_url(self.env, instance.resource, req.href)), ul, class_="mailinglistfeed") elif isinstance(instance, MailinglistMessage): if not req.perm(instance.resource).has_permission('MAILINGLIST_VIEW'): return system_message("Permission denied viewing mail.") else: limit = None text = wrap_and_quote(instance.body, 78)[0] if limit: text = "\n".join(text.split("\n")[0:limit]) textelement = tag.pre(text) + tag.a(tag.pre("(More...)"), href=get_resource_url(self.env, instance.resource, req.href)) else: textelement = tag.pre(text) return tag.div( tag.div("Mailinglist: ", tag.a(instance.conversation.mailinglist.name, href=get_resource_url(self.env, instance.conversation.mailinglist.resource, req.href))), tag.div("Subject: ", tag.a(instance.subject, href=get_resource_url(self.env, instance.resource, req.href))), tag.div("From: ", tag.a(instance.from_name, href="mailto:%s" % instance.from_email)), tag.div("To: ", instance.to_header), tag.div("Date: ", dateinfo(instance.date)), tag.div(textelement), class_="mailinglistmessage") return system_message("Unknown Mailinglist: %s" % content)
def expand_macro(self, formatter, name, content, args=None): if args is None: args = {} if name[-4:] in ('_svg', '_png'): name, type = name.split('_') else: type = (args.get('type') or self.env.config.get( 'blockdiag', 'default_type', 'png')).lower() if type not in ('svg', 'png'): return system_message( "Invalid type(%s). Type must be 'svg' or 'png'" % type) font = self.env.config.get('blockdiag', 'font', '') # nonascii unicode can't be passed to hashlib. id = make_hash('%s,%s,%s,%r' % (name, type, font, content)).hexdigest() ## Create img tag. params = {"src": formatter.req.href("%s/%s.%s" % (name, id, type))} for key, value in args.iteritems(): if key != "type": params[key] = value output = tag.img(**params) ## Generate image and cache it. def generate_image(): infile = mktemp(prefix='%s-' % name) outfile = mktemp(prefix='%s-' % name) try: try: f = codecs.open(infile, 'w', 'utf8') try: f.write(content) finally: f.close() cmd = [name, '-a', '-T', type, '-o', outfile, infile] if font: cmd.extend(['-f', font]) self.env.log.debug('(%s) command: %r' % (name, cmd)) try: proc = Popen(cmd, stderr=PIPE) stderr_value = proc.communicate()[1] except Exception, e: self.env.log.error('(%s) %r' % (name, e)) raise ImageGenerationError( "Failed to generate diagram. (%s is not found.)" % name) if proc.returncode != 0 or not os.path.isfile(outfile): self.env.log.error('(%s) %s' % (name, stderr_value)) raise ImageGenerationError( "Failed to generate diagram. (rc=%d)" % proc.returncode) f = open(outfile, 'rb') try: data = f.read() finally: f.close() return data except ImageGenerationError: raise except Exception, e: self.env.log.error('(%s) %r' % (name, e)) raise ImageGenerationError("Failed to generate diagram.")
def expand_macro(self, formatter, name, content, realms=[]): """Evaluate macro call and render results. Calls from web-UI come with pre-processed realm selection. """ env = self.env req = formatter.req tag_system = TagSystem(env) all_realms = tag_system.get_taggable_realms() if not all_realms: # Tag providers are required, no result without at least one. return '' args, kw = parse_args(content) query = args and args[0].strip() or None if not realms: # Check macro arguments for realms (typical wiki macro call). realms = 'realm' in kw and kw['realm'].split('|') or [] if query: # Add realms from query expression. realms.extend(query_realms(query, all_realms)) # Remove redundant realm selection for performance. if set(realms) == all_realms: query = re.sub('(^|\W)realm:\S+(\W|$)', ' ', query).strip() if name == 'TagCloud': # Set implicit 'all tagged realms' as default. if not realms: realms = all_realms if query: all_tags = collections.Counter() # Require per resource query including view permission checks. for resource, tags in tag_system.query(req, query): all_tags.update(tags) else: # Allow faster per tag query, side steps permission checks. all_tags = tag_system.get_all_tags(req, realms=realms) mincount = 'mincount' in kw and kw['mincount'] or None return self.render_cloud(req, all_tags, caseless_sort=self.caseless_sort, mincount=mincount, realms=realms) elif name == 'ListTagged': if content and _OBSOLETE_ARGS_RE.search(content): data = {'warning': 'obsolete_args'} else: data = {'warning': None} context = formatter.context # Use TagsQuery arguments (most likely wiki macro calls). cols = 'cols' in kw and kw['cols'] or self.default_cols format = 'format' in kw and kw['format'] or self.default_format if not realms: # Apply ListTagged defaults to macro call w/o realm. realms = list(set(all_realms) - set(self.exclude_realms)) if not realms: return '' query = '(%s) (%s)' % (query or '', ' or '.join( ['realm:%s' % (r) for r in realms])) query_result = tag_system.query(req, query) excludes = [ exc.strip() for exc in kw.get('exclude', '').split(':') if exc.strip() ] if excludes and query_result: filtered_result = [(resource, tags) for resource, tags in query_result if not any( fnmatchcase(resource.id, exc) for exc in excludes)] query_result = filtered_result if not query_result: return '' def _link(resource): if resource.realm == 'tag': # Keep realm selection in tag links. return builder.a(resource.id, href=self.get_href(req, realms, tag=resource)) elif resource.realm == 'ticket': # Return resource link including ticket status dependend # class to allow for common Trac ticket link style. ticket = Ticket(env, resource.id) return builder.a('#%s' % ticket.id, class_=ticket['status'], href=formatter.href.ticket(ticket.id), title=shorten_line(ticket['summary'])) return render_resource_link(env, context, resource, 'compact') if format == 'table': cols = [ col for col in cols.split('|') if col in self.supported_cols ] # Use available translations from Trac core. try: labels = TicketSystem(env).get_ticket_field_labels() labels['id'] = _('Id') except AttributeError: # Trac 0.11 neither has the attribute nor uses i18n. labels = {'id': 'Id', 'description': 'Description'} labels['realm'] = _('Realm') labels['tags'] = _('Tags') headers = [{'label': labels.get(col)} for col in cols] data.update({'cols': cols, 'headers': headers}) try: results = sorted( query_result, key=lambda r: embedded_numbers(to_unicode(r[0].id))) except (InvalidQuery, InvalidTagRealm) as e: return system_message(_("ListTagged macro error"), e) results = self._paginate(req, results, realms) rows = [] for resource, tags in results: desc = tag_system.describe_tagged_resource(req, resource) tags = sorted(tags) wiki_desc = format_to_oneliner(env, context, desc) if tags: rendered_tags = [ _link(Resource('tag', tag)) for tag in tags ] if 'oldlist' == format: resource_link = _link(resource) else: resource_link = builder.a(wiki_desc, href=get_resource_url( env, resource, context.href)) if 'table' == format: cells = [] for col in cols: if col == 'id': cells.append(_link(resource)) # Don't duplicate links to resource in both. elif col == 'description' and 'id' in cols: cells.append(wiki_desc) elif col == 'description': cells.append(resource_link) elif col == 'realm': cells.append(resource.realm) elif col == 'tags': cells.append( builder([(tag, ' ') for tag in rendered_tags])) rows.append({'cells': cells}) continue rows.append({ 'desc': wiki_desc, 'rendered_tags': None, 'resource_link': _link(resource) }) data.update({ 'format': format, 'paginator': results, 'results': rows, 'tags_url': req.href('tags') }) # Work around a bug in trac/templates/layout.html, that causes a # TypeError for the wiki macro call, if we use add_link() alone. add_stylesheet(req, 'common/css/search.css') return Chrome(env).render_template(req, 'listtagged_results.html', data, { 'fragment': True, 'domain': 'tractags' })
def expand_macro(self, formatter, name, content): args = None if content: content = stripws(content) # parse arguments # we expect the 1st argument to be a filename (filespec) args = [stripws(arg) for arg in self._split_args_re.split(content)[1::2]] if not args: return '' # strip unicode white-spaces and ZWSPs are copied from attachments # section (#10668) filespec = args.pop(0) # style information attr = {} style = {} link = '' # helper for the special case `source:` # from trac.versioncontrol.web_ui import BrowserModule # FIXME: somehow use ResourceSystem.get_known_realms() # ... or directly trac.wiki.extract_link try: browser_links = [res[0] for res in BrowserModule(self.env).get_link_resolvers()] except Exception: browser_links = [] while args: arg = args.pop(0) if self._size_re.match(arg): # 'width' keyword attr['width'] = arg elif arg == 'nolink': link = None elif arg.startswith('link='): val = arg.split('=', 1)[1] elt = extract_link(self.env, formatter.context, val.strip()) elt = find_element(elt, 'href') link = None if elt is not None: link = elt.attrib.get('href') elif arg in ('left', 'right'): style['float'] = arg elif arg == 'center': style['margin-left'] = style['margin-right'] = 'auto' style['display'] = 'block' style.pop('margin', '') elif arg in ('top', 'bottom', 'middle'): style['vertical-align'] = arg else: match = self._attr_re.match(arg) if match: key, val = match.groups() if (key == 'align' and val in ('left', 'right', 'center')) or \ (key == 'valign' and val in ('top', 'middle', 'bottom')): args.append(val) elif key in ('margin-top', 'margin-bottom'): style[key] = ' %dpx' % _arg_as_int(val, key, min=1) elif key in ('margin', 'margin-left', 'margin-right') \ and 'display' not in style: style[key] = ' %dpx' % _arg_as_int(val, key, min=1) elif key == 'border': style['border'] = ' %dpx solid' % _arg_as_int(val, key) else: m = self._quoted_re.search(val) # unquote "..." and '...' if m: val = m.group(1) attr[str(key)] = val # will be used as a __call__ kwd if self._quoted_re.match(filespec): filespec = filespec.strip('\'"') # parse filespec argument to get realm and id if contained. parts = [i.strip('\'"') for i in self._split_filespec_re.split(filespec)[1::2]] realm = parts[0] if parts else None url = raw_url = desc = None attachment = None interwikimap = InterWikiMap(self.env) if realm in ('http', 'https', 'ftp', 'data'): # absolute raw_url = url = filespec desc = url.rsplit('?')[0] elif realm in interwikimap: url, desc = interwikimap.url(realm, ':'.join(parts[1:])) raw_url = url elif filespec.startswith('//'): # server-relative raw_url = url = filespec[1:] desc = url.rsplit('?')[0] elif filespec.startswith('/'): # project-relative params = '' if '?' in filespec: filespec, params = filespec.rsplit('?', 1) url = formatter.href(filespec) if params: url += '?' + params raw_url, desc = url, filespec elif len(parts) == 3: # realm:id:attachment-filename # # or intertrac:realm:id realm, id, filename = parts intertrac_target = "%s:%s" % (id, filename) it = formatter.get_intertrac_url(realm, intertrac_target) if it: url, desc = it raw_url = url + unicode_quote('?format=raw') else: attachment = Resource(realm, id).child('attachment', filename) elif len(parts) == 2: realm, filename = parts if realm in browser_links: # source:path # TODO: use context here as well rev = None if '@' in filename: filename, rev = filename.rsplit('@', 1) url = formatter.href.browser(filename, rev=rev) raw_url = formatter.href.browser(filename, rev=rev, format='raw') desc = filespec else: # #ticket:attachment or WikiPage:attachment # FIXME: do something generic about shorthand forms... realm = None id, filename = parts if id and id[0] == '#': realm = 'ticket' id = id[1:] elif id == 'htdocs': raw_url = url = formatter.href.chrome('site', filename) desc = os.path.basename(filename) elif id == 'shared': raw_url = url = formatter.href.chrome('shared', filename) desc = os.path.basename(filename) else: realm = 'wiki' if realm: attachment = Resource(realm, id).child('attachment', filename) elif len(parts) == 1: # it's an attachment of the current resource attachment = formatter.resource.child('attachment', filespec) else: return system_message(_("No filespec given")) if attachment: try: desc = get_resource_summary(self.env, attachment) except ResourceNotFound: link = None raw_url = chrome_resource_path(formatter.context.req, 'common/attachment.png') desc = _('No image "%(id)s" attached to %(parent)s', id=attachment.id, parent=get_resource_name(self.env, attachment.parent)) else: if 'ATTACHMENT_VIEW' in formatter.perm(attachment): url = get_resource_url(self.env, attachment, formatter.href) raw_url = get_resource_url(self.env, attachment, formatter.href, format='raw') for key in ('title', 'alt'): if desc and key not in attr: attr[key] = desc if style: attr['style'] = '; '.join('%s:%s' % (k, escape(v)) for k, v in style.iteritems()) if not WikiSystem(self.env).is_safe_origin(raw_url, formatter.context.req): attr['crossorigin'] = 'anonymous' # avoid password prompt result = tag.img(src=raw_url, **attr) if link is not None: result = tag.a(result, href=link or url, style='padding:0; border:none') return result
class PlantUmlMacro(WikiMacroBase): """ A wiki processor that renders PlantUML diagrams in wiki text. Example: {{{ {{{ #!PlantUML @startuml Alice -> Bob: Authentication Reque st Bob --> Alice: Authentication Response Alice -> Bob: Another authentication Request Alice <-- Bob: another authentication Response @enduml }}} }}} Results in: {{{ #!PlantUML @startuml Alice -> Bob: Authentication Request Bob --> Alice: Authentication Response Alice -> Bob: Another authentication Request Alice <-- Bob: another authentication Response @enduml }}} """ implements(IRequestHandler) plantuml_jar = Option( 'plantuml', 'plantuml_jar', '', """Path to PlantUML jar file. The jar file can be downloaded from the [http://plantuml.sourceforge.net/download.html PlantUML] site.""") java_bin = Option( 'plantuml', 'java_bin', 'java', """Path to the Java binary file. The default is `java`, which and assumes that the Java binary is on the search path.""") def __init__(self): self.abs_img_dir = os.path.join(os.path.abspath(self.env.path), img_dir) if not os.path.isdir(self.abs_img_dir): os.makedirs(self.abs_img_dir) def expand_macro(self, formatter, name, content): if not self.plantuml_jar: return system_message( _("Installation error: plantuml_jar option not defined in trac.ini" )) if not os.path.exists(self.plantuml_jar): return system_message( _("Installation error: plantuml.jar not found at '%s'") % self.plantuml_jar) # Trac 0.12 supports expand_macro(self, formatter, name, content, args) # which allows us to readily differentiate between a WikiProcess and WikiMacro # call. To support Trac 0.11, some additional work is required. try: args = formatter.code_processor.args except AttributeError: args = None path = None if not 'path' in args: #Could be WikiProcessor or WikiMacro call if content.strip().startswith("@startuml"): markup = content path = None else: path = content if not path: return system_message(_("Path not specified")) elif args: #WikiProcessor with args path = args.get('path') if not path: return system_message(_("Path not specified")) if path: markup, exists = self._read_source_from_repos(formatter, path) if not exists: return system_message( _("File not found in repository: " + path)) else: if not content: return system_message(_("No UML text defined")) markup = content.encode('utf-8').strip() img_id = hashlib.sha1(markup).hexdigest() if not self._is_img_existing(img_id): cmd = '%s -jar -Djava.awt.headless=true "%s" -charset UTF-8 -pipe' % ( self.java_bin, self.plantuml_jar) p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE) (img_data, stderr) = p.communicate(input=markup) if p.returncode != 0: return system_message( _("Error running plantuml: '%s'") % stderr) self._write_img_to_file(img_id, img_data) link = formatter.href('plantuml', id=img_id) return tag.img(src=link) def get_macros(self): yield 'plantuml' #WikiProcessor syntax yield 'PlantUml' #WikiMacros syntax yield 'PlantUML' #deprecated, retained for backward compatibility # IRequestHandler def match_request(self, req): return re.match(r'/plantuml?$', req.path_info) def process_request(self, req): img_id = req.args.get('id') img_data = self._read_img_from_file(img_id) req.send(img_data, 'image/png', status=200) return "" # Internal def _get_img_path(self, img_id): img_path = os.path.join(self.abs_img_dir, img_id) img_path += '.png' return img_path def _is_img_existing(self, img_id): img_path = self._get_img_path(img_id) return os.path.isfile(img_path) def _write_img_to_file(self, img_id, data): img_path = self._get_img_path(img_id) open(img_path, 'wb').write(data) def _read_img_from_file(self, img_id): img_path = self._get_img_path(img_id) img_data = open(img_path, 'rb').read() return img_data def _read_source_from_repos(self, formatter, src_path): repos_mgr = RepositoryManager(self.env) try: #0.12+ repos_name, repos, source_obj = repos_mgr.get_repository_by_path( src_path) except AttributeError, e: #0.11 repos = repos_mgr.get_repository(formatter.req.authname) path, rev = _split_path(src_path) if repos.has_node(path, rev): node = repos.get_node(path, rev) content = node.get_content().read() exists = True else: rev = rev or repos.get_youngest_rev() # TODO: use `raise NoSuchNode(path, rev)` content = system_message( _("No such node '%s' at revision '%s'") % (path, rev)) exists = False return (content, exists)