écraser avec un import pour les blocs de champs (#60722) #1213
|
@ -257,6 +257,55 @@ def test_block_delete(pub):
|
|||
assert 'This block is still used' in resp
|
||||
|
||||
|
||||
def test_block_export_overwrite(pub):
|
||||
create_superuser(pub)
|
||||
BlockDef.wipe()
|
||||
block = BlockDef()
|
||||
block.name = 'foobar'
|
||||
block.fields = [fields.StringField(id='123', required=True, label='Test')]
|
||||
block.store()
|
||||
|
||||
app = login(get_app(pub))
|
||||
resp = app.get('/backoffice/forms/blocks/%s/' % block.id)
|
||||
resp = resp.click(href=re.compile('^export$'))
|
||||
xml_export = resp.text
|
||||
|
||||
block.slug = 'new-slug'
|
||||
block.name = 'New foobar'
|
||||
block.fields = [fields.StringField(id='123', required=True, label='Test bebore overwrite')]
|
||||
block.store()
|
||||
|
||||
resp = app.get('/backoffice/forms/blocks/%s/' % block.id)
|
||||
resp = resp.click('Overwrite')
|
||||
resp = resp.form.submit('cancel').follow()
|
||||
resp = resp.click('Overwrite')
|
||||
resp = resp.form.submit()
|
||||
assert 'There were errors processing your form.' in resp
|
||||
|
||||
resp.form['file'] = Upload('block', xml_export.encode('utf-8'))
|
||||
resp = resp.form.submit()
|
||||
resp = resp.follow()
|
||||
assert BlockDef.count() == 1
|
||||
|
||||
block.refresh_from_storage()
|
||||
assert block.fields[0].label == 'Test'
|
||||
assert block.name == 'foobar'
|
||||
assert block.slug == 'new-slug' # not overwritten
|
||||
|
||||
# unknown reference
|
||||
block.fields = [fields.StringField(id='1', data_source={'type': 'foobar'})]
|
||||
block.store()
|
||||
resp = app.get('/backoffice/forms/blocks/%s/' % block.id)
|
||||
resp = resp.click(href=re.compile('^export$'))
|
||||
xml_export = resp.text
|
||||
resp = app.get('/backoffice/forms/blocks/%s/' % block.id)
|
||||
resp = resp.click('Overwrite')
|
||||
resp.form['file'] = Upload('block', xml_export.encode('utf-8'))
|
||||
resp = resp.form.submit()
|
||||
assert 'Invalid File (Unknown referenced objects)' in resp
|
||||
assert '<ul><li>Unknown datasources: foobar</li></ul>' in resp
|
||||
|
||||
|
||||
def test_block_edit_duplicate_delete_field(pub):
|
||||
create_superuser(pub)
|
||||
BlockDef.wipe()
|
||||
|
@ -453,14 +502,14 @@ def test_block_edit_field_warnings(pub):
|
|||
blockdef.store()
|
||||
resp = app.get('/backoffice/forms/blocks/%s/' % blockdef.id)
|
||||
assert 'more than 30 fields' not in resp.text
|
||||
assert '<div id="new-field"><h3>New Field</h3>' in resp.text
|
||||
assert resp.pyquery('#new-field')
|
||||
assert resp.pyquery('#fields-list a[title="Duplicate"]').length
|
||||
|
||||
blockdef.fields.extend([fields.StringField(id='%d' % i, label='field %d' % i) for i in range(21, 51)])
|
||||
blockdef.store()
|
||||
resp = app.get('/backoffice/forms/blocks/%s/' % blockdef.id)
|
||||
assert 'This block of fields contains 60 fields.' in resp.text
|
||||
assert '<div id="new-field"><h3>New Field</h3>' not in resp.text
|
||||
assert not resp.pyquery('#new-field')
|
||||
assert not resp.pyquery('#fields-list a[title="Duplicate"]').length
|
||||
|
||||
|
||||
|
|
|
@ -1035,7 +1035,7 @@ def test_card_edit_field_warnings(pub):
|
|||
resp = app.get('/backoffice/cards/%s/fields/' % carddef.id)
|
||||
assert 'more than 200 fields' not in resp.text
|
||||
assert 'first field should be of type "page"' in resp.text
|
||||
assert '<div id="new-field"><h3>New Field</h3>' in resp.text
|
||||
assert resp.pyquery('#new-field')
|
||||
|
||||
carddef.fields.extend([fields.StringField(id='%d' % i, label='field %d' % i) for i in range(10, 210)])
|
||||
carddef.store()
|
||||
|
@ -1049,7 +1049,7 @@ def test_card_edit_field_warnings(pub):
|
|||
resp = app.get('/backoffice/cards/%s/fields/' % carddef.id)
|
||||
assert 'This card model contains 410 fields.' in resp.text
|
||||
assert 'first field should be of type "page"' in resp.text
|
||||
assert '<div id="new-field"><h3>New Field</h3>' not in resp.text
|
||||
assert not resp.pyquery('#new-field')
|
||||
assert '>Duplicate<' not in resp.text
|
||||
|
||||
|
||||
|
|
|
@ -3600,7 +3600,7 @@ def test_form_edit_field_warnings(pub):
|
|||
resp = app.get('/backoffice/forms/%s/fields/' % formdef.id)
|
||||
assert 'more than 200 fields' not in resp.text
|
||||
assert 'first field should be of type "page"' in resp.text
|
||||
assert '<div id="new-field"><h3>New Field</h3>' in resp.text
|
||||
assert resp.pyquery('#new-field')
|
||||
|
||||
formdef.fields.extend([fields.StringField(id='%d' % i, label='field %d' % i) for i in range(10, 210)])
|
||||
formdef.store()
|
||||
|
@ -3615,7 +3615,7 @@ def test_form_edit_field_warnings(pub):
|
|||
assert 'This form contains 410 fields.' in resp.text
|
||||
assert 'no new fields can be added.' in resp.text
|
||||
assert 'first field should be of type "page"' in resp.text
|
||||
assert '<div id="new-field"><h3>New Field</h3>' not in resp.text
|
||||
assert not resp.pyquery('#new-field')
|
||||
assert '>Duplicate<' not in resp.text
|
||||
assert resp.pyquery('aside .errornotice')
|
||||
assert not resp.pyquery('aside form[action=new]')
|
||||
|
@ -3628,7 +3628,7 @@ def test_form_edit_field_warnings(pub):
|
|||
|
||||
resp = app.get('/backoffice/forms/%s/fields/' % formdef.id)
|
||||
assert 'no new fields should be added.' in resp.text
|
||||
assert '<div id="new-field"><h3>New Field</h3>' in resp.text
|
||||
assert resp.pyquery('#new-field')
|
||||
assert '>Duplicate<' in resp.text
|
||||
assert not resp.pyquery('aside .errornotice')
|
||||
assert resp.pyquery('aside form[action=new]')
|
||||
|
|
|
@ -54,6 +54,7 @@ class BlockDirectory(FieldsDirectory):
|
|||
'inspect',
|
||||
'duplicate',
|
||||
('history', 'snapshots_dir'),
|
||||
'overwrite',
|
||||
]
|
||||
field_def_page_class = BlockFieldDefPage
|
||||
blacklisted_types = ['page', 'table', 'table-select', 'tablerows', 'ranked-items', 'blocks', 'computed']
|
||||
|
@ -106,6 +107,14 @@ class BlockDirectory(FieldsDirectory):
|
|||
r = TemplateIO(html=True)
|
||||
r += htmltext('<div id="appbar">')
|
||||
r += htmltext('<h2>%s</h2>') % self.objectdef.name
|
||||
r += htmltext('<span class="actions">')
|
||||
r += htmltext('<a class="extra-actions-menu-opener"></a>')
|
||||
r += htmltext('<ul class="extra-actions-menu">')
|
||||
|
||||
r += htmltext('<li><a href="export">%s</a></li>') % _('Export')
|
||||
r += htmltext('<li><a href="delete" rel="popup">%s</a></li>') % _('Delete')
|
||||
r += htmltext('</ul>')
|
||||
r += htmltext('<a href="settings" rel="popup" role="button">%s</a>') % _('Settings')
|
||||
r += htmltext('</span>')
|
||||
r += htmltext('</div>')
|
||||
r += utils.last_modification_block(obj=self.objectdef)
|
||||
r += get_session().display_message()
|
||||
|
@ -132,17 +141,25 @@ class BlockDirectory(FieldsDirectory):
|
|||
|
||||
def get_new_field_form_sidebar(self, page_id):
|
||||
r = TemplateIO(html=True)
|
||||
r += htmltext('<ul id="sidebar-actions">')
|
||||
r += htmltext('<li><a href="delete" rel="popup">%s</a></li>') % _('Delete')
|
||||
r += htmltext('<li><a href="duplicate" rel="popup">%s</a></li>') % _('Duplicate')
|
||||
r += htmltext('<li><a href="export">%s</a></li>') % _('Export')
|
||||
if get_publisher().snapshot_class:
|
||||
r += htmltext('<li><a rel="popup" href="history/save">%s</a></li>') % _('Save snapshot')
|
||||
r += htmltext('<li><a href="history/">%s</a></li>') % _('History')
|
||||
r += htmltext('<li><a href="inspect">%s</a></li>') % _('Inspector')
|
||||
r += htmltext('<li><a href="settings" rel="popup">%s</a></li>') % _('Settings')
|
||||
r += htmltext('</ul>')
|
||||
r += super().get_new_field_form_sidebar(page_id=page_id)
|
||||
r += htmltext('<h3>%s</h3>') % _('Actions')
|
||||
r += htmltext('<ul class="sidebar--buttons">')
|
||||
r += htmltext('<li><a class="button button-paragraph" href="duplicate" rel="popup">%s</a>') % _(
|
||||
fpeters
commented
Et les autres actions dans la barre latérale. Et les autres actions dans la barre latérale.
|
||||
'Duplicate'
|
||||
)
|
||||
if get_publisher().snapshot_class:
|
||||
r += htmltext('<li><a class="button button-paragraph" href="history/save">%s</a>') % _(
|
||||
'Save snapshot'
|
||||
)
|
||||
r += htmltext('<li><a class="button button-paragraph" rel="popup" href="overwrite">%s</a>') % _(
|
||||
'Overwrite'
|
||||
fpeters
commented
Dont la nouvelle action pour écraser. Dont la nouvelle action pour écraser.
|
||||
)
|
||||
r += htmltext('</ul>')
|
||||
r += htmltext('<h3>%s</h3>') % _('Navigation')
|
||||
r += htmltext('<ul class="sidebar--buttons">')
|
||||
r += htmltext('<li><a class="button button-paragraph" href="history/">%s</a></li>') % _('History')
|
||||
r += htmltext('<li><a class="button button-paragraph" href="inspect">%s</a></li>') % _('Inspector')
|
||||
r += htmltext('</ul>')
|
||||
return r.getvalue()
|
||||
|
||||
def delete(self):
|
||||
|
@ -209,6 +226,41 @@ class BlockDirectory(FieldsDirectory):
|
|||
content_type='application/x-wcs-form',
|
||||
)
|
||||
|
||||
def overwrite(self):
|
||||
form = Form(enctype='multipart/form-data')
|
||||
form.widgets.append(
|
||||
HtmlWidget(
|
||||
'<div class="warningnotice"><p>%s</p></div>'
|
||||
% _('Field data will be lost if overwriting with an incompatible block.')
|
||||
fpeters
commented
Un peu du mal à trouver une phrase adéquate ici; en pratique on utiliser des uuid depuis le début pour les champs des blocs, et le stockage se fait dans des colonnes jsonb, donc un revert de l'écrasement rendrait accès aux données. Mais c'est quand même bien de décourager. Un peu du mal à trouver une phrase adéquate ici; en pratique on utiliser des uuid depuis le début pour les champs des blocs, et le stockage se fait dans des colonnes jsonb, donc un revert de l'écrasement rendrait accès aux données. Mais c'est quand même bien de décourager.
|
||||
)
|
||||
)
|
||||
form.add(FileWidget, 'file', title=_('File'), required=True)
|
||||
form.add_submit('submit', _('Submit'))
|
||||
form.add_submit('cancel', _('Cancel'))
|
||||
if form.get_widget('cancel').parse():
|
||||
return redirect('.')
|
||||
|
||||
if form.is_submitted() and not form.has_errors():
|
||||
try:
|
||||
return self.overwrite_submit(form)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
get_response().breadcrumb.append(('overwrite', _('Overwrite')))
|
||||
get_response().set_title(title=_('Overwrite'))
|
||||
r = TemplateIO(html=True)
|
||||
r += htmltext('<h2>%s</h2>') % _('Overwrite')
|
||||
r += form.render()
|
||||
return r.getvalue()
|
||||
|
||||
def overwrite_submit(self, form):
|
||||
blockdef = BlocksDirectory.import_blockdef(form)
|
||||
self.objectdef.name = blockdef.name
|
||||
self.objectdef.digest_template = blockdef.digest_template
|
||||
self.objectdef.fields = blockdef.fields
|
||||
self.objectdef.store(comment=_('Overwritten'))
|
||||
return redirect('.')
|
||||
|
||||
def settings(self):
|
||||
get_response().breadcrumb.append(('settings', _('Settings')))
|
||||
form = Form()
|
||||
|
@ -409,7 +461,8 @@ class BlocksDirectory(Directory):
|
|||
r += form.render()
|
||||
return r.getvalue()
|
||||
|
||||
def import_submit(self, form):
|
||||
@classmethod
|
||||
def import_blockdef(cls, form):
|
||||
fp = form.get_widget('file').parse().fp
|
||||
|
||||
error, reason = False, None
|
||||
|
@ -433,6 +486,10 @@ class BlocksDirectory(Directory):
|
|||
form.set_error('file', msg)
|
||||
raise ValueError()
|
||||
|
||||
return blockdef
|
||||
|
||||
def import_submit(self, form):
|
||||
blockdef = self.import_blockdef(form)
|
||||
initial_blockdef_name = blockdef.name
|
||||
blockdef_names = [x.name for x in BlockDef.select()]
|
||||
copy_no = 1
|
||||
|
|
|
@ -527,8 +527,8 @@ class FieldsDirectory(Directory):
|
|||
)
|
||||
return r.getvalue()
|
||||
|
||||
r += htmltext('<div id="new-field">')
|
||||
r += htmltext('<h3>%s</h3>') % _('New Field')
|
||||
r += htmltext('<div id="new-field">')
|
||||
get_request().form = None # ignore the eventual ?page=x
|
||||
form = self.get_new_field_form(page_id)
|
||||
r += form.render()
|
||||
|
|
|
@ -222,13 +222,8 @@ div#new-action, div#new-trigger, div#new-field {
|
|||
}
|
||||
}
|
||||
|
||||
div#new-field {
|
||||
margin: 2em 0 4px 0;
|
||||
padding: 5px 5px;
|
||||
fpeters
commented
Petit ajustement stylistique pour que le titre "Nouveau champ" apparaisse comme les titres Actions/Navigation/Applications. Petit ajustement stylistique pour que le titre "Nouveau champ" apparaisse comme les titres Actions/Navigation/Applications.
|
||||
}
|
||||
|
||||
div#new-field form {
|
||||
margin-bottom: 2em;
|
||||
form#import-fields {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
aside#sidebar div.news h3,
|
||||
|
|
Loading…
Reference in New Issue
Premier commit pour reprendre la barre latérale, en suivant ce qui a été fait pour l'écran d'un formulaire, quelques actions dans le menu kebab.