def register_ew_resources(manager): manager.register_directory( 'js', pkg_resources.resource_filename('allura', 'lib/widgets/resources/js')) manager.register_directory( 'css', pkg_resources.resource_filename('allura', 'lib/widgets/resources/css')) manager.register_directory( 'allura', pkg_resources.resource_filename('allura', 'public/nf')) for ep in iter_entry_points('allura'): try: app = ep.load() resource_path = os.path.join('nf', ep.name.lower()) resource_cls = app.has_resource(resource_path) if resource_cls: manager.register_directory( 'tool/%s' % ep.name.lower(), pkg_resources.resource_filename( resource_cls.__module__, resource_path)) except ImportError: log.warning('Cannot import entry point %s', ep) raise for ep in iter_entry_points('allura.theme'): try: theme = ep.load() theme.register_ew_resources(manager, ep.name) except ImportError: log.warning('Cannot import entry point %s', ep) raise
def profile_sections(self): """ Loads and caches user profile sections from the entry-point group ``[allura.user_profile.sections]``. Profile sections are loaded unless disabled (see `allura.lib.helpers.iter_entry_points`) and are sorted according to the `user_profile_sections.order` config value. The config should contain a comma-separated list of entry point names in the order that they should appear in the profile. Unknown or disabled sections are ignored, and available sections that are not specified in the order come after all explicitly ordered sections, sorted randomly. """ if hasattr(UserProfileApp, '_sections'): return UserProfileApp._sections sections = {} for ep in h.iter_entry_points('allura.user_profile.sections'): sections[ep.name] = ep.load() section_ordering = tg.config.get('user_profile_sections.order', '') ordered_sections = [] for section in re.split(r'\s*,\s*', section_ordering): if section in sections: ordered_sections.append(sections.pop(section)) UserProfileApp._sections = ordered_sections + sections.values() return UserProfileApp._sections
def entry_point_timers(cls, module_prefix=None): timers = [] for ep in h.iter_entry_points('allura.timers'): if not module_prefix or ep.module_name.startswith(module_prefix): func = ep.load() timers += aslist(func()) return timers
def basic_setup(self): global log, M if self.args[0]: # Probably being called from the command line - load the config # file self.config = conf = appconfig('config:%s' % self.args[0], relative_to=os.getcwd()) # ... logging does not understand section#subsection syntax logging_config = self.args[0].split('#')[0] logging.config.fileConfig( logging_config, disable_existing_loggers=False) log = logging.getLogger('allura.command') log.info('Initialize command with config %r', self.args[0]) load_environment(conf.global_conf, conf.local_conf) self.setup_globals() from allura import model M = model ming.configure(**conf) if asbool(conf.get('activitystream.recording.enabled', False)): activitystream.configure(**h.convert_bools(conf, prefix='activitystream.')) pylons.tmpl_context.user = M.User.anonymous() else: # Probably being called from another script (websetup, perhaps?) log = logging.getLogger('allura.command') conf = pylons.config self.tools = pylons.app_globals.entry_points['tool'].values() for ep in h.iter_entry_points('allura.command_init'): log.info('Running command_init for %s', ep.name) ep.load()(conf) log.info('Loaded tools')
def clone(self, repo_type=None, source_url=None, mount_point=None, mount_label=None, **kw): require_access(c.project, 'admin') if repo_type is None: return ('<form method="get">' '<input name="repo_type" value="Git">' '<input name="source_url">' '<input type="submit">' '</form>') for ep in h.iter_entry_points('allura', repo_type): break if ep is None or source_url is None: raise exc.HTTPNotFound h.log_action(log, 'install tool').info('clone repo from %s', source_url, meta=dict(tool_type=repo_type, mount_point=mount_point, mount_label=mount_label)) c.project.install_app(repo_type, mount_point=mount_point, mount_label=mount_label, init_from_url=source_url) M.AuditLog.log('Create repo as clone') redirect('tools')
def clone(self, repo_type=None, source_url=None, mount_point=None, mount_label=None, **kw): require_access(c.project, 'admin') if repo_type is None: return ( '<form method="get">' '<input name="repo_type" value="Git">' '<input name="source_url">' '<input type="submit">' '</form>') for ep in h.iter_entry_points('allura', repo_type): break if ep is None or source_url is None: raise exc.HTTPNotFound h.log_action(log, 'install tool').info( 'clone repo from %s', source_url, meta=dict(tool_type=repo_type, mount_point=mount_point, mount_label=mount_label)) c.project.install_app( repo_type, mount_point=mount_point, mount_label=mount_label, init_from_url=source_url) M.AuditLog.log('Create repo as clone') redirect('tools')
def command(self): from allura import model as M # self.basic_setup() existing_thumbs = 0 base.log.info('Collecting application attachment classes') package_model_map = {} for m in Mapper.all_mappers(): sess = m.session cls = m.mapped_class if issubclass(cls, M.BaseAttachment): if sess is M.project_orm_session: package = cls.__module__.split('.', 1)[0] l = package_model_map.get(package, []) l.append(cls) package_model_map[package] = l if len(self.args) > 1: projects = M.Project.query.find({'shortname': self.args[1]}) else: projects = M.Project.query.find() for p in projects: base.log.info('=' * 20) base.log.info("Processing project '%s'", p.shortname) c.project = p if self.options.force: existing_thumbs += M.BaseAttachment.query.find({'type': 'thumbnail'}).count() base.log.info('Removing %d current thumbnails (per --force)', existing_thumbs) M.BaseAttachment.query.remove({'type': 'thumbnail'}) # ProjectFile's live in main collection (unlike File's) # M.ProjectFile.query.find({'app_config_id': None, 'type': 'attachment'}).all() for app in p.app_configs: base.log.info("Processing application '%s' mounted at '%s' of type '%s'", app.options['mount_label'], app.options['mount_point'], app.tool_name) # Any application may contain DiscussionAttachment's, it has discussion_id field self.process_att_of_type(M.DiscussionAttachment, {'app_config_id': app._id, 'discussion_id': {'$ne': None}}) # Otherwise, we'll take attachment classes belonging to app's package ep = iter_entry_points('allura', app.tool_name).next() app_package = ep.module_name.split('.', 1)[0] if app_package == 'allura': # Apps in allura known to not define own attachment types continue classes = package_model_map.get(app_package, []) for cls in classes: self.process_att_of_type(cls, {'app_config_id': app._id, 'discussion_id': None}) base.log.info('-' * 10) base.log.info('Recreated %d thumbs', self.created_thumbs) if self.options.force: if existing_thumbs != self.created_thumbs: base.log.warning('There were %d thumbs before --force operation started, but %d recreated', existing_thumbs, self.created_thumbs) ThreadLocalORMSession.flush_all()
def __init__(self): super(AdminFormResponsive, self).__init__() # use alternate template if responsive overrides are on, but not actually using template override for this # since that would change all forms, and we want to just do individual ones right now for tmpl_override_ep in h.iter_entry_points('allura.theme.override'): if tmpl_override_ep.name == 'responsive': self.template = 'jinja:allura:templates_responsive/widgets/admin_form.html' break
def _cache_eps(section_name, dict_cls=dict): d = dict_cls() for ep in h.iter_entry_points(section_name): try: value = ep.load() except Exception: log.exception('Could not load entry point [%s] %s', section_name, ep) else: d[ep.name] = value return d
def _load_paths(self): """ Load all the paths to be processed, including defaults, in the default order. """ paths = self.default_paths[:] # copy default_paths paths[-1:0] = [ # insert all eps just before last item, by default [ep.name, pkg_resources.resource_filename(ep.module_name, "")] for ep in iter_entry_points(self.override_entrypoint) ] return paths
def by_app(app): """ Return a ToolImporter subclass instance given its target_app class. """ importers = {} for ep in h.iter_entry_points('allura.importers'): importer = ep.load() if app in aslist(importer.target_app): importers[ep.name] = importer() return importers
def load_sections(app): sections = {} for ep in h.iter_entry_points('allura.%s.sections' % app): sections[ep.name] = ep.load() section_ordering = tg.config.get('%s_sections.order' % app, '') ordered_sections = [] for section in re.split(r'\s*,\s*', section_ordering): if section in sections: ordered_sections.append(sections.pop(section)) sections = ordered_sections + sections.values() return sections
def tool_importers(self): """ List of all tool importers that import from the same source as this project importer. """ tools = {} for ep in h.iter_entry_points('allura.importers'): epv = ep.load() if epv.source == self.source: tools[ep.name] = epv() return tools
def test_subclassed_ep(self, pkg_resources): class App(object): pass class BetterApp(App): pass pkg_resources.iter_entry_points.return_value = [self._make_ep("myapp", App), self._make_ep("myapp", BetterApp)] eps = list(h.iter_entry_points("allura")) self.assertEqual(len(eps), 1) self.assertEqual(BetterApp, eps[0].load())
def index(self, *a, **kw): importer_matrix = defaultdict(dict) tools_with_importers = set() hidden = set(aslist(config.get('hidden_importers'), sep=',')) visible = lambda ep: ep.name not in hidden for ep in filter(visible, h.iter_entry_points('allura.importers')): importer = ep.load() for tool in aslist(importer.target_app): tools_with_importers.add(tool.tool_label) importer_matrix[importer.source][tool.tool_label] = ep.name return { 'importer_matrix': importer_matrix, 'tools': tools_with_importers, }
def test_subclassed_ep(self, pkg_resources): class App(object): pass class BetterApp(App): pass pkg_resources.iter_entry_points.return_value = [ self._make_ep('myapp', App), self._make_ep('myapp', BetterApp)] eps = list(h.iter_entry_points('allura')) self.assertEqual(len(eps), 1) self.assertEqual(BetterApp, eps[0].load())
def test_ambiguous_eps(self, pkg_resources): class App(object): pass class BetterApp(App): pass class BestApp(object): pass pkg_resources.iter_entry_points.return_value = [ self._make_ep('myapp', App), self._make_ep('myapp', BetterApp), self._make_ep('myapp', BestApp)] self.assertRaisesRegexp(ImportError, 'Ambiguous \[allura\] entry points detected. ' 'Multiple entry points with name "myapp".', list, h.iter_entry_points('allura'))
def _load_rules(self): """ Load and pre-process the rules from the entry points. Rules are specified per-tool as a list of the form: template_path_rules = [ ['>', 'tool1'], # this tool must be resolved before tool1 ['<', 'tool2'], # this tool must be resolved after tool2 ['=', 'tool3'], # this tool replaces all of tool3's templates ] Returns two lists of rules, order_rules and replacement_rules. order_rules represents all of the '>' and '<' rules and are returned as a list of pairs of the form ('a', 'b') indicating that path 'a' must come before path 'b'. replacement_rules represent all of the '=' rules and are returned as a dictionary mapping the paths to replace to the paths to replace with. """ order_rules = [] replacement_rules = {} for ep in iter_entry_points(self.override_entrypoint): for rule in getattr(ep.load(), 'template_path_rules', []): if rule[0] == '>': order_rules.append((ep.name, rule[1])) elif rule[0] == '=': replacement_rules[rule[1]] = ep.name elif rule[0] == '<': order_rules.append((rule[1], ep.name)) else: raise jinja2.TemplateError( 'Unknown template path rule in %s: %s' % ( ep.name, ' '.join(rule))) return order_rules, replacement_rules
def test_disabled(self, pkg_resources): pkg_resources.iter_entry_points.return_value = [ self._make_ep('myapp', object)] self.assertEqual([], list(h.iter_entry_points('allura')))
def _lookup(self, source, *rest): for ep in iter_entry_points('allura.project_importers', source): return ep.load()(self.neighborhood), rest
def entry_point_timers(self): timers = [] for ep in h.iter_entry_points('allura.timers'): func = ep.load() timers += aslist(func()) return timers
import tg import pkg_resources from paste import fileapp from paste.deploy.converters import aslist from pylons import tmpl_context as c from pylons.util import call_wsgi_application from timermiddleware import Timer, TimerMiddleware from webob import exc, Request import pysolr from allura.lib import helpers as h import allura.model.repository log = logging.getLogger(__name__) tool_entry_points = list(h.iter_entry_points('allura')) class StaticFilesMiddleware(object): '''Custom static file middleware Map everything in allura/public/nf/* to <script_name>/* For each plugin, map everything <module>/nf/<ep_name>/* to <script_name>/<ep_name>/* ''' CACHE_MAX_AGE = 60 * 60 * 24 * 365 def __init__(self, app, script_name=''): self.app = app self.script_name = script_name self.directories = [(self.script_name + ep.name.lower() + '/', ep) for ep in tool_entry_points]
def _make_core_app(root, global_conf, full_stack=True, **app_conf): """ Set allura up with the settings found in the PasteDeploy configuration file used. :param root: The controller module containing the TG root :param global_conf: The global settings for allura (those defined under the ``[DEFAULT]`` section). :type global_conf: dict :param full_stack: Should the whole TG2 stack be set up? :type full_stack: str or bool :return: The allura application with all the relevant middleware loaded. This is the PasteDeploy factory for the allura application. ``app_conf`` contains all the application-specific settings (those defined under ``[app:main]``. """ # Run all the initialization code here mimetypes.init([pkg_resources.resource_filename('allura', 'etc/mime.types')] + mimetypes.knownfiles) # Configure MongoDB ming.configure(**app_conf) # Configure ActivityStream if asbool(app_conf.get('activitystream.recording.enabled', False)): activitystream.configure(**h.convert_bools(app_conf, prefix='activitystream.')) # Configure EW variable provider ew.render.TemplateEngine.register_variable_provider(get_tg_vars) # Set FormEncode language to english, as we don't support any other locales formencode.api.set_stdtranslation(domain='FormEncode', languages=['en']) # Create base app base_config = ForgeConfig(root) load_environment = base_config.make_load_environment() # Code adapted from tg.configuration, replacing the following lines: # make_base_app = base_config.setup_tg_wsgi_app(load_environment) # app = make_base_app(global_conf, full_stack=True, **app_conf) # Configure the Pylons environment load_environment(global_conf, app_conf) app = tg.TGApp() for mw_ep in h.iter_entry_points('allura.middleware'): Middleware = mw_ep.load() if getattr(Middleware, 'when', 'inner') == 'inner': app = Middleware(app, config) # Required for pylons app = RoutesMiddleware(app, config['routes.map']) # Required for sessions app = SessionMiddleware(app, config) # Handle "Remember me" functionality app = RememberLoginMiddleware(app, config) # Redirect 401 to the login page app = LoginRedirectMiddleware(app) # Add instrumentation app = AlluraTimerMiddleware(app, app_conf) # Clear cookies when the CSRF field isn't posted if not app_conf.get('disable_csrf_protection'): app = CSRFMiddleware(app, '_session_id') if asbool(config.get('cors.enabled', False)): # Handle CORS requests allowed_methods = aslist(config.get('cors.methods')) allowed_headers = aslist(config.get('cors.headers')) cache_duration = asint(config.get('cors.cache_duration', 0)) app = CORSMiddleware(app, allowed_methods, allowed_headers, cache_duration) # Setup the allura SOPs app = allura_globals_middleware(app) # Ensure http and https used per config if config.get('override_root') != 'task': app = SSLMiddleware(app, app_conf.get('no_redirect.pattern'), app_conf.get('force_ssl.pattern'), app_conf.get('force_ssl.logged_in')) # Setup resource manager, widget context SOP app = ew.WidgetMiddleware( app, compress=not asbool(global_conf['debug']), # compress=True, script_name=app_conf.get('ew.script_name', '/_ew_resources/'), url_base=app_conf.get('ew.url_base', '/_ew_resources/'), extra_headers=eval(app_conf.get('ew.extra_headers', 'None')), cache_max_age=asint(app_conf.get('ew.cache_header_seconds', 60*60*24*365)), ) # Handle static files (by tool) app = StaticFilesMiddleware(app, app_conf.get('static.script_name')) # Handle setup and flushing of Ming ORM sessions app = MingMiddleware(app) # Set up the registry for stacked object proxies (SOPs). # streaming=true ensures they won't be cleaned up till # the WSGI application's iterator is exhausted app = RegistryManager(app, streaming=True) # "task" wsgi would get a 2nd request to /error/document if we used this middleware if config.get('override_root') != 'task': # Converts exceptions to HTTP errors, shows traceback in debug mode # don't use TG footer with extra CSS & images that take time to load tg.error.footer_html = '<!-- %s %s -->' app = tg.error.ErrorHandler(app, global_conf, **config['pylons.errorware']) app = SetRequestHostFromConfig(app, config) # Redirect some status codes to /error/document if asbool(config['debug']): app = StatusCodeRedirect(app, base_config.handle_status_codes) else: app = StatusCodeRedirect( app, base_config.handle_status_codes + [500]) for mw_ep in h.iter_entry_points('allura.middleware'): Middleware = mw_ep.load() if getattr(Middleware, 'when', 'inner') == 'outer': app = Middleware(app, config) return app
def by_name(name): """ Return a ToolImporter subclass instance given its entry-point name. """ for ep in h.iter_entry_points('allura.importers', name): return ep.load()()
def _lookup(self, source, *rest): # iter_entry_points is a generator with 0 or 1 items, so a loop is the easiest way to handle for ep in iter_entry_points('allura.project_importers', source): return ep.load()(self.neighborhood), rest raise exc.HTTPNotFound
import pkg_resources from paste import fileapp from paste.deploy.converters import aslist from pylons import tmpl_context as c from pylons.util import call_wsgi_application from timermiddleware import Timer, TimerMiddleware from webob import exc, Request import pysolr from allura.lib import helpers as h import allura.model.repo log = logging.getLogger(__name__) tool_entry_points = list(h.iter_entry_points('allura')) class StaticFilesMiddleware(object): '''Custom static file middleware Map everything in allura/public/nf/* to <script_name>/* For each plugin, map everything <module>/nf/<ep_name>/* to <script_name>/<ep_name>/* ''' CACHE_MAX_AGE=60*60*24*365 def __init__(self, app, script_name=''): self.app = app self.script_name = script_name self.directories = [ (self.script_name + ep.name.lower() + '/', ep) for ep in tool_entry_points]
def _make_core_app(root, global_conf, full_stack=True, **app_conf): """ Set allura up with the settings found in the PasteDeploy configuration file used. :param root: The controller module containing the TG root :param global_conf: The global settings for allura (those defined under the ``[DEFAULT]`` section). :type global_conf: dict :param full_stack: Should the whole TG2 stack be set up? :type full_stack: str or bool :return: The allura application with all the relevant middleware loaded. This is the PasteDeploy factory for the allura application. ``app_conf`` contains all the application-specific settings (those defined under ``[app:main]``. """ # Run all the initialization code here mimetypes.init([pkg_resources.resource_filename('allura', 'etc/mime.types')] + mimetypes.knownfiles) # Configure MongoDB ming.configure(**app_conf) # Configure ActivityStream if asbool(app_conf.get('activitystream.recording.enabled', False)): activitystream.configure(**h.convert_bools(app_conf, prefix='activitystream.')) # Configure EW variable provider ew.render.TemplateEngine.register_variable_provider(get_tg_vars) # Set FormEncode language to english, as we don't support any other locales formencode.api.set_stdtranslation(domain='FormEncode', languages=['en']) # Create base app base_config = ForgeConfig(root) load_environment = base_config.make_load_environment() # Code adapted from tg.configuration, replacing the following lines: # make_base_app = base_config.setup_tg_wsgi_app(load_environment) # app = make_base_app(global_conf, full_stack=True, **app_conf) # Configure the TG environment load_environment(global_conf, app_conf) app = tg.TGApp() for mw_ep in h.iter_entry_points('allura.middleware'): Middleware = mw_ep.load() if getattr(Middleware, 'when', 'inner') == 'inner': app = Middleware(app, config) # Required for sessions app = SessionMiddleware(app, config, data_serializer=BeakerPickleSerializerWithLatin1()) # Handle "Remember me" functionality app = RememberLoginMiddleware(app, config) # Redirect 401 to the login page app = LoginRedirectMiddleware(app) # Add instrumentation app = AlluraTimerMiddleware(app, app_conf) # Clear cookies when the CSRF field isn't posted if not app_conf.get('disable_csrf_protection'): app = CSRFMiddleware(app, '_session_id') if asbool(config.get('cors.enabled', False)): # Handle CORS requests allowed_methods = aslist(config.get('cors.methods')) allowed_headers = aslist(config.get('cors.headers')) cache_duration = asint(config.get('cors.cache_duration', 0)) app = CORSMiddleware(app, allowed_methods, allowed_headers, cache_duration) # Setup the allura SOPs app = allura_globals_middleware(app) # Ensure http and https used per config if config.get('override_root') != 'task': app = SSLMiddleware(app, app_conf.get('no_redirect.pattern'), app_conf.get('force_ssl.pattern'), app_conf.get('force_ssl.logged_in')) # Setup resource manager, widget context SOP app = ew.WidgetMiddleware( app, compress=True, use_cache=not asbool(global_conf['debug']), script_name=app_conf.get('ew.script_name', '/_ew_resources/'), url_base=app_conf.get('ew.url_base', '/_ew_resources/'), extra_headers=ast.literal_eval(app_conf.get('ew.extra_headers', '[]')), cache_max_age=asint(app_conf.get('ew.cache_header_seconds', 60*60*24*365)), # settings to pass through to jinja Environment for EW core widgets # these are for the easywidgets' own [easy_widgets.engines] entry point # (the Allura [easy_widgets.engines] entry point is named "jinja" (not jinja2) but it doesn't need # any settings since it is a class that uses the same jinja env as the rest of allura) **{ 'jinja2.auto_reload': asbool(config['auto_reload_templates']), 'jinja2.bytecode_cache': AlluraJinjaRenderer._setup_bytecode_cache(), 'jinja2.cache_size': asint(config.get('jinja_cache_size', -1)), } ) # Handle static files (by tool) app = StaticFilesMiddleware(app, app_conf.get('static.script_name')) # Handle setup and flushing of Ming ORM sessions app = MingMiddleware(app) # Set up the registry for stacked object proxies (SOPs). # streaming=true ensures they won't be cleaned up till # the WSGI application's iterator is exhausted app = RegistryManager(app, streaming=True) # "task" wsgi would get a 2nd request to /error/document if we used this middleware if config.get('override_root') not in ('task', 'basetest_project_root'): if asbool(config['debug']): # Converts exceptions to HTTP errors, shows traceback in debug mode # don't use TG footer with extra CSS & images that take time to load tg.error.footer_html = '<!-- %s %s -->' app = tg.error.ErrorHandler(app, global_conf, **config['tg.errorware']) else: app = ErrorMiddleware(app, config, **config['tg.errorware']) app = SetRequestHostFromConfig(app, config) # Redirect some status codes to /error/document if asbool(config['debug']): app = StatusCodeRedirect(app, base_config.handle_status_codes) else: app = StatusCodeRedirect( app, base_config.handle_status_codes + [500]) for mw_ep in h.iter_entry_points('allura.middleware'): Middleware = mw_ep.load() if getattr(Middleware, 'when', 'inner') == 'outer': app = Middleware(app, config) return app
def _cache_eps(section_name, dict_cls=dict): d = dict_cls() for ep in h.iter_entry_points(section_name): value = ep.load() d[ep.name] = value return d