review/web_ui.py @ ac324e064f82

Don't give away the repo filesystem path in the web UI footer.

Now that we have a read-only mode people might want to expose the web UI to
the world.  We shouldn't display sensitive data on the pages any more.
author Steve Losh <steve@stevelosh.com>
date Sat, 27 Mar 2010 12:11:12 -0400
parents 6df093d9f238
children 048ef4889301
"""The review extension's web UI."""
from __future__ import with_statement

import sys, os
from hashlib import md5

from mercurial import commands, cmdutil, hg, templatefilters
from mercurial.node import short, hex
from mercurial.util import email

import api

package_path = os.path.split(os.path.realpath(__file__))[0]
template_path = os.path.join(package_path, 'web_templates')
media_path = os.path.join(package_path, 'web_media')
top_path = os.path.split(package_path)[0]
bundled_path = os.path.join(top_path, 'bundled')
cherrypy_path = os.path.join(bundled_path, 'cherrypy')
jinja2_path = os.path.join(bundled_path, 'jinja2')

sys.path.insert(0, cherrypy_path)
sys.path.insert(0, jinja2_path)

import cherrypy
from jinja2 import Environment, FileSystemLoader


TEMPLATE_DIR = os.path.join(package_path, 'web_templates')
jinja_env = Environment(loader=FileSystemLoader(TEMPLATE_DIR))

LOG_PAGE_LEN = 1000000

def _item_gravatar(item):
    return 'http://www.gravatar.com/avatar/%s/' % md5(email(item.author)).hexdigest()

def _line_type(line):
    return 'rem' if line[0] == '-' else 'add' if line[0] == '+' else 'con'

utils = {
    'node_short': short,
    'basename': os.path.basename,
    'md5': md5,
    'email': email,
    'templatefilters': templatefilters,
    'len': len,
    'item_gravatar': _item_gravatar,
    'line_type': _line_type,
}

class ReviewWebUI(object):
    def __init__(self, datastore, read_only):
        self.datastore = datastore
        self.read_only = read_only
    
    
    @cherrypy.expose
    def index(self):
        rev_max = self.datastore.target['tip'].rev()
        rev_min = rev_max - LOG_PAGE_LEN if rev_max >= LOG_PAGE_LEN else 0
        rcsets = [self.datastore[r] for r in xrange(rev_max, rev_min, -1)]
        
        return jinja_env.get_template('index.html').render(
            read_only=self.read_only,
            utils=utils, datastore=self.datastore, title='',
            rcsets=rcsets,
        )
    
    
    @cherrypy.expose
    def changeset(self, *args, **kwargs):
        if len(args) != 1:
            return 'OH GOD HOW DID THIS GET HERE I AM NOT GOOD WITH LINKS'
        rev_id = args[0]
        
        if kwargs and not self.read_only:
            filename = kwargs.get('filename', '')
            lines = str(kwargs['lines']) if 'lines' in kwargs else ''
            if lines:
                lines = lines.split(',')
            body = kwargs['new-comment-body']
            print filename, lines, body
            
            if body:
                rcset = self.datastore[rev_id]
                rcset.add_comment(body, filename, lines)
            
            raise cherrypy.HTTPRedirect("/changeset/%s/" % rev_id)
        
        rcset = self.datastore[rev_id]
        rev = rcset.target[rev_id]
        
        return jinja_env.get_template('changeset.html').render(
            read_only=self.read_only,
            utils=utils, datastore=self.datastore,
            title='%s:%s' % (rev.rev(), short(rev.node())),
            rcset=rcset, rev=rev,
        )
    
    
    @cherrypy.expose
    def pull(self, **kwargs):
        if not self.read_only:
            if 'path' not in kwargs:
                return 'OH GOD HOW DID THIS GET HERE I AM NOT GOOD WITH LINKS'
            path = kwargs['path']
            
            commands.pull(
                self.datastore.repo.ui, self.datastore.repo, path, **{
                    'update': True
                }
            )
        
        raise cherrypy.HTTPRedirect("/")
    
    
    @cherrypy.expose
    def push(self, **kwargs):
        if not self.read_only:
            if 'path' not in kwargs:
                return 'OH GOD HOW DID THIS GET HERE I AM NOT GOOD WITH LINKS'
            path = kwargs['path']
            
            commands.push(
                self.datastore.repo.ui,self.datastore.repo, path, **{}
            )
        
        raise cherrypy.HTTPRedirect("/")
    


def load_interface(ui, repo, read_only=False, open=False, port=8080):
    if open:
        import webbrowser
        webbrowser.open('http://localhost:%d/' % port)
        
    conf = {
        '/media': {
            'tools.staticdir.on': True,
            'tools.staticdir.dir': media_path,
        }
    }
    
    cherrypy.quickstart(ReviewWebUI(api.ReviewDatastore(ui, repo), read_only=read_only), config=conf)