Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preliminary step to deprecate docstring based publication control #1197

Merged
merged 12 commits into from
Feb 24, 2024
Merged
5 changes: 5 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ https://github.com/zopefoundation/Zope/blob/4.x/CHANGES.rst
- Fix redirections to URLs with host given as IP-literal with brackets.
Fixes `#1191 <https://github.com/zopefoundation/Zope/issues/1191>`_.

- Introduce the decorator ``ZPublisher.zpublish`` to explicitly
control publication by ``ZPublisher``.
For details see
`#1197 <https://github.com/zopefoundation/Zope/pull/1197>`_.

- Fix ``Content-Disposition`` filename for clients without rfc6266 support.
(`#1198 <https://github.com/zopefoundation/Zope/pull/1198>`_)

Expand Down
34 changes: 33 additions & 1 deletion docs/zdgbook/ObjectPublishing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,22 @@ Publishable Object Requirements
-------------------------------

Zope has few restrictions on publishable objects. The basic rule is
that the object must have a doc string. This requirement goes for
that the object must have been marked as zpublishable. This requirement goes for
methods, too.
An object or method is marked as zpublishable by decorating
its class (or a base class) or underlying function, respectively,
with the ``Zpublisher.zpublish`` decorator.
For backward compatibility, the existence of a docstring, too,
marks an object or method as zpublishable; but this will be removed in
the future.
If you decorate a method or class with ``zpublsh(False)``,
you explicitly mark it or its instances, respectively, as not
zpublishable.
If you decorate a method with ``zpublish(methods=...)``
where the `...` is either a single request method name
or a sequence of request method names,
you specify that the object is zpublishable only for the mentioned request
methods.

Another requirement is that a publishable object must not have a name
that begins with an underscore. These two restrictions are designed to
Expand Down Expand Up @@ -270,9 +284,13 @@ allow you to navigate between methods.

Consider this example::

from ZPublisher import zpublish

@zpublish
class Example:
"""example class"""

@zpublish
def one(self):
"""render page one"""
return """<html>
Expand All @@ -282,6 +300,7 @@ Consider this example::
</body>
</html>"""

@zpublish
def two(self):
"""render page two"""
return """<html>
Expand All @@ -298,9 +317,11 @@ the URL, relative links returned by ``index_html`` won't work right.

For example::

@zpublish
class Example:
"""example class""""

@zpublish
def index_html(self):
"""render default view"""
return """<html>
Expand Down Expand Up @@ -375,7 +396,9 @@ acquisition, you can use traversal to walk over acquired objects.
Consider the the following object hierarchy::

from Acquisition import Implicit
from ZPublisher import zpublish

@zpublish
class Node(Implicit):
...

Expand All @@ -401,20 +424,27 @@ method that your acquire from outside your container.
For example::

from Acquisition import Implicit
from ZPublisher import zpublish

@zpublish
class Basket(Implicit):
...
@zpublish
def number_of_items(self):
"""Returns the number of contained items."""
...

@zpublish
class Vegetable(Implicit):
...
@zpublish
def texture(self):
"""Returns the texture of the vegetable."""

@zpublish
class Fruit(Implicit):
...
@zpublish
def color(self):
"""Returns the color of the fruit."""

Expand Down Expand Up @@ -582,6 +612,7 @@ called from the web.

Consider this function::

@zpublish
def greet(name):
"""Greet someone by name."""
return "Hello, %s!" % name
Expand Down Expand Up @@ -663,6 +694,7 @@ Argument Conversion
The publisher supports argument conversion. For example consider this
function::

@zpublish
def one_third(number):
"""returns the number divided by three"""
return number / 3.0
Expand Down
6 changes: 3 additions & 3 deletions src/App/ApplicationManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
from urllib import parse

from AccessControl.class_init import InitializeClass
from AccessControl.requestmethod import requestmethod
from Acquisition import Implicit
from App.CacheManager import CacheManager
from App.config import getConfiguration
Expand All @@ -33,6 +32,7 @@
from OFS.Traversable import Traversable
from Persistence import Persistent
from Products.PageTemplates.PageTemplateFile import PageTemplateFile
from ZPublisher import zpublish


