c2cgeoportal-geoportal 2.5.0.100__py2.py3-none-any.whl → 2.7.1.83__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 (224) hide show
  1. c2cgeoportal_geoportal/__init__.py +261 -130
  2. c2cgeoportal_geoportal/lib/__init__.py +72 -120
  3. c2cgeoportal_geoportal/lib/authentication.py +170 -21
  4. c2cgeoportal_geoportal/lib/bashcolor.py +17 -13
  5. c2cgeoportal_geoportal/lib/cacheversion.py +19 -11
  6. c2cgeoportal_geoportal/lib/caching.py +66 -160
  7. c2cgeoportal_geoportal/lib/check_collector.py +17 -10
  8. c2cgeoportal_geoportal/lib/checker.py +62 -64
  9. c2cgeoportal_geoportal/lib/common_headers.py +170 -0
  10. c2cgeoportal_geoportal/lib/dbreflection.py +70 -31
  11. c2cgeoportal_geoportal/lib/filter_capabilities.py +124 -96
  12. c2cgeoportal_geoportal/lib/fulltextsearch.py +50 -0
  13. c2cgeoportal_geoportal/lib/functionality.py +36 -21
  14. c2cgeoportal_geoportal/lib/headers.py +14 -5
  15. c2cgeoportal_geoportal/lib/i18n.py +39 -0
  16. c2cgeoportal_geoportal/lib/layers.py +29 -10
  17. c2cgeoportal_geoportal/lib/lingua_extractor.py +408 -211
  18. c2cgeoportal_geoportal/lib/loader.py +18 -16
  19. c2cgeoportal_geoportal/lib/metrics.py +29 -18
  20. c2cgeoportal_geoportal/lib/oauth2.py +1036 -0
  21. c2cgeoportal_geoportal/lib/wmstparsing.py +115 -90
  22. c2cgeoportal_geoportal/lib/xsd.py +29 -19
  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/+dot+prospector.yaml → advance_create/{{cookiecutter.project}}/geoportal/.prospector.yaml} +8 -4
  29. c2cgeoportal_geoportal/scaffolds/{create/geoportal/Dockerfile_tmpl → advance_create/{{cookiecutter.project}}/geoportal/Dockerfile} +24 -15
  30. c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/alembic.ini +1 -0
  31. c2cgeoportal_geoportal/scaffolds/{create/geoportal/alembic.yaml_tmpl → advance_create/{{cookiecutter.project}}/geoportal/alembic.yaml} +1 -1
  32. c2cgeoportal_geoportal/scaffolds/{create/geoportal/development.ini_tmpl → advance_create/{{cookiecutter.project}}/geoportal/development.ini} +34 -15
  33. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/gunicorn.conf.py +104 -0
  34. c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/lingua-client.cfg +1 -0
  35. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/production.ini +38 -0
  36. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/requirements.txt +2 -0
  37. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/setup.py +25 -0
  38. c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/tools/extract-messages.js +8 -6
  39. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/tsconfig.json +8 -0
  40. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/webpack.api.js +75 -0
  41. c2cgeoportal_geoportal/scaffolds/{create/geoportal/webpack.apps.js_tmpl → advance_create/{{cookiecutter.project}}/geoportal/webpack.apps.js} +31 -28
  42. c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/webpack.commons.js +3 -7
  43. c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/webpack.config.js +4 -4
  44. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/__init__.py +47 -0
  45. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/authentication.py +10 -0
  46. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/dev.py +14 -0
  47. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/models.py +8 -0
  48. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/multi_organization.py +7 -0
  49. c2cgeoportal_geoportal/scaffolds/{create/geoportal/+package+_geoportal → advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/resources.py +4 -3
  50. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static-ngeo/api/index.js +12 -0
  51. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static-ngeo/js/{{cookiecutter.package}}module.js +25 -0
  52. c2cgeoportal_geoportal/scaffolds/{create/geoportal/+package+_geoportal/subscribers.py_tmpl → advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/subscribers.py} +3 -6
  53. c2cgeoportal_geoportal/scaffolds/advance_update/cookiecutter.json +18 -0
  54. c2cgeoportal_geoportal/scaffolds/{update/geoportal/CONST_Makefile_tmpl → advance_update/{{cookiecutter.project}}/geoportal/CONST_Makefile} +32 -20
  55. c2cgeoportal_geoportal/scaffolds/create/cookiecutter.json +18 -0
  56. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.dockerignore +14 -0
  57. c2cgeoportal_geoportal/scaffolds/create/{+dot+editorconfig → {{cookiecutter.project}}/.editorconfig} +4 -8
  58. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/main.yaml +43 -0
  59. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/rebuild.yaml +46 -0
  60. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/update_l10n.yaml +65 -0
  61. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.gitignore +16 -0
  62. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.prettierignore +1 -0
  63. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.prettierrc.yaml +2 -0
  64. c2cgeoportal_geoportal/scaffolds/create/{Dockerfile_tmpl → {{cookiecutter.project}}/Dockerfile} +34 -24
  65. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/Makefile +14 -0
  66. c2cgeoportal_geoportal/scaffolds/create/{README.rst_tmpl → {{cookiecutter.project}}/README.rst} +4 -4
  67. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/build +158 -0
  68. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/ci/config.yaml +25 -0
  69. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/ci/requirements.txt +1 -0
  70. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose-lib.yaml +474 -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} +43 -18
  73. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/env.default +82 -0
  74. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/env.project +60 -0
  75. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/vars.yaml +396 -0
  76. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/css/mobile.css +0 -0
  77. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/images/markers/marker-blue.png +0 -0
  78. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/images/markers/marker-gold.png +0 -0
  79. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/images/markers/marker-green.png +0 -0
  80. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/images/markers/marker.png +0 -0
  81. c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/data/Readme.txt +1 -1
  82. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/demo.map.tmpl +224 -0
  83. c2cgeoportal_geoportal/scaffolds/create/{mapserver/mapserver.map.tmpl_tmpl → {{cookiecutter.project}}/mapserver/mapserver.map.tmpl} +7 -15
  84. c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A3_Landscape.jrxml +17 -9
  85. c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A3_Portrait.jrxml +17 -9
  86. c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A4_Landscape.jrxml +17 -9
  87. c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/A4_Portrait.jrxml +17 -9
  88. c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/config.yaml.tmpl +30 -27
  89. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/legend.jrxml +109 -0
  90. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/localisation.properties +4 -0
  91. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/localisation_fr.properties +4 -0
  92. c2cgeoportal_geoportal/scaffolds/create/{project.yaml_tmpl → {{cookiecutter.project}}/project.yaml} +6 -6
  93. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/pyproject.toml +3 -0
  94. c2cgeoportal_geoportal/scaffolds/create/{qgisserver/pg_service.conf.tmpl_tmpl → {{cookiecutter.project}}/qgisserver/pg_service.conf.tmpl} +2 -2
  95. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/scripts/db-backup +107 -0
  96. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/scripts/db-restore +111 -0
  97. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/setup.cfg +7 -0
  98. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/spell-ignore-words.txt +3 -0
  99. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/tilegeneration/config.yaml.tmpl +195 -0
  100. c2cgeoportal_geoportal/scaffolds/update/cookiecutter.json +18 -0
  101. c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/.upgrade.yaml +191 -0
  102. c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/CONST_CHANGELOG.txt +1153 -0
  103. c2cgeoportal_geoportal/scaffolds/update/{geoportal → {{cookiecutter.project}}/geoportal}/CONST_config-schema.yaml +99 -47
  104. c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/geoportal/CONST_vars.yaml +1412 -0
  105. c2cgeoportal_geoportal/scripts/__init__.py +17 -33
  106. c2cgeoportal_geoportal/scripts/c2cupgrade.py +295 -200
  107. c2cgeoportal_geoportal/scripts/create_demo_theme.py +5 -6
  108. c2cgeoportal_geoportal/scripts/manage_users.py +34 -37
  109. c2cgeoportal_geoportal/scripts/pcreate.py +312 -0
  110. c2cgeoportal_geoportal/scripts/theme2fts.py +92 -25
  111. c2cgeoportal_geoportal/scripts/urllogin.py +23 -17
  112. c2cgeoportal_geoportal/templates/login.html +88 -84
  113. c2cgeoportal_geoportal/templates/notlogin.html +62 -0
  114. c2cgeoportal_geoportal/templates/testi18n.html +6 -8
  115. c2cgeoportal_geoportal/views/__init__.py +23 -4
  116. c2cgeoportal_geoportal/views/dev.py +9 -7
  117. c2cgeoportal_geoportal/views/dynamic.py +70 -40
  118. c2cgeoportal_geoportal/views/entry.py +93 -24
  119. c2cgeoportal_geoportal/views/fulltextsearch.py +36 -29
  120. c2cgeoportal_geoportal/views/geometry_processing.py +15 -7
  121. c2cgeoportal_geoportal/views/i18n.py +91 -9
  122. c2cgeoportal_geoportal/views/layers.py +173 -134
  123. c2cgeoportal_geoportal/views/login.py +206 -87
  124. c2cgeoportal_geoportal/views/mapserverproxy.py +59 -35
  125. c2cgeoportal_geoportal/views/memory.py +13 -13
  126. c2cgeoportal_geoportal/views/ogcproxy.py +48 -30
  127. c2cgeoportal_geoportal/views/pdfreport.py +31 -27
  128. c2cgeoportal_geoportal/views/printproxy.py +67 -53
  129. c2cgeoportal_geoportal/views/profile.py +25 -24
  130. c2cgeoportal_geoportal/views/proxy.py +97 -68
  131. c2cgeoportal_geoportal/views/raster.py +47 -29
  132. c2cgeoportal_geoportal/views/resourceproxy.py +13 -11
  133. c2cgeoportal_geoportal/views/shortener.py +31 -24
  134. c2cgeoportal_geoportal/views/theme.py +475 -365
  135. c2cgeoportal_geoportal/views/tinyowsproxy.py +46 -39
  136. c2cgeoportal_geoportal/views/vector_tiles.py +80 -0
  137. {c2cgeoportal_geoportal-2.5.0.100.dist-info → c2cgeoportal_geoportal-2.7.1.83.dist-info}/METADATA +16 -11
  138. c2cgeoportal_geoportal-2.7.1.83.dist-info/RECORD +185 -0
  139. {c2cgeoportal_geoportal-2.5.0.100.dist-info → c2cgeoportal_geoportal-2.7.1.83.dist-info}/WHEEL +1 -1
  140. {c2cgeoportal_geoportal-2.5.0.100.dist-info → c2cgeoportal_geoportal-2.7.1.83.dist-info}/entry_points.txt +3 -1
  141. tests/__init__.py +24 -3
  142. tests/test_cachebuster.py +1 -3
  143. tests/test_caching.py +19 -26
  144. tests/test_checker.py +2 -3
  145. tests/test_decimaljson.py +4 -4
  146. tests/test_headerstween.py +0 -3
  147. tests/test_i18n.py +31 -0
  148. tests/test_init.py +12 -27
  149. tests/test_locale_negociator.py +6 -6
  150. tests/test_mapserverproxy_route_predicate.py +0 -2
  151. tests/test_raster.py +18 -5
  152. tests/test_wmstparsing.py +7 -8
  153. c2cgeoportal_geoportal/scaffolds/__init__.py +0 -226
  154. c2cgeoportal_geoportal/scaffolds/create/+dot+dockerignore_tmpl +0 -11
  155. c2cgeoportal_geoportal/scaffolds/create/+dot+github/workflows/ci.yaml_tmpl +0 -56
  156. c2cgeoportal_geoportal/scaffolds/create/+dot+gitignore_tmpl +0 -12
  157. c2cgeoportal_geoportal/scaffolds/create/build_tmpl +0 -144
  158. c2cgeoportal_geoportal/scaffolds/create/docker-compose-lib.yaml_tmpl +0 -302
  159. c2cgeoportal_geoportal/scaffolds/create/docker-compose.override.sample.yaml_tmpl +0 -54
  160. c2cgeoportal_geoportal/scaffolds/create/env.default_tmpl +0 -49
  161. c2cgeoportal_geoportal/scaffolds/create/env.project_tmpl +0 -39
  162. c2cgeoportal_geoportal/scaffolds/create/geoportal/+dot+dockerignore_tmpl +0 -5
  163. c2cgeoportal_geoportal/scaffolds/create/geoportal/+dot+eslintrc_tmpl +0 -19
  164. c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/__init__.py_tmpl +0 -48
  165. c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/models.py_tmpl +0 -10
  166. c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/static/images/favicon.ico +0 -0
  167. c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/static/robot.txt +0 -3
  168. c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/static-ngeo/api/index.js_tmpl +0 -37
  169. c2cgeoportal_geoportal/scaffolds/create/geoportal/+package+_geoportal/static-ngeo/js/+package+module.js_tmpl +0 -22
  170. c2cgeoportal_geoportal/scaffolds/create/geoportal/production.ini_tmpl +0 -106
  171. c2cgeoportal_geoportal/scaffolds/create/geoportal/requirements.txt +0 -2
  172. c2cgeoportal_geoportal/scaffolds/create/geoportal/setup.py_tmpl +0 -18
  173. c2cgeoportal_geoportal/scaffolds/create/geoportal/tsconfig.json_tmpl +0 -9
  174. c2cgeoportal_geoportal/scaffolds/create/geoportal/vars.yaml_tmpl +0 -224
  175. c2cgeoportal_geoportal/scaffolds/create/geoportal/webpack.api.js_tmpl +0 -71
  176. c2cgeoportal_geoportal/scaffolds/create/mapserver/demo.map.tmpl_tmpl +0 -262
  177. c2cgeoportal_geoportal/scaffolds/create/mapserver/tinyows.xml +0 -36
  178. c2cgeoportal_geoportal/scaffolds/create/print/print-apps/+package+/config.yaml +0 -166
  179. c2cgeoportal_geoportal/scaffolds/create/print/print-apps/+package+/legend.jrxml +0 -27
  180. c2cgeoportal_geoportal/scaffolds/create/qgisserver/geomapfish.yaml.tmpl_tmpl +0 -29
  181. c2cgeoportal_geoportal/scaffolds/create/scripts/publish-docker +0 -124
  182. c2cgeoportal_geoportal/scaffolds/create/setup.cfg_tmpl +0 -3
  183. c2cgeoportal_geoportal/scaffolds/create/spell-ignore-words.txt +0 -1
  184. c2cgeoportal_geoportal/scaffolds/create/tilegeneration/config.yaml.tmpl_tmpl +0 -169
  185. c2cgeoportal_geoportal/scaffolds/create/yamllint.yaml +0 -11
  186. c2cgeoportal_geoportal/scaffolds/update/+dot+upgrade.yaml_tmpl +0 -171
  187. c2cgeoportal_geoportal/scaffolds/update/CONST_CHANGELOG.txt_tmpl +0 -64
  188. c2cgeoportal_geoportal/scaffolds/update/geoportal/CONST_vars.yaml_tmpl +0 -783
  189. c2cgeoportal_geoportal/templates/dynamic.js +0 -21
  190. c2cgeoportal_geoportal-2.5.0.100.dist-info/RECORD +0 -162
  191. tests/test_get_url.py +0 -96
  192. tests/test_lib.py +0 -77
  193. /c2cgeoportal_geoportal/{scaffolds/create/geoportal/+package+_geoportal/static/css/desktop.css → py.typed} +0 -0
  194. /c2cgeoportal_geoportal/scaffolds/{create/geoportal/Makefile_tmpl → advance_create/{{cookiecutter.project}}/geoportal/Makefile} +0 -0
  195. /c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/language_mapping +0 -0
  196. /c2cgeoportal_geoportal/scaffolds/{create → advance_create/{{cookiecutter.project}}}/geoportal/lingua-server.cfg +0 -0
  197. /c2cgeoportal_geoportal/scaffolds/{create/geoportal/+package+_geoportal → advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/views/__init__.py +0 -0
  198. /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
  199. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal/static/css/iframe_api.css → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/css/desktop.css} +0 -0
  200. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal/static/css/mobile.css → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/css/iframe_api.css} +0 -0
  201. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/banner_left.png +0 -0
  202. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/banner_right.png +0 -0
  203. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/images/blank.png +0 -0
  204. /c2cgeoportal_geoportal/scaffolds/create/{geoportal/+package+_geoportal → {{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal}/static/robot.txt.tmpl +0 -0
  205. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/data/TM_EUROPE_BORDERS-0.3.sql +0 -0
  206. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Arial.ttf +0 -0
  207. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Arialbd.ttf +0 -0
  208. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Arialbi.ttf +0 -0
  209. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Ariali.ttf +0 -0
  210. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/NotoSans-Bold.ttf +0 -0
  211. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/NotoSans-BoldItalic.ttf +0 -0
  212. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/NotoSans-Italic.ttf +0 -0
  213. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/NotoSans-Regular.ttf +0 -0
  214. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Verdana.ttf +0 -0
  215. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Verdanab.ttf +0 -0
  216. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Verdanai.ttf +0 -0
  217. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts/Verdanaz.ttf +0 -0
  218. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/fonts.conf +0 -0
  219. /c2cgeoportal_geoportal/scaffolds/create/{mapserver → {{cookiecutter.project}}/mapserver}/tinyows.xml.tmpl +0 -0
  220. /c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/logo.png +0 -0
  221. /c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/north.svg +0 -0
  222. /c2cgeoportal_geoportal/scaffolds/create/{print/print-apps/+package+ → {{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}}/results.jrxml +0 -0
  223. /c2cgeoportal_geoportal/scaffolds/create/{run_alembic.sh → {{cookiecutter.project}}/run_alembic.sh} +0 -0
  224. {c2cgeoportal_geoportal-2.5.0.100.dist-info → c2cgeoportal_geoportal-2.7.1.83.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
- # Copyright (c) 2011-2020, Camptocamp SA
3
+ # Copyright (c) 2011-2022, 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
- from typing import Dict, List, Union
34
- import urllib.parse
33
+ from typing import Any, Dict, List, Optional, Union
35
34
 
36
- from pyramid.httpexceptions import HTTPBadGateway, exception_response
37
- from pyramid.response import Response
35
+ import pyramid.request
36
+ import pyramid.response
38
37
  import requests
38
+ from pyramid.httpexceptions import HTTPBadGateway, exception_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 previus
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,34 +95,52 @@ 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],
117
125
  url,
118
126
  method,
119
- "\n".join(["{}: {}".format(*h) for h in list(headers.items())]),
127
+ "\n".join(
128
+ [
129
+ f"{h}: {v if h not in ('Authorization', 'Cookies') else '***'}"
130
+ for h, v in list(headers.items())
131
+ ]
132
+ ),
120
133
  ]
121
- if method in ("POST", "PUT"):
134
+ if method in ("POST", "PUT") and body is not None:
122
135
  errors += ["--- Query with body ---", "%s"]
123
136
  args1.append(body.decode("utf-8"))
124
- LOG.error("\n".join(errors), *args1, exc_info=True)
137
+ LOG.exception("\n".join(errors), *args1)
125
138
 
126
- 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
+ )
127
142
 
128
- if not response.ok: # pragma: no cover
143
+ if not response.ok:
129
144
  errors = [
130
145
  "Error '%s' in response of URL:",
131
146
  "%s",
@@ -136,12 +151,17 @@ class Proxy:
136
151
  ]
137
152
  args2: List[Union[str, int]] = [
138
153
  response.reason,
139
- url,
154
+ url.url(),
140
155
  response.status_code,
141
156
  method,
142
- "\n".join(["{}: {}".format(*h) for h in list(headers.items())]),
157
+ "\n".join(
158
+ [
159
+ f"{h}: {v if h not in ('Authorization', 'Cookies') else '***'}"
160
+ for h, v in list(headers.items())
161
+ ]
162
+ ),
143
163
  ]
144
- if method in ("POST", "PUT"):
164
+ if method in ("POST", "PUT") and body is not None:
145
165
  errors += ["--- Query with body ---", "%s"]
146
166
  args2.append(body.decode("utf-8"))
147
167
  errors += ["--- Return content ---", "%s"]
@@ -149,19 +169,26 @@ class Proxy:
149
169
  LOG.error("\n".join(errors), *args2)
150
170
 
151
171
  raise exception_response(response.status_code)
172
+ if not response.headers.get("Content-Type", "").startswith("image/"):
173
+ LOG.debug("Get result for URL: %s:\n%s.", url, body)
152
174
 
153
175
  return response
154
176
 
155
- @CACHE_REGION.cache_on_arguments()
156
- def _proxy_cache(self, method, *args, **kwargs): # pragma: no cover
177
+ @CACHE_REGION.cache_on_arguments() # type: ignore
178
+ def _proxy_cache(self, method: str, *args: Any, **kwargs: Any) -> pyramid.response.Response:
157
179
  # Method is only for the cache
158
180
  del method
159
181
  kwargs["cache"] = True
160
182
  return self._proxy(*args, **kwargs)
161
183
 
162
184
  def _proxy_response(
163
- self, service_name, url, headers_update=None, public=False, **kwargs
164
- ): # pragma: no cover
185
+ self,
186
+ service_name: str,
187
+ url: Url,
188
+ headers_update: Optional[Dict[str, str]] = None,
189
+ public: bool = False,
190
+ **kwargs: Any,
191
+ ) -> pyramid.response.Response:
165
192
  if headers_update is None:
166
193
  headers_update = {}
167
194
  cache = kwargs.get("cache", False)
@@ -170,23 +197,25 @@ class Proxy:
170
197
  else:
171
198
  response = self._proxy(url, **kwargs)
172
199
 
173
- cache_control = (PUBLIC_CACHE if public else PRIVATE_CACHE) if cache else NO_CACHE
200
+ cache_control = (
201
+ (Cache.PUBLIC if public else Cache.PRIVATE)
202
+ if cache
203
+ else (Cache.PUBLIC_NO if public else Cache.PRIVATE_NO)
204
+ )
174
205
  return self._build_response(
175
206
  response, response.content, cache_control, service_name, headers_update=headers_update
176
207
  )
177
208
 
178
209
  def _build_response(
179
210
  self,
180
- response,
181
- content,
182
- cache_control,
183
- service_name,
184
- headers=None,
185
- headers_update=None,
186
- content_type=None,
187
- ):
188
- if isinstance(content, str):
189
- content = content.encode("utf-8")
211
+ response: pyramid.response.Response,
212
+ content: bytes,
213
+ cache_control: Cache,
214
+ service_name: str,
215
+ headers: Optional[Dict[str, str]] = None,
216
+ headers_update: Optional[Dict[str, str]] = None,
217
+ content_type: Optional[str] = None,
218
+ ) -> pyramid.response.Response:
190
219
  if headers_update is None:
