[Checkins] SVN: Products.CMFDefault/trunk/Products/CMFDefault/browser/ Folders added for content, form, membership and skin views and views moved into relevant folders.

Charlie Clark charlie at begeistert.org
Fri Jun 25 10:31:04 EDT 2010


Log message for revision 113835:
  Folders added for content, form, membership and skin views and views moved into relevant folders.

Changed:
  D   Products.CMFDefault/trunk/Products/CMFDefault/browser/__init__.py
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/__init__.py
  D   Products.CMFDefault/trunk/Products/CMFDefault/browser/authentication.py
  U   Products.CMFDefault/trunk/Products/CMFDefault/browser/configure.zcml
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/content/
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/content/__init__.py
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/content/configure.zcml
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/content/document.py
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/content/favorite.py
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/content/file.py
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/content/folder.py
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/content/interfaces.py
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/content/link.py
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/content/metadata.py
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/content/newsitem.py
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/content/templates/
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/content/templates/document.pt
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/content/templates/folder.pt
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/content/templates/folder_contents.pt
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/content/templates/link.pt
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/__init__.py
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/btreefolder.txt
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/document.txt
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/favorite.txt
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/file.txt
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/folder.txt
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/folder_utest.txt
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/link.txt
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/metadata.txt
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/test_btreefolder.py
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/test_document.py
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/test_favorite.py
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/test_file.py
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/test_folder.py
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/test_link.py
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/test_metadata.py
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/utils.py
  D   Products.CMFDefault/trunk/Products/CMFDefault/browser/document.py
  D   Products.CMFDefault/trunk/Products/CMFDefault/browser/favorite.py
  D   Products.CMFDefault/trunk/Products/CMFDefault/browser/file.py
  D   Products.CMFDefault/trunk/Products/CMFDefault/browser/folder.py
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/form/
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/form/__init__.py
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/form/batch_widgets.pt
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/form/configure.zcml
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/form/form_widgets.pt
  D   Products.CMFDefault/trunk/Products/CMFDefault/browser/icons.py
  D   Products.CMFDefault/trunk/Products/CMFDefault/browser/interfaces.py
  D   Products.CMFDefault/trunk/Products/CMFDefault/browser/link.py
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/__init__.py
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/authentication.py
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/configure.zcml
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/templates/
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/templates/forbidden.pt
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/templates/logged_out.pt
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/templates/login.pt
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/templates/mail_password.pt
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/tests/
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/tests/authentication.txt
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/tests/test_authentication.py
  D   Products.CMFDefault/trunk/Products/CMFDefault/browser/metadata.py
  D   Products.CMFDefault/trunk/Products/CMFDefault/browser/newsitem.py
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/skins/
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/skins/__init__.py
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/skins/configure.zcml
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/skins/icons.py
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/skins/tests/
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/skins/tests/__init__.py
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/skins/tests/test_ursa.py
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/skins/ursa.py
  D   Products.CMFDefault/trunk/Products/CMFDefault/browser/templates/
  D   Products.CMFDefault/trunk/Products/CMFDefault/browser/tests/
  D   Products.CMFDefault/trunk/Products/CMFDefault/browser/ursa.py
  D   Products.CMFDefault/trunk/Products/CMFDefault/browser/utils.py
  A   Products.CMFDefault/trunk/Products/CMFDefault/browser/utils.py

-=-
Deleted: Products.CMFDefault/trunk/Products/CMFDefault/browser/__init__.py
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/__init__.py	2010-06-25 12:36:59 UTC (rev 113834)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/__init__.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -1,16 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2006 Zope Foundation and Contributors.
-#
-# 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$
-"""

Added: Products.CMFDefault/trunk/Products/CMFDefault/browser/__init__.py
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/__init__.py	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/__init__.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,16 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Foundation and Contributors.
+#
+# 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: Products.CMFDefault/trunk/Products/CMFDefault/browser/__init__.py
___________________________________________________________________
Added: svn:keywords
   + Id
Added: svn:eol-style
   + native

Deleted: Products.CMFDefault/trunk/Products/CMFDefault/browser/authentication.py
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/authentication.py	2010-06-25 12:36:59 UTC (rev 113834)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/authentication.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -1,281 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2010 Zope Foundation and Contributors.
-#
-# 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.
-#
-##############################################################################
-"""Authentication browser views.
-
-$Id$
-"""
-
-from urllib import quote, urlencode
-
-from Products.Five import BrowserView
-from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
-from zExceptions import Forbidden
-from zExceptions import Redirect
-from zope.app.form.browser import TextWidget
-from zope.formlib import form
-from zope.interface import implements
-from zope.interface import Interface
-from zope.schema import Bool
-from zope.schema import Choice
-from zope.schema import Password
-from zope.schema import TextLine
-from zope.schema import URI
-from zope.schema.interfaces import ISource
-from zope.site.hooks import getSite
-
-from Products.CMFCore.CookieCrumbler import ATTEMPT_LOGIN
-from Products.CMFCore.CookieCrumbler import ATTEMPT_NONE
-from Products.CMFCore.utils import getToolByName
-from Products.CMFDefault.formlib.form import EditFormBase
-from Products.CMFDefault.utils import Message as _
-from Products.CMFDefault.browser.utils import ViewBase, memoize
-
-
-def _expireAuthCookie(view):
-    try:
-        cctool = getToolByName(view, 'cookie_authentication')
-        method = cctool.getCookieMethod('expireAuthCookie',
-                                        cctool.defaultExpireAuthCookie)
-        method(view.request.response, cctool.auth_cookie)
-    except AttributeError:
-        view.request.response.expireCookie('__ac', path='/')
-
-
-class UnauthorizedView(BrowserView):
-
-    """Exception view for Unauthorized.
-    """
-
-    forbidden_template = ViewPageTemplateFile('templates/forbidden.pt')
-
-    def __call__(self):
-        try:
-            atool = getToolByName(self, 'portal_actions')
-            target = atool.getActionInfo('user/login')['url']
-        except (AttributeError, ValueError):
-            # re-raise the unhandled exception
-            raise self.context
-
-        req = self.request
-        attempt = getattr(req, '_cookie_auth', ATTEMPT_NONE)
-        if attempt not in (ATTEMPT_NONE, ATTEMPT_LOGIN):
-            # An authenticated user was denied access to something.
-            # XXX: hack context to get the right @@standard_macros/page
-            #      why do we get the wrong without this hack?
-            self.context = self.__parent__
-            raise Forbidden(self.forbidden_template())
-
-        _expireAuthCookie(self)
-        came_from = req.get('came_from', None)
-        if came_from is None:
-            came_from = req.get('ACTUAL_URL')
-            query = req.get('QUERY_STRING')
-            if query:
-                # Include the query string in came_from
-                if not query.startswith('?'):
-                    query = '?' + query
-                came_from = came_from + query
-        url = '%s?came_from=%s' % (target, quote(came_from))
-        raise Redirect(url)
-
-
-class NameSource(object):
-
-    implements(ISource)
-
-    def __contains__(self, value):
-        rich_context = getSite()
-        mtool = getToolByName(rich_context, 'portal_membership')
-        if mtool.getMemberById(value):
-            return True
-        candidates = mtool.searchMembers('email', value)
-        for candidate in candidates:
-            if candidate['email'].lower() == value.lower():
-                return True
-        return False
-
-available_names = NameSource()
-
-
-class ILoginSchema(Interface):
-
-    """Schema for login form.
-    """
-
-    came_from = URI(
-        required=False)
-
-    name = TextLine(
-        title=_(u'Member ID'),
-        description=_(u'Case sensitive'))
-
-    password = Password(
-        title=_(u'Password'),
-        description=_(u'Case sensitive'))
-
-    persistent = Bool(
-        title=_(u'Remember my ID.'),
-        description=_(u'Saves your member ID in a cookie.'),
-        default=True)
-
-
-class IMailPasswordSchema(Interface):
-
-    """Schema for mail password form.
-    """
-
-    name = Choice(
-        title=_(u'Member ID'),
-        description=_(u'Member ID or email address'),
-        source=available_names)
-
-
-class LoginFormView(EditFormBase):
-
-    """Form view for ILoginSchema.
-    """
-
-    base_template = EditFormBase.template
-    template = ViewPageTemplateFile('templates/login.pt')
-    label = _(u'Log in')
-    prefix = ''
-
-    form_fields = form.FormFields(ILoginSchema)
-
-    actions = form.Actions(
-        form.Action(
-            name='login',
-            label=_(u'Login'),
-            validator='handle_login_validate',
-            success='handle_login_success',
-            failure='handle_failure'))
-
-    def setUpWidgets(self, ignore_request=False):
-        try:
-            cctool = self._getTool('cookie_authentication')
-            ac_name_id = cctool.name_cookie
-            ac_password_id = cctool.pw_cookie
-            ac_persistent_id = cctool.persist_cookie
-        except AttributeError:
-            ac_name_id = '__ac_name'
-            ac_password_id = '__ac_password'
-            ac_persistent_id = '__ac_persistent'
-        ac_name = self.request.get(ac_name_id)
-        if ac_name is not None:
-            self.request.form['name'] = ac_name
-            self.request.form[ac_name_id] = ac_name
-        ac_persistent = self.request.get(ac_persistent_id)
-        if ac_persistent is not None:
-            self.request.form['persistent'] = ac_persistent
-        ac_persistent_used = self.request.get("%s.used" % ac_persistent_id)
-        if ac_persistent_used is not None:
-            self.request.form['persistent.used'] = ac_persistent_used
-        super(LoginFormView,
-              self).setUpWidgets(ignore_request=ignore_request)
-        self.widgets['came_from'].hide = True
-        self.widgets['name'].name = ac_name_id
-        self.widgets['password'].name = ac_password_id
-        self.widgets['persistent'].name = ac_persistent_id
-
-    def handle_login_validate(self, action, data):
-        mtool = self._getTool('portal_membership')
-        if mtool.isAnonymousUser():
-            _expireAuthCookie(self)
-            return (_(u'Login failure'),)
-        return None
-
-    def handle_login_success(self, action, data):
-        return self._setRedirect('portal_actions', 'user/logged_in',
-                                 'came_from')
-
-
-class MailPasswordFormView(EditFormBase):
-
-    """Form view for IMailPasswordSchema.
-    """
-
-    base_template = EditFormBase.template
-    template = ViewPageTemplateFile('templates/mail_password.pt')
-    label = _(u"Don't panic!")
-    description = _(u"Just enter your member ID below, click 'Send', and "
-                    u"your password will be mailed to you if you gave a "
-                    u"valid email address when you signed on.")
-
-    form_fields = form.FormFields(IMailPasswordSchema)
-    form_fields['name'].custom_widget = TextWidget
-
-    actions = form.Actions(
-        form.Action(
-            name='send',
-            label=_(u'Send'),
-            success='handle_send_success',
-            failure='handle_failure'))
-
-    def setUpWidgets(self, ignore_request=False):
-        try:
-            cctool = self._getTool('cookie_authentication')
-            ac_name_id = cctool.name_cookie
-        except AttributeError:
-            ac_name_id = '__ac_name'
-        ac_name = self.request.get(ac_name_id)
-        if ac_name and not self.request.has_key('%s.name' % self.prefix):
-            self.request.form['%s.name' % self.prefix] = ac_name
-        super(MailPasswordFormView,
-              self).setUpWidgets(ignore_request=ignore_request)
-
-    def handle_send_success(self, action, data):
-        mtool = self._getTool('portal_membership')
-        if not mtool.getMemberById(data['name']):
-            candidates = mtool.searchMembers('email', data['name'])
-            for candidate in candidates:
-                if candidate['email'].lower() == data['name'].lower():
-                    data['name'] = candidate['username']
-                    break
-        rtool = self._getTool('portal_registration')
-        rtool.mailPassword(data['name'], self.request)
-        self.status = _(u'Your password has been mailed to you.')
-        return self._setRedirect('portal_actions', 'user/login')
-
-
-class Logout(ViewBase):
-    """Log the user out"""
-    
-    template = ViewPageTemplateFile("templates/logged_out.pt")
-    
-    @memoize
-    def logged_in(self):
-        """Check whether the user is (still logged in)"""
-        mtool = self._getTool('portal_membership')
-        return not mtool.isAnonymousUser()
-        
-    @memoize
-    def logout(self):
-        """Log the user out"""
-        _expireAuthCookie(self)
-    
-    @memoize
-    def clear_skin_cookie(self):
-        """Remove skin cookie"""
-        stool = self._getTool('portal_skins')
-        stool.clearSkinCookie()
-    
-    def __call__(self):
-        """Clear cookies and return the template"""
-        if 'portal_status_message' in self.request:
-            return self.template()
-        if self.logged_in():
-            self.clear_skin_cookie()
-            self.logout()
-            status = "?" + urlencode({'portal_status_message':
-                                      _(u'You have been logged out.')})
-            return self.request.response.redirect(self.request.URL + status)

Modified: Products.CMFDefault/trunk/Products/CMFDefault/browser/configure.zcml
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/configure.zcml	2010-06-25 12:36:59 UTC (rev 113834)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/configure.zcml	2010-06-25 14:31:03 UTC (rev 113835)
@@ -1,204 +1,13 @@
 <configure
     xmlns="http://namespaces.zope.org/zope"
     xmlns:browser="http://namespaces.zope.org/browser">
+    
+  <include package=".skins" />
+  
+  <include package=".form" />
 
-  <browser:page
-      for="Products.CMFCore.interfaces.IFolderish"
-      layer="..interfaces.ICMFDefaultSkin"
-      name="view.html"
-      class=".folder.FolderView"
-      template="templates/folder.pt"
-      permission="zope2.View"
-      />
+  <include package=".content" />
+  
+  <include package=".membership" />
 
-  <utility
-      component=".folder.contents_delta_vocabulary"
-      name="cmf.contents delta vocabulary"
-      provides="zope.schema.interfaces.IVocabularyFactory"
-      />
-
-  <browser:page
-      for="Products.CMFCore.interfaces.IFolderish"
-      layer="..interfaces.ICMFDefaultSkin"
-      name="edit.html"
-      class=".folder.ContentsView"
-      permission="cmf.ListFolderContents"
-      />
-
-  <adapter factory=".metadata.MinimalMetadataSchemaAdapter"/>
-
-  <browser:page
-      for="Products.CMFCore.interfaces.IMutableMinimalDublinCore"
-      layer="..interfaces.ICMFDefaultSkin"
-      name="properties.html"
-      class=".metadata.MinimalMetadataEditView"
-      permission="zope2.ManageProperties"
-      />
-
-  <adapter factory=".metadata.MetadataSchemaAdapter"/>
-
-  <browser:page
-      for="Products.CMFCore.interfaces.IMutableDublinCore"
-      layer="..interfaces.ICMFDefaultSkin"
-      name="properties.html"
-      class=".metadata.MetadataEditView"
-      permission="cmf.ModifyPortalContent"
-      />
-
-  <adapter factory=".document.DocumentSchemaAdapter"/>
-
-  <utility
-      component=".document.TextFormatVocabularyFactory"
-      name="cmf.AvailableTextFormats"
-      />
-
-  <browser:page
-      for="..interfaces.IDocument"
-      layer="..interfaces.ICMFDefaultSkin"
-      name="view.html"
-      class=".document.DocumentView"
-      template="templates/document.pt"
-      permission="zope2.View"
-      />
-
-  <browser:page
-      for="..interfaces.IMutableDocument"
-      layer="..interfaces.ICMFDefaultSkin"
-      name="edit.html"
-      class=".document.DocumentEditView"
-      permission="cmf.ModifyPortalContent"
-      />
-
-  <adapter factory=".newsitem.NewsItemSchemaAdapter"/>
-
-  <browser:page
-      for="..interfaces.IMutableNewsItem"
-      layer="..interfaces.ICMFDefaultSkin"
-      name="edit.html"
-      class=".newsitem.NewsItemEditView"
-      permission="cmf.ModifyPortalContent"
-      />
-
-  <adapter factory=".link.LinkSchemaAdapter"/>
-
-  <adapter
-      name="cmf.link"
-      factory=".link.LinkAddView"
-      />
-
-  <browser:page
-      for="..interfaces.ILink"
-      layer="..interfaces.ICMFDefaultSkin"
-      name="view.html"
-      class=".link.LinkView"
-      template="templates/link.pt"
-      permission="zope2.View"
-      />
-
-  <browser:page
-      for="..interfaces.IMutableLink"
-      layer="..interfaces.ICMFDefaultSkin"
-      name="edit.html"
-      class=".link.LinkEditView"
-      permission="cmf.ModifyPortalContent"
-      />
-
-  <adapter factory=".favorite.FavoriteSchemaAdapter"/>
-
-  <adapter
-      name="cmf.favorite"
-      factory=".favorite.FavoriteAddView"
-      />
-
-  <browser:page
-      for="..interfaces.IMutableFavorite"
-      layer="..interfaces.ICMFDefaultSkin"
-      name="edit.html"
-      class=".favorite.FavoriteEditView"
-      permission="cmf.ModifyPortalContent"
-      />
-
-  <adapter factory=".file.FileSchemaAdapter"/>
-
-  <adapter
-      name="cmf.file"
-      factory=".file.FileAddView"
-      />
-
-  <adapter
-      name="cmf.image"
-      factory=".file.FileAddView"
-      />
-
-  <browser:page
-      for="..interfaces.IMutableFile"
-      layer="..interfaces.ICMFDefaultSkin"
-      name="edit.html"
-      class=".file.FileEditView"
-      permission="cmf.ModifyPortalContent"
-      />
-
-  <browser:page
-      for="*"
-      layer="..interfaces.ICMFDefaultSkin"
-      name="form_widget"
-      template="templates/form_widgets.pt"
-      permission="zope2.View"
-      />
-
-  <browser:page
-      for="*"
-      layer="..interfaces.ICMFDefaultSkin"
-      name="batch_widget"
-      template="templates/batch_widgets.pt"
-      permission="zope2.View"
-      />
-
-  <browser:page
-      for="*"
-      name="ursine_globals"
-      class=".ursa.UrsineGlobals"
-      permission="zope.Public"
-      />
-      
-  <browser:page
-      for="*"
-      layer="..interfaces.ICMFDefaultSkin"
-      name="icons.css"
-      class=".icons.View"
-      permission="zope.Public"
-      />
-
-  <browser:page
-      for="Products.CMFCore.interfaces.ISiteRoot"
-      layer="..interfaces.ICMFDefaultSkin"
-      name="login.html"
-      class=".authentication.LoginFormView"
-      permission="zope2.View"
-      />
-
-  <browser:page
-      for="Products.CMFCore.interfaces.ISiteRoot"
-      layer="..interfaces.ICMFDefaultSkin"
-      name="logout.html"
-      class=".authentication.Logout"
-      permission="zope2.View"
-      />
-
-  <browser:page
-      for="Products.CMFCore.interfaces.ISiteRoot"
-      layer="..interfaces.ICMFDefaultSkin"
-      name="mail_password.html"
-      class=".authentication.MailPasswordFormView"
-      permission="zope2.View"
-      />
-
-  <browser:page
-      for="zExceptions.Unauthorized"
-      layer="..interfaces.ICMFDefaultSkin"
-      name="index.html"
-      class=".authentication.UnauthorizedView"
-      permission="zope.Public"
-      />
-
 </configure>

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/content/__init__.py (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/__init__.py)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/content/__init__.py	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/content/__init__.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,16 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Foundation and Contributors.
+#
+# 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 content views.
+
+$Id$
+"""