class FakeConnection:
Expand Down Expand Up @@ -365,7 +365,7 @@ def db_size(self):
return '%.1fM' % (s / 1048576.0)
return '%.1fK' % (s / 1024.0)

@requestmethod('POST')
@zpublish(methods='POST')
def manage_minimize(self, value=1, REQUEST=None):
"Perform a full sweep through the cache"
# XXX Add a deprecation warning about value?
Expand All @@ -376,7 +376,7 @@ def manage_minimize(self, value=1, REQUEST=None):
url = f'{REQUEST["URL1"]}/manage_main?manage_tabs_message={msg}'
REQUEST.RESPONSE.redirect(url)

@requestmethod('POST')
@zpublish(methods='POST')
def manage_pack(self, days=0, REQUEST=None):
"""Pack the database"""
if not isinstance(days, (int, float)):
Expand Down
2 changes: 2 additions & 0 deletions src/App/DavLockManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from App.special_dtml import DTMLFile
from OFS.Lockable import wl_isLocked
from OFS.SimpleItem import Item
from ZPublisher import zpublish


class DavLockManager(Item, Implicit):
Expand Down Expand Up @@ -73,6 +74,7 @@ def unlockObjects(self, paths=[]):
ob.wl_clearLocks()

@security.protected(webdav_manage_locks)
@zpublish
def manage_unlockObjects(self, paths=[], REQUEST=None):
" Management screen action to unlock objects. "
if paths:
Expand Down
5 changes: 5 additions & 0 deletions src/App/FactoryDispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from Acquisition import aq_base
from ExtensionClass import Base
from OFS.metaconfigure import get_registered_packages
from ZPublisher import zpublish


def _product_packages():
Expand All @@ -45,6 +46,7 @@ def _product_packages():
return _packages


@zpublish
class Product(Base):
"""Model a non-persistent product wrapper.
"""
Expand All @@ -68,6 +70,7 @@ def Destination(self):
InitializeClass(Product)


@zpublish
class ProductDispatcher(Implicit):
" "
# Allow access to factory dispatchers
Expand Down Expand Up @@ -95,6 +98,7 @@ def __bobo_traverse__(self, REQUEST, name):
return dispatcher.__of__(self)


@zpublish
class FactoryDispatcher(Implicit):
"""Provide a namespace for product "methods"
"""
Expand Down Expand Up @@ -157,6 +161,7 @@ def __getattr__(self, name):
_owner = UnownableOwner

# Provide a replacement for manage_main that does a redirection:
@zpublish
def manage_main(trueself, self, REQUEST, update_menu=0):
"""Implement a contents view by redirecting to the true view
"""
Expand Down
4 changes: 4 additions & 0 deletions src/App/Management.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@
from App.special_dtml import DTMLFile
from ExtensionClass import Base
from zope.interface import implementer
from ZPublisher import zpublish


@zpublish
class Tabs(Base):
"""Mix-in provides management folder tab support."""

Expand Down Expand Up @@ -68,6 +70,7 @@ def filtered_manage_options(self, REQUEST=None):

manage_workspace__roles__ = ('Authenticated',)

@zpublish
def manage_workspace(self, REQUEST):
"""Dispatch to first interface in manage_options
"""
Expand Down Expand Up @@ -181,6 +184,7 @@ def manage_page_header(self, *args, **kw):
security.declarePublic('zope_copyright') # NOQA: D001
zope_copyright = DTMLFile('dtml/copyright', globals())

@zpublish
@security.public
def manage_zmi_logout(self, REQUEST, RESPONSE):
"""Logout current user"""
Expand Down
16 changes: 12 additions & 4 deletions src/App/ProductContext.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
from App.FactoryDispatcher import FactoryDispatcher
from OFS.ObjectManager import ObjectManager
from zope.interface import implementedBy
from ZPublisher import zpublish
from ZPublisher import zpublish_marked
from ZPublisher import zpublish_wrap


