[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&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&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&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&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">
+ <a href="" tal:attributes="href item_info/url"
+ tal:content="item_info/title">TITLE</a></tal:case
+ ><tal:case tal:condition="item_info/description">
+ <br /><tal:span tal:content="item_info/description"
+ >DESCRIPTION</tal:span></tal:case
+ ><tal:case tal:condition="item_info/format">
+ <br /><span><tal:span tal:content="item_info/format" i18n:translate=""
+ >FORMAT</tal:span><tal:case tal:condition="item_info/size"
+ >, <tal:span tal:content="item_info/size">99.9 KB</tal:span></tal:case
+ ></span></tal:case></p
+></metal:macro>
+
+ <metal:macro metal:define-macro="navigation"
+ tal:define="prev_info view/navigation_previous;
+ next_info view/navigation_next"
+ ><p class="BatchNavigation" tal:condition="python: prev_info or next_info"
+ ><tal:case tal:condition="prev_info">
+ <a href="" tal:attributes="href prev_info/url"
+ tal:content="prev_info/title"
+ i18n:translate="">PREVIOUS N ITEMS</a></tal:case
+ ><tal:case tal:condition="python: prev_info and next_info">
+ </tal:case
+ ><tal:case tal:condition="next_info">
+ <a href="" tal:attributes="href next_info/url"
+ tal:content="next_info/title"
+ i18n:translate="">NEXT N ITEMS</a></tal:case
+ ></p
+></metal:macro>
+
+<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"><<</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">>></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