Welcome, guest | Sign In | My Account | Store | Cart

Notice! PyPM is being replaced with the ActiveState Platform, which enhances PyPM’s build and deploy capabilities. Create your free Platform account to download ActivePython or customize Python with the packages you require and get automatic updates.

Download
ActivePython
INSTALL>
pypm install jyu.portalview

How to install jyu.portalview

  1. Download and install ActivePython
  2. Open Command Prompt
  3. Type pypm install jyu.portalview
 Python 2.7Python 3.2Python 3.3
Windows (32-bit)
1.0.3 Available View build log
Windows (64-bit)
1.0.3 Available View build log
Mac OS X (10.5+)
1.0.3 Available View build log
Linux (32-bit)
1.0.3 Available View build log
Linux (64-bit)
1.0.3 Available View build log
 
License
GPL
Dependencies
Lastest release
version 1.0.3 on Jan 5th, 2011

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:

    System Message: WARNING/2 (<string>, line 22)

    Literal block expected; none found.

>>> 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:

    System Message: WARNING/2 (<string>, line 28)

    Literal block expected; none found.

>>> browser.open(portal_url + '/++resource++jyu.portalview.images/portalview_icon.png')
  • ++resource++jyu.portalview.images/column_icon.png:

    System Message: WARNING/2 (<string>, line 32)

    Literal block expected; none found.

>>> browser.open(portal_url + '/++resource++jyu.portalview.images/column_icon.png')
  • ++resource++jyu.portalview.images/box_feed_icon.gif:

    System Message: WARNING/2 (<string>, line 36)

    Literal block expected; none found.

>>> browser.open(portal_url + '/++resource++jyu.portalview.images/box_feed_icon.gif')
  • ++resource++jyu.portalview.images/box_bullet.gif:

    System Message: WARNING/2 (<string>, line 40)

    Literal block expected; none found.

>>> browser.open(portal_url + '/++resource++jyu.portalview.images/box_bullet.gif')
Creating content

By default, Portal View is addable by any contributor. Let's

  1. open the front page:

    System Message: WARNING/2 (<string>, line 49)

    Literal block expected; none found.

>>> browser.open(portal_url)
  1. enter the log in details:

    System Message: WARNING/2 (<string>, line 53)

    Literal block expected; none found.

>>> browser.getControl(name='__ac_name').value = 'contributor'
>>> browser.getControl(name='__ac_password').value = 'secret'
  1. and log in:

    System Message: WARNING/2 (<string>, line 58)

    Literal block expected; none found.

>>> 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')

System Message: ERROR/3 (<string>, line 65)

Inconsistent literal block quoting.

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')

System Message: ERROR/3 (<string>, line 70)

Inconsistent literal block quoting.

Traceback (most recent call last): ... LinkNotFoundError

To add a single Portal View

  1. click it from the add item menu:

    System Message: WARNING/2 (<string>, line 78)

    Literal block expected; none found.

>>> browser.getLink(id='portal-view').click()
  1. enter some information:

    System Message: WARNING/2 (<string>, line 82)

    Literal block expected; none found.

>>> browser.getControl(name='title').value = 'frontpage'
>>> browser.getControl(name='css').value = """
... /* This is a test style */
... .portalViewColumn, .portalViewBox { border: thin solid blue; }
... """
  1. and submit the form:

    System Message: WARNING/2 (<string>, line 90)

    Literal block expected; none found.

>>> browser.getControl("Save").click()

A new Portal View has been created:

>>> 'frontpage' in self.portal.objectIds()

System Message: ERROR/3 (<string>, line 95)

Inconsistent literal block quoting.

True

Default settings

By default, a new Portal View is created with both left and right portlets disable. That's why

  1. Portal View implements ILocalPortletAssignable interface to enable local portlet management:

    System Message: WARNING/2 (<string>, line 104)

    Literal block expected; none found.

