def expand_macro(self, formatter, name, content, args={}): "Produces XHTML code to display mindmaps" # Test if this is the long or short version of a macro call try: # Starting from Trac 0.12 the `args` argument should be set for long # macros with arguments. However, it can be still empty and is not # used at all in 0.11. if not args: # Check for multi-line content, i.e. long macro form args, content = content.split("\n", 1) except: # Short macro largs, kwargs = parse_args(content) if not largs: raise TracError("File name missing!") file = largs[0] url = extract_url(self.env, formatter.context, file, raw=True) else: # Long macro largs, kwargs = parse_args(args) digest = md5() digest.update(unicode(content).encode('utf-8')) hash = digest.hexdigest() if not self._check_cache(hash): mm = MindMap(content) self._set_cache(hash, mm) url = formatter.context.href.mindmap(hash + '.mm') return self.produce_html(formatter.context, url, kwargs)
def expand_macro(self, formatter, name, content, args={}): "Produces XHTML code to display mindmaps" # Test if this is the long or short version of a macro call try: # Starting from Trac 0.12 the `args` argument should be set for long # macros with arguments. However, it can be still empty and is not # used at all in 0.11. if not args: # Check for multi-line content, i.e. long macro form args, content = content.split("\n",1) except: # Short macro largs, kwargs = parse_args( content ) if not largs: raise TracError("File name missing!") file = largs[0] url = extract_url (self.env, formatter.context, file, raw=True) else: # Long macro largs, kwargs = parse_args( args ) digest = md5() digest.update(unicode(content).encode('utf-8')) hash = digest.hexdigest() if not self._check_cache(hash): mm = MindMap(content) self._set_cache(hash, mm) url = formatter.context.href.mindmap(hash + '.mm') return self.produce_html(formatter.context, url, kwargs)
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): """ Returns the outcome from macro. Supported arguments: - project: Name of the project to show status / provide follow buttons. Defaults to current project """ req = formatter.req # Parse optional arguments if args is None: args = parse_args(content) if len(args) > 1: args = args[1] # Read optional project name - fallback to current project_name = self.env.project_identifier if args and 'project' in args: project_name = args.get('project', '').strip() # Load project id from db project_id = Project.get(env_name=project_name).id watchers, is_watching = self._get_status(req, project_id) # If user is already watching, do not show the block if is_watching: return tag.div('') # Show macro only when user has permission to view timeline if name not in self.macros or 'TIMELINE_VIEW' not in req.perm or not project_id: # Return default content / instructions return tag.div( tag.h2(_('Follow project'), **{'class': 'title'}), tag.p(_('Project cannot be found or no permission to follow it')), **{'class': 'watch'} ) # For anonymous users, advice login/registering if req.authname == 'anonymous': return tag.div( tag.h2(_('Follow project'), **{'class': 'title'}), tag.p(_('Only registered users can follow the project activity. ')), tag.p(tag.a('Please login or register to service first', href=req.href('../home/user'))), **{'class': 'watch'} ) # Return rendered HTML with JS attached to it data = { 'project_id': project_id, 'env_name': self.env.project_identifier, 'project_name': project_name } chrome = Chrome(self.env) stream = chrome.render_template(req, 'multiproject_watch.html', data, fragment=True) if req.form_token: stream |= chrome._add_form_token(req.form_token) return stream
def expand_macro(self, formatter, name, content): formatter.req.perm.assert_permission('TICKET_VIEW') self._seen_tickets = [] options, kw = parse_args(content) if len(options) == 0: options = [''] # Generate graph header # result = "digraph G%s { rankdir = \"LR\"\n node [ style=filled ]\n" \ # % (options[0]) result = 'digraph G { rankdir = \"LR\"\n node [ style=filled ]\n' graphviz = Graphviz(self.env) if len(options) > 1 and options[1] is not '': self._maxdepth = int(options[1]) if len(options) == 0 or (len(options) > 0 and options[0] == ''): result += self._depgraph_all(formatter.req) else: result += self._depgraph(formatter.req, options[0], 0) # Add footer result += "\n}" # Draw graph and return result to browser return graphviz.expand_macro(formatter, "graphviz", result)
def expand_macro(self, formatter, name, content): args, kwargs = parse_args(content, strict=False) if len(args) > 0 or len(kwargs) > 1: raise TracError("Usage: [[BibFullRef]] or [[BibFullRef(auto=true|false)]]") showAuto = kwargs.get("auto", "false") if showAuto != "false" and showAuto != "true": raise TracError("Usage: [[BibFullRef(auto=true|false)]]") cite = _getCited(formatter.req) auto = _getAuto(formatter.req) for source in _getLoaded(formatter.req): for key, value in source.iteritems(): cite[key] = value if showAuto == "true": for a in auto: for key, value in a.iteritems(): cite[key] = value for format in self.formatter: heading = self.config.get("bibtex", "heading") or "References" div = format.format_fullref(cite, heading) return div
def expand_macro(self, formatter, name, content): args, kwargs = parse_args(content, strict=False) if len(args) > 1: raise TracError("Usage: [[BibNoCite(BibTexKey)]]") elif len(args) < 1: raise TracError("Usage: [[BibNoCite(BibTexKey)]]") key = args[0] cite = _getCited(formatter.req) auto = _getAuto(formatter.req) if key not in cite: found = False for source in _getLoaded(formatter.req): if key in source: found = True cite[key] = source[key] break if not found: for a in auto: if key in a: cite[key] = a[key] found = True break if not found: raise TracError("Unknown key '" + key + "'") return
def expand_macro(self, formatter, name, content, args=None): """ Returns the outcome from macro. Supported arguments: - count: Number of entries to show """ req = formatter.req papi = Projects() userstore = get_userstore() projects = [] # Parse optional arguments if args is None: args = parse_args(content) if len(args) > 1: args = args[1] projects = papi.get_projects_for_rss('MOSTACTIVE', limit_count=args.get('count', 5), limit_activity=self.min_activity) return Chrome(self.env).render_template(req, 'multiproject_active.html', {'active_projects': projects}, fragment=True)
def expand_macro(self, formatter, name, content): args, kwargs = parse_args(content, strict=False) if len(args) > 1: raise TracError('Usage: [[BibCite(BibTexKey)]]') elif len(args) < 1: raise TracError('Usage: [[BibCite(BibTexKey)]]') key = args[0] bibdb = getattr(formatter, BIBDB, {}) citelist = getattr(formatter, CITELIST, []) if key not in citelist: citelist.append(key) setattr(formatter, CITELIST, citelist) index = citelist.index(key) + 1 return ''.join([ '[', str(tag.a(name='cite_%s' % key)), str(tag.a(href='#%s' % key)('%d' % index)), ']' ])
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 __init__(self, env, req, content): ''' Initialize the Envoironment ''' # parse passed macro arguments args, kw = parse_args(content) self.macroid = str(kw.get('macroid')) or '1' self.macroargs = args # replace generic values for k in kw.keys(): kw[k] = kw[k].replace('$user', req.authname) self.macrokw = kw # set constants self.const = PPConstant # set trac environment, request self.tracenv = env self.tracreq = req # load configuration items self.conf = PPConfiguration(env) # create cache self.cache = ppFSFileCache(self.conf.get('cachepath'), datetime.date.today().isoformat(), int(self.conf.get('cachedirsize'))) # initialize the cache hash value with environment settings self.mhash = self.cache.newHashObject() self.mhash.update(content) self.mhash.update(self.macroid) self.mhash.update(self.tracreq.authname) self.mhash.update(str(datetime.date.today()))
def expand_macro(self,formatter,name,args): anon_params,named_params = parse_args(args) if len(anon_params) <> 1: raise TracError('Error: mandatory param, you must provide a company name.') corp = anon_params[0]; type = named_params.pop('type', 'noborder') if type == 'noborder': useborder = 'no' jsfunc = 'CompanyInsiderBox' elif type == 'popup': useborder = 'yes' jsfunc = 'CompanyInsiderPopup' elif type == 'border': useborder = 'yes' jsfunc = 'CompanyInsiderBox' else: raise TracError('Error: Type must be one from noborder, border or popup.') span_id = str(uuid.uuid4()) res = ('<script ' + 'src="http://www.linkedin.com/companyInsider?script&useBorder=' + useborder + '"' + 'type="text/javascript"></script>\n' + '<span id="' + span_id + '"></span>\n' + '<script type="text/javascript">\n' + 'new LinkedIn.'+jsfunc+'("' + span_id + '","' + corp + '");\n'+ '</script>') return res;
def expand_macro(self, formatter, name, content): args, kwargs = parse_args(content, strict=False) if len(args) > 0 or len(kwargs) > 1: raise TracError( 'Usage: [[BibFullRef]] or [[BibFullRef(auto=true|false)]]') showAuto = kwargs.get("auto", "false") if (showAuto != "false" and showAuto != "true"): raise TracError("Usage: [[BibFullRef(auto=true|false)]]") cite = getCited(formatter.req) auto = getAuto(formatter.req) for source in getLoaded(formatter.req): for key, value in source.iteritems(): cite[key] = value if showAuto == "true": for key, value in auto.iteritems(): cite[key] = value for format in self.formatter: div = format.format_fullref(cite, 'References') return div
def _parse_args(self, args, content): """ Parse args from incoming args or from content. Depending on which is set. If called as a macro, the content holds the arguments. Otherwise it's the args param that holds the arguments. In macro case, a tuple is returned, first element is list of arguments, second is a dict. We only support the dict format. """ env_name = '' count = 0 title = 'Latest announcements' if args is None: args = parse_args(content) if len(args) > 1: args = args[1] if args is not None and 'project' in args: env_name = args['project'] env_name = env_name.strip('" \'') env_name = env_name.encode('utf-8') env_name = safe_string(env_name) if args is not None and 'count' in args: count = args['count'] count = safe_int(count) if args is not None and 'title' in args: title = args['title'] title = title.strip('"') self.log.debug("Returning args: {0}".format((env_name, count, title))) return env_name, count, title
def expand_macro(self, formatter, name, args): outputText = "" #check arguments if (args == None or len(args) == 0): return None largs, kwargs = parse_args(args) if not kwargs.has_key('url'): return _usage() url = kwargs['url'] if kwargs.has_key('details'): details = kwargs['details'] else: details = "false" if kwargs.has_key('proxy'): proxy = kwargs['proxy'] else: proxy = None try: if proxy != None: proxyHandler = urllib2.ProxyHandler({"http": proxy}) feedData = feedparser.parse(url, handlers=[proxyHandler]) else: response = urlopen(Request(url)) response.close() feedData = feedparser.parse(url) except HTTPError, e: outputText += "HTTP_ERROR(" + str(e.code) + ")"
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 _parse_args(self, args, content): """ Parse args from incoming args or from content. Depending on which is set. If called as a macro, the content holds the arguments. Otherwise it's the args param that holds the arguments. In macro case, a tuple is returned, first element is list of arguments, second is a dict. We only support the dict format. """ env_name = "" count = 0 title = "Latest announcements" if args is None: args = parse_args(content) if len(args) > 1: args = args[1] if args is not None and "project" in args: env_name = args["project"] env_name = env_name.strip("\" '") env_name = env_name.encode("utf-8") env_name = safe_string(env_name) if args is not None and "count" in args: count = args["count"] count = safe_int(count) if args is not None and "title" in args: title = args["title"] title = title.strip('"') self.log.debug("Returning args: {0}".format((env_name, count, title))) return env_name, count, title
def harvest(self, req, content): """TicketQuery provider method.""" # Parse args and kwargs. argv, kwargs = parse_args(content, strict=False) # Define minimal set of values. std_fields = ['description', 'owner', 'status', 'summary'] kwargs['col'] = "|".join(std_fields) # Options from old 'wikiticketcalendar' section have been migrated to # 'wikicalendar' configuration section. due_field = self.config.get('wikicalendar', 'ticket.due_field') if due_field: kwargs['col'] += '|' + due_field # Construct the query-string. query_string = '&'.join(['%s=%s' % i for i in kwargs.iteritems()]) # Get the Query object. query = Query.from_string(self.env, query_string) # Initialize query and get 1st "page" of Ticket objects. result = query.execute(req) # Collect tickets from all other query "pages", if available. while query.offset + query.max < query.num_items: query.offset += query.max result.extend(query.execute(req)) # Filter tickets according to (view) permission. tickets = [t for t in result if 'TICKET_VIEW' in req.perm('ticket', t['id'])] return tickets
def expand_macro(self, formatter, name, args): anon_params, named_params = parse_args(args) if len(anon_params) <> 1: raise TracError( 'Error: mandatory param, you must provide a company name.') corp = anon_params[0] type = named_params.pop('type', 'noborder') if type == 'noborder': useborder = 'no' jsfunc = 'CompanyInsiderBox' elif type == 'popup': useborder = 'yes' jsfunc = 'CompanyInsiderPopup' elif type == 'border': useborder = 'yes' jsfunc = 'CompanyInsiderBox' else: raise TracError( 'Error: Type must be one from noborder, border or popup.') span_id = str(uuid.uuid4()) res = ( '<script ' + 'src="http://www.linkedin.com/companyInsider?script&useBorder=' + useborder + '"' + 'type="text/javascript"></script>\n' + '<span id="' + span_id + '"></span>\n' + '<script type="text/javascript">\n' + 'new LinkedIn.' + jsfunc + '("' + span_id + '","' + corp + '");\n' + '</script>') return res
def expand_macro(self, formatter, name, content, args=None): """ Returns the outcome from macro. """ req = formatter.req userstore = get_userstore() user = userstore.getUser(req.authname) msgsrv = self.env[MessageService] # Parse optional arguments if args is None: args = parse_args(content) if len(args) > 1: args = args[1] data = { 'groups': msgsrv.get_messages_grouped_by(user.id) } # FIXME: Temporary fix for IE8 + jQuery 1.4.4 + Transparency combination agent = req.get_header('user-agent') if agent and 'MSIE 8.0' not in agent: add_script(req, 'multiproject/js/transparency.js') add_script(req, 'multiproject/js/multiproject.js') add_script(req, 'multiproject/js/messages_group_macro.js') chrome = Chrome(self.env) return chrome.render_template(req, 'multiproject_messages_group_macro.html', data, fragment=True)
def expand_macro(self, formatter, name, args): """Return a welcome heading with the project name extracted from trac.ini. + User-defined welcome string suffix and prefix. + Supports both standard and dictionary methods. + With defaults. """ prefix = suffix = '' args, kw = parse_args(args) if args: prefix = args.pop(0).strip() if args: suffix = args.pop(0).strip() elif kw: if 'prefix' in kw: prefix = kw['prefix'].strip() if 'suffix' in kw: suffix = kw['suffix'].strip() default = { 'prefix': 'Welcome to the', 'suffix': 'Project' } if not prefix: prefix = default['prefix'] if not suffix: suffix = default['suffix'] return tag.h1('%s %s %s' % (prefix, self.env.project_name, suffix), id='welcome')
def expand_macro(self, formatter, name, content, args): fdx = False start_with_scene = False end_with_scene = False # self.log.debug("EXPAND ARGUMENTS: %s " % args) # self.log.debug("EXPAND INITIAL CONTENT: %s" % content) req = formatter.req if content: args2,kw = parse_args(content) # self.log.debug("RENDER ARGUMENTS: %s " % args2) # self.log.debug("RENDER KW: %s " % kw) if 'fdx' in kw: fdx = self._parse_filespec(kw['fdx'].strip(), formatter.context, self.env) if 'start_with_scene' in kw: start_with_scene = int(kw['start_with_scene']) if 'end_with_scene' in kw: end_with_scene = int(kw['end_with_scene']) elif 'start_with_scene' in kw: end_with_scene = int(kw['start_with_scene']) + 1 if args != {} and args != None and args['mode'] == "full": add_stylesheet(req, 'scrippets/css/scrippets-full.css') mode = "-full" elif kw != {} and fdx: add_stylesheet(req, 'scrippets/css/scrippets.css') mode = "" return self.render_fdx_subset(fdx,start_with_scene,end_with_scene,mode,formatter) else: add_stylesheet(req, 'scrippets/css/scrippets.css') mode = "" return self.render_inline_content(self.env, formatter.context, content,mode)
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): env = formatter.env req = formatter.req if not 'VOTE_VIEW' in req.perm: return # Simplify function calls. format_author = partial(Chrome(self.env).format_author, req) if not content: args = [] compact = None kw = {} top = 5 else: args, kw = parse_args(content) compact = 'compact' in args and True top = as_int(kw.get('top'), 5, min=0) if name == 'LastVoted': lst = tag.ul() for i in self.get_votes(req, top=top): resource = Resource(i[0], i[1]) # Anotate who and when. voted = ('by %s at %s' % (format_author(i[3]), format_datetime(to_datetime(i[4])))) lst(tag.li(tag.a( get_resource_description(env, resource, compact and 'compact' or 'default'), href=get_resource_url(env, resource, formatter.href), title=(compact and '%+i %s' % (i[2], voted) or None)), (not compact and Markup(' %s %s' % (tag.b('%+i' % i[2]), voted)) or ''))) return lst elif name == 'TopVoted': realm = kw.get('realm') lst = tag.ul() for i in self.get_top_voted(req, realm=realm, top=top): if 'up-only' in args and i[2] < 1: break resource = Resource(i[0], i[1]) lst(tag.li(tag.a( get_resource_description(env, resource, compact and 'compact' or 'default'), href=get_resource_url(env, resource, formatter.href), title=(compact and '%+i' % i[2] or None)), (not compact and ' (%+i)' % i[2] or ''))) return lst elif name == 'VoteList': lst = tag.ul() resource = resource_from_path(env, req.path_info) for i in self.get_votes(req, resource, top=top): vote = ('at %s' % format_datetime(to_datetime(i[4]))) lst(tag.li( compact and format_author(i[3]) or Markup(u'%s by %s %s' % (tag.b('%+i' % i[2]), tag(format_author(i[3])), vote)), title=(compact and '%+i %s' % (i[2], vote) or None))) return lst
def expand_macro(self, formatter, name, content): req = formatter.req args, kwargs = parse_args(content) args += [None, None] path, limit, rev = args[:3] limit = kwargs.pop('limit', limit) rev = kwargs.pop('rev', rev) if 'CHANGESET_VIEW' not in req.perm: return Markup('<i>Changelog not available</i>') repo = self.env.get_repository(req.authname) if rev is None: rev = repo.get_youngest_rev() rev = repo.normalize_rev(rev) path = repo.normalize_path(path) if limit is None: limit = 5 else: limit = int(limit) node = repo.get_node(path, rev) out = StringIO() out.write('<div class="changelog">\n') for npath, nrev, nlog in node.get_history(limit): change = repo.get_changeset(nrev) datetime = format_datetime(change.date, '%Y/%m/%d %H:%M:%S', req.tz) out.write(wiki_to_html("'''[%s] by %s on %s'''\n\n%s" % (nrev, change.author, datetime, change.message), self.env, req)); out.write('</div>\n') return out.getvalue()
def expand_macro(self, formatter, name, content): req = formatter.req # Parse arguments args, kwargs = parse_args(content, strict=False) assert not args and not ('status' in kwargs or 'format' in kwargs), \ "Invalid input!" # hack the `format` kwarg in order to display all-tickets stats # when no kwargs are supplied kwargs['format'] = 'count' # special case for values equal to 'self': replace with current # ticket number, if available for key in kwargs.keys(): if kwargs[key] == 'self': current_ticket = self._this_ticket(req) if current_ticket: kwargs[key] = current_ticket # Create & execute the query string qstr = '&'.join(['%s=%s' % item for item in kwargs.iteritems()]) query = Query.from_string(self.env, qstr, max=0) # Calculate stats qres = query.execute(req) tickets = apply_ticket_permissions(self.env, req, qres) stats = get_ticket_stats(self.stats_provider, tickets) stats_data = query_stats_data(req, stats, query.constraints) # ... and finally display them add_stylesheet(req, 'common/css/roadmap.css') chrome = Chrome(self.env) return chrome.render_template(req, 'progressmeter.html', stats_data, fragment=True)
def expand_macro(self, formatter, name, content): req = formatter.req args, kwargs = parse_args(content) args += [None, None] path, limit, rev = args[:3] limit = kwargs.pop('limit', limit) rev = kwargs.pop('rev', rev) if 'CHANGESET_VIEW' not in req.perm: return Markup('<i>Changelog not available</i>') repo = self.env.get_repository(req.authname) if rev is None: rev = repo.get_youngest_rev() rev = repo.normalize_rev(rev) path = repo.normalize_path(path) if limit is None: limit = 5 else: limit = int(limit) node = repo.get_node(path, rev) out = StringIO() out.write('<div class="changelog">\n') for npath, nrev, nlog in node.get_history(limit): change = repo.get_changeset(nrev) datetime = format_datetime(change.date, '%Y/%m/%d %H:%M:%S', req.tz) out.write( wiki_to_html( "'''[%s] by %s on %s'''\n\n%s" % (nrev, change.author, datetime, change.message), self.env, req)) out.write('</div>\n') return out.getvalue()
def expand_macro(self, formatter, name, content): args, opts = parse_args(content) if len(args) != 1: raise TracError("Requied single table name") table = args[0] excludes = [e.strip() for e in opts.get('exclude', '').split('|')] ccount = int(opts.get('column_count', 3)) cdtsys = CustomDBTableSystem(self.env) try: items = cdtsys.sorted_column(table, 'name') except: raise TracError("Table not found: %s" % table) if excludes: items = [e for e in items if e not in excludes] ttable = tag.table(class_='wiki customdbtable') tr = td = None i = 0 for item in items: if i % ccount == 0: tr = tag.tr() ttable(tr) td = tag.td(item) tr(td) i += 1 while i % ccount != 0: tr(tag.td()) i += 1 return ttable
def expand_macro(self,formatter,name,content): args, kwargs = parse_args(content) if len(args) < 1: raise TracError('Usage: [[ZotRelated(key)]]') if len(args) > 1: raise TracError('Usage: [[ZotRelated(key)]]') citelist = getattr(formatter, CITELIST,[]) key = args if key[0:2] == '0_': key = key[2:10] else: key = key[0:8] columns = self.env.config.get('zotero', 'columns','firstCreator, year, publicationTitle, title' ) columns = columns.split(',') columns = [c.strip() for c in columns] labels = self.env.config.get('zotero', 'labels','Authors, Year, Publication, Title' ) labels = labels.split(',') labels = [l.strip() for l in labels] model = ZoteroModelProvider(self.env) item_ids = model.get_items_ids_by_keys( key ) rids = model.get_items_related(item_ids) rids_all = [] for id, lid in rids: if str(id) == str(item_ids[0]): rids_all.append(lid) else: rids_all.append(id) if len(rids_all) > 0: item_mata = model.get_item_columns_by_iids(rids_all,columns) return render_refs_box(self,formatter.req, rids_all)
def expand_macro(self, formatter, name, content): from trac.mimeview.api import Mimeview mime_map = Mimeview(self.env).mime_map mime_type_filter = '' args, kw = parse_args(content) if args: mime_type_filter = args.pop(0).strip().rstrip('*') mime_types = {} for key, mime_type in mime_map.iteritems(): if (not mime_type_filter or mime_type.startswith(mime_type_filter)) and key != mime_type: mime_types.setdefault(mime_type, []).append(key) return tag.div(class_='mimetypes')( tag.table(class_='wiki')( tag.thead(tag.tr( tag.th(_("MIME Types")), # always use plural tag.th(tag.a("WikiProcessors", href=formatter.context.href.wiki( 'WikiProcessors'))))), tag.tbody( tag.tr(tag.th(tag.tt(mime_type), style="text-align: left"), tag.td(tag.code( ' '.join(sorted(mime_types[mime_type]))))) for mime_type in sorted(mime_types.keys()))))
def harvest(self, req, content): """TicketQuery provider method.""" # Parse args and kwargs. argv, kwargs = parse_args(content, strict=False) # Define minimal set of values. std_fields = ['description', 'owner', 'status', 'summary'] kwargs['col'] = "|".join(std_fields) # Options from old 'wikiticketcalendar' section have been migrated to # 'wikicalendar' configuration section. due_field = self.config.get('wikicalendar', 'ticket.due_field') if due_field: kwargs['col'] += '|' + due_field # Construct the query-string. query_string = '&'.join(['%s=%s' % i for i in kwargs.iteritems()]) # Get the Query object. query = Query.from_string(self.env, query_string) # Initialize query and get 1st "page" of Ticket objects. result = query.execute(req) # Collect tickets from all other query "pages", if available. while query.offset + query.max < query.num_items: query.offset += query.max result.extend(query.execute(req)) # Filter tickets according to (view) permission. tickets = [ t for t in result if 'TICKET_VIEW' in req.perm('ticket', t['id']) ] return tickets
def expand_macro(self, formatter, name, content): add_stylesheet(formatter.req, 'notebox/css/notebox.css') args, kwargs = parse_args(content) width = len(args) > 2 and args[2] or '70%' return tag.div(format_to_html(self.env, formatter.context, args[1]), class_='notebox-%s' % (args[0], ), style='width: %s' % (width, ))
def __init__(self, env, req, content): """ Initialize the Envoironment """ # parse passed macro arguments args, kw = parse_args(content) self.macroid = str(kw.get("macroid")) or "1" self.macroargs = args # replace generic values for k in kw.keys(): kw[k] = kw[k].replace("$user", req.authname) self.macrokw = kw # set constants self.const = PPConstant # set trac environment, request self.tracenv = env self.tracreq = req # load configuration items self.conf = PPConfiguration(env) # create cache self.cache = ppFSFileCache( self.conf.get("cachepath"), datetime.date.today().isoformat(), int(self.conf.get("cachedirsize")) ) # initialize the cache hash value with environment settings self.mhash = self.cache.newHashObject() self.mhash.update(content) self.mhash.update(self.macroid) self.mhash.update(self.tracreq.authname) self.mhash.update(str(datetime.date.today()))
def expand_macro(self, formatter, name, content): add_stylesheet(formatter.req, 'notebox/css/notebox.css') args, kwargs = parse_args(content) width = len(args) > 2 and args[2] or '70%' return tag.div(format_to_html(self.env, formatter.context, args[1]), class_='notebox-%s' % (args[0],), style='width: %s' % (width,))
def expand_macro(self, formatter, name, content): args, kwargs = parse_args(content, strict=False) if len(args) > 1: raise TracError('Usage: [[BibNoCite(BibTexKey)]]') elif len(args) < 1: raise TracError('Usage: [[BibNoCite(BibTexKey)]]') key = args[0] cite = getCited(formatter.req) auto = getAuto(formatter.req) if key not in cite: found = False for source in getLoaded(formatter.req): if key in source: found = True cite[key] = source[key] break if not found and key in auto: cite[key] = auto[key] elif not found: raise TracError("Unknown key '" + key + "'") return
def expand_macro(self, formatter, name, args): from trac.config import Option section_filter = key_filter = '' args, kw = parse_args(args) if args: section_filter = args.pop(0).strip() if args: key_filter = args.pop(0).strip() registry = Option.get_registry(self.compmgr) sections = {} for (section, key), option in registry.iteritems(): if section.startswith(section_filter): sections.setdefault(section, {})[key] = option return tag.div(class_='tracini')( (tag.h3(tag.code('[%s]' % section), id='%s-section' % section), tag.table(class_='wiki')( tag.tbody(tag.tr(tag.td(tag.tt(option.name)), tag.td(format_to_oneliner( self.env, formatter.context, to_unicode(option.__doc__)))) for option in sorted(sections[section].itervalues(), key=lambda o: o.name) if option.name.startswith(key_filter)))) for section in sorted(sections))
def harvest(self, req, content): """TicketQuery provider method.""" # Options in 'wikicalendar' configuration section take precedence over # those in old 'wikiticketcalendar' section. c = self.config if 'wikicalendar' in c.sections(): tkt_due_field = c.get('wikicalendar', 'ticket.due_field') else: tkt_due_field = c.get('wikiticketcalendar', 'ticket.due_field.name') # Parse args and kwargs. argv, kwargs = parse_args(content, strict=False) # Define minimal set of values. std_fields = ['description', 'owner', 'status', 'summary'] kwargs['col'] = "|".join(std_fields + [tkt_due_field]) # Construct the querystring. query_string = '&'.join( ['%s=%s' % item for item in kwargs.iteritems()]) # Get the Query Object. query = Query.from_string(self.env, query_string) # Get the tickets. tickets = self._get_tickets(query, req) return tickets
def expand_macro(self, formatter, name, content): from trac.mimeview.api import Mimeview mime_map = Mimeview(self.env).mime_map mime_type_filter = '' args, kw = parse_args(content) if args: mime_type_filter = args.pop(0).strip().rstrip('*') mime_types = {} for key, mime_type in mime_map.iteritems(): if (not mime_type_filter or mime_type.startswith(mime_type_filter)) and key != mime_type: mime_types.setdefault(mime_type, []).append(key) return tag.div(class_='mimetypes')( tag.table(class_='wiki')( tag.thead(tag.tr( tag.th(_("MIME Types")), # always use plural tag.th(tag.a("WikiProcessors", href=formatter.context.href.wiki( 'WikiProcessors'))))), tag.tbody( tag.tr(tag.th(tag.code(mime_type), style="text-align: left"), tag.td(tag.code( ' '.join(sorted(mime_types[mime_type]))))) for mime_type in sorted(mime_types.keys()))))
def expand_macro(self, formatter, name, args): outputText = "" #check arguments if (args == None or len(args) == 0): return None largs, kwargs = parse_args(args) if not kwargs.has_key('url'): return _usage() url = kwargs['url'] if kwargs.has_key('details'): details=kwargs['details'] else: details="false" if kwargs.has_key('proxy'): proxy=kwargs['proxy'] else: proxy=None try: if proxy != None: proxyHandler = urllib2.ProxyHandler({"http":proxy}) feedData = feedparser.parse(url, handlers = [proxyHandler]) else: response = urlopen(Request(url)) response.close() feedData = feedparser.parse(url) except HTTPError, e: outputText += "HTTP_ERROR("+str(e.code)+")"
def expand_macro(self, formatter, name, content): req = formatter.req query_string = "" argv, kwargs = parse_args(content, strict=False) if "order" not in kwargs: kwargs["order"] = "id" if "max" not in kwargs: kwargs["max"] = "0" # unlimited by default query_string = "&".join(["%s=%s" % item for item in kwargs.iteritems()]) query = Query.from_string(self.env, query_string) tickets = query.execute(req) tickets = [t for t in tickets if "TICKET_VIEW" in req.perm("ticket", t["id"])] ticket_ids = [t["id"] for t in tickets] # locate the tickets geoticket = GeoTicket(self.env) locations = geoticket.locate_tickets(ticket_ids, req) if not locations: return tag.div(tag.b("MapTickets: "), "No locations found for ", tag.i(content)) data = dict(locations=Markup(simplejson.dumps(locations)), query_href=query.get_href(req), query_string=content) # set an id for the map map_id = req.environ.setdefault("MapTicketsId", 0) + 1 req.environ["MapTicketsId"] = map_id data["map_id"] = "tickets-map-%d" % map_id return Chrome(self.env).render_template(req, "map_tickets.html", data, None, fragment=True)
def expand_macro(self, formatter, name, content, args=None): """ Returns the outcome from macro. Supported arguments: - count: Number of entries to show """ req = formatter.req papi = Projects() userstore = get_userstore() # Parse optional arguments if args is None: args = parse_args(content) if len(args) > 1: args = args[1] featured_projects = papi.get_projects_for_rss('FEATURED', limit_count=args.get( 'count', 5)) data = {'featured_projects': featured_projects} return Chrome(self.env).render_template(req, 'multiproject_featured.html', data, fragment=True)
def _parse_macro_content(self, content, req): args, kwargs = parse_args(content, strict=False) assert not args and not ('status' in kwargs or 'format' in kwargs), \ "Invalid input!" # hack the `format` kwarg in order to display all-tickets stats when # no kwargs are supplied kwargs['format'] = 'count' # special case for values equal to 'self': replace with current ticket # number, if available for key in kwargs.keys(): if kwargs[key] == 'self': current_ticket = self._this_ticket(req) if current_ticket: kwargs[key] = current_ticket try: spkw = kwargs.pop('stats_provider') xtnpt = ExtensionPoint(ITicketGroupStatsProvider) found = False for impl in xtnpt.extensions(self): if impl.__class__.__name__ == spkw: found = True stats_provider = impl break if not found: raise TracError("Supplied stats provider does not exist!") except KeyError: # if the `stats_provider` keyword argument is not provided, # propagate the stats provider defined in the config file stats_provider = self._sp return stats_provider, kwargs
def expand_macro(self, formatter, name, content): args, kw = parse_args(content) if not ('site' in kw and 'chapter' in kw): raise Exception("'site' and 'chapter' are required arguments") if not (re.match("^[a-zA-Z0-9-]+$",kw['site']) and re.match("^[a-zA-Z0-9-]+$",kw['site'])): raise Exception("'site' or 'chapter' setting contains invalid characters") template_dict = {'height': int(kw.get('height', 253)), 'width': int(kw.get('width', 450)), 'SiteId': kw['site'], 'chapterId': kw['chapter'], 'scheme': formatter.req.scheme} template = """ <object id="%(SiteId)s" name="MediahubVideoPlayer" type="application/x-shockwave-flash" data="%(scheme)s://players.mymediahub.com/MediaHubPlayer.swf" height="%(height)s" width="%(width)s"> <param name="movie" value="%(scheme)s://players.mymediahub.com/MediaHubPlayer.swf" /> <param name="allowFullScreen" value="true" /> <param name="allowScriptAccess" value="always" /> <param name="wmode" value="transparent" /> <param name="flashvars" value="ServiceUrl=%(scheme)s://sites.mymediahub.com/Services/&SiteId=%(SiteId)s" /> <a href="%(scheme)s://sites.mymediahub.com/Devices/Services/GetChapterVideo.ashx?chapterId=%(chapterId)s&siteId=%(SiteId)s"> <img border="0" alt="Video" src="%(scheme)s://sites.mymediahub.com/Devices/Services/GetEmbedPreviewStill.ashx?siteId=%(SiteId)s" width="%(width)s" height="%(height)s" class="MediahubPreviewStill" /> </a> </object> """ return template % template_dict
def expand_macro(self, formatter, name, content): args, opts = parse_args(content) if len(args) != 1: raise TracError("Requied single table name") table = args[0] excludes = [e.strip() for e in opts.get('exclude', '').split('|')] cdtsys = CustomDBTableSystem(self.env) try: columns = cdtsys.column_names(table) except: raise TracError("Table not found: %s" % table) _columns = opts.get('column', '') if _columns: _columns = [c.strip() for c in _columns.split('|')] if len(_columns) == 0: raise TracError("No column specified") for c in _columns: if c not in columns: raise TracError("Column not found: %s" % c) else: _columns = columns ttable = tag.table(class_='wiki customdbtable') for row in cdtsys.sorted_dicts(table): if row['name'] in excludes: continue tr = tag.tr() for c in _columns: tr(tag.td(row[c])) ttable(tr) return ttable
def options(self, content, result='all'): """ Parse macro args given in content, returning an options dict. Options will be filled in from trac.ini and defaulted if missing, so that the returned dict will surely contain each option that was specified as a MacroOption. The resolved options are retained, and will be used when accessing the option variables as declared through MacroOption. This method returns a dictionary with all the resolved options in the macro call, not only those registered. You can use the extras() method to extract these extra options. """ if not self.tracconfig: raise Exception('TracMacroConfig not setup properly - no config') good_result = [ 'all', 'wellknown', 'extra', 'nothing' ] if not result is None and not result in good_result: raise ValueError("TracMacroConfig.options(result='%s') invalid;" " use one of %s, or None." % ( result, ','.join([ "%s" % x for x in good_result ]))) self.results_list, options = parse_args(content, strict=False) self._log('parse incoming %s' % options) self._parse(options) if result is None or result == 'nothing': return None results = {} for key, ent in self.results.iteritems(): if result == 'all' or \ (result == 'wellknown' and key in self.wanted) or \ (result == 'extra' and not key in self.wanted): results[key] = ent[0] self._log('parse results %s' % results) return results
def expand_macro(self, formatter, name, content): # Parse content for arguments args_list, args_dict = parse_args(content) from_dt, to_dt = parse_period(list(args_dict.get('period', '').split('/'))) category = args_dict.get('category', '') author = args_dict.get('author', '') recent = int(args_dict.get('recent', 0)) format = args_dict.get('format', 'inline').lower() heading = args_dict.get('heading', '') max_size = int(args_dict.get('max_size', 0)) show_meta = args_dict.get('meta', '') != 'off' and True or False # Get blog posts all_posts = get_blog_posts(self.env, author=author, category=category, from_dt=from_dt, to_dt=to_dt) # Trim posts against permissions and count post_list = [] post_instances = [] if format in ['float', 'full']: recent = recent or self.env.config.getint('fullblog', 'num_items_front') recent = recent or len(all_posts) count = 0 for post in all_posts: if count == recent: break bp = BlogPost(self.env, post[0]) if 'BLOG_VIEW' in formatter.req.perm(bp.resource): count += 1 post_instances.append(bp) post_list.append(post) # Rendering add_stylesheet(formatter.req, 'tracfullblog/css/fullblog.css') add_stylesheet(formatter.req, 'common/css/code.css') if format == 'inline': data = {'heading': heading, 'posts': post_list, 'blog_personal_blog': self.config.getbool( 'fullblog', 'personal_blog'), 'show_meta': show_meta, 'execute_blog_macro': True} return Chrome(self.env).render_template(formatter.req, 'fullblog_macro_monthlist.html', data=data, fragment=True) elif format == 'full': return self._render_full_format(formatter, post_list, post_instances, heading, max_size, show_meta) elif format == 'float': # Essentially a 'full' list - just wrapped inside a new div return tag.div(self._render_full_format(formatter, post_list, post_instances, heading, max_size, show_meta), class_="blogflash") else: raise TracError("Invalid 'format' argument used for macro %s." % name)
def _get_problems(self, silent): res = u"" resargs = u"" respages = u"" base_pages = [] for page in sorted(WikiSystem(self.env).get_pages()): for line in WikiPage(self.env, page).text.replace('\r', '').split(u'\n'): regres = self.macro_re.search(line) if regres != None: (prefix, base_page_name, lang_code) = self._get_page_info(page) basename = self._get_translated_page(prefix, \ base_page_name, self.base_lang) if not basename in base_pages: base_pages.append(basename) resargs += self._check_args(page, regres.group(1), lang_code) if self.languages.get(lang_code, None) == None: respages += "||[[wiki:/%s]]||Translated page language code unknown||\n" % page base_pages.sort() for base_page in base_pages: (prefix, page, lang_code) = self._get_page_info(base_page) translations = self._get_translations(prefix, page) basever = 0 if not self.base_lang in translations: respages += "||[[wiki:/%s]]||Base language is missing for translated pages||\n" % base_page else: basever = WikiPage(self.env, base_page).version for translation in translations: transpage = self._get_translated_page(prefix, page, translation) regres = self.macro_re.search( WikiPage(self.env, transpage).text) if regres != None: argstr = regres.group(1) if argstr != None and len(argstr) > 0: args, kw = parse_args(argstr) try: rev = int(kw[u'revision']) if rev != 0 and rev > basever: respages += "||[[wiki:/%s]]||Revision %s is higher than base revision %s||\n" \ % (transpage, rev, basever) except: pass else: respages += "||[[wiki:/%s]]||Translated page misses macro 'TranslatedPages'||\n" % transpage if len(resargs): res += u"=== Errors in supplied arguments ===\n||= Page =||= Arguments =||= Issue =||\n" + resargs if len(respages): res += u"=== Errors in page structure ===\n||= Page =||= Issue =||\n" + respages if not len(res): if (silent): return u" " res = u'none\n' return u"== Problem pages ==\n" + res
def parse_options(db, content, options): """Parses the parameters, makes some sanity checks, and creates default values for missing parameters. """ cursor = db.cursor() # check arguments _, parsed_options = parse_args(content, strict=False) options.update(parsed_options) startdatearg = options.get('startdate') if startdatearg: options['startdate'] = datetime(*strptime(startdatearg, "%Y-%m-%d")[0:5]).date() enddatearg = options.get("enddate") options['enddate'] = None if enddatearg: options['enddate'] = datetime(*strptime(enddatearg, "%Y-%m-%d")[0:5]).date() if not options['enddate'] and options.get('milestone'): # use first milestone milestone = options['milestone'].split('|')[0] # try to get end date from db cursor.execute("SELECT completed, due FROM milestone WHERE name = %s", [milestone]) row = cursor.fetchone() if not row: raise TracError("Couldn't find milestone %s" % (milestone)) if row[0]: options['enddate'] = datetime.fromtimestamp(row[0]).date() elif row[1]: options['enddate'] = datetime.fromtimestamp(row[1]).date() if not options['enddate']: options['enddate'] = datetime.now().date() todayarg = options.get('today') if not todayarg: options['today'] = datetime.now().date() if 'interval_days' in options: try: options['interval_days'] = int(options['interval_days']) except (ValueError, TypeError): options['interval_days'] = 1 else: options['interval_days'] = 1 if 'change' in options and options['change'].lower() not in ('false', '0'): options['change'] = True else: options['change'] = False # all arguments that are no key should be treated as part of the query query_args = {} for key in options.keys(): if not key in AVAILABLE_OPTIONS: query_args[key] = options[key] return options, query_args
def render_macro(self, req, name, content): _, parsed_options = parse_args(content, strict=False) options = copy.copy(DEFAULT_OPTIONS) options.update(parsed_options) view = self.render(req, options) return Markup(view)
def expand_macro(self, formatter, name, args): from trac.config import ConfigSection, Option section_filter = key_filter = '' args, kw = parse_args(args) if args: section_filter = args.pop(0).strip() if args: key_filter = args.pop(0).strip() def getdoc(option_or_section): doc = to_unicode(option_or_section.__doc__) if doc: doc = dgettext(option_or_section.doc_domain, doc) return doc registry = ConfigSection.get_registry(self.compmgr) sections = dict((name, getdoc(section)) for name, section in registry.iteritems() if name.startswith(section_filter)) registry = Option.get_registry(self.compmgr) options = {} for (section, key), option in registry.iteritems(): if section.startswith(section_filter): options.setdefault(section, {})[key] = option sections.setdefault(section, '') def default_cell(option): default = option.default if default is True: default = 'true' elif default is False: default = 'false' elif default == 0: default = '0.0' if isinstance(default, float) else '0' elif default: default = ', '.join(to_unicode(val) for val in default) \ if isinstance(default, (list, tuple)) \ else to_unicode(default) else: return tag.td(_("(no default)"), class_='nodefault') return tag.td(tag.code(default), class_='default') return tag.div(class_='tracini')( (tag.h3(tag.code('[%s]' % section), id='%s-section' % section), format_to_html(self.env, formatter.context, section_doc), tag.table(class_='wiki')(tag.tbody( tag.tr( tag.td(tag.tt(option.name)), tag.td( format_to_oneliner(self.env, formatter.context, getdoc(option))), default_cell(option)) for option in sorted(options.get(section, {}).itervalues(), key=lambda o: o.name) if option.name.startswith(key_filter)))) for section, section_doc in sorted(sections.iteritems()))
def expand_macro(self, formatter, name, content): args, kwargs = parse_args(content) format = kwargs.get('format', 'compact') glob = kwargs.get('glob', '*') order = kwargs.get('order') desc = as_bool(kwargs.get('desc', 0)) rm = RepositoryManager(self.env) all_repos = dict(rdata for rdata in rm.get_all_repositories().items() if fnmatchcase(rdata[0], glob)) if format == 'table': repo = self._render_repository_index(formatter.context, all_repos, order, desc) add_stylesheet(formatter.req, 'common/css/browser.css') wiki_format_messages = self.config['changeset'] \ .getbool('wiki_format_messages') data = {'repo': repo, 'order': order, 'desc': 1 if desc else None, 'reponame': None, 'path': '/', 'stickyrev': None, 'wiki_format_messages': wiki_format_messages} from trac.web.chrome import Chrome return Chrome(self.env).render_template( formatter.req, 'repository_index.html', data, None, fragment=True) def get_repository(reponame): try: return rm.get_repository(reponame) except TracError: return all_repos = [(reponame, get_repository(reponame)) for reponame in all_repos] all_repos = sorted(((reponame, repos) for reponame, repos in all_repos if repos and not as_bool(repos.params.get('hidden')) and repos.is_viewable(formatter.perm)), reverse=desc) def repolink(reponame, repos): label = reponame or _('(default)') return Markup(tag.a(label, title=_('View repository %(repo)s', repo=label), href=formatter.href.browser(repos.reponame or None))) if format == 'list': return tag.dl([ tag(tag.dt(repolink(reponame, repos)), tag.dd(repos.params.get('description'))) for reponame, repos in all_repos]) else: # compact return Markup(', ').join([repolink(reponame, repos) for reponame, repos in all_repos])
def expand_macro(self, formatter, name, content): # process arguments args, kw = parse_args(content) title = '' for i in range(0, len(args)): title += args[i] return ("<div> " + "<h3 class=\"foldable\">" + title + "</h3>" + "<div>")
def parse_options(env, content, options): """Parses the parameters, makes some sanity checks, and creates default values for missing parameters. """ _, parsed_options = parse_args(content, strict=False) options.update(parsed_options) today = datetime.now().date() startdatearg = options.get('startdate') if startdatearg: options['startdate'] = \ datetime(*strptime(startdatearg, "%Y-%m-%d")[0:5]).date() enddatearg = options.get('enddate') options['enddate'] = None if enddatearg: options['enddate'] = \ datetime(*strptime(enddatearg, "%Y-%m-%d")[0:5]).date() if not options['enddate'] and options.get('milestone'): # use first milestone milestone = options['milestone'].split('|')[0] # try to get end date from db for completed, due in env.db_query( """ SELECT completed, due FROM milestone WHERE name = %s """, (milestone, )): if completed: options['enddate'] = from_utimestamp(completed).date() elif due: due = from_utimestamp(due).date() if due >= today: options['enddate'] = due break else: raise TracError("Couldn't find milestone %s" % milestone) options['enddate'] = options['enddate'] or today options['today'] = options.get('today') or today if options.get('weekends'): options['weekends'] = parse_bool(options['weekends']) if options.get('spent'): options['spent'] = parse_bool(options['spent']) # all arguments that are no key should be treated as part of the query query_args = {} for key in options.keys(): if key not in AVAILABLE_OPTIONS: query_args[key] = options[key] return options, query_args