Index: /quintagroup.portletmanager/trunk/quintagroup/__init__.py
===================================================================
--- /quintagroup.portletmanager/trunk/quintagroup/__init__.py	(revision 344)
+++ /quintagroup.portletmanager/trunk/quintagroup/__init__.py	(revision 344)
@@ -0,0 +1,6 @@
+# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages
+try:
+    __import__('pkg_resources').declare_namespace(__name__)
+except ImportError:
+    from pkgutil import extend_path
+    __path__ = extend_path(__path__, __name__)
Index: /quintagroup.portletmanager/trunk/quintagroup/portletmanager/browser/configure.zcml
===================================================================
--- /quintagroup.portletmanager/trunk/quintagroup/portletmanager/browser/configure.zcml	(revision 344)
+++ /quintagroup.portletmanager/trunk/quintagroup/portletmanager/browser/configure.zcml	(revision 344)
@@ -0,0 +1,14 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:browser="http://namespaces.zope.org/browser"
+    xmlns:five="http://namespaces.zope.org/five">
+
+  <browser:page
+       for="plone.portlets.interfaces.ILocalPortletAssignable"
+       class=".manage.ManageContextualPortlets"
+       name="manage-portlets"
+       template="templates/manage-contextual.pt"
+       permission="plone.app.portlets.ManagePortlets"
+     />
+</configure>
+
Index: /quintagroup.portletmanager/trunk/quintagroup/portletmanager/browser/manage.py
===================================================================
--- /quintagroup.portletmanager/trunk/quintagroup/portletmanager/browser/manage.py	(revision 344)
+++ /quintagroup.portletmanager/trunk/quintagroup/portletmanager/browser/manage.py	(revision 344)
@@ -0,0 +1,102 @@
+from zope.interface import implements
+from zope.component import getMultiAdapter, getUtility
+from zope.publisher.interfaces.browser import IDefaultBrowserLayer
+
+from AccessControl import Unauthorized
+from Acquisition import aq_inner
+from Acquisition import aq_base
+from Products.Five import BrowserView
+
+from Products.CMFCore.utils import getToolByName
+from Products.CMFCore.interfaces import ISiteRoot
+
+from plone.portlets.interfaces import IPortletManager
+from plone.portlets.interfaces import IPortletAssignmentMapping
+from plone.portlets.interfaces import ILocalPortletAssignmentManager
+
+from plone.portlets.constants import USER_CATEGORY
+from plone.portlets.constants import GROUP_CATEGORY
+from plone.portlets.constants import CONTENT_TYPE_CATEGORY
+from plone.portlets.constants import CONTEXT_CATEGORY
+
+from plone.app.portlets.storage import PortletAssignmentMapping
+from plone.app.portlets.storage import UserPortletAssignmentMapping
+
+from plone.app.portlets.interfaces import IPortletPermissionChecker
+
+from plone.app.portlets.browser.interfaces import IManagePortletsView
+from plone.app.portlets.browser.interfaces import IManageContextualPortletsView
+from plone.app.portlets.browser.interfaces import IManageDashboardPortletsView
+from plone.app.portlets.browser.interfaces import IManageGroupPortletsView
+from plone.app.portlets.browser.interfaces import IManageContentTypePortletsView
+
+from plone.app.portlets import utils
+from plone.memoize.view import memoize
+
+class ManageContextualPortlets(BrowserView):
+    implements(IManageContextualPortletsView)
+    
+    def __init__(self, context, request):
+        super(ManageContextualPortlets, self).__init__(context, request)
+        self.request.set('disable_border', True)
+        
+    # IManagePortletsView implementation
+    
+    @property
+    def category(self):
+        return CONTEXT_CATEGORY
+        
+    @property
+    def key(self):
+        return '/'.join(self.context.getPhysicalPath())
+    
+    def getAssignmentMappingUrl(self, manager):
+        baseUrl = str(getMultiAdapter((self.context, self.request), name='absolute_url'))
+        return '%s/++contextportlets++%s' % (baseUrl, manager.__name__)
+    
+    def getAssignmentsForManager(self, manager):
+        assignments = getMultiAdapter((self.context, manager), IPortletAssignmentMapping)
+        return assignments.values()
+    
+    # view @@manage-portlets
+    
+    def has_legacy_portlets(self):
+        left_slots = getattr(aq_base(self.context), 'left_slots', [])
+        right_slots = getattr(aq_base(self.context), 'right_slots', [])
+        
+        return (left_slots or right_slots)
+
+    # view @@set-portlet-blacklist-status
+    def set_blacklist_status(self, manager, group_status, content_type_status, context_status):
+        portletManager = getUtility(IPortletManager, name=manager)
+        assignable = getMultiAdapter((self.context, portletManager,), ILocalPortletAssignmentManager)
+        assignments = getMultiAdapter((self.context, portletManager), IPortletAssignmentMapping)
+        
+        IPortletPermissionChecker(assignments.__of__(aq_inner(self.context)))()
+        
+        def int2status(status):
+            if status == 0:
+                return None
+            elif status > 0:
+                return True
+            else:
+                return False
+        
+        assignable.setBlacklistStatus(GROUP_CATEGORY, int2status(group_status))
+        assignable.setBlacklistStatus(CONTENT_TYPE_CATEGORY, int2status(content_type_status))
+        assignable.setBlacklistStatus(CONTEXT_CATEGORY, int2status(context_status))
+        
+        baseUrl = str(getMultiAdapter((self.context, self.request), name='absolute_url'))
+        self.request.response.redirect(baseUrl + '/@@manage-portlets')
+        return ''
+    
+    # view @@convert-legacy-portlets
+    
+    def convert_legacy_portlets(self):
+        utils.convert_legacy_portlets(self.context)
+        self.context.request.response.redirect(self.context.absolute_url() + '/@@manage-portlets')
+    
+    def is_portal_root(self):
+        context = aq_inner(self.context)
+        portal = getUtility(ISiteRoot)
+        return aq_base(context) is aq_base(portal)
Index: /quintagroup.portletmanager/trunk/quintagroup/portletmanager/browser/templates/column.pt
===================================================================
--- /quintagroup.portletmanager/trunk/quintagroup/portletmanager/browser/templates/column.pt	(revision 344)
+++ /quintagroup.portletmanager/trunk/quintagroup/portletmanager/browser/templates/column.pt	(revision 344)
@@ -0,0 +1,14 @@
+<tal:block repeat="portlet options/portlets">
+<div tal:attributes="id string:portletwrapper-${portlet/hash};
+                     class string:portletWrapper kssattr-portlethash-${portlet/hash};"
+     tal:content="structure python:view.safe_render(portlet['renderer'])" />
+</tal:block>
+
+<div class="managePortletsLink" tal:condition="view/can_manage_portlets"
+      i18n:domain="plone">
+  <a title="View portlet management screen"
+     i18n:attributes="title"
+     i18n:translate="manage_portlets_link"
+     tal:attributes="href string:${view/base_url}/@@manage-portlets"
+     >Manage portlets</a>
+</div>
Index: /quintagroup.portletmanager/trunk/quintagroup/portletmanager/browser/templates/error_message.pt
===================================================================
--- /quintagroup.portletmanager/trunk/quintagroup/portletmanager/browser/templates/error_message.pt	(revision 344)
+++ /quintagroup.portletmanager/trunk/quintagroup/portletmanager/browser/templates/error_message.pt	(revision 344)
@@ -0,0 +1,20 @@
+<dl class="portlet portletError"
+    i18n:domain="plone">
+
+    <dt class="portletHeader">
+        <span class="portletTopLeft"></span>
+        <a href="#" 
+           class="tile"
+           i18n:translate="box_error">Error</a>
+        <span class="portletTopRight"></span>
+    </dt>
+
+    <dd i18n:translate="text_error_rendering_portlet">
+        There was an error while rendering the portlet.
+    </dd>
+
+    <dd class="portletFooter">
+        <span class="portletBottomLeft"></span>
+        <span class="portletBottomRight"></span>
+    </dd>
+</dl>
Index: /quintagroup.portletmanager/trunk/quintagroup/portletmanager/browser/templates/manage-contextual.pt
===================================================================
--- /quintagroup.portletmanager/trunk/quintagroup/portletmanager/browser/templates/manage-contextual.pt	(revision 344)
+++ /quintagroup.portletmanager/trunk/quintagroup/portletmanager/browser/templates/manage-contextual.pt	(revision 344)
@@ -0,0 +1,88 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:metal="http://xml.zope.org/namespaces/metal"
+      xmlns:tal="http://xml.zope.org/namespaces/tal"
+      xmlns:i18n="http://xml.zope.org/namespaces/i18n"
+      metal:use-macro="context/main_template/macros/master"
+      i18n:domain="plone">
+<head>
+    <div metal:fill-slot="javascript_head_slot" tal:omit-tag="">
+        <link type="text/css" rel="kinetic-stylesheet"
+            tal:attributes="href string:${context/absolute_url}/++resource++manage-portlets.kss"/>
+    </div>
+</head>
+
+<body>
+<div metal:fill-slot="main" tal:define="ploneview context/@@plone;
+                                        parent context/@@plone_context_state/parent">
+
+  <h1 class="documentFirstHeading"
+      i18n:translate="title_manage_contextual_portlets">
+      <img i18n:name="context_icon" tal:replace="structure python:ploneview.getIcon(context).html_tag()" />
+      Manage portlets for
+      "<span i18n:name="context_title" tal:content="context/Title" />"
+  </h1>
+  <dl class="portalMessage info"
+      tal:condition="here/@@plone/isDefaultPageInFolder">
+      <dt i18n:translate="">
+          Info
+      </dt>
+      <dd i18n:translate="label_edit_default_view_container">
+           You are managing portlets for the default view of a container.
+           If you wanted to manage portlets for the container itself,
+         <a href=""
+            i18n:name="go_here"
+            i18n:translate="label_edit_default_view_container_go_here"
+            tal:attributes="href string:${context/aq_inner/aq_parent/absolute_url}/@@manage-portlets">go here</a>.
+      </dd>
+  </dl>
+
+  <a href=""
+       class="link-parent"
+       tal:attributes="href string:${parent/absolute_url}/@@manage-portlets"
+       tal:condition="not:view/is_portal_root"
+       i18n:translate="return_to_view">
+    Up
+  </a>
+  <a href=""
+       tal:attributes="href string:${context/absolute_url}"
+       i18n:translate="return_to_view">
+    &laquo; Return
+  </a>
+
+  <p i18n:translate="description_manage_contextual_portlets">
+    The portlet columns will now display only those portlets
+    explicitly assigned in this context. Use the buttons on each portlet 
+    to move them up or down, delete or edit them. To add a new portlet,
+    use the drop-down list at the top of the column.
+  </p>
+
+  <p i18n:translate="description_portlets_block_unblock">
+      If you wish to block or unblock certain categories of portlets, you can 
+      do so using the drop-down boxes.
+  </p>
+
+  <div tal:condition="view/has_legacy_portlets">
+
+    <h2 i18n:translate="title_legacy_portlets">Legacy portlets</h2>
+
+    <p i18n:translate="action_convert_legacy_portlets">
+      There are legacy portlets defined here. Click the button to
+      convert them to new-style portlets.
+    </p>
+
+    <form method="POST"
+          tal:attributes="action string:${context/absolute_url}/@@convert-legacy-portlets">
+
+        <input type="submit"
+               class="context"
+               value="Convert portlets"
+               i18n:attributes="value label_convert_portlets"
+               name="convert" />
+    
+    </form>
+
+  </div>
+
+</div>
+</body>
+</html>
Index: /quintagroup.portletmanager/trunk/quintagroup/portletmanager/manager.py
===================================================================
--- /quintagroup.portletmanager/trunk/quintagroup/portletmanager/manager.py	(revision 344)
+++ /quintagroup.portletmanager/trunk/quintagroup/portletmanager/manager.py	(revision 344)
@@ -0,0 +1,55 @@
+import sys
+from ZODB.POSException import ConflictError
+
+from zope.interface import Interface
+from zope.component import adapts, getMultiAdapter
+
+from zope.publisher.interfaces.browser import IBrowserView
+from zope.publisher.interfaces.browser import IDefaultBrowserLayer
+
+from Acquisition import  aq_inner,  aq_acquire
+from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
+from Products.CMFCore.utils import getToolByName
+
+from plone.portlets.interfaces import  ILocalPortletAssignable
+
+from plone.app.portlets.interfaces import IColumn
+from plone.app.portlets.interfaces import IDashboard
+from plone.app.portlets.manager import PortletManagerRenderer
+
+import logging
+logger = logging.getLogger('portlets')
+
+class ColumnPortletManagerRenderer(PortletManagerRenderer):
+    """A renderer for the column portlets
+    """
+    adapts(Interface, IDefaultBrowserLayer, IBrowserView, IColumn)
+    template = ViewPageTemplateFile('browser/templates/column.pt')
+    error_message = ViewPageTemplateFile('browser/templates/error_message.pt')
+
+    def _context(self):
+        context = aq_inner(self.context)
+        return context
+
+    def base_url(self):
+        """If context is a default-page, return URL of folder, else
+        return URL of context.
+        """
+        return str(getMultiAdapter((self._context(), self.request,), name=u'absolute_url'))
+
+    def can_manage_portlets(self):
+        context = self._context()
+        if not ILocalPortletAssignable.providedBy(context):
+            return False
+        mtool = getToolByName(context, 'portal_membership')
+        return mtool.checkPermission("Portlets: Manage portlets", context)
+
+    def safe_render(self, portlet_renderer):
+        try:
+            return portlet_renderer.render()
+        except ConflictError:
+            raise
+        except Exception:
+            logger.exception('Error while rendering %r' % (self,))
+            aq_acquire(self, 'error_log').raising(sys.exc_info())
+            return self.error_message()
Index: /quintagroup.portletmanager/trunk/quintagroup/portletmanager/overrides.zcml
===================================================================
--- /quintagroup.portletmanager/trunk/quintagroup/portletmanager/overrides.zcml	(revision 344)
+++ /quintagroup.portletmanager/trunk/quintagroup/portletmanager/overrides.zcml	(revision 344)
@@ -0,0 +1,13 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:browser="http://namespaces.zope.org/browser"
+    xmlns:five="http://namespaces.zope.org/five">
+
+  <include package=".browser"/>
+
+  <adapter 
+        factory=".manager.ColumnPortletManagerRenderer"
+        provides="plone.portlets.interfaces.IPortletManagerRenderer"
+        />
+
+</configure>
Index: /quintagroup.portletmanager/trunk/setup.py
===================================================================
--- /quintagroup.portletmanager/trunk/setup.py	(revision 344)
+++ /quintagroup.portletmanager/trunk/setup.py	(revision 344)
@@ -0,0 +1,33 @@
+from setuptools import setup, find_packages
+
+version = '0.1.0'
+
+setup(name='quintagroup.portletmanager',
+      version=version,
+      description="Quintagroup portlet manager",
+      long_description="""Quintagroup portlet manager""",
+      # Get more strings from http://www.python.org/pypi?%3Aaction=list_classifiers
+      classifiers=[
+        "Framework :: Plone",
+        "Framework :: Zope2",
+        "Framework :: Zope3",
+        "Programming Language :: Python",
+        "Topic :: Software Development :: Libraries :: Python Modules",
+        ],
+      keywords='quintagroup portlet manager',
+      author='Taras Melnychuk',
+      author_email='fenix@quintagroup.com',
+      url='http://svn.quintagroup.com/products/quintagroup.portletmanager/trunk/',
+      license='GPL',
+      packages=find_packages(exclude=['ez_setup']),
+      namespace_packages=['quintagroup'],
+      include_package_data=True,
+      zip_safe=False,
+      install_requires=[
+          'setuptools',
+          # -*- Extra requirements: -*-
+      ],
+      entry_points="""
+      # -*- Entry points: -*-
+      """,
+      )