>>> from plone.portlets.interfaces import ILocalPortletAssignable
>>> ILocalPortletAssignable.providedBy(self.portal.frontpage)
True
  1. and comes with all portlet managers disabled:

    System Message: WARNING/2 (<string>, line 110)

    Literal block expected; none found.

>>> 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.

System Message: ERROR/3 (<string>, line 144)

Inconsistent literal block quoting.

... 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')

System Message: ERROR/3 (<string>, line 153)

Inconsistent literal block quoting.

'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):

System Message: ERROR/3 (<string>, line 178)

Inconsistent literal block quoting.

... 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

System Message: ERROR/3 (<string>, line 191)

Inconsistent literal block quoting.

True

>>> 'Text 4.4' in browser.contents
True
Publishing content

By default, Portal View can be published by any reviewer. Let's

  1. log out:

    System Message: WARNING/2 (<string>, line 203)

    Literal block expected; none found.

>>> browser.getLink('Log out').click()
  1. open the front page:

    System Message: WARNING/2 (<string>, line 207)

    Literal block expected; none found.

>>> browser.open(portal_url)
  1. enter the log in details:

    System Message: WARNING/2 (<string>, line 211)

    Literal block expected; none found.

>>> browser.getControl(name='__ac_name').value = 'reviewer'
>>> browser.getControl(name='__ac_password').value = 'secret'
  1. and log in:

    System Message: WARNING/2 (<string>, line 216)

    Literal block expected; none found.

>>> browser.getControl(name='submit').click()
>>> 'You are now logged in' in browser.contents
True

To publish the complete Portal View with all its subcontent

  1. navigate to the folder_contents of the parent folder of the content:

    System Message: WARNING/2 (<string>, line 224)

    Literal block expected; none found.

>>> browser.open(portal_url + '/folder_contents')
  1. check the content:

    System Message: WARNING/2 (<string>, line 228)

    Literal block expected; none found.

>>> browser.getControl('frontpage').selected = True
>>> browser.getControl(name='paths:list').value
['/plone/frontpage']
  1. click Change state:

    System Message: WARNING/2 (<string>, line 234)

    Literal block expected; none found.

>>> browser.getControl(name='content_status_history:method').click()
  1. check to Include children:

    System Message: WARNING/2 (<string>, line 238)

    Literal block expected; none found.

>>> browser.getControl('Include contained items').selected = True
>>> browser.getControl(name='include_children').value
True
  1. select Publish:

    System Message: WARNING/2 (<string>, line 244)

    Literal block expected; none found.

>>> browser.getControl('Publish').selected = True
>>> browser.getControl(name='workflow_action').value
['publish']
  1. click Save:

    System Message: WARNING/2 (<string>, line 250)

    Literal block expected; none found.

>>> 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')

System Message: ERROR/3 (<string>, line 260)

Inconsistent literal block quoting.

'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

  1. navigate directly to the content:

    System Message: WARNING/2 (<string>, line 278)

    Literal block expected; none found.

>>> browser.open(portal_url + '/frontpage/right/title-4.3')
  1. reject the content:

    System Message: WARNING/2 (<string>, line 282)

    Literal block expected; none found.

>>> browser.getLink('Send back').click()
The results

Working Portal View shows all its subcontent on a single page. Let's

  1. log out:

    System Message: WARNING/2 (<string>, line 291)

    Literal block expected; none found.

>>> browser.getLink('Log out').click()
  1. open the front page:

    System Message: WARNING/2 (<string>, line 295)

    Literal block expected; none found.

>>> browser.open(portal_url + '/frontpage')
  1. verify that we see all but the rejected content:

    System Message: WARNING/2 (<string>, line 299)

    Literal block expected; none found.

>>> "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')

System Message: ERROR/3 (<string>, line 353)

Inconsistent literal block quoting.

<Products.Five.metaclass.PortalView object at ...>

The internals of the Portal View do a plenty of things. It

  1. builds safe CSS-declarations from the CSS-field:

    System Message: WARNING/2 (<string>, line 359)

    Literal block expected; none found.

