[CMF-checkins] SVN: CMF/trunk/CMFDefault/ viewification, part 1 (merging from tseaver-viewification branch):

Yvo Schubbe y.2006_ at wcm-solutions.de
Tue Feb 21 11:23:54 EST 2006


Log message for revision 41740:
  viewification, part 1 (merging from tseaver-viewification branch):
  - added 'decode' and 'memoize' decorator methods to utils
  - added form utilities (FormViewBase, form_widget macros)
  - added batch utilities (BatchViewBase, batch_widget macros)
  - added folder views (index_html, folder_contents)
  - added README.txt and TODO.txt

Changed:
  A   CMF/trunk/CMFDefault/browser/
  A   CMF/trunk/CMFDefault/browser/README.txt
  A   CMF/trunk/CMFDefault/browser/TODO.txt
  A   CMF/trunk/CMFDefault/browser/__init__.py
  A   CMF/trunk/CMFDefault/browser/configure.zcml
  A   CMF/trunk/CMFDefault/browser/folder.py
  A   CMF/trunk/CMFDefault/browser/templates/
  A   CMF/trunk/CMFDefault/browser/templates/batch_widgets.pt
  A   CMF/trunk/CMFDefault/browser/templates/folder.pt
  A   CMF/trunk/CMFDefault/browser/templates/folder_contents.pt
  A   CMF/trunk/CMFDefault/browser/templates/form_widgets.pt
  A   CMF/trunk/CMFDefault/browser/utils.py
  U   CMF/trunk/CMFDefault/configure.zcml

-=-
Added: CMF/trunk/CMFDefault/browser/README.txt
===================================================================
--- CMF/trunk/CMFDefault/browser/README.txt	2006-02-21 15:45:07 UTC (rev 41739)
+++ CMF/trunk/CMFDefault/browser/README.txt	2006-02-21 16:23:53 UTC (rev 41740)
@@ -0,0 +1,25 @@
+Experimental Browser Views
+
+  This sub-package provides Zope 3-style browser views for some CMF content
+  interfaces. The views are disabled by default.
+
+  The content of this sub-package is experimental and might be refactored
+  without further notice. Documentation and unittests are still missing but
+  the views should work just as well as the corresponding skin methods.
+
+  See TODO.txt for a detailed list of converted skin methods.
+
+  Enabling the Browser Views
+
+    Either add this directive to your own ZCML files::
+
+      <include package="Products.CMFDefault.browser"/>
+
+    Or uncomment the include directive in CMFDefault's main configure.zcml.
+
+  Using the Browser Views
+
+    In an un-customized CMFDefault site you will notice no difference because
+    the browser views are just different in implementation, not in look and
+    feel. But the browser view machinery bypasses the CMF skin machinery, so
+    you will notice that TTW customizations no longer have any effect.


Property changes on: CMF/trunk/CMFDefault/browser/README.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: CMF/trunk/CMFDefault/browser/TODO.txt
===================================================================
--- CMF/trunk/CMFDefault/browser/TODO.txt	2006-02-21 15:45:07 UTC (rev 41739)
+++ CMF/trunk/CMFDefault/browser/TODO.txt	2006-02-21 16:23:53 UTC (rev 41740)
@@ -0,0 +1,115 @@
+Converting skins to views:
+
+  [x] batch_widget:
+
+      batch_widgets.pt -> templates/batch_widgets.pt
+      getBatchItemInfos.py -> BatchViewBase.listItemInfos
+      getBatchNavigation.py -> BatchViewBase.navigation_previous
+                               BatchViewBase.navigation_next
+
+  [x] form_widget:
+
+      form_widgets.pt -> templates/form_widgets.pt
+
+  [x] index_html:
+
+      index_html.py -> FolderView
+      index_html_template.pt -> folder.pt
+
+  [x] folder_contents:
+
+      folder_contents.py -> FolderContentsView
+      folder_contents_template.pt -> templates/folder_contents.pt
+      validateItemIds.py -> FolderContentsView.validateItemIds
+      validateClipboardData.py -> FolderContentsView.validateClipboardData
+      folder_cut_control.py -> FolderContentsView.cut_control
+      folder_copy_control.py -> FolderContentsView.copy_control
+      folder_paste_control.py -> FolderContentsView.paste_control
+      folder_delete_control.py -> FolderContentsView.delete_control
+      folder_sort_control.py -> FolderContentsView.sort_control
+      folder_up_control.py -> FolderContentsView.up_control
+      folder_down_control.py -> FolderContentsView.down_control
+      folder_top_control.py -> FolderContentsView.top_control
+      folder_bottom_control.py -> FolderContentsView.bottom_control
+
+  [ ] folder_edit_form:
+
+      folder_edit_form.py -> MetadataMinimalEditView
+      folder_edit_template.pt -> templates/metadata_minimal_edit.pt
+      folder_edit_control.py -> MetadataMinimalEditView.edit_control
+
+  [ ] metadata_edit_form:
+
+      metadata_edit_form.py -> MetadataEditView
+      metadata_edit_template.pt -> templates/metadata_edit.pt
+      metadata_edit_control.py -> MetadataEditView.edit_control
+
+  [ ] full_metadata_edit_form:
+
+      full_metadata_edit_form.py -> MetadataEditView
+      full_metadata_edit_template.pt -> templates/metadata_full_edit.pt
+      metadata_edit_control.py -> MetadataEditView.edit_control
+
+  [ ] document_view:
+
+      document_view.py -> DocumentView
+      document_view_template.pt -> document.pt
+
+  [ ] document_edit_form:
+
+      document_edit_form.py -> DocumentEditView
+      document_edit_template.pt -> templates/document_edit.pt
+      validateHTML.py -> DocumentEditView.validateHTML
+      validateTextFile.py -> DocumentEditView.validateTextFile
+      document_edit_control.py -> DocumentEditView.edit_control
+
+  [ ] newsitem_view:
+
+      newsitem_view.py -> DocumentView
+      document_view_template.pt -> document.pt
+
+  [ ] newsitem_edit_form:
+
+      newsitem_edit_form.py -> NewsItemEditView
+      newsitem_edit_template.pt -> templates/newsitem_edit.pt
+      validateHTML.py -> DocumentEditView.validateHTML
+      validateTextFile.py -> DocumentEditView.validateTextFile
+      newsitem_edit_control.py -> NewsItemEditView.edit_control
+
+  [ ] link_view:
+
+      link_view.py -> LinkView
+      link_view_template.pt -> templates/link.pt
+
+  [ ] link_edit_form:
+
+      link_edit_form.py -> LinkEditView
+      link_edit_template.pt -> templates/link_edit.pt
+      link_edit_control.py -> LinkEditView.edit_control
+
+  [ ] favorite_view:
+
+      favorite_view.py -> LinkView
+      link_view_template.pt -> templates/link.pt
+
+  [ ] file_view:
+
+      file_view.py -> FileView
+      file_view_template.pt -> templates/file.pt
+
+  [ ] file_edit_form:
+
+      file_edit_form.py -> FileEditView
+      file_edit_template.pt -> templates/file_edit.pt
+      file_edit_control.py -> FileEditView.edit_control
+
+  [ ] image_view:
+
+      image_view.py -> ImageView
+      image_view_template.pt -> templates/image.pt
+
+  [ ] image_edit_form:
+
+      image_edit_form.py -> ImageEditView
+      image_edit_template.pt -> templates/image_edit.pt
+      image_edit_control.py -> ImageEditView.edit_control