191
220
  headers_update = {}
192
221
  headers = response.headers if headers is None else headers
@@ -204,28 +233,28 @@ class Proxy:
204
233
  "Trailers",
205
234
  "Transfer-Encoding",
206
235
  "Upgrade",
207
- ]: # pragma: no cover
236
+ ]:
208
237
  if header in headers:
209
238
  del headers[header]
210
239
  # Other problematic headers
211
- for header in ["Content-Length", "Content-Location", "Content-Encoding"]: # pragma: no cover
240
+ for header in ["Content-Length", "Content-Location", "Content-Encoding"]:
212
241
  if header in headers:
213
242
  del headers[header]
214
243
 
215
244
  headers.update(headers_update)
216
245
 
217
- response = Response(content, status=response.status_code, headers=headers)
246
+ response = pyramid.response.Response(content, status=response.status_code, headers=headers)
218
247
 
219
248
  return set_common_headers(
220
249
  self.request, service_name, cache_control, response=response, content_type=content_type
221
250
  )
222
251
 
223
252
  @staticmethod
224
- def _get_lower_params(params):
253
+ def _get_lower_params(params: Dict[str, str]) -> Dict[str, str]:
225
254
  return dict((k.lower(), str(v).lower()) for k, v in params.items())
226
255
 
227
- def _get_headers(self):
228
- headers = self.request.headers
229
- if "Cookie" in headers: # pragma: no cover
256
+ def get_headers(self) -> Dict[str, str]:
257
+ headers: Dict[str, str] = self.request.headers
258
+ if "Cookie" in headers:
230
259
  headers.pop("Cookie")
