Example #1
0
    def setup_method(self, _):
        import transaction
        from geoalchemy2 import WKTElement
        from sqlalchemy import func

        from c2cgeoportal_commons.models import DBSession
        from c2cgeoportal_commons.models.main import FullTextSearch

        entry1 = FullTextSearch()
        entry1.label = "label 1"
        entry1.layer_name = "layer1"
        entry1.ts = func.to_tsvector("french", "soleil travail")
        entry1.the_geom = WKTElement("POINT(-90 -45)", 21781)
        entry1.public = True

        entry2 = FullTextSearch()
        entry2.label = "label 2"
        entry2.layer_name = "layer2"
        entry2.ts = func.to_tsvector("french", "pluie semaine")
        entry2.the_geom = WKTElement("POINT(-90 -45)", 21781)
        entry1.public = True

        entry3 = FullTextSearch()
        entry3.label = "label 3"
        entry3.layer_name = "layer2"
        entry3.ts = func.to_tsvector("french", "vent neige")
        entry3.the_geom = WKTElement("POINT(-90 -45)", 21781)
        entry1.public = True

        DBSession.add_all([entry1, entry2, entry3])
        transaction.commit()

        init_region({"backend": "dogpile.cache.memory"}, "std")
        init_region({"backend": "dogpile.cache.memory"}, "obj")
Example #2
0
def cleanup_db():
    """ Cleanup the database """
    import transaction
    import c2cgeoportal_geoportal.lib
    from c2cgeoportal_commons.models import DBSession
    from c2cgeoportal_commons.models.main import OGCServer, TreeItem, Role, RestrictionArea, \
        Interface, Functionality, FullTextSearch
    from c2cgeoportal_commons.models.static import Shorturl, User

    transaction.commit()
    for ra in DBSession.query(RestrictionArea).all():
        ra.roles = []
        DBSession.delete(ra)
    for ti in DBSession.query(TreeItem).all():
        DBSession.delete(ti)
    DBSession.query(OGCServer).delete()
    DBSession.query(Interface).delete()
    for r in DBSession.query(Role).all():
        r.functionnalities = []
        DBSession.delete(r)
    DBSession.query(User).delete()
    DBSession.query(Functionality).delete()
    DBSession.query(FullTextSearch).delete()
    DBSession.query(Shorturl).delete()
    transaction.commit()

    c2cgeoportal_geoportal.lib.ogc_server_wms_url_ids = None
    c2cgeoportal_geoportal.lib.ogc_server_wfs_url_ids = None

    caching.init_region({
        "backend": "dogpile.cache.null",
    })
    caching.invalidate_region()
Example #3
0
    def __call__(self, filename, options):
        init_region({"backend": "dogpile.cache.memory"})

        with open(filename) as config_file:
            config = yaml.load(config_file, Loader=yaml.BaseLoader)
            # For application config (config.yaml)
            if "vars" in config:
                return self._collect_app_config(filename)
            # For the print config
            elif "templates" in config:
                return self._collect_print_config(config, filename)
            else:
                raise Exception("Not a known config file")
    def __call__(self, filename, options):
        init_region({"backend": "dogpile.cache.memory"})

        with open(filename) as config_file:
            config = yaml.load(config_file)
            # for application config (config.yaml)
            if "vars" in config:
                return self._collect_app_config(filename)
            # for the print config
            elif "templates" in config:
                return self._collect_print_config(config, filename)
            else:
                raise Exception("Not a known config file")
    def __call__(self, filename, options, fileobj=None, lineno=0):
        del fileobj, lineno
        init_region({"backend": "dogpile.cache.memory"}, "std")
        init_region({"backend": "dogpile.cache.memory"}, "obj")

        with open(filename) as config_file:
            gmf_config = yaml.load(config_file, Loader=yaml.BaseLoader)  # nosec
            # For application config (config.yaml)
            if "vars" in gmf_config:
                return self._collect_app_config(filename)
            # For the print config
            if "templates" in gmf_config:
                return self._collect_print_config(gmf_config, filename)
            raise Exception("Not a known config file")
Example #6
0
def setup_db():
    """ Cleanup the database """
    cleanup_db()

    from c2cgeoportal_commons.models import DBSession
    from c2cgeoportal_commons.models.main import Role

    DBSession.add_all([Role(name) for name in ("anonymous", "registered", "intranet")])

    transaction.commit()

    c2cgeoportal_geoportal.lib.ogc_server_wms_url_ids = None
    c2cgeoportal_geoportal.lib.ogc_server_wfs_url_ids = None

    caching.init_region({"backend": "dogpile.cache.null"}, "std")
    caching.init_region({"backend": "dogpile.cache.null"}, "obj")
    caching.invalidate_region()
Example #7
0
    def __call__(
        self,
        filename: str,
        options: Dict[str, Any],
        fileobj: Optional[Dict[str, Any]] = None,
        lineno: int = 0,
    ) -> List[Message]:
        del options, fileobj, lineno

        print(f"Running {self.__class__.__name__} on {filename}")

        init_region({"backend": "dogpile.cache.memory"}, "std")
        init_region({"backend": "dogpile.cache.memory"}, "obj")

        with open(filename, encoding="utf8") as config_file:
            gmf_config = yaml.load(config_file,
                                   Loader=yaml.BaseLoader)  # nosec
            # For application config (config.yaml)
            if "vars" in gmf_config:
                return self._collect_app_config(filename)
            # For the print config
            if "templates" in gmf_config:
                return self._collect_print_config(gmf_config, filename)
            raise Exception("Not a known config file")
Example #8
0
 def setup_class(self):
     init_region({"backend": "dogpile.cache.memory"}, "std")
     init_region({"backend": "dogpile.cache.memory"}, "obj")
Example #9
0
def setup_common():
    caching.init_region({"backend": "dogpile.cache.null"}, "std")
    caching.init_region({"backend": "dogpile.cache.null"}, "obj")
Example #10
0
    def __call__(self, filename, options):

        init_region({"backend": "dogpile.cache.memory"})

        int_filename = filename
        if re.match("^" + re.escape("./{}/templates".format(self.config["package"])), filename):
            try:
                empty_template = Template("")

                class Lookup(TemplateLookup):
                    @staticmethod
                    def get_template(uri):
                        return empty_template

                class MyTemplate(MakoTemplate):
                    def prepare(self, **options):
                        options.update({"input_encoding": self.encoding})
                        lookup = Lookup(**options)
                        if self.source:
                            self.tpl = Template(self.source, lookup=lookup, **options)
                        else:
                            self.tpl = Template(
                                uri=self.name,
                                filename=self.filename,
                                lookup=lookup, **options)

                try:
                    processed = template(
                        filename,
                        {
                            "request": _Request(self.config),
                            "lang": "fr",
                            "debug": False,
                            "extra_params": {},
                            "permalink_themes": "",
                            "fulltextsearch_groups": [],
                            "wfs_types": [],
                            "_": lambda x: x,
                        },
                        template_adapter=MyTemplate
                    )
                    int_filename = os.path.join(os.path.dirname(filename), "_" + os.path.basename(filename))
                    with open(int_filename, "wb") as file_open:
                        file_open.write(processed.encode("utf-8"))
                except Exception:
                    print(colorize(
                        "ERROR! Occurred during the '{}' template generation".format(filename),
                        RED
                    ))
                    print(colorize(traceback.format_exc(), RED))
                    if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") == "TRUE":
                        # Continue with the original one
                        int_filename = filename
                    else:
                        raise
            except Exception:
                print(traceback.format_exc())

        message_str = subprocess.check_output([
            "node", "tools/extract-messages.js", int_filename
        ]).decode("utf-8")
        if int_filename != filename:
            os.unlink(int_filename)
        try:
            messages = []
            for contexts, message in loads(message_str):
                for context in contexts.split(", "):
                    messages.append(Message(
                        None, message, None, [], "", "", context.split(":")
                    ))
            return messages
        except Exception:
            print(colorize("An error occurred", RED))
            print(colorize(message_str, RED))
            print("------")
            raise
