1
"""A server that uses a loggerhead.conf file.
3
We recreate the branch discovery and url scheme of the old branchview
4
code. It's all a bit horrible really.
11
import bzrlib.lru_cache
14
from bzrlib.util.configobj.configobj import ConfigObj
16
from configobj import ConfigObj
18
from paste.request import path_info_pop
19
from paste import httpexceptions
20
from paste.wsgiwrappers import WSGIResponse
22
from loggerhead.apps.branch import BranchWSGIApp
23
from loggerhead.apps import favicon_app, static_app, robots_app
24
from loggerhead.templatefunctions import templatefunctions
25
from loggerhead.zptsupport import load_template
26
from loggerhead import util
28
log = logging.getLogger("loggerhead.controllers")
30
from loggerhead.history import is_branch
33
class Project(object):
34
"""A project contains the branches.
36
There is some complication because we don't want to hold on to the
37
branches when they are not being browsed."""
39
def __init__(self, name, config, root_config, graph_cache):
41
self.friendly_name = config.get('name', name)
42
self.description = config.get('description', '')
43
self.long_description = config.get('long_description', '')
45
self._root_config = root_config
46
self.graph_cache = graph_cache
49
self.view_data_by_name = {}
50
for view_name in config.sections:
51
log.debug('Configuring (project %s) branch %s...', name, view_name)
53
view_name, config[view_name], config[view_name].get('folder'))
55
self._auto_folder = config.get('auto_publish_folder', None)
57
if self._auto_folder is not None:
58
self._recheck_auto_folders()
60
def _recheck_auto_folders(self):
61
if self._auto_folder is None:
64
# scan a folder for bazaar branches, and add them automatically
65
for path, folders, filenames in os.walk(self._auto_folder):
66
for folder in folders:
67
folder = os.path.join(path, folder)
69
auto_list.append(folder)
71
if auto_list == self._auto_list:
72
# nothing has changed; do nothing.
77
log.debug('Rescanning auto-folder for project %s ...', self.name)
78
for folder in auto_list:
79
view_name = os.path.basename(folder)
80
log.debug('Auto-configuring (project %s) branch %s...',
83
self._add_view(view_name, ConfigObj(), folder)
84
self._auto_list = auto_list
86
def _get_branch_url(self, view, view_config, folder):
87
url = view_config.get('url', None)
90
url = self._config.get('url_prefix', None)
92
return posixpath.join(url, folder) + '/'
95
def _get_description(self, view, view_config, history):
96
description = view_config.get('description', None)
97
if description is not None:
99
description = history._branch.get_config().get_user_option(
103
def _add_view(self, view_name, view_config, folder):
104
b = bzrlib.branch.Branch.open(folder)
105
view = BranchWSGIApp(b, view_name, view_config, self.graph_cache)
108
history = view.get_history()
109
friendly_name = view_config.get('branch_name', None)
110
if friendly_name is None:
111
friendly_name = history.get_config().get_nickname()
112
if friendly_name is None:
113
friendly_name = view_name
114
branch_url = self._get_branch_url(view, view_config, view_name)
115
description = self._get_description(view, view_config, history)
116
self.view_data_by_name[view_name] = {
117
'branch_path': folder,
118
'config': view_config,
119
'description': description,
120
'friendly_name': friendly_name,
121
'graph_cache': self.graph_cache,
123
'served_url': branch_url,
125
self.view_names.append(view_name)
129
def view_named(self, name):
130
view_data = self.view_data_by_name.get(name)
131
if view_data is None:
133
view_data = view_data.copy()
134
branch_path = view_data.pop('branch_path')
135
description = view_data.pop('description')
136
name = view_data.pop('name')
137
b = bzrlib.branch.Branch.open(branch_path)
139
view = BranchWSGIApp(b, **view_data)
140
view.description = description
144
def call(self, environ, start_response):
145
segment = path_info_pop(environ)
147
raise httpexceptions.HTTPNotFound()
149
view = self.view_named(segment)
151
raise httpexceptions.HTTPNotFound()
153
return view.app(environ, start_response)
159
"""The root of the server -- renders as the browse view,
160
dispatches to Project above for each 'project'."""
162
def __init__(self, config):
165
self.projects_by_name = {}
166
graph_cache = bzrlib.lru_cache.LRUCache()
167
for project_name in self.config.sections:
168
project = Project(project_name, self.config[project_name],
169
self.config, graph_cache)
170
self.projects.append(project)
171
self.projects_by_name[project_name] = project
173
def browse(self, response):
174
# This is insanely complicated because we want to open and
175
# lock all the branches, render the view and then unlock the
177
for p in self.projects:
178
p._recheck_auto_folders()
180
class branch(object):
183
def static_url(path):
184
return self._static_url_base + path
185
views_by_project = {}
188
for p in self.projects:
189
views_by_project[p] = []
190
for vn in p.view_names:
193
views_by_project[p].append(v)
195
'projects': self.projects,
197
'title': self.config.get('title', None),
199
'views_by_project': views_by_project,
201
vals.update(templatefunctions)
202
response.headers['Content-Type'] = 'text/html'
203
template = load_template('loggerhead.templates.browse')
204
template.expand_into(response, **vals)
209
def __call__(self, environ, start_response):
210
self._static_url_base = environ['loggerhead.static.url'] = \
211
environ['SCRIPT_NAME']
212
segment = path_info_pop(environ)
214
raise httpexceptions.HTTPMovedPermanently.relative_redirect(
215
environ['SCRIPT_NAME'] + '/', environ)
217
response = WSGIResponse()
218
self.browse(response)
219
return response(environ, start_response)
220
elif segment == 'robots.txt':
221
return robots_app(environ, start_response)
222
elif segment == 'static':
223
return static_app(environ, start_response)
224
elif segment == 'favicon.ico':
225
return favicon_app(environ, start_response)
227
project = self.projects_by_name.get(segment)
229
raise httpexceptions.HTTPNotFound()
230
return project.call(environ, start_response)