def plugin_name(ds_name, ro): LodelContext.expose_modules(globals(), {'lodel.settings': ['Settings']}) # fetching connection identifier given datasource name try: ds_identifier = getattr(Settings.datasources, ds_name) except (NameError, AttributeError): raise DatasourcePluginError("Unknown or unconfigured datasource \ '%s'" % ds_name) # fetching read_only flag try: read_only = getattr(ds_identifier, 'read_only') except (NameError, AttributeError): raise SettingsError("Malformed datasource configuration for '%s' \ : missing read_only key" % ds_name) # fetching datasource identifier try: ds_identifier = getattr(ds_identifier, 'identifier') except (NameError, AttributeError) as e: raise SettingsError("Malformed datasource configuration for '%s' \ : missing identifier key" % ds_name) # settings and ro arg consistency check if read_only and not ro: raise DatasourcePluginError("ro argument was set to False but \ True found in settings for datasource '%s'" % ds_name) res = ds_identifier.split('.') if len(res) != 2: raise SettingsError("expected value for identifier is like \ DS_PLUGIN_NAME.DS_INSTANCE_NAME. But got %s" % ds_identifier) return res
def plugin_validator(value, ptype=None): if value: LodelContext.expose_modules(globals(), {'lodel.plugin.hooks': ['LodelHook']}) value = copy.copy(value) @LodelHook('lodel2_dyncode_bootstraped') def plugin_type_checker(hookname, caller, payload): LodelContext.expose_modules( globals(), { 'lodel.plugin.plugins': ['Plugin'], 'lodel.plugin.exceptions': ['PluginError'] }) if value is None: return try: plugin = Plugin.get(value) except PluginError: msg = "No plugin named %s found" msg %= value raise ValidationError(msg) if plugin._type_conf_name.lower() != ptype.lower(): msg = "A plugin of type '%s' was expected but found a plugin \ named '%s' that is a '%s' plugin" msg %= (ptype, value, plugin._type_conf_name) raise ValidationError(msg) return value return None
def start(): #Load plugins LodelContext.expose_modules(globals(), { 'lodel.logger': 'logger', 'lodel.plugin': ['Plugin']}) logger.debug("Loader.start() called") Plugin.load_all() LodelHook.call_hook('lodel2_bootstraped', '__main__', None)
def plugin_list_confspec(cls): LodelContext.expose_modules(globals(), { 'lodel.settings.validator': ['confspec_append']}) res = dict() for pcls in cls.plugin_types(): plcs = pcls.plist_confspec() confspec_append(res, plcs) return res
def call_hook(cls, hook_name, caller, payload): LodelContext.expose_modules(globals(), {'lodel.logger': 'logger'}) logger.debug("Calling hook '%s'" % hook_name) if hook_name in cls._hooks: for hook in cls._hooks[hook_name]: logger.debug("Lodel hook '%s' calls %s" % (hook_name, hook)) payload = hook(hook_name, caller, payload) return payload
def log(self): LodelContext.expose_modules(globals(), {'lodel.logger': 'logger'}) msg = "Webui HTTP exception : %s" % self if self.status_code / 100 == 4: logger.security(msg) elif self.status_code / 100 == 5: logger.error(msg) else: logger.warning(msg)
def __bootstrap(self): LodelContext.expose_modules( globals(), {'lodel.plugin.plugins': ['Plugin', 'PluginError']}) logger.debug("Settings bootstraping") if self.__conf_specs is None: lodel2_specs = LODEL2_CONF_SPECS else: lodel2_specs = self.__conf_specs self.__conf_specs = None loader = SettingsLoader(self.__conf_dir) plugin_list = [] for ptype_name, ptype in Plugin.plugin_types().items(): pls = ptype.plist_confspecs() lodel2_specs = confspec_append(lodel2_specs, **pls) cur_list = loader.getoption(pls['section'], pls['key'], pls['validator'], pls['default']) if cur_list is None: continue try: if isinstance(cur_list, str): cur_list = [cur_list] plugin_list += cur_list except TypeError: plugin_list += [cur_list] # Remove invalid plugin names plugin_list = [plugin for plugin in plugin_list if len(plugin) > 0] # Checking confspecs for section in lodel2_specs: if section.lower() != section: raise SettingsError( "Only lower case are allowed in section name (thank's ConfigParser...)" ) for kname in lodel2_specs[section]: if kname.lower() != kname: raise SettingsError( "Only lower case are allowed in section name (thank's ConfigParser...)" ) # Starting the Plugins class logger.debug("Starting lodel.plugin.Plugin class") Plugin.start(plugin_list) # Fetching conf specs from plugins specs = [lodel2_specs] errors = list() for plugin_name in plugin_list: try: specs.append(Plugin.get(plugin_name).confspecs) except PluginError as e: errors.append(SettingsError(msg=str(e))) if len(errors) > 0: # Raise all plugins import errors raise SettingsErrors(errors) self.__conf_specs = self.__merge_specs(specs) self.__populate_from_specs(self.__conf_specs, loader) self.__started = True
def list_hook_debug_hook(name, caller, payload): LodelContext.expose_modules(globals(), {'lodel.logger': 'logger'}) hlist = LodelHook.hook_list() for name, reg_hooks in hlist.items(): for hook, priority in reg_hooks: logger.debug("{modname}.{funname} is registered as hook \ {hookname} with priority {priority}".format(modname=hook.__module__, funname=hook.__name__, priority=priority, hookname=name))
def _get_ds_connection_conf(ds_identifier, ds_plugin_name): LodelContext.expose_modules(globals(), {'lodel.settings': ['Settings']}) if ds_plugin_name not in Settings.datasource._fields: msg = "Unknown or unconfigured datasource plugin %s" msg %= ds_plugin_name raise DatasourcePluginError(msg) ds_conf = getattr(Settings.datasource, ds_plugin_name) if ds_identifier not in ds_conf._fields: msg = "Unknown or unconfigured datasource instance %s" msg %= ds_identifier raise DatasourcePluginError(msg) ds_conf = getattr(ds_conf, ds_identifier) return {k: getattr(ds_conf, k) for k in ds_conf._fields}
def dyncode_from_em(model): # Generation of LeObject child classes code cls_code, bootstrap_instr = generate_classes(model) # Header imports = """from lodel.context import LodelContext LodelContext.expose_modules(globals(), { 'lodel.leapi.leobject': ['LeObject'], 'lodel.leapi.datahandlers.base_classes': ['DataField'], 'lodel.plugin.hooks': ['LodelHook']}) """ # generates the list of all classes in the editorial model class_list = [LeObject.name2objname(cls.uid) for cls in get_classes(model)] # formating all components of output res_code = """#-*- coding: utf-8 -*- {imports} {classes} {bootstrap_instr} #List of dynamically generated classes dynclasses = {class_list} #Dict of dynamically generated classes indexed by name dynclasses_dict = {class_dict} {common_code} """.format( imports=imports, classes=cls_code, bootstrap_instr=bootstrap_instr, class_list='[' + (', '.join([cls for cls in class_list])) + ']', class_dict='{' + (', '.join(["'%s': %s" % (cls, cls) for cls in class_list])) + '}', common_code=common_code(), ) return res_code
def __init_from_settings(): from lodel.context import LodelContext try: LodelContext.expose_modules(globals(), { 'lodel.settings': ['Settings']}) except Exception: return False LodelContext.expose_modules(globals(), { 'lodel.settings.settings': [('Settings', 'Lodel2Settings')]}) if not Lodel2Settings.started(): return False # capture warning disabled, because the custom format raises error (unable # to give the _ preffixed arguments to logger resulting into a KeyError # exception ) #logging.captureWarnings(True) # Log warnings logger.setLevel(logging.DEBUG) for name in Settings.logging._fields: add_handler(name, getattr(Settings.logging, name)) return True
def plugin_type_checker(hookname, caller, payload): LodelContext.expose_modules( globals(), { 'lodel.plugin.plugins': ['Plugin'], 'lodel.plugin.exceptions': ['PluginError'] }) if value is None: return try: plugin = Plugin.get(value) except PluginError: msg = "No plugin named %s found" msg %= value raise ValidationError(msg) if plugin._type_conf_name.lower() != ptype.lower(): msg = "A plugin of type '%s' was expected but found a plugin \ named '%s' that is a '%s' plugin" msg %= (ptype, value, plugin._type_conf_name) raise ValidationError(msg)
def _discover(cls, path): # Ensure plugins symlink creation LodelContext.expose_modules(globals(), { 'lodel.plugins': 'plugins'}) res = [] to_explore = [path] while len(to_explore) > 0: cur_path = to_explore.pop() for f in os.listdir(cur_path): f_path = os.path.join(cur_path, f) if f not in ['.', '..'] and os.path.isdir(f_path): # Check if it is a plugin directory test_result = cls.dir_is_plugin(f_path) if not (test_result is False): logger.info("Plugin '%s' found in %s" % ( test_result['name'], f_path)) res.append(test_result) else: to_explore.append(f_path) return res
def emfield_val(value): LodelContext.expose_modules(globals(), {'lodel.plugin.hooks': ['LodelHook']}) spl = value.split('.') if len(spl) != 2: msg = "Expected a value in the form CLASSNAME.FIELDNAME but got : %s" raise ValidationError(msg % value) value = tuple(spl) # Late validation hook @LodelHook('lodel2_dyncode_bootstraped') def emfield_conf_check(hookname, caller, payload): import leapi_dyncode as dyncode # <-- dirty & quick classnames = {cls.__name__.lower(): cls for cls in dyncode.dynclasses} if value[0].lower() not in classnames: msg = "Following dynamic class do not exists in current EM : %s" raise ValidationError(msg % value[0]) ccls = classnames[value[0].lower()] if value[1].lower() not in ccls.fieldnames(True): msg = "Following field not found in class %s : %s" raise ValidationError(msg % value) return value
# You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # import sys import os.path import re import socket import inspect import copy from lodel.context import LodelContext LodelContext.expose_modules( globals(), { 'lodel.mlnamedobject.mlnamedobject': ['MlNamedObject'], 'lodel.exceptions': [ 'LodelException', 'LodelExceptions', 'LodelFatalError', 'FieldValidationError' ] }) ## # @package lodel.settings.validator Lodel2 settings validators/cast module. # # Validator are registered in the Validator class. # @note to get a list of registered default validators just run # <pre>$ python scripts/settings_validator.py</pre> # # @remarks Should we reconsider specifying conf right in this module? ##
# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # ## @package lodel.plugins.dummy.main Plugin's loader module from lodel.context import LodelContext LodelContext.expose_modules(globals(), { 'lodel.plugin': ['LodelHook', 'CustomMethod'], 'lodel.settings': 'settings' }) ## @brief callback method using lodel's hook system # @param hook_name str # @param caller function : action to perform # @param payload : data to pass to the caller # @return payload @LodelHook('leapi_get_post') @LodelHook('leapi_update_pre') @LodelHook('leapi_update_post') @LodelHook('leapi_delete_pre') @LodelHook('leapi_delete_post') @LodelHook('leapi_insert_pre') @LodelHook('leapi_insert_post')
# This file is part of Lodel 2 (https://github.com/OpenEdition) # # Copyright (C) 2015-2017 Cléo UMS-3287 # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published # by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # ## @package lodel.plugins.dummy_datasource.main The main module of the plugin. from lodel.context import LodelContext LodelContext.expose_modules(globals(), {'lodel.plugin': ['LodelHook']}) from .datasource import DummyDatasource as Datasource ## @brief returns the migration handler of this plugin. # @retunr DummyMigrationHandler def migration_handler_class(): from .migration_handler import DummyMigrationHandler as migration_handler return migration_handler
def lodel2_plugins_custom_methods(self, caller, dynclasses): LodelContext.expose_modules(globals(), {'lodel.plugin.plugins': ['CustomMethod']}) CustomMethod.set_registered(dynclasses)
# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # import jinja2 import os from lodel.context import LodelContext LodelContext.expose_modules(globals(), {'lodel.settings': ['Settings']}) LodelContext.expose_dyncode(globals()) from ...client import WebUiClient as WebUiClient from .api import api_lodel_templates from .exceptions.not_allowed_custom_api_key_error import NotAllowedCustomAPIKeyError from ...main import root_url as root_url from ...main import static_url as static_url from ...main import PLUGIN_PATH TEMPLATE_PATH = os.path.realpath(os.path.join(PLUGIN_PATH, 'templates/')) class TemplateLoader(object):
# but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # import datetime from lodel.context import LodelContext LodelContext.expose_modules( globals(), { 'lodel.editorial_model.components': ['EmClass', 'EmField'], 'lodel.editorial_model.model': ['EditorialModel'], 'lodel.leapi.datahandlers.base_classes': ['DataHandler'], 'lodel.plugin': ['LodelHook'], 'lodel.logger': 'logger' }) from leapi_dyncode import * #<-- TODO : handle this !!! from .utils import connect, object_collection_name, mongo_fieldname from .datasource import MongoDbDatasource from .exceptions import * class MigrationHandler(object): ## @brief Constructs a MongoDbMigrationHandler # @param host str
# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # from lodel.context import LodelContext LodelContext.expose_modules( globals(), { 'lodel.plugin.plugins': ['Plugin', 'MetaPlugType'], 'lodel.plugin.exceptions': [ 'PluginError', 'PluginTypeError', 'LodelScriptError', 'DatasourcePluginError' ], 'lodel.validator.validator': ['Validator'] }) ##@brief SessionHandlerPlugin metaclass designed to implements a wrapper #between SessionHandlerPlugin classmethod and plugin loader functions class SessionPluginWrapper(MetaPlugType): ##@brief Constant that stores all possible session actions # #Key is the SessionHandlerPlugin method name and value is SessionHandler #plugin function name _ACTIONS = {
# You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # ## @package lodel.plugin.datasource_plugin Datasource plugins management module # # It contains the base classes for all the datasource plugins that could be added to Lodel from lodel.context import LodelContext LodelContext.expose_modules( globals(), { 'lodel.plugin.plugins': ['Plugin'], 'lodel.plugin.exceptions': [ 'PluginError', 'PluginTypeError', 'LodelScriptError', 'DatasourcePluginError' ], 'lodel.validator.validator': ['Validator'], 'lodel.exceptions': [ 'LodelException', 'LodelExceptions', 'LodelFatalError', 'DataNoneValid', 'FieldValidationError' ] }) ## @brief The plugin type that is used in the global settings of Lodel _glob_typename = 'datasource' ## @brief Main abstract class from which the plugins' datasource classes must inherit. class AbstractDatasource(object): ## @brief Trigger LodelFatalError when abtract method called
# This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # import pymongo from pymongo import MongoClient from lodel.context import LodelContext LodelContext.expose_modules(globals(), { 'lodel.settings.settings': [('Settings', 'settings')], 'lodel.logger': 'logger'}) common_collections = { 'object': 'objects', 'relation': 'relation' } LODEL_SORT_OPERATORS_MAP = { 'ASC': pymongo.ASCENDING, 'DESC': pymongo.DESCENDING } MONGODB_SORT_OPERATORS_MAP = { 'ASC': 1, 'DESC': -1
# Contains custom exceptions too import copy import importlib import inspect import warnings from lodel.context import LodelContext LodelContext.expose_modules( globals(), { 'lodel.exceptions': [ 'LodelException', 'LodelExceptions', 'LodelFatalError', 'DataNoneValid', 'FieldValidationError' ], 'lodel.mlnamedobject.mlnamedobject': ['MlNamedObject'], 'lodel.leapi.datahandlers.exceptions': ['LodelDataHandlerConsistencyException', 'LodelDataHandlerException'], 'lodel.validator.validator': ['ValidationError'], 'lodel.logger': 'logger', 'lodel.utils.mlstring': ['MlString'] }) ## # @brief Base class for all DataHandlers # @ingroup lodel2_datahandlers # # @remarks Some of the methods and properties in this "abstract" class are # bounded to its children. This implies that the parent # is aware of its children, which is an absolute anti-pattern
# but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # # @package lodel.auth.exceptions # @brief Defines the specific exceptions used in the authentication process from lodel.context import LodelContext LodelContext.expose_modules(globals(), { 'lodel.logger': 'logger', 'lodel.plugin.hooks': ['LodelHook']}) ## @brief Handles common errors with a Client class ClientError(Exception): ## @brief The logger function to use to log the error message _loglvl = 'warning' ## @brief Error string _err_str = "Error" ## @brief the hook name to trigger with error _action = 'lodel2_ui_error' ## @brief the hook payload _payload = None ## @brief Constructor
# @package lodel.editorial_model.components #@brief Defines all @ref lodel2_em "EM" components #@ingroup lodel2_em import itertools import warnings import copy import hashlib from lodel.context import LodelContext LodelContext.expose_modules( globals(), { 'lodel.utils.mlstring': ['MlString'], 'lodel.mlnamedobject.mlnamedobject': ['MlNamedObject'], 'lodel.settings': ['Settings'], 'lodel.editorial_model.exceptions': ['EditorialModelError', 'assert_edit'], 'lodel.leapi.leobject': ['CLASS_ID_FIELDNAME'] }) ## @brief Abstract class to represent editorial model components # @see EmClass EmField # @todo forbid '.' in uid #@ingroup lodel2_em class EmComponent(MlNamedObject): ## @brief Instanciate an EmComponent # @param uid str : uniq identifier
# ## @package lodel.leapi.datahandlers.datas # This module contains specific datahandlers extending the basic ones from the lodel.leapi.datahandlers.datas_base module. import warnings import inspect import re from lodel.context import LodelContext LodelContext.expose_modules( globals(), { 'lodel.leapi.datahandlers.datas_base': ['Boolean', 'Integer', 'Varchar', 'DateTime', 'Text', 'File'], 'lodel.exceptions': [ 'LodelException', 'LodelExceptions', 'LodelFatalError', 'DataNoneValid', 'FieldValidationError' ] }) ## @brief Data field designed to handle formated strings class FormatString(Varchar): help = 'Automatic string field, designed to use the str % operator to build its content' base_type = 'char' ## @brief Constructor # @param _field_list list : List of fields to use # @param _format_string str : formatted string
# In this class, there is the MongoDbDatasource class, that handles the basic # operations that can be done (CRUD ones). import re import warnings import copy import functools from bson.son import SON from collections import OrderedDict import pymongo from pymongo.errors import BulkWriteError from lodel.context import LodelContext LodelContext.expose_modules(globals(), { 'lodel.logger': 'logger', 'lodel.leapi.leobject': ['CLASS_ID_FIELDNAME'], 'lodel.leapi.datahandlers.base_classes': ['Reference', 'MultipleRef'], 'lodel.exceptions': ['LodelException', 'LodelFatalError'], 'lodel.plugin.datasource_plugin': ['AbstractDatasource']}) from . import utils from .exceptions import * from .utils import object_collection_name, collection_name, \ MONGODB_SORT_OPERATORS_MAP, connection_string, mongo_fieldname ## @brief Datasource class # @ingroup plugin_mongodb_datasource class MongoDbDatasource(AbstractDatasource): ## @brief Stores existing connections #
# This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published # by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # from lodel.context import LodelContext LodelContext.expose_modules(globals(), {'lodel.validator.validator': ['Validator']}) #Define a minimal confspec used by multisite loader LODEL2_CONFSPECS = { 'lodel2': { 'debug': (True, Validator('bool')) }, 'lodel2.server': { 'listen_address': ('127.0.0.1', Validator('dummy')), #'listen_address': ('', Validator('ip')), #<-- not implemented 'listen_port': (1337, Validator('int')), 'uwsgi_workers': (8, Validator('int')), 'uwsgicmd': ('/usr/bin/uwsgi', Validator('dummy')), 'virtualenv': (None, Validator('path', none_is_valid=True)), }, 'lodel2.logging.*': {
# (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # from lodel.context import LodelContext LodelContext.expose_modules( globals(), { 'lodel.plugin': ['LodelHook'], 'lodel.settings': ['Settings'], 'lodel.logger': 'logger' }) ## @package lodel.plugin.core_hooks # @brief Lodel2 internal hooks declaration # @ingroup lodel2_plugins ## @brief Bootstrap hook that checks datasources configuration # @param hook_name str # @param caller * : the hook's caller # @param payload * : data to be given to the hook # @throw NameError when : a set datasource family name can not be found or a datasource identifier does not match with a configured datasource. @LodelHook('lodel2_bootstraped') def datasources_bootstrap_hook(hook_name, caller, payload):