if not hasattr(Products, 'meta_types'):
Expand Down Expand Up @@ -99,6 +102,9 @@ class will be registered.
productObject = self.__prod
pid = productObject.id

if instance_class is not None and not zpublish_marked(instance_class):
zpublish(instance_class)

if permissions:
if isinstance(permissions, str): # You goofed it!
raise TypeError(
Expand Down Expand Up @@ -130,19 +136,21 @@ class will be registered.
for method in legacy:
if isinstance(method, tuple):
name, method = method
mname = method.__name__
aliased = 1
else:
name = method.__name__
aliased = 0
if name not in OM.__dict__:
method = zpublish_wrap(method)
setattr(OM, name, method)
setattr(OM, name + '__roles__', pr)
if aliased:
# Set the unaliased method name and its roles
# to avoid security holes. XXX: All "legacy"
# methods need to be eliminated.
setattr(OM, method.__name__, method)
setattr(OM, method.__name__ + '__roles__', pr)
setattr(OM, mname, method)
setattr(OM, mname + '__roles__', pr)

if isinstance(initial, tuple):
name, initial = initial
Expand Down Expand Up @@ -186,7 +194,7 @@ class __FactoryDispatcher__(FactoryDispatcher):
'container_filter': container_filter
},)

m[name] = initial
m[name] = zpublish_wrap(initial)
m[name + '__roles__'] = pr

for method in constructors[1:]:
Expand All @@ -195,7 +203,7 @@ class __FactoryDispatcher__(FactoryDispatcher):
else:
name = os.path.split(method.__name__)[-1]
if name not in productObject.__dict__:
m[name] = method
m[name] = zpublish_wrap(method)
m[name + '__roles__'] = pr

def getApplication(self):
Expand Down
2 changes: 2 additions & 0 deletions src/App/Undo.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from App.Management import Tabs
from App.special_dtml import DTMLFile
from DateTime.DateTime import DateTime
from ZPublisher import zpublish


class UndoSupport(Tabs, Implicit):
Expand Down Expand Up @@ -98,6 +99,7 @@ def undoable_transactions(self, first_transaction=None,
return r

@security.protected(undo_changes)
@zpublish
def manage_undo_transactions(self, transaction_info=(), REQUEST=None):
"""
"""
Expand Down
3 changes: 3 additions & 0 deletions src/App/special_dtml.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
from DocumentTemplate.DT_String import DTReturn
from DocumentTemplate.DT_String import _marker
from Shared.DC.Scripts.Bindings import Bindings
from ZPublisher import zpublish


LOG = getLogger('special_dtml')
Expand All @@ -46,10 +47,12 @@ class Code:
pass


@zpublish
class HTML(DocumentTemplate.HTML, Persistence.Persistent):
"Persistent HTML Document Templates"


@zpublish
class ClassicHTMLFile(DocumentTemplate.HTMLFile, MethodObject.Method):
"Persistent HTML Document Templates read from files"

Expand Down
4 changes: 4 additions & 0 deletions src/OFS/Application.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
from zExceptions import Forbidden
from zExceptions import Redirect as RedirectException
from zope.interface import implementer
from ZPublisher import zpublish

from . import Folder
from . import misc_
Expand Down Expand Up @@ -107,6 +108,7 @@ def Redirect(self, destination, URL1):

ZopeRedirect = Redirect

@zpublish
@security.protected(view_management_screens)
def getZMIMainFrameTarget(self, REQUEST):
"""Utility method to get the right hand side ZMI frame source URL
Expand Down Expand Up @@ -200,11 +202,13 @@ def ZopeVersion(self, major=False):

return version

@zpublish
def DELETE(self, REQUEST, RESPONSE):
"""Delete a resource object."""
self.dav__init(REQUEST, RESPONSE)
raise Forbidden('This resource cannot be deleted.')

@zpublish
def MOVE(self, REQUEST, RESPONSE):
"""Move a resource to a new location."""
self.dav__init(REQUEST, RESPONSE)
Expand Down
Loading
Loading