Property changes on: CMF/trunk/CMFDefault/browser/TODO.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: CMF/trunk/CMFDefault/browser/__init__.py
===================================================================
--- CMF/trunk/CMFDefault/browser/__init__.py	2006-02-21 15:45:07 UTC (rev 41739)
+++ CMF/trunk/CMFDefault/browser/__init__.py	2006-02-21 16:23:53 UTC (rev 41740)
@@ -0,0 +1,16 @@
+##############################################################################
+#
+# Copyright (c) 2006 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.
+#
+##############################################################################
+"""CMFDefault browser views.
+
+$Id$
+"""


Property changes on: CMF/trunk/CMFDefault/browser/__init__.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: CMF/trunk/CMFDefault/browser/configure.zcml
===================================================================
--- CMF/trunk/CMFDefault/browser/configure.zcml	2006-02-21 15:45:07 UTC (rev 41739)
+++ CMF/trunk/CMFDefault/browser/configure.zcml	2006-02-21 16:23:53 UTC (rev 41740)
@@ -0,0 +1,42 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:browser="http://namespaces.zope.org/browser"
+    xmlns:five="http://namespaces.zope.org/five">
+
+  <five:traversable class="Products.CMFCore.PortalFolder.PortalFolder"/>
+
+  <browser:page
+      for="Products.CMFCore.interfaces.IFolderish"
+      name="index_html"
+      class=".folder.FolderView"
+      template="templates/folder.pt"
+      permission="zope2.View"
+      layer="cmf"
+      />
+
+  <browser:page
+      for="Products.CMFCore.interfaces.IFolderish"
+      name="folder_contents"
+      class=".folder.FolderContentsView"
+      template="templates/folder_contents.pt"
+      permission="cmf.ListFolderContents"
+      layer="cmf"
+      />
+
+  <browser:page
+      for="*"
+      name="form_widget"
+      template="templates/form_widgets.pt"
+      permission="zope2.View"
+      layer="cmf"
+      />
+
+  <browser:page
+      for="*"
+      name="batch_widget"
+      template="templates/batch_widgets.pt"
+      permission="zope2.View"
+      layer="cmf"
+      />
+
+</configure>


Property changes on: CMF/trunk/CMFDefault/browser/configure.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: CMF/trunk/CMFDefault/browser/folder.py
===================================================================
--- CMF/trunk/CMFDefault/browser/folder.py	2006-02-21 15:45:07 UTC (rev 41739)
+++ CMF/trunk/CMFDefault/browser/folder.py	2006-02-21 16:23:53 UTC (rev 41740)
@@ -0,0 +1,400 @@
+##############################################################################
+#
+# Copyright (c) 2006 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.
+#
+##############################################################################
+"""Browser views for folders.
+
+$Id$
+"""
+
+from DocumentTemplate import sequence
+from ZTUtils import LazyFilter
+from ZTUtils import make_query
+
+from Products.CMFDefault.exceptions import CopyError
+from Products.CMFDefault.exceptions import zExceptions_Unauthorized
+from Products.CMFDefault.permissions import AddPortalContent
+from Products.CMFDefault.permissions import DeleteObjects
+from Products.CMFDefault.permissions import ListFolderContents
+from Products.CMFDefault.permissions import ManageProperties
+from Products.CMFDefault.permissions import ViewManagementScreens
+from Products.CMFDefault.utils import Message as _
+
+from utils import BatchViewBase
+from utils import decode
+from utils import FormViewBase
+from utils import memoize
+
+
+class FolderView(BatchViewBase):
+
+    """View for IFolderish.
+    """
+
+    # helpers
+
+    @memoize
+    def _getItems(self):
+        (key, reverse) = self.context.getDefaultSorting()
+        items = self.context.contentValues()
+        items = sequence.sort(items,
+                              ((key, 'cmp', reverse and 'desc' or 'asc'),))
+        return LazyFilter(items, skip='View')
+
+    # interface
+
+    @memoize
+    def has_local(self):
+        return 'local_pt' in self.context.objectIds()
+
+
+class FolderContentsView(BatchViewBase, FormViewBase):
+
+    """Contents view for IFolderish.
+    """
+
+    _BUTTONS = ({'id': 'items_new',
+                 'title': _(u'New...'),
+                 'permissions': (ViewManagementScreens, AddPortalContent),
+                 'conditions': ('checkAllowedContentTypes',),
+                 'redirect': ('portal_types', 'object/new')},
+                {'id': 'items_rename',
+                 'title': _(u'Rename...'),
+                 'permissions': (ViewManagementScreens, AddPortalContent),
+                 'conditions': ('checkItems', 'checkAllowedContentTypes'),
+                 'transform': ('validateItemIds',),
+                 'redirect': ('portal_types', 'object/rename_items',
+                              'b_start, ids, key, reverse')},
+                {'id': 'items_cut',
+                 'title': _(u'Cut'),
+                 'permissions': (ViewManagementScreens,),
+                 'conditions': ('checkItems',),
+                 'transform': ('validateItemIds', 'cut_control'),
+                 'redirect': ('portal_types', 'object/folderContents',
+                              'b_start, key, reverse')},
+                {'id': 'items_copy',
+                 'title': _(u'Copy'),
+                 'permissions': (ViewManagementScreens,),
+                 'conditions': ('checkItems',),
+                 'transform': ('validateItemIds', 'copy_control'),
+                 'redirect': ('portal_types', 'object/folderContents',
+                              'b_start, key, reverse')},
+                {'id': 'items_paste',
+                 'title': _(u'Paste'),
+                 'permissions': (ViewManagementScreens, AddPortalContent),
+                 'conditions': ('checkClipboardData',),
+                 'transform': ('validateClipboardData', 'paste_control'),
+                 'redirect': ('portal_types', 'object/folderContents',
+                              'b_start, key, reverse')},
+                {'id': 'items_delete',
+                 'title': _(u'Delete'),
+                 'permissions': (ViewManagementScreens, DeleteObjects),
+                 'conditions': ('checkItems',),
+                 'transform': ('validateItemIds', 'delete_control'),
+                 'redirect': ('portal_types', 'object/folderContents',
+                              'b_start, key, reverse')},
+                {'id': 'items_sort',
+                 'permissions': (ManageProperties,),
+                 'transform': ('sort_control',),
+                 'redirect': ('portal_types', 'object/folderContents',
+                              'b_start')},
+                {'id': 'items_up',
+                 'permissions': (ManageProperties,),
+                 'transform': ('validateItemIds', 'up_control'),
+                 'redirect': ('portal_types', 'object/folderContents',
+                              'b_start, key, reverse')},
+                {'id': 'items_down',
+                 'permissions': (ManageProperties,),
+                 'transform': ('validateItemIds', 'down_control'),
+                 'redirect': ('portal_types', 'object/folderContents',
+                              'b_start, key, reverse')},
+                {'id': 'items_top',
+                 'permissions': (ManageProperties,),
+                 'transform': ('validateItemIds', 'top_control'),
+                 'redirect': ('portal_types', 'object/folderContents',
+                              'b_start, key, reverse')},
+                {'id': 'items_bottom',
+                 'permissions': (ManageProperties,),
+                 'transform': ('validateItemIds', 'bottom_control'),
+                 'redirect': ('portal_types', 'object/folderContents',
+                              'b_start, key, reverse')})
+
+    # helpers
+
+    @memoize
+    def _getSorting(self):
+        key = self.request.form.get('key', None)
+        if key:
+            return (key, self.request.form.get('reverse', 0))
+        else:
+            return self.context.getDefaultSorting()
+
+    @memoize
+    def _isDefaultSorting(self):
+        return self._getSorting() == self.context.getDefaultSorting()
+
+    @memoize
+    def _getHiddenVars(self):
+        b_start = self._getBatchStart()
+        is_default = self._isDefaultSorting()
+        (key, reverse) = is_default and ('', 0) or self._getSorting()
+        return {'b_start': b_start, 'key': key, 'reverse': reverse}
+
+    @memoize
+    def _getItems(self):
+        (key, reverse) = self._getSorting()
+        self.context.filterCookie()
+        folderfilter = self.request.get('folderfilter', '')
+        filter = self.context.decodeFolderFilter(folderfilter)
+        items = self.context.listFolderContents(contentFilter=filter)
+        return sequence.sort(items,
+                             ((key, 'cmp', reverse and 'desc' or 'asc'),))
+
+    # interface
+
+    @memoize
+    @decode
+    def up_info(self):
+        mtool = self._getTool('portal_membership')
+        allowed = mtool.checkPermission(ListFolderContents, self.context,
+                                        'aq_parent')
+        if allowed:
+            up_obj = self.context.aq_inner.aq_parent
+            if hasattr(up_obj, 'portal_url'):
+                up_url = up_obj.getActionInfo('object/folderContents')['url']
+                return {'icon': '%s/UpFolder_icon.gif' % self._getPortalURL(),
+                        'id': up_obj.getId(),
+                        'url': up_url}
+            else:
+                return {'icon': '',
+                        'id': 'Root',
+                        'url': ''}
+        else:
+            return {}
+
+    @memoize
+    def listColumnInfos(self):
+        (key, reverse) = self._getSorting()
+        columns = ( {'key': 'Type',
+                     'title': _(u'Type'),
+                     'width': '20',
+                     'colspan': '2'}
+                  , {'key': 'getId',
+                     'title': _(u'Name'),
+                     'width': '360',
+                     'colspan': None}
+                  , {'key': 'modified',
+                     'title': _(u'Last Modified'),
+                     'width': '180',
+                     'colspan': None}
+                  , {'key': 'position',
+                     'title': _(u'Position'),
+                     'width': '80',
+                     'colspan': None }
+                  )
+        for column in columns:
+            if key == column['key'] and not reverse and key != 'position':
+                query = make_query(key=column['key'], reverse=1)
+            else:
+                query = make_query(key=column['key'])
+            column['url'] = '%s?%s' % (self._getViewURL(), query)
+        return tuple(columns)
+
+    @memoize
+    @decode
+    def listItemInfos(self):
+        b_start = self._getBatchStart()
+        (key, reverse) = self._getSorting()
+        batch_obj = self._getBatchObj()
+        items_manage_allowed = self._checkPermission(ViewManagementScreens)
+        portal_url = self._getPortalURL()
+
+        items = []
+        i = 1
+        for item in batch_obj:
+            item_icon = item.getIcon(1)
+            item_id = item.getId()
+            item_position = (key == 'position') and str(b_start + i) or '...'
+            i += 1
+            item_url = item.getActionInfo(('object/folderContents',
+                                           'object/view'))['url']
+            items.append({'checkbox': items_manage_allowed and ('cb_%s' %
+                                                               item_id) or '',
+                          'icon': item_icon and ('%s/%s' %
+                                               (portal_url, item_icon)) or '',
+                          'id': item_id,
+                          'modified': item.ModificationDate(),
+                          'position': item_position,
+                          'title': item.Title(),
+                          'type': item.Type() or None,
+                          'url': item_url})
+        return tuple(items)
+
+    @memoize
+    def listDeltas(self):
+        length = self._getBatchObj().sequence_length
+        deltas = range(1, min(5, length)) + range(5, length, 5)
+        return tuple(deltas)
+
+    @memoize
+    def is_orderable(self):
+        length = len(self._getBatchObj())
+        items_move_allowed = self._checkPermission(ManageProperties)
+        (key, reverse) = self._getSorting()
+        return items_move_allowed and (key == 'position') and length > 1
+
+    @memoize
+    def is_sortable(self):
+        items_move_allowed = self._checkPermission(ManageProperties)
+        return items_move_allowed and not self._isDefaultSorting()
+
+    # checkers
+
+    def checkAllowedContentTypes(self):
+        return bool(self.context.allowedContentTypes())
+
+    def checkClipboardData(self):
+        return bool(self.context.cb_dataValid())
+
+    def checkItems(self):
+        return bool(self._getItems())
+
+    # validators
+
+    def validateItemIds(self, ids=(), **kw):
+        if ids:
+            return True
+        else:
+            return False, _(u'Please select one or more items first.')
+
+    def validateClipboardData(self, **kw):
+        if self.context.cb_dataValid():
+            return True
+        else:
+            return False, _(u'Please copy or cut one or more items to paste '
+                            u'first.')
+
+    # controllers
+
+    def cut_control(self, ids, **kw):
+        """Cut objects from a folder and copy to the clipboard.
+        """
+        try:
+            self.context.manage_cutObjects(ids, self.request)
+            if len(ids) == 1:
+                return True, _(u'Item cut.')
+            else:
+                return True, _(u'Items cut.')
+        except CopyError:
+            return False, _(u'CopyError: Cut failed.')
+        except zExceptions_Unauthorized:
+            return False, _(u'Unauthorized: Cut failed.')
+
+    def copy_control(self, ids, **kw):
+        """Copy objects from a folder to the clipboard.
+        """
+        try:
+            self.context.manage_copyObjects(ids, self.request)
+            if len(ids) == 1:
+                return True, _(u'Item copied.')
+            else:
+                return True, _(u'Items copied.')
+        except CopyError:
+            return False, _(u'CopyError: Copy failed.')
+
+    def paste_control(self, **kw):
+        """Paste objects to a folder from the clipboard.
+        """
+        try:
+            result = self.context.manage_pasteObjects(self.request['__cp'])
+            if len(result) == 1:
+                return True, _(u'Item pasted.')
+            else:
+                return True, _(u'Items pasted.')
+        except CopyError:
+            return False, _(u'CopyError: Paste failed.')
+        except zExceptions_Unauthorized:
+            return False, _(u'Unauthorized: Paste failed.')
+
+    def delete_control(self, ids, **kw):
+        """Delete objects from a folder.
+        """
+        self.context.manage_delObjects(list(ids))
+        if len(ids) == 1:
+            return True, _(u'Item deleted.')
+        else:
+            return True, _(u'Items deleted.')
+
+    def sort_control(self, key='position', reverse=0, **kw):
+        """Sort objects in a folder.
+        """
+        self.context.setDefaultSorting(key, reverse)
+        return True
+
+    def up_control(self, ids, delta, **kw):
+        subset_ids = [ obj.getId()
+                       for obj in self.context.listFolderContents() ]
+        try:
+            attempt = self.context.moveObjectsUp(ids, delta,
+                                                 subset_ids=subset_ids)
+            if attempt == 1:
+                return True, _(u'Item moved up.')
+            elif attempt > 1:
+                return True, _(u'Items moved up.')
+            else:
+                return False, _(u'Nothing to change.')
+        except ValueError:
+            return False, _(u'ValueError: Move failed.')
+
+    def down_control(self, ids, delta, **kw):
+        subset_ids = [ obj.getId()
+                       for obj in self.context.listFolderContents() ]
+        try:
+            attempt = self.context.moveObjectsDown(ids, delta,
+                                                   subset_ids=subset_ids)
+            if attempt == 1:
+                return True, _(u'Item moved down.')
+            elif attempt > 1:
+                return True, _(u'Items moved down.')
+            else:
+                return False, _(u'Nothing to change.')
+        except ValueError:
+            return False, _(u'ValueError: Move failed.')
+
+    def top_control(self, ids, **kw):
+        subset_ids = [ obj.getId()
+                       for obj in self.context.listFolderContents() ]
+        try:
+            attempt = self.context.moveObjectsToTop(ids,
+                                                    subset_ids=subset_ids)
+            if attempt == 1:
+                return True, _(u'Item moved to top.')
+            elif attempt > 1:
+                return True, _(u'Items moved to top.')
+            else:
+                return False, _(u'Nothing to change.')
+        except ValueError:
+            return False, _(u'ValueError: Move failed.')
+
+    def bottom_control(self, ids, **kw):
+        subset_ids = [ obj.getId()
+                       for obj in self.context.listFolderContents() ]
+        try:
+            attempt = self.context.moveObjectsToBottom(ids,
+                                                       subset_ids=subset_ids)
+            if attempt == 1:
+                return True, _(u'Item moved to bottom.')
+            elif attempt > 1:
+                return True, _(u'Items moved to bottom.')
+            else:
+                return False, _(u'Nothing to change.')
+        except ValueError:
+            return False, _(u'ValueError: Move failed.')


Property changes on: CMF/trunk/CMFDefault/browser/folder.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Copied: CMF/trunk/CMFDefault/browser/templates/batch_widgets.pt (from rev 41723, CMF/trunk/CMFDefault/skins/zpt_generic/batch_widgets.pt)
===================================================================
--- CMF/trunk/CMFDefault/skins/zpt_generic/batch_widgets.pt	2006-02-21 12:21:26 UTC (rev 41723)
+++ CMF/trunk/CMFDefault/browser/templates/batch_widgets.pt	2006-02-21 16:23:53 UTC (rev 41740)
@@ -0,0 +1,54 @@
+<html>
+<body>
+
+ <metal:macro metal:define-macro="summary"
+ ><p class="BatchSummary" tal:condition="view/summary_length"
+     i18n:translate="">Found <span tal:replace="view/summary_length"
+      i18n:name="count">N</span> <span i18n:name="type"><span tal:omit-tag=""
+       tal:content="view/summary_type" i18n:translate="">ITEMS</span></span
+  ><tal:case tal:condition="python: view.summary_match() is not None"
+   > matching '<span tal:replace="view/summary_match" i18n:name="text"
+    >SEARCH TERM</span>'</tal:case>.</p
+ ><p class="BatchSummary" tal:condition="not:view/summary_length"
+     i18n:translate="">There are no items matching your specified criteria.</p
+></metal:macro>
+
+ <metal:macro metal:define-macro="listing">
+ <p class="BatchListing" tal:repeat="item_info view/listItemInfos"
+ ><a href="" tal:attributes="href item_info/url"
+  ><img src="" alt="" title="" border="0" width="16" height="16"
+      tal:attributes="src item_info/icon; alt item_info/type;
+                      title item_info/type"
+      i18n:attributes="alt; title" /></a
+ ><tal:case tal:condition="item_info/title">&nbsp;
+  <a href="" tal:attributes="href item_info/url"
+     tal:content="item_info/title">TITLE</a></tal:case
+ ><tal:case tal:condition="item_info/description">
+  <br /><tal:span tal:content="item_info/description"
+ >DESCRIPTION</tal:span></tal:case
+ ><tal:case tal:condition="item_info/format">
+  <br /><span><tal:span tal:content="item_info/format" i18n:translate=""
+  >FORMAT</tal:span><tal:case tal:condition="item_info/size"
+  >, <tal:span tal:content="item_info/size">99.9 KB</tal:span></tal:case
+ ></span></tal:case></p
+></metal:macro>
+
+ <metal:macro metal:define-macro="navigation"
+    tal:define="prev_info view/navigation_previous;
+                next_info view/navigation_next"
+ ><p class="BatchNavigation" tal:condition="python: prev_info or next_info"
+  ><tal:case tal:condition="prev_info">
+  <a href="" tal:attributes="href prev_info/url"
+     tal:content="prev_info/title"
+     i18n:translate="">PREVIOUS N ITEMS</a></tal:case
+ ><tal:case tal:condition="python: prev_info and next_info">
+  &nbsp;&nbsp;</tal:case
+ ><tal:case tal:condition="next_info">
+  <a href="" tal:attributes="href next_info/url"
+     tal:content="next_info/title"
+     i18n:translate="">NEXT N ITEMS</a></tal:case
+ ></p
+></metal:macro>
+
+</body>
+</html>


Property changes on: CMF/trunk/CMFDefault/browser/templates/batch_widgets.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Copied: CMF/trunk/CMFDefault/browser/templates/folder.pt (from rev 41723, CMF/trunk/CMFDefault/skins/zpt_generic/index_html_template.pt)
===================================================================
--- CMF/trunk/CMFDefault/skins/zpt_generic/index_html_template.pt	2006-02-21 12:21:26 UTC (rev 41723)
+++ CMF/trunk/CMFDefault/browser/templates/folder.pt	2006-02-21 16:23:53 UTC (rev 41740)
@@ -0,0 +1,38 @@
+<html metal:use-macro="context/@@standard_macros/page">
+<body>
+
+<metal:slot metal:fill-slot="header" i18n:domain="cmf_default"
+><tal:case tal:condition="not: view/has_local"
+><h1 id="DesktopTitle" tal:content="view/title">Page Title</h1>
+
+<div id="DesktopDescription" tal:content="view/description">Description
+ of the resource goes here, perhaps even wrapping lines;  this is to make it
+ long enough to test.</div></tal:case
+><tal:case tal:condition="view/has_local"
+><div metal:use-macro="context/local_pt/macros/header | default">'local_pt'
+ header goes here.</div></tal:case
+></metal:slot>
+
+<metal:slot metal:fill-slot="body" i18n:domain="cmf_default">
+  <div id="content_well"
+       style="float: left; top: 0; width: 78%;">
+    <div tal:condition="not: view/has_local">
+
+<metal:macro metal:use-macro="context/@@batch_widget/listing" />
+<metal:macro metal:use-macro="context/@@batch_widget/navigation" />
+
+    </div>
+    <div tal:condition="view/has_local">
+      <div metal:use-macro="context/local_pt/macros/body | default">
+        'local_pt' body goes here.
+      </div>
+    </div>
+  </div>
+  <div id="right_sidebar"
+       style="float: right; width: 20%">
+<tal:span tal:replace="structure context/news_box" />
+  </div>
+</metal:slot>
+
+</body>
+</html>

Copied: CMF/trunk/CMFDefault/browser/templates/folder_contents.pt (from rev 41723, CMF/trunk/CMFDefault/skins/zpt_generic/folder_contents_template.pt)
===================================================================
--- CMF/trunk/CMFDefault/skins/zpt_generic/folder_contents_template.pt	2006-02-21 12:21:26 UTC (rev 41723)
+++ CMF/trunk/CMFDefault/browser/templates/folder_contents.pt	2006-02-21 16:23:53 UTC (rev 41740)
@@ -0,0 +1,96 @@
+<html metal:use-macro="context/@@standard_macros/page">
+<body>
+
+<metal:slot metal:fill-slot="header" i18n:domain="cmf_default">
+<h1 i18n:translate="">Folder Contents: <tal:span
+    tal:content="view/title" i18n:name="obj_title">Title</tal:span></h1>
+</metal:slot>
+
+<metal:slot metal:fill-slot="body" i18n:domain="cmf_default">
+<div class="Desktop">
+
+<p tal:define="up_info view/up_info" tal:condition="up_info"
+><tal:case tal:condition="up_info/url"
+ ><a href="" tal:attributes="href up_info/url"
+  ><img src="" alt="[Link]" border="0" tal:attributes="src up_info/icon"
+      i18n:attributes="alt" /></a>
+  <span tal:omit-tag="" i18n:translate="">Up to</span>
+  <a href="" tal:attributes="href up_info/url"
+     tal:content="up_info/id">ID</a></tal:case
+><tal:case tal:condition="not: up_info/url"
+ ><span class="mild" i18n:translate="">Root</span></tal:case></p>
+
+<form action="folder_contents" method="post"
+   tal:attributes="action view/form_action"
+><metal:macro metal:use-macro="context/@@form_widget/hidden_vars" />
+ <table class="BatchTable"
+    tal:condition="view/listItemInfos">
+  <thead>
+   <tr class="list-header">
+    <th width="80" tal:repeat="column_info view/listColumnInfos"
+       tal:attributes="width column_info/width; colspan column_info/colspan"
+    ><a href="" tal:attributes="href column_info/url"
+        tal:content="column_info/title">COLUMN TITLE</a></th>
+   </tr>
+  </thead>
+  <tbody tal:repeat="item_info view/listItemInfos">
+   <tr class="" tal:define="even repeat/item_info/even"
+      tal:attributes="class python: (even and 'row-hilite') or 'row-normal'">
+      <td width="5"
+      ><input type="checkbox" name="ids:list" value="" id=""
+          tal:attributes="value item_info/id; id item_info/checkbox"
+          tal:condition="item_info/checkbox" /></td>
+      <td
+      ><a href="" tal:attributes="href item_info/url"
+          tal:condition="item_info/icon"
+       ><img src="" alt="" border="0"
+           tal:attributes="src item_info/icon; alt item_info/type"
+           i18n:attributes="alt" /></a></td>
+      <td
+      ><a href="" tal:attributes="href item_info/url"
+       ><tal:span tal:content="item_info/id">ID</tal:span>
+        <tal:case tal:condition="item_info/title"
+           tal:content="string:(${item_info/title})">(Title)</tal:case
+      ></a></td>
+      <td
+      ><tal:span tal:content="item_info/modified">2001</tal:span></td>
+      <td
+      ><tal:span tal:content="item_info/position">1</tal:span></td>
+   </tr>
+  </tbody>
+ </table>
+ <metal:macro metal:use-macro="context/@@batch_widget/navigation" />
+ <metal:macro metal:use-macro="context/@@form_widget/buttons" />
+<tal:case tal:condition="python: view.is_orderable() or view.is_sortable()"
+> <div class="FormButtons"
+ ><tal:case tal:condition="view/is_orderable">
+  <input type="submit" name="items_up" value="Up"
+     i18n:attributes="value" />
+  /
+  <input type="submit" name="items_down" value="Down"
+     i18n:attributes="value" />
+  by
+  <select name="delta:int">
+   <option value=""
+      tal:repeat="delta view/listDeltas"
+      tal:attributes="value delta"
+      tal:content="delta">
+   </option>
+  </select>
+  <input type="submit" name="items_top" value="Top"
+     i18n:attributes="value" />
+  <input type="submit" name="items_bottom" value="Bottom"
+     i18n:attributes="value" /></tal:case
+ ><tal:case tal:condition="view/is_sortable">
+  <input type="submit" name="items_sort" value="Set Sorting as Default"
+     i18n:attributes="value" /></tal:case
+></div>
+</tal:case></form>
+
+<div tal:replace="structure context/folder_filter_form">Filter Form Here</div>
+
+</div>
+</metal:slot>
+
+</body>
+</html>

