c2cgeoportal-geoportal 2.3.5.80__py3-none-any.whl → 2.9rc2__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 (197) hide show
  1. c2cgeoportal_geoportal/__init__.py +960 -0
  2. c2cgeoportal_geoportal/lib/__init__.py +256 -0
  3. c2cgeoportal_geoportal/lib/authentication.py +250 -0
  4. c2cgeoportal_geoportal/lib/bashcolor.py +46 -0
  5. c2cgeoportal_geoportal/lib/cacheversion.py +75 -0
  6. c2cgeoportal_geoportal/lib/caching.py +176 -0
  7. c2cgeoportal_geoportal/lib/check_collector.py +80 -0
  8. c2cgeoportal_geoportal/lib/checker.py +295 -0
  9. c2cgeoportal_geoportal/lib/common_headers.py +170 -0
  10. c2cgeoportal_geoportal/lib/dbreflection.py +266 -0
  11. c2cgeoportal_geoportal/lib/filter_capabilities.py +360 -0
  12. c2cgeoportal_geoportal/lib/fulltextsearch.py +50 -0
  13. c2cgeoportal_geoportal/lib/functionality.py +166 -0
  14. c2cgeoportal_geoportal/lib/headers.py +62 -0
  15. c2cgeoportal_geoportal/lib/i18n.py +38 -0
  16. c2cgeoportal_geoportal/lib/layers.py +132 -0
  17. c2cgeoportal_geoportal/lib/lingva_extractor.py +937 -0
  18. c2cgeoportal_geoportal/lib/loader.py +57 -0
  19. c2cgeoportal_geoportal/lib/metrics.py +117 -0
  20. c2cgeoportal_geoportal/lib/oauth2.py +1186 -0
  21. c2cgeoportal_geoportal/lib/oidc.py +304 -0
  22. c2cgeoportal_geoportal/lib/wmstparsing.py +353 -0
  23. c2cgeoportal_geoportal/lib/xsd.py +166 -0
  24. c2cgeoportal_geoportal/py.typed +0 -0
  25. c2cgeoportal_geoportal/resources.py +49 -0
  26. c2cgeoportal_geoportal/scaffolds/advance_create/ci/config.yaml +26 -0
  27. c2cgeoportal_geoportal/scaffolds/advance_create/cookiecutter.json +18 -0
  28. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/.dockerignore +6 -0
  29. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/.eslintrc.yaml +19 -0
  30. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/.prospector.yaml +30 -0
  31. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/Dockerfile +75 -0
  32. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/Makefile +6 -0
  33. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/alembic.ini +58 -0
  34. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/alembic.yaml +19 -0
  35. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/development.ini +121 -0
  36. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/gunicorn.conf.py +139 -0
  37. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/language_mapping +3 -0
  38. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/lingva-client.cfg +5 -0
  39. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/lingva-server.cfg +6 -0
  40. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/production.ini +38 -0
  41. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/requirements.txt +2 -0
  42. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/setup.py +25 -0
  43. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/webpack.api.js +41 -0
  44. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/webpack.apps.js +64 -0
  45. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/webpack.commons.js +11 -0
  46. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/webpack.config.js +22 -0
  47. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/__init__.py +42 -0
  48. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/authentication.py +10 -0
  49. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/dev.py +14 -0
  50. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/models.py +8 -0
  51. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/multi_organization.py +7 -0
  52. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/resources.py +11 -0
  53. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static-ngeo/api/index.js +12 -0
  54. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static-ngeo/js/{{cookiecutter.package}}module.js +25 -0
  55. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/subscribers.py +39 -0
  56. c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/views/__init__.py +0 -0
  57. c2cgeoportal_geoportal/scaffolds/advance_update/cookiecutter.json +18 -0
  58. c2cgeoportal_geoportal/scaffolds/advance_update/{{cookiecutter.project}}/geoportal/CONST_Makefile +121 -0
  59. c2cgeoportal_geoportal/scaffolds/create/cookiecutter.json +18 -0
  60. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.dockerignore +14 -0
  61. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.editorconfig +17 -0
  62. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/main.yaml +73 -0
  63. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/rebuild.yaml +50 -0
  64. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.github/workflows/update_l10n.yaml +66 -0
  65. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.gitignore +16 -0
  66. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.pre-commit-config.yaml +35 -0
  67. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.prettierignore +1 -0
  68. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/.prettierrc.yaml +2 -0
  69. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/Dockerfile +75 -0
  70. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/Makefile +70 -0
  71. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/README.rst +29 -0
  72. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/build +179 -0
  73. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/ci/config.yaml +22 -0
  74. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/ci/docker-compose-check +25 -0
  75. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/ci/requirements.txt +2 -0
  76. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose-db.yaml +24 -0
  77. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose-lib.yaml +511 -0
  78. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose-qgis.yaml +21 -0
  79. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose.override.sample.yaml +59 -0
  80. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose.yaml +121 -0
  81. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/env.default +102 -0
  82. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/env.project +69 -0
  83. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/vars.yaml +430 -0
  84. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/locale/en/LC_MESSAGES/{{cookiecutter.package}}_geoportal-client.po +6 -0
  85. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/css/desktop.css +0 -0
  86. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/css/iframe_api.css +0 -0
  87. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/css/mobile.css +0 -0
  88. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/images/banner_left.png +0 -0
  89. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/images/banner_right.png +0 -0
  90. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/images/blank.png +0 -0
  91. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/images/markers/marker-blue.png +0 -0
  92. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/images/markers/marker-gold.png +0 -0
  93. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/images/markers/marker-green.png +0 -0
  94. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/images/markers/marker.png +0 -0
  95. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/static/robot.txt.tmpl +3 -0
  96. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/data/Readme.txt +69 -0
  97. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/data/TM_EUROPE_BORDERS-0.3.sql +70 -0
  98. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/demo.map.tmpl +224 -0
  99. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/fonts/Arial.ttf +0 -0
  100. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/fonts/Arialbd.ttf +0 -0
  101. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/fonts/Arialbi.ttf +0 -0
  102. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/fonts/Ariali.ttf +0 -0
  103. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/fonts/NotoSans-Bold.ttf +0 -0
  104. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/fonts/NotoSans-BoldItalic.ttf +0 -0
  105. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/fonts/NotoSans-Italic.ttf +0 -0
  106. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/fonts/NotoSans-Regular.ttf +0 -0
  107. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/fonts/Verdana.ttf +0 -0
  108. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/fonts/Verdanab.ttf +0 -0
  109. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/fonts/Verdanai.ttf +0 -0
  110. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/fonts/Verdanaz.ttf +0 -0
  111. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/fonts.conf +12 -0
  112. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/mapserver.conf +15 -0
  113. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/mapserver.map.tmpl +87 -0
  114. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/mapserver/tinyows.xml.tmpl +36 -0
  115. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/A3_Landscape.jrxml +207 -0
  116. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/A3_Portrait.jrxml +185 -0
  117. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/A4_Landscape.jrxml +200 -0
  118. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/A4_Portrait.jrxml +170 -0
  119. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/config.yaml.tmpl +175 -0
  120. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/legend.jrxml +109 -0
  121. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/localisation.properties +4 -0
  122. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/localisation_fr.properties +4 -0
  123. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/logo.png +0 -0
  124. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/north.svg +93 -0
  125. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/print/print-apps/{{cookiecutter.package}}/results.jrxml +25 -0
  126. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/project.yaml +18 -0
  127. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/pyproject.toml +7 -0
  128. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/qgisserver/pg_service.conf.tmpl +15 -0
  129. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/run_alembic.sh +11 -0
  130. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/scripts/db-backup +126 -0
  131. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/scripts/db-restore +132 -0
  132. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/setup.cfg +7 -0
  133. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/spell-ignore-words.txt +5 -0
  134. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/tests/__init__.py +0 -0
  135. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/tests/test_app.py +43 -0
  136. c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/tilegeneration/config.yaml.tmpl +195 -0
  137. c2cgeoportal_geoportal/scaffolds/update/cookiecutter.json +18 -0
  138. c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/.upgrade.yaml +67 -0
  139. c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/CONST_CHANGELOG.txt +295 -0
  140. c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/CONST_create_template/tests/test_testapp.py +48 -0
  141. c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/geoportal/CONST_config-schema.yaml +922 -0
  142. c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/geoportal/CONST_vars.yaml +1503 -0
  143. c2cgeoportal_geoportal/scripts/__init__.py +64 -0
  144. c2cgeoportal_geoportal/scripts/c2cupgrade.py +879 -0
  145. c2cgeoportal_geoportal/scripts/create_demo_theme.py +80 -0
  146. c2cgeoportal_geoportal/scripts/manage_users.py +140 -0
  147. c2cgeoportal_geoportal/scripts/pcreate.py +314 -0
  148. c2cgeoportal_geoportal/scripts/theme2fts.py +347 -0
  149. c2cgeoportal_geoportal/scripts/urllogin.py +81 -0
  150. c2cgeoportal_geoportal/templates/login.html +90 -0
  151. c2cgeoportal_geoportal/templates/notlogin.html +62 -0
  152. c2cgeoportal_geoportal/templates/testi18n.html +12 -0
  153. c2cgeoportal_geoportal/views/__init__.py +59 -0
  154. c2cgeoportal_geoportal/views/dev.py +57 -0
  155. c2cgeoportal_geoportal/views/dynamic.py +208 -0
  156. c2cgeoportal_geoportal/views/entry.py +174 -0
  157. c2cgeoportal_geoportal/views/fulltextsearch.py +189 -0
  158. c2cgeoportal_geoportal/views/geometry_processing.py +75 -0
  159. c2cgeoportal_geoportal/views/i18n.py +129 -0
  160. c2cgeoportal_geoportal/views/layers.py +713 -0
  161. c2cgeoportal_geoportal/views/login.py +679 -0
  162. c2cgeoportal_geoportal/views/mapserverproxy.py +191 -0
  163. c2cgeoportal_geoportal/views/memory.py +90 -0
  164. c2cgeoportal_geoportal/views/ogcproxy.py +120 -0
  165. c2cgeoportal_geoportal/views/pdfreport.py +245 -0
  166. c2cgeoportal_geoportal/views/printproxy.py +143 -0
  167. c2cgeoportal_geoportal/views/profile.py +127 -0
  168. c2cgeoportal_geoportal/views/proxy.py +259 -0
  169. c2cgeoportal_geoportal/views/raster.py +193 -0
  170. c2cgeoportal_geoportal/views/resourceproxy.py +73 -0
  171. c2cgeoportal_geoportal/views/shortener.py +152 -0
  172. c2cgeoportal_geoportal/views/theme.py +1322 -0
  173. c2cgeoportal_geoportal/views/tinyowsproxy.py +189 -0
  174. c2cgeoportal_geoportal/views/vector_tiles.py +83 -0
  175. {c2cgeoportal_geoportal-2.3.5.80.dist-info → c2cgeoportal_geoportal-2.9rc2.dist-info}/METADATA +21 -24
  176. c2cgeoportal_geoportal-2.9rc2.dist-info/RECORD +192 -0
  177. {c2cgeoportal_geoportal-2.3.5.80.dist-info → c2cgeoportal_geoportal-2.9rc2.dist-info}/WHEEL +1 -1
  178. c2cgeoportal_geoportal-2.9rc2.dist-info/entry_points.txt +28 -0
  179. c2cgeoportal_geoportal-2.9rc2.dist-info/top_level.txt +2 -0
  180. tests/__init__.py +100 -0
  181. tests/test_cachebuster.py +71 -0
  182. tests/test_caching.py +275 -0
  183. tests/test_checker.py +85 -0
  184. tests/test_decimaljson.py +47 -0
  185. tests/test_headerstween.py +64 -0
  186. tests/test_i18n.py +31 -0
  187. tests/test_init.py +193 -0
  188. tests/test_locale_negociator.py +69 -0
  189. tests/test_mapserverproxy_route_predicate.py +64 -0
  190. tests/test_raster.py +267 -0
  191. tests/test_wmstparsing.py +238 -0
  192. tests/xmlstr.py +103 -0
  193. c2cgeoportal_geoportal-2.3.5.80.dist-info/DESCRIPTION.rst +0 -8
  194. c2cgeoportal_geoportal-2.3.5.80.dist-info/RECORD +0 -7
  195. c2cgeoportal_geoportal-2.3.5.80.dist-info/entry_points.txt +0 -22
  196. c2cgeoportal_geoportal-2.3.5.80.dist-info/metadata.json +0 -1
  197. c2cgeoportal_geoportal-2.3.5.80.dist-info/top_level.txt +0 -1
