~loggerhead-team/loggerhead/trunk-rich

« back to all changes in this revision

Viewing changes to loggerhead/controllers/__init__.py

Support serving /robots.txt when using apps.transport (serve-branches or bzr serve --http)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
#
 
2
# Copyright (C) 2008  Canonical Ltd.
2
3
# Copyright (C) 2006  Robey Pointer <robey@lag.net>
3
4
# Copyright (C) 2006  Goffredo Baroncelli <kreijack@inwind.it>
4
5
#
15
16
# You should have received a copy of the GNU General Public License
16
17
# along with this program; if not, write to the Free Software
17
18
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
 
#
19
19
 
20
 
import logging
21
 
import os
22
 
import re
23
 
import sys
24
20
import time
25
21
 
26
 
import turbogears
27
 
from turbogears import controllers
28
 
from cherrypy import HTTPRedirect, NotFound
29
 
from configobj import ConfigObj
 
22
from paste.request import path_info_pop, parse_querystring
30
23
 
31
24
from loggerhead import util
32
 
from loggerhead.branchview import BranchView
33
 
from loggerhead.history import History, is_branch
34
 
 
35
 
log = logging.getLogger("loggerhead.controllers")
36
 
 
37
 
 
38
 
def cherrypy_friendly(s):
39
 
    """
40
 
    convert a config section name into a name that pleases cherrypy.
41
 
    """
42
 
    return re.sub(r'[^\w\d_]', '_', s)
43
 
 
44
 
 
45
 
class Project (object):
46
 
    def __init__(self, name, config):
47
 
        self.name = name
48
 
        self.friendly_name = config.get('name', name)
49
 
        self.description = config.get('description', '')
50
 
        self.long_description = config.get('long_description', '')
51
 
        self._config = config
52
 
        
53
 
        self._views = []
54
 
        for view_name in config.sections:
55
 
            log.debug('Configuring (project %r) branch %r...', name, view_name)
56
 
            self._add_view(view_name, config[view_name], config[view_name].get('folder'))
57
 
        
58
 
        auto_folder = config.get('auto_publish_folder', None)
59
 
        if auto_folder is not None:
60
 
            auto_list = []
61
 
            # scan a folder for bazaar branches, and add them automatically
62
 
            for path, folders, filenames in os.walk(auto_folder):
63
 
                for folder in folders:
64
 
                    folder = os.path.join(path, folder)
65
 
                    if is_branch(folder):
66
 
                        auto_list.append(folder)
67
 
            auto_list.sort()
68
 
            for folder in auto_list:
69
 
                view_name = os.path.basename(folder)
70
 
                log.debug('Auto-configuring (project %r) branch %r...', name, view_name)
71
 
                self._add_view(view_name, ConfigObj(), folder)
72
 
    
73
 
    def _add_view(self, view_name, view_config, folder):
74
 
        c_view_name = cherrypy_friendly(view_name)
75
 
        view = BranchView(self.name, c_view_name, view_name, folder, view_config, self._config)
76
 
        self._views.append(view)
77
 
        setattr(self, c_view_name, view)
78
 
        
79
 
    views = property(lambda self: self._views)
80
 
 
81
 
 
82
 
class Root (controllers.RootController):
83
 
    def __init__(self):
84
 
        self._projects = []
85
 
        for project_name in sys._loggerhead_config.sections:
86
 
            c_project_name = cherrypy_friendly(project_name)
87
 
            project = Project(c_project_name, sys._loggerhead_config[project_name])
88
 
            self._projects.append(project)
89
 
            setattr(self, c_project_name, project)
90
 
        
91
 
    @turbogears.expose(template='loggerhead.templates.browse')
92
 
    def index(self):
93
 
        return {
94
 
            'projects': self._projects,
 
25
from loggerhead.templatefunctions import templatefunctions
 
26
from loggerhead.zptsupport import load_template
 
27
 
 
28
 
 
29
class BufferingWriter(object):
 
30
 
 
31
    def __init__(self, writefunc, buf_limit):
 
32
        self.bytes = 0
 
33
        self.buf = []
 
34
        self.buflen = 0
 
35
        self.writefunc = writefunc
 
36
        self.buf_limit = buf_limit
 
37
 
 
38
    def flush(self):
 
39
        self.writefunc(''.join(self.buf))
 
40
        self.buf = []
 
41
        self.buflen = 0
 
42
 
 
43
    def write(self, data):
 
44
        self.buf.append(data)
 
45
        self.buflen += len(data)
 
46
        self.bytes += len(data)
 
47
        if self.buflen > self.buf_limit:
 
48
            self.flush()
 
49
 
 
50
 
 
51
class TemplatedBranchView(object):
 
52
 
 
53
    template_path = None
 
54
 
 
55
    def __init__(self, branch, history_callable):
 
56
        self._branch = branch
 
57
        self._history_callable = history_callable
 
58
        self.__history = None
 
59
        self.log = branch.log
 
60
 
 
61
    @property
 
62
    def _history(self):
 
63
        if self.__history is not None:
 
64
            return self.__history
 
65
        self.__history = self._history_callable()
 
66
        return self.__history
 
67
 
 
68
    def __call__(self, environ, start_response):
 
69
        z = time.time()
 
70
        kwargs = dict(parse_querystring(environ))
 
71
        util.set_context(kwargs)
 
72
        args = []
 
73
        while True:
 
74
            arg = path_info_pop(environ)
 
75
            if arg is None:
 
76
                break
 
77
            args.append(arg)
 
78
 
 
79
        path = None
 
80
        if len(args) > 1:
 
81
            path = unicode('/'.join(args[1:]), 'utf-8')
 
82
        self.args = args
 
83
 
 
84
        vals = {
 
85
            'static_url': self._branch.static_url,
 
86
            'branch': self._branch,
95
87
            'util': util,
96
 
            'title': sys._loggerhead_config.get('title', ''),
 
88
            'url': self._branch.context_url,
97
89
        }
98
 
 
99
 
    def _check_rebuild(self):
100
 
        for p in self._projects:
101
 
            for v in p.views:
102
 
                v.check_rebuild()
103
 
 
104
 
 
105
 
# singleton:
106
 
Root = Root()
107
 
 
108
 
 
109
 
# for use in profiling the very-slow get_change() method:
110
 
#h = Root.bazaar.bzr_dev.get_history()
111
 
#w = list(h.get_revision_history())
112
 
#h._get_changes_profiled(w[:100])
113
 
 
 
90
        vals.update(templatefunctions)
 
91
        headers = {}
 
92
 
 
93
        vals.update(self.get_values(path, kwargs, headers))
 
94
 
 
95
        self.log.info('Getting information for %s: %r secs' % (
 
96
            self.__class__.__name__, time.time() - z))
 
97
        if 'Content-Type' not in headers:
 
98
            headers['Content-Type'] = 'text/html'
 
99
        writer = start_response("200 OK", headers.items())
 
100
        template = load_template(self.template_path)
 
101
        z = time.time()
 
102
        w = BufferingWriter(writer, 8192)
 
103
        template.expand_into(w, **vals)
 
104
        w.flush()
 
105
        self.log.info(
 
106
            'Rendering %s: %r secs, %s bytes' % (
 
107
                self.__class__.__name__, time.time() - z, w.bytes))
 
108
        return []
 
109
 
 
110
    def get_revid(self):
 
111
        h = self._history
 
112
        if h is None:
 
113
            return None
 
114
        if len(self.args) > 0 and self.args != ['']:
 
115
            return h.fix_revid(self.args[0])
 
116
        else:
 
117
            return h.last_revid