{ "info": { "author": "Michael Kerrin", "author_email": "zope3-users@zope.org", "bugtrack_url": null, "classifiers": [ "Environment :: Web Environment", "Framework :: Zope3", "Intended Audience :: Developers", "License :: OSI Approved :: Zope Public License", "Programming Language :: Python" ], "description": "===================\nz3c.condtionalviews\n===================\n\nz3c.conditionalviews is a mechanism to validate a HTTP request based on some\nconditional protocol like entity tags, or last modification date. It is also\nextensible so that protocols like WebDAV can define there own conditional\nprotocol like the IF header.\n\nIt works by implementing each conditional protocol as a `IHTTPValidator`\nutility, see etag and lastmodification modules for the most common use cases.\nThen when certain views are called by the publisher we lookup these utilities\nand ask them to validate the request object according to whatever protocol\nthe utility implements.\n\nAt the time of the view is called, and when we validate the request, we\ngenerally have access to the context, request and view itself. So the\n`IHTTPValidator` utilities generally adapt these 3 objects to an object\nimplementing an interface specific to the protocol in question. For example\nthe entity tag validator looks up an adapter implementing `IEtag`.\n\nIntegration with Zope\n=====================\n\n >>> import zope.component\n >>> import zope.interface\n >>> import z3c.conditionalviews.interfaces\n >>> import z3c.conditionalviews.tests\n\nDecorator\n---------\n\nIn order to integrate common browser views that can be cached, we can decorate\nthe views call method with the `z3c.conditionalviews.ConditionalView` object.\nNote that all the views used in this test are defined in the ftesting.zcml\nfile.\n\n >>> response = http(r\"\"\"\n ... GET /@@simpleview.html HTTP/1.1\n ... Host: localhost\n ... \"\"\", handle_errors = False)\n >>> response.getStatus()\n 200\n >>> response.getHeader('content-length')\n '82'\n >>> response.getHeader('content-type')\n 'text/plain'\n >>> print response.getBody()\n xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nSince we haven't yet defined an adapter implementing IETag, the response\ncontains no ETag header.\n\n >>> response.getHeader('ETag') is None\n True\n\nDefine our IETag implementation.\n\n >>> class SimpleEtag(object):\n ... zope.interface.implements(z3c.conditionalviews.interfaces.IETag)\n ... def __init__(self, context, request, view):\n ... pass\n ... weak = False\n ... etag = \"3d32b-211-bab57a40\"\n\n >>> zope.component.getGlobalSiteManager().registerAdapter(\n ... SimpleEtag,\n ... (zope.interface.Interface,\n ... zope.publisher.interfaces.browser.IBrowserRequest,\n ... zope.interface.Interface))\n\n >>> response = http(r\"\"\"\n ... GET /@@simpleview.html HTTP/1.1\n ... Host: localhost\n ... \"\"\", handle_errors = False)\n >>> response.getStatus()\n 200\n >>> response.getHeader('content-length')\n '82'\n >>> response.getHeader('content-type')\n 'text/plain'\n >>> response.getHeader('ETag')\n '\"3d32b-211-bab57a40\"'\n >>> print response.getBody()\n xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nNow by setting the request header If-None-Match: \"3d32b-211-bab57a40\", our\nview fails the validation and a 304 response is returned.\n\n >>> response = http(r\"\"\"\n ... GET /@@simpleview.html HTTP/1.1\n ... Host: localhost\n ... If-None-Match: \"3d32b-211-bab57a40\"\n ... \"\"\", handle_errors = False)\n >>> response.getStatus()\n 304\n >>> response.getHeader('ETag')\n '\"3d32b-211-bab57a40\"'\n >>> response.getBody()\n ''\n\nXXX - this seems wrong the content-length and content-type should not be set\nfor this response.\n\n >>> response.getHeader('content-length')\n '0'\n >>> response.getHeader('content-type')\n 'text/plain'\n\nNow make sure that we haven't broken the publisher, by making sure that we\ncan still pass arguments to the different views.\n\n >>> response = http(r\"\"\"\n ... GET /@@simpleview.html?letter=y HTTP/1.1\n ... Host: localhost\n ... \"\"\", handle_errors = False)\n >>> response.getStatus()\n 200\n >>> response.getHeader('content-length')\n '82'\n >>> print response.getBody()\n yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy\n yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy\n\nWe are now getting a charset value for this request because the default\nvalue for the SimpleView is not a unicode string, while the data received\nfrom the request is automatically converted to unicode by default.\n\n >>> response.getHeader('content-type')\n 'text/plain;charset=utf-8'\n\nSince there is a query string present in the request, we don't set the ETag\nheader.\n\n >>> response.getHeader('ETag') is None\n True\n\nThe query string present in the following request causes the request to\nbe valid, otherwise it would be invalid.\n\n >>> response = http(r\"\"\"\n ... GET /@@simpleview.html?letter=y HTTP/1.1\n ... If-None-Match: \"3d32b-211-bab57a40\"\n ... Host: localhost\n ... \"\"\", handle_errors = False)\n >>> response.getStatus()\n 200\n\nGeneric HTTP conditional publication\n====================================\n\nWe can integrate the validation method with the publication call method. This\nas the effect of trying to validate every request that passes through the\npublications `callObject` method. This is useful to validate requests that\nmodify objects so that the client can say modify this resource if it hasn't\nchanged since it last downloaded the resource, or if there is no existing\nresource at a location.\n\nThis has the added benifit in that we don't have to specify how some one\nimplements the PUT method.\n\n >>> resp = http(r\"\"\"\n ... PUT /testfile HTTP/1.1\n ... Authorization: Basic mgr:mgrpw\n ... Content-type: text/plain\n ... Content-length: 55\n ... aaaaaaaaaa\n ... aaaaaaaaaa\n ... aaaaaaaaaa\n ... aaaaaaaaaa\n ... aaaaaaaaaa\"\"\", handle_errors = False)\n >>> resp.getStatus()\n 201\n >>> resp.getHeader('Content-length')\n '0'\n >>> resp.getHeader('Location')\n 'http://localhost/testfile'\n >>> resp.getHeader('ETag', None) is None\n True\n \nWe can now get the resource and the entity tag.\n\n >>> resp = http(r\"\"\"\n ... GET /testfile HTTP/1.1\n ... Authorization: Basic mgr:mgrpw\n ... \"\"\")\n >>> resp.getStatus()\n 200\n >>> resp.getHeader('ETag')\n '\"testfile:1\"'\n >>> print resp.getBody()\n aaaaaaaaaa\n aaaaaaaaaa\n aaaaaaaaaa\n aaaaaaaaaa\n aaaaaaaaaa\n\nWe could have used the HEAD method to get the entity tag.\n\n >>> resp = http(r\"\"\"\n ... HEAD /testfile HTTP/1.1\n ... Authorization: Basic mgr:mgrpw\n ... \"\"\")\n >>> resp.getStatus()\n 200\n >>> resp.getHeader('ETag')\n '\"testfile:1\"'\n\nWith no 'If-None-Match' header we override the data.\n\n >>> resp = http(r\"\"\"\n ... PUT /testfile HTTP/1.1\n ... Authorization: Basic mgr:mgrpw\n ... Content-type: text/plain\n ... Content-length: 55\n ... bbbbbbbbbb\n ... bbbbbbbbbb\n ... bbbbbbbbbb\n ... bbbbbbbbbb\n ... bbbbbbbbbb\"\"\", handle_errors = False)\n >>> resp.getStatus()\n 200\n >>> resp.getHeader('Content-length')\n '0'\n >>> resp.getHeader('Location', None) is None\n True\n >>> resp.getHeader('ETag')\n '\"testfile:2\"'\n\n >>> resp = http(r\"\"\"\n ... GET /testfile HTTP/1.1\n ... Authorization: Basic mgr:mgrpw\n ... \"\"\")\n >>> resp.getStatus()\n 200\n >>> print resp.getBody()\n bbbbbbbbbb\n bbbbbbbbbb\n bbbbbbbbbb\n bbbbbbbbbb\n bbbbbbbbbb\n\nSpecifying a `If-None-Match: \"*\"` header, says to upload the data only if there\nis no resource at the location specified in the request URI. If there is a\nresource at the location then a `412 Precondition Failed` response is\nreturned and the resource is not modified'\n\n >>> resp = http(r\"\"\"\n ... PUT /testfile HTTP/1.1\n ... Authorization: Basic mgr:mgrpw\n ... If-None-Match: \"*\"\n ... Content-type: text/plain\n ... Content-length: 55\n ... cccccccccc\n ... cccccccccc\n ... cccccccccc\n ... cccccccccc\n ... cccccccccc\"\"\")\n >>> resp.getStatus()\n 412\n >>> resp.getHeader('Content-length')\n '0'\n >>> resp.getHeader('Location', None) is None\n True\n >>> resp.getHeader('ETag')\n '\"testfile:2\"'\n\nThe file does not change.\n\n >>> resp = http(r\"\"\"\n ... GET /testfile HTTP/1.1\n ... Authorization: Basic mgr:mgrpw\n ... \"\"\")\n >>> resp.getStatus()\n 200\n >>> print resp.getBody()\n bbbbbbbbbb\n bbbbbbbbbb\n bbbbbbbbbb\n bbbbbbbbbb\n bbbbbbbbbb\n\nAnd now since testfile2 does exist yet we content the content.\n\n >>> resp = http(r\"\"\"\n ... PUT /testfile2 HTTP/1.1\n ... Authorization: Basic mgr:mgrpw\n ... If-None-Match: \"*\"\n ... Content-type: text/plain\n ... Content-length: 55\n ... yyyyyyyyyy\n ... yyyyyyyyyy\n ... yyyyyyyyyy\n ... yyyyyyyyyy\n ... yyyyyyyyyy\"\"\")\n >>> resp.getStatus()\n 201\n >>> resp.getHeader('Content-length')\n '0'\n >>> resp.getHeader('Location')\n 'http://localhost/testfile2'\n >>> resp.getHeader('ETag', None) is None # No etag adapter is configured\n True\n\n >>> resp = http(r\"\"\"\n ... GET /testfile2 HTTP/1.1\n ... Authorization: Basic mgr:mgrpw\n ... \"\"\")\n >>> resp.getStatus()\n 200\n >>> print resp.getBody()\n yyyyyyyyyy\n yyyyyyyyyy\n yyyyyyyyyy\n yyyyyyyyyy\n yyyyyyyyyy\n\nWe can now delete the resource, only if it hasn't changed. So for the\n'/testfile' resource we can use its first entity tag to confirm this.\n\n >>> resp = http(r\"\"\"\n ... DELETE /testfile HTTP/1.1\n ... Authorization: Basic mgr:mgrpw\n ... If-Match: \"testfile:1\"\n ... \"\"\")\n >>> resp.getStatus()\n 412\n\nAnd the file still exists.\n\n >>> resp = http(r\"\"\"\n ... GET /testfile HTTP/1.1\n ... Authorization: Basic mgr:mgrpw\n ... \"\"\")\n >>> resp.getStatus()\n 200\n\nBut using a valid entity tag we can delete the resource.\n\n >>> resp = http(r\"\"\"\n ... DELETE /testfile HTTP/1.1\n ... Authorization: Basic mgr:mgrpw\n ... If-Match: \"testfile:2\"\n ... \"\"\")\n >>> resp.getStatus()\n 200\n >>> resp.getBody()\n ''\n\n >>> resp = http(r\"\"\"\n ... GET /testfile HTTP/1.1\n ... Authorization: Basic mgr:mgrpw\n ... \"\"\")\n >>> resp.getStatus()\n 404\n\nMethod not allowed\n==================\n\nWe should still get a `405 Method Not Allowed` status for methods that aren't\nregistered yet.\n\nWe need to be logged in order to traverse to the file.\n\n >>> resp = http(r\"\"\"\n ... FROG /testfile2 HTTP/1.1\n ... Authorization: Basic mgr:mgrpw\n ... \"\"\")\n >>> resp.getStatus()\n 405\n >>> resp.getHeader('ETag', None) is None\n True\n\nCleanup\n=======\n\n >>> zope.component.getGlobalSiteManager().unregisterAdapter(\n ... SimpleEtag,\n ... (zope.interface.Interface,\n ... zope.publisher.interfaces.browser.IBrowserRequest,\n ... zope.interface.Interface))\n True\n\n\n===============================\nChanges in z3c.conditionalviews\n===============================\n\n1.0 (2008-09-27)\n================\n\n- Using IDCTimes instead of IZopeDublinCore because IDCTimes is the actual\n required interface.\n\n\n1.0b\n====\n\n- Fixed time zone issue in processing `If-Modified` protocol.\n\n0.9\n===\n\n- Initial release.", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "http://svn.zope.org/z3c.conditionalviews/", "keywords": null, "license": "ZPL 2.1", "maintainer": null, "maintainer_email": null, "name": "z3c.conditionalviews", "package_url": "https://pypi.org/project/z3c.conditionalviews/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/z3c.conditionalviews/", "project_urls": { "Download": "UNKNOWN", "Homepage": "http://svn.zope.org/z3c.conditionalviews/" }, "release_url": "https://pypi.org/project/z3c.conditionalviews/1.0/", "requires_dist": null, "requires_python": null, "summary": "This package intergrates with the zope publisher to validate conditional\nrequests based on the `If-None-Match`, `If-Match`, and `If-Modified-Since`,\n`If-UnModifiedSince` protocols.", "version": "1.0" }, "last_serial": 802004, "releases": { "0.9": [ { "comment_text": "", "digests": { "md5": "f27d5f818d0e1447276f2dadccb4739f", "sha256": "56b9bc479689aebd5929fefff455ea65ed3cd83ce01333a1bc41bf02ff67afee" }, "downloads": -1, "filename": "z3c.conditionalviews-0.9.tar.gz", "has_sig": false, "md5_digest": "f27d5f818d0e1447276f2dadccb4739f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13815, "upload_time": "2007-07-02T18:16:47", "url": "https://files.pythonhosted.org/packages/b7/7b/1dfe705613f137da4d2a34ffea51bd219efe1c1ef574ccf44d6c2f1238ba/z3c.conditionalviews-0.9.tar.gz" } ], "1.0": [ { "comment_text": "", "digests": { "md5": "6f659472e8b941580b06704ee8ec7633", "sha256": "ce632a610181976c673216abe0b7b50525bd43efd24986e6477a7ca6037f2ea9" }, "downloads": -1, "filename": "z3c.conditionalviews-1.0.tar.gz", "has_sig": false, "md5_digest": "6f659472e8b941580b06704ee8ec7633", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16278, "upload_time": "2008-09-27T09:56:00", "url": "https://files.pythonhosted.org/packages/b7/0f/2ae3036dd12441ed9b092f7dea542f63cc862b857d583569ad6ab7ea9552/z3c.conditionalviews-1.0.tar.gz" } ], "1.0b": [ { "comment_text": "", "digests": { "md5": "543e77a7c01d1f96acc645d2312407c4", "sha256": "c7b9c60b3247cfabf6c4e45f1bd6ccff5fe00cb8924dae2b4737d40b5736e679" }, "downloads": -1, "filename": "z3c.conditionalviews-1.0b.tar.gz", "has_sig": false, "md5_digest": "543e77a7c01d1f96acc645d2312407c4", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13957, "upload_time": "2007-07-10T12:39:22", "url": "https://files.pythonhosted.org/packages/54/e2/0925379c34fcd828b587da0ee5c5b4909ee7a249cb823b79254abf10955b/z3c.conditionalviews-1.0b.tar.gz" } ], "1.0b1": [ { "comment_text": "", "digests": { "md5": "a21422022b070871213369dde0127938", "sha256": "faacaf254ca96f144e460a8ed7727129a1e4405d636dda766286645665526365" }, "downloads": -1, "filename": "z3c.conditionalviews-1.0b1.tar.gz", "has_sig": false, "md5_digest": "a21422022b070871213369dde0127938", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16109, "upload_time": "2008-02-25T19:06:38", "url": "https://files.pythonhosted.org/packages/5b/0d/34946191fde0f413d26696097220c3e3aec98abc7a6dfc5ab27503faff48/z3c.conditionalviews-1.0b1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "6f659472e8b941580b06704ee8ec7633", "sha256": "ce632a610181976c673216abe0b7b50525bd43efd24986e6477a7ca6037f2ea9" }, "downloads": -1, "filename": "z3c.conditionalviews-1.0.tar.gz", "has_sig": false, "md5_digest": "6f659472e8b941580b06704ee8ec7633", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16278, "upload_time": "2008-09-27T09:56:00", "url": "https://files.pythonhosted.org/packages/b7/0f/2ae3036dd12441ed9b092f7dea542f63cc862b857d583569ad6ab7ea9552/z3c.conditionalviews-1.0.tar.gz" } ] }