From ecc7c4f8294e08cca418b10fa8a26b9ee2943794 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Kl=C3=BCnder?= Date: Thu, 13 Oct 2016 15:55:15 +0200 Subject: [PATCH] editor forms for new feature models --- src/c3nav/editor/forms.py | 82 +++++++++++++-------- src/c3nav/editor/static/editor/js/editor.js | 6 +- src/c3nav/editor/urls.py | 2 +- src/c3nav/editor/views.py | 47 ++++++------ src/c3nav/mapdata/models/features.py | 6 ++ src/c3nav/mapdata/packageio/read.py | 1 + 6 files changed, 86 insertions(+), 58 deletions(-) diff --git a/src/c3nav/editor/forms.py b/src/c3nav/editor/forms.py index bee80c43..d88f02e7 100644 --- a/src/c3nav/editor/forms.py +++ b/src/c3nav/editor/forms.py @@ -1,63 +1,87 @@ +import json from collections import OrderedDict from django.conf import settings -from django.forms import CharField, Form, ModelForm, ValidationError +from django.forms import CharField, Form, ModelForm from django.forms.models import ModelChoiceField from django.forms.widgets import HiddenInput from django.utils.translation import ugettext_lazy as _ +from shapely.geometry.geo import mapping -from c3nav.mapdata.models import Feature, Package +from c3nav.mapdata.models import Package +from c3nav.mapdata.models.features import Inside, Room from c3nav.mapdata.permissions import get_unlocked_packages -class FeatureForm(ModelForm): +class FeatureFormMixin(ModelForm): def __init__(self, *args, feature_type, request=None, **kwargs): self.feature_type = feature_type self.request = request super().__init__(*args, **kwargs) - self.fields['level'].widget = HiddenInput() - self.fields['geometry'].widget = HiddenInput() + creating = not self.instance.pk - titles = OrderedDict((lang_code, '') for lang_code, language in settings.LANGUAGES) - if self.instance is not None and self.instance.pk: + # disable name on non-direct editing + if not creating and not settings.DIRECT_EDITING: self.fields['name'].disabled = True + + # restrict package choices and field_name + if not creating: if not settings.DIRECT_EDITING: self.fields['package'].widget = HiddenInput() self.fields['package'].disabled = True - titles.update(self.instance.titles) + self.initial['package'] = self.instance.package.name elif not settings.DIRECT_EDITING: unlocked_packages = get_unlocked_packages(request) if len(unlocked_packages) == 1: self.fields['package'].widget = HiddenInput() self.fields['package'].initial = next(iter(unlocked_packages)) + self.fields['package'].disabled = True else: self.fields['package'] = ModelChoiceField( - queryset=Package.objects.filter(name__in=unlocked_packages) + queryset=Package.objects.filter(name__in=unlocked_packages), ) + self.fields['package'].to_field_name = 'name' - language_titles = dict(settings.LANGUAGES) - for language in titles.keys(): - new_title = self.data.get('title_' + language) - if new_title is not None: - titles[language] = new_title - self.fields['title_' + language] = CharField(label=language_titles.get(language, language), required=False, - initial=titles[language].strip(), max_length=50) - self.titles = titles + # hide level widget and set field_name + self.fields['level'].widget = HiddenInput() + self.fields['level'].to_field_name = 'name' + if not creating: + self.initial['level'] = self.instance.level.name - def clean(self): - super().clean() - if not any(self.titles.values()): - raise ValidationError( - _('You have to select a title in at least one language.') - ) + # hide geometry widget + self.fields['geometry'].widget = HiddenInput() + if not creating: + self.initial['geometry'] = json.dumps(mapping(self.instance.geometry), separators=(',', ':')) - def get_languages(self): - pass + # parse titles + self.titles = None + if hasattr(self.instance, 'titles'): + titles = OrderedDict((lang_code, '') for lang_code, language in settings.LANGUAGES) + if self.instance is not None and self.instance.pk: + titles.update(self.instance.titles) - class Meta: - # generate extra fields in the number specified via extra_fields - model = Feature - fields = ['name', 'package', 'level', 'geometry'] + language_titles = dict(settings.LANGUAGES) + for language in titles.keys(): + new_title = self.data.get('title_' + language) + if new_title is not None: + titles[language] = new_title + self.fields['title_' + language] = CharField(label=language_titles.get(language, language), + required=False, + initial=titles[language].strip(), max_length=50) + self.titles = titles + + +def create_editor_form(feature_model, add_fields=None): + class EditorForm(FeatureFormMixin, ModelForm): + class Meta: + model = feature_model + fields = ['name', 'package', 'level', 'geometry'] + (add_fields if add_fields is not None else []) + + feature_model.EditorForm = EditorForm + + +create_editor_form(Inside) +create_editor_form(Room) class CommitForm(Form): diff --git a/src/c3nav/editor/static/editor/js/editor.js b/src/c3nav/editor/static/editor/js/editor.js index ce740156..6f9ca30e 100644 --- a/src/c3nav/editor/static/editor/js/editor.js +++ b/src/c3nav/editor/static/editor/js/editor.js @@ -321,8 +321,7 @@ editor = { editor.map.fitBounds(editor._editing.getBounds()); $('.leaflet-drawbar').hide(); - var endpoint = editor.feature_types[editor._creating].endpoint; - var path = '/editor/' + endpoint + '/add/'; + var path = '/editor/features/' + editor._creating + '/add/'; $('#mapeditcontrols').removeClass('list'); $('body').addClass('controls'); $('#mapeditdetail').load(path, editor.edit_form_loaded); @@ -333,8 +332,7 @@ editor = { if (editor._creating !== null || editor._editing !== null) return; editor._highlight_layer.clearLayers(); editor._editing = editor.features[name].layer; - var endpoint = editor.feature_types[editor._editing.feature.properties.feature_type].endpoint; - var path = '/editor/'+endpoint+'/edit/' + name + '/'; + var path = '/editor/features/'+ editor._editing.feature.properties.feature_type +'/edit/' + name + '/'; $('#mapeditcontrols').removeClass('list'); $('#mapeditdetail').load(path, editor.edit_form_loaded); $('body').addClass('controls'); diff --git a/src/c3nav/editor/urls.py b/src/c3nav/editor/urls.py index fb8f8f23..8a98c18e 100644 --- a/src/c3nav/editor/urls.py +++ b/src/c3nav/editor/urls.py @@ -6,7 +6,7 @@ from c3nav.editor.views import edit_feature, finalize, oauth_callback urlpatterns = [ url(r'^$', TemplateView.as_view(template_name='editor/map.html'), name='editor.index'), url(r'^features/(?P[^/]+)/add/$', edit_feature, name='editor.feature.add'), - url(r'^features/edit/(?P[^/]+)/$', edit_feature, name='editor.feature.edit'), + url(r'^features/(?P[^/]+)/edit/(?P[^/]+)/$', edit_feature, name='editor.feature.edit'), url(r'^finalize/$', finalize, name='editor.finalize'), url(r'^oauth/(?P[^/]+)/callback$', oauth_callback, name='editor.oauth.callback') ] diff --git a/src/c3nav/editor/views.py b/src/c3nav/editor/views.py index 1176014c..f2c31179 100644 --- a/src/c3nav/editor/views.py +++ b/src/c3nav/editor/views.py @@ -4,36 +4,34 @@ from django.core.exceptions import PermissionDenied, SuspiciousOperation from django.core.signing import BadSignature from django.http.response import Http404 from django.shortcuts import get_object_or_404, render +from django.utils import translation -# from c3nav.editor.forms import FeatureForm from c3nav.editor.hosters import get_hoster_for_package, hosters -from c3nav.mapdata.models.features import FEATURE_TYPES, Feature +from c3nav.mapdata.models.features import FEATURE_TYPES from c3nav.mapdata.models.package import Package from c3nav.mapdata.packageio.write import json_encode from c3nav.mapdata.permissions import can_access_package -def edit_feature(request, feature_type=None, name=None): +def edit_feature(request, feature_type, name=None): + model = FEATURE_TYPES.get(feature_type) + if model is None: + raise Http404() + + feature = None if name is not None: # Edit existing feature - feature = get_object_or_404(Feature, name=name) + feature = get_object_or_404(model, name=name) if not can_access_package(request, feature.package): raise PermissionDenied - feature_type = FEATURE_TYPES.get(feature.feature_type) - else: - # Create new feature - feature = None - feature_type = FEATURE_TYPES.get(feature_type) - if feature_type is None: - raise Http404() if request.method == 'POST': if feature is not None and request.POST.get('delete') == '1': # Delete this feature! if request.POST.get('delete_confirm') == '1': if not settings.DIRECT_EDITING: - title_en = feature.titles.get('en', next(iter(feature.titles.values()))) - commit_msg = 'Deleted %s: %s' % (feature_type.title_en.lower(), title_en) + with translation.override('en'): + commit_msg = 'Deleted %s: %s' % (model._meta.verbose_name, feature.title) return render(request, 'editor/feature_success.html', { 'data': signing.dumps({ 'type': 'editor.edit', @@ -41,7 +39,7 @@ def edit_feature(request, feature_type=None, name=None): 'package_name': feature.package.name, 'commit_id': feature.package.commit_id, 'commit_msg': commit_msg, - 'file_path': feature.tofilename(), + 'file_path': feature.get_filename(), }) }) @@ -54,22 +52,23 @@ def edit_feature(request, feature_type=None, name=None): 'path': request.path }) - form = FeatureForm(instance=feature, data=request.POST, feature_type=feature_type, request=request) + form = model.EditorForm(instance=feature, data=request.POST, feature_type=feature_type, request=request) if form.is_valid(): # Update/create feature commit_type = 'Created' if feature is None else 'Updated' action = 'create' if feature is None else 'edit' feature = form.instance - feature.feature_type = feature_type.name - feature.titles = {} - for language, title in form.titles.items(): - if title: - feature.titles[language] = title + + if form.titles is not None: + feature.titles = {} + for language, title in form.titles.items(): + if title: + feature.titles[language] = title if not settings.DIRECT_EDITING: content = json_encode(feature.tofile()) - title_en = feature.titles.get('en', next(iter(feature.titles.values()))) - commit_msg = '%s %s: %s' % (commit_type, feature_type.title_en.lower(), title_en) + with translation.override('en'): + commit_msg = '%s %s: %s' % (commit_type, model._meta.verbose_name, feature.title) return render(request, 'editor/feature_success.html', { 'data': signing.dumps({ 'type': 'editor.edit', @@ -77,7 +76,7 @@ def edit_feature(request, feature_type=None, name=None): 'package_name': feature.package.name, 'commit_id': feature.package.commit_id, 'commit_msg': commit_msg, - 'file_path': feature.tofilename(), + 'file_path': feature.get_filename(), 'content': content, }) }) @@ -86,7 +85,7 @@ def edit_feature(request, feature_type=None, name=None): return render(request, 'editor/feature_success.html', {}) else: - form = FeatureForm(instance=feature, feature_type=feature_type, request=request) + form = model.EditorForm(instance=feature, feature_type=feature_type, request=request) return render(request, 'editor/feature.html', { 'form': form, diff --git a/src/c3nav/mapdata/models/features.py b/src/c3nav/mapdata/models/features.py index 1e9bc5eb..43f3a4cd 100644 --- a/src/c3nav/mapdata/models/features.py +++ b/src/c3nav/mapdata/models/features.py @@ -23,9 +23,15 @@ class Feature(MapdataModel): level = models.ForeignKey('mapdata.Level', on_delete=models.CASCADE, verbose_name=_('level')) geometry = GeometryField() + EditorForm = None + class Meta: abstract = True + @property + def title(self): + return self.name + @classmethod def fromfile(cls, data, file_path): kwargs = super().fromfile(data, file_path) diff --git a/src/c3nav/mapdata/packageio/read.py b/src/c3nav/mapdata/packageio/read.py index 7db8bfd6..d7656650 100644 --- a/src/c3nav/mapdata/packageio/read.py +++ b/src/c3nav/mapdata/packageio/read.py @@ -160,6 +160,7 @@ class ReaderItem: # Change name references to the referenced object for name, model in self.relations.items(): if name in self.data: + print(name, self.data[name]) self.data[name] = self.reader.saved_items[model][self.data[name]].obj obj, created = self.model.objects.update_or_create(name=self.data['name'], defaults=self.data)