editor forms for new feature models

This commit is contained in:
Laura Klünder 2016-10-13 15:55:15 +02:00
parent 4b74eb0cf5
commit ecc7c4f829
6 changed files with 86 additions and 58 deletions

View file

@ -1,63 +1,87 @@
import json
from collections import OrderedDict from collections import OrderedDict
from django.conf import settings 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.models import ModelChoiceField
from django.forms.widgets import HiddenInput from django.forms.widgets import HiddenInput
from django.utils.translation import ugettext_lazy as _ 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 from c3nav.mapdata.permissions import get_unlocked_packages
class FeatureForm(ModelForm): class FeatureFormMixin(ModelForm):
def __init__(self, *args, feature_type, request=None, **kwargs): def __init__(self, *args, feature_type, request=None, **kwargs):
self.feature_type = feature_type self.feature_type = feature_type
self.request = request self.request = request
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.fields['level'].widget = HiddenInput() creating = not self.instance.pk
self.fields['geometry'].widget = HiddenInput()
titles = OrderedDict((lang_code, '') for lang_code, language in settings.LANGUAGES) # disable name on non-direct editing
if self.instance is not None and self.instance.pk: if not creating and not settings.DIRECT_EDITING:
self.fields['name'].disabled = True self.fields['name'].disabled = True
# restrict package choices and field_name
if not creating:
if not settings.DIRECT_EDITING: if not settings.DIRECT_EDITING:
self.fields['package'].widget = HiddenInput() self.fields['package'].widget = HiddenInput()
self.fields['package'].disabled = True self.fields['package'].disabled = True
titles.update(self.instance.titles) self.initial['package'] = self.instance.package.name
elif not settings.DIRECT_EDITING: elif not settings.DIRECT_EDITING:
unlocked_packages = get_unlocked_packages(request) unlocked_packages = get_unlocked_packages(request)
if len(unlocked_packages) == 1: if len(unlocked_packages) == 1:
self.fields['package'].widget = HiddenInput() self.fields['package'].widget = HiddenInput()
self.fields['package'].initial = next(iter(unlocked_packages)) self.fields['package'].initial = next(iter(unlocked_packages))
self.fields['package'].disabled = True
else: else:
self.fields['package'] = ModelChoiceField( 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) # hide level widget and set field_name
for language in titles.keys(): self.fields['level'].widget = HiddenInput()
new_title = self.data.get('title_' + language) self.fields['level'].to_field_name = 'name'
if new_title is not None: if not creating:
titles[language] = new_title self.initial['level'] = self.instance.level.name
self.fields['title_' + language] = CharField(label=language_titles.get(language, language), required=False,
initial=titles[language].strip(), max_length=50)
self.titles = titles
def clean(self): # hide geometry widget
super().clean() self.fields['geometry'].widget = HiddenInput()
if not any(self.titles.values()): if not creating:
raise ValidationError( self.initial['geometry'] = json.dumps(mapping(self.instance.geometry), separators=(',', ':'))
_('You have to select a title in at least one language.')
)
def get_languages(self): # parse titles
pass 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: language_titles = dict(settings.LANGUAGES)
# generate extra fields in the number specified via extra_fields for language in titles.keys():
model = Feature new_title = self.data.get('title_' + language)
fields = ['name', 'package', 'level', 'geometry'] 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): class CommitForm(Form):

View file