231
260
  return headers
@@ -1,6 +1,4 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- # Copyright (c) 2012-2019, Camptocamp SA
1
+ # Copyright (c) 2012-2021, Camptocamp SA
4
2
  # All rights reserved.
5
3
 
6
4
  # Redistribution and use in source and binary forms, with or without
@@ -28,31 +26,40 @@
28
26
  # either expressed or implied, of the FreeBSD Project.
29
27
 
30
28
 
31
- from decimal import Decimal
29
+ import decimal
32
30
  import logging
33
31
  import math
34
32
  import os
35
- from typing import Any, Dict # noqa, pylint: disable=unused-import
33
+ import traceback
34
+ from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple
36
35
 
36
+ import numpy
37
+ import pyramid.request
38
+ import zope.event.classhandler
37
39
  from pyramid.httpexceptions import HTTPBadRequest, HTTPNotFound
38
40
  from pyramid.view import view_config
39
- import zope.event.classhandler
41
+ from rasterio.io import DatasetReader
40
42
 
41
43
  from c2cgeoportal_commons.models import InvalidateCacheEvent
42
- 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
43
48
 
44
49
  LOG = logging.getLogger(__name__)
45
50
 
46
51
 
47
52
  class Raster:
48
- data: Dict[str, Any] = {}
53
+ """All the view concerned the raster (point, not the profile profile)."""
49
54
 
