restore deleted objects
This commit is contained in:
parent
9ac3eeeca0
commit
353d9f9ea0
4 changed files with 26 additions and 60 deletions
|
@ -146,7 +146,7 @@ class ChangedObject(models.Model):
|
||||||
setattr(instance, field.name, field.to_python(value))
|
setattr(instance, field.name, field.to_python(value))
|
||||||
elif field.many_to_one or field.one_to_one:
|
elif field.many_to_one or field.one_to_one:
|
||||||
if is_created_pk(value):
|
if is_created_pk(value):
|
||||||
obj = self.changeset.get_created_object(field.related_model, value)
|
obj = self.changeset.get_created_object(field.related_model, value, allow_deleted=True)
|
||||||
setattr(instance, field.get_cache_name(), obj)
|
setattr(instance, field.get_cache_name(), obj)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
|
@ -258,7 +258,7 @@ class ChangedObject(models.Model):
|
||||||
return (self.updated_fields or self._m2m_added_cache or self._m2m_removed_cache or self.is_created or
|
return (self.updated_fields or self._m2m_added_cache or self._m2m_removed_cache or self.is_created or
|
||||||
(not self.is_created and self.deleted))
|
(not self.is_created and self.deleted))
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, standalone=False, **kwargs):
|
||||||
if self.changeset.proposed is not None or self.changeset.applied is not None:
|
if self.changeset.proposed is not None or self.changeset.applied is not None:
|
||||||
raise TypeError('can not add change object to uneditable changeset.')
|
raise TypeError('can not add change object to uneditable changeset.')
|
||||||
self.m2m_added = {name: tuple(values) for name, values in self._m2m_added_cache.items()}
|
self.m2m_added = {name: tuple(values) for name, values in self._m2m_added_cache.items()}
|
||||||
|
@ -266,12 +266,12 @@ class ChangedObject(models.Model):
|
||||||
if not self.does_something:
|
if not self.does_something:
|
||||||
self.stale = True
|
self.stale = True
|
||||||
if not self.stale:
|
if not self.stale:
|
||||||
if self.changeset.pk is None:
|
if not standalone and self.changeset.pk is None:
|
||||||
self.changeset.save()
|
self.changeset.save()
|
||||||
self.changeset = self.changeset
|
self.changeset = self.changeset
|
||||||
else:
|
else:
|
||||||
self.existing_object_pk = None
|
self.existing_object_pk = None
|
||||||
if not self.changeset.fill_changes_cache():
|
if not standalone and not self.changeset.fill_changes_cache():
|
||||||
self.update_changeset_cache()
|
self.update_changeset_cache()
|
||||||
if not self.stale or self.pk is not None:
|
if not self.stale or self.pk is not None:
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
|
@ -92,10 +92,6 @@ a.list-group-item .badge {
|
||||||
.itemtable td:first-child {
|
.itemtable td:first-child {
|
||||||
padding-left:0;
|
padding-left:0;
|
||||||
}
|
}
|
||||||
.itemtable td:last-child {
|
|
||||||
padding-right:0;
|
|
||||||
text-align:right;
|
|
||||||
}
|
|
||||||
.itemtable tr.highlight td {
|
.itemtable tr.highlight td {
|
||||||
background-color:#FFFFDD;
|
background-color:#FFFFDD;
|
||||||
}
|
}
|
||||||
|
@ -157,34 +153,6 @@ form button.invisiblesubmit {
|
||||||
width:23px;
|
width:23px;
|
||||||
padding-right:0;
|
padding-right:0;
|
||||||
}
|
}
|
||||||
.change-group tr td:last-child {
|
|
||||||
white-space:nowrap;
|
|
||||||
text-align:right;
|
|
||||||
}
|
|
||||||
.change-group tr td .glyphicon {
|
|
||||||
top:2px;
|
|
||||||
}
|
|
||||||
.change-group tr td .tooltip {
|
|
||||||
font-size:14px;
|
|
||||||
margin-top: -1px;
|
|
||||||
}
|
|
||||||
.change-group tr td:last-child .btn {
|
|
||||||
margin-top: -6px;
|
|
||||||
margin-bottom: -2px;
|
|
||||||
margin-left: 5px;
|
|
||||||
margin-right: -5px;
|
|
||||||
}
|
|
||||||
.change-group .glyphicon-share-alt {
|
|
||||||
transform: scale(-1, 1);
|
|
||||||
-webkit-transform: scale(-1, 1);
|
|
||||||
}
|
|
||||||
.change-group tr td:last-child>i:last-child {
|
|
||||||
margin-right: -2px;
|
|
||||||
}
|
|
||||||
.change-group tr td:last-child>i {
|
|
||||||
margin-left: 7px;
|
|
||||||
margin-right: -2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Styles inside leaflet */
|
/* Styles inside leaflet */
|
||||||
.leaflet-container {
|
.leaflet-container {
|
||||||
|
|
|
@ -24,11 +24,15 @@
|
||||||
<table class="table table-condensed table-h-bordered change-group">
|
<table class="table table-condensed table-h-bordered change-group">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th colspan="3">
|
<th colspan="2">
|
||||||
{% if obj.edit_url %}
|
{% if obj.edit_url %}
|
||||||
<a class="btn btn-default btn-xs pull-right" data-force-next-zoom href="{{ obj.edit_url }}">
|
<a class="btn btn-default btn-xs pull-right" data-force-next-zoom href="{{ obj.edit_url }}">
|
||||||
{% trans 'Edit' %}
|
{% trans 'Edit' %}
|
||||||
</a>
|
</a>
|
||||||
|
{% elif obj.deleted and can_edit %}
|
||||||
|
<button type="submit" name="restore" value="{{ obj.pk }}" class="btn btn-warning btn-xs pull-right">
|
||||||
|
{% trans 'Restore' %}
|
||||||
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if obj.title %}
|
{% if obj.title %}
|
||||||
{{ obj.title }} <small>({{ obj.desc }})</small>
|
{{ obj.title }} <small>({{ obj.desc }})</small>
|
||||||
|
@ -42,22 +46,10 @@
|
||||||
{% for change in obj.changes %}
|
{% for change in obj.changes %}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="text-{{ change.class }}"><i class="glyphicon glyphicon-{{ change.icon }}"></i></td>
|
<td class="text-{{ change.class }}"><i class="glyphicon glyphicon-{{ change.icon }}"></i></td>
|
||||||
<td{% if change.discarded %} class="text-muted"{% endif %}>
|
<td{% if obj.deleted and change.icon != 'minus' %} class="text-muted"{% endif %}>
|
||||||
{% if change.value %}<u>{% else %}<em>{% endif %}{{ change.title }}{% if change.value %}</u>:{% else %}</em>{% endif %}
|
{% if change.value %}<u>{% else %}<em>{% endif %}{{ change.title }}{% if change.value %}</u>:{% else %}</em>{% endif %}
|
||||||
{{ change.value }}
|
{{ change.value }}
|
||||||
</td>
|
</td>
|
||||||
<td class="text-muted">
|
|
||||||
{% if change.apply_problem %}
|
|
||||||
<i class="glyphicon glyphicon-alert text-danger" data-toggle="tooltip" data-placement="left" title="{{ change.apply_problem }}"></i>
|
|
||||||
{% elif change.has_no_effect %}
|
|
||||||
<i class="glyphicon glyphicon-ok text-success" data-toggle="tooltip" data-placement="left" title="{% trans 'This change has no effect. You can restore it.' %}"></i>
|
|
||||||
{% endif %}
|
|
||||||
{% if change.can_restore %}
|
|
||||||
<button type="submit" name="restore" value="{{ change.pk }}" class="btn btn-xs btn-default" data-toggle="tooltip" data-placement="left" title="{% trans 'Restore original state' %}">
|
|
||||||
<i class="glyphicon glyphicon-share-alt"></i>
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -68,7 +60,7 @@
|
||||||
<button type="submit" class="btn btn-danger" name="delete" value="1">{% trans 'Delete' %}</button>
|
<button type="submit" class="btn btn-danger" name="delete" value="1">{% trans 'Delete' %}</button>
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
{% if can_edit %}
|
{% if can_edit %}
|
||||||
<a href="{% url 'editor.changesets.edit' pk=pk %}" class="btn btn-default">{% trans 'Edit' %}</a>
|
<a href="{% url 'editor.changesets.edit' pk=changeset.pk %}" class="btn btn-default">{% trans 'Edit' %}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<button type="submit" class="btn btn-primary" name="propose" value="1">{% trans 'Propose' %}</button>
|
<button type="submit" class="btn btn-primary" name="propose" value="1">{% trans 'Propose' %}</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -24,15 +24,20 @@ def changeset_detail(request, pk):
|
||||||
can_edit = False
|
can_edit = False
|
||||||
changeset = get_object_or_404(ChangeSet.qs_for_request(request), pk=pk)
|
changeset = get_object_or_404(ChangeSet.qs_for_request(request), pk=pk)
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST' and can_edit:
|
||||||
restore = request.POST.get('restore')
|
restore = request.POST.get('restore')
|
||||||
if restore and restore.isdigit():
|
if restore and restore.isdigit():
|
||||||
change = changeset.changes.filter(pk=restore).first()
|
try:
|
||||||
if change is not None and change.can_restore:
|
changed_object = changeset.changed_objects_set.get(pk=restore)
|
||||||
if request.POST.get('restore_confirm') != '1':
|
except:
|
||||||
return render(request, 'editor/changeset_restore_confirm.html', {'pk': change.pk})
|
pass
|
||||||
change.restore(request.user if request.user.is_authenticated else None)
|
else:
|
||||||
messages.success(request, _('Original state has been restored!'))
|
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('delete') == '1':
|
elif request.POST.get('delete') == '1':
|
||||||
if request.POST.get('delete_confirm') == '1':
|
if request.POST.get('delete_confirm') == '1':
|
||||||
|
@ -78,7 +83,7 @@ def changeset_detail(request, pk):
|
||||||
|
|
||||||
obj_desc = _('%(model)s #%(id)s') % {'model': obj.__class__._meta.verbose_name, 'id': pk}
|
obj_desc = _('%(model)s #%(id)s') % {'model': obj.__class__._meta.verbose_name, 'id': pk}
|
||||||
if is_created_pk(pk):
|
if is_created_pk(pk):
|
||||||
obj_still_exists = pk in changeset.created_objects[obj.__class__]
|
obj_still_exists = pk in changeset.created_objects.get(obj.__class__, ())
|
||||||
else:
|
else:
|
||||||
obj_still_exists = pk not in changeset.deleted_existing.get(obj.__class__, ())
|
obj_still_exists = pk not in changeset.deleted_existing.get(obj.__class__, ())
|
||||||
|
|
||||||
|
@ -96,10 +101,12 @@ def changeset_detail(request, pk):
|
||||||
changed_object_data = {
|
changed_object_data = {
|
||||||
'model': obj.__class__,
|
'model': obj.__class__,
|
||||||
'model_title': obj.__class__._meta.verbose_name,
|
'model_title': obj.__class__._meta.verbose_name,
|
||||||
|
'pk': changed_object.pk,
|
||||||
'desc': obj_desc,
|
'desc': obj_desc,
|
||||||
'title': obj.title if getattr(obj, 'titles', None) else None,
|
'title': obj.title if getattr(obj, 'titles', None) else None,
|
||||||
'changes': changes,
|
'changes': changes,
|
||||||
'edit_url': edit_url,
|
'edit_url': edit_url,
|
||||||
|
'deleted': changed_object.deleted,
|
||||||
'order': (changed_object.deleted and changed_object.is_created, not changed_object.is_created),
|
'order': (changed_object.deleted and changed_object.is_created, not changed_object.is_created),
|
||||||
}
|
}
|
||||||
changed_objects_data.append(changed_object_data)
|
changed_objects_data.append(changed_object_data)
|
||||||
|
@ -210,7 +217,6 @@ def changeset_detail(request, pk):
|
||||||
created = _('created at %(datetime)s') % {'datetime': date_format(changeset.created, 'DATETIME_FORMAT')}
|
created = _('created at %(datetime)s') % {'datetime': date_format(changeset.created, 'DATETIME_FORMAT')}
|
||||||
|
|
||||||
ctx = {
|
ctx = {
|
||||||
'pk': changeset.pk,
|
|
||||||
'changeset': changeset,
|
'changeset': changeset,
|
||||||
'created': created,
|
'created': created,
|
||||||
'can_edit': can_edit,
|
'can_edit': can_edit,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue