Skip to content

Commit

Permalink
Merge pull request #2612 from plone/dx-siteroot-master
Browse files Browse the repository at this point in the history
PLIP 2454 DX Plone Site
  • Loading branch information
mauritsvanrees authored Sep 11, 2021
2 parents 8eecde1 + 6918a1a commit 06db2c5
Show file tree
Hide file tree
Showing 15 changed files with 190 additions and 97 deletions.
109 changes: 88 additions & 21 deletions Products/CMFPlone/Portal.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
from AccessControl import ClassSecurityInfo
from AccessControl import Permissions
from AccessControl import Unauthorized
from AccessControl.class_init import InitializeClass
from Acquisition import aq_base
from ComputedAttribute import ComputedAttribute
from five.localsitemanager.registry import PersistentComponents
from OFS.ObjectManager import REPLACEABLE
from plone.dexterity.content import Container
from plone.i18n.locales.interfaces import IMetadataLanguageAvailability
from Products.CMFCore import permissions
from Products.CMFCore.interfaces import IContentish
from Products.CMFCore.interfaces import ISiteRoot
from Products.CMFCore.permissions import AccessContentsInformation
from Products.CMFCore.permissions import AddPortalMember
from Products.CMFCore.permissions import MailForgottenPassword
from Products.CMFCore.permissions import RequestReview
from Products.CMFCore.permissions import ReviewPortalContent
from Products.CMFCore.permissions import SetOwnPassword
from Products.CMFCore.permissions import SetOwnProperties
from Products.CMFCore.PortalFolder import PortalFolderBase
from Products.CMFCore.PortalObject import PortalObjectBase
from Products.CMFCore.utils import UniqueObject
from Products.CMFCore.Skinnable import SkinnableObjectManager
from Products.CMFCore.utils import _checkPermission
from Products.CMFCore.utils import getToolByName
from Products.CMFDynamicViewFTI.browserdefault import BrowserDefaultMixin
from Products.CMFPlone import PloneMessageFactory as _
from Products.CMFCore.utils import UniqueObject
from Products.CMFPlone import bbb
from Products.CMFPlone.DublinCore import DefaultDublinCoreImpl
from Products.CMFPlone.PloneFolder import OrderedContainer
from Products.CMFPlone import PloneMessageFactory as _
from Products.CMFPlone.interfaces.siteroot import IPloneSiteRoot
from Products.CMFPlone.interfaces.syndication import ISyndicatable
from Products.CMFPlone.permissions import AddPortalContent
Expand All @@ -23,27 +33,67 @@
from Products.CMFPlone.permissions import ModifyPortalContent
from Products.CMFPlone.permissions import ReplyToItem
from Products.CMFPlone.permissions import View
from plone.i18n.locales.interfaces import IMetadataLanguageAvailability
from Products.Five.component.interfaces import IObjectManagerSite
from zope.component import queryUtility
from zope.component.interfaces import ComponentLookupError
from zope.event import notify
from zope.interface import classImplementsOnly
from zope.interface import implementedBy
from zope.interface import implementer
from zope.traversing.interfaces import BeforeTraverseEvent


if bbb.HAS_ZSERVER:
from webdav.NullResource import NullResource


@implementer(IPloneSiteRoot, ISyndicatable)
class PloneSite(PortalObjectBase, DefaultDublinCoreImpl, OrderedContainer,
BrowserDefaultMixin, UniqueObject):
@implementer(IPloneSiteRoot, ISiteRoot, ISyndicatable, IObjectManagerSite)
class PloneSite(Container, SkinnableObjectManager, UniqueObject):
""" The Plone site object. """

security = ClassSecurityInfo()
meta_type = portal_type = 'Plone Site'

# Ensure certain attributes come from the correct base class.
_checkId = SkinnableObjectManager._checkId
manage_main = PortalFolderBase.manage_main

def __getattr__(self, name):
try:
# Try DX
return super().__getattr__(name)
except AttributeError:
# Check portal_skins
return SkinnableObjectManager.__getattr__(self, name)

def __setattr__(self, name, obj):
# handle re setting an item as an attribute
if self._tree is not None and name in self:
del self[name]
self[name] = obj
else:
super().__setattr__(name, obj)

def __delattr__(self, name):
try:
return super().__delattr__(name)
except AttributeError:
return self.__delitem__(name)

# Removes the 'Components Folder'

manage_options = (
PortalObjectBase.manage_options[:2] +
PortalObjectBase.manage_options[3:])
Container.manage_options[:2] +
Container.manage_options[3:])

__ac_permissions__ = (
(AccessContentsInformation, ()),
(AddPortalMember, ()),
(SetOwnPassword, ()),
(SetOwnProperties, ()),
(MailForgottenPassword, ()),
(RequestReview, ()),
(ReviewPortalContent, ()),
(AddPortalContent, ()),
(AddPortalFolders, ()),
(ListPortalMembers, ()),
Expand All @@ -53,13 +103,6 @@ class PloneSite(PortalObjectBase, DefaultDublinCoreImpl, OrderedContainer,
'manage_renameForm', 'manage_renameObject',
'manage_renameObjects')))