50
- def __init__(self, request):
55
+ data: Dict[str, "fiona.collection.Collection"] = {}
56
+
57
+ def __init__(self, request: pyramid.request.Request):
51
58
  self.request = request
52
59
  self.rasters = self.request.registry.settings["raster"]
53
60
 
54
- @zope.event.classhandler.handler(InvalidateCacheEvent)
55
- def handle(event: InvalidateCacheEvent): # pylint: disable=unused-variable
61
+ @zope.event.classhandler.handler(InvalidateCacheEvent) # type: ignore
62
+ def handle(event: InvalidateCacheEvent) -> None:
56
63
  del event
57
64
  for _, v in Raster.data.items():
58
65
  v.close()
@@ -60,21 +67,21 @@ class Raster:
60
67
 
61
68
  def _get_required_finite_float_param(self, name: str) -> float:
62
69
  if name not in self.request.params:
63
- raise HTTPBadRequest("'{}' should be in the query string parameters".format(name))
70
+ raise HTTPBadRequest(f"'{name}' should be in the query string parameters")
64
71
  try:
65
72
  result = float(self.request.params[name])
66
73
  except ValueError:
67
- raise HTTPBadRequest(
68
- "'{}' ({}) 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"
69
76
  )
70
77
  if not math.isfinite(result):
