def test_render_store_layout_configuration_in_state(self, browser): config = IPageConfiguration(self.content) state = { "default": [ {"cols": [{"blocks": []}]}, {"cols": [{"blocks": []}]} ] } config.store(state) configdata = {'somekey': 'somevalue'} payload = json.dumps({'name': 'default', 'layoutindex': '1', 'config': configdata}) self.portal.REQUEST.set('data', payload) response = json.loads(self.content.restrictedTraverse( '@@sl-ajax-reload-layout-view')()) browser.open_html(response['content']) self.assertDictEqual( configdata, json.loads( browser.css('.sl-layout-content').first.attrib['data-config'])) self.assertTrue(browser.css('.sl-layout-content.somevalue'), 'Expect one item')
def update_page_state_on_block_remove(block, event): if event.newParent is None: # Be sure it's not cut/paste block_uid = IUUID(block) parent = aq_parent(aq_inner(block)) # Do nothing if the event wasn't fired by the block's parent. # This happens when an ancestor is deleted, e.g. the Plone site itself. if parent is not event.oldParent: return config = IPageConfiguration(parent) page_state = config.load() for container in page_state.values(): for layout in container: for column in layout['cols']: cache_amound_blocks = len(column['blocks']) column['blocks'] = [item for item in column['blocks'] if item['uid'] != block_uid] if cache_amound_blocks != len(column['blocks']): # Block has been removed break config.store(page_state)
def migrate_prepare_page_state(self): config = IPageConfiguration(self.new) page_config = { "default": [{ "cols": [ { "blocks": [] }, { "blocks": [] }, { "blocks": [] }, { "blocks": [] }, ] }, { "cols": [{ "blocks": [] }, { "blocks": [] }] }] } config.store(page_config)
def update_page_state_on_copy_paste_block(block, event): """Update the uid of the new created block in the page state. block: new block event.original: origin of the copy event - usually the simplelayout page""" # Only update page state, if the original object is a Simplelayout page. if not ISimplelayout.providedBy(event.original): return # The event is triggered recursively - we need to check if the block is # actually part of the original page if event.original.get(block.id) is None: return origin_block_uid = IUUID(event.original.get(block.id)) page_config = IPageConfiguration(block.aq_parent) page_state = unwrap_persistence(page_config.load()) new_block_uid = IUUID(block) new_page_state = json.loads( json.dumps(page_state).replace(origin_block_uid, new_block_uid)) # We should not update object positions here, because: # 1. "Ordered folder" makes sure that the order is the same as before # when copy / pasting. # 2. Updating positions does not work here, because our objects are not # acquisition wrappable yet (since not yet pasted) and the updating # mechanism will trigger events (such as plone.app.referenceablebehavior), # which require acquisition. page_config.store(new_page_state, update_positions=False)
def _get_uids(self): page_conf = IPageConfiguration(self.context) # look for blocks in default slot first ... state_string = json.dumps(unwrap_persistence( page_conf.load().get('default', {}))) # ... then look in all slots. state_string += json.dumps(unwrap_persistence( page_conf.load())) return re.findall(r'\"uid\"\: \"(.+?)\"', state_string)
def move_sl_block_into_slot(old_page, new_page, block, slot_name): page_configuration = IPageConfiguration(new_page) page_state = page_configuration.load() # initiate layout if it is in its initial state if slot_name == 'default' and page_state == page_configuration._default_page_config(): # setup different layouts if ISimplelayoutTwoColumnView.providedBy(old_page): # two columns page_state[slot_name] = [ {'cols': [ {'blocks': []}, {'blocks': []} ]} ] elif ISimplelayoutTwoColumnOneOnTopView.providedBy(old_page): # two columns and a top row page_state[slot_name] = [ {'cols': [{'blocks': []}]}, {'cols': [ {'blocks': []}, {'blocks': []} ]} ] if slot_name not in page_state: # normal single column layout page_state[slot_name] = [{'cols': [{'blocks': []}]}] if slot_name == 'default': slot = page_state['default'] # two column layout if ISimplelayoutTwoColumnView.providedBy(old_page): if ISlotA.providedBy(block): # left column slot[0]['cols'][0]['blocks'].append({'uid': IUUID(block)}) elif ISlotB.providedBy(block): # right column slot[0]['cols'][1]['blocks'].append({'uid': IUUID(block)}) else: raise ValueError('Block has unused slot in layout.') # two columns and a top row layout elif ISimplelayoutTwoColumnOneOnTopView.providedBy(old_page): if ISlotA.providedBy(block): # top row slot[0]['cols'][0]['blocks'].append({'uid': IUUID(block)}) elif ISlotB.providedBy(block): # bottom row, left column slot[1]['cols'][0]['blocks'].append({'uid': IUUID(block)}) elif ISlotC.providedBy(block): # bottom row, right column slot[1]['cols'][1]['blocks'].append({'uid': IUUID(block)}) else: raise ValueError('Block has unused slot in layout.') else: slot[0]['cols'][0]['blocks'].append({'uid': IUUID(block)}) else: page_state[slot_name][0]['cols'][0]['blocks'].append({ 'uid': IUUID(block)}) page_configuration.store(page_state)
def create_block(self, portlet, manager, type_): config = IPageConfiguration(self.new) normalizer = getUtility(IFileNameNormalizer).normalize if type_ == 'teaser': block = create(Builder('sl textblock') .within(self.new) .titled(portlet.teasertitle) .having(text=RichTextValue(portlet.teaserdesc), image=NamedBlobImage( filename=normalizer( portlet.image.filename).decode('utf-8'), data=portlet.image.data))) blockconfig = IBlockConfiguration(block) blockconfigdata = blockconfig.load() blockconfigdata['scale'] = 'large' blockconfigdata['imagefloat'] = 'no-float' blockconfig.store(blockconfigdata) if portlet.internal_target: teaser = ITeaser(block) target = uuidToObject(portlet.internal_target) if target: intids = getUtility(IIntIds) teaser.internal_link = RelationValue(intids.getId(target)) elif type_ == 'static': block = create(Builder('sl textblock') .within(self.new) .titled(portlet.header) .having(text=RichTextValue(portlet.text))) else: return uid = IUUID(block) page_state = config.load() if manager == 'ftw.subsite.front1': page_state['default'][0]['cols'][0]['blocks'].append({'uid': uid}) elif manager == 'ftw.subsite.front2': page_state['default'][0]['cols'][1]['blocks'].append({'uid': uid}) elif manager == 'ftw.subsite.front3': page_state['default'][0]['cols'][2]['blocks'].append({'uid': uid}) elif manager == 'ftw.subsite.front4': page_state['default'][0]['cols'][3]['blocks'].append({'uid': uid}) elif manager == 'ftw.subsite.front5': page_state['default'][1]['cols'][0]['blocks'].append({'uid': uid}) elif manager == 'ftw.subsite.front6': page_state['default'][1]['cols'][1]['blocks'].append({'uid': uid}) # Don't know where manager 7 belongs elif manager == 'ftw.subsite.front7': page_state['default'][1]['cols'][0]['blocks'].append({'uid': uid}) config.store(page_state)
def save_state(self): data = self.request.form.get('data') if data: json_conf = json.loads(data) page_conf = IPageConfiguration(self.context) page_conf.store(json_conf) else: raise BadRequest('No data given.') self.request.response.setHeader("Content-type", "application/json") return ''
def test_syncs_pagestate_before_publishing(self): page = create(Builder('sl content page')) create(Builder('sl textblock').within(page)) self.assertEquals([], flattened_block_uids( IPageConfiguration(page).load())) page.restrictedTraverse('@@publisher.publish')() self.assertEquals(['staticuid00000000000000000000002'], flattened_block_uids( IPageConfiguration(page).load()))
def create_block(self, portlet, manager, type_): config = IPageConfiguration(self.new) normalizer = getUtility(IFileNameNormalizer).normalize if type_ == 'teaser': block = create( Builder('sl textblock').within(self.new).titled( portlet.teasertitle).having( text=RichTextValue(portlet.teaserdesc), image=NamedBlobImage(filename=normalizer( portlet.image.filename).decode('utf-8'), data=portlet.image.data))) blockconfig = IBlockConfiguration(block) blockconfigdata = blockconfig.load() blockconfigdata['scale'] = 'large' blockconfigdata['imagefloat'] = 'no-float' blockconfig.store(blockconfigdata) if portlet.internal_target: teaser = ITeaser(block) target = uuidToObject(portlet.internal_target) if target: intids = getUtility(IIntIds) teaser.internal_link = RelationValue(intids.getId(target)) elif type_ == 'static': block = create( Builder('sl textblock').within(self.new).titled( portlet.header).having(text=RichTextValue(portlet.text))) else: return uid = IUUID(block) page_state = config.load() if manager == 'ftw.subsite.front1': page_state['default'][0]['cols'][0]['blocks'].append({'uid': uid}) elif manager == 'ftw.subsite.front2': page_state['default'][0]['cols'][1]['blocks'].append({'uid': uid}) elif manager == 'ftw.subsite.front3': page_state['default'][0]['cols'][2]['blocks'].append({'uid': uid}) elif manager == 'ftw.subsite.front4': page_state['default'][0]['cols'][3]['blocks'].append({'uid': uid}) elif manager == 'ftw.subsite.front5': page_state['default'][1]['cols'][0]['blocks'].append({'uid': uid}) elif manager == 'ftw.subsite.front6': page_state['default'][1]['cols'][1]['blocks'].append({'uid': uid}) # Don't know where manager 7 belongs elif manager == 'ftw.subsite.front7': page_state['default'][1]['cols'][0]['blocks'].append({'uid': uid}) config.store(page_state)
def structure(self): """ Render a simplelayout slot by name. Name is given by the simplelayout expression. """ page_conf = IPageConfiguration(self.context) storage = page_conf.load() sl_renderer = SimplelayoutRenderer(self.context, storage, self.name, view=self.view) return sl_renderer.render_slot()
def _blocks_without_state(self): page_conf = IPageConfiguration(self.context) saved_blocks = [] for container in page_conf.load().values(): for row in container: for col in row['cols']: for block in col['blocks']: if 'uid' in block: saved_blocks.append(block['uid']) return filter(lambda x: x[0] not in saved_blocks, self._blocks().items())
def setup_block(self, container='portletright'): page_config = IPageConfiguration(self.container) page_config.store({container: [ { "cols": [ { "blocks": [ { "uid": IUUID(self.block) } ] } ] }]}) transaction.commit()
def test_support_images_in_gallery_blocks(self, browser): page = create(Builder('sl content page')) gallery = create(Builder('sl galleryblock').within(page)) image = create(Builder('image').with_dummy_content().within(gallery)) page_config = IPageConfiguration(page) page_config.store( {"default": [{"cols": [{"blocks": [ {"uid": IUUID(gallery)}, ]}]}]}) transaction.commit() browser.login().visit(page, view='@@leadimage') src_url = browser.css('img').first.attrib['src'] self.assertRegexpMatches(src_url, r'^{}'.format( re.escape(image.absolute_url())))
def setUp(self): super(TestSimplelayoutView, self).setUp() self.setup_sample_ftis(self.layer['portal']) self.setup_block_views() self.container = create(Builder('sample container')) self.page_config = IPageConfiguration(self.container) self.url = self.container.absolute_url() + '/@@simplelayout-view' self.payload = { "default": [ { "cols": [ { "blocks": [ { "uid": "c774b0ca2a5544bf9bb46d865b11bff9" } ] } ] }, { "cols": [ { "blocks": [ { "uid": "413fb945952d4403a58ab1958c38f1d2" } ] } ] } ] }
def migrate_prepare_page_state(self): config = IPageConfiguration(self.new) page_config = { "default": [ {"cols": [ {"blocks": []}, {"blocks": []}, {"blocks": []}, {"blocks": []}, ]}, {"cols": [ {"blocks": []}, {"blocks": []}] }]} config.store(page_config)
def test_data_setter(self): page = create(Builder('sl content page').titled(u'The Page')) block = create( Builder('sl textblock').titled(u'The Block').within(page)) component = getAdapter( page, IDataCollector, name='ftw.simplelayout:SimplelayoutPageAnnotations') component.setData( {"default": [ { "cols": [ { "blocks": [{ "uid": IUUID(block) }] }, ] }, ]}, {}) self.assertEquals( {"default": [ { "cols": [ { "blocks": [{ "uid": IUUID(block) }] }, ] }, ]}, IPageConfiguration(page).load())
def has_blocks(self): config = IPageConfiguration(self.context) data = config.load() if self.manager.__name__ == 'plone.rightcolumn': portlet_container = data.get('portletright', []) elif self.manager.__name__ == 'plone.leftcolumn': portlet_container = data.get('portletleft', []) for layout in portlet_container: for columns in layout.values(): for column in columns: if len(column['blocks']): return True else: continue return False
def test_unauthorized_when_not_allowed_to_change_layouts(self): page = create(Builder("sample container")) config = IPageConfiguration(page) config.store( { "default": [ {"cols": [{"blocks": [{"uid": "foo"}, {"uid": "bar"}]}]}, {"cols": [{"blocks": []}, {"blocks": []}]}, ] } ) # The user should not change layouts page.manage_permission("ftw.simplelayout: Change Layouts", roles=[], acquire=0) # When he changes layouts, Unauthorized is raised with self.assertRaises(Unauthorized): config.store({"default": [{"cols": [{"blocks": [{"uid": "foo"}, {"uid": "bar"}]}]}]}) # But he is allowed to move blocks config.store( { "default": [ {"cols": [{"blocks": [{"uid": "foo"}]}]}, {"cols": [{"blocks": [{"uid": "bar"}]}, {"blocks": []}]}, ] } )
def test_unauthorized_when_not_allowed_to_change_layouts(self): page = create(Builder('sample container')) config = IPageConfiguration(page) config.store( {'default': [{'cols': [{'blocks': [{'uid': 'foo'}, {'uid': 'bar'}]}]}, {'cols': [{'blocks': []}, {'blocks': []}]}]} ) # The user should not change layouts page.manage_permission('ftw.simplelayout: Change Layouts', roles=[], acquire=0) # When he changes layouts, Unauthorized is raised with self.assertRaises(Unauthorized): config.store( {'default': [{'cols': [{'blocks': [{'uid': 'foo'}, {'uid': 'bar'}]}]}]} ) # But he is allowed to move blocks config.store( {'default': [{'cols': [{'blocks': [{'uid': 'foo'}]}]}, {'cols': [{'blocks': [{'uid': 'bar'}]}, {'blocks': []}]}]} )
def test_setting_and_loading_config(self): config = IPageConfiguration(create(Builder("sample container"))) config.store({"default": [{"cols": [{"blocks": [{"uid": "foo"}]}]}]}) self.assertEquals({"default": [{"cols": [{"blocks": [{"uid": "foo"}]}]}]}, config.load()) config.store({"default": [{"cols": [{"blocks": [{"uid": "bar"}]}]}]}) self.assertEquals({"default": [{"cols": [{"blocks": [{"uid": "bar"}]}]}]}, config.load())
def rows(self): """ Return datastructure for rendering blocks. """ page_conf = IPageConfiguration(self.context) blocks = self._blocks() rows = page_conf.load().get(self.name, self.one_layout_one_column) user_can_edit = self.user_can_edit() for row in rows: row['class'] = 'sl-layout' for col in row['cols']: col['class'] = 'sl-column sl-col-{}'.format(len(row['cols'])) col['blocks'] = filter( lambda block: block.get('uid', '') in blocks, col['blocks']) for block in col['blocks']: obj = blocks[block['uid']] self.create_or_update_block(obj, block) # Remove hidden blocks for users not having the permission # to edit content. col['blocks'] = [ block for block in col['blocks'] if not block['is_hidden'] or block['is_hidden'] and user_can_edit ] # Append blocks, which are not in the simplelayout configuration into # the last column. if self.name == 'default': for uid, obj in self._blocks_without_state(): block = self.create_or_update_block(obj, uid=uid) # Skip hidden blocks for users not having the permission # to edit content. if block['is_hidden'] and not user_can_edit: continue rows[-1]['cols'][-1]['blocks'].append(block) return rows
def create_page_state(obj, block): page_state = { "default": [ { "cols": [ { "blocks": [ { "uid": IUUID(block) } ] } ] }, ] } page_config = IPageConfiguration(obj) page_config.store(page_state) transaction.commit()
def save_state(self, page, block): self.page_state = { "default": [ { "cols": [ { "blocks": [ { "uid": IUUID(block) } ] } ] } ] } page_config = IPageConfiguration(page) page_config.store(self.page_state) transaction.commit()
def test_store_partial_updates(self): page = create(Builder("sample container")) config = IPageConfiguration(page) state = { "default": [ {"cols": [{"blocks": [{"uid": "foo"}]}]}, {"cols": [{"blocks": [{"uid": "bar"}]}, {"blocks": []}]}, ], "sidebar": [{"cols": [{"blocks": [{"uid": "baz"}, {"uid": "foobar"}]}]}], } config.store(state) partial_state = {"sidebar": [{"cols": [{"blocks": [{"uid": "baz"}]}]}]} config.store(partial_state) # Remove block "foobar" from "sidebar" slot new_state = { "default": [ {"cols": [{"blocks": [{"uid": "foo"}]}]}, {"cols": [{"blocks": [{"uid": "bar"}]}, {"blocks": []}]}, ], "sidebar": [{"cols": [{"blocks": [{"uid": "baz"}]}]}], } self.assertEquals(new_state, config.load())
def test_loaded_config_mutations_are_not_stored(self): config = IPageConfiguration(create(Builder("sample container"))) config.store({"default": [{"cols": [{"blocks": [{"uid": "foo"}]}]}]}) config.load()["default"][0]["cols"][0]["blocks"].append({"uid": "bar"}) self.assertEquals({"default": [{"cols": [{"blocks": [{"uid": "foo"}]}]}]}, config.load())
def update_page_state_on_copy_paste_block(block, event): """Update the uid of the new created block in the page state. block: new block event.original: origin of the copy event - usually the simplelayout page""" # Only update page state, if the original object is a Simplelayout page. if not ISimplelayout.providedBy(event.original): return # The event is triggered recursively - we need to check if the block is # actually part of the original page if event.original.get(block.id) is None: return origin_block_uid = IUUID(event.original.get(block.id)) page_config = IPageConfiguration(block.aq_parent) page_state = unwrap_persistence(page_config.load()) new_block_uid = IUUID(block) new_page_state = json.loads( json.dumps(page_state).replace(origin_block_uid, new_block_uid)) page_config.store(new_page_state)
def test_setting_and_loading_config(self): config = IPageConfiguration(create(Builder('sample container'))) config.store( {'default': [{'cols': [{'blocks': [{'uid': 'foo'}]}]}]} ) self.assertEquals( {'default': [{'cols': [{'blocks': [{'uid': 'foo'}]}]}]}, config.load()) config.store( {'default': [{'cols': [{'blocks': [{'uid': 'bar'}]}]}]} ) self.assertEquals( {'default': [{'cols': [{'blocks': [{'uid': 'bar'}]}]}]}, config.load())
def test_loaded_config_mutations_are_not_stored(self): config = IPageConfiguration(create(Builder('sample container'))) config.store( {'default': [{'cols': [{'blocks': [{'uid': 'foo'}]}]}]} ) config.load()['default'][0]['cols'][0]['blocks'].append( {'uid': 'bar'}) self.assertEquals( {'default': [{'cols': [{'blocks': [{'uid': 'foo'}]}]}]}, config.load())
def setUp(self): self.page = create(Builder('sl content page')) self.block_without_image = create(Builder('sl textblock') .within(self.page)) self.block_with_image = create(Builder('sl textblock') .within(self.page) .with_dummy_image()) self.page_state = { "default": [ { "cols": [ { "blocks": [ { "uid": IUUID(self.block_with_image) } ] } ] }, { "cols": [ { "blocks": [ { "uid": IUUID(self.block_without_image) } ] } ] } ] } self.page_config = IPageConfiguration(self.page) self.page_config.store(self.page_state) transaction.commit()
def test_store_partial_updates(self): page = create(Builder('sample container')) config = IPageConfiguration(page) state = {'default': [{'cols': [{'blocks': [{'uid': 'foo'}]}]}, {'cols': [{'blocks': [{'uid': 'bar'}]}, {'blocks': []}]}], 'sidebar': [{'cols': [{'blocks': [{'uid': 'baz'}, {'uid': 'foobar'}]}]}]} config.store(state) partial_state = {'sidebar': [{'cols': [ {'blocks': [{'uid': 'baz'}]}]}]} config.store(partial_state) # Remove block "foobar" from "sidebar" slot new_state = {'default': [{'cols': [{'blocks': [{'uid': 'foo'}]}]}, {'cols': [{'blocks': [{'uid': 'bar'}]}, {'blocks': []}]}], 'sidebar': [{'cols': [{'blocks': [{'uid': 'baz'}]}]}]} self.assertEquals(new_state, config.load())
class TestLeadImage(SimplelayoutTestCase): layer = FTW_SIMPLELAYOUT_CONTENT_TESTING def setUp(self): self.page = create(Builder('sl content page')) self.block_without_image = create(Builder('sl textblock') .within(self.page)) self.block_with_image = create(Builder('sl textblock') .within(self.page) .with_dummy_image()) self.page_state = { "default": [ { "cols": [ { "blocks": [ { "uid": IUUID(self.block_with_image) } ] } ] }, { "cols": [ { "blocks": [ { "uid": IUUID(self.block_without_image) } ] } ] } ] } self.page_config = IPageConfiguration(self.page) self.page_config.store(self.page_state) transaction.commit() @browsing def test_use_first_image_in_page_state(self, browser): browser.login().visit(self.page, view='@@leadimage') self.assertTrue( browser.css('img').first.attrib['src'].startswith( self.block_with_image.absolute_url())) @browsing def test_default_scale_is_preview(self, browser): browser.login().visit(self.page, view='@@leadimage') self.assertEquals( '400', browser.css('img').first.attrib['width']) @browsing def test_render_image_with_other_scale(self, browser): browser.login().visit(self.page, view='@@leadimage', data={'scale': 'mini'}) self.assertEquals( '200', browser.css('img').first.attrib['width']) @browsing def test_get_image_from_block_in_any_container(self, browser): page = create(Builder('sl content page')) block = create(Builder('sl textblock').with_dummy_image().within(page)) IPageConfiguration(page).store( {"portletright": [{"cols": [{"blocks": [ {"uid": IUUID(block)}, ]}]}]}) transaction.commit() browser.login().visit(page, view='@@leadimage') src_url = browser.css('img').first.attrib['src'] self.assertRegexpMatches(src_url, r'^{}'.format( re.escape(block.absolute_url()))) @browsing def test_support_images_in_gallery_blocks(self, browser): page = create(Builder('sl content page')) gallery = create(Builder('sl galleryblock').within(page)) image = create(Builder('image').with_dummy_content().within(gallery)) page_config = IPageConfiguration(page) page_config.store( {"default": [{"cols": [{"blocks": [ {"uid": IUUID(gallery)}, ]}]}]}) transaction.commit() browser.login().visit(page, view='@@leadimage') src_url = browser.css('img').first.attrib['src'] self.assertRegexpMatches(src_url, r'^{}'.format( re.escape(image.absolute_url())))
def getData(self): return unwrap_persistence(IPageConfiguration(self.context).load())
class TestSimplelayoutView(SimplelayoutTestCase): layer = FTW_SIMPLELAYOUT_FUNCTIONAL_TESTING def setUp(self): super(TestSimplelayoutView, self).setUp() self.setup_sample_ftis(self.layer['portal']) self.setup_block_views() self.container = create(Builder('sample container')) self.page_config = IPageConfiguration(self.container) self.url = self.container.absolute_url() + '/@@simplelayout-view' self.payload = { "default": [ { "cols": [ { "blocks": [ { "uid": "c774b0ca2a5544bf9bb46d865b11bff9" } ] } ] }, { "cols": [ { "blocks": [ { "uid": "413fb945952d4403a58ab1958c38f1d2" } ] } ] } ] } @browsing def test_render_blocks_not_in_page_configuration(self, browser): # Fallback for not saved blocks thru the simplelayout JS lib. create(Builder('sample block') .titled('TextBlock title') .within(self.container) .having(text=RichTextValue('The text')) ) browser.login().visit(self.container, view='@@simplelayout-view') self.assertEqual(browser.url, self.url) self.assertEquals('OK', browser.css('.sl-block').first.text) @browsing def test_invalid_simplelayout_save_state_request(self, browser): with browser.expect_http_error(reason='Bad Request'): browser.login().visit(self.container, view='sl-ajax-save-state-view', data={}) @browsing def test_store_save_simplelayout_state_thru_view(self, browser): payload = {"data": json.dumps(self.payload)} if IS_PLONE_5: from plone.protect.authenticator import createToken payload["_authenticator"] = createToken() browser.login().visit(self.container, view='sl-ajax-save-state-view', data=payload) browser.visit(self.container) self.assertEquals(self.payload, self.page_config.load()) @browsing def test_render_blocks_in_different_layouts(self, browser): block1 = create(Builder('sample block') .titled('Block 1') .within(self.container)) block2 = create(Builder('sample block') .titled('Block 1') .within(self.container)) self.payload['default'][0]['cols'][0][ 'blocks'][0]['uid'] = IUUID(block1) self.payload['default'][1]['cols'][0][ 'blocks'][0]['uid'] = IUUID(block2) self.page_config.store(self.payload) transaction.commit() browser.login().visit(self.container) self.assertEquals(2, len(browser.css('.sl-layout')), 'Expect 2 layouts') self.assertEquals(2, len(browser.css('.sl-column.sl-col-1')), 'Expect two, one column layouts') @browsing def test_render_blocks_in_different_columns(self, browser): block1 = create(Builder('sample block') .titled('Block 1') .within(self.container)) block2 = create(Builder('sample block') .titled('Block 1') .within(self.container)) self.payload['default'][0]['cols'][0][ 'blocks'][0]['uid'] = IUUID(block1) self.payload['default'][1]['cols'][0][ 'blocks'][0]['uid'] = IUUID(block2) # Move Block into layout 1, column 2 data_colmn = self.payload['default'][1]['cols'][0] self.payload['default'].pop() self.payload['default'][0]['cols'].append(data_colmn) self.page_config.store(self.payload) transaction.commit() browser.login().visit(self.container) self.assertEquals(2, len(browser.css('.sl-column.sl-col-2')), 'Expect 2 columns') @browsing def test_skips_unavailable_blocks(self, browser): # The block may no longer exist or may not be visible for # the current user. block1 = create(Builder('sample block') .titled('Block 1') .within(self.container)) self.payload['default'][0]['cols'][0][ 'blocks'][0]['uid'] = IUUID(block1) self.payload['default'][1]['cols'][0][ 'blocks'][0]['uid'] = '123xNONxEXISTINGxUIDx123' self.page_config.store(self.payload) transaction.commit() browser.login().visit(self.container) self.assertEquals( ['http://nohost/plone/samplecontainer/block-1'], map(lambda node: node.attrib.get('data-url'), browser.css('.sl-block'))) @browsing def test_empty_block_state_does_not_break_the_view(self, browser): self.payload['default'][0]['cols'][0]['blocks'].append({}) self.page_config.store(self.payload) transaction.commit() browser.login().visit(self.container) self.assertTrue( browser.css('body.template-simplelayout-view'), 'Expect to be on the simplelayout template, not the error page.') @browsing def test_simplelayout_default_config_from_control_panel(self, browser): browser.login().visit(self.container, view='@@simplelayout-view') registry = getUtility(IRegistry) settings = registry.forInterface(ISimplelayoutDefaultSettings) settings.slconfig = u'{"layouts": [1, 2]}' transaction.commit() browser.login().visit(self.container, view='@@simplelayout-view') data_attr_value = json.loads(browser.css( '[data-sl-settings]').first.attrib['data-sl-settings']) self.assertEquals([1, 2], data_attr_value['layouts'], 'Expect the layout setting in default config.') @browsing def test_simplelayout_config_updated_by_permissions(self, browser): browser.login().visit(self.container, view='@@simplelayout-view') data_attr_value = json.loads(browser.css( '[data-sl-settings]').first.attrib['data-sl-settings']) self.assertTrue(data_attr_value['canChangeLayout'], 'Should have the Change layouts permission.') self.container.manage_permission('ftw.simplelayout: Change Layouts', roles=[], acquire=0) transaction.commit() browser.visit(self.container, view='@@simplelayout-view') data_attr_value = json.loads(browser.css( '[data-sl-settings]').first.attrib['data-sl-settings']) self.assertFalse(data_attr_value['canChangeLayout'], 'Should NOT have the Change layouts permission.') @browsing def test_prevent_layout_changes_if_not_allowed(self, browser): self.container.manage_permission('ftw.simplelayout: Change Layouts', roles=[], acquire=0) transaction.commit() self.payload['default'].append({'cols': [{}]}) payload = {"data": json.dumps(self.payload)} with browser.expect_unauthorized(): browser.login().visit(self.container, view='sl-ajax-save-state-view', data=payload) @browsing def test_simplelayout_config_updated_by_adapter(self, browser): class ContainerConfigAdapter(object): implements(ISimplelayoutContainerConfig) def __init__(self, context, request): pass def __call__(self, settings): settings['layouts'] = [1] def default_page_layout(self): return None provideAdapter(ContainerConfigAdapter, adapts=(ISampleSimplelayoutContainer, Interface)) transaction.commit() try: browser.login().visit(self.container, view='@@simplelayout-view') data_attr_value = json.loads(browser.css( '[data-sl-settings]').first.attrib['data-sl-settings']) self.assertEquals([1], data_attr_value['layouts']) finally: # Unregister adapter - since the component registry is not isolatet # per test sm = getGlobalSiteManager() sm.unregisterAdapter(ContainerConfigAdapter, required=( ISampleSimplelayoutContainer, Interface), provided=ISimplelayoutContainerConfig) @browsing def test_simplelayout_default_page_layouts_by_adapter(self, browser): class ContainerConfigAdapter(object): implements(ISimplelayoutContainerConfig) def __init__(self, context, request): pass def __call__(self, settings): pass def default_page_layout(self): return { "default": [ {"cols": [{"blocks": []}, {"blocks": []}]} ] } provideAdapter(ContainerConfigAdapter, adapts=(ISampleSimplelayoutContainer, Interface)) transaction.commit() try: browser.login().visit(self.container, view='@@simplelayout-view') # This should result in one layout with two columns self.assertEquals(1, len(browser.css('.sl-layout'))) self.assertEquals(2, len(browser.css('.sl-column.sl-col-2'))) finally: # Unregister adapter - since the component registry is not isolatet # per test sm = getGlobalSiteManager() sm.unregisterAdapter(ContainerConfigAdapter, required=( ISampleSimplelayoutContainer, Interface), provided=ISimplelayoutContainerConfig) @browsing def test_simplelayout_config_updated_view(self, browser): class CustomSimplelayoutView(SimplelayoutView): def update_simplelayout_settings(self, settings): settings['layouts'] = [1, 4] provideAdapter(CustomSimplelayoutView, adapts=(Interface, Interface), provides=IBrowserView, name='customview') browser.login().visit(self.container, view='@@customview') data_attr_value = json.loads(browser.css( '[data-sl-settings]').first.attrib['data-sl-settings']) self.assertEquals([1, 4], data_attr_value['layouts']) @browsing def test_show_fallback_view_on_block_render_problems(self, browser): block = create(Builder('sample block') .titled('TextBlock title') .within(self.container)) properties = getMultiAdapter((block, block.REQUEST), IBlockProperties) properties.set_view('block_view_broken') transaction.commit() browser.login().visit(self.container) self.assertEquals( 'The block could not be rendered. Please check the log for ' 'details.', browser.css('.sl-block').first.text) @browsing def test_empty_sl_page_renders_at_least_one_layout(self, browser): browser.login().visit(self.container) # By default it's a one column layout. self.assertEquals(1, len(browser.css('.sl-column.sl-col-1')), 'There should be at least a empty one column layout') @browsing def test_normalized_portal_type_as_css_klass_on_block(self, browser): create(Builder('sample block') .titled('TextBlock title') .within(self.container)) browser.login().visit(self.container) self.assertEquals('sl-block sampleblock', browser.css('.sl-block').first.attrib['class'], 'Expect "sample" as css klass on block structure.') @browsing def test_block_has_anchor(self, browser): create(Builder('sample block') .titled('Block 1') .within(self.container)) browser.login().open(self.container) self.assertEqual( 'block-1', browser.css('.sl-layout a').first.attrib['name'] ) @browsing def test_canEdit_is_false_if_border_disabled(self, browser): browser.login().visit(self.container, data={'disable_border': 1}) data_attr_value = json.loads(browser.css( '[data-sl-settings]').first.attrib['data-sl-settings']) self.assertFalse(data_attr_value['canEdit'], 'Edit should be disabled if disable_border is there.') @browsing def test_sl_container_has_sl_edit_css_class(self, browser): browser.login().visit(self.container) self.assertTrue(len(browser.css('.sl-can-edit')), 'Expect the sl-can-edit class on sl-simplelayout.') @browsing def test_sl_container_no_sl_edi_css_class(self, browser): browser.login().visit(self.container, data={'disable_border': 1}) self.assertFalse(len(browser.css('.sl-can-edit')), 'no sl-can-edit class expected.') @browsing def test_simplelayout_renderer_only_one_layout_of_storage(self, browser): storage = self.payload.copy() sl_renderer = SimplelayoutRenderer(self.container, storage, 'default') browser.parse(sl_renderer.render_layout(index=1)) self.assertEquals(1, len(browser.css('.sl-layout')), 'Expect only one layout') browser.parse(sl_renderer.render_layout(index=0)) self.assertEquals(1, len(browser.css('.sl-layout')), 'Expect only one layout') browser.parse(sl_renderer.render_layout()) self.assertEquals(2, len(browser.css('.sl-layout')), 'Expect both layouts') @browsing def test_simplelayout_renderer_raises_ValueError(self, browser): storage = self.payload.copy() sl_renderer = SimplelayoutRenderer(self.container, storage, 'default') with self.assertRaises(ValueError): sl_renderer.render_layout(index=4) @browsing def test_simplelayout_renderer_can_omit_sl_layout_class(self, browser): storage = self.payload.copy() sl_renderer = SimplelayoutRenderer(self.container, storage, 'default') browser.open_html(sl_renderer.render_layout(index=0, is_update=True)) self.assertEquals(0, len(browser.css('.sl-layout')), 'Expect no sl-layout wrapper')
def setData(self, data, metadata): IPageConfiguration(self.context).store(data)
def test_restore_slot_default_when_removing_last_layout(self): """If the last layout was removed from a state, the slot default should be restored. """ class ConfigAdapter(object): def __init__(self, context, request): pass def __call__(self, settings): pass def default_page_layout(self): return {'default': [{'cols': [{'blocks': [{'uid': 'foo'}]}]}]} adapter_registration_kwargs = { 'factory': ConfigAdapter, 'required': (ISampleSimplelayoutContainer, Interface), 'provided': ISimplelayoutContainerConfig} container = create(Builder('sample container')) gsm = getGlobalSiteManager() gsm.registerAdapter(**adapter_registration_kwargs) try: config = IPageConfiguration(container) config.store(config.load()) self.assertEquals( {'default': [{'cols': [{'blocks': [{'uid': 'foo'}]}]}]}, config.load()) config.store({'default': [{'cols': [{'blocks': []}]}], 'portlet': [{'cols': [{'blocks': [{'uid': 'bar'}]}]}]}) self.assertEquals( {'default': [{'cols': [{'blocks': []}]}], 'portlet': [{'cols': [{'blocks': [{'uid': 'bar'}]}]}]}, config.load()) config.store({'default': []}) self.assertEquals( {'default': [{'cols': [{'blocks': [{'uid': 'foo'}]}]}], 'portlet': [{'cols': [{'blocks': [{'uid': 'bar'}]}]}]}, config.load()) finally: gsm.unregisterAdapter(**adapter_registration_kwargs)