Copied: CMF/trunk/CMFDefault/browser/templates/form_widgets.pt (from rev 41723, CMF/trunk/CMFDefault/skins/zpt_generic/form_widgets.pt)
===================================================================
--- CMF/trunk/CMFDefault/skins/zpt_generic/form_widgets.pt	2006-02-21 12:21:26 UTC (rev 41723)
+++ CMF/trunk/CMFDefault/browser/templates/form_widgets.pt	2006-02-21 16:23:53 UTC (rev 41740)
@@ -0,0 +1,19 @@
+<html>
+<body>
+
+<metal:macro metal:define-macro="hidden_vars">
+ <tal:loop tal:repeat="hidden_var view/listHiddenVarInfos"
+ ><input type="hidden" name="HiddenVarName" value=""
+     tal:attributes="name hidden_var/name; value hidden_var/value" /></tal:loop
+></metal:macro>
+
+ <metal:macro metal:define-macro="buttons"
+><div class="FormButtons">
+  <tal:loop tal:repeat="button view/listButtonInfos"
+  ><input type="submit" name="ButtonName" value="ButtonValue"
+      tal:attributes="name button/name; value button/value"
+      i18n:attributes="value" /></tal:loop></div
+></metal:macro>
+
+</body>
+</html>


Property changes on: CMF/trunk/CMFDefault/browser/templates/form_widgets.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: CMF/trunk/CMFDefault/browser/utils.py
===================================================================
--- CMF/trunk/CMFDefault/browser/utils.py	2006-02-21 15:45:07 UTC (rev 41739)
+++ CMF/trunk/CMFDefault/browser/utils.py	2006-02-21 16:23:53 UTC (rev 41740)
@@ -0,0 +1,270 @@
+##############################################################################
+#
+# Copyright (c) 2006 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.
+#
+##############################################################################
+"""Browser view utilities.
+
+$Id$
+"""
+
+from Products.PythonScripts.standard import thousands_commas
+from ZTUtils import Batch
+from ZTUtils import make_query
+
+from Products.CMFCore.utils import getToolByName
+from Products.CMFDefault.utils import html_marshal
+from Products.CMFDefault.utils import Message as _
+from Products.CMFDefault.utils import toUnicode
+
+
+def decode(meth):
+    def decoded_meth(self, *args, **kw):
+        return toUnicode(meth(self, *args, **kw), self._getDefaultCharset())
+    return decoded_meth
+
+def memoize(meth):
+    def memoized_meth(self, *args):
+        if not hasattr(self, '__memo__'):
+            self.__memo__ = {}
+        sig = (meth, args)
+        if sig not in self.__memo__:
+            self.__memo__[sig] = meth(self, *args)
+        return self.__memo__[sig]
+    return memoized_meth
+
+
+class ViewBase:
+
+    # helpers
+
+    @memoize
+    def _getTool(self, name):
+        return getToolByName(self.context, name)
+
+    @memoize
+    def _checkPermission(self, permission):
+        mtool = self._getTool('portal_membership')
+        return mtool.checkPermission(permission, self.context)
+
+    @memoize
+    def _getPortalURL(self):
+        utool = self._getTool('portal_url')
+        return utool()
+
+    @memoize
+    def _getViewURL(self):
+        return self.request['ACTUAL_URL']
+
+    @memoize
+    def _getDefaultCharset(self):
+        ptool = self._getTool('portal_properties')
+        return ptool.getProperty('default_charset', None)
+
+    # interface
+
+    @memoize
+    @decode
+    def title(self):
+        return self.context.Title()
+
+    @memoize
+    @decode
+    def description(self):
+        return self.context.Description()
+
+
+class FormViewBase(ViewBase):
+
+    # helpers
+
+    def _setRedirect(self, provider_id, action_path, keys=''):
+        provider = self._getTool(provider_id)
+        try:
+            target = provider.getActionInfo(action_path, self.context)['url']
+        except ValueError:
+            target = self._getPortalURL()
+
+        kw = {}
+        message = self.request.other.get('portal_status_message', '')
+        if message:
+            kw['portal_status_message'] = message
+        for k in keys.split(','):
+            k = k.strip()
+            v = self.request.form.get(k, None)
+            if v:
+                kw[k] = v
+
+        query = kw and ( '?%s' % make_query(kw) ) or ''
+        self.request.RESPONSE.redirect( '%s%s' % (target, query) )
+
+        return True
+
+    # interface
+
+    def __call__(self, **kw):
+        form = self.request.form
+        for button in self._BUTTONS:
+            if button['id'] in form:
+                for permission in button.get('permissions', ()):
+                    if not self._checkPermission(permission):
+                        break
+                else:
+                    for transform in button.get('transform', ()):
+                        status = getattr(self, transform)(**form)
+                        if isinstance(status, bool):
+                            status = (status,)
+                        if len(status) > 1:
+                            self.request.other['portal_status_message'] = status[1]
+                        if not status[0]:
+                            return self.index()
+                    if self._setRedirect(*button['redirect']):
+                        return
+        return self.index()
+
+    @memoize
+    def form_action(self):
+        return self._getViewURL()
+
+    @memoize
+    def listButtonInfos(self):
+        form = self.request.form
+        buttons = []
+        for button in self._BUTTONS:
+            if button.get('title', None):
+                for permission in button.get('permissions', ()):
+                    if not self._checkPermission(permission):
+                        break
+                else:
+                    for condition in button.get('conditions', ()):
+                        if not getattr(self, condition)():
+                            break
+                    else:
+                        buttons.append({'name': button['id'],
+                                        'value': button['title']})
+        return tuple(buttons)
+
+    @memoize
+    @decode
+    def listHiddenVarInfos(self):
+        kw = self._getHiddenVars()
+        vars = [ {'name': name, 'value': value}
+                 for name, value in html_marshal(**kw) ]
+        return tuple(vars)
+
+
+class BatchViewBase(ViewBase):
+
+    # helpers
+
+    _BATCH_SIZE = 25
+
+    @memoize
+    def _getBatchStart(self):
+        return self.request.form.get('b_start', 0)
+
+    @memoize
+    def _getBatchObj(self):
+        b_start = self._getBatchStart()
+        items = self._getItems()
+        return Batch(items, self._BATCH_SIZE, b_start, orphan=0)
+
+    @memoize
+    def _getHiddenVars(self):
+        return {}
+
+    @memoize
+    def _getNavigationURL(self, b_start):
+        target = self._getViewURL()
+        kw = self._getHiddenVars()
+
+        kw['b_start'] = b_start
+        for k, v in kw.items():
+            if not v or k == 'portal_status_message':
+                del kw[k]
+
+        query = kw and ('?%s' % make_query(kw)) or ''
+        return u'%s%s' % (target, query)
+
+    # interface
+
+    @memoize
+    @decode
+    def listItemInfos(self):
+        batch_obj = self._getBatchObj()
+        portal_url = self._getPortalURL()
+
+        items = []
+        for item in batch_obj:
+            item_description = item.Description()
+            item_icon = item.getIcon(1)
+            item_title = item.Title()
+            item_type = remote_type = item.Type()
+            if item_type == 'Favorite' and not item_icon == 'p_/broken':
+                item = item.getObject()
+                item_description = item_description or item.Description()
+                item_title = item_title or item.Title()
+                remote_type = item.Type()
+            is_file = remote_type in ('File', 'Image')
+            is_link = remote_type == 'Link'
+            items.append({'description': item_description,
+                          'format': is_file and item.Format() or '',
+                          'icon': item_icon and ('%s/%s' %
+                                               (portal_url, item_icon)) or '',
+                          'size': is_file and ('%0.0f kb' %
+                                            (item.get_size() / 1024.0)) or '',
+                          'title': item_title,
+                          'type': item_type,
+                          'url': is_link and item.getRemoteUrl() or
+                                 item.absolute_url()})
+        return tuple(items)
+
+    @memoize
+    def navigation_previous(self):
+        batch_obj = self._getBatchObj().previous
+        if batch_obj is None:
+            return None
+
+        length = len(batch_obj)
+        url = self._getNavigationURL(batch_obj.first)
+        if length == 1:
+            title = _(u'Previous item')
+        else:
+            title = _(u'Previous ${count} items', mapping={'count': length})
+        return {'title': title, 'url': url}
+
+    @memoize
+    def navigation_next(self):
+        batch_obj = self._getBatchObj().next
+        if batch_obj is None:
+            return None
+
+        length = len(batch_obj)
+        url = self._getNavigationURL(batch_obj.first)
+        if length == 1:
+            title = _(u'Next item')
+        else:
+            title = _(u'Next ${count} items', mapping={'count': length})
+        return {'title': title, 'url': url}
+
+    @memoize
+    def summary_length(self):
+        length = self._getBatchObj().sequence_length
+        return length and thousands_commas(length) or ''
+
+    @memoize
+    def summary_type(self):
+        length = self._getBatchObj().sequence_length
+        return (length == 1) and _(u'item') or _(u'items')
+
+    @memoize
+    @decode
+    def summary_match(self):
+        return self.request.form.get('SearchableText')


Property changes on: CMF/trunk/CMFDefault/browser/utils.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Modified: CMF/trunk/CMFDefault/configure.zcml
===================================================================
--- CMF/trunk/CMFDefault/configure.zcml	2006-02-21 15:45:07 UTC (rev 41739)
+++ CMF/trunk/CMFDefault/configure.zcml	2006-02-21 16:23:53 UTC (rev 41740)
@@ -1,9 +1,10 @@
 <configure
-    xmlns="http://namespaces.zope.org/zope"
-    >
+    xmlns="http://namespaces.zope.org/zope">
 
-  <include
-    package=".skin"
-    />
+  <include package=".skin"/>
 
+  <!-- uncomment this to enable browser views
+  <include package=".browser"/>
+  -->
+
 </configure>



More information about the CMF-checkins mailing list