71
78
  raise HTTPBadRequest(
72
- "'{}' ({}) 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"
73
80
  )
74
81
  return result
75
82
 
76
- @view_config(route_name="raster", renderer="fast_json")
77
- def raster(self):
83
+ @view_config(route_name="raster", renderer="fast_json") # type: ignore
84
+ def raster(self) -> Dict[str, Any]:
78
85
  lon = self._get_required_finite_float_param("lon")
79
86
  lat = self._get_required_finite_float_param("lat")
80
87
 
@@ -85,7 +92,7 @@ class Raster:
85
92
  if layer in self.rasters:
86
93
  rasters[layer] = self.rasters[layer]
87
94
  else:
88
- raise HTTPNotFound("Layer {} not found".format(layer))
95
+ raise HTTPNotFound(f"Layer {layer} not found")
89
96
  else:
90
97
  rasters = self.rasters
91
98
 
@@ -93,10 +100,10 @@ class Raster:
93
100
  for ref in list(rasters.keys()):
94
101
  result[ref] = self._get_raster_value(rasters[ref], ref, lon, lat)
95
102
 
96
- set_common_headers(self.request, "raster", NO_CACHE)
103
+ set_common_headers(self.request, "raster", Cache.PUBLIC_NO)
97
104
  return result
98
105
 
