How to install jyu.portalview
- Download and install ActivePython
- Open Command Prompt
- Type
pypm install jyu.portalview
Lastest release
Contents
Portal View is a content type for displaying a portal like aggregating composite view. It's a folderish object, which may contain only supported content types (created into column specific Portal View Column Folders).
Portal View has originally been developed for the public front page (and internal portal pages) of the University of Jyvaskyla.
Detailed Documentation
Public resources
Portal View installs the following public resources:
++resource++jyu.portalview.styles/portalview.css:
>>> from Products.Five.testbrowser import Browser >>> browser = Browser(); portal_url = self.portal.absolute_url() >>> browser.open(portal_url + '/++resource++jyu.portalview.styles/portalview.css')
++resource++jyu.portalview.images/portalview_icon.png:
>>> browser.open(portal_url + '/++resource++jyu.portalview.images/portalview_icon.png')
++resource++jyu.portalview.images/column_icon.png:
>>> browser.open(portal_url + '/++resource++jyu.portalview.images/column_icon.png')
++resource++jyu.portalview.images/box_feed_icon.gif:
>>> browser.open(portal_url + '/++resource++jyu.portalview.images/box_feed_icon.gif')
++resource++jyu.portalview.images/box_bullet.gif:
>>> browser.open(portal_url + '/++resource++jyu.portalview.images/box_bullet.gif')
Creating content
By default, Portal View is addable by any contributor. Let's
open the front page:
>>> browser.open(portal_url)
enter the log in details:
>>> browser.getControl(name='__ac_name').value = 'contributor' >>> browser.getControl(name='__ac_password').value = 'secret'
and log in:
>>> browser.getControl(name='submit').click() >>> 'You are now logged in' in browser.contents True
Now there is a Portal View in the add item menu:
>>> browser.getLink(id='portal-view').url.endswith('createObject?type_name=Portal+View')
True
Yet, we wont't see Portal View Column Folder in the add item menu, because it's only addable under Portal View:
>>> browser.getLink(id='portal-view-column-folder')
Traceback (most recent call last): ... LinkNotFoundError
To add a single Portal View
click it from the add item menu:
>>> browser.getLink(id='portal-view').click()
enter some information:
>>> browser.getControl(name='title').value = 'frontpage' >>> browser.getControl(name='css').value = """ ... /* This is a test style */ ... .portalViewColumn, .portalViewBox { border: thin solid blue; } ... """
and submit the form:
>>> browser.getControl("Save").click()
A new Portal View has been created:
>>> 'frontpage' in self.portal.objectIds()
True
Default settings
By default, a new Portal View is created with both left and right portlets disable. That's why
Portal View implements ILocalPortletAssignable interface to enable local portlet management:
>>> from plone.portlets.interfaces import ILocalPortletAssignable >>> ILocalPortletAssignable.providedBy(self.portal.frontpage) True
and comes with all portlet managers disabled:
>>> from zope.component import getUtility, getMultiAdapter >>> from plone.portlets.interfaces import IPortletManager, ILocalPortletAssignmentManager >>> from plone.portlets.constants import CONTEXT_CATEGORY, GROUP_CATEGORY, CONTENT_TYPE_CATEGORY >>> left = getMultiAdapter((self.portal.frontpage, getUtility(IPortletManager, name = u'plone.leftcolumn')), ILocalPortletAssignmentManager) >>> right = getMultiAdapter((self.portal.frontpage, getUtility(IPortletManager, name = u'plone.leftcolumn')), ILocalPortletAssignmentManager) >>> >>> # Verifies that left context portlets are blacklisted correctly. ... left.getBlacklistStatus(CONTEXT_CATEGORY) True
>>> # Verifies that left group portlets are blacklisted correctly. ... left.getBlacklistStatus(GROUP_CATEGORY) True
>>> # Verifies that left content type portlets are blacklisted correctly. ... left.getBlacklistStatus(CONTENT_TYPE_CATEGORY) True
>>> # Verifies that right context portlets are blacklisted correctly. ... right.getBlacklistStatus(CONTEXT_CATEGORY) True
>>> # Verifies that right group portlets are blacklisted correctly. ... right.getBlacklistStatus(GROUP_CATEGORY) True
>>> # Verifies that right content type portlets are blacklisted correctly. ... right.getBlacklistStatus(CONTENT_TYPE_CATEGORY) True
For a fast start, a new Portal View is always created with four initial columns (Portal View Column Folders) to contain boxed content:
>>> from jyu.portalview.config import COLUMNS >>> # This doctest asserts 4 columns to be created by default.
... COLUMNS 4
>>> self.portal.frontpage.objectIds() ['left', 'center-left', 'center-right', 'right']
The four automatically created Portal View Column Folders are set to be 25% wide:
>>> self.portal.frontpage.get('left').get('width')
'25'
>>> self.portal.frontpage.get('center-left').get('width') '25'
>>> self.portal.frontpage.get('center-right').get('width') '25'
>>> self.portal.frontpage.get('right').get('width') '25'
Now, of course, it's posssible to create more columns by clicking Portal View Column Folder from the add item menu on Portal View:
>>> browser.open(portal_url + '/frontpage') >>> browser.getLink('Add Portal View Column Folder').url.endswith('createObject?type_name=Portal+View+Column+Folder') True
Adding subcontent
The subcontent to be boxed, must be created below Portal View Column Folders, also known as columns:
>>> columns = ['left', 'center-left', 'center-right', 'right'] >>> for i in range(4):
... for j in range(4): ... browser.open(portal_url + '/frontpage/%s' % columns[i]) ... browser.getLink(id='document').click() ... browser.getControl(name='title').value = 'Title %s.%s' % (str(i+1), str(j+1)) ... browser.getControl(name='description').value = 'Description %s.%s' % (str(i+1), str(j+1)) ... browser.getControl(name='text').value = 'Text %s.%s' % (str(i+1), str(j+1)) ... browser.getControl("Save").click()
The default view of Portal View renders all the available subcontent within the Portal View visible for the current user:
>>> browser.open(portal_url + '/frontpage') >>> 'Text 1.1' in browser.contents
True
>>> 'Text 4.4' in browser.contents True
Publishing content
By default, Portal View can be published by any reviewer. Let's
log out:
>>> browser.getLink('Log out').click()
open the front page:
>>> browser.open(portal_url)
enter the log in details:
>>> browser.getControl(name='__ac_name').value = 'reviewer' >>> browser.getControl(name='__ac_password').value = 'secret'
and log in:
>>> browser.getControl(name='submit').click() >>> 'You are now logged in' in browser.contents True
To publish the complete Portal View with all its subcontent
navigate to the folder_contents of the parent folder of the content:
>>> browser.open(portal_url + '/folder_contents')
check the content:
>>> browser.getControl('frontpage').selected = True >>> browser.getControl(name='paths:list').value ['/plone/frontpage']
click Change state:
>>> browser.getControl(name='content_status_history:method').click()
check to Include children:
>>> browser.getControl('Include contained items').selected = True >>> browser.getControl(name='include_children').value True
select Publish:
>>> browser.getControl('Publish').selected = True >>> browser.getControl(name='workflow_action').value ['publish']
click Save:
>>> browser.getControl(name='form.button.FolderPublish').click() Traceback (most recent call last): ... HTTPError: HTTP Error 404: Not Found
Now the Portal View and all it's contents are published:
>>> from Products.CMFCore.utils import getToolByName >>> workflow = getToolByName(self.portal, 'portal_workflow') >>> workflow.getInfoFor(self.portal.frontpage, 'review_state')
'published'
>>> workflow.getInfoFor(self.portal.frontpage.get('left'), 'review_state') 'published'
>>> workflow.getInfoFor(self.portal.frontpage.get('left').get('title-1.1'), 'review_state') 'published'
>>> workflow.getInfoFor(self.portal.frontpage.get('right'), 'review_state') 'published'
>>> workflow.getInfoFor(self.portal.frontpage.get('right').get('title-4.4'), 'review_state') 'published'
Altough, to reject a single page for testing purposes, let's
navigate directly to the content:
>>> browser.open(portal_url + '/frontpage/right/title-4.3')
reject the content:
>>> browser.getLink('Send back').click()
The results
Working Portal View shows all its subcontent on a single page. Let's
log out:
>>> browser.getLink('Log out').click()
open the front page:
>>> browser.open(portal_url + '/frontpage')
verify that we see all but the rejected content:
>>> "Text 1.1" in browser.contents True
>>> "Text 1.2" in browser.contents True
>>> "Text 1.3" in browser.contents True
>>> "Text 1.4" in browser.contents True
>>> "Text 2.1" in browser.contents True
>>> "Text 2.2" in browser.contents True
>>> "Text 2.3" in browser.contents True
>>> "Text 2.4" in browser.contents True
>>> "Text 3.1" in browser.contents True
>>> "Text 3.2" in browser.contents True
>>> "Text 3.4" in browser.contents True
>>> "Text 3.4" in browser.contents True
>>> "Text 4.1" in browser.contents True
>>> "Text 4.2" in browser.contents True
>>> "Text 4.3" in browser.contents False
>>> "Text 4.4" in browser.contents True
Internals
Portal View renders itself through PortalView:
>>> self.portal.frontpage.restrictedTraverse('@@view')
<Products.Five.metaclass.PortalView object at ...>
The internals of the Portal View do a plenty of things. It
builds safe CSS-declarations from the CSS-field:
>>> view = self.portal.frontpage.restrictedTraverse('@@view') >>> view.prefixedStyles '#portalview .portalViewColumn,\n#portalview .portalViewBox { border: thin solid blue; }'
queries for all contents within the Portal View:
>>> len(view.testQueryContents()) 24
splits them into columns (Portal View Column Folders):
>>> columns, objects = view.testExtractResults(view.testQueryContents()) >>> len(columns) 4
>>> [c.getPath() for c in columns] ['/plone/frontpage/left', '/plone/frontpage/center-left', '/plone/frontpage/center-right', '/plone/frontpage/right']
and other objects, which are categorized after their parents:
>>> len(objects) 6
>>> keys = objects.keys() >>> keys.sort() >>> keys ['/plone', '/plone/frontpage', '/plone/frontpage/center-left', '/plone/frontpage/center-right', '/plone/frontpage/left', '/plone/frontpage/right']
Finally, Portal View returns a list, which includes all the necessary information to build the view. That consists of
list of columns, in which:
>>> columns = view.columns >>> len(columns) 4
there is id and class attributes for the column and its wrapper:
>>> columns[3]['wrapper']['class'] 'portalViewColumnWrapper portalViewColumnWrapper-25'
>>> columns[3]['wrapper']['id'] 'portalview-column-wrapper-right'
>>> columns[3]['class'] 'portalViewColumn'
>>> columns[3]['id'] 'portalview-column-right'
list of boxes for every column:
>>> len(columns[0]['boxes']) 5
>>> len(columns[3]['boxes']) 4
including id and class attributes for every box and their wrappers:
>>> columns[3]['boxes'][3]['wrapper']['class'] 'portletWrapper portalViewBoxWrapper state-published'
>>> columns[3]['boxes'][3]['wrapper']['id'] 'portalview-box-wrapper-right-title-4.4'
>>> columns[3]['boxes'][3]['class'] 'portlet portalViewBox portalViewBox-title-4.4'
>>> columns[3]['boxes'][3]['id'] 'portalview-box-right-title-4.4'
not to forget the object of the box itself:
>>> columns[3]['boxes'][3]['object'] <ATDocument at /plone/frontpage/right/title-4.4>
It's good to know that every column and box has both a general class and an unique id to it easy to manipulate them with CSS.
Columns within columns
With Portal View it's also possible to build more complex layouts. For example, start with a 20% width left column, 60% width middle column and 20% right column, and continue by adding a 100% top column, 50% left column and 50% right column inside the middle column.
The following should be enough to test this feature:
open the front page:
>>> browser.open(portal_url)
enter the log in details:
>>> browser.getControl(name='__ac_name').value = 'reviewer' >>> browser.getControl(name='__ac_password').value = 'secret'
and log in:
>>> browser.getControl(name='submit').click() >>> 'You are now logged in' in browser.contents True
Now, let's cut an existing column and paste it inside an another column:
open the third column:
>>> browser.open(portal_url + '/frontpage/center-right')
cut it:
>>> browser.getLink(id='cut').click()
open the fourth column:
>>> browser.open(portal_url + '/frontpage/center-left/folder_contents')
paste the copied column into it:
>>> browser.getLink(id='paste').click()
Portal View still shows all of our content on a single page. Let's
log out:
>>> browser.getLink('Log out').click()
open the front page:
>>> browser.open(portal_url + '/frontpage')
and verify that we see all but the rejected content:
>>> "Text 1.1" in browser.contents True
>>> "Text 2.1" in browser.contents True
>>> "Text 3.1" in browser.contents True
>>> "Text 4.1" in browser.contents True
>>> "Text 4.3" in browser.contents False
and that subcolumn ids are created correctly:
>>> "portalview-column-wrapper-center-left-center-right" in browser.contents True
1.0.3 (2009-11-16)
- Initial release as a public product.