15
15
# along with this program; if not, write to the Free Software
16
16
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22
# This app is a wrapper around the diff script run through the trampoline
18
# Author: David Coles, Will Grant
20
'''Components of the webapp for diffing user files.'''
27
30
import ivle.interpret
30
# handle_with_trampoline controls the way in which fileservice_lib is invoked.
31
# If False, it will simply be called directly by this handler.
32
# If True, the request will get marshalled into a CGI environment and the
33
# trampoline will invoke services/fileservices within the user's jail (SetUID'd
34
# to them). This script will then wrap the CGI environment in a replica of the
35
# original environment and handle it that way.
36
# This is a lot of overhead but it's the only way to properly ensure we are
37
# acting "as" that user and therefore we don't run into permissions problems.
38
# If set to True, it will be a lot more efficient, but there will be
39
# permissions issues unless all user's files are owned by the web server user.
40
HANDLE_WITH_TRAMPOLINE = True
42
diffservice_path = os.path.join(ivle.conf.share_path, 'services/diffservice')
45
"""Handler for the File Services application."""
46
req.styles = ["media/diff/diff.css"] # CSS styles
47
req.write_html_head_foot = True # Have dispatch print head and foot
50
if not HANDLE_WITH_TRAMPOLINE:
54
req.throw_redirect(os.path.join(req.uri,req.user.login));
55
interp_object = ivle.interpret.interpreter_objects["cgi-python"]
56
user_jail_dir = os.path.join(ivle.conf.jail_base, req.user.login)
57
ivle.interpret.interpret_file(req, req.user, user_jail_dir,
58
diffservice_path, interp_object)
31
from ivle.webapp.base.xhtml import XHTMLView
32
from ivle.webapp.base.plugins import ViewPlugin, MediaPlugin
33
from ivle.webapp.errors import NotFound, BadRequest
35
class DiffView(XHTMLView):
36
'''A view to present a nice XHTML Subversion diff from a user's jail.'''
37
template = 'template.html'
39
def __init__(self, req, path):
42
def authorize(self, req):
43
return req.user is not None
45
def populate(self, req, ctx):
46
self.plugin_styles[Plugin] = ['diff.css']
48
revfields = req.get_fieldstorage().getlist("r")
49
if len(revfields) > 2:
50
raise BadRequest('A maximum of two revisions can be given.')
52
revs = [revfield.value for revfield in revfields]
54
jail_dir = os.path.join(ivle.conf.jail_base, req.user.login)
55
(out, err) = ivle.interpret.execute_raw(req.user, jail_dir, '/home',
56
os.path.join(ivle.conf.share_path, 'services/diffservice'),
60
response = cjson.decode(out)
61
if 'error' in response:
62
if response['error'] == 'notfound':
65
raise AssertionError('Unknown error from diffservice: %s' %
68
# No error. We must be safe.
69
diff = response['diff']
71
# Split up the udiff into individual files
72
diff_matcher = re.compile(
73
r'^Index: (.*)\n\=+\n((?:[^I].*\n)*)',re.MULTILINE
76
# Create a dict with (name, HTMLdiff) pairs for each non-empty diff.
77
ctx['files'] = dict([(fd[0], genshi.XML(htmlfy_diff(fd[1])))
78
for fd in diff_matcher.findall(diff)
82
def htmlfy_diff(difftext):
83
"""Adds HTML markup to a udiff string"""
84
output = cgi.escape(difftext)
86
r'^([\+\-]{3})\s(\S+)\s\((.+)\)$':
87
r'<span class="diff-files">\1 \2 <em>(\3)</em></span>',
89
r'<span class="diff-range">@@ \1 @@</span>',
91
r'<span class="diff-add">+\1</span>',
93
r'<span class="diff-sub">-\1</span>',
95
r'<span class="diff-special">\\\1</span>'
99
output = re.compile(match, re.MULTILINE).sub(subs[match], output)
101
return '<pre class="diff">%s</pre>' % output
103
class Plugin(ViewPlugin, MediaPlugin):
104
'''Registration class for diff components.'''
106
('diff/', DiffView, {'path': ''}),
107
('diff/*(path)', DiffView),