Example #11
0
def includeme(config):
    """
    This function returns a Pyramid WSGI application.
    """

    settings = config.get_settings()

    config.include("c2cgeoportal_commons")

    call_hook(settings, "after_settings", settings)

    get_user_from_request = create_get_user_from_request(settings)
    config.add_request_method(get_user_from_request,
                              name="user",
                              property=True)
    config.add_request_method(get_user_from_request, name="get_user")

    # Configure 'locale' dir as the translation dir for c2cgeoportal app
    config.add_translation_dirs("c2cgeoportal_geoportal:locale/")

    config.include('c2cwsgiutils.pyramid.includeme')
    health_check = HealthCheck(config)

    # Initialise DBSessions
    init_dbsessions(settings, config, health_check)

    # Initialize the dbreflection module
    dbreflection.init()

    checker.init(config, health_check)
    check_collector.init(config, health_check)

    config.include("pyramid_closure")

    # dogpile.cache configuration
    if 'cache' in settings:
        caching.init_region(settings['cache'])
        from c2cgeoportal_commons.models.main import InvalidateCacheEvent

        @zope.event.classhandler.handler(InvalidateCacheEvent)
        def handle(event: InvalidateCacheEvent):
            del event
            caching.invalidate_region()

    # Register a tween to get back the cache buster path.
    config.add_tween(
        "c2cgeoportal_geoportal.lib.cacheversion.CachebusterTween")

    # Bind the mako renderer to other file extensions
    add_mako_renderer(config, ".html")
    add_mako_renderer(config, ".js")

    # Add the "geojson" renderer
    config.add_renderer("geojson", GeoJSON())

    # Add decimal json renderer
    config.add_renderer("decimaljson", DecimalJSON())

    # Add the "xsd" renderer
    config.add_renderer(
        "xsd", XSD(sequence_callback=dbreflection.xsd_sequence_callback))

    # Add the set_user_validator directive, and set a default user validator
    config.add_directive("set_user_validator", set_user_validator)
    config.set_user_validator(default_user_validator)

    if settings.get("ogcproxy_enable", False):  # pragma: no cover
        # Add an OGCProxy view
        config.add_route_predicate("ogc_server", OgcproxyRoutePredicate)
        config.add_route("ogcproxy", "/ogcproxy", ogc_server=True)
        config.add_view("papyrus_ogcproxy.views:ogcproxy",
                        route_name="ogcproxy")

    # Add routes to the mapserver proxy
    config.add_route_predicate("mapserverproxy", MapserverproxyRoutePredicate)
    config.add_route(
        "mapserverproxy",
        "/mapserv_proxy",
        mapserverproxy=True,
        pregenerator=C2CPregenerator(role=True),
    )

    # Add route to the tinyows proxy
    config.add_route(
        "tinyowsproxy",
        "/tinyows_proxy",
        pregenerator=C2CPregenerator(role=True),
    )

    # Add routes to csv view
    config.add_route("csvecho", "/csv", request_method="POST")

    # Add route to the export GPX/KML view
    config.add_route("exportgpxkml", "/exportgpxkml")

    # Add routes to the echo service
    config.add_route("echo", "/echo", request_method="POST")

    # Add routes to the entry view class
    config.add_route("base", "/", static=True)
    config.add_route("loginform", "/login.html", request_method="GET")
    add_cors_route(config, "/login", "login")
    config.add_route("login", "/login", request_method="POST")
    add_cors_route(config, "/logout", "login")
    config.add_route("logout", "/logout", request_method="GET")
    add_cors_route(config, "/loginchange", "login")
    config.add_route("loginchange", "/loginchange", request_method="POST")
    add_cors_route(config, "/loginresetpassword", "login")
    config.add_route("loginresetpassword",
                     "/loginresetpassword",
                     request_method="POST")
    add_cors_route(config, "/loginuser", "login")
    config.add_route("loginuser", "/loginuser", request_method="GET")
    config.add_route("testi18n", "/testi18n.html", request_method="GET")
    config.add_route("apijs", "/api.js", request_method="GET")
    config.add_route("xapijs", "/xapi.js", request_method="GET")
    config.add_route("apihelp", "/apihelp.html", request_method="GET")
    config.add_route("xapihelp", "/xapihelp.html", request_method="GET")
    config.add_route(
        "themes",
        "/themes",
        request_method="GET",
        pregenerator=C2CPregenerator(role=True),
    )
    config.add_route("invalidate", "/invalidate", request_method="GET")

    # Print proxy routes
    config.add_route("printproxy", "/printproxy", request_method="HEAD")
    add_cors_route(config, "/printproxy/*all", "print")
    config.add_route(
        "printproxy_capabilities",
        "/printproxy/capabilities.json",
        request_method="GET",
        pregenerator=C2CPregenerator(role=True),
    )
    config.add_route("printproxy_report_create",
                     "/printproxy/report.{format}",
                     request_method="POST",
                     header=JSON_CONTENT_TYPE)
    config.add_route("printproxy_status",
                     "/printproxy/status/{ref}.json",
                     request_method="GET")
    config.add_route("printproxy_cancel",
                     "/printproxy/cancel/{ref}",
                     request_method="DELETE")
    config.add_route("printproxy_report_get",
                     "/printproxy/report/{ref}",
                     request_method="GET")
    # For v2
    config.add_route(
        "printproxy_info",
        "/printproxy/info.json",
        request_method="GET",
        pregenerator=C2CPregenerator(role=True),
    )
    config.add_route(
        "printproxy_create",
        "/printproxy/create.json",
        request_method="POST",
    )
    config.add_route(
        "printproxy_get",
        "/printproxy/{file}.printout",
        request_method="GET",
    )

    # Full-text search routes
    add_cors_route(config, "/fulltextsearch", "fulltextsearch")
    config.add_route("fulltextsearch", "/fulltextsearch")

    # Access to raster data
    add_cors_route(config, "/raster", "raster")
    config.add_route("raster", "/raster", request_method="GET")

    add_cors_route(config, "/profile.{ext}", "profile")
    config.add_route("profile.csv", "/profile.csv", request_method="POST")
    config.add_route("profile.json", "/profile.json", request_method="POST")

    # Shortener
    add_cors_route(config, "/short/create", "shortener")
    config.add_route("shortener_create",
                     "/short/create",
                     request_method="POST")
    config.add_route("shortener_get", "/short/{ref}", request_method="GET")

    # Geometry processing
    config.add_route("difference", "/difference", request_method="POST")

    # PDF report tool
    config.add_route("pdfreport",
                     "/pdfreport/{layername}/{ids}",
                     request_method="GET")

    # Add routes for the "layers" web service
    add_cors_route(config, "/layers/*all", "layers")
    config.add_route("layers_count",
                     "/layers/{layer_id:\\d+}/count",
                     request_method="GET")
    config.add_route(
        "layers_metadata",
        "/layers/{layer_id:\\d+}/md.xsd",
        request_method="GET",
        pregenerator=C2CPregenerator(role=True),
    )
    config.add_route("layers_read_many",
                     "/layers/{layer_id:\\d+,?(\\d+,)*\\d*$}",
                     request_method="GET")  # supports URLs like /layers/1,2,3
    config.add_route("layers_read_one",
                     "/layers/{layer_id:\\d+}/{feature_id}",
                     request_method="GET")
    config.add_route("layers_create",
                     "/layers/{layer_id:\\d+}",
                     request_method="POST",
                     header=JSON_CONTENT_TYPE)
    config.add_route("layers_update",
                     "/layers/{layer_id:\\d+}/{feature_id}",
                     request_method="PUT",
                     header=JSON_CONTENT_TYPE)
    config.add_route("layers_delete",
                     "/layers/{layer_id:\\d+}/{feature_id}",
                     request_method="DELETE")
    config.add_route(
        "layers_enumerate_attribute_values",
        "/layers/{layer_name}/values/{field_name}",
        request_method="GET",
        pregenerator=C2CPregenerator(),
    )
    # There is no view corresponding to that route, it is to be used from
    # mako templates to get the root of the "layers" web service
    config.add_route("layers_root", "/layers/", request_method="HEAD")

    # Resource proxy (load external url, useful when loading non https content)
    config.add_route("resourceproxy", "/resourceproxy", request_method="GET")

    # Scan view decorator for adding routes
    config.scan(ignore=[
        "c2cgeoportal_geoportal.scripts", "c2cgeoportal_geoportal.wsgi_app"
    ])

    if "subdomains" in settings:  # pragma: no cover
        config.registry.registerUtility(MultiDomainStaticURLInfo(),
                                        IStaticURLInfo)

    # Add the static view (for static resources)
    _add_static_view(config, "static", "c2cgeoportal_geoportal:static")
    _add_static_view(config, "project", "c2cgeoportal_geoportal:project")

    add_admin_interface(config)
    add_static_view(config)

    # Handles the other HTTP errors raised by the views. Without that,
    # the client receives a status=200 without content.
    config.add_view(error_handler, context=HTTPException)
