backoffice: add restore/export snapshots (#4960)
This commit is contained in:
parent
b415ed66de
commit
c5a9ae2094
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)})
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue