Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Add support for multiple panel locations, currently limited to above …

…and below page contents, and the portal top and footer locations. This fixes issue #2.
  • Loading branch information...
commit 920691158079a2383e73856fb75d41d0a85fbd45 1 parent 827159c
@malthe malthe authored
View
121 README.rst
@@ -1,26 +1,115 @@
-Overview
-========
+Panels are sets of portlets appearing in various layout configurations
+which you can insert into a selection of Plone's existing locations
+(above and below page contents, portal top and footer).
-This add-on extends Plone with "portlet panels", where a panel is a
-container for a number of portlet assignments.
+It aims to render a number of existing add-ons obsolete, including
+`Collage <http://pypi.python.org/pypi/Products.Collage>`_,
+`collective.portletpage
+<http://pypi.python.org/pypi/collective.portletpage>`_ and
+`Products.ContentWellPortlets
+<http://pypi.python.org/pypi/Products.ContentWellPortlets>`_. The
+functionality represented by these add-ons is mostly available in
+panels, too, and comes in an implementation that builds directly on
+Plone's portlets framework.
+
+Compatibility: Plone 4+ required.
+
+
+Introduction
+============
+
+You often have a need to add supplemental content to existing pages,
+content items or folders.
+
+The included portlets infrastructure serves some, but not all of these
+needs, letting you add portlets in the left and right column, which
+are inherited down the content hierarchy.
+
+Panels provide a simple mechanism to add portlets to additional
+locations, without inheritance, and let you display portlets in
+different layouts.
+
+
+Examples
+--------
+
+*Front page*
+
+ Instead of, or in addition to, a page acting as the default
+ content on a folder, you can create a collage of portlets and
+ display it below an introductory text.
+
+ There's a lot of flexibility because you can integrate the panel
+ display with Plone's built-in content views. For instance, you
+ could provide a search interface in addition to the standard
+ folder listing.
+
+
+*Supplement static content*
+
+ Use panels to add portlets above or below a static page.
+
+
+*Features or advertisement*
+
+ Add panels to the 'portal top' location which by default renders
+ just below the section navigation for an impressive effect.
+
+
+Documentation
+=============
Usage
-----
-Panels can be added to any viewlet manager in principle, although in
-the default setup, the panel management interface is registered only
-for the "below content" manager.
+To create a new panel, or manage existing ones, the editor clicks the
+"Manage panels" link appearing in the footer (it links to
+``@@manage-panels``) — similar to Plone's column portlets.
+
+The management view is an overlay of the default content view.
+
+*Creating a new panel*
+
+ To create a new panel, there's a collapsible form appearing just below
+ the page title ("Add panel").
+
+ If no panels exist already, the add form appears open to begin
+ with.
+
+ Then, simply choose a location (a default choice is already
+ provided), and select a layout. Then add portlets using the
+ "Manage panel" form (see next).
+
+*Managing existing panels*
+
+ In the management interface, a collapsible form appears below
+ existing panels ("Manage panel").
+
+ This is similar to Plone's standard portlet interface and in fact
+ uses it directly.
+
+ It provides options to add a new portlet, change panel layout or
+ manage existing portlet assignments.
+
+
+Locations
+---------
+
+The management interface checks with Plone's viewlet visibility
+settings to list only the applicable adding locations. This allows an
+administrator to visit the portal's ``@@manage-viewlets`` screen and
+put a restriction on panel locations.
+
+Adding additional locations is currently not supported. The limitation
+here is that Plone's viewlet manager framework does not provide
+labels, or other enumeration.
-To manage panels, there's a "Manage panels" link available in the
-portal footer, similar to Plone's portlet management links. Clicking
-this link adds a panel management interface to the default content
-view.
Layouts
-------
There's a choice of layout for each panel, selected when the panel is
-first added with an option to change later on. These layouts are
+first added, with an option to change later on. These layouts are
registered as components using an included ZCML-directive.
Use the directive to add additional layouts::
@@ -32,11 +121,15 @@ Use the directive to add additional layouts::
layer=".interfaces.IThemeSpecific"
/>
-The package includes a layout that arranges portlets horizontally,
-automatically adjusting to equal width.
+
+License
+-------
+
+GPLv3 (http://www.gnu.org/licenses/gpl.html).
Author
------
Malthe Borch <mborch@gmail.com>
+
View
11 setup.py
@@ -1,15 +1,20 @@
import os
+import sys
+
+reload(sys).setdefaultencoding("UTF-8")
+
from setuptools import setup, find_packages
def read(*pathnames):
- return open(os.path.join(os.path.dirname(__file__), *pathnames)).read()
+ return open(os.path.join(os.path.dirname(__file__), *pathnames)).read().\
+ decode('utf-8')
version = '1.0-dev'
setup(name='collective.panels',
version=version,
- description="Portlet panels for Plone.",
+ description="Add-on for Plone that adds portlet panels.",
long_description='\n'.join([
read('README.rst'),
read('CHANGES.rst'),
@@ -22,7 +27,7 @@ def read(*pathnames):
keywords='plone portlets',
author='Malthe Borch',
author_email='mborch@gmail.com',
- license='GPL',
+ license="GPLv3+",
packages=find_packages('src'),
package_dir={'': 'src'},
namespace_packages=['collective'],
View
38 src/collective/panels/adding.pt
@@ -1,20 +1,35 @@
<div class="panels" i18n:domain="collective.panels" tal:condition="view/can_add">
- <dl tal:define="collapsed python: ' collapsedOnLoad' if view.has_panels else ''"
+ <dl tal:define="adding_id python: 'adding-%d' % id(view);
+ collapsed python: ' collapsedOnLoad' if view.has_panels else ''"
tal:attributes="class string:collapsible${collapsed}">
<dt class="collapsibleHeader" i18n:translate="">Add panel</dt>
- <dd class="collapsibleContent"
- tal:define="manager view/default_viewlet_manager">
+ <dd class="collapsibleContent">
<form action="#">
<div class="field">
- <label class="horizontal" for="manager">Location</label>
+ <label class="horizontal" for="location">Location</label>
<div class="formHelp" i18n:translate="">
Select the location where a new panel should be added.
</div>
- <select name="manager">
- <option tal:attributes="value manager/name">
- <tal:title i18n:translate="" tal:content="manager/title" />
- &nbsp;
- </option>
+ <select name="location"
+ tal:attributes="onchange string:(function($, selected) {
+ var links = $('#${adding_id} a'),
+ name = selected.value
+ $.each(links, function(i, j) {
+ var link = $(j)
+ var href = link.attr('href')
+ var k = href.lastIndexOf('++')
+ var l = href.lastIndexOf('/')
+ href = href.substring(0, k) +
+ '++' + name + '/+/' +
+ href.substring(l)
+ $(link).attr('href', href)
+ })
+ })(jQuery, this.options[this.selectedIndex]);;
+ this.blur()">
+ <option tal:define="default_location view/default_location"
+ tal:repeat="location view/available_locations"
+ tal:attributes="value location/name;
+ selected python: location == default_location"><tal:title i18n:translate="" tal:content="location/title" />&nbsp;</option>
</select>
</div>
</form>
@@ -22,7 +37,10 @@
<label class="horizontal">Layout</label>
<div class="formHelp">Select a panel layout.</div>
</div>
- <dl class="add-panel" tal:define="add_url string:${context/absolute_url}/++panel++${view/default_viewlet_manager/name}/+">
+ <dl class="add-panel"
+ tal:define="name view/default_location/name;
+ add_url string:${context/absolute_url}/++panel++${name}/+"
+ tal:attributes="id adding_id">
<tal:panels repeat="layout view/available_layouts">
<dt class="hiddenStructure" tal:content="layout/title" />
<dd>
View
92 src/collective/panels/browser.py
@@ -6,9 +6,12 @@
from plone.portlets.constants import CONTEXT_CATEGORY
from plone.app.portlets.browser.editmanager import EditPortletManagerRenderer
-from plone.app.layout.viewlets.interfaces import IBelowContentBody
+from plone.app.viewletmanager.interfaces import IViewletSettingsStorage
+from plone.app.layout.viewlets import interfaces
+from plone.app.layout.viewlets import ViewletBase
from plone.memoize.ram import cache
+from plone.memoize.view import memoize
from plone.protect import protect
from plone.protect import PostOnly
from plone.protect import CheckAuthenticator
@@ -19,28 +22,26 @@
from zope.component import getAdapters
from zope.component import getMultiAdapter
from zope.component import getSiteManager
+from zope.component import getUtility
from zope.component import ComponentLookupError
+from zope.security import checkPermission
+from zope.viewlet.interfaces import IViewlet
from AccessControl import getSecurityManager
from Products.Five.browser import BrowserView
-from Products.statusmessages.interfaces import IStatusMessage
-
-from .i18n import MessageFactory as _
-
-from zope.security import checkPermission
-
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
-from plone.app.layout.viewlets import ViewletBase
+from Products.statusmessages.interfaces import IStatusMessage
from .interfaces import ILayout
from .interfaces import IManagePanels
from .traversal import PanelManager
from .traversal import encode
+from .i18n import MessageFactory as _
def addable_portlets_cache_key(function, view):
roles = getSecurityManager().getUser().getRoles()
- return set(roles), view.manager.__name__
+ return set(roles), view.__parent__.__name__
def batch(iterable, size):
@@ -224,9 +225,14 @@ def can_manage(self):
class AddingViewlet(BaseViewlet):
index = ViewPageTemplateFile("adding.pt")
- available_viewlet_managers = {
- IBelowContentBody: _(u"Below page content"),
- }
+ # Order is important here; the default location will be the first
+ # available (non-hidden) manager.
+ all_viewlet_managers = (
+ (interfaces.IBelowContentBody, _(u"Below page content")),
+ (interfaces.IAboveContentBody, _(u"Above page content")),
+ (interfaces.IPortalFooter, _(u"Portal footer")),
+ (interfaces.IPortalTop, _(u"Portal top")),
+ )
@property
def available_layouts(self):
@@ -240,7 +246,7 @@ def can_add(self):
#
# This gets pretty esoteric when there's more than one viewlet
# manager in play, but it's convenient for the case of one.
- for manager in self.iter_panel_managers():
+ for manager in self._iter_panel_managers():
for panel in manager:
if len(panel) == 0:
break
@@ -250,8 +256,8 @@ def can_add(self):
return False
@property
- def default_viewlet_manager(self):
- for data in self.encode_viewlet_managers():
+ def default_location(self):
+ for data in self.available_locations:
return data
@property
@@ -259,35 +265,59 @@ def has_panels(self):
# This is used to determine whether to initially collapse the
# interface to add new panels. It should be collapsed if
# there's a panel defined already.
- for manager in self.iter_panel_managers():
+ for manager in self._iter_panel_managers():
for panel in manager:
return True
return False
- def iter_panel_managers(self):
- for name, iface in self.iter_viewlet_managers():
- name = encode(name)
- yield PanelManager(self.context, self.request, name)
+ @property
+ @memoize
+ def available_locations(self):
+ return list(self._iter_locations())
- def iter_viewlet_managers(self):
+ @property
+ @memoize
+ def _lookup(self):
gsm = getSiteManager()
- lookup = gsm.adapters.lookupAll
+ return gsm.adapters.lookupAll
+ def _iter_locations(self):
+ for name, title in self._iter_viewlet_managers():
+ yield {
+ 'name': encode(name),
+ 'title': title,
+ }
+
+ def _iter_panel_managers(self):
+ for name, title in self._iter_viewlet_managers():
+ name = encode(name)
+ yield PanelManager(self.context, self.request, name)
+
+ def _iter_viewlet_managers(self):
spec = tuple(map(providedBy, (
self.context, self.request, self.__parent__
)))
- for iface in self.available_viewlet_managers:
- for name, factory in lookup(spec, iface):
- yield name, iface
+ storage = getUtility(IViewletSettingsStorage)
+ skinname = self.context.getCurrentSkinName()
- def encode_viewlet_managers(self):
- for name, iface in self.iter_viewlet_managers():
- yield {
- 'name': encode(name),
- 'title': self.available_viewlet_managers[iface],
- }
+ for viewlet, iface, title in self._iter_enabled_viewlet_managers():
+ for name, factory in self._lookup(spec, iface):
+ hidden = storage.getHidden(name, skinname)
+
+ if viewlet not in hidden:
+ yield name, title
+
+ def _iter_enabled_viewlet_managers(self):
+ spec = tuple(map(providedBy, (
+ self.context, self.request, self.__parent__
+ )))
+
+ for iface, title in self.all_viewlet_managers:
+ for name, factory in self._lookup(spec + (iface, ), IViewlet):
+ if issubclass(factory, DisplayViewlet):
+ yield name, iface, title
class DisplayViewlet(BaseViewlet):
View
36 src/collective/panels/configure.zcml
@@ -40,13 +40,40 @@
permission="zope2.View"
/>
+ <!-- Panel display viewlets (possible locations) -->
+
<browser:viewlet
+ for="zope.annotation.interfaces.IAnnotatable"
+ name="collective.panels.abovecontent"
+ manager="plone.app.layout.viewlets.interfaces.IAboveContentBody"
+ class=".browser.DisplayViewlet"
+ permission="zope2.View"
+ />
+
+ <browser:viewlet
+ for="zope.annotation.interfaces.IAnnotatable"
name="collective.panels.belowcontent"
manager="plone.app.layout.viewlets.interfaces.IBelowContentBody"
class=".browser.DisplayViewlet"
permission="zope2.View"
/>
+ <browser:viewlet
+ for="zope.annotation.interfaces.IAnnotatable"
+ name="collective.panels.portalfooter"
+ manager="plone.app.layout.viewlets.interfaces.IPortalFooter"
+ class=".browser.DisplayViewlet"
+ permission="zope2.View"
+ />
+
+ <browser:viewlet
+ for="zope.annotation.interfaces.IAnnotatable"
+ name="collective.panels.portaltop"
+ manager="plone.app.layout.viewlets.interfaces.IPortalTop"
+ class=".browser.DisplayViewlet"
+ permission="zope2.View"
+ />
+
<!-- Views -->
<browser:pages
@@ -71,6 +98,15 @@
</browser:pages>
+ <configure package="plone.app.portlets.browser">
+ <browser:page
+ for="collective.panels.content.Panel"
+ name="manage-portlets-macros"
+ template="templates/edit-manager-macros.pt"
+ permission="plone.app.portlets.ManageOwnPortlets"
+ />
+ </configure>
+
<browser:page
name="display"
for=".content.Panel"
Please sign in to comment.
Something went wrong with that request. Please try again.