Example #12
0
 def test_nocache(self):
     init_region({"backend": "dogpile.cache.null"}, "std")
     assert get_cache_version() != get_cache_version()
Example #13
0
 def test_cache_invalidation(self):
     init_region({"backend": "dogpile.cache.memory"}, "std")
     cache_version = get_cache_version()
     invalidate_region()
     assert cache_version != get_cache_version()
Example #14
0
 def test_cache(self):
     init_region({"backend": "dogpile.cache.memory"}, "std")
     cache_version = get_cache_version()
     assert cache_version == get_cache_version()
Example #15
0
def includeme(config):
    """ This function returns a Pyramid WSGI application.
    """

    # update the settings object from the YAML application config file
    settings = config.get_settings()
    settings.update(c2c.template.get_config(settings.get("app.cfg")))

    call_hook(settings, "after_settings", settings)

    get_user_from_request = create_get_user_from_request(settings)
    config.add_request_method(get_user_from_request, name="user", property=True)
    config.add_request_method(get_user_from_request, name="get_user")

    # configure 'locale' dir as the translation dir for c2cgeoportal app
    config.add_translation_dirs("c2cgeoportal_geoportal:locale/")

    config.include(c2cwsgiutils.pyramid.includeme)
    health_check = HealthCheck(config)

    # Initialise DBSessions
    from c2cgeoportal_commons.models import init_dbsessions
    init_dbsessions(settings, config, health_check)

    # initialize the dbreflection module
    dbreflection.init()

    from c2cgeoportal_geoportal.lib import checker, check_collector
    checker.init(config, health_check)
    check_collector.init(config, health_check)

    config.include("pyramid_closure")

    # dogpile.cache configuration
    caching.init_region(settings["cache"])
    caching.invalidate_region()

    # Register a tween to get back the cache buster path.
    config.add_tween("c2cgeoportal_geoportal.lib.cacheversion.CachebusterTween")

    # bind the mako renderer to other file extensions
    add_mako_renderer(config, ".html")
    add_mako_renderer(config, ".js")

    # add the "geojson" renderer
    config.add_renderer("geojson", GeoJSON())

    # add decimal json renderer
    config.add_renderer("decimaljson", DecimalJSON())

    # add the "xsd" renderer
    config.add_renderer("xsd", XSD(
        sequence_callback=dbreflection.xsd_sequence_callback
    ))

    # add the set_user_validator directive, and set a default user
    # validator
    config.add_directive("set_user_validator", set_user_validator)
    config.set_user_validator(default_user_validator)

    if settings.get("ogcproxy_enable", False):  # pragma: no cover
        # add an OGCProxy view
        config.add_route_predicate("ogc_server", OgcproxyRoutePredicate)
        config.add_route(
            "ogcproxy", "/ogcproxy",
            ogc_server=True
        )
        config.add_view("papyrus_ogcproxy.views:ogcproxy", route_name="ogcproxy")

    # add routes to the mapserver proxy
    config.add_route_predicate("mapserverproxy", MapserverproxyRoutePredicate)
    config.add_route(
        "mapserverproxy", "/mapserv_proxy",
        mapserverproxy=True, pregenerator=C2CPregenerator(role=True),
    )

    # add route to the tinyows proxy
    config.add_route(
        "tinyowsproxy", "/tinyows_proxy",
        pregenerator=C2CPregenerator(role=True),
    )

    # add routes to csv view
    config.add_route("csvecho", "/csv", request_method="POST")

    # add route to the export GPX/KML view
    config.add_route("exportgpxkml", "/exportgpxkml")

    # add routes to the echo service
    config.add_route("echo", "/echo", request_method="POST")

    # add routes to the entry view class
    config.add_route("base", "/", static=True)
    config.add_route("loginform", "/login.html", request_method="GET")
    add_cors_route(config, "/login", "login")
    config.add_route("login", "/login", request_method="POST")
    add_cors_route(config, "/logout", "login")
    config.add_route("logout", "/logout", request_method="GET")
    add_cors_route(config, "/loginchange", "login")
    config.add_route("loginchange", "/loginchange", request_method="POST")
    add_cors_route(config, "/loginresetpassword", "login")
    config.add_route("loginresetpassword", "/loginresetpassword", request_method="POST")
    add_cors_route(config, "/loginuser", "login")
    config.add_route("loginuser", "/loginuser", request_method="GET")
    config.add_route("testi18n", "/testi18n.html", request_method="GET")
    config.add_route("apijs", "/api.js", request_method="GET")
    config.add_route("xapijs", "/xapi.js", request_method="GET")
    config.add_route("apihelp", "/apihelp.html", request_method="GET")
    config.add_route("xapihelp", "/xapihelp.html", request_method="GET")
    config.add_route(
        "themes", "/themes",
        request_method="GET",
        pregenerator=C2CPregenerator(role=True),
    )
    config.add_route("invalidate", "/invalidate", request_method="GET")

    # print proxy routes
    config.add_route("printproxy", "/printproxy", request_method="HEAD")
    add_cors_route(config, "/printproxy/*all", "print")
    config.add_route(
        "printproxy_capabilities", "/printproxy/capabilities.json",
        request_method="GET", pregenerator=C2CPregenerator(role=True),
    )
    config.add_route(
        "printproxy_report_create", "/printproxy/report.{format}",
        request_method="POST", header=JSON_CONTENT_TYPE
    )
    config.add_route(
        "printproxy_status", "/printproxy/status/{ref}.json",
        request_method="GET"
    )
    config.add_route(
        "printproxy_cancel", "/printproxy/cancel/{ref}",
        request_method="DELETE"
    )
    config.add_route(
        "printproxy_report_get", "/printproxy/report/{ref}",
        request_method="GET"
    )
    # v2
    config.add_route(
        "printproxy_info", "/printproxy/info.json",
        request_method="GET",
        pregenerator=C2CPregenerator(role=True),
    )
    config.add_route(
        "printproxy_create", "/printproxy/create.json",
        request_method="POST",
    )
    config.add_route(
        "printproxy_get", "/printproxy/{file}.printout",
        request_method="GET",
    )

    # full text search routes
    add_cors_route(config, "/fulltextsearch", "fulltextsearch")
    config.add_route("fulltextsearch", "/fulltextsearch")

    # Access to raster data
    add_cors_route(config, "/raster", "raster")
    config.add_route("raster", "/raster", request_method="GET")

    add_cors_route(config, "/profile.{ext}", "profile")
    config.add_route("profile.csv", "/profile.csv", request_method="POST")
    config.add_route("profile.json", "/profile.json", request_method="POST")

    # shortener
    add_cors_route(config, "/short/create", "shortener")
    config.add_route("shortener_create", "/short/create", request_method="POST")
    config.add_route("shortener_get", "/short/{ref}", request_method="GET")

    # Geometry processing
    config.add_route("difference", "/difference", request_method="POST")

    # PDF report tool
    config.add_route("pdfreport", "/pdfreport/{layername}/{ids}", request_method="GET")

    # add routes for the "layers" web service
    add_cors_route(config, "/layers/*all", "layers")
    config.add_route(
        "layers_count", "/layers/{layer_id:\\d+}/count",
        request_method="GET"
    )
    config.add_route(
        "layers_metadata", "/layers/{layer_id:\\d+}/md.xsd",
        request_method="GET",
        pregenerator=C2CPregenerator(role=True),
    )
    config.add_route(
        "layers_read_many",
        "/layers/{layer_id:\\d+,?(\\d+,)*\\d*$}",
        request_method="GET")  # supports URLs like /layers/1,2,3
    config.add_route(
        "layers_read_one", "/layers/{layer_id:\\d+}/{feature_id}",
        request_method="GET")
    config.add_route(
        "layers_create", "/layers/{layer_id:\\d+}",
        request_method="POST", header=JSON_CONTENT_TYPE)
    config.add_route(
        "layers_update", "/layers/{layer_id:\\d+}/{feature_id}",
        request_method="PUT", header=JSON_CONTENT_TYPE)
    config.add_route(
        "layers_delete", "/layers/{layer_id:\\d+}/{feature_id}",
        request_method="DELETE")
    config.add_route(
        "layers_enumerate_attribute_values",
        "/layers/{layer_name}/values/{field_name}",
        request_method="GET",
        pregenerator=C2CPregenerator(),
    )
    # there is no view corresponding to that route, it is to be used from
    # mako templates to get the root of the "layers" web service
    config.add_route("layers_root", "/layers/", request_method="HEAD")

    # Resource proxy (load external url, useful when loading non https content)
    config.add_route("resourceproxy", "/resourceproxy", request_method="GET")

    # scan view decorator for adding routes
    config.scan(ignore=["c2cgeoportal_geoportal.scripts", "c2cgeoportal_geoportal.wsgi_app"])

    if "subdomains" in settings:  # pragma: no cover
        config.registry.registerUtility(
            MultiDomainStaticURLInfo(), IStaticURLInfo)

    # add the static view (for static resources)
    _add_static_view(config, "static", "c2cgeoportal_geoportal:static")
    _add_static_view(config, "project", "c2cgeoportal_geoportal:project")

    add_admin_interface(config)
    add_static_view(config)

    # Handles the other HTTP errors raised by the views. Without that,
    # the client receives a status=200 without content.
    config.add_view(error_handler, context=HTTPException)
