"""When you launch the `PubliForge` application, this module determines which
mode you are running: front, agent or both. According to this mode, it sets up
database, creates a instance of :class:`~.lib.build.front.FrontBuildManager`
class, an instance of :class:`~.lib.handler.HandlerManager`, an instance of
:class:`~.lib.build.agent.AgentBuildManager` class and makes required
directories.
"""
import mimetypes
from logging import getLogger
from os import makedirs
from os.path import exists, join, dirname, splitext
from ConfigParser import ConfigParser
from beaker.cache import CacheManager
from beaker.util import parse_cache_config_options
from sqlalchemy import engine_from_config
from sqlalchemy.exc import OperationalError, ProgrammingError
from pyramid_beaker import session_factory_from_settings
from pyramid.security import Allow, Authenticated
from pyramid.security import ALL_PERMISSIONS, NO_PERMISSION_REQUIRED
from pyramid.threadlocal import get_current_request
from pyramid.renderers import get_renderer
from pyramid.events import NewRequest, BeforeRender
from .lib.i18n import _
from .lib.config import settings_get_list
from .lib.utils import camel_case
from .lib.log import log_activity, log_activity_setup
from .lib.viewutils import connect_user
from .lib.breadcrumbs import Breadcrumbs
from .lib.menu import Menu
from .lib.form import Form
from .lib.handler import HandlerManager
from .lib.build.front import FrontBuildManager
from .lib.build.agent import AgentBuildManager
from .lib.opener import OpenerManager
from .views.selection import Selection
from .models import DBSession, Base
from .models.processors import Processor
LOG = getLogger(__name__)
# =============================================================================
[docs]class Initialize(object):
"""Initialization application class."""
# -------------------------------------------------------------------------
def __init__(self, global_config, configurator):
"""Constructor method."""
settings = configurator.registry.settings
self.configurator = configurator
self.is_front = bool(settings.get('storage.root'))
self.is_agent = bool(settings.get('build.root'))
self.global_config = global_config
self._config = ConfigParser({'here': global_config['here']})
self._config.read(global_config['__file__'])
mimetypes.init((
join(dirname(__file__), 'Static', 'Images', 'MimeTypes',
'mime.types'),))
# -------------------------------------------------------------------------
[docs] def all(self):
"""Check settings and create registry objects."""
# Check general settings
settings = self.configurator.registry.settings
if not settings.get('uid'):
exit('*** Must define an "uid".')
if not self.is_front and not self.is_agent:
exit('*** Must be at least a web site ("storage.root" needed) '
'or an agent ("build.root" needed).')
# Translations
self.configurator.add_translation_dirs(
*(settings_get_list(settings, 'translation_dirs') +
['publiforge:Locale', 'colander:locale']))
# XML-RPC
self.configurator.include('pyramid_rpc.xmlrpc')
self.configurator.add_xmlrpc_endpoint('xmlrpc', '/xmlrpc')
# Log
self.configurator.registry['log_activity'] = \
log_activity_setup(self.global_config)
# Initialize agent
if self.is_agent:
self.configurator.registry['abuild'] = AgentBuildManager(settings)
if not self.configurator.registry['abuild'].processor_list():
exit('*** Should have defined at least one processor.')
fronts = self.configurator.registry['abuild'].front_list()
if not fronts:
exit('*** Must have at least one authorized front.')
if not settings.get('buildspace.root') \
and (len(fronts) > 1 or fronts[0] != settings.get('uid')):
exit('*** Must define a directory for buildspaces.')
self.configurator.include(add_routes_agent)
# Initialize front
if self.is_front:
if not settings.get('temporary_dir'):
exit('*** Must define a temporary directory.')
if not exists(settings['temporary_dir']):
makedirs(settings['temporary_dir'])
cache_manager = CacheManager(
**parse_cache_config_options(settings))
self.configurator.registry['opener'] = OpenerManager(settings)
self.configurator.registry['handler'] = HandlerManager(
settings, cache_manager, self.configurator.registry['opener'])
if not self.configurator.registry['handler'].vcs_list():
exit('*** Should have defined at least one VCS.')
self.configurator.registry['fbuild'] = FrontBuildManager(settings)
if not self.configurator.registry['fbuild'].agent_urls():
exit('*** Should have at least one available agent.')
settings.update(
{'session.secret': settings.get('encryption', '-')})
self.configurator.set_session_factory(
session_factory_from_settings(settings))
self.configurator.add_subscriber(new_request, NewRequest)
self.configurator.add_subscriber(before_render, BeforeRender)
self.configurator.set_default_permission('view')
self.configurator.include(add_routes_front)
self.configurator.scan()
self._skin(settings)
self.configurator.registry['opener'].add_static_views(
self.configurator)
self._initialize_sql(settings)
return self.is_front, self.is_agent
# -------------------------------------------------------------------------
def _skin(self, settings):
"""Set up skin."""
if settings.get('skin.static.name'):
if not settings.get('skin.static.path'):
exit('*** Must define a static path.')
self.configurator.add_static_view(
settings['skin.static.name'], settings['skin.static.path'],
cache_max_age=3600)
if settings.get('skin.static.name') != 'Static':
self.configurator.add_static_view(
'Static', join(dirname(__file__), 'Static'),
cache_max_age=3600)
# -------------------------------------------------------------------------
@classmethod
def _initialize_sql(cls, settings):
"""Database initialization."""
# Initialize SqlAlchemy session
try:
dbengine = engine_from_config(settings, 'sqlalchemy.', echo=False)
except KeyError:
exit('*** Database is not defined.')
DBSession.configure(bind=dbengine)
Base.metadata.bind = dbengine
# Check database and empty processor table
try:
DBSession.query(Processor).delete()
DBSession.commit()
except (ProgrammingError, OperationalError):
exit('*** Run "pfpopulate" script!')
DBSession.close()
# =============================================================================
[docs]def perm_group_finder(login, request):
"""Return permission groups of current user.
:param login: (string)
User login.
:param request: (:class:`pyramid.request.Request` instance)
Current request.
"""
# Already authenticated
if login == request.session.get('login'):
return ['group:%s' % perm for perm in request.session['perms']]
# Maintenance
if request.registry.settings.get('maintenance') == 'true':
return []
# Auto login
# pylint: disable = E1103
user = connect_user(request, login)
if not isinstance(user, int):
user.setup_environment(request)
log_activity(request, 'autologin')
return ['group:%s' % perm for perm in request.session['perms']]
return None
# =============================================================================
[docs]class RootFactory(object):
"""Return the traversal root of the application.
Its main role is to define Access Control List (ACL). See
:ref:`frontreference_permissions` for a complete description of
permissions.
"""
# pylint: disable = R0903
__acl__ = [
(Allow, Authenticated, 'view'),
(Allow, 'group:admin', ALL_PERMISSIONS),
(Allow, 'group:doc_editor', 'doc.update'),
(Allow, 'group:doc_manager', ('doc.update', 'doc.create')),
(Allow, 'group:usr_user', 'usr.read'),
(Allow, 'group:usr_editor', ('usr.read', 'usr.update')),
(Allow, 'group:usr_manager', ('usr.read', 'usr.update', 'usr.create')),
(Allow, 'group:grp_user', 'grp.read'),
(Allow, 'group:grp_editor', ('grp.read', 'grp.update')),
(Allow, 'group:grp_manager', ('grp.read', 'grp.update', 'grp.create')),
(Allow, 'group:stg_user', 'stg.read'),
(Allow, 'group:stg_editor', ('stg.read', 'stg.update')),
(Allow, 'group:stg_manager', ('stg.read', 'stg.update', 'stg.create')),
(Allow, 'group:idx_user', 'idx.read'),
(Allow, 'group:idx_editor', ('idx.read', 'idx.update')),
(Allow, 'group:idx_manager', ('idx.read', 'idx.update', 'idx.create')),
(Allow, 'group:prj_user', 'prj.read'),
(Allow, 'group:prj_editor', ('prj.read', 'prj.update')),
(Allow, 'group:prj_manager', ('prj.read', 'prj.update', 'prj.create'))]
# -------------------------------------------------------------------------
def __init__(self, request):
"""Constructor method."""
pass
# =============================================================================
[docs]def new_request(event):
"""A subscriber for :class:`pyramid.events.NewRequest` events."""
request = event.request
if 'lang' not in request.session:
request.session['lang'] = request.accept_language.best_match(
settings_get_list(request.registry.settings, 'languages'),
request.registry.settings.get('pyramid.default_locale_name'))
request.locale_name = request.session['lang']
if not request.is_xhr:
request.breadcrumbs = Breadcrumbs(request)
# =============================================================================
[docs]def before_render(event):
"""A subscriber for :class:`pyramid.events.BeforeRender` events."""
request = event.get('request') or get_current_request()
if request.is_xhr:
return
def translate(text, domain='publiforge', **kwargs):
"""Translation from a string."""
return request.localizer.translate(_(text, domain, **kwargs))
event['base'] = get_renderer(
request.registry.settings.get('skin.template.base') or
'Templates/base.pt').implementation()
event['title'] = request.registry.settings.get(
'skin.title', 'PubliForge').decode('utf8')
event['page_id'] = camel_case(
splitext(event.get('renderer_name', '').split('/')[-1:][0])[0])
event['_'] = translate
event['route'] = lambda name, *elts, **kwargs: \
request.route_path(name, *elts, **kwargs).decode('utf8')
event['form'] = Form(request)
event['selection'] = Selection(request)
event['menu'] = Menu(request)
# =============================================================================
[docs]def add_routes_front(config):
"""Add Web front routes."""
# pylint: disable = R0915
# Security
config.add_route('login', '/login')
config.add_route('login_test', '/login_test')
config.add_route('logout', '/logout')
config.add_view(
'.views.security.login', route_name='login',
renderer=config.registry.settings.get('skin.template.login') or
'Templates/login.pt', permission=NO_PERMISSION_REQUIRED)
config.add_route('maintenance', '/maintenance')
# Home
config.add_route('home', '/')
config.add_route('favicon', '/favicon.ico')
config.add_route('robots', '/robots.txt')
# Site
config.add_route('site_admin', '/site/admin')
# User
config.add_route('user_account', '/user/account')
config.add_route('user_admin', '/user/admin')
config.add_route('user_create', '/user/create')
config.add_route('user_view', '/user/view/{user_id}')
config.add_route('user_edit', '/user/edit/{user_id}')
# Group
config.add_route('group_admin', '/group/admin')
config.add_route('group_create', '/group/create')
config.add_route('group_view', '/group/view/{group_id}')
config.add_route('group_edit', '/group/edit/{group_id}')
# Storage
config.add_route('storage_admin', '/storage/admin')
config.add_route('storage_index', '/storage/index')
config.add_route('storage_create', '/storage/create')
config.add_route('storage_view', '/storage/view/{storage_id}')
config.add_route('storage_edit', '/storage/edit/{storage_id}')
config.add_route('storage_root', '/storage/browse/{storage_id}')
config.add_route('storage_browse', '/storage/browse/{storage_id}/*path')
# File
config.add_route('file_search', '/file/search')
config.add_route('file_read', '/file/read/{storage_id}/*path')
config.add_route('file_render', '/file/render/{storage_id}/*path')
config.add_route('file_write', '/file/write/{storage_id}/*path')
config.add_route('file_edit', '/file/edit/{storage_id}/*path')
config.add_route('file_autocheck', '/file/autocheck/{storage_id}/*path')
config.add_route('file_autosave', '/file/autosave/{storage_id}/*path')
config.add_route('file_media', '/file/media/{storage_id}/*path')
config.add_route(
'file_resources_dirs', '/file/resources/dirs/{storage_id}/*path')
config.add_route(
'file_resources_files', '/file/resources/files/{storage_id}/*path')
config.add_route(
'file_navigate', '/file/go_{direction}/{storage_id}/*path')
config.add_route(
'file_navigate_search', '/file/search_{direction}/{storage_id}/*path')
config.add_route('file_info', '/file/info/{storage_id}/*path')
config.add_route('file_download', '/file/download/{storage_id}/*path')
config.add_route(
'file_revision', '/file/revision/{revision}/{storage_id}/*path')
config.add_route('file_diff', '/file/diff/{revision}/{storage_id}/*path')
# Selection
config.add_route('selection_rm_storage', '/selection/remove/{storage_id}')
config.add_route(
'selection_rm_file', '/selection/remove/{storage_id}/*path')
config.add_route('selection_overview', '/selection/overview/{storage_id}')
config.add_route('selection_add', '/selection/add/*path')
# Indexer
config.add_route('indexer_admin', '/indexer/admin')
config.add_route('indexer_create', '/indexer/create')
config.add_route('indexer_view', '/indexer/view/{indexer_id}')
config.add_route('indexer_edit', '/indexer/edit/{indexer_id}')
# Project
config.add_route('project_admin', '/project/admin')
config.add_route('project_index', '/project/index')
config.add_route('project_create', '/project/create')
config.add_route('project_view', '/project/view/{project_id}')
config.add_route('project_edit', '/project/edit/{project_id}')
config.add_route('project_dashboard', '/project/dashboard/{project_id}')
# Role
config.add_route('role_create', '/role/create/{project_id}')
config.add_route('role_view', '/role/view/{project_id}-{role_id}')
config.add_route('role_edit', '/role/edit/{project_id}-{role_id}')
# Processing
config.add_route('processing_create', '/processing/create/{project_id}')
config.add_route(
'processing_view', '/processing/view/{project_id}-{processing_id}')
config.add_route(
'processing_edit', '/processing/edit/{project_id}-{processing_id}')
# Task
config.add_route(
'task_index_task_pack', '/task/index/{project_id}/{task_id}-{pack_id}')
config.add_route('task_index_task', '/task/index/{project_id}/{task_id}')
config.add_route('task_index', '/task/index/{project_id}')
config.add_route('task_create', '/task/create/{project_id}')
config.add_route('task_view', '/task/view/{project_id}-{task_id}')
config.add_route('task_edit', '/task/edit/{project_id}-{task_id}')
# Job
config.add_route('job_index', '/job/index/{project_id}')
config.add_route('job_log', '/job/log/{project_id}/{job_id}')
config.add_route('job_create', '/job/create/{project_id}')
config.add_route('job_view', '/job/view/{project_id}/{job_id}')
config.add_route('job_edit', '/job/edit/{project_id}/{job_id}')
# Pack
config.add_route('pack_index', '/pack/index/{project_id}')
config.add_route('pack_index_pack', '/pack/index/{project_id}/{pack_id}')
config.add_route('pack_create', '/pack/create/{project_id}')
config.add_route('pack_view', '/pack/view/{project_id}-{pack_id}')
config.add_route('pack_edit', '/pack/edit/{project_id}-{pack_id}')
config.add_route('pack_note', '/pack/note/{project_id}-{pack_id}')
# Build
config.add_route(
'build_launch',
'/build/launch/{project_id}-{processing_id}-{pack_ids}')
config.add_route('build_view', '/build/view/{build_id}')
config.add_route('build_progress', '/build/progress/{build_id}')
config.add_route('build_complete', '/build/complete/{build_id}/{key}')
config.add_route('build_info', '/build/info/{build_id}')
config.add_route('build_log', '/build/log/{build_id}')
config.add_route('build_results', '/build/results/{project_id}')
# XML-RPC
config.add_xmlrpc_method(
'.views.maestro.storages', endpoint='xmlrpc',
method='maestro_storages', permission=NO_PERMISSION_REQUIRED)
config.add_xmlrpc_method(
'.views.maestro.indexes', endpoint='xmlrpc',
method='maestro_indexes', permission=NO_PERMISSION_REQUIRED)
config.add_xmlrpc_method(
'.views.maestro.search', endpoint='xmlrpc',
method='maestro_search', permission=NO_PERMISSION_REQUIRED)
config.add_xmlrpc_method(
'.views.maestro.file_info', endpoint='xmlrpc',
method='maestro_file_info', permission=NO_PERMISSION_REQUIRED)
config.add_xmlrpc_method(
'.views.maestro.file_download', endpoint='xmlrpc',
method='maestro_file_download', permission=NO_PERMISSION_REQUIRED)
config.add_xmlrpc_method(
'.views.maestro.pack_info', endpoint='xmlrpc',
method='maestro_pack_info', permission=NO_PERMISSION_REQUIRED)
config.add_xmlrpc_method(
'.views.maestro.pack_upload', endpoint='xmlrpc',
method='maestro_pack_upload', permission=NO_PERMISSION_REQUIRED)
config.add_xmlrpc_method(
'.views.maestro.build', endpoint='xmlrpc',
method='maestro_build', permission=NO_PERMISSION_REQUIRED)
config.add_xmlrpc_method(
'.views.maestro.build_log', endpoint='xmlrpc',
method='maestro_build_log', permission=NO_PERMISSION_REQUIRED)
config.add_xmlrpc_method(
'.views.maestro.results', endpoint='xmlrpc',
method='maestro_results', permission=NO_PERMISSION_REQUIRED)
config.add_xmlrpc_method(
'.views.maestro.result_download', endpoint='xmlrpc',
method='maestro_result_download', permission=NO_PERMISSION_REQUIRED)
# =============================================================================
[docs]def add_routes_agent(config):
"""Add agent routes and methods."""
config.add_xmlrpc_method(
'.views.xmlrpc.agent_id', endpoint='xmlrpc',
method='agent_id', permission=NO_PERMISSION_REQUIRED)
config.add_xmlrpc_method(
'.views.xmlrpc.processor_list', endpoint='xmlrpc',
method='processor_list', permission=NO_PERMISSION_REQUIRED)
config.add_xmlrpc_method(
'.views.xmlrpc.processor_xml', endpoint='xmlrpc',
method='processor_xml', permission=NO_PERMISSION_REQUIRED)
config.add_xmlrpc_method(
'.views.xmlrpc.activity', endpoint='xmlrpc',
method='activity', permission=NO_PERMISSION_REQUIRED)
config.add_xmlrpc_method(
'.views.xmlrpc.synchronizing', endpoint='xmlrpc',
method='synchronizing', permission=NO_PERMISSION_REQUIRED)
config.add_xmlrpc_method(
'.views.xmlrpc.start', endpoint='xmlrpc',
method='start', permission=NO_PERMISSION_REQUIRED)
config.add_xmlrpc_method(
'.views.xmlrpc.progress', endpoint='xmlrpc',
method='progress', permission=NO_PERMISSION_REQUIRED)
config.add_xmlrpc_method(
'.views.xmlrpc.stop', endpoint='xmlrpc',
method='stop', permission=NO_PERMISSION_REQUIRED)
config.add_xmlrpc_method(
'.views.xmlrpc.result', endpoint='xmlrpc',
method='result', permission=NO_PERMISSION_REQUIRED)
config.add_xmlrpc_method(
'.views.xmlrpc.buildspace_cleanup', endpoint='xmlrpc',
method='buildspace_cleanup', permission=NO_PERMISSION_REQUIRED)
config.add_xmlrpc_method(
'.views.xmlrpc.buildspace_send_signature', endpoint='xmlrpc',
method='buildspace_send_signature', permission=NO_PERMISSION_REQUIRED)
config.add_xmlrpc_method(
'.views.xmlrpc.buildspace_receive_delta', endpoint='xmlrpc',
method='buildspace_receive_delta', permission=NO_PERMISSION_REQUIRED)
config.add_xmlrpc_method(
'.views.xmlrpc.output_list', endpoint='xmlrpc',
method='output_list', permission=NO_PERMISSION_REQUIRED)
config.add_xmlrpc_method(
'.views.xmlrpc.output_send_delta', endpoint='xmlrpc',
method='output_send_delta', permission=NO_PERMISSION_REQUIRED)