mirror of
https://github.com/umap-project/umap.git
synced 2025-04-28 19:42:36 +02:00
parent
e2f154f62e
commit
39f38a9cdf
9 changed files with 90 additions and 5 deletions
|
@ -4,7 +4,6 @@ from django.contrib.auth import get_user_model
|
|||
from django.contrib.gis.geos import Point
|
||||
from django.forms.utils import ErrorList
|
||||
from django.template.defaultfilters import slugify
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from .models import DataLayer, Map, Team
|
||||
|
||||
|
@ -92,7 +91,7 @@ class MapSettingsForm(forms.ModelForm):
|
|||
return self.cleaned_data["center"]
|
||||
|
||||
class Meta:
|
||||
fields = ("settings", "name", "center", "slug")
|
||||
fields = ("settings", "name", "center", "slug", "tags")
|
||||
model = Map
|
||||
|
||||
|
||||
|
|
23
umap/migrations/0027_map_tags.py
Normal file
23
umap/migrations/0027_map_tags.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
# Generated by Django 5.1.6 on 2025-02-26 16:18
|
||||
|
||||
import django.contrib.postgres.fields
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("umap", "0026_datalayer_modified_at_datalayer_share_status"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="map",
|
||||
name="tags",
|
||||
field=django.contrib.postgres.fields.ArrayField(
|
||||
base_field=models.CharField(max_length=200),
|
||||
blank=True,
|
||||
default=list,
|
||||
size=None,
|
||||
),
|
||||
),
|
||||
]
|
|
@ -4,6 +4,7 @@ import uuid
|
|||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.gis.db import models
|
||||
from django.contrib.postgres.fields import ArrayField
|
||||
from django.core.files.base import File
|
||||
from django.core.files.storage import storages
|
||||
from django.core.signing import Signer
|
||||
|
@ -236,6 +237,7 @@ class Map(NamedModel):
|
|||
settings = models.JSONField(
|
||||
blank=True, null=True, verbose_name=_("settings"), default=dict
|
||||
)
|
||||
tags = ArrayField(models.CharField(max_length=200), blank=True, default=list)
|
||||
|
||||
objects = models.Manager()
|
||||
public = PublicManager()
|
||||
|
@ -420,7 +422,8 @@ class Map(NamedModel):
|
|||
return {
|
||||
"iconUrl": {
|
||||
"default": "%sumap/img/marker.svg" % settings.STATIC_URL,
|
||||
}
|
||||
},
|
||||
"tags": {"choices": settings.UMAP_TAGS},
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ from email.utils import parseaddr
|
|||
|
||||
import environ
|
||||
from django.conf.locale import LANG_INFO
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
import umap as project_module
|
||||
|
||||
|
@ -290,6 +291,19 @@ UMAP_HOME_FEED = "latest"
|
|||
UMAP_IMPORTERS = {}
|
||||
UMAP_HOST_INFOS = {}
|
||||
UMAP_LABEL_KEYS = ["name", "title"]
|
||||
UMAP_TAGS = (
|
||||
("art", _("Art and Culture")),
|
||||
("bike", _("Bike")),
|
||||
("environment", _("Environment")),
|
||||
("education", _("Education")),
|
||||
("food", _("Food and Agriculture")),
|
||||
("history", _("History")),
|
||||
("public", _("Public sector")),
|
||||
("sport", _("Sport and Leisure")),
|
||||
("travel", _("Travel")),
|
||||
("trekking", _("Trekking")),
|
||||
("tourism", _("Tourism")),
|
||||
)
|
||||
|
||||
UMAP_READONLY = env("UMAP_READONLY", default=False)
|
||||
UMAP_GZIP = True
|
||||
|
|
|
@ -138,6 +138,8 @@ export class MutatingForm extends Form {
|
|||
} else if (properties.type === Number) {
|
||||
if (properties.step) properties.handler = 'Range'
|
||||
else properties.handler = 'IntInput'
|
||||
} else if (properties.type === Array) {
|
||||
properties.handler = 'CheckBoxes'
|
||||
} else if (properties.choices) {
|
||||
const text_length = properties.choices.reduce(
|
||||
(acc, [_, label]) => acc + label.length,
|
||||
|
|
|
@ -324,6 +324,24 @@ Fields.CheckBox = class extends BaseElement {
|
|||
}
|
||||
}
|
||||
|
||||
Fields.CheckBoxes = class extends BaseElement {
|
||||
build() {
|
||||
const initial = this.get() || []
|
||||
for (const [value, label] of this.properties.choices) {
|
||||
const tpl = `<label><input type=checkbox value="${value}" name="${this.name}" data-ref=input />${label}</label>`
|
||||
const [root, { input }] = Utils.loadTemplateWithRefs(tpl)
|
||||
this.container.appendChild(root)
|
||||
input.checked = initial.includes(value)
|
||||
input.addEventListener('change', () => this.sync())
|
||||
}
|
||||
super.build()
|
||||
}
|
||||
|
||||
value() {
|
||||
return Array.from(this.root.querySelectorAll('input:checked')).map((el) => el.value)
|
||||
}
|
||||
}
|
||||
|
||||
Fields.Select = class extends BaseElement {
|
||||
getTemplate() {
|
||||
return `<select name="${this.name}" data-ref=select></select>`
|
||||
|
@ -1296,13 +1314,14 @@ Fields.ManageEditors = class extends BaseElement {
|
|||
placeholder: translate("Type editor's username"),
|
||||
}
|
||||
this.autocomplete = new AjaxAutocompleteMultiple(this.container, options)
|
||||
this._values = this.toHTML()
|
||||
if (this._values)
|
||||
this._values = this.toHTML() || []
|
||||
if (this._values) {
|
||||
for (let i = 0; i < this._values.length; i++)
|
||||
this.autocomplete.displaySelected({
|
||||
item: { value: this._values[i].id, label: this._values[i].name },
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
value() {
|
||||
return this._values
|
||||
|
|
|
@ -516,6 +516,9 @@ export const SCHEMA = {
|
|||
helpEntries: ['sync'],
|
||||
default: false,
|
||||
},
|
||||
tags: {
|
||||
type: Array,
|
||||
},
|
||||
team: {
|
||||
type: Object,
|
||||
},
|
||||
|
|
|
@ -755,6 +755,12 @@ export default class Umap {
|
|||
const form = builder.build()
|
||||
container.appendChild(form)
|
||||
|
||||
const tags = DomUtil.createFieldset(container, translate('Tags'))
|
||||
const tagsFields = ['properties.tags']
|
||||
const tagsBuilder = new MutatingForm(this, tagsFields, {
|
||||
umap: this,
|
||||
})
|
||||
tags.appendChild(tagsBuilder.build())
|
||||
const credits = DomUtil.createFieldset(container, translate('Credits'))
|
||||
const creditsFields = [
|
||||
'properties.licence',
|
||||
|
@ -1168,6 +1174,7 @@ export default class Umap {
|
|||
const formData = new FormData()
|
||||
formData.append('name', this.properties.name)
|
||||
formData.append('center', JSON.stringify(this.geometry()))
|
||||
formData.append('tags', this.properties.tags || [])
|
||||
formData.append('settings', JSON.stringify(geojson))
|
||||
const uri = this.urls.get('map_save', { map_id: this.id })
|
||||
const [data, _, error] = await this.server.post(uri, {}, formData)
|
||||
|
|
|
@ -226,3 +226,18 @@ def test_hover_tooltip_setting_should_be_persistent(live_server, map, page):
|
|||
- text: always never on hover
|
||||
""")
|
||||
expect(page.locator(".umap-field-showLabel input[value=null]")).to_be_checked()
|
||||
|
||||
|
||||
def test_can_edit_map_tags(live_server, map, page):
|
||||
map.settings["properties"]["tags"] = ["art"]
|
||||
map.edit_status = Map.ANONYMOUS
|
||||
map.save()
|
||||
page.goto(f"{live_server.url}{map.get_absolute_url()}?edit")
|
||||
page.get_by_role("button", name="Edit map name and caption").click()
|
||||
page.get_by_text("Tags").click()
|
||||
expect(page.get_by_label("Art and Culture")).to_be_checked()
|
||||
page.get_by_label("Bike").check()
|
||||
with page.expect_response(re.compile("./update/settings/.*")):
|
||||
page.get_by_role("button", name="Save").click()
|
||||
saved = Map.objects.get(pk=map.pk)
|
||||
assert saved.tags == ["art", "bike"]
|
||||
|
|
Loading…
Reference in a new issue