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
4 changes: 4 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
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
14 changes: 11 additions & 3 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 @@ -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
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
6 changes: 6 additions & 0 deletions src/OFS/DTMLMethod.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
from zExceptions import ResourceLockedError
from zExceptions.TracebackSupplement import PathTracebackSupplement
from zope.contenttype import guess_content_type
from ZPublisher import zpublish
from ZPublisher.HTTPRequest import default_encoding
from ZPublisher.Iterators import IStreamIterator

Expand All @@ -51,6 +52,7 @@ class Code:
pass


@zpublish
class DTMLMethod(
PathReprProvider,
RestrictedDTML,
Expand Down Expand Up @@ -265,6 +267,7 @@ def get_size(self):
security.declareProtected(change_proxy_roles, 'manage_proxyForm') # NOQA: D001,E501
manage_proxyForm = DTMLFile('dtml/documentProxy', globals())

@zpublish
@security.protected(change_dtml_methods)
def manage_edit(self, data, title, SUBMIT='Change', REQUEST=None):
""" Replace contents with 'data', title with 'title'.
Expand Down Expand Up @@ -293,6 +296,7 @@ def manage_edit(self, data, title, SUBMIT='Change', REQUEST=None):
message = "Saved changes."
return self.manage_main(self, REQUEST, manage_tabs_message=message)

@zpublish
@security.protected(change_dtml_methods)
def manage_upload(self, file='', REQUEST=None):
""" Replace the contents of the document with the text in 'file'.
Expand Down Expand Up @@ -336,6 +340,7 @@ def _validateProxy(self, roles=None):
'do not have proxy roles.\n<!--%s, %s-->' % (
self.__name__, user, roles))

@zpublish
@security.protected(change_proxy_roles)
@requestmethod('POST')
def manage_proxy(self, roles=(), REQUEST=None):
Expand Down Expand Up @@ -363,6 +368,7 @@ def document_src(self, REQUEST=None, RESPONSE=None):
RESPONSE.setHeader('Content-Type', 'text/plain')
return self.read()

@zpublish
@security.protected(change_dtml_methods)
def PUT(self, REQUEST, RESPONSE):
""" Handle HTTP PUT requests.
Expand Down
3 changes: 3 additions & 0 deletions src/OFS/FindSupport.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from ExtensionClass import Base
from OFS.interfaces import IFindSupport
from zope.interface import implementer
from ZPublisher import zpublish
from ZPublisher.HTTPRequest import default_encoding


Expand All @@ -52,6 +53,7 @@ class FindSupport(Base):
},
)

@zpublish
@security.protected(view_management_screens)
def ZopeFind(self, obj, obj_ids=None, obj_metatypes=None,
obj_searchterm=None, obj_expr=None,
Expand All @@ -69,6 +71,7 @@ def ZopeFind(self, obj, obj_ids=None, obj_metatypes=None,
pre=pre, apply_func=None, apply_path=''
)

@zpublish
@security.protected(view_management_screens)
def ZopeFindAndApply(self, obj, obj_ids=None, obj_metatypes=None,
obj_searchterm=None, obj_expr=None,
Expand Down
2 changes: 2 additions & 0 deletions src/OFS/Folder.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from OFS.SimpleItem import PathReprProvider
from webdav.Collection import Collection
from zope.interface import implementer
from ZPublisher import zpublish


manage_addFolderForm = DTMLFile('dtml/folderAdd', globals())
Expand All @@ -50,6 +51,7 @@ def manage_addFolder(
return self.manage_main(self, REQUEST)


@zpublish
@implementer(IFolder)
class Folder(
PathReprProvider,
Expand Down
Loading
Loading