[Checkins] SVN: z3c.contents/trunk/ Implemented copy paste move

Roger Ineichen roger at projekt01.ch
Sat Mar 1 21:50:38 EST 2008


Log message for revision 84389:
  Implemented copy paste move
  Cleanup imports
  Cleanup buildout setup
  Added coverage folder to ignore file list
  Added more tests

Changed:
  _U  z3c.contents/trunk/
  U   z3c.contents/trunk/setup.py
  U   z3c.contents/trunk/src/z3c/contents/README.txt
  U   z3c.contents/trunk/src/z3c/contents/browser.py
  U   z3c.contents/trunk/src/z3c/contents/column.py
  U   z3c.contents/trunk/src/z3c/contents/tests.py

-=-

Property changes on: z3c.contents/trunk
___________________________________________________________________
Name: svn:ignore
   - .installed.cfg
bin
develop-eggs
parts

   + .installed.cfg
bin
develop-eggs
parts
coverage


Modified: z3c.contents/trunk/setup.py
===================================================================
--- z3c.contents/trunk/setup.py	2008-03-02 00:41:59 UTC (rev 84388)
+++ z3c.contents/trunk/setup.py	2008-03-02 02:50:36 UTC (rev 84389)
@@ -51,11 +51,12 @@
     namespace_packages = ['z3c'],
     extras_require = dict(
         test = [
-            'zope.testbrowser',
-            'zope.app.securitypolicy',
+            'z3c.macro',
+            'z3c.table',
+            'zope.app.pagetemplate',
             'zope.app.testing',
-            'zope.app.twisted',
-            'z3c.testing',
+            'zope.component',
+            'zope.testing',
             ],
         ),
     install_requires = [
@@ -63,11 +64,16 @@
         'z3c.batching',
         'z3c.form',
         'z3c.formui',
-        'z3c.pagelet',
+        'z3c.macro',
         'z3c.table',
         'z3c.template',
+        'zope.annotation',
         'zope.contentprovider',
+        'zope.copypastemove',
+        'zope.exceptions',
         'zope.interface',
+        'zope.security',
+        'zope.traversing',
         ],
     zip_safe = False,
 )
\ No newline at end of file

Modified: z3c.contents/trunk/src/z3c/contents/README.txt
===================================================================
--- z3c.contents/trunk/src/z3c/contents/README.txt	2008-03-02 00:41:59 UTC (rev 84388)
+++ z3c.contents/trunk/src/z3c/contents/README.txt	2008-03-02 02:50:36 UTC (rev 84389)
@@ -115,20 +115,17 @@
     <div>
       <div class="buttons">
         <input type="submit" id="contents-buttons-copy"
-         name="contents.buttons.copy"
-         class="submit-widget button-field" value="Copy" />
+               name="contents.buttons.copy"
+               class="submit-widget button-field" value="Copy" />
         <input type="submit" id="contents-buttons-cut"
-         name="contents.buttons.cut"
-         class="submit-widget button-field" value="Cut" />
-        <input type="submit" id="contents-buttons-paste"
-         name="contents.buttons.paste"
-         class="submit-widget button-field" value="Paste" />
+               name="contents.buttons.cut"
+               class="submit-widget button-field" value="Cut" />
         <input type="submit" id="contents-buttons-delete"
-         name="contents.buttons.delete"
-         class="submit-widget button-field" value="Delete" />
+               name="contents.buttons.delete"
+               class="submit-widget button-field" value="Delete" />
         <input type="submit" id="contents-buttons-rename"
-         name="contents.buttons.rename"
-         class="submit-widget button-field" value="Rename" />
+               name="contents.buttons.rename"
+               class="submit-widget button-field" value="Rename" />
       </div>
     </div>
   </form>
@@ -224,20 +221,17 @@
     <div>
       <div class="buttons">
         <input type="submit" id="contents-buttons-copy"
-         name="contents.buttons.copy"
-         class="submit-widget button-field" value="Copy" />
+               name="contents.buttons.copy"
+               class="submit-widget button-field" value="Copy" />
         <input type="submit" id="contents-buttons-cut"
