--- a
+++ b/Allura/allura/lib/repository.py
@@ -0,0 +1,181 @@
+import logging
+import shutil
+from urllib import quote
+
+from pylons import c, g
+from tg import expose, redirect, url
+from tg.decorators import with_trailing_slash, without_trailing_slash
+from pymongo.bson import ObjectId
+
+from allura import version
+from allura.lib import helpers as h
+from allura import model as M
+from allura.lib import security
+from allura.lib.decorators import audit
+from allura.app import Application, SitemapEntry, DefaultAdminController, ConfigOption
+
+log = logging.getLogger(__name__)
+
+
+class RepositoryApp(Application):
+ END_OF_REF_ESCAPE='~'
+ __version__ = version.__version__
+ permissions = [ 'read', 'write', 'create', 'admin', 'configure' ]
+ config_options = Application.config_options + [
+ ConfigOption('cloned_from_project_id', ObjectId, None),
+ ConfigOption('cloned_from_repo_id', ObjectId, None)
+ ]
+ tool_label='Repository'
+ default_mount_label='Source'
+ default_mount_point='src'
+ ordinal=2
+ forkable=False
+ default_branch_name=None # master or default or some such
+ repo=None # override with a property in child class
+
+ def __init__(self, project, config):
+ Application.__init__(self, project, config)
+ self.admin = RepoAdminController(self)
+
+ @property
+ @h.exceptionless([], log)
+ def sitemap(self):
+ menu_id = self.config.options.mount_label.title()
+ with h.push_config(c, app=self):
+ return [
+ SitemapEntry(menu_id, '.')[self.sidebar_menu()] ]
+
+ def admin_menu(self):
+ admin_url = c.project.url()+'admin/'+self.config.options.mount_point+'/'
+ links = [SitemapEntry('Viewable Files', admin_url + 'extensions', className='nav_child')]
+ # if self.permissions and has_artifact_access('configure', app=self)():
+ # links.append(SitemapEntry('Permissions', admin_url + 'permissions', className='nav_child'))
+ return links
+
+ @h.exceptionless([], log)
+ def sidebar_menu(self):
+ if not self.repo or self.repo.status != 'ready':
+ return [
+ SitemapEntry(self.repo.status) ]
+ if self.default_branch_name:
+ default_branch_url = (
+ c.app.url
+ + url(quote(self.default_branch_name + self.END_OF_REF_ESCAPE))
+ + '/')
+ else:
+ default_branch_url = c.app.url
+ links = [
+ SitemapEntry('Browse', default_branch_url,ui_icon='folder-collapsed') ]
+ if c.app.repo.heads:
+ links.append(
+ SitemapEntry(
+ 'History', default_branch_url+'log/',
+ ui_icon='document-b', small=c.app.repo.heads[0].count))
+ if self.forkable and self.repo.status == 'ready':
+ links.append(SitemapEntry('Fork', c.app.url + 'fork', ui_icon='fork'))
+ if security.has_artifact_access('admin', app=c.app)():
+ links.append(SitemapEntry('Admin',
+ c.project.url()+'admin/'+self.config.options.mount_point,
+ ui_icon='tool-admin'))
+ if self.repo.upstream_repo.name:
+ links += [
+ SitemapEntry('Clone of'),
+ SitemapEntry(self.repo.upstream_repo.name, self.repo.upstream_repo.url,
+ className='nav_child'),
+ SitemapEntry('Request Merge', c.app.url + 'merge_request',
+ ui_icon='merge',
+ className='nav_child')
+ ]
+ if self.repo.branches:
+ links.append(SitemapEntry('Branches'))
+ for b in self.repo.branches:
+ links.append(SitemapEntry(
+ b.name, url(c.app.url, dict(branch='ref/' + b.name)),
+ className='nav_child',
+ small=b.count))
+ if self.repo.repo_tags:
+ links.append(SitemapEntry('Tags'))
+ for b in self.repo.repo_tags:
+ links.append(SitemapEntry(
+ b.name, url(c.app.url, dict(branch='ref/' + b.name)),
+ className='nav_child',
+ small=b.count))
+ return links
+
+ def install(self, project):
+ self.config.options['project_name'] = project.name
+ super(RepositoryApp, self).install(project)
+ role_developer = M.ProjectRole.query.get(name='Developer')._id
+ self.config.acl.update(
+ configure=c.project.acl['tool'],
+ read=c.project.acl['read'],
+ create=[role_developer],
+ write=[role_developer],
+ admin=c.project.acl['tool'])
+
+ def uninstall(self, project):
+ g.publish('audit', 'repo.uninstall', dict(project_id=project._id))
+
+ @classmethod
+ @audit('repo.init')
+ def _init(cls, routing_key, data):
+ c.app.repo.init()
+ M.Notification.post_user(
+ c.user, c.app.repo, 'created',
+ text='Repository %s/%s created' % (
+ c.project.shortname, c.app.config.options.mount_point))
+
+ @classmethod
+ @audit('repo.clone')
+ def _clone(cls, routing_key, data):
+ if not c.app.forkable:
+ return cls._init(cls, routing_key, data)
+ c.app.repo.init_as_clone(
+ data['cloned_from_path'],
+ data['cloned_from_name'],
+ data['cloned_from_url'])
+ M.Notification.post_user(
+ c.user, c.app.repo, 'created',
+ text='Repository %s/%s created' % (
+ c.project.short_name, c.app.config.options.mount_point))
+
+ @classmethod
+ @audit('repo.refresh')
+ def _refresh(cls, routing_key, data):
+ c.app.repo.refresh()
+
+ @classmethod
+ @audit('repo.uninstall')
+ def _uninstall(cls, routing_key, data):
+ "Remove all the tool's artifacts and the physical repository"
+ repo = c.app.repo
+ if repo is not None:
+ shutil.rmtree(repo.full_fs_path, ignore_errors=True)
+ repo.delete()
+ super(RepositoryApp, c.app).uninstall(project=c.project)
+
+class RepoAdminController(DefaultAdminController):
+
+ def __init__(self, app):
+ self.app = app
+ self.repo = app.repo
+
+ def _check_security(self):
+ security.require(security.has_artifact_access('configure', app=self.app))
+
+ @with_trailing_slash
+ @expose()
+ def index(self, **kw):
+ redirect('extensions')
+
+ @without_trailing_slash
+ @expose('jinja:repo/admin_extensions.html')
+ def extensions(self, **kw):
+ return dict(app=self.app,
+ allow_config=True,
+ additional_viewable_extensions=getattr(self.repo, 'additional_viewable_extensions', ''))
+
+ @without_trailing_slash
+ @expose()
+ def set_extensions(self, **post_data):
+ self.repo.additional_viewable_extensions = post_data['additional_viewable_extensions']