Ejemplo n.º 1
0
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)])
Ejemplo n.º 2
0
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)