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.
Files changed (225) hide show
  1. c2cgeoportal_geoportal/__init__.py +245 -95
  2. c2cgeoportal_geoportal/lib/__init__.py +67 -43
  3. c2cgeoportal_geoportal/lib/authentication.py +50 -26
  4. c2cgeoportal_geoportal/lib/bashcolor.py +17 -13
  5. c2cgeoportal_geoportal/lib/cacheversion.py +16 -8
  6. c2cgeoportal_geoportal/lib/caching.py +65 -193
  7. c2cgeoportal_geoportal/lib/check_collector.py +17 -10
  8. c2cgeoportal_geoportal/lib/checker.py +67 -65
  9. c2cgeoportal_geoportal/lib/common_headers.py +167 -0
  10. c2cgeoportal_geoportal/lib/dbreflection.py +61 -46
  11. c2cgeoportal_geoportal/lib/filter_capabilities.py +126 -88
  12. c2cgeoportal_geoportal/lib/fulltextsearch.py +6 -5
  13. c2cgeoportal_geoportal/lib/functionality.py +20 -17
  14. c2cgeoportal_geoportal/lib/headers.py +14 -5
  15. c2cgeoportal_geoportal/lib/i18n.py +4 -4
  16. c2cgeoportal_geoportal/lib/layers.py +30 -11
  17. c2cgeoportal_geoportal/lib/lingua_extractor.py +363 -240
  18. c2cgeoportal_geoportal/lib/loader.py +11 -16
  19. c2cgeoportal_geoportal/lib/metrics.py +28 -17
  20. c2cgeoportal_geoportal/lib/oauth2.py +392 -206
  21. c2cgeoportal_geoportal/lib/wmstparsing.py +105 -84
  22. c2cgeoportal_geoportal/lib/xsd.py +26 -16
  23. c2cgeoportal_geoportal/resources.py +15 -9
  24. c2cgeoportal_geoportal/scaffolds/advance_create/ci/config.yaml +26 -0
  25. c2cgeoportal_geoportal/scaffolds/advance_create/cookiecutter.json +18 -0
  26. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/.dockerignore +6 -0
  27. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/.eslintrc.yaml +19 -0
  28. c2cgeoportal_geoportal/scaffolds/{create/geoportal/+dot+prospector.yaml → advance_create/{{cookiecutter.project}}/geoportal/.prospector.yaml} +8 -2
  29. c2cgeoportal_geoportal/scaffolds/{create/geoportal/Dockerfile_tmpl → advance_create/{{cookiecutter.project}}/geoportal/Dockerfile} +22 -15
  30. c2cgeoportal_geoportal/scaffolds/{create/geoportal/alembic.yaml_tmpl → advance_create/{{cookiecutter.project}}/geoportal/alembic.yaml} +1 -1
  31. c2cgeoportal_geoportal/scaffolds/{create/geoportal/development.ini_tmpl → advance_create/{{cookiecutter.project}}/geoportal/development.ini} +34 -15
  32. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/gunicorn.conf.py +100 -0
  33. c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/lingua-client.cfg +1 -0
  34. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/production.ini +38 -0
  35. c2cgeoportal_geoportal/scaffolds/{create/geoportal/setup.py_tmpl → advance_create/{{cookiecutter.project}}/geoportal/setup.py} +6 -7
  36. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/tsconfig.json +8 -0
  37. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/webpack.api.js +77 -0
  38. c2cgeoportal_geoportal/scaffolds/{create/geoportal/webpack.apps.js_tmpl → advance_create/{{cookiecutter.project}}/geoportal/webpack.apps.js} +29 -28
  39. c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/webpack.commons.js +4 -7
  40. c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/webpack.config.js +1 -1
  41. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/__init__.py +42 -0
  42. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/authentication.py +10 -0
  43. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/dev.py +14 -0
  44. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/models.py +8 -0
  45. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/multi_organization.py +7 -0
  46. c2cgeoportal_geoportal/scaffolds/{create/geoportal/+package+_geoportal → advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/resources.py +4 -3
  47. 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
  48. 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
  49. c2cgeoportal_geoportal/scaffolds/{create/geoportal/+package+_geoportal/subscribers.py_tmpl → advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/subscribers.py} +1 -3
  50. c2cgeoportal_geoportal/scaffolds/advance_update/cookiecutter.json +18 -0
  51. c2cgeoportal_geoportal/scaffolds/{update/geoportal/CONST_Makefile_tmpl → advance_update/{{cookiecutter.project}}/geoportal/CONST_Makefile} +3 -27
  52. c2cgeoportal_geoportal/scaffolds/create/cookiecutter.json +18 -0
  53. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.dockerignore +14 -0
  54. c2cgeoportal_geoportal/scaffolds/create/{+dot+editorconfig → {{cookiecutter.project}}/.editorconfig} +2 -5
  55. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/main.yaml +57 -0
  56. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/rebuild.yaml +46 -0
  57. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/update_l10n.yaml +66 -0
  58. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.gitignore +16 -0
  59. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.prettierignore +1 -0
  60. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.prettierrc.yaml +2 -0
  61. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/Dockerfile +76 -0
  62. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/Makefile +70 -0
  63. c2cgeoportal_geoportal/scaffolds/create/{README.rst_tmpl → {{cookiecutter.project}}/README.rst} +4 -4
  64. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/build +186 -0
  65. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/ci/config.yaml +22 -0
  66. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/ci/docker-compose-check +25 -0
  67. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/ci/requirements.txt +1 -0
  68. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose-db.yaml +26 -0
  69. c2cgeoportal_geoportal/scaffolds/create/{docker-compose-lib.yaml → {{cookiecutter.project}}/docker-compose-lib.yaml} +165 -22
  70. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose-qgis.yaml +23 -0
  71. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose.override.sample.yaml +66 -0
  72. c2cgeoportal_geoportal/scaffolds/create/{docker-compose.yaml → {{cookiecutter.project}}/docker-compose.yaml} +20 -15
  73. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/env.default +101 -0
  74. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/env.project +69 -0
  75. c2cgeoportal_geoportal/scaffolds/create/{geoportal/vars.yaml_tmpl → {{cookiecutter.project}}/geoportal/vars.yaml} +126 -36
  76. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/css/mobile.css +0 -0
  77. c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/data/Readme.txt +3 -3
  78. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/demo.map.tmpl +224 -0
  79. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/mapserver.conf +15 -0
  80. c2cgeoportal_geoportal/scaffolds/create/{mapserver/mapserver.map.tmpl_tmpl → {{cookiecutter.project}}/mapserver/mapserver.map.tmpl} +9 -18
  81. c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A3_Landscape.jrxml +13 -8
  82. c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A3_Portrait.jrxml +13 -8
  83. c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A4_Landscape.jrxml +13 -8
  84. c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A4_Portrait.jrxml +13 -8
  85. c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/config.yaml.tmpl +11 -4
  86. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/localisation.properties +4 -0
  87. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/localisation_fr.properties +4 -0
  88. c2cgeoportal_geoportal/scaffolds/create/{project.yaml_tmpl → {{cookiecutter.project}}/project.yaml} +6 -6
  89. c2cgeoportal_geoportal/scaffolds/create/{pyproject.toml → {{cookiecutter.project}}/pyproject.toml} +4 -0
  90. c2cgeoportal_geoportal/scaffolds/create/{qgisserver/pg_service.conf.tmpl_tmpl → {{cookiecutter.project}}/qgisserver/pg_service.conf.tmpl} +2 -2
  91. c2cgeoportal_geoportal/scaffolds/create/{run_alembic.sh → {{cookiecutter.project}}/run_alembic.sh} +3 -5
  92. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/scripts/db-backup +110 -0
  93. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/scripts/db-restore +114 -0
  94. c2cgeoportal_geoportal/scaffolds/create/{setup.cfg_tmpl → {{cookiecutter.project}}/setup.cfg} +1 -1
  95. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/spell-ignore-words.txt +5 -0
  96. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/tests/__init__.py +0 -0
  97. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/tests/test_app.py +38 -0
  98. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/tilegeneration/config.yaml.tmpl +195 -0
  99. c2cgeoportal_geoportal/scaffolds/update/cookiecutter.json +18 -0
  100. c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/.upgrade.yaml +61 -0
  101. c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/CONST_CHANGELOG.txt +273 -0
  102. c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/CONST_create_template/tests/test_testapp.py +48 -0
  103. c2cgeoportal_geoportal/scaffolds/update/{geoportal → {{cookiecutter.project}}/geoportal}/CONST_config-schema.yaml +64 -17
  104. c2cgeoportal_geoportal/scaffolds/update/{geoportal/CONST_vars.yaml_tmpl → {{cookiecutter.project}}/geoportal/CONST_vars.yaml} +396 -19
  105. c2cgeoportal_geoportal/scripts/__init__.py +16 -30
  106. c2cgeoportal_geoportal/scripts/c2cupgrade.py +272 -234
  107. c2cgeoportal_geoportal/scripts/create_demo_theme.py +3 -6
  108. c2cgeoportal_geoportal/scripts/manage_users.py +34 -39
  109. c2cgeoportal_geoportal/scripts/pcreate.py +310 -0
  110. c2cgeoportal_geoportal/scripts/theme2fts.py +128 -24
  111. c2cgeoportal_geoportal/scripts/urllogin.py +19 -11
  112. c2cgeoportal_geoportal/templates/login.html +88 -84
  113. c2cgeoportal_geoportal/templates/notlogin.html +59 -59
  114. c2cgeoportal_geoportal/templates/testi18n.html +6 -8
  115. c2cgeoportal_geoportal/views/__init__.py +23 -6
  116. c2cgeoportal_geoportal/views/dev.py +9 -7
  117. c2cgeoportal_geoportal/views/dynamic.py +56 -19
  118. c2cgeoportal_geoportal/views/entry.py +85 -24
  119. c2cgeoportal_geoportal/views/fulltextsearch.py +29 -23
  120. c2cgeoportal_geoportal/views/geometry_processing.py +17 -9
  121. c2cgeoportal_geoportal/views/i18n.py +91 -9
  122. c2cgeoportal_geoportal/views/layers.py +166 -133
  123. c2cgeoportal_geoportal/views/login.py +161 -93
  124. c2cgeoportal_geoportal/views/mapserverproxy.py +47 -31
  125. c2cgeoportal_geoportal/views/memory.py +12 -12
  126. c2cgeoportal_geoportal/views/ogcproxy.py +52 -30
  127. c2cgeoportal_geoportal/views/pdfreport.py +30 -26
  128. c2cgeoportal_geoportal/views/printproxy.py +60 -52
  129. c2cgeoportal_geoportal/views/profile.py +24 -23
  130. c2cgeoportal_geoportal/views/proxy.py +88 -72
  131. c2cgeoportal_geoportal/views/raster.py +37 -26
  132. c2cgeoportal_geoportal/views/resourceproxy.py +13 -11
  133. c2cgeoportal_geoportal/views/shortener.py +27 -25
  134. c2cgeoportal_geoportal/views/theme.py +472 -332
  135. c2cgeoportal_geoportal/views/tinyowsproxy.py +42 -44
  136. c2cgeoportal_geoportal/views/vector_tiles.py +80 -0
  137. {c2cgeoportal_geoportal-2.6.0.dist-info → c2cgeoportal_geoportal-2.8.1.180.dist-info}/METADATA +19 -8
  138. c2cgeoportal_geoportal-2.8.1.180.dist-info/RECORD +191 -0
  139. {c2cgeoportal_geoportal-2.6.0.dist-info → c2cgeoportal_geoportal-2.8.1.180.dist-info}/WHEEL +1 -1
  140. {c2cgeoportal_geoportal-2.6.0.dist-info → c2cgeoportal_geoportal-2.8.1.180.dist-info}/entry_points.txt +3 -0
  141. tests/__init__.py +10 -5
  142. tests/test_cachebuster.py +3 -5
  143. tests/test_caching.py +18 -26
  144. tests/test_checker.py +1 -3
  145. tests/test_decimaljson.py +5 -5
  146. tests/test_headerstween.py +1 -3
  147. tests/test_i18n.py +2 -2
  148. tests/test_init.py +16 -20
  149. tests/test_locale_negociator.py +4 -6
  150. tests/test_mapserverproxy_route_predicate.py +1 -4
  151. tests/test_raster.py +15 -17
  152. tests/test_wmstparsing.py +10 -12
  153. tests/xmlstr.py +1 -3
  154. c2cgeoportal_geoportal/scaffolds/__init__.py +0 -227
  155. c2cgeoportal_geoportal/scaffolds/create/+dot+dockerignore_tmpl +0 -12
  156. c2cgeoportal_geoportal/scaffolds/create/+dot+github/workflows/main.yaml_tmpl +0 -89
  157. c2cgeoportal_geoportal/scaffolds/create/+dot+github/workflows/rebuild.yaml_tmpl +0 -78
  158. c2cgeoportal_geoportal/scaffolds/create/+dot+gitignore_tmpl +0 -16
  159. c2cgeoportal_geoportal/scaffolds/create/Dockerfile_tmpl +0 -67
  160. c2cgeoportal_geoportal/scaffolds/create/Makefile +0 -3
  161. c2cgeoportal_geoportal/scaffolds/create/build_tmpl +0 -167
  162. c2cgeoportal_geoportal/scaffolds/create/ci/config.yaml_tmpl +0 -23
  163. c2cgeoportal_geoportal/scaffolds/create/ci/requirements.txt +0 -1
  164. c2cgeoportal_geoportal/scaffolds/create/ci/trigger +0 -68
  165. c2cgeoportal_geoportal/scaffolds/create/docker-compose.override.sample.yaml +0 -54
  166. c2cgeoportal_geoportal/scaffolds/create/env.default_tmpl +0 -67
  167. c2cgeoportal_geoportal/scaffolds/create/env.project_tmpl +0 -48
  168. c2cgeoportal_geoportal/scaffolds/create/geoportal/+dot+dockerignore_tmpl +0 -6
  169. c2cgeoportal_geoportal/scaffolds/create/geoportal/+dot+eslintrc_tmpl +0 -15
  170. c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/__init__.py_tmpl +0 -58
  171. c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/models.py_tmpl +0 -10
  172. c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/static/robot.txt +0 -3
  173. c2cgeoportal_geoportal/scaffolds/create/geoportal/production.ini_tmpl +0 -106
  174. c2cgeoportal_geoportal/scaffolds/create/geoportal/tools/extract-messages.js +0 -39
  175. c2cgeoportal_geoportal/scaffolds/create/geoportal/tsconfig.json_tmpl +0 -9
  176. c2cgeoportal_geoportal/scaffolds/create/geoportal/webpack.api.js_tmpl +0 -72
  177. c2cgeoportal_geoportal/scaffolds/create/mapserver/demo.map.tmpl_tmpl +0 -262
  178. c2cgeoportal_geoportal/scaffolds/create/mapserver/tinyows.xml +0 -36
  179. c2cgeoportal_geoportal/scaffolds/create/print/print-apps/+package+/config.yaml +0 -168
  180. c2cgeoportal_geoportal/scaffolds/create/qgisserver/geomapfish.yaml.tmpl_tmpl +0 -16
  181. c2cgeoportal_geoportal/scaffolds/create/spell-ignore-words.txt +0 -1
  182. c2cgeoportal_geoportal/scaffolds/create/tilegeneration/config.yaml.tmpl_tmpl +0 -185
  183. c2cgeoportal_geoportal/scaffolds/create/yamllint.yaml +0 -11
  184. c2cgeoportal_geoportal/scaffolds/update/+dot+upgrade.yaml_tmpl +0 -181
  185. c2cgeoportal_geoportal/scaffolds/update/CONST_CHANGELOG.txt_tmpl +0 -454
  186. c2cgeoportal_geoportal/templates/dynamic.js +0 -21
  187. c2cgeoportal_geoportal-2.6.0.dist-info/RECORD +0 -173
  188. /c2cgeoportal_geoportal/{scaffolds/create/geoportal/+package+_geoportal/static/css/desktop.css → py.typed} +0 -0
  189. /c2cgeoportal_geoportal/scaffolds/{create/geoportal/Makefile_tmpl → advance_create/{{cookiecutter.project}}/geoportal/Makefile} +0 -0
  190. /c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/alembic.ini +0 -0
  191. /c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/language_mapping +0 -0
  192. /c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/lingua-server.cfg +0 -0
  193. /c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/requirements.txt +0 -0
  194. /c2cgeoportal_geoportal/scaffolds/{create/geoportal/+package+_geoportal → advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/views/__init__.py +0 -0
  195. /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
  196. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal/static/css/iframe_api.css → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/css/desktop.css} +0 -0
  197. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal/static/css/mobile.css → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/css/iframe_api.css} +0 -0
  198. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/banner_left.png +0 -0
  199. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/banner_right.png +0 -0
  200. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/blank.png +0 -0
  201. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/markers/marker-blue.png +0 -0
  202. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/markers/marker-gold.png +0 -0
  203. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/markers/marker-green.png +0 -0
  204. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/markers/marker.png +0 -0
  205. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/robot.txt.tmpl +0 -0
  206. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/data/TM_EUROPE_BORDERS-0.3.sql +0 -0
  207. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Arial.ttf +0 -0
  208. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Arialbd.ttf +0 -0
  209. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Arialbi.ttf +0 -0
  210. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Ariali.ttf +0 -0
  211. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/NotoSans-Bold.ttf +0 -0
  212. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/NotoSans-BoldItalic.ttf +0 -0
  213. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/NotoSans-Italic.ttf +0 -0
  214. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/NotoSans-Regular.ttf +0 -0
  215. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Verdana.ttf +0 -0
  216. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Verdanab.ttf +0 -0
  217. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Verdanai.ttf +0 -0
  218. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Verdanaz.ttf +0 -0
  219. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts.conf +0 -0
  220. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/tinyows.xml.tmpl +0 -0
  221. /c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/legend.jrxml +0 -0
  222. /c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/logo.png +0 -0
  223. /c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/north.svg +0 -0
  224. /c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/results.jrxml +0 -0
  225. {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
- # -*- coding: utf-8 -*-
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 urllib.parse
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 c2cgeoportal_geoportal.lib.caching import (
41
- NO_CACHE,
42
- PRIVATE_CACHE,
43
- PUBLIC_CACHE,
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
- def __init__(self, request):
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["host_forward_host"]
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(self, url, params=None, method=None, cache=False, body=None, headers=None):
59
- # get query string
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
- parsed_url = urllib.parse.urlparse(url)
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: # pragma: no cover
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
- if parsed_url.hostname not in self.host_forward_host and "Host" in headers: # pragma: no cover
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 releay want to respect the specification, we should chain with the content of the previous
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: # pragma: no cover
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(method, url, data=body, headers=headers, **self.http_options)
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: # pragma: no cover
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
- "{}: {}".format(h, v if h not in ("Authorization", "Cookies") else "***")
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.error("\n".join(errors), *args1, exc_info=True)
135
+ LOG.exception("\n".join(errors), *args1)
130
136
 
131
- raise HTTPBadGateway("Error on backend, See logs for detail")
137
+ raise HTTPBadGateway( # pylint: disable=raise-missing-from
138
+ "Error on backend, See logs for detail"
139
+ )
132
140
 
133
- if not response.ok: # pragma: no cover
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
- "{}: {}".format(h, v if h not in ("Authorization", "Cookies") else "***")
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): # pragma: no cover
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, service_name, url, headers_update=None, public=False, **kwargs
176
- ): # pragma: no cover
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 = (PUBLIC_CACHE if public else PRIVATE_CACHE) if cache else NO_CACHE
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
- ]: # pragma: no cover
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"]: # pragma: no cover
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 dict((k.lower(), str(v).lower()) for k, v in params.items())
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 _get_headers(self):
240
- headers = self.request.headers
241
- if "Cookie" in headers: # pragma: no cover
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
- # -*- coding: utf-8 -*-
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 # noqa, pylint: disable=unused-import
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.caching import NO_CACHE, set_common_headers
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
- data: Dict[str, Any] = {}
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): # pylint: disable=unused-variable
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".format(name))
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".format(name, self.request.params[name])
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".format(name, self.request.params[name])
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".format(layer))
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", NO_CACHE)
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(self, layer, name, lon, lat):
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
- if "round" in layer:
139
- result = self._round(result, layer["round"])
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
- result = decimal.Decimal(str(result))
149
+ result_d = decimal.Decimal(str(result))
142
150
 