@ -321,8 +321,7 @@ editor = {
editor.map.fitBounds(editor._editing.getBounds()); editor.map.fitBounds(editor._editing.getBounds());
$('.leaflet-drawbar').hide(); $('.leaflet-drawbar').hide();
var endpoint = editor.feature_types[editor._creating].endpoint; var path = '/editor/features/' + editor._creating + '/add/';
var path = '/editor/' + endpoint + '/add/';
$('#mapeditcontrols').removeClass('list'); $('#mapeditcontrols').removeClass('list');
$('body').addClass('controls'); $('body').addClass('controls');
$('#mapeditdetail').load(path, editor.edit_form_loaded); $('#mapeditdetail').load(path, editor.edit_form_loaded);
@ -333,8 +332,7 @@ editor = {
if (editor._creating !== null || editor._editing !== null) return; if (editor._creating !== null || editor._editing !== null) return;
editor._highlight_layer.clearLayers(); editor._highlight_layer.clearLayers();
editor._editing = editor.features[name].layer; editor._editing = editor.features[name].layer;
var endpoint = editor.feature_types[editor._editing.feature.properties.feature_type].endpoint; var path = '/editor/features/'+ editor._editing.feature.properties.feature_type +'/edit/' + name + '/';
var path = '/editor/'+endpoint+'/edit/' + name + '/';
$('#mapeditcontrols').removeClass('list'); $('#mapeditcontrols').removeClass('list');
$('#mapeditdetail').load(path, editor.edit_form_loaded); $('#mapeditdetail').load(path, editor.edit_form_loaded);
$('body').addClass('controls'); $('body').addClass('controls');

View file

@ -6,7 +6,7 @@ from c3nav.editor.views import edit_feature, finalize, oauth_callback
urlpatterns = [ urlpatterns = [
url(r'^$', TemplateView.as_view(template_name='editor/map.html'), name='editor.index'), url(r'^$', TemplateView.as_view(template_name='editor/map.html'), name='editor.index'),
url(r'^features/(?P<feature_type>[^/]+)/add/$', edit_feature, name='editor.feature.add'), url(r'^features/(?P<feature_type>[^/]+)/add/$', edit_feature, name='editor.feature.add'),
url(r'^features/edit/(?P<name>[^/]+)/$', edit_feature, name='editor.feature.edit'), url(r'^features/(?P<feature_type>[^/]+)/edit/(?P<name>[^/]+)/$', edit_feature, name='editor.feature.edit'),
url(r'^finalize/$', finalize, name='editor.finalize'), url(r'^finalize/$', finalize, name='editor.finalize'),
url(r'^oauth/(?P<hoster>[^/]+)/callback$', oauth_callback, name='editor.oauth.callback') url(r'^oauth/(?P<hoster>[^/]+)/callback$', oauth_callback, name='editor.oauth.callback')
] ]

View file

@ -4,36 +4,34 @@ from django.core.exceptions import PermissionDenied, SuspiciousOperation
from django.core.signing import BadSignature from django.core.signing import BadSignature
from django.http.response import Http404 from django.http.response import Http404
from django.shortcuts import get_object_or_404, render 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.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.models.package import Package
from c3nav.mapdata.packageio.write import json_encode from c3nav.mapdata.packageio.write import json_encode
from c3nav.mapdata.permissions import can_access_package 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: if name is not None:
# Edit existing feature # 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): if not can_access_package(request, feature.package):
raise PermissionDenied 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 request.method == 'POST':
if feature is not None and request.POST.get('delete') == '1': if feature is not None and request.POST.get('delete') == '1':
# Delete this feature! # Delete this feature!
if request.POST.get('delete_confirm') == '1': if request.POST.get('delete_confirm') == '1':
if not settings.DIRECT_EDITING: if not settings.DIRECT_EDITING:
title_en = feature.titles.get('en', next(iter(feature.titles.values()))) with translation.override('en'):
commit_msg = 'Deleted %s: %s' % (feature_type.title_en.lower(), title_en) commit_msg = 'Deleted %s: %s' % (model._meta.verbose_name, feature.title)
return render(request, 'editor/feature_success.html', { return render(request, 'editor/feature_success.html', {
'data': signing.dumps({ 'data': signing.dumps({
'type': 'editor.edit', 'type': 'editor.edit',
@ -41,7 +39,7 @@ def edit_feature(request, feature_type=None, name=None):
'package_name': feature.package.name, 'package_name': feature.package.name,
'commit_id': feature.package.commit_id, 'commit_id': feature.package.commit_id,
'commit_msg': commit_msg, '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 '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(): if form.is_valid():
# Update/create feature # Update/create feature
commit_type = 'Created' if feature is None else 'Updated' commit_type = 'Created' if feature is None else 'Updated'
action = 'create' if feature is None else 'edit' action = 'create' if feature is None else 'edit'
feature = form.instance feature = form.instance
feature.feature_type = feature_type.name
feature.titles = {} if form.titles is not None:
for language, title in form.titles.items(): feature.titles = {}
if title: for language, title in form.titles.items():
feature.titles[language] = title if title:
feature.titles[language] = title
if not settings.DIRECT_EDITING: if not settings.DIRECT_EDITING:
content = json_encode(feature.tofile()) content = json_encode(feature.tofile())
title_en = feature.titles.get('en', next(iter(feature.titles.values()))) with translation.override('en'):
commit_msg = '%s %s: %s' % (commit_type, feature_type.title_en.lower(), title_en) commit_msg = '%s %s: %s' % (commit_type, model._meta.verbose_name, feature.title)
return render(request, 'editor/feature_success.html', { return render(request, 'editor/feature_success.html', {
'data': signing.dumps({ 'data': signing.dumps({
'type': 'editor.edit', 'type': 'editor.edit',
@ -77,7 +76,7 @@ def edit_feature(request, feature_type=None, name=None):
'package_name': feature.package.name, 'package_name': feature.package.name,
'commit_id': feature.package.commit_id, 'commit_id': feature.package.commit_id,
'commit_msg': commit_msg, 'commit_msg': commit_msg,
'file_path': feature.tofilename(), 'file_path': feature.get_filename(),
'content': content, 'content': content,
}) })
}) })
@ -86,7 +85,7 @@ def edit_feature(request, feature_type=None, name=None):
return render(request, 'editor/feature_success.html', {}) return render(request, 'editor/feature_success.html', {})
else: 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', { return render(request, 'editor/feature.html', {
'form': form, 'form': form,

View file

@ -23,9 +23,15 @@ class Feature(MapdataModel):
level = models.ForeignKey('mapdata.Level', on_delete=models.CASCADE, verbose_name=_('level')) level = models.ForeignKey('mapdata.Level', on_delete=models.CASCADE, verbose_name=_('level'))
geometry = GeometryField() geometry = GeometryField()
EditorForm = None
class Meta: class Meta:
abstract = True abstract = True
@property
def title(self):
return self.name
@classmethod @classmethod
def fromfile(cls, data, file_path): def fromfile(cls, data, file_path):
kwargs = super().fromfile(data, file_path) kwargs = super().fromfile(data, file_path)

View file

@ -160,6 +160,7 @@ class ReaderItem:
# Change name references to the referenced object # Change name references to the referenced object
for name, model in self.relations.items(): for name, model in self.relations.items():
if name in self.data: if name in self.data:
print(name, self.data[name])
self.data[name] = self.reader.saved_items[model][self.data[name]].obj 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) obj, created = self.model.objects.update_or_create(name=self.data['name'], defaults=self.data)