Skip to content
This repository has been archived by the owner on Feb 12, 2020. It is now read-only.

Commit

Permalink
New package
Browse files Browse the repository at this point in the history
  • Loading branch information
wichert committed Sep 24, 2008
0 parents commit 2b44535
Show file tree
Hide file tree
Showing 11 changed files with 320 additions and 0 deletions.
43 changes: 43 additions & 0 deletions README.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
Introduction
============

This package provides a hook into Zope's ZPublisher that is run after the
publisher has completed publication, but before the the transaction is commited
and the response is returned to the requesting browser. This is practical for
caching purposes: it is the ideal place to determine and insert caching headers
into the response.

Hooks use `zope.eventz_'s event mechanism using the
plone.validatehook.interfaces.IPostValidationEvent. This is based on the
standard ObjectEvent from `zope.component`_.

Example
=======

As an example we will write a bit of code which logs the path of every published
object. This is the code for the event handler::

from zope.interface import Interface
from zope.component import adapter
from plone.postpublicationhook.interfaces import IAfterPublicationEvent
import logging

logger = logging.getLogger("LogRequest")

@adapter(Interface, IAfterPublicationEvent)
def LogRequest(object, event):
if getattr(object, "getPhysicalPath", None) is None:
path="Unknown path"
else:
path="/".join(object.getPhysicalPath()

logger.info("Request for object %s" % path)


To use this code you need to register it in zcml::

<subscriber handler=".events.LogRequest" />


.. _zope.event: http://pypi.python.org/pypi/zope.event
.. _zope.component: http://pypi.python.org/pypi/zope.component
10 changes: 10 additions & 0 deletions docs/HISTORY.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Changelog
=========

1.0rc1 - September 24, 2008
---------------------------

* Initial release
[wichert]


43 changes: 43 additions & 0 deletions docs/LICENSE.ZPL
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
Some code carries the Zope Public License (as indicated in the comments):

Zope Public License (ZPL) Version 2.0

This software is Copyright (c) Zope Corporation (tm) and Contributors.
All rights reserved.

This license has been certified as open source. It has also been designated as
GPL compatible by the Free Software Foundation (FSF).

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

* Redistributions in source code must retain the above copyright notice, this
list of conditions, and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions, and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* The name Zope Corporation (tm) must not be used to endorse or promote
products derived from this software without prior written permission from
Zope Corporation.
* The right to distribute this software or to use it for any purpose does not
give you the right to use Servicemarks (sm) or Trademarks (tm) of Zope
Corporation. Use of them is covered in a separate agreement
(see http://www.zope.com/Marks).
If any files are modified, you must cause the modified files to carry prominent
notices stating that you changed the files and the date of any change.

Disclaimer
THIS SOFTWARE IS PROVIDED BY ZOPE CORPORATION ``AS IS'' AND ANY EXPRESSED OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL ZOPE CORPORATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

This software consists of contributions made by Zope Corporation and many
individuals on behalf of Zope Corporation. Specific attributions are listed
in the accompanying credits file.
6 changes: 6 additions & 0 deletions plone/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages
try:
__import__('pkg_resources').declare_namespace(__name__)
except ImportError:
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
5 changes: 5 additions & 0 deletions plone/postpublicationhook/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

def initialize(context):
from plone.postpublicationhook.hook import InstallHook

InstallHook()
7 changes: 7 additions & 0 deletions plone/postpublicationhook/configure.zcml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:five="http://namespaces.zope.org/five">

<five:registerPackage package="." initialize=".initialize" />

</configure>
13 changes: 13 additions & 0 deletions plone/postpublicationhook/event.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from zope.interface import implements
from zope.component.interfaces import ObjectEvent
from plone.postpublicationhook.interfaces import IAfterPublicationEvent


class AfterPublicationEvent(ObjectEvent):
implements(IAfterPublicationEvent)

def __init__(self, context, request):
super(AfterPublicationEvent, self).__init__(context)
self.request=request


147 changes: 147 additions & 0 deletions plone/postpublicationhook/hook.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import logging
import sys
from ZPublisher.Publish import call_object
from ZPublisher.Publish import missing_name
from ZPublisher.Publish import dont_publish_class
from ZPublisher.Publish import get_module_info
from ZPublisher.Publish import Retry
from ZPublisher.mapply import mapply
from zope.publisher.browser import setDefaultSkin
from zope.security.management import newInteraction
from zope.security.management import endInteraction
from zExceptions import Redirect

import zope.event
from plone.postpublicationhook import AfterPublicationEvent


def publish(request, module_name, after_list, debug=0,
# Optimize:
call_object=call_object,
missing_name=missing_name,
dont_publish_class=dont_publish_class,
mapply=mapply,
):

(bobo_before, bobo_after, object, realm, debug_mode, err_hook,
validated_hook, transactions_manager)= get_module_info(module_name)

parents=None
response=None

try:
# TODO pass request here once BaseRequest implements IParticipation
newInteraction()

request.processInputs()

request_get=request.get
response=request.response

# First check for "cancel" redirect:
if request_get('SUBMIT','').strip().lower()=='cancel':
cancel=request_get('CANCEL_ACTION','')
if cancel:
raise Redirect, cancel

after_list[0]=bobo_after
if debug_mode:
response.debug_mode=debug_mode
if realm and not request.get('REMOTE_USER',None):
response.realm=realm

if bobo_before is not None:
bobo_before()

# Get the path list.
# According to RFC1738 a trailing space in the path is valid.
path=request_get('PATH_INFO')

request['PARENTS']=parents=[object]

if transactions_manager:
transactions_manager.begin()

object=request.traverse(path, validated_hook=validated_hook)

if transactions_manager:
transactions_manager.recordMetaData(object, request)

result=mapply(object, request.args, request,
call_object,1,
missing_name,
dont_publish_class,
request, bind=1)

if result is not response:
response.setBody(result)

# This is the only change from the canonical publish method
zope.event.notify(AfterPublicationEvent(request, object))

if transactions_manager:
transactions_manager.commit()
endInteraction()

return response
except:
# DM: provide nicer error message for FTP
sm = None
if response is not None:
sm = getattr(response, "setMessage", None)

if sm is not None:
from asyncore import compact_traceback
cl,val= sys.exc_info()[:2]
sm('%s: %s %s' % (
getattr(cl,'__name__',cl), val,
debug_mode and compact_traceback()[-1] or ''))

if err_hook is not None:
if parents:
parents=parents[0]
try:
try:
return err_hook(parents, request,
sys.exc_info()[0],
sys.exc_info()[1],
sys.exc_info()[2],
)
except Retry:
if not request.supports_retry():
return err_hook(parents, request,
sys.exc_info()[0],
sys.exc_info()[1],
sys.exc_info()[2],
)
finally:
if transactions_manager:
transactions_manager.abort()
endInteraction()

# Only reachable if Retry is raised and request supports retry.
newrequest=request.retry()
request.close() # Free resources held by the request.
# Set the default layer/skin on the newly generated request
setDefaultSkin(newrequest)
try:
return publish(newrequest, module_name, after_list, debug)
finally:
newrequest.close()

else:
if transactions_manager:
transactions_manager.abort()
endInteraction()
raise



def InstallHook():
"""Install our own publish method."""
from plone.postpublicationhook import hook
import ZPublisher.Publish

ZPublisher.Publish.publish=hook.publish
logging.info("Monkeypatch ZPublisher publish method to send AfterPublicationEvent")

8 changes: 8 additions & 0 deletions plone/postpublicationhook/interfaces.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from zope.component.interfaces import IObjectEvent
from zope.interface import Attribute

class IAfterPublicationEvent(IObjectEvent):
"""An event which is fired after publication, but before the transaction is
commited."""

request = Attribute("The request")
3 changes: 3 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[egg_info]
tag_build = dev
tag_svn_revision = true
35 changes: 35 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from setuptools import setup, find_packages
import os.path

version = '1.0'

setup(name='plone.postpublicationhook',
version=version,
description="Zope 2 post-publication hook",
long_description=open("README.txt").read() + "\n" +
open(os.path.join("docs", "HISTORY.txt")).read(),
classifiers=[
"Programming Language :: Python",
"Environment :: Web Environment",
"Framework :: Zope2",
"Intended Audience :: Developers",
"License :: OSI Approved :: Zope Public License",
"Topic :: Software Development :: Libraries :: Python Modules",
],
keywords='',
author='Wichert Akkerman',
author_email='wichert@wiggy.net',
url='',
license='ZPL',
packages=find_packages(exclude=['ez_setup']),
namespace_packages=['plone'],
include_package_data=True,
zip_safe=False,
install_requires=[
'setuptools',
'zope.event',
'zope.interface',
'zope.security',
],
)

0 comments on commit 2b44535

Please sign in to comment.