Skip to content

Commit

Permalink
Merge pull request #87 from plone/thet-fcfixes
Browse files Browse the repository at this point in the history
More folder contents fixes
  • Loading branch information
jensens committed Apr 11, 2016
2 parents 59f1b26 + 2203918 commit fcaa054
Show file tree
Hide file tree
Showing 11 changed files with 315 additions and 25 deletions.
21 changes: 14 additions & 7 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,38 @@ Incompatibilities:

New:

- Add ``Creator``, ``Description``, ``end``, ``start`` and ``location`` to the available columns and context attributes for folder_contents.
- Show attributes from ``_unsafe_metadata`` if user has "Modify Portal Content" permissions.
[thet]

Show attributes from ``_unsafe_metadata`` if user has "Modify Portal Content" permissions.
- Add ``Creator``, ``Description``, ``end``, ``start`` and ``location`` to the available columns and context attributes for folder_contents.
[thet]

Fixes:

- Remove ``portal_type`` from available columns and use ``Type`` instead, which is meant to be read by humans.
``portal_type`` is now available on the attributes object.
- Folder contents: When pasting, handle "Disallowed subobject type" ValueError and present a helpful error message.
Fixes: plone/mockup#657
[thet]

- Folder contents: Acquire the top most visible portal object to operate on.
Fixes some issues in INavigationRoot or ISite based subsites and virtual hosting environments pointing to subsites.
Fixes include: show correct breadcrumb paths, paste to correct location.
Fixes: #86
[thet]

- Added most notably `portal_type`, `review_state` and `Subject` but also `exclude_from_nav`, `is_folderish`, `last_comment_date`, `meta_type` and `total_comments` to ``BaseVocabularyView`` ``translate_ignored`` list.
Some of them are necessary for frontend logic and others cannot be translated.
Fixes https://github.com/plone/plone.app.content/issues/77
[thet]

- Remove ``portal_type`` from available columns and use ``Type`` instead, which is meant to be read by humans.
``portal_type`` is now available on the attributes object.
[thet]

- Vocabulary permissions are considered View permission by default, if not
stated different in PERMISSION global. Renamed _permissions to PERMISSIONS,
Deprecated BBB name in place. Also minor code-style changes
[jensens, thet]

- Fix folder contents path and breadcrumbs settings to show correct paths and render the toolbar correctly in navigation root subsites and virtual hosting environments pointing to subsites.
[thet]

- Fix test isolation problem and remove an unnecessary test dependency on ``plone.app.widgets``.
[thet]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Basic content

plone.app.content provides some helper base classes for content. Here are
some simple examples of using them.
.. code-block:: python
>>> from plone.app.content import container, item
Expand All @@ -15,12 +16,14 @@ available from Plone's "add item" menu. Factories are registered as named
utilities.

Note that we need to define a portal_type to keep CMF happy.
.. code-block:: python
>>> from zope.interface import implements, Interface
>>> from zope import schema
>>> from zope.component.factory import Factory
First, a container:
.. code-block:: python
>>> class IMyContainer(Interface):
... title = schema.TextLine(title=u"My title")
Expand All @@ -35,6 +38,7 @@ First, a container:
>>> containerFactory = Factory(MyContainer)
Then, an item:
.. code-block:: python
>>> class IMyType(Interface):
... title = schema.TextLine(title=u"My title")
Expand All @@ -49,18 +53,21 @@ Then, an item:
>>> itemFactory = Factory(MyType)
We can now create the items.
.. code-block:: python
>>> container = containerFactory("my-container")
>>> container.id
'my-container'
>>> container.title = "A sample container"
We need to add it to an object manager for acquisition to do its magic.
.. code-block:: python
>>> newid = self.portal._setObject(container.id, container)
>>> container = getattr(self.portal, newid)
We will add the item directly to the container later.
.. code-block:: python
>>> item = itemFactory("my-item")
>>> item.id
Expand All @@ -70,6 +77,7 @@ We will add the item directly to the container later.
Note that both the container type and the item type are contentish. This is
important, because CMF provides event handlers that automatically index
objects that are IContentish.
.. code-block:: python
>>> from Products.CMFCore.interfaces import IContentish
>>> IContentish.providedBy(container)
Expand All @@ -78,6 +86,7 @@ objects that are IContentish.
True
Only the container is folderish, though:
.. code-block:: python
>>> from Products.CMFCore.interfaces import IFolderish
>>> bool(container.isPrincipiaFolderish)
Expand All @@ -91,6 +100,7 @@ Only the container is folderish, though:
We can use the more natural Zope3-style container API, or the traditional
ObjectManager one.
.. code-block:: python
>>> container['my-item'] = item
>>> 'my-item' in container
Expand All @@ -106,6 +116,7 @@ ObjectManager one.
True
Both pieces of content should have been cataloged.
.. code-block:: python
>>> container = self.portal['my-container']
>>> item = container['my-item']
Expand All @@ -118,6 +129,7 @@ Both pieces of content should have been cataloged.
['A non-folderish item']
If we modify an object and trigger a modified event, it should be updated.
.. code-block:: python
>>> from zope.lifecycleevent import ObjectModifiedEvent
>>> from zope.event import notify
Expand Down
18 changes: 14 additions & 4 deletions plone/app/content/browser/contents/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,14 @@


def get_top_site_from_url(context, request):
"""Find the top-most site, which is in the url path.
"""Find the top-most site, which is still in the url path.
If the current context is within a subsite within a PloneSiteRoot and no
virtual hosting is in place, the PloneSiteRoot is returned.
When at the same context but in a virtual hosting environment with the
virtual host root pointing to the subsites, it returns the subsite instead
of the PloneSiteRoot.
For this given content structure:
/Plone/Subsite
Expand Down Expand Up @@ -68,6 +75,10 @@ class ContentsBaseAction(BrowserView):
failure_msg = _('Failure')
required_obj_permission = None

@property
def site(self):
return get_top_site_from_url(self.context, self.request)

def objectTitle(self, obj):
context = aq_inner(obj)
title = utils.pretty_title_or_id(context, context)
Expand Down Expand Up @@ -100,11 +111,10 @@ def finish(self):
def __call__(self):
self.protect()
self.errors = []
site = getSite()
context = aq_inner(self.context)
selection = self.get_selection()

self.dest = site.restrictedTraverse(
self.dest = self.site.restrictedTraverse(
str(self.request.form['folder'].lstrip('/')))

self.catalog = getToolByName(context, 'portal_catalog')
Expand Down Expand Up @@ -153,7 +163,7 @@ def message(self, missing=[]):
)

return self.json({
'status': 'success',
'status': 'warning' if self.errors else 'success',
'msg': translated_msg
})

Expand Down
12 changes: 9 additions & 3 deletions plone/app/content/browser/contents/paste.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from plone.app.content.interfaces import IStructureAction
from Products.CMFPlone import PloneMessageFactory as _
from ZODB.POSException import ConflictError
from zope.component.hooks import getSite
from zope.i18n import translate
from zope.interface import implementer

Expand Down Expand Up @@ -35,9 +34,8 @@ class PasteActionView(ContentsBaseAction):
def __call__(self):
self.protect()
self.errors = []
site = getSite()

self.dest = site.restrictedTraverse(
self.dest = self.site.restrictedTraverse(
str(self.request.form['folder'].lstrip('/')))

try:
Expand All @@ -51,5 +49,13 @@ def __call__(self):
self.errors.append(
_(u'You are not authorized to paste ${title} here.',
mapping={u'title': self.objectTitle(self.dest)}))
except ValueError as e:
if 'Disallowed subobject type: ' in e.message:
msg_parts = e.message.split(':')
self.errors.append(
_(u'Disallowed subobject type "${type}"',
mapping={u'type': msg_parts[1].strip()}))
else:
raise e

return self.message()
6 changes: 2 additions & 4 deletions plone/app/content/browser/contents/properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@ def __init__(self, context, request):
self.request = request

def get_options(self):
site = getSite()
base_url = site.absolute_url()
base_vocabulary = '%s/@@getVocabulary?name=' % base_url
base_vocabulary = '%s/@@getVocabulary?name=' % getSite().absolute_url()
return {
'title': translate(_('Properties'), context=self.request),
'id': 'properties',
Expand All @@ -34,7 +32,7 @@ def get_options(self):
'template': self.template(
vocabulary_url='%splone.app.vocabularies.Users' % (
base_vocabulary)
)
)
}
}

Expand Down
6 changes: 2 additions & 4 deletions plone/app/content/browser/contents/tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ def __init__(self, context, request):
self.request = request

def get_options(self):
site = getSite()
base_url = site.absolute_url()
base_vocabulary = '%s/@@getVocabulary?name=' % base_url
base_vocabulary = '%s/@@getVocabulary?name=' % getSite().absolute_url()
return {
'title': translate(_('Tags'), context=self.request),
'id': 'tags',
Expand All @@ -31,7 +29,7 @@ def get_options(self):
'template': self.template(
vocabulary_url='%splone.app.vocabularies.Keywords' % (
base_vocabulary)
)
)
}
}

Expand Down
3 changes: 2 additions & 1 deletion plone/app/content/browser/vocabulary.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
MAX_BATCH_SIZE = 500 # prevent overloading server

DEFAULT_PERMISSION = 'View'
DEFAULT_PERMISSION_SECURE = 'Modify portal content'
PERMISSIONS = {
'plone.app.vocabularies.Catalog': 'View',
'plone.app.vocabularies.Keywords': 'Modify portal content',
Expand Down Expand Up @@ -193,7 +194,7 @@ def __call__(self):
if attributes:
base_path = getNavigationRoot(context)
sm = getSecurityManager()
can_edit = sm.checkPermission('Modify portal content', context)
can_edit = sm.checkPermission(DEFAULT_PERMISSION_SECURE, context)
for vocab_item in results:
if not results_are_brains:
vocab_item = vocab_item.value
Expand Down
2 changes: 1 addition & 1 deletion plone/app/content/tests/test_basecontent.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
def test_suite():
return unittest.TestSuite((
ztc.ZopeDocFileSuite(
'basecontent.txt',
'basecontent.rst',
package='plone.app.content',
test_class=ContentFunctionalTestCase,
optionflags=(doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE)),
Expand Down
Loading

0 comments on commit fcaa054

Please sign in to comment.