Added: Products.CMFDefault/trunk/Products/CMFDefault/browser/content/configure.zcml
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/content/configure.zcml	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/content/configure.zcml	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,141 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:browser="http://namespaces.zope.org/browser">
+
+  <browser:page
+      for="Products.CMFCore.interfaces.IFolderish"
+      layer="Products.CMFDefault.interfaces.ICMFDefaultSkin"
+      name="view.html"
+      class=".folder.FolderView"
+      template="templates/folder.pt"
+      permission="zope2.View"
+      />
+
+  <utility
+      component=".folder.contents_delta_vocabulary"
+      name="cmf.contents delta vocabulary"
+      provides="zope.schema.interfaces.IVocabularyFactory"
+      />
+
+  <browser:page
+      for="Products.CMFCore.interfaces.IFolderish"
+      layer="Products.CMFDefault.interfaces.ICMFDefaultSkin"
+      name="edit.html"
+      class=".folder.ContentsView"
+      permission="cmf.ListFolderContents"
+      />
+
+  <adapter factory=".metadata.MinimalMetadataSchemaAdapter"/>
+
+  <browser:page
+      for="Products.CMFCore.interfaces.IMutableMinimalDublinCore"
+      layer="Products.CMFDefault.interfaces.ICMFDefaultSkin"
+      name="properties.html"
+      class=".metadata.MinimalMetadataEditView"
+      permission="zope2.ManageProperties"
+      />
+
+  <adapter factory=".metadata.MetadataSchemaAdapter"/>
+
+  <browser:page
+      for="Products.CMFCore.interfaces.IMutableDublinCore"
+      layer="Products.CMFDefault.interfaces.ICMFDefaultSkin"
+      name="properties.html"
+      class=".metadata.MetadataEditView"
+      permission="cmf.ModifyPortalContent"
+      />
+
+  <adapter factory=".document.DocumentSchemaAdapter"/>
+
+  <utility
+      component=".document.TextFormatVocabularyFactory"
+      name="cmf.AvailableTextFormats"
+      />
+
+  <browser:page
+      for="Products.CMFDefault.interfaces.IDocument"
+      layer="Products.CMFDefault.interfaces.ICMFDefaultSkin"
+      name="view.html"
+      class=".document.DocumentView"
+      template="templates/document.pt"
+      permission="zope2.View"
+      />
+
+  <browser:page
+      for="Products.CMFDefault.interfaces.IMutableDocument"
+      layer="Products.CMFDefault.interfaces.ICMFDefaultSkin"
+      name="edit.html"
+      class=".document.DocumentEditView"
+      permission="cmf.ModifyPortalContent"
+      />
+
+  <adapter factory=".newsitem.NewsItemSchemaAdapter"/>
+
+  <browser:page
+      for="Products.CMFDefault.interfaces.IMutableNewsItem"
+      layer="Products.CMFDefault.interfaces.ICMFDefaultSkin"
+      name="edit.html"
+      class=".newsitem.NewsItemEditView"
+      permission="cmf.ModifyPortalContent"
+      />
+
+  <adapter factory=".link.LinkSchemaAdapter"/>
+
+  <adapter
+      name="cmf.link"
+      factory=".link.LinkAddView"
+      />
+
+  <browser:page
+      for="Products.CMFDefault.interfaces.ILink"
+      layer="Products.CMFDefault.interfaces.ICMFDefaultSkin"
+      name="view.html"
+      class=".link.LinkView"
+      template="templates/link.pt"
+      permission="zope2.View"
+      />
+
+  <browser:page
+      for="Products.CMFDefault.interfaces.IMutableLink"
+      layer="Products.CMFDefault.interfaces.ICMFDefaultSkin"
+      name="edit.html"
+      class=".link.LinkEditView"
+      permission="cmf.ModifyPortalContent"
+      />
+
+  <adapter factory=".favorite.FavoriteSchemaAdapter"/>
+
+  <adapter
+      name="cmf.favorite"
+      factory=".favorite.FavoriteAddView"
+      />
+
+  <browser:page
+      for="Products.CMFDefault.interfaces.IMutableFavorite"
+      layer="Products.CMFDefault.interfaces.ICMFDefaultSkin"
+      name="edit.html"
+      class=".favorite.FavoriteEditView"
+      permission="cmf.ModifyPortalContent"
+      />
+
+  <adapter factory=".file.FileSchemaAdapter"/>
+
+  <adapter
+      name="cmf.file"
+      factory=".file.FileAddView"
+      />
+
+  <adapter
+      name="cmf.image"
+      factory=".file.FileAddView"
+      />
+
+  <browser:page
+      for="Products.CMFDefault.interfaces.IMutableFile"
+      layer="Products.CMFDefault.interfaces.ICMFDefaultSkin"
+      name="edit.html"
+      class=".file.FileEditView"
+      permission="cmf.ModifyPortalContent"
+      />
+
+</configure>

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/content/document.py (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/document.py)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/content/document.py	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/content/document.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,153 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Foundation and Contributors.
+#
+# 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 documents.
+
+$Id$
+"""
+
+from zope.component import adapts
+from zope.formlib import form
+from zope.interface import implements
+from zope.interface import Interface
+from zope.schema import ASCIILine
+from zope.schema import Bytes
+from zope.schema import Choice
+from zope.schema import Text
+from zope.schema import TextLine
+
+from Products.CMFDefault.Document import REST_AVAILABLE
+from Products.CMFDefault.formlib.form import ContentEditFormBase
+from Products.CMFDefault.formlib.schema import ProxyFieldProperty
+from Products.CMFDefault.formlib.schema import SchemaAdapterBase
+from Products.CMFDefault.formlib.vocabulary import StaticVocabulary
+from Products.CMFDefault.formlib.widgets import ChoiceRadioWidget
+from Products.CMFDefault.formlib.widgets import TextInputWidget
+from Products.CMFDefault.interfaces import IMutableDocument
+from Products.CMFDefault.utils import Message as _
+
+from Products.CMFDefault.browser.utils import decode, memoize, ViewBase
+
+available_text_formats = (
+        (u'structured-text', 'structured-text', _(u'structured-text')),
+        (u'plain', 'plain', _(u'plain text')),
+        (u'html', 'html', _(u'html')))
+
+if REST_AVAILABLE:
+    available_text_formats +=  ( ( u'restructured-text'
+                                 , 'restructured-text'
+                                 , _(u'restructured-text')
+                                 ), )
+
+TextFormatVocabularyFactory = StaticVocabulary(available_text_formats)
+
+
+class IDocumentSchema(Interface):
+
+    """Schema for document views.
+    """
+
+    safety_belt = ASCIILine(
+        required=False)
+
+    title = TextLine(
+        title=_(u'Title'),
+        readonly=True)
+
+    description = Text(
+        title=_(u'Description'),
+        readonly=True)
+
+    text_format = Choice(
+        title=_(u'Format'),
+        vocabulary='cmf.AvailableTextFormats')
+
+    upload = Bytes(
+        title=_(u'Upload'),
+        required=False)
+
+    text = Text(
+        title=_(u'Body'),
+        required=False,
+        missing_value=u'')
+
+
+class DocumentSchemaAdapter(SchemaAdapterBase):
+
+    """Adapter for IMutableDocument.
+    """
+
+    adapts(IMutableDocument)
+    implements(IDocumentSchema)
+
+    safety_belt = ProxyFieldProperty(IDocumentSchema['safety_belt'],
+                                     '_safety_belt')
+    title = ProxyFieldProperty(IDocumentSchema['title'], 'Title')
+    description = ProxyFieldProperty(IDocumentSchema['description'],
+                                     'Description')
+    text_format = ProxyFieldProperty(IDocumentSchema['text_format'])
+    upload = None
+    text = ProxyFieldProperty(IDocumentSchema['text'],
+                              'EditableBody', '_edit')
+
+
+class DocumentView(ViewBase):
+
+    """View for IDocument.
+    """
+
+    # interface
+
+    @memoize
+    @decode
+    def text(self):
+        return self.context.CookedBody()
+
+
+class DocumentEditView(ContentEditFormBase):
+
+    """Edit view for IMutableDocument.
+    """
+
+    form_fields = form.FormFields(IDocumentSchema)
+    form_fields['text_format'].custom_widget = ChoiceRadioWidget
+    form_fields['text'].custom_widget = TextInputWidget
+
+    def setUpWidgets(self, ignore_request=False):
+        super(DocumentEditView,
+              self).setUpWidgets(ignore_request=ignore_request)
+        self.widgets['safety_belt'].hide = True
+        self.widgets['description'].height = 3
+        self.widgets['text_format'].orientation = 'horizontal'
+        self.widgets['upload'].displayWidth = 60
+        self.widgets['text'].height = 20
+
+    def _handle_success(self, action, data):
+        body = data.get('upload')
+        if body:
+            data['text'] = body.decode(self._getDefaultCharset())
+        changed = super(DocumentEditView, self)._handle_success(action, data)
+        if changed:
+            self.context.updateSafetyBelt(data.get('safety_belt'))
+        return changed
+
+    def handle_validate(self, action, data):
+        errors = super(DocumentEditView, self).handle_validate(action, data)
+        if errors:
+            return errors
+        safety_belt = self.request.form['form.safety_belt']
+        if not self.context.isValidSafetyBelt(safety_belt):
+            return (_(u'Intervening changes from elsewhere detected. Please '
+                      u'refetch the document and reapply your changes.'),)
+        # make sure applyChanges doesn't try to update safety_belt
+        self.request.form['form.safety_belt'] = self.context._safety_belt
+        return None

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/content/favorite.py (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/favorite.py)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/content/favorite.py	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/content/favorite.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,152 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Foundation and Contributors.
+#
+# 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 favorites.
+
+$Id$
+"""
+
+import urlparse
+
+from zope.app.form.browser import BytesWidget
+from zope.component import adapts
+from zope.formlib import form
+from zope.interface import implements
+from zope.interface import Interface
+from zope.schema import ASCIILine
+from zope.schema import BytesLine
+from zope.schema import Text
+from zope.schema import TextLine
+
+from Products.CMFCore.utils import getToolByName
+from Products.CMFDefault.formlib.form import ContentAddFormBase
+from Products.CMFDefault.formlib.form import ContentEditFormBase
+from Products.CMFDefault.formlib.schema import ProxyFieldProperty
+from Products.CMFDefault.formlib.schema import SchemaAdapterBase
+from Products.CMFDefault.formlib.widgets import IDInputWidget
+from Products.CMFDefault.interfaces import IMutableFavorite
+from Products.CMFDefault.utils import Message as _
+
+
+class IFavoriteSchema(Interface):
+
+    title = TextLine(
+        title=_(u'Title'),
+        required=False,
+        missing_value=u'')
+
+    language = TextLine(
+        title=_(u'Language'),
+        required=False,
+        missing_value=u'',
+        max_length=2)
+
+    description = Text(
+        title=_(u'Description'),
+        required=False,
+        missing_value=u'')
+
+    remote_url = BytesLine(
+        title=_(u'URL'),
+        description=_(u'URL relative to the site root.'),
+        required=False,
+        missing_value=u'')
+
+
+class FavoriteSchemaAdapter(SchemaAdapterBase):
+
+    adapts(IMutableFavorite)
+    implements(IFavoriteSchema)
+
+    _remote_url = ProxyFieldProperty(IFavoriteSchema['remote_url'])
+
+    def _getRemoteURL(self):
+        return self._remote_url
+
+    def _setRemoteURL(self, value):
+        self._remote_url = value
+        self.context.remote_uid = self.context._getUidByUrl()
+
+    title = ProxyFieldProperty(IFavoriteSchema['title'], 'Title', 'setTitle')
+    language = ProxyFieldProperty(IFavoriteSchema['language'],
+                                  'Language', 'setLanguage')
+    description = ProxyFieldProperty(IFavoriteSchema['description'],
+                                     'Description', 'setDescription')
+    remote_url = property(_getRemoteURL, _setRemoteURL)
+
+
+class FavoriteURIWidget(BytesWidget):
+
+    """Custom widget for remote_url.
+    """
+
+    def _toFieldValue(self, input):
+        value = super(FavoriteURIWidget, self)._toFieldValue(input)
+        if not value:
+            return value
+        # strip off scheme and machine from URL if present
+        tokens = urlparse.urlparse(value, 'http')
+        if tokens[1]:
+            # There is a nethost, remove it
+            tokens = ('', '') + tokens[2:]
+            value = urlparse.urlunparse(tokens)
+        # if URL begins with site URL, remove site URL
+        context = getattr(self.context.context, 'context',
+                          self.context.context)
+        portal_url = getToolByName(context, 'portal_url').getPortalPath()
+        if value.startswith(portal_url):
+            value = value[len(portal_url):]
+        # if site is still absolute, make it relative
+        if value[:1]=='/':
+            value = value[1:]
+        return value
+
+
+class FavoriteAddView(ContentAddFormBase):
+
+    """Add view for IMutableFavorite.
+    """
+
+    form_fields = (
+        form.FormFields(ASCIILine(__name__='id', title=_(u'ID'))) +
+        form.FormFields(IFavoriteSchema).omit('language')
+        )
+    form_fields['id'].custom_widget = IDInputWidget
+    form_fields['remote_url'].custom_widget = FavoriteURIWidget
+
+    def setUpWidgets(self, ignore_request=False):
+        super(FavoriteAddView,
+              self).setUpWidgets(ignore_request=ignore_request)
+        self.widgets['description'].height = 3
+
+    def create(self, data):
+        obj = super(FavoriteAddView, self).create(dict(id=data['id']))
+        adapted = IFavoriteSchema(obj)
+        adapted.title = data['title']
+        adapted.language = u''
+        adapted.description = data['description']
+        adapted.remote_url = data['remote_url']
+        return obj
+
+
+class FavoriteEditView(ContentEditFormBase):
+
+    """Edit view for IMutableFavorite.
+    """
+
+    form_fields = form.FormFields(IFavoriteSchema).omit('language')
+    form_fields['remote_url'].custom_widget = FavoriteURIWidget
+
+    def setUpWidgets(self, ignore_request=False):
+        super(FavoriteEditView,
+              self).setUpWidgets(ignore_request=ignore_request)
+        self.widgets['description'].height = 3

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/content/file.py (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/file.py)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/content/file.py	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/content/file.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,126 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+#
+# 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 files.
+
+$Id$
+"""
+
+from zope.component import adapts
+from zope.formlib import form
+from zope.interface import implements
+from zope.interface import Interface
+from zope.schema import ASCIILine
+from zope.schema import Text
+from zope.schema import TextLine
+
+from Products.CMFDefault.formlib.form import ContentAddFormBase
+from Products.CMFDefault.formlib.form import ContentEditFormBase
+from Products.CMFDefault.formlib.schema import FileUpload
+from Products.CMFDefault.formlib.schema import ProxyFieldProperty
+from Products.CMFDefault.formlib.schema import SchemaAdapterBase
+from Products.CMFDefault.interfaces import IMutableFile
+from Products.CMFDefault.utils import Message as _
+
+
+class IFileSchema(Interface):
+
+    """Schema for file views.
+    """
+
+    title = TextLine(
+        title=_(u'Title'),
+        required=False,
+        missing_value=u'')
+
+    language = TextLine(
+        title=_(u'Language'),
+        required=False,
+        missing_value=u'',
+        max_length=2)
+
+    description = Text(
+        title=_(u'Description'),
+        required=False,
+        missing_value=u'')
+
+    format = ASCIILine(
+        title=_(u'Content type'),
+        readonly=True)
+
+    file = FileUpload(
+        title=_(u'Upload'),
+        required=False)
+
+
+class FileSchemaAdapter(SchemaAdapterBase):
+
+    """Adapter for IMutableFile.
+    """
+
+    adapts(IMutableFile)
+    implements(IFileSchema)
+
+    def _getFile(self):
+        return ''
+
+    def _setFile(self, value):
+        self.context.manage_upload(value)
+
+    title = ProxyFieldProperty(IFileSchema['title'], 'Title', 'setTitle')
+    language = ProxyFieldProperty(IFileSchema['language'],
+                                  'Language', 'setLanguage')
+    description = ProxyFieldProperty(IFileSchema['description'],
+                                     'Description', 'setDescription')
+    format = ProxyFieldProperty(IFileSchema['format'], 'Format')
+    file = property(_getFile, _setFile)
+
+
+class FileAddView(ContentAddFormBase):
+
+    """Add view for IMutableFile.
+    """
+
+    form_fields = (
+        form.FormFields(IFileSchema).select('title', 'description') +
+        form.FormFields(FileUpload(__name__='file', title=_(u'Upload')))
+        )
+
+    def setUpWidgets(self, ignore_request=False):
+        super(FileAddView,
+              self).setUpWidgets(ignore_request=ignore_request)
+        self.widgets['description'].height = 3
+        self.widgets['file'].displayWidth = 60
+
+    def create(self, data):
+        obj = super(FileAddView,
+                    self).create(dict(id=data['file'].filename))
+        adapted = IFileSchema(obj)
+        adapted.title = data['title']
+        adapted.language = u''
+        adapted.description = data['description']
+        adapted.file = data['file']
+        return obj
+
+
+class FileEditView(ContentEditFormBase):
+
+    """Edit view for IMutableFile.
+    """
+
+    form_fields = form.FormFields(IFileSchema).omit('language')
+
+    def setUpWidgets(self, ignore_request=False):
+        super(FileEditView,
+              self).setUpWidgets(ignore_request=ignore_request)
+        self.widgets['description'].height = 3
+        self.widgets['file'].displayWidth = 60

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/content/folder.py (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/folder.py)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/content/folder.py	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/content/folder.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,614 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Foundation and Contributors.
+#
+# 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$
+"""
+
+import urllib
+
+from five.formlib.formbase import PageForm
+from zope.formlib import form
+from zope import schema
+from zope.schema.vocabulary import SimpleTerm
+from zope.schema.vocabulary import SimpleVocabulary
+from zope.sequencesort.ssort import sort
+from ZTUtils import Batch
+from ZTUtils import LazyFilter
+
+from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
+
+from Products.CMFCore.interfaces import IDynamicType
+
+from Products.CMFDefault.exceptions import CopyError
+from Products.CMFDefault.exceptions import zExceptions_Unauthorized
+from Products.CMFDefault.formlib.form import _EditFormMixin
+from Products.CMFDefault.permissions import ListFolderContents
+from Products.CMFDefault.permissions import ManageProperties
+from Products.CMFDefault.utils import Message as _
+
+from Products.CMFDefault.browser.utils import decode, memoize, ViewBase
+from interfaces import IDeltaItem, IFolderItem, IHidden
+
+def contents_delta_vocabulary(context):
+    """Vocabulary for the pulldown for moving objects up and down.
+    """
+    length = len(context.contentIds())
+    deltas = [SimpleTerm(i, str(i), str(i)) 
+            for i in range(1, min(5, length)) + range(5, length, 5)]
+    return SimpleVocabulary(deltas)
+
+
+class BatchViewBase(ViewBase):
+    """ Helper class for creating batch-based views.
+    """
+
+    _BATCH_SIZE = 25
+    hidden_fields = form.FormFields(IHidden)
+    prefix = ''
+    
+    @memoize
+    def setUpWidgets(self, ignore_request=False):
+        self.hidden_widgets = form.setUpWidgets(self.hidden_fields, self.prefix, 
+                                                self.context, self.request, 
+                                                ignore_request=ignore_request)
+
+    @memoize
+    def _getBatchStart(self):
+        return self._getHiddenVars().get('b_start', 0)
+
+    @memoize
+    def _getBatchObj(self):
+        b_start = self._getBatchStart()
+        items = self._get_items()
+        return Batch(items, self._BATCH_SIZE, b_start, orphan=0)
+
+    @memoize
+    def _getHiddenVars(self):
+        data = {}
+        if hasattr(self, 'hidden_widgets'):
+            form.getWidgetsData(self.hidden_widgets, self.prefix, data)
+        return data
+
+    @memoize
+    def _getNavigationVars(self):
+        return self._getHiddenVars()
+
+    @memoize
+    def expand_prefix(self, key,):
+        """Return a form specific query key for use in GET strings"""
+        return "%s%s" % (form.expandPrefix(self.prefix), key)
+
+    @memoize
+    def _getNavigationURL(self, b_start=None):
+        target = self._getViewURL()
+        kw = self._getNavigationVars().copy()
+        if 'bstart' not in kw:
+            kw['b_start'] = b_start
+
+        for k, v in kw.items():
+            if not v or k == 'portal_status_message':
+                pass
+            else:
+                new_key = self.expand_prefix(k)
+                kw[new_key] = v
+            del kw[k]
+
+        query = kw and ('?%s' % urllib.urlencode(kw)) or ''
+
+        return u'%s%s' % (target, query)
+
+    # interface
+
+    @memoize
+    @decode
+    def listBatchItems(self):
+        batch_obj = self._getBatchObj()
+
+        items = []
+        for item in batch_obj:
+            item_description = item.Description()
+            item_title = item.Title()
+            item_type = remote_type = item.Type()
+            if item_type == 'Favorite':
+                try:
+                    item = item.getObject()
+                    item_description = item_description or item.Description()
+                    item_title = item_title or item.Title()
+                    remote_type = item.Type()
+                except KeyError:
+                    pass
+            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.getIconURL(),
+                          '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}
+
+    def page_range(self):
+        """Create a range of up to ten pages around the current page"""
+        pages = [(idx + 1, b_start) for idx, b_start in enumerate(
+                    range(0, 
+                        self._getBatchObj().sequence_length, 
+                        self._BATCH_SIZE)
+                    )
+                ]
+        range_start = max(self.page_number() - 5, 0)
+        range_stop = min(max(self.page_number() + 5, 10), len(pages))
+        _page_range = []
+        for page, b_start in pages[range_start:range_stop]:
+            _page_range.append(
+                {'number':page, 
+                 'url':self._getNavigationURL(b_start)
+                }
+                              )
+        return _page_range
+
+    @memoize
+    def page_count(self):
+        """Count total number of pages in the batch"""
+        batch_obj = self._getBatchObj()
+        count = (batch_obj.sequence_length - 1) / self._BATCH_SIZE + 1
+        return count
+
+    @memoize
+    def page_number(self):
+        """Get the number of the current page in the batch"""
+        return (self._getBatchStart() / self._BATCH_SIZE) + 1
+
+    @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')       
+
+
+class ContentsView(BatchViewBase, _EditFormMixin, PageForm):
+    """Folder contents view"""
+    
+    template = ViewPageTemplateFile('templates/folder_contents.pt')
+    prefix = 'form'
+    
+    object_actions = form.Actions(
+        form.Action(
+            name='rename',
+            label=_(u'Rename'),
+            validator='validate_items',
+            condition='has_subobjects',
+            success='handle_rename'),
+        form.Action(
+            name='cut',
+            label=_(u'Cut'),
+            condition='has_subobjects',
+            validator='validate_items',
+            success='handle_cut'),
+        form.Action(
+            name='copy',
+            label=_(u'Copy'),
+            condition='has_subobjects',
+            validator='validate_items',
+            success='handle_copy'),
+        form.Action(
+            name='paste',
+            label=_(u'Paste'),
+            condition='check_clipboard_data',
+            success='handle_paste'),
+        form.Action(
+            name='delete',
+            label=_(u'Delete'),
+            condition='has_subobjects',
+            validator='validate_items',
+            success='handle_delete')
+            )
+            
+    delta_actions = form.Actions(
+        form.Action(
+            name='up',
+            label=_(u'Up'),
+            condition='is_orderable',
+            validator='validate_items',
+            success='handle_up'),
+        form.Action(
+            name='down',
+            label=_(u'Down'),
+            condition='is_orderable',
+            validator='validate_items',
+            success='handle_down')
+            )
+            
+    absolute_actions = form.Actions(
+        form.Action(
+            name='top',
+            label=_(u'Top'),
+            condition='is_orderable',
+            validator='validate_items',
+            success='handle_top'),
+        form.Action(
+            name='bottom',
+            label=_(u'Bottom'),
+            condition='is_orderable',
+            validator='validate_items',
+            success='handle_bottom')
+            )
+
+    sort_actions = form.Actions(
+        form.Action(
+            name='sort_order',
+            label=_(u'Set as Default Sort'),
+            condition='can_sort_be_changed',
+            validator='validate_items',
+            success='handle_top')
+            )
+            
+    actions = object_actions + delta_actions + absolute_actions + sort_actions
+    errors = ()
+    
+    def __init__(self, *args, **kw):
+        super(ContentsView, self).__init__(*args, **kw)
+        self.form_fields = form.FormFields()
+        self.delta_field = form.FormFields(IDeltaItem)
+        self.contents = self.context.contentValues()        
+                
+    def content_fields(self):
+        """Create content field objects only for batched items"""
+        for item in self._getBatchObj():
+            for name, field in schema.getFieldsInOrder(IFolderItem):
+                field = form.FormField(field, name, item.id)
+                self.form_fields += form.FormFields(field)
+
+    @memoize
+    @decode
+    def up_info(self):
+        """Link to the contens view of the parent object"""
+        up_obj = self.context.aq_inner.aq_parent
+        mtool = self._getTool('portal_membership')
+        allowed = mtool.checkPermission(ListFolderContents, up_obj)
+        if allowed:
+            if IDynamicType.providedBy(up_obj):
+                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 {}
+        
+    def setUpWidgets(self, ignore_request=False):
+        """Create widgets for the folder contents."""
+        super(ContentsView, self).setUpWidgets(ignore_request)
+        data = {}
+        self.content_fields()
+        for i in self._getBatchObj():
+            data['%s.name' % i.id] = i.getId()
+        self.widgets = form.setUpDataWidgets(
+                self.form_fields, self.prefix, self.context,
+                self.request, data=data, ignore_request=ignore_request)
+        self.widgets += form.setUpWidgets(
+                self.delta_field, self.prefix, self.context,
+                self.request, ignore_request=ignore_request)
+                
+    @memoize
+    def _get_sorting(self):
+        """How should the contents be sorted"""
+        data = self._getHiddenVars()
+        key = data.get('sort_key')
+        if key:
+            return (key, data.get('reverse', 0))
+        else:
+            return self.context.getDefaultSorting()
+            
+    @memoize
+    def _is_default_sorting(self,):
+        return self._get_sorting() == self.context.getDefaultSorting()
+        
+    @memoize
+    def column_headings(self):
+        key, reverse = self._get_sorting()
+        columns = ( {'sort_key': 'Type',
+                     'title': _(u'Type'),
+                     'colspan': '2'}
+                  , {'sort_key': 'getId',
+                     'title': _(u'Name')}
+                  , {'sort_key': 'modified',
+                     'title': _(u'Last Modified')}
+                  , {'sort_key': 'position',
+                     'title': _(u'Position')}
+                  )
+        for column in columns:
+            paras = {'form.sort_key':column['sort_key']}
+            if key == column['sort_key'] \
+            and not reverse and key != 'position':
+                paras['form.reverse'] = 1
+            query = urllib.urlencode(paras)
+            column['url'] = '%s?%s' % (self._getViewURL(), query)
+        return tuple(columns)
+        
+    @memoize
+    def _get_items(self):
+        key, reverse = self._get_sorting()
+        items = self.contents
+        return sort(items, ((key, 'cmp', reverse and 'desc' or 'asc'),))
+
+    @memoize
+    def listBatchItems(self):
+        """Return the widgets for the form in the interface field order"""
+        batch_obj = self._getBatchObj()
+        b_start = self._getBatchStart()
+        key, reverse = self._get_sorting()
+        fields = []
+
+        for idx, item in enumerate(batch_obj):
+            field = {'ModificationDate':item.ModificationDate()}
+            field['select'] = self.widgets['%s.select' % item.getId()]
+            field['name'] = self.widgets['%s.name' % item.getId()]
+            field['url'] = item.absolute_url()
+            field['title'] = item.TitleOrId()
+            field['icon'] = item.icon
+            field['position'] = (key == 'position') \
+                                and str(b_start + idx + 1) \
+                                or '...'
+            field['type'] = item.Type() or None
+            fields.append(field.copy())
+        return fields
+                
+    def _get_ids(self, data):
+        """Identify objects that have been selected"""
+        ids = [k[:-7] for k, v in data.items()
+                 if v is True and k.endswith('.select')]
+        return ids
+
+    #Action conditions
+    @memoize
+    def has_subobjects(self, action=None):
+        """Return false if the user cannot rename subobjects"""
+        return bool(self.contents)
+    
+    @memoize
+    def check_clipboard_data(self, action=None):
+        """Any data in the clipboard"""
+        return bool(self.context.cb_dataValid())
+    
+    @memoize
+    def can_sort_be_changed(self, action=None):
+        """Returns true if the default sort key may be changed 
+            may be sorted for display"""
+        items_move_allowed = self._checkPermission(ManageProperties)
+        return items_move_allowed and not \
+            self._get_sorting() == self.context.getDefaultSorting()
+
+    @memoize
+    def is_orderable(self, action=None):
+        """Returns true if the displayed contents can be
+            reorded."""
+        (key, reverse) = self._get_sorting()        
+        return key == 'position' and len(self.contents) > 1
+    
+    #Action validators
+    def validate_items(self, action=None, data=None):
+        """Check whether any items have been selected for 
+        the requested action."""
+        super(ContentsView, self).validate(action, data)
+        if data is None or data == {}:
+            return [_(u"Please select one or more items first.")]
+        else:
+            return []
+            
+    #Action handlers
+    def handle_rename(self, action, data):
+        """Redirect to rename view passing the ids of objects to be renamed"""
+        # currently redirects to a PythonScript
+        # should be replaced with a dedicated form
+        self.request.form['ids'] = self._get_ids(data)
+        keys = ",".join(self._getHiddenVars().keys() + ['ids'])
+        # keys = 'b_start, ids, key, reverse'
+        return self._setRedirect('portal_types', 'object/rename_items', keys)
+        
+    def handle_cut(self, action, data):
+        """Cut the selected objects and put them in the clipboard"""
+        ids = self._get_ids(data)
+        try:
+            self.context.manage_cutObjects(ids, self.request)
+            if len(ids) == 1:
+                self.status = _(u'Item cut.')
+            else:
+                self.status = _(u'Items cut.')
+        except CopyError:
+            self.status = _(u'CopyError: Cut failed.')
+        except zExceptions_Unauthorized:
+            self.status = _(u'Unauthorized: Cut failed.')
+        return self._setRedirect('portal_types', 'object/new_contents')    
+
+    def handle_copy(self, action, data):
+        """Copy the selected objects to the clipboard"""
+        ids = self._get_ids(data)
+        try:
+            self.context.manage_copyObjects(ids, self.request)
+            if len(ids) == 1:
+                self.status = _(u'Item copied.')
+            else:
+                self.status = _(u'Items copied.')
+        except CopyError:
+            self.status = _(u'CopyError: Copy failed.')
+        return self._setRedirect('portal_types', 'object/new_contents')
+    
+    def handle_paste(self, action, data):
+        """Paste the objects from the clipboard into the folder"""
+        try:
+            result = self.context.manage_pasteObjects(self.request['__cp'])
+            if len(result) == 1:
+                self.status = _(u'Item pasted.')
+            else:
+                self.status = _(u'Items pasted.')
+        except CopyError, error:
+            self.status = _(u'CopyError: Paste failed.')
+            self.request['RESPONSE'].expireCookie('__cp', 
+                    path='%s' % (self.request['BASEPATH1'] or "/"))
+
+        except zExceptions_Unauthorized:
+            self.status = _(u'Unauthorized: Paste failed.')
+        return self._setRedirect('portal_types', 'object/new_contents')
+
+    def handle_delete(self, action, data):
+        """Delete the selected objects"""
+        ids = self._get_ids(data)
+        self.context.manage_delObjects(list(ids))
+        if len(ids) == 1:
+            self.status = _(u'Item deleted.')
+        else:
+            self.status = _(u'Items deleted.')
+        return self._setRedirect('portal_types', 'object/new_contents')
+    
+    def handle_up(self, action, data):
+        """Move the selected objects up the selected number of places"""
+        ids = self._get_ids(data)
+        delta = data.get('delta', 1)
+        subset_ids = [obj.getId()
+                       for obj in self.context.listFolderContents()]
+        try:
+            attempt = self.context.moveObjectsUp(ids, delta,
+                                                 subset_ids=subset_ids)
+            if attempt == 1:
+                self.status = _(u'Item moved up.')
+            elif attempt > 1:
+                self.status = _(u'Items moved up.')
+            else:
+                self.status = _(u'Nothing to change.')
+        except ValueError:
+            self.status = _(u'ValueError: Move failed.')
+        return self._setRedirect('portal_types', 'object/new_contents')
+
+    def handle_down(self, action, data):
+        """Move the selected objects down the selected number of places"""
+        ids = self._get_ids(data)
+        delta = data.get('delta', 1)
+        subset_ids = [obj.getId()
+                       for obj in self.context.listFolderContents()]
+        try:
+            attempt = self.context.moveObjectsDown(ids, delta,
+                                                 subset_ids=subset_ids)
+            if attempt == 1:
+                self.status = _(u'Item moved down.')
+            elif attempt > 1:
+                self.status = _(u'Items moved down.')
+            else:
+                self.status = _(u'Nothing to change.')
+        except ValueError:
+            self.status = _(u'ValueError: Move failed.')
+        return self._setRedirect('portal_types', 'object/new_contents')
+            
+    def handle_top(self, action, data):
+        """Move the selected objects to the top of the page"""
+        ids = self._get_ids(data)
+        subset_ids = [obj.getId()
+                       for obj in self.context.listFolderContents()]
+        try:
+            attempt = self.context.moveObjectsToTop(ids,
+                                                    subset_ids=subset_ids)
+            if attempt == 1:
+                self.status = _(u'Item moved to top.')
+            elif attempt > 1:
+                self.status = _(u'Items moved to top.')
+            else:
+                self.status = _(u'Nothing to change.')
+        except ValueError:
+            self.status = _(u'ValueError: Move failed.')
+        return self._setRedirect('portal_types', 'object/new_contents')
+
+    def handle_bottom(self, action, data):
+        """Move the selected objects to the bottom of the page"""
+        ids = self._get_ids(data)
+        subset_ids = [obj.getId()
+                       for obj in self.context.listFolderContents()]
+        try:
+            attempt = self.context.moveObjectsToBottom(ids,
+                                                       subset_ids=subset_ids)
+            if attempt == 1:
+                self.status = _(u'Item moved to bottom.')
+            elif attempt > 1:
+                self.status = _(u'Items moved to bottom.')
+            else:
+                self.status = _(u'Nothing to change.')
+        except ValueError:
+            self.status = _(u'ValueError: Move failed.')
+        return self._setRedirect('portal_types', 'object/new_contents')
+        
+    def handle_sort_order(self, action, data):
+        """Set the sort options for the folder."""
+        key = data['position']
+        reverse = data.get('reverse', 0)
+        self.context.setDefaultSorting(key, reverse)
+        self.status = _(u"Sort order changed")
+        return self._setRedirect('portal_types', 'object/new_contents')
+
+
+class FolderView(BatchViewBase):
+
+    """View for IFolderish.
+    """
+
+    @memoize
+    def _get_items(self):
+        (key, reverse) = self.context.getDefaultSorting()
+        items = self.context.contentValues()
+        items = sort(items, ((key, 'cmp', reverse and 'desc' or 'asc'),))
+        return LazyFilter(items, skip='View')
+
+    @memoize
+    def has_local(self):
+        return 'local_pt' in self.context.objectIds()

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/content/interfaces.py (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/interfaces.py)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/content/interfaces.py	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/content/interfaces.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,61 @@
+##############################################################################
+#
+# Copyright (c) 2009 Zope Foundation and Contributors.
+#
+# 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 interfaces.
+
+$Id$
+"""
+
+from zope.interface import Interface
+from zope.schema import Bool
+from zope.schema import Choice
+from zope.schema import Int
+from zope.schema import TextLine
+
+
+class IFolderItem(Interface):
+    """Schema for folderish objects contents."""
+    
+    select = Bool(
+        required=False)
+        
+    name = TextLine(
+        title=u"Name",
+        required=False,
+        readonly=True)
+
+
+class IDeltaItem(Interface):
+    """Schema for delta"""    
+    delta = Choice(
+        title=u"By",
+        description=u"Move an object up or down the chosen number of places.",
+        required=False,
+        vocabulary=u'cmf.contents delta vocabulary',
+        default=1)
+
+        
+class IHidden(Interface):
+    """Schema for hidden items"""
+    
+    b_start = Int(
+        title=u"Batch start",
+        required=False,
+        default=0)
+        
+    sort_key = TextLine(
+        title=u"Sort key",
+        required=False)
+        
+    reverse = Int(
+        title=u"Reverse sort order",
+        required=False)

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/content/link.py (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/link.py)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/content/link.py	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/content/link.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,153 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Foundation and Contributors.
+#
+# 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 links.
+
+$Id$
+"""
+
+import urlparse
+
+from zope.app.form.browser import BytesWidget
+from zope.component import adapts
+from zope.formlib import form
+from zope.interface import implements
+from zope.interface import Interface
+from zope.schema import ASCIILine
+from zope.schema import BytesLine
+from zope.schema import Text
+from zope.schema import TextLine
+
+from Products.CMFDefault.formlib.form import ContentAddFormBase
+from Products.CMFDefault.formlib.form import ContentEditFormBase
+from Products.CMFDefault.formlib.schema import ProxyFieldProperty
+from Products.CMFDefault.formlib.schema import SchemaAdapterBase
+from Products.CMFDefault.formlib.widgets import IDInputWidget
+from Products.CMFDefault.interfaces import IMutableLink
+from Products.CMFDefault.utils import Message as _
+
+from Products.CMFDefault.browser.utils import decode, memoize, ViewBase
+
+
+class ILinkSchema(Interface):
+
+    title = TextLine(
+        title=_(u'Title'),
+        required=False,
+        missing_value=u'')
+
+    language = TextLine(
+        title=_(u'Language'),
+        required=False,
+        missing_value=u'',
+        max_length=2)
+
+    description = Text(
+        title=_(u'Description'),
+        required=False,
+        missing_value=u'')
+
+    remote_url = BytesLine(
+        title=_(u'URL'),
+        required=False,
+        missing_value=u'')
+
+
+class LinkSchemaAdapter(SchemaAdapterBase):
+
+    adapts(IMutableLink)
+    implements(ILinkSchema)
+
+    title = ProxyFieldProperty(ILinkSchema['title'], 'Title', 'setTitle')
+    language = ProxyFieldProperty(ILinkSchema['language'],
+                                  'Language', 'setLanguage')
+    description = ProxyFieldProperty(ILinkSchema['description'],
+                                     'Description', 'setDescription')
+    remote_url = ProxyFieldProperty(ILinkSchema['remote_url'])
+
+
+class LinkView(ViewBase):
+
+    """View for ILink.
+    """
+
+    # interface
+
+    @memoize
+    @decode
+    def url(self):
+        return self.context.getRemoteUrl()
+
+
+class LinkURIWidget(BytesWidget):
+
+    """Custom widget for remote_url.
+    """
+
+    def _toFieldValue(self, input):
+        value = super(LinkURIWidget, self)._toFieldValue(input)
+        if not value:
+            return value
+        tokens = urlparse.urlparse(value, 'http')
+        if tokens[0] == 'http':
+            if tokens[1]:
+                # We have a nethost. All is well.
+                return urlparse.urlunparse(tokens)
+            elif tokens[2:] == ('', '', '', ''):
+                # Empty URL
+                return u''
+            else:
+                # Relative URL, keep it that way, without http:
+                tokens = ('', '') + tokens[2:]
+                return urlparse.urlunparse(tokens)
+        else:
+            # Other scheme, keep original
+            return urlparse.urlunparse(tokens)
+
+
+class LinkAddView(ContentAddFormBase):
+
+    """Add view for IMutableLink.
+    """
+
+    form_fields = (
+        form.FormFields(ASCIILine(__name__='id', title=_(u'ID'))) +
+        form.FormFields(ILinkSchema).omit('language')
+        )
+    form_fields['id'].custom_widget = IDInputWidget
+    form_fields['remote_url'].custom_widget = LinkURIWidget
+
+    def setUpWidgets(self, ignore_request=False):
+        super(LinkAddView, self).setUpWidgets(ignore_request=ignore_request)
+        self.widgets['description'].height = 3
+
+    def create(self, data):
+        obj = super(LinkAddView, self).create(dict(id=data['id']))
+        adapted = ILinkSchema(obj)
+        adapted.title = data['title']
+        adapted.language = u''
+        adapted.description = data['description']
+        adapted.remote_url = data['remote_url']
+        return obj
+
+
+class LinkEditView(ContentEditFormBase):
+
+    """Edit view for IMutableLink.
+    """
+
+    form_fields = form.FormFields(ILinkSchema).omit('language')
+    form_fields['remote_url'].custom_widget = LinkURIWidget
+
+    def setUpWidgets(self, ignore_request=False):
+        super(LinkEditView, self).setUpWidgets(ignore_request=ignore_request)
+        self.widgets['description'].height = 3

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/content/metadata.py (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/metadata.py)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/content/metadata.py	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/content/metadata.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,268 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Foundation and Contributors.
+#
+# 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 metadata.
+
+$Id$
+"""
+
+from Acquisition import aq_self
+from zope.app.form.browser import DatetimeI18nWidget
+from zope.component import adapts
+from zope.component import getUtility
+from zope.formlib import form
+from zope.interface import implements
+from zope.interface import Interface
+from zope.schema import Choice
+from zope.schema import Datetime
+from zope.schema import Set
+from zope.schema import Text
+from zope.schema import TextLine
+from zope.schema import Tuple
+from zope.schema import URI
+
+from Products.CMFCore.interfaces import IDiscussionTool
+from Products.CMFCore.interfaces import IMutableDublinCore
+from Products.CMFCore.interfaces import IMutableMinimalDublinCore
+from Products.CMFDefault.formlib.form import ContentEditFormBase
+from Products.CMFDefault.formlib.schema import ProxyFieldProperty
+from Products.CMFDefault.formlib.schema import SchemaAdapterBase
+from Products.CMFDefault.formlib.vocabulary import SimpleVocabulary
+from Products.CMFDefault.formlib.widgets import SubjectInputWidget
+from Products.CMFDefault.formlib.widgets import TupleInputWidget
+from Products.CMFDefault.utils import Message as _
+
+available_settings = [
+        ('off', False, _(u'Off')),
+        ('on', True, _(u'On')) ]
+
+
+class IMinimalMetadataSchema(Interface):
+
+    """Schema for minimal metadata views.
+    """
+
+    title = TextLine(
+        title=_(u'Title'),
+        required=False,
+        missing_value=u'')
+
+    description = Text(
+        title=_(u'Description'),
+        required=False,
+        missing_value=u'')
+
+
+class IMetadataSchema(Interface):
+
+    """Schema for metadata views.
+    """
+
+    allow_discussion = Choice(
+        title=_(u'Enable Discussion?'),
+        required=False,
+        vocabulary=SimpleVocabulary.fromTitleItems(available_settings))
+
+    identifier = URI(
+        title=_(u'Identifier'),
+        readonly=True)
+
+    title = TextLine(
+        title=_(u'Title'),
+        required=False,
+        missing_value=u'')
+
+    description = Text(
+        title=_(u'Description'),
+        required=False,
+        missing_value=u'')
+
+    subject = Set(
+        title=_(u'Subject'),
+        required=False,
+        missing_value=set(),
+        value_type=TextLine())
+
+    contributors = Tuple(
+        title=_(u'Contributors'),
+        required=False,
+        missing_value=(),
+        value_type=TextLine())
+
+    created = Datetime(
+        title=_(u'Creation Date'),
+        readonly=True)
+
+    modified = Datetime(
+        title=_(u'Last Modified Date'),
+        readonly=True)
+
+    effective = Datetime(
+        title=_(u'Effective Date'),
+        required=False
+        )
+
+    expires = Datetime(
+        title=_(u'Expiration Date'),
+        required=False
+        )
+
+    format = TextLine(
+        title=_(u'Format'),
+        required=False,
+        missing_value=u'')
+
+    language = TextLine(
+        title=_(u'Language'),
+        required=False,
+        missing_value=u'')
+
+    rights = TextLine(
+        title=_(u'Rights'),
+        required=False,
+        missing_value=u'')
+
+
+class MinimalMetadataSchemaAdapter(SchemaAdapterBase):
+
+    """Adapter for IMutableMinimalDublinCore.
+    """
+
+    adapts(IMutableMinimalDublinCore)
+    implements(IMinimalMetadataSchema)
+
+    title = ProxyFieldProperty(IMetadataSchema['title'], 'Title', 'setTitle')
+    description = ProxyFieldProperty(IMetadataSchema['description'],
+                                     'Description', 'setDescription')
+
+
+class MetadataSchemaAdapter(SchemaAdapterBase):
+
+    """Adapter for IMutableDublinCore.
+    """
+
+    adapts(IMutableDublinCore)
+    implements(IMetadataSchema)
+
+    _effective = ProxyFieldProperty(IMetadataSchema['effective'],
+                                    'effective', 'setEffectiveDate')
+    _expires = ProxyFieldProperty(IMetadataSchema['expires'],
+                                  'expires', 'setExpirationDate')
+
+    def _getEffective(self):
+        if self.context.EffectiveDate() == 'None':
+            return None
+        return self._effective
+
+    def _getExpires(self):
+        if self.context.ExpirationDate() == 'None':
+            return None
+        return self._expires
+
+    def _getAllowDiscussion(self):
+        context = aq_self(self.context)
+        return getattr(context, 'allow_discussion', None)
+
+    def _setAllowDiscussion(self, value):
+        dtool = getUtility(IDiscussionTool)
+        dtool.overrideDiscussionFor(self.context, value)
+
+    allow_discussion = property(_getAllowDiscussion, _setAllowDiscussion)
+    identifier = ProxyFieldProperty(IMetadataSchema['identifier'],
+                                    'Identifier')
+    title = ProxyFieldProperty(IMetadataSchema['title'], 'Title', 'setTitle')
+    description = ProxyFieldProperty(IMetadataSchema['description'],
+                                     'Description', 'setDescription')
+    subject = ProxyFieldProperty(IMetadataSchema['subject'],
+                                 'Subject', 'setSubject')
+    contributors = ProxyFieldProperty(IMetadataSchema['contributors'],
+                                      'listContributors', 'setContributors')
+    created = ProxyFieldProperty(IMetadataSchema['created'])
+    modified = ProxyFieldProperty(IMetadataSchema['modified'])
+    effective = property(_getEffective, _effective.__set__)
+    expires = property(_getExpires, _expires.__set__)
+    format = ProxyFieldProperty(IMetadataSchema['format'],
+                                     'Format', 'setFormat')
+    language = ProxyFieldProperty(IMetadataSchema['language'],
+                                     'Language', 'setLanguage')
+    rights = ProxyFieldProperty(IMetadataSchema['rights'],
+                                     'Rights', 'setRights')
+
+
+class MinimalMetadataEditView(ContentEditFormBase):
+
+    """Edit view for IMutableMinimalDublinCore.
+    """
+
+    form_fields = form.FormFields(IMinimalMetadataSchema)
+
+    label = _(u'Properties')
+
+    def setUpWidgets(self, ignore_request=False):
+        super(MinimalMetadataEditView,
+              self).setUpWidgets(ignore_request=ignore_request)
+        self.widgets['description'].height = 4
+
+
+class MetadataEditView(ContentEditFormBase):
+
+    """Edit view for IMutableDublinCore.
+    """
+
+    actions = form.Actions(
+        form.Action(
+            name='change',
+            label=_(u'Change'),
+            validator='handle_validate',
+            success='handle_change_success',
+            failure='handle_failure'),
+        form.Action(
+            name='change_and_edit',
+            label=_(u'Change and Edit'),
+            validator='handle_validate',
+            success='handle_change_and_edit_success',
+            failure='handle_failure'),
+        form.Action(
+            name='change_and_view',
+            label=_(u'Change and View'),
+            validator='handle_validate',
+            success='handle_change_and_view_success',
+            failure='handle_failure'))
+
+    form_fields = form.FormFields(IMetadataSchema)
+    form_fields['subject'].custom_widget = SubjectInputWidget
+    form_fields['contributors'].custom_widget = TupleInputWidget
+    form_fields['effective'].custom_widget = DatetimeI18nWidget
+    form_fields['expires'].custom_widget = DatetimeI18nWidget
+
+    label = _(u'Properties')
+
+    def setUpWidgets(self, ignore_request=False):
+        super(MetadataEditView,
+              self).setUpWidgets(ignore_request=ignore_request)
+        self.widgets['allow_discussion']._messageNoValue = _(u'Default')
+        self.widgets['description'].height = 4
+        self.widgets['subject'].split = True
+        self.widgets['contributors'].height = 6
+        self.widgets['contributors'].split = True
+        self.widgets['created'].split = True
+        self.widgets['modified'].split = True
+        self.widgets['effective'].split = True
+        self.widgets['expires'].split = True
+
+    def handle_change_success(self, action, data):
+        self._handle_success(action, data)
+        return self._setRedirect('portal_types', 'object/metadata')
+
+    def handle_change_and_edit_success(self, action, data):
+        self._handle_success(action, data)
+        return self._setRedirect('portal_types', 'object/edit')

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/content/newsitem.py (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/newsitem.py)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/content/newsitem.py	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/content/newsitem.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,90 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Foundation and Contributors.
+#
+# 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 news items.
+
+$Id$
+"""
+
+from zope.component import adapts
+from zope.formlib import form
+from zope.interface import implements
+from zope.interface import Interface
+from zope.schema import Choice
+from zope.schema import Text
+from zope.schema import TextLine
+
+from Products.CMFDefault.formlib.form import ContentEditFormBase
+from Products.CMFDefault.formlib.schema import ProxyFieldProperty
+from Products.CMFDefault.formlib.schema import SchemaAdapterBase
+from Products.CMFDefault.formlib.widgets import ChoiceRadioWidget
+from Products.CMFDefault.formlib.widgets import TextInputWidget
+from Products.CMFDefault.interfaces import IMutableNewsItem
+from Products.CMFDefault.utils import Message as _
+
+
+class INewsItemSchema(Interface):
+
+    """Schema for news item views.
+    """
+
+    title = TextLine(
+        title=_(u'Title'),
+        readonly=True)
+
+    text_format = Choice(
+        title=_(u'Format'),
+        vocabulary='cmf.AvailableTextFormats')
+
+    description = Text(
+        title=_(u'Lead-in'),
+        required=False,
+        missing_value=u'')
+
+    text = Text(
+        title=_(u'Body'),
+        required=False,
+        missing_value=u'')
+
+
+class NewsItemSchemaAdapter(SchemaAdapterBase):
+
+    """Adapter for IMutableNewsItem.
+    """
+
+    adapts(IMutableNewsItem)
+    implements(INewsItemSchema)
+
+    title = ProxyFieldProperty(INewsItemSchema['title'], 'Title')
+    text_format = ProxyFieldProperty(INewsItemSchema['text_format'])
+    description = ProxyFieldProperty(INewsItemSchema['description'],
+                                     'Description', 'setDescription')
+    text = ProxyFieldProperty(INewsItemSchema['text'],
+                              'EditableBody', '_edit')
+
+
+class NewsItemEditView(ContentEditFormBase):
+
+    """Edit view for INewsItem.
+    """
+
+    form_fields = form.FormFields(INewsItemSchema)
+    form_fields['text_format'].custom_widget = ChoiceRadioWidget
+    form_fields['description'].custom_widget = TextInputWidget
+    form_fields['text'].custom_widget = TextInputWidget
+
+    def setUpWidgets(self, ignore_request=False):
+        super(NewsItemEditView,
+              self).setUpWidgets(ignore_request=ignore_request)
+        self.widgets['text_format'].orientation = 'horizontal'
+        self.widgets['description'].height = 8
+        self.widgets['text'].height = 16

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/content/templates/document.pt (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/templates/document.pt)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/content/templates/document.pt	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/content/templates/document.pt	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,27 @@
+<html metal:use-macro="context/@@standard_macros/page">
+<head>
+
+<metal:slot fill-slot="style_slot">
+<tal:span tal:replace="structure context/getBaseTag" />
+</metal:slot>
+
+</head>
+<body>
+
+<metal:slot metal:fill-slot="body">
+<h1 id="DesktopTitle" tal:content="view/title">Document Title</h1>
+
+<div id="DesktopDescription" tal:content="view/description">Document
+ Description goes here.</div>
+
+<metal:macro metal:use-macro="context/content_byline/macros/byline" />
+
+<tal:span tal:replace="structure view/text">Cooked Body</tal:span>
+
+<div class="Discussion" tal:condition="context/viewThreadsAtBottom|nothing">
+ <tal:span tal:replace="structure context/viewThreadsAtBottom" />
+</div>
+</metal:slot>
+
+</body>
+</html>

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/content/templates/folder.pt (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/templates/folder.pt)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/content/templates/folder.pt	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/content/templates/folder.pt	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,36 @@
+<html metal:use-macro="context/@@standard_macros/page">
+<body>
+
+<metal:slot metal:fill-slot="body" 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>
+
+  <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: Products.CMFDefault/trunk/Products/CMFDefault/browser/content/templates/folder_contents.pt (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/templates/folder_contents.pt)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/content/templates/folder_contents.pt	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/content/templates/folder_contents.pt	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,82 @@
+<html metal:use-macro="context/@@standard_macros/page">
+<body>
+
+<metal:slot metal:fill-slot="body" i18n:domain="cmf_default">
+<h1 i18n:translate="">Folder Contents: <tal:span
+    tal:content="view/title" i18n:name="obj_title">Title</tal:span></h1>
+
+<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>
+
+<ul class="errors" tal:condition="view/errors">
+ <li tal:repeat="error view/error_views"><tal:span
+     tal:replace="structure error" /></li>
+</ul>
+
+<p class="status" 
+  tal:condition="exists: request/portal_status_message"
+  tal:content="request/portal_status_message"></p>
+  
+<form class="form" action="." method="post" enctype="multipart/form-data"
+   tal:attributes="action request/ACTUAL_URL">
+   <tal:block repeat="widget view/hidden_widgets"
+              replace="structure widget/hidden" />
+   <table tal:condition="view/has_subobjects">
+     <tr>
+       <th tal:repeat="column view/column_headings"
+            tal:attributes="colspan column/colspan | nothing"><a href="column"
+         tal:content="column/title"
+         tal:attributes="href column/url"
+         >Column Title</a></th>
+     </tr>
+   <tr tal:repeat="item view/listBatchItems" 
+      tal:attributes="class python: (repeat['item'].even() and 'row-hilite') or ''">
+     <td tal:content="structure item/select">Checkbox</td>
+     <td><a href="" tal:attributes="href item/url"
+         tal:condition="item/icon"
+      ><img src="" alt="" border="0"
+          tal:attributes="src item/icon; alt item/type"
+          i18n:attributes="alt" /></a></td>
+     <td><a tal:attributes="href string:${item/url}/edit.html" tal:content="string:${item/name} (${item/title})"></a></td>
+     <td tal:content="item/ModificationDate"></td>
+    <td tal:content="item/position"></td>
+   </tr>
+   </table>
+<div class="buttons">
+  <tal:loop tal:repeat="action view/object_actions" 
+   tal:replace="structure action/render" />
+</div>
+<div class="buttons">
+  <tal:loop tal:repeat="action view/delta_actions" 
+   tal:replace="structure action/render" />
+   <div tal:condition="view/is_orderable"
+        tal:define="widget python:view.widgets.get('delta');
+                    hint widget/hint | nothing">
+     <label tal:attributes="for widget/name; title python: hint or None"
+       tal:content="widget/label">Move By</label>
+     <tal:block tal:replace="structure view/widgets/delta" />
+   </div>
+</div>
+<div class="buttons">
+  <tal:loop tal:repeat="action view/absolute_actions" 
+   tal:replace="structure action/render" />
+</div>
+<div class="buttons">
+  <tal:loop tal:repeat="action view/sort_actions" 
+   tal:replace="structure action/render" />
+</div>
+</form>
+<metal:macro metal:use-macro="context/@@batch_widget/navigation" />
+<metal:macro metal:use-macro="context/@@batch_widget/pagination" />
+</metal:slot>
+
+</body>
+</html>
\ No newline at end of file

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/content/templates/link.pt (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/templates/link.pt)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/content/templates/link.pt	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/content/templates/link.pt	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,29 @@
+<html metal:use-macro="context/@@standard_macros/page">
+<head>
+
+<metal:slot fill-slot="style_slot">
+<tal:span tal:replace="structure context/getBaseTag" />
+</metal:slot>
+
+</head>
+<body>
+
+<metal:slot metal:fill-slot="body" i18n:domain="cmf_default">
+<h1 id="DesktopTitle" tal:content="view/title">Document Title</h1>
+
+<div id="DesktopDescription" tal:content="view/description">Document
+ Description goes here.</div>
+
+<metal:macro metal:use-macro="context/content_byline/macros/byline" />
+
+<p i18n:translate="">Link:
+ <a href="" tal:attributes="href view/url" tal:content="view/url"
+    i18n:name="link">http://www.zope.org</a></p>
+
+<div class="Discussion" tal:condition="context/viewThreadsAtBottom|nothing">
+ <tal:span tal:replace="structure context/viewThreadsAtBottom" />
+</div>
+</metal:slot>
+
+</body>
+</html>

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/__init__.py (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/tests/__init__.py)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/__init__.py	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/__init__.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1 @@
+# Unit tests for CMFDefault browser views.

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/btreefolder.txt (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/tests/btreefolder.txt)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/btreefolder.txt	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/btreefolder.txt	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,58 @@
+Folder Views
+------------
+
+Set up user.
+
+    >>> uf = app.site.acl_users
+    >>> uf._doAddUser('mgr', 'mgrpw', ['Manager'], [])
+
+Create the browser object we'll be using.
+
+    # BBB for Zope 2.12
+    >>> try:
+    ...     from Testing.testbrowser import Browser
+    ... except ImportError:
+    ...     from Products.Five.testbrowser import Browser
+
+    >>> browser = Browser()
+    >>> browser.handleErrors = False
+    >>> browser.addHeader('Authorization', 'Basic mgr:mgrpw')
+
+Use the add form without input.
+
+    >>> browser.open('http://localhost/site/++add++CMF BTree Folder')
+    >>> '[[cmf_default][Add [[cmf_default][CMF BTree Folder]]]]' in browser.contents
+    True
+    >>> browser.getControl('[[zope][Add]]').click()
+    >>> '[[zope][There were errors]]' in browser.contents
+    True
+    >>> '[[zope][Required input is missing.]]' in browser.contents
+    True
+
+Use the add form with valid input.
+
+    >>> from StringIO import StringIO
+    >>> browser.open('http://localhost/site/++add++CMF BTree Folder')
+    >>> '[[cmf_default][Add [[cmf_default][CMF BTree Folder]]]]' in browser.contents
+    True
+    >>> browser.getControl(name='form.id').value = 'myFolder'
+    >>> browser.getControl('[[zope][Add]]').click()
+    >>> '[[cmf_default][[[cmf_default][CMF BTree Folder]] added.]]' in browser.contents
+    True
+
+Use the folder contents form without input.
+
+    >>> browser.open('http://localhost/site/myFolder/@@edit.html')
+    >>> '[[cmf_default][Folder Contents: ]]' in browser.contents
+    True
+
+Try to add something to this folder.
+
+    >>> browser.open('http://localhost/site/myFolder/++add++Document')
+    >>> '[[cmf_default][Add [[cmf_default][Document]]]]' in browser.contents
+    True
+    >>> browser.getControl(name='form.id').value = 'myDocument'
+    >>> browser.getControl('[[zope][Add]]').click()
+    >>> '[[cmf_default][[[cmf_default][Document]] added.]]' in browser.contents
+    True
+

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/document.txt (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/tests/document.txt)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/document.txt	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/document.txt	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,75 @@
+Document Views
+--------------
+
+Set up Document and user.
+
+    >>> from Products.CMFDefault.Document import Document
+    >>> id = app.site._setObject('myDocument', Document('myDocument'))
+
+    >>> uf = app.site.acl_users
+    >>> uf._doAddUser('mgr', 'mgrpw', ['Manager'], [])
+    >>> from zope.site.hooks import setSite
+    >>> setSite(app.site)
+
+Create the browser object we'll be using.
+
+    # BBB for Zope 2.12
+    >>> try:
+    ...     from Testing.testbrowser import Browser
+    ... except ImportError:
+    ...     from Products.Five.testbrowser import Browser
+
+    >>> browser = Browser()
+    >>> browser.handleErrors = False
+    >>> browser.addHeader('Authorization', 'Basic mgr:mgrpw')
+
+Use the edit form with invalid input.
+
+    >>> browser.open('http://localhost/site/myDocument/@@edit.html')
+    >>> browser.getControl('[[cmf_default][Body]]').value = '<script>'
+    >>> browser.getControl('[[cmf_default][Change]]').click()
+    >>> '[[zope][There were errors]]' in browser.contents
+    True
+    >>> '[[cmf_default][Dynamic tag \'script\' not ' in browser.contents
+    True
+
+Use the edit form with valid input.
+
+    >>> browser.getControl('[[cmf_default][Body]]').value = 'spam'
+    >>> browser.getControl('[[cmf_default][Change]]').click()
+    >>> '[[zope][There were errors]]' in browser.contents
+    False
+
+Look at the result.
+
+    >>> browser.open('http://localhost/site/myDocument/@@view.html')
+    >>> 'spam' in browser.contents
+    True
+
+Changes can't be saved if the safety belt is invalid.
+
+    >>> browser.open('http://localhost/site/myDocument/@@edit.html')
+
+    >>> app.site.myDocument._safety_belt = '1'
+
+    >>> browser.getControl('[[cmf_default][Body]]').value = 'spam and eggs'
+    >>> browser.getControl('[[cmf_default][Change]]').click()
+    >>> '[[zope][There were errors]]' in browser.contents
+    True
+    >>> '[[cmf_default][Intervening changes from ' in browser.contents
+    True
+
+Changes can't be saved if the document is locked.
+
+    >>> browser.open('http://localhost/site/myDocument/@@edit.html')
+
+    >>> from webdav.LockItem import LockItem
+    >>> lock = LockItem(uf.getUser('mgr').__of__(uf))
+    >>> app.site.myDocument.wl_setLock(lock.getLockToken(), lock)
+
+    >>> browser.getControl('[[cmf_default][Body]]').value = 'spam and eggs'
+    >>> browser.getControl('[[cmf_default][Change]]').click()
+    >>> '[[zope][There were errors]]' in browser.contents
+    True
+    >>> '[[cmf_default][This resource is locked ' in browser.contents
+    True

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/favorite.txt (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/tests/favorite.txt)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/favorite.txt	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/favorite.txt	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,62 @@
+Favorite Views
+--------------
+
+Set up user.
+
+    >>> uf = app.site.acl_users
+    >>> uf._doAddUser('mgr', 'mgrpw', ['Manager'], [])
+
+Create the browser object we'll be using.
+
+    # BBB for Zope 2.12
+    >>> try:
+    ...     from Testing.testbrowser import Browser
+    ... except ImportError:
+    ...     from Products.Five.testbrowser import Browser
+
+    >>> browser = Browser()
+    >>> browser.handleErrors = False
+    >>> browser.addHeader('Authorization', 'Basic mgr:mgrpw')
+
+Use the add form without input.
+
+    >>> browser.open('http://localhost/site/++add++Favorite')
+    >>> '[[cmf_default][Add [[cmf_default][Favorite]]]]' in browser.contents
+    True
+    >>> browser.getControl('[[zope][Add]]').click()
+    >>> '[[zope][There were errors]]' in browser.contents
+    True
+    >>> '[[zope][Required input is missing.]]' in browser.contents
+    True
+
+Use the add form with valid input.
+
+    >>> browser.open('http://localhost/site/++add++Favorite')
+    >>> '[[cmf_default][Add [[cmf_default][Favorite]]]]' in browser.contents
+    True
+    >>> browser.getControl(name='form.id').value = 'myFavorite'
+    >>> browser.getControl(name='form.title').value = 'FAVORITE TITLE'
+    >>> browser.getControl(name='form.description').value = 'FAVORITE DESCRIPTION.'
+    >>> browser.getControl(name='form.remote_url').value = 'PATH/TO/ITEM'
+    >>> browser.getControl('[[zope][Add]]').click()
+    >>> '[[cmf_default][[[cmf_default][Favorite]] added.]]' in browser.contents
+    True
+
+Use the edit form without input.
+
+    >>> browser.open('http://localhost/site/myFavorite/@@edit.html')
+    >>> '[[cmf_default][Edit [[cmf_default][Favorite]]]]' in browser.contents
+    True
+    >>> browser.getControl('[[cmf_default][Change]]').click()
+    >>> '[[cmf_default][Nothing to change.]]' in browser.contents
+    True
+
+Use the edit form with valid input.
+
+    >>> browser.open('http://localhost/site/myFavorite/@@edit.html')
+    >>> '[[cmf_default][Edit [[cmf_default][Favorite]]]]' in browser.contents
+    True
+    >>> browser.getControl(name='form.remote_url').value = 'PATH/TO/OTHER'
+    >>> browser.getControl('[[cmf_default][Change]]').click()
+    >>> '[[cmf_default][[[cmf_default][Favorite]] changed.]]' in browser.contents
+    True

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/file.txt (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/tests/file.txt)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/file.txt	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/file.txt	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,64 @@
+File Views
+----------
+
+Set up user.
+
+    >>> uf = app.site.acl_users
+    >>> uf._doAddUser('mgr', 'mgrpw', ['Manager'], [])
+
+Create the browser object we'll be using.
+
+    # BBB for Zope 2.12
+    >>> try:
+    ...     from Testing.testbrowser import Browser
+    ... except ImportError:
+    ...     from Products.Five.testbrowser import Browser
+
+    >>> browser = Browser()
+    >>> browser.handleErrors = False
+    >>> browser.addHeader('Authorization', 'Basic mgr:mgrpw')
+
+Use the add form without input.
+
+    >>> browser.open('http://localhost/site/++add++File')
+    >>> '[[cmf_default][Add [[cmf_default][File]]]]' in browser.contents
+    True
+    >>> browser.getControl('[[zope][Add]]').click()
+    >>> '[[zope][There were errors]]' in browser.contents
+    True
+    >>> '[[zope][Required input is missing.]]' in browser.contents
+    True
+
+Use the add form with valid input.
+
+    >>> from StringIO import StringIO
+    >>> browser.open('http://localhost/site/++add++File')
+    >>> '[[cmf_default][Add [[cmf_default][File]]]]' in browser.contents
+    True
+    >>> browser.getControl(name='form.title').value = 'FILE TITLE'
+    >>> browser.getControl(name='form.description').value = 'FILE DESCRIPTION.'
+    >>> ctrl = browser.getControl(name='form.file')
+    >>> ctrl.add_file(StringIO('FILE DATA'), 'text/plain', 'myFile')
+    >>> browser.getControl('[[zope][Add]]').click()
+    >>> '[[cmf_default][[[cmf_default][File]] added.]]' in browser.contents
+    True
+
+Use the edit form without input.
+
+    >>> browser.open('http://localhost/site/myFile/@@edit.html')
+    >>> '[[cmf_default][Edit [[cmf_default][File]]]]' in browser.contents
+    True
+    >>> browser.getControl('[[cmf_default][Change]]').click()
+    >>> '[[cmf_default][Nothing to change.]]' in browser.contents
+    True
+
+Use the edit form with valid input.
+
+    >>> browser.open('http://localhost/site/myFile/@@edit.html')
+    >>> '[[cmf_default][Edit [[cmf_default][File]]]]' in browser.contents
+    True
+    >>> ctrl = browser.getControl(name='form.file')
+    >>> ctrl.add_file(StringIO('FILE DATA 2'), 'text/plain', 'test.txt')
+    >>> browser.getControl('[[cmf_default][Change]]').click()
+    >>> '[[cmf_default][[[cmf_default][File]] changed.]]' in browser.contents
+    True

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/folder.txt (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/tests/folder.txt)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/folder.txt	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/folder.txt	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,67 @@
+Form Views
+--------------
+
+Set up user.
+
+    >>> uf = app.site.acl_users
+    >>> uf._doAddUser('mgr', 'mgrpw', ['Manager'], [])
+    >>> from zope.site.hooks import setSite
+    >>> setSite(app.site)
+
+Create the browser object we'll be using.
+
+    # BBB for Zope 2.12
+    >>> try:
+    ...     from Testing.testbrowser import Browser
+    ... except ImportError:
+    ...     from Products.Five.testbrowser import Browser
+
+    >>> browser = Browser()
+    >>> browser.handleErrors = False
+    >>> browser.addHeader('Authorization', 'Basic mgr:mgrpw')
+
+Open the contents view with the various options selected.
+All sort options apart from position are reversible.
+
+    >>> browser.open('http://localhost/site/@@edit.html?form.b_start=25')
+    >>> 'name="form.b_start" type="hidden" value="25"' in browser.contents
+    True
+    >>> browser.open('http://localhost/site/@@edit.html?form.sort_key=Type')
+    >>> 'name="form.sort_key" type="hidden" value="Type"' in browser.contents
+    True
+    >>> '<a href="http://localhost/site/@@edit.html?form.sort_key=Type&amp;form.reverse=1">' \
+    ... in browser.contents
+    True
+    >>> browser.open('http://localhost/site/@@edit.html?form.sort_key=getId')
+    >>> 'name="form.sort_key" type="hidden" value="getId"' in browser.contents
+    True
+    >>> '<a href="http://localhost/site/@@edit.html?form.sort_key=getId&amp;form.reverse=1">' \
+    ... in browser.contents
+    True
+    >>> browser.open('http://localhost/site/@@edit.html?form.sort_key=modified')
+    >>> 'name="form.sort_key" type="hidden" value="modified"' in browser.contents
+    True
+    >>> '<a href="http://localhost/site/@@edit.html?form.sort_key=modified&amp;form.reverse=1">' \
+    ... in browser.contents
+    True
+    >>> browser.open('http://localhost/site/@@edit.html?form.sort_key=position')
+    >>> 'name="form.sort_key" type="hidden" value="position"' in browser.contents
+    True
+    >>> '<a href="http://localhost/site/@@edit.html?form.sort_key=position&amp;form.reverse=1">' \
+    ... in browser.contents
+    False
+
+
+
+    >>> browser.open('http://localhost/site/@@edit.html?form.sort_key=Type&form.reverse=1')
+    >>> 'name="form.reverse" type="hidden" value="1"' in browser.contents
+    True
+    >>> browser.open('http://localhost/site/@@edit.html?form.sort_key=getId&form.reverse=1')
+    >>> 'name="form.reverse" type="hidden" value="1"' in browser.contents
+    True
+    >>> browser.open('http://localhost/site/@@edit.html?form.sort_key=modified&form.reverse=1')
+    >>> 'name="form.reverse" type="hidden" value="1"' in browser.contents
+    True
+    >>> browser.open('http://localhost/site/@@edit.html?form.sort_key=position&form.reverse=1')
+    >>> 'name="form.reverse" type="hidden" value="1"' in browser.contents
+    True
\ No newline at end of file

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/link.txt (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/tests/link.txt)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/link.txt	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/link.txt	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,62 @@
+Link Views
+----------
+
+Set up user.
+
+    >>> uf = app.site.acl_users
+    >>> uf._doAddUser('mgr', 'mgrpw', ['Manager'], [])
+
+Create the browser object we'll be using.
+
+    # BBB for Zope 2.12
+    >>> try:
+    ...     from Testing.testbrowser import Browser
+    ... except ImportError:
+    ...     from Products.Five.testbrowser import Browser
+
+    >>> browser = Browser()
+    >>> browser.handleErrors = False
+    >>> browser.addHeader('Authorization', 'Basic mgr:mgrpw')
+
+Use the add form without input.
+
+    >>> browser.open('http://localhost/site/++add++Link')
+    >>> '[[cmf_default][Add [[cmf_default][Link]]]]' in browser.contents
+    True
+    >>> browser.getControl('[[zope][Add]]').click()
+    >>> '[[zope][There were errors]]' in browser.contents
+    True
+    >>> '[[zope][Required input is missing.]]' in browser.contents
+    True
+
+Use the add form with valid input.
+
+    >>> browser.open('http://localhost/site/++add++Link')
+    >>> '[[cmf_default][Add [[cmf_default][Link]]]]' in browser.contents
+    True
+    >>> browser.getControl(name='form.id').value = 'myLink'
+    >>> browser.getControl(name='form.title').value = 'LINK TITLE'
+    >>> browser.getControl(name='form.description').value = 'LINK DESCRIPTION.'
+    >>> browser.getControl(name='form.remote_url').value = 'WWW.EXAMPLE.ORG'
+    >>> browser.getControl('[[zope][Add]]').click()
+    >>> '[[cmf_default][[[cmf_default][Link]] added.]]' in browser.contents
+    True
+
+Use the edit form without input.
+
+    >>> browser.open('http://localhost/site/myLink/@@edit.html')
+    >>> '[[cmf_default][Edit [[cmf_default][Link]]]]' in browser.contents
+    True
+    >>> browser.getControl('[[cmf_default][Change]]').click()
+    >>> '[[cmf_default][Nothing to change.]]' in browser.contents
+    True
+
+Use the edit form with valid input.
+
+    >>> browser.open('http://localhost/site/myLink/@@edit.html')
+    >>> '[[cmf_default][Edit [[cmf_default][Link]]]]' in browser.contents
+    True
+    >>> browser.getControl(name='form.remote_url').value = 'WWW.EXAMPLE.COM'
+    >>> browser.getControl('[[cmf_default][Change]]').click()
+    >>> '[[cmf_default][[[cmf_default][Link]] changed.]]' in browser.contents
+    True

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/metadata.txt (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/tests/metadata.txt)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/metadata.txt	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/metadata.txt	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,42 @@
+Metadata Views
+--------------
+
+Set up content and user.
+
+    >>> from Products.CMFDefault.Document import Document 
+    >>> id = app.site._setObject('myContent', Document('myContent'))
+    >>> app.site.myContent._setPortalTypeName('Document')
+
+    >>> uf = app.site.acl_users
+    >>> uf._doAddUser('mgr', 'mgrpw', ['Manager'], [])
+
+Create the browser object we'll be using.
+
+    # BBB for Zope 2.12
+    >>> try:
+    ...     from Testing.testbrowser import Browser
+    ... except ImportError:
+    ...     from Products.Five.testbrowser import Browser
+
+    >>> browser = Browser()
+    >>> browser.handleErrors = False
+    >>> browser.addHeader('Authorization', 'Basic mgr:mgrpw')
+
+Use the edit form with valid input.
+
+    >>> browser.open('http://localhost/site/myContent/@@properties.html')
+    >>> '[[cmf_default][Properties]]' in browser.contents
+    True
+    >>> browser.getControl(name='form.allow_discussion').value = ('off',)
+    >>> browser.getControl(name='form.title').value = 'CONTENT TITLE'
+    >>> browser.getControl(name='form.description').value = 'CONTENT DESCRIPTION.'
+    >>> browser.getControl(name='form.subject').value = 'spam\neggs'
+    >>> browser.getControl(name='form.contributors').value = 'foo\nbar\nbaz'
+    >>> browser.getControl(name='form.effective').value = '2006 11 11  12:00:00 '
+    >>> browser.getControl(name='form.expires').value = '2007 11 11  12:00:00 '
+    >>> browser.getControl(name='form.format').value = 'text/html'
+    >>> browser.getControl(name='form.language').value = 'test'
+    >>> browser.getControl(name='form.rights').value = 'CONTENT RIGHTS'
+    >>> browser.getControl('[[cmf_default][Change]]').click()
+    >>> '[[cmf_default][[[cmf_default][Document]] changed.]]' in browser.contents
+    True

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/test_btreefolder.py (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/tests/test_btreefolder.py)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/test_btreefolder.py	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/test_btreefolder.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,35 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation and Contributors.
+#
+# 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.
+#
+##############################################################################
+""" Test Products.CMFDefault.browser.folder with CMFBTreeFolder
+
+$Id$
+"""
+
+import unittest
+from Testing import ZopeTestCase
+
+from Products.CMFDefault.browser.content.tests.utils import clearVocabulary
+from Products.CMFDefault.browser.content.tests.utils import setupVocabulary
+from Products.CMFDefault.testing import FunctionalLayer
+
+
+ftest_suite = ZopeTestCase.FunctionalDocFileSuite('btreefolder.txt',
+                setUp=setupVocabulary,
+                tearDown=clearVocabulary
+                )
+ftest_suite.layer = FunctionalLayer
+
+def test_suite():
+    return unittest.TestSuite((
+        ftest_suite,
+    ))

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/test_document.py (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/tests/test_document.py)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/test_document.py	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/test_document.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,36 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation and Contributors.
+#
+# 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.
+#
+##############################################################################
+""" Test Products.CMFDefault.browser.document
+
+$Id$
+"""
+
+import unittest
+from Testing import ZopeTestCase
+
+from Products.CMFDefault.browser.content.tests.utils import clearVocabulary
+from Products.CMFDefault.browser.content.tests.utils import setupVocabulary
+from Products.CMFDefault.testing import FunctionalLayer
+
+
+ftest_suite = ZopeTestCase.FunctionalDocFileSuite(
+                'document.txt',
+                setUp=setupVocabulary,
+                tearDown=clearVocabulary,
+               )
+ftest_suite.layer = FunctionalLayer
+
+def test_suite():
+    return unittest.TestSuite((
+        ftest_suite,
+    ))

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/test_favorite.py (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/tests/test_favorite.py)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/test_favorite.py	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/test_favorite.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,30 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation and Contributors.
+#
+# 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.
+#
+##############################################################################
+""" Test Products.CMFDefault.browser.favorite
+
+$Id$
+"""
+
+import unittest
+from Testing import ZopeTestCase
+
+from Products.CMFDefault.testing import FunctionalLayer
+
+
+ftest_suite = ZopeTestCase.FunctionalDocFileSuite('favorite.txt')
+ftest_suite.layer = FunctionalLayer
+
+def test_suite():
+    return unittest.TestSuite((
+        ftest_suite,
+    ))

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/test_file.py (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/tests/test_file.py)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/test_file.py	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/test_file.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,30 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation and Contributors.
+#
+# 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.
+#
+##############################################################################
+""" Test Products.CMFDefault.browser.file
+
+$Id$
+"""
+
+import unittest
+from Testing import ZopeTestCase
+
+from Products.CMFDefault.testing import FunctionalLayer
+
+
+ftest_suite = ZopeTestCase.FunctionalDocFileSuite('file.txt')
+ftest_suite.layer = FunctionalLayer
+
+def test_suite():
+    return unittest.TestSuite((
+        ftest_suite,
+    ))

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/test_folder.py (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/tests/test_folder.py)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/test_folder.py	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/test_folder.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,139 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation and Contributors.
+#
+# 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.
+#
+##############################################################################
+""" Test Products.CMFDefault.browser.folder
+
+$Id$
+"""
+
+import unittest
+
+from AccessControl.SecurityManagement import newSecurityManager
+from AccessControl.User import UnrestrictedUser
+from Testing import ZopeTestCase
+
+from zope.component import getSiteManager
+from zope.publisher.browser import TestRequest
+from zope.publisher.interfaces.browser import IBrowserPublisher
+
+from Products.CMFCore.PortalFolder import PortalFolder
+from Products.CMFCore.tests.base.dummy import DummySite, DummyTool
+from Products.CMFCore.tests.base.dummy import DummyUserFolder, DummyContent
+from Products.CMFCore.interfaces import IPropertiesTool
+
+from Products.CMFDefault.browser.content.folder import ContentsView
+from Products.CMFDefault.browser.content.tests.utils import clearVocabulary
+from Products.CMFDefault.browser.content.tests.utils import setupVocabulary
+from Products.CMFDefault.testing import FunctionalLayer
+
+
+class FolderBrowserViewTests(unittest.TestCase):
+
+    def setUp(self):
+        """Setup a site"""
+        # maybe there is a base class for this?
+        self.site = site = DummySite('site')
+        self.sm = getSiteManager()
+        mtool = site._setObject('portal_membership', DummyTool())
+        ptool = site._setObject('portal_properties', DummyTool())
+        self.sm.registerUtility(ptool, IPropertiesTool)
+        ttool = site._setObject('portal_types', DummyTool())
+        utool = site._setObject('portal_url', DummyTool())
+        folder = PortalFolder('test_folder')
+        self.folder = site._setObject('test_folder', folder)
+        self.uf = self.site._setObject('acl_users', DummyUserFolder())
+        
+    def _make_one(self, name="DummyItem"):
+        content = DummyContent(name)
+        content.portal_type = "Dummy Content"
+        self.folder._setObject(name, content)
+
+    def _make_batch(self):
+        """Add enough objects to force pagination"""
+        batch_size = ContentsView._BATCH_SIZE
+        for i in range(batch_size + 2):
+            content_id = "Dummy%s" % i
+            self._make_one(content_id)
+
+    def site_login(self):
+        newSecurityManager(None, 
+                    UnrestrictedUser('god', '', ['Manager'], ''))
+    
+    def test_view(self):
+        view = ContentsView(self.folder, TestRequest())
+        self.failUnless(IBrowserPublisher.providedBy(view))
+        
+    def test_up_info(self):
+        view = ContentsView(self.folder, TestRequest())
+        self.assertEquals({'url':u'', 'id':u'Root', 'icon':u''},
+                            view.up_info())
+        
+    def test_list_batch_items(self):
+        view = ContentsView(self.folder, TestRequest())
+        self.assertEquals(view.listBatchItems(), [])
+    
+    def test_is_orderable(self):
+        view = ContentsView(self.folder, TestRequest())
+        self.failIf(view.is_orderable())
+        
+    def test_sort_can_be_changed(self):
+        view = ContentsView(self.folder, TestRequest())
+        self.failIf(view.can_sort_be_changed())
+    
+    def test_empty_has_subobjects(self):
+        view = ContentsView(self.folder, TestRequest())
+        self.failIf(view.has_subobjects())
+        
+    def test_has_subobjects(self):
+        self._make_one()
+        view = ContentsView(self.folder, TestRequest())
+        self.failUnless(view.has_subobjects())
+        
+    def test_check_clipboard_data(self):
+        view = ContentsView(self.folder, TestRequest())
+        self.failIf(view.check_clipboard_data())
+    
+    def test_validate_items(self):
+        """Cannot validate forms without widgets"""
+        view = ContentsView(self.folder, TestRequest())
+        self.assertRaises(AttributeError, 
+                            view.validate_items, "", {'foo':'bar'})
+                            
+    def test_get_ids(self):
+        view = ContentsView(self.folder, TestRequest())
+        self.assertEquals(
+                        view._get_ids({'foo':'bar'}),
+                        [])
+        self.assertEquals(
+                        view._get_ids({'DummyItem1.select':True,
+                                       'DummyItem2.select':False,
+                                       'DummyItem3.select':True}),
+                        ['DummyItem1', 'DummyItem3'])
+        self.assertEquals(
+                        view._get_ids({'delta':True,
+                                       'delta':1}),
+                        []
+                        )
+
+
+ftest_suite = ZopeTestCase.FunctionalDocFileSuite('folder.txt',
+                        setUp=setupVocabulary,
+                        tearDown=clearVocabulary,
+                        )
+                        
+ftest_suite.layer = FunctionalLayer
+
+def test_suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(FolderBrowserViewTests))
+    suite.addTest(unittest.TestSuite((ftest_suite,)))
+    return suite

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/test_link.py (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/tests/test_link.py)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/test_link.py	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/test_link.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,30 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation and Contributors.
+#
+# 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.
+#
+##############################################################################
+""" Test Products.CMFDefault.browser.link
+
+$Id$
+"""
+
+import unittest
+from Testing import ZopeTestCase
+
+from Products.CMFDefault.testing import FunctionalLayer
+
+
+ftest_suite = ZopeTestCase.FunctionalDocFileSuite('link.txt')
+ftest_suite.layer = FunctionalLayer
+
+def test_suite():
+    return unittest.TestSuite((
+        ftest_suite,
+    ))

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/test_metadata.py (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/tests/test_metadata.py)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/test_metadata.py	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/test_metadata.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,30 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation and Contributors.
+#
+# 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.
+#
+##############################################################################
+""" Test Products.CMFDefault.browser.metadata
+
+$Id$
+"""
+
+import unittest
+from Testing import ZopeTestCase
+
+from Products.CMFDefault.testing import FunctionalLayer
+
+
+ftest_suite = ZopeTestCase.FunctionalDocFileSuite('metadata.txt')
+ftest_suite.layer = FunctionalLayer
+
+def test_suite():
+    return unittest.TestSuite((
+        ftest_suite,
+    ))

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/utils.py (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/tests/utils.py)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/utils.py	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/content/tests/utils.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,30 @@
+##############################################################################
+#
+# Copyright (c) 2009 Zope Foundation and Contributors.
+#
+# 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.
+#
+##############################################################################
+""" Shared utility functions for browser view tests
+
+$Id$
+"""
+
+try:
+    from Zope2.App.schema import Zope2VocabularyRegistry
+except ImportError:  # Zope2 <= 2.12
+    from Products.Five.schema import Zope2VocabularyRegistry
+
+
+def setupVocabulary(testcase):
+    from zope.schema.vocabulary import setVocabularyRegistry
+    setVocabularyRegistry(Zope2VocabularyRegistry())
+
+def clearVocabulary(testcase):
+    from zope.schema.vocabulary import _clear
+    _clear()

Deleted: Products.CMFDefault/trunk/Products/CMFDefault/browser/document.py
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/document.py	2010-06-25 12:36:59 UTC (rev 113834)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/document.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -1,155 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2006 Zope Foundation and Contributors.
-#
-# 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 documents.
-
-$Id$
-"""
-
-from zope.component import adapts
-from zope.formlib import form
-from zope.interface import implements
-from zope.interface import Interface
-from zope.schema import ASCIILine
-from zope.schema import Bytes
-from zope.schema import Choice
-from zope.schema import Text
-from zope.schema import TextLine
-
-from Products.CMFDefault.Document import REST_AVAILABLE
-from Products.CMFDefault.formlib.form import ContentEditFormBase
-from Products.CMFDefault.formlib.schema import ProxyFieldProperty
-from Products.CMFDefault.formlib.schema import SchemaAdapterBase
-from Products.CMFDefault.formlib.vocabulary import StaticVocabulary
-from Products.CMFDefault.formlib.widgets import ChoiceRadioWidget
-from Products.CMFDefault.formlib.widgets import TextInputWidget
-from Products.CMFDefault.interfaces import IMutableDocument
-from Products.CMFDefault.utils import Message as _
-
-from utils import decode
-from utils import memoize
-from utils import ViewBase
-
-available_text_formats = (
-        (u'structured-text', 'structured-text', _(u'structured-text')),
-        (u'plain', 'plain', _(u'plain text')),
-        (u'html', 'html', _(u'html')))
-
-if REST_AVAILABLE:
-    available_text_formats +=  ( ( u'restructured-text'
-                                 , 'restructured-text'
-                                 , _(u'restructured-text')
-                                 ), )
-
-TextFormatVocabularyFactory = StaticVocabulary(available_text_formats)
-
-
-class IDocumentSchema(Interface):
-
-    """Schema for document views.
-    """
-
-    safety_belt = ASCIILine(
-        required=False)
-
-    title = TextLine(
-        title=_(u'Title'),
-        readonly=True)
-
-    description = Text(
-        title=_(u'Description'),
-        readonly=True)
-
-    text_format = Choice(
-        title=_(u'Format'),
-        vocabulary='cmf.AvailableTextFormats')
-
-    upload = Bytes(
-        title=_(u'Upload'),
-        required=False)
-
-    text = Text(
-        title=_(u'Body'),
-        required=False,
-        missing_value=u'')
-
-
-class DocumentSchemaAdapter(SchemaAdapterBase):
-
-    """Adapter for IMutableDocument.
-    """
-
-    adapts(IMutableDocument)
-    implements(IDocumentSchema)
-
-    safety_belt = ProxyFieldProperty(IDocumentSchema['safety_belt'],
-                                     '_safety_belt')
-    title = ProxyFieldProperty(IDocumentSchema['title'], 'Title')
-    description = ProxyFieldProperty(IDocumentSchema['description'],
-                                     'Description')
-    text_format = ProxyFieldProperty(IDocumentSchema['text_format'])
-    upload = None
-    text = ProxyFieldProperty(IDocumentSchema['text'],
-                              'EditableBody', '_edit')
-
-
-class DocumentView(ViewBase):
-
-    """View for IDocument.
-    """
-
-    # interface
-
-    @memoize
-    @decode
-    def text(self):
-        return self.context.CookedBody()
-
-
-class DocumentEditView(ContentEditFormBase):
-
-    """Edit view for IMutableDocument.
-    """
-
-    form_fields = form.FormFields(IDocumentSchema)
-    form_fields['text_format'].custom_widget = ChoiceRadioWidget
-    form_fields['text'].custom_widget = TextInputWidget
-
-    def setUpWidgets(self, ignore_request=False):
-        super(DocumentEditView,
-              self).setUpWidgets(ignore_request=ignore_request)
-        self.widgets['safety_belt'].hide = True
-        self.widgets['description'].height = 3
-        self.widgets['text_format'].orientation = 'horizontal'
-        self.widgets['upload'].displayWidth = 60
-        self.widgets['text'].height = 20
-
-    def _handle_success(self, action, data):
-        body = data.get('upload')
-        if body:
-            data['text'] = body.decode(self._getDefaultCharset())
-        changed = super(DocumentEditView, self)._handle_success(action, data)
-        if changed:
-            self.context.updateSafetyBelt(data.get('safety_belt'))
-        return changed
-
-    def handle_validate(self, action, data):
-        errors = super(DocumentEditView, self).handle_validate(action, data)
-        if errors:
-            return errors
-        safety_belt = self.request.form['form.safety_belt']
-        if not self.context.isValidSafetyBelt(safety_belt):
-            return (_(u'Intervening changes from elsewhere detected. Please '
-                      u'refetch the document and reapply your changes.'),)
-        # make sure applyChanges doesn't try to update safety_belt
-        self.request.form['form.safety_belt'] = self.context._safety_belt
-        return None

Deleted: Products.CMFDefault/trunk/Products/CMFDefault/browser/favorite.py
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/favorite.py	2010-06-25 12:36:59 UTC (rev 113834)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/favorite.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -1,152 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2006 Zope Foundation and Contributors.
-#
-# 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 favorites.
-
-$Id$
-"""
-
-import urlparse
-
-from zope.app.form.browser import BytesWidget
-from zope.component import adapts
-from zope.formlib import form
-from zope.interface import implements
-from zope.interface import Interface
-from zope.schema import ASCIILine
-from zope.schema import BytesLine
-from zope.schema import Text
-from zope.schema import TextLine
-
-from Products.CMFCore.utils import getToolByName
-from Products.CMFDefault.formlib.form import ContentAddFormBase
-from Products.CMFDefault.formlib.form import ContentEditFormBase
-from Products.CMFDefault.formlib.schema import ProxyFieldProperty
-from Products.CMFDefault.formlib.schema import SchemaAdapterBase
-from Products.CMFDefault.formlib.widgets import IDInputWidget
-from Products.CMFDefault.interfaces import IMutableFavorite
-from Products.CMFDefault.utils import Message as _
-
-
-class IFavoriteSchema(Interface):
-
-    title = TextLine(
-        title=_(u'Title'),
-        required=False,
-        missing_value=u'')
-
-    language = TextLine(
-        title=_(u'Language'),
-        required=False,
-        missing_value=u'',
-        max_length=2)
-
-    description = Text(
-        title=_(u'Description'),
-        required=False,
-        missing_value=u'')
-
-    remote_url = BytesLine(
-        title=_(u'URL'),
-        description=_(u'URL relative to the site root.'),
-        required=False,
-        missing_value=u'')
-
-
-class FavoriteSchemaAdapter(SchemaAdapterBase):
-
-    adapts(IMutableFavorite)
-    implements(IFavoriteSchema)
-
-    _remote_url = ProxyFieldProperty(IFavoriteSchema['remote_url'])
-
-    def _getRemoteURL(self):
-        return self._remote_url
-
-    def _setRemoteURL(self, value):
-        self._remote_url = value
-        self.context.remote_uid = self.context._getUidByUrl()
-
-    title = ProxyFieldProperty(IFavoriteSchema['title'], 'Title', 'setTitle')
-    language = ProxyFieldProperty(IFavoriteSchema['language'],
-                                  'Language', 'setLanguage')
-    description = ProxyFieldProperty(IFavoriteSchema['description'],
-                                     'Description', 'setDescription')
-    remote_url = property(_getRemoteURL, _setRemoteURL)
-
-
-class FavoriteURIWidget(BytesWidget):
-
-    """Custom widget for remote_url.
-    """
-
-    def _toFieldValue(self, input):
-        value = super(FavoriteURIWidget, self)._toFieldValue(input)
-        if not value:
-            return value
-        # strip off scheme and machine from URL if present
-        tokens = urlparse.urlparse(value, 'http')
-        if tokens[1]:
-            # There is a nethost, remove it
-            tokens = ('', '') + tokens[2:]
-            value = urlparse.urlunparse(tokens)
-        # if URL begins with site URL, remove site URL
-        context = getattr(self.context.context, 'context',
-                          self.context.context)
-        portal_url = getToolByName(context, 'portal_url').getPortalPath()
-        if value.startswith(portal_url):
-            value = value[len(portal_url):]
-        # if site is still absolute, make it relative
-        if value[:1]=='/':
-            value = value[1:]
-        return value
-
-
-class FavoriteAddView(ContentAddFormBase):
-
-    """Add view for IMutableFavorite.
-    """
-
-    form_fields = (
-        form.FormFields(ASCIILine(__name__='id', title=_(u'ID'))) +
-        form.FormFields(IFavoriteSchema).omit('language')
-        )
-    form_fields['id'].custom_widget = IDInputWidget
-    form_fields['remote_url'].custom_widget = FavoriteURIWidget
-
-    def setUpWidgets(self, ignore_request=False):
-        super(FavoriteAddView,
-              self).setUpWidgets(ignore_request=ignore_request)
-        self.widgets['description'].height = 3
-
-    def create(self, data):
-        obj = super(FavoriteAddView, self).create(dict(id=data['id']))
-        adapted = IFavoriteSchema(obj)
-        adapted.title = data['title']
-        adapted.language = u''
-        adapted.description = data['description']
-        adapted.remote_url = data['remote_url']
-        return obj
-
-
-class FavoriteEditView(ContentEditFormBase):
-
-    """Edit view for IMutableFavorite.
-    """
-
-    form_fields = form.FormFields(IFavoriteSchema).omit('language')
-    form_fields['remote_url'].custom_widget = FavoriteURIWidget
-
-    def setUpWidgets(self, ignore_request=False):
-        super(FavoriteEditView,
-              self).setUpWidgets(ignore_request=ignore_request)
-        self.widgets['description'].height = 3

