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

📄 changeset.py

📁 一款基于web的项目管理、bug跟踪系统。提供了与svn集成的操作界面、问题跟踪
💻 PY
📖 第 1 页 / 共 3 页
字号:
# -*- coding: utf-8 -*-## Copyright (C) 2003-2008 Edgewall Software# Copyright (C) 2003-2005 Jonas Borgstr枚m <jonas@edgewall.com># Copyright (C) 2004-2006 Christopher Lenz <cmlenz@gmx.de># Copyright (C) 2005-2006 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>#         Christopher Lenz <cmlenz@gmx.de>#         Christian Boos <cboos@neuf.fr>from datetime import datetimeimport osimport posixpathimport refrom StringIO import StringIOimport timefrom genshi.builder import tagfrom trac.config import Option, BoolOption, IntOptionfrom trac.core import *from trac.mimeview import Mimeview, is_binary, Contextfrom trac.perm import IPermissionRequestorfrom trac.resource import Resource, ResourceNotFoundfrom trac.search import ISearchSource, search_to_sql, shorten_resultfrom trac.timeline.api import ITimelineEventProviderfrom trac.util import embedded_numbers, content_dispositionfrom trac.util.compat import any, sorted, groupbyfrom trac.util.datefmt import pretty_timedelta, utcfrom trac.util.text import unicode_urlencode, shorten_line, CRLFfrom trac.util.translation import _from trac.versioncontrol import Changeset, Node, NoSuchChangesetfrom trac.versioncontrol.diff import get_diff_options, diff_blocks, unified_difffrom trac.versioncontrol.web_ui.browser import BrowserModule, \                                               DefaultPropertyRendererfrom trac.web import IRequestHandler, RequestDonefrom trac.web.chrome import add_ctxtnav, add_link, add_script, add_stylesheet, \                            prevnext_nav, INavigationContributor, Chromefrom trac.wiki import IWikiSyntaxProvider, WikiParserfrom trac.wiki.formatter import format_to_htmlclass IPropertyDiffRenderer(Interface):    """Render node properties in TracBrowser and TracChangeset views."""    def match_property_diff(name):        """Indicate whether this renderer can treat the given property diffs        Returns a quality number, ranging from 0 (unsupported) to 9        (''perfect'' match).        """    def render_property_diff(name, old_context, old_props,                             new_context, new_props, options):        """Render the given diff of property to HTML.        `name` is the property name as given to `match_property_diff()`,        `old_context` corresponds to the old node being render        (useful when the rendering depends on the node kind)        and `old_props` is the corresponding collection of all properties.        Same for `new_node` and `new_props`.        `options` are the current diffs options.        The rendered result can be one of the following:        - `None`: the property change will be shown the normal way          (''changed from `old` to `new`'')        - an `unicode` value: the change will be shown as textual content        - `Markup` or other Genshi content: the change will shown as block          markup        """class DefaultPropertyDiffRenderer(Component):    """Implement default behavior for rendering property differences."""    implements(IPropertyDiffRenderer)    def match_property_diff(self, name):        # Support everything but hidden properties.        hidden_properties = DefaultPropertyRenderer(self.env).hidden_properties        return name not in hidden_properties and 1 or 0    def render_property_diff(self, name, old_context, old_props,                             new_context, new_props, options):        old, new = old_props[name], new_props[name]        # Render as diff only if multiline (see #3002)        if '\n' not in old and '\n' not in new:            return None        unidiff = '--- \n+++ \n' + \                  '\n'.join(unified_diff(old.splitlines(), new.splitlines(),                                         options.get('contextlines', 3)))        return tag.li('Property ', tag.strong(name),                      Mimeview(self.env).render(old_context, 'text/x-diff',                                                unidiff))class ChangesetModule(Component):    """Provide flexible functionality for showing sets of differences.    If the differences shown are coming from a specific changeset,    then that changeset informations can be shown too.    In addition, it is possible to show only a subset of the changeset:    Only the changes affecting a given path will be shown.    This is called the ''restricted'' changeset.    But the differences can also be computed in a more general way,    between two arbitrary paths and/or between two arbitrary revisions.    In that case, there's no changeset information displayed.    """    implements(INavigationContributor, IPermissionRequestor, IRequestHandler,               ITimelineEventProvider, IWikiSyntaxProvider, ISearchSource)    property_diff_renderers = ExtensionPoint(IPropertyDiffRenderer)        timeline_show_files = Option('timeline', 'changeset_show_files', '0',        """Number of files to show (`-1` for unlimited, `0` to disable).        This can also be `location`, for showing the common prefix for the        changed files. (since 0.11).        """)    timeline_long_messages = BoolOption('timeline', 'changeset_long_messages',                                        'false',        """Whether wiki-formatted changeset messages should be multiline or not.        If this option is not specified or is false and `wiki_format_messages`        is set to true, changeset messages will be single line only, losing        some formatting (bullet points, etc).""")    timeline_collapse = BoolOption('timeline', 'changeset_collapse_events',                                   'false',        """Whether consecutive changesets from the same author having         exactly the same message should be presented as one event.        That event will link to the range of changesets in the log view.        (''since 0.11'')""")    max_diff_files = IntOption('changeset', 'max_diff_files', 0,        """Maximum number of modified files for which the changeset view will        attempt to show the diffs inlined (''since 0.10'').""")    max_diff_bytes = IntOption('changeset', 'max_diff_bytes', 10000000,        """Maximum total size in bytes of the modified files (their old size        plus their new size) for which the changeset view will attempt to show        the diffs inlined (''since 0.10'').""")    wiki_format_messages = BoolOption('changeset', 'wiki_format_messages',                                      'true',        """Whether wiki formatting should be applied to changeset messages.                If this option is disabled, changeset messages will be rendered as        pre-formatted text.""")    # INavigationContributor methods    def get_active_navigation_item(self, req):        return 'browser'    def get_navigation_items(self, req):        return []    # IPermissionRequestor methods    def get_permission_actions(self):        return ['CHANGESET_VIEW']    # IRequestHandler methods    _request_re = re.compile(r"/changeset(?:/([^/]+))?(/.*)?$")    def match_request(self, req):        match = re.match(self._request_re, req.path_info)        if match:            new, new_path = match.groups()            if new:                req.args['new'] = new            if new_path:                req.args['new_path'] = new_path            return True    def process_request(self, req):        """The appropriate mode of operation is inferred from the request        parameters:         * If `new_path` and `old_path` are equal (or `old_path` is omitted)           and `new` and `old` are equal (or `old` is omitted),           then we're about to view a revision Changeset: `chgset` is True.           Furthermore, if the path is not the root, the changeset is           ''restricted'' to that path (only the changes affecting that path,           its children or its ancestor directories will be shown).         * In any other case, the set of changes corresponds to arbitrary           differences between path@rev pairs. If `new_path` and `old_path`           are equal, the ''restricted'' flag will also be set, meaning in this           case that the differences between two revisions are restricted to           those occurring on that path.        In any case, either path@rev pairs must exist.        """        req.perm.require('CHANGESET_VIEW')                repos = self.env.get_repository(req.authname)        # -- retrieve arguments        new_path = req.args.get('new_path')        new = req.args.get('new')        old_path = req.args.get('old_path')        old = req.args.get('old')        xhr = req.get_header('X-Requested-With') == 'XMLHttpRequest'        # -- support for the revision log ''View changes'' form,        #    where we need to give the path and revision at the same time        if old and '@' in old:            old, old_path = old.split('@', 1)        if new and '@' in new:            new, new_path = new.split('@', 1)        # -- normalize and check for special case        try:            new_path = repos.normalize_path(new_path)            new = repos.normalize_rev(new)                        repos.authz.assert_permission_for_changeset(new)                        old_path = repos.normalize_path(old_path or new_path)            old = repos.normalize_rev(old or new)        except NoSuchChangeset, e:            raise ResourceNotFound(e.message, _('Invalid Changeset Number'))        if old_path == new_path and old == new: # revert to Changeset            old_path = old = None        style, options, diff_data = get_diff_options(req)        # -- setup the `chgset` and `restricted` flags, see docstring above.        chgset = not old and not old_path        if chgset:            restricted = new_path not in ('', '/') # (subset or not)        else:            restricted = old_path == new_path # (same path or not)        # -- redirect if changing the diff options        if req.args.has_key('update'):            if chgset:                if restricted:                    req.redirect(req.href.changeset(new, new_path))                else:                    req.redirect(req.href.changeset(new))            else:                req.redirect(req.href.changeset(new, new_path, old=old,                                                old_path=old_path))        # -- preparing the data        if chgset:            prev = repos.get_node(new_path, new).get_previous()            if prev:                prev_path, prev_rev = prev[:2]            else:                prev_path, prev_rev = new_path, repos.previous_rev(new)            data = {'old_path': prev_path, 'old_rev': prev_rev,                    'new_path': new_path, 'new_rev': new}        else:            if not new:                new = repos.youngest_rev            elif not old:                old = repos.youngest_rev            if not old_path:                old_path = new_path            data = {'old_path': old_path, 'old_rev': old,                    'new_path': new_path, 'new_rev': new}        data['diff'] = diff_data        data['wiki_format_messages'] = self.wiki_format_messages        if chgset:            req.perm('changeset', new).require('CHANGESET_VIEW')            chgset = repos.get_changeset(new)            # TODO: find a cheaper way to reimplement r2636            req.check_modified(chgset.date, [                style, ''.join(options), repos.name,                repos.rev_older_than(new, repos.youngest_rev),                chgset.message, xhr,                pretty_timedelta(chgset.date, None, 3600)])        format = req.args.get('format')        if format in ['diff', 'zip']:            req.perm.require('FILE_VIEW')            # choosing an appropriate filename            rpath = new_path.replace('/','_')            if chgset:                if restricted:                    filename = 'changeset_%s_r%s' % (rpath, new)                else:                    filename = 'changeset_r%s' % new            else:                if restricted:                    filename = 'diff-%s-from-r%s-to-r%s' \                                  % (rpath, old, new)                elif old_path == '/': # special case for download (#238)                    filename = '%s-r%s' % (rpath, old)                else:                    filename = 'diff-from-%s-r%s-to-%s-r%s' \                               % (old_path.replace('/','_'), old, rpath, new)            if format == 'diff':                self._render_diff(req, filename, repos, data)            elif format == 'zip':                self._render_zip(req, filename, repos, data)        # -- HTML format        self._render_html(req, repos, chgset, restricted, xhr, data)                if chgset:            diff_params = 'new=%s' % new        else:            diff_params = unicode_urlencode({'new_path': new_path,                                             'new': new,                                             'old_path': old_path,                                             'old': old})        add_link(req, 'alternate', '?format=diff&'+diff_params,                 _('Unified Diff'), 'text/plain', 'diff')        add_link(req, 'alternate', '?format=zip&'+diff_params, _('Zip Archive'),                 'application/zip', 'zip')        add_script(req, 'common/js/diff.js')        add_stylesheet(req, 'common/css/changeset.css')        add_stylesheet(req, 'common/css/diff.css')        add_stylesheet(req, 'common/css/code.css')        if chgset:            if restricted:                prevnext_nav(req, _('Change'))            else:                prevnext_nav(req, _('Changeset'))        else:

⌨️ 快捷键说明

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