[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>
- – 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