{ "info": { "author": "Plone Foundation", "author_email": "plone-developers@lists.sourceforge.net", "bugtrack_url": null, "classifiers": [ "Development Status :: 4 - Beta", "Environment :: Web Environment", "Framework :: Plone", "Framework :: Plone :: 4.3", "Framework :: Plone :: 5.0", "Framework :: Plone :: 5.1", "Framework :: Plone :: 5.2", "License :: OSI Approved :: GNU General Public License v2 (GPLv2)", "Programming Language :: Python", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "Introduction\n============\n\n.. image:: https://secure.travis-ci.org/plone/plone.app.drafts.png?branch=master\n :alt: Travis CI badge\n :target: http://travis-ci.org/plone/plone.app.drafts\n\n.. image:: https://coveralls.io/repos/plone/plone.app.drafts/badge.png?branch=master\n :alt: Coveralls badge\n :target: https://coveralls.io/r/plone/plone.app.drafts\n\nplone.app.drafts implements services for managing auto-saved content drafts in Plone.\nThis addresses two problems:\n\n* If the browser is accidentally closed or crashes whilst a page is being edited, all changes are lost.\n* Some data may need to be managed asynchronously,\n e.g. via a pop-up dialogue box in the visual editor.\n This data should not be saved until the form is saved\n (and in the case of an add form, it is impossible to do so).\n\nThe former problem pertains to any content add or edit form.\nThe latter applies in particular to the \"tiles\" model as implemented by `plone.app.tiles`_ and its dependencies.\n\nInstallation\n============\n\nYou can install plone.app.drafts as normal,\nby depending on it in your ``setup.py`` or adding it to the ``eggs`` list in your buildout.\nThe package will self-configure for Plone; there is no need to add a ZCML slug.\n\nYou will also need to install the product from the portal_quickinstaller tool or Plone's Add-ons control panel.\n\nDraft storage\n=============\n\nAfter installation,\nyou should notice a new tool called ``portal_drafts`` in the ZMI.\nWhen drafts have been created, you can browse them here, and purge them if you believe they are stale.\n\nTo access the draft storage in code, look it up as a (local) utility:\n\n >>> from zope.component import getUtility\n >>> draftStorage = getUtility(IDraftStorage)\n\nThe draft storage contains methods for creating, finding and discarding drafts.\nThis is mostly useful for integration logic.\n\nA draft is accessed by using a hierarchy of keys, all strings:\n\n1. The user id of the user owning the draft\n2. A unique \"target object\" key that represents the object being drafted\n3. A unique draft name\n\nThe target object key is by convention a string representation of a unique integer id (via `zope.intid`_) for drafts that represent existing content objects being edited, or a string like::\n\n ':'\n\n(e.g. ``'123456:Document'``) for drafts representing objects being added to a particular container.\n\nThe draft name is unique and assigned when the draft is created.\n\nSee the ``IDraftStorage`` interface for details on how to access drafts based on these keys.\n\nThe draft object itself is a minimal persistent object providing the ``IDraft`` interface.\nImportantly, it is *not* a full-blown content object.\nIt has no intrinsic security, no workflow, and no standard fields.\nIt *is* however annotatable, i.e. it may be adapted to ``IAnnotations``.\n\nA draft has a few basic attributes\n(``__name__``, ``_draftUserId``, and ``_draftTargetKey``),\nbut is otherwise a blank canvas.\nDraft data may be stored as attributes, or in annotations.\nThe attributes that are used depends on how the draft is integrated.\nThe two primary patterns are:\n\nAutosave\n An explicit or timed background request submits the edit form to a handler,\n which extracts the form data and saves it on the draft object,\n e.g. in a ``form`` dictionary.\n The draft is updated periodically.\n On a successful save or cancel the draft is simply discarded.\n If the user returns to the edit screen after a browser crash or abandoned session,\n however, the request may be restored by copying the draft data to the real ``request.form`` dictionary prior to rendering the edit form.\n Provided the edit form is well-behaved,\n it should then show the last auto-saved values.\n These values can then be edited, before they are saved as normal.\n\nAsynchronous updates\n An AJAX dialogue box can be used to configure an object asynchronously.\n For example, a content type that supports attachments may use such a dialogue box to upload attached files.\n These must be stored temporarily, but should not be persisted with the real content object until the underlying edit form is saved\n (and should be discarded if it is cancelled).\n The file upload handler can save the data to the drafts storage,\n and then copy it to its final location on save.\n\n A helper function called ``syncDraft`` is provided for this purpose in the ``plone.app.drafts.utils`` module.\n It looks up any number of named ``IDraftSyncer`` multi-adapters (on the draft object and the target content object) and calls them in turn.\n\nCurrent draft management\n========================\n\nTo access the current draft from code,\nuse the ``getCurrentDraft()`` helper function, passing it the current request:\n\n >>> from plone.app.drafts.utils import getCurrentDraft\n >>> currentDraft = getCurrentDraft(request)\n\nThis may return ``None`` if there is no current draft.\nIt is possible that the necessary information for creating a draft (user id and target key) are known,\nbut that no draft has been created yet.\nIn this case, you can request that a new draft is created on demand, by passing ``create=True``.\n\nThe current draft user id, target key and (once the draft has been created) draft name are looked up from the request, by adapting it to the interface ``ICurrentDraftManagement``.\nYou should not normally need to use this yourself, unless you are integrating the draft storage with an external framework.\n\nThe default ``ICurrentDraftManagement`` adapter allows the user id, target key and draft name to be set explicitly.\nIf they are not set, they are read from the request.\nThis means that they may come in request parameters, or in cookies.\nThe request keys are ``plone.app.drafts.targetKey`` and ``plone.app.drafts.draftName``.\nThe user id always defaults to the currently logged in user's id.\n\nThe ``ICurrentDraftManagement`` adapter also exposes lifecycle functions that can save or discard the current draft information.\nThe default implementation does this using cookies that are set for a path corresponding to the edit page.\nIt is the responsibility of the add/edit form integration code to ensure that this cookie is set for a path that is specific enough not to \"leak\" to other edit pages,\nbut still allows AJAX dialogue boxes and other asynchronous requests to obtain the draft information if required.\n\nIntegration\n===========\n\nArchetypes integration is provided in the ``archetypes`` module,\nwhich is configured if Archetypes is installed.\nThe integration works as follows:\n\n* An ``IEditBegunEvent`` is fired by Archetypes when the user enters an add/edit form.\n An event handler for this event will calculate a target key for the context, taking \"add forms\" based on the ``portal_factory`` tool into account.\n Provided a key can be calculated, it is saved via an ``ICurrentDraftManagement`` adapter as explained below.\n A draft is not created immediately, but if a single draft is discovered in the storage for this user id and key,\n that draft name is saved so that it will be returned when ``getCurrentDraft()`` is called.\n The cookie path is calculated as well and saved.\n This ensures that if the draft is created in an asynchronous request with a \"deeper\" URL, the cookie path will be the same.\n\n* An event handler is installed for ``IObjectInitializedEvent`` and ``IObjectEditedEvent``, which are fired when the user clicks *Save* on a valid add or edit form, respectively.\n This handler will get the current draft if it has been created during the editing cycle, and uses the ``syncDraft()`` method to synchronise it as necessary.\n The draft is then discarded, as is the current draft information, causing the cookies to expire.\n\n* An event handler is also installed for ``IEditCancelledEvent``, which is fired when the user clicks *Cancel*.\n This simply discards the draft and current draft information without synchronisation.\n\nThe draft proxy\n---------------\n\nSimple drafting integration will tend to just store data on the draft object directly.\nHowever, it may sometimes be useful to have an object that behaves as a facade onto a \"real\" object, so that:\n\n* If an attribute or annotation value that has never been set on the draft is read, the value from the underlying target object is used.\n\n* If an attribute or annotation value is set, it is written to the draft.\n If it is subsequently read, it is read from the draft.\n\n* If an attribute or annotation value is deleted,\n it is deleted from the draft, and the fact that it was deleted is recorded so that this may be later synchronised with the underlying object when the draft is \"saved\"\n (e.g. via an ``IDraftSyncer`` adapter).\n\nTo get these semantics, create a ``DraftProxy`` object like so:\n\n >>> from plone.app.drafts.proxy import DraftProxy\n >>> proxy = DraftProxy(draft, target)\n\nHere, ``draft`` is an ``IDraft`` object and ``target`` is the object it is a draft of.\nIf attributes are deleted, they will be stored in one of two sets:\n\n >>> deletedAttributes = getattr(draft, '_proxyDeleted', set())\n >>> deletedAnnotations = getattr(draft, '_proxyAnnotationsDeleted', set())\n\nNote that these attributes may not be present if nothing has ever been deleted,\nso we need to fetch them defensively.\n\n.. _plone.app.tiles: http://pypi.python.org/pypi/plone.app.tiles\n.. _zope.intid: http://pypi.python.org/pypi/zope.intid\n\nChangelog\n=========\n\n1.1.3 (2019-02-10)\n------------------\n\n- Python 3 compatible\n [vangheem, petschki]\n\n\n1.1.2 (2017-07-03)\n------------------\n\n- Fix issue where draft sync failed because draft might have been withotu aq wrapper\n [datakurre]\n\n1.1.1 (2016-09-09)\n------------------\n\n- Remove forgotten debug print\n [datakurre]\n\n1.1.0 (2016-09-09)\n------------------\n\n- Add support for drafted content preview for Dexterity content when request is\n marked with IDisplayFormDrafting\n [datakurre]\n\n- Fix not set cookie values twice\n [vangheem]\n\n- Fix to always sync drafts before object modified event subscribers\n (especially indexing) are called\n [datakurre]\n\n- Behavior shortname ``plone.draftable`` added.\n [jensens, datakurre]\n\n- Update testing infrastructure and fix code analysis errors.\n [gforcada]\n\n\n1.0 (2016-03-28)\n----------------\n\n- Make sure draft is available before initializing the draft proxy object\n [vangheem]\n\n1.0b3 (2015-06-10)\n------------------\n\n- Fix issue where drafting caused 'AttributeError: This object has no id'\n [datakurre]\n- Fix issue where add forms with different content type but the context had conflicting draft\n [datakurre]\n\n1.0b2 (2015-06-02)\n------------------\n\n- Fix rare issue where Dexterity draft had wrong portal_type\n [datakurre]\n\n1.0b1 (2015-05-26)\n------------------\n\n- Add support for drafting on Dexterity add and edit forms\n when the drafting behavior is enabled for the content type\n [datakurre]\n\n- Add autosave (using AJAX validation calls) support for\n Dexterity add and edit forms when drafting behavior is\n enabled for the content type\n [datakurre]\n\n- Change to use UUIDs instead of intids\n [datakurre]\n\n- Change Archetypes-support to be disabled by default\n [datakurre]\n\n Archetypes-support can included in zcml with::\n\n \n\n\n1.0a2 (2011-10-11)\n------------------\n\n- Added MANIFEST.in to fix our previous release. It was missing the history file.\n\n\n1.0a1 (2011-10-10)\n------------------\n\n- Initial release.", "description_content_type": "", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "http://plone.org", "keywords": "plone draft content", "license": "GPL", "maintainer": "", "maintainer_email": "", "name": "plone.app.drafts", "package_url": "https://pypi.org/project/plone.app.drafts/", "platform": "", "project_url": "https://pypi.org/project/plone.app.drafts/", "project_urls": { "Homepage": "http://plone.org" }, "release_url": "https://pypi.org/project/plone.app.drafts/1.1.3/", "requires_dist": null, "requires_python": "", "summary": "Low-level container for draft content", "version": "1.1.3" }, "last_serial": 4802492, "releases": { "1.0": [ { "comment_text": "", "digests": { "md5": "c546ad411575ddcf7c932ffef4e78106", "sha256": "fb552dfacc23847ff6171cbb204216e82689ba5f14974f25b877871d76a84066" }, "downloads": -1, "filename": "plone.app.drafts-1.0.tar.gz", "has_sig": false, "md5_digest": "c546ad411575ddcf7c932ffef4e78106", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 35494, "upload_time": "2016-03-28T16:32:44", "url": "https://files.pythonhosted.org/packages/67/76/ea48cc8a936efd08ca12d2cb97a45b106a7db335d412f57d50e6ca7a0462/plone.app.drafts-1.0.tar.gz" } ], "1.0a1": [ { "comment_text": "", "digests": { "md5": "9c1475adb6eb3df764f14292088e48e6", "sha256": "375a30d220853a82747883449be034c11acc09d100ea954ec9f7c3d5b5da7670" }, "downloads": -1, "filename": "plone.app.drafts-1.0a1.tar.gz", "has_sig": false, "md5_digest": "9c1475adb6eb3df764f14292088e48e6", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 17825, "upload_time": "2011-10-11T14:37:23", "url": "https://files.pythonhosted.org/packages/14/69/0b12d977d881af35d2a08de606dfbc9365d5efbeca31b5e606e59636307a/plone.app.drafts-1.0a1.tar.gz" } ], "1.0a2": [ { "comment_text": "", "digests": { "md5": "568d38873698dbddbed25ab0ac2baa2d", "sha256": "413e0503d326816c32a07f1cbd9e9cc254c14515cdcc73021b0820228941fa2a" }, "downloads": -1, "filename": "plone.app.drafts-1.0a2.tar.gz", "has_sig": false, "md5_digest": "568d38873698dbddbed25ab0ac2baa2d", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 28237, "upload_time": "2011-10-11T15:48:33", "url": "https://files.pythonhosted.org/packages/8f/d4/2bc5ee79e4556450b06981d7b8d67f99afaf8c0c3826336cedf201207c67/plone.app.drafts-1.0a2.tar.gz" } ], "1.0b1": [ { "comment_text": "", "digests": { "md5": "0b3b5fc1f2985bc36f75f8a58a3e695d", "sha256": "6499681ff2fe78b4a7128454c6e40ae96c665856b3e0c30ec9b4703054d9e7e6" }, "downloads": -1, "filename": "plone.app.drafts-1.0b1.tar.gz", "has_sig": false, "md5_digest": "0b3b5fc1f2985bc36f75f8a58a3e695d", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 37047, "upload_time": "2015-05-26T19:08:21", "url": "https://files.pythonhosted.org/packages/6d/51/2a8ec0e5860df69105b1a20a8469f05adeaecbe20089f1d7d85bc7f359ec/plone.app.drafts-1.0b1.tar.gz" } ], "1.0b2": [ { "comment_text": "", "digests": { "md5": "829db98c62961a60753f8a723a565647", "sha256": "487b52f694660dfd8f4adb5d4c71a3e6e86f2273b4af20dbfeda7094126962e3" }, "downloads": -1, "filename": "plone.app.drafts-1.0b2.tar.gz", "has_sig": false, "md5_digest": "829db98c62961a60753f8a723a565647", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 35200, "upload_time": "2015-06-02T16:31:44", "url": "https://files.pythonhosted.org/packages/df/33/9128db83a8c4698ae13f0fc33da731c72c99c6791bddb1adfd7a19500d08/plone.app.drafts-1.0b2.tar.gz" } ], "1.0b3": [ { "comment_text": "", "digests": { "md5": "ddd1c18b6114f681bda02a01dad52f62", "sha256": "b4023c5008adf9f2b41eb9ced75455b29cfd4f27d7f28c9feea031a903f1afd4" }, "downloads": -1, "filename": "plone.app.drafts-1.0b3.tar.gz", "has_sig": false, "md5_digest": "ddd1c18b6114f681bda02a01dad52f62", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 35339, "upload_time": "2015-06-10T15:49:38", "url": "https://files.pythonhosted.org/packages/03/80/84c2e55ea42f79c58a93dec26a70c06270a1e28ea24194ffe40d9aec7a6d/plone.app.drafts-1.0b3.tar.gz" } ], "1.1.0": [ { "comment_text": "", "digests": { "md5": "d6122ef4d4df1332804d4997e1856567", "sha256": "752f12b3c7d79eeadefd629b057d35a617bd0db4fa65d90bd375dddb7c86e252" }, "downloads": -1, "filename": "plone.app.drafts-1.1.0.tar.gz", "has_sig": false, "md5_digest": "d6122ef4d4df1332804d4997e1856567", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 34633, "upload_time": "2016-09-08T22:38:35", "url": "https://files.pythonhosted.org/packages/99/91/dc84f9e624ad469b47eacbf4faaeb29dacb64f58c8b1c4ee7daa6db2f417/plone.app.drafts-1.1.0.tar.gz" } ], "1.1.1": [ { "comment_text": "", "digests": { "md5": "0567994626bfc8ba0c8f266616d105ef", "sha256": "e0ca53b4ced7de2d6f6164322cd3faec9084aa6fad88c5aeb055bb6ac2758e52" }, "downloads": -1, "filename": "plone.app.drafts-1.1.1.tar.gz", "has_sig": false, "md5_digest": "0567994626bfc8ba0c8f266616d105ef", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 32757, "upload_time": "2016-09-08T23:06:11", "url": "https://files.pythonhosted.org/packages/f6/00/5f81102546fbdf9e10bc6ed9f59fd66ee1e829924af5777024480964dc36/plone.app.drafts-1.1.1.tar.gz" } ], "1.1.2": [ { "comment_text": "", "digests": { "md5": "f7e0ee9cafe384d8e1a48894b1dd7be8", "sha256": "f75306bc775828ed172721980371d032d9450797524d1186b405cecbbcefb6b5" }, "downloads": -1, "filename": "plone.app.drafts-1.1.2-py2-none-any.whl", "has_sig": false, "md5_digest": "f7e0ee9cafe384d8e1a48894b1dd7be8", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 38159, "upload_time": "2017-07-03T20:09:00", "url": "https://files.pythonhosted.org/packages/da/23/99181c9ed8d31ae3e66f34fd0579ec002a5b162b886208333f0bb4845015/plone.app.drafts-1.1.2-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "2ef6e83ac833029d58fd437b461d794c", "sha256": "624130f7b640b4077c56f3e9bce811b571dc2f22d274ce28549f28fdabf594f9" }, "downloads": -1, "filename": "plone.app.drafts-1.1.2.tar.gz", "has_sig": false, "md5_digest": "2ef6e83ac833029d58fd437b461d794c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 32726, "upload_time": "2017-07-03T20:08:57", "url": "https://files.pythonhosted.org/packages/f7/14/30a590aaf63f541925ab9d3dc52b89d018d3b28c1981b1854c49a86d0ffe/plone.app.drafts-1.1.2.tar.gz" } ], "1.1.3": [ { "comment_text": "", "digests": { "md5": "8844fc9336e9e23acf15ca7ac2ce0797", "sha256": "553be74a6be4c78b24bc43fecee63d9e594ab5531556d28f9c497e17bd333d8c" }, "downloads": -1, "filename": "plone.app.drafts-1.1.3.tar.gz", "has_sig": false, "md5_digest": "8844fc9336e9e23acf15ca7ac2ce0797", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 35543, "upload_time": "2019-02-10T16:11:18", "url": "https://files.pythonhosted.org/packages/09/61/7478d07b056725e6efba9fec772b02c7e1f101e3b0b88fdfe72a972e5e3a/plone.app.drafts-1.1.3.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "8844fc9336e9e23acf15ca7ac2ce0797", "sha256": "553be74a6be4c78b24bc43fecee63d9e594ab5531556d28f9c497e17bd333d8c" }, "downloads": -1, "filename": "plone.app.drafts-1.1.3.tar.gz", "has_sig": false, "md5_digest": "8844fc9336e9e23acf15ca7ac2ce0797", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 35543, "upload_time": "2019-02-10T16:11:18", "url": "https://files.pythonhosted.org/packages/09/61/7478d07b056725e6efba9fec772b02c7e1f101e3b0b88fdfe72a972e5e3a/plone.app.drafts-1.1.3.tar.gz" } ] }