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

📄 browser.py

📁 一款基于web的项目管理、bug跟踪系统。提供了与svn集成的操作界面、问题跟踪
💻 PY
📖 第 1 页 / 共 2 页
字号:
# -*- coding: utf-8 -*-## Copyright (C) 2003-2008 Edgewall Software# Copyright (C) 2003-2005 Jonas Borgstr枚m <jonas@edgewall.com># Copyright (C) 2005-2007 Christian Boos <cboos@neuf.fr># All rights reserved.## This software is licensed as described in the file COPYING, which# you should have received as part of this distribution. The terms# are also available at http://trac.edgewall.org/wiki/TracLicense.## This software consists of voluntary contributions made by many# individuals. For the exact contribution history, see the revision# history and logs, available at http://trac.edgewall.org/log/.## Author: Jonas Borgstr枚m <jonas@edgewall.com>from datetime import datetime, timedeltafrom fnmatch import fnmatchcaseimport reimport osimport urllibfrom genshi.builder import tagfrom trac.config import ListOption, BoolOption, Optionfrom trac.core import *from trac.mimeview.api import Mimeview, is_binary, get_mimetype, \                              IHTMLPreviewAnnotator, Contextfrom trac.perm import IPermissionRequestorfrom trac.resource import ResourceNotFound, Resourcefrom trac.util import sorted, embedded_numbersfrom trac.util.datefmt import http_date, utcfrom trac.util.html import escape, Markupfrom trac.util.text import shorten_linefrom trac.util.translation import _from trac.web import IRequestHandler, RequestDonefrom trac.web.chrome import add_ctxtnav, add_link, add_script, add_stylesheet, \                            prevnext_nav, INavigationContributorfrom trac.wiki.api import IWikiSyntaxProviderfrom trac.wiki.formatter import format_to_html, format_to_onelinerfrom trac.versioncontrol.api import NoSuchChangeset, NoSuchNodefrom trac.versioncontrol.web_ui.util import *CHUNK_SIZE = 4096class IPropertyRenderer(Interface):    """Render node properties in TracBrowser and TracChangeset views."""    def match_property(name, mode):        """Indicate whether this renderer can treat the given property        `mode` is the current rendering context, which can be:         - 'browser' rendered in the browser view         - 'changeset' rendered in the changeset view as a node property         - 'revprop' rendered in the changeset view as a revision property        Other identifiers might be used by plugins, so it's advised to simply        ignore unknown modes.        Returns a quality number, ranging from 0 (unsupported) to 9        (''perfect'' match).        """    def render_property(name, mode, context, props):        """Render the given property.        `name` is the property name as given to `match()`,        `mode` is the same as for `match_property`,        `context` is the context for the node being render        (useful when the rendering depends on the node kind) and        `props` is the collection of the corresponding properties        (i.e. the `node.get_properties()`).        The rendered result can be one of the following:        - `None`: the property will be skipped        - an `unicode` value: the property will be displayed as text        - a `RenderedProperty` instance: the property will only be displayed          using the instance's `content` attribute, and the other attributes          will also be used in some display contexts (like `revprop`)        - `Markup` or other Genshi content: the property will be displayed          normally, using that content as a block-level markup        """class RenderedProperty(object):    def __init__(self, name=None, name_attributes=None,                 content=None, content_attributes=None):        self.name = name        self.name_attributes = name_attributes        self.content = content        self.content_attributes = content_attributesclass DefaultPropertyRenderer(Component):    """Implement default (pre-0.11) behavior for rendering properties."""    implements(IPropertyRenderer)    hidden_properties = ListOption('browser', 'hide_properties', 'svk:merge',        doc="""Comma-separated list of version control properties to hide from        the repository browser.        (''since 0.9'')""")    def match_property(self, name, mode):        # Support everything but hidden properties.        return name not in self.hidden_properties and 1 or 0    def render_property(self, name, mode, context, props):        # No special treatment besides respecting newlines in values.        value = props[name]        if value and '\n' in value:            value = Markup(''.join(['<br />%s' % escape(v)                                    for v in value.split('\n')]))        return valueclass WikiPropertyRenderer(Component):    """Render properties as wiki text."""    implements(IPropertyRenderer)    wiki_properties = ListOption('browser', 'wiki_properties',                                 'trac:description',        doc="""Comma-separated list of version control properties to render        as wiki content in the repository browser.        (''since 0.11'')""")    oneliner_properties = ListOption('browser', 'oneliner_properties',                                 'trac:summary',        doc="""Comma-separated list of version control properties to render        as oneliner wiki content in the repository browser.        (''since 0.11'')""")    def match_property(self, name, mode):        return (name in self.wiki_properties or \                name in self.oneliner_properties) and 4 or 0    def render_property(self, name, mode, context, props):        if name in self.wiki_properties:            return format_to_html(self.env, context, props[name])        else:            return format_to_oneliner(self.env, context, props[name])class TimeRange(object):    min = datetime(1, 1, 1, 0, 0, 0, 0, utc) # tz aware version of datetime.min        def __init__(self, base):        self.oldest = self.newest = base        self._total = None    def seconds_between(self, dt1, dt2):        delta = dt1 - dt2        return delta.days * 24 * 3600 + delta.seconds        def to_seconds(self, dt):        return self.seconds_between(dt, TimeRange.min)    def from_seconds(self, secs):        return TimeRange.min + timedelta(*divmod(secs, 24* 3600))    def relative(self, datetime):        if self._total is None:            self._total = float(self.seconds_between(self.newest, self.oldest))        age = 1.0        if self._total:            age = self.seconds_between(datetime, self.oldest) / self._total        return age    def insert(self, datetime):        self._total = None        self.oldest = min(self.oldest, datetime)        self.newest = max(self.newest, datetime)class BrowserModule(Component):    implements(INavigationContributor, IPermissionRequestor, IRequestHandler,               IWikiSyntaxProvider, IHTMLPreviewAnnotator)    property_renderers = ExtensionPoint(IPropertyRenderer)    downloadable_paths = ListOption('browser', 'downloadable_paths',                                    '/trunk, /branches/*, /tags/*',        doc="""List of repository paths that can be downloaded.                Leave the option empty if you want to disable all downloads, otherwise        set it to a comma-separated list of authorized paths (those paths are        glob patterns, i.e. "*" can be used as a wild card)        (''since 0.10'')""")    color_scale = BoolOption('browser', 'color_scale', True,        doc="""Enable colorization of the ''age'' column.                This uses the same color scale as the source code annotation:        blue is older, red is newer.        (''since 0.11'')""")    NEWEST_COLOR = (255,136,136)    newest_color = Option('browser', 'newest_color', repr(NEWEST_COLOR),        doc="""(r,g,b) color triple to use for the color corresponding        to the newest color, for the color scale used in ''blame'' or        the browser ''age'' column if `color_scale` is enabled.        (''since 0.11'')""")    OLDEST_COLOR = (136,136,255)    oldest_color = Option('browser', 'oldest_color', repr(OLDEST_COLOR),        doc="""(r,g,b) color triple to use for the color corresponding        to the oldest color, for the color scale used in ''blame'' or        the browser ''age'' column if `color_scale` is enabled.        (''since 0.11'')""")    intermediate_point = Option('browser', 'intermediate_point', '',        doc="""If set to a value between 0 and 1 (exclusive), this will be the        point chosen to set the `intermediate_color` for interpolating        the color value.        (''since 0.11'')""")    intermediate_color = Option('browser', 'intermediate_color', '',        doc="""(r,g,b) color triple to use for the color corresponding        to the intermediate color, if two linear interpolations are used        for the color scale (see `intermediate_point`).        If not set, the intermediate color between `oldest_color` and        `newest_color` will be used.        (''since 0.11'')""")    render_unsafe_content = BoolOption('browser', 'render_unsafe_content',                                        'false',        """Whether attachments should be rendered in the browser, or        only made downloadable.         Pretty much any file may be interpreted as HTML by the browser,        which allows a malicious user to attach a file containing cross-site        scripting attacks.                For public sites where anonymous users can create attachments it is        recommended to leave this option disabled (which is the default).""")    # public methods    def get_custom_colorizer(self):        """Returns a converter for values from [0.0, 1.0] to a RGB triple."""                def interpolate(old, new, value):            # Provides a linearly interpolated color triple for `value`            # which must be a floating point value between 0.0 and 1.0            return tuple([int(b+(a-b)*value) for a,b in zip(new,old)])        def parse_color(rgb, default):            # Get three ints out of a `rgb` string or return `default`            try:                t = tuple([int(v) for v in re.split(r'(\d+)', rgb)[1::2]])                return len(t) == 3 and t or default            except ValueError:                return default                newest_color = parse_color(self.newest_color, self.NEWEST_COLOR)        oldest_color = parse_color(self.oldest_color, self.OLDEST_COLOR)        try:            intermediate = float(self.intermediate_point)        except ValueError:            intermediate = None        if intermediate:            intermediate_color = parse_color(self.intermediate_color, None)            if not intermediate_color:                intermediate_color = tuple([(a+b)/2 for a,b in                                            zip(newest_color, oldest_color)])            def colorizer(value):                if value <= intermediate:                    value = value / intermediate                    return interpolate(oldest_color, intermediate_color, value)                else:                    value = (value - intermediate) / (1.0 - intermediate)                    return interpolate(intermediate_color, newest_color, value)        else:            def colorizer(value):                return interpolate(oldest_color, newest_color, value)        return colorizer            # INavigationContributor methods    def get_active_navigation_item(self, req):        return 'browser'    def get_navigation_items(self, req):        if 'BROWSER_VIEW' in req.perm:            yield ('mainnav', 'browser',                   tag.a(_('Browse Source'), href=req.href.browser()))    # IPermissionRequestor methods    def get_permission_actions(self):        return ['BROWSER_VIEW', 'FILE_VIEW']    # IRequestHandler methods    def match_request(self, req):        import re        match = re.match(r'/(export|browser|file)(?:(/.*))?', req.path_info)        if match:            mode, path = match.groups()            if mode == 'export':                if path and '/' in path:                    _, rev, path = path.split('/', 2)                    req.args['rev'] = rev                    req.args['format'] = 'raw'            elif mode == 'file':                req.redirect(req.href.browser(path, rev=req.args.get('rev'),                                              format=req.args.get('format')),                             permanent=True)            req.args['path'] = path or '/'            return True    def process_request(self, req):        go_to_preselected = req.args.get('preselected')        if go_to_preselected:            req.redirect(go_to_preselected)        path = req.args.get('path', '/')        rev = req.args.get('rev', None)        order = req.args.get('order', None)        desc = req.args.get('desc', None)        xhr = req.get_header('X-Requested-With') == 'XMLHttpRequest'        # Find node for the requested path/rev        repos = self.env.get_repository(req.authname)        try:            if rev:                rev = repos.normalize_rev(rev)            # If `rev` is `None`, we'll try to reuse `None` consistently,            # as a special shortcut to the latest revision.            rev_or_latest = rev or repos.youngest_rev            node = get_existing_node(req, repos, path, rev_or_latest)        except NoSuchChangeset, e:            raise ResourceNotFound(e.message, _('Invalid Changeset Number'))        context = Context.from_request(req, 'source', path, node.created_rev)        path_links = get_path_links(req.href, path, rev, order, desc)        if len(path_links) > 1:            add_link(req, 'up', path_links[-2]['href'], _('Parent directory'))        data = {            'context': context,            'path': path, 'rev': node.rev, 'stickyrev': rev,            'created_path': node.created_path,            'created_rev': node.created_rev,            'properties': xhr or self.render_properties('browser', context,                                                        node.get_properties()),

⌨️ 快捷键说明

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