Example #16
0
 def setup_class(cls):
     init_region({"backend": "dogpile.cache.memory"})
Example #17
0
    def test_get_class(self):
        from geoalchemy2 import Geometry

        from c2cgeoportal_geoportal.lib.dbreflection import _AssociationProxy, get_class

        init_region({"backend": "dogpile.cache.memory"}, "std")
        init_region({"backend": "dogpile.cache.memory"}, "obj")

        self._create_table("table_a")
        modelclass = get_class("table_a")

        # test the class
        assert modelclass.__name__.startswith("Table_a_")
        self.assertEqual(modelclass.__table__.name, "table_a")
        self.assertEqual(modelclass.__table__.schema, "public")

        self.assertTrue(isinstance(modelclass.point.type, Geometry))
        self.assertTrue(isinstance(modelclass.linestring.type, Geometry))
        self.assertTrue(isinstance(modelclass.polygon.type, Geometry))
        self.assertTrue(isinstance(modelclass.multipoint.type, Geometry))
        self.assertTrue(isinstance(modelclass.multilinestring.type, Geometry))
        self.assertTrue(isinstance(modelclass.multipolygon.type, Geometry))

        self.assertTrue(isinstance(modelclass.child1, _AssociationProxy))
        self.assertTrue(modelclass.child1.nullable)
        self.assertEqual(modelclass.child1_id.info.get("association_proxy"), "child1")
        self.assertTrue(isinstance(modelclass.child2, _AssociationProxy))
        self.assertFalse(modelclass.child2.nullable)
        self.assertEqual(modelclass.child2_id.info.get("association_proxy"), "child2")

        child1_asso_proxy = getattr(modelclass, modelclass.child1_id.info["association_proxy"])
        self.assertEqual("name", child1_asso_proxy.value_attr)
        self.assertEqual("name", child1_asso_proxy.order_by)

        # test the Table object
        table = modelclass.__table__
        self.assertTrue("id" in table.c)
        self.assertTrue("child1_id" in table.c)
        self.assertTrue("child2_id" in table.c)
        self.assertTrue("point" in table.c)
        self.assertTrue("linestring" in table.c)
        self.assertTrue("polygon" in table.c)
        self.assertTrue("multipoint" in table.c)
        self.assertTrue("multilinestring" in table.c)
        self.assertTrue("multipolygon" in table.c)
        col_child1_id = table.c["child1_id"]
        self.assertEqual(col_child1_id.name, "child1_id")
        col_child2_id = table.c["child2_id"]
        self.assertEqual(col_child2_id.name, "child2_id")
        col_point = table.c["point"]
        self.assertEqual(col_point.name, "point")
        self.assertEqual(col_point.type.geometry_type, "POINT")
        col_linestring = table.c["linestring"]
        self.assertEqual(col_linestring.name, "linestring")
        self.assertEqual(col_linestring.type.geometry_type, "LINESTRING")
        col_polygon = table.c["polygon"]
        self.assertEqual(col_polygon.name, "polygon")
        self.assertEqual(col_polygon.type.geometry_type, "POLYGON")
        col_multipoint = table.c["multipoint"]
        self.assertEqual(col_multipoint.name, "multipoint")
        self.assertEqual(col_multipoint.type.geometry_type, "MULTIPOINT")
        col_multilinestring = table.c["multilinestring"]
        self.assertEqual(col_multilinestring.name, "multilinestring")
        self.assertEqual(col_multilinestring.type.geometry_type, "MULTILINESTRING")
        col_multipolygon = table.c["multipolygon"]
        self.assertEqual(col_multipolygon.name, "multipolygon")
        self.assertEqual(col_multipolygon.type.geometry_type, "MULTIPOLYGON")

        assert get_class("table_a") is modelclass
Example #18
0
def setup_common():
    logging.getLogger("c2cgeoportal_geoportal").setLevel(logging.DEBUG)

    caching.init_region({"backend": "dogpile.cache.null"}, "std")
    caching.init_region({"backend": "dogpile.cache.null"}, "obj")
Example #19
0
    def __call__(self, filename, options):

        init_region({"backend": "dogpile.cache.memory"})

        int_filename = filename
        if re.match(
                "^" +
                re.escape("./{}/templates".format(self.config["package"])),
                filename):
            try:
                empty_template = Template("")

                class Lookup(TemplateLookup):
                    @staticmethod
                    def get_template(uri):
                        del uri  # unused
                        return empty_template

                class MyTemplate(MakoTemplate):
                    tpl = None

                    def prepare(self, **kwargs):
                        options.update({"input_encoding": self.encoding})
                        lookup = Lookup(**kwargs)
                        if self.source:
                            self.tpl = Template(self.source,
                                                lookup=lookup,
                                                **kwargs)
                        else:
                            self.tpl = Template(uri=self.name,
                                                filename=self.filename,
                                                lookup=lookup,
                                                **kwargs)

                try:
                    processed = template(filename, {
                        "request": _Request(self.config),
                        "lang": "fr",
                        "debug": False,
                        "extra_params": {},
                        "permalink_themes": "",
                        "fulltextsearch_groups": [],
                        "wfs_types": [],
                        "_": lambda x: x,
                    },
                                         template_adapter=MyTemplate)
                    int_filename = os.path.join(
                        os.path.dirname(filename),
                        "_" + os.path.basename(filename))
                    with open(int_filename, "wb") as file_open:
                        file_open.write(processed.encode("utf-8"))
                except Exception:
                    print(
                        colorize(
                            "ERROR! Occurred during the '{}' template generation"
                            .format(filename), RED))
                    print(colorize(traceback.format_exc(), RED))
                    if os.environ.get("IGNORE_I18N_ERRORS", "FALSE") == "TRUE":
                        # Continue with the original one
                        int_filename = filename
                    else:
                        raise
            except Exception:
                print(traceback.format_exc())

        message_str = subprocess.check_output(
            ["node", "tools/extract-messages.js",
             int_filename]).decode("utf-8")
        if int_filename != filename:
            os.unlink(int_filename)
        try:
            messages = []
            for contexts, message in loads(message_str):
                for context in contexts.split(", "):
                    messages.append(
                        Message(None, message, None, [], "", "",
                                context.split(":")))
            return messages
        except Exception:
            print(colorize("An error occurred", RED))
            print(colorize(message_str, RED))
            print("------")
            raise
Example #20
0
    def __call__(
        self,
        filename: str,
        options: Dict[str, Any],
        fileobj: Optional[Dict[str, Any]] = None,
        lineno: int = 0,
    ) -> List[Message]:
        del fileobj, lineno

        print(f"Running {self.__class__.__name__} on {filename}")

        cleaner = self.get_message_cleaner(filename)

        init_region({"backend": "dogpile.cache.memory"}, "std")
        init_region({"backend": "dogpile.cache.memory"}, "obj")

        int_filename = filename
        if re.match("^" + re.escape(f"./{self.config['package']}/templates"),
                    filename):
            try:
                empty_template = Template("")  # nosec

                class Lookup(TemplateLookup):  # type: ignore
                    def get_template(self, uri: str) -> Template:
                        del uri  # unused
                        return empty_template

                class MyTemplate(MakoTemplate):  # type: ignore
                    tpl = None

                    def prepare(self, **kwargs: Any) -> None:
                        options.update({"input_encoding": self.encoding})
                        lookup = Lookup(**kwargs)
                        if self.source:
                            self.tpl = Template(self.source,
                                                lookup=lookup,
                                                **kwargs)  # nosec
                        else:
                            self.tpl = Template(  # nosec
                                uri=self.name,
                                filename=self.filename,
                                lookup=lookup,
                                **kwargs)

                try:
                    request = pyramid.threadlocal.get_current_request()
                    request = _Request() if request is None else request
                    processed = template(
                        filename,
                        {
                            "request": request,
                            "lang": "fr",
                            "debug": False,
                            "extra_params": {},
                            "permalink_themes": "",
                            "fulltextsearch_groups": [],
                            "wfs_types": [],
                            "_": lambda x: x,
                        },
                        template_adapter=MyTemplate,
                    )
                    int_filename = os.path.join(
                        os.path.dirname(filename),
                        "_" + os.path.basename(filename))
                    with open(int_filename, "wb") as file_open:
                        file_open.write(processed.encode("utf-8"))
                except Exception:
                    print(
                        colorize(
                            f"ERROR! Occurred during the '{filename}' template generation",
                            Color.RED))
                    print(colorize(traceback.format_exc(), Color.RED))
                    if _get_config_str("IGNORE_I18N_ERRORS",
                                       "FALSE") == "TRUE":
                        # Continue with the original one
                        int_filename = filename
                    else:
                        raise
            except Exception:
                print(traceback.format_exc())

        # Path in geomapfish-tools
        script_path = "geoportal/tools/extract-messages.js"
        if not os.path.isfile(script_path):
            # Path in geomapfish runner
            script_path = "/app/tools/extract-messages.js"
        message_str = subprocess.check_output(
            ["node", script_path, int_filename]).decode("utf-8")
        if int_filename != filename:
            os.unlink(int_filename)
        try:
            messages = []
            for contexts, message in json.loads(message_str):
                assert message is not None
                message = cleaner(message)
                for context in contexts.split(", "):
                    messages.append(
                        Message(None, message, None, [], "", "",
                                context.split(":")))
            return messages
        except Exception:
            print(colorize("An error occurred", Color.RED))
            print(colorize(message_str, Color.RED))
            print("------")
            raise
