def convert_content(self, req, input_type, text, output_type): page_name = req.args.get('page', 'WikiStart') wikipage = WikiPage(self.env, page_name) wikiprint = WikiPrint(self.env) page = wikiprint.wikipage_to_html(text, page_name, req) #Get page title from first header in outline out = StringIO.StringIO() context = Context(Resource('wiki', page_name), req.abs_href, req.perm) context.req = req outline = OutlineFormatter(self.env, context) outline.format(text, out, 1, 1) title = wikipage.name for depth, anchor, text in outline.outline: if depth == 1: title = text break out = wikiprint.html_to_pdf(req, [page], book = (output_type == 'pdfbook'), title=title, subject="%s - %s" % (self.env.project_name, page_name), version=str(wikipage.version), date=format_datetime(to_datetime(None))) return (out, 'application/pdf')
def get_project_events(self, project, days, minutes): """ List all events in project that happened in a given time span. """ events = [] project_href = Href(conf.url_projects_path + "/" + project.env_name) req = DummyReq('user', 'password', 'method', 'uri', 'args') req.permissions = ( 'TICKET_VIEW', 'CHANGESET_VIEW', 'WIKI_VIEW', 'ATTACHMENT_VIEW', 'DISCUSSION_VIEW', 'MILESTONE_VIEW') req.authname = 'authname' req.abs_href = project_href project_env = open_environment(conf.getEnvironmentSysPath(project.env_name), use_cache=True) event_provider = ProjectTimelineEvents(project_env) last_events = event_provider.get_timeline_events(req, time_in_days=days, time_in_minutes=minutes) for event in last_events: context = Context(resource=Resource(), href=project_href) context.req = req context.perm = req.perm events.append([project, event, context]) events.sort(lambda x, y: cmp(y[1]['date'], x[1]['date'])) return events
def email_default_context(): class NoEmailViewPerm(MockPerm): def has_permission(self, action, realm_or_resource=None, id=False, version=False): return action != "EMAIL_VIEW" __contains__ = has_permission context = Context(Resource("wiki", "WikiStart"), href=Href("/"), perm=NoEmailViewPerm()) context.req = None # 0.12 FIXME .req shouldn't be required by formatter return context
def wikipage_to_html(self, text, page_name, req): """ Converts a wiki text to HTML, and makes some replacements in order to fix internal and external links and references """ self.env.log.debug('WikiPrint => Start function wikipage_to_html') #Remove exclude expressions for r in EXCLUDE_RES: text = r.sub('', text) #Escape [[PageOutline]], to avoid wiki processing for r in [re.compile(r'\[\[TOC(\(.*\))?\]\]'), re.compile(r'\[\[PageOutline(\(.*\))?\]\]')]: text = r.sub('![[pdf-toc]]', text) for macro in self.omit_macros: r = re.compile(r'\[\[' + macro + r'\(.*?\]\]') text = r.sub('', text) r = re.compile(r'^\{\{\{\r?\n#!' + macro + r'\r?\n(^.*\r?\n)*?^\}\}\}', re.MULTILINE) text = r.sub('', text) link_format = req.args.get('link_format', None) if self.omit_links: r1 = re.compile(r'\[wiki:(.*?) (.*?)\]') text = r1.sub('[\g<2>]', text) r2 = re.compile(r'\[wiki:(.*?)\]') text = r2.sub('[\g<1>]', text) elif link_format: #Keep links to the same export format r = re.compile(r'(?<=\[wiki:)(.*?)(?=(?: .*?)?\])') text = r.sub('\g<1>?format=%s&link_format=%s' % (link_format, link_format), text) if self.rebase_links: r = re.compile(r'\[wiki:(.*?)\]') text = r.sub('[%s/wiki/\g<1>]' % self.rebase_links, text) self.env.log.debug('WikiPrint => Wiki input for WikiPrint: %r' % text) #First create a Context object from the wiki page context = Context(Resource('wiki', page_name), req.abs_href, req.perm) context.req = req #Now convert in that context page = format_to_html(self.env, context, text) self.env.log.debug('WikiPrint => Wiki to HTML output: %r' % page) self.env.log.debug('WikiPrint => HTML output for WikiPrint is: %r' % page) self.env.log.debug('WikiPrint => Finish function wikipage_to_html') return page
def get_list_pages(request, dbp, obj, resource): require_permission(request.req, resource, dbp.env) artifact_id = request.req.args.get('artifact', None) if artifact_id is None: raise Exception("No artifact was specified.") dbp.load_artifact(artifact_id) artifact = dbp.pool.get_item(artifact_id) results = [] for pagename, page_version_id, ref_count in dbp.get_wiki_page_ref_counts(artifact): page = WikiPage(dbp.env, pagename) results.append( {'href': get_resource_url(dbp.env, page.resource, request.req.href), 'title': pagename, 'date': user_time(request.req, format_datetime, page.time), 'author': page.author, 'excerpt': shorten_result(page.text)} ) data = { 'context': Context.from_request(request.req, resource), 'artifact': artifact, 'results': results, } return 'list_pages.html', data, None
def template_data(self, req, query, kwargs=None): db = self.env.get_read_db() tickets = query.execute(req, db) filtered_tickets = [ticket for ticket in tickets if 'TICKET_VIEW' in req.perm(Ticket(self.env, int(ticket['id'])).resource)] context = Context.from_request(req, 'query') return query.template_data(context, filtered_tickets, None, datetime.now(req.tz), req)
def _handle_builder(self, req): m = self.BUILDER_RE.match(req.path_info) try: builder = m.group(1) or None except Exception as e: builder = None master = BuildBotSystem(self.buildbot_url) if builder is None: data = {'names': master.getAllBuilders()} return 'bbw_allbuilders.html', data, 'text/html' else: class Foo: pass b = Foo() b.name = str(builder) b.current = 'CURRENT-TEXT' b.recent = [] b.slaves = [] data = {'builder': b} try: master = BuildBotSystem(self.buildbot_url) data = {'builder': master.getBuilder(builder)} except Exception as e: print('Error fetching builder stats', e) data['context'] = Context.from_request(req, ('buildbot', builder)) return 'bbw_builder.html', data, 'text/html'
def get_edit_artifact(request, dbp, obj, resource): require_permission(request.req, resource, dbp.env, operation="MODIFY") assert(isinstance(obj, Instance)) # otherwise, we're trying to edit something that is not an artifact aa_attributes = [name for name, val in obj.get_values()] attr_suggestions = [attr.name for attr in obj.get_attributes() if not attr.name in aa_attributes] values = [] for name,val in obj.get_values(): if type(val) is list: for v in val: values.append((str(uuid.uuid4()), name, v)) else: values.append((str(uuid.uuid4()), name, val)) # track access dbp.track_it("artifact", obj.get_id(), "edit", request.req.authname, str(datetime.now())) data = { 'context': Context.from_request(request.req, resource), 'spec_name': obj.__class__.get_name() if not obj.__class__ == Instance else "", 'artifact': obj, 'artifact_values': values, 'attr_suggestions' : attr_suggestions, 'default': obj.str_attr, 'url_path': request.req.href.customartifacts('artifact', obj.get_id()), } return 'edit_artifact_%s.html' % (request.get_format(),), data, None
def process_request(self, req): # Allow all POST requests (with a valid __FORM_TOKEN, ensuring that # the client has at least some permission). Additionally, allow GET # requests from TRAC_ADMIN for testing purposes. if req.method != 'POST': req.perm.require('TRAC_ADMIN') # @todo: Embed "tips" within the rendered output for the editor # (recognize TracLinks, table-stuff, macros, processors) # @todo: Save the content in server-side user-specific field for recovery realm = req.args.get('realm', 'wiki') id = req.args.get('id') version = req.args.get('version') if version is not None: try: version = int(version) except ValueError: version = None text = req.args.get('text', '') flavor = req.args.get('flavor') options = {} if 'escape_newlines' in req.args: options['escape_newlines'] = bool(int(req.args['escape_newlines'] or 0)) if 'shorten' in req.args: options['shorten'] = bool(int(req.args['shorten'] or 0)) resource = Resource(realm, id=id, version=version) context = Context.from_request(req, resource) rendered = format_to(self.env, flavor, context, text, **options) req.send(rendered.encode('utf-8'))
def _convert_query(self, req, query, sheet_query=True, sheet_history=False): # no paginator query.max = 0 query.has_more_pages = False query.offset = 0 # extract all fields except custom fields cols = ['id'] cols.extend(f['name'] for f in query.fields if not f.get('custom')) cols.extend(name for name in ('time', 'changetime') if name not in cols) query.cols = cols if hasattr(self.env, 'get_read_db'): db = self.env.get_read_db() else: db = self.env.get_db_cnx() tickets = query.execute(req, db) # add custom fields to avoid error to join many tables custom_fields = [f['name'] for f in query.fields if f.get('custom')] self._fill_custom_fields(tickets, custom_fields, db) context = Context.from_request(req, 'query', absurls=True) cols.extend([name for name in custom_fields if name not in cols]) data = query.template_data(context, tickets) book = Workbook(encoding='utf-8', style_compression=1) if sheet_query: self._create_sheet_query(req, context, data, book) if sheet_history: self._create_sheet_history(req, context, data, book) return get_workbook_content(book), 'application/vnd.ms-excel'
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 _do_ticket(self, req, template, data, content_type): if 'ticket' in data and 'linked_tickets' in data: ticket = data['ticket'] context = Context.from_request(req, ticket.resource) # Add name:#n links to link fields of Ticket instance when # flowing from storage to browser if req.method == 'GET': RemoteLinksProvider(self.env).augment_ticket(ticket) # Rerender link fields for field in data['fields']: if field['type'] == 'link': name = field['name'] field['rendered'] = format_to_oneliner(self.env, context, ticket[name]) # Add RemoteTicket objects for linked issues table, and pass list # of rejects that could not be retrieved linked_tickets, linked_rejects = self._remote_tickets(ticket, context) data['linked_tickets'].extend(linked_tickets) data['linked_rejects'].extend(linked_rejects) # Provide list of remote sites if newlinked form options are present if 'newlinked_options' in data: remote_sites = RemoteTicketSystem(self.env).get_remote_tracs() data['remote_sites'] = remote_sites return (template, data, content_type)
def pre_process_request(self, req, handler): if req.path_info.startswith('/wiki'): if req.method == 'POST' and req.args.get('action','view') == 'view': post_handler = None for poster in self.macro_posters: if not hasattr(poster, 'match_macro_post'): continue rv = poster.match_macro_post(req) if isinstance(rv, (str, unicode)): rv = rv in req.args.keys() if rv: post_handler = poster break if post_handler: post_handler.process_macro_post(req) else: # Silly stuff here self.log.debug('MacroPostModule: Unclaimed POST, scanning page %s', req.path_info[6:]) page = WikiPage(self.env, req.path_info[6:]) matches = self.macro_re.findall(page.text) + self.proc_re.findall(page.text) for name in matches: self.log.debug('MacroPostModule: Found macro "%s"', name) resource = Resource('wiki', name) context = Context.from_request(req, resource) wp = WikiProcessor(Formatter(self.env, context), name) if wp.macro_provider is None: self.log.debug('MacroPostModule: Invalid name!!! How did that happen') continue if hasattr(wp.macro_provider, 'process_macro_post') and \ not hasattr(wp.macro_provider, 'match_macro_post'): wp.macro_provider.process_macro_post(req) req.environ['REQUEST_METHOD'] = 'GET' # Revert back to a GET return handler
def wiki_to_html(self, wikitext, req): self.env.log.debug('start function wiki_to_html') # pylint: disable-msg=E1101 # Remove some macros (TOC is better handled in ODT itself) for macro in self.remove_macros: wikitext = re.sub('\[\[%s(\([^)]*\))?\]\]' % macro, "", wikitext) # Now convert wiki to HTML out = StringIO() context = Context.from_request(req, absurls=True) Formatter(self.env, # pylint: disable-msg=E1101 context('wiki', self.page_name)).format(wikitext, out) html = Markup(out.getvalue()) html = html.encode("utf-8", 'replace') # Clean up the HTML html = re.sub('<span class="icon">.</span>', '', html) # Remove external link icon tidy_options = dict(output_xhtml=1, add_xml_decl=1, indent=1, tidy_mark=0, input_encoding='utf8', output_encoding='utf8', doctype='auto', wrap=0, char_encoding='utf8') html = tidy.parseString(html, **tidy_options) # Replace nbsp with entity: # http://www.mail-archive.com/[email protected]/msg03670.html html = str(html).replace(" ", " ") # Tidy creates newlines after <pre> (by indenting) html = re.sub('<pre([^>]*)>\n', '<pre\\1>', html) return html
def process_request(self, req): # Allow all POST requests (with a valid __FORM_TOKEN, ensuring that # the client has at least some permission). Additionally, allow GET # requests from TRAC_ADMIN for testing purposes. if req.method != 'POST': req.perm.require('TRAC_ADMIN') realm = req.args.get('realm', 'wiki') id = req.args.get('id') version = req.args.get('version') if version is not None: try: version = int(version) except ValueError: version = None text = req.args.get('text', '') flavor = req.args.get('flavor') options = {} if 'escape_newlines' in req.args: options['escape_newlines'] = bool(int(req.args['escape_newlines'] or 0)) if 'shorten' in req.args: options['shorten'] = bool(int(req.args['shorten'] or 0)) resource = Resource(realm, id=id, version=version) context = Context.from_request(req, resource) rendered = format_to(self.env, flavor, context, text, **options) req.send(rendered.encode('utf-8'))
def process_request(self, req): page_name = req.args.get('page_name', '') if page_name == '': page_name = 'index' # Add breadcrumb package_name, filename = self._get_package_and_filename(page_name) index_file = filename[:-len('.txt')] + '/index.txt' if not self._resource_exists(package_name, filename): # check if it is a folder, then match with index if self._resource_exists(package_name, index_file): filename = index_file else: error_msg = _('Page %(page_name)s not found', page_name=page_name) raise ResourceNotFound(error_msg) utf8_string = resource_string(package_name, filename) help_contents = utf8_string.decode('UTF-8') # add navigation bar self._set_prev_next(req) data = dict(context=Context.from_request(req), contents=help_contents, page_name=page_name) return ('agilo_help.html', data, 'text/html')
def refresh_project(self, project_identifier, from_date, to_date=datetime.now(datefmt.localtz), afilters=None, update=False): """ Will refresh a project events in cache in given date range. .. NOTE:: Dates needs to be given in datefmt.localtz form """ # Initialize objects project = Project.get(env_name=project_identifier) if not project: conf.log.warning('Project {0} is already removed from system or it cannot be found'.format(project_identifier)) return e = open_environment(conf.getEnvironmentSysPath(project.env_name), use_cache=True) pte = ProjectTimelineEvents(e) providers = pte.event_providers project_href = Href(conf.url_projects_path + '/' + project.env_name) context = Context(resource=Resource(), href=project_href) req = self._create_dummy_req(project_identifier) context.req = req context.perm = req.perm # Read events from timeline events = [] for provider in providers: try: # Use filters in parameter or check filters from providers if afilters: filters = afilters else: available_filters = provider.get_timeline_filters(req) or [] filters = [f[0] for f in available_filters] for event in provider.get_timeline_events(req, from_date, to_date, filters): event_data = self._event_data(provider, event) if event_data['author'] != 'trac': # Skip system events events.append(CachedEvent.from_event_data(project, event_data, context, filters[0])) except: conf.log.error("Could not read timeline events for %s from %s" % (project_identifier, str(provider))) # Write events into sql table self._write_events_into_cache(events, update)
def _get_welcome_page(self, req): rendered_page = None wiki_welcome = WikiPage(self.env, 'WelcomePage') if wiki_welcome: context = Context.from_request(req, wiki_welcome.resource) wiki_renderer = WikiTextRenderer(self.env) rendered_page = wiki_renderer.render(context, 'text/x-trac-wiki', wiki_welcome.text) return rendered_page
def setup_widget(self, widgetns): r"""(Insert | update) the IWidgetProvider in the global namespace. @param widgetns widget name. @throws RuntimeError if a widget with requested name cannot be found. """ globs = self.globalns globs['ctx'] = Context.from_request(globs['req']) globs['auth_ctx'] = Context.from_request(globs['auth_req']) for wp in self.dbsys.providers : if widgetns in set(wp.get_widgets()) : globs['widget'] = wp break else : raise InvalidIdentifier('Cannot load widget provider for %s' % widgetns)
def setUp(self): """ Set up the testing environment. """ self.env = EnvironmentStub(enable=[CodeExample]) self.req = Mock(base_path='', chrome={}, args={}, abs_href=Href('/'), href=Href('/'), session={}, perm=[], authname=None, tz=None, locale='utf-8') self.context = Context.from_request(self.req)
def get_html(self, req): """ Get the raw content from the repository and convert to html. """ mime_type, chunk = self.get_raw() if not mime_type.startswith('text'): raise TracError("Invalid mime-type: %s" % mime_type) # Hack to support images, we change the path from relative # the document being requested to absolute. # 1: Ignore http and ftp urls to allow images to be fetched # 2: Assume URLS beginning with "/" are relative to top-level # 3: Assume URLS that do not include "http/ftp" are relative to # current path. def fixup(m): text = m.group(1) if text.startswith('http:') or text.startswith('ftp:'): return m.group(0) if text.startswith('/'): text = text[1:] dir = self.dir if dir.endswith('/'): dir = dir[:-1] return '.. image:: %s/%s' % (req.href.docs(dir), text) chunk = re.sub('\.\. image\:\:\s*(\S+)', fixup, chunk, re.MULTILINE) # Assume all wiki pages are ReStructuredText documents result = self.mimeview.render(Context.from_request(req), mime_type, chunk) if not isinstance(result, (str, unicode)): result = unicode(result) # Hack to pretty-print source code (assumes all literal-blocks # contain source code). result = result.replace('<pre class="literal-block">', '<pre class="literal-block prettyprint">') if 'prettyprint' in result: # FIXME: Add as an event listener instead? result += """ <script type="text/javascript"> var origOnLoad = window.onload; function onLoad() { if (origOnLoad) { if(typeof(origOnLoad) == "string") { eval(origOnLoad); } else { origOnLoad(); } } prettyPrint(); } window.onload = onLoad; </script> """ return Markup(result)
def render_admin_panel(self, req, cat, page, path_info): """ Returns the admin view and handles the form actions """ req.perm.require('TRAC_ADMIN') project = Project.get(self.env) if req.method == 'POST': myprojects_url = conf.url_home_path + "/myprojects" conf.log.exception("Redirect URL: %s" % myprojects_url) thisurl = Href(req.base_path)(req.path_info) context = Context.from_request(req) # Handle project remove if 'remove' in req.args: # Don't allow removing home if project.env_name == conf.sys_home_project_name: add_warning(req, 'Cannot remove home project') return req.redirect(thisurl) # Archive the project before removal archive = ProjectArchive() archived = archive.archive(project) if not archived: add_warning(req, 'Could not archive project "%s". Will not remove the project' % project.project_name) return req.redirect(thisurl) # Notify listeners. The project object still exists, but database does not for listener in self.project_change_listeners: listener.project_archived(project) # Do the actual project removal projects = Projects() if projects.remove_project(project): # Notify listeners. The project object still exists, but database does not for listener in self.project_change_listeners: listener.project_deleted(project) # NOTE: We no longer have project tables/session etc available req.redirect(myprojects_url) else: add_warning(req, 'Could not remove project "%s". Try again later' % project.project_name) return req.redirect(thisurl) add_script(req, 'multiproject/js/jquery-ui.js') add_script(req, 'multiproject/js/multiproject.js') add_script(req, 'multiproject/js/admin_system.js') add_stylesheet(req, 'multiproject/css/jquery-ui.css') # NOTE: Trac automatically puts 'project' dict in chrome thus it cannot be used data = {'multiproject': { 'project':project, 'home_url':conf.url_home_path }} return 'admin_system.html', data
def process_request(self, req): """ Process request for listing, creating and removing projects """ add_script(req, 'multiproject/js/summary.js') data = {} if req.authname == 'anonymous': req.perm.require('PROJECT_VIEW') if not ('PROJECT_VIEW' in req.perm or 'PROJECT_PRIVATE_VIEW' in req.perm): return 'no_access.html', data, None # Load project from db project = Project.get(self.env) if req.args.get('action') == 'timeline_events': return self.get_summary_timeline_events(req, project.created) # TODO: Move project timeline implementation into macro # Get recent timeline events # TODO: Move project downloads implementation into macro # Get list of featured downloads downloads = [] if self.env.is_component_enabled('tracdownloads.api.DownloadsApi'): api = None # In case of import error we don't have it, so don't return any downloads try: from tracdownloads.api import DownloadsApi api = DownloadsApi(self.env) except ImportError: self.log.warning("Unable to import DownloadsApi, but it's enabled.") if api: downloads = api.get_summary_items() # Load wiki:SummaryPage and show it as a main page summary_content = None summary_wiki = WikiPage(self.env, 'SummaryPage') if summary_wiki: context = Context.from_request(req, summary_wiki.resource) wiki_renderer = WikiTextRenderer(self.env) summary_content = wiki_renderer.render(context, 'text/x-trac-wiki', summary_wiki.text) data = { '_project_': project, # project object of a project we are in 'downloads': downloads, # list of featured downloads 'wiki_summary': summary_content, # Rendered content of the SummaryPage 'is_summary': True, 'env': self.env, } return 'summary.html', data, None
def setUp(self): env = EnvironmentStub(enable=[Chrome, PatchRenderer]) req = Mock(base_path='', chrome={}, args={}, session={}, abs_href=Href('/'), href=Href('/'), perm=MockPerm(), authname=None, tz=None) self.context = Context.from_request(req) self.patch = Mimeview(env).renderers[0] patch_html = open(os.path.join(os.path.split(__file__)[0], 'patch.html')) self.patch_html = Stream(list(HTMLParser(patch_html)))
def setUp(self): self.env = EnvironmentStub(enable=[Chrome, PygmentsRenderer]) self.pygments = Mimeview(self.env).renderers[0] self.req = Mock(base_path='', chrome={}, args={}, abs_href=Href('/'), href=Href('/'), session={}, perm=None, authname=None, tz=None) self.context = Context.from_request(self.req) pygments_html = open(os.path.join(os.path.split(__file__)[0], 'pygments.html')) self.pygments_html = Stream(list(HTMLParser(pygments_html)))
def _export_ticket(self, req, ticket, writer, field_names): cols = [unicode(ticket.id)] for name in field_names: value = ticket[name] or '' if name in ('cc', 'reporter'): context = Context.from_request(req, ticket.resource) value = Chrome(self.env).format_emails(context, value, ' ') cols.append(unicode(value).encode('utf-8')) writer.writerow(cols)
def process_request(self, req): req.perm.require('BROWSER_VIEW') presel = req.args.get('preselected') if presel and (presel + '/').startswith(req.href.browser() + '/'): req.redirect(presel) path = req.args.get('path', '/') rev = req.args.get('rev', '') if rev in ('', 'HEAD'): rev = None order = req.args.get('order', 'name').lower() desc = req.args.has_key('desc') xhr = req.get_header('X-Requested-With') == 'XMLHttpRequest' rm = RepositoryManager(self.env) all_repositories = rm.get_all_repositories() reponame, repos, path = rm.get_repository_by_path(path) # Repository index show_index = not reponame and path == '/' if show_index: if repos and (as_bool(all_repositories[''].get('hidden')) or not repos.can_view(req.perm)): repos = None if not repos and reponame: raise ResourceNotFound(_("Repository '%(repo)s' not found", repo=reponame)) if reponame and reponame != repos.reponame: # Redirect alias qs = req.query_string req.redirect(req.href.browser(repos.reponame or None, path) + (qs and '?' + qs or '')) reponame = repos and repos.reponame or None # Find node for the requested path/rev context = Context.from_request(req) node = None display_rev = lambda rev: rev if repos: try: if rev: rev = repos.normalize_rev(rev) # If `rev` is `None`, we'll try to reuse `None` consistently, # as a special shortcut to the latest revision. rev_or_latest = rev or repos.youngest_rev node = get_existing_node(req, repos, path, rev_or_latest) except NoSuchChangeset, e: raise ResourceNotFound(e.message, _('Invalid changeset number')) context = context(repos.resource.child('source', path, version=rev_or_latest)) display_rev = repos.display_rev
def post_process_request(self, req, template, data, content_type): if template == 'report_view.html' and req.args.get('id'): format = req.args.get('format') if format == 'xls': resource = Resource('report', req.args['id']) data['context'] = Context.from_request(req, resource, absurls=True) self._convert_report(req, data) elif not format: self._add_alternate_links(req) return template, data, content_type
def get_view_spec(request, dbp, obj, resource): require_permission(request.req, resource, dbp.env) # track access dbp.track_it("spec", obj.get_id(), "view", request.req.authname, str(datetime.now())) data = { 'context': Context.from_request(request.req, resource), 'spec': obj, 'artifacts_url': request.req.href.customartifacts(spec=obj.get_name()), } return 'view_spec_page.html', data, None
def process_request(self, req): req.perm.assert_permission('TICKET_VIEW') if 'id' in req.args.keys(): try: ticket = int(req.args.get('id')) except ValueError: raise TracError('Need integer ticket id.') sql = ("SELECT 1 FROM ticket WHERE id=%s" %ticket) db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute(sql) row = cursor.fetchone() if not row: raise TracError('Cannot build dependency graph for non-existent ticket %d.' % ticket) depth = -1 for key in req.args.keys(): if key == 'depth': depth = req.args[key] options = '%s,%s' %(ticket, depth) add_ctxtnav(req, 'Back to Ticket #%s'%ticket, req.href.ticket(ticket)) title = 'Ticket #%s Dependency Graph' %ticket headline = 'Dependency Graph for Ticket #%s' %ticket else: constraints = {} for key in req.args.keys(): if isinstance(req.args[key], (list, tuple)): constraints[key] = '|'.join(req.args[key]) else: constraints[key] = req.args[key] options = 'query:' + '&'.join(key + '=' + constraints[key] for key in constraints) title = 'Ticket query Dependency Graph' headline = 'Dependency Graph for Query' add_ctxtnav(req, 'Back to query', req.href('query', **req.args)) data = {} context = Context.from_request(req, '') formatter = Formatter(self.env, context) graph = DepGraphMacro(self.env).expand_macro(formatter, 'DepGraph', options) data['title'] = title data['headline'] = headline data['depgraph'] = Markup(graph) return 'depgraph.html', data, None
def process_request(self, req): # Allow all POST requests (with a valid __FORM_TOKEN, ensuring that # the client has at least some permission). Additionally, allow GET # requests from TRAC_ADMIN for testing purposes. if req.method != 'POST': req.perm.require('TRAC_ADMIN') # @todo: Embed "tips" within the rendered output for the editor # (recognize TracLinks, table-stuff, macros, processors) # @todo: Save the content in server-side user-specific field for recovery realm = req.args.get('realm', 'wiki') id = req.args.get('id') version = req.args.get('version') if version is not None: try: version = int(version) except ValueError: version = None text = req.args.get('text', '') flavor = req.args.get('flavor') options = {} if 'escape_newlines' in req.args: options['escape_newlines'] = bool(int(req.args['escape_newlines'] or 0)) if 'shorten' in req.args: options['shorten'] = bool(int(req.args['shorten'] or 0)) resource = Resource(realm, id=id, version=version) context = Context.from_request(req, resource) rendered = format_to_cke_html(self.env, context, text, self.code_styles, **options) # since Trac renders underlined text as `<span class="underlined">text</span> # instead of u-tag, we need to adjust it for compatibility's sake # see also discussion at Google Groups: # https://groups.google.com/group/trac-dev/browse_thread/thread/833206a932d1f918 html = HTML(rendered) html |= Transformer('//span[@class="underline"]').rename('u').attr('class', None) # CKEditor renders indentation by using p style="margin-left: 40px" # instead of blockquote-tag html |= Transformer('//blockquote/p').attr('style', 'margin-left: 40px') html |= Transformer('//blockquote').unwrap() buffer = StringIO() html.render(out=buffer, encoding='utf-8') req.send( buffer.getvalue() )
def _wiki_view(self, req, stream): add_stylesheet(req, 'tags/css/tractags.css') tags = self._page_tags(req) if not tags: return stream li = [] for tag_ in tags: resource = Resource('tag', tag_) anchor = render_resource_link(self.env, Context.from_request(req, resource), resource) anchor = anchor(rel='tag') li.append(tag.li(anchor, ' ')) # TRANSLATOR: Header label text for tag list at wiki page bottom. insert = tag.ul(class_='tags')(tag.li(_("Tags"), class_='header'), li) return stream | (Transformer('//div[contains(@class,"wikipage")]') .after(insert))
def new_csv_export(self, req, query, sep=',', mimetype='text/plain'): self.log.debug( "T&E plugin has overridden QueryModule.csv_export so to enforce field permissions" ) ## find the columns that should be hidden hidden_fields = [] fields = self.config.getlist(csection, 'fields', []) self.log.debug('QueryModule.csv_export: found : %s' % fields) for field in fields: perms = self.config.getlist(csection, '%s.permission' % field, []) #self.log.debug('QueryModule.csv_export: read permission config: %s has %s' % (field, perms)) for (perm, denial) in [s.split(":") for s in perms]: perm = perm.upper() #self.log.debug('QueryModule.csv_export: testing permission: %s:%s should act= %s' % # (field, perm, (not req.perm.has_permission(perm) or perm == "ALWAYS"))) if (not req.perm.has_permission(perm) or perm == "ALWAYS") and denial.lower() in ["remove", "hide"]: hidden_fields.append(field) ## END find the columns that should be hidden content = StringIO() cols = query.get_columns() writer = csv.writer(content, delimiter=sep) writer = csv.writer(content, delimiter=sep, quoting=csv.QUOTE_MINIMAL) writer.writerow( [unicode(c).encode('utf-8') for c in cols if c not in hidden_fields]) context = Context.from_request(req) results = query.execute(req, self.env.get_db_cnx()) self.log.debug('QueryModule.csv_export: hidding columns %s' % hidden_fields) for result in results: ticket = Resource('ticket', result['id']) if 'TICKET_VIEW' in req.perm(ticket): values = [] for col in cols: if col not in hidden_fields: value = result[col] if col in ('cc', 'reporter'): value = Chrome(self.env).format_emails( context(ticket), value) values.append(unicode(value).encode('utf-8')) writer.writerow(values) return (content.getvalue(), '%s;charset=utf-8' % mimetype)
def filter_stream(self, req, method, filename, stream, data): if filename != 'ticket.html' and (filename != 'typedticket.html'): return stream ticket = data['ticket'] id = ticket.id try: int(id) isint = 1 except: isint = 0 if isint == 1: content = "[[TicketBackLinks(%d)]]" % ticket.id content = WikiTextRenderer(self.env).render( Context.from_request(req), 'text/x-trac-wiki', content) stream |= Transformer("//div[@class='description']").after( tag.div(content, **{'class': "description"})) return stream
def get_edit_artifact(request, dbp, obj, resource): require_permission(request.req, resource, dbp.env, operation="MODIFY") assert ( isinstance(obj, Instance) ) # otherwise, we're trying to edit something that is not an artifact aa_attributes = [name for name, val in obj.get_values()] attr_suggestions = [ attr.name for attr in obj.get_attributes() if not attr.name in aa_attributes ] values = [] for name, val in obj.get_values(): if type(val) is list: for v in val: values.append((str(uuid.uuid4()), name, v)) else: values.append((str(uuid.uuid4()), name, val)) # track access dbp.track_it("artifact", obj.get_id(), "edit", request.req.authname, str(datetime.now())) data = { 'context': Context.from_request(request.req, resource), 'spec_name': obj.__class__.get_name() if not obj.__class__ == Instance else "", 'artifact': obj, 'artifact_values': values, 'attr_suggestions': attr_suggestions, 'default': obj.str_attr, 'url_path': request.req.href.customartifacts('artifact', obj.get_id()), } return 'edit_artifact_%s.html' % (request.get_format(), ), data, None
def process_request(self, req): path = req.path_info[len("/watchlist/manual"):].strip('/') if path.startswith('attachments'): return self.handle_attachment(req, path) language = path if not language: language = req.session.get('language', 'en-US') # Try to find a suitable language if no manual exists # in the requested one. if language not in self.manuals: # Try to find a main language, # e.g. 'xy' instead of 'xy-ZV' l = language.split('-')[0] language = 'en-US' # fallback if no other is found if l in self.manuals: language = l else: # Prefer 'en-US' before any other English dialect if l == 'en' and 'en-US' in self.manuals: language = 'en-US' else: # If there is none try to find # any other 'xy-*' language l += '-' for lang in sorted(self.manuals.keys()): if lang.startswith(l): language = lang break req.redirect(req.href.watchlist('manual', language)) try: f = open(self.manuals[language], 'r') text = to_unicode(f.read()) except Exception as e: raise HTTPNotFound(e) wldict = dict(format_text=lambda text: format_to_html( self.env, Context.from_request(req), text), text=text) return ("watchlist_manual.html", wldict, "text/html")
def process_request(self, req): """Initially this will render static widgets. With time it will be more and more dynamic and flexible. """ if self.env[QueryModule] is not None: add_ctxtnav(req, _('Custom Query'), req.href.query()) if self.env[ReportModule] is not None: add_ctxtnav(req, _('Reports'), req.href.report()) template, layout_data = self.expand_layout_data(req, 'bootstrap_grid', self.DASHBOARD_SCHEMA) widgets = self.expand_widget_data(req, layout_data) return template, { 'context' : Context.from_request(req), 'layout' : layout_data, 'widgets' : widgets, 'title' : _(self.mainnav_label), 'default' : { 'height' : self.default_widget_height or None } }, None
def get_new_artifact(request, dbp, obj, resource): require_permission(request.req, resource, dbp.env, operation="CREATE") assert ( obj is Instance or isinstance(obj, Entity) ) # otherwise, we're trying to instantiate something that is not an artifact attr_suggestions = [attr.name for attr in obj.get_attributes()] # track access dbp.track_it("artifact", obj.get_id(), "new", request.req.authname, str(datetime.now())) data = { 'context': Context.from_request(request.req, resource), 'spec_name': obj.get_name() if not obj == Instance else "", 'attr_suggestions': attr_suggestions, 'url_path': request.req.href.customartifacts('artifact'), } return 'edit_artifact_%s.html' % (request.get_format(), ), data, None
def pre_process_request(self, req, handler): if req.path_info.startswith('/wiki'): if req.method == 'POST' and req.args.get('action', 'view') == 'view': post_handler = None for poster in self.macro_posters: if not hasattr(poster, 'match_macro_post'): continue rv = poster.match_macro_post(req) if isinstance(rv, (str, unicode)): rv = rv in req.args.keys() if rv: post_handler = poster break if post_handler: post_handler.process_macro_post(req) else: # Silly stuff here self.log.debug( 'MacroPostModule: Unclaimed POST, scanning page %s', req.path_info[6:]) page = WikiPage(self.env, req.path_info[6:]) matches = self.macro_re.findall( page.text) + self.proc_re.findall(page.text) for name in matches: self.log.debug('MacroPostModule: Found macro "%s"', name) resource = Resource('wiki', name) context = Context.from_request(req, resource) wp = WikiProcessor(Formatter(self.env, context), name) if wp.macro_provider is None: self.log.debug( 'MacroPostModule: Invalid name!!! How did that happen' ) continue if hasattr(wp.macro_provider, 'process_macro_post') and \ not hasattr(wp.macro_provider, 'match_macro_post'): wp.macro_provider.process_macro_post(req) req.environ['REQUEST_METHOD'] = 'GET' # Revert back to a GET return handler
def _check_redirect(self, req): """Checks if the request should be redirected.""" if req.path_info == '/' or req.path_info == '/wiki': wiki = 'WikiStart' elif not req.path_info.startswith('/wiki/'): return False else: wiki = req.path_info[6:] wp = WikiPage(self.env, wiki, req.args.get('version')) if not wp.exists: return False # Check for redirect "macro": m = MACRO.match(wp.text) if not m: return False wikitarget = m.groups()[0] self.redirect_target = extract_url(self.env, Context.from_request(req), wikitarget) if not self.redirect_target: self.redirect_target = req.href.wiki(wikitarget) return True
def expand_widget_data(self, req, schema): """Expand raw widget data and format it for use in template """ # TODO: Implement dynamic dashboard specification widgets_spec = schema.get('widgets', {}) widgets_index = dict([wnm, wp] \ for wp in DashboardSystem(self.env).widget_providers \ for wnm in wp.get_widgets() ) self.log.debug("Bloodhound: Widget index %s" % (widgets_index,)) ctx = Context.from_request(req) for w in widgets_spec.itervalues(): w['c'] = widgets_index.get(w['args'][0]) w['args'][1] = ctx self.log.debug("Bloodhound: Widget specs %s" % (widgets_spec,)) chrome = Chrome(self.env) render = chrome.render_template data_strm = ( (k, w, self._render_widget(w['c'], *w['args'])) \ for k,w in widgets_spec.iteritems()) return dict([k, {'title' : data['title'], 'content' : render(wctx.req, template, data['data'], fragment=True), 'ctxtnav' : w.get('ctxtnav', True) and data.get('ctxtnav') or None, 'altlinks' : w.get('altlinks', True) and data.get('altlinks') or None}] \ for k, w, (template, data, wctx) in data_strm)
def get_edit_spec(request, dbp, obj, resource): require_permission(request.req, resource, dbp.env, operation="MODIFY") assert (obj is Instance or isinstance(obj, Entity)) # track access dbp.track_it("spec", obj.get_id(), "edit", request.req.authname, str(datetime.now())) data = { 'context': Context.from_request(request.req, resource), 'spec': obj, 'attributes': [(str(uuid.uuid4()), attr.name, attr.owner_spec, attr.get_type_readable(), attr.get_multiplicity_readable()) for attr in obj.get_attributes()], 'types': ['Text', 'Number'], # , 'Adaptive Artifact' 'multiplicities': ['1', '0..*', '1..*'], 'url_path': request.req.href.customartifacts('spec', obj.get_name()), } return 'edit_spec_page.html', data, None
def wiki_to_html(self, wikitext, req): self.env.log.debug( 'start function wiki_to_html') # pylint: disable-msg=E1101 # Remove some macros (TOC is better handled in ODT itself) for macro in self.remove_macros: wikitext = re.sub('\[\[%s(\([^)]*\))?\]\]' % macro, "", wikitext) # Now convert wiki to HTML out = StringIO() context = Context.from_request(req, absurls=True) Formatter( self.env, # pylint: disable-msg=E1101 context('wiki', self.page_name)).format(wikitext, out) html = Markup(out.getvalue()) html = html.encode("utf-8", 'replace') # Clean up the HTML html = re.sub('<span class="icon">.</span>', '', html) # Remove external link icon tidy_options = dict(output_xhtml=1, add_xml_decl=1, indent=1, tidy_mark=0, input_encoding='utf8', output_encoding='utf8', doctype='auto', wrap=0, char_encoding='utf8') html = tidy.parseString(html, **tidy_options) # Replace nbsp with entity: # http://www.mail-archive.com/[email protected]/msg03670.html html = str(html).replace(" ", " ") # Tidy creates newlines after <pre> (by indenting) html = re.sub('<pre([^>]*)>\n', '<pre\\1>', html) return html
if len(v) == 1: args[k] = v[0] except TypeError: pass args.update({'page': page, 'max': maxrows}) qrymdl = self.env[QueryModule] if qrymdl is None: raise TracError('Query module not available (disabled?)') data = qrymdl.process_request(fakereq)[1] except TracError, exc: if data is not None: exc.title = data.get('title', 'TracQuery') raise else: qryctx = Context.from_request(fakereq) query = data['query'] idxs = count() headers = [dict(title=h['label'], col=h['name'], hidden=False, asc=h['name'] == query.order and not query.desc) \ for h in data['headers']] data.update( dict(header_groups=[headers], numrows=len(data['tickets']), row_groups=[(group_value, [{ '__color__' : t['priority_value'], '__idx__' : idxs.next(), 'cell_groups' : [[ { 'header' : h,
def display_html(self, req, query): """returns the HTML according to a query for /hours view""" db = self.env.get_db_cnx() # The most recent query is stored in the user session; orig_list = None orig_time = datetime.now(utc) query_time = int(req.session.get('query_time', 0)) query_time = datetime.fromtimestamp(query_time, utc) query_constraints = unicode(query.constraints) if query_constraints != req.session.get('query_constraints') \ or query_time < orig_time - timedelta(hours=1): tickets = query.execute(req, db) # New or outdated query, (re-)initialize session vars req.session['query_constraints'] = query_constraints req.session['query_tickets'] = ' '.join( [str(t['id']) for t in tickets]) else: orig_list = [ int(id) for id in req.session.get('query_tickets', '').split() ] tickets = query.execute(req, db, orig_list) orig_time = query_time context = Context.from_request(req, 'query') ticket_data = query.template_data(context, tickets, orig_list, orig_time, req) # For clients without JavaScript, we add a new constraint here if # requested constraints = ticket_data['clauses'][0] if 'add' in req.args: field = req.args.get('add_filter') if field: constraint = constraints.setdefault(field, {}) constraint.setdefault('values', []).append('') # FIXME: '' not always correct (e.g. checkboxes) req.session['query_href'] = query.get_href(context.href) req.session['query_time'] = to_timestamp(orig_time) req.session['query_tickets'] = ' '.join( [str(t['id']) for t in tickets]) # data dictionary for genshi data = {} # get data for saved queries query_id = req.args.get('query_id') if query_id: try: query_id = int(query_id) except ValueError: add_warning( req, "query_id should be an integer, you put '%s'" % query_id) query_id = None if query_id: data['query_id'] = query_id query_data = self.get_query(query_id) data['query_title'] = query_data['title'] data['query_description'] = query_data['description'] data.setdefault('report', None) data.setdefault('description', None) data['all_columns'] = query.get_all_columns() + self.get_columns() # Don't allow the user to remove the id column data['all_columns'].remove('id') data['all_textareas'] = query.get_all_textareas() # need to re-get the cols because query will remove our fields cols = req.args.get('col') if isinstance(cols, basestring): cols = [cols] if not cols: cols = query.get_columns() + self.get_default_columns() data['col'] = cols now = datetime.now() # get the date range for the query if 'from_year' in req.args: from_date = get_date(req.args['from_year'], req.args.get('from_month'), req.args.get('from_day')) else: from_date = datetime(now.year, now.month, now.day) from_date = from_date - timedelta(days=7) # 1 week ago, by default if 'to_year' in req.args: to_date = get_date(req.args['to_year'], req.args.get('to_month'), req.args.get('to_day'), end_of_day=True) else: to_date = now data['prev_week'] = from_date - timedelta(days=7) data['months'] = list(enumerate(calendar.month_name)) data['years'] = range(now.year, now.year - 10, -1) data['days'] = range(1, 32) data['users'] = get_all_users(self.env) data['cur_worker_filter'] = req.args.get('worker_filter', '*any') data['from_date'] = from_date data['to_date'] = to_date ticket_ids = [t['id'] for t in tickets] # generate data for ticket_times time_records = self.get_ticket_hours( ticket_ids, from_date=from_date, to_date=to_date, worker_filter=data['cur_worker_filter']) data['query'] = ticket_data['query'] data['context'] = ticket_data['context'] data['row'] = ticket_data['row'] if 'comments' in req.args.get('row', []): data['row'].append('comments') data['constraints'] = ticket_data['clauses'] our_labels = dict([(f['name'], f['label']) for f in self.fields]) labels = TicketSystem(self.env).get_ticket_field_labels() labels.update(our_labels) data['labels'] = labels order = req.args.get('order') desc = bool(req.args.get('desc')) data['order'] = order data['desc'] = desc headers = [{ 'name': col, 'label': labels.get(col), 'href': self.get_href(query, req.args, context.href, order=col, desc=(col == order and not desc)) } for col in cols] data['headers'] = headers data['fields'] = ticket_data['fields'] data['modes'] = ticket_data['modes'] # group time records time_records_by_ticket = {} for record in time_records: id = record['ticket'] if id not in time_records_by_ticket: time_records_by_ticket[id] = [] time_records_by_ticket[id].append(record) data['extra_group_fields'] = dict(ticket=dict(name='ticket', type='select', label='Ticket'), worker=dict(name='worker', type='select', label='Worker')) num_items = 0 data['groups'] = [] # merge ticket data into ticket_time records for key, tickets in ticket_data['groups']: ticket_times = [] total_time = 0 total_estimated_time = 0 for ticket in tickets: records = time_records_by_ticket.get(ticket['id'], []) [rec.update(ticket) for rec in records] ticket_times += records # sort ticket_times, if needed if order in our_labels: ticket_times.sort(key=lambda x: x[order], reverse=desc) data['groups'].append((key, ticket_times)) num_items += len(ticket_times) data['double_count_warning'] = '' # group by ticket id or other time_ticket fields if necessary if req.args.get('group') in data['extra_group_fields']: query.group = req.args.get('group') if not query.group == "id": data[ 'double_count_warning'] = "Warning: estimated hours may be counted more than once if a ticket appears in multiple groups" tickets = data['groups'][0][1] groups = {} for time_rec in tickets: key = time_rec[query.group] if not key in groups: groups[key] = [] groups[key].append(time_rec) data['groups'] = sorted(groups.items()) total_times = dict( (k, self.format_hours(sum(rec['seconds_worked'] for rec in v))) for k, v in data['groups']) total_estimated_times = {} for key, records in data['groups']: seen_tickets = set() est = 0 for record in records: # do not double-count tickets id = record['ticket'] if id in seen_tickets: continue seen_tickets.add(id) estimatedhours = record.get('estimatedhours') or 0 try: estimatedhours = float(estimatedhours) except ValueError: estimatedhours = 0 est += estimatedhours * 3600 total_estimated_times[key] = self.format_hours(est) data['total_times'] = total_times data['total_estimated_times'] = total_estimated_times # format records for record in time_records: if 'seconds_worked' in record: record['seconds_worked'] = self.format_hours( record['seconds_worked']) # XXX misleading name if 'time_started' in record: record['time_started'] = self.format_date( record['time_started']) if 'time_submitted' in record: record['time_submitted'] = self.format_date( record['time_submitted']) data['query'].num_items = num_items data['labels'] = TicketSystem(self.env).get_ticket_field_labels() data['labels'].update(labels) data['can_add_hours'] = req.perm.has_permission('TICKET_ADD_HOURS') data['multiproject'] = self.env.is_component_enabled(MultiprojectHours) from web_ui import TracUserHours data['user_hours'] = self.env.is_component_enabled(TracUserHours) # return the rss, if requested if req.args.get('format') == 'rss': return self.queryhours2rss(req, data) # return the csv, if requested if req.args.get('format') == 'csv': self.queryhours2csv(req, data) # add rss link rss_href = req.href(req.path_info, format='rss') add_link(req, 'alternate', rss_href, _('RSS Feed'), 'application/rss+xml', 'rss') # add csv link add_link(req, 'alternate', req.href(req.path_info, format='csv', **req.args), 'CSV', 'text/csv', 'csv') # add navigation of weeks prev_args = dict(req.args) next_args = dict(req.args) prev_args['from_year'] = (from_date - timedelta(days=7)).year prev_args['from_month'] = (from_date - timedelta(days=7)).month prev_args['from_day'] = (from_date - timedelta(days=7)).day prev_args['to_year'] = from_date.year prev_args['to_month'] = from_date.month prev_args['to_day'] = from_date.day next_args['from_year'] = to_date.year next_args['from_month'] = to_date.month next_args['from_day'] = to_date.day next_args['to_year'] = (to_date + timedelta(days=7)).year next_args['to_month'] = (to_date + timedelta(days=7)).month next_args['to_day'] = (to_date + timedelta(days=7)).day add_link(req, 'prev', self.get_href(query, prev_args, context.href), _('Prev Week')) add_link(req, 'next', self.get_href(query, next_args, context.href), _('Next Week')) prevnext_nav(req, _('Prev Week'), _('Next Week')) add_ctxtnav(req, 'Cross-Project Hours', req.href.hours('multiproject')) add_ctxtnav( req, 'Hours by User', req.href.hours('user', from_day=from_date.day, from_month=from_date.month, from_year=from_date.year, to_day=to_date.year, to_month=to_date.month, to_year=to_date.year)) add_ctxtnav(req, 'Saved Queries', req.href.hours('query/list')) add_stylesheet(req, 'common/css/report.css') add_script(req, 'common/js/query.js') return ('hours_timeline.html', data, 'text/html')
def expand_macro(self, formatter, name, text, args=None): assert text.isdigit(), "Argument must be a number" out = "<dl class='lastevents'>" add_stylesheet(formatter.req, 'tracprojectmanager/css/lastevents.css') all_events = [] if hasattr(self.env, 'cached_lastevents'): expiration = self.env.cached_lastevents[1] + timedelta( seconds=EVENT_CACHE_INTERVAL) if datetime.now() < expiration: all_events = self.env.cached_lastevents[0] if not all_events: stop = datetime.now(formatter.req.tz) start = stop - timedelta(days=EVENT_MAX_DAYS) projects = get_project_list(self.env, formatter.req) user = formatter.req.authname for project, project_path, project_url, env in projects: env_timeline = TimelineModule(env) for provider in env_timeline.event_providers: filters = [ x[0] for x in provider.get_timeline_filters(formatter.req) ] self.env.log.info("Project %s - Filters: %s", project, filters) try: events = provider.get_timeline_events( formatter.req, start, stop, filters) #self.env.log.info("Event count: %d", len([x for x in events])) for event in events: context = Context(formatter.resource, Href(project_url), formatter.req.perm) context.req = formatter.req #context = Context.from_request(formatter.req) if len(event) == 6: # 0.10 events kind, url, title, date, author, desc = event else: # 0.11 events if len(event) == 5: # with special provider kind, date, author, data, provider = event else: kind, date, author, data = event title = to_unicode( provider.render_timeline_event( context, 'title', event)) url = provider.render_timeline_event( context, 'url', event) desc = to_unicode( provider.render_timeline_event( context, 'description', event)) all_events.append((project, kind, date, title, url, author, desc)) except Exception, ex: #import sys self.env.log.warning("Exception: %s" % traceback.format_exc()) #out = out + "%s<br/>" % traceback.format_exc() all_events.sort(cmp=lambda x, y: x[2] < y[2] and 1 or -1) self.env.cached_lastevents = [all_events, datetime.now()]
class IncludeMacro(WikiMacroBase): """A macro to include other resources in wiki pages. More documentation to follow. """ implements(IPermissionRequestor) # Default output formats for sources that need them default_formats = { 'wiki': 'text/x-trac-wiki', } # IWikiMacroProvider methods 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)) ctxt = Context.from_request(req)
def get_list(self, realm, wl, req, fields=None): db = self.env.get_db_cnx() cursor = db.cursor() context = Context.from_request(req) locale = getattr(req, 'locale', LC_TIME) ticketlist = [] extradict = {} if not fields: fields = set(self.default_fields['ticket']) else: fields = set(fields) if 'changetime' in fields: max_changetime = datetime(1970, 1, 1, tzinfo=utc) min_changetime = datetime.now(utc) if 'time' in fields: max_time = datetime(1970, 1, 1, tzinfo=utc) min_time = datetime.now(utc) for sid, last_visit in wl.get_watched_resources( 'ticket', req.authname): ticketdict = {} try: ticket = Ticket(self.env, sid, db) exists = ticket.exists except: exists = False if not exists: ticketdict['deleted'] = True if 'id' in fields: ticketdict['id'] = sid ticketdict['ID'] = '#' + sid if 'author' in fields: ticketdict['author'] = '?' if 'changetime' in fields: ticketdict['changedsincelastvisit'] = 1 ticketdict['changetime'] = '?' ticketdict['ichangetime'] = 0 if 'time' in fields: ticketdict['time'] = '?' ticketdict['itime'] = 0 if 'comment' in fields: ticketdict['comment'] = tag.strong(t_("deleted"), class_='deleted') if 'notify' in fields: ticketdict['notify'] = wl.is_notify(req, 'ticket', sid) if 'description' in fields: ticketdict['description'] = '' if 'owner' in fields: ticketdict['owner'] = '' if 'reporter' in fields: ticketdict['reporter'] = '' ticketlist.append(ticketdict) continue render_elt = lambda x: x if not (Chrome(self.env).show_email_addresses or \ 'EMAIL_VIEW' in req.perm(ticket.resource)): render_elt = obfuscate_email_address # Copy all requested fields from ticket if fields: for f in fields: ticketdict[f] = ticket.values.get(f, u'') else: ticketdict = ticket.values.copy() changetime = ticket.time_changed if wl.options['attachment_changes']: for attachment in Attachment.select(self.env, 'ticket', sid, db): if attachment.date > changetime: changetime = attachment.date if 'attachment' in fields: attachments = [] for attachment in Attachment.select(self.env, 'ticket', sid, db): wikitext = u'[attachment:"' + u':'.join([ attachment.filename, 'ticket', sid ]) + u'" ' + attachment.filename + u']' attachments.extend([ tag(', '), format_to_oneliner(self.env, context, wikitext, shorten=False) ]) if attachments: attachments.reverse() attachments.pop() ticketdict['attachment'] = moreless(attachments, 5) # Changes are special. Comment, commentnum and last author are included in them. if 'changes' in fields or 'author' in fields or 'comment' in fields or 'commentnum' in fields: changes = [] # If there are now changes the reporter is the last author author = ticket.values['reporter'] commentnum = u"0" comment = u"" want_changes = 'changes' in fields for date, cauthor, field, oldvalue, newvalue, permanent in ticket.get_changelog( changetime, db): author = cauthor if field == 'comment': if 'commentnum' in fields: ticketdict['commentnum'] = to_unicode(oldvalue) if 'comment' in fields: comment = to_unicode(newvalue) comment = moreless(comment, 200) ticketdict['comment'] = comment if not want_changes: break else: if want_changes: label = self.fields['ticket'].get(field, u'') if label: changes.extend([ tag( tag.strong(label), ' ', render_property_diff( self.env, req, ticket, field, oldvalue, newvalue)), tag('; ') ]) if want_changes: # Remove the last tag('; '): if changes: changes.pop() changes = moreless(changes, 5) ticketdict['changes'] = tag(changes) if 'id' in fields: ticketdict['id'] = sid ticketdict['ID'] = format_to_oneliner(self.env, context, '#' + sid, shorten=True) if 'cc' in fields: if render_elt == obfuscate_email_address: ticketdict['cc'] = ', '.join( [render_elt(c) for c in ticketdict['cc'].split(', ')]) if 'author' in fields: ticketdict['author'] = render_elt(author) if 'changetime' in fields: ichangetime = to_timestamp(changetime) ticketdict.update( changetime=format_datetime(changetime, locale=locale, tzinfo=req.tz), ichangetime=ichangetime, changedsincelastvisit=(last_visit < ichangetime and 1 or 0), changetime_delta=pretty_timedelta(changetime), changetime_link=req.href.timeline( precision='seconds', from_=trac_format_datetime(changetime, 'iso8601', tzinfo=req.tz))) if changetime > max_changetime: max_changetime = changetime if changetime < min_changetime: min_changetime = changetime if 'time' in fields: time = ticket.time_created ticketdict.update(time=format_datetime(time, locale=locale, tzinfo=req.tz), itime=to_timestamp(time), time_delta=pretty_timedelta(time), time_link=req.href.timeline( precision='seconds', from_=trac_format_datetime( time, 'iso8601', tzinfo=req.tz))) if time > max_time: max_time = time if time < min_time: min_time = time if 'description' in fields: description = ticket.values['description'] description = moreless(description, 200) ticketdict['description'] = description if 'notify' in fields: ticketdict['notify'] = wl.is_notify(req, 'ticket', sid) if 'owner' in fields: ticketdict['owner'] = render_elt(ticket.values['owner']) if 'reporter' in fields: ticketdict['reporter'] = render_elt(ticket.values['reporter']) if 'tags' in fields and self.tagsystem: tags = [] for t in self.tagsystem.get_tags(req, Resource('ticket', sid)): tags.extend( [tag.a(t, href=req.href('tags', q=t)), tag(', ')]) if tags: tags.pop() ticketdict['tags'] = moreless(tags, 10) ticketlist.append(ticketdict) if 'changetime' in fields: extradict['max_changetime'] = format_datetime(max_changetime, locale=locale, tzinfo=req.tz) extradict['min_changetime'] = format_datetime(min_changetime, locale=locale, tzinfo=req.tz) if 'time' in fields: extradict['max_time'] = format_datetime(max_time, locale=locale, tzinfo=req.tz) extradict['min_time'] = format_datetime(min_time, locale=locale, tzinfo=req.tz) return ticketlist, extradict
def render_admin_panel(self, req, cat, page, path_info): """ Returns the admin view and handles the form actions """ req.perm.require('TRAC_ADMIN') project = Project.get(self.env) if req.method == 'POST': myprojects_url = conf.url_home_path + "/myprojects" conf.log.exception("Redirect URL: %s" % myprojects_url) thisurl = Href(req.base_path)(req.path_info) context = Context.from_request(req) # Handle project remove if 'remove' in req.args: # Don't allow removing home if project.env_name == conf.sys_home_project_name: add_warning(req, 'Cannot remove home project') return req.redirect(thisurl) # Archive the project before removal archive = ProjectArchive() archived = archive.archive(project) if not archived: add_warning( req, 'Could not archive project "%s". Will not remove the project' % project.project_name) return req.redirect(thisurl) # Notify listeners. The project object still exists, but database does not for listener in self.project_change_listeners: listener.project_archived(project) # Do the actual project removal projects = Projects() if projects.remove_project(project): # Notify listeners. The project object still exists, but database does not for listener in self.project_change_listeners: listener.project_deleted(project) # NOTE: We no longer have project tables/session etc available req.redirect(myprojects_url) else: add_warning( req, 'Could not remove project "%s". Try again later' % project.project_name) return req.redirect(thisurl) add_script(req, 'multiproject/js/jquery-ui.js') add_script(req, 'multiproject/js/multiproject.js') add_script(req, 'multiproject/js/admin_system.js') add_stylesheet(req, 'multiproject/css/jquery-ui.css') # NOTE: Trac automatically puts 'project' dict in chrome thus it cannot be used data = { 'multiproject': { 'project': project, 'home_url': conf.url_home_path } } return 'admin_system.html', data
def web_context(*args, **kwargs): return Context.from_request(*args, **kwargs)
def render_macro(self, req, name, content): args = [x.strip() for x in content.split(',')] # return args if len(args) == 1: args.append(None) elif len(args) != 2: return system_message('Invalid arguments "%s"' % content) # Pull out the arguments source, theader = args try: source_format, source_obj = source.split(':', 1) except ValueError: # If no : is present, assume its a wiki page source_format, source_obj = 'source', source # Apply a default format if needed try: if theader is not None: isTheader, theader_content = theader.split("=", 1) except AttributeError: pass try: dest_format = self.default_formats[source_format] except KeyError: pass if 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) 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) # If we have a preview format, use it # Escape if needed # if not self.config.getbool('wiki', 'render_unsafe_content', False): # try: # out = HTMLParser(StringIO(out)).parse() | HTMLSanitizer() # except ParseError: # out = escape(out) # reader = str(out).split("||") need_header = 1 foo = '' foo += '<table class="sortable" style="border:1px solid #000;">' try: if theader is not None: if isTheader == "header": custom_theader = theader_content.split(";") foo += '<tr>' for theader_cell in custom_theader: foo += '<th style="border:1px solid #000;">' + str( theader_cell) + '</th>' foo += "</tr>\n" except AttributeError: pass for row in str(out).splitlines(): foo += "<tr>\n" for cell in row.split("||"): if cell.startswith("|"): foo += '<td align="right" style="border:1px solid #000;">' if dest_format: foo += (str( Mimeview(self.env).render( None, "text/x-trac-wiki", str(cell.lstrip("|"))))[16:])[:-19] foo += "</td>" else: foo += '<td align="right" style="border:1px solid #000;">' if dest_format: foo += (str( Mimeview(self.env).render( None, "text/x-trac-wiki", str(cell))).lstrip("<p>")[16:])[:-19] foo += "</td>" # foo +=str(row) # for cell in row: # foo += cell # foo += "-" foo += "</tr></table>" return foo
def process_request(self, req): """ Processing the request. """ req.perm('blog').assert_permission('BLOG_VIEW') blog_core = FullBlogCore(self.env) format = req.args.get('format', '').lower() command, pagename, path_items, listing_data = self._parse_path(req) action = req.args.get('action', 'view').lower() try: version = int(req.args.get('version', 0)) except: version = 0 data = {} template = 'fullblog_view.html' data['blog_about'] = BlogPost(self.env, 'about') data['blog_infotext'] = blog_core.get_bloginfotext() blog_month_names = map_month_names( self.env.config.getlist('fullblog', 'month_names')) data['blog_month_names'] = blog_month_names self.env.log.debug( "Blog debug: command=%r, pagename=%r, path_items=%r" % (command, pagename, path_items)) if not command: # Request for just root (display latest) data['blog_post_list'] = [] count = 0 maxcount = self.num_items blog_posts = get_blog_posts(self.env) for post in blog_posts: bp = BlogPost(self.env, post[0], post[1]) if 'BLOG_VIEW' in req.perm(bp.resource): data['blog_post_list'].append(bp) count += 1 if maxcount and count == maxcount: # Only display a certain number on front page (from config) break data['blog_list_title'] = "Recent posts" + \ (len(blog_posts) > maxcount and \ " (max %d) - Browse or Archive for more" % (maxcount,) \ or '') add_link(req, 'alternate', req.href.blog(format='rss'), 'RSS Feed', 'application/rss+xml', 'rss') elif command == 'archive': # Requesting the archive page template = 'fullblog_archive.html' data['blog_archive'] = [] for period, period_posts in group_posts_by_month( get_blog_posts(self.env)): allowed_posts = [] for post in period_posts: bp = BlogPost(self.env, post[0], post[1]) if 'BLOG_VIEW' in req.perm(bp.resource): allowed_posts.append(post) if allowed_posts: data['blog_archive'].append((period, allowed_posts)) add_link(req, 'alternate', req.href.blog(format='rss'), 'RSS Feed', 'application/rss+xml', 'rss') elif command == 'view' and pagename: # Requesting a specific blog post the_post = BlogPost(self.env, pagename, version) req.perm(the_post.resource).require('BLOG_VIEW') if not the_post.version: raise HTTPNotFound("No blog post named '%s'." % pagename) if req.method == 'POST': # Adding/Previewing a comment # Permission? req.perm(the_post.resource).require('BLOG_COMMENT') comment = BlogComment(self.env, pagename) comment.comment = req.args.get('comment', '') comment.author = (req.authname != 'anonymous' and req.authname) \ or req.args.get('author') comment.time = datetime.datetime.now(utc) warnings = [] if 'cancelcomment' in req.args: req.redirect(req.href.blog(pagename)) elif 'previewcomment' in req.args: warnings.extend( blog_core.create_comment(req, comment, verify_only=True)) elif 'submitcomment' in req.args and not warnings: warnings.extend(blog_core.create_comment(req, comment)) if not warnings: req.redirect( req.href.blog(pagename) + '#comment-' + str(comment.number)) data['blog_comment'] = comment # Push all warnings out to the user. for field, reason in warnings: if field: add_warning(req, "Field '%s': %s" % (field, reason)) else: add_warning(req, reason) data['blog_post'] = the_post context = Context.from_request(req, the_post.resource, absurls=format == 'rss' and True or False) data['context'] = context if format == 'rss': return 'fullblog_post.rss', data, 'application/rss+xml' # Regular web response context = Context.from_request(req, the_post.resource) data['blog_attachments'] = AttachmentModule( self.env).attachment_data(context) # Previous and Next ctxtnav prev, next = blog_core.get_prev_next_posts(req.perm, the_post.name) if prev: add_link(req, 'prev', req.href.blog(prev), prev) if next: add_link(req, 'next', req.href.blog(next), next) if arity(prevnext_nav) == 4: # 0.12 compat following trac:changeset:8597 prevnext_nav(req, 'Previous Post', 'Next Post') else: prevnext_nav(req, 'Post') # RSS feed for post and comments add_link(req, 'alternate', req.href.blog(pagename, format='rss'), 'RSS Feed', 'application/rss+xml', 'rss') elif command in ['create', 'edit']: template = 'fullblog_edit.html' default_pagename = blog_core._get_default_postname(req.authname) the_post = BlogPost(self.env, pagename or default_pagename) warnings = [] if command == 'create' and req.method == 'GET' and not the_post.version: # Support appending query arguments for populating intial fields the_post.update_fields(req.args) if command == 'create' and the_post.version: # Post with name or suggested name already exists if 'BLOG_CREATE' in req.perm and the_post.name == default_pagename \ and not req.method == 'POST': if default_pagename: add_notice( req, "Suggestion for new name already exists " "('%s'). Please make a new name." % the_post.name) elif pagename: warnings.append( ('', "A post named '%s' already exists. Enter new name." % the_post.name)) the_post = BlogPost(self.env, '') if command == 'edit': req.perm(the_post.resource).require( 'BLOG_VIEW') # Starting point if req.method == 'POST': # Create or edit a blog post if 'blog-cancel' in req.args: if req.args.get('action', '') == 'edit': req.redirect(req.href.blog(pagename)) else: req.redirect(req.href.blog()) # Assert permissions if command == 'create': req.perm(Resource('blog', None)).require('BLOG_CREATE') elif command == 'edit': if the_post.author == req.authname: req.perm(the_post.resource).require('BLOG_MODIFY_OWN') else: req.perm(the_post.resource).require('BLOG_MODIFY_ALL') # Check input orig_author = the_post.author if not the_post.update_fields(req.args): warnings.append(('', "None of the fields have changed.")) version_comment = req.args.get('new_version_comment', '') if 'blog-preview' in req.args: warnings.extend( blog_core.create_post(req, the_post, req.authname, version_comment, verify_only=True)) elif 'blog-save' in req.args and not warnings: warnings.extend( blog_core.create_post(req, the_post, req.authname, version_comment)) if not warnings: req.redirect(req.href.blog(the_post.name)) context = Context.from_request(req, the_post.resource) data['context'] = context data['blog_attachments'] = AttachmentModule( self.env).attachment_data(context) data['blog_action'] = 'preview' data['blog_version_comment'] = version_comment if (orig_author and orig_author != the_post.author) and ( not 'BLOG_MODIFY_ALL' in req.perm(the_post.resource)): add_notice(req, "If you change the author you cannot " \ "edit the post again due to restricted permissions.") data['blog_orig_author'] = orig_author for field, reason in warnings: if field: add_warning(req, "Field '%s': %s" % (field, reason)) else: add_warning(req, reason) data['blog_edit'] = the_post elif command == 'delete': bp = BlogPost(self.env, pagename) req.perm(bp.resource).require('BLOG_DELETE') if 'blog-cancel' in req.args: req.redirect(req.href.blog(pagename)) comment = int(req.args.get('comment', '0')) warnings = [] if comment: # Deleting a specific comment bc = BlogComment(self.env, pagename, comment) if not bc.number: raise TracError( "Cannot delete. Blog post name and/or comment number missing." ) if req.method == 'POST' and comment and pagename: warnings.extend(blog_core.delete_comment(bc)) if not warnings: add_notice(req, "Blog comment %d deleted." % comment) req.redirect(req.href.blog(pagename)) template = 'fullblog_delete.html' data['blog_comment'] = bc else: # Delete a version of a blog post or all versions # with comments and attachments if only version. if not bp.version: raise TracError( "Cannot delete. Blog post '%s' does not exist." % (bp.name)) version = int(req.args.get('version', '0')) if req.method == 'POST': if 'blog-version-delete' in req.args: if bp.version != version: raise TracError( "Cannot delete. Can only delete most recent version." ) warnings.extend( blog_core.delete_post(bp, version=bp.versions[-1])) elif 'blog-delete' in req.args: version = 0 warnings.extend( blog_core.delete_post(bp, version=version)) if not warnings: if version > 1: add_notice( req, "Blog post '%s' version %d deleted." % (pagename, version)) req.redirect(req.href.blog(pagename)) else: add_notice(req, "Blog post '%s' deleted." % pagename) req.redirect(req.href.blog()) template = 'fullblog_delete.html' data['blog_post'] = bp for field, reason in warnings: if field: add_warning(req, "Field '%s': %s" % (field, reason)) else: add_warning(req, reason) elif command.startswith('listing-'): # 2007/10 or category/something or author/theuser title = category = author = '' from_dt = to_dt = None if command == 'listing-month': from_dt = listing_data['from_dt'] to_dt = listing_data['to_dt'] title = "Posts for the month of %s %d" % ( blog_month_names[from_dt.month - 1], from_dt.year) add_link(req, 'alternate', req.href.blog(format='rss'), 'RSS Feed', 'application/rss+xml', 'rss') elif command == 'listing-category': category = listing_data['category'] if category: title = "Posts in category %s" % category add_link(req, 'alternate', req.href.blog('category', category, format='rss'), 'RSS Feed', 'application/rss+xml', 'rss') elif command == 'listing-author': author = listing_data['author'] if author: title = "Posts by author %s" % author add_link(req, 'alternate', req.href.blog('author', author, format='rss'), 'RSS Feed', 'application/rss+xml', 'rss') if not (author or category or (from_dt and to_dt)): raise HTTPNotFound("Not a valid path for viewing blog posts.") blog_posts = [] for post in get_blog_posts(self.env, category=category, author=author, from_dt=from_dt, to_dt=to_dt): bp = BlogPost(self.env, post[0], post[1]) if 'BLOG_VIEW' in req.perm(bp.resource): blog_posts.append(bp) data['blog_post_list'] = blog_posts data['blog_list_title'] = title else: raise HTTPNotFound("Not a valid blog path.") if (not command or command.startswith('listing-')) and format == 'rss': data['context'] = Context.from_request(req, absurls=True) data['blog_num_items'] = self.num_items return 'fullblog.rss', data, 'application/rss+xml' data['blog_months'], data['blog_authors'], data['blog_categories'], \ data['blog_total'] = \ blog_core.get_months_authors_categories( user=req.authname, perm=req.perm) if 'BLOG_CREATE' in req.perm('blog'): add_ctxtnav(req, 'New Post', href=req.href.blog('create'), title="Create new Blog Post") add_stylesheet(req, 'tracfullblog/css/fullblog.css') add_stylesheet(req, 'common/css/code.css') data['blog_personal_blog'] = self.env.config.getbool( 'fullblog', 'personal_blog') data['blog_archive_rss_icon'] = self.all_rss_icons \ or self.archive_rss_icon data['blog_all_rss_icons'] = self.all_rss_icons return (template, data, None)
master = BuildBotSystem(self.buildbot_url) if builder is None: data = {'names': master.getAllBuilders()} return 'bbw_allbuilders.html', data, 'text/html' else: class Foo: pass b = Foo() b.name = str(builder) b.current = 'CURRENT-TEXT' b.recent = [] b.slaves = [] data = {'builder': b} try: master = BuildBotSystem(self.buildbot_url) data = {'builder': master.getBuilder(builder)} except Exception, e: print 'Error fetching builder stats', e data['context'] = Context.from_request(req, ('buildbot', builder)) return 'bbw_builder.html', data, 'text/html' def match_request(self, req): return req.path_info.startswith('/buildbot') and 1 or 0 def process_request(self, req): if req.path_info.startswith('/buildbot/builder'): return self._handle_builder(req) return 'bbw_welcome.html', {'url': self.buildbot_url}, 'text/html'
def _render_config(self, req, config_name): db = self.env.get_db_cnx() config = BuildConfig.fetch(self.env, config_name, db=db) if not config: raise HTTPNotFound("Build configuration '%s' does not exist." \ % config_name) repos = self.env.get_repository(req.authname) repos.authz.assert_permission(config.branch) data = {'title': 'Build Configuration "%s"' \ % config.label or config.name, 'page_mode': 'view_config'} add_link(req, 'up', req.href.build(), 'Build Status') description = config.description if description: description = wiki_to_html(description, self.env, req) pending_builds = list( Build.select(self.env, config=config.name, status=Build.PENDING)) inprogress_builds = list( Build.select(self.env, config=config.name, status=Build.IN_PROGRESS)) rev = '' for b in repos.git.get_branches(): if b[0] == config.branch: rev = b[1] break data['config'] = { 'name': config.name, 'label': config.label, 'branch': config.branch, 'active': config.active, 'description': description, 'browser_href': req.href.browser(rev=rev), 'builds_pending': len(pending_builds), 'builds_inprogress': len(inprogress_builds) } context = Context.from_request(req, config.resource) data['context'] = context data['config']['attachments'] = AttachmentModule( self.env).attachment_data(context) platforms = list( TargetPlatform.select(self.env, config=config_name, db=db)) data['config']['platforms'] = [{ 'name': platform.name, 'id': platform.id, 'builds_pending': len( list( Build.select(self.env, config=config.name, status=Build.PENDING, platform=platform.id))), 'builds_inprogress': len( list( Build.select(self.env, config=config.name, status=Build.IN_PROGRESS, platform=platform.id))) } for platform in platforms] has_reports = False for report in Report.select(self.env, config=config.name, db=db): has_reports = True break if has_reports: chart_generators = [] for generator in ReportChartController(self.env).generators: for category in generator.get_supported_categories(): chart_generators.append({ 'href': req.href.build(config.name, 'chart/' + category) }) data['config']['charts'] = chart_generators charts_license = self.config.get('bitten', 'charts_license') if charts_license: data['config']['charts_license'] = charts_license page = max(1, int(req.args.get('page', 1))) more = False data['page_number'] = page repos = self.env.get_repository(req.authname) builds_per_page = 12 * len(platforms) idx = 0 builds = {} for platform, rev, build in collect_changes(repos, config): if idx >= page * builds_per_page: more = True break elif idx >= (page - 1) * builds_per_page: builds.setdefault(rev, {}) builds[rev].setdefault('href', req.href.changeset(rev)) if build and build.status != Build.PENDING: build_data = _get_build_data(self.env, req, build) build_data['steps'] = [] for step in BuildStep.select(self.env, build=build.id, db=db): build_data['steps'].append({ 'name': step.name, 'description': step.description, 'duration': to_datetime(step.stopped, utc) - \ to_datetime(step.started, utc), 'failed': not step.successful, 'errors': step.errors, 'href': build_data['href'] + '#step_' + step.name }) builds[rev][platform.id] = build_data idx += 1 data['config']['builds'] = builds if page > 1: if page == 2: prev_href = req.href.build(config.name) else: prev_href = req.href.build(config.name, page=page - 1) add_link(req, 'prev', prev_href, 'Previous Page') if more: next_href = req.href.build(config.name, page=page + 1) add_link(req, 'next', next_href, 'Next Page') prevnext_nav(req, 'Page') return data
def process_request(self, req): req.perm.require('BUILD_VIEW') db = self.env.get_db_cnx() build_id = int(req.args.get('id')) build = Build.fetch(self.env, build_id, db=db) if not build: raise HTTPNotFound("Build '%s' does not exist." \ % build_id) if req.method == 'POST': if req.args.get('action') == 'invalidate': self._do_invalidate(req, build, db) req.redirect(req.href.build(build.config, build.id)) add_link(req, 'up', req.href.build(build.config), 'Build Configuration') data = { 'title': 'Build %s - %s' % (build_id, _status_title[build.status]), 'page_mode': 'view_build', 'build': {} } config = BuildConfig.fetch(self.env, build.config, db=db) data['build']['config'] = { 'name': config.label or config.name, 'href': req.href.build(config.name) } context = Context.from_request(req, build.resource) data['context'] = context data['build']['attachments'] = AttachmentModule( self.env).attachment_data(context) formatters = [] for formatter in self.log_formatters: formatters.append(formatter.get_formatter(req, build)) summarizers = {} # keyed by report type for summarizer in self.report_summarizers: categories = summarizer.get_supported_categories() summarizers.update(dict([(cat, summarizer) for cat in categories])) data['build'].update(_get_build_data(self.env, req, build)) steps = [] for step in BuildStep.select(self.env, build=build.id, db=db): steps.append({ 'name': step.name, 'description': step.description, 'duration': pretty_timedelta(step.started, step.stopped), 'failed': step.status == BuildStep.FAILURE, 'errors': step.errors, 'log': self._render_log(req, build, formatters, step), 'reports': self._render_reports(req, config, build, summarizers, step) }) data['build']['steps'] = steps data['build']['can_delete'] = ('BUILD_DELETE' in req.perm \ and build.status != build.PENDING) repos = self.env.get_repository(req.authname) repos.authz.assert_permission(config.branch) chgset = repos.get_changeset(build.rev) data['build']['chgset_author'] = chgset.author add_script(req, 'common/js/folding.js') add_script(req, 'bitten/tabset.js') add_stylesheet(req, 'bitten/bitten.css') return 'bitten_build.html', data, None
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) # If there was a fragment name given then find the fragment.
def format_to_html(req, env, text): req = Mock(href=Href('/'), abs_href=Href('http://www.example.com/'), authname='anonymous', perm=MockPerm(), args={}) context = Context.from_request(req) return trac.wiki.formatter.format_to_html(env, context, text)
def convert_content(self, req, input_type, source, output_type): # get parameters from trac ini file self.img_max_x = self.env.config.get('pagetodoc', 'img_max_x', self.img_max_x) self.img_max_y = self.env.config.get('pagetodoc', 'img_max_y', self.img_max_y) self.img_max_y = self.env.config.get('pagetodoc', 'dpi', self.dpi) # XSL-Transformation xsltfilepath = self.env.config.get('pagetodoc', 'xsltfile', '') # TBD: Fehler ausgeben, wenn xsltfile nicht gelesen werden kann # TBD: Parameter aus der trac.ini an zentraler Stelle auslesen if xsltfilepath == '': message = "You have to set the 'xsltfile' option in the " \ "[pagetodoc] section in trac.ini" raise_dependency_issue(message, req, self.env) if not os.path.exists(xsltfilepath): message = ("Value for 'xsltfile' in the [pagetodoc] section in " \ "trac.ini does not exist: '%s'.")% xsltfilepath raise_dependency_issue(message, req, self.env) # maybe for later use #codepage = self.env.config.get('trac', 'charset', 'iso-8859-1') codepage = 'iso-8859-1' # Convert Wiki markup to HTML, new style out = StringIO() context = Context.from_request(req, 'wiki', req.path_info[6:]) Formatter(self.env, context).format(source, out) html = Markup(out.getvalue()).encode(codepage, 'replace') # remove the bad HTML produced by the breadcrumbs plugin # RFE: find a universal way to do this html = re.compile('(<lh[^>]*>)').sub('', html) # temporary files and folders self.tempdir = mkdtemp(prefix="page2doc") htmlfilehandle, htmlfilepath = mkstemp(prefix='trac_', dir=self.tempdir, suffix = ".html") wordfilehandle, wordfilepath = mkstemp(prefix='word_', dir=self.tempdir, suffix = ".doc") os.close(wordfilehandle) # for debug: set all rights #self.chmod_tmp_dir(self.tempdir) # images # replace href with absolute path and if existing, base auth login try: # this will work if the authentication type is basic (and not over SSL?) login = base64.b64decode(req.environ['HTTP_AUTHORIZATION'][6:]) + '@' except (KeyError, TypeError): login = '' html = re.sub('<img src="(?!\w+://)', '<img src="%s://%s%s:%d' % (req.scheme, login, req.server_name, req.server_port), html) # save images to disk html = re.sub('<img src="([^"]*)"', self.download_image, html) # write HTML page to disk os.write(htmlfilehandle, '<html><body>' + html + '</body></html>') os.close(htmlfilehandle) # clean up the HTML page using HTML Tidy args = '-m -asxhtml -latin1 --doctype omit' tidypath = self.env.config.get('pagetodoc', 'tidypath', 'tidy') # verify that Tidy exists and is setup correctly tidy_error = dependency_failure("tidy", tidypath, "-v") if tidy_error: raise_dependency_issue(tidy_error, req, self.env) cmd = '"%s" %s %s' % (tidypath, args, htmlfilepath) self.execute_external_program(cmd) # workaround namespace self.perform_workarounds(htmlfilepath, 'html') if self.verbose: verb = '-v' else: verb = '' xsltprocpath = self.env.config.get('pagetodoc', 'xsltprocpath', 'xsltproc') # verify that Tidy exists and is setup correctly xsltproc_error = dependency_failure("xsltproc", xsltprocpath, "-V") if xsltproc_error: raise_dependency_issue(xsltproc_error, req, self.env) cmd = '%s %s --html -o %s %s %s' % ( xsltprocpath, verb, wordfilepath, xsltfilepath, htmlfilepath) self.execute_external_program(cmd) # workaround pre-tags self.perform_workarounds(wordfilepath, 'pre') zipfilepath = os.path.join( self.tempdir, os.path.basename(str(req.path_info) + '.zip')) # create a zip file and store all files into it zipfilehandle = zipfile.ZipFile(zipfilepath, "w") zipfilehandle.write(wordfilepath, os.path.basename(str(req.path_info) + '.htm')) for image in self.images: zipfilehandle.write(image, self.imagesubdir + os.path.basename(image)) zipfilehandle.close() zip_file = open(zipfilepath, "rb") zip = zip_file.read() zip_file.close() # delete temporary folders and files self.remove_dir(os.path.join(self.tempdir, self.logsubdir)) self.remove_dir(os.path.join(self.tempdir, self.imagesubdir)) self.remove_dir(self.tempdir) # reset image list self.images = [] return (zip, 'application/zip')
def _render_templates(self, req, cat, page, component): req.perm.assert_permission('TRAC_ADMIN') for key, value in req.args.items(): self.env.log.debug("Key: %s, Value: %s", key, value) testmanagersystem = TestManagerSystem(self.env) context = Context.from_request(req) data = {} data['template_overview'] = True data['edit_template'] = False data['tc_templates'] = testmanagersystem.get_templates(testmanagersystem.TEMPLATE_TYPE_TESTCASE) data['tcat_templates'] = testmanagersystem.get_templates(testmanagersystem.TEMPLATE_TYPE_TESTCATALOG) data['tcat_list'] = testmanagersystem.get_testcatalogs() data['tcat_selected'] = testmanagersystem.get_default_tcat_template_id() if req.method == 'POST': # add a Test Case template? if req.args.get('tc_add'): tc_name = req.args.get('tc_add_name') self.env.log.debug("Add new TC-template: %s" % tc_name) if len(tc_name) > 0: if testmanagersystem.template_exists(tc_name, testmanagersystem.TEMPLATE_TYPE_TESTCASE): data['tc_add_name'] = tc_name add_warning(req, _("A Test Case template with that name already exists")) else: data['template_overview'] = False data['edit_template'] = True data['t_edit_type'] = testmanagersystem.TEMPLATE_TYPE_TESTCASE data['t_edit_name'] = tc_name data['t_edit_action'] = 'ADD' else: add_warning(req, _("Please enter a Template name first")) # add a Test Catalog template? if req.args.get('tcat_add'): tcat_name = req.args.get('tcat_add_name') self.env.log.debug("Add new TCat-template: %s" % tcat_name) if len(tcat_name) > 0: if testmanagersystem.template_exists(tcat_name, testmanagersystem.TEMPLATE_TYPE_TESTCATALOG): data['tcat_add_name'] = tcat_name add_warning(req, _("A Test Catalog template with that name already exists")) else: data['template_overview'] = False data['edit_template'] = True data['t_edit_type'] = testmanagersystem.TEMPLATE_TYPE_TESTCATALOG data['t_edit_name'] = tcat_name data['t_edit_action'] = 'ADD' else: add_warning(req, _("Please enter a Template name first")) # delete a Test Case template? if req.args.get('tc_del'): tc_sel = req.args.get('tc_sel') for t_id in tc_sel: t = testmanagersystem.get_template_by_id(t_id) if testmanagersystem.template_in_use(t_id): add_warning(req, _("Template '%s' not removed as it is in use for a Test Catalog") % t['name']) continue self.env.log.debug("remove test case template with id: " + t_id) if not testmanagersystem.remove_template(t_id): add_warning(req, _("Error deleting Test Case template '%s'") % t['name']) else: add_notice(req, _("Test Case template '%s' deleted") % t['name']) data['tc_templates'] = testmanagersystem.get_templates(testmanagersystem.TEMPLATE_TYPE_TESTCASE) data['tcat_templates'] = testmanagersystem.get_templates(testmanagersystem.TEMPLATE_TYPE_TESTCATALOG) # delete a Test Catalog template? if req.args.get('tcat_del'): tcat_sel = req.args.get('tcat_sel') tcat_default = testmanagersystem.get_default_tcat_template_id() for t_id in tcat_sel: t = testmanagersystem.get_template_by_id(t_id) if t_id == tcat_default: add_warning(req, _("Template '%s' not removed as it is currently the default template") % t['name']) continue self.env.log.debug("remove test catalog template with id: " + t_id) if not testmanagersystem.remove_template(t_id): add_warning(req, _("Error deleting Test Catalog template '%s'") % t['name']) else: add_notice(req, _("Test Catalog template '%s' deleted") % t['name']) data['tc_templates'] = testmanagersystem.get_templates(testmanagersystem.TEMPLATE_TYPE_TESTCASE) data['tcat_templates'] = testmanagersystem.get_templates(testmanagersystem.TEMPLATE_TYPE_TESTCATALOG) # save default Test Catalog template if req.args.get('tcat_default_save'): tcat_default = req.args.get('tcat_default') if testmanagersystem.set_config_property('TEST_CATALOG_DEFAULT_TEMPLATE', tcat_default): add_notice(req, _("Default Test Catalog template updated")) data['tcat_selected'] = tcat_default else: add_warning(req, _("Failed to update default Test Catalog template")) # save templates for TestCatalogs if req.args.get('tc_templates_save'): warning = False for key, value in req.args.items(): self.env.log.debug("checking key: " + key) if 'TC_TEMPLATE_FOR_TCAT_' in key: self.env.log.debug("saving tc-template for: %s, value: %s" % (key, value)) if not testmanagersystem.set_config_property(key, value): warning = True if warning: add_warning(req, _("Failed to update Test Case templates")) else: add_notice(req, _("Default Test Case templates updated")) data['tcat_list'] = testmanagersystem.get_testcatalogs() # preview template if req.args.get('t_edit_preview'): data['template_overview'] = False data['edit_template'] = True data['t_edit_id'] = req.args.get('t_edit_id') data['t_edit_type'] = req.args.get('t_edit_type') data['t_edit_name'] = req.args.get('t_edit_name') data['t_edit_description'] = req.args.get('t_edit_description') data['t_edit_content'] = req.args.get('t_edit_content') data['t_edit_action'] = req.args.get('t_edit_action') data['t_show_preview'] = True data['t_preview_content'] = format_to_html(self.env, context, req.args.get('t_edit_content')) # save an edited template? if req.args.get('t_edit_save'): t_id = req.args.get('t_edit_id') t_type = req.args.get('t_edit_type') t_name = req.args.get('t_edit_name') t_desc = req.args.get('t_edit_description') t_cont = req.args.get('t_edit_content') t_action = req.args.get('t_edit_action') testmanagersystem.save_template(t_id, t_name, t_type, t_desc, t_cont, t_action) data['template_overview'] = True data['edit_template'] = False data['tc_templates'] = testmanagersystem.get_templates(testmanagersystem.TEMPLATE_TYPE_TESTCASE) data['tcat_templates'] = testmanagersystem.get_templates(testmanagersystem.TEMPLATE_TYPE_TESTCATALOG) add_notice(req, _("Template saved")) else: # method 'GET' (template selected for 'edit') if component: t_type = req.args.get('t_type') t_id = component self.env.log.debug("component: " + component) template = testmanagersystem.get_template_by_id(t_id) data['t_edit_id'] = template['id'] data['t_edit_type'] = template['type'] data['t_edit_name'] = template['name'] data['t_edit_description'] = template['description'] data['t_edit_content'] = template['content'] data['t_edit_action'] = 'EDIT' data['template_overview'] = False data['edit_template'] = True add_stylesheet(req, 'common/css/wiki.css') add_stylesheet(req, 'testmanager/css/admin.css') return 'admin_templates.html', data