Deleted: Products.CMFDefault/trunk/Products/CMFDefault/browser/file.py
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/file.py	2010-06-25 12:36:59 UTC (rev 113834)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/file.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -1,126 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2007 Zope Foundation and Contributors.
-#
-# 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 files.
-
-$Id$
-"""
-
-from zope.component import adapts
-from zope.formlib import form
-from zope.interface import implements
-from zope.interface import Interface
-from zope.schema import ASCIILine
-from zope.schema import Text
-from zope.schema import TextLine
-
-from Products.CMFDefault.formlib.form import ContentAddFormBase
-from Products.CMFDefault.formlib.form import ContentEditFormBase
-from Products.CMFDefault.formlib.schema import FileUpload
-from Products.CMFDefault.formlib.schema import ProxyFieldProperty
-from Products.CMFDefault.formlib.schema import SchemaAdapterBase
-from Products.CMFDefault.interfaces import IMutableFile
-from Products.CMFDefault.utils import Message as _
-
-
-class IFileSchema(Interface):
-
-    """Schema for file views.
-    """
-
-    title = TextLine(
-        title=_(u'Title'),
-        required=False,
-        missing_value=u'')
-
-    language = TextLine(
-        title=_(u'Language'),
-        required=False,
-        missing_value=u'',
-        max_length=2)
-
-    description = Text(
-        title=_(u'Description'),
-        required=False,
-        missing_value=u'')
-
-    format = ASCIILine(
-        title=_(u'Content type'),
-        readonly=True)
-
-    file = FileUpload(
-        title=_(u'Upload'),
-        required=False)
-
-
-class FileSchemaAdapter(SchemaAdapterBase):
-
-    """Adapter for IMutableFile.
-    """
-
-    adapts(IMutableFile)
-    implements(IFileSchema)
-
-    def _getFile(self):
-        return ''
-
-    def _setFile(self, value):
-        self.context.manage_upload(value)
-
-    title = ProxyFieldProperty(IFileSchema['title'], 'Title', 'setTitle')
-    language = ProxyFieldProperty(IFileSchema['language'],
-                                  'Language', 'setLanguage')
-    description = ProxyFieldProperty(IFileSchema['description'],
-                                     'Description', 'setDescription')
-    format = ProxyFieldProperty(IFileSchema['format'], 'Format')
-    file = property(_getFile, _setFile)
-
-
-class FileAddView(ContentAddFormBase):
-
-    """Add view for IMutableFile.
-    """
-
-    form_fields = (
-        form.FormFields(IFileSchema).select('title', 'description') +
-        form.FormFields(FileUpload(__name__='file', title=_(u'Upload')))
-        )
-
-    def setUpWidgets(self, ignore_request=False):
-        super(FileAddView,
-              self).setUpWidgets(ignore_request=ignore_request)
-        self.widgets['description'].height = 3
-        self.widgets['file'].displayWidth = 60
-
-    def create(self, data):
-        obj = super(FileAddView,
-                    self).create(dict(id=data['file'].filename))
-        adapted = IFileSchema(obj)
-        adapted.title = data['title']
-        adapted.language = u''
-        adapted.description = data['description']
-        adapted.file = data['file']
-        return obj
-
-
-class FileEditView(ContentEditFormBase):
-
-    """Edit view for IMutableFile.
-    """
-
-    form_fields = form.FormFields(IFileSchema).omit('language')
-
-    def setUpWidgets(self, ignore_request=False):
-        super(FileEditView,
-              self).setUpWidgets(ignore_request=ignore_request)
-        self.widgets['description'].height = 3
-        self.widgets['file'].displayWidth = 60

Deleted: Products.CMFDefault/trunk/Products/CMFDefault/browser/folder.py
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/folder.py	2010-06-25 12:36:59 UTC (rev 113834)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/folder.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -1,618 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2006 Zope Foundation and Contributors.
-#
-# 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$
-"""
-
-import urllib
-
-from five.formlib.formbase import PageForm
-from zope.formlib import form
-from zope import schema
-from zope.schema.vocabulary import SimpleTerm
-from zope.schema.vocabulary import SimpleVocabulary
-from zope.sequencesort.ssort import sort
-from ZTUtils import Batch
-from ZTUtils import LazyFilter
-
-from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
-
-from Products.CMFCore.interfaces import IDynamicType
-
-from Products.CMFDefault.browser.interfaces import IDeltaItem
-from Products.CMFDefault.browser.interfaces import IFolderItem
-from Products.CMFDefault.browser.interfaces import IHidden
-from Products.CMFDefault.browser.utils import decode
-from Products.CMFDefault.browser.utils import memoize
-from Products.CMFDefault.browser.utils import ViewBase
-from Products.CMFDefault.exceptions import CopyError
-from Products.CMFDefault.exceptions import zExceptions_Unauthorized
-from Products.CMFDefault.formlib.form import _EditFormMixin
-from Products.CMFDefault.permissions import ListFolderContents
-from Products.CMFDefault.permissions import ManageProperties
-from Products.CMFDefault.utils import Message as _
-
-
-def contents_delta_vocabulary(context):
-    """Vocabulary for the pulldown for moving objects up and down.
-    """
-    length = len(context.contentIds())
-    deltas = [SimpleTerm(i, str(i), str(i)) 
-            for i in range(1, min(5, length)) + range(5, length, 5)]
-    return SimpleVocabulary(deltas)
-
-
-class BatchViewBase(ViewBase):
-    """ Helper class for creating batch-based views.
-    """
-
-    _BATCH_SIZE = 25
-    hidden_fields = form.FormFields(IHidden)
-    prefix = ''
-    
-    @memoize
-    def setUpWidgets(self, ignore_request=False):
-        self.hidden_widgets = form.setUpWidgets(self.hidden_fields, self.prefix, 
-                                                self.context, self.request, 
-                                                ignore_request=ignore_request)
-
-    @memoize
-    def _getBatchStart(self):
-        return self._getHiddenVars().get('b_start', 0)
-
-    @memoize
-    def _getBatchObj(self):
-        b_start = self._getBatchStart()
-        items = self._get_items()
-        return Batch(items, self._BATCH_SIZE, b_start, orphan=0)
-
-    @memoize
-    def _getHiddenVars(self):
-        data = {}
-        if hasattr(self, 'hidden_widgets'):
-            form.getWidgetsData(self.hidden_widgets, self.prefix, data)
-        return data
-
-    @memoize
-    def _getNavigationVars(self):
-        return self._getHiddenVars()
-
-    @memoize
-    def expand_prefix(self, key,):
-        """Return a form specific query key for use in GET strings"""
-        return "%s%s" % (form.expandPrefix(self.prefix), key)
-
-    @memoize
-    def _getNavigationURL(self, b_start=None):
-        target = self._getViewURL()
-        kw = self._getNavigationVars().copy()
-        if 'bstart' not in kw:
-            kw['b_start'] = b_start
-
-        for k, v in kw.items():
-            if not v or k == 'portal_status_message':
-                pass
-            else:
-                new_key = self.expand_prefix(k)
-                kw[new_key] = v
-            del kw[k]
-
-        query = kw and ('?%s' % urllib.urlencode(kw)) or ''
-
-        return u'%s%s' % (target, query)
-
-    # interface
-
-    @memoize
-    @decode
-    def listBatchItems(self):
-        batch_obj = self._getBatchObj()
-
-        items = []
-        for item in batch_obj:
-            item_description = item.Description()
-            item_title = item.Title()
-            item_type = remote_type = item.Type()
-            if item_type == 'Favorite':
-                try:
-                    item = item.getObject()
-                    item_description = item_description or item.Description()
-                    item_title = item_title or item.Title()
-                    remote_type = item.Type()
-                except KeyError:
-                    pass
-            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.getIconURL(),
-                          '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}
-
-    def page_range(self):
-        """Create a range of up to ten pages around the current page"""
-        pages = [(idx + 1, b_start) for idx, b_start in enumerate(
-                    range(0, 
-                        self._getBatchObj().sequence_length, 
-                        self._BATCH_SIZE)
-                    )
-                ]
-        range_start = max(self.page_number() - 5, 0)
-        range_stop = min(max(self.page_number() + 5, 10), len(pages))
-        _page_range = []
-        for page, b_start in pages[range_start:range_stop]:
-            _page_range.append(
-                {'number':page, 
-                 'url':self._getNavigationURL(b_start)
-                }
-                              )
-        return _page_range
-
-    @memoize
-    def page_count(self):
-        """Count total number of pages in the batch"""
-        batch_obj = self._getBatchObj()
-        count = (batch_obj.sequence_length - 1) / self._BATCH_SIZE + 1
-        return count
-
-    @memoize
-    def page_number(self):
-        """Get the number of the current page in the batch"""
-        return (self._getBatchStart() / self._BATCH_SIZE) + 1
-
-    @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')       
-
-
-class ContentsView(BatchViewBase, _EditFormMixin, PageForm):
-    """Folder contents view"""
-    
-    template = ViewPageTemplateFile('templates/folder_contents.pt')
-    prefix = 'form'
-    
-    object_actions = form.Actions(
-        form.Action(
-            name='rename',
-            label=_(u'Rename'),
-            validator='validate_items',
-            condition='has_subobjects',
-            success='handle_rename'),
-        form.Action(
-            name='cut',
-            label=_(u'Cut'),
-            condition='has_subobjects',
-            validator='validate_items',
-            success='handle_cut'),
-        form.Action(
-            name='copy',
-            label=_(u'Copy'),
-            condition='has_subobjects',
-            validator='validate_items',
-            success='handle_copy'),
-        form.Action(
-            name='paste',
-            label=_(u'Paste'),
-            condition='check_clipboard_data',
-            success='handle_paste'),
-        form.Action(
-            name='delete',
-            label=_(u'Delete'),
-            condition='has_subobjects',
-            validator='validate_items',
-            success='handle_delete')
-            )
-            
-    delta_actions = form.Actions(
-        form.Action(
-            name='up',
-            label=_(u'Up'),
-            condition='is_orderable',
-            validator='validate_items',
-            success='handle_up'),
-        form.Action(
-            name='down',
-            label=_(u'Down'),
-            condition='is_orderable',
-            validator='validate_items',
-            success='handle_down')
-            )
-            
-    absolute_actions = form.Actions(
-        form.Action(
-            name='top',
-            label=_(u'Top'),
-            condition='is_orderable',
-            validator='validate_items',
-            success='handle_top'),
-        form.Action(
-            name='bottom',
-            label=_(u'Bottom'),
-            condition='is_orderable',
-            validator='validate_items',
-            success='handle_bottom')
-            )
-
-    sort_actions = form.Actions(
-        form.Action(
-            name='sort_order',
-            label=_(u'Set as Default Sort'),
-            condition='can_sort_be_changed',
-            validator='validate_items',
-            success='handle_top')
-            )
-            
-    actions = object_actions + delta_actions + absolute_actions + sort_actions
-    errors = ()
-    
-    def __init__(self, *args, **kw):
-        super(ContentsView, self).__init__(*args, **kw)
-        self.form_fields = form.FormFields()
-        self.delta_field = form.FormFields(IDeltaItem)
-        self.contents = self.context.contentValues()        
-                
-    def content_fields(self):
-        """Create content field objects only for batched items"""
-        for item in self._getBatchObj():
-            for name, field in schema.getFieldsInOrder(IFolderItem):
-                field = form.FormField(field, name, item.id)
-                self.form_fields += form.FormFields(field)
-
-    @memoize
-    @decode
-    def up_info(self):
-        """Link to the contens view of the parent object"""
-        up_obj = self.context.aq_inner.aq_parent
-        mtool = self._getTool('portal_membership')
-        allowed = mtool.checkPermission(ListFolderContents, up_obj)
-        if allowed:
-            if IDynamicType.providedBy(up_obj):
-                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 {}
-        
-    def setUpWidgets(self, ignore_request=False):
-        """Create widgets for the folder contents."""
-        super(ContentsView, self).setUpWidgets(ignore_request)
-        data = {}
-        self.content_fields()
-        for i in self._getBatchObj():
-            data['%s.name' % i.id] = i.getId()
-        self.widgets = form.setUpDataWidgets(
-                self.form_fields, self.prefix, self.context,
-                self.request, data=data, ignore_request=ignore_request)
-        self.widgets += form.setUpWidgets(
-                self.delta_field, self.prefix, self.context,
-                self.request, ignore_request=ignore_request)
-                
-    @memoize
-    def _get_sorting(self):
-        """How should the contents be sorted"""
-        data = self._getHiddenVars()
-        key = data.get('sort_key')
-        if key:
-            return (key, data.get('reverse', 0))
-        else:
-            return self.context.getDefaultSorting()
-            
-    @memoize
-    def _is_default_sorting(self,):
-        return self._get_sorting() == self.context.getDefaultSorting()
-        
-    @memoize
-    def column_headings(self):
-        key, reverse = self._get_sorting()
-        columns = ( {'sort_key': 'Type',
-                     'title': _(u'Type'),
-                     'colspan': '2'}
-                  , {'sort_key': 'getId',
-                     'title': _(u'Name')}
-                  , {'sort_key': 'modified',
-                     'title': _(u'Last Modified')}
-                  , {'sort_key': 'position',
-                     'title': _(u'Position')}
-                  )
-        for column in columns:
-            paras = {'form.sort_key':column['sort_key']}
-            if key == column['sort_key'] \
-            and not reverse and key != 'position':
-                paras['form.reverse'] = 1
-            query = urllib.urlencode(paras)
-            column['url'] = '%s?%s' % (self._getViewURL(), query)
-        return tuple(columns)
-        
-    @memoize
-    def _get_items(self):
-        key, reverse = self._get_sorting()
-        items = self.contents
-        return sort(items, ((key, 'cmp', reverse and 'desc' or 'asc'),))
-
-    @memoize
-    def listBatchItems(self):
-        """Return the widgets for the form in the interface field order"""
-        batch_obj = self._getBatchObj()
-        b_start = self._getBatchStart()
-        key, reverse = self._get_sorting()
-        fields = []
-
-        for idx, item in enumerate(batch_obj):
-            field = {'ModificationDate':item.ModificationDate()}
-            field['select'] = self.widgets['%s.select' % item.getId()]
-            field['name'] = self.widgets['%s.name' % item.getId()]
-            field['url'] = item.absolute_url()
-            field['title'] = item.TitleOrId()
-            field['icon'] = item.icon
-            field['position'] = (key == 'position') \
-                                and str(b_start + idx + 1) \
-                                or '...'
-            field['type'] = item.Type() or None
-            fields.append(field.copy())
-        return fields
-                
-    def _get_ids(self, data):
-        """Identify objects that have been selected"""
-        ids = [k[:-7] for k, v in data.items()
-                 if v is True and k.endswith('.select')]
-        return ids
-
-    #Action conditions
-    @memoize
-    def has_subobjects(self, action=None):
-        """Return false if the user cannot rename subobjects"""
-        return bool(self.contents)
-    
-    @memoize
-    def check_clipboard_data(self, action=None):
-        """Any data in the clipboard"""
-        return bool(self.context.cb_dataValid())
-    
-    @memoize
-    def can_sort_be_changed(self, action=None):
-        """Returns true if the default sort key may be changed 
-            may be sorted for display"""
-        items_move_allowed = self._checkPermission(ManageProperties)
-        return items_move_allowed and not \
-            self._get_sorting() == self.context.getDefaultSorting()
-
-    @memoize
-    def is_orderable(self, action=None):
-        """Returns true if the displayed contents can be
-            reorded."""
-        (key, reverse) = self._get_sorting()        
-        return key == 'position' and len(self.contents) > 1
-    
-    #Action validators
-    def validate_items(self, action=None, data=None):
-        """Check whether any items have been selected for 
-        the requested action."""
-        super(ContentsView, self).validate(action, data)
-        if data is None or data == {}:
-            return [_(u"Please select one or more items first.")]
-        else:
-            return []
-            
-    #Action handlers
-    def handle_rename(self, action, data):
-        """Redirect to rename view passing the ids of objects to be renamed"""
-        # currently redirects to a PythonScript
-        # should be replaced with a dedicated form
-        self.request.form['ids'] = self._get_ids(data)
-        keys = ",".join(self._getHiddenVars().keys() + ['ids'])
-        # keys = 'b_start, ids, key, reverse'
-        return self._setRedirect('portal_types', 'object/rename_items', keys)
-        
-    def handle_cut(self, action, data):
-        """Cut the selected objects and put them in the clipboard"""
-        ids = self._get_ids(data)
-        try:
-            self.context.manage_cutObjects(ids, self.request)
-            if len(ids) == 1:
-                self.status = _(u'Item cut.')
-            else:
-                self.status = _(u'Items cut.')
-        except CopyError:
-            self.status = _(u'CopyError: Cut failed.')
-        except zExceptions_Unauthorized:
-            self.status = _(u'Unauthorized: Cut failed.')
-        return self._setRedirect('portal_types', 'object/new_contents')    
-
-    def handle_copy(self, action, data):
-        """Copy the selected objects to the clipboard"""
-        ids = self._get_ids(data)
-        try:
-            self.context.manage_copyObjects(ids, self.request)
-            if len(ids) == 1:
-                self.status = _(u'Item copied.')
-            else:
-                self.status = _(u'Items copied.')
-        except CopyError:
-            self.status = _(u'CopyError: Copy failed.')
-        return self._setRedirect('portal_types', 'object/new_contents')
-    
-    def handle_paste(self, action, data):
-        """Paste the objects from the clipboard into the folder"""
-        try:
-            result = self.context.manage_pasteObjects(self.request['__cp'])
-            if len(result) == 1:
-                self.status = _(u'Item pasted.')
-            else:
-                self.status = _(u'Items pasted.')
-        except CopyError, error:
-            self.status = _(u'CopyError: Paste failed.')
-            self.request['RESPONSE'].expireCookie('__cp', 
-                    path='%s' % (self.request['BASEPATH1'] or "/"))
-
-        except zExceptions_Unauthorized:
-            self.status = _(u'Unauthorized: Paste failed.')
-        return self._setRedirect('portal_types', 'object/new_contents')
-
-    def handle_delete(self, action, data):
-        """Delete the selected objects"""
-        ids = self._get_ids(data)
-        self.context.manage_delObjects(list(ids))
-        if len(ids) == 1:
-            self.status = _(u'Item deleted.')
-        else:
-            self.status = _(u'Items deleted.')
-        return self._setRedirect('portal_types', 'object/new_contents')
-    
-    def handle_up(self, action, data):
-        """Move the selected objects up the selected number of places"""
-        ids = self._get_ids(data)
-        delta = data.get('delta', 1)
-        subset_ids = [obj.getId()
-                       for obj in self.context.listFolderContents()]
-        try:
-            attempt = self.context.moveObjectsUp(ids, delta,
-                                                 subset_ids=subset_ids)
-            if attempt == 1:
-                self.status = _(u'Item moved up.')
-            elif attempt > 1:
-                self.status = _(u'Items moved up.')
-            else:
-                self.status = _(u'Nothing to change.')
-        except ValueError:
-            self.status = _(u'ValueError: Move failed.')
-        return self._setRedirect('portal_types', 'object/new_contents')
-
-    def handle_down(self, action, data):
-        """Move the selected objects down the selected number of places"""
-        ids = self._get_ids(data)
-        delta = data.get('delta', 1)
-        subset_ids = [obj.getId()
-                       for obj in self.context.listFolderContents()]
-        try:
-            attempt = self.context.moveObjectsDown(ids, delta,
-                                                 subset_ids=subset_ids)
-            if attempt == 1:
-                self.status = _(u'Item moved down.')
-            elif attempt > 1:
-                self.status = _(u'Items moved down.')
-            else:
-                self.status = _(u'Nothing to change.')
-        except ValueError:
-            self.status = _(u'ValueError: Move failed.')
-        return self._setRedirect('portal_types', 'object/new_contents')
-            
-    def handle_top(self, action, data):
-        """Move the selected objects to the top of the page"""
-        ids = self._get_ids(data)
-        subset_ids = [obj.getId()
-                       for obj in self.context.listFolderContents()]
-        try:
-            attempt = self.context.moveObjectsToTop(ids,
-                                                    subset_ids=subset_ids)
-            if attempt == 1:
-                self.status = _(u'Item moved to top.')
-            elif attempt > 1:
-                self.status = _(u'Items moved to top.')
-            else:
-                self.status = _(u'Nothing to change.')
-        except ValueError:
-            self.status = _(u'ValueError: Move failed.')
-        return self._setRedirect('portal_types', 'object/new_contents')
-
-    def handle_bottom(self, action, data):
-        """Move the selected objects to the bottom of the page"""
-        ids = self._get_ids(data)
-        subset_ids = [obj.getId()
-                       for obj in self.context.listFolderContents()]
-        try:
-            attempt = self.context.moveObjectsToBottom(ids,
-                                                       subset_ids=subset_ids)
-            if attempt == 1:
-                self.status = _(u'Item moved to bottom.')
-            elif attempt > 1:
-                self.status = _(u'Items moved to bottom.')
-            else:
-                self.status = _(u'Nothing to change.')
-        except ValueError:
-            self.status = _(u'ValueError: Move failed.')
-        return self._setRedirect('portal_types', 'object/new_contents')
-        
-    def handle_sort_order(self, action, data):
-        """Set the sort options for the folder."""
-        key = data['position']
-        reverse = data.get('reverse', 0)
-        self.context.setDefaultSorting(key, reverse)
-        self.status = _(u"Sort order changed")
-        return self._setRedirect('portal_types', 'object/new_contents')
-
-
-class FolderView(BatchViewBase):
-
-    """View for IFolderish.
-    """
-
-    @memoize
-    def _get_items(self):
-        (key, reverse) = self.context.getDefaultSorting()
-        items = self.context.contentValues()
-        items = sort(items, ((key, 'cmp', reverse and 'desc' or 'asc'),))
-        return LazyFilter(items, skip='View')
-
-    @memoize
-    def has_local(self):
-        return 'local_pt' in self.context.objectIds()

Added: Products.CMFDefault/trunk/Products/CMFDefault/browser/form/__init__.py
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/form/__init__.py	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/form/__init__.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,16 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Foundation and Contributors.
+#
+# 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 form views.
+
+$Id$
+"""


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

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/form/batch_widgets.pt (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/templates/batch_widgets.pt)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/form/batch_widgets.pt	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/form/batch_widgets.pt	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,76 @@
+<html>
+<body>
+
+ <metal:macro metal:define-macro="summary" i18n:domain="cmf_default"
+ ><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" i18n:domain="cmf_default">
+ <p class="BatchListing" tal:repeat="item_info view/listBatchItems"
+ ><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>
+
+<metal:macro metal:define-macro="pagination"
+  tal:define="current_page view/page_number;
+              prev_info view/navigation_previous;
+              next_info view/navigation_next">
+	<a href="" 
+	   tal:condition="prev_info"
+		 tal:attributes="href prev_info/url">&lt;&lt;</a>
+  <tal:repeat condition="python: prev_info or next_info"
+                        repeat="page view/page_range">
+		<a tal:condition="python: not page['number'] == current_page" 
+		  tal:attributes="href page/url"
+		  tal:content="page/number">Page number</a>
+	  <span tal:condition="python: page['number'] == current_page"
+	        tal:content="page/number">Page number</span>
+		<tal:condition condition="not: repeat/page/end">|</tal:condition> 
+	</tal:repeat>
+	
+	<a href="" 
+	   tal:condition="python: current_page != view.page_count() and next_info" 
+	   tal:attributes="href next_info/url">&gt;&gt;</a>
+</metal:macro>
+
+</body>
+</html>

Added: Products.CMFDefault/trunk/Products/CMFDefault/browser/form/configure.zcml
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/form/configure.zcml	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/form/configure.zcml	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,22 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:browser="http://namespaces.zope.org/browser">
+    
+
+  <browser:page
+      for="*"
+      layer="Products.CMFDefault.interfaces.ICMFDefaultSkin"
+      name="form_widget"
+      template="form_widgets.pt"
+      permission="zope2.View"
+      />
+
+  <browser:page
+      for="*"
+      layer="Products.CMFDefault.interfaces.ICMFDefaultSkin"
+      name="batch_widget"
+      template="batch_widgets.pt"
+      permission="zope2.View"
+      />
+
+</configure>

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/form/form_widgets.pt (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/templates/form_widgets.pt)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/form/form_widgets.pt	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/form/form_widgets.pt	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,19 @@
+<html>
+<body>
+
+<metal:macro metal:define-macro="hidden_vars" i18n:domain="cmf_default">
+ <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" i18n:domain="cmf_default"
+><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>

Deleted: Products.CMFDefault/trunk/Products/CMFDefault/browser/icons.py
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/icons.py	2010-06-25 12:36:59 UTC (rev 113834)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/icons.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -1,73 +0,0 @@
-"""CSS for action icons
-$Id$
-"""
-from logging import getLogger
-
-LOG = getLogger("Action Icons CSS")
-
-from zope.component import getUtility
-
-from Products.Five.browser import BrowserView
-
-from Products.CMFCore.interfaces import IPropertiesTool
-from Products.CMFCore.Expression import getExprContext
-from Products.CMFCore.utils import getToolByName
-
-from Products.CMFDefault.browser.utils import memoize
-
-class View(BrowserView):
-    
-    def __init__(self, context, request):
-        super(View, self).__init__(context, request)
-        self.show_icons = self._show_icons
-
-    @property
-    @memoize
-    def _show_icons(self):
-        """Are action icons enabled?"""
-        ptool = getUtility(IPropertiesTool)
-        show = ptool.getProperty('enable_actionicons')
-        if show:
-            self.icon = ".icon {padding-left: 1.5em;}\n\n"
-        else:
-            self.icon = ".icon {padding-left: 0.5em;}\n\n"
-        return show
-
-    @property
-    @memoize
-    def template(self):
-        """Always return a template so there are no browser errors"""
-        if self.show_icons:    
-            return ".%s {background: url(%s) no-repeat 0.1em}"
-        else:
-            return ".%s {/* %s */}"
-
-    @memoize
-    def actions(self):
-        """List all action icons"""
-        atool = getToolByName(self.context, 'portal_actions')
-        all_actions = atool.listFilteredActionsFor(self.context)
-        icons = []
-        for cat in ['user', 'object', 'folder', 'workflow', 'global']:
-            cat_actions = all_actions[cat]
-            icons.append("/* %s actions */" % cat)
-            for a in cat_actions:
-                icons.append(self.template % (a['id'], a['icon']))
-        return "\n\n".join(icons)
-
-    @memoize
-    def types(self):
-        """List all type icons"""
-        ttool = getToolByName(self.context, 'portal_types')
-        types = ttool.listTypeInfo()
-        econtext = getExprContext(self.context)
-        icons = [self.template %  (t.id,
-                                  t.getIconExprObject()(econtext)) \
-                for t in types]
-        return "\n\n".join(icons)
-
-    def __call__(self):
-        self.request.response.setHeader("content-type", "text/css")
-        self.request.response.write(self.icon)
-        self.request.response.write(self.actions())
-        self.request.response.write(self.types())

Deleted: Products.CMFDefault/trunk/Products/CMFDefault/browser/interfaces.py
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/interfaces.py	2010-06-25 12:36:59 UTC (rev 113834)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/interfaces.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -1,61 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2009 Zope Foundation and Contributors.
-#
-# 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 interfaces.
-
-$Id$
-"""
-
-from zope.interface import Interface
-from zope.schema import Bool
-from zope.schema import Choice
-from zope.schema import Int
-from zope.schema import TextLine
-
-
-class IFolderItem(Interface):
-    """Schema for folderish objects contents."""
-    
-    select = Bool(
-        required=False)
-        
-    name = TextLine(
-        title=u"Name",
-        required=False,
-        readonly=True)
-
-
-class IDeltaItem(Interface):
-    """Schema for delta"""    
-    delta = Choice(
-        title=u"By",
-        description=u"Move an object up or down the chosen number of places.",
-        required=False,
-        vocabulary=u'cmf.contents delta vocabulary',
-        default=1)
-
-        
-class IHidden(Interface):
-    """Schema for hidden items"""
-    
-    b_start = Int(
-        title=u"Batch start",
-        required=False,
-        default=0)
-        
-    sort_key = TextLine(
-        title=u"Sort key",
-        required=False)
-        
-    reverse = Int(
-        title=u"Reverse sort order",
-        required=False)