@@ -0,0 +1,143 @@
1
+ # Copyright (c) 2011-2024, Camptocamp SA
2
+ # All rights reserved.
3
+
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are met:
6
+
7
+ # 1. Redistributions of source code must retain the above copyright notice, this
8
+ # list of conditions and the following disclaimer.
9
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
10
+ # this list of conditions and the following disclaimer in the documentation
11
+ # and/or other materials provided with the distribution.
12
+
13
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14
+ # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17
+ # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18
+ # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19
+ # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20
+ # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23
+
24
+ # The views and conclusions contained in the software and documentation are those
25
+ # of the authors and should not be interpreted as representing official policies,
26
+ # either expressed or implied, of the FreeBSD Project.
27
+
28
+
29
+ import json
30
+ import logging
31
+ import urllib.parse
32
+
33
+ import pyramid.request
34
+ import pyramid.response
35
+ import requests
36
+ from pyramid.httpexceptions import HTTPBadGateway, HTTPFound
37
+ from pyramid.view import view_config
38
+
39
+ from c2cgeoportal_commons.lib.url import Url
40
+ from c2cgeoportal_geoportal.lib import is_intranet
41
+ from c2cgeoportal_geoportal.lib.caching import get_region
42
+ from c2cgeoportal_geoportal.lib.common_headers import Cache
43
+ from c2cgeoportal_geoportal.lib.functionality import get_functionality
44
+ from c2cgeoportal_geoportal.views.proxy import Proxy
45
+
46
+ _LOG = logging.getLogger(__name__)
47
+ _CACHE_REGION = get_region("std")
48
+
49
+
50
+ class PrintProxy(Proxy):
51
+ """All the view concerned the print."""
52
+
53
+ def __init__(self, request: pyramid.request.Request):
54
+ Proxy.__init__(self, request)
55
+
56
+ @view_config(route_name="printproxy_capabilities") # type: ignore
57
+ def capabilities(self) -> pyramid.response.Response:
58
+ """Get print capabilities."""
59
+ templates = get_functionality("print_template", self.request, is_intranet(self.request))
60
+
61
+ # get query string
62
+ params = dict(self.request.params)
63
+ query_string = urllib.parse.urlencode(params)
64
+
65
+ resp, content = self._capabilities(
66
+ templates, query_string, self.request.method, self.request.referrer, self.request.host
67
+ )
68
+
69
+ response = self._build_response(resp, content, Cache.PRIVATE, "print")
70
+ # Mapfish print will check the referrer header to return the capabilities.
71
+ response.vary += ("Referrer", "Referer")
72
+ return response
73
+
74
+ @_CACHE_REGION.cache_on_arguments()
75
+ def _capabilities(
76
+ self, templates: list[str], query_string: dict[str, str], method: str, referrer: str, host: str
77
+ ) -> tuple[requests.Response, str]:
78
+ del query_string # Just for caching
79
+ del method # Just for caching
80
+ del referrer # Just for caching
81
+ del host # Just for caching
82
+
83
+ # Get URL
84
+ _url = self.request.get_organization_print_url() + "/capabilities.json"
85
+
86
+ response = self._proxy(Url(_url))
87
+ response.raise_for_status()
88
+
89
+ if self.request.method == "GET":
90
+ try:
91
+ capabilities = response.json()
92
+ except json.decoder.JSONDecodeError:
93
+ _LOG.exception("Unable to parse capabilities: %s", response.text)
94
+ raise HTTPBadGateway(response.text) # pylint: disable=raise-missing-from
95
+
96
+ capabilities["layouts"] = list(
97
+ layout for layout in capabilities["layouts"] if layout["name"] in templates
98
+ )
99
+
100
+ pretty = self.request.params.get("pretty", "false") == "true"
101
+ content = json.dumps(
102
+ capabilities, separators=None if pretty else (",", ":"), indent=4 if pretty else None
103
+ )
104
+ else:
105
+ content = ""
106
+
107
+ return response, content
108
+
109
+ @view_config(route_name="printproxy_report_create") # type: ignore
110
+ def report_create(self) -> pyramid.response.Response:
111
+ """Create PDF."""
112
+ return self._proxy_response(
113
+ "print",
114
+ Url(
115
+ f"{ self.request.get_organization_print_url()}/report.{self.request.matchdict.get('format')}"
116
+ ),
117
+ )
118
+
119
+ @view_config(route_name="printproxy_status") # type: ignore
120
+ def status(self) -> pyramid.response.Response:
121
+ """PDF status."""
122
+ return self._proxy_response(
123
+ "print",
124
+ Url(
125
+ f"{self.request.get_organization_print_url()}/status/{self.request.matchdict.get('ref')}.json"
126
+ ),
127
+ )
128
+
129
+ @view_config(route_name="printproxy_cancel") # type: ignore
130
+ def cancel(self) -> pyramid.response.Response:
131
+ """PDF cancel."""
132
+ return self._proxy_response(
133
+ "print",
134
+ Url(f"{self.request.get_organization_print_url()}/cancel/{self.request.matchdict.get('ref')}"),
135
+ )
136
+
137
+ @view_config(route_name="printproxy_report_get") # type: ignore
138
+ def report_get(self) -> pyramid.response.Response:
139
+ """Get the PDF."""
140
+ url = Url(f"{self.request.get_organization_print_url()}/report/{self.request.matchdict.get('ref')}")
141
+ if self.request.registry.settings.get("print_get_redirect", False):
142
+ raise HTTPFound(location=url.url())
143
+ return self._proxy_response("print", url)
@@ -0,0 +1,127 @@
1
+ # Copyright (c) 2012-2023, Camptocamp SA
2
+ # All rights reserved.
3
+
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are met:
6
+
7
+ # 1. Redistributions of source code must retain the above copyright notice, this
8
+ # list of conditions and the following disclaimer.
9
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
10
+ # this list of conditions and the following disclaimer in the documentation
11
+ # and/or other materials provided with the distribution.
12
+
13
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14
+ # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17
+ # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18
+ # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19
+ # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20
+ # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23
+
24
+ # The views and conclusions contained in the software and documentation are those
25
+ # of the authors and should not be interpreted as representing official policies,
26
+ # either expressed or implied, of the FreeBSD Project.
27
+
28
+
29
+ import math
30
+ from decimal import Decimal
31
+ from typing import Any
32
+
33
+ import geojson
34
+ import pyramid.request
35
+ from pyramid.httpexceptions import HTTPNotFound
36
+ from pyramid.i18n import TranslationStringFactory
37
+ from pyramid.view import view_config
38
+
39
+ from c2cgeoportal_geoportal.lib.common_headers import Cache, set_common_headers
40
+ from c2cgeoportal_geoportal.views.raster import Raster
41
+
42
+ _ = TranslationStringFactory("c2cgeoportal")
43
+
44
+
45
+ class Profile(Raster):
46
+ """All the view concerned the profile."""
47
+
48
+ def __init__(self, request: pyramid.request.Request):
49
+ Raster.__init__(self, request)
50
+
51
+ @view_config(route_name="profile.json", renderer="fast_json") # type: ignore
52
+ def json(self) -> dict[str, Any]:
53
+ """Answer to /profile.json."""
54
+ _, points = self._compute_points()
55
+ set_common_headers(self.request, "profile", Cache.PUBLIC_NO)
56
+ return {"profile": points}
57
+
58
+ def _compute_points(self) -> tuple[list[str], list[dict[str, Any]]]:
59
+ """Compute the alt=fct(dist) array."""
60
+ geom = geojson.loads(self.request.params["geom"], object_hook=geojson.GeoJSON.to_instance)
61
+
62
+ layers: list[str]
63
+ if "layers" in self.request.params:
64
+ rasters = {}
65
+ layers = self.request.params["layers"].split(",")
66
+ for layer in layers:
67
+ if layer in self.rasters:
68
+ rasters[layer] = self.rasters[layer]
69
+ else:
70
+ raise HTTPNotFound(f"Layer {layer!s} not found")
71
+ else:
72
+ rasters = self.rasters
73
+ layers = list(rasters.keys())
74
+ layers.sort()
75
+
76
+ points: list[dict[str, Any]] = []
77
+
78
+ dist = 0
79
+ prev_coord = None
80
+ coords = self._create_points(geom.coordinates, int(self.request.params["nbPoints"]))
81
+ for coord in coords:
82
+ if prev_coord is not None:
83
+ dist += self._dist(prev_coord, coord)
84
+
85
+ values = {}
86
+ for ref in list(rasters.keys()):
87
+ value = self._get_raster_value(self.rasters[ref], ref, coord[0], coord[1])
88
+ values[ref] = value
89
+
90
+ # 10cm accuracy is enough for distances
91
+ rounded_dist = Decimal(str(dist)).quantize(Decimal("0.1"))
92
+ points.append({"dist": rounded_dist, "values": values, "x": coord[0], "y": coord[1]})
93
+ prev_coord = coord
94
+
95
+ return layers, points
96
+
97
+ @staticmethod
98
+ def _dist(coord1: tuple[float, float], coord2: tuple[float, float]) -> float:
99
+ """Compute the distance between 2 points."""
100
+ return math.sqrt(math.pow(coord1[0] - coord2[0], 2.0) + math.pow(coord1[1] - coord2[1], 2.0))
101
+
102
+ def _create_points(self, coords: list[tuple[float, float]], nb_points: int) -> list[tuple[float, float]]:
103
+ """Add some points in order to reach roughly the asked number of points."""
104
+ total_length = 0
105
+ prev_coord = None
106
+ for coord in coords:
107
+ if prev_coord is not None:
108
+ total_length += self._dist(prev_coord, coord)
109
+ prev_coord = coord
110
+
111
+ if total_length == 0.0:
112
+ return coords
113
+
114
+ result: list[tuple[float, float]] = []
115
+ prev_coord = None
116
+ for coord in coords:
117
+ if prev_coord is not None:
118
+ cur_length = self._dist(prev_coord, coord)
119
+ cur_nb_points = max(int(nb_points * cur_length / total_length + 0.5), 1)
120
+ dx = (coord[0] - prev_coord[0]) / float(cur_nb_points)
121
+ dy = (coord[1] - prev_coord[1]) / float(cur_nb_points)
122
+ for i in range(1, cur_nb_points + 1):
123
+ result.append((prev_coord[0] + dx * i, prev_coord[1] + dy * i))
124
+ else:
125
+ result.append((coord[0], coord[1]))
126
+ prev_coord = coord
127
+ return result
@@ -0,0 +1,259 @@
1
+ # Copyright (c) 2011-2024, Camptocamp SA
2
+ # All rights reserved.
3
+
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are met:
6
+
7
+ # 1. Redistributions of source code must retain the above copyright notice, this
8
+ # list of conditions and the following disclaimer.
9
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
10
+ # this list of conditions and the following disclaimer in the documentation
11
+ # and/or other materials provided with the distribution.
12
+
13
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14
+ # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17
+ # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18
+ # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19
+ # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20
+ # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23
+
24
+ # The views and conclusions contained in the software and documentation are those
25
+ # of the authors and should not be interpreted as representing official policies,
26
+ # either expressed or implied, of the FreeBSD Project.
27
+
28
+
29
+ import logging
30
+ import sys
31
+ from typing import Any
32
+
33
+ import pyramid.request
34
+ import pyramid.response
35
+ import requests
36
+ from pyramid.httpexceptions import HTTPBadGateway, exception_response
37
+
38
+ from c2cgeoportal_commons.lib.url import Url
39
+ from c2cgeoportal_geoportal.lib.caching import get_region
40
+ from c2cgeoportal_geoportal.lib.common_headers import Cache, set_common_headers
41
+ from c2cgeoportal_geoportal.views import restrict_headers
42
+
43
+ _LOG = logging.getLogger(__name__)
44
+ _CACHE_REGION = get_region("std")
45
+
46
+
47
+ class Proxy:
48
+ """Some methods used by all the proxy."""
49
+
50
+ def __init__(self, request: pyramid.request.Request):
51
+ self.request = request
52
+ self.host_forward_host = request.registry.settings.get("host_forward_host", [])
53
+ self.headers_whitelist = request.registry.settings.get("headers_whitelist", [])
54
+ self.headers_blacklist = request.registry.settings.get("headers_blacklist", [])
55
+ self.http_options = self.request.registry.settings.get("http_options", {})
56
+
57
+ def _proxy(
58
+ self,
59
+ url: Url,
60
+ params: dict[str, str] | None = None,
61
+ method: str | None = None,
62
+ cache: bool = False,
63
+ body: bytes | None = None,
64
+ headers: dict[str, str] | None = None,
65
+ ) -> requests.models.Response:
66
+ # Get query string
67
+ params = dict(self.request.params) if params is None else params
68
+ url = url.clone().add_query(params, True)
69
+
70
+ _LOG.debug("Send query to URL:\n%s.", url)
71
+
72
+ if method is None:
73
+ method = self.request.method
74
+
75
+ headers = dict(self.request.headers if headers is None else headers)
76
+
77
+ # Forward request to target (without Host Header).
78
+ # The original Host will be added back by pyramid.
79
+ if url.hostname not in self.host_forward_host and "Host" in headers:
80
+ headers.pop("Host")
81
+
82
+ # Forward the request tracking ID to the other service. This will allow to follow the logs belonging
83
+ # to a single request coming from the user
84
+ headers.setdefault("X-Request-ID", self.request.c2c_request_id)
85
+ # If we really want to respect the specification, we should chain with the content of the previous
86
+ # proxy, see also:
87
+ # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Forwarded
88
+ forwarded = {"for": self.request.client_addr, "proto": self.request.scheme}
89
+ if "Host" in self.request.headers:
90
+ forwarded["host"] = self.request.headers["Host"]
91
+ forwarded_str = ";".join(["=".join(e) for e in forwarded.items()])
92
+ if "Forwarded" in headers:
93
+ headers["Forwarded"] = ",".join([headers["Forwarded"], forwarded_str])
94
+ else:
95
+ headers["Forwarded"] = forwarded_str
96
+ # Set alternative "X-Forwarded" headers
97
+ for forwarded_elements in reversed(headers["Forwarded"].split(",")):
98
+ for element in forwarded_elements.split(";"):
99
+ key, value = element.split("=")
100
+ header_key = f"X-Forwarded-{key.capitalize()}"
101
+ header_value = headers.get(header_key)
102
+ headers[header_key] = value if header_value is None else ", ".join([header_value, value])
103
+
104
+ if not cache:
105
+ headers["Cache-Control"] = "no-cache"
106
+
107
+ if method in ("POST", "PUT") and body is None:
108
+ body = self.request.body
109
+
110
+ headers = restrict_headers(headers, self.headers_whitelist, self.headers_blacklist)
111
+
112
+ try:
113
+ if method in ("POST", "PUT"):
114
+ response = requests.request(
115
+ method, url.url(), data=body, headers=headers, **self.http_options
116
+ )
117
+ else:
118
+ response = requests.request(method, url.url(), headers=headers, **self.http_options)
119
+ except Exception:
120
+ errors = ["Error '%s' while getting the URL:", "%s", "Method: %s", "--- With headers ---", "%s"]
121
+ args1 = [
122
+ sys.exc_info()[0],
123
+ url,
124
+ method,
125
+ "\n".join(
126
+ [
127
+ f"{h}: {v if h not in ('Authorization', 'Cookies') else '***'}"
128
+ for h, v in list(headers.items())
129
+ ]
130
+ ),
131
+ ]
132
+ if method in ("POST", "PUT") and body is not None:
133
+ errors += ["--- Query with body ---", "%s"]
134
+ args1.append(body.decode("utf-8"))
135
+ _LOG.exception("\n".join(errors), *args1)
136
+
137
+ raise HTTPBadGateway( # pylint: disable=raise-missing-from
138
+ "Error on backend, See logs for detail"
139
+ )
140
+
141
+ if not response.ok:
142
+ errors = [
143
+ "Error '%s' in response of URL:",
144
+ "%s",
145
+ "Status: %d",
146
+ "Method: %s",
147
+ "--- With headers ---",
148
+ "%s",
149
+ ]
150
+ args2: list[str | int] = [
151
+ response.reason,
152
+ url.url(),
153
+ response.status_code,
154
+ method,
155
+ "\n".join(
156
+ [
157
+ f"{h}: {v if h not in ('Authorization', 'Cookies') else '***'}"
158
+ for h, v in list(headers.items())
159
+ ]
160
+ ),
161
+ ]
162
+ if method in ("POST", "PUT") and body is not None:
163
+ errors += ["--- Query with body ---", "%s"]
164
+ args2.append(body.decode("utf-8"))
165
+ errors += ["--- Return content ---", "%s"]
166
+ args2.append(response.text)
167
+ _LOG.error("\n".join(errors), *args2)
168
+
169
+ raise exception_response(response.status_code)
170
+ if not response.headers.get("Content-Type", "").startswith("image/"):
171
+ _LOG.debug("Get result for URL: %s:\n%s.", url, body)
172
+
173
+ return response
174
+
175
+ @_CACHE_REGION.cache_on_arguments()
176
+ def _proxy_cache(self, host: str, method: str, *args: Any, **kwargs: Any) -> pyramid.response.Response:
177
+ # Method is only for the cache
178
+ del host, method
179
+
180
+ kwargs["cache"] = True
181
+ return self._proxy(*args, **kwargs)
182
+
183
+ def _proxy_response(
184
+ self,
185
+ service_name: str,
186
+ url: Url,
187
+ headers_update: dict[str, str] | None = None,
188
+ public: bool = False,
189
+ **kwargs: Any,
190
+ ) -> pyramid.response.Response:
191
+ if headers_update is None:
192
+ headers_update = {}
193
+ cache = kwargs.get("cache", False)
194
+ if cache is True:
195
+ response = self._proxy_cache(url, self.request.host, self.request.method, **kwargs)
196
+ else:
197
+ response = self._proxy(url, **kwargs)
198
+
199
+ cache_control = (
200
+ (Cache.PUBLIC if public else Cache.PRIVATE)
201
+ if cache
202
+ else (Cache.PUBLIC_NO if public else Cache.PRIVATE_NO)
203
+ )
204
+ return self._build_response(
205
+ response, response.content, cache_control, service_name, headers_update=headers_update
206
+ )
207
+
208
+ def _build_response(
209
+ self,
210
+ response: pyramid.response.Response,
211
+ content: bytes,
212
+ cache_control: Cache,
213
+ service_name: str,
214
+ headers: dict[str, str] | None = None,
215
+ headers_update: dict[str, str] | None = None,
216
+ content_type: str | None = None,
217
+ ) -> pyramid.response.Response:
218
+ if headers_update is None:
219
+ headers_update = {}
220
+ headers = response.headers if headers is None else headers
221
+
222
+ # Hop-by-hop Headers are not supported by WSGI
223
+ # See:
224
+ # https://www.python.org/dev/peps/pep-3333/#other-http-features
225
+ # chapter 13.5.1 at https://www.faqs.org/rfcs/rfc2616.html
226
+ for header in [
227
+ "Connection",
228
+ "Keep-Alive",
229
+ "Proxy-Authenticate",
230
+ "Proxy-Authorization",
231
+ "te",
232
+ "Trailers",
233
+ "Transfer-Encoding",
234
+ "Upgrade",
235
+ ]:
236
+ if header in headers:
237
+ del headers[header]
238
+ # Other problematic headers
239
+ for header in ["Content-Length", "Content-Location", "Content-Encoding"]:
240
+ if header in headers:
241
+ del headers[header]
242
+
243
+ headers.update(headers_update)
244
+
245
+ response = pyramid.response.Response(content, status=response.status_code, headers=headers)
246
+
247
+ return set_common_headers(
248
+ self.request, service_name, cache_control, response=response, content_type=content_type
249
+ )
250
+
251
+ @staticmethod
252
+ def _get_lower_params(params: dict[str, str]) -> dict[str, str]:
253
+ return {k.lower(): str(v).lower() for k, v in params.items()}
254
+
255
+ def get_headers(self) -> dict[str, str]:
256
+ headers: dict[str, str] = self.request.headers
257
+ if "Cookie" in headers:
258
+ headers.pop("Cookie")
259
+ return headers