-         name="contents.buttons.cut"
-         class="submit-widget button-field" value="Cut" />
-        <input type="submit" id="contents-buttons-paste"
-         name="contents.buttons.paste"
-         class="submit-widget button-field" value="Paste" />
+               name="contents.buttons.cut"
+               class="submit-widget button-field" value="Cut" />
         <input type="submit" id="contents-buttons-delete"
-         name="contents.buttons.delete"
-         class="submit-widget button-field" value="Delete" />
+               name="contents.buttons.delete"
+               class="submit-widget button-field" value="Delete" />
         <input type="submit" id="contents-buttons-rename"
-         name="contents.buttons.rename"
-         class="submit-widget button-field" value="Rename" />
+               name="contents.buttons.rename"
+               class="submit-widget button-field" value="Rename" />
       </div>
     </div>
   </form>
@@ -260,78 +254,87 @@
   <form action="http://127.0.0.1" method="post"
         enctype="multipart/form-data" class="edit-form"
         name="contents" id="contents">
-    <div class="viewspace">
-      <div class="required-info">
-         <span class="required">*</span>
-         &ndash; required
-      </div>
-      <div>
-        <table>
-          <thead>
-            <tr>
-              <th>X</th>
-              <th>Name</th>
-              <th>Created</th>
-              <th>Modified</th>
-            </tr>
-          </thead>
-          <tbody>
-            <tr>
-              <td><input type="checkbox" name="contents-checkBoxColumn-0-selectedItems" value="zero"  /></td>
-              <td>zero</td>
-              <td>01/01/01 01:01</td>
-              <td>02/02/02 02:02</td>
-            </tr>
-            <tr>
-              <td><input type="checkbox" name="contents-checkBoxColumn-0-selectedItems" value="third"  /></td>
-              <td>third</td>
-              <td>01/01/01 01:01</td>
-              <td>02/02/02 02:02</td>
-            </tr>
-            <tr>
-              <td><input type="checkbox" name="contents-checkBoxColumn-0-selectedItems" value="second"  /></td>
-              <td>second</td>
-              <td>01/01/01 01:01</td>
-              <td>02/02/02 02:02</td>
-            </tr>
-            <tr>
-              <td><input type="checkbox" name="contents-checkBoxColumn-0-selectedItems" value="fourth"  /></td>
-              <td>fourth</td>
-              <td>01/01/01 01:01</td>
-              <td>02/02/02 02:02</td>
-            </tr>
-            <tr>
-              <td><input type="checkbox" name="contents-checkBoxColumn-0-selectedItems" value="first"  /></td>
-              <td>first</td>
-              <td>01/01/01 01:01</td>
-              <td>02/02/02 02:02</td>
-            </tr>
-          </tbody>
-        </table>
-      </div>
-    </div>
-    <div>
-      <div class="buttons">
-        <input type="submit" id="contents-buttons-copy"
-         name="contents.buttons.copy"
-         class="submit-widget button-field" value="Copy" />
-        <input type="submit" id="contents-buttons-cut"
-         name="contents.buttons.cut"
-         class="submit-widget button-field" value="Cut" />
-        <input type="submit" id="contents-buttons-paste"
-         name="contents.buttons.paste"
-         class="submit-widget button-field" value="Paste" />
-        <input type="submit" id="contents-buttons-delete"
-         name="contents.buttons.delete"
-         class="submit-widget button-field" value="Delete" />
-        <input type="submit" id="contents-buttons-rename"
-         name="contents.buttons.rename"
-         class="submit-widget button-field" value="Rename" />
-      </div>
-    </div>
-  </form>
+  ...
+  <tbody>
+    <tr>
+      <td><input type="checkbox" name="contents-checkBoxColumn-0-selectedItems" value="zero"  /></td>
+      <td>zero</td>
+      <td>01/01/01 01:01</td>
+      <td>02/02/02 02:02</td>
+    </tr>
+    <tr>
+      <td><input type="checkbox" name="contents-checkBoxColumn-0-selectedItems" value="third"  /></td>
+      <td>third</td>
+      <td>01/01/01 01:01</td>
+      <td>02/02/02 02:02</td>
+    </tr>
+    <tr>
+      <td><input type="checkbox" name="contents-checkBoxColumn-0-selectedItems" value="second"  /></td>
+      <td>second</td>
+      <td>01/01/01 01:01</td>
+      <td>02/02/02 02:02</td>
+    </tr>
+    <tr>
+      <td><input type="checkbox" name="contents-checkBoxColumn-0-selectedItems" value="fourth"  /></td>
+      <td>fourth</td>
+      <td>01/01/01 01:01</td>
+      <td>02/02/02 02:02</td>
+    </tr>
+    <tr>
+      <td><input type="checkbox" name="contents-checkBoxColumn-0-selectedItems" value="first"  /></td>
+      <td>first</td>
+      <td>01/01/01 01:01</td>
+      <td>02/02/02 02:02</td>
+    </tr>
+  </tbody>
+  ...
 
+Let's make coverage happy and sort on the rename column:
 
+  >>> sorterRequest = TestRequest(form={'contents-sortOn': 'contents-renameColumn-1',
+  ...                                   'contents-sortOrder':'ascending'})
+  >>> alsoProvides(sorterRequest, IDivFormLayer)
+  >>> sortingPage = browser.ContentsPage(container, sorterRequest)
+  >>> sortingPage.update()
+  >>> print sortingPage.render()
+  <form action="http://127.0.0.1" method="post"
+        enctype="multipart/form-data" class="edit-form"
+        name="contents" id="contents">
+  ...
+  <tbody>
+    <tr>
+      <td><input type="checkbox" name="contents-checkBoxColumn-0-selectedItems" value="first"  /></td>
+      <td>first</td>
+      <td>01/01/01 01:01</td>
+      <td>02/02/02 02:02</td>
+    </tr>
+    <tr>
+      <td><input type="checkbox" name="contents-checkBoxColumn-0-selectedItems" value="fourth"  /></td>
+      <td>fourth</td>
+      <td>01/01/01 01:01</td>
+      <td>02/02/02 02:02</td>
+    </tr>
+    <tr>
+      <td><input type="checkbox" name="contents-checkBoxColumn-0-selectedItems" value="second"  /></td>
+      <td>second</td>
+      <td>01/01/01 01:01</td>
+      <td>02/02/02 02:02</td>
+    </tr>
+    <tr>
+      <td><input type="checkbox" name="contents-checkBoxColumn-0-selectedItems" value="third"  /></td>
+      <td>third</td>
+      <td>01/01/01 01:01</td>
+      <td>02/02/02 02:02</td>
+    </tr>
+    <tr>
+      <td><input type="checkbox" name="contents-checkBoxColumn-0-selectedItems" value="zero"  /></td>
+      <td>zero</td>
+      <td>01/01/01 01:01</td>
+      <td>02/02/02 02:02</td>
+    </tr>
+  </tbody>
+  ...
+
 Copy
 ----
 
@@ -344,7 +347,8 @@
 
   >>> secondPage = browser.ContentsPage(secondContainer, request)
 
-As you can see the second page for the second container has no items:
+As you can see the second page for the second container has no items and 
+no buttons:
 
   >>> secondPage.update()
   >>> print secondPage.render()
@@ -366,26 +370,13 @@
               <th>Modified</th>
             </tr>
           </thead>
+          <tbody>
+          </tbody>
         </table>
       </div>
     </div>
     <div>
       <div class="buttons">
-        <input type="submit" id="contents-buttons-copy"
-         name="contents.buttons.copy"
-         class="submit-widget button-field" value="Copy" />
-        <input type="submit" id="contents-buttons-cut"
-         name="contents.buttons.cut"
-         class="submit-widget button-field" value="Cut" />
-        <input type="submit" id="contents-buttons-paste"
-         name="contents.buttons.paste"
-         class="submit-widget button-field" value="Paste" />
-        <input type="submit" id="contents-buttons-delete"
-         name="contents.buttons.delete"
-         class="submit-widget button-field" value="Delete" />
-        <input type="submit" id="contents-buttons-rename"
-         name="contents.buttons.rename"
-         class="submit-widget button-field" value="Rename" />
       </div>
     </div>
   </form>
@@ -457,7 +448,9 @@
 
 Now we can go to the second page and paste our selected object. Just prepare
 a request which simualtes that we clicked at the paste button and we can see
-that we pasted the selected item to the second container:
+that we pasted the selected item to the second container. You can see that 
+there is an additional ``Paste`` button becase we have some items for paste
+in the clipboard:
 
   >>> pasteRequest = TestRequest(form={'contents.buttons.paste': 'Paste'})
   >>> alsoProvides(pasteRequest, IDivFormLayer)
@@ -469,7 +462,7 @@
         name="contents" id="contents">
   ...
         <div class="status">
-          <div class="summary">Data successfully copied</div>
+          <div class="summary">Data successfully pasted</div>
         </div>
   ...
     <thead>
@@ -489,6 +482,26 @@
       </tr>
     </tbody>
   ...
+      <div>
+        <div class="buttons">
+          <input type="submit" id="contents-buttons-copy"
+                 name="contents.buttons.copy"
+                 class="submit-widget button-field" value="Copy" />
+          <input type="submit" id="contents-buttons-cut"
+                 name="contents.buttons.cut"
+                 class="submit-widget button-field" value="Cut" />
+          <input type="submit" id="contents-buttons-paste"
+                 name="contents.buttons.paste"
+                 class="submit-widget button-field" value="Paste" />
+          <input type="submit" id="contents-buttons-delete"
+                 name="contents.buttons.delete"
+                 class="submit-widget button-field" value="Delete" />
+          <input type="submit" id="contents-buttons-rename"
+                 name="contents.buttons.rename"
+                 class="submit-widget button-field" value="Rename" />
+        </div>
+      </div>
+    </form>
 
 
 Cut
@@ -571,7 +584,7 @@
         name="contents" id="contents">
   ...
         <div class="status">
-          <div class="summary">Data successfully copied</div>
+          <div class="summary">Data successfully pasted</div>
         </div>
   ...
   <table>
@@ -605,9 +618,28 @@
     </tbody>
   </table>
   ...
+    <div>
+      <div class="buttons">
+        <input type="submit" id="contents-buttons-copy"
+         name="contents.buttons.copy"
+         class="submit-widget button-field" value="Copy" />
+        <input type="submit" id="contents-buttons-cut"
+         name="contents.buttons.cut"
+         class="submit-widget button-field" value="Cut" />
+        <input type="submit" id="contents-buttons-delete"
+         name="contents.buttons.delete"
+         class="submit-widget button-field" value="Delete" />
+        <input type="submit" id="contents-buttons-rename"
+         name="contents.buttons.rename"
+         class="submit-widget button-field" value="Rename" />
+      </div>
+    </div>
+  </form>
 
+
 As you can see the first page does not contain the ``first`` and ``second`` 
-item after paste them to the second container:
+item after paste them to the second container. Also the ``paste button`` is
+gone:
 
   >>> firstPage.update()
   >>> print firstPage.render()
@@ -646,6 +678,23 @@
     </tbody>
   </table>
   ...
+    <div>
+      <div class="buttons">
+        <input type="submit" id="contents-buttons-copy"
+               name="contents.buttons.copy"
+               class="submit-widget button-field" value="Copy" />
+        <input type="submit" id="contents-buttons-cut"
+               name="contents.buttons.cut"
+               class="submit-widget button-field" value="Cut" />
+        <input type="submit" id="contents-buttons-delete"
+               name="contents.buttons.delete"
+               class="submit-widget button-field" value="Delete" />
+        <input type="submit" id="contents-buttons-rename"
+               name="contents.buttons.rename"
+               class="submit-widget button-field" value="Rename" />
+      </div>
+    </div>
+  </form>
 
 
 Delete

Modified: z3c.contents/trunk/src/z3c/contents/browser.py
===================================================================
--- z3c.contents/trunk/src/z3c/contents/browser.py	2008-03-02 00:41:59 UTC (rev 84388)
+++ z3c.contents/trunk/src/z3c/contents/browser.py	2008-03-02 02:50:36 UTC (rev 84389)
@@ -21,15 +21,15 @@
 import zope.i18nmessageid
 import zope.i18n
 from zope.annotation.interfaces import IAnnotations
-from zope.dublincore.interfaces import IZopeDublinCore
-from zope.dublincore.interfaces import IDCDescriptiveProperties
 from zope.copypastemove import ItemNotFoundError
 from zope.copypastemove.interfaces import IPrincipalClipboard
 from zope.copypastemove.interfaces import IObjectCopier, IObjectMover
 from zope.copypastemove.interfaces import IContainerItemRenamer
+from zope.app.container.interfaces import IContainerNamesContainer
 from zope.exceptions import DuplicationError
 from zope.exceptions.interfaces import UserError
 from zope.security.interfaces import Unauthorized
+from zope.traversing.interfaces import TraversalError
 from zope.traversing import api
 
 from zope.app.container.interfaces import DuplicateIDError
@@ -53,13 +53,6 @@
     return IPrincipalClipboard(annotations, None)
 
 
-def getDCTitle(ob):
-    dc = IDCDescriptiveProperties(ob, None)
-    if dc is None:
-        return None
-    else:
-        return dc.title
-
 def safeGetAttr(obj, attr, default):
     """Attempts to read the attr, returning default if Unauthorized."""
     try:
@@ -68,6 +61,27 @@
         return default
 
 
+# conditions
+def canCut(form):
+    return form.supportsCut
+
+
+def canCopy(form):
+    return form.supportsCopy
+
+
+def canDelete(form):
+    return form.supportsDelete
+
+
+def canPaste(form):
+    return form.supportsPaste
+
+
+def canRename(form):
+    return form.supportsRename
+
+
 class ContentsPage(table.Table, form.Form):
     """Generic IContainer management page."""
 
@@ -77,25 +91,37 @@
 
     # internal defaults
     selectedItems = []
-    supportsPaste = False
     ignoreContext = False
 
+    supportsCut = False
+    supportsCopy = False
+    supportsDelete = False
+    supportsPaste = False
+    supportsRename = False
+
     # customize this part
+    allowCut = True
+    allowCopy = True
+    allowDelete = True
     allowPaste = True
+    allowRename = True
+
     prefix = 'contents'
 
     # error messages
-    deleteErrorMessage = _('Could not delete the selected items')
-    deleteNoItemsMessage = _('No items selected for delete')
-    deleteSucsessMessage = _('Data successfully deleted')
+    cutNoItemsMessage = _('No items selected for cut')
+    cutItemsSelected = _('Items selected for cut')
 
     copyItemsSelected = _('Items choosen for copy')
     copyNoItemsMessage = _('No items selected for copy')
     copySucsessMessage = _('Data successfully copied')
 
-    cutNoItemsMessage = _('No items selected for cut')
-    cutItemsSelected = _('Items selected for cut')
+    deleteErrorMessage = _('Could not delete the selected items')
+    deleteNoItemsMessage = _('No items selected for delete')
+    deleteSucsessMessage = _('Data successfully deleted')
 
+    pasteSucsessMessage = _('Data successfully pasted')
+
     renameErrorMessage = _('Could not rename all selected items')
     renameDuplicationMessage = _('Duplicated item name')
     renameItemNotFoundMessage = _('Item not found')
@@ -105,17 +131,41 @@
         super(ContentsPage, self).update()
         # second find out if we support paste
         self.clipboard = queryPrincipalClipboard(self.request)
-        if self.allowPaste:
-            self.supportsPaste = self.pasteable()
+        self.setupCopyPasteMove()
         self.updateWidgets()
         self.updateActions()
         self.actions.execute()
 
+    def setupCopyPasteMove(self):
+        hasContent = self.hasContent
+        if self.allowCut:
+            self.supportsCut = hasContent
+        if self.allowCopy:
+            self.supportsCopy = hasContent
+        if self.allowDelete:
+            self.supportsDelete = hasContent
+        if self.allowPaste:
+            self.supportsPaste = self.hasClipboardContents
+        if self.allowRename:
+            self.supportsRename = (hasContent and self.supportsCut and
+                    not IContainerNamesContainer.providedBy(self.context))
+
+    def updateAfterActionExecution(self):
+        """Adjust new container length and copa paste move status."""
+        super(ContentsPage, self).update()
+        self.setupCopyPasteMove()
+        self.updateActions()
+
     def render(self):
         """Render the template."""
         return self.template()
 
-    def pasteable(self):
+    @property
+    def hasContent(self):
+        return bool(self.values)
+
+    @property
+    def isPasteable(self):
         """Decide if there is anything to paste."""
         target = self.context
         if self.clipboard is None:
@@ -141,10 +191,11 @@
                     raise
         return True
 
+    @property
     def hasClipboardContents(self):
         """Interogate the ``PrinicipalAnnotation`` to see if clipboard
         contents exist."""
