backoffice: add restore/export snapshots (#4960)

This commit is contained in:
Frédéric Péters 2020-08-11 15:16:05 +02:00
parent b415ed66de
commit c5a9ae2094
4 changed files with 115 additions and 14 deletions

View File

@ -22,6 +22,20 @@ def pub(request, emails):
return pub
@pytest.fixture
def formdef_with_history(pub):
formdef = FormDef()
formdef.name = 'testform'
formdef.fields = []
formdef.store()
for i in range(5):
formdef.name = 'testform %s' % i
formdef.store()
return formdef
def teardown_module(module):
clean_temporary_pub()
@ -146,19 +160,49 @@ def test_form_snapshot_comments(pub):
assert pub.snapshot_class.select(order_by='-timestamp')[0].comment == 'New field "foobar"'
def test_form_snapshot_history(pub):
formdef = FormDef()
formdef.name = 'testform'
formdef.fields = []
formdef.store()
for i in range(5):
formdef.name = 'testform %s' % i
formdef.store()
def test_form_snapshot_history(pub, formdef_with_history):
create_superuser(pub)
create_role()
app = login(get_app(pub))
resp = app.get('/backoffice/forms/%s/history/' % formdef.id)
resp = app.get('/backoffice/forms/%s/history/' % formdef_with_history.id)
assert [x.attrib['class'] for x in resp.pyquery.find('ul.snapshots-list li')] == [
'new-day', 'collapsed', 'collapsed', 'collapsed', 'collapsed', 'collapsed']
def test_form_snapshot_export(pub, formdef_with_history):
create_superuser(pub)
create_role()
app = login(get_app(pub))
resp = app.get('/backoffice/forms/%s/history/' % formdef_with_history.id)
snapshot = pub.snapshot_class.select_object_history(formdef_with_history)[2]
resp_export = resp.click(href='%s/export' % snapshot.id)
assert resp_export.content_type == 'application/x-wcs-snapshot'
assert '>testform 2<' in resp_export.text
def test_form_snapshot_restore(pub, formdef_with_history):
create_superuser(pub)
create_role()
app = login(get_app(pub))
# restore as new
resp = app.get('/backoffice/forms/%s/history/' % formdef_with_history.id)
snapshot = pub.snapshot_class.select_object_history(formdef_with_history)[2]
resp = resp.click(href='%s/restore' % snapshot.id)
assert resp.form['action'].value == 'as-new'
resp = resp.form.submit('submit')
assert FormDef.count() == 2
formdef = FormDef.get(resp.location.split('/')[-2])
assert formdef.url_name != formdef_with_history.url_name
# restore over
resp = app.get('/backoffice/forms/%s/history/' % formdef_with_history.id)
snapshot = pub.snapshot_class.select_object_history(formdef_with_history)[2]
resp = resp.click(href='%s/restore' % snapshot.id)
resp.form['action'].value = 'overwrite'
resp = resp.form.submit('submit')
assert FormDef.count() == 2
formdef = FormDef.get(resp.location.split('/')[-2])
assert formdef.id == formdef_with_history.id
assert formdef.url_name == formdef_with_history.url_name

View File

@ -14,11 +14,13 @@
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
from quixote import get_publisher, get_response
from quixote import get_publisher, get_response, redirect
from quixote.directory import Directory
from quixote.html import TemplateIO, htmltext
from wcs.qommon import _, errors, misc, template
from wcs.qommon.backoffice.menu import html_top
from wcs.qommon.form import Form, RadiobuttonsWidget
from wcs.qommon.storage import Equal
@ -64,7 +66,7 @@ class SnapshotsDirectory(Directory):
class SnapshotDirectory(Directory):
_q_exports = ['']
_q_exports = ['', 'export', 'restore']
def __init__(self, instance, snapshot):
self.obj = instance
@ -79,3 +81,36 @@ class SnapshotDirectory(Directory):
def _q_index(self):
return ''
def export(self):
response = get_response()
response.set_content_type('application/x-wcs-snapshot')
response.set_header('content-disposition',
'attachment; filename=snapshot-%s-%s-%s.wcs' % (
self.snapshot.object_type,
self.snapshot.id,
self.snapshot.timestamp.strftime('%Y%m%d-%H%M'),
))
return '<?xml version="1.0"?>\n' + self.snapshot.serialization
def restore(self):
form = Form(enctype='multipart/form-data')
action = form.add(RadiobuttonsWidget, 'action',
options=(
('as-new', _('Restore as a new item'), 'as-new'),
('overwrite', _('Overwrite current content'), 'overwrite')),
value='as-new')
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
if form.get_submit() == 'cancel':
return redirect('..')
if form.get_submit() == 'submit':
self.snapshot.restore(as_new=bool(action.parse() == 'as-new'))
return redirect(self.snapshot.instance.get_admin_url())
get_response().breadcrumb.append(('restore', _('Restore')))
r = TemplateIO(html=True)
r += htmltext('<h2>%s</h2>') % _('Restore snapshot')
r += form.render()
return r.getvalue()

View File

@ -20,7 +20,7 @@ from django.utils.timezone import now
from quixote import get_publisher, get_session
from wcs.qommon import _
from wcs.qommon import _, misc
class UnknownUser:
@ -86,3 +86,21 @@ class Snapshot:
except KeyError:
self._user = UnknownUser()
return self._user
def restore(self, as_new=True):
instance = self.instance
if as_new:
for attr in ('id', 'url_name', 'internal_identifier', 'slug'):
setattr(instance, attr, None)
if hasattr(instance, 'disabled'):
instance.disabled = True
else:
# keep table and max field id from current object
current_object = self.get_object_class().get(instance.id)
for attr in ('max_field_id', 'table_name'):
if hasattr(current_object, attr):
setattr(instance, attr, getattr(current_object, attr))
instance.store(comment=_('Restored snapshot %(id)s (%(timestamp)s)') % {
'id': self.id,
'timestamp': misc.localstrftime(self.timestamp)})

View File

@ -22,6 +22,10 @@
</a>){% endif %}
<a href="{{snapshot.id}}/view/">{% trans "View" %}</a>
<a data-popup href="{{snapshot.id}}/restore">{% trans "Restore" %}</a>
<a href="{{snapshot.id}}/export">{% trans "Export" %}</a>
</li>
{% endfor %}
</ul>