99
- def _get_data(self, layer, name):
106
+ def _get_data(self, layer: Dict[str, Any], name: str) -> "fiona.collection.Collection":
100
107
  if name not in self.data:
101
108
  path = layer["file"]
102
109
  if layer.get("type", "shp_index") == "shp_index":
@@ -112,7 +119,9 @@ class Raster:
112
119
 
113
120
  return self.data[name]
114
121
 
115
- 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]:
116
125
  data = self._get_data(layer, name)
117
126
  type_ = layer.get("type", "shp_index")
118
127
  if type_ == "shp_index":
@@ -133,21 +142,25 @@ class Raster:
133
142
  else:
134
143
  raise ValueError("Unsupported type " + type_)
135
144
 
136
- if "round" in layer:
137
- 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"])
138
148
  elif result is not None:
139
- result = Decimal(str(result))
149
+ result_d = decimal.Decimal(str(result))
140
150
 
141
- return result
151
+ return result_d
142
152
 
143
153
  @staticmethod
144
- 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]:
145
157
  index = dataset.index(lon, lat)
146
158
 
147
159
  shape = dataset.shape
160
+ result: Optional[numpy.float32]
148
161
  if 0 <= index[0] < shape[0] and 0 <= index[1] < shape[1]:
149
162
 
150
- def get_index(index_):
163
+ def get_index(index_: int) -> Tuple[int, int]:
151
164
  return index_, index_ + 1
152
165
 
153
166
  result = dataset.read(1, window=(get_index(index[0]), get_index(index[1])))[0][0]
@@ -169,7 +182,12 @@ class Raster:
169
182
  return result
170
183
 
171
184
  @staticmethod
172
- def _round(value, round_to):
185
+ def _round(value: numpy.float32, round_to: float) -> Optional[decimal.Decimal]:
173
186
  if value is not None:
174
- return Decimal(str(value)).quantize(Decimal(str(round_to)))
187
+ decimal_value = decimal.Decimal(str(value))
188
+ try:
189
+ return decimal_value.quantize(decimal.Decimal(str(round_to)))
190
+ except decimal.InvalidOperation:
191
+ LOG.info("Error on rounding %s: %s", decimal_value, traceback.format_exc())
192
+ return decimal_value
175
193
  return None
@@ -1,6 +1,4 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- # Copyright (c) 2011-2019, 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-2019, 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
@@ -28,47 +26,51 @@
28
26
  # either expressed or implied, of the FreeBSD Project.
29
27
 
30
28
 
31
- from datetime import datetime
32
29
  import logging
33
30
  import random
34
31
  import string
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,19 +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
- hostname = uri_parts.hostname
85
86
  if "allowed_hosts" in self.settings:
86
- if hostname not in self.settings["allowed_hosts"]: # pragma: no cover
87
- raise HTTPBadRequest("The requested host is not allowed.")
87
+ if uri_parts.netloc not in self.settings["allowed_hosts"]:
88
+ raise HTTPBadRequest(
89
+ f"The requested host '{uri_parts.netloc}' is not part of allowed hosts: "
90
+ f"{', '.join(self.settings['allowed_hosts'])}"
91
+ )
88
92
  else:
93
+ hostname = uri_parts.hostname
89
94
  if hostname != self.request.server_name:
90
95
  raise HTTPBadRequest(
91
- "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}'"
92
97
  )
93
98
 
94
99
  shortened = False
@@ -102,16 +107,16 @@ class Shortener:
102
107
  tries = 0
103
108
  while not shortened:
104
109
  ref = "".join(
105
- random.choice(string.ascii_letters + string.digits)
110
+ random.choice(string.ascii_letters + string.digits) # nosec
106
111
  for i in range(self.settings.get("length", 4))
107
112
  )
108
113
  test_url = DBSession.query(Shorturl).filter(Shorturl.ref == ref).all()
109
114
  if not test_url:
110
115
  break
111
- tries += 1 # pragma: no cover
112
- if tries > 20: # pragma: no cover
116
+ tries += 1
117
+ if tries > 20:
113
118
  message = "No free ref found, considered to increase the length"
114
- logging.error(message)
119
+ logger.error(message)
115
120
  raise HTTPInternalServerError(message)
116
121
 
117
122
  user_email = self.request.user.email if self.request.user is not None else None
@@ -131,7 +136,7 @@ class Shortener:
131
136
  else:
132
137
  s_url = self.request.route_url("shortener_get", ref=ref)
133
138
 
134
- if email is not None: # pragma: no cover
139
+ if email is not None:
135
140
  send_email_config(
136
141
  self.request.registry.settings,
137
142
  "shortener",
@@ -139,7 +144,9 @@ class Shortener:
139
144
  full_url=url,
140
145
  short_url=s_url,
141
146
  message=self.request.params.get("message", ""),
147
+ application_url=self.request.route_url("base"),
148
+ current_url=self.request.current_route_url(),
142
149
  )
143
150
 
144
- set_common_headers(self.request, "shortener", NO_CACHE)
151
+ set_common_headers(self.request, "shortener", Cache.PRIVATE_NO)
145
152
  return {"short_url": s_url}