Deleted: Products.CMFDefault/trunk/Products/CMFDefault/browser/link.py
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/link.py	2010-06-25 12:36:59 UTC (rev 113834)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/link.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -1,155 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2006 Zope Foundation and Contributors.
-#
-# 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 links.
-
-$Id$
-"""
-
-import urlparse
-
-from zope.app.form.browser import BytesWidget
-from zope.component import adapts
-from zope.formlib import form
-from zope.interface import implements
-from zope.interface import Interface
-from zope.schema import ASCIILine
-from zope.schema import BytesLine
-from zope.schema import Text
-from zope.schema import TextLine
-
-from Products.CMFDefault.formlib.form import ContentAddFormBase
-from Products.CMFDefault.formlib.form import ContentEditFormBase
-from Products.CMFDefault.formlib.schema import ProxyFieldProperty
-from Products.CMFDefault.formlib.schema import SchemaAdapterBase
-from Products.CMFDefault.formlib.widgets import IDInputWidget
-from Products.CMFDefault.interfaces import IMutableLink
-from Products.CMFDefault.utils import Message as _
-
-from utils import decode
-from utils import memoize
-from utils import ViewBase
-
-
-class ILinkSchema(Interface):
-
-    title = TextLine(
-        title=_(u'Title'),
-        required=False,
-        missing_value=u'')
-
-    language = TextLine(
-        title=_(u'Language'),
-        required=False,
-        missing_value=u'',
-        max_length=2)
-
-    description = Text(
-        title=_(u'Description'),
-        required=False,
-        missing_value=u'')
-
-    remote_url = BytesLine(
-        title=_(u'URL'),
-        required=False,
-        missing_value=u'')
-
-
-class LinkSchemaAdapter(SchemaAdapterBase):
-
-    adapts(IMutableLink)
-    implements(ILinkSchema)
-
-    title = ProxyFieldProperty(ILinkSchema['title'], 'Title', 'setTitle')
-    language = ProxyFieldProperty(ILinkSchema['language'],
-                                  'Language', 'setLanguage')
-    description = ProxyFieldProperty(ILinkSchema['description'],
-                                     'Description', 'setDescription')
-    remote_url = ProxyFieldProperty(ILinkSchema['remote_url'])
-
-
-class LinkView(ViewBase):
-
-    """View for ILink.
-    """
-
-    # interface
-
-    @memoize
-    @decode
-    def url(self):
-        return self.context.getRemoteUrl()
-
-
-class LinkURIWidget(BytesWidget):
-
-    """Custom widget for remote_url.
-    """
-
-    def _toFieldValue(self, input):
-        value = super(LinkURIWidget, self)._toFieldValue(input)
-        if not value:
-            return value
-        tokens = urlparse.urlparse(value, 'http')
-        if tokens[0] == 'http':
-            if tokens[1]:
-                # We have a nethost. All is well.
-                return urlparse.urlunparse(tokens)
-            elif tokens[2:] == ('', '', '', ''):
-                # Empty URL
-                return u''
-            else:
-                # Relative URL, keep it that way, without http:
-                tokens = ('', '') + tokens[2:]
-                return urlparse.urlunparse(tokens)
-        else:
-            # Other scheme, keep original
-            return urlparse.urlunparse(tokens)
-
-
-class LinkAddView(ContentAddFormBase):
-
-    """Add view for IMutableLink.
-    """
-
-    form_fields = (
-        form.FormFields(ASCIILine(__name__='id', title=_(u'ID'))) +
-        form.FormFields(ILinkSchema).omit('language')
-        )
-    form_fields['id'].custom_widget = IDInputWidget
-    form_fields['remote_url'].custom_widget = LinkURIWidget
-
-    def setUpWidgets(self, ignore_request=False):
-        super(LinkAddView, self).setUpWidgets(ignore_request=ignore_request)
-        self.widgets['description'].height = 3
-
-    def create(self, data):
-        obj = super(LinkAddView, self).create(dict(id=data['id']))
-        adapted = ILinkSchema(obj)
-        adapted.title = data['title']
-        adapted.language = u''
-        adapted.description = data['description']
-        adapted.remote_url = data['remote_url']
-        return obj
-
-
-class LinkEditView(ContentEditFormBase):
-
-    """Edit view for IMutableLink.
-    """
-
-    form_fields = form.FormFields(ILinkSchema).omit('language')
-    form_fields['remote_url'].custom_widget = LinkURIWidget
-
-    def setUpWidgets(self, ignore_request=False):
-        super(LinkEditView, self).setUpWidgets(ignore_request=ignore_request)
-        self.widgets['description'].height = 3

Added: Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/__init__.py
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/__init__.py	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/__init__.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,16 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Foundation and Contributors.
+#
+# 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 membership views.
+
+$Id$
+"""


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

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/authentication.py (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/authentication.py)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/authentication.py	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/authentication.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,281 @@
+##############################################################################
+#
+# Copyright (c) 2010 Zope Foundation and Contributors.
+#
+# 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.
+#
+##############################################################################
+"""Authentication browser views.
+
+$Id$
+"""
+
+from urllib import quote, urlencode
+
+from Products.Five import BrowserView
+from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
+from zExceptions import Forbidden
+from zExceptions import Redirect
+from zope.app.form.browser import TextWidget
+from zope.formlib import form
+from zope.interface import implements
+from zope.interface import Interface
+from zope.schema import Bool
+from zope.schema import Choice
+from zope.schema import Password
+from zope.schema import TextLine
+from zope.schema import URI
+from zope.schema.interfaces import ISource
+from zope.site.hooks import getSite
+
+from Products.CMFCore.CookieCrumbler import ATTEMPT_LOGIN
+from Products.CMFCore.CookieCrumbler import ATTEMPT_NONE
+from Products.CMFCore.utils import getToolByName
+from Products.CMFDefault.formlib.form import EditFormBase
+from Products.CMFDefault.utils import Message as _
+from Products.CMFDefault.browser.utils import ViewBase, memoize
+
+
+def _expireAuthCookie(view):
+    try:
+        cctool = getToolByName(view, 'cookie_authentication')
+        method = cctool.getCookieMethod('expireAuthCookie',
+                                        cctool.defaultExpireAuthCookie)
+        method(view.request.response, cctool.auth_cookie)
+    except AttributeError:
+        view.request.response.expireCookie('__ac', path='/')
+
+
+class UnauthorizedView(BrowserView):
+
+    """Exception view for Unauthorized.
+    """
+
+    forbidden_template = ViewPageTemplateFile('templates/forbidden.pt')
+
+    def __call__(self):
+        try:
+            atool = getToolByName(self, 'portal_actions')
+            target = atool.getActionInfo('user/login')['url']
+        except (AttributeError, ValueError):
+            # re-raise the unhandled exception
+            raise self.context
+
+        req = self.request
+        attempt = getattr(req, '_cookie_auth', ATTEMPT_NONE)
+        if attempt not in (ATTEMPT_NONE, ATTEMPT_LOGIN):
+            # An authenticated user was denied access to something.
+            # XXX: hack context to get the right @@standard_macros/page
+            #      why do we get the wrong without this hack?
+            self.context = self.__parent__
+            raise Forbidden(self.forbidden_template())
+
+        _expireAuthCookie(self)
+        came_from = req.get('came_from', None)
+        if came_from is None:
+            came_from = req.get('ACTUAL_URL')
+            query = req.get('QUERY_STRING')
+            if query:
+                # Include the query string in came_from
+                if not query.startswith('?'):
+                    query = '?' + query
+                came_from = came_from + query
+        url = '%s?came_from=%s' % (target, quote(came_from))
+        raise Redirect(url)
+
+
+class NameSource(object):
+
+    implements(ISource)
+
+    def __contains__(self, value):
+        rich_context = getSite()
+        mtool = getToolByName(rich_context, 'portal_membership')
+        if mtool.getMemberById(value):
+            return True
+        candidates = mtool.searchMembers('email', value)
+        for candidate in candidates:
+            if candidate['email'].lower() == value.lower():
+                return True
+        return False
+
+available_names = NameSource()
+
+
+class ILoginSchema(Interface):
+
+    """Schema for login form.
+    """
+
+    came_from = URI(
+        required=False)
+
+    name = TextLine(
+        title=_(u'Member ID'),
+        description=_(u'Case sensitive'))
+
+    password = Password(
+        title=_(u'Password'),
+        description=_(u'Case sensitive'))
+
+    persistent = Bool(
+        title=_(u'Remember my ID.'),
+        description=_(u'Saves your member ID in a cookie.'),
+        default=True)
+
+
+class IMailPasswordSchema(Interface):
+
+    """Schema for mail password form.
+    """
+
+    name = Choice(
+        title=_(u'Member ID'),
+        description=_(u'Member ID or email address'),
+        source=available_names)
+
+
+class LoginFormView(EditFormBase):
+
+    """Form view for ILoginSchema.
+    """
+
+    base_template = EditFormBase.template
+    template = ViewPageTemplateFile('templates/login.pt')
+    label = _(u'Log in')
+    prefix = ''
+
+    form_fields = form.FormFields(ILoginSchema)
+
+    actions = form.Actions(
+        form.Action(
+            name='login',
+            label=_(u'Login'),
+            validator='handle_login_validate',
+            success='handle_login_success',
+            failure='handle_failure'))
+
+    def setUpWidgets(self, ignore_request=False):
+        try:
+            cctool = self._getTool('cookie_authentication')
+            ac_name_id = cctool.name_cookie
+            ac_password_id = cctool.pw_cookie
+            ac_persistent_id = cctool.persist_cookie
+        except AttributeError:
+            ac_name_id = '__ac_name'
+            ac_password_id = '__ac_password'
+            ac_persistent_id = '__ac_persistent'
+        ac_name = self.request.get(ac_name_id)
+        if ac_name is not None:
+            self.request.form['name'] = ac_name
+            self.request.form[ac_name_id] = ac_name
+        ac_persistent = self.request.get(ac_persistent_id)
+        if ac_persistent is not None:
+            self.request.form['persistent'] = ac_persistent
+        ac_persistent_used = self.request.get("%s.used" % ac_persistent_id)
+        if ac_persistent_used is not None:
+            self.request.form['persistent.used'] = ac_persistent_used
+        super(LoginFormView,
+              self).setUpWidgets(ignore_request=ignore_request)
+        self.widgets['came_from'].hide = True
+        self.widgets['name'].name = ac_name_id
+        self.widgets['password'].name = ac_password_id
+        self.widgets['persistent'].name = ac_persistent_id
+
+    def handle_login_validate(self, action, data):
+        mtool = self._getTool('portal_membership')
+        if mtool.isAnonymousUser():
+            _expireAuthCookie(self)
+            return (_(u'Login failure'),)
+        return None
+
+    def handle_login_success(self, action, data):
+        return self._setRedirect('portal_actions', 'user/logged_in',
+                                 'came_from')
+
+
+class MailPasswordFormView(EditFormBase):
+
+    """Form view for IMailPasswordSchema.
+    """
+
+    base_template = EditFormBase.template
+    template = ViewPageTemplateFile('templates/mail_password.pt')
+    label = _(u"Don't panic!")
+    description = _(u"Just enter your member ID below, click 'Send', and "
+                    u"your password will be mailed to you if you gave a "
+                    u"valid email address when you signed on.")
+
+    form_fields = form.FormFields(IMailPasswordSchema)
+    form_fields['name'].custom_widget = TextWidget
+
+    actions = form.Actions(
+        form.Action(
+            name='send',
+            label=_(u'Send'),
+            success='handle_send_success',
+            failure='handle_failure'))
+
+    def setUpWidgets(self, ignore_request=False):
+        try:
+            cctool = self._getTool('cookie_authentication')
+            ac_name_id = cctool.name_cookie
+        except AttributeError:
+            ac_name_id = '__ac_name'
+        ac_name = self.request.get(ac_name_id)
+        if ac_name and not self.request.has_key('%s.name' % self.prefix):
+            self.request.form['%s.name' % self.prefix] = ac_name
+        super(MailPasswordFormView,
+              self).setUpWidgets(ignore_request=ignore_request)
+
+    def handle_send_success(self, action, data):
+        mtool = self._getTool('portal_membership')
+        if not mtool.getMemberById(data['name']):
+            candidates = mtool.searchMembers('email', data['name'])
+            for candidate in candidates:
+                if candidate['email'].lower() == data['name'].lower():
+                    data['name'] = candidate['username']
+                    break
+        rtool = self._getTool('portal_registration')
+        rtool.mailPassword(data['name'], self.request)
+        self.status = _(u'Your password has been mailed to you.')
+        return self._setRedirect('portal_actions', 'user/login')
+
+
+class Logout(ViewBase):
+    """Log the user out"""
+    
+    template = ViewPageTemplateFile("templates/logged_out.pt")
+    
+    @memoize
+    def logged_in(self):
+        """Check whether the user is (still logged in)"""
+        mtool = self._getTool('portal_membership')
+        return not mtool.isAnonymousUser()
+        
+    @memoize
+    def logout(self):
+        """Log the user out"""
+        _expireAuthCookie(self)
+    
+    @memoize
+    def clear_skin_cookie(self):
+        """Remove skin cookie"""
+        stool = self._getTool('portal_skins')
+        stool.clearSkinCookie()
+    
+    def __call__(self):
+        """Clear cookies and return the template"""
+        if 'portal_status_message' in self.request:
+            return self.template()
+        if self.logged_in():
+            self.clear_skin_cookie()
+            self.logout()
+            status = "?" + urlencode({'portal_status_message':
+                                      _(u'You have been logged out.')})
+            return self.request.response.redirect(self.request.URL + status)