143
- return result
151
+ return result_d
144
152
 
145
153
  @staticmethod
146
- def _get_value(layer, name, dataset, lon, lat):
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), " "lon/lat: %dx%d, index: %dx%d, shape: %dx%d.",
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
- # -*- coding: utf-8 -*-
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.caching import NO_CACHE
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
- def __init__(self, request): # pragma: no cover
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): # pragma: no cover
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: # pragma: no cover
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 = NO_CACHE
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.text, cache_control, "externalresource", content_type=content_type
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 url not found: %s", target)
72
+ LOG.warning("Target URL not found: %s", target)
71
73
  return HTTPBadRequest("URL not allowed")
@@ -1,6 +1,4 @@
1
- # -*- coding: utf-8 -*-
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.caching import NO_CACHE, set_common_headers
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
- def __init__(self, request):
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 '{0!s}' not found".format(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", NO_CACHE)
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: # pragma: no cover
80
- raise HTTPBadRequest("The parameter url is too long ({} > {})".format(len(url), 8190))
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"]: # pragma: no cover
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: {}".format(
88
- uri_parts.netloc, ", ".join(self.settings["allowed_hosts"])
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 '{0!s}' should be '{1!s}'".format(hostname, self.request.server_name)
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 # pragma: no cover
116
- if tries > 20: # pragma: no cover
115
+ tries += 1
116
+ if tries > 20:
117
117
  message = "No free ref found, considered to increase the length"
118
- logging.error(message)
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: # pragma: no cover
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", NO_CACHE)
150
+ set_common_headers(self.request, "shortener", Cache.PRIVATE_NO)
149
151
  return {"short_url": s_url}