def get_mapview_config(): ''' Extracts and returns map view configuration of the reclineview extension. ''' namespace = 'ckanext.spatial.common_map.' return dict([(k.replace(namespace, ''), v) for k, v in config.items() if k.startswith(namespace)])
def make_flask_stack(conf: Union[Config, CKANConfig]) -> CKANApp: """ This has to pass the flask app through all the same middleware that Pylons used """ root = os.path.dirname( os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) debug = asbool(conf.get('debug', conf.get('DEBUG', False))) testing = asbool(conf.get('testing', conf.get('TESTING', False))) app = flask_app = CKANFlask(__name__, static_url_path='') # Register storage for accessing group images, site logo, etc. storage_folder = [] storage = uploader.get_storage_path() if storage: storage_folder = [os.path.join(storage, 'storage')] # Static files folders (core and extensions) public_folder = config.get_value(u'ckan.base_public_folder') app.static_folder = config.get_value('extra_public_paths').split(',') + [ os.path.join(root, public_folder) ] + storage_folder app.jinja_options = jinja_extensions.get_jinja_env_options() app.jinja_env.policies['ext.i18n.trimmed'] = True app.debug = debug app.testing = testing app.template_folder = os.path.join(root, 'templates') app.app_ctx_globals_class = CKAN_AppCtxGlobals app.url_rule_class = CKAN_Rule # Update Flask config with the CKAN values. We use the common config # object as values might have been modified on `load_environment` if config: app.config.update(config) else: app.config.update(conf) # Do all the Flask-specific stuff before adding other middlewares # Secret key needed for flask-debug-toolbar and sessions if not app.config.get('SECRET_KEY'): app.config['SECRET_KEY'] = config.get_value('beaker.session.secret') if not app.config.get('SECRET_KEY'): raise RuntimeError(u'You must provide a value for the secret key' ' with the SECRET_KEY config option') root_path = config.get_value('ckan.root_path') if debug: from flask_debugtoolbar import DebugToolbarExtension app.config['DEBUG_TB_INTERCEPT_REDIRECTS'] = False debug_ext = DebugToolbarExtension() # register path that includes `ckan.site_root` before # initializing debug app. In such a way, our route receives # higher precedence. # TODO: After removal of Pylons code, switch to # `APPLICATION_ROOT` config value for flask application. Right # now it's a bad option because we are handling both pylons # and flask urls inside helpers and splitting this logic will # bring us tons of headache. if root_path: app.add_url_rule( root_path.replace('{{LANG}}', '').rstrip('/') + '/_debug_toolbar/static/<path:filename>', '_debug_toolbar.static', debug_ext.send_static_file) debug_ext.init_app(app) from werkzeug.debug import DebuggedApplication app.wsgi_app = DebuggedApplication(app.wsgi_app, True) # Use Beaker as the Flask session interface class BeakerSessionInterface(SessionInterface): def open_session(self, app: Any, request: Any): if 'beaker.session' in request.environ: return request.environ['beaker.session'] def save_session(self, app: Any, session: Any, response: Any): session.save() namespace = 'beaker.session.' session_opts = { k.replace('beaker.', ''): v for k, v in config.items() if k.startswith(namespace) } if (not session_opts.get('session.data_dir') and session_opts.get('session.type', 'file') == 'file'): cache_dir = conf.get('cache_dir') or conf.get('cache.dir') session_opts['session.data_dir'] = '{data_dir}/sessions'.format( data_dir=cache_dir) app.wsgi_app = RootPathMiddleware(app.wsgi_app) app.wsgi_app = SessionMiddleware(app.wsgi_app, session_opts) app.session_interface = BeakerSessionInterface() # Add Jinja2 extensions and filters app.jinja_env.filters['empty_and_escape'] = \ jinja_extensions.empty_and_escape # Common handlers for all requests # # flask types do not mention that it's possible to return a response from # the `before_request` callback app.before_request(ckan_before_request) app.after_request(ckan_after_request) # Template context processors app.context_processor(helper_functions) app.context_processor(c_object) app.context_processor(_ungettext_alias) # Babel _ckan_i18n_dir = i18n.get_ckan_i18n_dir() pairs = [cast("tuple[str, str]", (_ckan_i18n_dir, u'ckan')) ] + [(p.i18n_directory(), p.i18n_domain()) for p in reversed(list(PluginImplementations(ITranslation)))] i18n_dirs, i18n_domains = zip(*pairs) app.config[u'BABEL_TRANSLATION_DIRECTORIES'] = ';'.join(i18n_dirs) app.config[u'BABEL_DOMAIN'] = 'ckan' app.config[u'BABEL_MULTIPLE_DOMAINS'] = ';'.join(i18n_domains) app.config[u'BABEL_DEFAULT_TIMEZONE'] = str(helpers.get_display_timezone()) babel = CKANBabel(app) babel.localeselector(get_locale) # WebAssets _setup_webassets(app) # Auto-register all blueprints defined in the `views` folder _register_core_blueprints(app) _register_error_handler(app) # Set up each IBlueprint extension as a Flask Blueprint for plugin in PluginImplementations(IBlueprint): plugin_blueprints = plugin.get_blueprint() if not isinstance(plugin_blueprints, list): plugin_blueprints = [plugin_blueprints] for blueprint in plugin_blueprints: app.register_extension_blueprint(blueprint) lib_plugins.register_package_blueprints(app) lib_plugins.register_group_blueprints(app) # Start other middleware for plugin in PluginImplementations(IMiddleware): app = plugin.make_middleware(app, config) for plugin in PluginImplementations(IMiddleware): try: app = plugin.make_error_log_middleware(app, config) except AttributeError: log.critical('Middleware class {0} is missing the method' 'make_error_log_middleware.'.format( plugin.__class__.__name__)) # Initialize repoze.who who_parser = WhoConfig(conf['here']) who_parser.parse(open(cast(str, conf['who.config_file']))) app = PluggableAuthenticationMiddleware( RepozeAdapterMiddleware(app), who_parser.identifiers, who_parser.authenticators, who_parser.challengers, who_parser.mdproviders, who_parser.request_classifier, who_parser.challenge_decider, logging.getLogger('repoze.who'), logging.WARN, # ignored who_parser.remote_user_key) # Update the main CKAN config object with the Flask specific keys # that were set here or autogenerated flask_config_keys = set(flask_app.config.keys()) - set(config.keys()) for key in flask_config_keys: config[key] = flask_app.config[key] # Prevent the host from request to be added to the new header location. app = HostHeaderMiddleware(app) app = I18nMiddleware(app) if config.get_value('ckan.tracking_enabled'): app = TrackingMiddleware(app, config) # Add a reference to the actual Flask app so it's easier to access # type_ignore_reason: custom attribute app._wsgi_app = flask_app # type: ignore return app
def create_library(name, path, depend_base=True): ''' Creates a fanstatic library `name` with the contents of a directory `path` using resource.config if found.''' def get_resource(lib_name, resource_name): ''' Attempt to get the resource from the current lib or if not try assume it is a fully qualified resource name. ''' try: res = getattr(module, '%s/%s' % (lib_name, resource_name)) except AttributeError: res = getattr(module, '%s' % resource_name) return res def create_resource(path, lib_name, count, inline=False): ''' create the fanstatic Resource ''' renderer = None kw = {} if not inline: # resource_name is name of the file without the .js/.css rel_path, filename = os.path.split(path) filename = os.path.join(rel_path, filename) path_min = min_path(os.path.join(resource_path, filename)) if os.path.exists(path_min): kw['minified'] = min_path(filename) if filename.endswith('.js'): renderer = core.render_js if path not in force_top: kw['bottom'] = True if filename.endswith('.css'): renderer = core.render_css core.set_resource_file_existence_checking(True) else: # This doesn't exist so stop fanstatic checking the filesystem if path not in force_top: kw['bottom'] = True core.set_resource_file_existence_checking(False) dependencies = [] if path in depends: for dependency in depends[path]: dependencies.append(get_resource(name, dependency)) if depend_base: dependencies.append(getattr(module, 'base/main')) if dependencies: kw['depends'] = dependencies if path in dont_bundle: kw['dont_bundle'] = True # IE conditionals condition = None other_browsers = False if path in IE_conditionals: other_browsers = ('others' in IE_conditionals[path]) condition = IE_conditionals[path][0] if inline or condition: kw['renderer'] = fanstatic_extensions.CkanCustomRenderer( condition=condition, script=inline, renderer=renderer, other_browsers=other_browsers) resource = Resource(library, path, **kw) # Add our customised ordering if path in custom_render_order: resource.order = custom_render_order[path] resource.custom_order = count # Update the attributes of the minified version of the resource to # that of the parents as fanstatic does not pass these on. update_attributes = ['custom_order', 'order', 'bottom', 'depends', 'dont_bundle', 'renderer'] if 'minified' in resource.modes: min_res = resource.modes['minified'] for attribute in update_attributes: setattr(min_res, attribute, getattr(resource, attribute)) # add the resource to this module fanstatic_name = '%s/%s' % (lib_name, path) setattr(module, fanstatic_name, resource) return resource resource_path = os.path.join(os.path.dirname(__file__), path) library = Library(name, path) module = sys.modules[__name__] # config options order = [] dont_bundle = [] force_top = [] depends = {} groups = {} IE_conditionals = {} custom_render_order = {} inline_scripts = {} # parse the resource.config file if it exists config_path = os.path.join(resource_path, 'resource.config') if os.path.exists(config_path): config = ConfigParser.RawConfigParser() config.read(config_path) if config.has_option('main', 'order'): order = config.get('main', 'order').split() if config.has_option('main', 'dont_bundle'): dont_bundle = config.get('main', 'dont_bundle').split() if config.has_option('main', 'force_top'): force_top = config.get('main', 'force_top').split() if config.has_section('depends'): items = config.items('depends') depends = dict((n, v.split()) for (n, v) in items) if config.has_section('groups'): items = config.items('groups') groups = dict((n, v.split()) for (n, v) in items) if config.has_section('custom render order'): items = config.items('custom render order') custom_render_order = dict((n, int(v)) for (n, v) in items) if config.has_section('inline scripts'): items = config.items('inline scripts') inline_scripts = dict((n, v) for (n, v) in items) if config.has_section('IE conditional'): items = config.items('IE conditional') for (n, v) in items: files = v.split() for f in files: if f not in IE_conditionals: IE_conditionals[f] = [] IE_conditionals[f].append(n) # add dependencies for resources in groups for group in groups: if group in depends: for resource in groups[group]: if resource not in depends: depends[resource] = [] for dep in depends[group]: if dep not in groups: dep_resources = [dep] else: dep_resources = groups[dep] diff = [ res for res in dep_resources if res not in depends[resource]] depends[resource].extend(diff) # process each .js/.css file found resource_list = [] for dirname, dirnames, filenames in os.walk(resource_path): for f in filenames: rel_path = dirname[len(path):] if rel_path: rel_path = rel_path[1:] filepath = os.path.join(rel_path, f) filename_only, extension = os.path.splitext(f) if extension in ('.css', '.js') and ( not filename_only.endswith('.min')): resource_list.append(filepath) # if groups are defined make sure the order supplied there is honored for group in groups: for resource in groups[group]: if resource not in order: # make sure any dependencies are met when we get to creating # the resource if resource in depends: for dep in depends[resource]: if dep not in order: order.append(dep) order.append(resource) # add inline scripts for inline in inline_scripts: resource_list.append(inline) if inline not in custom_render_order: custom_render_order[inline] = 20 # order resource_list so that resources are created in the correct order for resource_name in reversed(order): if resource_name in resource_list: resource_list.remove(resource_name) resource_list.insert(0, resource_name) # create the resources and keep them ordered as we define them. count = 0 for resource_name in resource_list: if resource_name in inline_scripts: inline = inline_scripts[resource_name].strip() else: inline = None create_resource(resource_name, name, count, inline=inline) count += 1 # add groups for group_name in groups: members = [] for member in groups[group_name]: fanstatic_name = '%s/%s' % (name, member) members.append(getattr(module, fanstatic_name)) group = Group(members) fanstatic_name = '%s/%s' % (name, group_name) setattr(module, fanstatic_name, group) # finally add the library to this module setattr(module, name, library) # add to fanstatic registry = get_library_registry() registry.add(library)