Added: Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/configure.zcml
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/configure.zcml	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/configure.zcml	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,37 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:browser="http://namespaces.zope.org/browser">
+
+  <browser:page
+      for="Products.CMFCore.interfaces.ISiteRoot"
+      layer="Products.CMFDefault.interfaces.ICMFDefaultSkin"
+      name="login.html"
+      class=".authentication.LoginFormView"
+      permission="zope2.View"
+      />
+
+  <browser:page
+      for="Products.CMFCore.interfaces.ISiteRoot"
+      layer="Products.CMFDefault.interfaces.ICMFDefaultSkin"
+      name="logout.html"
+      class=".authentication.Logout"
+      permission="zope2.View"
+      />
+
+  <browser:page
+      for="Products.CMFCore.interfaces.ISiteRoot"
+      layer="Products.CMFDefault.interfaces.ICMFDefaultSkin"
+      name="mail_password.html"
+      class=".authentication.MailPasswordFormView"
+      permission="zope2.View"
+      />
+
+  <browser:page
+      for="zExceptions.Unauthorized"
+      layer="Products.CMFDefault.interfaces.ICMFDefaultSkin"
+      name="index.html"
+      class=".authentication.UnauthorizedView"
+      permission="zope.Public"
+      />
+
+</configure>

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/templates/forbidden.pt (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/templates/forbidden.pt)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/templates/forbidden.pt	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/templates/forbidden.pt	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,18 @@
+<html metal:use-macro="context/@@standard_macros/page">
+<body>
+
+<metal:slot metal:fill-slot="body" i18n:domain="cmf_default">
+<h1 id="DesktopTitle">Site Error</h1>
+
+<p id="DesktopDescription" i18n:translate="">An error was encountered while
+ publishing this resource.</p>
+
+<p><strong><tal:span i18n:translate="">Error Type:</tal:span>
+ <tal:span i18n:translate="">Forbidden</tal:span></strong><br />
+ <strong><tal:span i18n:translate="">Error Value:</tal:span>
+ <tal:span i18n:translate="">You don't have all the permissions necessary
+  to see the requested page.</tal:span></strong></p>
+</metal:slot>
+
+</body>
+</html>

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/templates/logged_out.pt (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/templates/logged_out.pt)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/templates/logged_out.pt	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/templates/logged_out.pt	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,11 @@
+<html metal:use-macro="context/@@standard_macros/page">
+<body>
+
+<metal:slot metal:fill-slot="body" i18n:domain="cmf_default">
+<p tal:condition="view/logged_in"
+   i18n:translate="">You are logged in outside the portal. You may need to
+<a href="/manage_zmi_logout">log out of the Zope management interface</a>.</p>
+</metal:slot>
+
+</body>
+</html>

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/templates/login.pt (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/templates/login.pt)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/templates/login.pt	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/templates/login.pt	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,20 @@
+<html metal:use-macro="context/main_template/macros/master">
+<body>
+
+<metal:slot metal:fill-slot="body" i18n:domain="cmf_default">
+<metal:macro metal:use-macro="view/base_template/macros/form" />
+
+<p><a href=""
+    tal:attributes="href string:${portal_url}/mail_password.html"
+    i18n:translate=""
+   >I forgot my password!</a></p>
+
+<p i18n:translate="">Having trouble logging in? Make sure to enable cookies in
+ your web browser.</p>
+
+<p i18n:translate="">Don't forget to logout or exit your browser when you're
+ done.</p>
+</metal:slot>
+
+</body>
+</html>

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/templates/mail_password.pt (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/templates/mail_password.pt)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/templates/mail_password.pt	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/templates/mail_password.pt	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,18 @@
+<html metal:use-macro="context/main_template/macros/master">
+<body>
+
+<metal:slot metal:fill-slot="body" i18n:domain="cmf_default">
+<metal:macro metal:use-macro="view/base_template/macros/form" />
+
+<p i18n:translate="">If this will not work for you (for example, if you forget
+ your member ID or didn't enter your email address) send email to
+ <a href="mailto:me at example.org"
+    tal:define="ptool context/portal_properties"
+    tal:attributes="href string:mailto:${ptool/email_from_address}"
+    tal:content="ptool/email_from_address"
+    i18n:name="admin_email">me at example.org</a>.</p>
+
+</metal:slot>
+
+</body>
+</html>

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/tests/authentication.txt (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/tests/authentication.txt)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/tests/authentication.txt	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/tests/authentication.txt	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,167 @@
+Authentication Views
+--------------------
+
+Set up user.
+
+    >>> from urllib import quote
+    >>> uf = app.site.acl_users
+    >>> uf._doAddUser('mbr', 'mbrpw', ['Member'], [])
+    >>> mbr_credentials = quote('mbr:mbrpw'.encode('base64').rstrip())
+
+Create the browser object we'll be using.
+
+    # BBB for Zope 2.12
+    >>> try:
+    ...     from Testing.testbrowser import Browser
+    ... except ImportError:
+    ...     from Products.Five.testbrowser import Browser
+    >>> browser = Browser()
+    >>> # XXX: browser has no API for disabling redirects
+    >>> browser.mech_browser.set_handle_redirect(False)
+
+The view for zExceptions.Unauthorized redirects anonymous requests to the
+login_form. This works if raised by the object (here by the reconfig_form).
+
+    >>> browser.open('http://localhost/site/reconfig_form')
+    Traceback (most recent call last):
+    ...
+    HTTPError: HTTP Error 302: Moved Temporarily
+    >>> browser.contents
+    ''
+    >>> browser.headers['Location']
+    'http://localhost/site/login_form?came_from=http%3A//localhost/site/reconfig_form'
+
+And it works if raised by BaseRequest.traverse (here caused by manage_main).
+
+    >>> browser.open('http://localhost/site/manage_main')
+    Traceback (most recent call last):
+    ...
+    HTTPError: HTTP Error 302: Moved Temporarily
+    >>> browser.contents
+    ''
+    >>> browser.headers['Location']
+    'http://localhost/site/login_form?came_from=http%3A//localhost/site/manage_main'
+
+Same redirect with a query string. The query string is preserved.
+
+    >>> browser.open('http://localhost/site/manage_main?a:int=1&x:string=y')
+    Traceback (most recent call last):
+    ...
+    HTTPError: HTTP Error 302: Moved Temporarily
+    >>> browser.contents
+    ''
+    >>> browser.headers['Location']
+    'http://localhost/site/login_form?came_from=http%3A//localhost/site/manage_main%3Fa%3Aint%3D1%26x%3Astring%3Dy'
+    >>> quote('manage_main?a:int=1&x:string=y') in browser.headers['Location']
+    True
+
+And requests are redirected to the login_form if a login attempt fails.
+
+    >>> browser.post('http://localhost/site/manage_main',
+    ...              '__ac_name=mbr&__ac_password=wrong')
+    Traceback (most recent call last):
+    ...
+    HTTPError: HTTP Error 302: Moved Temporarily
+    >>> browser.contents
+    ''
+    >>> browser.headers['Location']
+    'http://localhost/site/login_form?came_from=http%3A//localhost/site/manage_main'
+
+The view for zExceptions.Unauthorized shows a Forbidden error if logged in.
+
+    >>> browser.cookies['__ac'] = '%s' % mbr_credentials
+    >>> browser.open('http://localhost/site/manage_main')
+    Traceback (most recent call last):
+    ...
+    HTTPError: HTTP Error 403: Forbidden
+    >>> 'zpt_stylesheet.css' in browser.contents
+    True
+    >>> '[[cmf_default][Forbidden]]' in browser.contents
+    True
+
+For the following examples we have to reset the browser object.
+
+    >>> browser = Browser()
+
+Use the login form without input.
+
+    >>> browser.open('http://localhost/site/@@login.html')
+    >>> '[[cmf_default][Log in]]' in browser.contents
+    True
+    >>> browser.getControl('[[cmf_default][Login]]').click()
+    >>> '[[zope][There were errors]]' in browser.contents
+    True
+
+Use the login form with invalid input.
+
+    >>> browser.open('http://localhost/site/@@login.html')
+    >>> browser.getControl('[[cmf_default][Member ID]]').value = 'unknown'
+    >>> browser.getControl('[[cmf_default][Login]]').click()
+    >>> '[[zope][There were errors]]' in browser.contents
+    True
+    >>> '[[cmf_default][Login failure]]' in browser.contents
+    True
+
+Use the login form with valid input but wrong password.
+
+    >>> browser.open('http://localhost/site/@@login.html')
+    >>> browser.getControl('[[cmf_default][Member ID]]').value = 'mbr'
+    >>> browser.getControl('[[cmf_default][Password]]').value = 'wrong'
+    >>> browser.getControl('[[cmf_default][Remember my ID.]]').selected = False
+    >>> browser.getControl('[[cmf_default][Login]]').click()
+    >>> '[[zope][There were errors]]' in browser.contents
+    True
+    >>> '[[cmf_default][Login failure]]' in browser.contents
+    True
+    >>> '__ac' not in browser.cookies
+    True
+
+Use the login form with valid input and correct password.
+
+    >>> browser.open('http://localhost/site/@@login.html')
+    >>> browser.getControl('[[cmf_default][Member ID]]').value = 'mbr'
+    >>> browser.getControl('[[cmf_default][Password]]').value = 'mbrpw'
+    >>> browser.getControl('[[cmf_default][Remember my ID.]]').selected = False
+    >>> browser.getControl('[[cmf_default][Login]]').click()
+    >>> '[[cmf_default][Login success]]' in browser.contents
+    True
+    >>> browser.cookies['__ac'] == '"%s"' % mbr_credentials
+    True
+
+Use the mail password form without input.
+
+    >>> browser.open('http://localhost/site/@@mail_password.html')
+    >>> "[[cmf_default][Don't panic!]]" in browser.contents
+    True
+    >>> browser.getControl('[[cmf_default][Send]]').click()
+    >>> '[[zope][There were errors]]' in browser.contents
+    True
+
+Use the mail password form with invalid input.
+
+    >>> browser.open('http://localhost/site/@@mail_password.html')
+    >>> browser.getControl('[[cmf_default][Member ID]]').value = 'unknown'
+    >>> browser.getControl('[[cmf_default][Send]]').click()
+    >>> '[[zope][There were errors]]' in browser.contents
+    True
+    >>> '[[zope][Constraint not satisfied]]' in browser.contents
+    True
+
+Log the user in and then out
+    >>> browser.open('http://localhost/site/@@login.html')
+    >>> browser.getControl('[[cmf_default][Member ID]]').value = 'mbr'
+    >>> browser.getControl('[[cmf_default][Password]]').value = 'mbrpw'
+    >>> browser.getControl('[[cmf_default][Login]]').click()
+    >>> '[[cmf_default][Login success]]' in browser.contents
+    True
+    >>> browser.open('http://localhost/site/@@logout.html')
+    >>> '[cmf_default][You have been logged out.' in browser.contents
+    True
+
+User logged in via ZMI and therefore cannot logout
+    >>> browser = Browser()
+    >>> browser.open('http://localhost/site/manage_main')
+    >>> browser.cookies['__ac'] = '%s' % mbr_credentials
+    >>> browser.open('http://localhost/site/@@logout.html')
+    >>> '[[cmf_default][You are logged in outside the portal' in browser.contents
+    True
\ No newline at end of file

Added: Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/tests/test_authentication.py
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/tests/test_authentication.py	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/tests/test_authentication.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,30 @@
+##############################################################################
+#
+# Copyright (c) 2010 Zope Foundation and Contributors.
+#
+# 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.
+#
+##############################################################################
+""" Test Products.CMFDefault.browser.authentication
+
+$Id$
+"""
+
+import unittest
+from Testing import ZopeTestCase
+
+from Products.CMFDefault.testing import FunctionalLayer
+
+
+ftest_suite = ZopeTestCase.FunctionalDocFileSuite('authentication.txt')
+ftest_suite.layer = FunctionalLayer
+
+def test_suite():
+    return unittest.TestSuite((
+        ftest_suite,
+    ))


Property changes on: Products.CMFDefault/trunk/Products/CMFDefault/browser/membership/tests/test_authentication.py
___________________________________________________________________
Added: svn:keywords
   + Id
Added: svn:eol-style
   + native

Deleted: Products.CMFDefault/trunk/Products/CMFDefault/browser/metadata.py
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/metadata.py	2010-06-25 12:36:59 UTC (rev 113834)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/metadata.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -1,268 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2006 Zope Foundation and Contributors.
-#
-# 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 metadata.
-
-$Id$
-"""
-
-from Acquisition import aq_self
-from zope.app.form.browser import DatetimeI18nWidget
-from zope.component import adapts
-from zope.component import getUtility
-from zope.formlib import form
-from zope.interface import implements
-from zope.interface import Interface
-from zope.schema import Choice
-from zope.schema import Datetime
-from zope.schema import Set
-from zope.schema import Text
-from zope.schema import TextLine
-from zope.schema import Tuple
-from zope.schema import URI
-
-from Products.CMFCore.interfaces import IDiscussionTool
-from Products.CMFCore.interfaces import IMutableDublinCore
-from Products.CMFCore.interfaces import IMutableMinimalDublinCore
-from Products.CMFDefault.formlib.form import ContentEditFormBase
-from Products.CMFDefault.formlib.schema import ProxyFieldProperty
-from Products.CMFDefault.formlib.schema import SchemaAdapterBase
-from Products.CMFDefault.formlib.vocabulary import SimpleVocabulary
-from Products.CMFDefault.formlib.widgets import SubjectInputWidget
-from Products.CMFDefault.formlib.widgets import TupleInputWidget
-from Products.CMFDefault.utils import Message as _
-
-available_settings = [
-        ('off', False, _(u'Off')),
-        ('on', True, _(u'On')) ]
-
-
-class IMinimalMetadataSchema(Interface):
-
-    """Schema for minimal metadata views.
-    """
-
-    title = TextLine(
-        title=_(u'Title'),
-        required=False,
-        missing_value=u'')
-
-    description = Text(
-        title=_(u'Description'),
-        required=False,
-        missing_value=u'')
-
-
-class IMetadataSchema(Interface):
-
-    """Schema for metadata views.
-    """
-
-    allow_discussion = Choice(
-        title=_(u'Enable Discussion?'),
-        required=False,
-        vocabulary=SimpleVocabulary.fromTitleItems(available_settings))
-
-    identifier = URI(
-        title=_(u'Identifier'),
-        readonly=True)
-
-    title = TextLine(
-        title=_(u'Title'),
-        required=False,
-        missing_value=u'')
-
-    description = Text(
-        title=_(u'Description'),
-        required=False,
-        missing_value=u'')
-
-    subject = Set(
-        title=_(u'Subject'),
-        required=False,
-        missing_value=set(),
-        value_type=TextLine())
-
-    contributors = Tuple(
-        title=_(u'Contributors'),
-        required=False,
-        missing_value=(),
-        value_type=TextLine())
-
-    created = Datetime(
-        title=_(u'Creation Date'),
-        readonly=True)
-
-    modified = Datetime(
-        title=_(u'Last Modified Date'),
-        readonly=True)
-
-    effective = Datetime(
-        title=_(u'Effective Date'),
-        required=False
-        )
-
-    expires = Datetime(
-        title=_(u'Expiration Date'),
-        required=False
-        )
-
-    format = TextLine(
-        title=_(u'Format'),
-        required=False,
-        missing_value=u'')
-
-    language = TextLine(
-        title=_(u'Language'),
-        required=False,
-        missing_value=u'')
-
-    rights = TextLine(
-        title=_(u'Rights'),
-        required=False,
-        missing_value=u'')
-
-
-class MinimalMetadataSchemaAdapter(SchemaAdapterBase):
-
-    """Adapter for IMutableMinimalDublinCore.
-    """
-
-    adapts(IMutableMinimalDublinCore)
-    implements(IMinimalMetadataSchema)
-
-    title = ProxyFieldProperty(IMetadataSchema['title'], 'Title', 'setTitle')
-    description = ProxyFieldProperty(IMetadataSchema['description'],
-                                     'Description', 'setDescription')
-
-
-class MetadataSchemaAdapter(SchemaAdapterBase):
-
-    """Adapter for IMutableDublinCore.
-    """
-
-    adapts(IMutableDublinCore)
-    implements(IMetadataSchema)
-
-    _effective = ProxyFieldProperty(IMetadataSchema['effective'],
-                                    'effective', 'setEffectiveDate')
-    _expires = ProxyFieldProperty(IMetadataSchema['expires'],
-                                  'expires', 'setExpirationDate')
-
-    def _getEffective(self):
-        if self.context.EffectiveDate() == 'None':
-            return None
-        return self._effective
-
-    def _getExpires(self):
-        if self.context.ExpirationDate() == 'None':
-            return None
-        return self._expires
-
-    def _getAllowDiscussion(self):
-        context = aq_self(self.context)
-        return getattr(context, 'allow_discussion', None)
-
-    def _setAllowDiscussion(self, value):
-        dtool = getUtility(IDiscussionTool)
-        dtool.overrideDiscussionFor(self.context, value)
-
-    allow_discussion = property(_getAllowDiscussion, _setAllowDiscussion)
-    identifier = ProxyFieldProperty(IMetadataSchema['identifier'],
-                                    'Identifier')
-    title = ProxyFieldProperty(IMetadataSchema['title'], 'Title', 'setTitle')
-    description = ProxyFieldProperty(IMetadataSchema['description'],
-                                     'Description', 'setDescription')
-    subject = ProxyFieldProperty(IMetadataSchema['subject'],
-                                 'Subject', 'setSubject')
-    contributors = ProxyFieldProperty(IMetadataSchema['contributors'],
-                                      'listContributors', 'setContributors')
-    created = ProxyFieldProperty(IMetadataSchema['created'])
-    modified = ProxyFieldProperty(IMetadataSchema['modified'])
-    effective = property(_getEffective, _effective.__set__)
-    expires = property(_getExpires, _expires.__set__)
-    format = ProxyFieldProperty(IMetadataSchema['format'],
-                                     'Format', 'setFormat')
-    language = ProxyFieldProperty(IMetadataSchema['language'],
-                                     'Language', 'setLanguage')
-    rights = ProxyFieldProperty(IMetadataSchema['rights'],
-                                     'Rights', 'setRights')
-
-
-class MinimalMetadataEditView(ContentEditFormBase):
-
-    """Edit view for IMutableMinimalDublinCore.
-    """
-
-    form_fields = form.FormFields(IMinimalMetadataSchema)
-
-    label = _(u'Properties')
-
-    def setUpWidgets(self, ignore_request=False):
-        super(MinimalMetadataEditView,
-              self).setUpWidgets(ignore_request=ignore_request)
-        self.widgets['description'].height = 4
-
-
-class MetadataEditView(ContentEditFormBase):
-
-    """Edit view for IMutableDublinCore.
-    """
-
-    actions = form.Actions(
-        form.Action(
-            name='change',
-            label=_(u'Change'),
-            validator='handle_validate',
-            success='handle_change_success',
-            failure='handle_failure'),
-        form.Action(
-            name='change_and_edit',
-            label=_(u'Change and Edit'),
-            validator='handle_validate',
-            success='handle_change_and_edit_success',
-            failure='handle_failure'),
-        form.Action(
-            name='change_and_view',
-            label=_(u'Change and View'),
-            validator='handle_validate',
-            success='handle_change_and_view_success',
-            failure='handle_failure'))
-
-    form_fields = form.FormFields(IMetadataSchema)
-    form_fields['subject'].custom_widget = SubjectInputWidget
-    form_fields['contributors'].custom_widget = TupleInputWidget
-    form_fields['effective'].custom_widget = DatetimeI18nWidget
-    form_fields['expires'].custom_widget = DatetimeI18nWidget
-
-    label = _(u'Properties')
-
-    def setUpWidgets(self, ignore_request=False):
-        super(MetadataEditView,
-              self).setUpWidgets(ignore_request=ignore_request)
-        self.widgets['allow_discussion']._messageNoValue = _(u'Default')
-        self.widgets['description'].height = 4
-        self.widgets['subject'].split = True
-        self.widgets['contributors'].height = 6
-        self.widgets['contributors'].split = True
-        self.widgets['created'].split = True
-        self.widgets['modified'].split = True
-        self.widgets['effective'].split = True
-        self.widgets['expires'].split = True
-
-    def handle_change_success(self, action, data):
-        self._handle_success(action, data)
-        return self._setRedirect('portal_types', 'object/metadata')
-
-    def handle_change_and_edit_success(self, action, data):
-        self._handle_success(action, data)
-        return self._setRedirect('portal_types', 'object/edit')

Deleted: Products.CMFDefault/trunk/Products/CMFDefault/browser/newsitem.py
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/newsitem.py	2010-06-25 12:36:59 UTC (rev 113834)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/newsitem.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -1,90 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2006 Zope Foundation and Contributors.
-#
-# 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 news items.
-
-$Id$
-"""
-
-from zope.component import adapts
-from zope.formlib import form
-from zope.interface import implements
-from zope.interface import Interface
-from zope.schema import Choice
-from zope.schema import Text
-from zope.schema import TextLine
-
-from Products.CMFDefault.formlib.form import ContentEditFormBase
-from Products.CMFDefault.formlib.schema import ProxyFieldProperty
-from Products.CMFDefault.formlib.schema import SchemaAdapterBase
-from Products.CMFDefault.formlib.widgets import ChoiceRadioWidget
-from Products.CMFDefault.formlib.widgets import TextInputWidget
-from Products.CMFDefault.interfaces import IMutableNewsItem
-from Products.CMFDefault.utils import Message as _
-
-
-class INewsItemSchema(Interface):
-
-    """Schema for news item views.
-    """
-
-    title = TextLine(
-        title=_(u'Title'),
-        readonly=True)
-
-    text_format = Choice(
-        title=_(u'Format'),
-        vocabulary='cmf.AvailableTextFormats')
-
-    description = Text(
-        title=_(u'Lead-in'),
-        required=False,
-        missing_value=u'')
-
-    text = Text(
-        title=_(u'Body'),
-        required=False,
-        missing_value=u'')
-
-
-class NewsItemSchemaAdapter(SchemaAdapterBase):
-
-    """Adapter for IMutableNewsItem.
-    """
-
-    adapts(IMutableNewsItem)
-    implements(INewsItemSchema)
-
-    title = ProxyFieldProperty(INewsItemSchema['title'], 'Title')
-    text_format = ProxyFieldProperty(INewsItemSchema['text_format'])
-    description = ProxyFieldProperty(INewsItemSchema['description'],
-                                     'Description', 'setDescription')
-    text = ProxyFieldProperty(INewsItemSchema['text'],
-                              'EditableBody', '_edit')
-
-
-class NewsItemEditView(ContentEditFormBase):
-
-    """Edit view for INewsItem.
-    """
-
-    form_fields = form.FormFields(INewsItemSchema)
-    form_fields['text_format'].custom_widget = ChoiceRadioWidget
-    form_fields['description'].custom_widget = TextInputWidget
-    form_fields['text'].custom_widget = TextInputWidget
-
-    def setUpWidgets(self, ignore_request=False):
-        super(NewsItemEditView,
-              self).setUpWidgets(ignore_request=ignore_request)
-        self.widgets['text_format'].orientation = 'horizontal'
-        self.widgets['description'].height = 8
-        self.widgets['text'].height = 16

Added: Products.CMFDefault/trunk/Products/CMFDefault/browser/skins/__init__.py
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/skins/__init__.py	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/skins/__init__.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,16 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Foundation and Contributors.
+#
+# 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 skins views.
+
+$Id$
+"""


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

Added: Products.CMFDefault/trunk/Products/CMFDefault/browser/skins/configure.zcml
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/skins/configure.zcml	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/skins/configure.zcml	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,20 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:browser="http://namespaces.zope.org/browser">
+    
+  <browser:page
+      for="*"
+      name="ursine_globals"
+      class=".ursa.UrsineGlobals"
+      permission="zope.Public"
+      />
+      
+  <browser:page
+      for="*"
+      layer="Products.CMFDefault.interfaces.ICMFDefaultSkin"
+      name="icons.css"
+      class=".icons.View"
+      permission="zope.Public"
+      />
+
+</configure>

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/skins/icons.py (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/icons.py)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/skins/icons.py	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/skins/icons.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,73 @@
+"""CSS for action icons
+$Id$
+"""
+from logging import getLogger
+
+LOG = getLogger("Action Icons CSS")
+
+from zope.component import getUtility
+
+from Products.Five.browser import BrowserView
+
+from Products.CMFCore.interfaces import IPropertiesTool
+from Products.CMFCore.Expression import getExprContext
+from Products.CMFCore.utils import getToolByName
+
+from Products.CMFDefault.browser.utils import memoize
+
+class View(BrowserView):
+    
+    def __init__(self, context, request):
+        super(View, self).__init__(context, request)
+        self.show_icons = self._show_icons
+
+    @property
+    @memoize
+    def _show_icons(self):
+        """Are action icons enabled?"""
+        ptool = getUtility(IPropertiesTool)
+        show = ptool.getProperty('enable_actionicons')
+        if show:
+            self.icon = ".icon {padding-left: 1.5em;}\n\n"
+        else:
+            self.icon = ".icon {padding-left: 0.5em;}\n\n"
+        return show
+
+    @property
+    @memoize
+    def template(self):
+        """Always return a template so there are no browser errors"""
+        if self.show_icons:    
+            return ".%s {background: url(%s) no-repeat 0.1em}"
+        else:
+            return ".%s {/* %s */}"
+
+    @memoize
+    def actions(self):
+        """List all action icons"""
+        atool = getToolByName(self.context, 'portal_actions')
+        all_actions = atool.listFilteredActionsFor(self.context)
+        icons = []
+        for cat in ['user', 'object', 'folder', 'workflow', 'global']:
+            cat_actions = all_actions[cat]
+            icons.append("/* %s actions */" % cat)
+            for a in cat_actions:
+                icons.append(self.template % (a['id'], a['icon']))
+        return "\n\n".join(icons)
+
+    @memoize
+    def types(self):
+        """List all type icons"""
+        ttool = getToolByName(self.context, 'portal_types')
+        types = ttool.listTypeInfo()
+        econtext = getExprContext(self.context)
+        icons = [self.template %  (t.id,
+                                  t.getIconExprObject()(econtext)) \
+                for t in types]
+        return "\n\n".join(icons)
+
+    def __call__(self):
+        self.request.response.setHeader("content-type", "text/css")
+        self.request.response.write(self.icon)
+        self.request.response.write(self.actions())
+        self.request.response.write(self.types())


Property changes on: Products.CMFDefault/trunk/Products/CMFDefault/browser/skins/tests/__init__.py
___________________________________________________________________
Added: svn:keywords
   + Id
Added: svn:eol-style
   + native

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/skins/tests/test_ursa.py (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/tests/test_ursa.py)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/skins/tests/test_ursa.py	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/skins/tests/test_ursa.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,430 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation and Contributors.
+#
+# 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.
+#
+##############################################################################
+""" Test Products.CMFDefault.browser.ursa
+
+$Id$
+"""
+import unittest
+from zope.component.testing import PlacelessSetup
+
+class UrsineGlobalsTests(unittest.TestCase, PlacelessSetup):
+
+    def setUp(self):
+        PlacelessSetup.setUp(self)
+
+    def tearDown(self):
+        PlacelessSetup.tearDown(self)
+
+    def _getTargetClass(self):
+        from Products.CMFDefault.browser.skins.ursa import UrsineGlobals
+        return UrsineGlobals
+
+    def _makeOne(self, context=None, request=None):
+        if context is None:
+            context = self._makeContext()
+        if request is None:
+            request = DummyRequest()
+        return self._getTargetClass()(context, request)
+
+    def _makeContext(self):
+        from zope.component import getSiteManager
+        from Products.CMFCore.interfaces import IPropertiesTool
+        context = DummyContext()
+        tool = context.portal_properties = DummyPropertiesTool()
+        sm = getSiteManager()
+        sm.registerUtility(tool, IPropertiesTool)
+        return context
+
+    def test_ctor_wo_def_charset_doesnt_set_content_type(self):
+        context = self._makeContext()
+        request = DummyRequest()
+        response = request.RESPONSE
+        view = self._makeOne(context, request)
+        self.assertEqual(len(response._set_headers), 0)
+
+    def test_ctor_w_resp_charset_doesnt_set_content_type(self):
+        context = self._makeContext()
+        request = DummyRequest()
+        response = request.RESPONSE
+        response._orig_headers['content-type'] = 'text/html; charset=UTF-8'
+        view = self._makeOne(context, request)
+        self.assertEqual(len(response._set_headers), 0)
+
+    def test_ctor_w_resp_charset_w_def_charset_doesnt_override_charset(self):
+        context = self._makeContext()
+        context.portal_properties.default_charset = 'latin1'
+        request = DummyRequest()
+        response = request.RESPONSE
+        response._orig_headers['content-type'] = 'text/html; charset=UTF-8'
+        view = self._makeOne(context, request)
+        self.assertEqual(len(response._set_headers), 0)
+
+    def test_ctor_wo_resp_charst_w_def_charset_sets_charset(self):
+        context = self._makeContext()
+        context.portal_properties.default_charset = 'latin1'
+        request = DummyRequest()
+        response = request.RESPONSE
+        response._orig_headers['content-type'] = 'text/html'
+        view = self._makeOne(context, request)
+        self.assertEqual(len(response._set_headers), 1)
+        self.assertEqual(response._set_headers[0],
+                         ('content-type', 'text/html; charset=latin1'))
+
+    def test_ptool(self):
+        view = self._makeOne()
+        tool = view.context.portal_properties
+        self.failUnless(view.ptool is tool)
+
+    def test_utool(self):
+        view = self._makeOne()
+        tool = view.context.portal_url = DummyURLTool()
+        self.failUnless(view.utool is tool)
+
+    def test_mtool(self):
+        view = self._makeOne()
+        tool = view.context.portal_membership = DummyMembershipTool()
+        self.failUnless(view.mtool is tool)
+
+    def test_atool(self):
+        view = self._makeOne()
+        tool = view.context.portal_actions = DummyActionsTool()
+        self.failUnless(view.atool is tool)
+
+    def test_wtool(self):
+        view = self._makeOne()
+        tool = view.context.portal_workflow = DummyWorkflowTool()
+        self.failUnless(view.wtool is tool)
+
+    def test_portal_object(self):
+        view = self._makeOne()
+        tool = view.context.portal_url = DummyURLTool()
+        portal = DummyContext()
+        tool.getPortalObject = lambda: portal
+        self.failUnless(view.portal_object is portal)
+
+    def test_portal_url(self):
+        view = self._makeOne()
+        tool = view.context.portal_url = DummyURLTool()
+        tool.__call__ = lambda: 'http://example.com/'
+        self.assertEqual(view.portal_url, 'http://example.com/')
+
+    def test_portal_title(self):
+        view = self._makeOne()
+        tool = view.context.portal_url = DummyURLTool()
+        portal = DummyContext()
+        portal.Title = lambda: 'TITLE'
+        tool.getPortalObject = lambda: portal
+        self.assertEqual(view.portal_title, 'TITLE')
+
+    def test_object_title(self):
+        view = self._makeOne()
+        view.context.Title = lambda: 'TITLE'
+        self.assertEqual(view.object_title, 'TITLE')
+
+    def test_object_description(self):
+        view = self._makeOne()
+        view.context.Title = lambda: 'TITLE'
+        self.assertEqual(view.object_title, 'TITLE')
+
+    def test_trunc_id(self):
+        view = self._makeOne()
+        view.context.getId = lambda: 'ID'
+        self.assertEqual(view.trunc_id, 'ID')
+
+    def test_trunc_id_w_long_id(self):
+        view = self._makeOne()
+        view.context.getId = lambda: 'X' * 20
+        self.assertEqual(view.trunc_id, 'X' * 15 + '...')
+
+    def test_icon_wo_getIconURL_w_icon(self):
+        view = self._makeOne()
+        view.context.getIconURL = lambda: 'ICON'
+        view.context.icon = 'ICON2'
+        self.assertEqual(view.icon, 'ICON')
+
+    def test_icon_wo_getIconURL_w_icon(self):
+        view = self._makeOne()
+        view.context.icon = 'ICON'
+        self.assertEqual(view.icon, 'ICON')
+
+    def test_icon_wo_getIconURL_wo_icon(self):
+        view = self._makeOne()
+        self.assertEqual(view.icon, '')
+
+    def test_typename(self):
+        view = self._makeOne()
+        view.context.getPortalTypeName = lambda: 'TYPENAME'
+        self.assertEqual(view.typename, 'TYPENAME')
+
+    def test_wf_state(self):
+        view = self._makeOne()
+        view.context.portal_workflow = DummyWorkflowTool()
+        self.assertEqual(view.wf_state, 'DUMMY')
+
+    def test_page_title_wo_match(self):
+        view = self._makeOne()
+        view.context.Title = lambda: 'CONTEXT'
+        tool = view.context.portal_url = DummyURLTool()
+        portal = DummyContext()
+        portal.Title = lambda: 'SITE'
+        tool.getPortalObject = lambda: portal
+        self.assertEqual(view.page_title, 'SITE: CONTEXT')
+
+    def test_page_title_w_match(self):
+        view = self._makeOne()
+        view.context.Title = lambda: 'MATCH'
+        tool = view.context.portal_url = DummyURLTool()
+        portal = DummyContext()
+        portal.Title = lambda: 'MATCH'
+        tool.getPortalObject = lambda: portal
+        self.assertEqual(view.page_title, 'MATCH')
+
+    def test_breadcrumbs_at_root(self):
+        PATHS_TO_CONTEXTS = []
+        site = DummySite(PATHS_TO_CONTEXTS)
+        ptool = site.portal_properties = DummyPropertiesTool()
+        ptool.title = lambda: 'SITE'
+        utool = site.portal_url = DummyURLTool(site, PATHS_TO_CONTEXTS)
+        utool.__call__ = lambda: 'http://example.com/'
+        view = self._makeOne(context=site)
+        crumbs = view.breadcrumbs
+        self.assertEqual(len(crumbs), 1)
+        self.assertEqual(crumbs[0]['id'], 'root')
+        self.assertEqual(crumbs[0]['title'], 'SITE')
+        self.assertEqual(crumbs[0]['url'], 'http://example.com/')
+
+    def test_breadcrumbs_not_root(self):
+        context = DummyContext()
+        context.Title = lambda: 'CONTEXT'
+        context.absolute_url = lambda: 'http://example.com/parent/child'
+        parent = DummyContext()
+        parent.Title = lambda: 'PARENT'
+        parent.absolute_url = lambda: 'http://example.com/parent'
+        PATHS_TO_CONTEXTS = [(('parent',), parent),
+                             (('parent', 'child'), context),
+                            ]
+        site = DummySite(PATHS_TO_CONTEXTS)
+        ptool = context.portal_properties = DummyPropertiesTool()
+        ptool.title = lambda: 'SITE'
+        utool = context.portal_url = DummyURLTool(site, PATHS_TO_CONTEXTS)
+        utool.__call__ = lambda: 'http://example.com/'
+
+        view = self._makeOne(context=context)
+
+        crumbs = view.breadcrumbs
+
+        self.assertEqual(len(crumbs), 3)
+        self.assertEqual(crumbs[0]['id'], 'root')
+        self.assertEqual(crumbs[0]['title'], 'SITE')
+        self.assertEqual(crumbs[0]['url'], 'http://example.com/')
+        self.assertEqual(crumbs[1]['id'], 'parent')
+        self.assertEqual(crumbs[1]['title'], 'PARENT')
+        self.assertEqual(crumbs[1]['url'], 'http://example.com/parent')
+        self.assertEqual(crumbs[2]['id'], 'child')
+        self.assertEqual(crumbs[2]['title'], 'CONTEXT')
+        self.assertEqual(crumbs[2]['url'], 'http://example.com/parent/child')
+
+    def test_member(self):
+        view = self._makeOne()
+        tool = view.context.portal_membership = DummyMembershipTool()
+        member = DummyUser()
+        tool.getAuthenticatedMember = lambda: member
+        self.failUnless(view.member is member)
+
+    def test_membersfolder(self):
+        view = self._makeOne()
+        tool = view.context.portal_membership = DummyMembershipTool()
+        membersfolder = object()
+        tool.getMembersFolder = lambda: membersfolder
+        self.failUnless(view.membersfolder is membersfolder)
+
+    def test_isAnon_tool_returns_True(self):
+        view = self._makeOne()
+        tool = view.context.portal_membership = DummyMembershipTool()
+        tool.isAnonymousUser = lambda: True
+        self.failUnless(view.isAnon)
+
+    def test_isAnon_tool_returns_False(self):
+        view = self._makeOne()
+        tool = view.context.portal_membership = DummyMembershipTool()
+        tool.isAnonymousUser = lambda: False
+        self.failIf(view.isAnon)
+
+    def test_uname_anonymous(self):
+        view = self._makeOne()
+        tool = view.context.portal_membership = DummyMembershipTool()
+        tool.isAnonymousUser = lambda: True
+        self.assertEqual(view.uname, 'Guest')
+
+    def test_uname_not_anonymous(self):
+        view = self._makeOne()
+        tool = view.context.portal_membership = DummyMembershipTool()
+        tool.isAnonymousUser = lambda: False
+        member = DummyUser()
+        member.getUserName = lambda: 'luser'
+        tool.getAuthenticatedMember = lambda: member
+        self.assertEqual(view.uname, 'luser')
+
+    def test_status_message_missing(self):
+        view = self._makeOne()
+        view.request.form = {}
+        self.assertEqual(view.status_message, None)
+
+    def test_status_message_missing(self):
+        view = self._makeOne()
+        view.request.form = {'portal_status_message': 'FOO'}
+        self.assertEqual(view.status_message, 'FOO')
+
+    def test_actions(self):
+        ACTIONS = {'global': [],
+                   'user': [],
+                   'object': [],
+                   'folder': [],
+                   'workflow': [],
+                  }
+        view = self._makeOne()
+        tool = view.context.portal_actions = DummyActionsTool(ACTIONS)
+        self.assertEqual(view.actions, ACTIONS)
+
+    def test_global_actions(self):
+        ACTIONS = {'global': [DummyAction('a'), DummyAction('b')],
+                   'user': [],
+                   'object': [],
+                   'folder': [],
+                   'workflow': [],
+                  }
+        view = self._makeOne()
+        tool = view.context.portal_actions = DummyActionsTool(ACTIONS)
+        self.assertEqual(view.global_actions, ACTIONS['global'])
+
+    def test_user_actions(self):
+        ACTIONS = {'global': [],
+                   'user': [DummyAction('a'), DummyAction('b')],
+                   'object': [],
+                   'folder': [],
+                   'workflow': [],
+                  }
+        view = self._makeOne()
+        tool = view.context.portal_actions = DummyActionsTool(ACTIONS)
+        self.assertEqual(view.user_actions, ACTIONS['user'])
+
+    def test_object_actions(self):
+        ACTIONS = {'global': [],
+                   'user': [],
+                   'object': [DummyAction('a'), DummyAction('b')],
+                   'folder': [],
+                   'workflow': [],
+                  }
+        view = self._makeOne()
+        tool = view.context.portal_actions = DummyActionsTool(ACTIONS)
+        self.assertEqual(view.object_actions, ACTIONS['object'])
+
+    def test_folder_actions(self):
+        ACTIONS = {'global': [],
+                   'user': [],
+                   'object': [],
+                   'folder': [DummyAction('a'), DummyAction('b')],
+                   'workflow': [],
+                  }
+        view = self._makeOne()
+        tool = view.context.portal_actions = DummyActionsTool(ACTIONS)
+        self.assertEqual(view.folder_actions, ACTIONS['folder'])
+
+    def test_workflow_actions(self):
+        ACTIONS = {'global': [],
+                   'user': [],
+                   'object': [],
+                   'folder': [],
+                   'workflow': [DummyAction('a'), DummyAction('b')],
+                  }
+        view = self._makeOne()
+        tool = view.context.portal_actions = DummyActionsTool(ACTIONS)
+        self.assertEqual(view.workflow_actions, ACTIONS['workflow'])
+
+class DummyContext:
+    pass
+
+class DummyAction:
+    def __init__(self, id):
+        self.id = id
+
+class DummySite:
+    def __init__(self, paths_to_contexts=()):
+        self.paths_to_contexts = paths_to_contexts[:]
+
+    def unrestrictedTraverse(self, path):
+        for known, context in self.paths_to_contexts:
+            if path == known:
+                return context
+        raise ValueError('Unknown path: %s' % path)
+
+class DummyPropertiesTool:
+    def getProperty(self, key, default):
+        return getattr(self, key, default)
+
+class DummyURLTool:
+    def __init__(self, site=None, paths_to_contexts=()):
+        self.site = site
+        self.paths_to_contexts = paths_to_contexts[:]
+
+    def getPortalObject(self):
+        return self.site
+
+    def getRelativeContentPath(self, context):
+        if context is self.site:
+            return ()
+        for path, known in self.paths_to_contexts:
+            if context is known:
+                return path
+        raise ValueError('Unknown context: %s' % context)
+
+class DummyMembershipTool:
+    pass
+
+class DummyActionsTool:
+    def __init__(self, actions=None):
+        if actions is None:
+            actions = {}
+        self.actions = actions.copy()
+
+    def listFilteredActionsFor(self, context):
+        return self.actions
+
+class DummyWorkflowTool:
+    review_state = 'DUMMY'
+    def getInfoFor(self, context, key, default):
+        if key == 'review_state':
+            return self.review_state
+
+class DummyUser:
+    pass
+
+class DummyResponse:
+    def __init__(self, **kw):
+        self._orig_headers = kw.copy()
+        self._set_headers = []
+
+    def getHeader(self, key):
+        return self._orig_headers.get(key, '')
+
+    def setHeader(self, key, value):
+        self._set_headers.append((key, value))
+
+class DummyRequest:
+    def __init__(self):
+        self.RESPONSE = DummyResponse()
+
+def test_suite():
+    return unittest.TestSuite((
+        unittest.makeSuite(UrsineGlobalsTests),
+    ))

Copied: Products.CMFDefault/trunk/Products/CMFDefault/browser/skins/ursa.py (from rev 113819, Products.CMFDefault/trunk/Products/CMFDefault/browser/ursa.py)
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/skins/ursa.py	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/skins/ursa.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,195 @@
+from Products.CMFDefault.utils import Message as _
+from Products.CMFDefault.utils import decode
+from Products.CMFDefault.browser.utils import ViewBase
+from Products.CMFDefault.browser.utils import memoize
+
+class UrsineGlobals(ViewBase):
+    """ Provide lazy / efficient template-level globals.
+
+    o Replaces 'getMainGlobals' in stock skin.
+    """
+    def __init__(self, context, request):
+        super(ViewBase, self).__init__(context, request)
+        ct = self.request.RESPONSE.getHeader('content-type') or ''
+        if not 'charset' in ct:
+            # Some newstyle views set a different charset - don't override it.
+            # Oldstyle views need the default_charset.
+            default_charset = self.ptool.getProperty('default_charset', None)
+            if default_charset:
+                self.request.RESPONSE.setHeader('content-type',
+                              '%s; charset=%s' % (ct, default_charset))
+
+    @property
+    @memoize
+    def ptool(self):
+        return self._getTool('portal_properties')
+
+    @property
+    @memoize
+    def utool(self):
+        return self._getTool('portal_url')
+
+    @property
+    @memoize
+    def mtool(self):
+        return self._getTool('portal_membership')
+
+    @property
+    @memoize
+    def atool(self):
+        return self._getTool('portal_actions')
+
+    @property
+    @memoize
+    def wtool(self):
+        return self._getTool('portal_workflow')
+
+    @property
+    @memoize
+    def portal_object(self):
+        return self.utool.getPortalObject()
+
+    @property
+    @memoize
+    def portal_url(self):
+        return self.utool()
+
+    @property
+    @memoize
+    def portal_title(self):
+        return self.portal_object.Title()
+
+    @property
+    @memoize
+    def object_title(self):
+        return self.context.Title()
+
+    @property
+    @memoize
+    def object_description(self):
+        return self.context.Description()
+
+    @property
+    @memoize
+    def trunc_id(self):
+        id = self.context.getId()
+        if len(id) > 15:
+            id = id[:15] + '...'
+        return id
+
+    @property
+    @memoize
+    def icon(self):
+        return getattr(self.context, 'getIconURL',
+                        lambda: getattr(self.context, 'icon', ''))()
+    @property
+    @memoize
+    def typename(self):
+        return self.context.getPortalTypeName()
+
+    @property
+    @memoize
+    def wf_state(self):
+        return self.wtool.getInfoFor(self.context, 'review_state', '')
+
+    @property
+    @memoize
+    def page_title(self):
+        site_title = self.portal_title
+        page_title = self.object_title
+
+        if page_title != site_title:
+            page_title = site_title + ": " + page_title
+
+        return decode(page_title, self.context)
+
+    @property
+    @memoize
+    def breadcrumbs(self):
+        # XXX Shouldn't we just be walking up the aq_inner chain?
+        result = [{'id': _(u'root'),
+                   'title' : self.ptool.title(),
+                   'url' : self.portal_url,
+                  }]
+
+        relative = self.utool.getRelativeContentPath(self.context)
+        portal = self.portal_object
+
+        for i, token in enumerate(relative):
+            now = relative[:i+1]
+            obj = portal.unrestrictedTraverse(now)
+            if token != 'talkback':
+                result.append({'id': token,
+                               'title': obj.Title(),
+                               'url': obj.absolute_url(),
+                              })
+
+        return result
+
+    @property
+    @memoize
+    def member(self):
+        return self.mtool.getAuthenticatedMember()
+
+    @property
+    @memoize
+    def membername(self):
+        return self.isAnon and 'Guest' or self.member.getUserName()
+
+    @property
+    @memoize
+    def membersfolder(self):
+        return self.mtool.getMembersFolder()
+
+    @property
+    @memoize
+    def isAnon(self):
+        return self.mtool.isAnonymousUser()
+
+    @property
+    @memoize
+    def uname(self):
+        return self.isAnon and 'Guest' or self.member.getUserName()
+
+    @property
+    @memoize
+    def status_message(self):
+        message = self.request.form.get('portal_status_message')
+        if message is not None:
+            message = decode(message, self.context)
+        return message
+ 
+    @property
+    @memoize
+    def actions(self):
+        return self.atool.listFilteredActionsFor(self.context)
+    
+    @property
+    @memoize
+    def user_actions(self):
+        return self.actions['user']
+    
+    @property
+    @memoize
+    def object_actions(self):
+        return self.actions['object']
+    
+    @property
+    @memoize
+    def workflow_actions(self):
+        return self.actions['workflow']
+    
+    @property
+    @memoize
+    def folder_actions(self):
+        return self.actions['folder']
+    
+    @property
+    @memoize
+    def global_actions(self):
+        return self.actions['global']
+    
+    @property
+    @memoize
+    def add_actions(self):
+        return self.actions.get('folder/add', ())

Deleted: Products.CMFDefault/trunk/Products/CMFDefault/browser/ursa.py
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/ursa.py	2010-06-25 12:36:59 UTC (rev 113834)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/ursa.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -1,195 +0,0 @@
-from Products.CMFDefault.utils import Message as _
-from Products.CMFDefault.utils import decode
-from Products.CMFDefault.browser.utils import ViewBase
-from Products.CMFDefault.browser.utils import memoize
-
-class UrsineGlobals(ViewBase):
-    """ Provide lazy / efficient template-level globals.
-
-    o Replaces 'getMainGlobals' in stock skin.
-    """
-    def __init__(self, context, request):
-        super(ViewBase, self).__init__(context, request)
-        ct = self.request.RESPONSE.getHeader('content-type') or ''
-        if not 'charset' in ct:
-            # Some newstyle views set a different charset - don't override it.
-            # Oldstyle views need the default_charset.
-            default_charset = self.ptool.getProperty('default_charset', None)
-            if default_charset:
-                self.request.RESPONSE.setHeader('content-type',
-                              '%s; charset=%s' % (ct, default_charset))
-
-    @property
-    @memoize
-    def ptool(self):
-        return self._getTool('portal_properties')
-
-    @property
-    @memoize
-    def utool(self):
-        return self._getTool('portal_url')
-
-    @property
-    @memoize
-    def mtool(self):
-        return self._getTool('portal_membership')
-
-    @property
-    @memoize
-    def atool(self):
-        return self._getTool('portal_actions')
-
-    @property
-    @memoize
-    def wtool(self):
-        return self._getTool('portal_workflow')
-
-    @property
-    @memoize
-    def portal_object(self):
-        return self.utool.getPortalObject()
-
-    @property
-    @memoize
-    def portal_url(self):
-        return self.utool()
-
-    @property
-    @memoize
-    def portal_title(self):
-        return self.portal_object.Title()
-
-    @property
-    @memoize
-    def object_title(self):
-        return self.context.Title()
-
-    @property
-    @memoize
-    def object_description(self):
-        return self.context.Description()
-
-    @property
-    @memoize
-    def trunc_id(self):
-        id = self.context.getId()
-        if len(id) > 15:
-            id = id[:15] + '...'
-        return id
-
-    @property
-    @memoize
-    def icon(self):
-        return getattr(self.context, 'getIconURL',
-                        lambda: getattr(self.context, 'icon', ''))()
-    @property
-    @memoize
-    def typename(self):
-        return self.context.getPortalTypeName()
-
-    @property
-    @memoize
-    def wf_state(self):
-        return self.wtool.getInfoFor(self.context, 'review_state', '')
-
-    @property
-    @memoize
-    def page_title(self):
-        site_title = self.portal_title
-        page_title = self.object_title
-
-        if page_title != site_title:
-            page_title = site_title + ": " + page_title
-
-        return decode(page_title, self.context)
-
-    @property
-    @memoize
-    def breadcrumbs(self):
-        # XXX Shouldn't we just be walking up the aq_inner chain?
-        result = [{'id': _(u'root'),
-                   'title' : self.ptool.title(),
-                   'url' : self.portal_url,
-                  }]
-
-        relative = self.utool.getRelativeContentPath(self.context)
-        portal = self.portal_object
-
-        for i, token in enumerate(relative):
-            now = relative[:i+1]
-            obj = portal.unrestrictedTraverse(now)
-            if token != 'talkback':
-                result.append({'id': token,
-                               'title': obj.Title(),
-                               'url': obj.absolute_url(),
-                              })
-
-        return result
-
-    @property
-    @memoize
-    def member(self):
-        return self.mtool.getAuthenticatedMember()
-
-    @property
-    @memoize
-    def membername(self):
-        return self.isAnon and 'Guest' or self.member.getUserName()
-
-    @property
-    @memoize
-    def membersfolder(self):
-        return self.mtool.getMembersFolder()
-
-    @property
-    @memoize
-    def isAnon(self):
-        return self.mtool.isAnonymousUser()
-
-    @property
-    @memoize
-    def uname(self):
-        return self.isAnon and 'Guest' or self.member.getUserName()
-
-    @property
-    @memoize
-    def status_message(self):
-        message = self.request.form.get('portal_status_message')
-        if message is not None:
-            message = decode(message, self.context)
-        return message
- 
-    @property
-    @memoize
-    def actions(self):
-        return self.atool.listFilteredActionsFor(self.context)
-    
-    @property
-    @memoize
-    def user_actions(self):
-        return self.actions['user']
-    
-    @property
-    @memoize
-    def object_actions(self):
-        return self.actions['object']
-    
-    @property
-    @memoize
-    def workflow_actions(self):
-        return self.actions['workflow']
-    
-    @property
-    @memoize
-    def folder_actions(self):
-        return self.actions['folder']
-    
-    @property
-    @memoize
-    def global_actions(self):
-        return self.actions['global']
-    
-    @property
-    @memoize
-    def add_actions(self):
-        return self.actions.get('folder/add', ())

Deleted: Products.CMFDefault/trunk/Products/CMFDefault/browser/utils.py
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/utils.py	2010-06-25 12:36:59 UTC (rev 113834)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/utils.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -1,105 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2006 Zope Foundation and Contributors.
-#
-# 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 AccessControl.SecurityInfo import ClassSecurityInfo
-from App.class_init import InitializeClass
-from Products.Five import BrowserView
-from zope.component import getUtility
-
-from Products.CMFCore.interfaces import IPropertiesTool
-from Products.CMFCore.utils import getToolByName
-from Products.CMFDefault.permissions import View
-from Products.CMFDefault.utils import getBrowserCharset
-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 MacroView(BrowserView):
-
-    """Allows to use macros from non-view templates.
-    """
-    
-    # The following allows to traverse the view/class and reach
-    # macros defined in page templates, e.g. in a use-macro.
-    security = ClassSecurityInfo()
-
-    def _macros(self):
-        return self.index.macros
-
-    security.declareProtected(View, 'macros')
-    macros = property(_macros, None, None)
-
-InitializeClass(MacroView)
-
-
-class ViewBase(BrowserView):
-
-    # 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 = getUtility(IPropertiesTool)
-        return ptool.getProperty('default_charset', None)
-
-    @memoize
-    def _getBrowserCharset(self):
-        return getBrowserCharset(self.request)
-
-    # interface
-
-    @memoize
-    @decode
-    def title(self):
-        return self.context.Title()
-
-    @memoize
-    @decode
-    def description(self):
-        return self.context.Description()

Added: Products.CMFDefault/trunk/Products/CMFDefault/browser/utils.py
===================================================================
--- Products.CMFDefault/trunk/Products/CMFDefault/browser/utils.py	                        (rev 0)
+++ Products.CMFDefault/trunk/Products/CMFDefault/browser/utils.py	2010-06-25 14:31:03 UTC (rev 113835)
@@ -0,0 +1,105 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Foundation and Contributors.
+#
+# 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 AccessControl.SecurityInfo import ClassSecurityInfo
+from App.class_init import InitializeClass
+from Products.Five import BrowserView
+from zope.component import getUtility
+
+from Products.CMFCore.interfaces import IPropertiesTool
+from Products.CMFCore.utils import getToolByName
+from Products.CMFDefault.permissions import View
+from Products.CMFDefault.utils import getBrowserCharset
+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 MacroView(BrowserView):
+
+    """Allows to use macros from non-view templates.
+    """
+    
+    # The following allows to traverse the view/class and reach
+    # macros defined in page templates, e.g. in a use-macro.
+    security = ClassSecurityInfo()
+
+    def _macros(self):
+        return self.index.macros
+
+    security.declareProtected(View, 'macros')
+    macros = property(_macros, None, None)
+
+InitializeClass(MacroView)
+
+
+class ViewBase(BrowserView):
+
+    # 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 = getUtility(IPropertiesTool)
+        return ptool.getProperty('default_charset', None)
+
+    @memoize
+    def _getBrowserCharset(self):
+        return getBrowserCharset(self.request)
+
+    # interface
+
+    @memoize
+    @decode
+    def title(self):
+        return self.context.Title()
+
+    @memoize
+    @decode
+    def description(self):
+        return self.context.Description()



More information about the checkins mailing list