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