c2cgeoportal-geoportal 2.3.5.80__py3-none-any.whl → 2.9rc45__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 (198) 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 +77 -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 +172 -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 +513 -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 +65 -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 +16 -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 +78 -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 +304 -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_vars.yaml.swp +0 -0
  142. c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/geoportal/CONST_config-schema.yaml +927 -0
  143. c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/geoportal/CONST_vars.yaml +1503 -0
  144. c2cgeoportal_geoportal/scripts/__init__.py +64 -0
  145. c2cgeoportal_geoportal/scripts/c2cupgrade.py +879 -0
  146. c2cgeoportal_geoportal/scripts/create_demo_theme.py +83 -0
  147. c2cgeoportal_geoportal/scripts/manage_users.py +140 -0
  148. c2cgeoportal_geoportal/scripts/pcreate.py +296 -0
  149. c2cgeoportal_geoportal/scripts/theme2fts.py +347 -0
  150. c2cgeoportal_geoportal/scripts/urllogin.py +81 -0
  151. c2cgeoportal_geoportal/templates/login.html +90 -0
  152. c2cgeoportal_geoportal/templates/notlogin.html +62 -0
  153. c2cgeoportal_geoportal/templates/testi18n.html +12 -0
  154. c2cgeoportal_geoportal/views/__init__.py +59 -0
  155. c2cgeoportal_geoportal/views/dev.py +57 -0
  156. c2cgeoportal_geoportal/views/dynamic.py +209 -0
  157. c2cgeoportal_geoportal/views/entry.py +174 -0
  158. c2cgeoportal_geoportal/views/fulltextsearch.py +189 -0
  159. c2cgeoportal_geoportal/views/geometry_processing.py +75 -0
  160. c2cgeoportal_geoportal/views/i18n.py +129 -0
  161. c2cgeoportal_geoportal/views/layers.py +713 -0
  162. c2cgeoportal_geoportal/views/login.py +684 -0
  163. c2cgeoportal_geoportal/views/mapserverproxy.py +234 -0
  164. c2cgeoportal_geoportal/views/memory.py +90 -0
  165. c2cgeoportal_geoportal/views/ogcproxy.py +120 -0
  166. c2cgeoportal_geoportal/views/pdfreport.py +245 -0
  167. c2cgeoportal_geoportal/views/printproxy.py +143 -0
  168. c2cgeoportal_geoportal/views/profile.py +192 -0
  169. c2cgeoportal_geoportal/views/proxy.py +261 -0
  170. c2cgeoportal_geoportal/views/raster.py +233 -0
  171. c2cgeoportal_geoportal/views/resourceproxy.py +73 -0
  172. c2cgeoportal_geoportal/views/shortener.py +152 -0
  173. c2cgeoportal_geoportal/views/theme.py +1322 -0
  174. c2cgeoportal_geoportal/views/tinyowsproxy.py +189 -0
  175. c2cgeoportal_geoportal/views/vector_tiles.py +83 -0
  176. {c2cgeoportal_geoportal-2.3.5.80.dist-info → c2cgeoportal_geoportal-2.9rc45.dist-info}/METADATA +21 -24
  177. c2cgeoportal_geoportal-2.9rc45.dist-info/RECORD +193 -0
  178. {c2cgeoportal_geoportal-2.3.5.80.dist-info → c2cgeoportal_geoportal-2.9rc45.dist-info}/WHEEL +1 -1
  179. c2cgeoportal_geoportal-2.9rc45.dist-info/entry_points.txt +28 -0
  180. c2cgeoportal_geoportal-2.9rc45.dist-info/top_level.txt +2 -0
  181. tests/__init__.py +100 -0
  182. tests/test_cachebuster.py +71 -0
  183. tests/test_caching.py +275 -0
  184. tests/test_checker.py +85 -0
  185. tests/test_decimaljson.py +47 -0
  186. tests/test_headerstween.py +64 -0
  187. tests/test_i18n.py +31 -0
  188. tests/test_init.py +193 -0
  189. tests/test_locale_negociator.py +69 -0
  190. tests/test_mapserverproxy_route_predicate.py +64 -0
  191. tests/test_raster.py +267 -0
  192. tests/test_wmstparsing.py +238 -0
  193. tests/xmlstr.py +103 -0
  194. c2cgeoportal_geoportal-2.3.5.80.dist-info/DESCRIPTION.rst +0 -8
  195. c2cgeoportal_geoportal-2.3.5.80.dist-info/RECORD +0 -7
  196. c2cgeoportal_geoportal-2.3.5.80.dist-info/entry_points.txt +0 -22
  197. c2cgeoportal_geoportal-2.3.5.80.dist-info/metadata.json +0 -1
  198. c2cgeoportal_geoportal-2.3.5.80.dist-info/top_level.txt +0 -1