-        if not self.supportsPaste:
+        if not self.isPasteable:
             return False
         # touch at least one item in clipboard to confirm contents
         items = self.clipboard.getContents()
@@ -157,7 +208,7 @@
                 return True
         return False
 
-    @button.buttonAndHandler(_('Copy'), name='copy')
+    @button.buttonAndHandler(_('Copy'), name='copy', condition=canCopy)
     def handleCopy(self, action):
         if not len(self.selectedItems):
             self.status = self.copyNoItemsMessage
@@ -187,7 +238,7 @@
         self.clipboard.clearContents()
         self.clipboard.addItems('copy', items)
 
-    @button.buttonAndHandler(_('Cut'), name='cut')
+    @button.buttonAndHandler(_('Cut'), name='cut', condition=canCut)
     def handleCut(self, action):
         if not len(self.selectedItems):
             self.status = self.cutNoItemsMessage
@@ -217,7 +268,7 @@
         self.clipboard.clearContents()
         self.clipboard.addItems('cut', items)
 
-    @button.buttonAndHandler(_('Paste'), name='paste')
+    @button.buttonAndHandler(_('Paste'), name='paste', condition=canPaste)
     def handlePaste(self, action):
         items = self.clipboard.getContents()
         moved = False
@@ -264,10 +315,10 @@
         else:
             # we need to update the table rows again, otherwise we don't 
             # see the new item in the table
-            super(ContentsPage, self).update()
-            self.status = self.copySucsessMessage
+            self.updateAfterActionExecution()
+            self.status = self.pasteSucsessMessage
 
-    @button.buttonAndHandler(_('Delete'), name='delete')
+    @button.buttonAndHandler(_('Delete'), name='delete', condition=canDelete)
     def handleDelete(self, action):
         if not len(self.selectedItems):
             self.status = self.deleteNoItemsMessage
@@ -278,11 +329,11 @@
         except KeyError:
             self.status = self.deleteErrorMessage
             transaction.doom()
+        # update the table rows before we start with rendering
+        self.updateAfterActionExecution()
         self.status = self.deleteSucsessMessage
-        # update the table rows before we start with rendering
-        super(ContentsPage, self).update()
 
-    @button.buttonAndHandler(_('Rename'), name='rename')
+    @button.buttonAndHandler(_('Rename'), name='rename', condition=canRename)
     def handlerRename(self, action):
         changed = False
         errorMessages = {}
@@ -315,7 +366,7 @@
         if changed:
             self.status = self.renameErrorMessage
             # update the table rows before we start with rendering
-            super(ContentsPage, self).update()
+            self.updateAfterActionExecution()
             # and set error message back to the new rename column
             renameCol = self.columnByName.get('renameColumn')
             if renameCol:

Modified: z3c.contents/trunk/src/z3c/contents/column.py
===================================================================
--- z3c.contents/trunk/src/z3c/contents/column.py	2008-03-02 00:41:59 UTC (rev 84388)
+++ z3c.contents/trunk/src/z3c/contents/column.py	2008-03-02 02:50:36 UTC (rev 84389)
@@ -17,7 +17,6 @@
 __docformat__ = "reStructuredText"
 
 import base64
-import binascii
 import zope.i18nmessageid
 from zope.traversing import api
 

Modified: z3c.contents/trunk/src/z3c/contents/tests.py
===================================================================
--- z3c.contents/trunk/src/z3c/contents/tests.py	2008-03-02 00:41:59 UTC (rev 84388)
+++ z3c.contents/trunk/src/z3c/contents/tests.py	2008-03-02 02:50:36 UTC (rev 84389)
@@ -28,15 +28,12 @@
 from zope.copypastemove.interfaces import IObjectMover
 from zope.copypastemove.interfaces import IObjectCopier
 from zope.copypastemove.interfaces import IPrincipalClipboard
-from zope.publisher.browser import TestRequest
 from zope.testing import doctest
 from zope.app.container.interfaces import IContainer
 from zope.app.container.interfaces import IContained
 from zope.app.testing import setup
 
-import z3c.testing
 from z3c.macro import tales
-import z3c.form.testing 
 import z3c.table.testing 
 
 



More information about the Checkins mailing list