示例#1
0
    def __init__(self, title, input, correct, file, line, setup=None,
                 teardown=None, context=None):
        unittest.TestCase.__init__(self, 'test')
        self.title = title
        self.input = input
        self.correct = correct
        self.file = file
        self.line = line
        self._setup = setup
        self._teardown = teardown

        req = Mock(href=Href('/'), abs_href=Href('http://www.example.com/'),
                   chrome={}, session={},
                   authname='anonymous', perm=MockPerm(), tz=utc, args={},
                   locale=locale_en, lc_time=locale_en)
        if context:
            if isinstance(context, tuple):
                context = web_context(req, *context)
        else:
            context = web_context(req, 'wiki', 'WikiStart')
        self.context = context

        all_test_components = [
                HelloWorldMacro, DivHelloWorldMacro, TableHelloWorldMacro,
                DivCodeMacro, DivCodeElementMacro, DivCodeStreamMacro,
                NoneMacro, WikiProcessorSampleMacro, SampleResolver]
        self.env = EnvironmentStub(enable=['trac.*'] + all_test_components)
        # -- macros support
        self.env.path = ''
        # -- intertrac support
        self.env.config.set('intertrac', 'trac.title', "Trac's Trac")
        self.env.config.set('intertrac', 'trac.url',
                            "http://trac.edgewall.org")
        self.env.config.set('intertrac', 't', 'trac')
        self.env.config.set('intertrac', 'th.title', "Trac Hacks")
        self.env.config.set('intertrac', 'th.url',
                            "http://trac-hacks.org")
        self.env.config.set('intertrac', 'th.compat', 'false')
        # -- safe schemes
        self.env.config.set('wiki', 'safe_schemes',
                            'file,ftp,http,https,svn,svn+ssh,'
                            'rfc-2396.compatible,rfc-2396+under_score')

        # TODO: remove the following lines in order to discover
        #       all the places were we should use the req.href
        #       instead of env.href
        self.env.href = req.href
        self.env.abs_href = req.abs_href
示例#2
0
文件: browser.py 项目: exocad/exotrac
    def process_request(self, req):
        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.lower() in ('', 'head'):
            rev = None
        format = req.args.get('format')
        order = req.args.get('order', 'name').lower()
        desc = 'desc' in req.args
        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.is_viewable(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 if qs else ''))
        reponame = repos.reponame if repos else None

        # Find node for the requested path/rev
        context = web_context(req)
        node = None
        changeset = 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, _('Invalid changeset number'))
            if node:
                try:
                    # use changeset instance to retrieve branches and tags
                    changeset = repos.get_changeset(node.rev)
                except NoSuchChangeset:
                    pass

            context = context.child(repos.resource.child('source', path,
                                                   version=rev_or_latest))
            display_rev = repos.display_rev
示例#3
0
 def _check_quickjump(self, req, kwd):
     """Look for search shortcuts"""
     # pylint: disable=maybe-no-member
     noquickjump = int(req.args.get('noquickjump', '0'))
     # Source quickjump   FIXME: delegate to ISearchSource.search_quickjump
     quickjump_href = None
     if kwd[0] == '/':
         quickjump_href = req.href.browser(kwd)
         name = kwd
         description = _('Browse repository path %(path)s', path=kwd)
     else:
         context = web_context(req, 'search')
         link = find_element(extract_link(self.env, context, kwd), 'href')
         if link is not None:
             quickjump_href = link.attrib.get('href')
             name = link.children
             description = link.attrib.get('title', '')
     if quickjump_href:
         # Only automatically redirect to local quickjump links
         base_path = req.base_path.replace('@', '%40')
         redirect_href = quickjump_href.replace('@', '%40')
         if not redirect_href.startswith(base_path or '/'):
             noquickjump = True
         if noquickjump:
             return {'href': quickjump_href, 'name': tag.EM(name),
                     'description': description}
         else:
             req.redirect(quickjump_href)
示例#4
0
 def _send_cards_and_stacks(self, req, cards, stacks, stack_names):
     context = web_context(req, 'cards')
     data = {
         'cards_by_id': serialized_cards_by_id(cards, self.env, context),
         'stacks_by_name': serialized_stacks_by_name(stacks, stack_names),
     }
     self._send_json(req, data)
示例#5
0
def main():
    options, args = parse_args()
    names = sorted(name for name in resource_listdir('trac.wiki',
                                                     'default-pages')
                        if not name.startswith('.'))
    if args:
        args = sorted(set(names) & set(map(os.path.basename, args)))
    else:
        args = names

    if options.download:
        download_default_pages(args, options.prefix)

    env = EnvironmentStub()
    load_components(env)
    with env.db_transaction:
        for name in names:
            wiki = WikiPage(env, name)
            wiki.text = resource_string('trac.wiki', 'default-pages/' +
                                        name).decode('utf-8')
            if wiki.text:
                wiki.save('trac', '')
            else:
                printout('%s: Skipped empty page' % name)

    req = Mock(href=Href('/'), abs_href=Href('http://localhost/'),
               perm=MockPerm())
    for name in args:
        wiki = WikiPage(env, name)
        if not wiki.exists:
            continue
        context = web_context(req, wiki.resource)
        out = DummyIO()
        DefaultWikiChecker(env, context, name).format(wiki.text, out)
示例#6
0
文件: wiki2rst.py 项目: pkdevbox/trac
def main():
    names = sorted(name for name in resource_listdir('trac.wiki',
                                                     'default-pages')
                        if not name.startswith('.'))

    env = EnvironmentStub()
    load_components(env)
    with env.db_transaction:
        for name in names:
            wiki = WikiPage(env, name)
            wiki.text = resource_string('trac.wiki', 'default-pages/' +
                                        name).decode('utf-8')
            if wiki.text:
                wiki.save('trac', '')
            else:
                printout('%s: Skipped empty page' % name)

    req = Mock(href=Href('/'), abs_href=Href('http://trac.edgewall.org/'),
               perm=MockPerm(), chrome={})
    for name in sys.argv[1:]:
        name = os.path.basename(name)
        wiki = WikiPage(env, name)
        if not wiki.exists:
            continue
        context = web_context(req, wiki.resource, absurls=True)
        rst = wiki2rest(env, context, wiki)
        sys.stdout.write(rst)
示例#7
0
文件: api.py 项目: pkdevbox/trac
    def from_request(*args, **kwargs):
        """:deprecated: since 1.0, use `web_context` instead. Will be removed
                        in release 1.3.1.
        """
        from trac.web.chrome import web_context

        return web_context(*args, **kwargs)
示例#8
0
 def _render_list(self, req):
     """products list"""
     products = [p for p in Product.select(self.env)
                 if 'PRODUCT_VIEW' in req.perm(Neighborhood('product',
                                                            p.prefix))]
     data = {'products': products,
             'context': web_context(req, Resource('product', None))}
     return 'product_list.html', data, None
示例#9
0
 def setUp(self):
     env = EnvironmentStub(enable=[Chrome, PatchRenderer])
     req = Mock(base_path='', chrome={'static_hash': None}, args={},
                session={}, abs_href=Href('/'), href=Href('/'), locale='',
                perm=MockPerm(), authname=None, tz=None)
     self.context = web_context(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, encoding='utf-8')))
示例#10
0
 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 = web_context(self.req)
     pygments_html = open(os.path.join(os.path.split(__file__)[0],
                                    'pygments.html'))
     self.pygments_html = Stream(list(HTMLParser(pygments_html, encoding='utf-8')))
示例#11
0
 def _render_list(self, req):
     """products list"""
     products = [p for p in Product.select(self.env)
                 if 'PRODUCT_VIEW' in req.perm(Neighborhood('product',
                                                            p.prefix))]
     map(lambda p: setattr(p, 'href', resolve_product_href(
         lookup_product_env(self.env, p.prefix), self.env)), products)
     data = {'products': products,
             'context': web_context(req, Resource('product', None))}
     return 'product_list.html', data, None
示例#12
0
    def post_process_request(self, req, template, data, content_type):
        if not req or not template or not isinstance(data, dict):
            return template, data, content_type

        model = None
        resource = None
        attachments = None

        if template in ('wiki_view.html', 'wiki_edit.html'):
            model = data.get('page')
        elif template == 'ticket.html':
            model = data.get('ticket')
        elif template in ('milestone_view.html', 'milestone_edit.html'):
            model = data.get('milestone')
        elif template == 'attachment.html':
            attachments = data.get('attachments')
            if attachments:
                resource = attachments['parent']

        if not resource and model and model.exists:
            resource = model.resource
        if not resource:
            return template, data, content_type
        if not attachments:
            attachments = data.get('attachments')
        if not attachments and model and resource:
            context = web_context(req, resource)
            attachments = AttachmentModule(self.env).attachment_data(context)
            data['attachments'] = attachments

        if template in ('wiki_edit.html', 'milestone_edit.html'):
            self._add_overlayview(req)
        add_stylesheet(req, 'tracdragdrop/tracdragdrop.css')
        if hasattr(req, 'locale'):
            locale = str(req.locale)
            if locale in self.messages_files:
                add_script(req, 'tracdragdrop/messages/%s.js' % locale)
        add_script(req, 'common/js/folding.js')
        add_script(req, 'tracdragdrop/tracdragdrop.js')
        script_data = {
            '_tracdragdrop': {
                'base_url': req.href().rstrip('/') + '/',
                'new_url': req.href('tracdragdrop', 'new', resource.realm,
                                    resource.id),
                'can_create': attachments.get('can_create') or False,
                'max_size': AttachmentModule(self.env).max_size,
            },
            'form_token': req.form_token,
        }
        if add_script_data:
            add_script_data(req, script_data)
        else:
            setattr(req, '_tracdragdrop_data', script_data)

        return template, data, content_type
示例#13
0
    def test_template_data(self):
        req = Mock(href=self.env.href, perm=MockPerm(), authname="anonymous", tz=None, locale=None)
        context = web_context(req, "query")

        query = Query.from_string(self.env, "owner=$USER&order=id")
        tickets = query.execute(req)
        data = query.template_data(context, tickets, req=req)
        self.assertEqual(["anonymous"], data["clauses"][0]["owner"]["values"])

        query = Query.from_string(self.env, "owner=$USER&order=id")
        tickets = query.execute(req)
        data = query.template_data(context, tickets)
        self.assertEqual(["$USER"], data["clauses"][0]["owner"]["values"])
示例#14
0
    def _render_editor(self, req, product):
        """common processing for creating rendering the edit page"""
        if product._exists:
            req.perm(product.resource).require('PRODUCT_MODIFY')
        else:
            req.perm(product.resource).require('PRODUCT_CREATE')

        chrome = Chrome(self.env)
        chrome.add_jquery_ui(req)
        chrome.add_wiki_toolbars(req)
        data = {'product': product,
                'context': web_context(req, product.resource)}
        return 'product_edit.html', data, None
示例#15
0
    def test_template_data(self):
        req = Mock(href=self.env.href, perm=MockPerm(), authname='anonymous',
                   tz=None, locale=None)
        context = web_context(req, 'query')

        query = Query.from_string(self.env, 'owner=$USER&order=id')
        tickets = query.execute(req)
        data = query.template_data(context, tickets, req=req)
        self.assertEqual(['anonymous'], data['clauses'][0]['owner']['values'])

        query = Query.from_string(self.env, 'owner=$USER&order=id')
        tickets = query.execute(req)
        data = query.template_data(context, tickets)
        self.assertEqual(['$USER'], data['clauses'][0]['owner']['values'])
示例#16
0
    def process_request(self, req):
        """process request handler"""

        req.perm.require('PRODUCT_VIEW')
        pid = req.args.get('productid', None)
        if pid:
            req.perm('product', pid).require('PRODUCT_VIEW')

        try:
            product = Product(self.env, {'prefix': pid})
        except ResourceNotFound:
            product = Product(self.env)

        path_info = req.args.get('pathinfo')
        if path_info and path_info != '/':
            if not product._exists:
                # bh:ticket:561 - Display product list and warning message
                if pid:
                    add_warning(req, _("Product %(pid)s not found", pid=pid))
                return self._render_list(req)
            else:
                raise HTTPNotFound(
                    _('Unable to render product page. Wrong setup?'))

        if pid:
            add_link(req, 'up', req.href.products(), _('Products'))

        action = req.args.get('action', 'view')
        if req.method == 'POST':
            if 'cancel' in req.args:
                req.redirect(req.href.products(product.prefix))
            elif action == 'edit':
                return self._do_save(req, product)
            elif action == 'delete':
                raise TracError(_('Product removal is not allowed!'))
        elif action in ('new', 'edit'):
            return self._render_editor(req, product)
        elif action == 'delete':
            raise TracError(_('Product removal is not allowed!'))

        if not product._exists:
            if pid:
                # bh:ticket:561 - Display product list and warning message
                add_warning(req, _("Product %(pid)s not found", pid=pid))
            return self._render_list(req)

        data = {'product': product,
                'context': web_context(req, product.resource)}
        return 'product_view.html', data, None
示例#17
0
 def process_request(self, req):
     link = req.args.get('link', '')
     parts = link.split(':', 1)
     if len(parts) > 1:
         resolver, target = parts
         if target[:1] + target[-1:] not in ('""', "''"):
             link = '%s:"%s"' % (resolver, target)
     from trac.web.chrome import web_context
     link_frag = extract_link(self.env, web_context(req), link)
     if isinstance(link_frag, (Element, Fragment)):
         elt = find_element(link_frag, 'href')
         if elt is None: # most probably no permissions to view
             raise PermissionError(_("Can't view %(link)s:", link=link))
         href = elt.attrib.get('href')
     else:
         href = req.href(link.rstrip(':'))
     req.redirect(href)
示例#18
0
    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,
                                          web_context(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))
示例#19
0
文件: batch.py 项目: pkdevbox/trac
    def test_timeline_events(self):
        """Regression test for #11288"""
        tktmod = web_ui.TicketModule(self.env)
        now = datetime.now(utc)
        start = now - timedelta(hours=1)
        stop = now + timedelta(hours=1)
        events = tktmod.get_timeline_events(self.req, start, stop,
                                            ['ticket_details'])
        self.assertEqual(True, all(ev[0] != 'batchmodify' for ev in events))

        ids = []
        for i in xrange(20):
            ticket = Ticket(self.env)
            ticket['summary'] = 'Ticket %d' % i
            ids.append(ticket.insert())
        ids.sort()
        new_values = {'summary': 'batch updated ticket',
                      'owner': 'ticket11288', 'reporter': 'ticket11288'}
        batch = BatchModifyModule(self.env)
        batch._save_ticket_changes(self.req, ids, new_values, '', 'leave')
        # shuffle ticket_change records
        with self.env.db_transaction as db:
            rows = db('SELECT * FROM ticket_change')
            db.execute('DELETE FROM ticket_change')
            rows = rows[0::4] + rows[1::4] + rows[2::4] + rows[3::4]
            db.executemany('INSERT INTO ticket_change VALUES (%s)' %
                           ','.join(('%s',) * len(rows[0])),
                           rows)

        events = tktmod.get_timeline_events(self.req, start, stop,
                                            ['ticket_details'])
        events = [ev for ev in events if ev[0] == 'batchmodify']
        self.assertEqual(1, len(events))
        batch_ev = events[0]
        self.assertEqual('anonymous', batch_ev[2])
        self.assertEqual(ids, sorted(batch_ev[3][0]))
        self.assertEqual('updated', batch_ev[3][1])

        context = web_context(self.req)
        self.assertEqual(
            self.req.href.query(id=','.join(str(t) for t in ids)),
            tktmod.render_timeline_event(context, 'url', batch_ev))
