team-3/src/c3nav/editor/views/changes.py
2017-06-29 18:36:04 +02:00

283 lines
11 KiB
Python

from itertools import chain
from operator import itemgetter
from django.conf import settings
from django.contrib import messages
from django.core.exceptions import PermissionDenied
from django.http import Http404
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from c3nav.editor.forms import ChangeSetForm
from c3nav.editor.models import ChangeSet
from c3nav.editor.utils import is_created_pk
from c3nav.editor.views.base import sidebar_view
from c3nav.mapdata.models.locations import LocationRedirect, LocationSlug
@sidebar_view
def changeset_detail(request, pk):
changeset = request.changeset
if str(pk) != str(request.changeset.pk):
changeset = get_object_or_404(ChangeSet.qs_for_request(request), pk=pk)
if not changeset.can_see(request):
raise Http404
can_edit = changeset.can_edit(request)
can_delete = changeset.can_delete(request)
if request.method == 'POST':
restore = request.POST.get('restore')
if restore and restore.isdigit():
if not can_edit:
raise PermissionDenied
try:
changed_object = changeset.changed_objects_set.get(pk=restore)
except:
pass
else:
if changed_object.deleted:
changed_object.deleted = False
changed_object.save(standalone=True)
messages.success(request, _('Object has been successfully restored.'))
return redirect(reverse('editor.changesets.detail', kwargs={'pk': changeset.pk}))
elif request.POST.get('propose') == '1':
if not request.user.is_authenticated:
messages.info(request, _('You need to log in to propose changes.'))
return redirect(reverse('editor.login')+'?redirect='+request.path)
if changeset.can_propose(request):
changeset.proposed = timezone.now()
changeset.session_id = None
changeset.save()
messages.success(request, _('You proposed your changes.'))
else:
messages.error(request, _('You cannot propose this changeset.'))
return redirect(reverse('editor.changesets.detail', kwargs={'pk': changeset.pk}))
elif request.POST.get('unpropose') == '1':
if changeset.can_unpropose(request):
changeset.proposed = None
changeset.assigned_to = None
changeset.session_id = None
changeset.save()
messages.success(request, _('You unproposed your changes.'))
else:
messages.error(request, _('You cannot unpropose this changeset.'))
return redirect(reverse('editor.changesets.detail', kwargs={'pk': changeset.pk}))
elif request.POST.get('delete') == '1':
if not changeset.can_delete(request):
raise PermissionDenied
if request.POST.get('delete_confirm') == '1':
changeset.delete()
return redirect(reverse('editor.index'))
return render(request, 'editor/delete.html', {
'model_title': ChangeSet._meta.verbose_name,
'obj_title': changeset.title,
})
changeset.fill_changes_cache(include_deleted_created=True)
objects = changeset.get_objects()
changed_objects_data = []
added_redirects = {}
removed_redirects = {}
for changed_object in changeset.changed_objects.get(LocationRedirect, {}).values():
if changed_object.is_created == changed_object.deleted:
continue
values = changed_object.updated_fields
redirect_list = (removed_redirects if changed_object.deleted else added_redirects)
redirect_list.setdefault(values['target'], []).append(values['slug'])
redirect_changed_objects = []
for pk in set(added_redirects.keys()) | set(removed_redirects.keys()):
obj = objects[LocationSlug][pk]
model = obj.__class__
try:
changeset.changed_objects[model][pk]
except KeyError:
redirect_changed_objects.append((model, {pk: changeset.get_changed_object(obj)}))
for model, changed_objects in chain(changeset.changed_objects.items(), redirect_changed_objects):
if model == LocationRedirect:
continue
for pk, changed_object in changed_objects.items():
obj = objects[model][pk]
obj_desc = _('%(model)s #%(id)s') % {'model': obj.__class__._meta.verbose_name, 'id': pk}
if is_created_pk(pk):
obj_still_exists = pk in changeset.created_objects.get(obj.__class__, ())
else:
obj_still_exists = pk not in changeset.deleted_existing.get(obj.__class__, ())
edit_url = None
if obj_still_exists and can_edit and not isinstance(obj, LocationRedirect):
reverse_kwargs = {'pk': obj.pk}
if hasattr(obj, 'level_id'):
reverse_kwargs['level'] = obj.level_id
elif hasattr(obj, 'space_id'):
reverse_kwargs['space'] = obj.space_id
edit_url = reverse('editor.' + obj.__class__._meta.default_related_name + '.edit',
kwargs=reverse_kwargs)
changes = []
changed_object_data = {
'model': obj.__class__,
'model_title': obj.__class__._meta.verbose_name,
'pk': changed_object.pk,
'desc': obj_desc,
'title': obj.title if getattr(obj, 'titles', None) else None,
'changes': changes,
'edit_url': edit_url,
'deleted': changed_object.deleted,
'order': (changed_object.deleted and changed_object.is_created, not changed_object.is_created),
}
changed_objects_data.append(changed_object_data)
form_fields = changeset.wrap_model(type(obj)).EditorForm._meta.fields
if changed_object.is_created:
changes.append({
'icon': 'plus',
'class': 'success',
'title': _('created'),
})
update_changes = []
for name, value in changed_object.updated_fields.items():
change_data = {
'icon': 'option-vertical',
'class': 'muted',
}
if name == 'geometry':
change_data.update({
'icon': 'map-marker',
'class': 'info',
'title': _('edited geometry'),
'order': (8,),
})
else:
if name.startswith('title_'):
lang = name[6:]
field_title = _('Title (%(lang)s)') % {'lang': dict(settings.LANGUAGES).get(lang, lang)}
field_value = str(value)
if field_value:
obj.titles[lang] = field_value
else:
obj.titles.pop(lang, None)
change_data.update({
'order': (4, tuple(code for code, title in settings.LANGUAGES).index(lang)),
})
else:
field = model._meta.get_field(name)
field_title = field.verbose_name
field_value = field.to_python(value)
if field.related_model is not None:
field_value = objects[field.related_model][field_value].title
order = 5
if name == 'slug':
order = 1
if name not in form_fields:
order = 0
change_data.update({
'order': (order, form_fields.index(name) if order else 1),
})
if not field_value:
change_data.update({
'title': _('remove %(field_title)s') % {'field_title': field_title},
})
else:
change_data.update({
'title': field_title,
'value': field_value,
})
update_changes.append(change_data)
changes.extend(sorted(update_changes, key=itemgetter('order')))
for m2m_mode in ('m2m_added', 'm2m_removed'):
m2m_list = getattr(changed_object, m2m_mode).items()
for name, values in sorted(m2m_list, key=lambda nv: form_fields.index(nv[0])):
field = model._meta.get_field(name)
for value in values:
changes.append({
'icon': 'chevron-right' if m2m_mode == 'm2m_added' else 'chevron-left',
'class': 'info',
'title': field.verbose_name,
'value': objects[field.related_model][value].title,
})
if isinstance(obj, LocationSlug):
for slug in added_redirects.get(obj.pk, ()):
changes.append({
'icon': 'chevron-right',
'class': 'info',
'title': _('Redirect slugs'),
'value': slug,
})
for slug in removed_redirects.get(obj.pk, ()):
changes.append({
'icon': 'chevron-left',
'class': 'info',
'title': _('Redirect slugs'),
'value': slug,
})
if changed_object.deleted:
changes.append({
'icon': 'minus',
'class': 'danger',
'title': _('deleted'),
'order': (9,),
})
changed_objects_data = sorted(changed_objects_data, key=itemgetter('order'))
ctx = {
'changeset': changeset,
'can_edit': can_edit,
'can_delete': can_delete,
'can_unpropose': changeset.can_unpropose(request),
'changed_objects': changed_objects_data,
}
return render(request, 'editor/changeset.html', ctx)
@sidebar_view
def changeset_edit(request, pk):
changeset = request.changeset
if str(pk) != str(request.changeset.pk):
changeset = get_object_or_404(ChangeSet.qs_for_request(request), pk=pk)
if not changeset.can_edit(request):
raise PermissionDenied
if request.method == 'POST':
form = ChangeSetForm(instance=changeset, data=request.POST)
if form.is_valid():
form.save()
return redirect(reverse('editor.changesets.detail', kwargs={'pk': changeset.pk}))
else:
form = ChangeSetForm(instance=changeset)
return render(request, 'editor/changeset_edit.html', {
'changeset': changeset,
'form': form,
})