Example #21
0
def includeme(config: pyramid.config.Configurator):
    """
    This function returns a Pyramid WSGI application.
    """

    settings = config.get_settings()

    config.include("c2cgeoportal_commons")

    call_hook(settings, "after_settings", settings)

    get_user_from_request = create_get_user_from_request(settings)
    config.add_request_method(get_user_from_request,
                              name="user",
                              property=True)
    config.add_request_method(get_user_from_request, name="get_user")

    # Configure 'locale' dir as the translation dir for c2cgeoportal app
    config.add_translation_dirs("c2cgeoportal_geoportal:locale/")

    config.include('c2cwsgiutils.pyramid.includeme')
    health_check = HealthCheck(config)

    # Initialise DBSessions
    init_dbsessions(settings, config, health_check)

    # Initialize the dbreflection module
    dbreflection.init()

    checker.init(config, health_check)
    check_collector.init(config, health_check)

    # dogpile.cache configuration
    if 'cache' in settings:
        caching.init_region(settings['cache'])
        from c2cgeoportal_commons.models.main import InvalidateCacheEvent

        @zope.event.classhandler.handler(InvalidateCacheEvent)
        def handle(event: InvalidateCacheEvent):  # pylint: disable=unused-variable
            del event
            caching.invalidate_region()

    # Register a tween to get back the cache buster path.
    if 'cache_path' not in config.get_settings():
        config.get_settings()['cache_path'] = ['static']
    config.add_tween(
        "c2cgeoportal_geoportal.lib.cacheversion.CachebusterTween")
    config.add_tween("c2cgeoportal_geoportal.lib.headers.HeadersTween")

    # Bind the mako renderer to other file extensions
    add_mako_renderer(config, ".html")
    add_mako_renderer(config, ".js")

    # Add the "geojson" renderer
    config.add_renderer("geojson", GeoJSON())

    # Add decimal json renderer
    config.add_renderer("decimaljson", DecimalJSON())

    # Add the "xsd" renderer
    config.add_renderer("xsd", XSD(include_foreign_keys=True))

    # Add the set_user_validator directive, and set a default user validator
    config.add_directive("set_user_validator", set_user_validator)
    config.set_user_validator(default_user_validator)

    config.add_route('dynamic', '/dynamic.json', request_method="GET")

    # Add routes to the mapserver proxy
    config.add_route_predicate("mapserverproxy", MapserverproxyRoutePredicate)
    config.add_route("mapserverproxy",
                     "/mapserv_proxy",
                     mapserverproxy=True,
                     pregenerator=C2CPregenerator(role=True),
                     request_method="GET")
    config.add_route("mapserverproxy_post",
                     "/mapserv_proxy",
                     mapserverproxy=True,
                     pregenerator=C2CPregenerator(role=True),
                     request_method="POST")
    add_cors_route(config, "/mapserv_proxy", "mapserver")

    # Add route to the tinyows proxy
    config.add_route(
        "tinyowsproxy",
        "/tinyows_proxy",
        pregenerator=C2CPregenerator(role=True),
    )

    # Add routes to the entry view class
    config.add_route("base", "/", static=True)
    config.add_route("loginform", "/login.html", request_method="GET")
    add_cors_route(config, "/login", "login")
    config.add_route("login", "/login", request_method="POST")
    add_cors_route(config, "/logout", "login")
    config.add_route("logout", "/logout", request_method="GET")
    add_cors_route(config, "/loginchange", "login")
    config.add_route("loginchange", "/loginchange", request_method="POST")
    add_cors_route(config, "/loginresetpassword", "login")
    config.add_route("loginresetpassword",
                     "/loginresetpassword",
                     request_method="POST")
    add_cors_route(config, "/loginuser", "login")
    config.add_route("loginuser", "/loginuser", request_method="GET")
    config.add_route("testi18n", "/testi18n.html", request_method="GET")

    config.add_renderer(".map", AssetRendererFactory)
    config.add_renderer(".css", AssetRendererFactory)
    config.add_renderer(".ico", AssetRendererFactory)
    config.add_route("localejson", "/locale.json", request_method="GET")

    # Cannot be at the header to do not load the model too early
    from c2cgeoportal_geoportal.views.entry import Entry

    def add_api_route(name: str, attr: str, path: str, renderer: str):
        config.add_route(name, path, request_method="GET")
        config.add_view(Entry, attr=attr, route_name=name, renderer=renderer)

    add_api_route('favicon', 'favicon', '/favicon.ico',
                  '/etc/geomapfish/static/images/favicon.ico')
    add_api_route('apijs', 'apijs', '/api.js', "/etc/static-ngeo/api.js")
    add_api_route('apijsmap', 'apijsmap', '/api.js.map',
                  "/etc/static-ngeo/api.js.map")
    add_api_route('apicss', 'apicss', '/api.css', "/etc/static-ngeo/api.css")
    add_api_route('apihelp', 'apihelp', '/apihelp/index.html',
                  "/etc/geomapfish/static/apihelp/index.html")
    c2cgeoportal_geoportal.views.add_redirect(config, 'apihelp_redirect',
                                              '/apihelp.html',
                                              '/apihelp/index.html')

    config.add_route(
        "themes",
        "/themes",
        request_method="GET",
        pregenerator=C2CPregenerator(role=True),
    )

    config.add_route("invalidate", "/invalidate", request_method="GET")

    # Print proxy routes
    config.add_route("printproxy", "/printproxy", request_method="HEAD")
    add_cors_route(config, "/printproxy/*all", "print")
    config.add_route(
        "printproxy_capabilities",
        "/printproxy/capabilities.json",
        request_method="GET",
        pregenerator=C2CPregenerator(role=True),
    )
    config.add_route("printproxy_report_create",
                     "/printproxy/report.{format}",
                     request_method="POST",
                     header=JSON_CONTENT_TYPE)
    config.add_route("printproxy_status",
                     "/printproxy/status/{ref}.json",
                     request_method="GET")
    config.add_route("printproxy_cancel",
                     "/printproxy/cancel/{ref}",
                     request_method="DELETE")
    config.add_route("printproxy_report_get",
                     "/printproxy/report/{ref}",
                     request_method="GET")

    # Full-text search routes
    add_cors_route(config, "/search", "fulltextsearch")
    config.add_route("fulltextsearch", "/search", request_method="GET")

    # Access to raster data
    add_cors_route(config, "/raster", "raster")
    config.add_route("raster", "/raster", request_method="GET")

    add_cors_route(config, "/profile.json", "profile")
    config.add_route("profile.json", "/profile.json", request_method="POST")

    # Shortener
    add_cors_route(config, "/short/create", "shortener")
    config.add_route("shortener_create",
                     "/short/create",
                     request_method="POST")
    config.add_route("shortener_get", "/s/{ref}", request_method="GET")

    # Geometry processing
    config.add_route("difference", "/difference", request_method="POST")

    # PDF report tool
    config.add_route("pdfreport",
                     "/pdfreport/{layername}/{ids}",
                     request_method="GET")

    # Add routes for the "layers" web service
    add_cors_route(config, "/layers/*all", "layers")
    config.add_route("layers_count",
                     "/layers/{layer_id:\\d+}/count",
                     request_method="GET")
    config.add_route(
        "layers_metadata",
        "/layers/{layer_id:\\d+}/md.xsd",
        request_method="GET",
        pregenerator=C2CPregenerator(role=True),
    )
    config.add_route("layers_read_many",
                     "/layers/{layer_id:\\d+,?(\\d+,)*\\d*$}",
                     request_method="GET")  # supports URLs like /layers/1,2,3
    config.add_route("layers_read_one",
                     "/layers/{layer_id:\\d+}/{feature_id}",
                     request_method="GET")
    config.add_route("layers_create",
                     "/layers/{layer_id:\\d+}",
                     request_method="POST",
                     header=GEOJSON_CONTENT_TYPE)
    config.add_route("layers_update",
                     "/layers/{layer_id:\\d+}/{feature_id}",
                     request_method="PUT",
                     header=GEOJSON_CONTENT_TYPE)
    config.add_route("layers_delete",
                     "/layers/{layer_id:\\d+}/{feature_id}",
                     request_method="DELETE")
    config.add_route(
        "layers_enumerate_attribute_values",
        "/layers/{layer_name}/values/{field_name}",
        request_method="GET",
        pregenerator=C2CPregenerator(),
    )
    # There is no view corresponding to that route, it is to be used from
    # mako templates to get the root of the "layers" web service
    config.add_route("layers_root", "/layers", request_method="HEAD")

    # Resource proxy (load external url, useful when loading non https content)
    config.add_route("resourceproxy", "/resourceproxy", request_method="GET")

    # Dev
    config.add_route("dev", "/dev/*path", request_method="GET")

    # Used memory in caches
    config.add_route("memory", "/memory", request_method="GET")

    # Scan view decorator for adding routes
    config.scan(ignore=[
        "c2cgeoportal_geoportal.lib", "c2cgeoportal_geoportal.scaffolds",
        "c2cgeoportal_geoportal.scripts"
    ])

    add_admin_interface(config)

    # Add the project static view with cache buster
    from c2cgeoportal_geoportal.lib.cacheversion import version_cache_buster
    config.add_static_view(
        name="static",
        path="/etc/geomapfish/static",
        cache_max_age=int(config.get_settings()["default_max_age"]),
    )
    config.add_cache_buster("/etc/geomapfish/static", version_cache_buster)

    # Add the project static view without cache buster
    config.add_static_view(
        name="static-ngeo",
        path="/etc/static-ngeo",
        cache_max_age=int(config.get_settings()["default_max_age"]),
    )

    # Handles the other HTTP errors raised by the views. Without that,
    # the client receives a status=200 without content.
    config.add_view(error_handler, context=HTTPException)

    c2cwsgiutils.index.additional_title = '<div class="row"><div class="col-lg-3"><h2>GeoMapFish</h2>' \
        '</div><div class="col-lg">'
    c2cwsgiutils.index.additional_auth.extend([
        '<a href="../tiles/admin/">TileCloud chain admin</a><br>',
        '<a href="../tiles/c2c/">TileCloud chain c2c tools</a><br>',
        '<a href="../invalidate">Invalidate the cache</a><br>',
        '<a href="../memory">Memory status</a><br>',
    ])
    if config.get_settings().get('enable_admin_interface', False):
        c2cwsgiutils.index.additional_noauth.append(
            '<a href="../admin/">Admin</a><br>')

    c2cwsgiutils.index.additional_noauth.append(
        '</div></div><div class="row"><div class="col-lg-3"><h3>Interfaces</h3></div><div class="col-lg">'
    )
    c2cwsgiutils.index.additional_noauth.append(
        '<a href="../">Default</a><br>')
    for interface in config.get_settings().get("interfaces", []):
        if not interface.get('default', False):
            c2cwsgiutils.index.additional_noauth.append(
                '<a href="../{interface}">{interface}</a><br>'.format(
                    interface=interface['name']))
    c2cwsgiutils.index.additional_noauth.append(
        '<a href="../apihelp/index.html">API help</a><br>')
    c2cwsgiutils.index.additional_noauth.append('</div></div><hr>')