@@ -0,0 +1,360 @@
1
+ # Copyright (c) 2014-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 copy
30
+ import logging
31
+ import xml.sax.handler # nosec
32
+ import xml.sax.xmlreader # nosec
33
+ from collections.abc import Callable
34
+ from io import StringIO
35
+ from typing import Any, Union
36
+ from xml.sax.saxutils import XMLFilterBase, XMLGenerator # nosec
37
+
38
+ import defusedxml.expatreader
39
+ import pyramid.httpexceptions
40
+ import pyramid.request
41
+ import requests
42
+ from owslib.map.wms111 import ContentMetadata as ContentMetadata111
43
+ from owslib.map.wms130 import ContentMetadata as ContentMetadata130
44
+ from owslib.wms import WebMapService
45
+ from pyramid.httpexceptions import HTTPBadGateway
46
+
47
+ from c2cgeoportal_commons.lib.url import Url
48
+ from c2cgeoportal_geoportal.lib import caching, get_ogc_server_wfs_url_ids, get_ogc_server_wms_url_ids
49
+ from c2cgeoportal_geoportal.lib.layers import get_private_layers, get_protected_layers, get_writable_layers
50
+
51
+ _CACHE_REGION = caching.get_region("std")
52
+ _LOG = logging.getLogger(__name__)
53
+ ContentMetadata = Union[ContentMetadata111, ContentMetadata130]
54
+
55
+
56
+ @_CACHE_REGION.cache_on_arguments()
57
+ def wms_structure(request: pyramid.request.Request, wms_url: Url, host: str) -> dict[str, list[str]]:
58
+ """Get a simple serializable structure of the WMS capabilities."""
59
+ url = wms_url.clone().add_query({"SERVICE": "WMS", "VERSION": "1.1.1", "REQUEST": "GetCapabilities"})
60
+
61
+ # Forward request to target (without Host Header)
62
+ headers = {}
63
+ if url.hostname == "localhost" and host is not None:
64
+ headers["Host"] = host
65
+ try:
66
+ response = requests.get(
67
+ url.url(), headers=headers, **request.registry.settings.get("http_options", {})
68
+ )
69
+ except Exception:
70
+ _LOG.exception("Unable to GetCapabilities from wms_url '%s'", wms_url)
71
+ raise HTTPBadGateway( # pylint: disable=raise-missing-from
72
+ "Unable to GetCapabilities, see logs for details"
73
+ )
74
+
75
+ if not response.ok:
76
+ raise HTTPBadGateway(
77
+ f"GetCapabilities from wms_url {url.url()} return the error: "
78
+ f"{response.status_code:d} {response.reason}"
79
+ )
80
+
81
+ try:
82
+ wms = WebMapService(None, xml=response.content)
83
+ result: dict[str, list[str]] = {}
84
+
85
+ def _fill(name: str, parent: ContentMetadata) -> None:
86
+ if parent is None:
87
+ return
88
+ if parent.name not in result:
89
+ result[parent.name] = []
90
+ result[parent.name].append(name)
91
+ _fill(name, parent.parent)
92
+
93
+ for layer in list(wms.contents.values()):
94
+ _fill(layer.name, layer.parent)
95
+ return result
96
+
97
+ except AttributeError:
98
+ error = "WARNING! an error occurred while trying to read the mapfile and recover the themes."
99
+ error = f"{error}\nurl: {wms_url}\nxml:\n{response.text}"
100
+ _LOG.exception(error)
101
+ raise HTTPBadGateway(error) # pylint: disable=raise-missing-from
102
+
103
+ except SyntaxError:
104
+ error = "WARNING! an error occurred while trying to read the mapfile and recover the themes."
105
+ error = f"{error}\nurl: {wms_url}\nxml:\n{response.text}"
106
+ _LOG.exception(error)
107
+ raise HTTPBadGateway(error) # pylint: disable=raise-missing-from
108
+
109
+
110
+ def filter_capabilities(
111
+ request: pyramid.request.Request, content: str, wms: bool, url: Url, headers: dict[str, str]
112
+ ) -> str:
113
+ """Filter the WMS/WFS capabilities."""
114
+
115
+ wms_structure_ = wms_structure(request, url, headers.get("Host"))
116
+
117
+ ogc_server_ids = (
118
+ get_ogc_server_wms_url_ids(request, request.host)
119
+ if wms
120
+ else get_ogc_server_wfs_url_ids(request, request.host)
121
+ ).get(url.url())
122
+ gmf_private_layers = copy.copy(get_private_layers(ogc_server_ids))
123
+ for id_ in list(get_protected_layers(request, ogc_server_ids).keys()):
124
+ if id_ in gmf_private_layers:
125
+ del gmf_private_layers[id_]
126
+
127
+ private_layers = set()
128
+ for gmf_layer in list(gmf_private_layers.values()):
129
+ for ogc_layer in gmf_layer.layer.split(","):
130
+ private_layers.add(ogc_layer)
131
+ if ogc_layer in wms_structure_:
132
+ private_layers.update(wms_structure_[ogc_layer])
133
+
134
+ _LOG.debug(
135
+ "Filter capabilities of OGC server %s\nprivate_layers: %s",
136
+ ", ".join([str(e) for e in ogc_server_ids]),
137
+ ", ".join(private_layers),
138
+ )
139
+
140
+ parser = defusedxml.expatreader.create_parser(forbid_external=False)
141
+ # skip inclusion of DTDs
142
+ parser.setFeature(xml.sax.handler.feature_external_ges, False)
143
+ parser.setFeature(xml.sax.handler.feature_external_pes, False)
144
+
145
+ result = StringIO()
146
+ downstream_handler = XMLGenerator(result, "utf-8")
147
+ filter_handler = _CapabilitiesFilter(
148
+ parser, downstream_handler, "Layer" if wms else "FeatureType", layers_blacklist=private_layers
149
+ )
150
+ filter_handler.parse(StringIO(content))
151
+ return result.getvalue()
152
+
153
+
154
+ def filter_wfst_capabilities(content: str, wfs_url: Url, request: pyramid.request.Request) -> str:
155
+ """Filter the WTS capabilities."""
156
+
157
+ writable_layers: set[str] = set()
158
+ ogc_server_ids = get_ogc_server_wfs_url_ids(request, request.host).get(wfs_url.url())
159
+ if ogc_server_ids is None:
160
+ _LOG.error("No OGC server found for WFS URL %s", wfs_url)
161
+ raise pyramid.httpexceptions.HTTPInternalServerError("No OGC server found for WFS URL")
162
+
163
+ for gmf_layer in list(get_writable_layers(request, ogc_server_ids).values()):
164
+ writable_layers |= set(gmf_layer.layer.split(","))
165
+
166
+ _LOG.debug(
167
+ "Filter WFS-T capabilities of OGC server %s\nlayers: %s",
168
+ ", ".join([str(e) for e in ogc_server_ids]),
169
+ ", ".join(writable_layers),
170
+ )
171
+
172
+ parser = defusedxml.expatreader.create_parser(forbid_external=False)
173
+ # skip inclusion of DTDs
174
+ parser.setFeature(xml.sax.handler.feature_external_ges, False)
175
+ parser.setFeature(xml.sax.handler.feature_external_pes, False)
176
+
177
+ result = StringIO()
178
+ downstream_handler = XMLGenerator(result, "utf-8")
179
+ filter_handler = _CapabilitiesFilter(
180
+ parser, downstream_handler, "FeatureType", layers_whitelist=writable_layers
181
+ )
182
+ filter_handler.parse(StringIO(content))
183
+ return result.getvalue()
184
+
185
+
186
+ class _Layer:
187
+ def __init__(self, self_hidden: bool = False):
188
+ self.accumulator: list[Callable[[], None]] = []
189
+ self.hidden = True
190
+ self.self_hidden = self_hidden
191
+ self.has_children = False
192
+ self.children_nb = 0
193
+
194
+
195
+ class _CapabilitiesFilter(XMLFilterBase):
196
+ """
197
+ SAX filter to show only the allowed layers in a GetCapabilities request.
198
+
199
+ The filter removes elements of type `tag_name` where the `name` attribute is part of the set
200
+ `layers_blacklist` (when `layers_blacklist` is given) or is not part of the set `layers_whitelist` (when
201
+ `layers_whitelist` is given).
202
+ """
203
+
204
+ def __init__(
205
+ self,
206
+ upstream: XMLFilterBase,
207
+ downstream: XMLGenerator,
208
+ tag_name: str,
209
+ layers_blacklist: set[str] | None = None,
210
+ layers_whitelist: set[str] | None = None,
211
+ ):
212
+ XMLFilterBase.__init__(self, upstream)
213
+ self._downstream = downstream
214
+ self._accumulator: list[str] = []
215
+
216
+ assert (
217
+ layers_blacklist is not None or layers_whitelist is not None
218
+ ), "either layers_blacklist OR layers_whitelist must be set"
219
+ assert not (
220
+ layers_blacklist is not None and layers_whitelist is not None
221
+ ), "only either layers_blacklist OR layers_whitelist can be set"
222
+
223
+ if layers_blacklist is not None:
224
+ layers_blacklist = {layer.lower() for layer in layers_blacklist}
225
+ if layers_whitelist is not None:
226
+ layers_whitelist = {layer.lower() for layer in layers_whitelist}
227
+ self.layers_blacklist = layers_blacklist
228
+ self.layers_whitelist = layers_whitelist
229
+
230
+ self.layers_path: list[_Layer] = []
231
+ self.in_name = False
232
+ self.tag_name = tag_name
233
+ self.level = 0
234
+
235
+ def _complete_text_node(self) -> None:
236
+ if self._accumulator:
237
+ self._downstream.characters("".join(self._accumulator))
238
+ self._accumulator = []
239
+
240
+ def _do(self, action: Callable[[], Any]) -> None:
241
+ if self.layers_path:
242
+ self.layers_path[-1].accumulator.append(action)
243
+ else:
244
+ self._complete_text_node()
245
+ action()
246
+
247
+ def _add_child(self, layer: _Layer) -> None:
248
+ if not layer.hidden and not (layer.has_children and layer.children_nb == 0):
249
+ for action in layer.accumulator:
250
+ self._complete_text_node()
251
+ action()
252
+ layer.accumulator = []
253
+
254
+ def setDocumentLocator(self, locator: xml.sax.xmlreader.Locator) -> None: # noqa: ignore=N802
255
+ self._downstream.setDocumentLocator(locator)
256
+
257
+ def startDocument(self) -> None: # noqa: ignore=N802
258
+ self._downstream.startDocument()
259
+
260
+ def endDocument(self) -> None: # noqa: ignore=N802
261
+ self._downstream.endDocument()
262
+
263
+ def startPrefixMapping(self, prefix: str | None, uri: str) -> None: # noqa: ignore=N802
264
+ self._downstream.startPrefixMapping(prefix, uri)
265
+
266
+ def endPrefixMapping(self, prefix: str | None) -> None: # noqa: ignore=N802
267
+ self._downstream.endPrefixMapping(prefix)
268
+
269
+ def startElement(self, name: str, attrs: xml.sax.xmlreader.AttributesImpl) -> None: # noqa: ignore=N802
270
+ if name == self.tag_name:
271
+ self.level += 1
272
+ if self.layers_path:
273
+ parent_layer = self.layers_path[-1]
274
+ parent_layer.has_children = True
275
+ parent_layer.children_nb += 1
276
+ layer = _Layer(parent_layer.self_hidden) if len(self.layers_path) > 1 else _Layer()
277
+ self.layers_path.append(layer)
278
+
279
+ parent_layer.accumulator.append(lambda: self._add_child(layer))
280
+ else:
281
+ layer = _Layer()
282
+ self.layers_path.append(layer)
283
+ elif name == "Name" and self.layers_path:
284
+ self.in_name = True
285
+
286
+ self._do(lambda: self._downstream.startElement(name, attrs))
287
+
288
+ def endElement(self, name: str) -> None: # noqa: ignore=N802
289
+ self._do(lambda: self._downstream.endElement(name))
290
+
291
+ if name == self.tag_name:
292
+ self.level -= 1
293
+ if self.level == 0 and not self.layers_path[0].hidden:
294
+ for action in self.layers_path[0].accumulator:
295
+ self._complete_text_node()
296
+ action()
297
+ self.layers_path.pop()
298
+ elif name == "Name":
299
+ self.in_name = False
300
+
301
+ def startElementNS( # noqa: ignore=N802
302
+ self, name: tuple[str, str], qname: str, attrs: xml.sax.xmlreader.AttributesNSImpl
303
+ ) -> None:
304
+ self._do(lambda: self._downstream.startElementNS(name, qname, attrs))
305
+
306
+ def endElementNS(self, name: tuple[str, str], qname: str) -> None: # noqa: ignore=N802
307
+ self._do(lambda: self._downstream.endElementNS(name, qname))
308
+
309
+ def _keep_layer(self, layer_name: str) -> bool:
310
+ return (self.layers_blacklist is not None and layer_name not in self.layers_blacklist) or (
311
+ self.layers_whitelist is not None and layer_name in self.layers_whitelist
312
+ )
313
+
314
+ def characters(self, content: str) -> None:
315
+ if self.in_name and self.layers_path and not self.layers_path[-1].self_hidden is True:
316
+ layer_name = normalize_typename(content)
317
+ if self._keep_layer(layer_name):
318
+ for layer in self.layers_path:
319
+ layer.hidden = False
320
+ else:
321
+ # remove layer
322
+ self.layers_path[-1].self_hidden = True
323
+ if len(self.layers_path) > 1:
324
+ self.layers_path[-2].children_nb -= 1
325
+
326
+ self._do(lambda: self._accumulator.append(content))
327
+
328
+ def ignorableWhitespace(self, chars: str) -> None: # noqa: ignore=N802
329
+ self._do(lambda: self._accumulator.append(chars))
330
+
331
+ def processingInstruction(self, target: str, data: str) -> None: # noqa: ignore=N802
332
+ self._do(lambda: self._downstream.processingInstruction(target, data))
333
+
334
+ def skippedEntity(self, name: str) -> None: # noqa: ignore=N802
335
+ self._downstream.skippedEntity(name)
336
+
337
+
338
+ def normalize_tag(tag: str) -> str:
339
+ """
340
+ Drop the namespace from a tag and converts to lower case.
341
+
342
+ e.g. '{https://....}TypeName' -> 'TypeName'
343
+ """
344
+ normalized = tag
345
+ if len(tag) >= 3:
346
+ if tag[0] == "{":
347
+ normalized = tag[1:].split("}")[1]
348
+ return normalized.lower()
349
+
350
+
351
+ def normalize_typename(typename: str) -> str:
352
+ """
353
+ Drop the namespace from a type name and converts to lower case.
354
+
355
+ e.g. 'tows:parks' -> 'parks'
356
+ """
357
+ normalized = typename
358
+ if ":" in typename:
359
+ normalized = typename.split(":")[1]
360
+ return normalized.lower()
@@ -0,0 +1,50 @@
1
+ # Copyright (c) 2020-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
+ import re
29
+ from typing import Any
30
+
31
+
32
+ class Normalize:
33
+ """Normalize a text for the Full text search."""
34
+
35
+ def __init__(self, config: dict[str, Any]) -> None:
36
+ split = config.get("split_regex")
37
+ self.split_re = re.compile(split) if split is not None else None
38
+
39
+ self.word_replace = []
40
+ for search_regex, text in config.get("replace", {}).items():
41
+ self.word_replace.append((re.compile(search_regex), text))
42
+
43
+ def __call__(self, text: str) -> str:
44
+ if self.split_re is not None:
45
+ text = " ".join(self.split_re.split(text))
46
+
47
+ for search, replace in self.word_replace:
48
+ text = search.sub(replace, text)
49
+
50
+ return text
@@ -0,0 +1,166 @@
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.config
30
+ from typing import Any, cast
31
+
32
+ import pyramid.request
33
+ from sqlalchemy.orm import joinedload
34
+
35
+ from c2cgeoportal_commons.models import main, static
36
+ from c2cgeoportal_geoportal.lib import get_typed, get_types_map, is_intranet
37
+ from c2cgeoportal_geoportal.lib.caching import get_region
38
+
39
+ _LOG = logging.getLogger(__name__)
40
+ _CACHE_REGION_OBJ = get_region("obj")
41
+ _CACHE_REGION = get_region("std")
42
+
43
+
44
+ @_CACHE_REGION_OBJ.cache_on_arguments()
45
+ def _get_role(name: str) -> dict[str, Any]:
46
+ from c2cgeoportal_commons.models import DBSession # pylint: disable=import-outside-toplevel
47
+
48
+ assert DBSession is not None
49
+
50
+ role = (
51
+ DBSession.query(main.Role)
52
+ .filter(main.Role.name == name)
53
+ .options(joinedload(main.Role.functionalities))
54
+ .one_or_none()
55
+ )
56
+ struct = _role_to_struct(role)
57
+ return {"settings_functionalities": struct, "roles_functionalities": {name: struct}}
58
+
59
+
60
+ def _user_to_struct(user: static.User) -> dict[str, Any]:
61
+ return {
62
+ "settings_functionalities": _role_to_struct(user.settings_role),
63
+ "roles_functionalities": {role.name: _role_to_struct(role) for role in user.roles},
64
+ }
65
+
66
+
67
+ def _role_to_struct(role: main.Role | None) -> list[dict[str, Any]]:
68
+ return [{"name": f.name, "value": f.value} for f in role.functionalities] if role else []
69
+
70
+
71
+ def _get_db_functionality(
72
+ name: str,
73
+ user: dict[str, Any],
74
+ types: dict[str, dict[str, Any]],
75
+ request: pyramid.request.Request,
76
+ errors: set[str],
77
+ ) -> list[str | int | float | bool | list[Any] | dict[str, Any]]:
78
+ if types.get(name, {}).get("single", False):
79
+ values = [
80
+ get_typed(name, functionality["value"], types, request, errors)
81
+ for functionality in user["settings_functionalities"]
82
+ if functionality["name"] == name
83
+ ]
84
+ return [r for r in values if r is not None]
85
+ functionalities = {
86
+ functionality["value"]
87
+ for functionalities in user["roles_functionalities"].values()
88
+ for functionality in functionalities
89
+ if functionality["name"] == name
90
+ }
91
+ values = [
92
+ get_typed(name, functionality_value, types, request, errors)
93
+ for functionality_value in functionalities
94
+ ]
95
+
96
+ return [r for r in values if r is not None]
97
+
98
+
99
+ @_CACHE_REGION_OBJ.cache_on_arguments()
100
+ def _get_functionalities_type(request: pyramid.request.Request) -> dict[str, dict[str, Any]]:
101
+ return get_types_map(
102
+ request.registry.settings.get("admin_interface", {}).get("available_functionalities", [])
103
+ )
104
+
105
+
106
+ def get_functionality(
107
+ name: str, request: pyramid.request.Request, is_intranet_: bool
108
+ ) -> list[str | int | float | bool | list[Any] | dict[str, Any]]:
109
+ """Get all the functionality for the current user."""
110
+ result: list[str | int | float | bool | list[Any] | dict[str, Any]] = []
111
+ errors: set[str] = set()
112
+
113
+ if request.user is not None:
114
+ result = _get_db_functionality(
115
+ name, _user_to_struct(request.user), _get_functionalities_type(request), request, errors
116
+ )
117
+ if not result:
118
+ result = _get_db_functionality(
119
+ name,
120
+ _get_role(request.get_organization_role("registered")),
121
+ _get_functionalities_type(request),
122
+ request,
123
+ errors,
124
+ )
125
+
126
+ if not result and is_intranet_:
127
+ result = _get_db_functionality(
128
+ name,
129
+ _get_role(request.get_organization_role("intranet")),
130
+ _get_functionalities_type(request),
131
+ request,
132
+ errors,
133
+ )
134
+
135
+ if not result:
136
+ result = _get_db_functionality(
137
+ name,
138
+ _get_role(request.get_organization_role("anonymous")),
139
+ _get_functionalities_type(request),
140
+ request,
141
+ errors,
142
+ )
143
+
144
+ if errors != set():
145
+ _LOG.error("\n".join(errors))
146
+ return result
147
+
148
+
149
+ def get_mapserver_substitution_params(request: pyramid.request.Request) -> dict[str, str]:
150
+ """Get the parameters used by the mapserver substitution."""
151
+ params: dict[str, str] = {}
152
+ mss = get_functionality("mapserver_substitution", request, is_intranet(request))
153
+ if mss:
154
+ for s_ in mss:
155
+ s = cast(str, s_)
156
+ index = s.find("=")
157
+ if index > 0:
158
+ attribute = "s_" + s[:index]
159
+ value = s[index + 1 :]
160
+ if attribute in params:
161
+ params[attribute] += "," + value
162
+ else:
163
+ params[attribute] = value
164
+ else:
165
+ _LOG.warning("Mapserver Substitution '%s' does not respect pattern: <attribute>=<value>", s)
166
+ return params
@@ -0,0 +1,62 @@
1
+ # Copyright (c) 2013-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 re
30
+ from collections.abc import Callable
31
+
32
+ import pyramid.registry
33
+ import pyramid.request
34
+ import pyramid.response
35
+
36
+
37
+ class HeadersTween:
38
+ """Add the header on all the application."""
39
+
40
+ def __init__(
41
+ self,
42
+ handler: Callable[[pyramid.request.Request], pyramid.response.Response],
43
+ registry: pyramid.registry.Registry,
44
+ ) -> None:
45
+ self.handler = handler
46
+ self.settings = [
47
+ (re.compile(e["pattern"]), e["headers"]) for e in registry.settings["global_headers"]
48
+ ]
49
+
50
+ def __call__(self, request: pyramid.request.Request) -> pyramid.response.Response:
51
+ response = self.handler(request)
52
+
53
+ for pattern, headers in self.settings:
54
+ if pattern.match(request.path_info):
55
+ for header, value in headers.items():
56
+ if value is None:
57
+ if header in response.headers:
58
+ del response.headers[header]
59
+ else:
60
+ response.headers[header] = value
61
+
62
+ return response
@@ -0,0 +1,38 @@
1
+ # Copyright (c) 2020-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 os
30
+
31
+ LOCALE_PATH = "/etc/geomapfish/locale/"
32
+
33
+
34
+ def available_locale_names(path: str = LOCALE_PATH) -> list[str]:
35
+ """Get the available locales."""
36
+ if not os.path.exists(path):
37
+ return []
38
+ return [d for d in os.listdir(path) if os.path.isdir(os.path.join(path, d))]