security.declareProtected(Permissions.copy_or_move, 'manage_copyObjects')

manage_renameObject = OrderedContainer.manage_renameObject

moveObject = OrderedContainer.moveObject
moveObjectsByDelta = OrderedContainer.moveObjectsByDelta

# Switch off ZMI ordering interface as it assumes a slightly
# different functionality
has_order_support = 0
Expand All @@ -73,9 +116,28 @@ class PloneSite(PortalObjectBase, DefaultDublinCoreImpl, OrderedContainer,
description = ''
icon = 'misc_/CMFPlone/tool.gif'

# From PortalObjectBase
def __init__(self, id, title=''):
PortalObjectBase.__init__(self, id, title)
DefaultDublinCoreImpl.__init__(self)
super(PloneSite, self).__init__(id, title=title)
components = PersistentComponents('++etc++site')
components.__parent__ = self
self.setSiteManager(components)

# From PortalObjectBase
def __before_publishing_traverse__(self, arg1, arg2=None):
""" Pre-traversal hook.
"""
# XXX hack around a bug(?) in BeforeTraverse.MultiHook
REQUEST = arg2 or arg1

try:
notify(BeforeTraverseEvent(self, REQUEST))
except ComponentLookupError:
# allow ZMI access, even if the portal's site manager is missing
pass
self.setupCurrentSkin(REQUEST)

super(PloneSite, self).__before_publishing_traverse__(arg1, arg2)

def __browser_default__(self, request):
""" Set default so we can return whatever we want instead
Expand Down Expand Up @@ -173,4 +235,9 @@ def reindexObject(self, idxs=None):
def reindexObjectSecurity(self, skip_self=False):
pass


# Remove the IContentish interface so we don't listen to events that won't
# apply to the site root, ie handleUidAnnotationEvent
classImplementsOnly(PloneSite, implementedBy(PloneSite) - IContentish)

InitializeClass(PloneSite)
7 changes: 6 additions & 1 deletion Products/CMFPlone/browser/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,12 @@ def sites(self, root=None):
return result

def outdated(self, obj):
mig = obj.get('portal_migration', None)
# Try to pick the portal_migration as an attribute
# (Plone 5 unmigrated site root) or as an item
mig = (
getattr(obj, "portal_migration", None)
or obj.get('portal_migration', None)
)
if mig is not None:
return mig.needUpgrading()
return False
Expand Down
1 change: 1 addition & 0 deletions Products/CMFPlone/configure.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
<include package="plone.app.vocabularies" />
<include package="plone.app.workflow" />
<include package="plone.batching" />
<include package="plone.dexterity" />
<include package="plone.memoize" />
<include package="plone.outputfilters" />
<include package="plone.session" />
Expand Down
11 changes: 9 additions & 2 deletions Products/CMFPlone/factory.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from logging import getLogger
from plone.registry.interfaces import IRegistry
from plone.uuid.handlers import addAttributeUUID
from Products.CMFCore.interfaces import ISiteRoot
from Products.CMFPlone import PloneMessageFactory as _
from Products.CMFPlone.events import SiteManagerCreatedEvent
from Products.CMFPlone.interfaces import INonInstallable
Expand All @@ -10,6 +12,7 @@
from zope.component.hooks import setSite
from zope.event import notify
from zope.interface import implementer
from zope.lifecycleevent import ObjectCreatedEvent

_TOOL_ID = 'portal_setup'
_DEFAULT_PROFILE = 'Products.CMFPlone:plone'
Expand Down Expand Up @@ -120,8 +123,12 @@ def addPloneSite(context, site_id, title='Plone site', description='',
extension_ids=(), setup_content=True,
default_language='en', portal_timezone='UTC'):
"""Add a PloneSite to the context."""
context._setObject(site_id, PloneSite(site_id))
site = context._getOb(site_id)

site = PloneSite(site_id)
notify(ObjectCreatedEvent(site))
context[site_id] = site

site = context[site_id]
site.setLanguage(default_language)
# Set the accepted language for the rest of the request. This makes sure
# the front-page text gets the correct translation also when your browser
Expand Down
3 changes: 1 addition & 2 deletions Products/CMFPlone/profiles/default/types.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
<property
name="title">Controls the available content types in your portal</property>
<object name="Discussion Item" meta_type="Factory-based Type Information"/>
<object name="Plone Site"
meta_type="Factory-based Type Information with dynamic views"/>
<object name="TempFolder" meta_type="Factory-based Type Information"/>
<object name="Plone Site" meta_type="Dexterity FTI" />
</object>
93 changes: 69 additions & 24 deletions Products/CMFPlone/profiles/default/types/Plone_Site.xml
Original file line number Diff line number Diff line change
@@ -1,34 +1,79 @@
<?xml version="1.0"?>
<object name="Plone Site"
meta_type="Factory-based Type Information with dynamic views"
i18n:domain="plone" xmlns:i18n="http://xml.zope.org/namespaces/i18n">
<property name="title" i18n:translate=""></property>
<property name="description"
i18n:translate="">The root object in a Plone site.</property>
<property name="icon_expr"></property>
<property name="content_meta_type">Plone Site</property>
<property name="product">CMFPlone</property>
<property name="factory">manage_addSite</property>
<property name="immediate_view">folder_listing</property>
<property name="global_allow">False</property>
<property name="filter_content_types">False</property>
<property name="allowed_content_types"/>
<property name="allow_discussion">False</property>
<property name="default_view">folder_listing</property>
<property name="view_methods">
<element value="folder_listing"/>
<element value="folder_summary_view"/>
<element value="folder_full_view"/>
<element value="folder_tabular_view"/>
<element value="atct_album_view"/>
</property>
<object
i18n:domain="plone"
meta_type="Dexterity FTI"
name="Plone Site"
xmlns:i18n="http://xml.zope.org/namespaces/i18n">

<!-- Basic properties -->
<property
i18n:translate=""
name="title">Plone Site</property>
<property
i18n:translate=""
name="description"></property>

<property name="allow_discussion">False</property>
<property name="factory">manage_addSite</property>
<property name="icon_expr"></property>
<property name="link_target"></property>

<!-- Hierarchy control -->
<property name="allowed_content_types"/>
<property name="filter_content_types">False</property>
<property name="global_allow">False</property>

<!-- Schema, class and security -->
<property name="add_permission">cmf.AddPortalContent</property>
<property name="klass">Products.CMFPlone.Portal.PloneSite</property>
<property name="model_file">plone.app.contenttypes.schema:folder.xml</property>
<property name="model_source"></property>
<property name="schema"></property>

<!-- Enabled behaviors -->
<property name="behaviors" purge="false">
<element value="plone.dublincore" />
<element value="plone.richtext" />
<element value="plone.relateditems" />
<element value="plone.locking" />
<element value="plone.allowdiscussion" />
<element value="plone.excludefromnavigation" />
<element value="plone.tableofcontents" />
</property>

<!-- View information -->
<property name="add_view_expr">string:${folder_url}/addPlone Site</property>
<property name="default_view">listing_view</property>
<property name="default_view_fallback">False</property>
<property name="immediate_view">view</property>
<property name="view_methods">
<element value="album_view"/>
<element value="event_listing"/>
<element value="full_view"/>
<element value="listing_view"/>
<element value="summary_view"/>
<element value="tabular_view"/>
</property>

<!-- Method aliases -->
<alias from="(Default)" to="(dynamic view)"/>
<alias from="edit" to="@@site-controlpanel"/>
<alias from="edit" to="@@edit"/>
<alias from="sharing" to="@@sharing"/>
<alias from="view" to="(selected layout)"/>
<action title="View" action_id="view" category="object" condition_expr=""
url_expr="string:${object_url}" visible="True"
i18n:attributes="title">
<permission value="View"/>
</action>
<action
action_id="edit"
category="object"
condition_expr="not:object/@@plone_lock_info/is_locked_for_current_user|python:True"
i18n:attributes="title"
title="Edit"
url_expr="string:${object_url}/edit"
visible="True">
<permission value="Modify portal content"/>
</action>

</object>
24 changes: 0 additions & 24 deletions Products/CMFPlone/skins/plone_templates/index_html.pt

This file was deleted.

This file was deleted.

2 changes: 1 addition & 1 deletion Products/CMFPlone/tests/testActionsTool.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def testMissingActionProvider(self):
self.fail_tb('Should not bomb out if a provider is missing')

def testBrokenActionProvider(self):
self.portal.portal_types = None
self.portal.portal_types = self.portal.portal_catalog
try:
self.actions.listFilteredActionsFor(self.portal)
except:
Expand Down
2 changes: 1 addition & 1 deletion Products/CMFPlone/tests/testCatalogTool.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
group2 = 'g2'

base_content = ['Members', 'aggregator', 'aggregator',
'events', 'news', TEST_USER_ID, 'front-page', 'doc']
'events', 'news', TEST_USER_ID, 'doc']


class TestCatalogSetup(PloneTestCase):
Expand Down
2 changes: 1 addition & 1 deletion Products/CMFPlone/tests/testPloneFolder.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from Products.CMFPlone.utils import _createObjectByType

from AccessControl import Unauthorized
from zExceptions import Unauthorized
from Products.CMFCore.permissions import DeleteObjects

from zExceptions import BadRequest
Expand Down
Loading

0 comments on commit 06db2c5

Please sign in to comment.