vendor/Five/1.2b-r20590

changeset 0:3673ed425f80 1.2b-r20590 tip

Vendor import of Five 1.2b+ (r20590)
author fguillaume
date Fri, 02 Dec 2005 20:25:42 +0000
parents
children
files CHANGES.txt COPYING.txt CREDITS.txt INSTALL.txt README.txt __init__.py bbb/AccessControl_interfaces.py bbb/Acquisition_interfaces.py bbb/App_interfaces.py bbb/OFS_event.py bbb/OFS_interfaces.py bbb/OFS_subscribers.py bbb/__init__.py bbb/webdav_interfaces.py bridge.py browser/ReuseUtils.py browser/TrustedExpression.py browser/__init__.py browser/absoluteurl.py browser/adding.pt browser/adding.py browser/configure.zcml browser/menu.py browser/meta.zcml browser/metaconfigure.py browser/pagetemplatefile.py browser/resource.py browser/tests/__init__.py browser/tests/adding.txt browser/tests/birdmacro.pt browser/tests/cockatiel.pt browser/tests/condor.pt browser/tests/cps_test_localizer.pt browser/tests/cps_test_localizer.py browser/tests/cps_test_localizer.txt browser/tests/defaultview.zcml browser/tests/falcon.pt browser/tests/flamingo.pt browser/tests/framework.py browser/tests/i18n.pt browser/tests/menu.zcml browser/tests/ostrich.pt browser/tests/overrides.zcml browser/tests/owl.pt browser/tests/pages.py browser/tests/pages.txt browser/tests/pages.zcml browser/tests/pages/dirpage1.pt browser/tests/pages/dirpage2.pt browser/tests/pages_ftest.txt browser/tests/parakeet.pt browser/tests/pattern.png browser/tests/pts_test_languages.pt browser/tests/pts_test_languages.py browser/tests/pts_test_languages.txt browser/tests/resource.txt browser/tests/resource.zcml browser/tests/resource_ftest.txt browser/tests/seagull.pt browser/tests/security.pt browser/tests/skin.txt browser/tests/skin.zcml browser/tests/style.css browser/tests/tales_traversal.pt browser/tests/template_variables.pt browser/tests/test_absoluteurl.py browser/tests/test_adding.py browser/tests/test_defaultview.py browser/tests/test_i18n.py browser/tests/test_menu.py browser/tests/test_pages.py browser/tests/test_recurse.py browser/tests/test_resource.py browser/tests/test_skin.py browser/tests/test_traversable.py configure.zcml deprecated.zcml doc/ZopePublicLicense.txt doc/directives.txt doc/event.txt doc/features.txt doc/i18n.txt doc/localsite.txt doc/main.txt doc/manual.txt doc/presentations/five.mgp doc/presentations/five_directions.mgp doc/presentations/five_interface_tutorial.mgp doc/presentations/five_intro.mgp doc/presentations/five_misc_tutorial.mgp doc/presentations/five_views_tutorial.mgp doc/presentations/z3-banner.png doc/products/FiveMiscTutorial/__init__.py doc/products/FiveMiscTutorial/configure.zcml doc/products/FiveMiscTutorial/z3base.png doc/products/InterfaceTutorial/__init__.py doc/products/InterfaceTutorial/configure.zcml doc/products/InterfaceTutorial/module.py doc/products/InterfaceTutorial/other.py doc/products/README.txt doc/products/ViewsTutorial/__init__.py doc/products/ViewsTutorial/addDemoContent.pt doc/products/ViewsTutorial/browser.py doc/products/ViewsTutorial/configure.zcml doc/products/ViewsTutorial/democontent.py doc/products/ViewsTutorial/green5.png doc/products/ViewsTutorial/one.pt doc/products/ViewsTutorial/overview.pt doc/products/ViewsTutorial/overview2.pt doc/products/ViewsTutorial/someview.pt event.py event.zcml eventconfigure.py fiveconfigure.py fivedirectives.py form/__init__.py form/add.pt form/configure.zcml form/edit.pt form/macros.py form/meta.zcml form/metaconfigure.py form/tests/__init__.py form/tests/configure.zcml form/tests/forms.txt form/tests/framework.py form/tests/locales/de/LC_MESSAGES/formtest.mo form/tests/locales/de/LC_MESSAGES/formtest.po form/tests/locales/formtest.pot form/tests/schemacontent.py form/tests/test_forms.py i18n.py i18n.zcml interfaces.py interfaces.zcml meta.zcml metaclass.py metaconfigure.py monkey.py permissions.zcml runtests.py security.py services.zcml site/__init__.py site/browser.py site/configure.zcml site/interfaces.py site/localsite.py site/managesite.pt site/meta.zcml site/metaconfigure.py site/metadirectives.py site/tests/__init__.py site/tests/dummy.py site/tests/framework.py site/tests/functional.txt site/tests/sitemanager.txt site/tests/test_functional.py site/tests/test_localsite.py site/tests/test_sitemanager.py site/tests/test_utility.py site/utility.py sizeconfigure.py skel/README.txt skel/site.zcml skin/__init__.py skin/configure.zcml skin/five_template.pt skin/standardmacros.py skin/tests/__init__.py skin/tests/bird.pt skin/tests/configure.zcml skin/tests/demomacros.py skin/tests/dog.pt skin/tests/framework.py skin/tests/test_standardmacros.py testing/DEPENDENCIES.cfg testing/README.txt testing/__init__.py testing/cleanup.py testing/doctest.py testing/doctestunit.py testing/formparser.py testing/formparser.txt testing/loggingsupport.py testing/loghandler.py testing/module.py testing/profiling.txt testing/renormalizing.py testing/testrunner-edge-cases.txt testing/testrunner-ex/README.txt testing/testrunner-ex/sample1/__init__.py testing/testrunner-ex/sample1/sample11/__init__.py testing/testrunner-ex/sample1/sample11/sampletests.py testing/testrunner-ex/sample1/sample12/__init__.py testing/testrunner-ex/sample1/sample13/__init__.py testing/testrunner-ex/sample1/sample13/sampletests.py testing/testrunner-ex/sample1/sampletests/__init__.py testing/testrunner-ex/sample1/sampletests/test1.py testing/testrunner-ex/sample1/sampletests/test11.py testing/testrunner-ex/sample1/sampletests/test111.py testing/testrunner-ex/sample1/sampletests/test112.py testing/testrunner-ex/sample1/sampletests/test12.py testing/testrunner-ex/sample1/sampletests/test121.py testing/testrunner-ex/sample1/sampletests/test122.py testing/testrunner-ex/sample1/sampletests/test_one.py testing/testrunner-ex/sample1/sampletests_ntd.py testing/testrunner-ex/sample1/sampletests_ntds.py testing/testrunner-ex/sample1/sampletestsf.py testing/testrunner-ex/sample2/__init__.py testing/testrunner-ex/sample2/do-not-enter/sampletests.py testing/testrunner-ex/sample2/donotenter/sampletests.py testing/testrunner-ex/sample2/e.txt testing/testrunner-ex/sample2/sample21/__init__.py testing/testrunner-ex/sample2/sample21/sampletests.py testing/testrunner-ex/sample2/sample21/sampletests_i.py testing/testrunner-ex/sample2/sample22/__init__.py testing/testrunner-ex/sample2/sample22/sampletests_i.py testing/testrunner-ex/sample2/sample23/__init__.py testing/testrunner-ex/sample2/sample23/sampletests_i.py testing/testrunner-ex/sample2/sampletests/__init__.py testing/testrunner-ex/sample2/sampletests/test_1.py testing/testrunner-ex/sample2/sampletests/testone.py testing/testrunner-ex/sample2/sampletests_1.py testing/testrunner-ex/sample2/sampletests_e.py testing/testrunner-ex/sample2/sampletests_f.py testing/testrunner-ex/sample2/sampletests_i.py testing/testrunner-ex/sample2/sampletests_ntd.py testing/testrunner-ex/sample2/sampletests_ntds.py testing/testrunner-ex/sample3/__init__.py testing/testrunner-ex/sample3/post_mortem5.txt testing/testrunner-ex/sample3/post_mortem6.txt testing/testrunner-ex/sample3/post_mortem_failure.txt testing/testrunner-ex/sample3/sample31/__init__.py testing/testrunner-ex/sample3/sample32/__init__.py testing/testrunner-ex/sample3/sample33/__init__.py testing/testrunner-ex/sample3/sampletests.py testing/testrunner-ex/sample3/sampletests_d.py testing/testrunner-ex/sample3/sampletests_ntd.py testing/testrunner-ex/sample3/set_trace5.txt testing/testrunner-ex/sample3/set_trace6.txt testing/testrunner-ex/samplelayers.py testing/testrunner-ex/sampletests.txt testing/testrunner-ex/sampletests/__init__.py testing/testrunner-ex/sampletests/test1.py testing/testrunner-ex/sampletests/test11.py testing/testrunner-ex/sampletests/test111.py testing/testrunner-ex/sampletests/test112.py testing/testrunner-ex/sampletests/test12.py testing/testrunner-ex/sampletests/test121.py testing/testrunner-ex/sampletests/test122.py testing/testrunner-ex/sampletests/test_one.py testing/testrunner-ex/sampletestsf.py testing/testrunner-ex/sampletestsl.txt testing/testrunner.py testing/testrunner.txt testing/tests.py tests/README.txt tests/__init__.py tests/adapters.py tests/boilerplate.py tests/bridge.txt tests/directives.zcml tests/event.txt tests/framework.py tests/locales/de/LC_MESSAGES/fivetest.mo tests/locales/de/LC_MESSAGES/fivetest.po tests/locales/en/LC_MESSAGES/fivetest.mo tests/locales/en/LC_MESSAGES/fivetest.po tests/locales/fivetest.pot tests/meta.zcml tests/metaconfigure.py tests/overrides.zcml tests/test_bridge.py tests/test_directives.py tests/test_event.py tests/test_i18n.py tests/test_registerclass.py tests/test_security.py tests/test_size.py tests/test_viewable.py tests/testing/__init__.py tests/testing/fancycontent.py tests/testing/folder.py tests/testing/restricted.py tests/testing/simplecontent.py tests/viewable.txt traversable.py utilities/__init__.py utilities/browser/__init__.py utilities/browser/configure.zcml utilities/browser/edit_markers.pt utilities/browser/manage_interfaces.pt utilities/browser/marker.py utilities/browser/tests/__init__.py utilities/browser/tests/framework.py utilities/browser/tests/test_marker.py utilities/configure.zcml utilities/interfaces.py utilities/marker.py version.txt viewable.py zcml.py
diffstat 303 files changed, 29850 insertions(+), 0 deletions(-) [+]
line diff
     1.1 new file mode 100644
     1.2 --- /dev/null
     1.3 +++ b/CHANGES.txt
     1.4 @@ -0,0 +1,449 @@
     1.5 +============
     1.6 +Five Changes
     1.7 +============
     1.8 +
     1.9 +Five 1.3c (unreleased)
    1.10 +======================
    1.11 +
    1.12 +This version is also included in Zope 2.9b1.
    1.13 +
    1.14 +Restructuring
    1.15 +-------------
    1.16 +
    1.17 +* (b6) Added ``original`` parameter to ObjectCopiedEvent backported from
    1.18 +  Zope 3.2
    1.19 +
    1.20 +* (b4) Cleaned up security test.
    1.21 +
    1.22 +* (b4) Made Five send a ContainerModifiedEvent when appropriate.
    1.23 +
    1.24 +Bugfixes
    1.25 +--------
    1.26 +
    1.27 +* (b6) Added backward compatibility for old _setObject and _delObject
    1.28 +  implementations without suppress_events.
    1.29 +
    1.30 +* (b3) Made the creation of custom skins work again.  It was broken in
    1.31 +  the port to Zope 3.2.
    1.32 +
    1.33 +* (b2) Fixed bug that broke WebDAV access for five:defaultViewable
    1.34 +  objects. The __browser_default__ now modifies only GET and POST
    1.35 +  requests.
    1.36 +
    1.37 +* (b2) Fixed some event recursion compatibility modes.
    1.38 +
    1.39 +Five 1.3b2 (2005-11-10)
    1.40 +=======================
    1.41 +
    1.42 +This version is also included in Zope 2.9b1.
    1.43 +
    1.44 +Changes compared to Five 1.3b:
    1.45 +
    1.46 +Bugfixes
    1.47 +--------
    1.48 +
    1.49 +* Fixed bug that broke WebDAV access for five:defaultViewable objects. The
    1.50 +  __browser_default__ now modifies only GET and POST requests.
    1.51 +
    1.52 +* Fixed some event recursion compatibility modes.
    1.53 +
    1.54 +Five 1.3b (2005-11-02)
    1.55 +======================
    1.56 +
    1.57 +Restructuring
    1.58 +-------------
    1.59 +
    1.60 +* Support for Zope 3.2 was added. Five now requires Zope 2.9 (which
    1.61 +  ships with Zope 3.2).
    1.62 +
    1.63 +* As scheduled, the temporary fork of the new test runner
    1.64 +  (``zope.testing``) at ``Five.testing`` was removed.  So was the
    1.65 +  ``runtests.py`` script.  Use the regular Zope test runner
    1.66 +  (``test.py`` or ``bin/zopectl test``) to run tests.
    1.67 +
    1.68 +* To reflect the Component Architecture simplification in Zope 3 since
    1.69 +  the X3 3.0 release, ``IFiveUtilityService`` was renamed to
    1.70 +  ``IFiveUtilityRegistry`` and ``SimpleLocalUtilityService`` was
    1.71 +  renamed to ``SimpleLocalUtilityRegistry``.  The old names are still
    1.72 +  available for a short period of time.
    1.73 +
    1.74 +* Event support: ``<five:containerEvents/>`` is the default.
    1.75 +
    1.76 +* Due to an incompatability with Zope 3.2's ObjectWidget and Zope 2's
    1.77 +  Page Templates, Five now ships with its own ObjectWidget
    1.78 +  implementation (which is just a thin wrapper around Zope's one to
    1.79 +  make it work in Zope 2).  If you use the ObjectWidget, please change
    1.80 +  your imports to ``Products.Five.form.objectwidget.ObjectWidget``.
    1.81 +
    1.82 +* Backwards compatability for Zope 3-style interfaces of Zope 2
    1.83 +  components has been removed as that functionality is now in the Zope
    1.84 +  2 core as of Zope 2.9.
    1.85 +
    1.86 +Five 1.2 (unreleased)
    1.87 +=====================
    1.88 +
    1.89 +Bugfixes
    1.90 +--------
    1.91 +
    1.92 +* Fixed bug that broke WebDAV access for five:defaultViewable objects. The
    1.93 +  __browser_default__ now modifies only GET and POST requests.
    1.94 +
    1.95 +* Fixed some event recursion compatibility modes.
    1.96 +
    1.97 +* Fixed loops in zcml loading due to events in some cases.
    1.98 +
    1.99 +* Made Five send a ContainerModifiedEvent when appropriate.
   1.100 +
   1.101 +Restructuring
   1.102 +-------------
   1.103 +
   1.104 +* Cleaned up security test.
   1.105 +
   1.106 +* Added monkey so that ++skin++ works with Zope <= 2.8.4.
   1.107 +
   1.108 +Five 1.2b (2005-11-02)
   1.109 +======================
   1.110 +
   1.111 +Features
   1.112 +--------
   1.113 +
   1.114 +* Added IMarkerInterfaces adapter: This adapter provides methods for
   1.115 +  inspecting and assigning marker interfaces. 'edit-markers.html' (or
   1.116 +  'manage_interfaces' in the ZMI) allows to change the behavior of specific
   1.117 +  objects by adding or removing marker interfaces TTW.
   1.118 +
   1.119 +* Added the five:registerClass directive: This does the necessary Zope 2
   1.120 +  registration for Five-based content. It is no longer necessary to add an
   1.121 +  ``initialize()`` function to the product's __init__ in order to register
   1.122 +  a meta type to be addable through the ZMI. See doc/products/ViewsTutorial
   1.123 +  for an example how to use the directive.
   1.124 +
   1.125 +* Local site support: Five has now support for creating local sites
   1.126 +  and thereby local utilities. This is mostly needed for allowing CMF
   1.127 +  to convert it's portal tools into local utilities.  See
   1.128 +  doc/localsite.txt for more information
   1.129 +
   1.130 +* Event support: When ``<five:containerEvents/>`` is specified, Five
   1.131 +  makes the standard Zope 2 containers send events instead of using
   1.132 +  manage_afterAdd, manage_beforeDelete and manage_afterClone. These
   1.133 +  methods are still called for a class declared
   1.134 +  ``<five:deprecatedManageAddDelete class=.../>``, and are called in
   1.135 +  compatibility mode with a deprecation warning for classes that don't
   1.136 +  use this directive.
   1.137 +
   1.138 +Restructuring
   1.139 +-------------
   1.140 +
   1.141 +* Removed backwards compatibility for Five 1.0 Zope core interfaces.
   1.142 +
   1.143 +* Removed backwards compatibility for Zope 2.7 and 2.8.0.
   1.144 +
   1.145 +* Added a (temporarily) forked copy of the "new-and-improved" test
   1.146 +  runner and supporting ``zope.testing`` package, lifted from
   1.147 +  http://svn.zope.org/zope.testing.  This code should be removed for
   1.148 +  Five 1.3, which will use the updated version of ``zope.testing`` in
   1.149 +  the Zope 2.9 / Zope 3.2 tree.
   1.150 +
   1.151 +  There is a test runner invoking script in the ``Five`` package.  For
   1.152 +  example, to run the Five tests with the new test runner, simply
   1.153 +  execute the following command line from your instance home::
   1.154 +
   1.155 +    $ bin/zopectl run Products/Five/runtests.py -v -s Products.Five
   1.156 +
   1.157 +* Moved the ``Five.testing`` package down to ``Five.tests.testing``,
   1.158 +  in order to make room for the 'zope.testing' code.
   1.159 +
   1.160 +* Removed backwards compatibility for some moved classes (AddForm,
   1.161 +  EditForm, ContentAdding)
   1.162 +
   1.163 +Five 1.1 (2005-10-04)
   1.164 +=====================
   1.165 +
   1.166 +Features
   1.167 +--------
   1.168 +
   1.169 +* When Zope was not in debug mode, an error in a ZCML file would cause Five to
   1.170 +  stop loading ZCML completely, making all subsequent products "dead". The
   1.171 +  effect would typically be that objects appeared to have no views at all. 
   1.172 +  Now a ZCML error will only stop the ZCML loading for that product, but the 
   1.173 +  rest of the products will load as usual. A traceback will still be printed 
   1.174 +  in the event log.
   1.175 +
   1.176 +  In debug mode the behaviour has not changed; a ZCML error will stop Zope 
   1.177 +  startup completely, and print a traceback if running in foreground mode.
   1.178 +  
   1.179 +Restructuring
   1.180 +-------------
   1.181 +
   1.182 +* The deprecated FivePageTemplateFile was removed, and the erroneous use of 
   1.183 +  this by EditView was changed.
   1.184 +
   1.185 +Bugfixes
   1.186 +--------
   1.187 +
   1.188 +* Repaired 'forms.txt' test which expected an error page when passing
   1.189 +  'handle_errors' as False;  it now expects an Unauthorized traceback.
   1.190 +  Note that this test fails on Zope 2.8.1, which incorrectly ignored
   1.191 +  'handle_errors'.
   1.192 +
   1.193 +* FiveTraversable should only do a view lookup and not call the traverse 
   1.194 +  method of its superclass.
   1.195 +
   1.196 +* Fixed manage_beforeDelete triggering for classes using five:sendEvents.
   1.197 +
   1.198 +* The redefinePermission directive was falsely registered under the
   1.199 +  ``zope`` namespace, not the ``meta`` namespace as it is in Zope 3.
   1.200 +
   1.201 +* Some parts of add.pt and edit.pt were not translated correctly or not
   1.202 +  translated at all. The fix depends on TAL changes in Zope 2.8.1 and changes
   1.203 +  in Zope X3-3.0.1 (shipped with 2.8.1). Form i18n is still broken with older
   1.204 +  Zope versions.
   1.205 +
   1.206 +* 'zope' domain translations are now set up by default. Form i18n needs them.
   1.207 +
   1.208 +* Added backwards compatibility for some moved classes (AddForm, EditForm, 
   1.209 +  ContentAdding)
   1.210 +
   1.211 +* The ZPT variable 'container' makes little sense in Zope3/Five, but is now
   1.212 +  always set to be the same as 'here' which is normal Zope2 behaviour.
   1.213 +  It is in Five 1.0.x set to be the same as 'view' which breaks some templates.
   1.214 +  
   1.215 +* In some hard to replicate cases, using the "modules" variable in ZPT cause
   1.216 +  an AuthenticationError. Using the secure module importer fixes this.
   1.217 +
   1.218 +* If you used some parts of Zope 3 (for example the mail delivery) Five 1.1
   1.219 +  transaction backport would conflict with Zope 3s transaction module.
   1.220 +  This is now solved.
   1.221 +
   1.222 +Five 1.1b (2005-07-13)
   1.223 +======================
   1.224 +
   1.225 +Features
   1.226 +--------
   1.227 +
   1.228 +* Zope 3-style i18n support has been provided.  Apart from being able
   1.229 +  to register translations through ZCML, Five now lets Zope 2 ZPTs
   1.230 +  automatically use Zope 3 translation domains.  Fallback to an
   1.231 +  old-style translation service (e.g. Localizer or PTS) is supported.
   1.232 +  This also includes the detection of preferred languages.  See
   1.233 +  ``doc/i18n.txt`` for more information.
   1.234 +
   1.235 +* Added support for Zope 3 -> Zope 2 interface bridging.  This
   1.236 +  functionality will be part of Zope 2.9, with Five you can already
   1.237 +  use it in Zope 2.7/2.8.  Since Zope 2 interfaces are rarely used and
   1.238 +  their Zope 3 equivalents are more meaningful (for the Component
   1.239 +  Architecture), the preferred way of dealing with interface migration
   1.240 +  is to write Zope 3 interfaces and bridge them to Zope 2 ones as
   1.241 +  needed.  To bridge, use the ``Interface.bridge.fromZ3Interface()``
   1.242 +  function.
   1.243 +
   1.244 +* Support for the standard <factory />, <modulealias /> and <hook />
   1.245 +  ZCML directives was added.
   1.246 +
   1.247 +* The default browser view name for all objects is now 'index.html',
   1.248 +  just as it is in Zope 3.  This means that a view by that name will
   1.249 +  be looked up if no specific view name is given in the URL.
   1.250 +
   1.251 +Restructuring
   1.252 +-------------
   1.253 +
   1.254 +* Restructured the Five source code to be easier to navigate in.
   1.255 +  Three subpackages were created, Five.browser, Five.form and
   1.256 +  Five.skin.
   1.257 +
   1.258 +* The former test product, ``FiveTest``, was converted into separate
   1.259 +  modules that provide the mock objects for the corresponding tests
   1.260 +  and are located right next to them.  Common test helpers have been
   1.261 +  moved to the Five.testing package.  Overall, the testing framework
   1.262 +  was much simplified and the individual tests clean up after
   1.263 +  themselves much like they do in Zope 3.
   1.264 +
   1.265 +* Relocated Zope core interfaces. Future Zope versions will ship with their
   1.266 +  own z3 interfaces. Five now patches the older Zope versions to make sure
   1.267 +  you can always find the interfaces in 'AccessControl.interfaces',
   1.268 +  'Acquisition.interfaces', 'App.interfaces', 'OFS.interfaces' and
   1.269 +  'webdav.interfaces'. Please don't use the aliases in 'Five.interfaces' or
   1.270 +  'Five.bbb.*interfaces' - they are only provided for backwards
   1.271 +  compatibility.
   1.272 +
   1.273 +* Zope 2.8 HTTPRequest is no longer patched. It has the required methods.
   1.274 +
   1.275 +Bugfixes
   1.276 +--------
   1.277 +
   1.278 +* The ZPT variable 'container' did not always contain the parent object
   1.279 +  of the context.
   1.280 +
   1.281 +* The deprecated get_transaction method is no longer used in Zope 2.8.
   1.282 +
   1.283 +Five 1.0.2 (2005-07-12)
   1.284 +=======================
   1.285 +
   1.286 +This version is also included in Zope 2.8.1
   1.287 +
   1.288 +* Fixed some issues with bridged interfaces: Bases and Methods were not
   1.289 +  bridged correctly. extends() was never True.
   1.290 +
   1.291 +* zope.security.checkPermission now behaves exactly like
   1.292 +  Five.security.checkPermission (in fact, the former now calls the
   1.293 +  latter through the indirection of Zope 3 security policies).
   1.294 +
   1.295 +* Fixed a bug with resource directories.  Resources within those were
   1.296 +  not rendering their absolute URL correctly.
   1.297 +
   1.298 +Five 1.0.1 (2005-05-31)
   1.299 +=======================
   1.300 +
   1.301 +This version is also included in Zope 2.8.0
   1.302 +
   1.303 +* Changed license headers to the ones used in the Zope.org repository.
   1.304 +  This makes merging between the main development line of Five (hosted
   1.305 +  on codespeak.net) and the version integrated into Zope 2.8 much
   1.306 +  easier.  The actual copyright ownership isn't affected because Five
   1.307 +  had been contributed to the Zope project anyway (which was blessed
   1.308 +  by all Five contributors).
   1.309 +
   1.310 +* Made automatically generated add and edit forms unicode-aware.
   1.311 +  ZPublisher does not automatically decode incoming form values to
   1.312 +  unicode, so AddView and EditView emulate this behaviour themselves
   1.313 +  now.  They also take care of setting the right charset on the
   1.314 +  outgoing form so that ZPublisher will encode it accordingly when
   1.315 +  sending the response to the client.  (In Zope 3, all charset
   1.316 +  negotation between the client and the server takes place in the
   1.317 +  publisher.)
   1.318 +
   1.319 +* Added ``IHTTPCharset`` adapter for ``IHTTPRequest`` so that
   1.320 +  application can find out the preferred character set of the HTTP
   1.321 +  client (Zope 2 applications needs to take care of their own charset
   1.322 +  header).  The adapter is used for the automatically-generated forms
   1.323 +  when determining encodings for unicode field content.
   1.324 +
   1.325 +* Modified edit.pt to make sure editforms have only one body tag.
   1.326 +
   1.327 +* Fixed the ``INameChooser`` adapter for ObjectManagers (e.g. Zope 2
   1.328 +  folders) and added unit tests.
   1.329 +
   1.330 +* Fixed small bug in BrowserDefault which caused an error if the class is
   1.331 +  defaultViewable but the object's interfaces have no defaultView.
   1.332 +
   1.333 +Five 1.0 (2005-04-27)
   1.334 +=====================
   1.335 +
   1.336 +Features
   1.337 +--------
   1.338 +
   1.339 +* Zope 3 style ``ISized`` adapters for objects are now exposed to the
   1.340 +  ZMI and other Zope 2 frameworks via the known ``get_size`` method,
   1.341 +  provided this is turned for the class in question via the
   1.342 +  five:sizable ZCML directive.
   1.343 +
   1.344 +* There is now a standard standard_macros. Five page templates can use
   1.345 +  context/@@standard_macros/view to get the default site layout, and
   1.346 +  people can register their own standard_macros in a skin.
   1.347 +
   1.348 +* The addform and editform directive now supports the widget ZCML
   1.349 +  subdirective, which previously was ignored.
   1.350 +
   1.351 +* Five now supports the vocabulary ZCML directive.
   1.352 +
   1.353 +Bugfixes
   1.354 +--------
   1.355 +
   1.356 +* Add and edit forms are now protected properly.
   1.357 +
   1.358 +* The checkbox widget did not work correctly in its off state, this
   1.359 +  has been fixed.
   1.360 +
   1.361 +Five 0.3 (2005-03-11)
   1.362 +=====================
   1.363 +
   1.364 +* Five now uses the Zope 2 page template engine, not the Zope 3
   1.365 +  engine. This allows better integration with Zope 2-based page
   1.366 +  templates, such as macros.
   1.367 +
   1.368 +  It uses TrustedExecutables technology (thanks to Dieter Maurer) to
   1.369 +  turn off Zope 2 security in page templates, so Five's security
   1.370 +  behavior is very similar to what it was before.
   1.371 +
   1.372 +* Five now supports the browser:menu, menuItem and menuItems
   1.373 +  directives.
   1.374 +
   1.375 +* A new Five-specific directive has been added:
   1.376 +  five:pagesFromDirectory.  This adds one page for each .pt file in a
   1.377 +  directory to the specified interface. This is useful for Five
   1.378 +  integration with CMF and other systems that have Page Templates
   1.379 +  macros that need to be shared between Zope2 and Five.
   1.380 +
   1.381 +* Five.security.checkPermission has been changed from a (unused)
   1.382 +  method for checking the existence of permissions. Use
   1.383 +  zope.app.security.permission.checkPermission if you need that
   1.384 +  functionality.
   1.385 +
   1.386 +  Instead Five.security.checkPermission is now a Five version of
   1.387 +  zope.security.checkPermission, which checks if the current user has
   1.388 +  a permission on an object.
   1.389 +
   1.390 +* Support for browser:editform. You can now use schemas for editing.
   1.391 +
   1.392 +* Support for browser:addform; add forms using '+'. You can now browse
   1.393 +  to 'container/+/addsomething.html' to get to a schema-driven add
   1.394 +  form.
   1.395 +
   1.396 +* Fixed a traversal bug which caused Zope to give the wrong error when
   1.397 +  a page could not be found (missing docstring instead of not
   1.398 +  found). Zope 2.7.4 (or higher) is required for this fix.
   1.399 +
   1.400 +Five 0.2b (2004-09-24)
   1.401 +======================
   1.402 +
   1.403 +* Added utility module, 'bridge', allowing reuse of Zope 2 interfaces
   1.404 +  (by introspecting them to create equivalent Zope 3 interfaces).
   1.405 +
   1.406 +* five:viewable was renamed to five:traversable, five:viewable still
   1.407 +  works but is deprecated; a deprecation warning is emitted when it is
   1.408 +  used.
   1.409 +
   1.410 +* like in Zope3, an ITraverser adapter is looked up to determine what
   1.411 +  happens when traversing into a Five traversable object.
   1.412 +
   1.413 +* added five:defaultViewable to make instances of a class directly
   1.414 +  viewable using browser:defaultView. This is hookable by the use of a
   1.415 +  IBrowserDefault adapter
   1.416 +
   1.417 +* deprecated use of Products.Five.api as public API for other products
   1.418 +  to use, instead import directly from Products.Five. Retired
   1.419 +  Traversable and Viewable from the public API; use ZCML directives
   1.420 +  (five:traversable, five:defaultView) instead of mixins to make
   1.421 +  instances of classes work with Five.
   1.422 +
   1.423 +* classes that Five monkeypatches now have a __five_method__
   1.424 +  attribute, making it easier for Five not to stomp on existing methods.
   1.425 +
   1.426 +* registered absolute_url view and IAbsoluteURL adapter for *
   1.427 +
   1.428 +* zope.app.traversing is registered by default, to make special
   1.429 +  namespaces available (eg: @@, ++resource++)
   1.430 +
   1.431 +* we now have resources (FileResource, ImageResource,
   1.432 +  PageTemplateResource) and directory resources.
   1.433 +
   1.434 +* Zope 3 'StandardMacros' now works with Five as well.
   1.435 +
   1.436 +* browser:page now correctly handles the allow_attributes and protects
   1.437 +  the named attributes on the view with the same permission used for
   1.438 +  the view.
   1.439 +
   1.440 +* zopeconf.py will try to find etc/zope.conf on INSTANCE_HOME. This
   1.441 +  requires Zope 2.7.2, as earlier Zope versions have a bug in this
   1.442 +  area which causes them to look in lib/python/Testing.
   1.443 +
   1.444 +* Exposed the Zope 3 event system to Five. A class can be made to send
   1.445 +  out event notifications using the five:sendEvents directive. Events can
   1.446 +  be subscribed to using the subscriber directive.
   1.447 +
   1.448 +* Change in findProducts so that non-filesystem products are skipped.
   1.449 +
   1.450 +Five 0.1 (2004-07-30)
   1.451 +=====================
   1.452 +
   1.453 +Initial public release (mainly Martijn's work)
     2.1 new file mode 100644
     2.2 --- /dev/null
     2.3 +++ b/COPYING.txt
     2.4 @@ -0,0 +1,16 @@
     2.5 +Five is distributed under the provisions of the Zope Public License
     2.6 +(ZPL) v2.1.  See doc/ZopePublicLicense.txt for the license text.
     2.7 +
     2.8 +Copyright (C) 2005 Five Contributors. See CREDITS.txt for a list of
     2.9 +Five contributors.
    2.10 +
    2.11 +Five contains source code derived from:
    2.12 +
    2.13 +- Zope 3, copyright (C) 2001-2005 by Zope Corporation.
    2.14 +
    2.15 +- metaclass.py is derived from PEAK, copyright (C) 1996-2004 by
    2.16 +  Phillip J. Eby and Tyler C. Sarna. PEAK may be used under the same
    2.17 +  terms as Zope.
    2.18 +
    2.19 +- TrustedExecutables. Dieter Mauer kindly allow licensing this under the
    2.20 +  ZPL 2.1.
     3.1 new file mode 100644
     3.2 --- /dev/null
     3.3 +++ b/CREDITS.txt
     3.4 @@ -0,0 +1,54 @@
     3.5 +Five contributors
     3.6 +-----------------
     3.7 +
     3.8 +- Martijn Faassen (faassen@infrae.com)
     3.9 +
    3.10 +- Sidnei da Silva (sidnei@awkly.org)
    3.11 +
    3.12 +- Philipp von Weitershausen (philikon@philikon.de)
    3.13 +
    3.14 +- Lennart Regebro (regebro@nuxeo.com)
    3.15 +
    3.16 +- Tres Seaver (tseaver@palladion.com)
    3.17 +
    3.18 +- Jan-Wijbrand Kolman (jw@infrae.com)
    3.19 +
    3.20 +- Stefan Holek (ssh@epy.co.at)
    3.21 +
    3.22 +- Florent Guillaume (fg@nuxeo.com)
    3.23 +
    3.24 +- Godefroid Chapelle (gotcha@bubblenet.be)
    3.25 +
    3.26 +- Andy Adiwidjaja (mail@adiwidjaja.com)
    3.27 +
    3.28 +- Stuart Bishop (stuart@stuartbishop.net)
    3.29 +
    3.30 +- Simon Eisenmann (simon@struktur.de)
    3.31 +
    3.32 +- Dieter Maurer (dieter@handshake.de)
    3.33 +
    3.34 +- Yvo Schubbe (y.2005-@wcm-solutions.de)
    3.35 +
    3.36 +- Malcolm Cleaton (malcolm@jamkit.com)
    3.37 +
    3.38 +- Tarek Ziadé (tziade@nuxeo.com)
    3.39 +
    3.40 +- Whit Morriss (whit@longnow.org)
    3.41 +
    3.42 +
    3.43 +Thank you
    3.44 +---------
    3.45 +
    3.46 +Infrae for the initial development and continuing support.
    3.47 +
    3.48 +Martijn Faassen would like to thank ETH Zurich for their support and
    3.49 +encouragement during the initial development of Five. 
    3.50 +
    3.51 +Nuxeo for significant contributions to making Five usable in the real
    3.52 +world.
    3.53 +
    3.54 +Dieter Maurer for use of code from TrustedExecutables within Five
    3.55 +under the ZPL.
    3.56 +
    3.57 +The Five developers would like to thank the Zope 3 developers, in
    3.58 +particular Jim Fulton, for the mountain to stand on.
     4.1 new file mode 100644
     4.2 --- /dev/null
     4.3 +++ b/INSTALL.txt
     4.4 @@ -0,0 +1,39 @@
     4.5 +How to install Five
     4.6 +===================
     4.7 +
     4.8 +Requirements for Five 1.2
     4.9 +-------------------------
    4.10 +
    4.11 +* Zope 2.8.1+ with Python 2.3.x.  Zope 2.8.5+ is recommended as it
    4.12 +  contains a bugfix regarding Zope 3-style skins.
    4.13 +
    4.14 +Note that Five 1.0 is already part of Zope 2.8.  This doesn't matter,
    4.15 +though, you can still install Five 1.2 into your Zope 2.8 instances.
    4.16 +It will override Zope's older version of Five.
    4.17 +
    4.18 +
    4.19 +Compatability matrix
    4.20 +--------------------
    4.21 +
    4.22 +The following table shows which Five version can and should be used
    4.23 +with which Zope 2 and Zope 3 versions.
    4.24 +
    4.25 +============ ======================= =========== ========
    4.26 +.                    Zope 2.7         Zope 2.8   Zope 2.9
    4.27 +------------ ----------------------- ----------- --------
    4.28 +.            Zope X3 3.0 (not incl.) Zope X3 3.0 Zope 3.2
    4.29 +============ ======================= =========== ========
    4.30 +Five 1.0                X             included
    4.31 +Five 1.1[#]_            X                 X
    4.32 +Five 1.2                                  X
    4.33 +Five 1.3                                         included
    4.34 +============ ======================= =========== ========
    4.35 +
    4.36 +.. [#] This branch is no longer actively maintained.
    4.37 +
    4.38 +
    4.39 +Running the tests
    4.40 +-----------------
    4.41 +
    4.42 +For information on how to install the automatic Five tests, please see
    4.43 +``tests/README.txt``.
     5.1 new file mode 100644
     5.2 --- /dev/null
     5.3 +++ b/README.txt
     5.4 @@ -0,0 +1,51 @@
     5.5 +Introduction
     5.6 +------------
     5.7 +
     5.8 +"It was the dawn of the third age of Zope. The Five project was a dream
     5.9 +given form. Its goal: to use Zope 3 technologies in Zope 2.7 by
    5.10 +creating a Zope 2 product where Zope 3 and Zope 2 could work out their
    5.11 +differences peacefully." -- Babylon 5, creatively quoted
    5.12 +
    5.13 +"The Law of Fives states simply that: ALL THINGS HAPPEN IN FIVES, OR
    5.14 +ARE DIVISIBLE BY OR ARE MULTIPLES OF FIVE, OR ARE SOMEHOW DIRECTLY OR
    5.15 +INDIRECTLY RELATED TO FIVE.
    5.16 +
    5.17 +THE LAW OF FIVES IS NEVER WRONG." -- Principia Discordia
    5.18 +
    5.19 +What is Five?
    5.20 +-------------
    5.21 +
    5.22 +The goal of five is to allow Zope 2 developers to use Zope 3
    5.23 +technology right now, inside of Zope 2. Additionally, this allows a
    5.24 +gradual evolution of Zope 2 code to Zope 3.
    5.25 +
    5.26 +Five already makes the following Zope 3 technologies available in Zope
    5.27 +2:
    5.28 +
    5.29 +* Zope 3 interfaces
    5.30 +
    5.31 +* ZCML (Zope Configuration Markup Language)
    5.32 +
    5.33 +* Adapters
    5.34 +
    5.35 +* Zope 3 views, even for standard Zope objects
    5.36 +
    5.37 +* layers & skins
    5.38 +
    5.39 +* schema/forms machinery, including edit and add forms.
    5.40 +
    5.41 +* Zope 2 security declarations in ZCML instead of in Python code.
    5.42 +
    5.43 +Together with another product, CMFonFive, Five can integrate into CMF.
    5.44 +
    5.45 +For more information, see ``doc/features.txt``.
    5.46 +
    5.47 +How to install Five
    5.48 +-------------------
    5.49 +
    5.50 +See ``INSTALL.txt``.
    5.51 +
    5.52 +How to use Five
    5.53 +---------------
    5.54 +
    5.55 +Please see ``doc/manual.txt``.
     6.1 new file mode 100644
     6.2 --- /dev/null
     6.3 +++ b/__init__.py
     6.4 @@ -0,0 +1,33 @@
     6.5 +##############################################################################
     6.6 +#
     6.7 +# Copyright (c) 2004, 2005 Zope Corporation and Contributors.
     6.8 +# All Rights Reserved.
     6.9 +#
    6.10 +# This software is subject to the provisions of the Zope Public License,
    6.11 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
    6.12 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
    6.13 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    6.14 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
    6.15 +# FOR A PARTICULAR PURPOSE.
    6.16 +#
    6.17 +##############################################################################
    6.18 +"""Initialize the Five product
    6.19 +
    6.20 +$Id: __init__.py 12884 2005-05-30 13:10:41Z philikon $
    6.21 +"""
    6.22 +import Acquisition
    6.23 +from Globals import INSTANCE_HOME
    6.24 +
    6.25 +import monkey
    6.26 +import zcml
    6.27 +
    6.28 +# trigger monkey patches
    6.29 +monkey.monkeyPatch()
    6.30 +
    6.31 +# public API provided by Five
    6.32 +# usage: from Products.Five import <something>
    6.33 +from browser import BrowserView
    6.34 +from skin.standardmacros import StandardMacros
    6.35 +
    6.36 +def initialize(context):
    6.37 +    zcml.load_site()
     7.1 new file mode 100644
     7.2 --- /dev/null
     7.3 +++ b/bbb/AccessControl_interfaces.py
     7.4 @@ -0,0 +1,235 @@
     7.5 +##############################################################################
     7.6 +#
     7.7 +# Copyright (c) 2004, 2005 Zope Corporation and Contributors.
     7.8 +# All Rights Reserved.
     7.9 +#
    7.10 +# This software is subject to the provisions of the Zope Public License,
    7.11 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
    7.12 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
    7.13 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    7.14 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
    7.15 +# FOR A PARTICULAR PURPOSE.
    7.16 +#
    7.17 +##############################################################################
    7.18 +"""AccessControl z3 interfaces.
    7.19 +
    7.20 +$Id: AccessControl_interfaces.py 12884 2005-05-30 13:10:41Z philikon $
    7.21 +"""
    7.22 +from zope.interface import Attribute
    7.23 +from zope.interface import Interface
    7.24 +
    7.25 +
    7.26 +# XXX: might contain non-API methods and outdated comments;
    7.27 +#      not synced with ZopeBook API Reference;
    7.28 +#      based on AccessControl.Owned.Owned
    7.29 +class IOwned(Interface):
    7.30 +
    7.31 +    manage_owner = Attribute("""Manage owner view""")
    7.32 +
    7.33 +    def owner_info():
    7.34 +        """Get ownership info for display
    7.35 +        """
    7.36 +
    7.37 +    def getOwner(info=0):
    7.38 +        """Get the owner
    7.39 +
    7.40 +        If a true argument is provided, then only the owner path and id are
    7.41 +        returned. Otherwise, the owner object is returned.
    7.42 +        """
    7.43 +
    7.44 +    def getOwnerTuple():
    7.45 +        """Return a tuple, (userdb_path, user_id) for the owner.
    7.46 +
    7.47 +        o Ownership can be acquired, but only from the containment path.
    7.48 +
    7.49 +        o If unowned, return None.
    7.50 +        """
    7.51 +
    7.52 +    def getWrappedOwner():
    7.53 +        """Get the owner, modestly wrapped in the user folder.
    7.54 +
    7.55 +        o If the object is not owned, return None.
    7.56 +
    7.57 +        o If the owner's user database doesn't exist, return Nobody.
    7.58 +
    7.59 +        o If the owner ID does not exist in the user database, return Nobody.
    7.60 +        """
    7.61 +
    7.62 +    def changeOwnership(user, recursive=0):
    7.63 +        """Change the ownership to the given user.  If 'recursive' is
    7.64 +        true then also take ownership of all sub-objects, otherwise
    7.65 +        sub-objects retain their ownership information."""
    7.66 +
    7.67 +    def userCanTakeOwnership():
    7.68 +        """ """
    7.69 +
    7.70 +    def manage_takeOwnership(REQUEST, RESPONSE, recursive=0):
    7.71 +        """
    7.72 +        Take ownership (responsibility) for an object. If 'recursive'
    7.73 +        is true, then also take ownership of all sub-objects.
    7.74 +        """
    7.75 +
    7.76 +    def manage_changeOwnershipType(explicit=1,
    7.77 +                                   RESPONSE=None, REQUEST=None):
    7.78 +        """Change the type (implicit or explicit) of ownership.
    7.79 +        """
    7.80 +
    7.81 +    def _deleteOwnershipAfterAdd():
    7.82 +        """ """
    7.83 +
    7.84 +    def manage_fixupOwnershipAfterAdd():
    7.85 +        """ """
    7.86 +
    7.87 +
    7.88 +# XXX: might contain non-API methods and outdated comments;
    7.89 +#      not synced with ZopeBook API Reference;
    7.90 +#      based on AccessControl.PermissionMapping.RoleManager
    7.91 +class IPermissionMappingSupport(Interface):
    7.92 +
    7.93 +    def manage_getPermissionMapping():
    7.94 +        """Return the permission mapping for the object
    7.95 +
    7.96 +        This is a list of dictionaries with:
    7.97 +
    7.98 +          permission_name -- The name of the native object permission
    7.99 +
   7.100 +          class_permission -- The class permission the permission is
   7.101 +             mapped to.
   7.102 +        """
   7.103 +
   7.104 +    def manage_setPermissionMapping(permission_names=[],
   7.105 +                                    class_permissions=[], REQUEST=None):
   7.106 +        """Change the permission mapping
   7.107 +        """
   7.108 +
   7.109 +
   7.110 +# XXX: might contain non-API methods and outdated comments;
   7.111 +#      not synced with ZopeBook API Reference;
   7.112 +#      based on AccessControl.Role.RoleManager
   7.113 +class IRoleManager(IPermissionMappingSupport):
   7.114 +
   7.115 +    """An object that has configurable permissions"""
   7.116 +
   7.117 +    permissionMappingPossibleValues = Attribute("""Acquired attribute""")
   7.118 +
   7.119 +    def ac_inherited_permissions(all=0):
   7.120 +        """Get all permissions not defined in ourself that are inherited.
   7.121 +
   7.122 +        This will be a sequence of tuples with a name as the first item and an
   7.123 +        empty tuple as the second.
   7.124 +        """
   7.125 +
   7.126 +    def permission_settings(permission=None):
   7.127 +        """Return user-role permission settings. If 'permission' 
   7.128 +           is passed to the method then only the settings for 'permission'
   7.129 +           is returned.
   7.130 +        """
   7.131 +
   7.132 +    manage_roleForm = Attribute(""" """)
   7.133 +
   7.134 +    def manage_role(role_to_manage, permissions=[], REQUEST=None):
   7.135 +        """Change the permissions given to the given role"""
   7.136 +
   7.137 +    manage_acquiredForm = Attribute(""" """)
   7.138 +
   7.139 +    def manage_acquiredPermissions(permissions=[], REQUEST=None):
   7.140 +        """Change the permissions that acquire"""
   7.141 +
   7.142 +    manage_permissionForm = Attribute(""" """)
   7.143 +
   7.144 +    def manage_permission(permission_to_manage,
   7.145 +                          roles=[], acquire=0, REQUEST=None):
   7.146 +        """Change the settings for the given permission
   7.147 +
   7.148 +        If optional arg acquire is true, then the roles for the permission
   7.149 +        are acquired, in addition to the ones specified, otherwise the
   7.150 +        permissions are restricted to only the designated roles."""
   7.151 +
   7.152 +    def manage_access(REQUEST, **kw):
   7.153 +        """Return an interface for making permissions settings"""
   7.154 +
   7.155 +    def manage_changePermissions(REQUEST):
   7.156 +        """Change all permissions settings, called by management screen"""
   7.157 +
   7.158 +    def permissionsOfRole(role):
   7.159 +        """used by management screen"""
   7.160 +
   7.161 +    def rolesOfPermission(permission):
   7.162 +        """used by management screen"""
   7.163 +
   7.164 +    def acquiredRolesAreUsedBy(permission):
   7.165 +        """used by management screen"""
   7.166 +
   7.167 +
   7.168 +    # Local roles support
   7.169 +    # -------------------
   7.170 +    #
   7.171 +    # Local roles allow a user to be given extra roles in the context
   7.172 +    # of a particular object (and its children). When a user is given
   7.173 +    # extra roles in a particular object, an entry for that user is made
   7.174 +    # in the __ac_local_roles__ dict containing the extra roles.
   7.175 +
   7.176 +    __ac_local_roles__  = Attribute(""" """)
   7.177 +
   7.178 +    manage_listLocalRoles = Attribute(""" """)
   7.179 +
   7.180 +    manage_editLocalRoles = Attribute(""" """)
   7.181 +
   7.182 +    def has_local_roles():
   7.183 +        """ """
   7.184 +
   7.185 +    def get_local_roles():
   7.186 +        """ """
   7.187 +
   7.188 +    def users_with_local_role(role):
   7.189 +        """ """
   7.190 +
   7.191 +    def get_valid_userids():
   7.192 +        """ """
   7.193 +
   7.194 +    def get_local_roles_for_userid(userid):
   7.195 +        """ """
   7.196 +
   7.197 +    def manage_addLocalRoles(userid, roles, REQUEST=None):
   7.198 +        """Set local roles for a user."""
   7.199 +
   7.200 +    def manage_setLocalRoles(userid, roles, REQUEST=None):
   7.201 +        """Set local roles for a user."""
   7.202 +
   7.203 +    def manage_delLocalRoles(userids, REQUEST=None):
   7.204 +        """Remove all local roles for a user."""
   7.205 +
   7.206 +    #------------------------------------------------------------
   7.207 +
   7.208 +    def access_debug_info():
   7.209 +        """Return debug info"""
   7.210 +
   7.211 +    def valid_roles():
   7.212 +        """Return list of valid roles"""
   7.213 +
   7.214 +    def validate_roles(roles):
   7.215 +        """Return true if all given roles are valid"""
   7.216 +
   7.217 +    def userdefined_roles():
   7.218 +        """Return list of user-defined roles"""
   7.219 +
   7.220 +    def manage_defined_roles(submit=None, REQUEST=None):
   7.221 +        """Called by management screen."""
   7.222 +
   7.223 +    def _addRole(role, REQUEST=None):
   7.224 +        """ """
   7.225 +
   7.226 +    def _delRoles(roles, REQUEST=None):
   7.227 +        """ """
   7.228 +
   7.229 +    def _has_user_defined_role(role):
   7.230 +        """ """
   7.231 +
   7.232 +    def manage_editRoles(REQUEST, acl_type='A', acl_roles=[]):
   7.233 +        """ """
   7.234 +
   7.235 +    def _setRoles(acl_type, acl_roles):
   7.236 +        """ """
   7.237 +
   7.238 +    def possible_permissions():
   7.239 +        """ """
     8.1 new file mode 100644
     8.2 --- /dev/null
     8.3 +++ b/bbb/Acquisition_interfaces.py
     8.4 @@ -0,0 +1,60 @@
     8.5 +##############################################################################
     8.6 +#
     8.7 +# Copyright (c) 2004, 2005 Zope Corporation and Contributors.
     8.8 +# All Rights Reserved.
     8.9 +#
    8.10 +# This software is subject to the provisions of the Zope Public License,
    8.11 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
    8.12 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
    8.13 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    8.14 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
    8.15 +# FOR A PARTICULAR PURPOSE.
    8.16 +#
    8.17 +##############################################################################
    8.18 +"""Acquisition z3 interfaces.
    8.19 +
    8.20 +$Id: Acquisition_interfaces.py 12884 2005-05-30 13:10:41Z philikon $
    8.21 +"""
    8.22 +from zope.interface import Attribute
    8.23 +from zope.interface import Interface
    8.24 +
    8.25 +
    8.26 +class IAcquirer(Interface):
    8.27 +
    8.28 +    """Acquire attributes from containers."""
    8.29 +
    8.30 +    def __of__(context):
    8.31 +        """Get the object in a context."""
    8.32 +
    8.33 +
    8.34 +class IAcquisitionWrapper(Interface):
    8.35 +
    8.36 +    """Wrapper object for acquisition."""
    8.37 +
    8.38 +    def aq_acquire(name, filter=None, extra=None, explicit=True, default=0,
    8.39 +                   containment=0):
    8.40 +        """Get an attribute, acquiring it if necessary."""
    8.41 +
    8.42 +    def aq_inContextOf(obj, inner=1):
    8.43 +        """Test whether the object is currently in the context of the argument.
    8.44 +        """
    8.45 +
    8.46 +    aq_base = Attribute(
    8.47 +        """Get the object unwrapped."""
    8.48 +        )
    8.49 +
    8.50 +    aq_parent = Attribute(
    8.51 +        """Get the parent of an object."""
    8.52 +        )
    8.53 +
    8.54 +    aq_self = Attribute(
    8.55 +        """Get the object with the outermost wrapper removed."""
    8.56 +        )
    8.57 +
    8.58 +    aq_inner = Attribute(
    8.59 +        """Get the object with all but the innermost wrapper removed."""
    8.60 +        )
    8.61 +
    8.62 +    aq_chain = Attribute(
    8.63 +        """Get a list of objects in the acquisition environment."""
    8.64 +        )
     9.1 new file mode 100644
     9.2 --- /dev/null
     9.3 +++ b/bbb/App_interfaces.py
     9.4 @@ -0,0 +1,78 @@
     9.5 +##############################################################################
     9.6 +#
     9.7 +# Copyright (c) 2004, 2005 Zope Corporation and Contributors.
     9.8 +# All Rights Reserved.
     9.9 +#
    9.10 +# This software is subject to the provisions of the Zope Public License,
    9.11 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
    9.12 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
    9.13 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    9.14 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
    9.15 +# FOR A PARTICULAR PURPOSE.
    9.16 +#
    9.17 +##############################################################################
    9.18 +"""App z3 interfaces.
    9.19 +
    9.20 +$Id: App_interfaces.py 12884 2005-05-30 13:10:41Z philikon $
    9.21 +"""
    9.22 +from zope.interface import Attribute
    9.23 +from zope.interface import Interface
    9.24 +
    9.25 +
    9.26 +# XXX: might contain non-API methods and outdated comments;
    9.27 +#      not synced with ZopeBook API Reference;
    9.28 +#      based on App.Management.Navigation
    9.29 +class INavigation(Interface):
    9.30 +
    9.31 +    """Basic navigation UI support"""
    9.32 +
    9.33 +    manage = Attribute(""" """)
    9.34 +    manage_menu = Attribute(""" """)
    9.35 +    manage_top_frame = Attribute(""" """)
    9.36 +    manage_page_header = Attribute(""" """)
    9.37 +    manage_page_footer = Attribute(""" """)
    9.38 +    manage_form_title = Attribute("""Add Form""")
    9.39 +    zope_quick_start = Attribute(""" """)
    9.40 +    manage_copyright = Attribute(""" """)
    9.41 +    manage_zmi_prefs = Attribute(""" """)
    9.42 +
    9.43 +    def manage_zmi_logout(REQUEST, RESPONSE):
    9.44 +        """Logout current user"""
    9.45 +
    9.46 +INavigation.setTaggedValue('manage_page_style.css', Attribute(""" """))
    9.47 +
    9.48 +
    9.49 +# XXX: might contain non-API methods and outdated comments;
    9.50 +#      not synced with ZopeBook API Reference;
    9.51 +#      based on App.PersistentExtra.PersistentUtil
    9.52 +class IPersistentExtra(Interface):
    9.53 +
    9.54 +    def bobobase_modification_time():
    9.55 +        """ """
    9.56 +
    9.57 +    def locked_in_version():
    9.58 +        """Was the object modified in any version?
    9.59 +        """
    9.60 +
    9.61 +    def modified_in_version():
    9.62 +        """Was the object modified in this version?
    9.63 +        """
    9.64 +
    9.65 +
    9.66 +# XXX: might contain non-API methods and outdated comments;
    9.67 +#      not synced with ZopeBook API Reference;
    9.68 +#      based on App.Undo.UndoSupport
    9.69 +class IUndoSupport(Interface):
    9.70 +
    9.71 +    manage_UndoForm = Attribute("""Manage Undo form""")
    9.72 +
    9.73 +    def get_request_var_or_attr(name, default):
    9.74 +        """ """
    9.75 +
    9.76 +    def undoable_transactions(first_transaction=None,
    9.77 +                              last_transaction=None,
    9.78 +                              PrincipiaUndoBatchSize=None):
    9.79 +        """ """
    9.80 +
    9.81 +    def manage_undo_transactions(transaction_info=(), REQUEST=None):
    9.82 +        """ """
    10.1 new file mode 100644
    10.2 --- /dev/null
    10.3 +++ b/bbb/OFS_event.py
    10.4 @@ -0,0 +1,61 @@
    10.5 +##############################################################################
    10.6 +#
    10.7 +# Copyright (c) 2005 Zope Corporation and Contributors.
    10.8 +# All Rights Reserved.
    10.9 +#
   10.10 +# This software is subject to the provisions of the Zope Public License,
   10.11 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
   10.12 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
   10.13 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   10.14 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
   10.15 +# FOR A PARTICULAR PURPOSE.
   10.16 +#
   10.17 +##############################################################################
   10.18 +"""
   10.19 +OFS event definitions.
   10.20 +
   10.21 +$Id: OFS_event.py 19416 2005-11-02 15:18:34Z efge $
   10.22 +"""
   10.23 +
   10.24 +from zope.interface import implements
   10.25 +from zope.app.event.objectevent import ObjectEvent
   10.26 +import OFS_interfaces
   10.27 +
   10.28 +class ObjectWillBeMovedEvent(ObjectEvent):
   10.29 +    """An object will be moved."""
   10.30 +    implements(OFS_interfaces.IObjectWillBeMovedEvent)
   10.31 +
   10.32 +    def __init__(self, object, oldParent, oldName, newParent, newName):
   10.33 +        ObjectEvent.__init__(self, object)
   10.34 +        self.oldParent = oldParent
   10.35 +        self.oldName = oldName
   10.36 +        self.newParent = newParent
   10.37 +        self.newName = newName
   10.38 +
   10.39 +class ObjectWillBeAddedEvent(ObjectWillBeMovedEvent):
   10.40 +    """An object will be added to a container."""
   10.41 +    implements(OFS_interfaces.IObjectWillBeAddedEvent)
   10.42 +
   10.43 +    def __init__(self, object, newParent=None, newName=None):
   10.44 +        #if newParent is None:
   10.45 +        #    newParent = object.__parent__
   10.46 +        #if newName is None:
   10.47 +        #    newName = object.__name__
   10.48 +        ObjectWillBeMovedEvent.__init__(self, object, None, None,
   10.49 +                                        newParent, newName)
   10.50 +
   10.51 +class ObjectWillBeRemovedEvent(ObjectWillBeMovedEvent):
   10.52 +    """An object will be removed from a container."""
   10.53 +    implements(OFS_interfaces.IObjectWillBeRemovedEvent)
   10.54 +
   10.55 +    def __init__(self, object, oldParent=None, oldName=None):
   10.56 +        #if oldParent is None:
   10.57 +        #    oldParent = object.__parent__
   10.58 +        #if oldName is None:
   10.59 +        #    oldName = object.__name__
   10.60 +        ObjectWillBeMovedEvent.__init__(self, object, oldParent, oldName,
   10.61 +                                        None, None)
   10.62 +
   10.63 +class ObjectClonedEvent(ObjectEvent):
   10.64 +    """An object has been cloned into a container."""
   10.65 +    implements(OFS_interfaces.IObjectClonedEvent)
    11.1 new file mode 100644
    11.2 --- /dev/null
    11.3 +++ b/bbb/OFS_interfaces.py
    11.4 @@ -0,0 +1,771 @@
    11.5 +##############################################################################
    11.6 +#
    11.7 +# Copyright (c) 2004, 2005 Zope Corporation and Contributors.
    11.8 +# All Rights Reserved.
    11.9 +#
   11.10 +# This software is subject to the provisions of the Zope Public License,
   11.11 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
   11.12 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
   11.13 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   11.14 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
   11.15 +# FOR A PARTICULAR PURPOSE.
   11.16 +#
   11.17 +##############################################################################
   11.18 +"""OFS z3 interfaces.
   11.19 +
   11.20 +$Id: OFS_interfaces.py 19416 2005-11-02 15:18:34Z efge $
   11.21 +"""
   11.22 +from zope.interface import Attribute
   11.23 +from zope.interface import Interface
   11.24 +from zope.schema import Bool, BytesLine, Tuple
   11.25 +from zope.app.traversing.interfaces import IContainmentRoot
   11.26 +
   11.27 +from AccessControl_interfaces import IOwned
   11.28 +from AccessControl_interfaces import IRoleManager
   11.29 +from Acquisition_interfaces import IAcquirer
   11.30 +from App_interfaces import INavigation
   11.31 +from App_interfaces import IUndoSupport
   11.32 +from persistent.interfaces import IPersistent
   11.33 +from webdav_interfaces import IDAVCollection
   11.34 +from webdav_interfaces import IDAVResource
   11.35 +
   11.36 +
   11.37 +# create IOrderedContainer
   11.38 +from Products.Five.fiveconfigure import createZope2Bridge
   11.39 +from OFS.IOrderSupport import IOrderedContainer as z2IOrderedContainer
   11.40 +import OFS_interfaces
   11.41 +
   11.42 +createZope2Bridge(z2IOrderedContainer, OFS_interfaces, 'IOrderedContainer')
   11.43 +
   11.44 +del createZope2Bridge
   11.45 +del z2IOrderedContainer
   11.46 +del OFS_interfaces
   11.47 +
   11.48 +
   11.49 +# XXX: might contain non-API methods and outdated comments;
   11.50 +#      not synced with ZopeBook API Reference;
   11.51 +#      based on OFS.CopySupport.CopySource
   11.52 +class ICopySource(Interface):
   11.53 +
   11.54 +    """Interface for objects which allow themselves to be copied."""
   11.55 +
   11.56 +    def _canCopy(op=0):
   11.57 +        """Called to make sure this object is copyable. The op var
   11.58 +        is 0 for a copy, 1 for a move."""
   11.59 +
   11.60 +    def _notifyOfCopyTo(container, op=0):
   11.61 +        """Overide this to be pickly about where you go! If you dont
   11.62 +        want to go there, raise an exception. The op variable is
   11.63 +        0 for a copy, 1 for a move."""
   11.64 +
   11.65 +    def _getCopy(container):
   11.66 +        """ """
   11.67 +
   11.68 +    def _postCopy(container, op=0):
   11.69 +        """Called after the copy is finished to accomodate special cases.
   11.70 +        The op var is 0 for a copy, 1 for a move."""
   11.71 +
   11.72 +    def _setId(id):
   11.73 +        """Called to set the new id of a copied object."""
   11.74 +
   11.75 +    def cb_isCopyable():
   11.76 +        """Is object copyable? Returns 0 or 1"""
   11.77 +
   11.78 +    def cb_isMoveable():
   11.79 +        """Is object moveable? Returns 0 or 1"""
   11.80 +
   11.81 +    def cb_userHasCopyOrMovePermission():
   11.82 +        """ """
   11.83 +
   11.84 +
   11.85 +# XXX: might contain non-API methods and outdated comments;
   11.86 +#      not synced with ZopeBook API Reference;
   11.87 +#      based on OFS.FTPInterface.FTPInterface
   11.88 +class IFTPAccess(Interface):
   11.89 +
   11.90 +    """Provide support for FTP access"""
   11.91 +
   11.92 +    def manage_FTPstat(REQUEST):
   11.93 +        """Returns a stat-like tuple. (marshalled to a string) Used by
   11.94 +        FTP for directory listings, and MDTM and SIZE"""
   11.95 +
   11.96 +    def manage_FTPlist(REQUEST):
   11.97 +        """Returns a directory listing consisting of a tuple of
   11.98 +        (id,stat) tuples, marshaled to a string. Note, the listing it
   11.99 +        should include '..' if there is a Folder above the current
  11.100 +        one.
  11.101 +
  11.102 +        In the case of non-foldoid objects it should return a single
  11.103 +        tuple (id,stat) representing itself."""
  11.104 +
  11.105 +
  11.106 +# XXX: might contain non-API methods and outdated comments;
  11.107 +#      not synced with ZopeBook API Reference;
  11.108 +#      based on OFS.Traversable.Traversable
  11.109 +class ITraversable(Interface):
  11.110 +
  11.111 +    def absolute_url(relative=0):
  11.112 +        """Return the absolute URL of the object.
  11.113 +
  11.114 +        This a canonical URL based on the object's physical
  11.115 +        containment path.  It is affected by the virtual host
  11.116 +        configuration, if any, and can be used by external
  11.117 +        agents, such as a browser, to address the object.
  11.118 +
  11.119 +        If the relative argument is provided, with a true value, then
  11.120 +        the value of virtual_url_path() is returned.
  11.121 +
  11.122 +        Some Products incorrectly use '/'+absolute_url(1) as an
  11.123 +        absolute-path reference.  This breaks in certain virtual
  11.124 +        hosting situations, and should be changed to use
  11.125 +        absolute_url_path() instead.
  11.126 +        """
  11.127 +
  11.128 +    def absolute_url_path():
  11.129 +        """Return the path portion of the absolute URL of the object.
  11.130 +
  11.131 +        This includes the leading slash, and can be used as an
  11.132 +        'absolute-path reference' as defined in RFC 2396.
  11.133 +        """
  11.134 +
  11.135 +    def virtual_url_path():
  11.136 +        """Return a URL for the object, relative to the site root.
  11.137 +
  11.138 +        If a virtual host is configured, the URL is a path relative to
  11.139 +        the virtual host's root object.  Otherwise, it is the physical
  11.140 +        path.  In either case, the URL does not begin with a slash.
  11.141 +        """
  11.142 +
  11.143 +    def getPhysicalPath():
  11.144 +        '''Returns a path (an immutable sequence of strings)
  11.145 +        that can be used to access this object again
  11.146 +        later, for example in a copy/paste operation.  getPhysicalRoot()
  11.147 +        and getPhysicalPath() are designed to operate together.
  11.148 +        '''
  11.149 +
  11.150 +    def unrestrictedTraverse(path, default=None, restricted=0):
  11.151 +        """Lookup an object by path,
  11.152 +
  11.153 +        path -- The path to the object. May be a sequence of strings or a slash
  11.154 +        separated string. If the path begins with an empty path element
  11.155 +        (i.e., an empty string or a slash) then the lookup is performed
  11.156 +        from the application root. Otherwise, the lookup is relative to
  11.157 +        self. Two dots (..) as a path element indicates an upward traversal
  11.158 +        to the acquisition parent.
  11.159 +
  11.160 +        default -- If provided, this is the value returned if the path cannot
  11.161 +        be traversed for any reason (i.e., no object exists at that path or
  11.162 +        the object is inaccessible).
  11.163 +
  11.164 +        restricted -- If false (default) then no security checking is performed.
  11.165 +        If true, then all of the objects along the path are validated with
  11.166 +        the security machinery. Usually invoked using restrictedTraverse().
  11.167 +        """
  11.168 +
  11.169 +    def restrictedTraverse(path, default=None):
  11.170 +        """Trusted code traversal code, always enforces security"""
  11.171 +
  11.172 +
  11.173 +# XXX: might contain non-API methods and outdated comments;
  11.174 +#      not synced with ZopeBook API Reference;
  11.175 +#      based on many classes
  11.176 +class IZopeObject(Interface):
  11.177 +
  11.178 +    isPrincipiaFolderish = Bool(
  11.179 +        title=u"Is a folderish object",
  11.180 +        description=u"Should be false for simple items",
  11.181 +        )
  11.182 +
  11.183 +    meta_type = BytesLine(
  11.184 +        title=u"Meta type",
  11.185 +        description=u"The object's Zope2 meta type",
  11.186 +        )
  11.187 +
  11.188 +
  11.189 +# XXX: might contain non-API methods and outdated comments;
  11.190 +#      not synced with ZopeBook API Reference;
  11.191 +#      based on OFS.SimpleItem.Item and App.Management.Tabs
  11.192 +class IManageable(Interface):
  11.193 +
  11.194 +    """Something that is manageable in the ZMI"""
  11.195 +
  11.196 +    manage_tabs = Attribute("""Management tabs""")
  11.197 +
  11.198 +    manage_options = Tuple(
  11.199 +        title=u"Manage options",
  11.200 +        )
  11.201 +
  11.202 +    def manage(URL1):
  11.203 +        """Show management screen"""
  11.204 +
  11.205 +    def manage_afterAdd(item, container):
  11.206 +        """Gets called after being added to a container"""
  11.207 +
  11.208 +    def manage_beforeDelete(item, container):
  11.209 +        """Gets called before being deleted"""
  11.210 +
  11.211 +    def manage_afterClone(item):
  11.212 +        """Gets called after being cloned"""
  11.213 +
  11.214 +    def manage_editedDialog(REQUEST, **args):
  11.215 +        """Show an 'edited' dialog"""
  11.216 +
  11.217 +    def filtered_manage_options(REQUEST=None):
  11.218 +        """ """
  11.219 +
  11.220 +    def manage_workspace(REQUEST):
  11.221 +        """Dispatch to first interface in manage_options
  11.222 +        """
  11.223 +
  11.224 +    def tabs_path_default(REQUEST):
  11.225 +        """ """
  11.226 +
  11.227 +    def tabs_path_info(script, path):
  11.228 +        """ """
  11.229 +
  11.230 +    def class_manage_path():
  11.231 +        """ """
  11.232 +
  11.233 +
  11.234 +# XXX: might contain non-API methods and outdated comments;
  11.235 +#      not synced with ZopeBook API Reference;
  11.236 +#      based on OFS.SimpleItem.Item
  11.237 +class IItem(IZopeObject, IManageable, IFTPAccess, IDAVResource,
  11.238 +            ICopySource, ITraversable, IOwned, IUndoSupport):
  11.239 +
  11.240 +    __name__ = BytesLine(
  11.241 +        title=u"Name"
  11.242 +        )
  11.243 +
  11.244 +    title = BytesLine(
  11.245 +        title=u"Title"
  11.246 +        )
  11.247 +
  11.248 +    icon = BytesLine(
  11.249 +        title=u"Icon",
  11.250 +        description=u"Name of icon, relative to SOFTWARE_URL",
  11.251 +        )
  11.252 +
  11.253 +    def getId():
  11.254 +        """Return the id of the object as a string.
  11.255 +
  11.256 +        This method should be used in preference to accessing an id
  11.257 +        attribute of an object directly. The getId method is public.
  11.258 +        """
  11.259 +
  11.260 +    def title_or_id():
  11.261 +        """Returns the title if it is not blank and the id otherwise."""
  11.262 +
  11.263 +    def title_and_id():
  11.264 +        """Returns the title if it is not blank and the id otherwise.  If the
  11.265 +        title is not blank, then the id is included in parens."""
  11.266 +
  11.267 +    def raise_standardErrorMessage(client=None, REQUEST={},
  11.268 +                                   error_type=None, error_value=None, tb=None,
  11.269 +                                   error_tb=None, error_message='',
  11.270 +                                   tagSearch=None, error_log_url=''):
  11.271 +        """Raise standard error message"""
  11.272 +
  11.273 +
  11.274 +# XXX: based on OFS.SimpleItem.Item_w__name__
  11.275 +class IItemWithName(IItem):
  11.276 +
  11.277 +    """Item with name"""
  11.278 +
  11.279 +
  11.280 +# XXX: based on OFS.SimpleItem.SimpleItem
  11.281 +class ISimpleItem(IItem, IPersistent, IAcquirer, IRoleManager):
  11.282 +
  11.283 +    """Not-so-simple item"""
  11.284 +
  11.285 +
  11.286 +# XXX: might contain non-API methods and outdated comments;
  11.287 +#      not synced with ZopeBook API Reference;
  11.288 +#      based on OFS.CopySupport.CopyContainer
  11.289 +class ICopyContainer(Interface):
  11.290 +
  11.291 +    """Interface for containerish objects which allow cut/copy/paste"""
  11.292 +
  11.293 +    # The following three methods should be overridden to store sub-objects
  11.294 +    # as non-attributes.
  11.295 +    def _setOb(id, object):
  11.296 +        """ """
  11.297 +
  11.298 +    def _delOb(id):
  11.299 +        """ """
  11.300 +
  11.301 +    def _getOb(id, default=None):
  11.302 +        """ """
  11.303 +
  11.304 +    def manage_CopyContainerFirstItem(REQUEST):
  11.305 +        """ """
  11.306 +
  11.307 +    def manage_CopyContainerAllItems(REQUEST):
  11.308 +        """ """
  11.309 +
  11.310 +    def manage_cutObjects(ids=None, REQUEST=None):
  11.311 +        """Put a reference to the objects named in ids in the clip board"""
  11.312 +
  11.313 +    def manage_copyObjects(ids=None, REQUEST=None, RESPONSE=None):
  11.314 +        """Put a reference to the objects named in ids in the clip board"""
  11.315 +
  11.316 +    def _get_id(id):
  11.317 +        """Allow containers to override the generation of object copy id by
  11.318 +        attempting to call its _get_id method, if it exists."""
  11.319 +
  11.320 +    def manage_pasteObjects(cb_copy_data=None, REQUEST=None):
  11.321 +        """Paste previously copied objects into the current object.
  11.322 +           If calling manage_pasteObjects from python code, pass
  11.323 +           the result of a previous call to manage_cutObjects or
  11.324 +           manage_copyObjects as the first argument."""
  11.325 +
  11.326 +    manage_renameForm = Attribute("""Rename management view""")
  11.327 +
  11.328 +    def manage_renameObjects(ids=[], new_ids=[], REQUEST=None):
  11.329 +        """Rename several sub-objects"""
  11.330 +
  11.331 +    def manage_renameObject(id, new_id, REQUEST=None):
  11.332 +        """Rename a particular sub-object"""
  11.333 +
  11.334 +    def manage_clone(ob, id, REQUEST=None):
  11.335 +        """Clone an object, creating a new object with the given id."""
  11.336 +
  11.337 +    def cb_dataValid():
  11.338 +        """Return true if clipboard data seems valid."""
  11.339 +
  11.340 +    def cb_dataItems():
  11.341 +        """List of objects in the clip board"""
  11.342 +
  11.343 +    def _verifyObjectPaste(object, validate_src=1):
  11.344 +        """Verify whether the current user is allowed to paste the passed
  11.345 +        object into self. This is determined by checking to see if the
  11.346 +        user could create a new object of the same meta_type of the
  11.347 +        object passed in and checking that the user actually is
  11.348 +        allowed to access the passed in object in its existing
  11.349 +        context.
  11.350 +
  11.351 +        Passing a false value for the validate_src argument will skip
  11.352 +        checking the passed in object in its existing context. This is
  11.353 +        mainly useful for situations where the passed in object has no
  11.354 +        existing context, such as checking an object during an import
  11.355 +        (the object will not yet have been connected to the
  11.356 +        acquisition hierarchy)."""
  11.357 +
  11.358 +
  11.359 +# XXX: might contain non-API methods and outdated comments;
  11.360 +#      not synced with ZopeBook API Reference;
  11.361 +#      based on OFS.ObjectManager.ObjectManager
  11.362 +class IObjectManager(IZopeObject, ICopyContainer, INavigation, IManageable,
  11.363 +                     IAcquirer, IPersistent, IDAVCollection, ITraversable):
  11.364 +
  11.365 +    """Generic object manager
  11.366 +
  11.367 +    This interface provides core behavior for collections of heterogeneous
  11.368 +    objects.
  11.369 +    """
  11.370 +
  11.371 +    meta_types = Tuple(
  11.372 +        title=u"Meta types",
  11.373 +        description=u"Sub-object types that are specific to this object",
  11.374 +        )
  11.375 +
  11.376 +    isAnObjectManager = Bool(
  11.377 +        title=u"Is an object manager",
  11.378 +        )
  11.379 +
  11.380 +    manage_main = Attribute(""" """)
  11.381 +    manage_index_main = Attribute(""" """)
  11.382 +    manage_addProduct = Attribute(""" """)
  11.383 +    manage_importExportForm = Attribute(""" """)
  11.384 +
  11.385 +    def all_meta_types(interfaces=None):
  11.386 +        """ """
  11.387 +
  11.388 +    def _subobject_permissions():
  11.389 +        """ """
  11.390 +
  11.391 +    def filtered_meta_types(user=None):
  11.392 +        """Return a list of the types for which the user has adequate
  11.393 +        permission to add that type of object."""
  11.394 +
  11.395 +    def _setOb(id, object):
  11.396 +        """ """
  11.397 +
  11.398 +    def _delOb(id):
  11.399 +        """ """
  11.400 +
  11.401 +    def _getOb(id, default=None):
  11.402 +        """ """
  11.403 +
  11.404 +    def _setObject(id, object, roles=None, user=None, set_owner=1):
  11.405 +        """ """
  11.406 +
  11.407 +    def _delObject(id, dp=1):
  11.408 +        """ """
  11.409 +
  11.410 +    def objectIds(spec=None):
  11.411 +        """List the IDs of the subobjects of the current object.
  11.412 +
  11.413 +        If 'spec' is specified, returns only objects whose meta_types match
  11.414 +        'spec'.
  11.415 +        """
  11.416 +
  11.417 +    def objectValues(spec=None):
  11.418 +        """List the subobjects of the current object.
  11.419 +
  11.420 +        If 'spec' is specified, returns only objects whose meta_types match
  11.421 +        'spec'.
  11.422 +        """
  11.423 +
  11.424 +    def objectItems(spec=None):
  11.425 +        """List (ID, subobject) tuples for subobjects of the current object.
  11.426 +
  11.427 +        If 'spec' is specified, returns only objects whose meta_types match
  11.428 +        'spec'.
  11.429 +        """
  11.430 +
  11.431 +    def objectMap():
  11.432 +        """Return a tuple of mappings containing subobject meta-data"""
  11.433 +
  11.434 +    def superValues(t):
  11.435 +        """Return all of the objects of a given type located in this object
  11.436 +        and containing objects."""
  11.437 +
  11.438 +    def manage_delObjects(ids=[], REQUEST=None):
  11.439 +        """Delete a subordinate object
  11.440 +
  11.441 +        The objects specified in 'ids' get deleted.
  11.442 +        """
  11.443 +
  11.444 +    def tpValues():
  11.445 +        """Return a list of subobjects, used by tree tag."""
  11.446 +
  11.447 +    def manage_exportObject(id='', download=None, toxml=None,
  11.448 +                            RESPONSE=None,REQUEST=None):
  11.449 +        """Exports an object to a file and returns that file."""
  11.450 +
  11.451 +    def manage_importObject(file, REQUEST=None, set_owner=1):
  11.452 +        """Import an object from a file"""
  11.453 +
  11.454 +    def _importObjectFromFile(filepath, verify=1, set_owner=1):
  11.455 +        """ """
  11.456 +
  11.457 +    def __getitem__(key):
  11.458 +        """ """
  11.459 +
  11.460 +
  11.461 +# XXX: might contain non-API methods and outdated comments;
  11.462 +#      not synced with ZopeBook API Reference;
  11.463 +#      based on OFS.FindSupport.FindSupport
  11.464 +class IFindSupport(Interface):
  11.465 +
  11.466 +    """Find support for Zope Folders"""
  11.467 +
  11.468 +    manage_findFrame = Attribute(""" """)
  11.469 +    manage_findForm = Attribute(""" """)
  11.470 +    manage_findAdv = Attribute(""" """)
  11.471 +    manage_findResult = Attribute(""" """)
  11.472 +
  11.473 +    def ZopeFind(obj, obj_ids=None, obj_metatypes=None,
  11.474 +                 obj_searchterm=None, obj_expr=None,
  11.475 +                 obj_mtime=None, obj_mspec=None,
  11.476 +                 obj_permission=None, obj_roles=None,
  11.477 +                 search_sub=0,
  11.478 +                 REQUEST=None, result=None, pre=''):
  11.479 +        """Zope Find interface"""
  11.480 +
  11.481 +    PrincipiaFind = ZopeFind
  11.482 +
  11.483 +    def ZopeFindAndApply(obj, obj_ids=None, obj_metatypes=None,
  11.484 +                         obj_searchterm=None, obj_expr=None,
  11.485 +                         obj_mtime=None, obj_mspec=None,
  11.486 +                         obj_permission=None, obj_roles=None,
  11.487 +                         search_sub=0,
  11.488 +                         REQUEST=None, result=None, pre='',
  11.489 +                         apply_func=None, apply_path=''):
  11.490 +        """Zope Find interface and apply"""
  11.491 +
  11.492 +
  11.493 +# XXX: might contain non-API methods and outdated comments;
  11.494 +#      not synced with ZopeBook API Reference;
  11.495 +#      based on OFS.PropertyManager.PropertyManager
  11.496 +class IPropertyManager(Interface):
  11.497 +
  11.498 +    """
  11.499 +    The PropertyManager mixin class provides an object with
  11.500 +    transparent property management. An object which wants to
  11.501 +    have properties should inherit from PropertyManager.
  11.502 +
  11.503 +    An object may specify that it has one or more predefined
  11.504 +    properties, by specifying an _properties structure in its
  11.505 +    class::
  11.506 +
  11.507 +      _properties=({'id':'title', 'type': 'string', 'mode': 'w'},
  11.508 +                   {'id':'color', 'type': 'string', 'mode': 'w'},
  11.509 +                   )
  11.510 +
  11.511 +    The _properties structure is a sequence of dictionaries, where
  11.512 +    each dictionary represents a predefined property. Note that if a
  11.513 +    predefined property is defined in the _properties structure, you
  11.514 +    must provide an attribute with that name in your class or instance
  11.515 +    that contains the default value of the predefined property.
  11.516 +
  11.517 +    Each entry in the _properties structure must have at least an 'id'
  11.518 +    and a 'type' key. The 'id' key contains the name of the property,
  11.519 +    and the 'type' key contains a string representing the object's type.
  11.520 +    The 'type' string must be one of the values: 'float', 'int', 'long',
  11.521 +    'string', 'lines', 'text', 'date', 'tokens', 'selection', or
  11.522 +    'multiple section'.
  11.523 +
  11.524 +    For 'selection' and 'multiple selection' properties, there is an
  11.525 +    addition item in the property dictionay, 'select_variable' which
  11.526 +    provides the name of a property or method which returns a list of
  11.527 +    strings from which the selection(s) can be chosen.
  11.528 +
  11.529 +    Each entry in the _properties structure may *optionally* provide a
  11.530 +    'mode' key, which specifies the mutability of the property. The 'mode'
  11.531 +    string, if present, must contain 0 or more characters from the set
  11.532 +    'w','d'.
  11.533 +
  11.534 +    A 'w' present in the mode string indicates that the value of the
  11.535 +    property may be changed by the user. A 'd' indicates that the user
  11.536 +    can delete the property. An empty mode string indicates that the
  11.537 +    property and its value may be shown in property listings, but that
  11.538 +    it is read-only and may not be deleted.
  11.539 +
  11.540 +    Entries in the _properties structure which do not have a 'mode' key
  11.541 +    are assumed to have the mode 'wd' (writeable and deleteable).
  11.542 +
  11.543 +    To fully support property management, including the system-provided
  11.544 +    tabs and user interfaces for working with properties, an object which
  11.545 +    inherits from PropertyManager should include the following entry in
  11.546 +    its manage_options structure::
  11.547 +
  11.548 +      {'label':'Properties', 'action':'manage_propertiesForm',}
  11.549 +
  11.550 +    to ensure that a 'Properties' tab is displayed in its management
  11.551 +    interface. Objects that inherit from PropertyManager should also
  11.552 +    include the following entry in its __ac_permissions__ structure::
  11.553 +
  11.554 +      ('Manage properties', ('manage_addProperty',
  11.555 +                             'manage_editProperties',
  11.556 +                             'manage_delProperties',
  11.557 +                             'manage_changeProperties',)),
  11.558 +    """
  11.559 +    manage_propertiesForm = Attribute(""" """)
  11.560 +    manage_propertyTypeForm = Attribute(""" """)
  11.561 +
  11.562 +    title = BytesLine(
  11.563 +        title=u"Title"
  11.564 +        )
  11.565 +
  11.566 +    _properties = Tuple(
  11.567 +        title=u"Properties",
  11.568 +        )
  11.569 +
  11.570 +    propertysheets = Attribute(""" """)
  11.571 +
  11.572 +    def valid_property_id(id):
  11.573 +        """ """
  11.574 +
  11.575 +    def hasProperty(id):
  11.576 +        """Return true if object has a property 'id'"""
  11.577 +
  11.578 +    def getProperty(id, d=None):
  11.579 +        """Get the property 'id', returning the optional second
  11.580 +           argument or None if no such property is found."""
  11.581 +
  11.582 +    def getPropertyType(id):
  11.583 +        """Get the type of property 'id', returning None if no
  11.584 +           such property exists"""
  11.585 +
  11.586 +    def _wrapperCheck(object):
  11.587 +        """Raise an error if an object is wrapped."""
  11.588 +
  11.589 +    def _setPropValue(id, value):
  11.590 +        """ """
  11.591 +
  11.592 +    def _delPropValue(id):
  11.593 +        """ """
  11.594 +
  11.595 +    def _setProperty(id, value, type='string'):
  11.596 +        """Set property.
  11.597 +
  11.598 +        For selection and multiple selection properties the value argument
  11.599 +        indicates the select variable of the property.
  11.600 +        """
  11.601 +
  11.602 +    def _updateProperty(id, value):
  11.603 +        """Update the value of an existing property.
  11.604 +
  11.605 +        If value is a string, an attempt will be made to convert the value to
  11.606 +        the type of the existing property.
  11.607 +        """
  11.608 +
  11.609 +    def _delProperty(id):
  11.610 +        """ """
  11.611 +
  11.612 +    def propertyIds():
  11.613 +        """Return a list of property ids """
  11.614 +
  11.615 +    def propertyValues():
  11.616 +        """Return a list of actual property objects """
  11.617 +
  11.618 +    def propertyItems():
  11.619 +        """Return a list of (id,property) tuples """
  11.620 +
  11.621 +    def _propertyMap():
  11.622 +        """Return a tuple of mappings, giving meta-data for properties """
  11.623 +
  11.624 +    def propertyMap():
  11.625 +        """
  11.626 +        Return a tuple of mappings, giving meta-data for properties.
  11.627 +        Return copies of the real definitions for security.
  11.628 +        """
  11.629 +
  11.630 +    def propertyLabel(id):
  11.631 +        """Return a label for the given property id
  11.632 +        """
  11.633 +
  11.634 +    def propdict():
  11.635 +        """ """
  11.636 +
  11.637 +    # Web interface
  11.638 +
  11.639 +    def manage_addProperty(id, value, type, REQUEST=None):
  11.640 +        """Add a new property via the web. Sets a new property with
  11.641 +        the given id, type, and value."""
  11.642 +
  11.643 +    def manage_editProperties(REQUEST):
  11.644 +        """Edit object properties via the web.
  11.645 +        The purpose of this method is to change all property values,
  11.646 +        even those not listed in REQUEST; otherwise checkboxes that
  11.647 +        get turned off will be ignored.  Use manage_changeProperties()
  11.648 +        instead for most situations.
  11.649 +        """
  11.650 +
  11.651 +    def manage_changeProperties(REQUEST=None, **kw):
  11.652 +        """Change existing object properties.
  11.653 +
  11.654 +        Change object properties by passing either a mapping object
  11.655 +        of name:value pairs {'foo':6} or passing name=value parameters
  11.656 +        """
  11.657 +
  11.658 +    def manage_changePropertyTypes(old_ids, props, REQUEST=None):
  11.659 +        """Replace one set of properties with another
  11.660 +
  11.661 +        Delete all properties that have ids in old_ids, then add a
  11.662 +        property for each item in props.  Each item has a new_id,
  11.663 +        new_value, and new_type.  The type of new_value should match
  11.664 +        new_type.
  11.665 +        """
  11.666 +
  11.667 +    def manage_delProperties(ids=None, REQUEST=None):
  11.668 +        """Delete one or more properties specified by 'ids'."""
  11.669 +
  11.670 +
  11.671 +# XXX: based on OFS.Folder.Folder
  11.672 +class IFolder(IObjectManager, IPropertyManager, IRoleManager,
  11.673 +              IDAVCollection, IItem, IFindSupport):
  11.674 +
  11.675 +    """Folders are basic container objects that provide a standard
  11.676 +    interface for object management. Folder objects also implement a
  11.677 +    management interface and can have arbitrary properties."""
  11.678 +
  11.679 +
  11.680 +# XXX: based on OFS.OrderedFolder.OrderedFolder
  11.681 +class IOrderedFolder(IOrderedContainer, IFolder):
  11.682 +
  11.683 +    """Ordered folder"""
  11.684 +
  11.685 +
  11.686 +# XXX: might contain non-API methods and outdated comments;
  11.687 +#      not synced with ZopeBook API Reference;
  11.688 +#      based on OFS.Application.Application
  11.689 +class IApplication(IFolder, IContainmentRoot):
  11.690 +
  11.691 +    """Top-level system object"""
  11.692 +
  11.693 +    isTopLevelPrincipiaApplicationObject = Bool(
  11.694 +        title=u"Is top level Principa application object",
  11.695 +        )
  11.696 +
  11.697 +    HelpSys = Attribute("Help system")
  11.698 +
  11.699 +    p_ = Attribute(""" """)
  11.700 +    misc_ = Attribute("Misc.")
  11.701 +
  11.702 +    def PrincipiaRedirect(destination, URL1):
  11.703 +        """Utility function to allow user-controlled redirects"""
  11.704 +
  11.705 +    Redirect = ZopeRedirect = PrincipiaRedirect
  11.706 +
  11.707 +    def __bobo_traverse__(REQUEST, name=None):
  11.708 +        """Bobo traverse"""
  11.709 +
  11.710 +    def PrincipiaTime(*args):
  11.711 +        """Utility function to return current date/time"""
  11.712 +
  11.713 +    ZopeTime = PrincipiaTime
  11.714 +
  11.715 +    def ZopeAttributionButton():
  11.716 +        """Returns an HTML fragment that displays the 'powered by zope'
  11.717 +        button along with a link to the Zope site."""
  11.718 +
  11.719 +    test_url = ZopeAttributionButton
  11.720 +
  11.721 +    def absolute_url(relative=0):
  11.722 +        '''The absolute URL of the root object is BASE1 or "/".'''
  11.723 +
  11.724 +    def absolute_url_path():
  11.725 +        '''The absolute URL path of the root object is BASEPATH1 or "/".'''
  11.726 +
  11.727 +    def virtual_url_path():
  11.728 +        '''The virtual URL path of the root object is empty.'''
  11.729 +
  11.730 +    def getPhysicalPath():
  11.731 +        '''Returns a path that can be used to access this object again
  11.732 +        later, for example in a copy/paste operation.  Designed to
  11.733 +        be used with getPhysicalRoot().
  11.734 +        '''
  11.735 +
  11.736 +    def getPhysicalRoot():
  11.737 +        """Returns self"""
  11.738 +
  11.739 +    def fixupZClassDependencies(rebuild=0):
  11.740 +        """ """
  11.741 +
  11.742 +    def checkGlobalRegistry():
  11.743 +        """Check the global (zclass) registry for problems, which can
  11.744 +        be caused by things like disk-based products being deleted.
  11.745 +        Return true if a problem is found"""
  11.746 +
  11.747 +
  11.748 +##################################################
  11.749 +# Event interfaces
  11.750 +
  11.751 +from zope.app.event.interfaces import IObjectEvent
  11.752 +
  11.753 +class IObjectWillBeMovedEvent(IObjectEvent):
  11.754 +    """An object will be moved."""
  11.755 +    oldParent = Attribute("The old location parent for the object.")
  11.756 +    oldName = Attribute("The old location name for the object.")
  11.757 +    newParent = Attribute("The new location parent for the object.")
  11.758 +    newName = Attribute("The new location name for the object.")
  11.759 +
  11.760 +class IObjectWillBeAddedEvent(IObjectWillBeMovedEvent):
  11.761 +    """An object will be added to a container."""
  11.762 +
  11.763 +class IObjectWillBeRemovedEvent(IObjectWillBeMovedEvent):
  11.764 +    """An object will be removed from a container"""
  11.765 +
  11.766 +class IObjectClonedEvent(IObjectEvent):
  11.767 +    """An object has been cloned (a la Zope 2).
  11.768 +
  11.769 +    This is for Zope 2 compatibility, subscribers should really use
  11.770 +    IObjectCopiedEvent or IObjectAddedEvent, depending on their use
  11.771 +    cases.
  11.772 +
  11.773 +    event.object is the copied object, already added to its container.
  11.774 +    Note that this event is dispatched to all sublocations.
  11.775 +    """
    12.1 new file mode 100644
    12.2 --- /dev/null
    12.3 +++ b/bbb/OFS_subscribers.py
    12.4 @@ -0,0 +1,162 @@
    12.5 +##############################################################################
    12.6 +#
    12.7 +# Copyright (c) 2005 Zope Corporation and Contributors.
    12.8 +# All Rights Reserved.
    12.9 +#
   12.10 +# This software is subject to the provisions of the Zope Public License,
   12.11 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
   12.12 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
   12.13 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   12.14 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
   12.15 +# FOR A PARTICULAR PURPOSE.
   12.16 +#
   12.17 +##############################################################################
   12.18 +"""
   12.19 +Five subscriber definitions.
   12.20 +
   12.21 +$Id: OFS_subscribers.py 19705 2005-11-10 14:00:27Z efge $
   12.22 +"""
   12.23 +
   12.24 +import warnings
   12.25 +import sys
   12.26 +
   12.27 +from zLOG import LOG, ERROR
   12.28 +from Acquisition import aq_base
   12.29 +from App.config import getConfiguration
   12.30 +from AccessControl import getSecurityManager
   12.31 +from ZODB.POSException import ConflictError
   12.32 +from OFS.ObjectManager import BeforeDeleteException
   12.33 +from zope.app.container.contained import dispatchToSublocations
   12.34 +from OFS_interfaces import IObjectManager
   12.35 +
   12.36 +
   12.37 +deprecatedManageAddDeleteClasses = []
   12.38 +
   12.39 +
   12.40 +def compatibilityCall(method_name, *args):
   12.41 +    """Call a method if events have not been setup yet.
   12.42 +
   12.43 +    This is the case for some unit tests that have not been converted to
   12.44 +    use the component architecture.
   12.45 +    """
   12.46 +    if deprecatedManageAddDeleteClasses:
   12.47 +        # Events initialized, don't do compatibility call
   12.48 +        return
   12.49 +    if method_name == 'manage_afterAdd':
   12.50 +        callManageAfterAdd(*args)
   12.51 +    elif method_name == 'manage_beforeDelete':
   12.52 +        callManageBeforeDelete(*args)
   12.53 +    else:
   12.54 +        callManageAfterClone(*args)
   12.55 +
   12.56 +def maybeWarnDeprecated(ob, method_name):
   12.57 +    """Send a warning if a method is deprecated.
   12.58 +    """
   12.59 +    if not deprecatedManageAddDeleteClasses:
   12.60 +        # Directives not fully loaded
   12.61 +        return
   12.62 +    for cls in deprecatedManageAddDeleteClasses:
   12.63 +        if isinstance(ob, cls):
   12.64 +            # Already deprecated through zcml
   12.65 +            return
   12.66 +    if getattr(getattr(ob, method_name), '__five_method__', False):
   12.67 +        # Method knows it's deprecated
   12.68 +        return
   12.69 +    class_ = ob.__class__
   12.70 +    warnings.warn(
   12.71 +        "%s.%s.%s is deprecated and will be removed in Zope 2.11, "
   12.72 +        "you should use event subscribers instead, and meanwhile "
   12.73 +        "mark the class with <five:deprecatedManageAddDelete/>"
   12.74 +        % (class_.__module__, class_.__name__, method_name),
   12.75 +        DeprecationWarning)
   12.76 +
   12.77 +
   12.78 +##################################################
   12.79 +# Adapters and subscribers
   12.80 +
   12.81 +class ObjectManagerSublocations(object):
   12.82 +    """Get the sublocations for an ObjectManager.
   12.83 +    """
   12.84 +    def __init__(self, container):
   12.85 +        self.container = container
   12.86 +
   12.87 +    def sublocations(self):
   12.88 +        for ob in self.container.objectValues():
   12.89 +            yield ob
   12.90 +
   12.91 +# The following subscribers should really be defined in ZCML
   12.92 +# but we don't have enough control over subscriber ordering for
   12.93 +# that to work exactly right.
   12.94 +# (Sometimes IItem comes before IObjectManager, sometimes after,
   12.95 +# depending on some of Zope's classes.)
   12.96 +# This code can be simplified when Zope is completely rid of
   12.97 +# manage_afterAdd & co, then IItem wouldn't be relevant anymore and we
   12.98 +# could have a simple subscriber for IObjectManager that directly calls
   12.99 +# dispatchToSublocations.
  12.100 +
  12.101 +def dispatchObjectWillBeMovedEvent(ob, event):
  12.102 +    """Multi-subscriber for IItem + IObjectWillBeMovedEvent.
  12.103 +    """
  12.104 +    # First, dispatch to sublocations
  12.105 +    if IObjectManager.providedBy(ob):
  12.106 +        dispatchToSublocations(ob, event)
  12.107 +    # Next, do the manage_beforeDelete dance
  12.108 +    callManageBeforeDelete(ob, event.object, event.oldParent)
  12.109 +
  12.110 +def dispatchObjectMovedEvent(ob, event):
  12.111 +    """Multi-subscriber for IItem + IObjectMovedEvent.
  12.112 +    """
  12.113 +    # First, do the manage_afterAdd dance
  12.114 +    callManageAfterAdd(ob, event.object, event.newParent)
  12.115 +    # Next, dispatch to sublocations
  12.116 +    if IObjectManager.providedBy(ob):
  12.117 +        dispatchToSublocations(ob, event)
  12.118 +
  12.119 +def dispatchObjectClonedEvent(ob, event):
  12.120 +    """Multi-subscriber for IItem + IObjectClonedEvent.
  12.121 +    """
  12.122 +    # First, do the manage_afterClone dance
  12.123 +    callManageAfterClone(ob, event.object)
  12.124 +    # Next, dispatch to sublocations
  12.125 +    if IObjectManager.providedBy(ob):
  12.126 +        dispatchToSublocations(ob, event)
  12.127 +
  12.128 +
  12.129 +def callManageAfterAdd(ob, item, container):
  12.130 +    """Compatibility subscriber for manage_afterAdd.
  12.131 +    """
  12.132 +    if container is None:
  12.133 +        return
  12.134 +    if getattr(aq_base(ob), 'manage_afterAdd', None) is None:
  12.135 +        return
  12.136 +    maybeWarnDeprecated(ob, 'manage_afterAdd')
  12.137 +    ob.manage_afterAdd(item, container)
  12.138 +
  12.139 +def callManageBeforeDelete(ob, item, container):
  12.140 +    """Compatibility subscriber for manage_beforeDelete.
  12.141 +    """
  12.142 +    if container is None:
  12.143 +        return
  12.144 +    if getattr(aq_base(ob), 'manage_beforeDelete', None) is None:
  12.145 +        return
  12.146 +    maybeWarnDeprecated(ob, 'manage_beforeDelete')
  12.147 +    try:
  12.148 +        ob.manage_beforeDelete(item, container)
  12.149 +    except BeforeDeleteException:
  12.150 +        raise
  12.151 +    except ConflictError:
  12.152 +        raise
  12.153 +    except:
  12.154 +        LOG('Zope', ERROR, '_delObject() threw', error=sys.exc_info())
  12.155 +        # In debug mode when non-Manager, let exceptions propagate.
  12.156 +        if getConfiguration().debug_mode:
  12.157 +            if not getSecurityManager().getUser().has_role('Manager'):
  12.158 +                raise
  12.159 +
  12.160 +def callManageAfterClone(ob, item):
  12.161 +    """Compatibility subscriber for manage_afterClone.
  12.162 +    """
  12.163 +    if getattr(aq_base(ob), 'manage_afterClone', None) is None:
  12.164 +        return
  12.165 +    maybeWarnDeprecated(ob, 'manage_afterClone')
  12.166 +    ob.manage_afterClone(item)
    13.1 new file mode 100644
    13.2 --- /dev/null
    13.3 +++ b/bbb/__init__.py
    13.4 @@ -0,0 +1,1 @@
    13.5 +# make this directory a package
    14.1 new file mode 100644
    14.2 --- /dev/null
    14.3 +++ b/bbb/webdav_interfaces.py
    14.4 @@ -0,0 +1,153 @@
    14.5 +##############################################################################
    14.6 +#
    14.7 +# Copyright (c) 2004, 2005 Zope Corporation and Contributors.
    14.8 +# All Rights Reserved.
    14.9 +#
   14.10 +# This software is subject to the provisions of the Zope Public License,
   14.11 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
   14.12 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
   14.13 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   14.14 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
   14.15 +# FOR A PARTICULAR PURPOSE.
   14.16 +#
   14.17 +##############################################################################
   14.18 +"""webdav z3 interfaces.
   14.19 +
   14.20 +$Id: webdav_interfaces.py 12884 2005-05-30 13:10:41Z philikon $
   14.21 +"""
   14.22 +from zope.interface import Interface
   14.23 +from zope.schema import Bool, Tuple
   14.24 +
   14.25 +
   14.26 +# create IWriteLock
   14.27 +from Products.Five.fiveconfigure import createZope2Bridge
   14.28 +from webdav.WriteLockInterface import WriteLockInterface
   14.29 +import webdav_interfaces
   14.30 +
   14.31 +createZope2Bridge(WriteLockInterface, webdav_interfaces, 'IWriteLock')
   14.32 +
   14.33 +del createZope2Bridge
   14.34 +del WriteLockInterface
   14.35 +del webdav_interfaces
   14.36 +
   14.37 +
   14.38 +# XXX: might contain non-API methods and outdated comments;
   14.39 +#      not synced with ZopeBook API Reference;
   14.40 +#      based on webdav.Resource.Resource
   14.41 +class IDAVResource(IWriteLock):
   14.42 +
   14.43 +    """Provide basic WebDAV support for non-collection objects."""
   14.44 +
   14.45 +    __dav_resource__ = Bool(
   14.46 +        title=u"Is DAV resource"
   14.47 +        )
   14.48 +
   14.49 +    __http_methods__ = Tuple(
   14.50 +        title=u"HTTP methods",
   14.51 +        description=u"Sequence of valid HTTP methods"
   14.52 +        )
   14.53 +
   14.54 +    def dav__init(request, response):
   14.55 +        """Init expected HTTP 1.1 / WebDAV headers which are not
   14.56 +        currently set by the base response object automagically.
   14.57 +
   14.58 +        Also, we sniff for a ZServer response object, because we don't
   14.59 +        want to write duplicate headers (since ZS writes Date
   14.60 +        and Connection itself).
   14.61 +        """
   14.62 +
   14.63 +    def dav__validate(object, methodname, REQUEST):
   14.64 +        """ """
   14.65 +
   14.66 +    def dav__simpleifhandler(request, response, method='PUT',
   14.67 +                             col=0, url=None, refresh=0):
   14.68 +        """ """
   14.69 +
   14.70 +    def HEAD(REQUEST, RESPONSE):
   14.71 +        """Retrieve resource information without a response body."""
   14.72 +
   14.73 +    def PUT(REQUEST, RESPONSE):
   14.74 +        """Replace the GET response entity of an existing resource.
   14.75 +        Because this is often object-dependent, objects which handle
   14.76 +        PUT should override the default PUT implementation with an
   14.77 +        object-specific implementation. By default, PUT requests
   14.78 +        fail with a 405 (Method Not Allowed)."""
   14.79 +
   14.80 +    def OPTIONS(REQUEST, RESPONSE):
   14.81 +        """Retrieve communication options."""
   14.82 +
   14.83 +    def TRACE(REQUEST, RESPONSE):
   14.84 +        """Return the HTTP message received back to the client as the
   14.85 +        entity-body of a 200 (OK) response. This will often usually
   14.86 +        be intercepted by the web server in use. If not, the TRACE
   14.87 +        request will fail with a 405 (Method Not Allowed), since it
   14.88 +        is not often possible to reproduce the HTTP request verbatim
   14.89 +        from within the Zope environment."""
   14.90 +
   14.91 +    def DELETE(REQUEST, RESPONSE):
   14.92 +        """Delete a resource. For non-collection resources, DELETE may
   14.93 +        return either 200 or 204 (No Content) to indicate success."""
   14.94 +
   14.95 +    def PROPFIND(REQUEST, RESPONSE):
   14.96 +        """Retrieve properties defined on the resource."""
   14.97 +
   14.98 +    def PROPPATCH(REQUEST, RESPONSE):
   14.99 +        """Set and/or remove properties defined on the resource."""
  14.100 +
  14.101 +    def MKCOL(REQUEST, RESPONSE):
  14.102 +        """Create a new collection resource. If called on an existing
  14.103 +        resource, MKCOL must fail with 405 (Method Not Allowed)."""
  14.104 +
  14.105 +    def COPY(REQUEST, RESPONSE):
  14.106 +        """Create a duplicate of the source resource whose state
  14.107 +        and behavior match that of the source resource as closely
  14.108 +        as possible. Though we may later try to make a copy appear
  14.109 +        seamless across namespaces (e.g. from Zope to Apache), COPY
  14.110 +        is currently only supported within the Zope namespace."""
  14.111 +
  14.112 +    def MOVE(REQUEST, RESPONSE):
  14.113 +        """Move a resource to a new location. Though we may later try to
  14.114 +        make a move appear seamless across namespaces (e.g. from Zope
  14.115 +        to Apache), MOVE is currently only supported within the Zope
  14.116 +        namespace."""
  14.117 +
  14.118 +    def LOCK(REQUEST, RESPONSE):
  14.119 +        """Lock a resource"""
  14.120 +
  14.121 +    def UNLOCK(REQUEST, RESPONSE):
  14.122 +        """Remove an existing lock on a resource."""
  14.123 +
  14.124 +    def manage_DAVget():
  14.125 +        """Gets the document source"""
  14.126 +
  14.127 +    def listDAVObjects():
  14.128 +        """ """
  14.129 +
  14.130 +
  14.131 +# XXX: might contain non-API methods and outdated comments;
  14.132 +#      not synced with ZopeBook API Reference;
  14.133 +#      based on webdav.Collection.Collection
  14.134 +class IDAVCollection(IDAVResource):
  14.135 +
  14.136 +    """The Collection class provides basic WebDAV support for
  14.137 +    collection objects. It provides default implementations
  14.138 +    for all supported WebDAV HTTP methods. The behaviors of some
  14.139 +    WebDAV HTTP methods for collections are slightly different
  14.140 +    than those for non-collection resources."""
  14.141 +
  14.142 +    __dav_collection__ = Bool(
  14.143 +        title=u"Is a DAV collection",
  14.144 +        description=u"Should be true",
  14.145 +        )
  14.146 +
  14.147 +    def PUT(REQUEST, RESPONSE):
  14.148 +        """The PUT method has no inherent meaning for collection
  14.149 +        resources, though collections are not specifically forbidden
  14.150 +        to handle PUT requests. The default response to a PUT request
  14.151 +        for collections is 405 (Method Not Allowed)."""
  14.152 +
  14.153 +    def DELETE(REQUEST, RESPONSE):
  14.154 +        """Delete a collection resource. For collection resources, DELETE
  14.155 +        may return either 200 (OK) or 204 (No Content) to indicate total
  14.156 +        success, or may return 207 (Multistatus) to indicate partial
  14.157 +        success. Note that in Zope a DELETE currently never returns 207."""
    15.1 new file mode 100644
    15.2 --- /dev/null
    15.3 +++ b/bridge.py
    15.4 @@ -0,0 +1,89 @@
    15.5 +##############################################################################
    15.6 +#
    15.7 +# Copyright (c) 2004, 2005 Zope Corporation and Contributors.
    15.8 +# All Rights Reserved.
    15.9 +#
   15.10 +# This software is subject to the provisions of the Zope Public License,
   15.11 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
   15.12 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
   15.13 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   15.14 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
   15.15 +# FOR A PARTICULAR PURPOSE.
   15.16 +#
   15.17 +##############################################################################
   15.18 +""" Z2 -> Z3 bridge utilities.
   15.19 +
   15.20 +$Id: bridge.py 14504 2005-07-11 15:49:59Z yuppie $
   15.21 +"""
   15.22 +from Interface._InterfaceClass import Interface as Z2_InterfaceClass
   15.23 +from Interface import Interface as Z2_Interface
   15.24 +from Interface import Attribute as Z2_Attribute
   15.25 +from Interface.Method import Method as Z2_Method
   15.26 +
   15.27 +from zope.interface.interface import InterfaceClass as Z3_InterfaceClass
   15.28 +from zope.interface.interface import Interface as Z3_Interface
   15.29 +from zope.interface.interface import Attribute as Z3_Attribute
   15.30 +from zope.interface.interface import Method as Z3_Method
   15.31 +
   15.32 +_bridges = {Z2_Interface: Z3_Interface}
   15.33 +
   15.34 +def fromZ2Interface(z2i):
   15.35 +    """ Return a Zope 3 interface corresponding to 'z2i'.
   15.36 +
   15.37 +    o 'z2i' must be a Zope 2 interface.
   15.38 +    """
   15.39 +    if not isinstance(z2i, Z2_InterfaceClass):
   15.40 +        raise ValueError, 'Not a Zope 2 interface!'
   15.41 +
   15.42 +    if z2i in _bridges:
   15.43 +        return _bridges[z2i]
   15.44 +
   15.45 +    name = z2i.getName()
   15.46 +    bases = [ fromZ2Interface(x) for x in z2i.getBases() ]
   15.47 +    attrs = {}
   15.48 +
   15.49 +    for k, v in z2i.namesAndDescriptions():
   15.50 +        if isinstance(v, Z2_Method):
   15.51 +            v = fromZ2Method(v)
   15.52 +
   15.53 +        elif isinstance(v, Z2_Attribute):
   15.54 +            v = fromZ2Attribute(v)
   15.55 +
   15.56 +        attrs[k] = v
   15.57 +
   15.58 +    # XXX: Note that we pass the original interface's __module__;
   15.59 +    #      we may live to regret that.
   15.60 +    z3i = Z3_InterfaceClass(name=name,
   15.61 +                            bases=tuple(bases),
   15.62 +                            attrs=attrs,
   15.63 +                            __doc__=z2i.getDoc(),
   15.64 +                            __module__=z2i.__module__)
   15.65 +    _bridges[z2i] = z3i
   15.66 +    return z3i
   15.67 +
   15.68 +def fromZ2Attribute(z2a):
   15.69 +    """ Return a Zope 3 interface attribute corresponding to 'z2a'.
   15.70 +
   15.71 +    o 'z2a' must be a Zope 2 interface attribute.
   15.72 +    """
   15.73 +    if not isinstance(z2a, Z2_Attribute):
   15.74 +        raise ValueError, 'Not a Zope 2 interface attribute!'
   15.75 +
   15.76 +    return Z3_Attribute(z2a.getName(), z2a.getDoc())
   15.77 +
   15.78 +def fromZ2Method(z2m):
   15.79 +    """ Return a Zope 3 interface method corresponding to 'z2m'.
   15.80 +
   15.81 +    o 'z2m' must be a Zope 2 interface method.
   15.82 +    """
   15.83 +    if not isinstance(z2m, Z2_Method):
   15.84 +        raise ValueError, 'Not a Zope 2 interface method!'
   15.85 +
   15.86 +    z3m = Z3_Method(z2m.getName(), z2m.getDoc())
   15.87 +    sig = z2m.getSignatureInfo()
   15.88 +    z3m.positional = sig['positional']
   15.89 +    z3m.required = sig['required']
   15.90 +    z3m.optional = sig['optional']
   15.91 +    z3m.varargs = sig['varargs']
   15.92 +    z3m.kwargs = sig['kwargs']
   15.93 +    return z3m
    16.1 new file mode 100644
    16.2 --- /dev/null
    16.3 +++ b/browser/ReuseUtils.py
    16.4 @@ -0,0 +1,32 @@
    16.5 +##############################################################################
    16.6 +#
    16.7 +# Copyright (c) 2004, 2005 Zope Corporation and Contributors.
    16.8 +# All Rights Reserved.
    16.9 +#
   16.10 +# This software is subject to the provisions of the Zope Public License,
   16.11 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
   16.12 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
   16.13 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   16.14 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
   16.15 +# FOR A PARTICULAR PURPOSE.
   16.16 +#
   16.17 +##############################################################################
   16.18 +"""Utils to be reused
   16.19 +
   16.20 +$Id: ReuseUtils.py 12907 2005-05-31 06:26:15Z philikon $
   16.21 +"""
   16.22 +from new import function
   16.23 +
   16.24 +def rebindFunction(f,rebindDir=None,**rebinds):
   16.25 +  '''return *f* with some globals rebound.'''
   16.26 +  d= {}
   16.27 +  if rebindDir : d.update(rebindDir)
   16.28 +  if rebinds: d.update(rebinds)
   16.29 +  if not d: return f
   16.30 +  f= getattr(f,'im_func',f)
   16.31 +  fd= f.func_globals.copy()
   16.32 +  fd.update(d)
   16.33 +  nf= function(f.func_code,fd,f.func_name,f.func_defaults or ())
   16.34 +  nf.__doc__= f.__doc__
   16.35 +  if f.__dict__ is not None: nf.__dict__= f.__dict__.copy()
   16.36 +  return nf
    17.1 new file mode 100644
    17.2 --- /dev/null
    17.3 +++ b/browser/TrustedExpression.py
    17.4 @@ -0,0 +1,108 @@
    17.5 +##############################################################################
    17.6 +#
    17.7 +# Copyright (c) 2004, 2005 Zope Corporation and Contributors.
    17.8 +# All Rights Reserved.
    17.9 +#
   17.10 +# This software is subject to the provisions of the Zope Public License,
   17.11 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
   17.12 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
   17.13 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   17.14 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
   17.15 +# FOR A PARTICULAR PURPOSE.
   17.16 +#
   17.17 +##############################################################################
   17.18 +"""Trusted expression
   17.19 +
   17.20 +$Id: TrustedExpression.py 18543 2005-10-14 13:37:26Z regebro $
   17.21 +"""
   17.22 +from sys import modules
   17.23 +
   17.24 +from Products.PageTemplates.PythonExpr import PythonExpr
   17.25 +
   17.26 +from Products.PageTemplates.Expressions import \
   17.27 +     SubPathExpr, PathExpr, \
   17.28 +     StringExpr, \
   17.29 +     getEngine, installHandlers,\
   17.30 +     SecureModuleImporter
   17.31 +
   17.32 +from ReuseUtils import rebindFunction
   17.33 +
   17.34 +ModuleImporter = SecureModuleImporter
   17.35 +
   17.36 +def trustedTraverse(ob, path, ignored,):
   17.37 +  if not path: return self
   17.38 +
   17.39 +  get = getattr
   17.40 +  has = hasattr
   17.41 +  N = None
   17.42 +  M = rebindFunction # artifical marker
   17.43 +
   17.44 +  if isinstance(path, str): path = path.split('/')
   17.45 +  else: path=list(path)
   17.46 +  
   17.47 +  REQUEST={'TraversalRequestNameStack': path}
   17.48 +  path.reverse()
   17.49 +  pop=path.pop
   17.50 +
   17.51 +  if len(path) > 1 and not path[0]:
   17.52 +    # Remove trailing slash
   17.53 +    path.pop(0)
   17.54 +
   17.55 +  if not path[-1]:
   17.56 +    # If the path starts with an empty string, go to the root first.
   17.57 +    pop()
   17.58 +    self=ob.getPhysicalRoot()
   17.59 +
   17.60 +  object = ob
   17.61 +  while path:
   17.62 +    name=pop()
   17.63 +    __traceback_info__ = path, name
   17.64 +
   17.65 +    if name == '..':
   17.66 +      o=getattr(object, 'aq_parent', M)
   17.67 +      if o is not M:
   17.68 +        object=o
   17.69 +        continue
   17.70 +
   17.71 +    t=get(object, '__bobo_traverse__', M)
   17.72 +    if t is not M: o=t(REQUEST, name)
   17.73 +    else:
   17.74 +      o = get(object, name, M)
   17.75 +      if o is M:
   17.76 +        try: o = object[name]
   17.77 +        except (AttributeError, TypeError): # better exception
   17.78 +          raise AttributeError(name)
   17.79 +    object = o
   17.80 +
   17.81 +  return object
   17.82 +
   17.83 +
   17.84 +class SubPathExpr(SubPathExpr):
   17.85 +  _eval = rebindFunction(SubPathExpr._eval.im_func,
   17.86 +                         restrictedTraverse=trustedTraverse,
   17.87 +                         )
   17.88 +
   17.89 +class PathExpr(PathExpr):
   17.90 +  __init__ = rebindFunction(PathExpr.__init__.im_func,
   17.91 +                            SubPathExpr=SubPathExpr,
   17.92 +                            )
   17.93 +
   17.94 +class StringExpr(StringExpr):
   17.95 +  __init__ = rebindFunction(StringExpr.__init__.im_func,
   17.96 +                            PathExpr=PathExpr,
   17.97 +                            )
   17.98 +  
   17.99 +installHandlers = rebindFunction(installHandlers,
  17.100 +                                 PathExpr=PathExpr,
  17.101 +                                 StringExpr=StringExpr,
  17.102 +                                 PythonExpr=PythonExpr,
  17.103 +                                 )
  17.104 +
  17.105 +_engine=None
  17.106 +getEngine = rebindFunction(getEngine,
  17.107 +                           _engine=_engine,
  17.108 +                           installHandlers=installHandlers
  17.109 +                           )
  17.110 +
  17.111 +
  17.112 +  
    18.1 new file mode 100644
    18.2 --- /dev/null
    18.3 +++ b/browser/__init__.py
    18.4 @@ -0,0 +1,32 @@
    18.5 +##############################################################################
    18.6 +#
    18.7 +# Copyright (c) 2004, 2005 Zope Corporation and Contributors.
    18.8 +# All Rights Reserved.
    18.9 +#
   18.10 +# This software is subject to the provisions of the Zope Public License,
   18.11 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
   18.12 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
   18.13 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   18.14 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
   18.15 +# FOR A PARTICULAR PURPOSE.
   18.16 +#
   18.17 +##############################################################################
   18.18 +"""Provide basic browser functionality
   18.19 +
   18.20 +$Id: __init__.py 12884 2005-05-30 13:10:41Z philikon $
   18.21 +"""
   18.22 +import Acquisition
   18.23 +from AccessControl import ClassSecurityInfo
   18.24 +from Globals import InitializeClass
   18.25 +
   18.26 +class BrowserView(Acquisition.Explicit):
   18.27 +    security = ClassSecurityInfo()
   18.28 +
   18.29 +    def __init__(self, context, request):
   18.30 +        self.context = context
   18.31 +        self.request = request
   18.32 +
   18.33 +    # XXX do not create any methods on the subclass called index_html,
   18.34 +    # as this makes Zope 2 traverse into that first!
   18.35 +
   18.36 +InitializeClass(BrowserView)
    19.1 new file mode 100644
    19.2 --- /dev/null
    19.3 +++ b/browser/absoluteurl.py
    19.4 @@ -0,0 +1,84 @@
    19.5 +##############################################################################
    19.6 +#
    19.7 +# Copyright (c) 2004, 2005 Zope Corporation and Contributors.
    19.8 +# All Rights Reserved.
    19.9 +#
   19.10 +# This software is subject to the provisions of the Zope Public License,
   19.11 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
   19.12 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
   19.13 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   19.14 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
   19.15 +# FOR A PARTICULAR PURPOSE.
   19.16 +#
   19.17 +##############################################################################
   19.18 +"""Absolute URL
   19.19 +
   19.20 +$Id: absoluteurl.py 13254 2005-06-09 21:40:41Z philikon $
   19.21 +"""
   19.22 +from Acquisition import aq_inner, aq_parent
   19.23 +from OFS.interfaces import ITraversable
   19.24 +
   19.25 +from zope.interface import implements
   19.26 +from zope.app import zapi
   19.27 +from zope.app.traversing.browser.interfaces import IAbsoluteURL
   19.28 +
   19.29 +from Products.Five.browser import BrowserView
   19.30 +
   19.31 +class AbsoluteURL(BrowserView):
   19.32 +    """An adapter for Zope3-style absolute_url using Zope2 methods
   19.33 +
   19.34 +    (original: zope.app.traversing.browser.absoluteurl)
   19.35 +    """
   19.36 +    implements(IAbsoluteURL)
   19.37 +
   19.38 +    def __init__(self, context, request):
   19.39 +        self.context, self.request = context, request
   19.40 +
   19.41 +    def __str__(self):
   19.42 +        context = aq_inner(self.context)
   19.43 +        return context.absolute_url()
   19.44 +
   19.45 +    __call__ = __str__
   19.46 +
   19.47 +    def breadcrumbs(self):
   19.48 +        context = aq_inner(self.context)
   19.49 +        container = aq_parent(context)
   19.50 +        request = self.request
   19.51 +
   19.52 +        name = context.getId()
   19.53 +        
   19.54 +        if container is None or self._isVirtualHostRoot() \
   19.55 +            or not ITraversable.providedBy(container):
   19.56 +            return (
   19.57 +                {'name': name, 'url': context.absolute_url()},)
   19.58 +
   19.59 +        view = zapi.getViewProviding(container, IAbsoluteURL, request)
   19.60 +        base = tuple(view.breadcrumbs())
   19.61 +        base += (
   19.62 +            {'name': name, 'url': ("%s/%s" % (base[-1]['url'], name))},)
   19.63 +
   19.64 +        return base
   19.65 +
   19.66 +    def _isVirtualHostRoot(self):
   19.67 +        virtualrootpath = self.request.get('VirtualRootPhysicalPath', None)
   19.68 +        if virtualrootpath is None:
   19.69 +            return False
   19.70 +        context = aq_inner(self.context)
   19.71 +        return context.restrictedTraverse(virtualrootpath) == context
   19.72 +
   19.73 +class SiteAbsoluteURL(AbsoluteURL):
   19.74 +    """An adapter for Zope3-style absolute_url using Zope2 methods
   19.75 +
   19.76 +    This one is just used to stop breadcrumbs from crumbing up
   19.77 +    to the Zope root.
   19.78 +
   19.79 +    (original: zope.app.traversing.browser.absoluteurl)
   19.80 +    """
   19.81 +
   19.82 +    def breadcrumbs(self):
   19.83 +        context = self.context
   19.84 +        request = self.request
   19.85 +
   19.86 +        return ({'name': context.getId(),
   19.87 +                 'url': context.absolute_url()
   19.88 +                 },)
    20.1 new file mode 100644
    20.2 --- /dev/null
    20.3 +++ b/browser/adding.pt
    20.4 @@ -0,0 +1,7 @@
    20.5 +<html metal:use-macro="context/@@standard_macros/page">
    20.6 +<body>
    20.7 +<div metal:fill-slot="main">
    20.8 +  <p>+ screen not yet supported by Five</p>
    20.9 +</div>
   20.10 +</body>
   20.11 +</html>
    21.1 new file mode 100644
    21.2 --- /dev/null
    21.3 +++ b/browser/adding.py
    21.4 @@ -0,0 +1,261 @@
    21.5 +##############################################################################
    21.6 +#
    21.7 +# Copyright (c) 2002-2005 Zope Corporation and Contributors.
    21.8 +# All Rights Reserved.
    21.9 +#
   21.10 +# This software is subject to the provisions of the Zope Public License,
   21.11 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
   21.12 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
   21.13 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   21.14 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
   21.15 +# FOR A PARTICULAR PURPOSE.
   21.16 +#
   21.17 +##############################################################################
   21.18 +"""Adding View
   21.19 +
   21.20 +The Adding View is used to add new objects to a container. It is sort of a
   21.21 +factory screen.
   21.22 +"""
   21.23 +__docformat__ = 'restructuredtext'
   21.24 +
   21.25 +from warnings import warn
   21.26 +from zope.interface import implements
   21.27 +from zope.publisher.interfaces import IPublishTraverse
   21.28 +from zope.component.interfaces import IFactory
   21.29 +
   21.30 +from zope.app.exception.interfaces import UserError
   21.31 +from zope.app.container.interfaces import IAdding, INameChooser
   21.32 +from zope.app.container.interfaces import IContainerNamesContainer
   21.33 +from zope.app.container.constraints import checkFactory, checkObject
   21.34 +
   21.35 +from zope.app import zapi
   21.36 +from zope.app.event.objectevent import ObjectCreatedEvent
   21.37 +from zope.event import notify
   21.38 +
   21.39 +from zExceptions import BadRequest
   21.40 +
   21.41 +from Products.Five import BrowserView
   21.42 +from Products.Five.traversable import Traversable
   21.43 +from Products.Five.browser.pagetemplatefile import ZopeTwoPageTemplateFile
   21.44 +
   21.45 +from Acquisition import Implicit
   21.46 +from OFS.SimpleItem import SimpleItem
   21.47 +
   21.48 +class BasicAdding(Implicit, BrowserView):
   21.49 +    implements(IAdding, IPublishTraverse)
   21.50 +
   21.51 +    def add(self, content):
   21.52 +        """See zope.app.container.interfaces.IAdding
   21.53 +        """
   21.54 +        container = self.context
   21.55 +        name = self.contentName
   21.56 +        chooser = INameChooser(container)
   21.57 +
   21.58 +        # check precondition
   21.59 +        checkObject(container, name, content)
   21.60 +
   21.61 +        if IContainerNamesContainer.providedBy(container):
   21.62 +            # The container picks it's own names.
   21.63 +            # We need to ask it to pick one.
   21.64 +            name = chooser.chooseName(self.contentName or '', content)
   21.65 +        else:
   21.66 +            request = self.request
   21.67 +            name = request.get('add_input_name', name)
   21.68 +
   21.69 +            if name is None:
   21.70 +                name = chooser.chooseName(self.contentName or '', content)
   21.71 +            elif name == '':
   21.72 +                name = chooser.chooseName('', content)
   21.73 +            else:
   21.74 +                # Invoke the name chooser even when we have a
   21.75 +                # name. It'll do useful things with it like converting
   21.76 +                # the incoming unicode to an ASCII string.
   21.77 +                name = chooser.chooseName(name, container)
   21.78 +        
   21.79 +        content.id = name
   21.80 +        container._setObject(name, content)
   21.81 +        self.contentName = name # Set the added object Name
   21.82 +        return container._getOb(name)
   21.83 +
   21.84 +    contentName = None # usually set by Adding traverser
   21.85 +
   21.86 +    def nextURL(self):
   21.87 +        """See zope.app.container.interfaces.IAdding"""
   21.88 +        # XXX this is definitely not right for all or even most uses
   21.89 +        # of Five, but can be overridden by an AddView subclass, using
   21.90 +        # the class attribute of a zcml:addform directive
   21.91 +        return (str(zapi.getView(self.context, "absolute_url", self.request))
   21.92 +                + '/manage_main')
   21.93 +
   21.94 +    # set in BrowserView.__init__
   21.95 +    request = None 
   21.96 +    context = None
   21.97 +
   21.98 +    def renderAddButton(self):
   21.99 +        warn("The renderAddButton method is deprecated, use nameAllowed",
  21.100 +            DeprecationWarning, 2)
  21.101 +    
  21.102 +
  21.103 +    def publishTraverse(self, request, name):
  21.104 +        """See zope.app.container.interfaces.IAdding"""
  21.105 +        if '=' in name:
  21.106 +            view_name, content_name = name.split("=", 1)
  21.107 +            self.contentName = content_name
  21.108 +
  21.109 +            if view_name.startswith('@@'):
  21.110 +                view_name = view_name[2:]
  21.111 +            return zapi.getView(self, view_name, request)
  21.112 +
  21.113 +        if name.startswith('@@'):
  21.114 +            view_name = name[2:]
  21.115 +        else:
  21.116 +            view_name = name
  21.117 +
  21.118 +        view = zapi.queryView(self, view_name, request)
  21.119 +        if view is not None:
  21.120 +            return view
  21.121 +
  21.122 +        factory = zapi.queryUtility(IFactory, name)
  21.123 +        if factory is None:
  21.124 +            return super(BasicAdding, self).publishTraverse(request, name)
  21.125 +
  21.126 +        return factory
  21.127 +
  21.128 +    def action(self, type_name='', id=''):
  21.129 +        if not type_name:
  21.130 +            raise UserError("You must select the type of object to add.")
  21.131 +
  21.132 +        if type_name.startswith('@@'):
  21.133 +            type_name = type_name[2:]
  21.134 +
  21.135 +        if '/' in type_name:
  21.136 +            view_name  = type_name.split('/', 1)[0]
  21.137 +        else:
  21.138 +            view_name = type_name
  21.139 +
  21.140 +        if zapi.queryView(self, view_name, self.request) is not None:
  21.141 +            url = "%s/%s=%s" % (
  21.142 +                zapi.getView(self, "absolute_url", self.request),
  21.143 +                type_name, id)
  21.144 +            self.request.response.redirect(url)
  21.145 +            return
  21.146 +
  21.147 +        if not self.contentName:
  21.148 +            self.contentName = id
  21.149 +
  21.150 +        factory = zapi.getUtility(IFactory, type_name)
  21.151 +        content = factory()
  21.152 +
  21.153 +        notify(ObjectCreatedEvent(content))
  21.154 +        self.add(content)
  21.155 +        self.request.response.redirect(self.nextURL())
  21.156 +
  21.157 +    def namesAccepted(self):
  21.158 +        return not IContainerNamesContainer.providedBy(self.context)
  21.159 +
  21.160 +    def nameAllowed(self):
  21.161 +        """Return whether names can be input by the user."""
  21.162 +        return not IContainerNamesContainer.providedBy(self.context)
  21.163 +    
  21.164 +
  21.165 +class Adding(BasicAdding):
  21.166 +
  21.167 +    menu_id = None
  21.168 +    index = ZopeTwoPageTemplateFile("adding.pt")
  21.169 +
  21.170 +    def addingInfo(self):
  21.171 +        """Return menu data.
  21.172 +
  21.173 +        This is sorted by title.
  21.174 +        """
  21.175 +        container = self.context
  21.176 +        menu_service = zapi.getService("BrowserMenu")
  21.177 +        result = []
  21.178 +        for menu_id in (self.menu_id, 'zope.app.container.add'):
  21.179 +            if not menu_id:
  21.180 +                continue
  21.181 +            for item in menu_service.getMenu(menu_id, self, self.request):
  21.182 +                extra = item.get('extra')
  21.183 +                if extra:
  21.184 +                    factory = extra.get('factory')
  21.185 +                    if factory:
  21.186 +                        factory = zapi.getUtility(IFactory, factory)
  21.187 +                        if not checkFactory(container, None, factory):
  21.188 +                            continue
  21.189 +                        elif item['extra']['factory'] != item['action']:
  21.190 +                            item['has_custom_add_view']=True
  21.191 +                result.append(item)
  21.192 +
  21.193 +        result.sort(lambda a, b: cmp(a['title'], b['title']))
  21.194 +        return result
  21.195 +
  21.196 +    def isSingleMenuItem(self):
  21.197 +        "Return whether there is single menu item or not."
  21.198 +        return len(self.addingInfo()) == 1
  21.199 +
  21.200 +    def hasCustomAddView(self):
  21.201 +       "This should be called only if there is `singleMenuItem` else return 0"
  21.202 +       if self.isSingleMenuItem():
  21.203 +           menu_item = self.addingInfo()[0]
  21.204 +           if 'has_custom_add_view' in menu_item:
  21.205 +               return True
  21.206 +       return False
  21.207 +
  21.208 +class ContentAdding(Adding, Traversable, SimpleItem):
  21.209 +
  21.210 +    menu_id = "add_content"
  21.211 +
  21.212 +class ObjectManagerNameChooser:
  21.213 +    """A name chooser for a Zope object manager.
  21.214 +    """
  21.215 +    
  21.216 +    implements(INameChooser)
  21.217 +    
  21.218 +    def __init__(self, context):
  21.219 +        self.context = context
  21.220 +
  21.221 +    def checkName(self, name, object):
  21.222 +        # ObjectManager can only deal with ASCII names. Specially
  21.223 +        # ObjectManager._checkId can only deal with strings.
  21.224 +        try:
  21.225 +            name = name.encode('ascii')
  21.226 +        except UnicodeDecodeError:
  21.227 +            raise UserError, "Id must contain only ASCII characters."
  21.228 +
  21.229 +        try:
  21.230 +            self.context._checkId(name, allow_dup=False)
  21.231 +        except BadRequest, e:
  21.232 +            msg = ' '.join(e.args) or "Id is in use or invalid"
  21.233 +            raise UserError, msg
  21.234 +
  21.235 +    def chooseName(self, name, object):
  21.236 +        if not name:
  21.237 +            name = object.__class__.__name__
  21.238 +        else:
  21.239 +            try:
  21.240 +                name = name.encode('ascii')
  21.241 +            except UnicodeDecodeError:
  21.242 +                raise UserError, "Id must contain only ASCII characters."
  21.243 +
  21.244 +        dot = name.rfind('.')
  21.245 +        if dot >= 0:
  21.246 +            suffix = name[dot:]
  21.247 +            name = name[:dot]
  21.248 +        else:
  21.249 +            suffix = ''
  21.250 +
  21.251 +        n = name + suffix
  21.252 +        i = 0
  21.253 +        while True:
  21.254 +            i += 1
  21.255 +            try:
  21.256 +                self.context._getOb(n)
  21.257 +            except AttributeError:
  21.258 +                break
  21.259 +            n = name + '-' + str(i) + suffix
  21.260 +            
  21.261 +        # Make sure the name is valid.  We may have started with
  21.262 +        # something bad.
  21.263 +        self.checkName(n, object)
  21.264 +
  21.265 +        return n
    22.1 new file mode 100644
    22.2 --- /dev/null
    22.3 +++ b/browser/configure.zcml
    22.4 @@ -0,0 +1,76 @@
    22.5 +<configure xmlns="http://namespaces.zope.org/zope"
    22.6 +           xmlns:browser="http://namespaces.zope.org/browser">
    22.7 +
    22.8 +  <serviceType
    22.9 +      id="BrowserMenu"
   22.10 +      interface="zope.app.publisher.interfaces.browser.IBrowserMenuService"
   22.11 +      />
   22.12 +
   22.13 +  <service
   22.14 +      serviceType="BrowserMenu"
   22.15 +      permission="zope.Public"
   22.16 +      component="zope.app.publisher.browser.globalbrowsermenuservice.globalBrowserMenuService"
   22.17 +      />
   22.18 +
   22.19 +  <browser:defaultView name="index.html" />
   22.20 +
   22.21 +  <browser:page
   22.22 +      for="*"
   22.23 +      name="absolute_url"
   22.24 +      class=".absoluteurl.AbsoluteURL"
   22.25 +      permission="zope.Public"
   22.26 +      allowed_interface="zope.app.traversing.browser.interfaces.IAbsoluteURL"
   22.27 +      />
   22.28 +
   22.29 +  <view
   22.30 +      for="*"
   22.31 +      factory=".absoluteurl.AbsoluteURL"
   22.32 +      type="zope.publisher.interfaces.http.IHTTPRequest"
   22.33 +      permission="zope.Public"
   22.34 +      provides="zope.app.traversing.browser.interfaces.IAbsoluteURL"
   22.35 +      />
   22.36 +
   22.37 +  <browser:page
   22.38 +      for="zope.app.traversing.interfaces.IContainmentRoot"
   22.39 +      name="absolute_url"
   22.40 +      class=".absoluteurl.SiteAbsoluteURL"
   22.41 +      permission="zope.Public"
   22.42 +      allowed_interface="zope.app.traversing.browser.interfaces.IAbsoluteURL"
   22.43 +      />
   22.44 +
   22.45 +  <view
   22.46 +      for="zope.app.traversing.interfaces.IContainmentRoot"
   22.47 +      factory=".absoluteurl.SiteAbsoluteURL"
   22.48 +      type="zope.publisher.interfaces.http.IHTTPRequest"
   22.49 +      permission="zope.Public"
   22.50 +      provides="zope.app.traversing.browser.interfaces.IAbsoluteURL"
   22.51 +      />
   22.52 +
   22.53 +  <browser:view
   22.54 +      for="OFS.interfaces.IObjectManager"
   22.55 +      name="+"
   22.56 +      class=".adding.ContentAdding"
   22.57 +      permission="zope2.ViewManagementScreens"
   22.58 +      >
   22.59 +
   22.60 +    <browser:page name="index.html"  template="adding.pt" />
   22.61 +    <browser:page name="action.html" attribute="action" />
   22.62 +
   22.63 +  </browser:view>
   22.64 +
   22.65 +  <adapter
   22.66 +      for="OFS.interfaces.IObjectManager"
   22.67 +      factory=".adding.ObjectManagerNameChooser"
   22.68 +      provides="zope.app.container.interfaces.INameChooser"
   22.69 +      />
   22.70 +
   22.71 +  <!-- Menu access -->
   22.72 +  <browser:page
   22.73 +      for="*"
   22.74 +      name="view_get_menu"
   22.75 +      permission="zope.Public"
   22.76 +      class=".menu.MenuAccessView"
   22.77 +      allowed_interface="zope.app.publisher.interfaces.browser.IMenuAccessView"
   22.78 +      />
   22.79 +
   22.80 +</configure>
    23.1 new file mode 100644
    23.2 --- /dev/null
    23.3 +++ b/browser/menu.py
    23.4 @@ -0,0 +1,29 @@
    23.5 +##############################################################################
    23.6 +#
    23.7 +# Copyright (c) 2005 Zope Corporation and Contributors.
    23.8 +# All Rights Reserved.
    23.9 +#
   23.10 +# This software is subject to the provisions of the Zope Public License,
   23.11 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
   23.12 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
   23.13 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   23.14 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
   23.15 +# FOR A PARTICULAR PURPOSE.
   23.16 +#
   23.17 +##############################################################################
   23.18 +"""Some menu code
   23.19 +
   23.20 +$Id: menu.py 14512 2005-07-11 18:40:51Z philikon $
   23.21 +"""
   23.22 +from zope.interface import implements
   23.23 +from zope.app import zapi
   23.24 +from zope.app.publisher.interfaces.browser import IMenuAccessView
   23.25 +from zope.app.servicenames import BrowserMenu
   23.26 +from Products.Five import BrowserView
   23.27 +
   23.28 +class MenuAccessView(BrowserView):
   23.29 +    implements(IMenuAccessView)
   23.30 +
   23.31 +    def __getitem__(self, menu_id):
   23.32 +        browser_menu_service = zapi.getService(BrowserMenu)
   23.33 +        return browser_menu_service.getMenu(menu_id, self.context, self.request)
    24.1 new file mode 100644
    24.2 --- /dev/null
    24.3 +++ b/browser/meta.zcml
    24.4 @@ -0,0 +1,107 @@
    24.5 +<configure
    24.6 +    xmlns="http://namespaces.zope.org/zope"
    24.7 +    xmlns:meta="http://namespaces.zope.org/meta">
    24.8 +
    24.9 +  <meta:directives namespace="http://namespaces.zope.org/browser">
   24.10 +
   24.11 +    <meta:directive
   24.12 +        name="layer"
   24.13 +        schema="zope.app.publisher.browser.metadirectives.ILayerDirective"
   24.14 +        handler="zope.app.publisher.browser.metaconfigure.layer"
   24.15 +        />
   24.16 +
   24.17 +    <meta:directive
   24.18 +        name="skin"
   24.19 +        schema="zope.app.publisher.browser.metadirectives.ISkinDirective"
   24.20 +        handler="zope.app.publisher.browser.metaconfigure.skin"
   24.21 +        />
   24.22 +
   24.23 +    <meta:directive
   24.24 +        name="defaultSkin"
   24.25 +        schema="zope.app.publisher.browser.metadirectives.IDefaultSkinDirective"
   24.26 +        handler="zope.app.publisher.browser.metaconfigure.defaultSkin"
   24.27 +        />
   24.28 +
   24.29 +    <meta:directive
   24.30 +        name="defaultView"
   24.31 +        schema="zope.app.publisher.browser.metadirectives.IDefaultViewDirective"
   24.32 +        handler=".metaconfigure.defaultView"
   24.33 +        />
   24.34 +
   24.35 +    <meta:directive
   24.36 +        name="page"
   24.37 +        schema="zope.app.publisher.browser.metadirectives.IPageDirective"
   24.38 +        handler=".metaconfigure.page"
   24.39 +        />
   24.40 +
   24.41 +    <meta:complexDirective
   24.42 +        name="pages"
   24.43 +        schema="zope.app.publisher.browser.metadirectives.IPagesDirective"
   24.44 +        handler=".metaconfigure.pages"
   24.45 +        >
   24.46 +
   24.47 +      <meta:subdirective
   24.48 +          name="page"
   24.49 +          schema="zope.app.publisher.browser.metadirectives.IPagesPageSubdirective"
   24.50 +          />
   24.51 +
   24.52 +    </meta:complexDirective>
   24.53 +
   24.54 +    <meta:directive
   24.55 +        name="resource"
   24.56 +        schema="zope.app.publisher.browser.metadirectives.IResourceDirective"
   24.57 +        handler=".metaconfigure.resource"
   24.58 +        />
   24.59 +
   24.60 +    <meta:directive
   24.61 +        name="resourceDirectory"
   24.62 +        schema="zope.app.publisher.browser.metadirectives.IResourceDirectoryDirective"
   24.63 +        handler=".metaconfigure.resourceDirectory"
   24.64 +        />
   24.65 +
   24.66 +    <meta:directive
   24.67 +        name="menu"
   24.68 +        schema="zope.app.publisher.browser.metadirectives.IMenuDirective"
   24.69 +        handler="zope.app.publisher.browser.globalbrowsermenuservice.menuDirective"
   24.70 +        />
   24.71 +
   24.72 +    <meta:directive
   24.73 +        name="menuItem"
   24.74 +        schema="zope.app.publisher.browser.metadirectives.IMenuItemDirective"
   24.75 +        handler="zope.app.publisher.browser.globalbrowsermenuservice.menuItemDirective"
   24.76 +        />
   24.77 +
   24.78 +    <meta:complexDirective
   24.79 +        name="menuItems"
   24.80 +        schema="zope.app.publisher.browser.metadirectives.IMenuItemsDirective"
   24.81 +        handler="zope.app.publisher.browser.globalbrowsermenuservice.menuItemsDirective"
   24.82 +        >
   24.83 +
   24.84 +      <meta:subdirective
   24.85 +          name="menuItem"
   24.86 +          schema="zope.app.publisher.browser.metadirectives.IMenuItemSubdirective"
   24.87 +          />
   24.88 +
   24.89 +    </meta:complexDirective>
   24.90 +
   24.91 +    <meta:complexDirective
   24.92 +        name="view"
   24.93 +        schema="zope.app.publisher.browser.metadirectives.IViewDirective"
   24.94 +        handler=".metaconfigure.view"
   24.95 +        >
   24.96 +
   24.97 +      <meta:subdirective
   24.98 +          name="page"
   24.99 +          schema="zope.app.publisher.browser.metadirectives.IViewPageSubdirective"
  24.100 +          />
  24.101 +
  24.102 +      <meta:subdirective
  24.103 +          name="defaultPage"
  24.104 +          schema="zope.app.publisher.browser.metadirectives.IViewDefaultPageSubdirective"
  24.105 +          />
  24.106 +
  24.107 +    </meta:complexDirective>
  24.108 +
  24.109 +  </meta:directives>
  24.110 +
  24.111 +</configure>
    25.1 new file mode 100644
    25.2 --- /dev/null
    25.3 +++ b/browser/metaconfigure.py
    25.4 @@ -0,0 +1,466 @@
    25.5 +##############################################################################
    25.6 +#
    25.7 +# Copyright (c) 2004, 2005 Zope Corporation and Contributors.
    25.8 +# All Rights Reserved.
    25.9 +#
   25.10 +# This software is subject to the provisions of the Zope Public License,
   25.11 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
   25.12 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
   25.13 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   25.14 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
   25.15 +# FOR A PARTICULAR PURPOSE.
   25.16 +#
   25.17 +##############################################################################
   25.18 +"""Browser directives
   25.19 +
   25.20 +Directives to emulate the 'http://namespaces.zope.org/browser'
   25.21 +namespace in ZCML known from zope.app.
   25.22 +
   25.23 +$Id: metaconfigure.py 18911 2005-10-25 09:36:24Z regebro $
   25.24 +"""
   25.25 +import os
   25.26 +
   25.27 +from zope.interface import Interface
   25.28 +from zope.component import getGlobalService, ComponentLookupError
   25.29 +from zope.configuration.exceptions import ConfigurationError
   25.30 +from zope.component.servicenames import Presentation
   25.31 +from zope.publisher.interfaces.browser import IBrowserRequest
   25.32 +from zope.app.publisher.browser.viewmeta import pages as zope_app_pages
   25.33 +from zope.app.publisher.browser.viewmeta import view as zope_app_view
   25.34 +from zope.app.publisher.browser.viewmeta import providesCallable
   25.35 +from zope.app.publisher.browser.globalbrowsermenuservice import\
   25.36 +     menuItemDirective
   25.37 +from zope.app.component.metaconfigure import handler
   25.38 +from zope.app.component.interface import provideInterface
   25.39 +from zope.app.container.interfaces import IAdding
   25.40 +
   25.41 +from Products.Five.browser import BrowserView
   25.42 +from Products.Five.browser.resource import FileResourceFactory, ImageResourceFactory
   25.43 +from Products.Five.browser.resource import PageTemplateResourceFactory
   25.44 +from Products.Five.browser.resource import DirectoryResourceFactory
   25.45 +from Products.Five.browser.pagetemplatefile import ZopeTwoPageTemplateFile
   25.46 +from Products.Five.metaclass import makeClass
   25.47 +from Products.Five.security import getSecurityInfo, protectClass, \
   25.48 +    protectName, initializeClass
   25.49 +
   25.50 +import ExtensionClass
   25.51 +
   25.52 +def page(_context, name, permission, for_,
   25.53 +         layer='default', template=None, class_=None,
   25.54 +         allowed_interface=None, allowed_attributes=None,
   25.55 +         attribute='__call__', menu=None, title=None,
   25.56 +         ):
   25.57 +
   25.58 +    _handle_menu(_context, menu, title, [for_], name, permission)
   25.59 +
   25.60 +    if not (class_ or template):
   25.61 +        raise ConfigurationError("Must specify a class or template")
   25.62 +    if allowed_attributes is None:
   25.63 +        allowed_attributes = []
   25.64 +    if allowed_interface is not None:
   25.65 +        for interface in allowed_interface:
   25.66 +            attrs = [n for n, d in interface.namesAndDescriptions(1)]
   25.67 +            allowed_attributes.extend(attrs)
   25.68 +
   25.69 +    if attribute != '__call__':
   25.70 +        if template:
   25.71 +            raise ConfigurationError(
   25.72 +                "Attribute and template cannot be used together.")
   25.73 +
   25.74 +        if not class_:
   25.75 +            raise ConfigurationError(
   25.76 +                "A class must be provided if attribute is used")
   25.77 +
   25.78 +    if template:
   25.79 +        template = os.path.abspath(str(_context.path(template)))
   25.80 +        if not os.path.isfile(template):
   25.81 +            raise ConfigurationError("No such file", template)
   25.82 +
   25.83 +    if class_:
   25.84 +        # new-style classes do not work with Five. As we want to import
   25.85 +        # packages from z3 directly, we ignore new-style classes for now.
   25.86 +        if type(class_) == type:
   25.87 +            return
   25.88 +        if attribute != '__call__':
   25.89 +            if not hasattr(class_, attribute):
   25.90 +                raise ConfigurationError(
   25.91 +                    "The provided class doesn't have the specified attribute "
   25.92 +                    )
   25.93 +        cdict = getSecurityInfo(class_)
   25.94 +        if template:
   25.95 +            new_class = makeClassForTemplate(template, bases=(class_, ),
   25.96 +                                             cdict=cdict)
   25.97 +        elif attribute != "__call__":
   25.98 +            # we're supposed to make a page for an attribute (read:
   25.99 +            # method) and it's not __call__.  We thus need to create a
  25.100 +            # new class using our mixin for attributes.
  25.101 +            cdict.update({'__page_attribute__': attribute})
  25.102 +            new_class = makeClass(class_.__name__,
  25.103 +                                  (class_, ViewMixinForAttributes),
  25.104 +                                  cdict)
  25.105 +
  25.106 +            # in case the attribute does not provide a docstring,
  25.107 +            # ZPublisher refuses to publish it.  So, as a workaround,
  25.108 +            # we provide a stub docstring
  25.109 +            func = getattr(new_class, attribute)
  25.110 +            if not func.__doc__:
  25.111 +                # cannot test for MethodType/UnboundMethod here
  25.112 +                # because of ExtensionClass
  25.113 +                if hasattr(func, 'im_func'):
  25.114 +                    # you can only set a docstring on functions, not
  25.115 +                    # on method objects
  25.116 +                    func = func.im_func
  25.117 +                func.__doc__ = "Stub docstring to make ZPublisher work"
  25.118 +        else:
  25.119 +            # we could use the class verbatim here, but we'll execute
  25.120 +            # some security declarations on it so we really shouldn't
  25.121 +            # modify the original.  So, instead we make a new class
  25.122 +            # with just one base class -- the original
  25.123 +            new_class = makeClass(class_.__name__, (class_,), cdict)
  25.124 +
  25.125 +    else:
  25.126 +        # template
  25.127 +        new_class = makeClassForTemplate(template)
  25.128 +
  25.129 +    _handle_for(_context, for_)
  25.130 +
  25.131 +    _context.action(
  25.132 +        discriminator = ('view', for_, name, IBrowserRequest, layer),
  25.133 +        callable = handler,
  25.134 +        args = (Presentation, 'provideAdapter',
  25.135 +                IBrowserRequest, new_class, name, [for_], Interface, layer,
  25.136 +                _context.info),
  25.137 +        )
  25.138 +    _context.action(
  25.139 +        discriminator = ('five:protectClass', new_class),
  25.140 +        callable = protectClass,
  25.141 +        args = (new_class, permission)
  25.142 +        )
  25.143 +    if allowed_attributes:
  25.144 +        for attr in allowed_attributes:
  25.145 +            _context.action(
  25.146 +                discriminator = ('five:protectName', new_class, attr),
  25.147 +                callable = protectName,
  25.148 +                args = (new_class, attr, permission)
  25.149 +                )
  25.150 +    _context.action(
  25.151 +        discriminator = ('five:initialize:class', new_class),
  25.152 +        callable = initializeClass,
  25.153 +        args = (new_class,)
  25.154 +        )
  25.155 +
  25.156 +class pages(zope_app_pages):
  25.157 +
  25.158 +    def page(self, _context, name, attribute='__call__', template=None,
  25.159 +             menu=None, title=None):
  25.160 +        return page(_context,
  25.161 +                    name=name,
  25.162 +                    attribute=attribute,
  25.163 +                    template=template,
  25.164 +                    menu=menu, title=title,
  25.165 +                    **(self.opts))
  25.166 +
  25.167 +def defaultView(_context, name, for_=None):
  25.168 +
  25.169 +    type = IBrowserRequest
  25.170 +
  25.171 +    _context.action(
  25.172 +        discriminator = ('defaultViewName', for_, type, name),
  25.173 +        callable = handler,
  25.174 +        args = (Presentation,
  25.175 +                'setDefaultViewName', for_, type, name),
  25.176 +        )
  25.177 +
  25.178 +    _handle_for(_context, for_)
  25.179 +
  25.180 +# view (named view with pages)
  25.181 +
  25.182 +class view(zope_app_view):
  25.183 +
  25.184 +    def __call__(self):
  25.185 +        (_context, name, for_, permission, layer, class_,
  25.186 +         allowed_interface, allowed_attributes) = self.args
  25.187 +
  25.188 +        required = {}
  25.189 +
  25.190 +        cdict = {}
  25.191 +        pages = {}
  25.192 +
  25.193 +        for pname, attribute, template in self.pages:
  25.194 +            try:
  25.195 +                s = getGlobalService(Presentation)
  25.196 +            except ComponentLookupError, err:
  25.197 +                pass
  25.198 +
  25.199 +            if template:
  25.200 +                cdict[pname] = ZopeTwoPageTemplateFile(template)
  25.201 +                if attribute and attribute != name:
  25.202 +                    cdict[attribute] = cdict[pname]
  25.203 +            else:
  25.204 +                if not hasattr(class_, attribute):
  25.205 +                    raise ConfigurationError("Undefined attribute",
  25.206 +                                             attribute)
  25.207 +
  25.208 +            attribute = attribute or pname
  25.209 +            required[pname] = permission
  25.210 +
  25.211 +            pages[pname] = attribute
  25.212 +
  25.213 +        # This should go away, but noone seems to remember what to do. :-(
  25.214 +        if hasattr(class_, 'publishTraverse'):
  25.215 +
  25.216 +            def publishTraverse(self, request, name,
  25.217 +                                pages=pages, getattr=getattr):
  25.218 +
  25.219 +                if name in pages:
  25.220 +                    return getattr(self, pages[name])
  25.221 +                view = zapi.queryView(self, name, request)
  25.222 +                if view is not None:
  25.223 +                    return view
  25.224 +
  25.225 +                m = class_.publishTraverse.__get__(self)
  25.226 +                return m(request, name)
  25.227 +
  25.228 +        else:
  25.229 +            def publishTraverse(self, request, name,
  25.230 +                                pages=pages, getattr=getattr):
  25.231 +
  25.232 +                if name in pages:
  25.233 +                    return getattr(self, pages[name])
  25.234 +                view = zapi.queryView(self, name, request)
  25.235 +                if view is not None:
  25.236 +                    return view
  25.237 +
  25.238 +                raise NotFoundError(self, name, request)
  25.239 +
  25.240 +        cdict['publishTraverse'] = publishTraverse
  25.241 +
  25.242 +        if not hasattr(class_, 'browserDefault'):
  25.243 +            if self.default or self.pages:
  25.244 +                default = self.default or self.pages[0][0]
  25.245 +                cdict['browserDefault'] = (
  25.246 +                    lambda self, request, default=default:
  25.247 +                    (self, (default, ))
  25.248 +                    )
  25.249 +            elif providesCallable(class_):
  25.250 +                cdict['browserDefault'] = (
  25.251 +                    lambda self, request: (self, ())
  25.252 +                    )
  25.253 +
  25.254 +        if class_ is not None:
  25.255 +            bases = (class_, ViewMixinForTemplates)
  25.256 +        else:
  25.257 +            bases = (ViewMixinForTemplates)
  25.258 +
  25.259 +        try:
  25.260 +            cname = str(name)
  25.261 +        except:
  25.262 +            cname = "GeneratedClass"
  25.263 +            
  25.264 +        newclass = makeClass(cname, bases, cdict)
  25.265 +        
  25.266 +        _handle_for(_context, for_)
  25.267 +
  25.268 +        if self.provides is not None:
  25.269 +            _context.action(
  25.270 +                discriminator = None,
  25.271 +                callable = provideInterface,
  25.272 +                args = ('', self.provides)
  25.273 +                )
  25.274 +
  25.275 +        _context.action(
  25.276 +            discriminator = ('view', for_, name, IBrowserRequest, layer,
  25.277 +                             self.provides),
  25.278 +            callable = handler,
  25.279 +            args = (Presentation, 'provideAdapter',
  25.280 +                    IBrowserRequest, newclass, name, [for_],  self.provides,
  25.281 +                    layer, _context.info),
  25.282 +            )
  25.283 +
  25.284 +def _handle_for(_context, for_):
  25.285 +    if for_ is not None:
  25.286 +        _context.action(
  25.287 +            discriminator = None,
  25.288 +            callable = provideInterface,
  25.289 +            args = ('', for_)
  25.290 +            )
  25.291 +
  25.292 +def _handle_menu(_context, menu, title, for_, name, permission):
  25.293 +    if menu or title:
  25.294 +        if not (menu and title):
  25.295 +            raise ConfigurationError(
  25.296 +                "If either menu or title are specified, they must "
  25.297 +                "both be specified.")
  25.298 +
  25.299 +        if len(for_) != 1:
  25.300 +            raise ConfigurationError(
  25.301 +                "Menus can be specified only for single-view, not for "
  25.302 +                "multi-views.")
  25.303 +
  25.304 +        return menuItemDirective(
  25.305 +            _context, menu, for_[0], '@@' + str(name), title,
  25.306 +            permission=permission)
  25.307 +
  25.308 +    return []
  25.309 +
  25.310 +_factory_map = {'image':{'prefix':'ImageResource',
  25.311 +                         'count':0,
  25.312 +                         'factory':ImageResourceFactory},
  25.313 +                'file':{'prefix':'FileResource',
  25.314 +                        'count':0,
  25.315 +                        'factory':FileResourceFactory},
  25.316 +                'template':{'prefix':'PageTemplateResource',
  25.317 +                            'count':0,
  25.318 +                            'factory':PageTemplateResourceFactory}
  25.319 +                }
  25.320 +
  25.321 +def resource(_context, name, layer='default', permission='zope.Public',
  25.322 +             file=None, image=None, template=None):
  25.323 +
  25.324 +    if ((file and image) or (file and template) or
  25.325 +        (image and template) or not (file or image or template)):
  25.326 +        raise ConfigurationError(
  25.327 +            "Must use exactly one of file or image or template"
  25.328 +            "attributes for resource directives"
  25.329 +            )
  25.330 +
  25.331 +    res = file or image or template
  25.332 +    res_type = ((file and 'file') or
  25.333 +                 (image and 'image') or
  25.334 +                 (template and 'template'))
  25.335 +    factory_info = _factory_map.get(res_type)
  25.336 +    factory_info['count'] += 1
  25.337 +    res_factory = factory_info['factory']
  25.338 +    class_name = '%s%s' % (factory_info['prefix'], factory_info['count'])
  25.339 +    new_class = makeClass(class_name, (res_factory.resource,), {})
  25.340 +    factory = res_factory(name, res, resource_factory=new_class)
  25.341 +
  25.342 +    _context.action(
  25.343 +        discriminator = ('resource', name, IBrowserRequest, layer),
  25.344 +        callable = handler,
  25.345 +        args = (Presentation, 'provideResource',
  25.346 +                name, IBrowserRequest, factory, layer),
  25.347 +        )
  25.348 +    _context.action(
  25.349 +        discriminator = ('five:protectClass', new_class),
  25.350 +        callable = protectClass,
  25.351 +        args = (new_class, permission)
  25.352 +        )
  25.353 +    _context.action(
  25.354 +        discriminator = ('five:initialize:class', new_class),
  25.355 +        callable = initializeClass,
  25.356 +        args = (new_class,)
  25.357 +        )
  25.358 +
  25.359 +_rd_map = {ImageResourceFactory:{'prefix':'DirContainedImageResource',
  25.360 +                                 'count':0},
  25.361 +           FileResourceFactory:{'prefix':'DirContainedFileResource',
  25.362 +                                'count':0},
  25.363 +           PageTemplateResourceFactory:{'prefix':'DirContainedPTResource',
  25.364 +                                        'count':0},
  25.365 +           DirectoryResourceFactory:{'prefix':'DirectoryResource',
  25.366 +                                     'count':0}
  25.367 +           }
  25.368 +
  25.369 +def resourceDirectory(_context, name, directory, layer='default',
  25.370 +                      permission='zope.Public'):
  25.371 +
  25.372 +    if not os.path.isdir(directory):
  25.373 +        raise ConfigurationError(
  25.374 +            "Directory %s does not exist" % directory
  25.375 +            )
  25.376 +
  25.377 +    resource = DirectoryResourceFactory.resource
  25.378 +    f_cache = {}
  25.379 +    resource_factories = dict(resource.resource_factories)
  25.380 +    resource_factories['default'] = resource.default_factory
  25.381 +    for ext, factory in resource_factories.items():
  25.382 +        if f_cache.get(factory) is not None:
  25.383 +            continue
  25.384 +        factory_info = _rd_map.get(factory)
  25.385 +        factory_info['count'] += 1
  25.386 +        class_name = '%s%s' % (factory_info['prefix'], factory_info['count'])
  25.387 +        factory_name = '%s%s' % (factory.__name__, factory_info['count'])
  25.388 +        f_resource = makeClass(class_name, (factory.resource,), {})
  25.389 +        f_cache[factory] = makeClass(factory_name, (factory,),
  25.390 +                                     {'resource':f_resource})
  25.391 +    for ext, factory in resource_factories.items():
  25.392 +        resource_factories[ext] = f_cache[factory]
  25.393 +    default_factory = resource_factories['default']
  25.394 +    del resource_factories['default']
  25.395 +
  25.396 +    cdict = {'resource_factories':resource_factories,
  25.397 +             'default_factory':default_factory}
  25.398 +
  25.399 +    factory_info = _rd_map.get(DirectoryResourceFactory)
  25.400 +    factory_info['count'] += 1
  25.401 +    class_name = '%s%s' % (factory_info['prefix'], factory_info['count'])
  25.402 +    dir_factory = makeClass(class_name, (resource,), cdict)
  25.403 +    factory = DirectoryResourceFactory(name, directory,
  25.404 +                                       resource_factory=dir_factory)
  25.405 +
  25.406 +    new_classes = [dir_factory,
  25.407 +                   ] + [f.resource for f in f_cache.values()]
  25.408 +
  25.409 +    _context.action(
  25.410 +        discriminator = ('resource', name, IBrowserRequest, layer),
  25.411 +        callable = handler,
  25.412 +        args = (Presentation, 'provideResource',
  25.413 +                name, IBrowserRequest, factory, layer),
  25.414 +        )
  25.415 +    for new_class in new_classes:
  25.416 +        _context.action(
  25.417 +            discriminator = ('five:protectClass', new_class),
  25.418 +            callable = protectClass,
  25.419 +            args = (new_class, permission)
  25.420 +            )
  25.421 +        _context.action(
  25.422 +            discriminator = ('five:initialize:class', new_class),
  25.423 +            callable = initializeClass,
  25.424 +            args = (new_class,)
  25.425 +            )
  25.426 +
  25.427 +#
  25.428 +# mixin classes / class factories
  25.429 +#
  25.430 +
  25.431 +class ViewMixinForAttributes(BrowserView):
  25.432 +
  25.433 +    # we have an attribute that we can simply tell ZPublisher to go to
  25.434 +    def __browser_default__(self, request):
  25.435 +        return self, (self.__page_attribute__,)
  25.436 +
  25.437 +    # this is technically not needed because ZPublisher finds our
  25.438 +    # attribute through __browser_default__; but we also want to be
  25.439 +    # able to call pages from python modules, PythonScripts or ZPT
  25.440 +    def __call__(self, *args, **kw):
  25.441 +        attr = self.__page_attribute__
  25.442 +        meth = getattr(self, attr)
  25.443 +        return meth(*args, **kw)
  25.444 +
  25.445 +class ViewMixinForTemplates(BrowserView):
  25.446 +
  25.447 +    # short cut to get to macros more easily
  25.448 +    def __getitem__(self, name):
  25.449 +        if name == 'macros':
  25.450 +            return self.index.macros
  25.451 +        return self.index.macros[name]
  25.452 +
  25.453 +    # make the template publishable
  25.454 +    def __call__(self, *args, **kw):
  25.455 +        return self.index(self, *args, **kw)
  25.456 +
  25.457 +def makeClassForTemplate(filename, globals=None, used_for=None,
  25.458 +                         bases=(), cdict=None):
  25.459 +    # XXX needs to deal with security from the bases?
  25.460 +    if cdict is None:
  25.461 +        cdict = {}
  25.462 +    cdict.update({'index': ZopeTwoPageTemplateFile(filename, globals)})
  25.463 +    bases += (ViewMixinForTemplates,)
  25.464 +    class_ = makeClass("SimpleViewClass from %s" % filename, bases, cdict)
  25.465 +
  25.466 +    if used_for is not None:
  25.467 +        class_.__used_for__ = used_for
  25.468 +
  25.469 +    return class_
  25.470 +
    26.1 new file mode 100644
    26.2 --- /dev/null
    26.3 +++ b/browser/pagetemplatefile.py
    26.4 @@ -0,0 +1,97 @@
    26.5 +##############################################################################
    26.6 +#
    26.7 +# Copyright (c) 2004, 2005 Zope Corporation and Contributors.
    26.8 +# All Rights Reserved.
    26.9 +#
   26.10 +# This software is subject to the provisions of the Zope Public License,
   26.11 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
   26.12 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
   26.13 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   26.14 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
   26.15 +# FOR A PARTICULAR PURPOSE.
   26.16 +#
   26.17 +##############################################################################
   26.18 +"""A 'PageTemplateFile' without security restrictions.
   26.19 +
   26.20 +$Id: pagetemplatefile.py 15603 2005-08-04 09:56:10Z yuppie $
   26.21 +"""
   26.22 +import os, sys
   26.23 +
   26.24 +from Globals import package_home
   26.25 +from AccessControl import getSecurityManager
   26.26 +from Shared.DC.Scripts.Bindings import Unauthorized, UnauthorizedBinding
   26.27 +from Products.PageTemplates.PageTemplateFile import PageTemplateFile
   26.28 +
   26.29 +from zope.app.pagetemplate.viewpagetemplatefile import ViewMapper
   26.30 +from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile
   26.31 +
   26.32 +from Products.Five.browser.ReuseUtils import rebindFunction
   26.33 +from Products.Five.browser.TrustedExpression import getEngine, ModuleImporter
   26.34 +
   26.35 +class ZopeTwoPageTemplateFile(PageTemplateFile):
   26.36 +    """A strange hybrid between Zope 2 and Zope 3 page template.
   26.37 +
   26.38 +    Uses Zope 2's engine, but with security disabled and with some
   26.39 +    initialization and API from Zope 3.
   26.40 +    """
   26.41 +        
   26.42 +    def __init__(self, filename, _prefix=None, content_type=None):
   26.43 +        # XXX doesn't use content_type yet
   26.44 +        
   26.45 +        self.ZBindings_edit(self._default_bindings)
   26.46 +
   26.47 +        path = self.get_path_from_prefix(_prefix)
   26.48 +        self.filename = os.path.join(path, filename)
   26.49 +        if not os.path.isfile(self.filename):
   26.50 +            raise ValueError("No such file", self.filename)
   26.51 +
   26.52 +        basepath, ext = os.path.splitext(self.filename)
   26.53 +        self.__name__ = os.path.basename(basepath)
   26.54 + 
   26.55 +    def get_path_from_prefix(self, _prefix):
   26.56 +        if isinstance(_prefix, str):
   26.57 +            path = _prefix
   26.58 +        else:
   26.59 +            if _prefix is None:
   26.60 +                _prefix = sys._getframe(2).f_globals
   26.61 +            path = package_home(_prefix)
   26.62 +        return path 
   26.63 +
   26.64 +    _cook = rebindFunction(PageTemplateFile._cook,
   26.65 +                           getEngine=getEngine)
   26.66 +    
   26.67 +    pt_render = rebindFunction(PageTemplateFile.pt_render,
   26.68 +                               getEngine=getEngine)
   26.69 +
   26.70 +    def _pt_getContext(self):
   26.71 +        try:
   26.72 +            root = self.getPhysicalRoot()
   26.73 +            view = self._getContext()
   26.74 +        except AttributeError:
   26.75 +            # self has no attribute getPhysicalRoot. This typically happens 
   26.76 +            # when the template has no proper acquisition context. 
   26.77 +            # That also means it has no view.  /regebro
   26.78 +            root = self.context.getPhysicalRoot()
   26.79 +            view = None
   26.80 +
   26.81 +        here = self.context.aq_inner
   26.82 +
   26.83 +        request = getattr(root, 'REQUEST', None)
   26.84 +        c = {'template': self,
   26.85 +             'here': here,
   26.86 +             'context': here,
   26.87 +             'container': here,
   26.88 +             'nothing': None,
   26.89 +             'options': {},
   26.90 +             'root': root,
   26.91 +             'request': request,
   26.92 +             'modules': ModuleImporter,
   26.93 +             }
   26.94 +        if view:
   26.95 +            c['view'] = view
   26.96 +            c['views'] = ViewMapper(here, request)
   26.97 +
   26.98 +        return c
   26.99 +
  26.100 +    pt_getContext = rebindFunction(_pt_getContext,
  26.101 +                                   SecureModuleImporter=ModuleImporter)
    27.1 new file mode 100644
    27.2 --- /dev/null
    27.3 +++ b/browser/resource.py
    27.4 @@ -0,0 +1,232 @@
    27.5 +##############################################################################
    27.6 +#
    27.7 +# Copyright (c) 2004, 2005 Zope Corporation and Contributors.
    27.8 +# All Rights Reserved.
    27.9 +#
   27.10 +# This software is subject to the provisions of the Zope Public License,
   27.11 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
   27.12 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
   27.13 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   27.14 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
   27.15 +# FOR A PARTICULAR PURPOSE.
   27.16 +#
   27.17 +##############################################################################
   27.18 +"""Provide basic resource functionality
   27.19 +
   27.20 +$Id: resource.py 13268 2005-06-10 14:18:23Z philikon $
   27.21 +"""
   27.22 +import os
   27.23 +import urllib
   27.24 +
   27.25 +from Acquisition import Explicit
   27.26 +from ComputedAttribute import ComputedAttribute
   27.27 +from OFS.Traversable import Traversable as OFSTraversable
   27.28 +
   27.29 +from zope.exceptions import NotFoundError
   27.30 +from zope.interface import implements
   27.31 +from zope.component.interfaces import IResource
   27.32 +from zope.component import getViewProviding
   27.33 +from zope.publisher.interfaces.browser import IBrowserPublisher
   27.34 +from zope.app.traversing.browser.interfaces import IAbsoluteURL
   27.35 +from zope.app.datetimeutils import time as timeFromDateTimeString
   27.36 +from zope.app.publisher.fileresource import File, Image
   27.37 +from zope.app.publisher.pagetemplateresource import PageTemplate
   27.38 +from zope.app.publisher.browser.resources import empty
   27.39 +
   27.40 +from Products.Five.browser import BrowserView
   27.41 +
   27.42 +_marker = []
   27.43 +
   27.44 +class Resource(Explicit):
   27.45 +    """A publishable resource
   27.46 +    """
   27.47 +    implements(IResource)
   27.48 +
   27.49 +    def __init__(self, request):
   27.50 +        self.request = request
   27.51 +
   27.52 +    def __call__(self):
   27.53 +        name = self.__name__
   27.54 +        container = self.__parent__
   27.55 +
   27.56 +        url = str(getViewProviding(container, IAbsoluteURL, self.request))
   27.57 +        url = urllib.unquote(url)
   27.58 +        if not isinstance(container, DirectoryResource):
   27.59 +            name = '++resource++%s' % name
   27.60 +        return "%s/%s" % (url, name)
   27.61 +
   27.62 +class PageTemplateResource(BrowserView, Resource):
   27.63 +    #implements(IBrowserPublisher)
   27.64 +
   27.65 +    def __browser_default__(self, request):
   27.66 +        return self, ('render',)
   27.67 +
   27.68 +    def render(self):
   27.69 +        """Rendered content"""
   27.70 +        pt = self.context
   27.71 +        return pt(self.request)
   27.72 +
   27.73 +class FileResource(BrowserView, Resource):
   27.74 +    """A publishable file-based resource"""
   27.75 +    #implements(IBrowserPublisher)
   27.76 +
   27.77 +    def __browser_default__(self, request):
   27.78 +        return self, (request.REQUEST_METHOD,)
   27.79 +
   27.80 +    def GET(self):
   27.81 +        """Default content"""
   27.82 +        file = self.context
   27.83 +        request = self.request
   27.84 +        response = request.response
   27.85 +
   27.86 +        # HTTP If-Modified-Since header handling. This is duplicated
   27.87 +        # from OFS.Image.Image - it really should be consolidated
   27.88 +        # somewhere...
   27.89 +        header = request.environ.get('If-Modified-Since', None)
   27.90 +        if header is not None:
   27.91 +            header = header.split(';')[0]
   27.92 +            # Some proxies seem to send invalid date strings for this
   27.93 +            # header. If the date string is not valid, we ignore it
   27.94 +            # rather than raise an error to be generally consistent
   27.95 +            # with common servers such as Apache (which can usually
   27.96 +            # understand the screwy date string as a lucky side effect
   27.97 +            # of the way they parse it).
   27.98 +            try:    mod_since=long(timeFromDateTimeString(header))
   27.99 +            except: mod_since=None
  27.100 +            if mod_since is not None:
  27.101 +                if getattr(file, 'lmt', None):
  27.102 +                    last_mod = long(file.lmt)
  27.103 +                else:
  27.104 +                    last_mod = long(0)
  27.105 +                if last_mod > 0 and last_mod <= mod_since:
  27.106 +                    response.setStatus(304)
  27.107 +                    return ''
  27.108 +
  27.109 +        response.setHeader('Content-Type', file.content_type)
  27.110 +        response.setHeader('Last-Modified', file.lmh)
  27.111 +
  27.112 +        # Cache for one day
  27.113 +        response.setHeader('Cache-Control', 'public,max-age=86400')
  27.114 +        f = open(file.path, 'rb')
  27.115 +        data = f.read()
  27.116 +        f.close()
  27.117 +
  27.118 +        return data
  27.119 +
  27.120 +    def HEAD(self):
  27.121 +        file = self.context
  27.122 +        response = self.request.response
  27.123 +        response = self.request.response
  27.124 +        response.setHeader('Content-Type', file.content_type)
  27.125 +        response.setHeader('Last-Modified', file.lmh)
  27.126 +        # Cache for one day
  27.127 +        response.setHeader('Cache-Control', 'public,max-age=86400')
  27.128 +        return ''
  27.129 +
  27.130 +class ResourceFactory:
  27.131 +
  27.132 +    factory = None
  27.133 +    resource = None
  27.134 +
  27.135 +    def __init__(self, name, path, resource_factory=None):
  27.136 +        self.__name = name
  27.137 +        self.__rsrc = self.factory(path, name)
  27.138 +        if resource_factory is not None:
  27.139 +            self.resource = resource_factory
  27.140 +
  27.141 +    def __call__(self, request):
  27.142 +        resource = self.resource(self.__rsrc, request)
  27.143 +        return resource
  27.144 +
  27.145 +def _PageTemplate(self, path, name):
  27.146 +    # PageTemplate doesn't take a name parameter,
  27.147 +    # which makes it different from FileResource.
  27.148 +    # This is probably an error.
  27.149 +    template = PageTemplate(path)
  27.150 +    template.__name__ = name
  27.151 +    return template
  27.152 +
  27.153 +class PageTemplateResourceFactory(ResourceFactory):
  27.154 +    """A factory for Page Template resources"""
  27.155 +
  27.156 +    factory = _PageTemplate
  27.157 +    resource = PageTemplateResource
  27.158 +
  27.159 +class FileResourceFactory(ResourceFactory):
  27.160 +    """A factory for File resources"""
  27.161 +
  27.162 +    factory = File
  27.163 +    resource = FileResource
  27.164 +
  27.165 +class ImageResourceFactory(ResourceFactory):
  27.166 +    """A factory for Image resources"""
  27.167 +
  27.168 +    factory = Image
  27.169 +    resource = FileResource
  27.170 +
  27.171 +
  27.172 +# we only need this class a context for DirectoryResource
  27.173 +class Directory:
  27.174 +
  27.175 +    def __init__(self, path, name):
  27.176 +        self.path = path
  27.177 +        self.__name__ = name
  27.178 +
  27.179 +class DirectoryResource(BrowserView, Resource, OFSTraversable):
  27.180 +    #implements(IBrowserPublisher)
  27.181 +
  27.182 +    resource_factories = {
  27.183 +        'gif':  ImageResourceFactory,
  27.184 +        'png':  ImageResourceFactory,
  27.185 +        'jpg':  ImageResourceFactory,
  27.186 +        'pt':   PageTemplateResourceFactory,
  27.187 +        'zpt':  PageTemplateResourceFactory,
  27.188 +        'html': PageTemplateResourceFactory,
  27.189 +        }
  27.190 +
  27.191 +    default_factory = FileResourceFactory
  27.192 +
  27.193 +    def __init__(self, context, request):
  27.194 +        BrowserView.__init__(self, context, request)
  27.195 +        # OFSTraversable.absolute_url() assumes self.REQUEST being
  27.196 +        # accessible:
  27.197 +        self.REQUEST = request
  27.198 +
  27.199 +    def getId(self):
  27.200 +        name = self.__name__
  27.201 +        if not name.startswith('++resource++'):
  27.202 +            name = '++resource++%s' % self.__name__
  27.203 +        return name
  27.204 +
  27.205 +    def __browser_default__(self, request):
  27.206 +        '''See interface IBrowserPublisher'''
  27.207 +        return empty, ()
  27.208 +
  27.209 +    def __getitem__(self, name):
  27.210 +        res = self.get(name, None)
  27.211 +        if res is None:
  27.212 +            raise KeyError, name
  27.213 +        return res
  27.214 +
  27.215 +    def get(self, name, default=_marker):
  27.216 +        path = self.context.path
  27.217 +        filename = os.path.join(path, name)
  27.218 +        if not os.path.isfile(filename):
  27.219 +            if default is _marker:
  27.220 +                raise NotFoundError(name)
  27.221 +            return default
  27.222 +        ext = name.split('.')[-1]
  27.223 +        factory = self.resource_factories.get(ext, self.default_factory)
  27.224 +        resource = factory(name, filename)(self.request)
  27.225 +        resource.__name__ = name
  27.226 +        resource.__parent__ = self
  27.227 +        # XXX __of__ wrapping is usually done on traversal.
  27.228 +        # However, we don't want to subclass Traversable (or do we?)
  27.229 +        # The right thing should probably be a specific (and very simple)
  27.230 +        # traverser that does __getitem__ and __of__.
  27.231 +        return resource.__of__(self)
  27.232 +
  27.233 +class DirectoryResourceFactory(ResourceFactory):
  27.234 +
  27.235 +    factory = Directory
  27.236 +    resource = DirectoryResource
    28.1 new file mode 100644
    28.2 --- /dev/null
    28.3 +++ b/browser/tests/__init__.py
    28.4 @@ -0,0 +1,1 @@
    28.5 +# import this
    29.1 new file mode 100644
    29.2 --- /dev/null
    29.3 +++ b/browser/tests/adding.txt
    29.4 @@ -0,0 +1,53 @@
    29.5 +============
    29.6 +Adding tests
    29.7 +============
    29.8 +
    29.9 +ObjectManagerNameChooser
   29.10 +------------------------
   29.11 +
   29.12 +First we need to import and setup some prerequisites:
   29.13 +
   29.14 +  >>> from Products.Five.tests.testing import manage_addFiveTraversableFolder
   29.15 +  >>> from Products.Five.browser.adding import ObjectManagerNameChooser
   29.16 +
   29.17 +  >>> manage_addFiveTraversableFolder(self.folder, 'testoid', 'Testoid')
   29.18 +  >>> chooser = ObjectManagerNameChooser(self.folder)
   29.19 +
   29.20 +Now we can start.  ``INameChooser`` defines a ``checkName()`` method
   29.21 +that checks whether a given name is valid in the container or not.
   29.22 +Under the hood, ``ObjectManagerNameChooser`` calls ``_checkId()`` of
   29.23 +the object manager.  Valid names/ids are those that aren't in use yet
   29.24 +and don't contain invalid characters.
   29.25 +
   29.26 +  >>> chooser.checkName('abc', object())
   29.27 +
   29.28 +  >>> chooser.checkName('testoid', object())
   29.29 +  Traceback (most recent call last):
   29.30 +  ...
   29.31 +  UserError: The id "testoid" is invalid - it is already in use.
   29.32 +
   29.33 +  >>> chooser.checkName('slash/slash', object())
   29.34 +  Traceback (most recent call last):
   29.35 +  ...
   29.36 +  UserError: The id "slash/slash" contains characters illegal in URLs.
   29.37 +
   29.38 +``INameChooser`` also promises us a ``chooseName()`` method that
   29.39 +chooses a name for us in case we don't have one or that chooses a
   29.40 +different name in case the one we chose was invalid.
   29.41 +
   29.42 +  >>> chooser.chooseName('', self.folder.testoid)
   29.43 +  'FiveTraversableFolder'
   29.44 +
   29.45 +  >>> chooser.chooseName('abc', self.folder.testoid)
   29.46 +  'abc'
   29.47 +
   29.48 +  >>> chooser.chooseName('testoid', self.folder.testoid)
   29.49 +  'testoid-1'
   29.50 +
   29.51 +Of course, if we start out with something bad, it isn't going to
   29.52 +become good automagically:
   29.53 +
   29.54 +  >>> chooser.chooseName('slash/slash', object())
   29.55 +  Traceback (most recent call last):
   29.56 +  ...
   29.57 +  UserError: The id "slash/slash" contains characters illegal in URLs.
    30.1 new file mode 100644
    30.2 --- /dev/null
    30.3 +++ b/browser/tests/birdmacro.pt
    30.4 @@ -0,0 +1,1 @@
    30.5 +<html metal:define-macro="birdmacro"><head><title>bird macro</title></head><body>Color: <metal:block define-slot="color" /><metal:block define-slot="birdinfo" /></body></html>
    31.1 new file mode 100644
    31.2 --- /dev/null
    31.3 +++ b/browser/tests/cockatiel.pt
    31.4 @@ -0,0 +1,2 @@
    31.5 +<p>Have you ever seen a cockatiel?</p>
    31.6 +<p tal:content="string:maybe">dunno</p>
    32.1 new file mode 100644
    32.2 --- /dev/null
    32.3 +++ b/browser/tests/condor.pt
    32.4 @@ -0,0 +1,3 @@
    32.5 +<p tal:content="context/mymethod">Alpha</p>
    32.6 +<p tal:content="view/eagle">Beta</p>
    32.7 +<div tal:replace="structure context/@@flamingo.html">Gamma</div>
    32.8 \ No newline at end of file
    33.1 new file mode 100644
    33.2 --- /dev/null
    33.3 +++ b/browser/tests/cps_test_localizer.pt
    33.4 @@ -0,0 +1,7 @@
    33.5 +<html>
    33.6 +<body>
    33.7 +<!-- fivetest is a Zope 3 style i18n domain, default is a Localizer domain -->
    33.8 +<p i18n:domain="fivetest" i18n:translate="">This is a message</p>
    33.9 +<p i18n:domain="default" i18n:translate="">Object actions</p>
   33.10 +</body>
   33.11 +</html>
    34.1 new file mode 100644
    34.2 --- /dev/null
    34.3 +++ b/browser/tests/cps_test_localizer.py
    34.4 @@ -0,0 +1,61 @@
    34.5 +##############################################################################
    34.6 +#
    34.7 +# Copyright (c) 2005 Zope Corporation and Contributors.
    34.8 +# All Rights Reserved.
    34.9 +#
   34.10 +# This software is subject to the provisions of the Zope Public License,
   34.11 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
   34.12 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
   34.13 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   34.14 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
   34.15 +# FOR A PARTICULAR PURPOSE.
   34.16 +#
   34.17 +##############################################################################
   34.18 +"""Test the Localizer language integration for CPS.  This test
   34.19 +requires a full blown CPS installation to run.  It is therefore
   34.20 +prefixed with ``cps_`` so it won't be picked up by the test runner.
   34.21 +
   34.22 +$Id: cps_test_localizer.py 14400 2005-07-07 17:55:08Z philikon $
   34.23 +"""
   34.24 +import os, sys
   34.25 +if __name__ == '__main__':
   34.26 +    execfile(os.path.join(sys.path[0], 'framework.py'))
   34.27 +
   34.28 +def test_suite():
   34.29 +    from Testing.ZopeTestCase import installProduct, FunctionalDocFileSuite
   34.30 +    installProduct('Five')
   34.31 +    installProduct('BTreeFolder2')
   34.32 +    installProduct('CMFCalendar')
   34.33 +    installProduct('CMFCore')
   34.34 +    installProduct('CMFDefault')
   34.35 +    installProduct('CMFTopic')
   34.36 +    installProduct('DCWorkflow')
   34.37 +    installProduct('Localizer')
   34.38 +    installProduct('MailHost')
   34.39 +    installProduct('CPSCore')
   34.40 +    installProduct('CPSDefault')
   34.41 +    installProduct('CPSDirectory')
   34.42 +    installProduct('CPSUserFolder')
   34.43 +    installProduct('TranslationService')
   34.44 +    installProduct('SiteAccess')
   34.45 +    # these products should (and used to be) be optional, but they
   34.46 +    # aren't right now.
   34.47 +    installProduct('CPSForum')
   34.48 +    installProduct('CPSSubscriptions')
   34.49 +    installProduct('CPSNewsLetters')
   34.50 +    installProduct('CPSSchemas')
   34.51 +    installProduct('CPSDocument')
   34.52 +    installProduct('PortalTransforms')
   34.53 +    installProduct('Epoz')
   34.54 +    # optional products, but apparently still needed...
   34.55 +    installProduct('CPSRSS')
   34.56 +    installProduct('CPSChat')
   34.57 +    installProduct('CPSCalendar')
   34.58 +    installProduct('CPSCollector')
   34.59 +    installProduct('CPSMailBoxer')
   34.60 +
   34.61 +    return FunctionalDocFileSuite('cps_test_localizer.txt',
   34.62 +                                  package='Products.Five.browser.tests')
   34.63 +
   34.64 +if __name__ == '__main__':
   34.65 +    framework()
    35.1 new file mode 100644
    35.2 --- /dev/null
    35.3 +++ b/browser/tests/cps_test_localizer.txt
    35.4 @@ -0,0 +1,115 @@
    35.5 +Localizer languages
    35.6 +===================
    35.7 +
    35.8 +Before we start, we need to set up a manager user to be able to create
    35.9 +the portal:
   35.10 +
   35.11 +  >>> uf = self.folder.acl_users
   35.12 +  >>> uf._doAddUser('manager', 'r00t', ['Manager'], [])
   35.13 +
   35.14 +We need to 1) configure the Zope 3 i18n message catalogs, 2) make the
   35.15 +CPS portal traversable, 3) register the Localizer languagees adapter
   35.16 +and 4) register our test page:
   35.17 +
   35.18 +  >>> configure_zcml = """
   35.19 +  ... <configure 
   35.20 +  ...     xmlns="http://namespaces.zope.org/zope"
   35.21 +  ...     xmlns:browser="http://namespaces.zope.org/browser"
   35.22 +  ...     xmlns:five="http://namespaces.zope.org/five"
   35.23 +  ...     xmlns:i18n="http://namespaces.zope.org/i18n"
   35.24 +  ...     >
   35.25 +  ...   <configure package="Products.Five.tests">
   35.26 +  ...     <i18n:registerTranslations directory="locales" />
   35.27 +  ...   </configure>
   35.28 +  ... 
   35.29 +  ...   <five:traversable class="Products.CPSDefault.Portal.CPSDefaultSite" />
   35.30 +  ... 
   35.31 +  ...   <adapter
   35.32 +  ...       for="zope.publisher.interfaces.http.IHTTPRequest"
   35.33 +  ...       provides="zope.i18n.interfaces.IUserPreferredLanguages"
   35.34 +  ...       factory="Products.Five.i18n.LocalizerLanguages"
   35.35 +  ...       />
   35.36 +  ... 
   35.37 +  ...   <configure package="Products.Five.browser.tests">
   35.38 +  ...     <browser:page
   35.39 +  ...         for="*"
   35.40 +  ...         template="cps_test_localizer.pt"
   35.41 +  ...         name="cps_test_localizer.html"
   35.42 +  ...         permission="zope2.View"
   35.43 +  ...         />
   35.44 +  ...   </configure>
   35.45 +  ... </configure>
   35.46 +  ... """
   35.47 +  >>> from Products.Five import zcml
   35.48 +  >>> zcml.load_string(configure_zcml)
   35.49 +
   35.50 +Create a CPS portal.  We print an additional line before creating it
   35.51 +because PortalTransforms might print stuff to stdout and doctest
   35.52 +doesn't allow us to ellide a first line.
   35.53 +
   35.54 +  >>> print "Ignore lines after me"; print http(r"""
   35.55 +  ... POST /test_folder_1_/manage_addProduct/CPSDefault/manage_addCPSDefaultSite HTTP/1.1
   35.56 +  ... Authorization: Basic manager:r00t
   35.57 +  ... Content-Length: 269
   35.58 +  ... Content-Type: application/x-www-form-urlencoded
   35.59 +  ... 
   35.60 +  ... id=cps&title=CPS+Portal&description=&manager_id=manager&manager_sn=CPS+manager&manager_givenName=Manager&manager_email=root%40localhost&manager_password=root&manager_password_confirmation=root&langs_list%3Alist=en&langs_list%3Alist=fr&langs_list%3Alist=de&submit=Create""")
   35.61 +  Ignore lines after me
   35.62 +  ...
   35.63 +  HTTP/1.1 200 OK
   35.64 +  ...
   35.65 +
   35.66 +Now for some actual testing... Our test page is a simple ZPT
   35.67 +translating two messages from different domains.  The first domain is
   35.68 +a Zope 3 style one, the second one comes from Localizer.
   35.69 +
   35.70 +Both systems should yield the same default language (English) when no
   35.71 +language is specified whatsoever:
   35.72 +
   35.73 +  >>> print http(r"""
   35.74 +  ... GET /test_folder_1_/cps/cps_test_localizer.html HTTP/1.1
   35.75 +  ... """)
   35.76 +  HTTP/1.1 200 OK
   35.77 +  ...
   35.78 +  <html>
   35.79 +  <body>
   35.80 +  <!-- fivetest is a Zope 3 style i18n domain, default is a Localizer domain -->
   35.81 +  <p>This is a message</p>
   35.82 +  <p>Object actions</p>
   35.83 +  </body>
   35.84 +  </html>
   35.85 +
   35.86 +Both systems should honour the HTTP ``Accept-Language`` header in the
   35.87 +same way:
   35.88 +
   35.89 +  >>> print http(r"""
   35.90 +  ... GET /test_folder_1_/cps/cps_test_localizer.html HTTP/1.1
   35.91 +  ... Accept-Language: de
   35.92 +  ... """)
   35.93 +  HTTP/1.1 200 OK
   35.94 +  ...
   35.95 +  <html>
   35.96 +  <body>
   35.97 +  <!-- fivetest is a Zope 3 style i18n domain, default is a Localizer domain -->
   35.98 +  <p>Dies ist eine Nachricht</p>
   35.99 +  <p>Objekt Aktionen</p>
  35.100 +  </body>
  35.101 +  </html>
  35.102 +
  35.103 +Both systems should also honour Localizer-specific ways of determining
  35.104 +the language, for example the ``LOCALIZER_LANGUAGE`` cookie:
  35.105 +
  35.106 +  >>> print http(r"""
  35.107 +  ... GET /test_folder_1_/cps/cps_test_localizer.html HTTP/1.1
  35.108 +  ... Accept-Language: de
  35.109 +  ... Cookie: LOCALIZER_LANGUAGE=en
  35.110 +  ... """)
  35.111 +  HTTP/1.1 200 OK
  35.112 +  ...
  35.113 +  <html>
  35.114 +  <body>
  35.115 +  <!-- fivetest is a Zope 3 style i18n domain, default is a Localizer domain -->
  35.116 +  <p>This is a message</p>
  35.117 +  <p>Object actions</p>
  35.118 +  </body>
  35.119 +  </html>
    36.1 new file mode 100644
    36.2 --- /dev/null
    36.3 +++ b/browser/tests/defaultview.zcml
    36.4 @@ -0,0 +1,38 @@
    36.5 +<configure xmlns="http://namespaces.zope.org/zope"
    36.6 +           xmlns:browser="http://namespaces.zope.org/browser"
    36.7 +           xmlns:five="http://namespaces.zope.org/five">
    36.8 +
    36.9 +  <five:defaultViewable
   36.10 +      class="Products.Five.tests.testing.simplecontent.SimpleContent" />
   36.11 +
   36.12 +  <browser:defaultView
   36.13 +      for="Products.Five.tests.testing.simplecontent.ISimpleContent"
   36.14 +      name="eagledefaultview.txt"
   36.15 +      />
   36.16 +
   36.17 +  <browser:page
   36.18 +      for="Products.Five.tests.testing.simplecontent.ISimpleContent"
   36.19 +      name="eagledefaultview.txt"
   36.20 +      class=".pages.SimpleView"
   36.21 +      attribute="eagle"
   36.22 +      permission="zope2.Public"
   36.23 +      />
   36.24 +
   36.25 +  <!-- this tests whether five:defaultViewable can be called on a class that
   36.26 +       already provides __call__, such as our CallableSimpleContent -->
   36.27 +
   36.28 +  <five:defaultViewable
   36.29 +      class="Products.Five.tests.testing.simplecontent.CallableSimpleContent" />
   36.30 +
   36.31 +  <!-- this tests whether five:defaultViewable can be called on a class that
   36.32 +       already provides index_html, such as our IndexSimpleContent -->
   36.33 +
   36.34 +  <five:defaultViewable
   36.35 +      class="Products.Five.tests.testing.simplecontent.IndexSimpleContent" />
   36.36 +
   36.37 +  <browser:defaultView
   36.38 +      for="Products.Five.tests.testing.simplecontent.IIndexSimpleContent"
   36.39 +      name="index_html"
   36.40 +      />
   36.41 +
   36.42 +</configure>
    37.1 new file mode 100644
    37.2 --- /dev/null
    37.3 +++ b/browser/tests/falcon.pt
    37.4 @@ -0,0 +1,1 @@
    37.5 +<p>The falcon has taken flight</p>
    37.6 \ No newline at end of file
    38.1 new file mode 100644
    38.2 --- /dev/null
    38.3 +++ b/browser/tests/flamingo.pt
    38.4 @@ -0,0 +1,2 @@
    38.5 +<p tal:content="context/mymethod">Replaced</p>
    38.6 +<p tal:content="python:context.mymethod()">Replaced</p>
    39.1 new file mode 100644
    39.2 --- /dev/null
    39.3 +++ b/browser/tests/framework.py
    39.4 @@ -0,0 +1,107 @@
    39.5 +##############################################################################
    39.6 +#
    39.7 +# ZopeTestCase 
    39.8 +#
    39.9 +# COPY THIS FILE TO YOUR 'tests' DIRECTORY.
   39.10 +#
   39.11 +# This version of framework.py will use the SOFTWARE_HOME
   39.12 +# environment variable to locate Zope and the Testing package.
   39.13 +#
   39.14 +# If the tests are run in an INSTANCE_HOME installation of Zope,
   39.15 +# Products.__path__ and sys.path with be adjusted to include the
   39.16 +# instance's Products and lib/python directories respectively.
   39.17 +#
   39.18 +# If you explicitly set INSTANCE_HOME prior to running the tests,
   39.19 +# auto-detection is disabled and the specified path will be used 
   39.20 +# instead.
   39.21 +#
   39.22 +# If the 'tests' directory contains a custom_zodb.py file, INSTANCE_HOME
   39.23 +# will be adjusted to use it.
   39.24 +#
   39.25 +# If you set the ZEO_INSTANCE_HOME environment variable a ZEO setup 
   39.26 +# is assumed, and you can attach to a running ZEO server (via the 
   39.27 +# instance's custom_zodb.py).
   39.28 +#
   39.29 +##############################################################################
   39.30 +#
   39.31 +# The following code should be at the top of every test module:
   39.32 +#
   39.33 +# import os, sys
   39.34 +# if __name__ == '__main__':
   39.35 +#     execfile(os.path.join(sys.path[0], 'framework.py'))
   39.36 +#
   39.37 +# ...and the following at the bottom:
   39.38 +#
   39.39 +# if __name__ == '__main__':
   39.40 +#     framework()
   39.41 +#
   39.42 +##############################################################################
   39.43 +
   39.44 +__version__ = '0.2.3'
   39.45 +
   39.46 +# Save start state
   39.47 +#
   39.48 +__SOFTWARE_HOME = os.environ.get('SOFTWARE_HOME', '')
   39.49 +__INSTANCE_HOME = os.environ.get('INSTANCE_HOME', '')
   39.50 +
   39.51 +if __SOFTWARE_HOME.endswith(os.sep):
   39.52 +    __SOFTWARE_HOME = os.path.dirname(__SOFTWARE_HOME)
   39.53 +
   39.54 +if __INSTANCE_HOME.endswith(os.sep):
   39.55 +    __INSTANCE_HOME = os.path.dirname(__INSTANCE_HOME)
   39.56 +
   39.57 +# Find and import the Testing package
   39.58 +#
   39.59 +if not sys.modules.has_key('Testing'):
   39.60 +    p0 = sys.path[0]
   39.61 +    if p0 and __name__ == '__main__':
   39.62 +        os.chdir(p0)
   39.63 +        p0 = ''
   39.64 +    s = __SOFTWARE_HOME
   39.65 +    p = d = s and s or os.getcwd()
   39.66 +    while d:
   39.67 +        if os.path.isdir(os.path.join(p, 'Testing')):
   39.68 +            zope_home = os.path.dirname(os.path.dirname(p))
   39.69 +            sys.path[:1] = [p0, p, zope_home]
   39.70 +            break
   39.71 +        p, d = s and ('','') or os.path.split(p)
   39.72 +    else:
   39.73 +        print 'Unable to locate Testing package.',
   39.74 +        print 'You might need to set SOFTWARE_HOME.'
   39.75 +        sys.exit(1)
   39.76 +
   39.77 +import Testing, unittest
   39.78 +execfile(os.path.join(os.path.dirname(Testing.__file__), 'common.py'))
   39.79 +
   39.80 +# Include ZopeTestCase support
   39.81 +#
   39.82 +if 1:   # Create a new scope
   39.83 +
   39.84 +    p = os.path.join(os.path.dirname(Testing.__file__), 'ZopeTestCase')
   39.85 +
   39.86 +    if not os.path.isdir(p):
   39.87 +        print 'Unable to locate ZopeTestCase package.',
   39.88 +        print 'You might need to install ZopeTestCase.'
   39.89 +        sys.exit(1)
   39.90 +
   39.91 +    ztc_common = 'ztc_common.py'
   39.92 +    ztc_common_global = os.path.join(p, ztc_common)
   39.93 +
   39.94 +    f = 0
   39.95 +    if os.path.exists(ztc_common_global):
   39.96 +        execfile(ztc_common_global)
   39.97 +        f = 1
   39.98 +    if os.path.exists(ztc_common):
   39.99 +        execfile(ztc_common)
  39.100 +        f = 1
  39.101 +
  39.102 +    if not f:
  39.103 +        print 'Unable to locate %s.' % ztc_common
  39.104 +        sys.exit(1)
  39.105 +
  39.106 +# Debug
  39.107 +#
  39.108 +print 'SOFTWARE_HOME: %s' % os.environ.get('SOFTWARE_HOME', 'Not set')
  39.109 +print 'INSTANCE_HOME: %s' % os.environ.get('INSTANCE_HOME', 'Not set')
  39.110 +sys.stdout.flush()
  39.111 +
    40.1 new file mode 100644
    40.2 --- /dev/null
    40.3 +++ b/browser/tests/i18n.pt
    40.4 @@ -0,0 +1,12 @@
    40.5 +<html i18n:domain="fivetest">
    40.6 +<body>
    40.7 +<p i18n:translate="">This is a message</p>
    40.8 +<p i18n:translate="explicit-msg">This is an explicit message</p>
    40.9 +<p i18n:translate="">These are <span tal:replace="python:4" i18n:name="number" /> messages</p>
   40.10 +<p i18n:translate="explicit-msgs">These are <span tal:replace="python:4" i18n:name="number" /> explicit messages</p>
   40.11 +<table summary="This is an attribute" i18n:attributes="summary">
   40.12 +</table>
   40.13 +<table summary="Explicit summary" title="Explicit title" i18n:attributes="summary explicit-summary; title explicit-title" >
   40.14 +</table>
   40.15 +</body>
   40.16 +</html>
    41.1 new file mode 100644
    41.2 --- /dev/null
    41.3 +++ b/browser/tests/menu.zcml
    41.4 @@ -0,0 +1,79 @@
    41.5 +<configure xmlns="http://namespaces.zope.org/zope"
    41.6 +           xmlns:meta="http://namespaces.zope.org/meta"
    41.7 +           xmlns:browser="http://namespaces.zope.org/browser"
    41.8 +           i18n_domain="fivetest">
    41.9 +
   41.10 +  <!-- make the zope2.Public permission work -->
   41.11 +  <meta:redefinePermission from="zope2.Public" to="zope.Public" />
   41.12 +
   41.13 +  <!-- browser menu support -->
   41.14 +  <browser:menu
   41.15 +      id="testmenu"
   41.16 +      title="Test menu"
   41.17 +      />
   41.18 +
   41.19 +  <browser:menuItem
   41.20 +      for="OFS.interfaces.IFolder"
   41.21 +      menu="testmenu"
   41.22 +      title="Test Menu Item"
   41.23 +      action="seagull.html"
   41.24 +      description="This is a test menu item"
   41.25 +      permission="zope2.Public"
   41.26 +      />
   41.27 +
   41.28 +  <browser:menuItem
   41.29 +      for="OFS.interfaces.IFolder"
   41.30 +      menu="testmenu"
   41.31 +      title="Protected Test Menu Item"
   41.32 +      action="seagull.html"
   41.33 +      description="This is a protected test menu item"
   41.34 +      permission="zope2.ViewManagementScreens"
   41.35 +      />
   41.36 +
   41.37 +  <browser:menuItems
   41.38 +      for="OFS.interfaces.IFolder"
   41.39 +      menu="testmenu">
   41.40 +
   41.41 +      <menuItem
   41.42 +          title="Test Menu Item 2"
   41.43 +          action="parakeet.html"
   41.44 +          description="This is a test menu item"
   41.45 +          permission="zope2.Public"
   41.46 +          />
   41.47 +
   41.48 +      <menuItem
   41.49 +          title="Test Menu Item 3"
   41.50 +          action="falcon.html"
   41.51 +          description="This is a test menu item"
   41.52 +          permission="zope2.Public"
   41.53 +          />
   41.54 +
   41.55 +      <menuItem
   41.56 +          title="Protected Test Menu Item 2"
   41.57 +          action="falcon.html"
   41.58 +          description="This is a protected test menu item"
   41.59 +          permission="zope2.ViewManagementScreens"
   41.60 +          />
   41.61 +
   41.62 +  </browser:menuItems>
   41.63 +
   41.64 +  <!-- page in a menu -->
   41.65 +  <browser:page
   41.66 +      for="OFS.interfaces.IFolder"
   41.67 +      template="cockatiel.pt"
   41.68 +      name="cockatiel_menu_public.html"
   41.69 +      permission="zope2.Public"
   41.70 +      title="Page in a menu (public)"
   41.71 +      menu="testmenu"
   41.72 +      />
   41.73 +
   41.74 +  <browser:page
   41.75 +      for="OFS.interfaces.IFolder"
   41.76 +      template="cockatiel.pt"
   41.77 +      name="cockatiel_menu_protected.html"
   41.78 +      permission="zope2.ViewManagementScreens"
   41.79 +      title="Page in a menu (protected)"
   41.80 +      menu="testmenu"
   41.81 +      />
   41.82 +
   41.83 +</configure>
   41.84 \ No newline at end of file
    42.1 new file mode 100644
    42.2 --- /dev/null
    42.3 +++ b/browser/tests/ostrich.pt
    42.4 @@ -0,0 +1,6 @@
    42.5 +<ul>
    42.6 +<li tal:repeat="item python:['Alpha', 'Beta', 'Gamma']" tal:content="item"/>
    42.7 +</ul>
    42.8 +<ul>
    42.9 +<li tal:repeat="item python:['Alpha', 'Beta', 'Gamma']" tal:content="python:repeat['item'].index"/>
   42.10 +</ul>
    43.1 new file mode 100644
    43.2 --- /dev/null
    43.3 +++ b/browser/tests/overrides.zcml
    43.4 @@ -0,0 +1,13 @@
    43.5 +<configure xmlns="http://namespaces.zope.org/zope"
    43.6 +           xmlns:browser="http://namespaces.zope.org/browser">
    43.7 +
    43.8 +  <!-- mouse instead of eagle -->
    43.9 +  <browser:page
   43.10 +    for="Products.Five.tests.testing.simplecontent.ISimpleContent"
   43.11 +    class=".pages.SimpleView"
   43.12 +    attribute="mouse"
   43.13 +    name="overridden_view"
   43.14 +    permission="zope2.Public"
   43.15 +    />
   43.16 +
   43.17 +</configure>
    44.1 new file mode 100644
    44.2 --- /dev/null
    44.3 +++ b/browser/tests/owl.pt
    44.4 @@ -0,0 +1,1 @@
    44.5 +<p tal:content="python:1+1">Some content</p>
    44.6 \ No newline at end of file
    45.1 new file mode 100644
    45.2 --- /dev/null
    45.3 +++ b/browser/tests/pages.py
    45.4 @@ -0,0 +1,67 @@
    45.5 +##############################################################################
    45.6 +#
    45.7 +# Copyright (c) 2004, 2005 Zope Corporation and Contributors.
    45.8 +# All Rights Reserved.
    45.9 +#
   45.10 +# This software is subject to the provisions of the Zope Public License,
   45.11 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
   45.12 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
   45.13 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   45.14 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
   45.15 +# FOR A PARTICULAR PURPOSE.
   45.16 +#
   45.17 +##############################################################################
   45.18 +"""Test browser pages
   45.19 +
   45.20 +$Id: pages.py 12884 2005-05-30 13:10:41Z philikon $
   45.21 +"""
   45.22 +from Products.Five import BrowserView
   45.23 +
   45.24 +class SimpleView(BrowserView):
   45.25 +    """More docstring. Please Zope"""
   45.26 +
   45.27 +    def eagle(self):
   45.28 +        """Docstring"""
   45.29 +        return "The eagle has landed"
   45.30 +
   45.31 +    def mouse(self):
   45.32 +        """Docstring"""
   45.33 +        return "The mouse has been eaten by the eagle"
   45.34 +
   45.35 +class FancyView(BrowserView):
   45.36 +    """Fancy, fancy stuff"""
   45.37 +
   45.38 +    def view(self):
   45.39 +        return "Fancy, fancy"
   45.40 +
   45.41 +class CallableNoDocstring:
   45.42 +
   45.43 +    def __call__(self):
   45.44 +        return "No docstring"
   45.45 +
   45.46 +def function_no_docstring(self):
   45.47 +    return "No docstring"
   45.48 +
   45.49 +class NoDocstringView(BrowserView):
   45.50 +
   45.51 +    def method(self):
   45.52 +        return "No docstring"
   45.53 +
   45.54 +    function = function_no_docstring
   45.55 +
   45.56 +    object = CallableNoDocstring()
   45.57 +
   45.58 +class NewStyleClass(object):
   45.59 +    """
   45.60 +    This is a testclass to verify that new style classes are ignored
   45.61 +    in browser:page
   45.62 +    """
   45.63 +
   45.64 +    def __init__(self, context, request):
   45.65 +        """Docstring"""
   45.66 +        self.context = context
   45.67 +        self.request = request
   45.68 +
   45.69 +    def method(self):
   45.70 +        """Docstring"""
   45.71 +        return
    46.1 new file mode 100644
    46.2 --- /dev/null
    46.3 +++ b/browser/tests/pages.txt
    46.4 @@ -0,0 +1,316 @@
    46.5 +Test browser pages
    46.6 +==================
    46.7 +
    46.8 +Let's register a quite large amount of test pages:
    46.9 +
   46.10 +  >>> import Products.Five.browser.tests
   46.11 +  >>> from Products.Five import zcml
   46.12 +  >>> zcml.load_config("configure.zcml", Products.Five)
   46.13 +  >>> zcml.load_config('pages.zcml', package=Products.Five.browser.tests)
   46.14 +
   46.15 +Let's add a test object that we view most of the pages off of:
   46.16 +
   46.17 +  >>> from Products.Five.tests.testing.simplecontent import manage_addSimpleContent
   46.18 +  >>> manage_addSimpleContent(self.folder, 'testoid', 'Testoid')
   46.19 +
   46.20 +We also need to create a stub user account and login; otherwise we
   46.21 +wouldn't have all the rights to do traversal etc.:
   46.22 +
   46.23 +  >>> uf = self.folder.acl_users
   46.24 +  >>> uf._doAddUser('manager', 'r00t', ['Manager'], [])
   46.25 +  >>> self.login('manager')
   46.26 +
   46.27 +Now for some actual testing...
   46.28 +
   46.29 +
   46.30 +Simple pages
   46.31 +------------
   46.32 +
   46.33 +A browser page that is a view class's attribute (method):
   46.34 +
   46.35 +  >>> view = self.folder.unrestrictedTraverse('testoid/eagle.txt')
   46.36 +  >>> view is not None
   46.37 +  True
   46.38 +  >>> from Products.Five.browser.tests.pages import SimpleView
   46.39 +  >>> isinstance(view, SimpleView)
   46.40 +  True
   46.41 +  >>> view()
   46.42 +  'The eagle has landed'
   46.43 +
   46.44 +A browser page that is a Page Template.
   46.45 +
   46.46 +  >>> view = self.folder.unrestrictedTraverse('testoid/owl.html')
   46.47 +  >>> view()
   46.48 +  '<p>2</p>\n'
   46.49 +
   46.50 +A browser page that is a PageTemplate plus a view class:
   46.51 +
   46.52 +  >>> view = self.folder.unrestrictedTraverse('testoid/falcon.html')
   46.53 +  >>> isinstance(view, SimpleView)
   46.54 +  True
   46.55 +  >>> view()
   46.56 +  '<p>The falcon has taken flight</p>\n'
   46.57 +
   46.58 +Test pages that have been registered through the cumulative
   46.59 +<browser:pages> directive:
   46.60 +
   46.61 +  >>> view = self.folder.unrestrictedTraverse('testoid/eagle-page.txt')
   46.62 +  >>> isinstance(view, SimpleView)
   46.63 +  True
   46.64 +  >>> view()
   46.65 +  'The eagle has landed'
   46.66 +
   46.67 +  >>> view = self.folder.unrestrictedTraverse('testoid/mouse-page.txt')
   46.68 +  >>> isinstance(view, SimpleView)
   46.69 +  True
   46.70 +  >>> view()
   46.71 +  'The mouse has been eaten by the eagle'
   46.72 +
   46.73 +Zope 2 objects always need a docstring in order to be published.  Five
   46.74 +adds a docstring automatically if a view method doesn't have it, but
   46.75 +it shouldn't modify existing ones:
   46.76 +
   46.77 +  >>> view = self.folder.unrestrictedTraverse('testoid/eagle.txt')
   46.78 +  >>> view.eagle.__doc__ == SimpleView.eagle.__doc__
   46.79 +  True
   46.80 +
   46.81 +Test whether new-style classes are ignored when registering browser
   46.82 +pages with view classes.  When traversing for a non-existing view, we
   46.83 +should get an AttributeError:
   46.84 +
   46.85 +  >>> self.folder.unrestrictedTraverse('testoid/@@new_style_class')
   46.86 +  Traceback (most recent call last):
   46.87 +  ...
   46.88 +  AttributeError: @@new_style_class
   46.89 +
   46.90 +
   46.91 +ZPT-based browser pages
   46.92 +-----------------------
   46.93 +
   46.94 +Test access to ``context`` from ZPTs:
   46.95 +
   46.96 +  >>> view = self.folder.unrestrictedTraverse('testoid/flamingo.html')
   46.97 +  >>> print view()
   46.98 +  <p>Hello world</p>
   46.99 +  <p>Hello world</p>
  46.100 +
  46.101 +Test macro access from ZPT pages:
  46.102 +
  46.103 +  >>> view = self.folder.unrestrictedTraverse('testoid/seagull.html')
  46.104 +  >>> view()
  46.105 +  '<html><head><title>bird macro</title></head><body>Color: gray</body></html>\n'
  46.106 +
  46.107 +Test whether old-style direct traversal still works with a
  46.108 +five:traversable class:
  46.109 +
  46.110 +  >>> old_view = self.folder.unrestrictedTraverse('testoid/direct')
  46.111 +  >>> old_view()
  46.112 +  'Direct traversal worked'
  46.113 +
  46.114 +test_zpt_things:
  46.115 +
  46.116 +  >>> view = self.folder.unrestrictedTraverse('testoid/condor.html')
  46.117 +  >>> print view()
  46.118 +  <p>Hello world</p>
  46.119 +  <p>The eagle has landed</p>
  46.120 +  <p>Hello world</p>
  46.121 +  <p>Hello world</p>
  46.122 +
  46.123 +Make sure that tal:repeat works in ZPT browser pages:
  46.124 +
  46.125 +  >>> view = self.folder.unrestrictedTraverse('testoid/ostrich.html')
  46.126 +  >>> print view()
  46.127 +  <ul>
  46.128 +  <li>Alpha</li>
  46.129 +  <li>Beta</li>
  46.130 +  <li>Gamma</li>
  46.131 +  </ul>
  46.132 +  <ul>
  46.133 +  <li>0</li>
  46.134 +  <li>1</li>
  46.135 +  <li>2</li>
  46.136 +  </ul>
  46.137 +
  46.138 +Test TALES traversal in ZPT pages:
  46.139 +
  46.140 +  >>> view = self.folder.unrestrictedTraverse('testoid/tales_traversal.html')
  46.141 +  >>> print view()
  46.142 +  <p>testoid</p>
  46.143 +  <p>test_folder_1_</p>
  46.144 +
  46.145 +Make sure that global template variables in ZPT pages are correct:
  46.146 +
  46.147 +  >>> view = self.folder.unrestrictedTraverse('testoid/template_variables.html')
  46.148 +  >>> print view()
  46.149 +  View is a view: True
  46.150 +  Context is testoid: True
  46.151 +  Contaxt.aq_parent is test_folder_1_: True
  46.152 +  Container is context: True
  46.153 +  Here is context: True
  46.154 +  Nothing is None: True
  46.155 +  Default works: True
  46.156 +  Root is the application: True
  46.157 +  Template is a template: True
  46.158 +  Traverse_subpath exists and is empty: True
  46.159 +  Request is a request: True
  46.160 +  User is manager: True
  46.161 +  Options exist: True
  46.162 +  Attrs exist: True
  46.163 +  Repeat exists: True
  46.164 +  Loop exists: True
  46.165 +  Modules exists: True
  46.166 +
  46.167 +Make sure that ZPT's aren't a security-less zone.  Let's logout and
  46.168 +try to access some protected stuff.  Let's not forgot to login again,
  46.169 +of course:
  46.170 +
  46.171 +  >>> from AccessControl import allow_module
  46.172 +  >>> allow_module('smtpd')
  46.173 +  >>> self.logout()
  46.174 +  >>> view = self.folder.unrestrictedTraverse('testoid/security.html')
  46.175 +  >>> print view()
  46.176 +  <div>NoneType</div>
  46.177 +  <div>smtpd</div>
  46.178 +  >>> self.login('manager')
  46.179 +
  46.180 +Test pages registered through the <five:pagesFromDirectory /> directive:
  46.181 +
  46.182 +  >>> view = self.folder.unrestrictedTraverse('testoid/dirpage1')
  46.183 +  >>> print view()
  46.184 +  <html>
  46.185 +  <p>This is page 1</p>
  46.186 +  </html>
  46.187 +
  46.188 +  >>> view = self.folder.unrestrictedTraverse('testoid/dirpage2')
  46.189 +  >>> print view()
  46.190 +  <html>
  46.191 +  <p>This is page 2</p>
  46.192 +  </html>
  46.193 +
  46.194 +
  46.195 +Low-level security
  46.196 +------------------
  46.197 +
  46.198 +This tests security on a low level (functional pages test has
  46.199 +high-level security tests).  Let's manually look up a protected view:
  46.200 +
  46.201 +  >>> from Products.Five.traversable import FakeRequest
  46.202 +  >>> from zope.app import zapi
  46.203 +  >>> request = FakeRequest()
  46.204 +  >>> view = zapi.getView(self.folder.testoid, 'eagle.txt', request)
  46.205 +
  46.206 +It's protecting the object with the permission, and not the attribute,
  46.207 +so we get ('',) instead of ('eagle',):
  46.208 +
  46.209 +  >>> getattr(view, '__ac_permissions__')
  46.210 +  (('View management screens', ('',)),)
  46.211 +
  46.212 +Wrap into an acquisition so that imPermissionRole objects can be
  46.213 +evaluated. __roles__ is a imPermissionRole object:
  46.214 +
  46.215 +  >>> view = view.__of__(self.folder.testoid)
  46.216 +  >>> view_roles = getattr(view, '__roles__', None)
  46.217 +  >>> view_roles
  46.218 +  ('Manager',)
  46.219 +
  46.220 +Check to see if view's context properly acquires its true
  46.221 +parent
  46.222 +
  46.223 +  >>> from Acquisition import aq_parent, aq_base, aq_inner
  46.224 +  >>> context = getattr(view, 'context')
  46.225 +
  46.226 +Check the wrapper type
  46.227 +
  46.228 +  >>> from Acquisition import ImplicitAcquisitionWrapper
  46.229 +  >>> type(context) == ImplicitAcquisitionWrapper
  46.230 +  True
  46.231 + 
  46.232 +The acquired parent is the view. This isn't 
  46.233 +usually what you want.
  46.234 +
  46.235 +  >>> aq_parent(context) == view
  46.236 +  True 
  46.237 +
  46.238 +To get what you usually want, do this
  46.239 +
  46.240 +  >>> context.aq_inner.aq_parent
  46.241 +  <Folder at /test_folder_1_> 
  46.242 +
  46.243 +C methods work the same
  46.244 +
  46.245 +  >>> aq_parent(aq_inner(context))
  46.246 +  <Folder at /test_folder_1_> 
  46.247 +
  46.248 +High-level security
  46.249 +-------------------
  46.250 +
  46.251 +  >>> protected_view_names = [
  46.252 +  ...     'eagle.txt', 'falcon.html', 'owl.html', 'flamingo.html',
  46.253 +  ...     'condor.html', 'protectededitform.html']
  46.254 +  >>> 
  46.255 +  >>> public_view_names = [
  46.256 +  ...     'public_attribute_page',
  46.257 +  ...     'public_template_page',
  46.258 +  ...     'public_template_class_page',
  46.259 +  ...     'nodoc-method', 'nodoc-function', 'nodoc-object',
  46.260 +  ...     'dirpage1', 'dirpage2']
  46.261 +
  46.262 +  >>> from Products.Five.tests.testing.restricted import checkRestricted
  46.263 +  >>> from Products.Five.tests.testing.restricted import checkUnauthorized
  46.264 +
  46.265 +As long as we're not authenticated, we should get Unauthorized for
  46.266 +protected views, but we should be able to view the public ones:
  46.267 +
  46.268 +  >>> self.logout()
  46.269 +  >>> for view_name in protected_view_names:
  46.270 +  ...     checkUnauthorized(
  46.271 +  ...         self.folder,
  46.272 +  ...         'context.restrictedTraverse("testoid/%s")()' % view_name)
  46.273 +
  46.274 +  >>> for view_name in public_view_names:
  46.275 +  ...     checkRestricted(
  46.276 +  ...         self.folder,
  46.277 +  ...         'context.restrictedTraverse("testoid/%s")()' % view_name)
  46.278 +  >>> self.login('manager')
  46.279 +
  46.280 +Being logged in as a manager again, we find that the protected pages
  46.281 +are not accessible to us:
  46.282 +
  46.283 +  >>> for view_name in protected_view_names:
  46.284 +  ...     checkRestricted(
  46.285 +  ...         self.folder,
  46.286 +  ...         'context.restrictedTraverse("testoid/%s")()' % view_name)
  46.287 +
  46.288 +  >>> checkRestricted(
  46.289 +  ...     self.folder,
  46.290 +  ...     'context.restrictedTraverse("testoid/eagle.method").eagle()')
  46.291 +
  46.292 +
  46.293 +Other
  46.294 +-----
  46.295 +
  46.296 +Make sure that browser pages can be overridden:
  46.297 +
  46.298 +  >>> zcml.load_string('''
  46.299 +  ...     <includeOverrides
  46.300 +  ...         package="Products.Five.browser.tests"
  46.301 +  ...         file="overrides.zcml" />
  46.302 +  ... ''')
  46.303 +  >>> view = self.folder.unrestrictedTraverse('testoid/overridden_view')
  46.304 +  >>> view()
  46.305 +  'The mouse has been eaten by the eagle'
  46.306 +
  46.307 +Test traversal to resources from within ZPT pages:
  46.308 +
  46.309 +  >>> zcml.load_config('resource.zcml', package=Products.Five.browser.tests)
  46.310 +  >>> view = self.folder.unrestrictedTraverse('testoid/parakeet.html')
  46.311 +  >>> print view()
  46.312 +  <html><body><img alt=""
  46.313 +                   src="http://nohost/test_folder_1_/testoid/++resource++pattern.png" /></body></html>
  46.314 +
  46.315 +
  46.316 +Clean up
  46.317 +--------
  46.318 +
  46.319 +  >>> from zope.app.tests.placelesssetup import tearDown
  46.320 +  >>> tearDown()
    47.1 new file mode 100644
    47.2 --- /dev/null
    47.3 +++ b/browser/tests/pages.zcml
    47.4 @@ -0,0 +1,224 @@
    47.5 +<configure xmlns="http://namespaces.zope.org/zope"
    47.6 +           xmlns:meta="http://namespaces.zope.org/meta"
    47.7 +           xmlns:browser="http://namespaces.zope.org/browser"
    47.8 +           xmlns:five="http://namespaces.zope.org/five">
    47.9 +
   47.10 +  <!-- make the zope2.Public permission work -->
   47.11 +  <meta:redefinePermission from="zope2.Public" to="zope.Public" />
   47.12 +
   47.13 +  <!-- attribute page -->
   47.14 +  <browser:page
   47.15 +      for="Products.Five.tests.testing.simplecontent.ISimpleContent"
   47.16 +      class=".pages.SimpleView"
   47.17 +      attribute="eagle"
   47.18 +      name="eagle.txt"
   47.19 +      permission="zope2.ViewManagementScreens"
   47.20 +      />
   47.21 +
   47.22 +  <browser:page
   47.23 +      for="Products.Five.tests.testing.simplecontent.ISimpleContent"
   47.24 +      class=".pages.SimpleView"
   47.25 +      name="eagle.method"
   47.26 +      permission="zope2.ViewManagementScreens"
   47.27 +      allowed_attributes="eagle"
   47.28 +      />
   47.29 +
   47.30 +  <!-- attribute page -->
   47.31 +  <browser:pages
   47.32 +      for="Products.Five.tests.testing.simplecontent.ISimpleContent"
   47.33 +      class=".pages.SimpleView"
   47.34 +      permission="zope2.ViewManagementScreens"
   47.35 +      >
   47.36 +    <browser:page
   47.37 +        name="eagle-page.txt"
   47.38 +        attribute="eagle"
   47.39 +        />
   47.40 +    <browser:page
   47.41 +        name="mouse-page.txt"
   47.42 +        attribute="mouse"
   47.43 +        />
   47.44 +  </browser:pages>
   47.45 +
   47.46 +  <!-- template/class page -->
   47.47 +  <browser:page
   47.48 +      for="Products.Five.tests.testing.simplecontent.ISimpleContent"
   47.49 +      class=".pages.SimpleView"
   47.50 +      template="falcon.pt"
   47.51 +      name="falcon.html"
   47.52 +      permission="zope2.ViewManagementScreens"
   47.53 +      />
   47.54 +
   47.55 +  <!-- template page (with simple python expression) -->
   47.56 +  <browser:page
   47.57 +      for="Products.Five.tests.testing.simplecontent.ISimpleContent"
   47.58 +      template="owl.pt"
   47.59 +      name="owl.html"
   47.60 +      permission="zope2.ViewManagementScreens"
   47.61 +      />
   47.62 +
   47.63 +  <!-- template page which calls on context using python and path
   47.64 +       expressions -->
   47.65 +  <browser:page
   47.66 +      for="Products.Five.tests.testing.simplecontent.ISimpleContent"
   47.67 +      template="flamingo.pt"
   47.68 +      name="flamingo.html"
   47.69 +      permission="zope2.ViewManagementScreens"
   47.70 +      />
   47.71 +
   47.72 +  <!-- template/class page which calls on context, view, views -->
   47.73 +  <browser:page
   47.74 +      for="Products.Five.tests.testing.simplecontent.ISimpleContent"
   47.75 +      class=".pages.SimpleView"
   47.76 +      template="condor.pt"
   47.77 +      name="condor.html"
   47.78 +      permission="zope2.ViewManagementScreens"
   47.79 +      />
   47.80 +
   47.81 +  <!-- template page that defines a macro page -->
   47.82 +  <browser:page
   47.83 +      for="Products.Five.tests.testing.simplecontent.ISimpleContent"
   47.84 +      template="birdmacro.pt"
   47.85 +      name="bird.html"
   47.86 +      permission="zope2.ViewManagementScreens"
   47.87 +      />
   47.88 +
   47.89 +  <!-- template page that uses macro page -->
   47.90 +  <browser:page
   47.91 +      for="Products.Five.tests.testing.simplecontent.ISimpleContent"
   47.92 +      template="seagull.pt"
   47.93 +      name="seagull.html"
   47.94 +      permission="zope2.ViewManagementScreens"
   47.95 +      />
   47.96 +
   47.97 +  <!-- test TALES -->
   47.98 +  <browser:page
   47.99 +      for="Products.Five.tests.testing.simplecontent.ISimpleContent"
  47.100 +      template="ostrich.pt"
  47.101 +      name="ostrich.html"
  47.102 +      permission="zope2.ViewManagementScreens"
  47.103 +      />
  47.104 +
  47.105 +  <browser:page
  47.106 +      for="Products.Five.tests.testing.simplecontent.ISimpleContent"
  47.107 +      template="tales_traversal.pt"
  47.108 +      name="tales_traversal.html"
  47.109 +      permission="zope2.ViewManagementScreens"
  47.110 +      />
  47.111 +
  47.112 +  <browser:page
  47.113 +      for="Products.Five.tests.testing.simplecontent.ISimpleContent"
  47.114 +      template="template_variables.pt"
  47.115 +      name="template_variables.html"
  47.116 +      permission="zope2.ViewManagementScreens"
  47.117 +      />
  47.118 +
  47.119 +  <!-- template security -->
  47.120 +
  47.121 +  <browser:page
  47.122 +      for="Products.Five.tests.testing.simplecontent.ISimpleContent"
  47.123 +      template="security.pt"
  47.124 +      name="security.html"
  47.125 +      permission="zope2.View"
  47.126 +      />
  47.127 +
  47.128 +  <!-- a publicly accessible page, attribute, template, template/class -->
  47.129 +
  47.130 +  <browser:page
  47.131 +      for="Products.Five.tests.testing.simplecontent.ISimpleContent"
  47.132 +      class=".pages.SimpleView"
  47.133 +      attribute="eagle"
  47.134 +      name="public_attribute_page"
  47.135 +      permission="zope2.Public"
  47.136 +      />
  47.137 +
  47.138 +  <browser:page
  47.139 +      for="Products.Five.tests.testing.simplecontent.ISimpleContent"
  47.140 +      template="owl.pt"
  47.141 +      name="public_template_page"
  47.142 +      permission="zope2.Public"
  47.143 +      />
  47.144 +
  47.145 +  <browser:page
  47.146 +      for="Products.Five.tests.testing.simplecontent.ISimpleContent"
  47.147 +      class=".pages.SimpleView"
  47.148 +      template="falcon.pt"
  47.149 +      name="public_template_class_page"
  47.150 +      permission="zope2.Public"
  47.151 +      />
  47.152 +
  47.153 +  <browser:page
  47.154 +      for="Products.Five.tests.testing.simplecontent.ISimpleContent"
  47.155 +      class=".pages.SimpleView"
  47.156 +      template="parakeet.pt"
  47.157 +      name="parakeet.html"
  47.158 +      permission="zope2.ViewManagementScreens"
  47.159 +      />
  47.160 +
  47.161 +  <!-- pages from methods/functions/callables that don't have docstrings -->
  47.162 +  <browser:pages
  47.163 +      for="Products.Five.tests.testing.simplecontent.ISimpleContent"
  47.164 +      class="Products.Five.browser.tests.pages.NoDocstringView"
  47.165 +      permission="zope2.Public">
  47.166 +    <browser:page
  47.167 +        name="nodoc-method"
  47.168 +        attribute="method"
  47.169 +        />
  47.170 +    <browser:page
  47.171 +        name="nodoc-function"
  47.172 +        attribute="function"
  47.173 +        />
  47.174 +    <browser:page
  47.175 +        name="nodoc-object"
  47.176 +        attribute="object"
  47.177 +        />
  47.178 +  </browser:pages>
  47.179 +
  47.180 +  <!-- five:pagesFromDirectory loads all .pt files in a directory as pages.
  47.181 +       This is mainly used to load Zope2 skin templates so they can be used
  47.182 +       in five skins and layers. -->
  47.183 +  <five:pagesFromDirectory
  47.184 +      for="Products.Five.tests.testing.simplecontent.ISimpleContent"
  47.185 +      module="Products.Five.browser.tests"
  47.186 +      directory="pages"
  47.187 +      permission="zope2.Public"
  47.188 +      />
  47.189 +
  47.190 +  <!-- browser:page directives with new style classes are ignored -->
  47.191 +
  47.192 +  <browser:page
  47.193 +      for="Products.Five.tests.testing.simplecontent.ISimpleContent"
  47.194 +      class=".pages.NewStyleClass"
  47.195 +      name="new_style_class"
  47.196 +      attribute="method"
  47.197 +      permission="zope2.Public"
  47.198 +      />
  47.199 +
  47.200 +  <!-- Verify that browser:view works, especially when no specific
  47.201 +       view attribute is specified -->
  47.202 +
  47.203 +  <browser:view
  47.204 +      name=""
  47.205 +      for="Products.Five.tests.testing.simplecontent.ISimpleContent"
  47.206 +      class=".pages.SimpleView"
  47.207 +      permission="zope2.Public"
  47.208 +      />
  47.209 +
  47.210 +  <!-- XXX this should really be in Five.form.tests -->
  47.211 +
  47.212 +  <!-- protected edit form for permission check -->
  47.213 +  <browser:editform
  47.214 +      schema="Products.Five.tests.testing.simplecontent.ISimpleContent"
  47.215 +      name="protectededitform.html"
  47.216 +      permission="zope2.ViewManagementScreens"
  47.217 +      />
  47.218 +
  47.219 +  <!-- stuff that we'll override in overrides.zcml -->
  47.220 +  <browser:page
  47.221 +      for="Products.Five.tests.testing.simplecontent.ISimpleContent"
  47.222 +      class=".pages.SimpleView"
  47.223 +      attribute="eagle"
  47.224 +      name="overridden_view"
  47.225 +      permission="zope2.Public"
  47.226 +      />
  47.227 +
  47.228 +</configure>
    48.1 new file mode 100644
    48.2 --- /dev/null
    48.3 +++ b/browser/tests/pages/dirpage1.pt
    48.4 @@ -0,0 +1,3 @@
    48.5 +<html>
    48.6 +<p>This is page 1</p>
    48.7 +</html>
    49.1 new file mode 100644
    49.2 --- /dev/null
    49.3 +++ b/browser/tests/pages/dirpage2.pt
    49.4 @@ -0,0 +1,3 @@
    49.5 +<html>
    49.6 +<p>This is page 2</p>
    49.7 +</html>
    50.1 new file mode 100644
    50.2 --- /dev/null
    50.3 +++ b/browser/tests/pages_ftest.txt
    50.4 @@ -0,0 +1,129 @@
    50.5 +Functional Browser Pages Test
    50.6 +=============================
    50.7 +
    50.8 +This test tests publishing aspects of browser pages.  Let's register
    50.9 +some:
   50.10 +
   50.11 +  >>> import Products.Five.browser.tests
   50.12 +  >>> from Products.Five import zcml
   50.13 +  >>> zcml.load_config("configure.zcml", Products.Five)
   50.14 +  >>> zcml.load_config('pages.zcml', package=Products.Five.browser.tests)
   50.15 +
   50.16 +Let's also add one of our stub objects to play with:
   50.17 +
   50.18 +  >>> from Products.Five.tests.testing.simplecontent import manage_addSimpleContent
   50.19 +  >>> manage_addSimpleContent(self.folder, 'testoid', 'Testoid')
   50.20 +
   50.21 +
   50.22 +Docstrings
   50.23 +----------
   50.24 +
   50.25 +In Zope 2, objects normally have to have a docstring in order to be
   50.26 +published.  This crazy requirement luckily isn't true for Zope 3, so
   50.27 +it should be possible to write docstring-less view classes that are
   50.28 +still published through ZPublisher.
   50.29 +
   50.30 +We see that even though the callables have no docstring, they are
   50.31 +published nevertheless:
   50.32 +
   50.33 +  >>> print http(r"""
   50.34 +  ... GET /test_folder_1_/testoid/nodoc-function HTTP/1.1
   50.35 +  ... """)
   50.36 +  HTTP/1.1 200 OK
   50.37 +  ...
   50.38 +  No docstring
   50.39 +
   50.40 +  >>> print http(r"""
   50.41 +  ... GET /test_folder_1_/testoid/nodoc-method HTTP/1.1
   50.42 +  ... """)
   50.43 +  HTTP/1.1 200 OK
   50.44 +  ...
   50.45 +  No docstring
   50.46 +
   50.47 +  >>> print http(r"""
   50.48 +  ... GET /test_folder_1_/testoid/nodoc-object HTTP/1.1
   50.49 +  ... """)
   50.50 +  HTTP/1.1 200 OK
   50.51 +  ...
   50.52 +  No docstring
   50.53 +
   50.54 +
   50.55 +Security
   50.56 +--------
   50.57 +
   50.58 +Browser pages need to be protected with a permission.  Let's test
   50.59 +those; we start by adding two users:
   50.60 +
   50.61 +  >>> uf = self.folder.acl_users
   50.62 +  >>> uf._doAddUser('viewer', 'secret', [], [])
   50.63 +  >>> uf._doAddUser('manager', 'r00t', ['Manager'], [])
   50.64 +
   50.65 +  >>> protected_view_names = [
   50.66 +  ...     'eagle.txt', 'falcon.html', 'owl.html', 'flamingo.html',
   50.67 +  ...     'condor.html', 'protectededitform.html']
   50.68 +  >>> 
   50.69 +  >>> public_view_names = [
   50.70 +  ...     'public_attribute_page',
   50.71 +  ...     'public_template_page',
   50.72 +  ...     'public_template_class_page',
   50.73 +  ...     'nodoc-method', 'nodoc-function', 'nodoc-object',
   50.74 +  ...     'dirpage1', 'dirpage2']
   50.75 +  >>> 
   50.76 +  >>> ViewManagementScreens = 'View management screens'
   50.77 +
   50.78 +As a normal user we shouldn't get to see those pages protected with
   50.79 +the 'View management screens' permission.  Thus we expect a 401
   50.80 +Unauthorized:
   50.81 +
   50.82 +  >>> for view_name in protected_view_names:
   50.83 +  ...     response = self.publish('/test_folder_1_/testoid/%s' % view_name,
   50.84 +  ...                             basic='viewer:secret')
   50.85 +  ...     status = response.getStatus()
   50.86 +  ...     self.failUnless(status == 401, (status, 401, view_name))
   50.87 +
   50.88 +The same should apply for the user if he has all other permissions
   50.89 +except 'View management screens':
   50.90 +
   50.91 +  >>> permissions = self.folder.possible_permissions()
   50.92 +  >>> permissions.remove(ViewManagementScreens)
   50.93 +  >>> self.folder._addRole('Viewer')
   50.94 +  >>> self.folder.manage_role('Viewer', permissions)
   50.95 +  >>> self.folder.manage_addLocalRoles('viewer', ['Viewer'])
   50.96 +
   50.97 +  >>> for view_name in protected_view_names:
   50.98 +  ...     response = self.publish('/test_folder_1_/testoid/%s' % view_name,
   50.99 +  ...                             basic='viewer:secret')
  50.100 +  ...     status = response.getStatus()
  50.101 +  ...     self.failUnless(status == 401, (status, 401, view_name))
  50.102 +
  50.103 +If we grant 'View management screens' now, the protected views should
  50.104 +become viewable:
  50.105 +
  50.106 +  >>> self.folder.manage_role('Viewer', [ViewManagementScreens])
  50.107 +  >>> for view_name in protected_view_names:
  50.108 +  ...     response = self.publish('/test_folder_1_/testoid/%s' % view_name,
  50.109 +  ...                             basic='viewer:secret')
  50.110 +  ...     status = response.getStatus()
  50.111 +  ...     self.failUnless(status == 200, (status, 200, view_name))
  50.112 +
  50.113 +Managers should always be able to view anything, including proctected
  50.114 +stuff:
  50.115 +
  50.116 +  >>> for view_name in protected_view_names:
  50.117 +  ...     response = self.publish('/test_folder_1_/testoid/%s' % view_name,
  50.118 +  ...                             basic='manager:r00t')
  50.119 +  ...     self.assertEqual(response.getStatus(), 200)
  50.120 +
  50.121 +All public views should always be accessible by anyone:
  50.122 +
  50.123 +  >>> for view_name in public_view_names:
  50.124 +  ...     response = self.publish('/test_folder_1_/testoid/%s' % view_name)
  50.125 +  ...     status = response.getStatus()
  50.126 +  ...     self.failUnless(status == 200, (status, 200, view_name))
  50.127 +
  50.128 +
  50.129 +Clean up
  50.130 +--------
  50.131 +
  50.132 +  >>> from zope.app.tests.placelesssetup import tearDown
  50.133 +  >>> tearDown()
    51.1 new file mode 100644
    51.2 --- /dev/null
    51.3 +++ b/browser/tests/parakeet.pt
    51.4 @@ -0,0 +1,1 @@
    51.5 +<html><body><img alt="" src="" tal:attributes="src context/++resource++pattern.png" /></body></html>
    52.1 new file mode 100644
    52.2 index 0000000000000000000000000000000000000000..bf5aa2988a1d3d9d8c7cfe967c2d67e42ac1134b
    52.3 GIT binary patch
    52.4 literal 170
    52.5 zc%17D@N?(olHy`uVBq!ia0vp^%plANB6FUp{{d2L0X`wFXU?4Y|NsBxUePk32xoyu
    52.6 zWHAE+w=f7ZGR&GI0Tg5}@$_|NzsxNp#xBnvqjv=;BwOMdQR1ARo12<f!r)w#npl#W
    52.7 zqEMb$lA#cik*eVC=^OAqMKuqokk8Y_F{I*FazR4Gfd@eA(-;-`c5*HO$}o7k`njxg
    52.8 HN@xNA)O0Nk
    52.9 
    53.1 new file mode 100644
    53.2 --- /dev/null
    53.3 +++ b/browser/tests/pts_test_languages.pt
    53.4 @@ -0,0 +1,7 @@
    53.5 +<html>
    53.6 +<body>
    53.7 +<!-- fivetest is a Zope 3 style i18n domain, default is a PTS domain -->
    53.8 +<p i18n:domain="fivetest" i18n:translate="">This is a message</p>
    53.9 +<p i18n:domain="PlacelessTranslationService" i18n:translate="">Reload this catalog</p>
   53.10 +</body>
   53.11 +</html>
    54.1 new file mode 100644
    54.2 --- /dev/null
    54.3 +++ b/browser/tests/pts_test_languages.py
    54.4 @@ -0,0 +1,30 @@
    54.5 +##############################################################################
    54.6 +#
    54.7 +# Copyright (c) 2005 Zope Corporation and Contributors.
    54.8 +# All Rights Reserved.
    54.9 +#
   54.10 +# This software is subject to the provisions of the Zope Public License,
   54.11 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
   54.12 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
   54.13 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   54.14 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
   54.15 +# FOR A PARTICULAR PURPOSE.
   54.16 +#
   54.17 +##############################################################################
   54.18 +"""Test the PTS language integration.
   54.19 +
   54.20 +$Id: pts_test_languages.py 14400 2005-07-07 17:55:08Z philikon $
   54.21 +"""
   54.22 +import os, sys
   54.23 +if __name__ == '__main__':
   54.24 +    execfile(os.path.join(sys.path[0], 'framework.py'))
   54.25 +
   54.26 +def test_suite():
   54.27 +    from Testing.ZopeTestCase import installProduct, FunctionalDocFileSuite
   54.28 +    installProduct('Five')
   54.29 +    installProduct('PlacelessTranslationService')
   54.30 +    return FunctionalDocFileSuite('pts_test_languages.txt',
   54.31 +                                  package='Products.Five.browser.tests')
   54.32 +
   54.33 +if __name__ == '__main__':
   54.34 +    framework()
    55.1 new file mode 100644
    55.2 --- /dev/null
    55.3 +++ b/browser/tests/pts_test_languages.txt
    55.4 @@ -0,0 +1,134 @@
    55.5 +PTS languages
    55.6 +=============
    55.7 +
    55.8 +Before we start, we need to set up a manager user to be able to create
    55.9 +the portal:
   55.10 +
   55.11 +  >>> uf = self.folder.acl_users
   55.12 +  >>> uf._doAddUser('manager', 'r00t', ['Manager'], [])
   55.13 +
   55.14 +We need to 1) configure the Zope 3 i18n message catalogs, 3) register
   55.15 +the PTS languagees adapter and 3) register our test page:
   55.16 +
   55.17 +  >>> configure_zcml = """
   55.18 +  ... <configure 
   55.19 +  ...     xmlns="http://namespaces.zope.org/zope"
   55.20 +  ...     xmlns:browser="http://namespaces.zope.org/browser"
   55.21 +  ...     xmlns:i18n="http://namespaces.zope.org/i18n"
   55.22 +  ...     >
   55.23 +  ...   <configure package="Products.Five.tests">
   55.24 +  ...     <i18n:registerTranslations directory="locales" />
   55.25 +  ...   </configure>
   55.26 +  ... 
   55.27 +  ...   <adapter
   55.28 +  ...       for="zope.publisher.interfaces.http.IHTTPRequest"
   55.29 +  ...       provides="zope.i18n.interfaces.IUserPreferredLanguages"
   55.30 +  ...       factory="Products.Five.i18n.PTSLanguages"
   55.31 +  ...       />
   55.32 +  ... 
   55.33 +  ...   <configure package="Products.Five.browser.tests">
   55.34 +  ...     <browser:page
   55.35 +  ...         for="Products.Five.interfaces.IFolder"
   55.36 +  ...         template="pts_test_languages.pt"
   55.37 +  ...         name="pts_test_languages.html"
   55.38 +  ...         permission="zope2.View"
   55.39 +  ...         />
   55.40 +  ...   </configure>
   55.41 +  ... </configure>
   55.42 +  ... """
   55.43 +  >>> from Products.Five import zcml
   55.44 +  >>> zcml.load_string(configure_zcml)
   55.45 +
   55.46 +Finally, we need a traversable folder so that the test page we
   55.47 +registered is found:
   55.48 +
   55.49 +  >>> from Products.Five.tests.testing import manage_addFiveTraversableFolder
   55.50 +  >>> manage_addFiveTraversableFolder(self.folder, 'ftf')
   55.51 +
   55.52 +Now for some actual testing... Our test page is a simple ZPT
   55.53 +translating two messages from different domains.  The first domain is
   55.54 +a Zope 3 style one, the second one comes from PTS.
   55.55 +
   55.56 +Both systems should yield the same default language (English) when no
   55.57 +language is specified whatsoever:
   55.58 +
   55.59 +  >>> print http(r"""
   55.60 +  ... GET /test_folder_1_/ftf/pts_test_languages.html HTTP/1.1
   55.61 +  ... """)
   55.62 +  HTTP/1.1 200 OK
   55.63 +  ...
   55.64 +  <html>
   55.65 +  <body>
   55.66 +  <!-- fivetest is a Zope 3 style i18n domain, default is a PTS domain -->
   55.67 +  <p>This is a message</p>
   55.68 +  <p>Reload this catalog</p>
   55.69 +  </body>
   55.70 +  </html>
   55.71 +
   55.72 +Both systems should honour the HTTP ``Accept-Language`` header in the
   55.73 +same way:
   55.74 +
   55.75 +  >>> print http(r"""
   55.76 +  ... GET /test_folder_1_/ftf/pts_test_languages.html HTTP/1.1
   55.77 +  ... Accept-Language: de
   55.78 +  ... """)
   55.79 +  HTTP/1.1 200 OK
   55.80 +  ...
   55.81 +  <html>
   55.82 +  <body>
   55.83 +  <!-- fivetest is a Zope 3 style i18n domain, default is a PTS domain -->
   55.84 +  <p>Dies ist eine Nachricht</p>
   55.85 +  <p>Diesen Katalog neu einlesen</p>
   55.86 +  </body>
   55.87 +  </html>
   55.88 +
   55.89 +Both systems should also honour Localizer-specific ways of determining
   55.90 +the language, for example the ``pts_language`` cookie...
   55.91 +
   55.92 +  >>> print http(r"""
   55.93 +  ... GET /test_folder_1_/ftf/pts_test_languages.html HTTP/1.1
   55.94 +  ... Accept-Language: de
   55.95 +  ... Cookie: pts_language=en
   55.96 +  ... """)
   55.97 +  HTTP/1.1 200 OK
   55.98 +  ...
   55.99 +  <html>
  55.100 +  <body>
  55.101 +  <!-- fivetest is a Zope 3 style i18n domain, default is a PTS domain -->
  55.102 +  <p>This is a message</p>
  55.103 +  <p>Reload this catalog</p>
  55.104 +  </body>
  55.105 +  </html>
  55.106 +
  55.107 +... and the ``language`` form field...
  55.108 +
  55.109 +  >>> print http(r"""
  55.110 +  ... GET /test_folder_1_/ftf/pts_test_languages.html?language=en HTTP/1.1
  55.111 +  ... Accept-Language: de
  55.112 +  ... """)
  55.113 +  HTTP/1.1 200 OK
  55.114 +  ...
  55.115 +  <html>
  55.116 +  <body>
  55.117 +  <!-- fivetest is a Zope 3 style i18n domain, default is a PTS domain -->
  55.118 +  <p>This is a message</p>
  55.119 +  <p>Reload this catalog</p>
  55.120 +  </body>
  55.121 +  </html>
  55.122 +
  55.123 +... and both the ``pts_language`` cookie and the ``language`` form field:
  55.124 +
  55.125 +  >>> print http(r"""
  55.126 +  ... GET /test_folder_1_/ftf/pts_test_languages.html?language=de HTTP/1.1
  55.127 +  ... Accept-Language: en
  55.128 +  ... Cookie: pts_language=fr
  55.129 +  ... """)
  55.130 +  HTTP/1.1 200 OK
  55.131 +  ...
  55.132 +  <html>
  55.133 +  <body>
  55.134 +  <!-- fivetest is a Zope 3 style i18n domain, default is a PTS domain -->
  55.135 +  <p>Dies ist eine Nachricht</p>
  55.136 +  <p>Diesen Katalog neu einlesen</p>
  55.137 +  </body>
  55.138 +  </html>
    56.1 new file mode 100644
    56.2 --- /dev/null
    56.3 +++ b/browser/tests/resource.txt
    56.4 @@ -0,0 +1,116 @@
    56.5 +Testing resources
    56.6 +=================
    56.7 +
    56.8 +Set up the test fixtures:
    56.9 +
   56.10 +  >>> import Products.Five.browser.tests
   56.11 +  >>> from Products.Five import zcml
   56.12 +  >>> zcml.load_config("configure.zcml", Products.Five)
   56.13 +  >>> zcml.load_config('resource.zcml', package=Products.Five.browser.tests)
   56.14 +
   56.15 +  >>> from Products.Five.tests.testing import manage_addFiveTraversableFolder
   56.16 +  >>> manage_addFiveTraversableFolder(self.folder, 'testoid', 'Testoid')
   56.17 +
   56.18 +  >>> import os, glob
   56.19 +  >>> _prefix = os.path.dirname(Products.Five.browser.tests.__file__)
   56.20 +  >>> dir_resource_names = [os.path.basename(r) for r in (
   56.21 +  ...     glob.glob('%s/*.png' % _prefix) +
   56.22 +  ...     glob.glob('%s/*.pt' % _prefix) +
   56.23 +  ...     glob.glob('%s/[a-z]*.py' % _prefix) +
   56.24 +  ...     glob.glob('%s/*.css' % _prefix))]
   56.25 +
   56.26 +
   56.27 +Resource types
   56.28 +--------------
   56.29 +
   56.30 +  >>> from Products.Five.browser.resource import Resource, PageTemplateResource
   56.31 +
   56.32 +Template resource
   56.33 +~~~~~~~~~~~~~~~~~
   56.34 +
   56.35 +  >>> resource = self.folder.unrestrictedTraverse('testoid/++resource++cockatiel.html')
   56.36 +  >>> isinstance(resource, Resource)
   56.37 +  True
   56.38 +  >>> resource()
   56.39 +  'http://nohost/test_folder_1_/testoid/++resource++cockatiel.html'
   56.40 +
   56.41 +File resource
   56.42 +~~~~~~~~~~~~~
   56.43 +
   56.44 +  >>> resource = self.folder.unrestrictedTraverse('testoid/++resource++style.css')
   56.45 +  >>> isinstance(resource, Resource)
   56.46 +  True
   56.47 +  >>> resource()
   56.48 +  'http://nohost/test_folder_1_/testoid/++resource++style.css'
   56.49 +
   56.50 +Image resource
   56.51 +~~~~~~~~~~~~~~
   56.52 +
   56.53 +  >>> resource = self.folder.unrestrictedTraverse('testoid/++resource++pattern.png')
   56.54 +  >>> isinstance(resource, Resource)
   56.55 +  True
   56.56 +  >>> resource()
   56.57 +  'http://nohost/test_folder_1_/testoid/++resource++pattern.png'
   56.58 +
   56.59 +Resource directory
   56.60 +~~~~~~~~~~~~~~~~~~
   56.61 +
   56.62 +  >>> base = 'testoid/++resource++fivetest_resources/%s'
   56.63 +  >>> base_url = 'http://nohost/test_folder_1_/' + base
   56.64 +  >>> abs_url = self.folder.unrestrictedTraverse(base % '')()
   56.65 +  >>> abs_url + '/' == base_url % ''
   56.66 +  True
   56.67 +
   56.68 +PageTemplateResource's __call__ renders the template
   56.69 +
   56.70 +  >>> for r in dir_resource_names:
   56.71 +  ...     resource = self.folder.unrestrictedTraverse(base % r)
   56.72 +  ...     self.assert_(isinstance(resource, Resource))
   56.73 +  ...     if not isinstance(resource, PageTemplateResource):
   56.74 +  ...         self.assertEquals(resource(), base_url % r)
   56.75 +
   56.76 +
   56.77 +Security
   56.78 +--------
   56.79 +
   56.80 +  >>> from Products.Five.tests.testing.restricted import checkRestricted
   56.81 +  >>> from Products.Five.tests.testing.restricted import checkUnauthorized
   56.82 +
   56.83 +  >>> resource_names = ['cockatiel.html', 'style.css', 'pattern.png']
   56.84 +
   56.85 +We should get Unauthorized as long as we're unauthenticated:
   56.86 +
   56.87 +  >>> for resource in resource_names:
   56.88 +  ...     checkUnauthorized(
   56.89 +  ...         self.folder,
   56.90 +  ...         'context.restrictedTraverse("testoid/++resource++%s")()' % resource)
   56.91 +
   56.92 +  >>> base = 'testoid/++resource++fivetest_resources/%s'
   56.93 +  >>> for resource in dir_resource_names:
   56.94 +  ...     path = base % resource
   56.95 +  ...     checkUnauthorized(self.folder, 'context.restrictedTraverse("%s")' % path)
   56.96 +
   56.97 +Now let's create a manager user account and log in:
   56.98 +
   56.99 +  >>> uf = self.folder.acl_users
  56.100 +  >>> uf._doAddUser('manager', 'r00t', ['Manager'], [])
  56.101 +  >>> self.login('manager')
  56.102 +
  56.103 +We can now view them all:
  56.104 +
  56.105 +  >>> for resource in resource_names:
  56.106 +  ...     checkRestricted(
  56.107 +  ...         self.folder,
  56.108 +  ...         'context.restrictedTraverse("testoid/++resource++%s")()' % resource)
  56.109 +
  56.110 +  >>> base = 'testoid/++resource++fivetest_resources/%s'
  56.111 +  >>> for resource in dir_resource_names:
  56.112 +  ...     path = base % resource
  56.113 +  ...     checkRestricted(self.folder, 'context.restrictedTraverse("%s")' % path)
  56.114 +
  56.115 +
  56.116 +Clean up
  56.117 +--------
  56.118 +
  56.119 +  >>> from zope.app.tests.placelesssetup import tearDown
  56.120 +  >>> tearDown()
    57.1 new file mode 100644
    57.2 --- /dev/null
    57.3 +++ b/browser/tests/resource.zcml
    57.4 @@ -0,0 +1,29 @@
    57.5 +<configure xmlns="http://namespaces.zope.org/zope"
    57.6 +           xmlns:browser="http://namespaces.zope.org/browser">
    57.7 +
    57.8 +  <!-- a couple simple resources -->
    57.9 +  <browser:resource
   57.10 +      template="cockatiel.pt"
   57.11 +      name="cockatiel.html"
   57.12 +      permission="zope2.ViewManagementScreens"
   57.13 +      />
   57.14 +
   57.15 +  <browser:resource
   57.16 +      file="style.css"
   57.17 +      name="style.css"
   57.18 +      permission="zope2.ViewManagementScreens"
   57.19 +      />
   57.20 +
   57.21 +  <browser:resource
   57.22 +      image="pattern.png"
   57.23 +      name="pattern.png"
   57.24 +      permission="zope2.ViewManagementScreens"
   57.25 +      />
   57.26 +
   57.27 +  <browser:resourceDirectory
   57.28 +      name="fivetest_resources"
   57.29 +      directory="."
   57.30 +      permission="zope2.ViewManagementScreens"
   57.31 +      />
   57.32 +
   57.33 +</configure>
   57.34 \ No newline at end of file
    58.1 new file mode 100644
    58.2 --- /dev/null
    58.3 +++ b/browser/tests/resource_ftest.txt
    58.4 @@ -0,0 +1,73 @@
    58.5 +Functional Resource Test
    58.6 +========================
    58.7 +
    58.8 +Set up the test fixtures:
    58.9 +
   58.10 +  >>> import Products.Five.browser.tests
   58.11 +  >>> from Products.Five import zcml
   58.12 +  >>> zcml.load_config("configure.zcml", Products.Five)
   58.13 +  >>> zcml.load_config('resource.zcml', package=Products.Five.browser.tests)
   58.14 +
   58.15 +  >>> from Products.Five.tests.testing import manage_addFiveTraversableFolder
   58.16 +  >>> manage_addFiveTraversableFolder(self.folder, 'testoid', 'Testoid')
   58.17 +
   58.18 +  >>> import os, glob
   58.19 +  >>> _prefix = os.path.dirname(Products.Five.browser.tests.__file__)
   58.20 +  >>> dir_resource_names = [os.path.basename(r) for r in (
   58.21 +  ...     glob.glob('%s/*.png' % _prefix) +
   58.22 +  ...     glob.glob('%s/*.pt' % _prefix) +
   58.23 +  ...     glob.glob('%s/[a-z]*.py' % _prefix) +
   58.24 +  ...     glob.glob('%s/*.css' % _prefix))]
   58.25 +
   58.26 +  >>> uf = self.folder.acl_users
   58.27 +  >>> uf._doAddUser('manager', 'r00t', ['Manager'], [])
   58.28 +
   58.29 +
   58.30 +Image resource
   58.31 +~~~~~~~~~~~~~~
   58.32 +
   58.33 +  >>> print http(r'''
   58.34 +  ... GET /test_folder_1_/testoid/++resource++pattern.png HTTP/1.1
   58.35 +  ... Authorization: Basic manager:r00t
   58.36 +  ... ''')
   58.37 +  HTTP/1.1 200 OK
   58.38 +  ...
   58.39 +
   58.40 +File resource
   58.41 +~~~~~~~~~~~~~
   58.42 +
   58.43 +  >>> print http(r'''
   58.44 +  ... GET /test_folder_1_/testoid/++resource++style.css HTTP/1.1
   58.45 +  ... Authorization: Basic manager:r00t
   58.46 +  ... ''')
   58.47 +  HTTP/1.1 200 OK
   58.48 +  ...
   58.49 +
   58.50 +Template resource
   58.51 +~~~~~~~~~~~~~~~~~
   58.52 +
   58.53 +  >>> print http(r'''
   58.54 +  ... GET /test_folder_1_/testoid/++resource++cockatiel.html HTTP/1.1
   58.55 +  ... Authorization: Basic manager:r00t
   58.56 +  ... ''')
   58.57 +  HTTP/1.1 200 OK
   58.58 +  ...
   58.59 +
   58.60 +Resource directory
   58.61 +~~~~~~~~~~~~~~~~~~
   58.62 +
   58.63 +Page templates aren't guaranteed to render, so exclude them from the test:
   58.64 +
   58.65 +  >>> base_url = '/test_folder_1_/testoid/++resource++fivetest_resources/%s'
   58.66 +  >>> for r in dir_resource_names:
   58.67 +  ...     if r.endswith('.pt'):
   58.68 +  ...         continue
   58.69 +  ...     response = self.publish(base_url % r, basic='manager:r00t')
   58.70 +  ...     self.assertEquals(200, response.getStatus())
   58.71 +
   58.72 +
   58.73 +Clean up
   58.74 +--------
   58.75 +
   58.76 +  >>> from zope.app.tests.placelesssetup import tearDown
   58.77 +  >>> tearDown()
    59.1 new file mode 100644
    59.2 --- /dev/null
    59.3 +++ b/browser/tests/seagull.pt
    59.4 @@ -0,0 +1,1 @@
    59.5 +<html metal:use-macro="context/@@bird.html/birdmacro"><metal:block fill-slot="color">gray</metal:block></html>
    60.1 new file mode 100644
    60.2 --- /dev/null
    60.3 +++ b/browser/tests/security.pt
    60.4 @@ -0,0 +1,5 @@
    60.5 +<div tal:define="comment string:Testing unrestricted code"
    60.6 +      tal:content="python:None.__class__.__name__" />
    60.7 +<div tal:define="comment string:Testing unrestricted modules access;
    60.8 +                 smtpd nocall:modules/smtpd"
    60.9 +      tal:content="python:smtpd.__name__" />
    61.1 new file mode 100644
    61.2 --- /dev/null
    61.3 +++ b/browser/tests/skin.txt
    61.4 @@ -0,0 +1,54 @@
    61.5 +Test layer and skin support
    61.6 +===========================
    61.7 +
    61.8 +Let's register a test layer and test skin:
    61.9 +
   61.10 +  >>> import Products.Five.browser.tests
   61.11 +  >>> from Products.Five import zcml
   61.12 +  >>> zcml.load_config("configure.zcml", Products.Five)
   61.13 +  >>> zcml.load_config("skin.zcml", package=Products.Five.browser.tests)
   61.14 +
   61.15 +Let's add a test object that we'll access the test page from:
   61.16 +
   61.17 +  >>> from Products.Five.tests.testing.simplecontent import manage_addSimpleContent
   61.18 +  >>> manage_addSimpleContent(self.folder, 'testoid', 'Testoid')
   61.19 +
   61.20 +The view was registered on a different layer than 'default', that's
   61.21 +why we can't access it straight away:
   61.22 +
   61.23 +  >>> print http(r"""
   61.24 +  ... GET /test_folder_1_/testoid/eagle.html HTTP/1.1
   61.25 +  ... """)
   61.26 +  HTTP/1.1 404 Not Found
   61.27 +  ...
   61.28 +
   61.29 +It works when we explicitly use the skin that includes that layer:
   61.30 +
   61.31 +  >>> print http(r"""
   61.32 +  ... GET /test_folder_1_/testoid/++skin++TestSkin/eagle.html HTTP/1.1
   61.33 +  ... """)
   61.34 +  HTTP/1.1 200 OK
   61.35 +  ...
   61.36 +  The eagle has landed
   61.37 +
   61.38 +Or when we make that skin the default skin:
   61.39 +
   61.40 +  >>> zcml.load_string('''
   61.41 +  ...   <browser:defaultSkin
   61.42 +  ...       xmlns:browser="http://namespaces.zope.org/browser"
   61.43 +  ...       name="TestSkin" />
   61.44 +  ... ''')
   61.45 +
   61.46 +  >>> print http(r"""
   61.47 +  ... GET /test_folder_1_/testoid/eagle.html HTTP/1.1
   61.48 +  ... """)
   61.49 +  HTTP/1.1 200 OK
   61.50 +  ...
   61.51 +  The eagle has landed
   61.52 +
   61.53 +
   61.54 +Clean up
   61.55 +--------
   61.56 +
   61.57 +  >>> from zope.app.tests.placelesssetup import tearDown
   61.58 +  >>> tearDown()
    62.1 new file mode 100644
    62.2 --- /dev/null
    62.3 +++ b/browser/tests/skin.zcml
    62.4 @@ -0,0 +1,24 @@
    62.5 +<configure xmlns="http://namespaces.zope.org/zope"
    62.6 +           xmlns:meta="http://namespaces.zope.org/meta"
    62.7 +           xmlns:browser="http://namespaces.zope.org/browser">
    62.8 +
    62.9 +  <!-- make the zope2.Public permission work -->
   62.10 +  <meta:redefinePermission from="zope2.Public" to="zope.Public" />
   62.11 +
   62.12 +  <browser:layer name="test" />
   62.13 +
   62.14 +  <browser:skin
   62.15 +      name="TestSkin"
   62.16 +      layers="test default"
   62.17 +      />
   62.18 +
   62.19 +  <browser:page
   62.20 +      for="Products.Five.tests.testing.simplecontent.ISimpleContent"
   62.21 +      class=".pages.SimpleView"
   62.22 +      attribute="eagle"
   62.23 +      name="eagle.html"
   62.24 +      permission="zope2.Public"
   62.25 +      layer="test"
   62.26 +      />
   62.27 +
   62.28 +</configure>
    63.1 new file mode 100644
    63.2 --- /dev/null
    63.3 +++ b/browser/tests/style.css
    63.4 @@ -0,0 +1,1 @@
    63.5 +a { text-decoration: none; }
    64.1 new file mode 100644
    64.2 --- /dev/null
    64.3 +++ b/browser/tests/tales_traversal.pt
    64.4 @@ -0,0 +1,2 @@
    64.5 +<p tal:content="context/non_existent_thingie/fubared|context/getId">dunno</p>
    64.6 +<p tal:content="context/test_folder_1_/test_folder_1_/getId">dunno</p>
    65.1 new file mode 100644
    65.2 --- /dev/null
    65.3 +++ b/browser/tests/template_variables.pt
    65.4 @@ -0,0 +1,23 @@
    65.5 +View is a view: <tal:block 
    65.6 +  content="python:hasattr(view,'context') and hasattr(view, 'request')" />
    65.7 +Context is testoid: <tal:block content="python:context.id == 'testoid'" />
    65.8 +Contaxt.aq_parent is test_folder_1_: <tal:block 
    65.9 +  content="python:context.aq_parent.id =='test_folder_1_'" />
   65.10 +Container is context: <tal:block content="python:container is context" />
   65.11 +Here is context: <tal:block content="python:here is context"/>
   65.12 +Nothing is None: <tal:block content="python:nothing is None"/>
   65.13 +Default works: <tal:block replace="non_existent_var|default" />True
   65.14 +Root is the application: <tal:block 
   65.15 +  replace="python:repr(root).find('Application') != -1" />
   65.16 +Template is a template: <tal:block 
   65.17 +  replace="python:repr(template.aq_base).startswith('<ZopeTwoPageTemplateFile')" />
   65.18 +Traverse_subpath exists and is empty: <tal:block 
   65.19 +  replace="python:traverse_subpath == []" />
   65.20 +Request is a request: <tal:block 
   65.21 +  replace="python:getattr(request, 'RESPONSE', None) is not None" />
   65.22 +User is manager: <tal:block replace="python:str(user) == 'manager'" />
   65.23 +Options exist: <tal:block replace="python:options is not None" />
   65.24 +Attrs exist: <tal:block replace="python:attrs is not None" />
   65.25 +Repeat exists: <tal:block replace="python:repeat is not None" />
   65.26 +Loop exists: <tal:block replace="python:loop is not None" />
   65.27 +Modules exists: <tal:block replace="python:modules is not None" />
   65.28 \ No newline at end of file
    66.1 new file mode 100644
    66.2 --- /dev/null
    66.3 +++ b/browser/tests/test_absoluteurl.py
    66.4 @@ -0,0 +1,98 @@
    66.5 +##############################################################################
    66.6 +#
    66.7 +# Copyright (c) 2004, 2005 Zope Corporation and Contributors.
    66.8 +# All Rights Reserved.
    66.9 +#
   66.10 +# This software is subject to the provisions of the Zope Public License,
   66.11 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
   66.12 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
   66.13 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   66.14 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
   66.15 +# FOR A PARTICULAR PURPOSE.
   66.16 +#
   66.17 +##############################################################################
   66.18 +"""Test AbsoluteURL
   66.19 +
   66.20 +$Id: test_absoluteurl.py 17853 2005-09-25 13:34:25Z tseaver $
   66.21 +"""
   66.22 +import os, sys
   66.23 +if __name__ == '__main__':
   66.24 +    execfile(os.path.join(sys.path[0], 'framework.py'))
   66.25 +
   66.26 +def test_absoluteurl():
   66.27 +    """This tests the absolute url view (IAbsoluteURL or @@absolute_url),
   66.28 +    in particular the breadcrumb functionality.
   66.29 +
   66.30 +    First we make some preparations:
   66.31 +
   66.32 +      >>> import Products.Five
   66.33 +      >>> from Products.Five import zcml
   66.34 +      >>> zcml.load_config("configure.zcml", Products.Five)
   66.35 +
   66.36 +      >>> from Products.Five.tests.testing import manage_addFiveTraversableFolder
   66.37 +      >>> manage_addFiveTraversableFolder(self.folder, 'testoid', 'Testoid')
   66.38 +
   66.39 +    A simple traversal will yield us the @@absolute_url view:
   66.40 +
   66.41 +      >>> view = self.folder.unrestrictedTraverse('testoid/@@absolute_url')
   66.42 +      >>> view()
   66.43 +      'http://nohost/test_folder_1_/testoid'
   66.44 +
   66.45 +    IAbsoluteURL also defines a breadcrumbs() method that returns a
   66.46 +    simple Python structure:
   66.47 +
   66.48 +      >>> for crumb in view.breadcrumbs():
   66.49 +      ...     info = crumb.items()
   66.50 +      ...     info.sort()
   66.51 +      ...     info
   66.52 +      [('name', ''), ('url', 'http://nohost')]
   66.53 +      [('name', 'test_folder_1_'), ('url', 'http://nohost/test_folder_1_')]
   66.54 +      [('name', 'testoid'), ('url', 'http://nohost/test_folder_1_/testoid')]
   66.55 +
   66.56 +    This test assures and demonstrates that the absolute url stops
   66.57 +    traversing through an object's parents when it has reached the
   66.58 +    root object.  In Zope 3 this is marked with the IContainmentRoot
   66.59 +    interface:
   66.60 +
   66.61 +      >>> from zope.interface import directlyProvides, providedBy
   66.62 +      >>> from zope.app.traversing.interfaces import IContainmentRoot
   66.63 +      >>> directlyProvides(self.folder, IContainmentRoot)
   66.64 +
   66.65 +      >>> for crumb in view.breadcrumbs():
   66.66 +      ...     info = crumb.items()
   66.67 +      ...     info.sort()
   66.68 +      ...     info
   66.69 +      [('name', 'test_folder_1_'), ('url', 'http://nohost/test_folder_1_')]
   66.70 +      [('name', 'testoid'), ('url', 'http://nohost/test_folder_1_/testoid')]
   66.71 +
   66.72 +      >>> directlyProvides(self.folder,
   66.73 +      ...                  providedBy(self.folder) - IContainmentRoot)
   66.74 +
   66.75 +    The absolute url view is obviously not affected by virtual hosting:
   66.76 +
   66.77 +      >>> request = self.app.REQUEST
   66.78 +      >>> request['PARENTS'] = [self.folder.test_folder_1_]
   66.79 +      >>> url = request.setServerURL(
   66.80 +      ...     protocol='http', hostname='foo.bar.com', port='80')
   66.81 +      >>> request.setVirtualRoot('')
   66.82 +
   66.83 +      >>> for crumb in view.breadcrumbs():
   66.84 +      ...     info = crumb.items()
   66.85 +      ...     info.sort()
   66.86 +      ...     info
   66.87 +      [('name', 'test_folder_1_'), ('url', 'http://foo.bar.com')]
   66.88 +      [('name', 'testoid'), ('url', 'http://foo.bar.com/testoid')]
   66.89 +
   66.90 +
   66.91 +    Clean up:
   66.92 +
   66.93 +      >>> from zope.app.tests.placelesssetup import tearDown
   66.94 +      >>> tearDown()
   66.95 +    """
   66.96 +
   66.97 +def test_suite():
   66.98 +    from Testing.ZopeTestCase import ZopeDocTestSuite
   66.99 +    return ZopeDocTestSuite()
  66.100 +
  66.101 +if __name__ == '__main__':
  66.102 +    framework()
    67.1 new file mode 100644
    67.2 --- /dev/null
    67.3 +++ b/browser/tests/test_adding.py
    67.4 @@ -0,0 +1,28 @@
    67.5 +##############################################################################
    67.6 +#
    67.7 +# Copyright (c) 2004, 2005 Zope Corporation and Contributors.
    67.8 +# All Rights Reserved.
    67.9 +#
   67.10 +# This software is subject to the provisions of the Zope Public License,
   67.11 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
   67.12 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
   67.13 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   67.14 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
   67.15 +# FOR A PARTICULAR PURPOSE.
   67.16 +#
   67.17 +##############################################################################
   67.18 +"""Test adding views
   67.19 +
   67.20 +$Id: test_adding.py 19157 2005-10-29 10:27:28Z philikon $
   67.21 +"""
   67.22 +import os, sys
   67.23 +if __name__ == '__main__':
   67.24 +    execfile(os.path.join(sys.path[0], 'framework.py'))
   67.25 +
   67.26 +def test_suite():
   67.27 +    from Testing.ZopeTestCase import ZopeDocFileSuite
   67.28 +    return ZopeDocFileSuite('adding.txt',
   67.29 +                            package="Products.Five.browser.tests")
   67.30 +
   67.31 +if __name__ == '__main__':
   67.32 +    framework()
    68.1 new file mode 100644
    68.2 --- /dev/null
    68.3 +++ b/browser/tests/test_defaultview.py
    68.4 @@ -0,0 +1,91 @@
    68.5 +##############################################################################
    68.6 +#
    68.7 +# Copyright (c) 2004, 2005 Zope Corporation and Contributors.
    68.8 +# All Rights Reserved.
    68.9 +#
   68.10 +# This software is subject to the provisions of the Zope Public License,
   68.11 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
   68.12 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
   68.13 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   68.14 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
   68.15 +# FOR A PARTICULAR PURPOSE.
   68.16 +#
   68.17 +##############################################################################
   68.18 +"""Test Default View functionality
   68.19 +
   68.20 +$Id: test_defaultview.py 17853 2005-09-25 13:34:25Z tseaver $
   68.21 +"""
   68.22 +import os, sys
   68.23 +if __name__ == '__main__':
   68.24 +    execfile(os.path.join(sys.path[0], 'framework.py'))
   68.25 +
   68.26 +def test_default_view():
   68.27 +    """
   68.28 +    Test default view functionality
   68.29 +
   68.30 +    Let's register a couple of default views and make our stub classes
   68.31 +    default viewable:
   68.32 +
   68.33 +      >>> import Products.Five.browser.tests
   68.34 +      >>> from Products.Five import zcml
   68.35 +      >>> zcml.load_config("configure.zcml", Products.Five)
   68.36 +      >>> zcml.load_config('defaultview.zcml', Products.Five.browser.tests)
   68.37 +
   68.38 +    Now let's add a couple of stub objects:
   68.39 +
   68.40 +      >>> from Products.Five.tests.testing.simplecontent import manage_addSimpleContent
   68.41 +      >>> from Products.Five.tests.testing.simplecontent import manage_addCallableSimpleContent
   68.42 +      >>> from Products.Five.tests.testing.simplecontent import manage_addIndexSimpleContent
   68.43 +
   68.44 +      >>> manage_addSimpleContent(self.folder, 'testoid', 'Testoid')
   68.45 +      >>> manage_addCallableSimpleContent(self.folder, 'testcall', 'TestCall')
   68.46 +      >>> manage_addIndexSimpleContent(self.folder, 'testindex', 'TestIndex')
   68.47 +
   68.48 +    As a last act of preparation, we create a manager login:
   68.49 +
   68.50 +      >>> uf = self.folder.acl_users
   68.51 +      >>> uf._doAddUser('manager', 'r00t', ['Manager'], [])
   68.52 +
   68.53 +    Test a simple default view:
   68.54 +
   68.55 +      >>> print http(r'''
   68.56 +      ... GET /test_folder_1_/testoid HTTP/1.1
   68.57 +      ... Authorization: Basic manager:r00t
   68.58 +      ... ''')
   68.59 +      HTTP/1.1 200 OK
   68.60 +      ...
   68.61 +      The eagle has landed
   68.62 +
   68.63 +    This tests whether an existing ``index_html`` method is still
   68.64 +    supported and called:
   68.65 +
   68.66 +      >>> print http(r'''
   68.67 +      ... GET /test_folder_1_/testindex HTTP/1.1
   68.68 +      ... ''')
   68.69 +      HTTP/1.1 200 OK
   68.70 +      ...
   68.71 +      Default index_html called
   68.72 +
   68.73 +    Disabled __call__ overriding for now.  Causese more trouble than it
   68.74 +    fixes.  Thus, no test here:
   68.75 +
   68.76 +      #>>> print http(r'''
   68.77 +      #... GET /test_folder_1_/testcall HTTP/1.1
   68.78 +      #... ''')
   68.79 +      #HTTP/1.1 200 OK
   68.80 +      #...
   68.81 +      #Default __call__ called
   68.82 +
   68.83 +
   68.84 +    Clean up:
   68.85 +
   68.86 +      >>> from zope.app.tests.placelesssetup import tearDown
   68.87 +      >>> tearDown()
   68.88 +    """
   68.89 +
   68.90 +def test_suite():
   68.91 +    from Testing.ZopeTestCase import FunctionalDocTestSuite
   68.92 +    return FunctionalDocTestSuite()
   68.93 +
   68.94 +if __name__ == '__main__':
   68.95 +    framework()
    69.1 new file mode 100644
    69.2 --- /dev/null
    69.3 +++ b/browser/tests/test_i18n.py
    69.4 @@ -0,0 +1,93 @@
    69.5 +##############################################################################
    69.6 +#
    69.7 +# Copyright (c) 2004, 2005 Zope Corporation and Contributors.
    69.8 +# All Rights Reserved.
    69.9 +#
   69.10 +# This software is subject to the provisions of the Zope Public License,
   69.11 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
   69.12 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
   69.13 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   69.14 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
   69.15 +# FOR A PARTICULAR PURPOSE.
   69.16 +#
   69.17 +##############################################################################
   69.18 +"""Unit tests for the i18n framework
   69.19 +
   69.20 +$Id: test_i18n.py 18829 2005-10-21 17:22:05Z yuppie $
   69.21 +"""
   69.22 +import os, sys
   69.23 +if __name__ == '__main__':
   69.24 +    execfile(os.path.join(sys.path[0], 'framework.py'))
   69.25 +
   69.26 +def test_zpt_i18n():
   69.27 +    """
   69.28 +    Test i18n functionality in ZPTs
   69.29 +
   69.30 +      >>> configure_zcml = '''
   69.31 +      ... <configure 
   69.32 +      ...     xmlns="http://namespaces.zope.org/zope"
   69.33 +      ...     xmlns:browser="http://namespaces.zope.org/browser"
   69.34 +      ...     xmlns:i18n="http://namespaces.zope.org/i18n">
   69.35 +      ...   <configure package="Products.Five.tests">
   69.36 +      ...     <i18n:registerTranslations directory="locales" />
   69.37 +      ...   </configure>
   69.38 +      ...   <configure package="Products.Five.browser.tests">
   69.39 +      ...     <browser:page
   69.40 +      ...         for="OFS.interfaces.IFolder"
   69.41 +      ...         template="i18n.pt"
   69.42 +      ...         name="i18n.html"
   69.43 +      ...         permission="zope2.View"
   69.44 +      ...         />
   69.45 +      ...   </configure>
   69.46 +      ... </configure>'''
   69.47 +
   69.48 +      >>> import Products.Five
   69.49 +      >>> from Products.Five import zcml
   69.50 +      >>> zcml.load_config("configure.zcml", Products.Five)
   69.51 +      >>> zcml.load_string(configure_zcml)
   69.52 +
   69.53 +    In order to be able to traverse to the PageTemplate view, we need
   69.54 +    a traversable object:
   69.55 +
   69.56 +      >>> from Products.Five.tests.testing import manage_addFiveTraversableFolder
   69.57 +      >>> manage_addFiveTraversableFolder(self.folder, 'testoid', 'Testoid')
   69.58 +
   69.59 +    We tell Zope to translate the messages by passing the
   69.60 +    ``Accept-Language`` header which is processed by the
   69.61 +    ``IUserPreferredLangauges`` adapter:
   69.62 +
   69.63 +      >>> print http(r'''
   69.64 +      ... GET /test_folder_1_/testoid/@@i18n.html HTTP/1.1
   69.65 +      ... Accept-Language: de
   69.66 +      ... ''')
   69.67 +      HTTP/1.1 200 OK
   69.68 +      ...
   69.69 +      <html>
   69.70 +      <body>
   69.71 +      <p>Dies ist eine Nachricht</p>
   69.72 +      <p>Dies ist eine explizite Nachricht</p>
   69.73 +      <p>Dies sind 4 Nachrichten</p>
   69.74 +      <p>Dies sind 4 explizite Nachrichten</p>
   69.75 +      <table summary="Dies ist ein Attribut">
   69.76 +      </table>
   69.77 +      <table summary="Explizite Zusammenfassung"
   69.78 +             title="Expliziter Titel">
   69.79 +      </table>
   69.80 +      </body>
   69.81 +      </html>
   69.82 +      ...
   69.83 +
   69.84 +
   69.85 +    Clean up:
   69.86 +
   69.87 +      >>> from zope.app.tests.placelesssetup import tearDown
   69.88 +      >>> tearDown()
   69.89 +    """
   69.90 +
   69.91 +def test_suite():
   69.92 +    from Testing.ZopeTestCase import FunctionalDocTestSuite
   69.93 +    from zope.testing.doctest import ELLIPSIS
   69.94 +    return FunctionalDocTestSuite(optionflags=ELLIPSIS)
   69.95 +
   69.96 +if __name__ == '__main__':
   69.97 +    framework()
    70.1 new file mode 100644
    70.2 --- /dev/null
    70.3 +++ b/browser/tests/test_menu.py
    70.4 @@ -0,0 +1,143 @@
    70.5 +##############################################################################
    70.6 +#
    70.7 +# Copyright (c) 2004, 2005 Zope Corporation and Contributors.
    70.8 +# All Rights Reserved.
    70.9 +#
   70.10 +# This software is subject to the provisions of the Zope Public License,
   70.11 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
   70.12 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
   70.13 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   70.14 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
   70.15 +# FOR A PARTICULAR PURPOSE.
   70.16 +#
   70.17 +##############################################################################
   70.18 +"""Test browser menus
   70.19 +
   70.20 +$Id: test_menu.py 14595 2005-07-12 21:26:12Z philikon $
   70.21 +"""
   70.22 +import os, sys
   70.23 +if __name__ == '__main__':
   70.24 +    execfile(os.path.join(sys.path[0], 'framework.py'))
   70.25 +
   70.26 +def test_menu():
   70.27 +    """
   70.28 +    Test menus
   70.29 +
   70.30 +    Before we can start we need to set up a few things.  For menu
   70.31 +    configuration, we have to start a new interaction:
   70.32 +
   70.33 +      >>> import Products.Five.browser.tests
   70.34 +      >>> from Products.Five import zcml
   70.35 +      >>> zcml.load_config("configure.zcml", Products.Five)
   70.36 +      >>> zcml.load_config('menu.zcml', package=Products.Five.browser.tests)
   70.37 +
   70.38 +      >>> from Products.Five.security import newInteraction
   70.39 +      >>> newInteraction()
   70.40 +
   70.41 +    Now for some actual testing... Let's look up the menu we registered:
   70.42 +
   70.43 +      >>> from Products.Five.traversable import FakeRequest
   70.44 +      >>> from zope.app.publisher.browser.globalbrowsermenuservice import \\
   70.45 +      ...     globalBrowserMenuService
   70.46 +
   70.47 +      >>> request = FakeRequest()
   70.48 +      >>> menu = globalBrowserMenuService.getMenu(
   70.49 +      ...     'testmenu', self.folder, request)
   70.50 +
   70.51 +    It should have 
   70.52 +
   70.53 +      >>> len(menu)
   70.54 +      4
   70.55 +
   70.56 +    Sort menu items by title so we get a stable testable result:
   70.57 +
   70.58 +      >>> menu.sort(lambda x, y: cmp(x['title'], y['title']))
   70.59 +      >>> from pprint import pprint
   70.60 +      >>> pprint(menu)
   70.61 +      [{'action': '@@cockatiel_menu_public.html',
   70.62 +        'description': '',
   70.63 +        'extra': None,
   70.64 +        'selected': '',
   70.65 +        'title': u'Page in a menu (public)'},
   70.66 +       {'action': u'seagull.html',
   70.67 +        'description': u'This is a test menu item',
   70.68 +        'extra': None,
   70.69 +        'selected': '',
   70.70 +        'title': u'Test Menu Item'},
   70.71 +       {'action': u'parakeet.html',
   70.72 +        'description': u'This is a test menu item',
   70.73 +        'extra': None,
   70.74 +        'selected': '',
   70.75 +        'title': u'Test Menu Item 2'},
   70.76 +       {'action': u'falcon.html',
   70.77 +        'description': u'This is a test menu item',
   70.78 +        'extra': None,
   70.79 +        'selected': '',
   70.80 +        'title': u'Test Menu Item 3'}]
   70.81 +
   70.82 +    Let's create a manager user account and log in.
   70.83 +
   70.84 +      >>> uf = self.folder.acl_users
   70.85 +      >>> uf._doAddUser('manager', 'r00t', ['Manager'], [])
   70.86 +      >>> self.login('manager')
   70.87 +      >>> newInteraction()
   70.88 +
   70.89 +      >>> menu = globalBrowserMenuService.getMenu(
   70.90 +      ...     'testmenu', self.folder, request)
   70.91 +
   70.92 +    We should get the protected menu items now:
   70.93 +
   70.94 +      >>> len(menu)
   70.95 +      7
   70.96 +
   70.97 +      >>> menu.sort(lambda x, y: cmp(x['title'], y['title']))
   70.98 +      >>> pprint(menu)
   70.99 +      [{'action': '@@cockatiel_menu_protected.html',
  70.100 +        'description': '',
  70.101 +        'extra': None,
  70.102 +        'selected': '',
  70.103 +        'title': u'Page in a menu (protected)'},
  70.104 +       {'action': '@@cockatiel_menu_public.html',
  70.105 +       'description': '',
  70.106 +       'extra': None,
  70.107 +       'selected': '',
  70.108 +       'title': u'Page in a menu (public)'},
  70.109 +      {'action': u'seagull.html',
  70.110 +       'description': u'This is a protected test menu item',
  70.111 +       'extra': None,
  70.112 +       'selected': '',
  70.113 +       'title': u'Protected Test Menu Item'},
  70.114 +      {'action': u'falcon.html',
  70.115 +       'description': u'This is a protected test menu item',
  70.116 +       'extra': None,
  70.117 +       'selected': '',
  70.118 +       'title': u'Protected Test Menu Item 2'},
  70.119 +      {'action': u'seagull.html',
  70.120 +       'description': u'This is a test menu item',
  70.121 +       'extra': None,
  70.122 +       'selected': '',
  70.123 +       'title': u'Test Menu Item'},
  70.124 +      {'action': u'parakeet.html',
  70.125 +       'description': u'This is a test menu item',
  70.126 +       'extra': None,
  70.127 +       'selected': '',
  70.128 +        'title': u'Test Menu Item 2'},
  70.129 +      {'action': u'falcon.html',
  70.130 +       'description': u'This is a test menu item',
  70.131 +       'extra': None,
  70.132 +       'selected': '',
  70.133 +       'title': u'Test Menu Item 3'}]
  70.134 +
  70.135 +
  70.136 +    Clean up:
  70.137 +
  70.138 +      >>> from zope.app.tests.placelesssetup import tearDown
  70.139 +      >>> tearDown()
  70.140 +    """
  70.141 +
  70.142 +def test_suite():
  70.143 +    from Testing.ZopeTestCase import ZopeDocTestSuite
  70.144 +    return ZopeDocTestSuite()
  70.145 +
  70.146 +if __name__ == '__main__':
  70.147 +    framework()
    71.1 new file mode 100644
    71.2 --- /dev/null
    71.3 +++ b/browser/tests/test_pages.py
    71.4 @@ -0,0 +1,78 @@
    71.5 +##############################################################################
    71.6 +#
    71.7 +# Copyright (c) 2004, 2005 Zope Corporation and Contributors.
    71.8 +# All Rights Reserved.
    71.9 +#
   71.10 +# This software is subject to the provisions of the Zope Public License,
   71.11 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
   71.12 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
   71.13 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   71.14 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
   71.15 +# FOR A PARTICULAR PURPOSE.
   71.16 +#
   71.17 +##############################################################################
   71.18 +"""Test browser pages
   71.19 +
   71.20 +$Id: test_pages.py 18860 2005-10-24 12:14:34Z philikon $
   71.21 +"""
   71.22 +import os, sys
   71.23 +if __name__ == '__main__':
   71.24 +    execfile(os.path.join(sys.path[0], 'framework.py'))
   71.25 +
   71.26 +def test_ViewAcquisitionWrapping():
   71.27 +    """
   71.28 +      >>> import Products.Five.browser.tests
   71.29 +      >>> from Products.Five import zcml
   71.30 +      >>> zcml.load_config("configure.zcml", Products.Five)
   71.31 +      >>> zcml.load_config('pages.zcml', package=Products.Five.browser.tests)
   71.32 +
   71.33 +      >>> from Products.Five.tests.testing.simplecontent import manage_addSimpleContent
   71.34 +      >>> manage_addSimpleContent(self.folder, 'testoid', 'Testoid')
   71.35 +      >>> uf = self.folder.acl_users
   71.36 +      >>> uf._doAddUser('manager', 'r00t', ['Manager'], [])
   71.37 +      >>> self.login('manager')
   71.38 +
   71.39 +      >>> view = self.folder.unrestrictedTraverse('testoid/eagle.txt')
   71.40 +      >>> view is not None
   71.41 +      True
   71.42 +      >>> from Products.Five.browser.tests.pages import SimpleView
   71.43 +      >>> isinstance(view, SimpleView)
   71.44 +      True
   71.45 +      >>> view()
   71.46 +      'The eagle has landed'
   71.47 +
   71.48 +    This sucks, but we know it
   71.49 +
   71.50 +      >>> from Acquisition import aq_parent, aq_base
   71.51 +      >>> aq_parent(view.context) is view
   71.52 +      True
   71.53 +
   71.54 +    This is the right way to get the context parent
   71.55 +
   71.56 +      >>> view.context.aq_inner.aq_parent is not view
   71.57 +      True
   71.58 +      >>> view.context.aq_inner.aq_parent is self.folder
   71.59 +      True
   71.60 +
   71.61 +    Clean up:
   71.62 +
   71.63 +      >>> from zope.app.tests.placelesssetup import tearDown
   71.64 +      >>> tearDown()
   71.65 +    """
   71.66 +
   71.67 +def test_suite():
   71.68 +    import unittest
   71.69 +    from Testing.ZopeTestCase import installProduct, ZopeDocTestSuite
   71.70 +    from Testing.ZopeTestCase import ZopeDocFileSuite
   71.71 +    from Testing.ZopeTestCase import FunctionalDocFileSuite
   71.72 +    installProduct('PythonScripts')  # for Five.tests.testing.restricted
   71.73 +    return unittest.TestSuite((
   71.74 +        ZopeDocTestSuite(),
   71.75 +        ZopeDocFileSuite('pages.txt', package='Products.Five.browser.tests'),
   71.76 +        FunctionalDocFileSuite('pages_ftest.txt',
   71.77 +                               package='Products.Five.browser.tests')
   71.78 +        ))
   71.79 +    return suite
   71.80 +
   71.81 +if __name__ == '__main__':
   71.82 +    framework()
    72.1 new file mode 100644
    72.2 --- /dev/null
    72.3 +++ b/browser/tests/test_recurse.py
    72.4 @@ -0,0 +1,78 @@
    72.5 +##############################################################################
    72.6 +#
    72.7 +# Copyright (c) 2004, 2005 Zope Corporation and Contributors.
    72.8 +# All Rights Reserved.
    72.9 +#
   72.10 +# This software is subject to the provisions of the Zope Public License,
   72.11 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
   72.12 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
   72.13 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   72.14 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
   72.15 +# FOR A PARTICULAR PURPOSE.
   72.16 +#
   72.17 +##############################################################################
   72.18 +"""Test default view recursion
   72.19 +
   72.20 +$Id: test_recurse.py 14595 2005-07-12 21:26:12Z philikon $
   72.21 +"""
   72.22 +import os, sys
   72.23 +if __name__ == '__main__':
   72.24 +    execfile(os.path.join(sys.path[0], 'framework.py'))
   72.25 +
   72.26 +def test_recursion():
   72.27 +    """
   72.28 +    Test recursion
   72.29 +
   72.30 +      >>> from zope.app.tests.placelesssetup import setUp, tearDown
   72.31 +      >>> setUp()
   72.32 +
   72.33 +    This test makes sure that recursion is avoided for view lookup.
   72.34 +    First, we need to set up a stub interface...
   72.35 +
   72.36 +      >>> from zope.interface import Interface, implements
   72.37 +      >>> class IRecurse(Interface):
   72.38 +      ...     pass
   72.39 +      ...
   72.40 +
   72.41 +    and a class that is callable and has a view method:
   72.42 +
   72.43 +      >>> from OFS.Traversable import Traversable
   72.44 +      >>> class Recurse(Traversable):
   72.45 +      ...     implements(IRecurse)
   72.46 +      ...     def view(self):
   72.47 +      ...         return self()
   72.48 +      ...     def __call__(self):
   72.49 +      ...         return 'foo'
   72.50 +      ...
   72.51 +
   72.52 +    Now we make the class default viewable and register a default view
   72.53 +    name for it:
   72.54 +
   72.55 +      >>> from Products.Five.fiveconfigure import classDefaultViewable
   72.56 +      >>> classDefaultViewable(Recurse)
   72.57 +
   72.58 +      >>> from zope.app import zapi
   72.59 +      >>> from zope.publisher.interfaces.browser import IBrowserRequest
   72.60 +      >>> pres = zapi.getGlobalService('Presentation')
   72.61 +      >>> pres.setDefaultViewName(IRecurse, IBrowserRequest, 'view')
   72.62 +
   72.63 +    Here comes the actual test:
   72.64 +
   72.65 +      >>> ob = Recurse()
   72.66 +      >>> ob.view()
   72.67 +      'foo'
   72.68 +      >>> ob()
   72.69 +      'foo'
   72.70 +
   72.71 +
   72.72 +    Clean up:
   72.73 +
   72.74 +      >>> tearDown()
   72.75 +    """
   72.76 +
   72.77 +def test_suite():
   72.78 +    from Testing.ZopeTestCase import ZopeDocTestSuite
   72.79 +    return ZopeDocTestSuite()
   72.80 +
   72.81 +if __name__ == '__main__':
   72.82 +    framework()
    73.1 new file mode 100644
    73.2 --- /dev/null
    73.3 +++ b/browser/tests/test_resource.py
    73.4 @@ -0,0 +1,35 @@
    73.5 +##############################################################################
    73.6 +#
    73.7 +# Copyright (c) 2004, 2005 Zope Corporation and Contributors.
    73.8 +# All Rights Reserved.
    73.9 +#
   73.10 +# This software is subject to the provisions of the Zope Public License,
   73.11 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
   73.12 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
   73.13 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   73.14 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
   73.15 +# FOR A PARTICULAR PURPOSE.
   73.16 +#
   73.17 +##############################################################################
   73.18 +"""Test browser resources
   73.19 +
   73.20 +$Id: test_resource.py 17853 2005-09-25 13:34:25Z tseaver $
   73.21 +"""
   73.22 +import os, sys
   73.23 +if __name__ == '__main__':
   73.24 +    execfile(os.path.join(sys.path[0], 'framework.py'))
   73.25 +
   73.26 +def test_suite():
   73.27 +    import unittest
   73.28 +    from Testing.ZopeTestCase import installProduct, ZopeDocFileSuite
   73.29 +    from Testing.ZopeTestCase import FunctionalDocFileSuite
   73.30 +    installProduct('PythonScripts')  # for Five.tests.testing.restricted
   73.31 +    return unittest.TestSuite((
   73.32 +            ZopeDocFileSuite('resource.txt',
   73.33 +                             package='Products.Five.browser.tests'),
   73.34 +            FunctionalDocFileSuite('resource_ftest.txt',
   73.35 +                                   package='Products.Five.browser.tests'),
   73.36 +            ))
   73.37 +
   73.38 +if __name__ == '__main__':
   73.39 +    framework()
    74.1 new file mode 100644
    74.2 --- /dev/null
    74.3 +++ b/browser/tests/test_skin.py
    74.4 @@ -0,0 +1,28 @@
    74.5 +##############################################################################
    74.6 +#
    74.7 +# Copyright (c) 2005 Zope Corporation and Contributors.
    74.8 +# All Rights Reserved.
    74.9 +#
   74.10 +# This software is subject to the provisions of the Zope Public License,
   74.11 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
   74.12 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
   74.13 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   74.14 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
   74.15 +# FOR A PARTICULAR PURPOSE.
   74.16 +#
   74.17 +##############################################################################
   74.18 +"""Test browser pages
   74.19 +
   74.20 +$Id: test_skin.py 20072 2005-11-19 09:31:28Z philikon $
   74.21 +"""
   74.22 +import os, sys
   74.23 +if __name__ == '__main__':
   74.24 +    execfile(os.path.join(sys.path[0], 'framework.py'))
   74.25 +
   74.26 +def test_suite():
   74.27 +    from Testing.ZopeTestCase import FunctionalDocFileSuite
   74.28 +    return FunctionalDocFileSuite('skin.txt',
   74.29 +                                  package='Products.Five.browser.tests')
   74.30 +
   74.31 +if __name__ == '__main__':
   74.32 +    framework()
    75.1 new file mode 100644
    75.2 --- /dev/null
    75.3 +++ b/browser/tests/test_traversable.py
    75.4 @@ -0,0 +1,106 @@
    75.5 +##############################################################################
    75.6 +#
    75.7 +# Copyright (c) 2004, 2005 Zope Corporation and Contributors.
    75.8 +# All Rights Reserved.
    75.9 +#
   75.10 +# This software is subject to the provisions of the Zope Public License,
   75.11 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
   75.12 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
   75.13 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   75.14 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
   75.15 +# FOR A PARTICULAR PURPOSE.
   75.16 +#
   75.17 +##############################################################################
   75.18 +"""Test Five-traversable classes
   75.19 +
   75.20 +$Id: test_traversable.py 17853 2005-09-25 13:34:25Z tseaver $
   75.21 +"""
   75.22 +import os, sys
   75.23 +if __name__ == '__main__':
   75.24 +    execfile(os.path.join(sys.path[0], 'framework.py'))
   75.25 +
   75.26 +def test_traversable():
   75.27 +    """
   75.28 +    Test the behaviour of Five-traversable classes.
   75.29 +
   75.30 +      >>> import Products.Five
   75.31 +      >>> from Products.Five import zcml
   75.32 +      >>> zcml.load_config("configure.zcml", Products.Five)
   75.33 +
   75.34 +    ``SimpleContent`` is a traversable class by default.  Its fallback
   75.35 +    traverser should raise NotFound when traversal fails.  (Note: If
   75.36 +    we return None in __fallback_traverse__, this test passes but for
   75.37 +    the wrong reason: None doesn't have a docstring so BaseRequest
   75.38 +    raises NotFoundError.)
   75.39 +
   75.40 +      >>> from Products.Five.tests.testing.simplecontent import manage_addSimpleContent
   75.41 +      >>> manage_addSimpleContent(self.folder, 'testoid', 'Testoid')
   75.42 +      >>> print http(r'''
   75.43 +      ... GET /test_folder_1_/testoid/doesntexist HTTP/1.1
   75.44 +      ... ''')
   75.45 +      HTTP/1.1 404 Not Found
   75.46 +      ...
   75.47 +
   75.48 +    Now let's take class which already has a __bobo_traverse__ method.
   75.49 +    Five should correctly use that as a fallback.
   75.50 +
   75.51 +      >>> configure_zcml = '''
   75.52 +      ... <configure xmlns="http://namespaces.zope.org/zope"
   75.53 +      ...            xmlns:meta="http://namespaces.zope.org/meta"
   75.54 +      ...            xmlns:browser="http://namespaces.zope.org/browser"
   75.55 +      ...            xmlns:five="http://namespaces.zope.org/five">
   75.56 +      ... 
   75.57 +      ... <!-- make the zope2.Public permission work -->
   75.58 +      ... <meta:redefinePermission from="zope2.Public" to="zope.Public" />
   75.59 +      ... 
   75.60 +      ... <five:traversable
   75.61 +      ...     class="Products.Five.tests.testing.fancycontent.FancyContent"
   75.62 +      ...     />
   75.63 +      ... 
   75.64 +      ... <browser:page
   75.65 +      ...     for="Products.Five.tests.testing.fancycontent.IFancyContent"
   75.66 +      ...     class="Products.Five.browser.tests.pages.FancyView"
   75.67 +      ...     attribute="view"
   75.68 +      ...     name="fancy"
   75.69 +      ...     permission="zope2.Public"
   75.70 +      ...     />
   75.71 +      ... 
   75.72 +      ... </configure>'''
   75.73 +      >>> zcml.load_string(configure_zcml)
   75.74 +
   75.75 +      >>> from Products.Five.tests.testing.fancycontent import manage_addFancyContent
   75.76 +      >>> info = manage_addFancyContent(self.folder, 'fancy', '')
   75.77 +
   75.78 +    In the following test we let the original __bobo_traverse__ method
   75.79 +    kick in:
   75.80 +
   75.81 +      >>> print http(r'''
   75.82 +      ... GET /test_folder_1_/fancy/something-else HTTP/1.1
   75.83 +      ... ''')
   75.84 +      HTTP/1.1 200 OK
   75.85 +      ...
   75.86 +      something-else
   75.87 +
   75.88 +    Of course we also need to make sure that Zope 3 style view lookup
   75.89 +    actually works:
   75.90 +
   75.91 +      >>> print http(r'''
   75.92 +      ... GET /test_folder_1_/fancy/fancy HTTP/1.1
   75.93 +      ... ''')
   75.94 +      HTTP/1.1 200 OK
   75.95 +      ...
   75.96 +      Fancy, fancy
   75.97 +
   75.98 +
   75.99 +    Clean up:
  75.100 +
  75.101 +      >>> from zope.app.tests.placelesssetup import tearDown
  75.102 +      >>> tearDown()
  75.103 +    """
  75.104 +
  75.105 +def test_suite():
  75.106 +    from Testing.ZopeTestCase import FunctionalDocTestSuite
  75.107 +    return FunctionalDocTestSuite()
  75.108 +
  75.109 +if __name__ == '__main__':
  75.110 +    framework()
    76.1 new file mode 100644
    76.2 --- /dev/null
    76.3 +++ b/configure.zcml
    76.4 @@ -0,0 +1,47 @@
    76.5 +<configure xmlns="http://namespaces.zope.org/zope"
    76.6 +           xmlns:five="http://namespaces.zope.org/five">
    76.7 +
    76.8 +  <include file="meta.zcml" />
    76.9 +  <include file="services.zcml" />
   76.10 +  <include file="interfaces.zcml" />
   76.11 +  <include file="permissions.zcml" />
   76.12 +  <include file="i18n.zcml" />
   76.13 +  <include file="deprecated.zcml"/>
   76.14 +  <include package=".site" />
   76.15 +  <include package=".browser" />
   76.16 +  <include package=".form" />
   76.17 +  <include package=".skin" />
   76.18 +  <include package=".utilities" />
   76.19 +
   76.20 +  <include package="zope.app.event" />
   76.21 +  <include package="zope.app.traversing" />
   76.22 +
   76.23 +  <!-- do 'traditional' traversing by default; needed by ZPT -->
   76.24 +  <adapter
   76.25 +      for="*"
   76.26 +      factory=".traversable.FiveTraversable"
   76.27 +      provides="zope.app.traversing.interfaces.ITraversable"
   76.28 +      />
   76.29 +
   76.30 +  <adapter
   76.31 +      for="*"
   76.32 +      factory="zope.app.traversing.adapters.Traverser"
   76.33 +      provides="zope.app.traversing.interfaces.ITraverser"
   76.34 +      />
   76.35 +
   76.36 +  <adapter
   76.37 +      for="*"
   76.38 +      factory=".viewable.BrowserDefault"
   76.39 +      provides=".interfaces.IBrowserDefault"
   76.40 +      />
   76.41 +
   76.42 +  <!-- this is really lying, but it's to please checkContainer -->
   76.43 +  <five:implements class="OFS.ObjectManager.ObjectManager"
   76.44 +                   interface="zope.app.container.interfaces.IContainer" />
   76.45 +
   76.46 +  <!-- make Zope 2's REQUEST implement the right thing -->
   76.47 +  <five:implements class="ZPublisher.HTTPRequest.HTTPRequest"
   76.48 +                   interface="zope.publisher.interfaces.browser.IBrowserRequest"
   76.49 +                   />
   76.50 +
   76.51 +</configure>
    77.1 new file mode 100644
    77.2 --- /dev/null
    77.3 +++ b/deprecated.zcml
    77.4 @@ -0,0 +1,51 @@
    77.5 +<configure xmlns="http://namespaces.zope.org/zope"
    77.6 +           xmlns:five="http://namespaces.zope.org/five">
    77.7 +
    77.8 +  <!-- deprecated in core Zope, should be fixed there in Zope 2.9 -->
    77.9 +
   77.10 +  <five:deprecatedManageAddDelete
   77.11 +      class="AccessControl.User.BasicUserFolder"/>
   77.12 +
   77.13 +  <five:deprecatedManageAddDelete
   77.14 +      class="App.Factory.Factory"/>
   77.15 +  <five:deprecatedManageAddDelete
   77.16 +      class="App.Permission.Permission"/>
   77.17 +
   77.18 +  <five:deprecatedManageAddDelete
   77.19 +      class="HelpSys.HelpTopic.HelpTopicBase"/>
   77.20 +
   77.21 +  <five:deprecatedManageAddDelete
   77.22 +      class="OFS.Cache.CacheManager"/>
   77.23 +
   77.24 +  <five:deprecatedManageAddDelete
   77.25 +      class="Products.OFSP.Draft.Draft"/>
   77.26 +  <five:deprecatedManageAddDelete
   77.27 +      class="Products.OFSP.Version.Version"/>
   77.28 +
   77.29 +  <five:deprecatedManageAddDelete
   77.30 +      class="Products.PythonScripts.PythonScript.PythonScript"/>
   77.31 +
   77.32 +  <five:deprecatedManageAddDelete
   77.33 +      class="Products.Sessions.BrowserIdManager.BrowserIdManager"/>
   77.34 +  <five:deprecatedManageAddDelete
   77.35 +      class="Products.Sessions.SessionDataManager.SessionDataManager"/>
   77.36 +
   77.37 +  <five:deprecatedManageAddDelete
   77.38 +      class="Products.SiteAccess.VirtualHostMonster.VirtualHostMonster"/>
   77.39 +  <five:deprecatedManageAddDelete
   77.40 +      class="Products.SiteAccess.SiteRoot.Traverser"/>
   77.41 +
   77.42 +  <five:deprecatedManageAddDelete
   77.43 +      class="Products.SiteErrorLog.SiteErrorLog.SiteErrorLog"/>
   77.44 +
   77.45 +  <five:deprecatedManageAddDelete
   77.46 +      class="Products.ZCatalog.CatalogAwareness.CatalogAware"/>
   77.47 +  <five:deprecatedManageAddDelete
   77.48 +      class="Products.ZCatalog.CatalogPathAwareness.CatalogAware"/>
   77.49 +
   77.50 +  <five:deprecatedManageAddDelete
   77.51 +      class="ZClasses.Property.ZCommonSheet"/>
   77.52 +  <five:deprecatedManageAddDelete
   77.53 +      class="ZClasses.ZClass.ZClass"/>
   77.54 +
   77.55 +</configure>
    78.1 new file mode 100644
    78.2 --- /dev/null
    78.3 +++ b/doc/ZopePublicLicense.txt
    78.4 @@ -0,0 +1,54 @@
    78.5 +Zope Public License (ZPL) Version 2.1
    78.6 +-------------------------------------
    78.7 +
    78.8 +A copyright notice accompanies this license document that
    78.9 +identifies the copyright holders.
   78.10 +
   78.11 +This license has been certified as open source. It has also
   78.12 +been designated as GPL compatible by the Free Software
   78.13 +Foundation (FSF).
   78.14 +
   78.15 +Redistribution and use in source and binary forms, with or
   78.16 +without modification, are permitted provided that the
   78.17 +following conditions are met:
   78.18 +
   78.19 +1. Redistributions in source code must retain the
   78.20 +   accompanying copyright notice, this list of conditions,
   78.21 +   and the following disclaimer.
   78.22 +
   78.23 +2. Redistributions in binary form must reproduce the accompanying
   78.24 +   copyright notice, this list of conditions, and the
   78.25 +   following disclaimer in the documentation and/or other
   78.26 +   materials provided with the distribution.
   78.27 +
   78.28 +3. Names of the copyright holders must not be used to
   78.29 +   endorse or promote products derived from this software
   78.30 +   without prior written permission from the copyright
   78.31 +   holders.
   78.32 +
   78.33 +4. The right to distribute this software or to use it for
   78.34 +   any purpose does not give you the right to use
   78.35 +   Servicemarks (sm) or Trademarks (tm) of the copyright
   78.36 +   holders. Use of them is covered by separate agreement
   78.37 +   with the copyright holders.
   78.38 +
   78.39 +5. If any files are modified, you must cause the modified
   78.40 +   files to carry prominent notices stating that you changed
   78.41 +   the files and the date of any change.
   78.42 +
   78.43 +Disclaimer
   78.44 +
   78.45 +  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS''
   78.46 +  AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
   78.47 +  NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
   78.48 +  AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
   78.49 +  NO EVENT SHALL THE COPYRIGHT HOLDERS BE
   78.50 +  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
   78.51 +  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   78.52 +  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   78.53 +  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   78.54 +  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   78.55 +  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
   78.56 +  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
   78.57 +  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
   78.58 +  DAMAGE.
    79.1 new file mode 100644
    79.2 --- /dev/null
    79.3 +++ b/doc/directives.txt
    79.4 @@ -0,0 +1,197 @@
    79.5 +=================================
    79.6 +ZCML Directives supported by Five
    79.7 +=================================
    79.8 +
    79.9 +Five tries to use the Zope 3 ZCML directives where possible, though
   79.10 +does sometimes subset the possible attributes. It also introduces a
   79.11 +few directives of its own under the ``five`` namespace.
   79.12 +
   79.13 +Directives are listed per namespace, in alphabetic order.
   79.14 +
   79.15 +zope ``http://namespaces.zope.org/zope``
   79.16 +========================================
   79.17 +
   79.18 +adapter
   79.19 +-------
   79.20 +
   79.21 +Hook an adapter factory to an interface.
   79.22 +
   79.23 +content
   79.24 +-------
   79.25 +
   79.26 +Declare interface and permissions on content object. Declares Zope 2
   79.27 +permissions.
   79.28 +
   79.29 +permission
   79.30 +----------
   79.31 +
   79.32 +Way to make Zope 2 permissions available to Five, ``title`` is
   79.33 +permission name.
   79.34 +
   79.35 +redefinePermission
   79.36 +------------------
   79.37 +
   79.38 +Redefine a permission in included ZCML as another one.
   79.39 +
   79.40 +service
   79.41 +-------
   79.42 +
   79.43 +Declare a global service
   79.44 +
   79.45 +serviceType
   79.46 +-----------
   79.47 +
   79.48 +Declare a type of service.
   79.49 +
   79.50 +skin
   79.51 +----
   79.52 +
   79.53 +Declare a skin, consisting of layers.
   79.54 +
   79.55 +utility
   79.56 +-------
   79.57 +
   79.58 +Declare a global utility.
   79.59 +
   79.60 +interface
   79.61 +---------
   79.62 +
   79.63 +Register an interface in ZCML.
   79.64 +
   79.65 +factory
   79.66 +-------
   79.67 +
   79.68 +Register an object factory.
   79.69 +
   79.70 +modulealias
   79.71 +-----------
   79.72 +
   79.73 +Provide a module under an alias name, e.g. for persistent backward
   79.74 +compatability.
   79.75 +
   79.76 +hook
   79.77 +----
   79.78 +
   79.79 +Install a hook on a hookable object.
   79.80 +
   79.81 +browser ``http://namespaces.zope.org/browser``
   79.82 +==============================================
   79.83 +
   79.84 +page
   79.85 +----
   79.86 +
   79.87 +Declare a page view for an interface. Permission is a Zope 2
   79.88 +permission.
   79.89 +
   79.90 +pages
   79.91 +-----
   79.92 +
   79.93 +Declare multiple page views for an interface. Permissions are Zope 2
   79.94 +permissions.
   79.95 +
   79.96 +defaultView
   79.97 +-----------
   79.98 +
   79.99 +Declare the name of the view that should be used for the default when viewing
  79.100 +the object; i.e. when the object is traversed to without a view.
  79.101 +
  79.102 +defaultSkin
  79.103 +-----------
  79.104 +
  79.105 +Declare the default skin used.
  79.106 +
  79.107 +editform
  79.108 +--------
  79.109 +
  79.110 +Create an edit form based on a schema.
  79.111 +
  79.112 +addform
  79.113 +--------
  79.114 +
  79.115 +Create an add form based on a schema.
  79.116 +
  79.117 +layer
  79.118 +-----
  79.119 +
  79.120 +Declare a layer.
  79.121 +
  79.122 +menu
  79.123 +----
  79.124 +
  79.125 +Declare a menu
  79.126 +
  79.127 +menuItem, menuItems
  79.128 +-------------------
  79.129 +
  79.130 +Declare menuItems
  79.131 +
  79.132 +five ``http://namespaces.zope.org/five``
  79.133 +========================================
  79.134 +
  79.135 +implements
  79.136 +----------
  79.137 +
  79.138 +Make a class declare it implements an interface.
  79.139 +
  79.140 +loadProducts
  79.141 +------------
  79.142 +
  79.143 +Loads ZCML in all Zope 2 products. First processes all ``meta.zcml``
  79.144 +files, then processes all ``configure.zcml`` files.
  79.145 +
  79.146 +loadProductsOverrides
  79.147 +---------------------
  79.148 +
  79.149 +Loads overriding ZCML in all products (``overrides.zcml``).
  79.150 +
  79.151 +traversable
  79.152 +-----------
  79.153 +
  79.154 +Make a Zope 2 content class traversable in the Zope 3 manner using
  79.155 +Five. This is used to attached views, resources and other things to
  79.156 +Zope 2 objects.
  79.157 +
  79.158 +defaultViewable
  79.159 +---------------
  79.160 +
  79.161 +Make a Zope 2 content class use a Zope 3 default view when looking at
  79.162 +it without any paths appended to it. This works then instead of
  79.163 +``index_html`` in Zope 2.
  79.164 +
  79.165 +sizable
  79.166 +-------
  79.167 +
  79.168 +Retrieve size information for a Zope 2 content class via a Zope 3
  79.169 +style ``ISized`` adapter.
  79.170 +
  79.171 +containerEvents
  79.172 +---------------
  79.173 +
  79.174 +Make events be sent for Zope 2 container objects, instead of calling old
  79.175 +methods like ``manage_afterAdd``. These old methods will still be called
  79.176 +for classes specified in a ``deprecatedManageAddDelete`` directive.
  79.177 +
  79.178 +deprecatedManageAddDelete
  79.179 +-------------------------
  79.180 +
  79.181 +Specify a class that needs its old deprecated methods like
  79.182 +``manage_afterAdd``, ``manage_beforeDelete`` and ``manage_afterClone``
  79.183 +to be called. Modern classes should use event subscribers instead.
  79.184 +
  79.185 +pagesFromDirectory
  79.186 +------------------
  79.187 +
  79.188 +Loads all files with .pt extension in a directory as pages.
  79.189 +
  79.190 +registerClass
  79.191 +-------------
  79.192 +
  79.193 +Registers Five content with Zope 2.
  79.194 +
  79.195 +localsite
  79.196 +---------
  79.197 +
  79.198 +Turns a class into an implementation of ``IPossibleSite`` so that its
  79.199 +instances can be serve as local sites.  Unless otherwise specified, a
  79.200 +default implementation's methods will be used to make the class comply
  79.201 +with the ``IPossibleSite`` interface.
    80.1 new file mode 100644
    80.2 --- /dev/null
    80.3 +++ b/doc/event.txt
    80.4 @@ -0,0 +1,287 @@
    80.5 +Events in Zope 2.9
    80.6 +==================
    80.7 +
    80.8 +Zope 2.9 (and Zope 2.8 when using Five 1.2) introduces a big change:
    80.9 +Zope 3 style container events.
   80.10 +
   80.11 +With container events, you finally have the ability to react to things
   80.12 +happening to objects without have to subclass ``manage_afterAdd``,
   80.13 +``manage_beforeDelete`` or ``manage_afterClone``. Instead, you just have
   80.14 +to register a subscriber for the appropriate event, for instance
   80.15 +IObjectAddedEvent, and make it do the work.
   80.16 +
   80.17 +Indeed, the old methods like ``manage_afterAdd`` are now deprecated, you
   80.18 +shouldn't use them anymore.
   80.19 +
   80.20 +Let's see how to migrate your products.
   80.21 +
   80.22 +Old product
   80.23 +-----------
   80.24 +
   80.25 +Suppose that in an old product you have code that needs to register
   80.26 +through a central tool whenever a document is created. Or it could be
   80.27 +indexing itself. Or it could initialize an attribute according to its
   80.28 +current path. Code like::
   80.29 +
   80.30 +    class CoolDocument(...):
   80.31 +        ...
   80.32 +        def manage_afterAdd(self, item, container):
   80.33 +            self.mangled_path = mangle('/'.join(self.getPhysicalPath()))
   80.34 +            getToolByName(self, 'portal_cool').registerCool(self)
   80.35 +            super(CoolDocument, self).manage_afterAdd(item, container)
   80.36 +
   80.37 +        def manage_afterClone(self, item):
   80.38 +            self.mangled_path = mangle('/'.join(self.getPhysicalPath()))
   80.39 +            getToolByName(self, 'portal_cool').registerCool(self)
   80.40 +            super(CoolDocument, self).manage_afterClone(item)
   80.41 +
   80.42 +        def manage_beforeDelete(self, item, container):
   80.43 +            super(CoolDocument, self).manage_beforeDelete(item, container)
   80.44 +            getToolByName(self, 'portal_cool').unregisterCool(self)
   80.45 +
   80.46 +This would be the best practice in Zope 2.8. Note the use of ``super()``
   80.47 +to call the base class, which is often omitted because people "know"
   80.48 +that SimpleItem for instance doesn't do anything in these methods.
   80.49 +
   80.50 +If you run this code in Zope 2.9, you will get deprecation warnings,
   80.51 +telling you that::
   80.52 +
   80.53 +    Calling Products.CoolProduct.CoolDocument.CoolDocument.manage_afterAdd
   80.54 +    is deprecated when using Five, instead use event subscribers or mark
   80.55 +    the class with <five:deprecatedManageAddDelete/>
   80.56 +
   80.57 +Using five:deprecatedManageAddDelete
   80.58 +------------------------------------
   80.59 +
   80.60 +The simplest thing you can do to deal with the deprecation warnings, and
   80.61 +have correct behavior, is to add in your products a ``configure.zcml``
   80.62 +file containing::
   80.63 +
   80.64 +    <configure
   80.65 +        xmlns="http://namespaces.zope.org/zope"
   80.66 +        xmlns:five="http://namespaces.zope.org/five">
   80.67 +
   80.68 +      <five:deprecatedManageAddDelete
   80.69 +          class="Products.CoolProduct.CoolDocument.CoolDocument"/>
   80.70 +
   80.71 +    </configure>
   80.72 +
   80.73 +This tells Zope that you acknowledge that your class contains deprecated
   80.74 +methods, and ask it to still call them in the proper manner. So Zope
   80.75 +will be sending events when an object is added, for instance, and in
   80.76 +addition call your old ``manage_afterAdd`` method.
   80.77 +
   80.78 +One subtlety here is that you may have to modify you methods to just do
   80.79 +their work, and not call their super class. This is necessary because
   80.80 +proper events are already dispatched to all relevant classes, and the
   80.81 +work of the super class will be done trough events, you must not redo it
   80.82 +by hand. If you call the super class, you will get a warning, saying for
   80.83 +instance::
   80.84 +
   80.85 +    CoolDocument.manage_afterAdd is deprecated and will be removed in
   80.86 +    Zope 2.11, you should use an IObjectAddedEvent subscriber instead.
   80.87 +
   80.88 +The fact that you must "just do your work" is especially important for
   80.89 +the rare cases where people subclass the ``manage_afterAdd`` of object
   80.90 +managers like folders, and decided to reimplement recursion into the
   80.91 +children themselves. If you do that, then there will be two recursions
   80.92 +going on in parallel, the one done by events, and the one done by your
   80.93 +code. This would be bad.
   80.94 +
   80.95 +Using subscribers
   80.96 +-----------------
   80.97 +
   80.98 +In the long run, and before Zope 2.11 where ``manage_afterAdd`` and
   80.99 +friends will be removed, you will want to use proper subscribers.
  80.100 +
  80.101 +First, you'll have to write a subscriber that "does the work", for
  80.102 +instance::
  80.103 +
  80.104 +    def addedCoolDocument(ob, event):
  80.105 +        """A Cool Document was added to a container."""
  80.106 +        self.mangled_path = mangle('/'.join(self.getPhysicalPath()))
  80.107 +
  80.108 +Note that we're not calling the ``portal_cool`` tool anymore, because
  80.109 +presumably this tool will also be modified to do its work through
  80.110 +events, and will have a similar subscriber doing the necessary
  80.111 +``registerCool``. Note also that here we don't care about the event, but
  80.112 +in more complex cases we would.
  80.113 +
  80.114 +Now we have to register our subscriber for our object. To do that, we
  80.115 +need to "mark" our object through an interface. We can define in our
  80.116 +product's ``interfaces.py``::
  80.117 +
  80.118 +    from zope.interface import Interface, Attribute
  80.119 +
  80.120 +    class ICoolDocument(Interface):
  80.121 +        """Cool Document."""
  80.122 +        mangled_path = Attribute("Our mangled path.")
  80.123 +        ...
  80.124 +
  80.125 +Then the class CoolDocument is marked with this interface::
  80.126 +
  80.127 +    from zope.interface import implements
  80.128 +    from Products.CoolProduct.interfaces import ICoolDocument
  80.129 +    class CoolDocument(...):
  80.130 +        implements(ICoolDocument)
  80.131 +        ...
  80.132 +
  80.133 +Finally we must link the event and the interface to the subscriber using
  80.134 +zcml, so in ``configure.zcml`` we'll add::
  80.135 +
  80.136 +    ...
  80.137 +      <subscriber
  80.138 +          for="Products.CoolProduct.interfaces.ICoolDocument
  80.139 +               zope.app.container.interfaces.IObjectAddedEvent"
  80.140 +          handler="Products.CoolProduct.CoolDocument.addedCoolDocument"
  80.141 +          />
  80.142 +    ...
  80.143 +
  80.144 +And that's it, everything is plugged. Note that IObjectAddedEvent takes
  80.145 +care of both ``manage_afterAdd`` and ``manage_afterClone``, as it's sent
  80.146 +whenever a new object is placed into a container. However this won't
  80.147 +take care of moves and renamings, we'll see below how to do that.
  80.148 +
  80.149 +Event dispatching
  80.150 +-----------------
  80.151 +
  80.152 +When an IObjectEvent (from which all the events we're talking here
  80.153 +derive) is initially sent, it concerns one object. For instance, a
  80.154 +specific object is removed. The ``event.object`` attribute is this
  80.155 +object.
  80.156 +
  80.157 +To be able to know about removals, we could just subscribe to the
  80.158 +appropriate event using a standard event subscriber. In that case, we'd
  80.159 +have to filter "by hand" to check if the object removed is of the type
  80.160 +we're interested in, which would be a chore. In addition, any subobjects
  80.161 +of the removed object wouldn't know what happens to them, and for
  80.162 +instance they wouldn't have any way of doing some cleanup before they
  80.163 +disappear.
  80.164 +
  80.165 +To solve these two problems, Zope 3 has an additional mechanism by which
  80.166 +any IObjectEvent is redispatched using multi-adapters of the form ``(ob,
  80.167 +event)``, so that a subscriber can be specific about the type of object
  80.168 +it's interested in. Furthermore, this is done recursively for all
  80.169 +sublocations ``ob`` of the initial object. The ``event`` won't change
  80.170 +though, and ``event.object`` will still be the original object for which
  80.171 +the event was initially sent (this corresponds to ``self`` and ``item``
  80.172 +in the ``manage_afterAdd`` method -- ``self`` is ``ob``, and ``item`` is
  80.173 +``event.object``).
  80.174 +
  80.175 +Understanding the hierarchy of events is important to see how to
  80.176 +subscribe to them.
  80.177 +
  80.178 + * IObjectEvent is the most general. Any event focused on an object
  80.179 +   derives from this.
  80.180 +
  80.181 + * IObjectMovedEvent is sent when an object changes location or is
  80.182 +   renamed. It is quite general, as it also encompasses the case where
  80.183 +   there's no old location (addition) or no new location (removal).
  80.184 +
  80.185 + * IObjectAddedEvent and IObjectRemovedEvent both derive from
  80.186 +   IObjectMovedEvent.
  80.187 +
  80.188 + * IObjectCopiedEvent is sent just after an object copy is made, but
  80.189 +   this doesn't mean the object has been put into its new container yet,
  80.190 +   so it doesn't have a location.
  80.191 +
  80.192 +There are only a few basic use cases about what one wants to do with
  80.193 +respect to events (but you might want to read the full story in
  80.194 +Five/tests/event.txt).
  80.195 +
  80.196 +The first use case is the one where the object has to be aware of its
  80.197 +path, like in the CoolDocument example above. That's strictly a Zope 2
  80.198 +concern, as Zope 3 has others ways to deal with this.
  80.199 +
  80.200 +In Zope 2 an object has a new path through creation, copy or move
  80.201 +(rename is a kind of move). The events sent during these three
  80.202 +operations are varied: creation sends IObjectAddedEvent, copy sends
  80.203 +IObjectCopiedEvent then IObjectAddedEvent, and move sends
  80.204 +IObjectMovedEvent.
  80.205 +
  80.206 +So to react to new paths, we have to subscribe to IObjectMovedEvent, but
  80.207 +this will also get us any IObjectRemovedEvent, which we'll have to
  80.208 +filter out by hand (this is unfortunate, and due to the way the Zope 3
  80.209 +interface hierarchy is organized). So to fix the CoolDocument
  80.210 +configuration we have to add::
  80.211 +
  80.212 +    def movedCoolDocument(ob, event):
  80.213 +        """A Cool Document was moved."""
  80.214 +        if not IObjectRemovedEvent.providedBy(event):
  80.215 +            addedCoolDocument(ob, event)
  80.216 +
  80.217 +And replace the subscriber with::
  80.218 +
  80.219 +    ...
  80.220 +      <subscriber
  80.221 +          for="Products.CoolProduct.interfaces.ICoolDocument
  80.222 +               zope.app.container.interfaces.IObjectMovedEvent"
  80.223 +          handler="Products.CoolProduct.CoolDocument.movedCoolDocument"
  80.224 +          />
  80.225 +    ...
  80.226 +
  80.227 +The second use case is when the object has to do some cleanup when it is
  80.228 +removed from its parent. This used to be in ``manage_beforeDelete``, now
  80.229 +we can do the work in a ``removedCoolDocument`` method and just
  80.230 +subscribe to IObjectRemovedEvent. But wait, this won't take into account
  80.231 +moves... So in the same vein as above, we would have to write::
  80.232 +
  80.233 +    def movedCoolDocument(ob, event):
  80.234 +        """A Cool Document was moved."""
  80.235 +        if not IObjectRemovedEvent.providedBy(event):
  80.236 +            addedCoolDocument(ob, event)
  80.237 +        if not IObjectAddedEvent.providedBy(event):
  80.238 +            removedCoolDocument(ob, event)
  80.239 +
  80.240 +The third use case is when your object has to stay registered with some
  80.241 +tool, for instance indexed in a catalog, or as above registered with
  80.242 +``portal_cool``. Here we have to know the old object's path to
  80.243 +unregister it, so we have to be called *before* it is removed. We'll use
  80.244 +``IObjectWillBe...`` events, that are sent before the actual operations
  80.245 +take place::
  80.246 +
  80.247 +    from OFS.interfaces import IObjectWillBeAddedEvent
  80.248 +    def beforeMoveCoolDocument(ob, event):
  80.249 +        """A Cool Document will be moved."""
  80.250 +        if not IObjectWillBeAddedEvent.providedBy(event):
  80.251 +            getToolByName(ob, 'portal_cool').unregisterCool(ob)
  80.252 +
  80.253 +    def movedCoolDocument(ob, event):
  80.254 +        """A Cool Document was moved."""
  80.255 +        if not IObjectRemovedEvent.providedBy(event):
  80.256 +            getToolByName(ob, 'portal_cool').registerCool(ob)
  80.257 +        ...
  80.258 +
  80.259 +And use an additional subscriber::
  80.260 +
  80.261 +    ...
  80.262 +      <subscriber
  80.263 +          for="Products.CoolProduct.interfaces.ICoolDocument
  80.264 +               OFS.interfaces.IObjectWillBeMovedEvent"
  80.265 +          handler="Products.CoolProduct.CoolDocument.beforeMoveCoolDocument"
  80.266 +          />
  80.267 +    ...
  80.268 +
  80.269 +This has to be done if the tool cannot react by itself to objects being
  80.270 +added and removed, which obviously would be better as it's ultimately
  80.271 +the tool's responsibility and not the object's.
  80.272 +
  80.273 +Note that if having tests like::
  80.274 +
  80.275 +    if not IObjectWillBeAddedEvent.providedBy(event):
  80.276 +    if not IObjectRemovedEvent.providedBy(event):
  80.277 +
  80.278 +seems cumbersome (and backwards), it is also possible to check what kind
  80.279 +of event you're dealing with using::
  80.280 +
  80.281 +    if event.oldParent is not None:
  80.282 +    if event.newParent is not None:
  80.283 +
  80.284 +(However be careful, the ``oldParent`` and ``newParent`` are the old and
  80.285 +new parents *of the original object* for which the event was sent, not
  80.286 +of the one to which the event was redispatched using the
  80.287 +multi-subscribers we have registered.)
  80.288 +
  80.289 +The ``IObjectWillBe...`` events are specific to Zope 2 (and imported
  80.290 +from ``OFS.interfaces``). Zope 3 doesn't really need them, as object
  80.291 +identity is often enough.
    81.1 new file mode 100644
    81.2 --- /dev/null
    81.3 +++ b/doc/features.txt
    81.4 @@ -0,0 +1,101 @@
    81.5 +=============
    81.6 +Five features
    81.7 +=============
    81.8 +
    81.9 +Five features are mostly Zope 3 features, though Five has some extras,
   81.10 +and some limitations.
   81.11 +
   81.12 +Zope 3 interfaces
   81.13 +=================
   81.14 +
   81.15 +Everything from the ``zope.interface`` package works.  Zope 3
   81.16 +interfaces are the foundation of the component architecture, and also
   81.17 +the foundation of schemas.
   81.18 +
   81.19 +ZCML
   81.20 +====
   81.21 +
   81.22 +ZCML is the Zope Configuration Markup Language, an XML application.
   81.23 +Zope 3 (and Five) code consists of a lot of components that can be
   81.24 +plugged together using ZCML.
   81.25 +
   81.26 +If you put a ``site.zcml`` in the home directory of your Zope
   81.27 +instance, this is the root of the ZCML tree. An example of
   81.28 +``site.zcml`` is in ``site.zcml.in``. If you don't place a
   81.29 +``site.zcml``, Five falls back on ``fallback.zcml``.
   81.30 +
   81.31 +ZCML in Five has special directive, ``five:loadProducts``, to load the
   81.32 +ZCML (``meta.zcml``, ``configure.zcml``) of all installed Zope 2
   81.33 +products, if available.
   81.34 +
   81.35 +Another special directive, ``five:loadProductsOverrides`` is available
   81.36 +to load any overriding ZCML (``overrides.zcml``) in these products. In
   81.37 +the ``overrides.zcml`` you can override existing views or adapters, in
   81.38 +this or in other products.
   81.39 +
   81.40 +Adapters
   81.41 +========
   81.42 +
   81.43 +You can use adapters in Five, just like in Zope 3.
   81.44 +
   81.45 +Zope 3 views
   81.46 +============
   81.47 +
   81.48 +Zope 3 views work in Five, including layers and skins. To make them
   81.49 +work however, you need to make a Zope 2 class "traversable". This can
   81.50 +be done by using the ``five:traversable`` directive in ZCML.
   81.51 +
   81.52 +Page templates
   81.53 +==============
   81.54 +
   81.55 +Five before release 0.3 used to use Zope 3's page template engine, but
   81.56 +in the interests of increased compatibility with Zope 2, we've
   81.57 +switched to using Zope 2's. There should be no real difference to any
   81.58 +code, however. We may decide to switch back to Zope 3's engine again
   81.59 +eventually if we can resolve the compatibility issues.
   81.60 +
   81.61 +One thing to be aware of is that the page template engine runs
   81.62 +completely in trusted mode, just like Python code. That is, as soon as
   81.63 +the page template engine is running, no Zope 2 or Zope 3 security
   81.64 +checks are made.
   81.65 +
   81.66 +Edit and add forms
   81.67 +==================
   81.68 +
   81.69 +Five supports edit and add forms. Typical Zope 3 examples of these
   81.70 +should work.
   81.71 +
   81.72 +Security declarations
   81.73 +=====================
   81.74 +
   81.75 +Five aims to eradicate ``declareProtected``, ``ClassSecurityInfo`` and
   81.76 +``initializeClass`` from your Zope 2 code.
   81.77 +
   81.78 +In order to do this, Five provides the Zope 3 way of declaring
   81.79 +permissions from ZCML, but uses the Zope 2 mechanisms to actually set
   81.80 +them. To declare permissions for methods and templates on views you
   81.81 +use the ``permission`` attribute on the ``browser:page`` directive,
   81.82 +and specify a Zope 2 permission (given a Zope 3 name). You can find a
   81.83 +list of these permissions in ``permissions.zcml`` in Five. The
   81.84 +permission check takes place before the view is executed.
   81.85 +
   81.86 +The ``content`` directive can also be used to declare permissions on
   81.87 +Zope 2 content classes. Note however that these permissions will be
   81.88 +ignored by views anyway, as they are trusted -- it only serves to
   81.89 +protect directly exposed methods on content classes (the python
   81.90 +scripts and the ZPublisher).
   81.91 +
   81.92 +Local Sites
   81.93 +===========
   81.94 +
   81.95 +Five supports the concept of a local sites and local site managers.
   81.96 +See localsite.txt_ for more information.
   81.97 +
   81.98 +.. _localsite.txt: localsite.html
   81.99 +
  81.100 +Object events
  81.101 +=============
  81.102 +
  81.103 +Five supports sending Zope 3 object events when objects are added,
  81.104 +moved, renamed, copied and deleted. The use of ``manage_afterAdd`` & co
  81.105 +methods is deprecated.
    82.1 new file mode 100644
    82.2 --- /dev/null
    82.3 +++ b/doc/i18n.txt
    82.4 @@ -0,0 +1,76 @@
    82.5 +Internationalization
    82.6 +====================
    82.7 +
    82.8 +Translation
    82.9 +-----------
   82.10 +
   82.11 +Five registers its own translation service mockup with the Page
   82.12 +Templates machinery and prevents any other product from also doing so.
   82.13 +That means, Five always assumes control over ZPT i18n.  When a certain
   82.14 +domain has not been registered the Zope 3 way, Five's translation
   82.15 +service will see that the utility lookup fails and use the next
   82.16 +available fallback translation service.  In case of no other
   82.17 +translation service installed, that is just a dummy fallback.  In case
   82.18 +you have Localizer and PTS installed, it falls back to that.
   82.19 +
   82.20 +To register Zope 3 style translation domains, use the following ZCML
   82.21 +statement::
   82.22 +
   82.23 +  <i18n:registerTranslations directory="locales" />
   82.24 +
   82.25 +where the 'i18n' prefix is bound to the
   82.26 +http://namespaces.zope.org/i18n namespace identifier.  The directory
   82.27 +(in this case 'locales') should conform to the `standard gettext
   82.28 +locale directory layout`__.
   82.29 +
   82.30 +.. __: http://www.gnu.org/software/gettext/manual/html_chapter/gettext_10.html#SEC148
   82.31 +
   82.32 +
   82.33 +Preferred languages and negotiation
   82.34 +-----------------------------------
   82.35 +
   82.36 +Fallback translation services such as PTS and Localizer have their own
   82.37 +way of determining the user-preferred languages and negotiating that
   82.38 +with the available languages in the respective domain.  Zope 3
   82.39 +translation domains typically adapt the request to
   82.40 +IUserPreferredLanguages to get a list of preferred languages; then
   82.41 +they use the INegotiator utility to negotiate between the preferred
   82.42 +and available languages.
   82.43 +
   82.44 +The goal of the sprint was to allow both fallback translation services
   82.45 +(PTS, Localizer) and Zope 3 translation domains come to the same
   82.46 +conclusion regarding which language should be chosen.  The use case is
   82.47 +that you have a site running Localizer or PTS and a bunch of "old"
   82.48 +products using either one of those for translation.  Now you have an
   82.49 +additional, "new" Five-based product using Zope 3 translation domains.
   82.50 +Most of the time, a page contains user messages from more than one
   82.51 +domain, so you would all domains be translated to the same language.
   82.52 +
   82.53 +
   82.54 +Adjusting behaviour to your environment
   82.55 +---------------------------------------
   82.56 +
   82.57 +The default behaviour for choosing languages in Five is the one of
   82.58 +Zope 3: analyze the Accept-Language HTTP header and nothing more.  In
   82.59 +addition, Five providees ``IUserPreferredLanguages`` adapters for
   82.60 +Localizer and PTS that choose languages the exact same way Localizer
   82.61 +or PTS would.  So, if you're using Five in a Localizer-environment,
   82.62 +you need this in your product's ``overrides.zcml``:
   82.63 +
   82.64 +    <adapter
   82.65 +        for="zope.publisher.interfaces.http.IHTTPRequest"
   82.66 +        provides="zope.i18n.interfaces.IUserPreferredLanguages"
   82.67 +        factory="Products.Five.i18n.LocalizerLanguages"
   82.68 +        />
   82.69 +
   82.70 +If you're using PTS:
   82.71 +
   82.72 +    <adapter
   82.73 +        for="zope.publisher.interfaces.http.IHTTPRequest"
   82.74 +        provides="zope.i18n.interfaces.IUserPreferredLanguages"
   82.75 +        factory="Products.Five.i18n.PTSLanguages"
   82.76 +        />
   82.77 +
   82.78 +That way Zope 3 translation domains will always come to the same
   82.79 +conclusion regarding the language as your original translation service
   82.80 +would.
    83.1 new file mode 100644
    83.2 --- /dev/null
    83.3 +++ b/doc/localsite.txt
    83.4 @@ -0,0 +1,126 @@
    83.5 +Local sites in Five
    83.6 +===================
    83.7 +
    83.8 +Intro
    83.9 +-----
   83.10 +
   83.11 +Zope 3 has a concept of local sites and site managers.  They allow one
   83.12 +to locally override component registrations and have components and
   83.13 +their configuration be persisted in the ZODB as well as managed
   83.14 +through the web interface.
   83.15 +
   83.16 +By default, Zope 3 has a global site which is configured through ZCML.
   83.17 +It provides the fallback for all component look-up.  Local sites are
   83.18 +typically set during traversal, when the traverser encounters an
   83.19 +``ISite`` object.  The last encountered ``ISite`` wins.  Component
   83.20 +look-up will cascade through all the sites in the hierarchy and fall
   83.21 +back to the global site where it can finally fail.
   83.22 +
   83.23 +Five also supports local sites, however by default only local
   83.24 +utilities.  Local adapters, such as ZODB-based views, could be
   83.25 +supported with a custom implementation of the local site manager and
   83.26 +local adapter registry.  This is not the focus of local sites in Five,
   83.27 +though.
   83.28 +
   83.29 +
   83.30 +Turning possible sites into sites
   83.31 +---------------------------------
   83.32 +
   83.33 +Five uses the same technique as Zope 3 for determining local sites:
   83.34 +sites are found during URL traversal.  However, since the Zope 2
   83.35 +ZPublisher doesn't emit the necessary events by default, Five needs to
   83.36 +set a ``BeforeTraverse`` hook on site objects.
   83.37 +
   83.38 +Setting this hook needs to be done an object-per-object basis and can
   83.39 +be performed through the ``manage_site.html`` browser page.  This view
   83.40 +operates on ``IPossibleSite`` objects (in other words, objects that
   83.41 +*can* be sites but aren't yet).  It sets the traversal hook on the
   83.42 +object and then marks it with the ``ISite`` interface to indicate that
   83.43 +it is a real site now, not just a possible site.
   83.44 +
   83.45 +Note that unlike the Zope 3 equivalent of this view, it does not set
   83.46 +the site manager to site; it is assumed that the site already knows
   83.47 +how to get its site manager.
   83.48 +
   83.49 +Also note that in order for the view to work, the object's class needs
   83.50 +to be Five-traversable, e.g. with the following ZCML statement:
   83.51 +
   83.52 +   <five:traversable class=".module.MyClass" />
   83.53 +
   83.54 +
   83.55 +Custom site implementations
   83.56 +---------------------------
   83.57 +
   83.58 +Anything can be a site, there are no restrictions (sites don't have to
   83.59 +be folders, for examples).  Sites can also be nested.  For all the
   83.60 +Component Architecture cares, every object in your URL graph could be
   83.61 +a site.
   83.62 +
   83.63 +The only requirement are two interfaces:
   83.64 +
   83.65 +``IPossibleSite``
   83.66 +
   83.67 +    Objects that can potentially be turned into a site need to provide
   83.68 +    this interface.  That requires them to have a ``setSiteManager()``
   83.69 +    and ``getSiteManager()`` method for setting and getting the local
   83.70 +    site manager of that site.  The site manager is the registry that
   83.71 +    takes care of local component look-up.
   83.72 +
   83.73 +``IFiveSiteManager``
   83.74 +
   83.75 +    This interface is a slight extension of the ``IServiceService`` or
   83.76 +    ``ISiteManager`` interface, respectively (the former in Zope X3
   83.77 +    3.0, the latter in later versions).  It defines the API of a local
   83.78 +    site manager that is to be used in a Five environment.  The site's
   83.79 +    ``getSiteManager()`` method should return an object providing this
   83.80 +    interface.
   83.81 +
   83.82 +
   83.83 +Five's default site manager
   83.84 +----------------------------
   83.85 +
   83.86 +If you want to instantly make your custom class an ``IPossibleSite``
   83.87 +implementation, you can use a default mix-in class from Five, e.g.::
   83.88 +
   83.89 +  class MySite(OFS.Folder, Products.Five.site.localsite.FiveSite):
   83.90 +      pass
   83.91 +
   83.92 +This default implementation of ``IPossibleSite`` features a site
   83.93 +manager implementation that knows how to register and look-up local
   83.94 +utilities.  It does so by adapting the site to
   83.95 +``IFiveUtilityRegistry``.
   83.96 +
   83.97 +The default adapter for this local utility registry simply stores the
   83.98 +utilities in a standard OFS Folder on called ``utilities`` on the site
   83.99 +object.  You probably want to exchange that simple behaviour with
  83.100 +something that works better in your application.  You can do so by
  83.101 +plugging in your own utility registry adapter, e.g.::
  83.102 +
  83.103 +  <adapter for=".interfaces.IMySite"
  83.104 +           provides="Products.Five.site.interfaces.IFiveUtilityRegistry"
  83.105 +           fatory=".module.MyUtilityRegistry" />
  83.106 +
  83.107 +All this implementation needs to do is comply with the
  83.108 +``IFiveUtilityRegistry`` interface, which essentially means the
  83.109 +standard utility look-up methods like ``queryUtility()``,
  83.110 +``getUtilitiesFor()``, etc.
  83.111 +
  83.112 +
  83.113 +Turning existing classes into possible sites
  83.114 +--------------------------------------------
  83.115 +
  83.116 +If you cannot or do not want to modify existing classes to mix in the
  83.117 +``FiveSite`` class, you can also use a structured monkey patch via
  83.118 +ZCML::
  83.119 +
  83.120 +   <five:localsite class=".module.MyClass" />
  83.121 +
  83.122 +This makes ``MyClass`` an ``IPossibleSite`` and sticks ``FiveSite``'s
  83.123 +``getSiteManager()`` and ``setSiteManager()`` methods on the class as
  83.124 +well.  You can also tell it to use a different site implementation's
  83.125 +methods for the monkey patch::
  83.126 +
  83.127 +   <five:localsite class=".module.MyClass"
  83.128 +                   site_class=".module.MySiteImpl" />
  83.129 +
  83.130 +Just make sure that this class implements ``IPossibleSite``.
    84.1 new file mode 100644
    84.2 --- /dev/null
    84.3 +++ b/doc/main.txt
    84.4 @@ -0,0 +1,103 @@
    84.5 +Five, the Zope 3 in Zope 2 project
    84.6 +==================================
    84.7 +       
    84.8 +What is Five?
    84.9 +-------------
   84.10 +
   84.11 +Five is a Zope 2 product that allows you to integrate Zope 3
   84.12 +technologies into Zope 2, today. Five right now allows you to use the
   84.13 +following Zope 3 technologies in Zope 2:
   84.14 +
   84.15 +* Zope 3 interfaces
   84.16 +
   84.17 +* adapters
   84.18 +
   84.19 +* pages (views), including skins and layers, and edit and add forms
   84.20 +
   84.21 +* ZCML
   84.22 +
   84.23 +It is possible to add Zope 3 style views to your own Zope 2 objects,
   84.24 +or to existing ones, even normal Folders!
   84.25 +
   84.26 +Five works with a straight Zope 2.7 installation, as long as Zope 3
   84.27 +has been installed. See Five's INSTALL.txt for more information on how
   84.28 +to set it up. Five 1.0 is also already included in Zope 2.8, newer
   84.29 +Five releases since are available however.
   84.30 +
   84.31 +We're in the process of evaluating lots more Zope 3 technologies for
   84.32 +integration into Zope 2. This is the right moment for interested Zope
   84.33 +2 and Zope 3 developers to jump in. We're looking for cooperation
   84.34 +between different Zope 2 projects so that this can be a foundational
   84.35 +system for us all.
   84.36 +
   84.37 +Download 
   84.38 +--------
   84.39 +
   84.40 +2005-10-04 -- We have release Five 1.1! Download it here:
   84.41 +
   84.42 +http://codespeak.net/z3/five/release/Five-1.1.tgz
   84.43 +
   84.44 +2005-07-13 -- We have released Five 1.1b! Download it here:
   84.45 +
   84.46 +http://codespeak.net/z3/five/release/Five-1.1b.tgz
   84.47 +
   84.48 +2005-07-12 -- We have released Five 1.0.2! This is also the version
   84.49 +that will be included in Zope 2.8.1. Download it here:
   84.50 +
   84.51 +http://codespeak.net/z3/five/release/Five-1.0.2.tgz
   84.52 +
   84.53 +2005-05-31 -- We have released Five 1.0.1! This is also the version
   84.54 +that will be included in Zope 2.8.0. Download it here:
   84.55 +
   84.56 +http://codespeak.net/z3/five/release/Five-1.0.1.tgz
   84.57 +
   84.58 +2005-04-27 -- We have released Five 1.0! Download it here:
   84.59 +
   84.60 +http://codespeak.net/z3/five/release/Five-1.0.tgz
   84.61 +
   84.62 +And view changes here:
   84.63 +
   84.64 +http://codespeak.net/z3/five/CHANGES.html
   84.65 +
   84.66 +2005-03-11 -- We have released Five 0.3! Download it here:
   84.67 +
   84.68 +http://codespeak.net/z3/five/release/Five-0.3.tgz
   84.69 +
   84.70 +2004-09-24 -- Five 0.2b is released. Download it here:
   84.71 +
   84.72 +http://codespeak.net/z3/five/release/Five-0.2b.tgz
   84.73 +
   84.74 +2004-07-30 -- We have released Five 0.1! Download it here:
   84.75 +
   84.76 +http://codespeak.net/z3/five/release/Five-0.1.tgz
   84.77 +
   84.78 +Joining the project
   84.79 +-------------------
   84.80 +
   84.81 +Five is kindly hosted on codespeak.net, and is part of the larger
   84.82 +*Zope 3 Base* project that offers an approachable area for
   84.83 +developers of Zope 3 related software.
   84.84 +
   84.85 +Five has a mailing list: 
   84.86 +
   84.87 +http://codespeak.net/mailman/listinfo/z3-five
   84.88 +
   84.89 +We're also active on IRC, at ``#z3-base`` on freenode.
   84.90 + 
   84.91 +Five is hosted in a subversion repository on codespeak.net. You can
   84.92 +browse this on the web here:
   84.93 +
   84.94 +http://codespeak.net/svn/z3/Five/
   84.95 +
   84.96 +You can check out Five using the following subversion command::
   84.97 +
   84.98 +  svn co http://codespeak.net/svn/z3/Five/trunk Five
   84.99 +
  84.100 +There's also a checkins mailing list for the Z3 project, here:
  84.101 +
  84.102 +http://codespeak.net/mailman/listinfo/z3-checkins
  84.103 +
  84.104 +If you want checkin access, please join the z3-five mailing list or
  84.105 +the ``#z3-base`` IRC channel, and ask us there.
  84.106 +
  84.107 +We hope to hear from you!
    85.1 new file mode 100644
    85.2 --- /dev/null
    85.3 +++ b/doc/manual.txt
    85.4 @@ -0,0 +1,339 @@
    85.5 +===========
    85.6 +Five Manual
    85.7 +===========
    85.8 +
    85.9 +Introduction
   85.10 +------------
   85.11 +
   85.12 +Five's goal is to let you, the Zope 2 developer, use Zope 3 code in
   85.13 +Zope 2. Our aim is to make as much of Zope 3 code work in Zope 2 as
   85.14 +possible, while integrating it with Zope 2.
   85.15 +
   85.16 +Five can be used inside your current Zope 2 project. The benefits are:
   85.17 +
   85.18 +* availability of Zope 3 technologies in Zope 2 like the component
   85.19 +  architecture and declarative configuration.
   85.20 +
   85.21 +* you can gradually evolve your Zope 2 project so it is better
   85.22 +  positioned for the migration to Zope 3.
   85.23 +
   85.24 +* you start learning about Zope 3 right now, preparing yourself better
   85.25 +  for the future. Since Zope 3 is open to contributions, you could
   85.26 +  even influence your future for the better.
   85.27 +
   85.28 +Five can also be used to develop new Zope 2 products, though depending
   85.29 +on your deployment requirements it might in that case make more sense
   85.30 +to develop for Zope 3 directly.
   85.31 +
   85.32 +Five is only useful on the Python (Product) level in Zope 2, not from
   85.33 +within the Zope Management Interface. Five makes no attempt to provide
   85.34 +a user interface, but is aimed squarely at the Python developer.
   85.35 +
   85.36 +Zope 3 interfaces
   85.37 +-----------------
   85.38 +
   85.39 +Interfaces?
   85.40 +===========
   85.41 +
   85.42 +An interface is simply a description of what an object provides to the
   85.43 +world, i.e. its public attribute and methods. It looks very much like
   85.44 +a class, but contains no implementation::
   85.45 +
   85.46 +  from zope.interface import Interface
   85.47 +
   85.48 +  # by convention, all interfaces are prefixed with ``I``
   85.49 +  class IElephant(Interface):
   85.50 +      """An elephant is a big object that barely fits in the cupboard.
   85.51 +      """
   85.52 +
   85.53 +      def getAngerLevel():
   85.54 +          """Anger level, maximum of 100.
   85.55 +
   85.56 +          The longer the elephant has been in the cupboard, the angrier.
   85.57 +          """
   85.58 +
   85.59 +      def isInCupboard():
   85.60 +          """Returns true if the elephant is indeed in cupboard.
   85.61 +          """
   85.62 +
   85.63 +      def trunkSmash(target):
   85.64 +          """Smash the target with trunk.
   85.65 +
   85.66 +          The anger level determines the force of the hit.
   85.67 +          """
   85.68 +
   85.69 +      def trample(target):
   85.70 +          """Trample the target.
   85.71 +          
   85.72 +          The anger level determines the rate of flattening of the target.
   85.73 +          """
   85.74 +
   85.75 +A concrete class somewhere can now claim that it implements the
   85.76 +interface (i.e. its instance will provide the interface)::
   85.77 +
   85.78 +  class PinkElephant:
   85.79 +      # this says all instances of this class provide IElephant
   85.80 +      implements(IElephant)
   85.81 +
   85.82 +      def getAngerLevel(self):
   85.83 +          return 0 # this elephant is peaceful
   85.84 +
   85.85 +      def isInCupboard(self):
   85.86 +          return False # it's never in a cupboard but can be found in bottles
   85.87 +
   85.88 +      def trunkSmash(self, target):
   85.89 +          target.tickle()
   85.90 +
   85.91 +      def trample(self, target):
   85.92 +          target.patOnHead()
   85.93 +
   85.94 +Interfaces themselves are good for a number of reasons:
   85.95 +
   85.96 +* They provide API documentation.
   85.97 +
   85.98 +* They help you make explicit the design of your application,
   85.99 +  hopefully improving it.
  85.100 +
  85.101 +* If an object provides an interface, that object is considered to be
  85.102 +  a *component*. This means you can use Zope 3's component
  85.103 +  architecture with these objects.
  85.104 +
  85.105 +In order to use Five, you'll have to make your objects provide
  85.106 +interfaces. Sometimes, you cannot change the code of class (as you are
  85.107 +not the maintainer), but you still want to make it implement an
  85.108 +interface. Five provides a ZCML directive to do this::
  85.109 +
  85.110 +  <five:implements class="tolkien.Oliphant" 
  85.111 +      implements="interfaces.IElephant" />
  85.112 +
  85.113 +Interfaces in Zope 2 versus Zope 3
  85.114 +==================================
  85.115 +
  85.116 +You may be familiar with Zope 2's way of declaring interfaces. Zope 2
  85.117 +has used the ``__implements__`` class attribute for interface
  85.118 +declarations. Zope 2 cannot detect Zope 3 interfaces and the Zope 3
  85.119 +machinery cannot detect Zope 2 interfaces. This is a good thing, as
  85.120 +Zope 2 has no way to deal with Zope 3 interfaces, and Zope 3 cannot
  85.121 +comprehend Zope 2 interfaces. This means you can safely make a class
  85.122 +declare both a Zope 2 and Zope 3 interface independently from each
  85.123 +other. It's a rare case where you need this though; you're usually
  85.124 +better off just switching to ``implements()`` for your application if
  85.125 +you are using Five.
  85.126 +
  85.127 +Switching from Zope 2 interfaces to Zope 3 interfaces is easy -- just
  85.128 +make your interfaces inherit from ``zope.interface.Interface`` instead
  85.129 +of ``Interface.Interface`` (or ``Interface.Base``). Next, change all
  85.130 +``__implements__`` to ``implements()``.
  85.131 +
  85.132 +This should get you going and your application may very well still
  85.133 +work. Later on, you will also have to change calls to
  85.134 +``isImplementedBy`` and such in your application to ``providedBy``, as
  85.135 +``isImplementedBy`` has been deprecated (you'll see the
  85.136 +DeprecationWarnings in your Zope log).
  85.137 +
  85.138 +Adapters
  85.139 +--------
  85.140 +
  85.141 +From a Python programmer's perspective, the immediate thing that Five
  85.142 +brings to do the table are adapters. This section goes through some
  85.143 +demo code to explain how everything is tied
  85.144 +together. ``demo/FiveDemo`` is a demo Product you can install and
  85.145 +examine that has all the presented here together.
  85.146 +
  85.147 +Zope 3 adapters depend on Zope 3 interfaces. To create a Zope 3
  85.148 +interface you need to subclass it from
  85.149 +``zope.interface.Interface``. Here is an example::
  85.150 +
  85.151 +  from zope.interface import Interface
  85.152 +
  85.153 +  class IMyInterface(Interface):
  85.154 +      """This is a Zope 3 interface.
  85.155 +      """
  85.156 +      def someMethod():
  85.157 +          """This method does amazing stuff.
  85.158 +          """
  85.159 +
  85.160 +Now to make some class declare that it implements this interface, you
  85.161 +need to use the ``implements()`` function in the class::
  85.162 +
  85.163 +  from zope.interface import implements
  85.164 +  from interfaces import IMyInterface
  85.165 +
  85.166 +  class MyClass:
  85.167 +      implements(IMyInterface)
  85.168 +
  85.169 +      def someMethod(self):
  85.170 +           return "I am alive! Alive!"
  85.171 +
  85.172 +For an explanation of the relation of Zope 3 interfaces to Zope 2
  85.173 +interfaces, see below.
  85.174 +
  85.175 +Now let's set up the interface that we are adapting to::
  85.176 +
  85.177 +  class INewInterface(Interface):
  85.178 +      """The interface we adapt to.
  85.179 +      """
  85.180 +
  85.181 +      def anotherMethod():
  85.182 +          """This method does more stuff.
  85.183 +          """
  85.184 +
  85.185 +Next we'll work on the class that implements the adapter. The
  85.186 +requirement to make a class that is an adapter is very simple; you
  85.187 +only need to take a context object as the constructor. The context
  85.188 +object is the object being adapted. An example::
  85.189 +
  85.190 +  from zope.interface import implements
  85.191 +  from interfaces import INewInterface
  85.192 +
  85.193 +  class MyAdapter:
  85.194 +      implements(INewInterface)
  85.195 + 
  85.196 +      def __init__(self, context):
  85.197 +          self.context = context
  85.198 +
  85.199 +      def anotherMethod(self):
  85.200 +          return "We have adapted: %s" % self.context.someMethod()
  85.201 +
  85.202 +Next, we hook it all up using zcml. If the classes are in a module
  85.203 +called ``classes.py`` and the interfaces in a module called
  85.204 +``interfaces.py``, we can declare ``MyAdapter`` to be an adapter for
  85.205 +``IMyInterface`` to ``INewInterface`` like this (in a file called
  85.206 +``configure.zcml``)::
  85.207 +
  85.208 +  <configure xmlns="http://namespaces.zope.org/zope">
  85.209 +
  85.210 +    <adapter 
  85.211 +      for=".interfaces.IMyInterface"
  85.212 +      provides=".interfaces.INewInterface"
  85.213 +      factory=".classes.MyAdapter" /> 
  85.214 +    
  85.215 +  </configure>
  85.216 +
  85.217 +Five will automatically pickup ``configure.zcml`` when it's placed in
  85.218 +the product's directory.  Any object that provides ``IMyInterface``
  85.219 +can now be adapted to ``INewInterface``, like this::
  85.220 +
  85.221 +  from classes import MyClass
  85.222 +  from interfaces import INewInterface
  85.223 +
  85.224 +  object = MyClass()
  85.225 +  adapted = INewInterface(object)
  85.226 +  print adapted.anotherMethod()
  85.227 +
  85.228 +Views in Five
  85.229 +-------------
  85.230 +
  85.231 +This section will give a brief introduction on how to use the five
  85.232 +view system. ``demo/FiveViewsDemo`` is a demo Product you can install
  85.233 +and examine that has all the presented here tied together, please
  85.234 +consult it for more details. ``tests/products/FiveTest`` actually
  85.235 +contains a more detailed set of test views, trying a number of
  85.236 +features. Finally, read up on the way Zope 3 does it. While Five is a
  85.237 +subset of Zope 3 functionality and has been adapted to work with Zope
  85.238 +2, much of Zope 3's documentation still works.
  85.239 +
  85.240 +Five enables you to create views for your own objects, or even built-in
  85.241 +Zope objects, as long as two things are the case:
  85.242 +
  85.243 +* The object provides an Zope 3 interface, typically through its class.
  85.244 +
  85.245 +* The object (typically its class) is made Zope 3 traversable. This
  85.246 +  allows Zope 3 views, resources and other things to be attached to a
  85.247 +  Zope 2 object.
  85.248 +
  85.249 +Typically you give your classes an interface using the ``implements``
  85.250 +directive in the class body::
  85.251 +
  85.252 +  class MyClass:
  85.253 +      implements(ISomeInterface)
  85.254 +
  85.255 +For existing objects that you cannot modify this is not
  85.256 +possible. Instead, we provide a ZCML directive to accomplish this. As
  85.257 +an example, to make Zope's ``Folder`` (and all its subclasses)
  85.258 +implement ``IFolder`` (an interface you defined), you can do the
  85.259 +following in ZCML::
  85.260 +
  85.261 +  <five:implements class="OFS.Folder.Folder" 
  85.262 +                   interface=".interfaces.IFolder" />
  85.263 +
  85.264 +``five`` in this case refers to the XML namespace for Five,
  85.265 +``http://namespace.zope.org/five``.
  85.266 +
  85.267 +We've provided another ZCML directive to make an object
  85.268 +traversable. To make your MyClass traversable, let's assume it is in
  85.269 +``mymodule``, in the same package as the zcml file we are editing::
  85.270 +
  85.271 +  <five:traversable class=".mymodule.MyClass" />
  85.272 +
  85.273 +To continue our example, to make Zope's ``Folder`` traversable through
  85.274 +Five, you need to declare this in ZCML as well:
  85.275 +
  85.276 +  <five:traversable class="OFS.Folder.Folder"/>
  85.277 +
  85.278 +This makes Folder traverse in the Zope 3 way first, looking up views
  85.279 +and other things, and then if they cannot be found, fall back on the
  85.280 +regular Zope 2 traversal. It does this by overriding the
  85.281 +``__bobo_traverse__`` hook. Old hooks that are already in place in an
  85.282 +object will be stored and become the secondary fallback. This allows
  85.283 +the ZMI to work still, but new views can be added on the fly.
  85.284 +
  85.285 +Note that at the point of writing it is only possible to make an object
  85.286 +viewable through ZCML if this object does not already provide its own
  85.287 +``__bobo_traverse__`` method.
  85.288 +
  85.289 +Views in Five are simple classes. The only requirements for a Five
  85.290 +view class are:
  85.291 +
  85.292 +* They need an ``__init__()`` that take a context and a request
  85.293 +  attribute. Typically this comes from a base class, such as
  85.294 +  ``BrowserView``.
  85.295 +
  85.296 +* They need to be initialized with the Zope 2 security system, as
  85.297 +  otherwise you cannot use the view.
  85.298 +
  85.299 +* This also means they need to be part of the Zope 2 acquisition
  85.300 +  system, as this is a requirement for Zope 2 security to
  85.301 +  function. The ``BrowserView`` base class, available from
  85.302 +  ``Products.Five``, already inherits from ``Acquisition.Explicit`` to
  85.303 +  make this be the case. Acquisition is explicit so no attributes can
  85.304 +  be acquired by accident.
  85.305 +
  85.306 +An example of a simple view::
  85.307 + 
  85.308 +  from Products.Five import BrowserView
  85.309 +
  85.310 +  class SimpleFolderView(BrowserView):
  85.311 +      security = ClassSecurityInfo()
  85.312 +
  85.313 +      security.declarePublic('eagle')
  85.314 +      def eagle(self):
  85.315 +          """Test
  85.316 +          """
  85.317 +          return "The eagle has landed: %s" % self.context.objectIds()
  85.318 +
  85.319 +  InitializeClass(SimpleFolderView)
  85.320 +
  85.321 +Note that it is not a good idea to give a view class its own
  85.322 +``index_html``, as this confuses Five's view lookup machinery.
  85.323 +
  85.324 +As you can see, the class is initialized with the Zope 2 security
  85.325 +system. This view uses methods in Python, but you can also use other
  85.326 +Zope 2 mechanisms such as ``PageTemplateFile``.
  85.327 +
  85.328 +Finally, we need to hook up the pages through ZCML::
  85.329 +
  85.330 +  <browser:page 
  85.331 +    for=".interfaces.IFolder"
  85.332 +    class=".browser.SimpleFolderView"
  85.333 +    attribute="eagle"
  85.334 +    name="eagle.txt"
  85.335 +    permission="zope2.ViewManagementScreens"
  85.336 +    />
  85.337 +
  85.338 +``browser`` in this refers to the XML namespace of Zope 3 for browser
  85.339 +related things; it's
  85.340 +``http://namespace.zope.org/browser``. ``permission`` declares the
  85.341 +Zope 2 permission needs in order to access this view. The file
  85.342 +``permissions.zcml`` in Five contains a mapping of Zope 2 permissions
  85.343 +to their Zope 3 names.
    86.1 new file mode 100644
    86.2 --- /dev/null
    86.3 +++ b/doc/presentations/five.mgp
    86.4 @@ -0,0 +1,127 @@
    86.5 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    86.6 +%deffont "standard" xfont "helvetica-medium-r"
    86.7 +%deffont "thick" xfont "helvetica-bold-r"
    86.8 +%deffont "typewriter" xfont "courier-medium-r"
    86.9 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   86.10 +%%
   86.11 +%% Default settings per each line numbers.
   86.12 +%%
   86.13 +%default 1 area 90 90, leftfill, size 2, fore "gray20", back "white", font "standard", hgap 0
   86.14 +%default 2 size 7, vgap 10, prefix " ", ccolor "blue"
   86.15 +%default 3 size 2, bar "gray70", vgap 10
   86.16 +%default 4 size 5, fore "gray20", vgap 30, prefix " ", font "standard"
   86.17 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   86.18 +%%
   86.19 +%% Default settings that are applied to TAB-indented lines.
   86.20 +%%
   86.21 +%tab 1 size 5, vgap 40, prefix "  ", icon box "red" 50
   86.22 +%tab 2 size 4, vgap 40, prefix "      ", icon arc "yellow" 50
   86.23 +%tab 3 size 3, vgap 40, prefix "            ", icon delta3 "white" 40
   86.24 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   86.25 +%page
   86.26 +
   86.27 +Five - Zope 3 in Zope 2
   86.28 +
   86.29 +
   86.30 +
   86.31 +
   86.32 +%center
   86.33 +Martijn Faassen, Infrae
   86.34 +faassen@infrae.com
   86.35 +
   86.36 +%page
   86.37 +
   86.38 +Motto
   86.39 +
   86.40 +
   86.41 +It was the dawn of the third age of Zope. The Five project was a dream given form. Its goal: to use Zope 3 technologies in Zope 2.7 by creating a Zope 2 product where Zope 3 and Zope 2 could work out their differences peacefully. 
   86.42 +
   86.43 +(Babylon 5 season 1 intro, creatively quoted)
   86.44 +
   86.45 +%page
   86.46 +
   86.47 +Motto 2
   86.48 +
   86.49 +
   86.50 +The Law of Fives states simply that: ALL THINGS HAPPEN IN FIVES, OR ARE DIVISIBLE BY OR ARE MULTIPLES OF FIVE, OR ARE SOMEHOW DIRECTLY OR INDIRECTLY RELATED TO FIVE.
   86.51 +
   86.52 +THE LAW OF FIVES IS NEVER WRONG. 
   86.53 +
   86.54 +(Principia Discordia)
   86.55 +
   86.56 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   86.57 +%page
   86.58 +
   86.59 +The problem
   86.60 +
   86.61 +
   86.62 +	We're using Zope 2 in production
   86.63 +
   86.64 +	Zope 2 is showing its age
   86.65 +
   86.66 +	Zope 3 has better ways to do things
   86.67 +
   86.68 +	But can't just switch, we have customers!
   86.69 +
   86.70 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   86.71 +%page
   86.72 +
   86.73 +Benefits of using Zope 3 in Zope 2
   86.74 +
   86.75 +
   86.76 +	Able to use Zope 3 technologies right away
   86.77 +
   86.78 +	Don't reinvent the wheel/APIs
   86.79 +
   86.80 +	Better prepared for Zope 3 transition
   86.81 +
   86.82 +	Evolution, not revolution
   86.83 +
   86.84 +	Convergence, not divergence
   86.85 +
   86.86 +%page
   86.87 +
   86.88 +What works now?
   86.89 +
   86.90 +
   86.91 +	Interfaces (zope.interface)
   86.92 +
   86.93 +	Schema (zope.schema)
   86.94 +
   86.95 +	ZCML (zope.configuration)
   86.96 +
   86.97 +	Adapters (zope.component)
   86.98 +
   86.99 +	Views, including layers, skins (zope.component)
  86.100 +
  86.101 +%page
  86.102 +
  86.103 +Brief demo
  86.104 +
  86.105 +
  86.106 +	Show ZCML, adapters and views in action
  86.107 + 
  86.108 +%page
  86.109 +
  86.110 +Next?
  86.111 +
  86.112 +
  86.113 +	Utilities (global ones should work)
  86.114 +
  86.115 +	Forms
  86.116 +
  86.117 +	Views (improve the current system)
  86.118 +
  86.119 +	Who knows?
  86.120 +
  86.121 +%page
  86.122 +
  86.123 +Plans
  86.124 +
  86.125 +
  86.126 +	Relicense from BSD to generic ZPL 2.1
  86.127 +
  86.128 +	Move from CVS at Infrae into SVN at codespeak.net
  86.129 +
  86.130 +	Convergence; join us!
  86.131 +
    87.1 new file mode 100644
    87.2 --- /dev/null
    87.3 +++ b/doc/presentations/five_directions.mgp
    87.4 @@ -0,0 +1,88 @@
    87.5 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    87.6 +%deffont "standard" xfont "helvetica-medium-r"
    87.7 +%deffont "thick" xfont "helvetica-bold-r"
    87.8 +%deffont "typewriter" xfont "courier-medium-r"
    87.9 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   87.10 +%%
   87.11 +%% Default settings per each line numbers.
   87.12 +%%
   87.13 +%default 1 area 90 90, leftfill, size 2, fore "gray20", back "white", font "standard", hgap 0
   87.14 +%default 2 size 7, vgap 10, prefix " ", ccolor "blue"
   87.15 +%default 3 size 2, bar "gray70", vgap 10
   87.16 +%default 4 size 5, fore "gray20", vgap 30, prefix " ", font "standard"
   87.17 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   87.18 +%%
   87.19 +%% Default settings that are applied to TAB-indented lines.
   87.20 +%%
   87.21 +%tab 1 size 5, vgap 40, prefix "  ", icon box "red" 50
   87.22 +%tab 2 size 4, vgap 40, prefix "      ", icon arc "yellow" 50
   87.23 +%tab 3 size 3, vgap 40, prefix "            ", icon delta3 "white" 40
   87.24 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   87.25 +%page
   87.26 +
   87.27 +Five - Zope 3 in Zope 2
   87.28 +
   87.29 +
   87.30 +
   87.31 +
   87.32 +%center
   87.33 +Martijn Faassen, Infrae
   87.34 +faassen@infrae.com
   87.35 +Five developer
   87.36 +
   87.37 +%page
   87.38 +
   87.39 +Five future directions
   87.40 +
   87.41 +
   87.42 +	What might happen
   87.43 +
   87.44 +%page
   87.45 +
   87.46 +Unique id service support
   87.47 +
   87.48 +