>>> view = self.portal.frontpage.restrictedTraverse('@@view')
>>> view.prefixedStyles
'#portalview .portalViewColumn,\n#portalview .portalViewBox { border: thin solid blue; }'
  1. queries for all contents within the Portal View:

    System Message: WARNING/2 (<string>, line 365)

    Literal block expected; none found.

>>> len(view.testQueryContents())
24
  1. splits them into columns (Portal View Column Folders):

    System Message: WARNING/2 (<string>, line 370)

    Literal block expected; none found.

>>> 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']
  1. and other objects, which are categorized after their parents:

    System Message: WARNING/2 (<string>, line 379)

    Literal block expected; none found.

>>> 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

  1. list of columns, in which:

    System Message: WARNING/2 (<string>, line 391)

    Literal block expected; none found.

>>> columns = view.columns
>>> len(columns)
4
  1. there is id and class attributes for the column and its wrapper:

    System Message: WARNING/2 (<string>, line 397)

    Literal block expected; none found.

>>> 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'
  1. list of boxes for every column:

    System Message: WARNING/2 (<string>, line 411)

    Literal block expected; none found.

>>> len(columns[0]['boxes'])
5
>>> len(columns[3]['boxes'])
4
  1. including id and class attributes for every box and their wrappers:

    System Message: WARNING/2 (<string>, line 419)

    Literal block expected; none found.

>>> 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'
  1. not to forget the object of the box itself:

    System Message: WARNING/2 (<string>, line 433)

    Literal block expected; none found.

>>> 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:

  1. open the front page:

    System Message: WARNING/2 (<string>, line 447)

    Literal block expected; none found.

>>> browser.open(portal_url)
  1. enter the log in details:

    System Message: WARNING/2 (<string>, line 451)

    Literal block expected; none found.

>>> browser.getControl(name='__ac_name').value = 'reviewer'
>>> browser.getControl(name='__ac_password').value = 'secret'
  1. and log in:

    System Message: WARNING/2 (<string>, line 456)

    Literal block expected; none found.

>>> 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:

  1. open the third column:

    System Message: WARNING/2 (<string>, line 464)

    Literal block expected; none found.

>>> browser.open(portal_url + '/frontpage/center-right')
  1. cut it:

    System Message: WARNING/2 (<string>, line 468)

    Literal block expected; none found.

>>> browser.getLink(id='cut').click()
  1. open the fourth column:

    System Message: WARNING/2 (<string>, line 472)

    Literal block expected; none found.

>>> browser.open(portal_url + '/frontpage/center-left/folder_contents')
  1. paste the copied column into it:

    System Message: WARNING/2 (<string>, line 476)

    Literal block expected; none found.

>>> browser.getLink(id='paste').click()

Portal View still shows all of our content on a single page. Let's

  1. log out:

    System Message: WARNING/2 (<string>, line 482)

    Literal block expected; none found.

>>> browser.getLink('Log out').click()
  1. open the front page:

    System Message: WARNING/2 (<string>, line 486)

    Literal block expected; none found.

>>> browser.open(portal_url + '/frontpage')
  1. and verify that we see all but the rejected content:

    System Message: WARNING/2 (<string>, line 490)

    Literal block expected; none found.

>>> "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
  1. and that subcolumn ids are created correctly:

    System Message: WARNING/2 (<string>, line 507)

    Literal block expected; none found.

>>> "portalview-column-wrapper-center-left-center-right" in browser.contents
True
1.0.3 (2009-11-16)
  • Initial release as a public product.

Subscribe to package updates

Last updated Jan 5th, 2011

Download Stats

Last month:1

What does the lock icon mean?

Builds marked with a lock icon are only available via PyPM to users with a current ActivePython Business Edition subscription.

Need custom builds or support?

ActivePython Enterprise Edition guarantees priority access to technical support, indemnification, expert consulting and quality-assured language builds.

Plan on re-distributing ActivePython?

Get re-distribution rights and eliminate legal risks with ActivePython OEM Edition.