⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 browser.py

📁 一款基于web的项目管理、bug跟踪系统。提供了与svn集成的操作界面、问题跟踪
💻 PY
📖 第 1 页 / 共 2 页
字号:
            'path_links': path_links,            'dir': node.isdir and self._render_dir(req, repos, node, rev),            'file': node.isfile and self._render_file(req, context, repos,                                                      node, rev),            'quickjump_entries': xhr or list(repos.get_quickjump_entries(rev)),            'wiki_format_messages':            self.config['changeset'].getbool('wiki_format_messages')        }        if xhr: # render and return the content only            data['xhr'] = True            return 'dir_entries.html', data, None        # Links for contextual navigation        add_ctxtnav(req, tag.a(_('Last Change'),                     href=req.href.changeset(node.rev, node.created_path)))        if node.isfile:            if data['file']['annotate']:                add_ctxtnav(req, _('Normal'),                             title=_('View file without annotations'),                             href=req.href.browser(node.created_path,                                                   rev=node.rev))            else:                add_ctxtnav(req, _('Annotate'),                             title=_('Annotate each line with the last '                                    'changed revision '                                    '(this can be time consuming...)'),                             href=req.href.browser(node.created_path,                                                   rev=node.rev,                                                  annotate='blame'))        add_ctxtnav(req, _('Revision Log'),                     href=req.href.log(path, rev=rev))        add_stylesheet(req, 'common/css/browser.css')        return 'browser.html', data, None    # Internal methods    def _render_dir(self, req, repos, node, rev=None):        req.perm.require('BROWSER_VIEW')        # Entries metadata        class entry(object):            __slots__ = 'name rev kind isdir path content_length'.split()            def __init__(self, node):                for f in entry.__slots__:                    setattr(self, f, getattr(n, f))                        entries = [entry(n) for n in node.get_entries()]        changes = get_changes(repos, [i.rev for i in entries])        if rev:            newest = repos.get_changeset(rev).date        else:            newest = datetime.now(req.tz)        # Color scale for the age column        timerange = custom_colorizer = None        if self.color_scale:            timerange = TimeRange(newest)            max_s = req.args.get('range_max_secs')            min_s = req.args.get('range_min_secs')            parent_range = [timerange.from_seconds(long(s))                            for s in [max_s, min_s] if s]            this_range = [c.date for c in changes.values() if c]            for dt in this_range + parent_range:                timerange.insert(dt)            custom_colorizer = self.get_custom_colorizer()        # Ordering of entries        order = req.args.get('order', 'name').lower()        desc = req.args.has_key('desc')        if order == 'date':            def file_order(a):                return changes[a.rev].date        elif order == 'size':            def file_order(a):                return (a.content_length,                        embedded_numbers(a.name.lower()))        else:            def file_order(a):                return embedded_numbers(a.name.lower())        dir_order = desc and 1 or -1        def browse_order(a):            return a.isdir and dir_order or 0, file_order(a)        entries = sorted(entries, key=browse_order, reverse=desc)        # ''Zip Archive'' alternate link        patterns = self.downloadable_paths        if node.path and patterns and \               filter(None, [fnmatchcase(node.path, p) for p in patterns]):            zip_href = req.href.changeset(rev or repos.youngest_rev, node.path,                                          old=rev, old_path='/', format='zip')            add_link(req, 'alternate', zip_href, _('Zip Archive'),                     'application/zip', 'zip')        add_script(req, 'common/js/expand_dir.js')        add_script(req, 'common/js/keyboard_nav.js')        return {'order': order, 'desc': desc and 1 or None,                'entries': entries, 'changes': changes,                'timerange': timerange, 'colorize_age': custom_colorizer,                'range_max_secs': (timerange and                                   timerange.to_seconds(timerange.newest)),                'range_min_secs': (timerange and                                   timerange.to_seconds(timerange.oldest)),                }    def _render_file(self, req, context, repos, node, rev=None):        req.perm(context.resource).require('FILE_VIEW')        mimeview = Mimeview(self.env)        # MIME type detection        content = node.get_content()        chunk = content.read(CHUNK_SIZE)        mime_type = node.content_type        if not mime_type or mime_type == 'application/octet-stream':            mime_type = mimeview.get_mimetype(node.name, chunk) or \                        mime_type or 'text/plain'        # Eventually send the file directly        format = req.args.get('format')        if format in ('raw', 'txt'):            req.send_response(200)            req.send_header('Content-Type',                            format == 'txt' and 'text/plain' or mime_type)            req.send_header('Content-Length', node.content_length)            req.send_header('Last-Modified', http_date(node.last_modified))            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')            req.end_headers()            while 1:                if not chunk:                    raise RequestDone                req.write(chunk)                chunk = content.read(CHUNK_SIZE)        else:            # The changeset corresponding to the last change on `node`             # is more interesting than the `rev` changeset.            changeset = repos.get_changeset(node.rev)            # add ''Plain Text'' alternate link if needed            if not is_binary(chunk) and mime_type != 'text/plain':                plain_href = req.href.browser(node.path, rev=rev, format='txt')                add_link(req, 'alternate', plain_href, _('Plain Text'),                         'text/plain')            # add ''Original Format'' alternate link (always)            raw_href = req.href.export(rev or repos.youngest_rev, node.path)            add_link(req, 'alternate', raw_href, _('Original Format'),                     mime_type)            self.log.debug("Rendering preview of node %s@%s with mime-type %s"                           % (node.name, str(rev), mime_type))            del content # the remainder of that content is not needed            add_stylesheet(req, 'common/css/code.css')            annotations = ['lineno']            force_source = False            if 'annotate' in req.args:                force_source = True                annotations.insert(0, req.args['annotate'])            preview_data = mimeview.preview_data(context, node.get_content(),                                                 node.get_content_length(),                                                 mime_type, node.created_path,                                                 raw_href,                                                 annotations=annotations,                                                 force_source=force_source)            return {                'changeset': changeset,                'size': node.content_length,                'preview': preview_data,                'annotate': force_source,                }    # public methods        def render_properties(self, mode, context, props):        """Prepare rendering of a collection of properties."""        return filter(None, [self.render_property(name, mode, context, props)                             for name in props])    def render_property(self, name, mode, context, props):        """Renders a node property to HTML."""        candidates = []        for renderer in self.property_renderers:            quality = renderer.match_property(name, mode)            if quality > 0:                candidates.append((quality, renderer))        if candidates:            renderer = sorted(candidates, reverse=True)[0][1]            rendered = renderer.render_property(name, mode, context, props)            if rendered:                if isinstance(rendered, RenderedProperty):                    value = rendered.content                    rendered = rendered                else:                    value = rendered                    rendered = None                prop = {'name': name, 'value': value, 'rendered': rendered}                return prop    # IWikiSyntaxProvider methods    def get_wiki_syntax(self):        return []    def get_link_resolvers(self):        """TracBrowser link resolvers.         - `source:` and `browser:`             * simple paths (/dir/file)             * paths at a given revision (/dir/file@234)             * paths with line number marks (/dir/file@234:10,20-30)             * paths with line number anchor (/dir/file@234#L100)            Marks and anchor can be combined.            The revision must be present when specifying line numbers.            In the few cases where it would be redundant (e.g. for tags), the            revision number itself can be omitted: /tags/v10/file@100-110#L99        """        return [('repos', self._format_browser_link),                ('export', self._format_export_link),                ('source', self._format_browser_link),                ('browser', self._format_browser_link)]    def _format_export_link(self, formatter, ns, export, label):        export, query, fragment = formatter.split_link(export)        if ':' in export:            rev, path = export.split(':', 1)        elif '@' in export:            path, rev = export.split('@', 1)        else:            rev, path = self.env.get_repository().youngest_rev, export        return tag.a(label, class_='source',                     href=formatter.href.export(rev, path) + fragment)    def _format_browser_link(self, formatter, ns, path, label):        path, query, fragment = formatter.split_link(path)        rev = marks = None        match = self.PATH_LINK_RE.match(path)        if match:            path, rev, marks = match.groups()        return tag.a(label, class_='source',                     href=(formatter.href.browser(path, rev=rev, marks=marks) +                           query + fragment))    PATH_LINK_RE = re.compile(r"([^@#:]*)"     # path                              r"[@:]([^#:]+)?" # rev                              r"(?::(\d+(?:-\d+)?(?:,\d+(?:-\d+)?)*))?" # marks                              )    # IHTMLPreviewAnnotator methods    def get_annotation_type(self):        return 'blame', _('Rev'), _('Revision in which the line changed')    def get_annotation_data(self, context):        """Cache the annotation data corresponding to each revision."""        return BlameAnnotator(self.env, context)    def annotate_row(self, context, row, lineno, line, blame_annotator):        blame_annotator.annotate(row, lineno)class BlameAnnotator(object):    def __init__(self, env, context):        self.env = env        # `context`'s resource is ('source', path, version=rev)        self.context = context        self.resource = context.resource        self.repos = env.get_repository()        # maintain state        self.prev_chgset = None        self.chgset_data = {}        add_script(context.req, 'common/js/blame.js')        add_stylesheet(context.req, 'common/css/changeset.css')        add_stylesheet(context.req, 'common/css/diff.css')        self.reset()    def reset(self):        rev = self.resource.version        node = self.repos.get_node(self.resource.id, rev)        # FIXME: get_annotations() should be in the Resource API        # -- get revision numbers for each line        self.annotations = node.get_annotations()        # -- from the annotations, retrieve changesets and        # determine the span of dates covered, for the color code.        # Note: changesets[i].rev can differ from annotations[i]        # (long form vs. compact, short rev form for the latter).        self.changesets = []        chgset = self.repos.get_changeset(rev)        chgsets = {rev: chgset}        self.timerange = TimeRange(chgset.date)        for idx in range(len(self.annotations)):            rev = self.annotations[idx]            chgset = chgsets.get(rev)            if not chgset:                chgset = self.repos.get_changeset(rev)                chgsets[rev] = chgset                self.timerange.insert(chgset.date)            # get list of changeset parallel to annotations            self.changesets.append(chgset)        # -- retrieve the original path of the source, for each rev        # (support for copy/renames)        self.paths = {}        for path, rev, chg in node.get_history():            self.paths[rev] = path        # -- get custom colorize function        browser = BrowserModule(self.env)        self.colorize_age = browser.get_custom_colorizer()    def annotate(self, row, lineno):        if lineno > len(self.annotations):            row.append(tag.th())            return        rev = self.annotations[lineno-1]        chgset = self.changesets[lineno-1]        path = self.paths.get(rev, None)        # Note: path will be None if copy/rename is not supported        # by get_history                # -- compute anchor and style once per revision        if rev not in self.chgset_data:            chgset_href = self.context.href.changeset(rev, path)            short_author = chgset.author.split(' ', 1)[0]            title = shorten_line('%s: %s' % (short_author, chgset.message))            anchor = tag.a('[%s]' % self.repos.short_rev(rev), # shortname                           title=title, href=chgset_href)            color = self.colorize_age(self.timerange.relative(chgset.date))            style = 'background-color: rgb(%d, %d, %d);' % color            self.chgset_data[rev] = (anchor, style)        else:            anchor, style = self.chgset_data[rev]        if self.prev_chgset != chgset:            self.prev_style = style        # optimize away the path if there's no copy/rename info        if not path or path == self.resource.id:            path = ''        # -- produce blame column, eventually with an anchor        style = self.prev_style        if lineno < len(self.changesets) and self.changesets[lineno] == chgset:            style += ' border-bottom: none;'        blame_col = tag.th(style=style, class_='blame r%s' % rev)        if self.prev_chgset != chgset:            blame_col.append(anchor)            self.prev_chgset = chgset        row.append(blame_col)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -