[Checkins] SVN: zmi.core/trunk/src/zmi/core/undo/ Copy zope.app.undo browser package to zmi.core.

Yusei Tahara yusei at domen.cx
Sat Aug 8 02:25:28 EDT 2009


Log message for revision 102567:
  Copy zope.app.undo browser package to zmi.core.
  

Changed:
  A   zmi.core/trunk/src/zmi/core/undo/
  A   zmi.core/trunk/src/zmi/core/undo/__init__.py
  A   zmi.core/trunk/src/zmi/core/undo/browser.py
  A   zmi.core/trunk/src/zmi/core/undo/configure.zcml
  A   zmi.core/trunk/src/zmi/core/undo/tests.py
  A   zmi.core/trunk/src/zmi/core/undo/undo_all.pt
  A   zmi.core/trunk/src/zmi/core/undo/undo_macros.pt
  A   zmi.core/trunk/src/zmi/core/undo/undo_more.pt

-=-
Added: zmi.core/trunk/src/zmi/core/undo/browser.py
===================================================================
--- zmi.core/trunk/src/zmi/core/undo/browser.py	                        (rev 0)
+++ zmi.core/trunk/src/zmi/core/undo/browser.py	2009-08-08 06:25:26 UTC (rev 102567)
@@ -0,0 +1,81 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Undo view
+
+$Id: browser.py 67630 2006-04-27 00:54:03Z jim $
+"""
+import zope.component
+from zope.security.interfaces import ForbiddenAttribute
+from zope.publisher.browser import BrowserView
+from zope.app.undo.interfaces import IUndoManager
+
+class UndoView(BrowserView):
+    """Undo view"""
+
+    def principalLastTransactionIsUndo(self):
+        """Return True if the authenticated principal's last
+        transaction is an undo transaction.
+        """
+        request = self.request
+        undo = zope.component.getUtility(IUndoManager)
+        txn_info = undo.getPrincipalTransactions(request.principal, first=0,
+                                                 last=1)
+        if txn_info:
+            return txn_info[0].get('undo', False)
+        return False
+
+    def undoPrincipalLastTransaction(self):
+        """Undo the authenticated principal's last transaction and
+        return where he/she came from"""
+        request = self.request
+        undo = zope.component.getUtility(IUndoManager)
+        txn_info = undo.getPrincipalTransactions(request.principal, first=0,
+                                                 last=1)
+        if txn_info:
+            id = txn_info[0]['id']
+            undo.undoPrincipalTransactions(request.principal, [id])
+        target = request.get('HTTP_REFERER', '@@SelectedManagementView.html')
+        request.response.redirect(target)
+
+    def undoAllTransactions(self, ids):
+        """Undo transactions specified in 'ids'."""
+        undo = zope.component.getUtility(IUndoManager)
+        undo.undoTransactions(ids)
+        self._redirect()
+
+    def undoPrincipalTransactions(self, ids):
+        """Undo transactions that were issued by the authenticated
+        user specified in 'ids'."""
+        undo = zope.component.getUtility(IUndoManager)
+        undo.undoPrincipalTransactions(self.request.principal, ids)
+        self._redirect()
+
+    def _redirect(self):
+        target = "@@SelectedManagementView.html"
+        self.request.response.redirect(target)
+
+    def getAllTransactions(self, first=0, last=-20, showall=False):
+        context = None
+        if not showall:
+            context = self.context
+        undo = zope.component.getUtility(IUndoManager)
+        return undo.getTransactions(context, first, last)
+
+    def getPrincipalTransactions(self, first=0, last=-20, showall=False):
+        context = None
+        if not showall:
+            context = self.context
+        undo = zope.component.getUtility(IUndoManager)
+        return undo.getPrincipalTransactions(self.request.principal, context,
+                                             first, last)

Added: zmi.core/trunk/src/zmi/core/undo/configure.zcml
===================================================================
--- zmi.core/trunk/src/zmi/core/undo/configure.zcml	                        (rev 0)
+++ zmi.core/trunk/src/zmi/core/undo/configure.zcml	2009-08-08 06:25:26 UTC (rev 102567)
@@ -0,0 +1,98 @@
+<configure
+    xmlns:browser="http://namespaces.zope.org/browser"
+    i18n_domain="zope"
+    >
+
+  <browser:pages
+      for="*"
+      permission="zope.UndoOwnTransactions"
+      class="zmi.core.undo.browser.UndoView"
+      >
+
+    <browser:page
+        name="principalLastTransactionIsUndo"
+        attribute="principalLastTransactionIsUndo"
+        />
+
+    <browser:page
+        name="undo.html"
+        attribute="undoPrincipalLastTransaction"
+        />
+
+    <browser:page
+        name="undoPrincipalTransactions.html"
+        attribute="undoPrincipalTransactions"
+        />
+
+    <browser:page
+        name="undoMore.html"
+        template="undo_more.pt"
+        />
+
+  </browser:pages>
+
+  <browser:pages
+      for="*"
+      permission="zope.UndoAllTransactions"
+      class="zmi.core.undo.browser.UndoView"
+      >
+
+    <browser:page
+        name="undoAllTransactions.html"
+        attribute="undoAllTransactions"
+        />
+
+    <browser:page
+        name="undoAll.html"
+        template="undo_all.pt"
+        />
+
+  </browser:pages>
+
+  <!-- We hereby imply that users having zope.UndoAllTransactions also
+       have zope.UndoOwnTransactions -->
+  <browser:page
+      for="*"
+      name="undo_macros"
+      permission="zope.UndoOwnTransactions"
+      template="undo_macros.pt"
+      />
+
+
+  <!-- menu items -->
+
+  <browser:menuItem
+      for="*"
+      menu="zmi_actions"
+      title="Undo!"
+      action="@@undo.html"
+      permission="zope.UndoOwnTransactions"
+      filter="not:context/@@principalLastTransactionIsUndo"
+      />
+
+  <browser:menuItem
+      for="*"
+      menu="zmi_actions"
+      title="Redo!"
+      action="@@undo.html"
+      permission="zope.UndoOwnTransactions"
+      filter="context/@@principalLastTransactionIsUndo"
+      />
+
+  <browser:menuItem
+      for="*"
+      menu="zmi_actions"
+      title="Undo more"
+      action="@@undoMore.html"
+      permission="zope.UndoOwnTransactions"
+      />
+
+  <browser:menuItem
+      for="*"
+      menu="zmi_actions"
+      title="Undo all"
+      action="@@undoAll.html"
+      permission="zope.UndoAllTransactions"
+      />
+
+</configure>

Added: zmi.core/trunk/src/zmi/core/undo/tests.py
===================================================================
--- zmi.core/trunk/src/zmi/core/undo/tests.py	                        (rev 0)
+++ zmi.core/trunk/src/zmi/core/undo/tests.py	2009-08-08 06:25:26 UTC (rev 102567)
@@ -0,0 +1,168 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Undo Tests
+
+$Id: test_undoview.py 98107 2009-03-14 17:56:45Z nadako $
+"""
+from datetime import datetime
+from unittest import TestCase, main, makeSuite
+
+from zope.component import provideUtility
+from zope.interface import implements
+from zope.principalregistry.principalregistry import principalRegistry
+from zope.publisher.browser import TestRequest
+
+from zope.app.component.testing import PlacefulSetup
+from zope.app.undo.interfaces import IUndoManager
+from zmi.core.undo.browser import UndoView
+
+class TestIUndoManager(object):
+    implements(IUndoManager)
+
+    def __init__(self):
+        self.dummy_db = [
+            dict(id='1', user_name='monkey', description='thing1',
+                 time='today', datetime=datetime(2001, 01, 01, 12, 00, 00)),
+            dict(id='2', user_name='monkey', description='thing2',
+                 time='today', datetime=datetime(2001, 01, 01, 12, 00, 00)),
+            dict(id='3', user_name='bonobo', description='thing3',
+                 time='today', datetime=datetime(2001, 01, 01, 12, 00, 00)),
+            dict(id='4', user_name='monkey', description='thing4',
+                 time='today', datetime=datetime(2001, 01, 01, 12, 00, 00)),
+            dict(id='5', user_name='bonobo', description='thing5',
+                 time='today', datetime=datetime(2001, 01, 01, 12, 00, 00)),
+            dict(id='6', user_name='bonobo', description='thing6',
+                 time='today', datetime=datetime(2001, 01, 01, 12, 00, 00)),
+            ]
+        self.dummy_db.reverse()
+
+    def getTransactions(self, context=None, first=0, last=-20):
+        self.context = context
+        list = [dict for dict in self.dummy_db
+                if not dict.get('undone', False)]
+        return list[first:abs(last)]
+
+    def getPrincipalTransactions(self, principal, context=None,
+                                 first=0, last=-20):
+        self.principal = principal
+        self.context = context
+        list = [dict for dict in self.dummy_db
+               if dict['user_name'] == principal.id
+               and not dict.get('undone', False)]
+        return list[first:abs(last)]
+
+    def _emulatePublication(self, request):
+        self._user_name = request.principal.id
+
+    def undoTransactions(self, ids):
+        # just remove an element for now
+        for db_record in self.dummy_db:
+            if db_record['id'] in ids:
+                db_record['undone'] = True
+
+        self.dummy_db.insert(0, dict(
+            id=str(len(self.dummy_db)+1), user_name=self._user_name,
+            description='undo', undo=True,
+            datetime=datetime(2001, 01, 01, 12, 00, 00)
+            ))
+
+    def undoPrincipalTransactions(self, principal, ids):
+        self.principal = principal
+        self.undoTransactions(ids)
+
+class Test(PlacefulSetup, TestCase):
+
+    def setUp(self):
+        super(Test, self).setUp()
+        self.request = TestRequest()
+        self.undo = TestIUndoManager()
+        provideUtility(self.undo, IUndoManager)
+        principalRegistry.definePrincipal('monkey', 'Monkey Patch',
+                                          login='monkey')
+        principalRegistry.definePrincipal('bonobo', 'Bonobo Abdul-Fasil',
+                                          login='bonobo')
+
+    def testPrincipalLastTransactionIsUndo(self):
+        request = self.request
+        bonobo = principalRegistry.getPrincipal('bonobo')
+        request.setPrincipal(bonobo)
+
+        view = UndoView(self.folder1, request)
+        self.failIf(view.principalLastTransactionIsUndo())
+
+        # now undo the last transaction
+        self.undo._emulatePublication(request)
+        self.undo.undoTransactions(['6'])
+
+        self.assert_(view.principalLastTransactionIsUndo())
+
+    def testUndoPrincipalLastTransaction(self):
+        request = self.request
+        bonobo = principalRegistry.getPrincipal('bonobo')
+        request.setPrincipal(bonobo)
+
+        self.undo._emulatePublication(request)
+        view = UndoView(self.folder1, request)
+        view.undoPrincipalLastTransaction()
+
+        last_txn = self.undo.getTransactions(None, 0, 1)[0]
+        self.assert_(last_txn.has_key('undo'))
+        self.assert_(last_txn['undo'])
+        self.assertEqual(last_txn['user_name'], bonobo.id)
+
+    def testGetAllTransactions(self):
+        request = self.request
+        view = UndoView(self.folder1, request)
+        expected = self.undo.getTransactions()
+        self.assertEqual(view.getAllTransactions(), expected)
+        self.assert_(self.undo.context is self.folder1)
+
+        # test showall parameter
+        view.getAllTransactions(showall=True)
+        self.assert_(self.undo.context is None)
+
+    def testGetPrincipalTransactions(self):
+        request = self.request
+        bonobo = principalRegistry.getPrincipal('bonobo')
+        request.setPrincipal(bonobo)
+
+        view = UndoView(self.folder1, request)
+        expected = self.undo.getPrincipalTransactions(bonobo)
+        self.assertEqual(view.getPrincipalTransactions(), expected)
+        self.assert_(self.undo.context is self.folder1)
+        self.assert_(self.undo.principal is bonobo)
+
+        # test showall parameter and principal
+        self.undo.principal = None
+        view.getPrincipalTransactions(showall=True)
+        self.assert_(self.undo.context is None)
+        self.assert_(self.undo.principal is bonobo)
+
+    def testUndoPrincipalTransactions(self):
+        request = self.request
+        bonobo = principalRegistry.getPrincipal('bonobo')
+        request.setPrincipal(bonobo)
+        view = UndoView(self.folder1, request)
+
+        # Just test whether it accepts the principal.  We know that
+        # our undo dummy above "works".
+        self.undo._emulatePublication(request)
+        view.undoPrincipalTransactions(['6'])
+        self.assert_(self.undo.principal is bonobo)
+
+def test_suite():
+    return makeSuite(Test)
+
+if __name__=='__main__':
+    main(defaultTest='test_suite')

Added: zmi.core/trunk/src/zmi/core/undo/undo_all.pt
===================================================================
--- zmi.core/trunk/src/zmi/core/undo/undo_all.pt	                        (rev 0)
+++ zmi.core/trunk/src/zmi/core/undo/undo_all.pt	2009-08-08 06:25:26 UTC (rev 102567)
@@ -0,0 +1,37 @@
+<html metal:use-macro="context/@@standard_macros/page"
+    i18n:domain="zope">
+<body>
+<div metal:fill-slot="body">
+
+<h2 i18n:translate="">Undo all</h2>
+
+<p i18n:translate="">This form lets you undo all transactions
+initiated by any user.</p>
+
+<p i18n:translate="">Select one or more transactions from the list
+below and click the button below. Please be aware that you may only
+undo a transaction if the object has not been modified in a later
+transaction by you or any other user.</p>
+
+<tal:var define="global macros nocall:context/@@undo_macros" />
+<div metal:use-macro="macros/global_vars" />
+
+<div metal:use-macro="macros/location_link" />
+
+<form action="@@undoAllTransactions.html" method="post">
+<div metal:use-macro="macros/previous_batch" />
+
+<div metal:use-macro="macros/undo_log">
+  <div metal:fill-slot="define_info">
+    <tal:var define="global undo_info python:view.getAllTransactions(
+                         first=first, last=-batch_size, showall=showall)" />
+  </div>
+</div>
+
+<div metal:use-macro="macros/submit_button" />
+<div metal:use-macro="macros/next_batch" />
+</form>
+
+</div>
+</body>
+</html>

Added: zmi.core/trunk/src/zmi/core/undo/undo_macros.pt
===================================================================
--- zmi.core/trunk/src/zmi/core/undo/undo_macros.pt	                        (rev 0)
+++ zmi.core/trunk/src/zmi/core/undo/undo_macros.pt	2009-08-08 06:25:26 UTC (rev 102567)
@@ -0,0 +1,126 @@
+<html i18n:domain="zope">
+<body>
+
+<!-- This file contains macros common to undo views. -->
+
+<div metal:define-macro="global_vars">
+  <tal:var define="global batch_size python:10;
+                   global first      python:int(request.get('first', 0));
+                   global showall    python:bool(request.get('showall', False))"
+                   />
+</div>
+
+<div metal:define-macro="location_link">
+  <span tal:condition="showall">
+    <p>
+      <span i18n:translate="">You are looking at transactions
+      regardless of location.</span> <a href="?"
+      i18n:translate="">View only transactions in this location</a>.
+    </p>
+  </span>
+
+  <span tal:condition="not:showall">
+    <p>
+      <span i18n:translate="">You are looking only at transactions
+      from this location.</span> <a href="?showall=true"
+      i18n:translate="">View transactions regardless of location</a>.
+    </p>
+  </span>
+</div>
+
+<div metal:define-macro="undo_log">
+
+  <!-- We expect the list of undo information in the global
+      'undo_info' variable -->
+
+  <div metal:define-slot="define_info">
+    <tal:var define="global undo_info python:[]" />
+  </div>
+
+    <table style="width: 100%; border: none;">
+
+      <tr>
+        <th></th>
+        <th i18n:translate="heading-location">Location</th>
+        <th i18n:translate="heading-request-info">Request info</th>
+        <th i18n:translate="heading-principal">Principal</th>
+        <th i18n:translate="heading-date">Date</th>
+        <th i18n:translate="heading-description">Description</th>
+      </tr>
+
+      <tal:block repeat="item undo_info">
+      <tr tal:attributes="class python:repeat['item'].odd() and
+                                       'content odd' or 'content even'">
+
+        <td width="16">
+          <input type="checkbox" name="ids:list" value="-1"
+                 tal:attributes="value item/id" />
+        </td>
+
+    <td tal:define="location item/location | nothing">
+          <tal:location replace="location" />
+          <tal:if condition="not:location"
+                  i18n:translate="label-not-available">not available</tal:if>
+        </td>
+
+        <td>
+          <tal:request_info replace="item/request_type | nothing" /><br />
+          <tal:request_info replace="item/request_info | nothing" />
+          <tal:if condition="python:not item.get('request_type', '') and
+                                    not item.get('request_info', '')"
+                  i18n:translate="label-not-available">not available</tal:if>
+        </td>
+
+        <td>
+          <tal:principal replace="item/principal/id | nothing" />
+          <tal:if condition="not:exists:item/principal/id"
+                  i18n:translate="label-not-available">not available</tal:if>
+        </td>
+
+        <td tal:define="formatter python:request.locale.dates.getFormatter(
+                                         'dateTime', 'medium')"
+            tal:content="python:formatter.format(item['datetime'])">
+        </td>
+
+        <td>
+          <tal:description i18n:translate=""
+            replace="item/description | nothing" />
+          <tal:if condition="not:item/description"
+                  i18n:translate="label-not-available">not available</tal:if>
+        </td>
+      </tr>
+      </tal:block>
+
+    </table>
+</div>
+
+<p metal:define-macro="next_batch">
+  <a tal:define="showall python:showall and '&showall=true' or ''"
+     tal:attributes="href python:'?first=%s%s'%(first+batch_size, showall)">
+    &lt;&lt;&lt;
+    <tal:text i18n:translate="">
+      View <tal:num replace="batch_size" i18n:name="number" />
+      earlier transactions
+    </tal:text>
+  </a>
+</p>
+
+<p metal:define-macro="previous_batch">
+  <a tal:define="showall python:showall and '&showall=true' or ''"
+     tal:condition="python:first >= batch_size"
+     tal:attributes="href python:'?first=%s%s'%(first-batch_size, showall)">
+    <tal:text i18n:translate="">
+      View <tal:num tal:replace="batch_size" i18n:name="number" />
+      later transactions
+    </tal:text>
+    &gt;&gt;&gt;
+  </a>
+</p>
+
+<p metal:define-macro="submit_button">
+  <input type="submit" value="Undo"
+         i18n:attributes="value undo-button" />
+</p>
+
+</body>
+</html>

Added: zmi.core/trunk/src/zmi/core/undo/undo_more.pt
===================================================================
--- zmi.core/trunk/src/zmi/core/undo/undo_more.pt	                        (rev 0)
+++ zmi.core/trunk/src/zmi/core/undo/undo_more.pt	2009-08-08 06:25:26 UTC (rev 102567)
@@ -0,0 +1,38 @@
+<html metal:use-macro="context/@@standard_macros/page"
+    i18n:domain="zope">
+<body>
+<div metal:fill-slot="body">
+
+<h2 i18n:translate="">Undo more</h2>
+
+<p i18n:translate="">This form lets you undo your last
+transactions. You are only viewing transactions initiated by you.</p>
+
+<p i18n:translate="">Select one or more transactions from the list
+below and click the button below. Please be aware that you may only
+undo a transaction if the object has not been modified in a later
+transaction by you or any other user.</p>
+
+<tal:var define="global macros nocall:context/@@undo_macros" />
+<div metal:use-macro="macros/global_vars" />
+
+<div metal:use-macro="macros/location_link" />
+
+<form action="@@undoPrincipalTransactions.html" method="post">
+<div metal:use-macro="macros/previous_batch" />
+
+<div metal:use-macro="macros/undo_log">
+  <div metal:fill-slot="define_info">
+    <tal:var define="global undo_info python:view.getPrincipalTransactions(
+                         first=first, last=-batch_size, showall=showall)" />
+  </div>
+</div>
+
+<div metal:use-macro="macros/submit_button" />
+<div metal:use-macro="macros/next_batch" />
+
+</form>
+
+</div>
+</body>
+</html>



More information about the Checkins mailing list