editor forms for new feature models
This commit is contained in:
parent
4b74eb0cf5
commit
ecc7c4f829
6 changed files with 86 additions and 58 deletions
|
@ -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):
|
||||||
|
|
|
@ -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');
|
||||||
|
|
|
@ -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')
|
||||||
]
|
]
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue