Skip to content

Commit

Permalink
Resurrect the Interfaces ZMI tab (#572)
Browse files Browse the repository at this point in the history
* - showing missed lines is not helpful with this much output

* - fix a longstanding error traversing into views from page templates

* - Resurrect the Interfaces ZMI tab

* - remove unused method and show ZMI message upon changes

* - bring back tests, this time as unit tests
  • Loading branch information
dataflake committed May 2, 2019
1 parent b6fa2ab commit d44dd2f
Show file tree
Hide file tree
Showing 12 changed files with 262 additions and 2 deletions.
3 changes: 3 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ Fixes
Features
++++++++

- Resurrect the Interfaces ZMI tab
(`#450 <https://github.com/zopefoundation/Zope/issues/450>`_)

- Better default logging configuration for simple waitress WSGI setups
(`#526 <https://github.com/zopefoundation/Zope/issues/526>`_)

Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ omit =

[coverage:report]
precision = 2
show_missing = True
show_missing = False
sort = Name

[coverage:html]
Expand Down
2 changes: 2 additions & 0 deletions src/OFS/SimpleItem.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ class Item(
isPrincipiaFolderish = 0
isTopLevelPrincipiaApplicationObject = 0

manage_options = ({'label': 'Interfaces', 'action': 'manage_interfaces'},)

def manage_afterAdd(self, item, container):
pass
manage_afterAdd.__five_method__ = True
Expand Down
1 change: 1 addition & 0 deletions src/Products/Five/utilities/browser/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# package
20 changes: 20 additions & 0 deletions src/Products/Five/utilities/browser/configure.zcml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<configure xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser">

<browser:page
for="*"
name="edit-markers.html"
template="edit_markers.pt"
class="Products.Five.utilities.browser.marker.EditView"
permission="zope2.ManageProperties"
/>

<browser:page
for="*"
name="manage_interfaces"
template="manage_interfaces.pt"
class="Products.Five.utilities.browser.marker.EditView"
permission="zope2.ManageProperties"
/>

</configure>
82 changes: 82 additions & 0 deletions src/Products/Five/utilities/browser/edit_markers.pt
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<html metal:use-macro="context/@@standard_macros/page">
<body>

<metal:slot metal:fill-slot="body">

<metal:macro metal:define-macro="heading">
<h1 i18n:translate="heading_edit_marker">Assign Marker Interfaces</h1>
</metal:macro>

<metal:macro metal:define-macro="main">
<p class="form-help formHelp" i18n:translate="">
Change the behavior of this object by adding or removing marker
interfaces. You can choose one or more interfaces to be added to the
list of provided interfaces for this object.
</p>

<p class="form-help formHelp" i18n:translate="">
A marker interface is used to identify an instance of a piece of
content. This allows you to enable and disable views based on marker
interfaces for example.
</p>

<form action="." method="post"
tal:attributes="action request/ACTUAL_URL">

<h3 i18n:translate="legend_provided">Provided interfaces</h3>

<table class="table table-striped table-hover table-sm">
<tr tal:repeat="interface view/getInterfaceNames">
<td class="zmi-object-check text-right">&nbsp;</td>
<td class="zmi-object-id"
tal:content="interface/name">Interface Name</td>
</tr>

<tr tal:repeat="interface view/getDirectlyProvidedNames">
<td class="zmi-object-check text-right">
<input type="checkbox" id="INTERFACE" name="remove:list"
tal:attributes="id interface/name;
value interface/name"/>
</td>
<td class="zmi-object-id"
tal:content="interface/name">Interface Name</td>
</tr>

<tr tal:condition="view/getDirectlyProvidedNames">
<td class="zmi-object-check text-right">&nbsp;</td>
<td class="zmi-controls">
<input class="btn btn-primary" type="submit" name="SAVE"
value="Remove" i18n:attributes="value"/>
</td>
</tr>
</table>

<h3 i18n:translate="legend_available_marker">
Available Marker Interfaces
</h3>

<table class="table table-striped table-hover table-sm">
<tr tal:repeat="interface view/getAvailableInterfaceNames">
<td class="zmi-object-check text-right">
<input type="checkbox" id="INTERFACE" name="add:list"
tal:attributes="id interface/name;
value interface/name"/>
</td>
<td class="zmi-object-id"
tal:content="interface/name">Interface Name</td>
</tr>
<tr tal:condition="view/getAvailableInterfaceNames">
<td class="zmi-object-check text-right">&nbsp;</td>
<td class="zmi-controls">
<input class="btn btn-primary" type="submit" name="SAVE"
value="Add" i18n:attributes="value"/>
</td>
</tr>
</table>

</form>
</metal:macro>
</metal:slot>

</body>
</html>
10 changes: 10 additions & 0 deletions src/Products/Five/utilities/browser/manage_interfaces.pt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<h1 tal:replace="structure context/manage_page_header">PAGE HEADER</h1>
<h2 tal:replace="structure context/manage_tabs">TABS</h2>

<main class="container-fluid">

<metal:macro metal:use-macro="context/@@edit-markers.html/main" />

</main>

<h1 tal:replace="structure context/manage_page_footer">PAGE FOOTER</h1>
55 changes: 55 additions & 0 deletions src/Products/Five/utilities/browser/marker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
##############################################################################
#
# Copyright (c) 2004, 2005 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Marker interfaces adapter views.
"""

from Products.Five.utilities.interfaces import IMarkerInterfaces


class EditView(object):
"""Marker interface edit view.
"""

def __init__(self, context, request):
self.context = context
self.request = request
self.adapted = IMarkerInterfaces(context)
self.context_url = self.context.absolute_url()

def __call__(self, SAVE=None, add=(), remove=()):
if SAVE:
self.update(add, remove)
url = '%s?manage_tabs_message=Changes+applied.'
self.request.response.redirect(url % self.request.ACTUAL_URL)
return ''
return self.index()

def _getNameLinkDicts(self, interfaceNames):
return [dict(name=name) for name in interfaceNames]

def getAvailableInterfaceNames(self):
return self._getNameLinkDicts(
self.adapted.getAvailableInterfaceNames())

def getDirectlyProvidedNames(self):
return self._getNameLinkDicts(self.adapted.getDirectlyProvidedNames())

def getInterfaceNames(self):
return self._getNameLinkDicts(self.adapted.getInterfaceNames())

def update(self, add, remove):
# this could return errors
add = self.adapted.dottedToInterfaces(add)
remove = self.adapted.dottedToInterfaces(remove)
self.adapted.update(add=add, remove=remove)
1 change: 1 addition & 0 deletions src/Products/Five/utilities/browser/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# package
82 changes: 82 additions & 0 deletions src/Products/Five/utilities/browser/tests/test_marker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
##############################################################################
#
# Copyright (c) 2004, 2005 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
""" Tests for the marker interface edit views
"""
import AccessControl
import Products.Five
import Products.Five.utilities
from Products.Five.tests.testing.simplecontent import ISimpleContent
from Products.Five.tests.testing.simplecontent import SimpleContent
from Products.Five.utilities.browser.marker import EditView
from Testing.ZopeTestCase import ZopeTestCase
from Zope2.App import zcml
from zope.component import ComponentLookupError
from zope.component.interface import provideInterface
from zope.component.testing import setUp as component_setUp
from zope.component.testing import tearDown as component_tearDown


ISimpleContentName = 'Products.Five.tests.testing.simplecontent.ISimpleContent'
IFooMarkerName = 'Products.Five.utilities.browser.tests.test_marker.IFooMarker'


class IFooMarker(ISimpleContent):
pass


class MarkerViewTests(ZopeTestCase):

def setUp(self):
super(MarkerViewTests, self).setUp()
component_setUp()
zcml.load_config('meta.zcml', Products.Five)
zcml.load_config('permissions.zcml', AccessControl)
zcml.load_config('configure.zcml', Products.Five.utilities)

def tearDown(self):
super(MarkerViewTests, self).tearDown()
component_tearDown()

def test_editview(self):
obj = SimpleContent('foo', 'Foo').__of__(self.app.test_folder_1_)
view = EditView(obj, {})

# Test state before making any changes
self.assertTrue(view.context.aq_inner is obj)
self.assertEqual(view.request, {})
self.assertEqual(view.getAvailableInterfaceNames(), [])
self.assertEqual(view.getDirectlyProvidedNames(), [])
self.assertIn({'name': ISimpleContentName}, view.getInterfaceNames())

# Try to add a marker interface that doesn't exist
self.assertRaises(ComponentLookupError, view.update,
('__main__.IFooMarker',), ())

# Now create the marker interface and try again
provideInterface('', IFooMarker)
self.assertIn({'name': IFooMarkerName},
view.getAvailableInterfaceNames())
self.assertEqual(view.getDirectlyProvidedNames(), [])

# And try again to add it to the object
view.update((IFooMarkerName,), ())
self.assertEqual(view.getAvailableInterfaceNames(), [])
self.assertIn({'name': IFooMarkerName},
view.getDirectlyProvidedNames())

# And remove it again
view.update((), (IFooMarkerName,))
self.assertIn({'name': IFooMarkerName},
view.getAvailableInterfaceNames())
self.assertEqual(view.getDirectlyProvidedNames(), [])
2 changes: 2 additions & 0 deletions src/Products/Five/utilities/configure.zcml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<configure xmlns="http://namespaces.zope.org/zope">

<include package=".browser"/>

<adapter
for="*"
provides=".interfaces.IMarkerInterfaces"
Expand Down
4 changes: 3 additions & 1 deletion src/Shared/DC/Scripts/Bindings.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from Acquisition import aq_base
from Acquisition import aq_inner
from Acquisition import aq_parent
from zope.component import queryMultiAdapter as qma


defaultBindings = {'name_context': 'context',
Expand Down Expand Up @@ -246,7 +247,8 @@ def __before_publishing_traverse__(self, self2, request):
path = request['TraversalRequestNameStack']
names = self.getBindingAssignments()
if not names.isNameAssigned('name_subpath') or \
(path and hasattr(aq_base(self), path[-1])):
(path and hasattr(aq_base(self), path[-1])) or \
(path and qma((self, request), name=path[-1]) is not None):
return
subpath = path[:]
path[:] = []
Expand Down

0 comments on commit d44dd2f

Please sign in to comment.