Example #22
0
def includeme(config: pyramid.config.Configurator) -> None:
    """Get the Pyramid WSGI application."""
    settings = config.get_settings()

    if "available_locale_names" not in settings:
        settings["available_locale_names"] = available_locale_names()

    call_hook(settings, "after_settings", settings)

    get_user_from_request = create_get_user_from_request(settings)
    config.add_request_method(get_user_from_request,
                              name="user",
                              property=True)
    config.add_request_method(get_user_from_request, name="get_user")
    # Be able for an organization to override the method to use alternate:
    # - Organization roles name for the standard roles 'anonymous', 'registered' and 'intranet'.
    config.add_request_method(lambda request, role_type: role_type,
                              name="get_organization_role")
    # - Organization print URL
    config.add_request_method(
        lambda request: request.registry.settings["print_url"],
        name="get_organization_print_url")
    # - Organization interface name (in the config and in the admin interface)
    config.add_request_method(lambda request, interface: interface,
                              name="get_organization_interface")

    # Configure 'locale' dir as the translation dir for c2cgeoportal app
    config.add_translation_dirs("c2cgeoportal_geoportal:locale/")

    config.include("pyramid_mako")
    config.include("c2cwsgiutils.pyramid.includeme")
    health_check = HealthCheck(config)
    config.registry["health_check"] = health_check

    metrics_config = config.registry.settings["metrics"]
    if metrics_config["memory_maps_rss"]:
        add_provider(MemoryMapProvider("rss"))
    if metrics_config["memory_maps_size"]:
        add_provider(MemoryMapProvider("size"))
    if metrics_config["memory_cache"]:
        add_provider(
            MemoryCacheSizeProvider(
                metrics_config.get("memory_cache_all", False)))
    if metrics_config["raster_data"]:
        add_provider(RasterDataSizeProvider())
    if metrics_config["total_python_object_memory"]:
        add_provider(TotalPythonObjectMemoryProvider())

    # Initialise DBSessions
    init_db_sessions(settings, config, health_check)

    checker.init(config, health_check)
    check_collector.init(config, health_check)

    # dogpile.cache configuration
    if "cache" in settings:
        register_backend("c2cgeoportal.hybrid",
                         "c2cgeoportal_geoportal.lib.caching",
                         "HybridRedisBackend")
        register_backend("c2cgeoportal.hybridsentinel",
                         "c2cgeoportal_geoportal.lib.caching",
                         "HybridRedisSentinelBackend")
        for name, cache_config in settings["cache"].items():
            caching.init_region(cache_config, name)

            @zope.event.classhandler.handler(InvalidateCacheEvent
                                             )  # type: ignore
            def handle(event: InvalidateCacheEvent) -> None:
                del event
                caching.invalidate_region()
                if caching.MEMORY_CACHE_DICT:
                    caching.get_region("std").delete_multi(
                        list(caching.MEMORY_CACHE_DICT.keys()))
                caching.MEMORY_CACHE_DICT.clear()

    # Register a tween to get back the cache buster path.
    if "cache_path" not in config.get_settings():
        config.get_settings()["cache_path"] = ["static", "static-geomapfish"]
    config.add_tween(
        "c2cgeoportal_geoportal.lib.cacheversion.CachebusterTween")
    config.add_tween("c2cgeoportal_geoportal.lib.headers.HeadersTween")

    # Bind the mako renderer to other file extensions
    add_mako_renderer(config, ".html")
    add_mako_renderer(config, ".js")

    # Add the "geojson" renderer
    config.add_renderer("geojson", GeoJSON())

    # Add the "xsd" renderer
    config.add_renderer("xsd", XSD(include_foreign_keys=True))

    # Add the set_user_validator directive, and set a default user validator
    config.add_directive("set_user_validator", set_user_validator)
    config.set_user_validator(default_user_validator)

    config.add_route("oauth2token", "/oauth/token", request_method="POST")
    config.add_route("oauth2loginform", "/oauth/login", request_method="GET")
    config.add_route("notlogin", "/notlogin", request_method="GET")

    config.add_route("dynamic", "/dynamic.json", request_method="GET")

    # Add routes to the mapserver proxy
    config.add_route_predicate("mapserverproxy", MapserverproxyRoutePredicate)
    config.add_route(
        "mapserverproxy",
        "/mapserv_proxy",
        mapserverproxy=True,
        pregenerator=C2CPregenerator(role=True),
        request_method="GET",
    )
    config.add_route(
        "mapserverproxy_post",
        "/mapserv_proxy",
        mapserverproxy=True,
        pregenerator=C2CPregenerator(role=True),
        request_method="POST",
    )
    # The tow next views are used to serve the application on the URL /mapserv_proxy/<ogc server name>
    # instead of /mapserv_proxy?ogcserver=<ogc server name>, required for QGIS server landing page
    config.add_route(
        "mapserverproxy_get_path",
        "/mapserv_proxy/{ogcserver}/*path",
        mapserverproxy=True,
        pregenerator=C2CPregenerator(role=True),
        request_method="GET",
    )
    config.add_route(
        "mapserverproxy_post_path",
        "/mapserv_proxy/{ogcserver}/*path",
        mapserverproxy=True,
        pregenerator=C2CPregenerator(role=True),
        request_method="POST",
    )
    add_cors_route(config, "/mapserv_proxy", "mapserver")

    # Add route to the tinyows proxy
    config.add_route("tinyowsproxy",
                     "/tinyows_proxy",
                     pregenerator=C2CPregenerator(role=True))

    # Add routes to the entry view class
    config.add_route("base", "/", static=True)
    config.add_route("loginform", "/login.html", request_method="GET")
    add_cors_route(config, "/login", "login")
    config.add_route("login", "/login", request_method="POST")
    add_cors_route(config, "/logout", "login")
    config.add_route("logout", "/logout", request_method="GET")
    add_cors_route(config, "/loginchangepassword", "login")
    config.add_route("change_password",
                     "/loginchangepassword",
                     request_method="POST")
    add_cors_route(config, "/loginresetpassword", "login")
    config.add_route("loginresetpassword",
                     "/loginresetpassword",
                     request_method="POST")
    add_cors_route(config, "/loginuser", "login")
    config.add_route("loginuser", "/loginuser", request_method="GET")
    config.add_route("testi18n", "/testi18n.html", request_method="GET")

    config.add_renderer(".map", AssetRendererFactory)
    config.add_renderer(".css", AssetRendererFactory)
    config.add_renderer(".ico", AssetRendererFactory)
    config.add_route("localejson", "/locale.json", request_method="GET")
    config.add_route("localepot", "/locale.pot", request_method="GET")

    def add_static_route(name: str, attr: str, path: str,
                         renderer: str) -> None:
        config.add_route(name, path, request_method="GET")
        config.add_view(Entry, attr=attr, route_name=name, renderer=renderer)

    add_static_route("favicon", "favicon", "/favicon.ico",
                     "/etc/geomapfish/static/images/favicon.ico")
    add_static_route("robot.txt", "robot_txt", "/robot.txt",
                     "/etc/geomapfish/static/robot.txt")
    config.add_route("apijs", "/api.js", request_method="GET")
    add_static_route("apijsmap", "apijsmap", "/api.js.map",
                     "/etc/static-ngeo/api.js.map")
    add_static_route("apicss", "apicss", "/api.css",
                     "/etc/static-ngeo/api.css")
    add_static_route("apihelp", "apihelp", "/apihelp/index.html",
                     "/etc/geomapfish/static/apihelp/index.html")
    c2cgeoportal_geoportal.views.add_redirect(config, "apihelp_redirect",
                                              "/apihelp.html", "apihelp")

    config.add_route("themes",
                     "/themes",
                     request_method="GET",
                     pregenerator=C2CPregenerator(role=True))

    config.add_route("invalidate", "/invalidate", request_method="GET")

    # Print proxy routes
    config.add_route("printproxy", "/printproxy", request_method="HEAD")
    add_cors_route(config, "/printproxy/*all", "print")
    config.add_route(
        "printproxy_capabilities",
        "/printproxy/capabilities.json",
        request_method="GET",
        pregenerator=C2CPregenerator(role=True),
    )
    config.add_route(
        "printproxy_report_create",
        "/printproxy/report.{format}",
        request_method="POST",
        header=JSON_CONTENT_TYPE,
    )
    config.add_route("printproxy_status",
                     "/printproxy/status/{ref}.json",
                     request_method="GET")
    config.add_route("printproxy_cancel",
                     "/printproxy/cancel/{ref}",
                     request_method="DELETE")
    config.add_route("printproxy_report_get",
                     "/printproxy/report/{ref}",
                     request_method="GET")

    # Full-text search routes
    add_cors_route(config, "/search", "fulltextsearch")
    config.add_route("fulltextsearch", "/search", request_method="GET")

    # Access to raster data
    add_cors_route(config, "/raster", "raster")
    config.add_route("raster", "/raster", request_method="GET")

    add_cors_route(config, "/profile.json", "profile")
    config.add_route("profile.json", "/profile.json", request_method="POST")

    # Access to vector tiles
    add_cors_route(config, "/vector_tiles", "vector_tiles")
    config.add_route("vector_tiles",
                     "/vector_tiles/{layer_name}/{z}/{x}/{y}.pbf",
                     request_method="GET")
    # There is no view corresponding to that route, it is to be used from
    # mako templates to get the root of the "vector_tiles" web service
    config.add_route("vector_tiles_root",
                     "/vector_tiles",
                     request_method="HEAD")

    # Shortener
    add_cors_route(config, "/short/create", "shortener")
    config.add_route("shortener_create",
                     "/short/create",
                     request_method="POST")
    config.add_route("shortener_get", "/s/{ref}", request_method="GET")

    # Geometry processing
    config.add_route("difference", "/difference", request_method="POST")

    # PDF report tool
    config.add_route("pdfreport",
                     "/pdfreport/{layername}/{ids}",
                     request_method="GET")

    # Add routes for the "layers" web service
    add_cors_route(config, "/layers/*all", "layers")
    config.add_route("layers_count",
                     "/layers/{layer_id:\\d+}/count",
                     request_method="GET")
    config.add_route(
        "layers_metadata",
        "/layers/{layer_id:\\d+}/md.xsd",
        request_method="GET",
        pregenerator=C2CPregenerator(role=True),
    )
    config.add_route("layers_read_many",
                     "/layers/{layer_id:\\d+,?(\\d+,)*\\d*$}",
                     request_method="GET")  # supports URLs like /layers/1,2,3
    config.add_route("layers_read_one",
                     "/layers/{layer_id:\\d+}/{feature_id}",
                     request_method="GET")
    config.add_route("layers_create",
                     "/layers/{layer_id:\\d+}",
                     request_method="POST",
                     header=GEOJSON_CONTENT_TYPE)
    config.add_route(
        "layers_update",
        "/layers/{layer_id:\\d+}/{feature_id}",
        request_method="PUT",
        header=GEOJSON_CONTENT_TYPE,
    )
    config.add_route("layers_delete",
                     "/layers/{layer_id:\\d+}/{feature_id}",
                     request_method="DELETE")
    config.add_route(
        "layers_enumerate_attribute_values",
        "/layers/{layer_name}/values/{field_name}",
        request_method="GET",
        pregenerator=C2CPregenerator(),
    )
    # There is no view corresponding to that route, it is to be used from
    # mako templates to get the root of the "layers" web service
    config.add_route("layers_root", "/layers", request_method="HEAD")

    # Resource proxy (load external url, useful when loading non https content)
    config.add_route("resourceproxy", "/resourceproxy", request_method="GET")

    # Dev
    config.add_route("dev", "/dev/*path", request_method="GET")

    # Used memory in caches
    config.add_route("memory", "/memory", request_method="GET")

    # Scan view decorator for adding routes
    config.scan(ignore=[
        "c2cgeoportal_geoportal.lib",
        "c2cgeoportal_geoportal.scaffolds",
        "c2cgeoportal_geoportal.scripts",
    ])

    add_admin_interface(config)
    add_getitfixed(config)

    # Add the project static view with cache buster
    config.add_static_view(
        name="static",
        path="/etc/geomapfish/static",
        cache_max_age=int(config.get_settings()["default_max_age"]),
    )
    config.add_cache_buster("/etc/geomapfish/static", version_cache_buster)

    # Add the project static view without cache buster
    config.add_static_view(
        name="static-ngeo",
        path="/etc/static-ngeo",
        cache_max_age=int(config.get_settings()["default_max_age"]),
    )

    # Add the c2cgeoportal static view with cache buster
    config.add_static_view(
        name="static-geomapfish",
        path="c2cgeoportal_geoportal:static",
        cache_max_age=int(config.get_settings()["default_max_age"]),
    )
    config.add_cache_buster("c2cgeoportal_geoportal:static",
                            version_cache_buster)

    # Add the project static view without cache buster
    config.add_static_view(
        name="static-ngeo-dist",
        path="/opt/c2cgeoportal/geoportal/node_modules/ngeo/dist",
        cache_max_age=int(config.get_settings()["default_max_age"]),
    )

    # Handles the other HTTP errors raised by the views. Without that,
    # the client receives a status=200 without content.
    config.add_view(error_handler, context=HTTPException)

    c2cwsgiutils.index.additional_title = (
        '<div class="row"><div class="col-lg-3"><h2>GeoMapFish</h2></div><div class="col-lg">'
    )

    c2cwsgiutils.index.additional_auth.extend([
        '<a href="../tiles/admin/">TileCloud chain admin</a><br>',
        '<a href="../tiles/c2c/">TileCloud chain c2c tools</a><br>',
        '<a href="../invalidate">Invalidate the cache</a><br>',
        '<a href="../memory">Memory status</a><br>',
    ])
    if config.get_settings().get("enable_admin_interface", False):
        c2cwsgiutils.index.additional_noauth.append(
            '<a href="../admin/">Admin</a><br>')

    c2cwsgiutils.index.additional_noauth.append(
        '</div></div><div class="row"><div class="col-lg-3"><h3>Interfaces</h3></div><div class="col-lg">'
    )
    c2cwsgiutils.index.additional_noauth.append(
        '<a href="../">Default</a><br>')
    for interface in config.get_settings().get("interfaces", []):
        if not interface.get("default", False):
            c2cwsgiutils.index.additional_noauth.append(
                '<a href="../{interface}">{interface}</a><br>'.format(
                    interface=interface["name"]))
    c2cwsgiutils.index.additional_noauth.append(
        '<a href="../apihelp/index.html">API help</a><br>')
    c2cwsgiutils.index.additional_noauth.append("</div></div><hr>")