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"> + <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"> + </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> _______________________________________________ CMF-checkins mailing list [email protected] http://mail.zope.org/mailman/listinfo/cmf-checkins