示例#20
0
    def _insert_crashdump_data(self, req, crashobj, data, author_id, field_changes):
        """Insert crashobj data into the template `data`"""
        replyto = req.args.get('replyto')
        data['replyto'] = replyto
        data['version'] = crashobj.resource.version
        data['description_change'] = None
        data['author_id'] = author_id

        if crashobj.resource.version is not None:
            crashobj.values.update(values)

        context = web_context(req, crashobj.resource)

        # Display the owner and reporter links when not obfuscated
        chrome = Chrome(self.env)
        for user in 'reporter', 'owner':
            if chrome.format_author(req, crashobj[user]) == crashobj[user]:
                data['%s_link' % user] = self._query_link(req, user,
                                                          crashobj[user])
        data['context'] = context
示例#21
0
    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 = as_int(req.args.get('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 = web_context(req, resource)
        rendered = format_to(self.env, flavor, context, text, **options)
        req.send(rendered.encode('utf-8'))
示例#22
0
 def wiki_to_html(event, wikitext):
     if wikitext is None:
         return ""
     try:
         req = Mock(
             href=Href(self.env.abs_href()),
             abs_href=self.env.abs_href,
             authname=event.author,
             perm=MockPerm(),
             chrome=dict(
                 warnings=[],
                 notices=[]
             ),
             args={}
         )
         resource = Resource(event.realm, event.target.id)
         context = web_context(req, resource)
         formatter = HtmlFormatter(self.env, context, wikitext)
         return formatter.generate(True)
     except Exception, e:
         self.log.error("Failed to render %s", repr(wikitext))
         self.log.error(exception_to_unicode(e, traceback=True))
         raise
示例#23
0
文件: web_ui.py 项目: exocad/exotrac
 def _check_quickjump(self, req, kwd):
     """Look for search shortcuts"""
     noquickjump = int(req.args.get("noquickjump", "0"))
     # Source quickjump   FIXME: delegate to ISearchSource.search_quickjump
     quickjump_href = None
     if kwd[0] == "/":
         quickjump_href = req.href.browser(kwd)
         name = kwd
         description = _("Browse repository path %(path)s", path=kwd)
     else:
         context = web_context(req, "search")
         link = find_element(extract_link(self.env, context, kwd), "href")
         if link is not None:
             quickjump_href = link.attrib.get("href")
             name = link.children
             description = link.attrib.get("title", "")
     if quickjump_href:
         # Only automatically redirect to local quickjump links
         if not quickjump_href.startswith(req.base_path or "/"):
             noquickjump = True
         if noquickjump:
             return {"href": quickjump_href, "name": tag.em(name), "description": description}
         else:
             req.redirect(quickjump_href)
示例#24
0
文件: resource.py 项目: pkdevbox/trac
 def setUp(self):
     self.env = EnvironmentStub(default_data=True)
     self.env.enable_component(self.FakeResourceManager)
     self.req = Mock(perm=MockPerm(), href=Href('/trac.cgi'))
     self.context = web_context(self.req)
示例#25
0
文件: web_ui.py 项目: skshel123/trac
    def _render_editor(self, req, page, action='edit', has_collision=False):
        if has_collision:
            if action == 'merge':
                page = WikiPage(self.env, page.name)
                req.perm(page.resource).require('WIKI_VIEW')
            else:
                action = 'collision'

        if not page.exists:
            req.perm(page.resource).require('WIKI_CREATE')
        else:
            req.perm(page.resource).require('WIKI_MODIFY')
        original_text = page.text
        comment = req.args.get('comment', '')
        if 'text' in req.args:
            page.text = req.args.get('text')
        elif 'template' in req.args:
            template = req.args.get('template')
            template = template[1:] if template.startswith('/') \
                                    else self.PAGE_TEMPLATES_PREFIX + template
            template_page = WikiPage(self.env, template)
            if template_page and template_page.exists and \
                    'WIKI_VIEW' in req.perm(template_page.resource):
                page.text = template_page.text
        elif 'version' in req.args:
            version = None
            if req.args.get('version'):  # Allow version to be empty
                version = req.args.as_int('version')
            if version is not None:
                old_page = WikiPage(self.env, page.name, version)
                req.perm(page.resource).require('WIKI_VIEW')
                page.text = old_page.text
                comment = _("Reverted to version %(version)s.",
                            version=version)
        if action in ('preview', 'diff'):
            page.readonly = 'readonly' in req.args

        author = get_reporter_id(req, 'author')
        defaults = {'editrows': str(self.default_edit_area_height)}
        prefs = {key: req.session.get('wiki_%s' % key, defaults.get(key))
                 for key in ('editrows', 'sidebyside')}

        if 'from_editor' in req.args:
            sidebyside = req.args.get('sidebyside') or None
            if sidebyside != prefs['sidebyside']:
                req.session.set('wiki_sidebyside', int(bool(sidebyside)), 0)
        else:
            sidebyside = prefs['sidebyside']

        if sidebyside:
            editrows = max(int(prefs['editrows']),
                           len(page.text.splitlines()) + 1)
        else:
            editrows = req.args.get('editrows')
            if editrows:
                if editrows != prefs['editrows']:
                    req.session.set('wiki_editrows', editrows,
                                    defaults['editrows'])
            else:
                editrows = prefs['editrows']

        data = self._page_data(req, page, action)
        context = web_context(req, page.resource)
        data.update({
            'context': context,
            'author': author,
            'comment': comment,
            'edit_rows': editrows,
            'sidebyside': sidebyside,
            'scroll_bar_pos': req.args.get('scroll_bar_pos', ''),
            'diff': None,
            'attachments': AttachmentModule(self.env).attachment_data(context)
        })
        if action in ('diff', 'merge'):
            old_text = original_text.splitlines() if original_text else []
            new_text = page.text.splitlines() if page.text else []
            diff_data, changes = self._prepare_diff(
                req, page, old_text, new_text, page.version, '')
            data.update({'diff': diff_data, 'changes': changes,
                         'action': 'preview', 'merge': action == 'merge',
                         'longcol': 'Version', 'shortcol': 'v'})
        elif sidebyside and action != 'collision':
            data['action'] = 'preview'

        self._wiki_ctxtnav(req, page)
        Chrome(self.env).add_wiki_toolbars(req)
        Chrome(self.env).add_auto_preview(req)
        add_script(req, 'common/js/wiki.js')
        return 'wiki_edit.html', data
示例#26
0
 def from_request(*args, **kwargs):
     """:deprecated: since 1.0, use `web_context` instead."""
     from trac.web.chrome import web_context
     return web_context(*args, **kwargs)
示例#27
0
 def setUp(self):
     self.env = EnvironmentStub(default_data=True)
     self.query_module = QueryModule(self.env)
     req = Mock(perm=MockPerm(), args={}, href=Href("/"))
     self.formatter = LinkFormatter(self.env, web_context(req))
示例#28
0
    def display_html(self, req, query):
        """returns the HTML according to a query for /hours view"""

        # 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)
            # 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, cached_ids=orig_list)
            orig_time = query_time

        context = web_context(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.getlist('col')
        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 = []
            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)
            if ticket_times:
                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 key not 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'
示例#29
0
    def _render_editor(self, req, page, action='edit', has_collision=False):
        if has_collision:
            if action == 'merge':
                page = WikiPage(self.env, page.name, version=None)
                req.perm(page.resource).require('WIKI_VIEW')
            else:
                action = 'collision'

        if page.readonly:
            req.perm(page.resource).require('WIKI_ADMIN')
        else:
            req.perm(page.resource).require('WIKI_MODIFY')
        original_text = page.text
        comment = req.args.get('comment', '')
        if 'text' in req.args:
            page.text = req.args.get('text')
        elif 'template' in req.args:
            template = self.PAGE_TEMPLATES_PREFIX + req.args.get('template')
            template_page = WikiPage(self.env, template)
            if template_page and template_page.exists and \
                   'WIKI_VIEW' in req.perm(template_page.resource):
                page.text = template_page.text
        elif 'version' in req.args:
            old_page = WikiPage(self.env, page.name,
                                version=int(req.args['version']))
            req.perm(page.resource).require('WIKI_VIEW')
            page.text = old_page.text
            comment = _("Reverted to version %(version)s.",
                        version=req.args['version'])
        if action in ('preview', 'diff'):
            page.readonly = 'readonly' in req.args

        author = get_reporter_id(req, 'author')
        defaults = {'editrows': 20}
        prefs = dict((key, req.session.get('wiki_%s' % key, defaults.get(key)))
                     for key in ('editrows', 'sidebyside'))

        if 'from_editor' in req.args:
            sidebyside = req.args.get('sidebyside') or None
            if sidebyside != prefs['sidebyside']:
                req.session.set('wiki_sidebyside', int(bool(sidebyside)), 0)
        else:
            sidebyside = prefs['sidebyside']

        if sidebyside:
            editrows = max(int(prefs['editrows']),
                           len(page.text.splitlines()) + 1)
        else:
            editrows = req.args.get('editrows')
            if editrows:
                if editrows != prefs['editrows']:
                    req.session.set('wiki_editrows', editrows,
                                    defaults['editrows'])
            else:
                editrows = prefs['editrows']

        data = self._page_data(req, page, action)
        context = web_context(req, page.resource)
        data.update({
            'author': author,
            'comment': comment,
            'edit_rows': editrows, 'sidebyside': sidebyside,
            'scroll_bar_pos': req.args.get('scroll_bar_pos', ''),
            'diff': None,
            'attachments': AttachmentModule(self.env).attachment_data(context),
        })
        if action in ('diff', 'merge'):
            old_text = original_text.splitlines() if original_text else []
            new_text = page.text.splitlines() if page.text else []
            diff_data, changes = self._prepare_diff(
                req, page, old_text, new_text, page.version, '')
            data.update({'diff': diff_data, 'changes': changes,
                         'action': 'preview', 'merge': action == 'merge',
                         'longcol': 'Version', 'shortcol': 'v'})
        elif sidebyside and action != 'collision':
            data['action'] = 'preview'

        self._wiki_ctxtnav(req, page)
        Chrome(self.env).add_wiki_toolbars(req)
        Chrome(self.env).add_auto_preview(req)
        add_script(req, 'common/js/folding.js')
        return 'wiki_edit.html', data, None
示例#30
0
                from trac.ticket.query import Query, QuerySyntaxError
                query = Query.from_string(self.env, query[6:], report=id)
                req.redirect(query.get_href(req))
            except QuerySyntaxError, e:
                req.redirect(
                    req.href.report(id, action='edit', error=to_unicode(e)))

        format = req.args.get('format')
        if format == 'sql':
            self._send_sql(req, id, title, description, sql)

        title = '{%i} %s' % (id, title)

        report_resource = Resource('report', id)
        req.perm.require('REPORT_VIEW', report_resource)
        context = web_context(req, report_resource)

        page = int(req.args.get('page', '1'))
        default_max = {
            'rss': self.items_per_page_rss,
            'csv': 0,
            'tab': 0
        }.get(format, self.items_per_page)
        max = req.args.get('max')
        limit = as_int(max, default_max, min=0)  # explict max takes precedence
        offset = (page - 1) * limit

        sort_col = req.args.get('sort', '')
        asc = req.args.get('asc', 1)
        asc = bool(int(asc))  # string '0' or '1' to int/boolean
示例#31
0
    def test_timeline_events(self):
        """Regression test for #11288"""
        req1 = MockRequest(self.env)
        tktmod = web_ui.TicketModule(self.env)
        now = datetime_now(utc)
        start = now - timedelta(hours=1)
        stop = now + timedelta(hours=1)
        events = tktmod.get_timeline_events(req1, start, stop,
                                            ['ticket_details'])
        self.assertTrue(all(ev[0] != 'batchmodify' for ev in events))

        prio_ids = {}
        for i in xrange(20):
            priority = ('', 'minor', 'major', 'critical')[i % 4]
            t = insert_ticket(self.env,
                              summary='Ticket %d' % i,
                              priority=priority)
            prio_ids.setdefault(t['priority'], []).append(t.id)
        tktids = prio_ids['critical'] + prio_ids['major'] + \
                 prio_ids['minor'] + prio_ids['']

        req2 = MockRequest(self.env,
                           method='POST',
                           authname='has_ta_&_bm',
                           path_info='/batchmodify',
                           args={
                               'batchmod_value_summary':
                               'batch updated ticket',
                               'batchmod_value_owner':
                               'ticket11288',
                               'batchmod_value_reporter':
                               'ticket11288',
                               'action':
                               'leave',
                               'selected_tickets':
                               ','.join(str(t) for t in tktids),
                           })

        batch = BatchModifyModule(self.env)
        self.assertTrue(batch.match_request(req2))
        with self.assertRaises(RequestDone):
            batch.process_request(req2)

        # shuffle ticket_change records
        with self.env.db_transaction as db:
            rows = db('SELECT * FROM ticket_change')
            db.execute('DELETE FROM ticket_change')
            rows = rows[0::4] + rows[1::4] + rows[2::4] + rows[3::4]
            db.executemany(
                'INSERT INTO ticket_change VALUES (%s)' % ','.join(
                    ('%s', ) * len(rows[0])), rows)

        events = tktmod.get_timeline_events(req1, start, stop,
                                            ['ticket_details'])
        events = [ev for ev in events if ev[0] == 'batchmodify']
        self.assertEqual(1, len(events))
        batch_ev = events[0]
        self.assertEqual('has_ta_&_bm', batch_ev[2])
        self.assertEqual(tktids, batch_ev[3][0])
        self.assertEqual('updated', batch_ev[3][1])

        context = web_context(req2)
        self.assertEqual(
            req2.href.query(id=','.join(str(t) for t in tktids)),
            tktmod.render_timeline_event(context, 'url', batch_ev))
示例#32
0
class TimelineModule(Component):

    implements(INavigationContributor, IPermissionRequestor, IRequestHandler,
               IRequestFilter, ITemplateProvider, IWikiSyntaxProvider)

    event_providers = ExtensionPoint(ITimelineEventProvider)

    default_daysback = IntOption('timeline', 'default_daysback', 30,
        """Default number of days displayed in the Timeline, in days.
        (''since 0.9.'')""")

    max_daysback = IntOption('timeline', 'max_daysback', 90,
        """Maximum number of days (-1 for unlimited) displayable in the
        Timeline. (''since 0.11'')""")

    abbreviated_messages = BoolOption('timeline', 'abbreviated_messages',
                                      True,
        """Whether wiki-formatted event messages should be truncated or not.

        This only affects the default rendering, and can be overriden by
        specific event providers, see their own documentation.
        (''Since 0.11'')""")

    _authors_pattern = re.compile(r'(-)?(?:"([^"]*)"|\'([^\']*)\'|([^\s]+))')

    # INavigationContributor methods

    def get_active_navigation_item(self, req):
        return 'timeline'

    def get_navigation_items(self, req):
        if 'TIMELINE_VIEW' in req.perm:
            yield ('mainnav', 'timeline',
                   tag.a(_("Timeline"), href=req.href.timeline(), accesskey=2))

    # IPermissionRequestor methods

    def get_permission_actions(self):
        return ['TIMELINE_VIEW']

    # IRequestHandler methods

    def match_request(self, req):
        return req.path_info == '/timeline'

    def process_request(self, req):
        req.perm.assert_permission('TIMELINE_VIEW')

        format = req.args.get('format')
        maxrows = int(req.args.get('max', 50 if format == 'rss' else 0))
        lastvisit = int(req.session.get('timeline.lastvisit', '0'))

        # indication of new events is unchanged when form is updated by user
        revisit = any(a in req.args for a in ['update', 'from', 'daysback',
                                              'author'])
        if revisit:
            lastvisit = int(req.session.get('timeline.nextlastvisit',
                                            lastvisit))

        # Parse the from date and adjust the timestamp to the last second of
        # the day
        fromdate = today = datetime.now(req.tz)
        yesterday = to_datetime(today.replace(tzinfo=None) - timedelta(days=1),
                                req.tz)
        precisedate = precision = None
        if 'from' in req.args:
            # Acquire from date only from non-blank input
            reqfromdate = req.args['from'].strip()
            if reqfromdate:
                precisedate = user_time(req, parse_date, reqfromdate)
                fromdate = precisedate.astimezone(req.tz)
            precision = req.args.get('precision', '')
            if precision.startswith('second'):
                precision = timedelta(seconds=1)
            elif precision.startswith('minute'):
                precision = timedelta(minutes=1)
            elif precision.startswith('hour'):
                precision = timedelta(hours=1)
            else:
                precision = None
        fromdate = to_datetime(datetime(fromdate.year, fromdate.month,
                                        fromdate.day, 23, 59, 59, 999999),
                               req.tz)

        daysback = as_int(req.args.get('daysback'),
                          90 if format == 'rss' else None)
        if daysback is None:
            daysback = as_int(req.session.get('timeline.daysback'), None)
        if daysback is None:
            daysback = self.default_daysback
        daysback = max(0, daysback)
        if self.max_daysback >= 0:
            daysback = min(self.max_daysback, daysback)

        authors = req.args.get('authors')
        if authors is None and format != 'rss':
            authors = req.session.get('timeline.authors')
        authors = (authors or '').strip()

        data = {'fromdate': fromdate, 'daysback': daysback,
                'authors': authors,
                'today': user_time(req, format_date, today),
                'yesterday': user_time(req, format_date, yesterday),
                'precisedate': precisedate, 'precision': precision,
                'events': [], 'filters': [],
                'abbreviated_messages': self.abbreviated_messages,
                'lastvisit': lastvisit}

        available_filters = []
        for event_provider in self.event_providers:
            available_filters += event_provider.get_timeline_filters(req) or []

        # check the request or session for enabled filters, or use default
        filters = [f[0] for f in available_filters if f[0] in req.args]
        if not filters and format != 'rss':
            filters = [f[0] for f in available_filters
                       if req.session.get('timeline.filter.' + f[0]) == '1']
        if not filters:
            filters = [f[0] for f in available_filters if len(f) == 2 or f[2]]

        # save the results of submitting the timeline form to the session
        if 'update' in req.args:
            for filter in available_filters:
                key = 'timeline.filter.%s' % filter[0]
                if filter[0] in req.args:
                    req.session[key] = '1'
                elif key in req.session:
                    del req.session[key]

        stop = fromdate
        start = to_datetime(stop.replace(tzinfo=None) - \
                                timedelta(days=daysback + 1),
                            req.tz)

        # create author include and exclude sets
        include = set()
        exclude = set()
        for match in self._authors_pattern.finditer(authors):
            name = (match.group(2) or match.group(3) or match.group(4)).lower()
            if match.group(1):
                exclude.add(name)
            else:
                include.add(name)

        # gather all events for the given period of time
        events = []
        for provider in self.event_providers:
            try:
                for event in provider.get_timeline_events(req, start, stop,
                                                          filters) or []:
                    # Check for 0.10 events
                    author = (event[2 if len(event) < 6 else 4] or '').lower()
                    if (not include or author in include) \
                       and not author in exclude:
                        events.append(self._event_data(provider, event))
            except Exception, e: # cope with a failure of that provider
                self._provider_failure(e, req, provider, filters,
                                       [f[0] for f in available_filters])

        # prepare sorted global list
        events = sorted(events, key=lambda e: e['date'], reverse=True)
        if maxrows:
            events = events[:maxrows]

        data['events'] = events

        if format == 'rss':
            data['email_map'] = Chrome(self.env).get_email_map()
            rss_context = web_context(req, absurls=True)
            rss_context.set_hints(wiki_flavor='html', shorten_lines=False)
            data['context'] = rss_context
            return 'timeline.rss', data, 'application/rss+xml'
        else:
            req.session.set('timeline.daysback', daysback,
                            self.default_daysback)
            req.session.set('timeline.authors', authors, '')
            # store lastvisit
            if events and not revisit:
                lastviewed = to_utimestamp(events[0]['date'])
                req.session['timeline.lastvisit'] = max(lastvisit, lastviewed)
                req.session['timeline.nextlastvisit'] = lastvisit
            html_context = web_context(req)
            html_context.set_hints(wiki_flavor='oneliner',
                                   shorten_lines=self.abbreviated_messages)
            data['context'] = html_context

        add_stylesheet(req, 'common/css/timeline.css')
        rss_href = req.href.timeline([(f, 'on') for f in filters],
                                     daysback=90, max=50, authors=authors,
                                     format='rss')
        add_link(req, 'alternate', auth_link(req, rss_href), _('RSS Feed'),
                 'application/rss+xml', 'rss')
        Chrome(self.env).add_jquery_ui(req)

        for filter_ in available_filters:
            data['filters'].append({'name': filter_[0], 'label': filter_[1],
                                    'enabled': filter_[0] in filters})

        # Navigation to the previous/next period of 'daysback' days
        previous_start = fromdate.replace(tzinfo=None) - \
                            timedelta(days=daysback + 1)
        previous_start = format_date(to_datetime(previous_start, req.tz),
                                     format='%Y-%m-%d', tzinfo=req.tz)
        add_link(req, 'prev', req.href.timeline(from_=previous_start,
                                                authors=authors,
                                                daysback=daysback),
                 _('Previous Period'))
        if today - fromdate > timedelta(days=0):
            next_start = fromdate.replace(tzinfo=None) + \
                            timedelta(days=daysback + 1)
            next_start = format_date(to_datetime(next_start, req.tz),
                                     format='%Y-%m-%d', tzinfo=req.tz)
            add_link(req, 'next', req.href.timeline(from_=next_start,
                                                    authors=authors,
                                                    daysback=daysback),
                     _('Next Period'))
        prevnext_nav(req, _('Previous Period'), _('Next Period'))

        return 'timeline.html', data, None
示例#33
0
    def _render_view(self, req, milestone):
        milestone_groups = []
        available_groups = []
        default_group_by_available = False
        ticket_fields = TicketSystem(self.env).get_ticket_fields()

        # collect fields that can be used for grouping
        for field in ticket_fields:
            if field['type'] == 'select' and field['name'] != 'milestone' \
                    or field['name'] in ('owner', 'reporter'):
                available_groups.append({
                    'name': field['name'],
                    'label': field['label']
                })
                if field['name'] == self.default_group_by:
                    default_group_by_available = True

        # determine the field currently used for grouping
        by = None
        if default_group_by_available:
            by = self.default_group_by
        elif available_groups:
            by = available_groups[0]['name']
        by = req.args.get('by', by)

        tickets = get_tickets_for_milestone(self.env,
                                            milestone=milestone.name,
                                            field=by)
        tickets = apply_ticket_permissions(self.env, req, tickets)
        stat = get_ticket_stats(self.stats_provider, tickets)

        context = web_context(req, milestone.resource)
        data = {
            'context': context,
            'milestone': milestone,
            'attachments': AttachmentModule(self.env).attachment_data(context),
            'available_groups': available_groups,
            'grouped_by': by,
            'groups': milestone_groups
        }
        data.update(milestone_stats_data(self.env, req, stat, milestone.name))

        if by:

            def per_group_stats_data(gstat, group_name):
                return milestone_stats_data(self.env, req, gstat,
                                            milestone.name, by, group_name)

            milestone_groups.extend(
                grouped_stats_data(self.env, self.stats_provider, tickets, by,
                                   per_group_stats_data))

        add_stylesheet(req, 'common/css/roadmap.css')

        def add_milestone_link(rel, milestone):
            href = req.href.milestone(milestone.name, by=req.args.get('by'))
            add_link(req, rel, href,
                     _('Milestone "%(name)s"', name=milestone.name))

        milestones = [
            m for m in Milestone.select(self.env)
            if 'MILESTONE_VIEW' in req.perm(m.resource)
        ]
        idx = [i for i, m in enumerate(milestones) if m.name == milestone.name]
        if idx:
            idx = idx[0]
            if idx > 0:
                add_milestone_link('first', milestones[0])
                add_milestone_link('prev', milestones[idx - 1])
            if idx < len(milestones) - 1:
                add_milestone_link('next', milestones[idx + 1])
                add_milestone_link('last', milestones[-1])
        prevnext_nav(req, _("Previous Milestone"), _("Next Milestone"),
                     _("Back to Roadmap"))

        return 'milestone_view.html', data
示例#34
0
文件: report.py 项目: t2y/trac
    def _render_view(self, req, id):
        """Retrieve the report results and pre-process them for rendering."""
        title, description, sql = self.get_report(id)
        try:
            args = self.get_var_args(req)
        except ValueError as e:
            raise TracError(_("Report failed: %(error)s", error=e))

        # If this is a saved custom query, redirect to the query module
        #
        # A saved query is either an URL query (?... or query:?...),
        # or a query language expression (query:...).
        #
        # It may eventually contain newlines, for increased clarity.
        #
        query = ''.join([line.strip() for line in sql.splitlines()])
        if query and (query[0] == '?' or query.startswith('query:?')):
            query = query if query[0] == '?' else query[6:]
            report_id = 'report=%s' % id
            if 'report=' in query:
                if not report_id in query:
                    err = _(
                        'When specified, the report number should be '
                        '"%(num)s".',
                        num=id)
                    req.redirect(req.href.report(id, action='edit', error=err))
            else:
                if query[-1] != '?':
                    query += '&'
                query += report_id
            req.redirect(req.href.query() + quote_query_string(query))
        elif query.startswith('query:'):
            try:
                from trac.ticket.query import Query, QuerySyntaxError
                query = Query.from_string(self.env, query[6:], report=id)
                req.redirect(query.get_href(req))
            except QuerySyntaxError as e:
                req.redirect(
                    req.href.report(id, action='edit', error=to_unicode(e)))

        format = req.args.get('format')
        if format == 'sql':
            self._send_sql(req, id, title, description, sql)

        title = '{%i} %s' % (id, title)

        report_resource = Resource('report', id)
        req.perm(report_resource).require('REPORT_VIEW')
        context = web_context(req, report_resource)

        page = int(req.args.get('page', '1'))
        default_max = {
            'rss': self.items_per_page_rss,
            'csv': 0,
            'tab': 0
        }.get(format, self.items_per_page)
        max = req.args.get('max')
        limit = as_int(max, default_max, min=0)  # explict max takes precedence
        offset = (page - 1) * limit

        sort_col = req.args.get('sort', '')
        asc = req.args.get('asc', 1)
        asc = bool(int(asc))  # string '0' or '1' to int/boolean

        def report_href(**kwargs):
            """Generate links to this report preserving user variables,
            and sorting and paging variables.
            """
            params = args.copy()
            if sort_col:
                params['sort'] = sort_col
            params['page'] = page
            if max:
                params['max'] = max
            params.update(kwargs)
            params['asc'] = '1' if params.get('asc', asc) else '0'
            return req.href.report(id, params)

        data = {
            'action': 'view',
            'report': {
                'id': id,
                'resource': report_resource
            },
            'context': context,
            'title': sub_vars(title, args),
            'description': sub_vars(description or '', args),
            'max': limit,
            'args': args,
            'show_args_form': False,
            'message': None,
            'paginator': None,
            'report_href': report_href,
        }

        res = self.execute_paginated_report(req, id, sql, args, limit, offset)

        if len(res) == 2:
            e, sql = res
            data['message'] = \
                tag_("Report execution failed: %(error)s %(sql)s",
                     error=tag.pre(exception_to_unicode(e)),
                     sql=tag(tag.hr(),
                             tag.pre(sql, style="white-space: pre")))
            return 'report_view.html', data, None

        cols, results, num_items, missing_args, limit_offset = res
        need_paginator = limit > 0 and limit_offset
        need_reorder = limit_offset is None
        results = [list(row) for row in results]
        numrows = len(results)

        paginator = None
        if need_paginator:
            paginator = Paginator(results, page - 1, limit, num_items)
            data['paginator'] = paginator
            if paginator.has_next_page:
                add_link(req, 'next', report_href(page=page + 1),
                         _('Next Page'))
            if paginator.has_previous_page:
                add_link(req, 'prev', report_href(page=page - 1),
                         _('Previous Page'))

            pagedata = []
            shown_pages = paginator.get_shown_pages(21)
            for p in shown_pages:
                pagedata.append([
                    report_href(page=p), None,
                    str(p),
                    _('Page %(num)d', num=p)
                ])
            fields = ['href', 'class', 'string', 'title']
            paginator.shown_pages = [dict(zip(fields, p)) for p in pagedata]
            paginator.current_page = {
                'href': None,
                'class': 'current',
                'string': str(paginator.page + 1),
                'title': None
            }
            numrows = paginator.num_items

        # Place retrieved columns in groups, according to naming conventions
        #  * _col_ means fullrow, i.e. a group with one header
        #  * col_ means finish the current group and start a new one

        field_labels = TicketSystem(self.env).get_ticket_field_labels()

        header_groups = [[]]
        for idx, col in enumerate(cols):
            if col in field_labels:
                title = field_labels[col]
            else:
                title = col.strip('_').capitalize()
            header = {
                'col': col,
                'title': title,
                'hidden': False,
                'asc': None,
            }

            if col == sort_col:
                header['asc'] = asc
                if not paginator and need_reorder:
                    # this dict will have enum values for sorting
                    # and will be used in sortkey(), if non-empty:
                    sort_values = {}
                    if sort_col in ('status', 'resolution', 'priority',
                                    'severity'):
                        # must fetch sort values for that columns
                        # instead of comparing them as strings
                        with self.env.db_query as db:
                            for name, value in db(
                                    "SELECT name, %s FROM enum WHERE type=%%s"
                                    % db.cast('value', 'int'), (sort_col, )):
                                sort_values[name] = value

                    def sortkey(row):
                        val = row[idx]
                        # check if we have sort_values, then use them as keys.
                        if sort_values:
                            return sort_values.get(val)
                        # otherwise, continue with string comparison:
                        if isinstance(val, basestring):
                            val = val.lower()
                        return val

                    results = sorted(results, key=sortkey, reverse=(not asc))

            header_group = header_groups[-1]

            if col.startswith('__') and col.endswith('__'):  # __col__
                header['hidden'] = True
            elif col[0] == '_' and col[-1] == '_':  # _col_
                header_group = []
                header_groups.append(header_group)
                header_groups.append([])
            elif col[0] == '_':  # _col
                header['hidden'] = True
            elif col[-1] == '_':  # col_
                header_groups.append([])
            header_group.append(header)

        # Structure the rows and cells:
        #  - group rows according to __group__ value, if defined
        #  - group cells the same way headers are grouped
        chrome = Chrome(self.env)
        row_groups = []
        authorized_results = []
        prev_group_value = None
        for row_idx, result in enumerate(results):
            col_idx = 0
            cell_groups = []
            row = {'cell_groups': cell_groups}
            realm = 'ticket'
            parent_realm = ''
            parent_id = ''
            email_cells = []
            for header_group in header_groups:
                cell_group = []
                for header in header_group:
                    value = cell_value(result[col_idx])
                    cell = {'value': value, 'header': header, 'index': col_idx}
                    col = header['col']
                    col_idx += 1
                    # Detect and create new group
                    if col == '__group__' and value != prev_group_value:
                        prev_group_value = value
                        # Brute force handling of email in group by header
                        row_groups.append(
                            (value and chrome.format_author(req, value), []))
                    # Other row properties
                    row['__idx__'] = row_idx
                    if col in self._html_cols:
                        row[col] = value
                    if col in ('report', 'ticket', 'id', '_id'):
                        row['id'] = value
                    # Special casing based on column name
                    col = col.strip('_')
                    if col in ('reporter', 'cc', 'owner'):
                        email_cells.append(cell)
                    elif col == 'realm':
                        realm = value
                    elif col == 'parent_realm':
                        parent_realm = value
                    elif col == 'parent_id':
                        parent_id = value
                    cell_group.append(cell)
                cell_groups.append(cell_group)
            if parent_realm:
                resource = Resource(realm,
                                    row.get('id'),
                                    parent=Resource(parent_realm, parent_id))
            else:
                resource = Resource(realm, row.get('id'))
            # FIXME: for now, we still need to hardcode the realm in the action
            if resource.realm.upper() + '_VIEW' not in req.perm(resource):
                continue
            authorized_results.append(result)
            if email_cells:
                for cell in email_cells:
                    emails = chrome.format_emails(context.child(resource),
                                                  cell['value'])
                    result[cell['index']] = cell['value'] = emails
            row['resource'] = resource
            if row_groups:
                row_group = row_groups[-1][1]
            else:
                row_group = []
                row_groups = [(None, row_group)]
            row_group.append(row)

        data.update({
            'header_groups': header_groups,
            'row_groups': row_groups,
            'numrows': numrows
        })

        if format == 'rss':
            data['email_map'] = chrome.get_email_map()
            data['context'] = web_context(req, report_resource, absurls=True)
            return 'report.rss', data, 'application/rss+xml'
        elif format == 'csv':
            filename = 'report_%s.csv' % id if id else 'report.csv'
            self._send_csv(req,
                           cols,
                           authorized_results,
                           mimetype='text/csv',
                           filename=filename)
        elif format == 'tab':
            filename = 'report_%s.tsv' % id if id else 'report.tsv'
            self._send_csv(req,
                           cols,
                           authorized_results,
                           '\t',
                           mimetype='text/tab-separated-values',
                           filename=filename)
        else:
            p = page if max is not None else None
            add_link(req, 'alternate',
                     auth_link(req, report_href(format='rss', page=None)),
                     _('RSS Feed'), 'application/rss+xml', 'rss')
            add_link(req, 'alternate', report_href(format='csv', page=p),
                     _('Comma-delimited Text'), 'text/plain')
            add_link(req, 'alternate', report_href(format='tab', page=p),
                     _('Tab-delimited Text'), 'text/plain')
            if 'REPORT_SQL_VIEW' in req.perm('report', id):
                add_link(req, 'alternate', req.href.report(id=id,
                                                           format='sql'),
                         _('SQL Query'), 'text/plain')

            # reuse the session vars of the query module so that
            # the query navigation links on the ticket can be used to
            # navigate report results as well
            try:
                req.session['query_tickets'] = \
                    ' '.join([str(int(row['id']))
                              for rg in row_groups for row in rg[1]])
                req.session['query_href'] = \
                    req.session['query_href'] = report_href()
                # Kludge: we have to clear the other query session
                # variables, but only if the above succeeded
                for var in ('query_constraints', 'query_time'):
                    if var in req.session:
                        del req.session[var]
            except (ValueError, KeyError):
                pass
            if set(data['args']) - set(['USER']):
                data['show_args_form'] = True
                add_script(req, 'common/js/folding.js')
            if missing_args:
                add_warning(
                    req,
                    _('The following arguments are missing: %(args)s',
                      args=", ".join(missing_args)))
            return 'report_view.html', data, None
示例#35
0
    def process_request(self, req):
        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.lower() in ('', 'head'):
            rev = None
        format = req.args.get('format')
        order = req.args.get('order', 'name').lower()
        desc = 'desc' in req.args

        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.is_viewable(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 if qs else ''))
        reponame = repos.reponame if repos else None

        # Find node for the requested path/rev
        context = web_context(req)
        node = None
        changeset = 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 as e:
                raise ResourceNotFound(e, _('Invalid changeset number'))
            if node:
                try:
                    # use changeset instance to retrieve branches and tags
                    changeset = repos.get_changeset(node.rev)
                except NoSuchChangeset:
                    pass

            context = context.child(
                repos.resource.child(self.realm, path, version=rev_or_latest))
            display_rev = repos.display_rev

        # Prepare template data
        path_links = get_path_links(req.href, reponame, path, rev, order, desc)

        repo_data = dir_data = file_data = None
        if show_index:
            repo_data = self._render_repository_index(context,
                                                      all_repositories, order,
                                                      desc)
        if node:
            if not node.is_viewable(req.perm):
                raise PermissionError(
                    'BROWSER_VIEW' if node.isdir else 'FILE_VIEW',
                    node.resource, self.env)
            if node.isdir:
                if format in ('zip', ):  # extension point here...
                    self._render_zip(req, context, repos, node, rev)
                    # not reached
                dir_data = self._render_dir(req, repos, node, rev, order, desc)
            elif node.isfile:
                file_data = self._render_file(req, context, repos, node, rev)

        if not repos and not (repo_data and repo_data['repositories']):
            # If no viewable repositories, check permission instead of
            # repos.is_viewable()
            req.perm.require('BROWSER_VIEW')
            if show_index:
                raise ResourceNotFound(_("No viewable repositories"))
            else:
                raise ResourceNotFound(_("No node %(path)s", path=path))

        quickjump_data = properties_data = None
        if node and not req.is_xhr:
            properties_data = self.render_properties('browser', context,
                                                     node.get_properties())
            quickjump_data = list(repos.get_quickjump_entries(rev))

        data = {
            'context':
            context,
            'reponame':
            reponame,
            'repos':
            repos,
            'repoinfo':
            all_repositories.get(reponame or ''),
            'path':
            path,
            'rev':
            node and node.rev,
            'stickyrev':
            rev,
            'display_rev':
            display_rev,
            'changeset':
            changeset,
            'created_path':
            node and node.created_path,
            'created_rev':
            node and node.created_rev,
            'properties':
            properties_data,
            'path_links':
            path_links,
            'order':
            order,
            'desc':
            1 if desc else None,
            'repo':
            repo_data,
            'dir':
            dir_data,
            'file':
            file_data,
            'quickjump_entries':
            quickjump_data,
            'wiki_format_messages':
            self.config['changeset'].getbool('wiki_format_messages'),
        }
        if req.is_xhr:  # render and return the content only
            return 'dir_entries.html', data

        if dir_data or repo_data:
            add_script(req, 'common/js/expand_dir.js')
            add_script(req, 'common/js/keyboard_nav.js')

        # Links for contextual navigation
        if node:
            if node.isfile:
                prev_rev = repos.previous_rev(rev=node.created_rev,
                                              path=node.created_path)
                if prev_rev:
                    href = req.href.browser(reponame,
                                            node.created_path,
                                            rev=prev_rev)
                    add_link(req, 'prev', href,
                             _('Revision %(num)s', num=display_rev(prev_rev)))
                if rev is not None:
                    add_link(req, 'up',
                             req.href.browser(reponame, node.created_path))
                next_rev = repos.next_rev(rev=node.created_rev,
                                          path=node.created_path)
                if next_rev:
                    href = req.href.browser(reponame,
                                            node.created_path,
                                            rev=next_rev)
                    add_link(req, 'next', href,
                             _('Revision %(num)s', num=display_rev(next_rev)))
                prevnext_nav(req, _('Previous Revision'), _('Next Revision'),
                             _('Latest Revision'))
            else:
                if path != '/':
                    add_link(req, 'up', path_links[-2]['href'],
                             _('Parent directory'))
                add_ctxtnav(
                    req,
                    tag.a(_('Last Change'),
                          href=req.href.changeset(node.created_rev, reponame,
                                                  node.created_path)))
            if node.isfile:
                annotate = data['file']['annotate']
                if annotate:
                    add_ctxtnav(req,
                                _('Normal'),
                                title=_('View file without annotations'),
                                href=req.href.browser(reponame,
                                                      node.created_path,
                                                      rev=rev))
                if annotate != 'blame':
                    add_ctxtnav(req,
                                _('Blame'),
                                title=_('Annotate each line with the last '
                                        'changed revision '
                                        '(this can be time consuming...)'),
                                href=req.href.browser(reponame,
                                                      node.created_path,
                                                      rev=rev,
                                                      annotate='blame'))
            add_ctxtnav(req,
                        _('Revision Log'),
                        href=req.href.log(reponame, path, rev=rev))
            path_url = repos.get_path_url(path, rev)
            if path_url:
                if path_url.startswith('//'):
                    path_url = req.scheme + ':' + path_url
                add_ctxtnav(req, _('Repository URL'), href=path_url)

        add_stylesheet(req, 'common/css/browser.css')
        return 'browser.html', data
示例#36
0
    def process_request(self, req):
        req.perm.require('LOG_VIEW')

        mode = req.args.get('mode', 'stop_on_copy')
        path = req.args.get('path', '/')
        rev = req.args.get('rev')
        stop_rev = req.args.get('stop_rev')
        revs = req.args.get('revs')
        format = req.args.get('format')
        verbose = req.args.get('verbose')
        limit = req.args.getint('limit', self.default_log_limit)

        rm = RepositoryManager(self.env)
        reponame, repos, path = rm.get_repository_by_path(path)

        if not repos:
            if path == '/':
                raise TracError(
                    _("No repository specified and no default"
                      " repository configured."))
            else:
                raise ResourceNotFound(
                    _("Repository '%(repo)s' not found",
                      repo=reponame or path.strip('/')))

        if reponame != repos.reponame:  # Redirect alias
            qs = req.query_string
            req.redirect(
                req.href.log(repos.reponame or None, path) +
                ('?' + qs if qs else ''))

        normpath = repos.normalize_path(path)

        # if `revs` parameter is given, then we're restricted to the
        # corresponding revision ranges.
        # If not, then we're considering all revisions since `rev`,
        # on that path, in which case `revranges` will be None.
        if revs:
            revranges = RevRanges(repos, revs, resolve=True)
            rev = revranges.b
        else:
            revranges = None
            rev = repos.normalize_rev(rev)

        # The `history()` method depends on the mode:
        #  * for ''stop on copy'' and ''follow copies'', it's `Node.history()`
        #    unless explicit ranges have been specified
        #  * for ''show only add, delete'' we're using
        #   `Repository.get_path_history()`
        cset_resource = repos.resource.child(self.realm)
        show_graph = False
        curr_revrange = []
        if mode == 'path_history':

            def history():
                for h in repos.get_path_history(path, rev):
                    if 'CHANGESET_VIEW' in req.perm(cset_resource(id=h[1])):
                        yield h

        elif revranges:
            show_graph = path == '/' and not verbose \
                         and not repos.has_linear_changesets \
                         and len(revranges) == 1

            def history():
                separator = False
                for a, b in reversed(revranges.pairs):
                    curr_revrange[:] = (a, b)
                    node = get_existing_node(req, repos, path, b)
                    for p, rev, chg in node.get_history():
                        if repos.rev_older_than(rev, a):
                            break
                        if 'CHANGESET_VIEW' in req.perm(cset_resource(id=rev)):
                            separator = True
                            yield p, rev, chg
                    else:
                        separator = False
                    if separator:
                        yield p, rev, None
        else:
            show_graph = path == '/' and not verbose \
                         and not repos.has_linear_changesets

            def history():
                node = get_existing_node(req, repos, path, rev)
                for h in node.get_history():
                    if 'CHANGESET_VIEW' in req.perm(cset_resource(id=h[1])):
                        yield h

        # -- retrieve history, asking for limit+1 results
        info = []
        depth = 1
        previous_path = normpath
        count = 0
        history_remaining = True
        for old_path, old_rev, old_chg in history():
            if stop_rev and repos.rev_older_than(old_rev, stop_rev):
                break
            old_path = repos.normalize_path(old_path)

            item = {
                'path': old_path,
                'rev': old_rev,
                'existing_rev': old_rev,
                'change': old_chg,
                'depth': depth,
            }

            if old_chg == Changeset.DELETE:
                item['existing_rev'] = repos.previous_rev(old_rev, old_path)
            if not (mode == 'path_history' and old_chg == Changeset.EDIT):
                info.append(item)
            if old_path and old_path != previous_path and \
                    not (mode == 'path_history' and old_path == normpath):
                depth += 1
                item['depth'] = depth
                item['copyfrom_path'] = old_path
                if mode == 'stop_on_copy':
                    break
                elif mode == 'path_history':
                    depth -= 1
            if old_chg is None:  # separator entry
                stop_limit = limit
            else:
                count += 1
                stop_limit = limit + 1
            if count >= stop_limit:
                break
            previous_path = old_path
        else:
            history_remaining = False
        if not info:
            node = get_existing_node(req, repos, path, rev)
            if repos.rev_older_than(stop_rev, node.created_rev):
                # FIXME: we should send a 404 error here
                raise TracError(
                    _(
                        "The file or directory '%(path)s' doesn't "
                        "exist at revision %(rev)s or at any "
                        "previous revision.",
                        path=path,
                        rev=repos.display_rev(rev)), _('Nonexistent path'))

        # Generate graph data
        graph = {}
        if show_graph:
            threads, vertices, columns = \
                make_log_graph(repos, (item['rev'] for item in info))
            graph.update(threads=threads,
                         vertices=vertices,
                         columns=columns,
                         colors=self.graph_colors,
                         line_width=0.04,
                         dot_radius=0.1)
            add_script(req, 'common/js/excanvas.js', ie_if='IE')
            add_script(req, 'common/js/log_graph.js')
            add_script_data(req, graph=graph)

        def make_log_href(path, **args):
            link_rev = rev
            if rev == str(repos.youngest_rev):
                link_rev = None
            params = {'rev': link_rev, 'mode': mode, 'limit': limit}
            params.update(args)
            if verbose:
                params['verbose'] = verbose
            return req.href.log(repos.reponame or None, path, **params)

        if format in ('rss', 'changelog'):
            info = [i for i in info if i['change']]  # drop separators
            if info and count > limit:
                del info[-1]
        elif info and history_remaining and count >= limit:
            # stop_limit reached, there _might_ be some more
            next_rev = info[-1]['rev']
            next_path = info[-1]['path']
            next_revranges = None
            if curr_revrange:
                new_revrange = (curr_revrange[0], next_rev) \
                               if info[-1]['change'] else None
                next_revranges = revranges.truncate(curr_revrange,
                                                    new_revrange)
                next_revranges = unicode(next_revranges) or None
            if next_revranges or not revranges:
                older_revisions_href = make_log_href(next_path,
                                                     rev=next_rev,
                                                     revs=next_revranges)
                add_link(
                    req, 'next', older_revisions_href,
                    _('Revision Log (restarting at %(path)s, rev. '
                      '%(rev)s)',
                      path=next_path,
                      rev=repos.display_rev(next_rev)))
            # only show fully 'limit' results, use `change == None` as a marker
            info[-1]['change'] = None

        revisions = [i['rev'] for i in info]
        changes = get_changes(repos, revisions, self.log)
        extra_changes = {}

        if format == 'changelog':
            for rev in revisions:
                changeset = changes[rev]
                cs = {}
                cs['message'] = wrap(changeset.message,
                                     70,
                                     initial_indent='\t',
                                     subsequent_indent='\t')
                files = []
                actions = []
                for cpath, kind, chg, bpath, brev in changeset.get_changes():
                    files.append(bpath if chg == Changeset.DELETE else cpath)
                    actions.append(chg)
                cs['files'] = files
                cs['actions'] = actions
                extra_changes[rev] = cs

        data = {
            'context':
            web_context(req, 'source', path, parent=repos.resource),
            'reponame':
            repos.reponame or None,
            'repos':
            repos,
            'path':
            path,
            'rev':
            rev,
            'stop_rev':
            stop_rev,
            'display_rev':
            repos.display_rev,
            'revranges':
            revranges,
            'mode':
            mode,
            'verbose':
            verbose,
            'limit':
            limit,
            'items':
            info,
            'changes':
            changes,
            'extra_changes':
            extra_changes,
            'graph':
            graph,
            'wiki_format_messages':
            self.config['changeset'].getbool('wiki_format_messages')
        }

        if format == 'changelog':
            return 'revisionlog.txt', data, {'content_type': 'text/plain'}
        elif format == 'rss':
            data['context'] = web_context(req,
                                          'source',
                                          path,
                                          parent=repos.resource,
                                          absurls=True)
            return ('revisionlog.rss', data, {
                'content_type': 'application/rss+xml'
            })

        item_ranges = []
        range = []
        for item in info:
            if item['change'] is None:  # separator
                if range:  # start new range
                    range.append(item)
                    item_ranges.append(range)
                    range = []
            else:
                range.append(item)
        if range:
            item_ranges.append(range)
        data['item_ranges'] = item_ranges

        add_stylesheet(req, 'common/css/diff.css')
        add_stylesheet(req, 'common/css/browser.css')

        path_links = get_path_links(req.href, repos.reponame, path, rev)
        if path_links:
            data['path_links'] = path_links
        if path != '/':
            add_link(req, 'up', path_links[-2]['href'], _('Parent directory'))

        rss_href = make_log_href(path,
                                 format='rss',
                                 revs=revs,
                                 stop_rev=stop_rev)
        add_link(req, 'alternate', auth_link(req, rss_href), _('RSS Feed'),
                 'application/rss+xml', 'rss')
        changelog_href = make_log_href(path,
                                       format='changelog',
                                       revs=revs,
                                       stop_rev=stop_rev)
        add_link(req, 'alternate', changelog_href, _('ChangeLog'),
                 'text/plain')

        add_ctxtnav(req,
                    _('View Latest Revision'),
                    href=req.href.browser(repos.reponame or None, path))
        if 'next' in req.chrome['links']:
            next = req.chrome['links']['next'][0]
            add_ctxtnav(
                req,
                tag.span(tag.a(_('Older Revisions'), href=next['href']),
                         Markup(' &rarr;')))

        return 'revisionlog.html', data
示例#37
0
 def _send_card(self, req, card):
     context = web_context(req, 'cards')
     self._send_json(req, card.serialized(self.env, context))
示例#38
0
    def _render_view(self, req, milestone):
        milestone_groups = []
        available_groups = []
        component_group_available = False
        ticket_fields = TicketSystem(self.env).get_ticket_fields()

        # collect fields that can be used for grouping
        for field in ticket_fields:
            if field['type'] == 'select' and field['name'] != 'milestone' \
                    or field['name'] in ('owner', 'reporter'):
                available_groups.append({
                    'name': field['name'],
                    'label': field['label']
                })
                if field['name'] == 'component':
                    component_group_available = True

        # determine the field currently used for grouping
        by = None
        if component_group_available:
            by = 'component'
        elif available_groups:
            by = available_groups[0]['name']
        by = req.args.get('by', by)

        tickets = get_tickets_for_milestone(self.env,
                                            milestone=milestone.name,
                                            field=by)
        tickets = apply_ticket_permissions(self.env, req, tickets)
        stat = get_ticket_stats(self.stats_provider, tickets)

        context = web_context(req, milestone.resource)
        data = {
            'context': context,
            'milestone': milestone,
            'attachments': AttachmentModule(self.env).attachment_data(context),
            'available_groups': available_groups,
            'grouped_by': by,
            'groups': milestone_groups
        }
        data.update(milestone_stats_data(self.env, req, stat, milestone.name))

        if by:
            groups = []
            for field in ticket_fields:
                if field['name'] == by:
                    if 'options' in field:
                        groups = field['options']
                        if field.get('optional'):
                            groups.insert(0, '')
                    else:
                        groups = [
                            group for group, in self.env.db_query("""
                                  SELECT DISTINCT COALESCE(%s, '') FROM ticket
                                  ORDER BY COALESCE(%s, '')
                                  """ % (by, by))
                        ]
            max_count = 0
            group_stats = []

            for group in groups:
                values = (group, ) if group else (None, group)
                group_tickets = [t for t in tickets if t[by] in values]
                if not group_tickets:
                    continue

                gstat = get_ticket_stats(self.stats_provider, group_tickets)
                if gstat.count > max_count:
                    max_count = gstat.count

                group_stats.append(gstat)

                gs_dict = {'name': group}
                gs_dict.update(
                    milestone_stats_data(self.env, req, gstat, milestone.name,
                                         by, group))
                milestone_groups.append(gs_dict)

            for idx, gstat in enumerate(group_stats):
                gs_dict = milestone_groups[idx]
                percent = 1.0
                if max_count:
                    percent = float(gstat.count) / float(max_count) * 100
                gs_dict['percent_of_max_total'] = percent

        add_stylesheet(req, 'common/css/roadmap.css')
        add_script(req, 'common/js/folding.js')

        def add_milestone_link(rel, milestone):
            href = req.href.milestone(milestone.name, by=req.args.get('by'))
            add_link(req, rel, href,
                     _('Milestone "%(name)s"', name=milestone.name))

        milestones = [
            m for m in Milestone.select(self.env)
            if 'MILESTONE_VIEW' in req.perm(m.resource)
        ]
        idx = [i for i, m in enumerate(milestones) if m.name == milestone.name]
        if idx:
            idx = idx[0]
            if idx > 0:
                add_milestone_link('first', milestones[0])
                add_milestone_link('prev', milestones[idx - 1])
            if idx < len(milestones) - 1:
                add_milestone_link('next', milestones[idx + 1])
                add_milestone_link('last', milestones[-1])
        prevnext_nav(req, _('Previous Milestone'), _('Next Milestone'),
                     _('Back to Roadmap'))

        return 'milestone_view.html', data, None
示例#39
0
    def process_request(self, req):
        req.perm.require('TAGS_VIEW')

        match = re.match(r'/tags/?(.*)', req.path_info)
        tag_id = match.group(1) and match.group(1) or None
        query = req.args.get('q', '')

        # Consider only providers, that are permitted for display.
        tag_system = TagSystem(self.env)
        all_realms = tag_system.get_taggable_realms(req.perm)
        if not (tag_id or query) or [r for r in all_realms if r in req.args
                                     ] == []:
            for realm in all_realms:
                if realm not in self.exclude_realms:
                    req.args[realm] = 'on'
        checked_realms = [r for r in all_realms if r in req.args]
        if query:
            # Add permitted realms from query expression.
            checked_realms.extend(query_realms(query, all_realms))
        realm_args = dict(
            zip([r for r in checked_realms], ['on' for r in checked_realms]))
        # Switch between single tag and tag query expression mode.
        if tag_id and not re.match(r"""(['"]?)(\S+)\1$""", tag_id, re.UNICODE):
            # Convert complex, invalid tag ID's --> query expression.
            req.redirect(req.href.tags(realm_args, q=tag_id))
        elif query:
            single_page = re.match(r"""(['"]?)(\S+)\1$""", query, re.UNICODE)
            if single_page:
                # Convert simple query --> single tag.
                req.redirect(req.href.tags(single_page.group(2), realm_args))

        data = dict(page_title=_("Tags"), checked_realms=checked_realms)
        # Populate the TagsQuery form field.
        data['tag_query'] = tag_id and tag_id or query
        data['tag_realms'] = list(
            dict(name=realm, checked=realm in checked_realms)
            for realm in all_realms)
        if tag_id:
            data['tag_page'] = WikiPage(self.env,
                                        tag_system.wiki_page_prefix + tag_id)
        if query or tag_id:
            macro = 'ListTagged'
            # TRANSLATOR: The meta-nav link label.
            add_ctxtnav(req, _("Back to Cloud"), req.href.tags())
            args = "%s,format=%s,cols=%s" % \
                   (tag_id and tag_id or query, self.default_format,
                    self.default_cols)
            data['mincount'] = None
        else:
            macro = 'TagCloud'
            mincount = as_int(req.args.get('mincount', None),
                              self.cloud_mincount)
            args = mincount and "mincount=%s" % mincount or None
            data['mincount'] = mincount
        formatter = Formatter(self.env, web_context(req, Resource('tag')))
        self.env.log.debug("%s macro arguments: %s", macro, args and args
                           or '(none)')
        macros = TagWikiMacros(self.env)
        try:
            # Query string without realm throws 'NotImplementedError'.
            data['tag_body'] = checked_realms and \
                               macros.expand_macro(formatter, macro, args,
                                                   realms=checked_realms) \
                               or ''
        except InvalidQuery, e:
            data['tag_query_error'] = to_unicode(e)
            data['tag_body'] = macros.expand_macro(formatter, 'TagCloud', '')
示例#40
0
文件: web_ui.py 项目: skshel123/trac
    def _render_view(self, req, page):
        version = page.resource.version

        # Add registered converters
        if page.exists:
            for conversion in Mimeview(self.env) \
                              .get_supported_conversions('text/x-trac-wiki'):
                conversion_href = req.href.wiki(page.name, version=version,
                                                format=conversion.key)
                add_link(req, 'alternate', conversion_href, conversion.name,
                         conversion.in_mimetype)

        data = self._page_data(req, page)
        if page.name == self.START_PAGE:
            data['title'] = ''

        ws = WikiSystem(self.env)
        context = web_context(req, page.resource)
        higher, related = [], []
        if not page.exists:
            if 'WIKI_CREATE' not in req.perm(page.resource):
                raise ResourceNotFound(_("Page %(name)s not found",
                                         name=page.name))
            formatter = OneLinerFormatter(self.env, context)
            if '/' in page.name:
                parts = page.name.split('/')
                for i in xrange(len(parts) - 2, -1, -1):
                    name = '/'.join(parts[:i] + [parts[-1]])
                    if not ws.has_page(name):
                        higher.append(ws._format_link(formatter, 'wiki',
                                                      '/' + name, name, False))
            else:
                name = page.name
            name = name.lower()
            related = [each for each in ws.pages
                       if name in each.lower()
                          and 'WIKI_VIEW' in req.perm(self.realm, each)]
            related.sort()
            related = [ws._format_link(formatter, 'wiki', '/' + each, each,
                                       False)
                       for each in related]

        latest_page = WikiPage(self.env, page.name)

        prev_version = next_version = None
        if version:
            version = as_int(version, None)
            if version is not None:
                for hist in latest_page.get_history():
                    v = hist[0]
                    if v != version:
                        if v < version:
                            if not prev_version:
                                prev_version = v
                                break
                        else:
                            next_version = v

        prefix = self.PAGE_TEMPLATES_PREFIX
        templates = [template[len(prefix):]
                     for template in ws.get_pages(prefix)
                     if 'WIKI_VIEW' in req.perm(self.realm, template)]

        # -- prev/up/next links
        if prev_version:
            add_link(req, 'prev',
                     req.href.wiki(page.name, version=prev_version),
                     _("Version %(num)s", num=prev_version))

        parent = None
        if version:
            add_link(req, 'up', req.href.wiki(page.name, version=None),
                     _("View latest version"))
        elif '/' in page.name:
            parent = page.name[:page.name.rindex('/')]
            add_link(req, 'up', req.href.wiki(parent, version=None),
                     _("View parent page"))

        if next_version:
            add_link(req, 'next',
                     req.href.wiki(page.name, version=next_version),
                     _('Version %(num)s', num=next_version))

        # Add ctxtnav entries
        if version:
            prevnext_nav(req, _("Previous Version"), _("Next Version"),
                         _("View Latest Version"))
        else:
            if parent:
                add_ctxtnav(req, _('Up'), req.href.wiki(parent))
            self._wiki_ctxtnav(req, page)

        # Plugin content validation
        fields = {'text': page.text}
        for manipulator in self.page_manipulators:
            manipulator.prepare_wiki_page(req, page, fields)
        text = fields.get('text', '')

        data.update({
            'context': context,
            'text': text,
            'latest_version': latest_page.version,
            'attachments': AttachmentModule(self.env).attachment_data(context),
            'start_page': self.START_PAGE,
            'default_template': self.DEFAULT_PAGE_TEMPLATE,
            'templates': templates,
            'version': version,
            'higher': higher, 'related': related,
            'resourcepath_template': 'wiki_page_path.html',
            'fullwidth': req.session.get('wiki_fullwidth'),
        })
        add_script(req, 'common/js/wiki.js')
        return 'wiki_view.html', data
示例#41
0
 def from_request(*args, **kwargs):
     """:deprecated: since 1.0, use `web_context` instead. Will be removed
                     in release 1.3.1.
     """
     from trac.web.chrome import web_context
     return web_context(*args, **kwargs)
示例#42
0
文件: resource.py 项目: t2y/trac
 def setUp(self):
     self.env = EnvironmentStub(default_data=True)
     self.req = Mock(perm=MockPerm(), href=Href('/trac.cgi'))
     self.context = web_context(self.req)
def get_view_artifact(request, dbp, obj, resource):
    require_permission(request.req, resource, dbp.env)

    artifact_url = request.req.href.customartifacts('artifact/{0}'.format(
        obj.get_id()))
    spec_name, spec_url, values = _get_artifact_details(obj, request.req)

    # Getting wiki pages that refer the artifact
    related_pages = []
    from trac.wiki.formatter import OutlineFormatter
    from trac.web.chrome import web_context

    class NullOut(object):
        def write(self, *args):
            pass

    for pagename, page_version_id, ref_count in dbp.get_wiki_page_ref_counts(
            obj):
        page = WikiPage(dbp.env, pagename)

        fmt = OutlineFormatter(dbp.env, web_context(request.req))
        fmt.format(page.text, NullOut())
        title = ''
        text = page.text
        if fmt.outline:
            title = fmt.outline[0][2]
            text = re.sub('[=]+[ ]+' + title + '[ ]+[=]+\s?', '', text)

        related_pages.append({
            'href':
            get_resource_url(dbp.env, page.resource, request.req.href),
            'title':
            title if title else pagename,
            'date':
            user_time(request.req, format_datetime, page.time),
            'author':
            page.author,
            'excerpt':
            shorten_result(text)
        })

    # Getting artifacts that this artifact refers to
    referred_artifacts = []
    from AdaptiveArtifacts import get_artifact_id_names_from_text
    for attribute_name, value in obj.get_values():
        for related_artifact_id, related_artifact_text in get_artifact_id_names_from_text(
                unicode(value)):
            if dbp.pool.get_item(related_artifact_id) is None:
                dbp.load_artifact(related_artifact_id)
            referred_artifacts.append(
                (dbp.pool.get_item(related_artifact_id),
                 "%s (%s)" % (related_artifact_text, attribute_name)))

    # Getting artifacts whose attribute values refer this artifact
    referring_artifacts = []
    for related_artifact_id, related_artifact_version_id, ref_count in dbp.get_related_artifact_ref_counts(
            obj):
        if dbp.pool.get_item(related_artifact_id) is None:
            dbp.load_artifact(related_artifact_id)
        artifact = dbp.pool.get_item(related_artifact_id)

        url = request.req.href.customartifacts('artifact/%d' %
                                               (artifact.get_id(), ),
                                               action='view')
        rel_spec_name = artifact.__class__.get_name(
        ) if not artifact.__class__ is Instance else None
        rel_spec_url = request.req.href.customartifacts(
            'spec', artifact.__class__.get_id(), action='view'),
        id_version, time, author, ipnr, comment, readonly = dbp.get_latest_version_details(
            artifact.get_id())
        referring_artifacts.append({
            'href':
            url,
            'spec_name':
            rel_spec_name,
            'spec_url':
            rel_spec_url,
            'author':
            author,
            'date':
            user_time(request.req, format_datetime, time),
            'artifact':
            artifact
        })

    # Build yuml url
    class YUMLDiagram(object):
        def __init__(self):
            self.classes = []
            self.base_url = "http://yuml.me/diagram/plain/class/"
            self._diagram = ""
            self.is_incomplete = False

        def add_class(self, header, body, associations):
            self.classes.append({
                'header': header,
                'body': body,
                'associations': associations
            })

        def serialize(self):
            for yuml_class in self.classes:
                yuml_fragment = "[" + yuml_class['header']
                if yuml_class['body']:
                    yuml_fragment += "|" + ";".join(yuml_class['body'])
                yuml_fragment += "],"
                self._diagram += yuml_fragment

                if yuml_class['associations']:
                    for association_target, association_label, in yuml_class[
                            'associations']:
                        yuml_fragment = "[%s]-%s>[%s]," % (
                            yuml_class['header'], association_label,
                            association_target)
                        self._diagram += yuml_fragment

        def get_dsl_text(self):
            return self._diagram.encode('utf8').replace(" ", "&nbsp;")

        def get_url(self):
            #Could be used for GET requests, as long as it doesn't exceed the maximum URL size
            #return self.base_url + quote(self.get_dsl_text(), "[],;:->=")
            from urllib2 import Request, urlopen
            from urllib import urlencode
            try:
                image_filename = urlopen(
                    Request(yuml.base_url,
                            data=urlencode({'dsl_text':
                                            yuml.get_dsl_text()}))).read()
            except HTTPError:
                return ""
            return self.base_url + image_filename

    yuml = YUMLDiagram()

    def artifact_to_yuml_class(rel_artifact, include_values=True):
        def sanitize(value):
            if type(value) == list:
                value = ",".join(value)
            for i, j in {
                    "[": "(",
                    "]": ")",
                    ",": ".",
                    ";": ".",
                    "->": "-",
                    "|": "\\",
            }.iteritems():
                value = value.replace(i, j)
            return value if len(value) < 128 else "..."

        rel_artifact_title = unicode(rel_artifact)
        rel_spec_name = (u" : " + rel_artifact.__class__.get_name()
                         ) if not rel_artifact.__class__ is Instance else u""
        header = rel_artifact_title + rel_spec_name
        body = []
        if include_values:
            for attribute_name, value in rel_artifact.get_values():
                body.append("%s = %s" %
                            (sanitize(attribute_name), sanitize(value)))
        return {'header': sanitize(header), 'body': body, 'associations': []}

    yuml_class = artifact_to_yuml_class(obj)
    yuml_class['body'].append(
        '{bg:orange}')  # color the main artifact differently
    yuml_class['associations'] = [
        (artifact_to_yuml_class(rel_artifact,
                                False)['header'], rel_artifact_text)
        for rel_artifact, rel_artifact_text in referred_artifacts
    ]
    yuml.add_class(**yuml_class)

    for rel_artifact in referring_artifacts:
        rel_yuml_class = artifact_to_yuml_class(rel_artifact['artifact'])
        rel_yuml_class['associations'] = [
            (artifact_to_yuml_class(obj, False)['header'], "")
        ]
        yuml.add_class(**rel_yuml_class)

    yuml.serialize()

    # track access
    dbp.track_it("artifact", obj.get_id(), "view", request.req.authname,
                 str(datetime.now()))

    data = {
        'context': Context.from_request(request.req, resource),
        'spec_name': spec_name,
        'spec_url': spec_url,
        'artifact': obj,
        'artifact_url': artifact_url,
        'artifacts_values': values,
        'related_pages': related_pages,
        'related_artifacts': referring_artifacts,
        'show_diagram': dbp.env.config.getbool('asa',
                                               'show_diagram',
                                               default=True),
        'yuml_url': yuml.get_url(),
    }
    return 'view_artifact_%s.html' % (request.get_format(), ), data, None
示例#44
0
                            and author not in exclude:
                        events.append(self._event_data(provider, event))
            except Exception, e:  # cope with a failure of that provider
                self._provider_failure(e, req, provider, filters,
                                       [f[0] for f in available_filters])

        # prepare sorted global list
        events = sorted(events, key=lambda e: e['date'], reverse=True)
        if maxrows:
            events = events[:maxrows]

        data['events'] = events

        if format == 'rss':
            data['email_map'] = Chrome(self.env).get_email_map()
            rss_context = web_context(req, absurls=True)
            rss_context.set_hints(wiki_flavor='html', shorten_lines=False)
            data['context'] = rss_context
            return 'timeline.rss', data, 'application/rss+xml'
        else:
            req.session.set('timeline.daysback', daysback,
                            self.default_daysback)
            req.session.set('timeline.authors', authors, '')
            # store lastvisit
            if events and not revisit:
                lastviewed = to_utimestamp(events[0]['date'])
                req.session['timeline.lastvisit'] = max(lastvisit, lastviewed)
                req.session['timeline.nextlastvisit'] = lastvisit
            html_context = web_context(req)
            html_context.set_hints(wiki_flavor='oneliner',
                                   shorten_lines=self.abbreviated_messages)
示例#45
0
    def post_process_request(self, req, template, data, content_type):
        if not req or not template or not isinstance(data, dict):
            return template, data, content_type

        model = None
        resource = None
        attachments = None

        if template in ('wiki_view.html', 'wiki_edit.html'):
            model = data.get('page')
        elif template == 'ticket.html':
            model = data.get('ticket')
        elif template in ('milestone_view.html', 'milestone_edit.html'):
            model = data.get('milestone')
        elif template == 'attachment.html':
            attachments = data.get('attachments')
            if attachments:
                resource = attachments['parent']

        if not resource and model and model.exists:
            resource = model.resource
        if not resource:
            return template, data, content_type
        if not attachments:
            attachments = data.get('attachments')
        if not attachments and model and resource:
            context = web_context(req, resource)
            attachments = AttachmentModule(self.env).attachment_data(context)
            # mark appending list of attachments in filter_stream
            attachments['tracdragdrop'] = True
            data['attachments'] = attachments

        if template in ('wiki_edit.html', 'milestone_edit.html'):
            self._add_overlayview(req)
        add_stylesheet(req, 'tracdragdrop/tracdragdrop.css')
        locale = req.locale and str(req.locale)
        if locale in self.messages_files:
            add_script(req, 'tracdragdrop/messages/%s.js' % locale)
        add_script(req, 'common/js/folding.js')
        add_script(req, 'tracdragdrop/tracdragdrop.js')
        script_data = {
            '_tracdragdrop': {
                'base_url':
                req.href().rstrip('/') + '/',
                'new_url':
                req.href('tracdragdrop', 'new', resource.realm, resource.id),
                'raw_parent_url':
                get_resource_url(self.env,
                                 resource.child('attachment'),
                                 req.href,
                                 format='raw'),
                'parent_name':
                get_resource_name(self.env, resource),
                'no_image_msg':
                dgettext('messages',
                         'No image "%(id)s" attached to %(parent)s'),
                'can_create':
                attachments.get('can_create') or False,
                'max_size':
                AttachmentModule(self.env).max_size,
            },
            'form_token': req.form_token,
        }
        add_script_data(req, script_data)

        return template, data, content_type
示例#46
0
    def _render_view(self, req, attachment):
        req.perm(attachment.resource).require('ATTACHMENT_VIEW')
        can_delete = 'ATTACHMENT_DELETE' in req.perm(attachment.resource)
        req.check_modified(attachment.date, str(can_delete))

        data = {
            'mode': 'view',
            'title': get_resource_name(self.env, attachment.resource),
            'attachment': attachment
        }

        with attachment.open() as fd:
            mimeview = Mimeview(self.env)

            # MIME type detection
            str_data = fd.read(1000)
            fd.seek(0)

            mime_type = mimeview.get_mimetype(attachment.filename, str_data)

            # Eventually send the file directly
            format = req.args.get('format')
            if format == 'zip':
                self._download_as_zip(req, attachment.resource.parent,
                                      [attachment])
            elif format in ('raw', 'txt'):
                if not self.render_unsafe_content:
                    # Force browser to download files instead of rendering
                    # them, since they might contain malicious code enabling
                    # XSS attacks
                    req.send_header('Content-Disposition', 'attachment')
                if format == 'txt':
                    mime_type = 'text/plain'
                elif not mime_type:
                    mime_type = 'application/octet-stream'
                if 'charset=' not in mime_type:
                    charset = mimeview.get_charset(str_data, mime_type)
                    mime_type = mime_type + '; charset=' + charset
                req.send_file(attachment.path, mime_type)

            # add ''Plain Text'' alternate link if needed
            if self.render_unsafe_content and \
                    mime_type and not mime_type.startswith('text/plain'):
                plaintext_href = get_resource_url(self.env,
                                                  attachment.resource,
                                                  req.href,
                                                  format='txt')
                add_link(req, 'alternate', plaintext_href, _("Plain Text"),
                         mime_type)

            # add ''Original Format'' alternate link (always)
            raw_href = get_resource_url(self.env,
                                        attachment.resource,
                                        req.href,
                                        format='raw')
            add_link(req, 'alternate', raw_href, _("Original Format"),
                     mime_type)

            self.log.debug("Rendering preview of file %s with mime-type %s",
                           attachment.filename, mime_type)

            data['preview'] = mimeview.preview_data(
                web_context(req, attachment.resource),
                fd,
                os.fstat(fd.fileno()).st_size,
                mime_type,
                attachment.filename,
                raw_href,
                annotations=['lineno'])
            return data
示例#47
0
文件: log.py 项目: thimalk/bloodhound
    def process_request(self, req):
        req.perm.require('LOG_VIEW')

        mode = req.args.get('mode', 'stop_on_copy')
        path = req.args.get('path', '/')
        rev = req.args.get('rev')
        stop_rev = req.args.get('stop_rev')
        revs = req.args.get('revs')
        format = req.args.get('format')
        verbose = req.args.get('verbose')
        limit = int(req.args.get('limit') or self.default_log_limit)

        rm = RepositoryManager(self.env)
        reponame, repos, path = rm.get_repository_by_path(path)

        if not repos:
            raise ResourceNotFound(_("Repository '%(repo)s' not found",
                                   repo=reponame))

        if reponame != repos.reponame:  # Redirect alias
            qs = req.query_string
            req.redirect(req.href.log(repos.reponame or None, path)
                         + ('?' + qs if qs else ''))

        normpath = repos.normalize_path(path)
        # if `revs` parameter is given, then we're restricted to the
        # corresponding revision ranges.
        # If not, then we're considering all revisions since `rev`,
        # on that path, in which case `revranges` will be None.
        revranges = None
        if revs:
            try:
                revranges = Ranges(revs)
                rev = revranges.b
            except ValueError:
                pass
        rev = unicode(repos.normalize_rev(rev))
        display_rev = repos.display_rev

        # The `history()` method depends on the mode:
        #  * for ''stop on copy'' and ''follow copies'', it's `Node.history()`
        #    unless explicit ranges have been specified
        #  * for ''show only add, delete'' we're using
        #   `Repository.get_path_history()`
        cset_resource = repos.resource.child('changeset')
        show_graph = False
        if mode == 'path_history':
            def history():
                for h in repos.get_path_history(path, rev):
                    if 'CHANGESET_VIEW' in req.perm(cset_resource(id=h[1])):
                        yield h
        elif revranges:
            def history():
                prevpath = path
                expected_next_item = None
                ranges = list(revranges.pairs)
                ranges.reverse()
                for (a, b) in ranges:
                    a = repos.normalize_rev(a)
                    b = repos.normalize_rev(b)
                    while not repos.rev_older_than(b, a):
                        node = get_existing_node(req, repos, prevpath, b)
                        node_history = list(node.get_history(2))
                        p, rev, chg = node_history[0]
                        if repos.rev_older_than(rev, a):
                            break # simply skip, no separator
                        if 'CHANGESET_VIEW' in req.perm(cset_resource(id=rev)):
                            if expected_next_item:
                                # check whether we're continuing previous range
                                np, nrev, nchg = expected_next_item
                                if rev != nrev: # no, we need a separator
                                    yield (np, nrev, None)
                            yield node_history[0]
                        prevpath = node_history[-1][0] # follow copy
                        b = repos.previous_rev(rev)
                        if len(node_history) > 1:
                            expected_next_item = node_history[-1]
                        else:
                            expected_next_item = None
                if expected_next_item:
                    yield (expected_next_item[0], expected_next_item[1], None)
        else:
            show_graph = path == '/' and not verbose \
                         and not repos.has_linear_changesets
            def history():
                node = get_existing_node(req, repos, path, rev)
                for h in node.get_history():
                    if 'CHANGESET_VIEW' in req.perm(cset_resource(id=h[1])):
                        yield h

        # -- retrieve history, asking for limit+1 results
        info = []
        depth = 1
        previous_path = normpath
        count = 0
        for old_path, old_rev, old_chg in history():
            if stop_rev and repos.rev_older_than(old_rev, stop_rev):
                break
            old_path = repos.normalize_path(old_path)

            item = {
                'path': old_path, 'rev': old_rev, 'existing_rev': old_rev,
                'change': old_chg, 'depth': depth,
            }

            if old_chg == Changeset.DELETE:
                item['existing_rev'] = repos.previous_rev(old_rev, old_path)
            if not (mode == 'path_history' and old_chg == Changeset.EDIT):
                info.append(item)
            if old_path and old_path != previous_path and \
                    not (mode == 'path_history' and old_path == normpath):
                depth += 1
                item['depth'] = depth
                item['copyfrom_path'] = old_path
                if mode == 'stop_on_copy':
                    break
                elif mode == 'path_history':
                    depth -= 1
            if old_chg is None: # separator entry
                stop_limit = limit
            else:
                count += 1
                stop_limit = limit + 1
            if count >= stop_limit:
                break
            previous_path = old_path
        if info == []:
            node = get_existing_node(req, repos, path, rev)
            if repos.rev_older_than(stop_rev, node.created_rev):
                # FIXME: we should send a 404 error here
                raise TracError(_("The file or directory '%(path)s' doesn't "
                    "exist at revision %(rev)s or at any previous revision.",
                    path=path, rev=display_rev(rev)), _('Nonexistent path'))

        # Generate graph data
        graph = {}
        if show_graph:
            threads, vertices, columns = \
                make_log_graph(repos, (item['rev'] for item in info))
            graph.update(threads=threads, vertices=vertices, columns=columns,
                         colors=self.graph_colors,
                         line_width=0.04, dot_radius=0.1)
            add_script(req, 'common/js/excanvas.js', ie_if='IE')
            add_script(req, 'common/js/log_graph.js')
            add_script_data(req, graph=graph)

        def make_log_href(path, **args):
            link_rev = rev
            if rev == str(repos.youngest_rev):
                link_rev = None
            params = {'rev': link_rev, 'mode': mode, 'limit': limit}
            params.update(args)
            if verbose:
                params['verbose'] = verbose
            return req.href.log(repos.reponame or None, path, **params)

        if format in ('rss', 'changelog'):
            info = [i for i in info if i['change']] # drop separators
            if info and count > limit:
                del info[-1]
        elif info and count >= limit:
            # stop_limit reached, there _might_ be some more
            next_rev = info[-1]['rev']
            next_path = info[-1]['path']
            next_revranges = None
            if revranges:
                next_revranges = str(revranges.truncate(next_rev))
            if next_revranges or not revranges:
                older_revisions_href = make_log_href(next_path, rev=next_rev,
                                                     revs=next_revranges)
                add_link(req, 'next', older_revisions_href,
                    _('Revision Log (restarting at %(path)s, rev. %(rev)s)',
                    path=next_path, rev=display_rev(next_rev)))
            # only show fully 'limit' results, use `change == None` as a marker
            info[-1]['change'] = None

        revisions = [i['rev'] for i in info]
        changes = get_changes(repos, revisions, self.log)
        extra_changes = {}

        if format == 'changelog':
            for rev in revisions:
                changeset = changes[rev]
                cs = {}
                cs['message'] = wrap(changeset.message, 70,
                                     initial_indent='\t',
                                     subsequent_indent='\t')
                files = []
                actions = []
                for cpath, kind, chg, bpath, brev in changeset.get_changes():
                    files.append(bpath if chg == Changeset.DELETE else cpath)
                    actions.append(chg)
                cs['files'] = files
                cs['actions'] = actions
                extra_changes[rev] = cs

        data = {
            'context': web_context(req, 'source', path, parent=repos.resource),
            'reponame': repos.reponame or None, 'repos': repos,
            'path': path, 'rev': rev, 'stop_rev': stop_rev,
            'display_rev': display_rev, 'revranges': revranges,
            'mode': mode, 'verbose': verbose, 'limit' : limit,
            'items': info, 'changes': changes, 'extra_changes': extra_changes,
            'graph': graph,
            'wiki_format_messages':
            self.config['changeset'].getbool('wiki_format_messages')
        }

        if format == 'changelog':
            return 'revisionlog.txt', data, 'text/plain'
        elif format == 'rss':
            data['email_map'] = Chrome(self.env).get_email_map()
            data['context'] = web_context(req, 'source',
                                          path, parent=repos.resource,
                                          absurls=True)
            return 'revisionlog.rss', data, 'application/rss+xml'

        item_ranges = []
        range = []
        for item in info:
            if item['change'] is None: # separator
                if range: # start new range
                    range.append(item)
                    item_ranges.append(range)
                    range = []
            else:
                range.append(item)
        if range:
            item_ranges.append(range)
        data['item_ranges'] = item_ranges

        add_stylesheet(req, 'common/css/diff.css')
        add_stylesheet(req, 'common/css/browser.css')

        path_links = get_path_links(req.href, repos.reponame, path, rev)
        if path_links:
            data['path_links'] = path_links
        if path != '/':
            add_link(req, 'up', path_links[-2]['href'], _('Parent directory'))

        rss_href = make_log_href(path, format='rss', revs=revs,
                                 stop_rev=stop_rev)
        add_link(req, 'alternate', auth_link(req, rss_href), _('RSS Feed'),
                 'application/rss+xml', 'rss')
        changelog_href = make_log_href(path, format='changelog', revs=revs,
                                       stop_rev=stop_rev)
        add_link(req, 'alternate', changelog_href, _('ChangeLog'), 'text/plain')

        add_ctxtnav(req, _('View Latest Revision'),
                    href=req.href.browser(repos.reponame or None, path))
        if 'next' in req.chrome['links']:
            next = req.chrome['links']['next'][0]
            add_ctxtnav(req, tag.span(tag.a(_('Older Revisions'),
                                            href=next['href']),
                                      Markup(' &rarr;')))

        return 'revisionlog.html', data, None
示例#48
0
 def from_request(*args, **kwargs):
     """:deprecated: since 1.0, use `web_context` instead."""
     from trac.web.chrome import web_context
     return web_context(*args, **kwargs)
示例#49
0
    def _render_view(self, req, milestone):
        milestone_groups = []
        available_groups = []
        component_group_available = False
        ticket_fields = TicketSystem(self.env).get_ticket_fields()

        # collect fields that can be used for grouping
        for field in ticket_fields:
            if field['type'] == 'select' and field['name'] != 'milestone' \
                    or field['name'] in ('owner', 'reporter'):
                available_groups.append({'name': field['name'],
                                         'label': field['label']})
                if field['name'] == 'component':
                    component_group_available = True

        # determine the field currently used for grouping
        by = None
        if component_group_available:
            by = 'component'
        elif available_groups:
            by = available_groups[0]['name']
        by = req.args.get('by', by)

        tickets = get_tickets_for_milestone(self.env, milestone=milestone.name,
                                            field=by)
        tickets = apply_ticket_permissions(self.env, req, tickets)
        stat = get_ticket_stats(self.stats_provider, tickets)

        context = web_context(req, milestone.resource)
        data = {
            'context': context,
            'milestone': milestone,
            'attachments': AttachmentModule(self.env).attachment_data(context),
            'available_groups': available_groups,
            'grouped_by': by,
            'groups': milestone_groups
            }
        data.update(milestone_stats_data(self.env, req, stat, milestone.name))

        if by:
            def per_group_stats_data(gstat, group_name):
                return milestone_stats_data(self.env, req, gstat,
                                            milestone.name, by, group_name)
            milestone_groups.extend(
                grouped_stats_data(self.env, self.stats_provider, tickets, by,
                                   per_group_stats_data))

        add_stylesheet(req, 'common/css/roadmap.css')
        add_script(req, 'common/js/folding.js')

        def add_milestone_link(rel, milestone):
            href = req.href.milestone(milestone.name, by=req.args.get('by'))
            add_link(req, rel, href, _('Milestone "%(name)s"',
                                       name=milestone.name))

        milestones = [m for m in Milestone.select(self.env)
                      if 'MILESTONE_VIEW' in req.perm(m.resource)]
        idx = [i for i, m in enumerate(milestones) if m.name == milestone.name]
        if idx:
            idx = idx[0]
            if idx > 0:
                add_milestone_link('first', milestones[0])
                add_milestone_link('prev', milestones[idx - 1])
            if idx < len(milestones) - 1:
                add_milestone_link('next', milestones[idx + 1])
                add_milestone_link('last', milestones[-1])
        prevnext_nav(req, _('Previous Milestone'), _('Next Milestone'),
                     _('Back to Roadmap'))

        return 'milestone_view.html', data, None
示例#50
0
文件: web_ui.py 项目: jier38/TracSite
    def process_request(self, req):
        req.perm.require('TAGS_VIEW')

        match = re.match(r'/tags/?(.*)', req.path_info)
        tag_id = match.group(1) and match.group(1) or None
        query = req.args.get('q', '')

        # Consider only providers, that are permitted for display.
        tag_system = TagSystem(self.env)
        all_realms = tag_system.get_taggable_realms(req.perm)
        if not (tag_id or query) or [r for r in all_realms
                                     if r in req.args] == []:
            for realm in all_realms:
                if realm not in self.exclude_realms:
                    req.args[realm] = 'on'
        checked_realms = [r for r in all_realms if r in req.args]
        if query:
            # Add permitted realms from query expression.
            checked_realms.extend(query_realms(query, all_realms))
        realm_args = dict(zip([r for r in checked_realms],
                              ['on' for r in checked_realms]))
        # Switch between single tag and tag query expression mode.
        if tag_id and not re.match(r"""(['"]?)(\S+)\1$""", tag_id, re.UNICODE):
            # Convert complex, invalid tag ID's --> query expression.
            req.redirect(req.href.tags(realm_args, q=tag_id))
        elif query:
            single_page = re.match(r"""(['"]?)(\S+)\1$""", query, re.UNICODE)
            if single_page:
                # Convert simple query --> single tag.
                req.redirect(req.href.tags(single_page.group(2), realm_args))

        data = dict(page_title=_("Tags"), checked_realms=checked_realms)
        # Populate the TagsQuery form field.
        data['tag_query'] = tag_id and tag_id or query
        data['tag_realms'] = list(dict(name=realm,
                                       checked=realm in checked_realms)
                                  for realm in all_realms)
        if tag_id:
            data['tag_page'] = WikiPage(self.env,
                                        tag_system.wiki_page_prefix + tag_id)
        if query or tag_id:
            macro = 'ListTagged'
            # TRANSLATOR: The meta-nav link label.
            add_ctxtnav(req, _("Back to Cloud"), req.href.tags())
            args = "%s,format=%s,cols=%s" % \
                   (tag_id and tag_id or query, self.default_format,
                    self.default_cols)
            data['mincount'] = 0
        else:
            macro = 'TagCloud'
            mincount = as_int(req.args.get('mincount', 0),
                              self.cloud_mincount)
            args = mincount and "mincount=%s" % mincount or None
            data['mincount'] = mincount

        # When using the given req the page isn't rendered properly. The call
        # to expand_macro() leads to Chrome().render_template(req, ...).
        # The function render_template() breaks something in the request handling.
        # That used to work with Genshi.
        #
        # With this mocked req everything is just fine.
        mock_req = MockRequest(self.env, path_info=req.path_info,
                           authname=req.authname, script_name=req.href())
        formatter = Formatter(self.env, web_context(mock_req, Resource('tag')))
        self.env.log.debug("%s macro arguments: %s", macro,
                           args and args or '(none)')
        macros = TagWikiMacros(self.env)
        try:
            # Query string without realm throws 'NotImplementedError'.
            data['tag_body'] = checked_realms and \
                               macros.expand_macro(formatter, macro, args,
                                                   realms=checked_realms) \
                               or ''
            data['tag_body'] = Markup(to_unicode(data['tag_body']))
        except InvalidQuery as e:
            data['tag_query_error'] = to_unicode(e)
            data['tag_body'] = macros.expand_macro(formatter, 'TagCloud', '')

        data['realm_args'] = realm_args
        add_stylesheet(req, 'tags/css/tractags.css')
        return 'tag_view.html', data, {'domain': 'tractags'}
示例#51
0
    def _render_view(self, req, page):
        version = page.resource.version

        # Add registered converters
        if page.exists:
            for conversion in Mimeview(self.env).get_supported_conversions(
                                                 'text/x-trac-wiki'):
                conversion_href = req.href.wiki(page.name, version=version,
                                                format=conversion[0])
                # or...
                conversion_href = get_resource_url(self.env, page.resource,
                                                req.href, format=conversion[0])
                add_link(req, 'alternate', conversion_href, conversion[1],
                         conversion[3])

        data = self._page_data(req, page)
        if page.name == 'WikiStart':
            data['title'] = ''

        ws = WikiSystem(self.env)
        context = web_context(req, page.resource)
        higher, related = [], []
        if not page.exists:
            if 'WIKI_CREATE' not in req.perm(page.resource):
                raise ResourceNotFound(_('Page %(name)s not found',
                                         name=page.name))
            formatter = OneLinerFormatter(self.env, context)
            if '/' in page.name:
                parts = page.name.split('/')
                for i in range(len(parts) - 2, -1, -1):
                    name = '/'.join(parts[:i] + [parts[-1]])
                    if not ws.has_page(name):
                        higher.append(ws._format_link(formatter, 'wiki',
                                                    '/' + name, name, False))
            else:
                name = page.name
            name = name.lower()
            related = [each for each in ws.pages
                       if name in each.lower()
                          and 'WIKI_VIEW' in req.perm('wiki', each)]
            related.sort()
            related = [ws._format_link(formatter, 'wiki', '/' + each, each,
                                       False)
                       for each in related]

        latest_page = WikiPage(self.env, page.name, version=None)
        req.perm(latest_page.resource).require('WIKI_VIEW')

        prev_version = next_version = None
        if version:
            try:
                version = int(version)
                for hist in latest_page.get_history():
                    v = hist[0]
                    if v != version:
                        if v < version:
                            if not prev_version:
                                prev_version = v
                                break
                        else:
                            next_version = v
            except ValueError:
                version = None

        prefix = self.PAGE_TEMPLATES_PREFIX
        templates = [template[len(prefix):]
                     for template in ws.get_pages(prefix)
                     if 'WIKI_VIEW' in req.perm('wiki', template)]

        # -- prev/up/next links
        if prev_version:
            add_link(req, 'prev',
                     req.href.wiki(page.name, version=prev_version),
                     _('Version %(num)s', num=prev_version))

        parent = None
        if version:
            add_link(req, 'up', req.href.wiki(page.name, version=None),
                     _('View latest version'))
        elif '/' in page.name:
            parent = page.name[:page.name.rindex('/')]
            add_link(req, 'up', req.href.wiki(parent, version=None),
                     _("View parent page"))

        if next_version:
            add_link(req, 'next',
                     req.href.wiki(page.name, version=next_version),
                     _('Version %(num)s', num=next_version))

        # Add ctxtnav entries
        if version:
            prevnext_nav(req, _('Previous Version'), _('Next Version'),
                         _('View Latest Version'))
        else:
            if parent:
                add_ctxtnav(req, _('Up'), req.href.wiki(parent))
            self._wiki_ctxtnav(req, page)

        # Plugin content validation
        fields = {'text': page.text}
        for manipulator in self.page_manipulators:
            manipulator.prepare_wiki_page(req, page, fields)
        text = fields.get('text', '')

        data.update({
            'context': context,
            'text': text,
            'latest_version': latest_page.version,
            'attachments': AttachmentModule(self.env).attachment_data(context),
            'default_template': self.DEFAULT_PAGE_TEMPLATE,
            'templates': templates,
            'version': version,
            'higher': higher, 'related': related,
            'resourcepath_template': 'wiki_page_path.html',
        })
        add_script(req, 'common/js/folding.js')
        return 'wiki_view.html', data, None
示例#52
0
文件: web_ui.py 项目: timgraham/trac
    def process_request(self, req):
        req.perm('timeline').require('TIMELINE_VIEW')

        format = req.args.get('format')
        maxrows = int(req.args.get('max', 50 if format == 'rss' else 0))
        lastvisit = int(req.session.get('timeline.lastvisit', '0'))

        # indication of new events is unchanged when form is updated by user
        revisit = any(a in req.args
                      for a in ['update', 'from', 'daysback', 'author'])
        if revisit:
            lastvisit = int(
                req.session.get('timeline.nextlastvisit', lastvisit))

        # Parse the from date and adjust the timestamp to the last second of
        # the day
        fromdate = today = datetime.now(req.tz)
        yesterday = to_datetime(
            today.replace(tzinfo=None) - timedelta(days=1), req.tz)
        precisedate = precision = None
        if 'from' in req.args:
            # Acquire from date only from non-blank input
            reqfromdate = req.args['from'].strip()
            if reqfromdate:
                try:
                    precisedate = user_time(req, parse_date, reqfromdate)
                except TracError as e:
                    add_warning(req, e)
                else:
                    fromdate = precisedate.astimezone(req.tz)
            precision = req.args.get('precision', '')
            if precision.startswith('second'):
                precision = timedelta(seconds=1)
            elif precision.startswith('minute'):
                precision = timedelta(minutes=1)
            elif precision.startswith('hour'):
                precision = timedelta(hours=1)
            else:
                precision = None
        fromdate = to_datetime(
            datetime(fromdate.year, fromdate.month, fromdate.day, 23, 59, 59,
                     999999), req.tz)

        daysback = as_int(req.args.get('daysback'),
                          90 if format == 'rss' else None)
        if daysback is None:
            daysback = as_int(req.session.get('timeline.daysback'), None)
        if daysback is None:
            daysback = self.default_daysback
        daysback = max(0, daysback)
        if self.max_daysback >= 0:
            daysback = min(self.max_daysback, daysback)

        authors = req.args.get('authors')
        if authors is None and format != 'rss':
            authors = req.session.get('timeline.authors')
        authors = (authors or '').strip()

        data = {
            'fromdate': fromdate,
            'daysback': daysback,
            'authors': authors,
            'today': user_time(req, format_date, today),
            'yesterday': user_time(req, format_date, yesterday),
            'precisedate': precisedate,
            'precision': precision,
            'events': [],
            'filters': [],
            'abbreviated_messages': self.abbreviated_messages,
            'lastvisit': lastvisit
        }

        available_filters = []
        for event_provider in self.event_providers:
            available_filters += event_provider.get_timeline_filters(req) or []

        # check the request or session for enabled filters, or use default
        filters = [f[0] for f in available_filters if f[0] in req.args]
        if not filters and format != 'rss':
            filters = [
                f[0] for f in available_filters
                if req.session.get('timeline.filter.' + f[0]) == '1'
            ]
        if not filters:
            filters = [f[0] for f in available_filters if len(f) == 2 or f[2]]

        # save the results of submitting the timeline form to the session
        if 'update' in req.args:
            for filter_ in available_filters:
                key = 'timeline.filter.%s' % filter_[0]
                if filter_[0] in req.args:
                    req.session[key] = '1'
                elif key in req.session:
                    del req.session[key]

        stop = fromdate
        start = to_datetime(
            stop.replace(tzinfo=None) - timedelta(days=daysback + 1), req.tz)

        # create author include and exclude sets
        include = set()
        exclude = set()
        for match in self._authors_pattern.finditer(authors):
            name = (match.group(2) or match.group(3) or match.group(4)).lower()
            if match.group(1):
                exclude.add(name)
            else:
                include.add(name)

        # gather all events for the given period of time
        events = []
        for provider in self.event_providers:
            try:
                for event in provider.get_timeline_events(
                        req, start, stop, filters) or []:
                    author = (event[2] or '').lower()
                    if (not include or author in include) \
                            and author not in exclude:
                        events.append(self._event_data(provider, event))
            except Exception as e:  # cope with a failure of that provider
                self._provider_failure(e, req, provider, filters,
                                       [f[0] for f in available_filters])

        # prepare sorted global list
        events = sorted(events, key=lambda e: e['date'], reverse=True)
        if maxrows:
            events = events[:maxrows]

        data['events'] = events

        if format == 'rss':
            rss_context = web_context(req, absurls=True)
            rss_context.set_hints(wiki_flavor='html', shorten_lines=False)
            data['context'] = rss_context
            return 'timeline.rss', data, 'application/rss+xml'
        else:
            req.session.set('timeline.daysback', daysback,
                            self.default_daysback)
            req.session.set('timeline.authors', authors, '')
            # store lastvisit
            if events and not revisit:
                lastviewed = to_utimestamp(events[0]['date'])
                req.session['timeline.lastvisit'] = max(lastvisit, lastviewed)
                req.session['timeline.nextlastvisit'] = lastvisit
            html_context = web_context(req)
            html_context.set_hints(wiki_flavor='oneliner',
                                   shorten_lines=self.abbreviated_messages)
            data['context'] = html_context

        add_stylesheet(req, 'common/css/timeline.css')
        rss_href = req.href.timeline([(f, 'on') for f in filters],
                                     daysback=90,
                                     max=50,
                                     authors=authors,
                                     format='rss')
        add_link(req, 'alternate', auth_link(req, rss_href), _('RSS Feed'),
                 'application/rss+xml', 'rss')
        Chrome(self.env).add_jquery_ui(req)

        for filter_ in available_filters:
            data['filters'].append({
                'name': filter_[0],
                'label': filter_[1],
                'enabled': filter_[0] in filters
            })

        # Navigation to the previous/next period of 'daysback' days
        previous_start = fromdate.replace(tzinfo=None) - \
                         timedelta(days=daysback + 1)
        previous_start = format_date(to_datetime(previous_start, req.tz),
                                     format='%Y-%m-%d',
                                     tzinfo=req.tz)
        add_link(
            req, 'prev',
            req.href.timeline(from_=previous_start,
                              authors=authors,
                              daysback=daysback), _("Previous Period"))
        if today - fromdate > timedelta(days=0):
            next_start = fromdate.replace(tzinfo=None) + \
                         timedelta(days=daysback + 1)
            next_start = format_date(to_datetime(next_start, req.tz),
                                     format='%Y-%m-%d',
                                     tzinfo=req.tz)
            add_link(
                req, 'next',
                req.href.timeline(from_=next_start,
                                  authors=authors,
                                  daysback=daysback), _("Next Period"))
        prevnext_nav(req, _("Previous Period"), _("Next Period"))

        return 'timeline.html', data, None
示例#53
0
 def find_change(stream):
     ticket = reported_tickets.pop()
     keywords = ticket['keywords'] or ''
     context = web_context(req, ticket)
     tag_ = self._query_link_words(context, 'keywords', keywords, class_, prepend=[tag.span(' ')])
     return itertools.chain(stream[0:5], tag_, stream[6:])
示例#54
0
 def setUp(self):
     self.env = EnvironmentStub(enable=[ReStructuredTextRenderer])
     self.renderer = ReStructuredTextRenderer(self.env)
     self.req = MockRequest(self.env)
     self.context = web_context(self.req)
示例#55
0
    def expand_macro(self, formatter, name, content):
        req = formatter.req

        if 'CHANGESET_VIEW' not in req.perm:
            return Markup('<i>Changelog not available</i>')

        context = web_context(req)
        context.href is req.href
        args, kwargs = parse_args(content)
        args += [None, None]
        path, limit, rev = args[:3]
        limit = kwargs.pop('limit', limit)
        rev = kwargs.pop('rev', rev)
        if ':' in path:
            reponame, path = path.split(':', 2)
        else:
            reponame = ''
        if '@' in path:
            path, rev = path.split('@', 2)
        repo = self.env.get_repository(reponame)
        path = repo.normalize_path(path)
        revstart = 0
        if rev is not None:
            for d in [':', '-']:
                if d in rev:
                    revstart, revstop = rev.split(d, 2)
                    if not revstop or revstop.lower() in ['head', '0']:
                        revstart = int(revstart)
                        rev = repo.get_youngest_rev()
                        limit = rev - revstart + 1
                    else:
                        revstart, revstop = int(revstart), int(revstop)
                        if revstart > revstop:
                            revstart, revstop = revstop, revstart
                        limit = revstop - revstart + 1
                        rev = revstop or None
                    break

        if rev is None:
            rev = repo.get_youngest_rev()
        rev = repo.normalize_rev(rev)
        if limit is None:
            limit = 5
        else:
            limit = int(limit)
        node = repo.get_node(path, rev)
        out = StringIO()
        out.write('</p>')  # close surrounding paragraph
        out.write('\n<div class="changelog">\n<dl class="wiki">')
        for npath, nrev, nlog in node.get_history(limit):
            if nrev < revstart:
                break
            change = repo.get_changeset(nrev)
            datetime = format_datetime(change.date, '%Y-%m-%d %H:%M:%S',
                                       req.tz)
            if not reponame:
                cset = str(nrev)
            else:
                cset = '%s/%s' % (nrev, reponame)
            header = wiki_to_oneliner("[%s] by %s on %s" %
                                      (cset, change.author, datetime),
                                      self.env,
                                      req=req)
            out.write('\n<dt id="changelog-changeset-%s">\n%s\n</dt>' %
                      (cset, header))
            message = _remove_p(
                format_to_html(self.env,
                               context,
                               change.message,
                               escape_newlines=True))
            out.write('\n<dd>\n%s\n</dd>' % message)
        out.write('\n</dl>\n</div>')
        out.write('\n<p>')  # re-open surrounding paragraph
        return out.getvalue()
示例#56
0
文件: log.py 项目: starworldx/trac
 def _format_to_html(self, authname, wiki):
     resource = Resource('wiki', 'WikiStart')
     req = MockRequest(self.env, authname=authname)
     return unicode(
         format_to_html(self.env, web_context(req, resource), wiki))
示例#57
0
 def setUp(self):
     self.env = EnvironmentStub(enable=[TextileRenderer])
     self.env.config.set('wiki', 'safe_schemes', 'https, http, data')
     self.renderer = TextileRenderer(self.env)
     self.req = MockRequest(self.env)
     self.context = web_context(self.req)
示例#58
0
 def setUp(self):
     self.env = EnvironmentStub(default_data=True)
     self.env.enable_component(self.FakeResourceManager)
     self.req = MockRequest(self.env)
     self.context = web_context(self.req)
示例#59
0
    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 = web_context(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 = web_context(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 = web_context(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'] = web_context(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)
示例#60
0
 def setUp(self):
     self.env = EnvironmentStub(default_data=True)
     self.query_module = QueryModule(self.env)
     req = Mock(perm=MockPerm(), args={}, href=Href('/'))
     self.formatter = LinkFormatter(self.env, web_context(req))