c2cgeoportal-geoportal 2.6.0__py2.py3-none-any.whl → 2.8.1.180__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- c2cgeoportal_geoportal/__init__.py +245 -95
- c2cgeoportal_geoportal/lib/__init__.py +67 -43
- c2cgeoportal_geoportal/lib/authentication.py +50 -26
- c2cgeoportal_geoportal/lib/bashcolor.py +17 -13
- c2cgeoportal_geoportal/lib/cacheversion.py +16 -8
- c2cgeoportal_geoportal/lib/caching.py +65 -193
- c2cgeoportal_geoportal/lib/check_collector.py +17 -10
- c2cgeoportal_geoportal/lib/checker.py +67 -65
- c2cgeoportal_geoportal/lib/common_headers.py +167 -0
- c2cgeoportal_geoportal/lib/dbreflection.py +61 -46
- c2cgeoportal_geoportal/lib/filter_capabilities.py +126 -88
- c2cgeoportal_geoportal/lib/fulltextsearch.py +6 -5
- c2cgeoportal_geoportal/lib/functionality.py +20 -17
- c2cgeoportal_geoportal/lib/headers.py +14 -5
- c2cgeoportal_geoportal/lib/i18n.py +4 -4
- c2cgeoportal_geoportal/lib/layers.py +30 -11
- c2cgeoportal_geoportal/lib/lingua_extractor.py +363 -240
- c2cgeoportal_geoportal/lib/loader.py +11 -16
- c2cgeoportal_geoportal/lib/metrics.py +28 -17
- c2cgeoportal_geoportal/lib/oauth2.py +392 -206
- c2cgeoportal_geoportal/lib/wmstparsing.py +105 -84
- c2cgeoportal_geoportal/lib/xsd.py +26 -16
- c2cgeoportal_geoportal/resources.py +15 -9
- c2cgeoportal_geoportal/scaffolds/advance_create/ci/config.yaml +26 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/cookiecutter.json +18 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/.dockerignore +6 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/.eslintrc.yaml +19 -0
- c2cgeoportal_geoportal/scaffolds/{create/geoportal/+dot+prospector.yaml → advance_create/{{cookiecutter.project}}/geoportal/.prospector.yaml} +8 -2
- c2cgeoportal_geoportal/scaffolds/{create/geoportal/Dockerfile_tmpl → advance_create/{{cookiecutter.project}}/geoportal/Dockerfile} +22 -15
- c2cgeoportal_geoportal/scaffolds/{create/geoportal/alembic.yaml_tmpl → advance_create/{{cookiecutter.project}}/geoportal/alembic.yaml} +1 -1
- c2cgeoportal_geoportal/scaffolds/{create/geoportal/development.ini_tmpl → advance_create/{{cookiecutter.project}}/geoportal/development.ini} +34 -15
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/gunicorn.conf.py +100 -0
- c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/lingua-client.cfg +1 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/production.ini +38 -0
- c2cgeoportal_geoportal/scaffolds/{create/geoportal/setup.py_tmpl → advance_create/{{cookiecutter.project}}/geoportal/setup.py} +6 -7
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/tsconfig.json +8 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/webpack.api.js +77 -0
- c2cgeoportal_geoportal/scaffolds/{create/geoportal/webpack.apps.js_tmpl → advance_create/{{cookiecutter.project}}/geoportal/webpack.apps.js} +29 -28
- c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/webpack.commons.js +4 -7
- c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/webpack.config.js +1 -1
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/__init__.py +42 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/authentication.py +10 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/dev.py +14 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/models.py +8 -0
- c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/multi_organization.py +7 -0
- c2cgeoportal_geoportal/scaffolds/{create/geoportal/+package+_geoportal → advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/resources.py +4 -3
- c2cgeoportal_geoportal/scaffolds/{create/geoportal/+package+_geoportal/static-ngeo/api/index.js_tmpl → advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static-ngeo/api/index.js} +1 -2
- c2cgeoportal_geoportal/scaffolds/{create/geoportal/+package+_geoportal/static-ngeo/js/+package+module.js_tmpl → advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static-ngeo/js/{{cookiecutter.package}}module.js} +4 -4
- c2cgeoportal_geoportal/scaffolds/{create/geoportal/+package+_geoportal/subscribers.py_tmpl → advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/subscribers.py} +1 -3
- c2cgeoportal_geoportal/scaffolds/advance_update/cookiecutter.json +18 -0
- c2cgeoportal_geoportal/scaffolds/{update/geoportal/CONST_Makefile_tmpl → advance_update/{{cookiecutter.project}}/geoportal/CONST_Makefile} +3 -27
- c2cgeoportal_geoportal/scaffolds/create/cookiecutter.json +18 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.dockerignore +14 -0
- c2cgeoportal_geoportal/scaffolds/create/{+dot+editorconfig → {{cookiecutter.project}}/.editorconfig} +2 -5
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/main.yaml +57 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/rebuild.yaml +46 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/update_l10n.yaml +66 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.gitignore +16 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.prettierignore +1 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.prettierrc.yaml +2 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/Dockerfile +76 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/Makefile +70 -0
- c2cgeoportal_geoportal/scaffolds/create/{README.rst_tmpl → {{cookiecutter.project}}/README.rst} +4 -4
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/build +186 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/ci/config.yaml +22 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/ci/docker-compose-check +25 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/ci/requirements.txt +1 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose-db.yaml +26 -0
- c2cgeoportal_geoportal/scaffolds/create/{docker-compose-lib.yaml → {{cookiecutter.project}}/docker-compose-lib.yaml} +165 -22
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose-qgis.yaml +23 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose.override.sample.yaml +66 -0
- c2cgeoportal_geoportal/scaffolds/create/{docker-compose.yaml → {{cookiecutter.project}}/docker-compose.yaml} +20 -15
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/env.default +101 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/env.project +69 -0
- c2cgeoportal_geoportal/scaffolds/create/{geoportal/vars.yaml_tmpl → {{cookiecutter.project}}/geoportal/vars.yaml} +126 -36
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/css/mobile.css +0 -0
- c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/data/Readme.txt +3 -3
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/demo.map.tmpl +224 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/mapserver.conf +15 -0
- c2cgeoportal_geoportal/scaffolds/create/{mapserver/mapserver.map.tmpl_tmpl → {{cookiecutter.project}}/mapserver/mapserver.map.tmpl} +9 -18
- c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A3_Landscape.jrxml +13 -8
- c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A3_Portrait.jrxml +13 -8
- c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A4_Landscape.jrxml +13 -8
- c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A4_Portrait.jrxml +13 -8
- c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/config.yaml.tmpl +11 -4
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/localisation.properties +4 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/localisation_fr.properties +4 -0
- c2cgeoportal_geoportal/scaffolds/create/{project.yaml_tmpl → {{cookiecutter.project}}/project.yaml} +6 -6
- c2cgeoportal_geoportal/scaffolds/create/{pyproject.toml → {{cookiecutter.project}}/pyproject.toml} +4 -0
- c2cgeoportal_geoportal/scaffolds/create/{qgisserver/pg_service.conf.tmpl_tmpl → {{cookiecutter.project}}/qgisserver/pg_service.conf.tmpl} +2 -2
- c2cgeoportal_geoportal/scaffolds/create/{run_alembic.sh → {{cookiecutter.project}}/run_alembic.sh} +3 -5
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/scripts/db-backup +110 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/scripts/db-restore +114 -0
- c2cgeoportal_geoportal/scaffolds/create/{setup.cfg_tmpl → {{cookiecutter.project}}/setup.cfg} +1 -1
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/spell-ignore-words.txt +5 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/tests/__init__.py +0 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/tests/test_app.py +38 -0
- c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/tilegeneration/config.yaml.tmpl +195 -0
- c2cgeoportal_geoportal/scaffolds/update/cookiecutter.json +18 -0
- c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/.upgrade.yaml +61 -0
- c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/CONST_CHANGELOG.txt +273 -0
- c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/CONST_create_template/tests/test_testapp.py +48 -0
- c2cgeoportal_geoportal/scaffolds/update/{geoportal → {{cookiecutter.project}}/geoportal}/CONST_config-schema.yaml +64 -17
- c2cgeoportal_geoportal/scaffolds/update/{geoportal/CONST_vars.yaml_tmpl → {{cookiecutter.project}}/geoportal/CONST_vars.yaml} +396 -19
- c2cgeoportal_geoportal/scripts/__init__.py +16 -30
- c2cgeoportal_geoportal/scripts/c2cupgrade.py +272 -234
- c2cgeoportal_geoportal/scripts/create_demo_theme.py +3 -6
- c2cgeoportal_geoportal/scripts/manage_users.py +34 -39
- c2cgeoportal_geoportal/scripts/pcreate.py +310 -0
- c2cgeoportal_geoportal/scripts/theme2fts.py +128 -24
- c2cgeoportal_geoportal/scripts/urllogin.py +19 -11
- c2cgeoportal_geoportal/templates/login.html +88 -84
- c2cgeoportal_geoportal/templates/notlogin.html +59 -59
- c2cgeoportal_geoportal/templates/testi18n.html +6 -8
- c2cgeoportal_geoportal/views/__init__.py +23 -6
- c2cgeoportal_geoportal/views/dev.py +9 -7
- c2cgeoportal_geoportal/views/dynamic.py +56 -19
- c2cgeoportal_geoportal/views/entry.py +85 -24
- c2cgeoportal_geoportal/views/fulltextsearch.py +29 -23
- c2cgeoportal_geoportal/views/geometry_processing.py +17 -9
- c2cgeoportal_geoportal/views/i18n.py +91 -9
- c2cgeoportal_geoportal/views/layers.py +166 -133
- c2cgeoportal_geoportal/views/login.py +161 -93
- c2cgeoportal_geoportal/views/mapserverproxy.py +47 -31
- c2cgeoportal_geoportal/views/memory.py +12 -12
- c2cgeoportal_geoportal/views/ogcproxy.py +52 -30
- c2cgeoportal_geoportal/views/pdfreport.py +30 -26
- c2cgeoportal_geoportal/views/printproxy.py +60 -52
- c2cgeoportal_geoportal/views/profile.py +24 -23
- c2cgeoportal_geoportal/views/proxy.py +88 -72
- c2cgeoportal_geoportal/views/raster.py +37 -26
- c2cgeoportal_geoportal/views/resourceproxy.py +13 -11
- c2cgeoportal_geoportal/views/shortener.py +27 -25
- c2cgeoportal_geoportal/views/theme.py +472 -332
- c2cgeoportal_geoportal/views/tinyowsproxy.py +42 -44
- c2cgeoportal_geoportal/views/vector_tiles.py +80 -0
- {c2cgeoportal_geoportal-2.6.0.dist-info → c2cgeoportal_geoportal-2.8.1.180.dist-info}/METADATA +19 -8
- c2cgeoportal_geoportal-2.8.1.180.dist-info/RECORD +191 -0
- {c2cgeoportal_geoportal-2.6.0.dist-info → c2cgeoportal_geoportal-2.8.1.180.dist-info}/WHEEL +1 -1
- {c2cgeoportal_geoportal-2.6.0.dist-info → c2cgeoportal_geoportal-2.8.1.180.dist-info}/entry_points.txt +3 -0
- tests/__init__.py +10 -5
- tests/test_cachebuster.py +3 -5
- tests/test_caching.py +18 -26
- tests/test_checker.py +1 -3
- tests/test_decimaljson.py +5 -5
- tests/test_headerstween.py +1 -3
- tests/test_i18n.py +2 -2
- tests/test_init.py +16 -20
- tests/test_locale_negociator.py +4 -6
- tests/test_mapserverproxy_route_predicate.py +1 -4
- tests/test_raster.py +15 -17
- tests/test_wmstparsing.py +10 -12
- tests/xmlstr.py +1 -3
- c2cgeoportal_geoportal/scaffolds/__init__.py +0 -227
- c2cgeoportal_geoportal/scaffolds/create/+dot+dockerignore_tmpl +0 -12
- c2cgeoportal_geoportal/scaffolds/create/+dot+github/workflows/main.yaml_tmpl +0 -89
- c2cgeoportal_geoportal/scaffolds/create/+dot+github/workflows/rebuild.yaml_tmpl +0 -78
- c2cgeoportal_geoportal/scaffolds/create/+dot+gitignore_tmpl +0 -16
- c2cgeoportal_geoportal/scaffolds/create/Dockerfile_tmpl +0 -67
- c2cgeoportal_geoportal/scaffolds/create/Makefile +0 -3
- c2cgeoportal_geoportal/scaffolds/create/build_tmpl +0 -167
- c2cgeoportal_geoportal/scaffolds/create/ci/config.yaml_tmpl +0 -23
- c2cgeoportal_geoportal/scaffolds/create/ci/requirements.txt +0 -1
- c2cgeoportal_geoportal/scaffolds/create/ci/trigger +0 -68
- c2cgeoportal_geoportal/scaffolds/create/docker-compose.override.sample.yaml +0 -54
- c2cgeoportal_geoportal/scaffolds/create/env.default_tmpl +0 -67
- c2cgeoportal_geoportal/scaffolds/create/env.project_tmpl +0 -48
- c2cgeoportal_geoportal/scaffolds/create/geoportal/+dot+dockerignore_tmpl +0 -6
- c2cgeoportal_geoportal/scaffolds/create/geoportal/+dot+eslintrc_tmpl +0 -15
- c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/__init__.py_tmpl +0 -58
- c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/models.py_tmpl +0 -10
- c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/static/robot.txt +0 -3
- c2cgeoportal_geoportal/scaffolds/create/geoportal/production.ini_tmpl +0 -106
- c2cgeoportal_geoportal/scaffolds/create/geoportal/tools/extract-messages.js +0 -39
- c2cgeoportal_geoportal/scaffolds/create/geoportal/tsconfig.json_tmpl +0 -9
- c2cgeoportal_geoportal/scaffolds/create/geoportal/webpack.api.js_tmpl +0 -72
- c2cgeoportal_geoportal/scaffolds/create/mapserver/demo.map.tmpl_tmpl +0 -262
- c2cgeoportal_geoportal/scaffolds/create/mapserver/tinyows.xml +0 -36
- c2cgeoportal_geoportal/scaffolds/create/print/print-apps/+package+/config.yaml +0 -168
- c2cgeoportal_geoportal/scaffolds/create/qgisserver/geomapfish.yaml.tmpl_tmpl +0 -16
- c2cgeoportal_geoportal/scaffolds/create/spell-ignore-words.txt +0 -1
- c2cgeoportal_geoportal/scaffolds/create/tilegeneration/config.yaml.tmpl_tmpl +0 -185
- c2cgeoportal_geoportal/scaffolds/create/yamllint.yaml +0 -11
- c2cgeoportal_geoportal/scaffolds/update/+dot+upgrade.yaml_tmpl +0 -181
- c2cgeoportal_geoportal/scaffolds/update/CONST_CHANGELOG.txt_tmpl +0 -454
- c2cgeoportal_geoportal/templates/dynamic.js +0 -21
- c2cgeoportal_geoportal-2.6.0.dist-info/RECORD +0 -173
- /c2cgeoportal_geoportal/{scaffolds/create/geoportal/+package+_geoportal/static/css/desktop.css → py.typed} +0 -0
- /c2cgeoportal_geoportal/scaffolds/{create/geoportal/Makefile_tmpl → advance_create/{{cookiecutter.project}}/geoportal/Makefile} +0 -0
- /c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/alembic.ini +0 -0
- /c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/language_mapping +0 -0
- /c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/lingua-server.cfg +0 -0
- /c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/requirements.txt +0 -0
- /c2cgeoportal_geoportal/scaffolds/{create/geoportal/+package+_geoportal → advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/views/__init__.py +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal/locale/en/LC_MESSAGES/+package+_geoportal-client.po → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/locale/en/LC_MESSAGES/{{cookiecutter.package}}_geoportal-client.po} +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal/static/css/iframe_api.css → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/css/desktop.css} +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal/static/css/mobile.css → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/css/iframe_api.css} +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/banner_left.png +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/banner_right.png +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/blank.png +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/markers/marker-blue.png +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/markers/marker-gold.png +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/markers/marker-green.png +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/markers/marker.png +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/robot.txt.tmpl +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/data/TM_EUROPE_BORDERS-0.3.sql +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Arial.ttf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Arialbd.ttf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Arialbi.ttf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Ariali.ttf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/NotoSans-Bold.ttf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/NotoSans-BoldItalic.ttf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/NotoSans-Italic.ttf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/NotoSans-Regular.ttf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Verdana.ttf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Verdanab.ttf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Verdanai.ttf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Verdanaz.ttf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts.conf +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/tinyows.xml.tmpl +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/legend.jrxml +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/logo.png +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/north.svg +0 -0
- /c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/results.jrxml +0 -0
- {c2cgeoportal_geoportal-2.6.0.dist-info → c2cgeoportal_geoportal-2.8.1.180.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,4 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
# Copyright (c) 2011-2021, Camptocamp SA
|
1
|
+
# Copyright (c) 2011-2024, Camptocamp SA
|
4
2
|
# All rights reserved.
|
5
3
|
|
6
4
|
# Redistribution and use in source and binary forms, with or without
|
@@ -30,64 +28,61 @@
|
|
30
28
|
|
31
29
|
import logging
|
32
30
|
import sys
|
33
|
-
import
|
34
|
-
from typing import Dict, List, Union
|
31
|
+
from typing import Any, Dict, List, Optional, Union
|
35
32
|
|
33
|
+
import pyramid.request
|
34
|
+
import pyramid.response
|
36
35
|
import requests
|
37
36
|
from pyramid.httpexceptions import HTTPBadGateway, exception_response
|
38
|
-
from pyramid.response import Response
|
39
37
|
|
40
|
-
from
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
get_region,
|
45
|
-
set_common_headers,
|
46
|
-
)
|
38
|
+
from c2cgeoportal_commons.lib.url import Url
|
39
|
+
from c2cgeoportal_geoportal.lib.caching import get_region
|
40
|
+
from c2cgeoportal_geoportal.lib.common_headers import Cache, set_common_headers
|
41
|
+
from c2cgeoportal_geoportal.views import restrict_headers
|
47
42
|
|
48
43
|
LOG = logging.getLogger(__name__)
|
49
44
|
CACHE_REGION = get_region("std")
|
50
45
|
|
51
46
|
|
52
47
|
class Proxy:
|
53
|
-
|
48
|
+
"""Some methods used by all the proxy."""
|
49
|
+
|
50
|
+
def __init__(self, request: pyramid.request.Request):
|
54
51
|
self.request = request
|
55
|
-
self.host_forward_host = request.registry.settings
|
52
|
+
self.host_forward_host = request.registry.settings.get("host_forward_host", [])
|
53
|
+
self.headers_whitelist = request.registry.settings.get("headers_whitelist", [])
|
54
|
+
self.headers_blacklist = request.registry.settings.get("headers_blacklist", [])
|
56
55
|
self.http_options = self.request.registry.settings.get("http_options", {})
|
57
56
|
|
58
|
-
def _proxy(
|
59
|
-
|
57
|
+
def _proxy(
|
58
|
+
self,
|
59
|
+
url: Url,
|
60
|
+
params: Optional[Dict[str, str]] = None,
|
61
|
+
method: Optional[str] = None,
|
62
|
+
cache: bool = False,
|
63
|
+
body: Optional[bytes] = None,
|
64
|
+
headers: Optional[Dict[str, str]] = None,
|
65
|
+
) -> requests.models.Response:
|
66
|
+
# Get query string
|
60
67
|
params = dict(self.request.params) if params is None else params
|
61
|
-
|
62
|
-
url_params = urllib.parse.parse_qs(parsed_url.query)
|
63
|
-
all_params: Dict[str, Union[str]] = {}
|
64
|
-
all_params.update(params)
|
65
|
-
all_params.update({k: ",".join(v) for k, v in url_params.items()})
|
66
|
-
query_string = urllib.parse.urlencode(all_params)
|
67
|
-
|
68
|
-
if parsed_url.port is None:
|
69
|
-
url = "{}://{}{}?{}".format(parsed_url.scheme, parsed_url.hostname, parsed_url.path, query_string)
|
70
|
-
else: # pragma: no cover
|
71
|
-
url = "{}://{}:{:d}{}?{}".format(
|
72
|
-
parsed_url.scheme, parsed_url.hostname, parsed_url.port, parsed_url.path, query_string
|
73
|
-
)
|
68
|
+
url = url.clone().add_query(params, True)
|
74
69
|
|
75
70
|
LOG.debug("Send query to URL:\n%s.", url)
|
76
71
|
|
77
72
|
if method is None:
|
78
73
|
method = self.request.method
|
79
74
|
|
80
|
-
if headers is None
|
81
|
-
headers = dict(self.request.headers)
|
75
|
+
headers = dict(self.request.headers if headers is None else headers)
|
82
76
|
|
83
|
-
# Forward request to target (without Host Header)
|
84
|
-
|
77
|
+
# Forward request to target (without Host Header).
|
78
|
+
# The original Host will be added back by pyramid.
|
79
|
+
if url.hostname not in self.host_forward_host and "Host" in headers:
|
85
80
|
headers.pop("Host")
|
86
81
|
|
87
82
|
# Forward the request tracking ID to the other service. This will allow to follow the logs belonging
|
88
83
|
# to a single request coming from the user
|
89
84
|
headers.setdefault("X-Request-ID", self.request.c2c_request_id)
|
90
|
-
# If we
|
85
|
+
# If we really want to respect the specification, we should chain with the content of the previous
|
91
86
|
# proxy, see also:
|
92
87
|
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Forwarded
|
93
88
|
forwarded = {"for": self.request.client_addr, "proto": self.request.scheme}
|
@@ -98,19 +93,30 @@ class Proxy:
|
|
98
93
|
headers["Forwarded"] = ",".join([headers["Forwarded"], forwarded_str])
|
99
94
|
else:
|
100
95
|
headers["Forwarded"] = forwarded_str
|
96
|
+
# Set alternative "X-Forwarded" headers
|
97
|
+
for forwarded_elements in reversed(headers["Forwarded"].split(",")):
|
98
|
+
for element in forwarded_elements.split(";"):
|
99
|
+
key, value = element.split("=")
|
100
|
+
header_key = f"X-Forwarded-{key.capitalize()}"
|
101
|
+
header_value = headers.get(header_key)
|
102
|
+
headers[header_key] = value if header_value is None else ", ".join([header_value, value])
|
101
103
|
|
102
104
|
if not cache:
|
103
105
|
headers["Cache-Control"] = "no-cache"
|
104
106
|
|
105
|
-
if method in ("POST", "PUT") and body is None:
|
107
|
+
if method in ("POST", "PUT") and body is None:
|
106
108
|
body = self.request.body
|
107
109
|
|
110
|
+
headers = restrict_headers(headers, self.headers_whitelist, self.headers_blacklist)
|
111
|
+
|
108
112
|
try:
|
109
113
|
if method in ("POST", "PUT"):
|
110
|
-
response = requests.request(
|
114
|
+
response = requests.request(
|
115
|
+
method, url.url(), data=body, headers=headers, **self.http_options
|
116
|
+
)
|
111
117
|
else:
|
112
|
-
response = requests.request(method, url, headers=headers, **self.http_options)
|
113
|
-
except Exception:
|
118
|
+
response = requests.request(method, url.url(), headers=headers, **self.http_options)
|
119
|
+
except Exception:
|
114
120
|
errors = ["Error '%s' while getting the URL:", "%s", "Method: %s", "--- With headers ---", "%s"]
|
115
121
|
args1 = [
|
116
122
|
sys.exc_info()[0],
|
@@ -118,19 +124,21 @@ class Proxy:
|
|
118
124
|
method,
|
119
125
|
"\n".join(
|
120
126
|
[
|
121
|
-
"{}: {
|
127
|
+
f"{h}: {v if h not in ('Authorization', 'Cookies') else '***'}"
|
122
128
|
for h, v in list(headers.items())
|
123
129
|
]
|
124
130
|
),
|
125
131
|
]
|
126
|
-
if method in ("POST", "PUT"):
|
132
|
+
if method in ("POST", "PUT") and body is not None:
|
127
133
|
errors += ["--- Query with body ---", "%s"]
|
128
134
|
args1.append(body.decode("utf-8"))
|
129
|
-
LOG.
|
135
|
+
LOG.exception("\n".join(errors), *args1)
|
130
136
|
|
131
|
-
raise HTTPBadGateway(
|
137
|
+
raise HTTPBadGateway( # pylint: disable=raise-missing-from
|
138
|
+
"Error on backend, See logs for detail"
|
139
|
+
)
|
132
140
|
|
133
|
-
if not response.ok:
|
141
|
+
if not response.ok:
|
134
142
|
errors = [
|
135
143
|
"Error '%s' in response of URL:",
|
136
144
|
"%s",
|
@@ -141,17 +149,17 @@ class Proxy:
|
|
141
149
|
]
|
142
150
|
args2: List[Union[str, int]] = [
|
143
151
|
response.reason,
|
144
|
-
url,
|
152
|
+
url.url(),
|
145
153
|
response.status_code,
|
146
154
|
method,
|
147
155
|
"\n".join(
|
148
156
|
[
|
149
|
-
"{}: {
|
157
|
+
f"{h}: {v if h not in ('Authorization', 'Cookies') else '***'}"
|
150
158
|
for h, v in list(headers.items())
|
151
159
|
]
|
152
160
|
),
|
153
161
|
]
|
154
|
-
if method in ("POST", "PUT"):
|
162
|
+
if method in ("POST", "PUT") and body is not None:
|
155
163
|
errors += ["--- Query with body ---", "%s"]
|
156
164
|
args2.append(body.decode("utf-8"))
|
157
165
|
errors += ["--- Return content ---", "%s"]
|
@@ -164,41 +172,49 @@ class Proxy:
|
|
164
172
|
|
165
173
|
return response
|
166
174
|
|
167
|
-
@CACHE_REGION.cache_on_arguments()
|
168
|
-
def _proxy_cache(self, method, *args, **kwargs
|
175
|
+
@CACHE_REGION.cache_on_arguments() # type: ignore
|
176
|
+
def _proxy_cache(self, host: str, method: str, *args: Any, **kwargs: Any) -> pyramid.response.Response:
|
169
177
|
# Method is only for the cache
|
170
|
-
del method
|
178
|
+
del host, method
|
179
|
+
|
171
180
|
kwargs["cache"] = True
|
172
181
|
return self._proxy(*args, **kwargs)
|
173
182
|
|
174
183
|
def _proxy_response(
|
175
|
-
self,
|
176
|
-
|
184
|
+
self,
|
185
|
+
service_name: str,
|
186
|
+
url: Url,
|
187
|
+
headers_update: Optional[Dict[str, str]] = None,
|
188
|
+
public: bool = False,
|
189
|
+
**kwargs: Any,
|
190
|
+
) -> pyramid.response.Response:
|
177
191
|
if headers_update is None:
|
178
192
|
headers_update = {}
|
179
193
|
cache = kwargs.get("cache", False)
|
180
194
|
if cache is True:
|
181
|
-
response = self._proxy_cache(url, self.request.method, **kwargs)
|
195
|
+
response = self._proxy_cache(url, self.request.host, self.request.method, **kwargs)
|
182
196
|
else:
|
183
197
|
response = self._proxy(url, **kwargs)
|
184
198
|
|
185
|
-
cache_control = (
|
199
|
+
cache_control = (
|
200
|
+
(Cache.PUBLIC if public else Cache.PRIVATE)
|
201
|
+
if cache
|
202
|
+
else (Cache.PUBLIC_NO if public else Cache.PRIVATE_NO)
|
203
|
+
)
|
186
204
|
return self._build_response(
|
187
205
|
response, response.content, cache_control, service_name, headers_update=headers_update
|
188
206
|
)
|
189
207
|
|
190
208
|
def _build_response(
|
191
209
|
self,
|
192
|
-
response,
|
193
|
-
content,
|
194
|
-
cache_control,
|
195
|
-
service_name,
|
196
|
-
headers=None,
|
197
|
-
headers_update=None,
|
198
|
-
content_type=None,
|
199
|
-
):
|
200
|
-
if isinstance(content, str):
|
201
|
-
content = content.encode("utf-8")
|
210
|
+
response: pyramid.response.Response,
|
211
|
+
content: bytes,
|
212
|
+
cache_control: Cache,
|
213
|
+
service_name: str,
|
214
|
+
headers: Optional[Dict[str, str]] = None,
|
215
|
+
headers_update: Optional[Dict[str, str]] = None,
|
216
|
+
content_type: Optional[str] = None,
|
217
|
+
) -> pyramid.response.Response:
|
202
218
|
if headers_update is None:
|
203
219
|
headers_update = {}
|
204
220
|
headers = response.headers if headers is None else headers
|
@@ -216,28 +232,28 @@ class Proxy:
|
|
216
232
|
"Trailers",
|
217
233
|
"Transfer-Encoding",
|
218
234
|
"Upgrade",
|
219
|
-
]:
|
235
|
+
]:
|
220
236
|
if header in headers:
|
221
237
|
del headers[header]
|
222
238
|
# Other problematic headers
|
223
|
-
for header in ["Content-Length", "Content-Location", "Content-Encoding"]:
|
239
|
+
for header in ["Content-Length", "Content-Location", "Content-Encoding"]:
|
224
240
|
if header in headers:
|
225
241
|
del headers[header]
|
226
242
|
|
227
243
|
headers.update(headers_update)
|
228
244
|
|
229
|
-
response = Response(content, status=response.status_code, headers=headers)
|
245
|
+
response = pyramid.response.Response(content, status=response.status_code, headers=headers)
|
230
246
|
|
231
247
|
return set_common_headers(
|
232
248
|
self.request, service_name, cache_control, response=response, content_type=content_type
|
233
249
|
)
|
234
250
|
|
235
251
|
@staticmethod
|
236
|
-
def _get_lower_params(params):
|
237
|
-
return
|
252
|
+
def _get_lower_params(params: Dict[str, str]) -> Dict[str, str]:
|
253
|
+
return {k.lower(): str(v).lower() for k, v in params.items()}
|
238
254
|
|
239
|
-
def
|
240
|
-
headers = self.request.headers
|
241
|
-
if "Cookie" in headers:
|
255
|
+
def get_headers(self) -> Dict[str, str]:
|
256
|
+
headers: Dict[str, str] = self.request.headers
|
257
|
+
if "Cookie" in headers:
|
242
258
|
headers.pop("Cookie")
|
243
259
|
return headers
|
@@ -1,6 +1,4 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
# Copyright (c) 2012-2021, Camptocamp SA
|
1
|
+
# Copyright (c) 2012-2023, Camptocamp SA
|
4
2
|
# All rights reserved.
|
5
3
|
|
6
4
|
# Redistribution and use in source and binary forms, with or without
|
@@ -33,28 +31,35 @@ import logging
|
|
33
31
|
import math
|
34
32
|
import os
|
35
33
|
import traceback
|
36
|
-
from typing import Any, Dict, Optional
|
34
|
+
from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple
|
37
35
|
|
38
36
|
import numpy
|
37
|
+
import pyramid.request
|
39
38
|
import zope.event.classhandler
|
40
39
|
from pyramid.httpexceptions import HTTPBadRequest, HTTPNotFound
|
41
40
|
from pyramid.view import view_config
|
41
|
+
from rasterio.io import DatasetReader
|
42
42
|
|
43
43
|
from c2cgeoportal_commons.models import InvalidateCacheEvent
|
44
|
-
from c2cgeoportal_geoportal.lib.
|
44
|
+
from c2cgeoportal_geoportal.lib.common_headers import Cache, set_common_headers
|
45
|
+
|
46
|
+
if TYPE_CHECKING:
|
47
|
+
import fiona.collection
|
45
48
|
|
46
49
|
LOG = logging.getLogger(__name__)
|
47
50
|
|
48
51
|
|
49
52
|
class Raster:
|
50
|
-
|
53
|
+
"""All the view concerned the raster (point, not the profile profile)."""
|
54
|
+
|
55
|
+
data: Dict[str, "fiona.collection.Collection"] = {}
|
51
56
|
|
52
|
-
def __init__(self, request):
|
57
|
+
def __init__(self, request: pyramid.request.Request):
|
53
58
|
self.request = request
|
54
59
|
self.rasters = self.request.registry.settings["raster"]
|
55
60
|
|
56
|
-
@zope.event.classhandler.handler(InvalidateCacheEvent)
|
57
|
-
def handle(event: InvalidateCacheEvent)
|
61
|
+
@zope.event.classhandler.handler(InvalidateCacheEvent) # type: ignore
|
62
|
+
def handle(event: InvalidateCacheEvent) -> None:
|
58
63
|
del event
|
59
64
|
for _, v in Raster.data.items():
|
60
65
|
v.close()
|
@@ -62,21 +67,21 @@ class Raster:
|
|
62
67
|
|
63
68
|
def _get_required_finite_float_param(self, name: str) -> float:
|
64
69
|
if name not in self.request.params:
|
65
|
-
raise HTTPBadRequest("'{}' should be in the query string parameters"
|
70
|
+
raise HTTPBadRequest(f"'{name}' should be in the query string parameters")
|
66
71
|
try:
|
67
72
|
result = float(self.request.params[name])
|
68
73
|
except ValueError:
|
69
|
-
raise HTTPBadRequest(
|
70
|
-
"'{}' ({}) parameters should be a number"
|
74
|
+
raise HTTPBadRequest( # pylint: disable=raise-missing-from
|
75
|
+
f"'{name}' ({self.request.params[name]}) parameters should be a number"
|
71
76
|
)
|
72
77
|
if not math.isfinite(result):
|
73
78
|
raise HTTPBadRequest(
|
74
|
-
"'{}' ({}) parameters should be a finite number"
|
79
|
+
f"'{name}' ({self.request.params[name]}) parameters should be a finite number"
|
75
80
|
)
|
76
81
|
return result
|
77
82
|
|
78
|
-
@view_config(route_name="raster", renderer="fast_json")
|
79
|
-
def raster(self):
|
83
|
+
@view_config(route_name="raster", renderer="fast_json") # type: ignore
|
84
|
+
def raster(self) -> Dict[str, Any]:
|
80
85
|
lon = self._get_required_finite_float_param("lon")
|
81
86
|
lat = self._get_required_finite_float_param("lat")
|
82
87
|
|
@@ -87,7 +92,7 @@ class Raster:
|
|
87
92
|
if layer in self.rasters:
|
88
93
|
rasters[layer] = self.rasters[layer]
|
89
94
|
else:
|
90
|
-
raise HTTPNotFound("Layer {} not found"
|
95
|
+
raise HTTPNotFound(f"Layer {layer} not found")
|
91
96
|
else:
|
92
97
|
rasters = self.rasters
|
93
98
|
|
@@ -95,10 +100,10 @@ class Raster:
|
|
95
100
|
for ref in list(rasters.keys()):
|
96
101
|
result[ref] = self._get_raster_value(rasters[ref], ref, lon, lat)
|
97
102
|
|
98
|
-
set_common_headers(self.request, "raster",
|
103
|
+
set_common_headers(self.request, "raster", Cache.PUBLIC_NO)
|
99
104
|
return result
|
100
105
|
|
101
|
-
def _get_data(self, layer, name):
|
106
|
+
def _get_data(self, layer: Dict[str, Any], name: str) -> "fiona.collection.Collection":
|
102
107
|
if name not in self.data:
|
103
108
|
path = layer["file"]
|
104
109
|
if layer.get("type", "shp_index") == "shp_index":
|
@@ -114,7 +119,9 @@ class Raster:
|
|
114
119
|
|
115
120
|
return self.data[name]
|
116
121
|
|
117
|
-
def _get_raster_value(
|
122
|
+
def _get_raster_value(
|
123
|
+
self, layer: Dict[str, Any], name: str, lon: float, lat: float
|
124
|
+
) -> Optional[decimal.Decimal]:
|
118
125
|
data = self._get_data(layer, name)
|
119
126
|
type_ = layer.get("type", "shp_index")
|
120
127
|
if type_ == "shp_index":
|
@@ -135,28 +142,32 @@ class Raster:
|
|
135
142
|
else:
|
136
143
|
raise ValueError("Unsupported type " + type_)
|
137
144
|
|
138
|
-
|
139
|
-
|
145
|
+
result_d = None
|
146
|
+
if "round" in layer and result is not None:
|
147
|
+
result_d = self._round(result, layer["round"])
|
140
148
|
elif result is not None:
|
141
|
-
|
149
|
+
result_d = decimal.Decimal(str(result))
|
142
150
|
|
143
|
-
return
|
151
|
+
return result_d
|
144
152
|
|
145
153
|
@staticmethod
|
146
|
-
def _get_value(
|
154
|
+
def _get_value(
|
155
|
+
layer: Dict[str, Any], name: str, dataset: DatasetReader, lon: float, lat: float
|
156
|
+
) -> Optional[numpy.float32]:
|
147
157
|
index = dataset.index(lon, lat)
|
148
158
|
|
149
159
|
shape = dataset.shape
|
160
|
+
result: Optional[numpy.float32]
|
150
161
|
if 0 <= index[0] < shape[0] and 0 <= index[1] < shape[1]:
|
151
162
|
|
152
|
-
def get_index(index_):
|
163
|
+
def get_index(index_: int) -> Tuple[int, int]:
|
153
164
|
return index_, index_ + 1
|
154
165
|
|
155
166
|
result = dataset.read(1, window=(get_index(index[0]), get_index(index[1])))[0][0]
|
156
167
|
result = None if result == layer.get("nodata", dataset.nodata) else result
|
157
168
|
else:
|
158
169
|
LOG.debug(
|
159
|
-
"Out of index for layer: %s (%s),
|
170
|
+
"Out of index for layer: %s (%s), lon/lat: %dx%d, index: %dx%d, shape: %dx%d.",
|
160
171
|
name,
|
161
172
|
layer["file"],
|
162
173
|
lon,
|
@@ -1,6 +1,4 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
# Copyright (c) 2011-2020, Camptocamp SA
|
1
|
+
# Copyright (c) 2011-2023, Camptocamp SA
|
4
2
|
# All rights reserved.
|
5
3
|
|
6
4
|
# Redistribution and use in source and binary forms, with or without
|
@@ -31,41 +29,45 @@
|
|
31
29
|
import ast
|
32
30
|
import logging
|
33
31
|
|
32
|
+
import pyramid.request
|
33
|
+
import pyramid.response
|
34
34
|
from pyramid.httpexceptions import HTTPBadRequest
|
35
35
|
from pyramid.view import view_config
|
36
36
|
|
37
|
-
from c2cgeoportal_geoportal.lib.
|
37
|
+
from c2cgeoportal_geoportal.lib.common_headers import Cache
|
38
38
|
from c2cgeoportal_geoportal.views.proxy import Proxy
|
39
39
|
|
40
40
|
LOG = logging.getLogger(__name__)
|
41
41
|
|
42
42
|
|
43
43
|
class ResourceProxy(Proxy):
|
44
|
-
|
44
|
+
"""All the views concerned the resources (it's a kind of proxy)."""
|
45
|
+
|
46
|
+
def __init__(self, request: pyramid.request.Request):
|
45
47
|
Proxy.__init__(self, request)
|
46
48
|
self.request = request
|
47
49
|
self.settings = request.registry.settings.get("resourceproxy", {})
|
48
50
|
|
49
|
-
@view_config(route_name="resourceproxy")
|
50
|
-
def proxy(self)
|
51
|
+
@view_config(route_name="resourceproxy") # type: ignore
|
52
|
+
def proxy(self) -> pyramid.response.Response:
|
51
53
|
target = self.request.params.get("target", "")
|
52
54
|
targets = self.settings.get("targets", [])
|
53
|
-
if target in targets:
|
55
|
+
if target in targets:
|
54
56
|
url = targets[target]
|
55
57
|
values = ast.literal_eval(self.request.params.get("values"))
|
56
58
|
url = url % values
|
57
59
|
|
58
60
|
response = self._proxy(url=url)
|
59
61
|
|
60
|
-
cache_control =
|
62
|
+
cache_control = Cache.PRIVATE_NO
|
61
63
|
content_type = response.headers["Content-Type"]
|
62
64
|
|
63
65
|
response = self._build_response(
|
64
|
-
response, response.
|
66
|
+
response, response.content, cache_control, "externalresource", content_type=content_type
|
65
67
|
)
|
66
68
|
for header in response.headers.keys():
|
67
69
|
if header not in self.settings["allowed_headers"]:
|
68
70
|
response.headers.pop(header)
|
69
71
|
return response
|
70
|
-
LOG.warning("Target
|
72
|
+
LOG.warning("Target URL not found: %s", target)
|
71
73
|
return HTTPBadRequest("URL not allowed")
|
@@ -1,6 +1,4 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
# Copyright (c) 2013-2021, Camptocamp SA
|
1
|
+
# Copyright (c) 2013-2023, Camptocamp SA
|
4
2
|
# All rights reserved.
|
5
3
|
|
6
4
|
# Redistribution and use in source and binary forms, with or without
|
@@ -32,67 +30,69 @@ import logging
|
|
32
30
|
import random
|
33
31
|
import string
|
34
32
|
from datetime import datetime
|
33
|
+
from typing import Dict
|
35
34
|
from urllib.parse import urlparse
|
36
35
|
|
36
|
+
import pyramid.request
|
37
37
|
from pyramid.httpexceptions import HTTPBadRequest, HTTPFound, HTTPInternalServerError, HTTPNotFound
|
38
38
|
from pyramid.view import view_config
|
39
39
|
|
40
40
|
from c2cgeoportal_commons.lib.email_ import send_email_config
|
41
41
|
from c2cgeoportal_commons.models import DBSession
|
42
42
|
from c2cgeoportal_commons.models.static import Shorturl
|
43
|
-
from c2cgeoportal_geoportal.lib.
|
43
|
+
from c2cgeoportal_geoportal.lib.common_headers import Cache, set_common_headers
|
44
44
|
|
45
45
|
logger = logging.getLogger(__name__)
|
46
46
|
|
47
47
|
|
48
48
|
class Shortener:
|
49
|
-
|
49
|
+
"""All the views conserne the shortener."""
|
50
|
+
|
51
|
+
def __init__(self, request: pyramid.request.Request):
|
50
52
|
self.request = request
|
51
53
|
self.settings = request.registry.settings.get("shortener", {})
|
52
54
|
self.short_bases = [self.request.route_url("shortener_get", ref="")]
|
53
55
|
if "base_url" in self.settings:
|
54
56
|
self.short_bases.append(self.settings["base_url"])
|
55
57
|
|
56
|
-
@view_config(route_name="shortener_get")
|
57
|
-
def get(self):
|
58
|
+
@view_config(route_name="shortener_get") # type: ignore
|
59
|
+
def get(self) -> HTTPFound:
|
58
60
|
ref = self.request.matchdict["ref"]
|
59
61
|
short_urls = DBSession.query(Shorturl).filter(Shorturl.ref == ref).all()
|
60
62
|
|
61
63
|
if len(short_urls) != 1:
|
62
|
-
raise HTTPNotFound("Ref '{
|
64
|
+
raise HTTPNotFound(f"Ref '{ref!s}' not found")
|
63
65
|
|
64
66
|
short_urls[0].nb_hits += 1
|
65
67
|
short_urls[0].last_hit = datetime.now()
|
66
68
|
|
67
|
-
set_common_headers(self.request, "shortener",
|
69
|
+
set_common_headers(self.request, "shortener", Cache.PUBLIC_NO)
|
68
70
|
return HTTPFound(location=short_urls[0].url)
|
69
71
|
|
70
|
-
@view_config(route_name="shortener_create", renderer="json")
|
71
|
-
def create(self):
|
72
|
-
|
72
|
+
@view_config(route_name="shortener_create", renderer="json") # type: ignore
|
73
|
+
def create(self) -> Dict[str, str]:
|
73
74
|
if "url" not in self.request.params:
|
74
75
|
raise HTTPBadRequest("The parameter url is required")
|
75
76
|
|
76
77
|
url = self.request.params["url"]
|
77
78
|
|
78
79
|
# see: https://httpd.apache.org/docs/2.2/mod/core.html#limitrequestline
|
79
|
-
if len(url) > 8190:
|
80
|
-
raise HTTPBadRequest("The parameter url is too long ({} > {})"
|
80
|
+
if len(url) > 8190:
|
81
|
+
raise HTTPBadRequest(f"The parameter url is too long ({len(url)} > {8190})")
|
81
82
|
|
82
83
|
# Check that it is an internal URL...
|
83
84
|
uri_parts = urlparse(url)
|
84
85
|
if "allowed_hosts" in self.settings:
|
85
|
-
if uri_parts.netloc not in self.settings["allowed_hosts"]:
|
86
|
+
if uri_parts.netloc not in self.settings["allowed_hosts"]:
|
86
87
|
raise HTTPBadRequest(
|
87
|
-
"The requested host '{}' is not part of allowed hosts:
|
88
|
-
|
89
|
-
)
|
88
|
+
f"The requested host '{uri_parts.netloc}' is not part of allowed hosts: "
|
89
|
+
f"{', '.join(self.settings['allowed_hosts'])}"
|
90
90
|
)
|
91
91
|
else:
|
92
92
|
hostname = uri_parts.hostname
|
93
93
|
if hostname != self.request.server_name:
|
94
94
|
raise HTTPBadRequest(
|
95
|
-
"The requested host '{
|
95
|
+
f"The requested host '{hostname!s}' should be '{self.request.server_name!s}'"
|
96
96
|
)
|
97
97
|
|
98
98
|
shortened = False
|
@@ -106,16 +106,16 @@ class Shortener:
|
|
106
106
|
tries = 0
|
107
107
|
while not shortened:
|
108
108
|
ref = "".join(
|
109
|
-
random.choice(string.ascii_letters + string.digits)
|
109
|
+
random.choice(string.ascii_letters + string.digits) # nosec
|
110
110
|
for i in range(self.settings.get("length", 4))
|
111
111
|
)
|
112
112
|
test_url = DBSession.query(Shorturl).filter(Shorturl.ref == ref).all()
|
113
113
|
if not test_url:
|
114
114
|
break
|
115
|
-
tries += 1
|
116
|
-
if tries > 20:
|
115
|
+
tries += 1
|
116
|
+
if tries > 20:
|
117
117
|
message = "No free ref found, considered to increase the length"
|
118
|
-
|
118
|
+
logger.error(message)
|
119
119
|
raise HTTPInternalServerError(message)
|
120
120
|
|
121
121
|
user_email = self.request.user.email if self.request.user is not None else None
|
@@ -135,7 +135,7 @@ class Shortener:
|
|
135
135
|
else:
|
136
136
|
s_url = self.request.route_url("shortener_get", ref=ref)
|
137
137
|
|
138
|
-
if email is not None:
|
138
|
+
if email is not None:
|
139
139
|
send_email_config(
|
140
140
|
self.request.registry.settings,
|
141
141
|
"shortener",
|
@@ -143,7 +143,9 @@ class Shortener:
|
|
143
143
|
full_url=url,
|
144
144
|
short_url=s_url,
|
145
145
|
message=self.request.params.get("message", ""),
|
146
|
+
application_url=self.request.route_url("base"),
|
147
|
+
current_url=self.request.current_route_url(),
|
146
148
|
)
|
147
149
|
|
148
|
-
set_common_headers(self.request, "shortener",
|
150
|
+
set_common_headers(self.request, "shortener", Cache.PRIVATE_NO)
|
149
151
|
return {"short_url": s_url}
|