mirror of
https://github.com/umap-project/umap.git
synced 2025-04-29 03:42:37 +02:00
Compare commits
29 commits
7515cf44a9
...
837a75557a
Author | SHA1 | Date | |
---|---|---|---|
![]() |
837a75557a | ||
![]() |
6945a5e867 | ||
![]() |
2ed890202e | ||
![]() |
cc7685ca3f | ||
![]() |
23688b6444 | ||
![]() |
d3c0947fa5 | ||
![]() |
a135e5c3e1 | ||
![]() |
2ad46046f8 | ||
![]() |
5271d7c19d | ||
![]() |
a535c86f6b | ||
![]() |
9aa1f58dc8 | ||
![]() |
102104f818 | ||
![]() |
35e5a4bda6 | ||
![]() |
6bae315efe | ||
![]() |
93120d91d6 | ||
![]() |
f037973a03 | ||
![]() |
c4614354ae | ||
![]() |
9ed466ea9b | ||
![]() |
168cc0c996 | ||
![]() |
ae4e7eb3e2 | ||
![]() |
3670b7dca6 | ||
![]() |
1c3cf9a7c5 | ||
![]() |
ada44fc0fc | ||
![]() |
ac6964c9f4 | ||
![]() |
82dcd90405 | ||
![]() |
d9e983aed4 | ||
![]() |
8f3f67588a | ||
![]() |
e97b619da8 | ||
![]() |
c08abce252 |
21 changed files with 315 additions and 70 deletions
26
.github/workflows/release-helm.yml
vendored
Normal file
26
.github/workflows/release-helm.yml
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
name: Release Charts
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
# depending on default permission settings for your org (contents being read-only or read-write for workloads), you will have to add permissions
|
||||||
|
# see: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#modifying-the-permissions-for-the-github_token
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Publish Helm charts
|
||||||
|
uses: stefanprodan/helm-gh-pages@v1.7.0
|
||||||
|
with:
|
||||||
|
charts_dir: charts
|
||||||
|
linting: off
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
|
@ -66,7 +66,11 @@ spec:
|
||||||
{{- end }}
|
{{- end }}
|
||||||
envFrom:
|
envFrom:
|
||||||
- secretRef:
|
- secretRef:
|
||||||
|
{{- if .Values.umap.envFromSecret }}
|
||||||
|
name: {{ .Values.umap.envFromSecret }}
|
||||||
|
{{- else }}
|
||||||
name: {{ include "umap.fullname" . }}-env
|
name: {{ include "umap.fullname" . }}-env
|
||||||
|
{{- end }}
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- name: config
|
- name: config
|
||||||
mountPath: /etc/umap/
|
mountPath: /etc/umap/
|
||||||
|
@ -80,7 +84,11 @@ spec:
|
||||||
volumes:
|
volumes:
|
||||||
- name: config
|
- name: config
|
||||||
secret:
|
secret:
|
||||||
|
{{- if .Values.umap.configFromSecret }}
|
||||||
|
secretName: {{ .Values.umap.configFromSecret }}
|
||||||
|
{{- else }}
|
||||||
secretName: {{ include "umap.fullname" . }}-config
|
secretName: {{ include "umap.fullname" . }}-config
|
||||||
|
{{- end }}
|
||||||
- name: statics
|
- name: statics
|
||||||
emptyDir: {}
|
emptyDir: {}
|
||||||
{{- if .Values.persistence.enabled }}
|
{{- if .Values.persistence.enabled }}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
{{ if not .Values.umap.configFromSecret }}
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
|
@ -6,4 +7,5 @@ metadata:
|
||||||
{{- include "umap.labels" . | nindent 4 }}
|
{{- include "umap.labels" . | nindent 4 }}
|
||||||
type: Opaque
|
type: Opaque
|
||||||
data:
|
data:
|
||||||
umap.conf: {{ .Values.umap.config | b64enc }}
|
umap.conf: {{ .Values.umap.config | b64enc }}
|
||||||
|
{{- end }}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
{{ if not .Values.umap.envFromSecret }}
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
|
@ -8,4 +9,5 @@ type: Opaque
|
||||||
data:
|
data:
|
||||||
{{- range $key, $value := .Values.umap.environment }}
|
{{- range $key, $value := .Values.umap.environment }}
|
||||||
{{ $key }}: "{{ $value | b64enc }}"
|
{{ $key }}: "{{ $value | b64enc }}"
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
|
@ -77,11 +77,18 @@ umap:
|
||||||
SECRET_KEY: CHANGE_ME
|
SECRET_KEY: CHANGE_ME
|
||||||
STATIC_ROOT: /srv/umap/static
|
STATIC_ROOT: /srv/umap/static
|
||||||
MEDIA_ROOT: /srv/umap/uploads
|
MEDIA_ROOT: /srv/umap/uploads
|
||||||
|
# Configure environment variables using an existing secret in the same namespace.
|
||||||
|
# In this case the values above are not used
|
||||||
|
envFromSecret: null
|
||||||
|
|
||||||
# You can also provide umap.conf content here:
|
# You can also provide umap.conf content here:
|
||||||
config: |
|
config: |
|
||||||
from umap.settings.base import *
|
from umap.settings.base import *
|
||||||
|
|
||||||
# See: https://github.com/umap-project/umap/blob/master/umap/settings/local.py.sample
|
# See: https://github.com/umap-project/umap/blob/master/umap/settings/local.py.sample
|
||||||
|
# Configure config file using an existing secret in the same namespace.
|
||||||
|
# In this case the values above are not used
|
||||||
|
configFromSecret: null
|
||||||
|
|
||||||
persistence:
|
persistence:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Force rtfd to use a recent version of mkdocs
|
# Force rtfd to use a recent version of mkdocs
|
||||||
mkdocs==1.6.1
|
mkdocs==1.6.1
|
||||||
pymdown-extensions==10.14.3
|
pymdown-extensions==10.14.3
|
||||||
mkdocs-material==9.6.10
|
mkdocs-material==9.6.11
|
||||||
mkdocs-static-i18n==1.3.0
|
mkdocs-static-i18n==1.3.0
|
||||||
|
|
|
@ -1,5 +1,13 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 3.0.4 - 2025-04-14
|
||||||
|
|
||||||
|
* do not show "back to home" button in iframes by @yohanboniface in #2644
|
||||||
|
* remove "hide home button" shown twice in map settings by @yohanboniface in #2645
|
||||||
|
* adapt Helm chart to match La Suite's expectations by @ohemelaar in #2646
|
||||||
|
* update colors by @yohanboniface in #2647
|
||||||
|
|
||||||
|
|
||||||
## 3.0.3 - 2025-04-11
|
## 3.0.3 - 2025-04-11
|
||||||
|
|
||||||
* do not try to remove a feature not yet added by @yohanboniface in #2637
|
* do not try to remove a feature not yet added by @yohanboniface in #2637
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Force rtfd to use a recent version of mkdocs
|
# Force rtfd to use a recent version of mkdocs
|
||||||
mkdocs==1.6.1
|
mkdocs==1.6.1
|
||||||
pymdown-extensions==10.14.3
|
pymdown-extensions==10.14.3
|
||||||
mkdocs-material==9.6.10
|
mkdocs-material==9.6.11
|
||||||
mkdocs-static-i18n==1.3.0
|
mkdocs-static-i18n==1.3.0
|
||||||
|
|
|
@ -32,7 +32,7 @@ dependencies = [
|
||||||
"django-agnocomplete==2.2.0",
|
"django-agnocomplete==2.2.0",
|
||||||
"django-environ==0.12.0",
|
"django-environ==0.12.0",
|
||||||
"django-probes==1.7.0",
|
"django-probes==1.7.0",
|
||||||
"Pillow==11.1.0",
|
"Pillow==11.2.1",
|
||||||
"psycopg==3.2.6",
|
"psycopg==3.2.6",
|
||||||
"requests==2.32.3",
|
"requests==2.32.3",
|
||||||
"rcssmin==1.2.1",
|
"rcssmin==1.2.1",
|
||||||
|
@ -47,7 +47,7 @@ dev = [
|
||||||
"ruff==0.11.4",
|
"ruff==0.11.4",
|
||||||
"djlint==1.36.4",
|
"djlint==1.36.4",
|
||||||
"mkdocs==1.6.1",
|
"mkdocs==1.6.1",
|
||||||
"mkdocs-material==9.6.10",
|
"mkdocs-material==9.6.11",
|
||||||
"mkdocs-static-i18n==1.3.0",
|
"mkdocs-static-i18n==1.3.0",
|
||||||
"vermin==1.6.0",
|
"vermin==1.6.0",
|
||||||
"pymdown-extensions==10.14.3",
|
"pymdown-extensions==10.14.3",
|
||||||
|
@ -58,20 +58,20 @@ test = [
|
||||||
"factory-boy==3.3.3",
|
"factory-boy==3.3.3",
|
||||||
"playwright>=1.39",
|
"playwright>=1.39",
|
||||||
"pytest==8.3.5",
|
"pytest==8.3.5",
|
||||||
"pytest-django==4.10.0",
|
"pytest-django==4.11.1",
|
||||||
"pytest-playwright==0.7.0",
|
"pytest-playwright==0.7.0",
|
||||||
"pytest-rerunfailures==15.0",
|
"pytest-rerunfailures==15.0",
|
||||||
"pytest-xdist>=3.5.0,<4",
|
"pytest-xdist>=3.5.0,<4",
|
||||||
"moto[s3]==5.1.3"
|
"moto[s3]==5.1.3"
|
||||||
]
|
]
|
||||||
docker = [
|
docker = [
|
||||||
"uvicorn==0.34.0",
|
"uvicorn==0.34.1",
|
||||||
]
|
]
|
||||||
s3 = [
|
s3 = [
|
||||||
"django-storages[s3]==1.14.6",
|
"django-storages[s3]==1.14.6",
|
||||||
]
|
]
|
||||||
sync = [
|
sync = [
|
||||||
"pydantic==2.11.2",
|
"pydantic==2.11.3",
|
||||||
"redis==5.2.1",
|
"redis==5.2.1",
|
||||||
"websockets==15.0.1",
|
"websockets==15.0.1",
|
||||||
]
|
]
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
VERSION = "3.0.3"
|
VERSION = "3.0.4"
|
||||||
|
|
|
@ -19,6 +19,7 @@ env = environ.Env()
|
||||||
|
|
||||||
INTERNAL_IPS = env.list("INTERNAL_IPS", default=["127.0.0.1"])
|
INTERNAL_IPS = env.list("INTERNAL_IPS", default=["127.0.0.1"])
|
||||||
ALLOWED_HOSTS = env.list("ALLOWED_HOSTS", default=["*"])
|
ALLOWED_HOSTS = env.list("ALLOWED_HOSTS", default=["*"])
|
||||||
|
CSRF_TRUSTED_ORIGINS = env.list("CSRF_TRUSTED_ORIGINS", default=[])
|
||||||
ADMINS = tuple(parseaddr(email) for email in env.list("ADMINS", default=[]))
|
ADMINS = tuple(parseaddr(email) for email in env.list("ADMINS", default=[]))
|
||||||
ASGI_APPLICATION = "umap.asgi.application"
|
ASGI_APPLICATION = "umap.asgi.application"
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ input[type="text"], input[type="password"], input[type="date"],
|
||||||
input[type="datetime-local"], input[type="email"], input[type="number"],
|
input[type="datetime-local"], input[type="email"], input[type="number"],
|
||||||
input[type="search"], input[type="tel"], input[type="time"], input[type="file"],
|
input[type="search"], input[type="tel"], input[type="time"], input[type="file"],
|
||||||
input[type="url"], textarea {
|
input[type="url"], textarea {
|
||||||
background-color: white;
|
background-color: var(--color-light);
|
||||||
border: 2px solid var(--color-darkBlue);
|
border: 2px solid var(--color-darkBlue);
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
display: block;
|
display: block;
|
||||||
|
@ -47,7 +47,7 @@ input[type=checkbox]:checked:after {
|
||||||
}
|
}
|
||||||
input[data-modified=true] {
|
input[data-modified=true] {
|
||||||
background-color: var(--color-lightCyan);
|
background-color: var(--color-lightCyan);
|
||||||
border: 1px solid var(--color-darkGray);
|
border: 1px solid var(--color-veryDarkGray);
|
||||||
}
|
}
|
||||||
input + select,
|
input + select,
|
||||||
select + input,
|
select + input,
|
||||||
|
@ -71,7 +71,7 @@ select {
|
||||||
.dark select {
|
.dark select {
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
background-color: var(--color-darkGray);
|
background-color: var(--color-darkGray);
|
||||||
border-color: var(--color-dark);
|
border-color: var(--color-veryDarkGray);
|
||||||
border-width: 1px;
|
border-width: 1px;
|
||||||
}
|
}
|
||||||
select[multiple="multiple"] {
|
select[multiple="multiple"] {
|
||||||
|
@ -89,7 +89,7 @@ input[type="submit"] {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
background-color: var(--color-darkBlue);
|
background-color: var(--color-darkBlue);
|
||||||
color: white;
|
color: var(--color-light);
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
min-height: 40px;
|
min-height: 40px;
|
||||||
}
|
}
|
||||||
|
@ -97,21 +97,21 @@ input[type="submit"] {
|
||||||
.dark [type="button"] {
|
.dark [type="button"] {
|
||||||
background-color: var(--color-darkerGray);
|
background-color: var(--color-darkerGray);
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
border: 1px solid #1b1f20;
|
border: 1px solid var(--color-veryDarkGray);
|
||||||
}
|
}
|
||||||
.button.primary {
|
.button.primary {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
.dark .button.primary:not([disabled]),
|
.dark .button.primary:not([disabled]),
|
||||||
.dark [type="button"].primary:not([disabled]) {
|
.dark [type="button"].primary:not([disabled]) {
|
||||||
background-color: var(--color-brightCyan);
|
background-color: var(--color-verySoftCyan);
|
||||||
color: var(--color-dark);
|
color: var(--color-dark);
|
||||||
border: 1px solid #1b1f20;
|
border: 1px solid var(--color-veryDarkGray);
|
||||||
}
|
}
|
||||||
.dark .button:hover,
|
.dark .button:hover,
|
||||||
.dark [type="button"]:hover,
|
.dark [type="button"]:hover,
|
||||||
.dark input[type="submit"]:hover {
|
.dark input[type="submit"]:hover {
|
||||||
background-color: #2e3436;
|
background-color: var(--color-darkerGray);
|
||||||
}
|
}
|
||||||
.dark a {
|
.dark a {
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
|
@ -151,14 +151,14 @@ button.round.small {
|
||||||
display: block;
|
display: block;
|
||||||
padding: 7px 7px;
|
padding: 7px 7px;
|
||||||
margin-bottom: 14px;
|
margin-bottom: 14px;
|
||||||
background: #393F3F;
|
background: var(--color-mediumGray);
|
||||||
color: var(--color-lightGray);
|
color: var(--color-lightGray);
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
border-radius: 0 2px;
|
border-radius: 0 2px;
|
||||||
}
|
}
|
||||||
.content .helptext {
|
.content .helptext {
|
||||||
background-color: #eee;
|
background-color: var(--color-lightGray);
|
||||||
color: #000;
|
color: var(--color-dark);
|
||||||
}
|
}
|
||||||
input + .help-text {
|
input + .help-text {
|
||||||
margin-top: -14px;
|
margin-top: -14px;
|
||||||
|
@ -205,8 +205,8 @@ input + .error {
|
||||||
margin-top: -14px;
|
margin-top: -14px;
|
||||||
margin-bottom: 14px;
|
margin-bottom: 14px;
|
||||||
background: var(--color-lightGray);
|
background: var(--color-lightGray);
|
||||||
color: #fff;
|
color: var(--color-light);
|
||||||
background-color: #cc0000;
|
background-color: var(--color-red);
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
border-radius: 0 2px;
|
border-radius: 0 2px;
|
||||||
}
|
}
|
||||||
|
@ -214,14 +214,14 @@ input[type="file"] + .error {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
input[value]:invalid {
|
input[value]:invalid {
|
||||||
border-color: red;
|
border-color: var(--color-red);
|
||||||
background-color: darkred;
|
background-color: var(--color-darkRed);
|
||||||
}
|
}
|
||||||
.dark input, .dark textarea {
|
.dark input, .dark textarea {
|
||||||
background-color: var(--color-darkerGray);
|
background-color: var(--color-darkerGray);
|
||||||
border-color: var(--color-dark);
|
border-color: var(--color-veryDarkGray);
|
||||||
border-width: 1px;
|
border-width: 1px;
|
||||||
color: #efefef;
|
color: var(--color-lightGray);
|
||||||
}
|
}
|
||||||
details {
|
details {
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
|
@ -229,7 +229,7 @@ details {
|
||||||
border-start-end-radius: 4px;
|
border-start-end-radius: 4px;
|
||||||
}
|
}
|
||||||
.dark details {
|
.dark details {
|
||||||
border: 1px solid #222;
|
border: 1px solid var(--color-veryDarkGray);
|
||||||
}
|
}
|
||||||
details fieldset {
|
details fieldset {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
@ -245,8 +245,8 @@ details summary {
|
||||||
padding: 0 5px;
|
padding: 0 5px;
|
||||||
}
|
}
|
||||||
.dark details summary {
|
.dark details summary {
|
||||||
background-color: #232729;
|
background-color: var(--color-darkerGray);
|
||||||
color: #fff;
|
color: var(--color-light);
|
||||||
}
|
}
|
||||||
.dark details fieldset {
|
.dark details fieldset {
|
||||||
border: 1px solid var(--color-darkGray);
|
border: 1px solid var(--color-darkGray);
|
||||||
|
@ -311,33 +311,34 @@ input.switch:empty ~ label:after {
|
||||||
width: 6em;
|
width: 6em;
|
||||||
-webkit-transition: all 100ms ease-in;
|
-webkit-transition: all 100ms ease-in;
|
||||||
transition: all 100ms ease-in;
|
transition: all 100ms ease-in;
|
||||||
color: #c9c9c7;
|
color: var(--color-mediumGray);
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
background-color: #ededed;
|
background-color: var(--color-lighterGray);
|
||||||
}
|
}
|
||||||
.dark input.switch:empty ~ label:before,
|
.dark input.switch:empty ~ label:before,
|
||||||
.dark input.switch:empty ~ label:after {
|
.dark input.switch:empty ~ label:after {
|
||||||
background-color: #272c2e;
|
background-color: var(--color-darkerGray);
|
||||||
}
|
}
|
||||||
input.switch:empty ~ label:after {
|
input.switch:empty ~ label:after {
|
||||||
width: 3em;
|
width: 3em;
|
||||||
margin-inline-start: 0.1em;
|
margin-inline-start: 0.1em;
|
||||||
background-color: #ededed;
|
background-color: var(--color-lightGray);
|
||||||
content: "OFF";
|
content: "OFF";
|
||||||
text-indent: 3.5em;
|
text-indent: 3.5em;
|
||||||
border: 1px solid #374E75;
|
border: 1px solid var(--color-darkerGray);
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
.dark input.switch:empty ~ label:after {
|
.dark input.switch:empty ~ label:after {
|
||||||
border: 1px solid #202425;
|
border: 1px solid var(--color-veryDarkGray);
|
||||||
background-color: #2c3233;
|
background-color: var(--color-darkerGray);
|
||||||
|
color: var(--color-lightGray);
|
||||||
}
|
}
|
||||||
input.switch:checked:empty ~ label:after {
|
input.switch:checked:empty ~ label:after {
|
||||||
content: ' ';
|
content: ' ';
|
||||||
}
|
}
|
||||||
.dark input.switch:checked ~ label:before,
|
.dark input.switch:checked ~ label:before,
|
||||||
input.switch:checked ~ label:before {
|
input.switch:checked ~ label:before {
|
||||||
background-color: var(--color-lightCyan);
|
background-color: var(--color-verySoftCyan);
|
||||||
border: 1px solid var(--color-lightGray);
|
border: 1px solid var(--color-lightGray);
|
||||||
color: var(--color-darkGray);
|
color: var(--color-darkGray);
|
||||||
content: "ON";
|
content: "ON";
|
||||||
|
@ -347,7 +348,7 @@ input.switch:checked ~ label:before {
|
||||||
}
|
}
|
||||||
.dark input.switch:checked ~ label:before {
|
.dark input.switch:checked ~ label:before {
|
||||||
border: none;
|
border: none;
|
||||||
background-color: var(--color-accent);
|
background-color: var(--color-verySoftCyan);
|
||||||
}
|
}
|
||||||
input.switch:checked ~ label:after {
|
input.switch:checked ~ label:after {
|
||||||
margin-inline-start: 3em;
|
margin-inline-start: 3em;
|
||||||
|
@ -387,9 +388,9 @@ input.switch:checked ~ label:after {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
.umap-multiplechoice label {
|
.umap-multiplechoice label {
|
||||||
border: 1px solid #374E75;
|
border: 1px solid var(--color-veryDarkGray);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
background-color: #c9c9c7;
|
background-color: var(--color-lightGray);
|
||||||
min-height: 30px;
|
min-height: 30px;
|
||||||
line-height: 30px;
|
line-height: 30px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
@ -397,12 +398,11 @@ input.switch:checked ~ label:after {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
.dark .umap-multiplechoice label {
|
.dark .umap-multiplechoice label {
|
||||||
border: 1px solid black;
|
border: 1px solid var(--color-veryDarkGray);
|
||||||
background-color: #2c3233;
|
background-color: var(--color-darkGray);
|
||||||
}
|
}
|
||||||
.umap-multiplechoice input[type='radio']:checked + label {
|
.umap-multiplechoice input[type='radio']:checked + label {
|
||||||
background-color: var(--color-accent);
|
background-color: var(--color-verySoftCyan);
|
||||||
box-shadow: inset 0 0 6px 0px #2c3233;
|
|
||||||
color: var(--color-darkGray);
|
color: var(--color-darkGray);
|
||||||
}
|
}
|
||||||
.inheritable .header .buttons {
|
.inheritable .header .buttons {
|
||||||
|
@ -420,7 +420,7 @@ input.switch:checked ~ label:after {
|
||||||
width: initial;
|
width: initial;
|
||||||
}
|
}
|
||||||
.inheritable + .inheritable {
|
.inheritable + .inheritable {
|
||||||
border-top: 1px solid #222;
|
border-top: 1px solid var(--color-darkerGray);
|
||||||
padding-top: 5px;
|
padding-top: 5px;
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
}
|
}
|
||||||
|
@ -504,7 +504,7 @@ i.info {
|
||||||
justify-content: space-around;
|
justify-content: space-around;
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
border-bottom: 1px solid #bebebe;
|
border-bottom: 1px solid var(--color-lighterGray);
|
||||||
}
|
}
|
||||||
.flat-tabs button {
|
.flat-tabs button {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
@ -515,14 +515,14 @@ i.info {
|
||||||
.flat-tabs button:hover,
|
.flat-tabs button:hover,
|
||||||
.flat-tabs .on {
|
.flat-tabs .on {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
border-bottom: 1px solid #444;
|
border-bottom: 1px solid var(--color-mediumGray);
|
||||||
}
|
}
|
||||||
.dark .flat-tabs button {
|
.dark .flat-tabs button {
|
||||||
color: #fff;
|
color: var(--color-light);
|
||||||
}
|
}
|
||||||
.dark .flat-tabs button:hover,
|
.dark .flat-tabs button:hover,
|
||||||
.dark .flat-tabs .on {
|
.dark .flat-tabs .on {
|
||||||
border-bottom: 1px solid #fff;
|
border-bottom: 1px solid var(--color-light);
|
||||||
}
|
}
|
||||||
.umap-pictogram-category h6 {
|
.umap-pictogram-category h6 {
|
||||||
font-size: 1.3em;
|
font-size: 1.3em;
|
||||||
|
@ -538,11 +538,11 @@ i.info {
|
||||||
height: 30px;
|
height: 30px;
|
||||||
line-height: 30px;
|
line-height: 30px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
background-color: #999;
|
background-color: var(--color-lightGray);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
color: black;
|
color: var(--color-dark);
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
@ -552,10 +552,10 @@ i.info {
|
||||||
}
|
}
|
||||||
.umap-pictogram-choice:hover,
|
.umap-pictogram-choice:hover,
|
||||||
.umap-color-picker span:hover {
|
.umap-color-picker span:hover {
|
||||||
background-color: #bebebe;
|
background-color: var(--color-lighterGray);
|
||||||
}
|
}
|
||||||
.umap-pictogram-choice.selected {
|
.umap-pictogram-choice.selected {
|
||||||
box-shadow: inset 0 0 0 1px #e9e9e9;
|
box-shadow: inset 0 0 0 1px var(--color-lighterGray);
|
||||||
}
|
}
|
||||||
|
|
||||||
.umap-pictogram-choice .leaflet-marker-icon {
|
.umap-pictogram-choice .leaflet-marker-icon {
|
||||||
|
|
|
@ -108,7 +108,7 @@ export default class Umap {
|
||||||
|
|
||||||
// Do not display in an iframe.
|
// Do not display in an iframe.
|
||||||
if (window.self !== window.top) {
|
if (window.self !== window.top) {
|
||||||
geojson.properties.homeControl = false
|
this.properties.homeControl = false
|
||||||
}
|
}
|
||||||
|
|
||||||
this._leafletMap.setup()
|
this._leafletMap.setup()
|
||||||
|
@ -833,7 +833,6 @@ export default class Umap {
|
||||||
UIFields.push(`properties.${name}Control`)
|
UIFields.push(`properties.${name}Control`)
|
||||||
}
|
}
|
||||||
UIFields = UIFields.concat([
|
UIFields = UIFields.concat([
|
||||||
'properties.homeControl',
|
|
||||||
'properties.moreControl',
|
'properties.moreControl',
|
||||||
'properties.scrollWheelZoom',
|
'properties.scrollWheelZoom',
|
||||||
'properties.miniMap',
|
'properties.miniMap',
|
||||||
|
|
|
@ -457,7 +457,6 @@ const locale = {
|
||||||
"Choose this dataset": "Choose this dataset",
|
"Choose this dataset": "Choose this dataset",
|
||||||
"GeoDataMine: thematic data from OpenStreetMap": "GeoDataMine: thematic data from OpenStreetMap",
|
"GeoDataMine: thematic data from OpenStreetMap": "GeoDataMine: thematic data from OpenStreetMap",
|
||||||
"Choose a theme": "Choose a theme",
|
"Choose a theme": "Choose a theme",
|
||||||
"Symplify all geometries to points": "Symplify all geometries to points",
|
|
||||||
"Choose this data": "Choose this data",
|
"Choose this data": "Choose this data",
|
||||||
"Search admin boundary": "Search admin boundary",
|
"Search admin boundary": "Search admin boundary",
|
||||||
"Please choose a theme and a boundary first.": "Please choose a theme and a boundary first.",
|
"Please choose a theme and a boundary first.": "Please choose a theme and a boundary first.",
|
||||||
|
@ -522,10 +521,10 @@ const locale = {
|
||||||
"Anonymous": "Anonymous",
|
"Anonymous": "Anonymous",
|
||||||
"created at {date}": "created at {date}",
|
"created at {date}": "created at {date}",
|
||||||
"modified at {date}": "modified at {date}",
|
"modified at {date}": "modified at {date}",
|
||||||
"Default zoom": "Default zoom",
|
"Default zoom": "Zoom predeterminat",
|
||||||
"Default latitude": "Default latitude",
|
"Default latitude": "Latitud predeterminada",
|
||||||
"Default longitude": "Default longitude",
|
"Default longitude": "Longitud predeterminada",
|
||||||
"Edit map default view": "Edit map default view",
|
"Edit map default view": "Edita la visualització predeterminada del mapa",
|
||||||
"Use current center and zoom": "Use current center and zoom",
|
"Use current center and zoom": "Use current center and zoom",
|
||||||
"Layer permalink": "Layer permalink",
|
"Layer permalink": "Layer permalink",
|
||||||
"Back to home": "Back to home",
|
"Back to home": "Back to home",
|
||||||
|
@ -538,7 +537,10 @@ const locale = {
|
||||||
"Images": "Images",
|
"Images": "Images",
|
||||||
"Iframes": "Iframes",
|
"Iframes": "Iframes",
|
||||||
"Tags": "Tags",
|
"Tags": "Tags",
|
||||||
"Geocode": "Geocode"
|
"Geocode": "Geocode",
|
||||||
|
"Display the back to home icon": "Display the back to home icon",
|
||||||
|
"Do you want to display layer switcher in caption bar?": "Do you want to display layer switcher in caption bar?",
|
||||||
|
"Simplify all geometries to points": "Simplify all geometries to points"
|
||||||
}
|
}
|
||||||
L.registerLocale("ca", locale)
|
L.registerLocale("ca", locale)
|
||||||
L.setLocale("ca")
|
L.setLocale("ca")
|
||||||
|
|
|
@ -457,7 +457,6 @@
|
||||||
"Choose this dataset": "Choose this dataset",
|
"Choose this dataset": "Choose this dataset",
|
||||||
"GeoDataMine: thematic data from OpenStreetMap": "GeoDataMine: thematic data from OpenStreetMap",
|
"GeoDataMine: thematic data from OpenStreetMap": "GeoDataMine: thematic data from OpenStreetMap",
|
||||||
"Choose a theme": "Choose a theme",
|
"Choose a theme": "Choose a theme",
|
||||||
"Symplify all geometries to points": "Symplify all geometries to points",
|
|
||||||
"Choose this data": "Choose this data",
|
"Choose this data": "Choose this data",
|
||||||
"Search admin boundary": "Search admin boundary",
|
"Search admin boundary": "Search admin boundary",
|
||||||
"Please choose a theme and a boundary first.": "Please choose a theme and a boundary first.",
|
"Please choose a theme and a boundary first.": "Please choose a theme and a boundary first.",
|
||||||
|
@ -522,10 +521,10 @@
|
||||||
"Anonymous": "Anonymous",
|
"Anonymous": "Anonymous",
|
||||||
"created at {date}": "created at {date}",
|
"created at {date}": "created at {date}",
|
||||||
"modified at {date}": "modified at {date}",
|
"modified at {date}": "modified at {date}",
|
||||||
"Default zoom": "Default zoom",
|
"Default zoom": "Zoom predeterminat",
|
||||||
"Default latitude": "Default latitude",
|
"Default latitude": "Latitud predeterminada",
|
||||||
"Default longitude": "Default longitude",
|
"Default longitude": "Longitud predeterminada",
|
||||||
"Edit map default view": "Edit map default view",
|
"Edit map default view": "Edita la visualització predeterminada del mapa",
|
||||||
"Use current center and zoom": "Use current center and zoom",
|
"Use current center and zoom": "Use current center and zoom",
|
||||||
"Layer permalink": "Layer permalink",
|
"Layer permalink": "Layer permalink",
|
||||||
"Back to home": "Back to home",
|
"Back to home": "Back to home",
|
||||||
|
@ -538,5 +537,8 @@
|
||||||
"Images": "Images",
|
"Images": "Images",
|
||||||
"Iframes": "Iframes",
|
"Iframes": "Iframes",
|
||||||
"Tags": "Tags",
|
"Tags": "Tags",
|
||||||
"Geocode": "Geocode"
|
"Geocode": "Geocode",
|
||||||
|
"Display the back to home icon": "Display the back to home icon",
|
||||||
|
"Do you want to display layer switcher in caption bar?": "Do you want to display layer switcher in caption bar?",
|
||||||
|
"Simplify all geometries to points": "Simplify all geometries to points"
|
||||||
}
|
}
|
|
@ -181,6 +181,17 @@ html[dir="rtl"] .leaflet-tooltip-pane > * {
|
||||||
.edit-enable [type="button"]:hover {
|
.edit-enable [type="button"]:hover {
|
||||||
background-color: #4d5759;
|
background-color: #4d5759;
|
||||||
}
|
}
|
||||||
|
.leaflet-control-toolbar .leaflet-toolbar-icon.dark:hover::before,
|
||||||
|
.leaflet-control-edit-enable a:hover::before {
|
||||||
|
content: attr(title);
|
||||||
|
background: #4d5759;
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: -222px;
|
||||||
|
min-width: 170px;
|
||||||
|
padding: .4rem;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
.umap-permanent-credits-container {
|
.umap-permanent-credits-container {
|
||||||
max-width: 20rem;
|
max-width: 20rem;
|
||||||
margin-inline-start: 5px!important;
|
margin-inline-start: 5px!important;
|
||||||
|
@ -990,9 +1001,9 @@ a.umap-control-caption,
|
||||||
height: var(--control-size);
|
height: var(--control-size);
|
||||||
line-height: var(--control-size);
|
line-height: var(--control-size);
|
||||||
}
|
}
|
||||||
/* Links are blue by default */
|
/* Links are dark cyan by default */
|
||||||
.leaflet-container a {
|
.leaflet-container a {
|
||||||
color: #0078a8;
|
color: var(--color-veryDarkCyan);
|
||||||
}
|
}
|
||||||
/* But not in controls */
|
/* But not in controls */
|
||||||
.leaflet-bar a {
|
.leaflet-bar a {
|
||||||
|
|
|
@ -6,12 +6,15 @@
|
||||||
--color-mediumGray: #3e4444;
|
--color-mediumGray: #3e4444;
|
||||||
--color-darkGray: #323737;
|
--color-darkGray: #323737;
|
||||||
--color-darkerGray: #2a2e30;
|
--color-darkerGray: #2a2e30;
|
||||||
|
--color-veryDarkGray: #1e2121;
|
||||||
--color-light: white;
|
--color-light: white;
|
||||||
--color-dark: black;
|
--color-dark: black;
|
||||||
--color-limeGreen: #b9f5d2;
|
--color-limeGreen: #b9f5d2;
|
||||||
|
--color-verySoftCyan: #a1eeeb;
|
||||||
--color-brightCyan: #46ece6;
|
--color-brightCyan: #46ece6;
|
||||||
--color-lightCyan: #d4fbf9;
|
--color-lightCyan: #d4fbf9;
|
||||||
--color-darkCyan: #009099;
|
--color-darkCyan: #009099;
|
||||||
|
--color-veryDarkCyan: #046460;
|
||||||
--color-red: #c60f13;
|
--color-red: #c60f13;
|
||||||
--color-darkRed: #5b2a2a;
|
--color-darkRed: #5b2a2a;
|
||||||
|
|
||||||
|
@ -64,6 +67,7 @@
|
||||||
.dark {
|
.dark {
|
||||||
--background-color: var(--color-darkGray);
|
--background-color: var(--color-darkGray);
|
||||||
--text-color: #efefef;
|
--text-color: #efefef;
|
||||||
|
--color-verySoftCyan: #8cdcd9;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (max-width:770px) {
|
@media only screen and (max-width:770px) {
|
||||||
|
|
140
umap/templates/umap/design_system.html
Normal file
140
umap/templates/umap/design_system.html
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
{% extends "umap/content.html" %}
|
||||||
|
|
||||||
|
{% load umap_tags i18n %}
|
||||||
|
|
||||||
|
{% block messages %}
|
||||||
|
{# We don't want to display errors (yet?). #}
|
||||||
|
{% endblock messages %}
|
||||||
|
|
||||||
|
{% block extra_head %}
|
||||||
|
{{ block.super }}
|
||||||
|
<style type="text/css">
|
||||||
|
h3, h4 {
|
||||||
|
margin-top: 2rem;
|
||||||
|
}
|
||||||
|
.colors-container {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.colors-container * {
|
||||||
|
width: 20%;
|
||||||
|
margin: 1rem;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 2rem 1px 1px 1px;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
position: initial;
|
||||||
|
padding: 0 1rem 1rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{% endblock extra_head %}
|
||||||
|
|
||||||
|
{% block maincontent %}
|
||||||
|
<div class="wrapper">
|
||||||
|
<h2 class="section">uMap Design System</h2>
|
||||||
|
<div class="row">
|
||||||
|
<h3>Forms</h3>
|
||||||
|
<h4>Copiable link</h4>
|
||||||
|
<div class="copiable-input">
|
||||||
|
<label>Lien vers la carte<input type="text" readonly value="http://example.org"></label><button class="icon icon-24 icon-copy" title="copier" type="button"></button>
|
||||||
|
</div>
|
||||||
|
<h4>Toggle</h4>
|
||||||
|
<div class="formbox">
|
||||||
|
<input type="checkbox" class="switch" id="inBbox"><label for="inBbox">Lister seulement les éléments visibles</label>
|
||||||
|
</div>
|
||||||
|
<h4>Multiple choice</h4>
|
||||||
|
<div class="formbox">
|
||||||
|
<label title="Afficher les boutons de zoom">Afficher les boutons de zoom</label>
|
||||||
|
<div class="umap-multiplechoice by3">
|
||||||
|
<input type="radio" name="zoomControl" id="zoomControl.0" value="true" checked><label for="zoomControl.0">toujours</label>
|
||||||
|
<input type="radio" name="zoomControl" id="zoomControl.1" value="false"><label for="zoomControl.1">jamais</label>
|
||||||
|
<input type="radio" name="zoomControl" id="zoomControl.2" value="null"><label for="zoomControl.2">caché</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h4>Select</h4>
|
||||||
|
<div class="formbox">
|
||||||
|
<label>Voulez-vous afficher un panneau latéral au chargement ?</label>
|
||||||
|
<select name="onLoadPanel">
|
||||||
|
<option value="none">Aucun</option>
|
||||||
|
<option value="caption">Légende</option>
|
||||||
|
<option value="databrowser">Explorateur : données</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<h4>You want it darker…</h4>
|
||||||
|
<div class="panel dark full on">
|
||||||
|
<h3><i class="icon icon-16 icon-settings" title=""></i><span>Titre avec icône</span></h3>
|
||||||
|
|
||||||
|
<details open="">
|
||||||
|
<summary><span>Options d'interface</span></summary>
|
||||||
|
<fieldset>
|
||||||
|
<form>
|
||||||
|
<div class="formbox">
|
||||||
|
<input type="checkbox" name="homeControl" data-ref="input" class="switch" id="homeControl">
|
||||||
|
<label title="Afficher le bouton de retour à l'accueil" for="homeControl">Afficher le bouton de retour à l'accueil</label>
|
||||||
|
</div>
|
||||||
|
<div class="formbox">
|
||||||
|
<label title="Afficher les boutons de zoom">Afficher les boutons de zoom</label>
|
||||||
|
<div class="umap-multiplechoice by3" data-ref="wrapper">
|
||||||
|
<input type="radio" name="zoomControl" id="zoomControl.0" value="true"><label for="zoomControl.0">toujours</label>
|
||||||
|
<input type="radio" name="zoomControl" id="zoomControl.1" value="false" checked><label for="zoomControl.1">jamais</label>
|
||||||
|
<input type="radio" name="zoomControl" id="zoomControl.2" value="null"><label for="zoomControl.2">caché</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="formbox">
|
||||||
|
<label title="Voulez-vous afficher un panneau latéral au chargement ?">Voulez-vous afficher un panneau latéral au chargement ?</label>
|
||||||
|
<select name="onLoadPanel">
|
||||||
|
<option value="none">Aucun</option>
|
||||||
|
<option value="caption">Légende</option>
|
||||||
|
<option value="databrowser">Explorateur : données</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</fieldset>
|
||||||
|
</details>
|
||||||
|
<details open="">
|
||||||
|
<summary><span>Actions avancées</span></summary>
|
||||||
|
<fieldset>
|
||||||
|
<div class="button-bar half">
|
||||||
|
<button class="button" type="button">
|
||||||
|
<i class="icon icon-24 icon-delete"></i>Supprimer
|
||||||
|
</button>
|
||||||
|
<button class="button" type="button">
|
||||||
|
<i class="icon icon-24 icon-empty"></i>Effacer les données
|
||||||
|
</button>
|
||||||
|
<button class="button" type="button">
|
||||||
|
<i class="icon icon-24 icon-empty"></i>Supprimer les calques
|
||||||
|
</button>
|
||||||
|
<button class="button" type="button">
|
||||||
|
<i class="icon icon-24 icon-clone"></i>Cloner cette carte
|
||||||
|
</button>
|
||||||
|
<button class="button" type="button">
|
||||||
|
<i class="icon icon-24 icon-download"></i>Ouvrir le panneau de partage
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
<h3>Colors</h3>
|
||||||
|
<div class="grid-container row colors-container"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock maincontent %}
|
||||||
|
{% block bottom_js %}
|
||||||
|
{{ block.super }}
|
||||||
|
<script type="text/javascript">
|
||||||
|
const colorsContainer = document.querySelector('.colors-container')
|
||||||
|
const styles = getComputedStyle(document.querySelector('html'))
|
||||||
|
let html = ""
|
||||||
|
for (const style of styles) {
|
||||||
|
if (style.startsWith("--") && style.includes("color")) {
|
||||||
|
const value = styles.getPropertyValue(style)
|
||||||
|
html += `<div style="border-color: ${value};">${style}</div>`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const template = document.createElement('template')
|
||||||
|
template.innerHTML = html
|
||||||
|
colorsContainer.appendChild(template.content)
|
||||||
|
</script>
|
||||||
|
{% endblock bottom_js %}
|
25
umap/tests/integration/test_iframe.py
Normal file
25
umap/tests/integration/test_iframe.py
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
from playwright.sync_api import expect
|
||||||
|
|
||||||
|
|
||||||
|
def test_home_control_is_hidden(live_server, map, tilelayer, page):
|
||||||
|
body = f"""
|
||||||
|
<html>
|
||||||
|
<head></head>
|
||||||
|
<body>
|
||||||
|
<iframe width="100%" height="300px" frameborder="0" allowfullscreen allow="geolocation"
|
||||||
|
src="{map.get_absolute_url()}?scaleControl=false&miniMap=false&scrollWheelZoom=false&zoomControl=true&editMode=disabled&moreControl=true&searchControl=null&tilelayersControl=null&embedControl=null&datalayersControl=true&onLoadPanel=caption&captionBar=false&captionMenus=true"></iframe>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
|
||||||
|
def handle(route):
|
||||||
|
route.fulfill(body=body)
|
||||||
|
|
||||||
|
url = f"{live_server.url}/test-iframe"
|
||||||
|
# Intercept the route
|
||||||
|
page.route(url, handle)
|
||||||
|
|
||||||
|
page.goto(url)
|
||||||
|
expect(
|
||||||
|
page.locator("iframe").content_frame.get_by_role("link", name="Home logo")
|
||||||
|
).to_be_hidden()
|
|
@ -205,6 +205,7 @@ urlpatterns += i18n_patterns(
|
||||||
)
|
)
|
||||||
urlpatterns += (
|
urlpatterns += (
|
||||||
path("stats/", cache_page(60 * 60)(views.stats), name="stats"),
|
path("stats/", cache_page(60 * 60)(views.stats), name="stats"),
|
||||||
|
path("design_system/", views.design_system, name="design_system"),
|
||||||
path(
|
path(
|
||||||
"favicon.ico",
|
"favicon.ico",
|
||||||
cache_control(max_age=60 * 60 * 24, immutable=True, public=True)(
|
cache_control(max_age=60 * 60 * 24, immutable=True, public=True)(
|
||||||
|
|
|
@ -1410,6 +1410,13 @@ def stats(request):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DesignSystem(TemplateView):
|
||||||
|
template_name = "umap/design_system.html"
|
||||||
|
|
||||||
|
|
||||||
|
design_system = DesignSystem.as_view()
|
||||||
|
|
||||||
|
|
||||||
@require_GET
|
@require_GET
|
||||||
@cache_control(max_age=60 * 60 * 24, immutable=True, public=True) # One day.
|
@cache_control(max_age=60 * 60 * 24, immutable=True, public=True) # One day.
|
||||||
def webmanifest(request):
|
def webmanifest(request):
|
||||||
|
|
Loading…
Reference in a new issue