On a Plone 4.x install, can either
- published content be restricted to a certain user/group or
- private content be made to show in menus and searches for not-logged-in users ?
I have a customer who wants to be able to have content that can only be viewed by a certain user/group, but will show up in a menu or search when not logged in.
What would be the best approach for achieving this functionality?
You'll have to customize your workflow as below:
- go to Zope Management Interface-> portal_workflow
- Create a new state, let's say "Trailer" (this is optional, you could customize an existing state instead...maybe the private state would be a good option for dealing with restrictions for specific users/groups)
- Remove all permissions but "Access contents information" from the Anonymous user in that specific state
- Push the "Update security settings" button
Done!
Now all contents in the "Trailer" state will be searchable but not viewable by anonymous users.
Note: if you choose to create a new state, as I'd suggest, be sure to add all needed transitions too.
Edit:
Unfortunately I wasn't aware that in recent Plone's versions, there's a new index in the portal_catalog (allowedRolesAndUsers) that prevents the process above to work as it used to. The process above is still correct, though you'll need to override the default indexer.
First create a new package with paster using the "plone" template. Then add in the main level of the package (e.g. my.package/my/package) a file called indexers.py
with this:
from zope.interface import Interface
from plone.indexer.decorator import indexer
from AccessControl.PermissionRole import rolesForPermissionOn
from Products.CMFCore.utils import getToolByName
from Products.CMFCore.CatalogTool import _mergedLocalRoles
@indexer(Interface)
def allowedRolesAndUsers(obj):
"""Return a list of roles and users with View permission.
Used by PortalCatalog to filter out items you're not allowed to see.
"""
allowed = {}
for r in rolesForPermissionOn('Access contents information', obj):
allowed[r] = 1
# shortcut roles and only index the most basic system role if the object
# is viewable by either of those
if 'Anonymous' in allowed:
return ['Anonymous']
elif 'Authenticated' in allowed:
return ['Authenticated']
localroles = {}
try:
acl_users = getToolByName(obj, 'acl_users', None)
if acl_users is not None:
localroles = acl_users._getAllLocalRoles(obj)
except AttributeError:
localroles = _mergedLocalRoles(obj)
for user, roles in localroles.items():
for role in roles:
if role in allowed:
allowed['user:' + user] = 1
if 'Owner' in allowed:
del allowed['Owner']
return list(allowed.keys())
and then in the same level add a file overrides.zcml
with this:
<configure xmlns="http://namespaces.zope.org/zope">
<adapter factory=".indexers.allowedRolesAndUsers" name="allowedRolesAndUsers" />
</configure>
In the end the tree of your product should look like this:
my.package/
├── my
│ ├── __init__.py
│ └── package
│ ├── configure.zcml
│ ├── overrides.zcml
│ ├── indexers.py
│ ├── __init__.py
│ ├── profiles
│ │ └── default
│ │ └── metadata.xml
│ └── tests.py
├── README.txt
├── setup.cfg
└── setup.py
Last thing, you need to include the newly created egg in your buildout.cfg:
eggs =
my.package
develop =
src/my.package
Rerun buildout. That's all.