--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/HISTORY.txt Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,71 @@
+Changelog
+=========
+
+0.3.11
+------
+ - added dotted decimal schema field, not handling locales :-/
+
+0.3.10
+------
+ - upgraded for ztfy.jqueryui 0.6.0
+ - added Color schema field and widget
+ - added StringLine schema field
+ - added "text:translate" TAL adapter
+ - moved ITransactionManager adapter from ztfy.scheduler package
+
+0.3.9
+-----
+ - added HTTP client based on httplib2, handling authentication and proxies
+
+0.3.8
+-----
+ - corrected encodings vocabulary
+
+0.3.7
+-----
+ - added encodings vocabulary
+
+0.3.6
+-----
+ - corrected code and translations in MissingPrincipal class
+ - added permissions on TextIndexNG index
+
+0.3.5
+-----
+ - re-add IList and IDict interfaces forgotten from bad merge :-(
+
+0.3.4
+-----
+ - better check for missing requests
+
+0.3.3
+-----
+ - Added "fanstatic:" TALES expression
+
+0.3.2
+-----
+ - Mark ztfy.utils.security functions and classes as deprecated
+
+0.3.1
+-----
+ - Updated signature in ztfy.utils.catalog.index to match last hurry.query release
+
+0.3
+---
+ - Switched to ZTK-1.1.2 and Python 2.6
+ - Added "getAge" function in date module
+ - Added session module and TALES adapter to get/set session values
+ - Check None value in catalog.getObjectId(...) and catalog.getObject(...) methods
+
+0.2.1
+-----
+ - Added 'site.locateAndRegister' facility function
+ - Update ServerTimezoneUtility parent classes
+
+0.2
+---
+ - Added 'data' namespace to access request data
+
+0.1
+---
+ - Initial release
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/README.txt Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,30 @@
+==================
+ztfy.utils package
+==================
+
+.. contents::
+
+What is ztfy.utils ?
+====================
+
+ztfy.utils is a set of classes and functions which can be used to handle many small operations.
+
+Internal sub-packages include :
+ - date : convert dates to unicode ISO format, parse ISO datetime, convert date to datetime
+ - request : get current request, get request annotations, get and set request data via annotations
+ - security : get unproxied value of a given object ; can be applied to lists or dicts
+ - timezone : convert datetime to a given timezone ; provides a server default timezone utility
+ - traversing : get object parents until a given interface is implemented
+ - unicode : convert any text to unicode for easy storage
+ - protocol : utility functions and modules for several nerwork protocols
+ - catalog : TextIndexNG index for Zope catalog and hurry.query "Text" query item
+ - text : simple text operations and text to HTML conversion
+ - html : HTML parser and HTML to text converter
+ - file : file upload data converter
+ - tal : text and HTML conversions for use from within TAL
+
+
+How to use ztfy.utils ?
+=======================
+
+A set of ztfy.utils usage are given as doctests in ztfy/utils/doctests/README.txt
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy.utils.egg-info/PKG-INFO Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,119 @@
+Metadata-Version: 1.0
+Name: ztfy.utils
+Version: 0.3.12
+Summary: ZTFY utility functions and classes for Zope3
+Home-page: http://www.ztfy.org
+Author: Thierry Florac
+Author-email: tflorac@ulthar.net
+License: ZPL
+Description: ==================
+ ztfy.utils package
+ ==================
+
+ .. contents::
+
+ What is ztfy.utils ?
+ ====================
+
+ ztfy.utils is a set of classes and functions which can be used to handle many small operations.
+
+ Internal sub-packages include :
+ - date : convert dates to unicode ISO format, parse ISO datetime, convert date to datetime
+ - request : get current request, get request annotations, get and set request data via annotations
+ - security : get unproxied value of a given object ; can be applied to lists or dicts
+ - timezone : convert datetime to a given timezone ; provides a server default timezone utility
+ - traversing : get object parents until a given interface is implemented
+ - unicode : convert any text to unicode for easy storage
+ - protocol : utility functions and modules for several nerwork protocols
+ - catalog : TextIndexNG index for Zope catalog and hurry.query "Text" query item
+ - text : simple text operations and text to HTML conversion
+ - html : HTML parser and HTML to text converter
+ - file : file upload data converter
+ - tal : text and HTML conversions for use from within TAL
+
+
+ How to use ztfy.utils ?
+ =======================
+
+ A set of ztfy.utils usage are given as doctests in ztfy/utils/doctests/README.txt
+
+
+ Changelog
+ =========
+
+ 0.3.11
+ ------
+ - added dotted decimal schema field, not handling locales :-/
+
+ 0.3.10
+ ------
+ - upgraded for ztfy.jqueryui 0.6.0
+ - added Color schema field and widget
+ - added StringLine schema field
+ - added "text:translate" TAL adapter
+ - moved ITransactionManager adapter from ztfy.scheduler package
+
+ 0.3.9
+ -----
+ - added HTTP client based on httplib2, handling authentication and proxies
+
+ 0.3.8
+ -----
+ - corrected encodings vocabulary
+
+ 0.3.7
+ -----
+ - added encodings vocabulary
+
+ 0.3.6
+ -----
+ - corrected code and translations in MissingPrincipal class
+ - added permissions on TextIndexNG index
+
+ 0.3.5
+ -----
+ - re-add IList and IDict interfaces forgotten from bad merge :-(
+
+ 0.3.4
+ -----
+ - better check for missing requests
+
+ 0.3.3
+ -----
+ - Added "fanstatic:" TALES expression
+
+ 0.3.2
+ -----
+ - Mark ztfy.utils.security functions and classes as deprecated
+
+ 0.3.1
+ -----
+ - Updated signature in ztfy.utils.catalog.index to match last hurry.query release
+
+ 0.3
+ ---
+ - Switched to ZTK-1.1.2 and Python 2.6
+ - Added "getAge" function in date module
+ - Added session module and TALES adapter to get/set session values
+ - Check None value in catalog.getObjectId(...) and catalog.getObject(...) methods
+
+ 0.2.1
+ -----
+ - Added 'site.locateAndRegister' facility function
+ - Update ServerTimezoneUtility parent classes
+
+ 0.2
+ ---
+ - Added 'data' namespace to access request data
+
+ 0.1
+ ---
+ - Initial release
+
+Keywords: ZTFY utilities for Zope3
+Platform: UNKNOWN
+Classifier: License :: OSI Approved :: Zope Public License
+Classifier: Development Status :: 4 - Beta
+Classifier: Programming Language :: Python
+Classifier: Framework :: Zope3
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy.utils.egg-info/SOURCES.txt Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,60 @@
+.hgignore
+.installed.cfg
+.project
+.pydevproject
+LICENSE
+MANIFEST.in
+README.txt
+bootstrap.py
+buildout.cfg
+setup.py
+bin/buildout
+src/ztfy/__init__.py
+src/ztfy.utils.egg-info/PKG-INFO
+src/ztfy.utils.egg-info/PKG-INFO.orig
+src/ztfy.utils.egg-info/SOURCES.txt
+src/ztfy.utils.egg-info/dependency_links.txt
+src/ztfy.utils.egg-info/entry_points.txt
+src/ztfy.utils.egg-info/namespace_packages.txt
+src/ztfy.utils.egg-info/not-zip-safe
+src/ztfy.utils.egg-info/requires.txt
+src/ztfy.utils.egg-info/top_level.txt
+src/ztfy/utils/__init__.py
+src/ztfy/utils/date.py
+src/ztfy/utils/encoding.py
+src/ztfy/utils/file.py
+src/ztfy/utils/html.py
+src/ztfy/utils/interfaces.py
+src/ztfy/utils/profilehooks.py
+src/ztfy/utils/property.py
+src/ztfy/utils/request.py
+src/ztfy/utils/schema.py
+src/ztfy/utils/security.py
+src/ztfy/utils/session.py
+src/ztfy/utils/site.py
+src/ztfy/utils/text.py
+src/ztfy/utils/traversing.py
+src/ztfy/utils/unicode.py
+src/ztfy/utils/zodb.py
+src/ztfy/utils/browser/__init__.py
+src/ztfy/utils/browser/color.py
+src/ztfy/utils/browser/encoding.py
+src/ztfy/utils/catalog/__init__.py
+src/ztfy/utils/catalog/index.py
+src/ztfy/utils/protocol/__init__.py
+src/ztfy/utils/protocol/http.py
+src/ztfy/utils/protocol/xmlrpc.py
+src/ztfy/utils/tal/__init__.py
+src/ztfy/utils/tal/fanstatic.py
+src/ztfy/utils/tal/html.py
+src/ztfy/utils/tal/interfaces.py
+src/ztfy/utils/tal/request.py
+src/ztfy/utils/tal/session.py
+src/ztfy/utils/tal/text.py
+src/ztfy/utils/tests/__init__.py
+src/ztfy/utils/tests/test_utilsdocs.py
+src/ztfy/utils/tests/test_utilsdocstrings.py
+src/ztfy/utils/timezone/__init__.py
+src/ztfy/utils/timezone/interfaces.py
+src/ztfy/utils/timezone/schema.py
+src/ztfy/utils/timezone/utility.py
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy.utils.egg-info/dependency_links.txt Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,1 @@
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy.utils.egg-info/entry_points.txt Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,3 @@
+
+ # -*- Entry points: -*-
+
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy.utils.egg-info/namespace_packages.txt Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,1 @@
+ztfy
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy.utils.egg-info/not-zip-safe Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,1 @@
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy.utils.egg-info/requires.txt Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,37 @@
+setuptools
+chardet
+fanstatic
+httplib2
+hurry.query
+pytz
+transaction
+z3c.form
+zc.set
+ZODB3
+zope.annotation
+zope.app.authentication
+zope.app.file
+zope.authentication
+zope.catalog
+zope.component
+zope.container
+zope.datetime
+zope.deprecation
+zope.i18n
+zope.i18nmessageid
+zope.index
+zope.interface
+zope.intid
+zope.location
+zope.pluggableauth
+zope.publisher
+zope.schema
+zope.security
+zope.session
+zope.tales
+zope.traversing
+zopyx.txng3.core
+ztfy.jqueryui >= 0.6.0
+
+[test]
+zope.testing
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy.utils.egg-info/top_level.txt Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,1 @@
+ztfy
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/__init__.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,6 @@
+# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages
+try:
+ __import__('pkg_resources').declare_namespace(__name__)
+except ImportError:
+ from pkgutil import extend_path
+ __path__ = extend_path(__path__, __name__)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/__init__.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,22 @@
+### -*- coding: utf-8 -*- ####################################################
+##############################################################################
+#
+# Copyright (c) 2008 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+
+from zope.i18nmessageid import MessageFactory
+_ = MessageFactory('ztfy.utils')
+
+
+from traversing import getParent
+from request import getRequest, getRequestPrincipal, getRequestData, setRequestData
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/browser/__init__.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,1 @@
+#
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/browser/color.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,53 @@
+### -*- coding: utf-8 -*- ####################################################
+##############################################################################
+#
+# Copyright (c) 2012 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+
+# import standard packages
+
+# import Zope3 interfaces
+from z3c.form.interfaces import ITextWidget, IFieldWidget, IFormLayer
+
+# import local interfaces
+from ztfy.utils.schema import IColorField
+
+# import Zope3 packages
+from z3c.form.browser.text import TextWidget
+from z3c.form.widget import FieldWidget
+from zope.component import adapter
+from zope.interface import implementer, implementsOnly
+
+# import local packages
+from ztfy.jqueryui import jquery_colorpicker
+
+
+class IColorWidget(ITextWidget):
+ """Color widget interface"""
+
+
+class ColorWidget(TextWidget):
+ """Color widget"""
+
+ implementsOnly(IColorWidget)
+
+ def update(self):
+ TextWidget.update(self)
+ jquery_colorpicker.need()
+
+
+@adapter(IColorField, IFormLayer)
+@implementer(IFieldWidget)
+def ColorFieldWidgetFactory(field, request):
+ """IColorField widget factory"""
+ return FieldWidget(field, ColorWidget(request))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/browser/configure.zcml Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,38 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ xmlns:z3c="http://namespaces.zope.org/z3c"
+ i18n_domain="ztfy.i18n">
+
+ <!-- Encoding selection widget -->
+ <adapter
+ factory=".encoding.EncodingSelectFieldWidget" />
+
+ <class class=".encoding.EncodingSelectWidget">
+ <require
+ interface=".encoding.IEncodingSelectWidget"
+ permission="zope.Public" />
+ </class>
+
+ <!-- Color selection widget -->
+ <adapter
+ factory=".color.ColorFieldWidgetFactory" />
+
+ <class class=".color.ColorWidget">
+ <require
+ interface=".color.IColorWidget"
+ permission="zope.Public" />
+ </class>
+
+ <z3c:widgetTemplate
+ mode="input"
+ template="templates/color_input.pt"
+ widget=".color.ColorWidget"
+ layer="z3c.form.interfaces.IFormLayer" />
+
+ <z3c:widgetTemplate
+ mode="display"
+ template="templates/color_display.pt"
+ widget=".color.ColorWidget"
+ layer="z3c.form.interfaces.IFormLayer" />
+
+</configure>
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/browser/encoding.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,54 @@
+### -*- coding: utf-8 -*- ####################################################
+##############################################################################
+#
+# Copyright (c) 2008-2012 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+__docformat__ = "restructuredtext"
+
+# import standard packages
+
+# import Zope3 interfaces
+from z3c.form.interfaces import ISelectWidget, IFieldWidget
+from zope.publisher.interfaces.browser import IBrowserRequest
+
+# import local interfaces
+
+# import Zope3 packages
+from z3c.form.browser.select import SelectWidget
+from z3c.form.widget import FieldWidget
+from zope.component import adapter
+from zope.interface import implementer, implementsOnly
+
+# import local packages
+from ztfy.utils.encoding import IEncodingField
+
+from ztfy.utils import _
+
+
+class IEncodingSelectWidget(ISelectWidget):
+ """ENcoding select widget interface"""
+
+
+class EncodingSelectWidget(SelectWidget):
+ """Encoding select widget"""
+
+ implementsOnly(IEncodingSelectWidget)
+
+ noValueMessage = _("-- automatic selection --")
+
+
+@adapter(IEncodingField, IBrowserRequest)
+@implementer(IFieldWidget)
+def EncodingSelectFieldWidget(field, request):
+ """IFieldWidget factory for IEncodingSelectWidget"""
+ return FieldWidget(field, EncodingSelectWidget(request))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/browser/templates/color_display.pt Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,14 @@
+<div class="colorSelector"
+ tal:attributes="id string:${view/id}_selector">
+ <input type="hidden"
+ tal:attributes="id view/id;
+ name view/name;
+ lang view/lang;
+ value view/value;" />
+ <div></div>
+ <script type="text/javascript" tal:content="string:
+ $$(document).ready(function() {
+ $$('DIV[id=${view/id}_selector] div').css('backgroundColor', '#${view/value}');
+ });
+ "></script>
+</div>
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/browser/templates/color_input.pt Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,32 @@
+<div class="colorSelector"
+ tal:attributes="id string:${view/id}_selector">
+ <input type="hidden"
+ tal:attributes="id view/id;
+ name view/name;
+ lang view/lang;
+ value view/value;" />
+ <div></div>
+ <script type="text/javascript" tal:content="string:
+ $$(document).ready(function() {
+ $$('DIV[id=${view/id}_selector] div').css('backgroundColor', '#${view/value}');
+ $$('DIV[id=${view/id}_selector]').ColorPicker({
+ color: '#${view/value}',
+ onShow: function (colpkr) {
+ $$(colpkr).fadeIn(500);
+ return false;
+ },
+ onHide: function (colpkr) {
+ $$(colpkr).fadeOut(500);
+ return false;
+ },
+ onChange: function (hsb, hex, rgb) {
+ $$('DIV[id=${view/id}_selector] div').css('backgroundColor', '#' + hex);
+ $$('INPUT[id=${view/id}]').val(hex);
+ },
+ onSubmit: function() {
+ $$('DIV[id=${view/id}_selector]').ColorPickerHide();
+ }
+ });
+ });
+ "></script>
+</div>
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/catalog/__init__.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,164 @@
+### -*- coding: utf-8 -*- ####################################################
+##############################################################################
+#
+# Copyright (c) 2008-2009 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+__docformat__ = "restructuredtext"
+
+# import standard packages
+
+# import Zope3 interfaces
+from zope.annotation.interfaces import IAnnotations
+from zope.app.file.interfaces import IFile
+from zope.catalog.interfaces import ICatalog
+from zope.container.interfaces import IContainer
+from zope.intid.interfaces import IIntIds
+
+# import local interfaces
+
+# import Zope3 packages
+from zope.component import queryUtility, getAllUtilitiesRegisteredFor
+
+# import local packages
+from ztfy.utils import request as request_utils
+
+from ztfy.utils import _
+
+
+#
+# IntIds utility functions
+#
+
+def getIntIdUtility(name='', request=None, context=None):
+ """Look for a named IIntIds utility"""
+ if request is None:
+ request = request_utils.queryRequest()
+ intids = request_utils.getRequestData('IntIdsUtility::' + name, request)
+ if intids is None:
+ intids = queryUtility(IIntIds, name, context=context)
+ if (request is not None) and (intids is not None):
+ request_utils.setRequestData('IntIdsUtility::' + name, intids, request)
+ return intids
+
+
+def getObjectId(object, intids_name='', request=None, context=None):
+ """Look for an object Id as recorded by given IIntIds utility"""
+ if object is None:
+ return None
+ if request is None:
+ request = request_utils.queryRequest()
+ intids = getIntIdUtility(intids_name, request, context)
+ if intids is not None:
+ return intids.queryId(object)
+ return None
+
+
+def getObject(id, intids_name='', request=None, context=None):
+ """Look for an object recorded by given IIntIds utility and id"""
+ if id is None:
+ return None
+ if request is None:
+ request = request_utils.getRequest()
+ intids = getIntIdUtility(intids_name, request, context)
+ if intids is not None:
+ return intids.queryObject(id)
+ return None
+
+
+#
+# Catalog utility functions
+#
+
+def queryCatalog(name='', context=None):
+ """Look for a registered catalog"""
+ return queryUtility(ICatalog, name, context=context)
+
+
+def indexObject(object, catalog_name='', index_name='', request=None, context=None):
+ """Index object into a registered catalog"""
+ if request is None:
+ request = request_utils.getRequest()
+ intids = getIntIdUtility('', request, context)
+ if intids is not None:
+ if ICatalog.providedBy(catalog_name):
+ catalog = catalog_name
+ else:
+ catalog = queryCatalog(catalog_name, context)
+ if catalog is not None:
+ id = intids.register(object)
+ if index_name:
+ catalog[index_name].index_doc(id, object)
+ else:
+ catalog.index_doc(id, object)
+ return True
+ return False
+
+
+def unindexObject(object, catalog_name='', index_name='', request=None, context=None):
+ """Remove object from a registered catalog"""
+ if request is None:
+ request = request_utils.getRequest()
+ id = getObjectId(object, '', request, context)
+ if id is not None:
+ if ICatalog.providedBy(catalog_name):
+ catalog = catalog_name
+ else:
+ catalog = queryCatalog(catalog_name, context)
+ if catalog is not None:
+ if index_name:
+ catalog[index_name].unindex_doc(id)
+ else:
+ catalog.unindex_doc(id)
+ return True
+ return False
+
+
+def _indexObject(object, intids, catalogs):
+ """Index object data into given set of catalogs"""
+ id = intids.register(object)
+ for catalog in catalogs:
+ catalog.index_doc(id, object)
+
+def _indexObjectValues(object, intids, catalogs):
+ """Index object values into given set of catalogs"""
+ container = IContainer(object, None)
+ if container is not None:
+ for subobject in container.values():
+ _indexAllObject(subobject, intids, catalogs)
+
+def _indexObjectAnnotations(object, intids, catalogs):
+ """Index object annotations into given set of catalogs"""
+ annotations = IAnnotations(object, None)
+ if annotations is not None:
+ keys = annotations.keys()
+ for key in keys:
+ _indexAllObject(annotations[key], intids, catalogs)
+ file = IFile(annotations[key], None)
+ if file is not None:
+ _indexObject(file, intids, catalogs)
+
+def _indexAllObject(object, intids, catalogs):
+ """Index object, object values and annotations into given set of catalogs"""
+ _indexObject(object, intids, catalogs)
+ _indexObjectValues(object, intids, catalogs)
+ _indexObjectAnnotations(object, intids, catalogs)
+
+def indexAllObjectValues(object, context=None):
+ """Reindex a whole container properties and contents (including annotations) into site's catalogs"""
+ if context is None:
+ context = object
+ intids = queryUtility(IIntIds, context=context)
+ if intids is not None:
+ catalogs = getAllUtilitiesRegisteredFor(ICatalog, context)
+ if catalogs:
+ _indexAllObject(object, intids, catalogs)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/catalog/configure.zcml Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,16 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ i18n_domain="ztfy.utils">
+
+ <class class=".index.TextIndexNG">
+ <require
+ interface="zope.catalog.interfaces.IAttributeIndex
+ zope.index.interfaces.IStatistics"
+ set_schema="zope.catalog.interfaces.IAttributeIndex"
+ permission="zope.ManageServices" />
+ <require
+ interface="zope.index.interfaces.IIndexSearch"
+ permission="zope.View" />
+ </class>
+
+</configure>
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/catalog/index.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,152 @@
+### -*- coding: utf-8 -*- ####################################################
+##############################################################################
+#
+# Copyright (c) 2008-2010 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+__docformat__ = "restructuredtext"
+
+# import standard packages
+import re
+from persistent import Persistent
+from BTrees import IFBTree
+
+# import Zope3 interfaces
+from zope.index.interfaces import IInjection, IStatistics, IIndexSearch
+from zopyx.txng3.core.interfaces import IStorageWithTermFrequency
+from zopyx.txng3.core.interfaces.ting import ITingIndex
+
+# import local interfaces
+
+# import Zope3 packages
+from zope.catalog.attribute import AttributeIndex
+from zope.component import createObject
+from zope.container.contained import Contained
+from zope.interface import implements
+from zopyx.txng3.core import config
+from zopyx.txng3.core.index import Index
+
+# import local packages
+from hurry.query.query import IndexTerm
+
+
+class TextIndexNG(AttributeIndex, Persistent, Contained):
+ """Adaptation of zopyx.txng3.core for use zope.catalog index"""
+
+ implements(IInjection, IStatistics, IIndexSearch, ITingIndex)
+
+ def __init__(self,
+ field_name=None,
+ interface=None,
+ field_callable=False,
+ use_stemmer=config.defaults['use_stemmer'],
+ dedicated_storage=config.defaults['dedicated_storage'],
+ ranking=config.defaults['ranking'],
+ use_normalizer=config.defaults['use_normalizer'],
+ languages=config.DEFAULT_LANGUAGE,
+ use_stopwords=config.defaults['use_stopwords'],
+ autoexpand_limit=config.defaults['autoexpand_limit'],
+ splitter=config.DEFAULT_SPLITTER,
+ index_unknown_languages=config.defaults['index_unknown_languages'],
+ query_parser=config.DEFAULT_PARSER,
+ lexicon=config.DEFAULT_LEXICON,
+ splitter_additional_chars=config.defaults['splitter_additional_chars'],
+ storage=config.DEFAULT_STORAGE,
+ splitter_casefolding=config.defaults['splitter_casefolding']):
+ spaces = re.compile(r'\s+')
+ if ranking:
+ util = createObject(storage)
+ if not IStorageWithTermFrequency.providedBy(util):
+ raise ValueError("This storage cannot be used for ranking")
+ _fields = spaces.split(field_name)
+ AttributeIndex.__init__(self, _fields[0], interface, field_callable)
+ if len(_fields) < 2:
+ dedicated_storage = False
+ self._index = Index(fields=_fields,
+ languages=spaces.split(languages),
+ use_stemmer=use_stemmer,
+ dedicated_storage=dedicated_storage,
+ ranking=ranking,
+ use_normalizer=use_normalizer,
+ use_stopwords=use_stopwords,
+ storage=storage,
+ autoexpand_limit=autoexpand_limit,
+ splitter=splitter,
+ lexicon=lexicon,
+ index_unknown_languages=index_unknown_languages,
+ query_parser=query_parser,
+ splitter_additional_chars=splitter_additional_chars,
+ splitter_casefolding=splitter_casefolding)
+ self.languages = languages
+ self.use_stemmer = use_stemmer
+ self.dedicated_storage = dedicated_storage
+ self.ranking = ranking
+ self.use_normalizer = use_normalizer
+ self.use_stopwords = use_stopwords
+ self.interface = interface
+ self.storage = storage
+ self.autoexpand_limit = autoexpand_limit
+ self.default_field = _fields[0]
+ self._fields = _fields
+ self.splitter = splitter
+ self.lexicon = lexicon
+ self.index_unknown_languages = index_unknown_languages
+ self.query_parser = query_parser
+ self.splitter_additional_chars = splitter_additional_chars
+ self.splitter_casefolding = splitter_casefolding
+
+ def clear(self):
+ self._index.clear()
+
+ def documentCount(self):
+ """See interface IStatistics"""
+ return len(self._index.getStorage(self.default_field))
+
+ def wordCount(self):
+ """See interface IStatistics"""
+ return len(self._index.getLexicon())
+
+ def index_doc(self, docid, value):
+ """See interface IInjection"""
+ v = self.interface(value, None)
+ if v is not None:
+ self.unindex_doc(docid)
+ self._index.index_object(v, docid)
+
+ def unindex_doc(self, docid):
+ """See interface IInjection"""
+ self._index.unindex_object(docid)
+
+ def apply(self, query):
+ if isinstance(query, dict):
+ kw = query
+ query = kw['query']
+ del kw['query']
+ ting_rr = self._index.search(query, **kw)
+ return ting_rr.getDocids().keys()
+
+
+class Text(IndexTerm):
+ """hurry.query search term"""
+
+ def __init__(self, index_id, text):
+ super(Text, self).__init__(index_id)
+ self.text = text
+
+ def getIndex(self, context=None):
+ index = super(Text, self).getIndex(context)
+ assert ITingIndex.providedBy(index)
+ return index
+
+ def apply(self, context=None):
+ index = self.getIndex(context)
+ return IFBTree.IFSet(index.apply(self.text))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/configure.zcml Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,30 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ xmlns:i18n="http://namespaces.zope.org/i18n"
+ i18n_domain="ztfy.utils">
+
+ <i18n:registerTranslations directory="locales" />
+
+ <!-- IPersistent adapters copied from zc.twist package -->
+ <adapter
+ factory=".zodb.transactionManager" />
+
+ <!-- Encodings management -->
+ <utility
+ name="ZTFY encodings"
+ component=".encoding.EncodingsVocabulary" />
+
+ <!-- Custom schema fields -->
+ <adapter
+ factory=".schema.DottedDecimalDataConverter"
+ trusted="True" />
+
+ <!-- Sub-packages -->
+ <include package=".tal" />
+
+ <include package=".catalog" />
+ <include package=".timezone" />
+
+ <include package=".browser" />
+
+</configure>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/date.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,133 @@
+### -*- coding: utf-8 -*- ####################################################
+##############################################################################
+#
+# Copyright (c) 2008 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+
+# import standard packages
+from datetime import datetime
+
+# import Zope3 interfaces
+
+# import local interfaces
+
+# import Zope3 packages
+from zope.datetime import parseDatetimetz
+from zope.i18n import translate
+
+# import local packages
+from timezone import gmtime
+
+from ztfy.utils import _
+
+
+def unidate(value):
+ """Get specified date converted to unicode ISO format
+
+ Dates are always assumed to be stored in GMT timezone
+
+ @param value: input date to convert to unicode
+ @type value: date or datetime
+ @return: input date converted to unicode
+ @rtype: unicode
+ """
+ if value is not None:
+ value = gmtime(value)
+ return unicode(value.isoformat('T'), 'ascii')
+ return None
+
+
+def parsedate(value):
+ """Get date specified in unicode ISO format to Python datetime object
+
+ Dates are always assumed to be stored in GMT timezone
+
+ @param value: unicode date to be parsed
+ @type value: unicode
+ @return: the specified value, converted to datetime
+ @rtype: datetime
+ """
+ if value is not None:
+ return gmtime(parseDatetimetz(value))
+ return None
+
+
+def datetodatetime(value):
+ """Get datetime value converted from a date or datetime object
+
+ @param value: a date or datetime value to convert
+ @type value: date or datetime
+ @return: input value converted to datetime
+ @rtype: datetime
+ """
+ if type(value) is datetime:
+ return value
+ return datetime(value.year, value.month, value.day)
+
+
+def getAge(value):
+ """Get age of a given datetime (including timezone) compared to current datetime (in UTC)
+
+ @param value: a datetime value, including timezone
+ @type value: datetime
+ @return: string representing value age
+ @rtype: gettext translated string
+ """
+ now = gmtime(datetime.utcnow())
+ delta = now - value
+ if delta.days > 60:
+ return translate(_("%d months ago")) % int(round(delta.days * 1.0 / 30))
+ elif delta.days > 10:
+ return translate(_("%d weeks ago")) % int(round(delta.days * 1.0 / 7))
+ elif delta.days > 2:
+ return translate(_("%d days ago")) % delta.days
+ elif delta.days == 2:
+ return translate(_("the day before yesterday"))
+ elif delta.days == 1:
+ return translate(_("yesterday"))
+ else:
+ hours = int(round(delta.seconds * 1.0 / 3600))
+ if hours > 1:
+ return translate(_("%d hours ago")) % hours
+ elif delta.seconds > 300:
+ return translate(_("%d minutes ago")) % int(round(delta.seconds * 1.0 / 60))
+ else:
+ return translate(_("less than 5 minutes ago"))
+
+
+def getDuration(v1, v2=None):
+ """Get delta between two dates"""
+ if v2 is None:
+ v2 = datetime.utcnow()
+ assert isinstance(v1, datetime) and isinstance(v2, datetime)
+ v1, v2 = min(v1, v2), max(v1, v2)
+ delta = v2 - v1
+ if delta.days > 60:
+ return translate(_("%d months")) % int(round(delta.days * 1.0 / 30))
+ elif delta.days > 10:
+ return translate(_("%d weeks")) % int(round(delta.days * 1.0 / 7))
+ elif delta.days >= 2:
+ return translate(_("%d days")) % delta.days
+ else:
+ hours = int(round(delta.seconds * 1.0 / 3600))
+ if delta.days == 1:
+ return translate(_("%d day and %d hours")) % (delta.days, hours)
+ else:
+ if hours > 2:
+ return translate(_("%d hours")) % hours
+ else:
+ minutes = int(round(delta.seconds * 1.0 / 60))
+ if minutes > 2:
+ return translate(_("%d minutes")) % minutes
+ else:
+ return translate(_("%d seconds")) % delta.seconds
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/doctests/README.txt Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,119 @@
+==================
+ZTFY.utils package
+==================
+
+Introduction
+------------
+
+This package is composed of a set of utility functions, which in complement with zope.app.zapi
+package can make Zope management easier.
+
+
+Unicode functions
+-----------------
+
+While working with extended characters sets containing accentuated characters, it's necessary to
+conversing strings to UTF8 so that they can be used without any conversion problem.
+
+ >>> from ztfy.utils import unicode
+
+'translateString' is a utility function which can be used, for example, to generate an object's id
+without space and with accentuated characters converted to their unaccentuated version:
+
+ >>> sample = 'Mon titre accentué'
+ >>> unicode.translateString(sample)
+ u'mon titre accentue'
+
+Results are lower-cased by default ; this can be avoided be setting the 'forceLower' parameter
+to False:
+
+ >>> unicode.translateString(sample, forceLower=False)
+ u'Mon titre accentue'
+
+If input string can contain 'slashes' (/) or 'backslashes' (\), they are normally removed ;
+by using the 'escapeSlashes' parameter, the input string is splitted and only the last element is
+returned ; this is handy to handle filenames on Windows platform:
+
+ >>> sample = 'Autre / chaîne / accentuée'
+ >>> unicode.translateString(sample)
+ u'autre chaine accentuee'
+ >>> unicode.translateString(sample, escapeSlashes=True)
+ u'accentuee'
+ >>> sample = 'C:\\Program Files\\My Application\\test.txt'
+ >>> unicode.translateString(sample)
+ u'cprogram filesmy applicationtest.txt'
+ >>> unicode.translateString(sample, escapeSlashes=True)
+ u'test.txt'
+
+To remove remaining spaces or convert them to another character, you can use the "spaces" parameter
+which can contain any string to be used instead of initial spaces:
+
+ >>> sample = 'C:\\Program Files\\My Application\\test.txt'
+ >>> unicode.translateString(sample, spaces=' ')
+ u'cprogram filesmy applicationtest.txt'
+ >>> unicode.translateString(sample, spaces='-')
+ u'cprogram-filesmy-applicationtest.txt'
+
+Spaces replacement is made in the last step, so using it with "escapeSlashes" parameter only affects
+the final result:
+
+ >>> unicode.translateString(sample, escapeSlashes=True, spaces='-')
+ u'test.txt'
+
+
+Dates functions
+---------------
+
+Dates functions are used to convert dates from/to string representation:
+
+ >>> from datetime import datetime
+ >>> from ztfy.utils import date
+ >>> now = datetime.fromtimestamp(1205000000)
+ >>> now
+ datetime.datetime(2008, 3, 8, 19, 13, 20)
+
+You can get an unicode representation of a date in ASCII format using 'unidate' fonction ; date is
+converted to GMT:
+
+ >>> udate = date.unidate(now)
+ >>> udate
+ u'2008-03-08T19:13:20+00:00'
+
+'parsedate' can be used to convert ASCII format into datetime:
+
+ >>> ddate = date.parsedate(udate)
+ >>> ddate
+ datetime.datetime(2008, 3, 8, 19, 13, 20, tzinfo=<StaticTzInfo 'GMT'>)
+
+'datetodatetime' can be used to convert a 'date' type to a 'datetime' value ; if a 'datetime' value
+is used as argument, it is returned 'as is':
+
+ >>> ddate.date()
+ datetime.date(2008, 3, 8)
+ >>> date.datetodatetime(ddate)
+ datetime.datetime(2008, 3, 8, 19, 13, 20, tzinfo=<StaticTzInfo 'GMT'>)
+ >>> date.datetodatetime(ddate.date())
+ datetime.datetime(2008, 3, 8, 0, 0)
+
+
+Timezones handling
+------------------
+
+Timezones handling game me headaches at first. I finally concluded that the best way (for me !) to handle
+TZ data was to store every datetime value in GMT timezone.
+As far as I know, there is no easy way to know the user's timezone from his request settings. So you can:
+- store this timezone in user's profile,
+- define a static server's timezone
+- create and register a ServerTimezoneUtility to handle server default timezone.
+
+My current default user's timezone is set to 'Europe/Paris' ; you should probably update this setting in
+'timezone.py' if you are located elsewhere.
+
+ >>> from ztfy.utils import timezone
+ >>> timezone.tztime(ddate)
+ datetime.datetime(2008, 3, 8, 19, 13, 20, tzinfo=<StaticTzInfo 'GMT'>)
+
+'gmtime' function can be used to convert a datetime to GMT:
+
+ >>> timezone.gmtime(now)
+ datetime.datetime(2008, 3, 8, 19, 13, 20, tzinfo=<StaticTzInfo 'GMT'>)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/encoding.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,159 @@
+### -*- coding: utf-8 -*- ####################################################
+##############################################################################
+#
+# Copyright (c) 2008-2012 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+__docformat__ = "restructuredtext"
+
+# import standard packages
+
+# import Zope3 interfaces
+from zope.schema.interfaces import IVocabularyFactory, IChoice
+
+# import local interfaces
+
+# import Zope3 packages
+from zope.i18n import translate
+from zope.interface import classProvides, implements
+from zope.schema import Choice
+from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary
+
+# import local packages
+from ztfy.utils.request import queryRequest
+
+from ztfy.utils import _
+
+
+ENCODINGS = {
+ 'ascii': _('English (ASCII)'),
+ 'big5': _('Traditional Chinese (big5)'),
+ 'big5hkscs': _('Traditional Chinese (big5hkscs)'),
+ 'cp037': _('English (cp037)'),
+ 'cp424': _('Hebrew (cp424)'),
+ 'cp437': _('English (cp437)'),
+ 'cp500': _('Western Europe (cp500)'),
+ 'cp720': _('Arabic (cp720)'),
+ 'cp737': _('Greek (cp737)'),
+ 'cp775': _('Baltic languages (cp775)'),
+ 'cp850': _('Western Europe (cp850)'),
+ 'cp852': _('Central and Eastern Europe (cp852)'),
+ 'cp855': _('Bulgarian, Byelorussian, Macedonian, Russian, Serbian (cp855)'),
+ 'cp856': _('Hebrew (cp856)'),
+ 'cp857': _('Turkish (cp857)'),
+ 'cp858': _('Western Europe (cp858)'),
+ 'cp860': _('Portuguese (cp860)'),
+ 'cp861': _('Icelandic (cp861)'),
+ 'cp862': _('Hebrew (cp862)'),
+ 'cp863': _('Canadian (cp863)'),
+ 'cp864': _('Arabic (cp864)'),
+ 'cp865': _('Danish, Norwegian (cp865)'),
+ 'cp866': _('Russian (cp866)'),
+ 'cp869': _('Greek (cp869)'),
+ 'cp874': _('Thai (cp874)'),
+ 'cp875': _('Greek (cp875)'),
+ 'cp932': _('Japanese (cp932)'),
+ 'cp949': _('Korean (cp949)'),
+ 'cp950': _('Traditional Chinese (cp950)'),
+ 'cp1006': _('Urdu (cp1006)'),
+ 'cp1026': _('Turkish (cp1026)'),
+ 'cp1140': _('Western Europe (cp1140)'),
+ 'cp1250': _('Central and Eastern Europe (cp1250)'),
+ 'cp1251': _('Bulgarian, Byelorussian, Macedonian, Russian, Serbian (cp1251)'),
+ 'cp1252': _('Western Europe (cp1252)'),
+ 'cp1253': _('Greek (cp1253)'),
+ 'cp1254': _('Turkish (cp1254)'),
+ 'cp1255': _('Hebrew (cp1255)'),
+ 'cp1256': _('Arabic (cp1256)'),
+ 'cp1257': _('Baltic languages (cp1257)'),
+ 'cp1258': _('Vietnamese (cp1258)'),
+ 'euc_jp': _('Japanese (euc_jp)'),
+ 'euc_jis_2004': _('Japanese (euc_jis_2004)'),
+ 'euc_jisx0213': _('Japanese (euc_jisx0213)'),
+ 'euc_kr': _('Korean (euc_kr)'),
+ 'gb2312': _('Simplified Chinese (gb2312)'),
+ 'gbk': _('Unified Chinese (gbk)'),
+ 'gb18030': _('Unified Chinese (gb18030)'),
+ 'hz': _('Simplified Chinese (hz)'),
+ 'iso2022_jp': _('Japanese (iso2022_jp)'),
+ 'iso2022_jp_1': _('Japanese (iso2022_jp_1)'),
+ 'iso2022_jp_2': _('Japanese, Korean, Simplified Chinese, Western Europe, Greek (iso2022_jp_2)'),
+ 'iso2022_jp_2004': _('Japanese (iso2022_jp_2004)'),
+ 'iso2022_jp_3': _('Japanese (iso2022_jp_3)'),
+ 'iso2022_jp_ext': _('Japanese (iso2022_jp_ext)'),
+ 'iso2022_kr': _('Korean (iso2022_kr)'),
+ 'latin_1': _('West Europe (latin_1)'),
+ 'iso8859_2': _('Central and Eastern Europe (iso8859_2)'),
+ 'iso8859_3': _('Esperanto, Maltese (iso8859_3)'),
+ 'iso8859_4': _('Baltic languages (iso8859_4)'),
+ 'iso8859_5': _('Bulgarian, Byelorussian, Macedonian, Russian, Serbian (iso8859_5)'),
+ 'iso8859_6': _('Arabic (iso8859_6)'),
+ 'iso8859_7': _('Greek (iso8859_7)'),
+ 'iso8859_8': _('Hebrew (iso8859_8)'),
+ 'iso8859_9': _('Turkish (iso8859_9)'),
+ 'iso8859_10': _('Nordic languages (iso8859_10)'),
+ 'iso8859_13': _('Baltic languages (iso8859_13)'),
+ 'iso8859_14': _('Celtic languages (iso8859_14)'),
+ 'iso8859_15': _('Western Europe (iso8859_15)'),
+ 'iso8859_16': _('South-Eastern Europe (iso8859_16)'),
+ 'johab': _('Korean (johab)'),
+ 'koi8_r': _('Russian (koi8_r)'),
+ 'koi8_u': _('Ukrainian (koi8_u)'),
+ 'mac_cyrillic': _('Bulgarian, Byelorussian, Macedonian, Russian, Serbian (mac_cyrillic)'),
+ 'mac_greek': _('Greek (mac_greek)'),
+ 'mac_iceland': _('Icelandic (mac_iceland)'),
+ 'mac_latin2': _('Central and Eastern Europe (mac_latin2)'),
+ 'mac_roman': _('Western Europe (mac_roman)'),
+ 'mac_turkish': _('Turkish (mac_turkish)'),
+ 'ptcp154': _('Kazakh (ptcp154)'),
+ 'shift_jis': _('Japanese (shift_jis)'),
+ 'shift_jis_2004': _('Japanese (shift_jis_2004)'),
+ 'shift_jisx0213': _('Japanese (shift_jisx0213)'),
+ 'utf_32': _('all languages (utf_32)'),
+ 'utf_32_be': _('all languages (utf_32_be)'),
+ 'utf_32_le': _('all languages (utf_32_le)'),
+ 'utf_16': _('all languages (utf_16)'),
+ 'utf_16_be': _('all languages (BMP only - utf_16_be)'),
+ 'utf_16_le': _('all languages (BMP only - utf_16_le)'),
+ 'utf_7': _('all languages (utf_7)'),
+ 'utf_8': _('all languages (utf_8)'),
+ 'utf_8_sig': _('all languages (utf_8_sig)'),
+ }
+
+
+class EncodingsVocabulary(SimpleVocabulary):
+
+ classProvides(IVocabularyFactory)
+
+ def __init__(self, terms, *interfaces):
+ request = queryRequest()
+ terms = [SimpleTerm(unicode(v), title=translate(t, context=request)) for v, t in ENCODINGS.iteritems()]
+ terms.sort(key=lambda x: x.title)
+ super(EncodingsVocabulary, self).__init__(terms, *interfaces)
+
+
+class IEncodingField(IChoice):
+ """Encoding field interface"""
+
+
+class EncodingField(Choice):
+ """Encoding field"""
+
+ implements(IEncodingField)
+
+ def __init__(self, values=None, vocabulary='ZTFY encodings', source=None, **kw):
+ if 'values' in kw:
+ del kw['values']
+ if 'source' in kw:
+ del kw['source']
+ kw['vocabulary'] = vocabulary
+ super(EncodingField, self).__init__(**kw)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/file.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,37 @@
+### -*- coding: utf-8 -*- ####################################################
+##############################################################################
+#
+# Copyright (c) 2008 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+
+# import standard packages
+
+# import Zope3 interfaces
+from z3c.form.interfaces import IFileWidget
+from zope.schema.interfaces import IBytes
+
+# import local interfaces
+
+# import Zope3 packages
+from z3c.form.converter import FileUploadDataConverter as BaseDataConverter
+from zope.component import adapts
+
+# import local packages
+
+
+class FileUploadDataConverter(BaseDataConverter):
+
+ adapts(IBytes, IFileWidget)
+
+ def toWidgetValue(self, value):
+ return value or ''
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/html.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,115 @@
+### -*- coding: utf-8 -*- ####################################################
+##############################################################################
+#
+# Copyright (c) 2008-2010 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+__docformat__ = "restructuredtext"
+
+# import standard packages
+from sgmllib import SGMLParser
+
+# import Zope3 interfaces
+
+# import local interfaces
+
+# import Zope3 packages
+
+# import local packages
+
+
+class HTMLParser(SGMLParser):
+
+ data = ''
+ entitydefs = { 'amp': '&', 'lt': '<', 'gt': '>',
+ 'apos': "'", 'quot': '"',
+ 'Agrave': 'À', 'Aacute': 'A', 'Acirc': 'Â', 'Atilde': 'A', 'Auml': 'Ä', 'Aring': 'A',
+ 'AElig': 'AE',
+ 'Ccedil': 'Ç',
+ 'Egrave': 'É', 'Eacute': 'È', 'Ecirc': 'Ê', 'Euml': 'Ë',
+ 'Igrave': 'I', 'Iacute': 'I', 'Icirc': 'I', 'Iuml': 'I',
+ 'Ntilde': 'N',
+ 'Ograve': 'O', 'Oacute': 'O', 'Ocirc': 'Ô', 'Otilde': 'O', 'Ouml': 'Ö', 'Oslash': 'O',
+ 'Ugrave': 'Ù', 'Uacute': 'U', 'Ucirc': 'Û', 'Uuml': 'Ü',
+ 'Yacute': 'Y',
+ 'THORN': 'T',
+ 'agrave': 'à', 'aacute': 'a', 'acirc': 'â', 'atilde': 'a', 'auml': 'ä', 'aring': 'a', 'aelig': 'ae',
+ 'ccedil': 'ç',
+ 'egrave': 'è', 'eacute': 'é', 'ecirc': 'ê', 'euml': 'ë',
+ 'igrave': 'i', 'iacute': 'i', 'icirc': 'î', 'iuml': 'ï',
+ 'ntilde': 'n',
+ 'ograve': 'o', 'oacute': 'o', 'ocirc': 'ô', 'otilde': 'o', 'ouml': 'ö', 'oslash': 'o',
+ 'ugrave': 'ù', 'uacute': 'u', 'ucirc': 'û', 'uuml': 'ü',
+ 'yacute': 'y',
+ 'thorn': 't',
+ 'yuml': 'ÿ' }
+
+ charrefs = { 34 : '"', 38 : '&', 39 : "'",
+ 60 : '<', 62 : '>',
+ 192 : 'À', 193 : 'A', 194 : 'Â', 195 : 'A', 196 : 'Ä', 197 : 'A',
+ 198 : 'AE',
+ 199 : 'Ç',
+ 200 : 'È', 201 : 'É', 202 : 'Ê', 203 : 'Ë',
+ 204 : 'I', 205 : 'I', 206 : 'Î', 207 : 'Ï',
+ 208 : 'D',
+ 209 : 'N',
+ 210 : 'O', 211 : 'O', 212 : 'Ô', 213 : 'O', 214 : 'Ö', 216 : 'O',
+ 215 : 'x',
+ 217 : 'Ù', 218 : 'U', 219 : 'Û', 220 : 'Ü',
+ 221 : 'Y', 222 : 'T',
+ 223 : 'sz',
+ 224 : 'à', 225 : 'a', 226 : 'â', 227 : 'a', 228 : 'ä', 229 : 'a',
+ 230 : 'ae',
+ 231 : 'ç',
+ 232 : 'è', 233 : 'é', 234 : 'ê', 235 : 'ë',
+ 236 : 'i', 237 : 'i', 238 : 'î', 239 : 'ï',
+ 240 : 'e',
+ 241 : 'n',
+ 242 : 'o', 243 : 'o', 244 : 'ô', 245 : 'o', 246 : 'ö', 248 : 'o',
+ 249 : 'ù', 250 : 'u', 251 : 'û', 252 : 'ü',
+ 253 : 'y', 255 : 'ÿ' }
+
+ def handle_data(self, data):
+ try:
+ self.data += data
+ except:
+ self.data += unicode(data, 'utf8')
+
+ def handle_charref(self, name):
+ try:
+ n = int(name)
+ except ValueError:
+ self.unknown_charref(name)
+ return
+ if not 0 <= n <= 255:
+ self.unknown_charref(name)
+ return
+ self.handle_data(self.charrefs.get(n) or unicode(chr(n), 'latin1'))
+
+ def start_td(self, attributes):
+ self.data += ' '
+
+ def start_p(self, attributes):
+ pass
+
+ def end_p(self):
+ self.data += '\n'
+
+
+def htmlToText(value):
+ """Utility function to extract text content from HTML"""
+ if value is None:
+ return ''
+ parser = HTMLParser()
+ parser.feed(value)
+ parser.close()
+ return parser.data
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/interfaces.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,142 @@
+### -*- coding: utf-8 -*- ####################################################
+##############################################################################
+#
+# Copyright (c) 2008-2010 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+__docformat__ = "restructuredtext"
+
+# import standard packages
+
+# import Zope3 interfaces
+from zope.component.interfaces import IObjectEvent
+
+# import local interfaces
+
+# import Zope3 packages
+from zope.interface import Interface
+
+# import local packages
+
+
+class INewSiteManagerEvent(IObjectEvent):
+ """Event interface for new site manager event"""
+
+
+#
+# Generic list interface
+#
+
+class IListInfo(Interface):
+ """Custom interface used to handle list-like components"""
+
+ def count():
+ """Get list items count"""
+
+ def index():
+ """Get position of the given item"""
+
+ def __contains__():
+ """Check if given value is int list"""
+
+ def __getitem__():
+ """Return item at given position"""
+
+ def __iter__():
+ """Iterator over list items"""
+
+
+class IListWriter(Interface):
+ """Writer interface for list-like components"""
+
+ def append():
+ """Append value to list"""
+
+ def extend():
+ """Extend list with given items"""
+
+ def insert():
+ """Insert item to given index"""
+
+ def pop():
+ """Pop item from list and returns it"""
+
+ def remove():
+ """Remove given item from list"""
+
+ def reverse():
+ """Sort list in reverse order"""
+
+ def sort():
+ """Sort list"""
+
+
+class IList(IListInfo, IListWriter):
+ """Marker interface for list-like components"""
+
+
+#
+# Generic dict interface
+#
+
+class IDictInfo(Interface):
+ """Custom interface used to handle dict-like components"""
+
+ def keys():
+ """Get list of keys for the dict"""
+
+ def has_key(key):
+ """Check to know if dict includes the given key"""
+
+ def get(key, default=None):
+ """Get given key or default from dict"""
+
+ def copy():
+ """Duplicate content of dict"""
+
+ def __contains__(key):
+ """Check if given key is in dict"""
+
+ def __getitem__(key):
+ """Get given key value from dict"""
+
+ def __iter__():
+ """Iterator over dictionnary keys"""
+
+
+class IDictWriter(Interface):
+ """Writer interface for dict-like components"""
+
+ def clear():
+ """Clear dict"""
+
+ def update(b):
+ """Update dict with given values"""
+
+ def setdefault(key, failobj=None):
+ """Create value for given key if missing"""
+
+ def pop(key, *args):
+ """Remove and return given key from dict"""
+
+ def popitem():
+ """Pop item from dict"""
+
+ def __setitem__(key, value):
+ """Update given key with given value"""
+
+ def __delitem__(key):
+ """Remove selected key from dict"""
+
+
+class IDict(IDictInfo, IDictWriter):
+ """Marker interface for dict-like components"""
Binary file src/ztfy/utils/locales/en/LC_MESSAGES/ztfy.utils.mo has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/locales/en/LC_MESSAGES/ztfy.utils.po Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,26 @@
+# #############################################################################
+#
+# Copyright (c) 2003-2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+# #############################################################################
+msgid ""
+msgstr ""
+"Project-Id-Version: ZTFY.utils\n"
+"POT-Creation-Date: Thu Apr 12 00:21:57 2012\n"
+"PO-Revision-Date: 2009-08-14 18:14+0200\n"
+"Last-Translator: Thierry Florac <tflorac@ulthar.net>\n"
+"Language-Team: French <traduc@traduc.org>\n"
+"Language: fr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: zope/app/locales/extract.py\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
Binary file src/ztfy/utils/locales/fr/LC_MESSAGES/ztfy.utils.mo has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/locales/fr/LC_MESSAGES/ztfy.utils.po Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,492 @@
+# #############################################################################
+#
+# Copyright (c) 2003-2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+# #############################################################################
+# Thierry Florac <thierry.florac@onf.fr>, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: ZTFY.utils\n"
+"POT-Creation-Date: Thu Apr 12 00:21:57 2012\n"
+"PO-Revision-Date: 2012-03-26 14:18+0200\n"
+"Last-Translator: Thierry Florac <thierry.florac@onf.fr>\n"
+"Language-Team: français <>\n"
+"Language: fr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: zope/app/locales/extract.py\n"
+"Plural-Forms: nplurals=2; plural=(n!=1);\n"
+
+#: ztfy/utils/browser/encoding.py:47
+msgid "-- automatic selection --"
+msgstr "-- sélection automatique --"
+
+#: ztfy/utils/date.py:101
+msgid "%d hours ago"
+msgstr "il y a %d heures"
+
+#: ztfy/utils/date.py:103
+msgid "%d minutes ago"
+msgstr "il y a %d minutes"
+
+#: ztfy/utils/date.py:105
+msgid "less than 5 minutes ago"
+msgstr "il y a moins de 5 minutes"
+
+#: ztfy/utils/date.py:116
+msgid "%d months"
+msgstr "%d mois"
+
+#: ztfy/utils/date.py:118
+msgid "%d weeks"
+msgstr "%d semaines"
+
+#: ztfy/utils/date.py:120
+msgid "%d days"
+msgstr "%d jours"
+
+#: ztfy/utils/date.py:124
+msgid "%d day and %d hours"
+msgstr "%d jours et %d heures"
+
+#: ztfy/utils/date.py:127
+msgid "%d hours"
+msgstr "%d heures"
+
+#: ztfy/utils/date.py:131
+msgid "%d minutes"
+msgstr "%d minutes"
+
+#: ztfy/utils/date.py:133
+msgid "%d seconds"
+msgstr "%d secondes"
+
+#: ztfy/utils/date.py:89
+msgid "%d months ago"
+msgstr "il y a %d mois"
+
+#: ztfy/utils/date.py:91
+msgid "%d weeks ago"
+msgstr "il y a %d semaines"
+
+#: ztfy/utils/date.py:93
+msgid "%d days ago"
+msgstr "il y a %d jours"
+
+#: ztfy/utils/date.py:95
+msgid "the day before yesterday"
+msgstr "avant-hier"
+
+#: ztfy/utils/date.py:97
+msgid "yesterday"
+msgstr "hier"
+
+#: ztfy/utils/encoding.py:100
+msgid "Greek (iso8859_7)"
+msgstr "Grec (iso8859-7)"
+
+#: ztfy/utils/encoding.py:101
+msgid "Hebrew (iso8859_8)"
+msgstr "Hébreu (iso8859-8)"
+
+#: ztfy/utils/encoding.py:102
+msgid "Turkish (iso8859_9)"
+msgstr "Turc (iso8859-9)"
+
+#: ztfy/utils/encoding.py:103
+msgid "Nordic languages (iso8859_10)"
+msgstr "Langues nordiques (iso8859-10)"
+
+#: ztfy/utils/encoding.py:104
+msgid "Baltic languages (iso8859_13)"
+msgstr "Langues baltes (iso8859-13)"
+
+#: ztfy/utils/encoding.py:105
+msgid "Celtic languages (iso8859_14)"
+msgstr "Langues celtes (iso8859-14)"
+
+#: ztfy/utils/encoding.py:106
+msgid "Western Europe (iso8859_15)"
+msgstr "Europe de l'ouest (iso8859-15)"
+
+#: ztfy/utils/encoding.py:107
+msgid "South-Eastern Europe (iso8859_16)"
+msgstr "Europe du sud-est (iso8859-16)"
+
+#: ztfy/utils/encoding.py:108
+msgid "Korean (johab)"
+msgstr "Coréen (johab)"
+
+#: ztfy/utils/encoding.py:109
+msgid "Russian (koi8_r)"
+msgstr "Russe (koi8-r)"
+
+#: ztfy/utils/encoding.py:110
+msgid "Ukrainian (koi8_u)"
+msgstr "Ukrainien (koi8-u)"
+
+#: ztfy/utils/encoding.py:111
+msgid "Bulgarian, Byelorussian, Macedonian, Russian, Serbian (mac_cyrillic)"
+msgstr "Burlage, Biélorusse, Macédonien, Russe, Serbe (mac-cyrillique)"
+
+#: ztfy/utils/encoding.py:112
+msgid "Greek (mac_greek)"
+msgstr "Grec (mac-grec)"
+
+#: ztfy/utils/encoding.py:113
+msgid "Icelandic (mac_iceland)"
+msgstr "Islandais (mac-islande)"
+
+#: ztfy/utils/encoding.py:114
+msgid "Central and Eastern Europe (mac_latin2)"
+msgstr "Europe centrale et de l'ouest (mac-latin2)"
+
+#: ztfy/utils/encoding.py:115
+msgid "Western Europe (mac_roman)"
+msgstr "Europe de l'ouest (mac-roman)"
+
+#: ztfy/utils/encoding.py:116
+msgid "Turkish (mac_turkish)"
+msgstr "Turc (mac-turc)"
+
+#: ztfy/utils/encoding.py:117
+msgid "Kazakh (ptcp154)"
+msgstr "Kazakh (ptcp-154)"
+
+#: ztfy/utils/encoding.py:118
+msgid "Japanese (shift_jis)"
+msgstr "Japonais (shift-jis)"
+
+#: ztfy/utils/encoding.py:119
+msgid "Japanese (shift_jis_2004)"
+msgstr "Japonais (shift-jis-2004)"
+
+#: ztfy/utils/encoding.py:120
+msgid "Japanese (shift_jisx0213)"
+msgstr "Japonais (shift-jisx-0213)"
+
+#: ztfy/utils/encoding.py:121
+msgid "all languages (utf_32)"
+msgstr "toutes langues (utf-32)"
+
+#: ztfy/utils/encoding.py:122
+msgid "all languages (utf_32_be)"
+msgstr "toutes langues (utf-32-be)"
+
+#: ztfy/utils/encoding.py:123
+msgid "all languages (utf_32_le)"
+msgstr "toutes langues (utf-32-le)"
+
+#: ztfy/utils/encoding.py:124
+msgid "all languages (utf_16)"
+msgstr "toutes langues (utf-16)"
+
+#: ztfy/utils/encoding.py:125
+msgid "all languages (BMP only - utf_16_be)"
+msgstr "toutes langues (BMP seulement - utf-16-be)"
+
+#: ztfy/utils/encoding.py:126
+msgid "all languages (BMP only - utf_16_le)"
+msgstr "toutes langues (BMP seulement - utf-16-le)"
+
+#: ztfy/utils/encoding.py:127
+msgid "all languages (utf_7)"
+msgstr "toutes langues (utf-7)"
+
+#: ztfy/utils/encoding.py:128
+msgid "all languages (utf_8)"
+msgstr "toutes langues (utf-8)"
+
+#: ztfy/utils/encoding.py:129
+msgid "all languages (utf_8_sig)"
+msgstr "toutes langues (utf-8-sig)"
+
+#: ztfy/utils/encoding.py:38
+msgid "English (ASCII)"
+msgstr "Anglais (ASCII)"
+
+#: ztfy/utils/encoding.py:39
+msgid "Traditional Chinese (big5)"
+msgstr "Chinois traditionnel (big5)"
+
+#: ztfy/utils/encoding.py:40
+msgid "Traditional Chinese (big5hkscs)"
+msgstr "Chinois traditionnel (big5hkscs)"
+
+#: ztfy/utils/encoding.py:41
+msgid "English (cp037)"
+msgstr "Anglais (cp037)"
+
+#: ztfy/utils/encoding.py:42
+msgid "Hebrew (cp424)"
+msgstr "Hébreu (cp424)"
+
+#: ztfy/utils/encoding.py:43
+msgid "English (cp437)"
+msgstr "Anglais (cp437)"
+
+#: ztfy/utils/encoding.py:44
+msgid "Western Europe (cp500)"
+msgstr "Europe de l'ouest (cp500)"
+
+#: ztfy/utils/encoding.py:45
+msgid "Arabic (cp720)"
+msgstr "Arabe (cp720)"
+
+#: ztfy/utils/encoding.py:46
+msgid "Greek (cp737)"
+msgstr "Grec (cp737)"
+
+#: ztfy/utils/encoding.py:47
+msgid "Baltic languages (cp775)"
+msgstr "Langues baltes (cp775)"
+
+#: ztfy/utils/encoding.py:48
+msgid "Western Europe (cp850)"
+msgstr "Europe de l'ouest (cp850)"
+
+#: ztfy/utils/encoding.py:49
+msgid "Central and Eastern Europe (cp852)"
+msgstr "Europe centrale et de l'est (cp852)"
+
+#: ztfy/utils/encoding.py:50
+msgid "Bulgarian, Byelorussian, Macedonian, Russian, Serbian (cp855)"
+msgstr "Bulgare, Biélorusse, Macédonien, Russe, Serbe (cp855)"
+
+#: ztfy/utils/encoding.py:51
+msgid "Hebrew (cp856)"
+msgstr "Hébreu (cp856)"
+
+#: ztfy/utils/encoding.py:52
+msgid "Turkish (cp857)"
+msgstr "Turc (cp857)"
+
+#: ztfy/utils/encoding.py:53
+msgid "Western Europe (cp858)"
+msgstr "Europe de l'ouest (cp858)"
+
+#: ztfy/utils/encoding.py:54
+msgid "Portuguese (cp860)"
+msgstr "Portuguais (cp860)"
+
+#: ztfy/utils/encoding.py:55
+msgid "Icelandic (cp861)"
+msgstr "Islandais (cp861)"
+
+#: ztfy/utils/encoding.py:56
+msgid "Hebrew (cp862)"
+msgstr "Hébreu (cp862)"
+
+#: ztfy/utils/encoding.py:57
+msgid "Canadian (cp863)"
+msgstr "Canadien (cp863)"
+
+#: ztfy/utils/encoding.py:58
+msgid "Arabic (cp864)"
+msgstr "Arabe (cp864)"
+
+#: ztfy/utils/encoding.py:59
+msgid "Danish, Norwegian (cp865)"
+msgstr "Danois, Norvégien (cp865)"
+
+#: ztfy/utils/encoding.py:60
+msgid "Russian (cp866)"
+msgstr "Russe (cp866)"
+
+#: ztfy/utils/encoding.py:61
+msgid "Greek (cp869)"
+msgstr "Grec (cp869)"
+
+#: ztfy/utils/encoding.py:62
+msgid "Thai (cp874)"
+msgstr "Thaï (cp874)"
+
+#: ztfy/utils/encoding.py:63
+msgid "Greek (cp875)"
+msgstr "Grec (cp875)"
+
+#: ztfy/utils/encoding.py:64
+msgid "Japanese (cp932)"
+msgstr "Japonais (cp932)"
+
+#: ztfy/utils/encoding.py:65
+msgid "Korean (cp949)"
+msgstr "Coréen (cp949)"
+
+#: ztfy/utils/encoding.py:66
+msgid "Traditional Chinese (cp950)"
+msgstr "Chinois traditionnel (cp950)"
+
+#: ztfy/utils/encoding.py:67
+msgid "Urdu (cp1006)"
+msgstr "Urdu (cp1006)"
+
+#: ztfy/utils/encoding.py:68
+msgid "Turkish (cp1026)"
+msgstr "Turc (cp1026)"
+
+#: ztfy/utils/encoding.py:69
+msgid "Western Europe (cp1140)"
+msgstr "Europe de l'ouest (cp1140)"
+
+#: ztfy/utils/encoding.py:70
+msgid "Central and Eastern Europe (cp1250)"
+msgstr "Europe centrale et de l'est (cp1250)"
+
+#: ztfy/utils/encoding.py:71
+msgid "Bulgarian, Byelorussian, Macedonian, Russian, Serbian (cp1251)"
+msgstr "Bulgare, Biélorusse, Macédonien, Russe, Serbe (cp1251)"
+
+#: ztfy/utils/encoding.py:72
+msgid "Western Europe (cp1252)"
+msgstr "Europe de l'ouest (cp1252)"
+
+#: ztfy/utils/encoding.py:73
+msgid "Greek (cp1253)"
+msgstr "Grec (cp1253)"
+
+#: ztfy/utils/encoding.py:74
+msgid "Turkish (cp1254)"
+msgstr "Turc (cp1254)"
+
+#: ztfy/utils/encoding.py:75
+msgid "Hebrew (cp1255)"
+msgstr "Hébreu (cp1255)"
+
+#: ztfy/utils/encoding.py:76
+msgid "Arabic (cp1256)"
+msgstr "Arabe (cp1256)"
+
+#: ztfy/utils/encoding.py:77
+msgid "Baltic languages (cp1257)"
+msgstr "Langues baltes (cp1257)"
+
+#: ztfy/utils/encoding.py:78
+msgid "Vietnamese (cp1258)"
+msgstr "Vietnamien (cp1258)"
+
+#: ztfy/utils/encoding.py:79
+msgid "Japanese (euc_jp)"
+msgstr "Japonais (euc-jp)"
+
+#: ztfy/utils/encoding.py:80
+msgid "Japanese (euc_jis_2004)"
+msgstr "Japonais (euc-jp-2004)"
+
+#: ztfy/utils/encoding.py:81
+msgid "Japanese (euc_jisx0213)"
+msgstr "Japonais (euc-jisx0213)"
+
+#: ztfy/utils/encoding.py:82
+msgid "Korean (euc_kr)"
+msgstr "Coréen (euc-kr)"
+
+#: ztfy/utils/encoding.py:83
+msgid "Simplified Chinese (gb2312)"
+msgstr "Chinois simplifié (gb2312)"
+
+#: ztfy/utils/encoding.py:84
+msgid "Unified Chinese (gbk)"
+msgstr "Chinois unifié (gbk)"
+
+#: ztfy/utils/encoding.py:85
+msgid "Unified Chinese (gb18030)"
+msgstr "Chinois unifié (gb18030)"
+
+#: ztfy/utils/encoding.py:86
+msgid "Simplified Chinese (hz)"
+msgstr "Chinois simplifié (hz)"
+
+#: ztfy/utils/encoding.py:87
+msgid "Japanese (iso2022_jp)"
+msgstr "Japonais (iso2002-jp)"
+
+#: ztfy/utils/encoding.py:88
+msgid "Japanese (iso2022_jp_1)"
+msgstr "Japonais (iso2022-jp-1)"
+
+#: ztfy/utils/encoding.py:89
+msgid ""
+"Japanese, Korean, Simplified Chinese, Western Europe, Greek (iso2022_jp_2)"
+msgstr "Japonais, Coréen, Chinois simplifié, Europe de l'ouest, Grec (iso2022-jp-2)"
+
+#: ztfy/utils/encoding.py:90
+msgid "Japanese (iso2022_jp_2004)"
+msgstr "Japonais (iso2022-jp-2004)"
+
+#: ztfy/utils/encoding.py:91
+msgid "Japanese (iso2022_jp_3)"
+msgstr "Japonais (iso2022-jp-3)"
+
+#: ztfy/utils/encoding.py:92
+msgid "Japanese (iso2022_jp_ext)"
+msgstr "Japonais (iso2022-jp-ext)"
+
+#: ztfy/utils/encoding.py:93
+msgid "Korean (iso2022_kr)"
+msgstr "Coréen (iso2022-kr)"
+
+#: ztfy/utils/encoding.py:94
+msgid "West Europe (latin_1)"
+msgstr "Europe de l'ouest (latin-1)"
+
+#: ztfy/utils/encoding.py:95
+msgid "Central and Eastern Europe (iso8859_2)"
+msgstr "Europe centrale et de l'est (iso8859-2)"
+
+#: ztfy/utils/encoding.py:96
+msgid "Esperanto, Maltese (iso8859_3)"
+msgstr "Espéranto, Maltais (iso8859-3)"
+
+#: ztfy/utils/encoding.py:97
+msgid "Baltic languages (iso8859_4)"
+msgstr "Langues baltes (iso8859-4)"
+
+#: ztfy/utils/encoding.py:98
+msgid "Bulgarian, Byelorussian, Macedonian, Russian, Serbian (iso8859_5)"
+msgstr "Bulgare, Biélorusse, Macédonien, Russe, Serbe (iso8859-5)"
+
+#: ztfy/utils/encoding.py:99
+msgid "Arabic (iso8859_6)"
+msgstr "Arabe (iso8859-6)"
+
+#: ztfy/utils/request.py:40
+msgid "No Request in interaction !"
+msgstr "Pas de requête en cours !"
+
+#: ztfy/utils/schema.py:58
+msgid "Color length must be 3 or 6 characters"
+msgstr "La longueur du code couleur doit être de 3 ou 6 caractères"
+
+#: ztfy/utils/schema.py:61
+msgid ""
+"Color value must contain only valid color codes (numbers or letters between "
+"'A' end 'F')"
+msgstr "Une couleur ne doit contenir que des caractères hexadécimaux (nombres et lettres de 'A' à 'F')"
+
+#: ztfy/utils/security.py:71
+msgid "< missing principal %s >"
+msgstr "< mandant inconnu %s >"
+
+#: ztfy/utils/security.py:75
+msgid "This principal can't be found in any authentication utility..."
+msgstr "Ce mandant ne peut pas être trouvé"
+
+#: ztfy/utils/timezone/interfaces.py:35
+msgid "Server timezone"
+msgstr "Fuseau horaire"
+
+#: ztfy/utils/timezone/interfaces.py:36
+msgid "Default server timezone"
+msgstr "Fuseau horaire par défaut du serveur"
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/locales/ztfy.utils.pot Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,485 @@
+##############################################################################
+#
+# Copyright (c) 2003-2004 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+msgid ""
+msgstr ""
+"Project-Id-Version: Meaningless\n"
+"POT-Creation-Date: Thu Apr 12 00:21:57 2012\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: Zope 3 Developers <zope-dev@zope.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: zope/app/locales/extract.py\n"
+
+#: ztfy/utils/browser/encoding.py:47
+msgid "-- automatic selection --"
+msgstr ""
+
+#: ztfy/utils/date.py:101
+msgid "%d hours ago"
+msgstr ""
+
+#: ztfy/utils/date.py:103
+msgid "%d minutes ago"
+msgstr ""
+
+#: ztfy/utils/date.py:105
+msgid "less than 5 minutes ago"
+msgstr ""
+
+#: ztfy/utils/date.py:116
+msgid "%d months"
+msgstr ""
+
+#: ztfy/utils/date.py:118
+msgid "%d weeks"
+msgstr ""
+
+#: ztfy/utils/date.py:120
+msgid "%d days"
+msgstr ""
+
+#: ztfy/utils/date.py:124
+msgid "%d day and %d hours"
+msgstr ""
+
+#: ztfy/utils/date.py:127
+msgid "%d hours"
+msgstr ""
+
+#: ztfy/utils/date.py:131
+msgid "%d minutes"
+msgstr ""
+
+#: ztfy/utils/date.py:133
+msgid "%d seconds"
+msgstr ""
+
+#: ztfy/utils/date.py:89
+msgid "%d months ago"
+msgstr ""
+
+#: ztfy/utils/date.py:91
+msgid "%d weeks ago"
+msgstr ""
+
+#: ztfy/utils/date.py:93
+msgid "%d days ago"
+msgstr ""
+
+#: ztfy/utils/date.py:95
+msgid "the day before yesterday"
+msgstr ""
+
+#: ztfy/utils/date.py:97
+msgid "yesterday"
+msgstr ""
+
+#: ztfy/utils/encoding.py:100
+msgid "Greek (iso8859_7)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:101
+msgid "Hebrew (iso8859_8)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:102
+msgid "Turkish (iso8859_9)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:103
+msgid "Nordic languages (iso8859_10)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:104
+msgid "Baltic languages (iso8859_13)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:105
+msgid "Celtic languages (iso8859_14)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:106
+msgid "Western Europe (iso8859_15)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:107
+msgid "South-Eastern Europe (iso8859_16)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:108
+msgid "Korean (johab)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:109
+msgid "Russian (koi8_r)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:110
+msgid "Ukrainian (koi8_u)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:111
+msgid "Bulgarian, Byelorussian, Macedonian, Russian, Serbian (mac_cyrillic)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:112
+msgid "Greek (mac_greek)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:113
+msgid "Icelandic (mac_iceland)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:114
+msgid "Central and Eastern Europe (mac_latin2)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:115
+msgid "Western Europe (mac_roman)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:116
+msgid "Turkish (mac_turkish)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:117
+msgid "Kazakh (ptcp154)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:118
+msgid "Japanese (shift_jis)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:119
+msgid "Japanese (shift_jis_2004)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:120
+msgid "Japanese (shift_jisx0213)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:121
+msgid "all languages (utf_32)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:122
+msgid "all languages (utf_32_be)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:123
+msgid "all languages (utf_32_le)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:124
+msgid "all languages (utf_16)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:125
+msgid "all languages (BMP only - utf_16_be)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:126
+msgid "all languages (BMP only - utf_16_le)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:127
+msgid "all languages (utf_7)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:128
+msgid "all languages (utf_8)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:129
+msgid "all languages (utf_8_sig)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:38
+msgid "English (ASCII)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:39
+msgid "Traditional Chinese (big5)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:40
+msgid "Traditional Chinese (big5hkscs)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:41
+msgid "English (cp037)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:42
+msgid "Hebrew (cp424)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:43
+msgid "English (cp437)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:44
+msgid "Western Europe (cp500)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:45
+msgid "Arabic (cp720)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:46
+msgid "Greek (cp737)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:47
+msgid "Baltic languages (cp775)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:48
+msgid "Western Europe (cp850)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:49
+msgid "Central and Eastern Europe (cp852)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:50
+msgid "Bulgarian, Byelorussian, Macedonian, Russian, Serbian (cp855)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:51
+msgid "Hebrew (cp856)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:52
+msgid "Turkish (cp857)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:53
+msgid "Western Europe (cp858)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:54
+msgid "Portuguese (cp860)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:55
+msgid "Icelandic (cp861)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:56
+msgid "Hebrew (cp862)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:57
+msgid "Canadian (cp863)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:58
+msgid "Arabic (cp864)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:59
+msgid "Danish, Norwegian (cp865)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:60
+msgid "Russian (cp866)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:61
+msgid "Greek (cp869)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:62
+msgid "Thai (cp874)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:63
+msgid "Greek (cp875)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:64
+msgid "Japanese (cp932)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:65
+msgid "Korean (cp949)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:66
+msgid "Traditional Chinese (cp950)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:67
+msgid "Urdu (cp1006)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:68
+msgid "Turkish (cp1026)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:69
+msgid "Western Europe (cp1140)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:70
+msgid "Central and Eastern Europe (cp1250)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:71
+msgid "Bulgarian, Byelorussian, Macedonian, Russian, Serbian (cp1251)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:72
+msgid "Western Europe (cp1252)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:73
+msgid "Greek (cp1253)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:74
+msgid "Turkish (cp1254)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:75
+msgid "Hebrew (cp1255)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:76
+msgid "Arabic (cp1256)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:77
+msgid "Baltic languages (cp1257)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:78
+msgid "Vietnamese (cp1258)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:79
+msgid "Japanese (euc_jp)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:80
+msgid "Japanese (euc_jis_2004)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:81
+msgid "Japanese (euc_jisx0213)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:82
+msgid "Korean (euc_kr)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:83
+msgid "Simplified Chinese (gb2312)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:84
+msgid "Unified Chinese (gbk)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:85
+msgid "Unified Chinese (gb18030)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:86
+msgid "Simplified Chinese (hz)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:87
+msgid "Japanese (iso2022_jp)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:88
+msgid "Japanese (iso2022_jp_1)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:89
+msgid "Japanese, Korean, Simplified Chinese, Western Europe, Greek (iso2022_jp_2)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:90
+msgid "Japanese (iso2022_jp_2004)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:91
+msgid "Japanese (iso2022_jp_3)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:92
+msgid "Japanese (iso2022_jp_ext)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:93
+msgid "Korean (iso2022_kr)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:94
+msgid "West Europe (latin_1)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:95
+msgid "Central and Eastern Europe (iso8859_2)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:96
+msgid "Esperanto, Maltese (iso8859_3)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:97
+msgid "Baltic languages (iso8859_4)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:98
+msgid "Bulgarian, Byelorussian, Macedonian, Russian, Serbian (iso8859_5)"
+msgstr ""
+
+#: ztfy/utils/encoding.py:99
+msgid "Arabic (iso8859_6)"
+msgstr ""
+
+#: ztfy/utils/request.py:40
+msgid "No Request in interaction !"
+msgstr ""
+
+#: ztfy/utils/schema.py:58
+msgid "Color length must be 3 or 6 characters"
+msgstr ""
+
+#: ztfy/utils/schema.py:61
+msgid "Color value must contain only valid color codes (numbers or letters between 'A' end 'F')"
+msgstr ""
+
+#: ztfy/utils/security.py:71
+msgid "< missing principal %s >"
+msgstr ""
+
+#: ztfy/utils/security.py:75
+msgid "This principal can't be found in any authentication utility..."
+msgstr ""
+
+#: ztfy/utils/timezone/interfaces.py:35
+msgid "Server timezone"
+msgstr ""
+
+#: ztfy/utils/timezone/interfaces.py:36
+msgid "Default server timezone"
+msgstr ""
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/overrides.zcml Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,8 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ i18n_domain="ztfy.utils">
+
+ <adapter
+ factory=".file.FileUploadDataConverter" />
+
+</configure>
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/profilehooks.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,599 @@
+"""
+Profiling hooks
+
+This module contains a couple of decorators (`profile` and `coverage`) that
+can be used to wrap functions and/or methods to produce profiles and line
+coverage reports.
+
+Usage example (Python 2.4 or newer):
+
+ from profilehooks import profile, coverage
+
+ @profile # or @coverage
+ def fn(n):
+ if n < 2: return 1
+ else: return n * fn(n-1)
+
+ print fn(42)
+
+Usage example (Python 2.3 or older):
+
+ from profilehooks import profile, coverage
+
+ def fn(n):
+ if n < 2: return 1
+ else: return n * fn(n-1)
+
+ # Now wrap that function in a decorator
+ fn = profile(fn) # or coverage(fn)
+
+ print fn(42)
+
+Reports for all thusly decorated functions will be printed to sys.stdout
+on program termination. You can alternatively request for immediate
+reports for each call by passing immediate=True to the profile decorator.
+
+There's also a @timecall decorator for printing the time to sys.stderr
+every time a function is called, when you just want to get a rough measure
+instead of a detailed (but costly) profile.
+
+Caveats
+
+ A thread on python-dev convinced me that hotshot produces bogus numbers.
+ See http://mail.python.org/pipermail/python-dev/2005-November/058264.html
+
+ I don't know what will happen if a decorated function will try to call
+ another decorated function. All decorators probably need to explicitly
+ support nested profiling (currently TraceFuncCoverage is the only one that
+ supports this, while HotShotFuncProfile has support for recursive functions.)
+
+ Profiling with hotshot creates temporary files (*.prof and *.prof.pickle for
+ profiling, *.cprof for coverage) in the current directory. These files are
+ not cleaned up. (In fact, the *.pickle versions are intentionally written
+ out in case you want to look at the profiler results without having to parse
+ the big *.prof file with hotshot.stats.load, which takes ages. Just unpickle
+ the file and you'll get a pstats object.)
+
+ Coverage analysis with hotshot seems to miss some executions resulting in
+ lower line counts and some lines errorneously marked as never executed. For
+ this reason coverage analysis now uses trace.py which is slower, but more
+ accurate.
+
+ Decorating functions causes doctest.testmod() to ignore doctests in those
+ functions.
+
+Copyright (c) 2004--2006 Marius Gedminas <marius@pov.lt>
+
+Released under the MIT licence since December 2006:
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+
+(Previously it was distributed under the GNU General Public Licence.)
+"""
+# $Id: profilehooks.py,v 1.2 2008/07/18 14:51:08 tflorac Exp $
+
+__author__ = "Marius Gedminas (marius@gedmin.as)"
+__copyright__ = "Copyright 2004-2006, Marius Gedminas"
+__license__ = "MIT"
+__version__ = "1.0"
+__date__ = "2006-12-06"
+
+
+import atexit
+import inspect
+import sys
+import re
+import cPickle as pickle
+
+# For profiling
+from profile import Profile
+import pstats
+
+# For hotshot profiling (inaccurate!)
+import hotshot
+import hotshot.stats
+
+# For trace.py coverage
+import trace
+
+# For hotshot coverage (inaccurate!; uses undocumented APIs; might break)
+import _hotshot
+import hotshot.log
+
+# For timecall
+import time
+
+
+def profile(fn=None, skip=0, filename=None, immediate=False):
+ """Mark `fn` for profiling.
+
+ If `immediate` is False, profiling results will be printed to sys.stdout on
+ program termination. Otherwise results will be printed after each call.
+
+ If `skip` is > 0, first `skip` calls to `fn` will not be profiled.
+
+ If `filename` is specified, the profile stats will be pickled and stored in
+ a file.
+
+ Usage:
+
+ def fn(...):
+ ...
+ fn = profile(fn, skip=1)
+
+ If you are using Python 2.4, you should be able to use the decorator
+ syntax:
+
+ @profile(skip=3)
+ def fn(...):
+ ...
+
+ or just
+
+ @profile
+ def fn(...):
+ ...
+
+ """
+ if fn is None: # @profile() syntax -- we are a decorator maker
+ def decorator(fn):
+ return profile(fn, skip=skip, filename=filename,
+ immediate=immediate)
+ return decorator
+ # @profile syntax -- we are a decorator.
+ fp = FuncProfile(fn, skip=skip, filename=filename, immediate=immediate)
+ # or HotShotFuncProfile
+ # We cannot return fp or fp.__call__ directly as that would break method
+ # definitions, instead we need to return a plain function.
+ def new_fn(*args, **kw):
+ return fp(*args, **kw)
+ new_fn.__doc__ = fn.__doc__
+ return new_fn
+
+
+def coverage(fn):
+ """Mark `fn` for line coverage analysis.
+
+ Results will be printed to sys.stdout on program termination.
+
+ Usage:
+
+ def fn(...):
+ ...
+ fn = coverage(fn)
+
+ If you are using Python 2.4, you should be able to use the decorator
+ syntax:
+
+ @coverage
+ def fn(...):
+ ...
+
+ """
+ fp = TraceFuncCoverage(fn) # or HotShotFuncCoverage
+ # We cannot return fp or fp.__call__ directly as that would break method
+ # definitions, instead we need to return a plain function.
+ def new_fn(*args, **kw):
+ return fp(*args, **kw)
+ new_fn.__doc__ = fn.__doc__
+ return new_fn
+
+
+def coverage_with_hotshot(fn):
+ """Mark `fn` for line coverage analysis.
+
+ Uses the 'hotshot' module for fast coverage analysis.
+
+ BUG: Produces inaccurate results.
+
+ See the docstring of `coverage` for usage examples.
+ """
+ fp = HotShotFuncCoverage(fn)
+ # We cannot return fp or fp.__call__ directly as that would break method
+ # definitions, instead we need to return a plain function.
+ def new_fn(*args, **kw):
+ return fp(*args, **kw)
+ new_fn.__doc__ = fn.__doc__
+ return new_fn
+
+
+class FuncProfile:
+ """Profiler for a function (uses profile)."""
+
+ # This flag is shared between all instances
+ in_profiler = False
+
+ def __init__(self, fn, skip=0, filename=None, immediate=False):
+ """Creates a profiler for a function.
+
+ Every profiler has its own log file (the name of which is derived from
+ the function name).
+
+ HotShotFuncProfile registers an atexit handler that prints profiling
+ information to sys.stderr when the program terminates.
+
+ The log file is not removed and remains there to clutter the current
+ working directory.
+ """
+ self.fn = fn
+ self.filename = filename
+ self.immediate = immediate
+ self.stats = pstats.Stats(Profile())
+ self.ncalls = 0
+ self.skip = skip
+ self.skipped = 0
+ atexit.register(self.atexit)
+
+ def __call__(self, *args, **kw):
+ """Profile a singe call to the function."""
+ self.ncalls += 1
+ if self.skip > 0:
+ self.skip -= 1
+ self.skipped += 1
+ return self.fn(*args, **kw)
+ if FuncProfile.in_profiler:
+ # handle recursive calls
+ return self.fn(*args, **kw)
+ # You cannot reuse the same profiler for many calls and accumulate
+ # stats that way. :-/
+ profiler = Profile()
+ try:
+ FuncProfile.in_profiler = True
+ return profiler.runcall(self.fn, *args, **kw)
+ finally:
+ FuncProfile.in_profiler = False
+ self.stats.add(profiler)
+ if self.immediate:
+ self.print_stats()
+ self.reset_stats()
+
+ def print_stats(self):
+ """Print profile information to sys.stdout."""
+ funcname = self.fn.__name__
+ filename = self.fn.func_code.co_filename
+ lineno = self.fn.func_code.co_firstlineno
+ print
+ print "*** PROFILER RESULTS ***"
+ print "%s (%s:%s)" % (funcname, filename, lineno)
+ print "function called %d times" % self.ncalls,
+ if self.skipped:
+ print "(%d calls not profiled)" % self.skipped
+ else:
+ print
+ print
+ stats = self.stats
+ if self.filename:
+ pickle.dump(stats, file(self.filename, 'w'))
+ stats.strip_dirs()
+ stats.sort_stats('cumulative', 'time', 'calls')
+ stats.print_stats(40)
+
+ def reset_stats(self):
+ """Reset accumulated profiler statistics."""
+ self.stats = pstats.Stats(Profile())
+ self.ncalls = 0
+ self.skipped = 0
+
+ def atexit(self):
+ """Stop profiling and print profile information to sys.stdout.
+
+ This function is registered as an atexit hook.
+ """
+ if not self.immediate:
+ self.print_stats()
+
+
+class HotShotFuncProfile:
+ """Profiler for a function (uses hotshot)."""
+
+ # This flag is shared between all instances
+ in_profiler = False
+
+ def __init__(self, fn, skip=0, filename=None):
+ """Creates a profiler for a function.
+
+ Every profiler has its own log file (the name of which is derived from
+ the function name).
+
+ HotShotFuncProfile registers an atexit handler that prints profiling
+ information to sys.stderr when the program terminates.
+
+ The log file is not removed and remains there to clutter the current
+ working directory.
+ """
+ self.fn = fn
+ self.filename = filename
+ if self.filename:
+ self.logfilename = filename + ".raw"
+ else:
+ self.logfilename = fn.__name__ + ".prof"
+ self.profiler = hotshot.Profile(self.logfilename)
+ self.ncalls = 0
+ self.skip = skip
+ self.skipped = 0
+ atexit.register(self.atexit)
+
+ def __call__(self, *args, **kw):
+ """Profile a singe call to the function."""
+ self.ncalls += 1
+ if self.skip > 0:
+ self.skip -= 1
+ self.skipped += 1
+ return self.fn(*args, **kw)
+ if HotShotFuncProfile.in_profiler:
+ # handle recursive calls
+ return self.fn(*args, **kw)
+ try:
+ HotShotFuncProfile.in_profiler = True
+ return self.profiler.runcall(self.fn, *args, **kw)
+ finally:
+ HotShotFuncProfile.in_profiler = False
+
+ def atexit(self):
+ """Stop profiling and print profile information to sys.stderr.
+
+ This function is registered as an atexit hook.
+ """
+ self.profiler.close()
+ funcname = self.fn.__name__
+ filename = self.fn.func_code.co_filename
+ lineno = self.fn.func_code.co_firstlineno
+ print
+ print "*** PROFILER RESULTS ***"
+ print "%s (%s:%s)" % (funcname, filename, lineno)
+ print "function called %d times" % self.ncalls,
+ if self.skipped:
+ print "(%d calls not profiled)" % self.skipped
+ else:
+ print
+ print
+ stats = hotshot.stats.load(self.logfilename)
+ # hotshot.stats.load takes ages, and the .prof file eats megabytes, but
+ # a pickled stats object is small and fast
+ if self.filename:
+ pickle.dump(stats, file(self.filename, 'w'))
+ # it is best to pickle before strip_dirs
+ stats.strip_dirs()
+ stats.sort_stats('cumulative', 'time', 'calls')
+ stats.print_stats(200)
+
+
+class HotShotFuncCoverage:
+ """Coverage analysis for a function (uses _hotshot).
+
+ HotShot coverage is reportedly faster than trace.py, but it appears to
+ have problems with exceptions; also line counts in coverage reports
+ are generally lower from line counts produced by TraceFuncCoverage.
+ Is this my bug, or is it a problem with _hotshot?
+ """
+
+ def __init__(self, fn):
+ """Creates a profiler for a function.
+
+ Every profiler has its own log file (the name of which is derived from
+ the function name).
+
+ HotShotFuncCoverage registers an atexit handler that prints profiling
+ information to sys.stderr when the program terminates.
+
+ The log file is not removed and remains there to clutter the current
+ working directory.
+ """
+ self.fn = fn
+ self.logfilename = fn.__name__ + ".cprof"
+ self.profiler = _hotshot.coverage(self.logfilename)
+ self.ncalls = 0
+ atexit.register(self.atexit)
+
+ def __call__(self, *args, **kw):
+ """Profile a singe call to the function."""
+ self.ncalls += 1
+ return self.profiler.runcall(self.fn, args, kw)
+
+ def atexit(self):
+ """Stop profiling and print profile information to sys.stderr.
+
+ This function is registered as an atexit hook.
+ """
+ self.profiler.close()
+ funcname = self.fn.__name__
+ filename = self.fn.func_code.co_filename
+ lineno = self.fn.func_code.co_firstlineno
+ print
+ print "*** COVERAGE RESULTS ***"
+ print "%s (%s:%s)" % (funcname, filename, lineno)
+ print "function called %d times" % self.ncalls
+ print
+ fs = FuncSource(self.fn)
+ reader = hotshot.log.LogReader(self.logfilename)
+ for what, (filename, lineno, funcname), tdelta in reader:
+ if filename != fs.filename:
+ continue
+ if what == hotshot.log.LINE:
+ fs.mark(lineno)
+ if what == hotshot.log.ENTER:
+ # hotshot gives us the line number of the function definition
+ # and never gives us a LINE event for the first statement in
+ # a function, so if we didn't perform this mapping, the first
+ # statement would be marked as never executed
+ if lineno == fs.firstlineno:
+ lineno = fs.firstcodelineno
+ fs.mark(lineno)
+ reader.close()
+ print fs
+
+
+class TraceFuncCoverage:
+ """Coverage analysis for a function (uses trace module).
+
+ HotShot coverage analysis is reportedly faster, but it appears to have
+ problems with exceptions.
+ """
+
+ # Shared between all instances so that nested calls work
+ tracer = trace.Trace(count=True, trace=False,
+ ignoredirs=[sys.prefix, sys.exec_prefix])
+
+ # This flag is also shared between all instances
+ tracing = False
+
+ def __init__(self, fn):
+ """Creates a profiler for a function.
+
+ Every profiler has its own log file (the name of which is derived from
+ the function name).
+
+ TraceFuncCoverage registers an atexit handler that prints profiling
+ information to sys.stderr when the program terminates.
+
+ The log file is not removed and remains there to clutter the current
+ working directory.
+ """
+ self.fn = fn
+ self.logfilename = fn.__name__ + ".cprof"
+ self.ncalls = 0
+ atexit.register(self.atexit)
+
+ def __call__(self, *args, **kw):
+ """Profile a singe call to the function."""
+ self.ncalls += 1
+ if TraceFuncCoverage.tracing:
+ return self.fn(*args, **kw)
+ try:
+ TraceFuncCoverage.tracing = True
+ return self.tracer.runfunc(self.fn, *args, **kw)
+ finally:
+ TraceFuncCoverage.tracing = False
+
+ def atexit(self):
+ """Stop profiling and print profile information to sys.stderr.
+
+ This function is registered as an atexit hook.
+ """
+ funcname = self.fn.__name__
+ filename = self.fn.func_code.co_filename
+ lineno = self.fn.func_code.co_firstlineno
+ print
+ print "*** COVERAGE RESULTS ***"
+ print "%s (%s:%s)" % (funcname, filename, lineno)
+ print "function called %d times" % self.ncalls
+ print
+ fs = FuncSource(self.fn)
+ for (filename, lineno), count in self.tracer.counts.items():
+ if filename != fs.filename:
+ continue
+ fs.mark(lineno, count)
+ print fs
+ never_executed = fs.count_never_executed()
+ if never_executed:
+ print "%d lines were not executed." % never_executed
+
+
+class FuncSource:
+ """Source code annotator for a function."""
+
+ blank_rx = re.compile(r"^\s*finally:\s*(#.*)?$")
+
+ def __init__(self, fn):
+ self.fn = fn
+ self.filename = inspect.getsourcefile(fn)
+ self.source, self.firstlineno = inspect.getsourcelines(fn)
+ self.sourcelines = {}
+ self.firstcodelineno = self.firstlineno
+ self.find_source_lines()
+
+ def find_source_lines(self):
+ """Mark all executable source lines in fn as executed 0 times."""
+ strs = trace.find_strings(self.filename)
+ lines = trace.find_lines_from_code(self.fn.func_code, strs)
+ self.firstcodelineno = sys.maxint
+ for lineno in lines:
+ self.firstcodelineno = min(self.firstcodelineno, lineno)
+ self.sourcelines.setdefault(lineno, 0)
+ if self.firstcodelineno == sys.maxint:
+ self.firstcodelineno = self.firstlineno
+
+ def mark(self, lineno, count=1):
+ """Mark a given source line as executed count times.
+
+ Multiple calls to mark for the same lineno add up.
+ """
+ self.sourcelines[lineno] = self.sourcelines.get(lineno, 0) + count
+
+ def count_never_executed(self):
+ """Count statements that were never executed."""
+ lineno = self.firstlineno
+ counter = 0
+ for line in self.source:
+ if self.sourcelines.get(lineno) == 0:
+ if not self.blank_rx.match(line):
+ counter += 1
+ lineno += 1
+ return counter
+
+ def __str__(self):
+ """Return annotated source code for the function."""
+ lines = []
+ lineno = self.firstlineno
+ for line in self.source:
+ counter = self.sourcelines.get(lineno)
+ if counter is None:
+ prefix = ' ' * 7
+ elif counter == 0:
+ if self.blank_rx.match(line):
+ prefix = ' ' * 7
+ else:
+ prefix = '>' * 6 + ' '
+ else:
+ prefix = '%5d: ' % counter
+ lines.append(prefix + line)
+ lineno += 1
+ return ''.join(lines)
+
+
+def timecall(fn):
+ """Wrap `fn` and print its execution time.
+
+ Example:
+
+ @timecall
+ def somefunc(x, y):
+ time.sleep(x * y)
+
+ somefunc(2, 3)
+
+ will print
+
+ somefunc: 6.0 seconds
+
+ """
+ def new_fn(*args, **kw):
+ try:
+ start = time.time()
+ return fn(*args, **kw)
+ finally:
+ duration = time.time() - start
+ funcname = fn.__name__
+ filename = fn.func_code.co_filename
+ lineno = fn.func_code.co_firstlineno
+ print >> sys.stderr, "\n %s (%s:%s):\n %.3f seconds\n" % (
+ funcname, filename, lineno, duration)
+ new_fn.__doc__ = fn.__doc__
+ return new_fn
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/property.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,65 @@
+### -*- coding: utf-8 -*- ####################################################
+##############################################################################
+#
+# Copyright (c) 2012 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+
+# import standard packages
+
+# import Zope3 interfaces
+
+# import local interfaces
+
+# import Zope3 packages
+
+# import local packages
+
+
+class cached(object):
+ """Custom property decorator to define a property or function
+ which is calculated only once
+
+ When applied on a function, caching is based on input arguments
+ """
+
+ def __init__(self, function):
+ self._function = function
+ self._cache = {}
+
+ def __call__(self, *args):
+ try:
+ return self._cache[args]
+ except KeyError:
+ self._cache[args] = self._function(*args)
+ return self._cache[args]
+
+ def expire(self, *args):
+ del self._cache[args]
+
+
+class cached_property(object):
+ """A read-only @property decorator that is only evaluated once. The value is cached
+ on the object itself rather than the function or class; this should prevent
+ memory leakage.
+ """
+ def __init__(self, fget, doc=None):
+ self.fget = fget
+ self.__doc__ = doc or fget.__doc__
+ self.__name__ = fget.__name__
+ self.__module__ = fget.__module__
+
+ def __get__(self, obj, cls):
+ if obj is None:
+ return self
+ obj.__dict__[self.__name__] = result = self.fget(obj)
+ return result
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/protocol/__init__.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,1 @@
+#
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/protocol/http.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,76 @@
+### -*- coding: utf-8 -*- ####################################################
+##############################################################################
+#
+# Copyright (c) 2012 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+
+# import standard packages
+import httplib2
+import urllib
+
+# import Zope3 interfaces
+
+# import local interfaces
+
+# import Zope3 packages
+
+# import local packages
+
+
+class HTTPClient(object):
+ """HTTP client"""
+
+ def __init__(self, method, protocol, servername, url, params={}, credentials=(),
+ proxy=(), rdns=True, proxy_auth=(), timeout=None, headers={}):
+ """Intialize HTTP connection"""
+ self.connection = None
+ self.method = method
+ self.protocol = protocol
+ self.servername = servername
+ self.url = url
+ self.params = params
+ self.location = None
+ self.credentials = credentials
+ self.proxy = proxy
+ self.rdns = rdns
+ self.proxy_auth = proxy_auth
+ self.timeout = timeout
+ self.headers = headers
+ if 'User-Agent' not in headers:
+ self.headers['User-Agent'] = 'ZTFY HTTP Client/1.0'
+
+ def getResponse(self):
+ """Common HTTP request"""
+ if self.proxy:
+ proxy_info = httplib2.ProxyInfo(httplib2.socks.PROXY_TYPE_HTTP,
+ proxy_host=self.proxy[0],
+ proxy_port=self.proxy[1],
+ proxy_rdns=self.rdns,
+ proxy_user=self.proxy_auth and self.proxy_auth[0] or None,
+ proxy_pass=self.proxy_auth and self.proxy_auth[1] or None)
+ proxy_info = None
+ else:
+ proxy_info = None
+ http = httplib2.Http(timeout=self.timeout, proxy_info=proxy_info)
+ if self.credentials:
+ http.add_credentials(self.credentials[0], self.credentials[1])
+ uri = '%s://%s%s' % (self.protocol, self.servername, self.url)
+ if self.params:
+ uri += '?' + urllib.urlencode(self.params)
+ response, content = http.request(uri, self.method, headers=self.headers)
+ return response, content
+
+
+def getClient(method, protocol, servername, url, params={}, credentials=(), proxy=(), rdns=True, proxy_auth=(), timeout=None, headers={}):
+ """HTTP client factory"""
+ return HTTPClient(method, protocol, servername, url, params, credentials, proxy, rdns, proxy_auth, timeout, headers)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/protocol/xmlrpc.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,98 @@
+### -*- coding: utf-8 -*- ####################################################
+##############################################################################
+#
+# Copyright (c) 2008 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+
+# import standard packages
+import base64
+import cookielib
+import urllib2
+import xmlrpclib
+
+# import Zope3 interfaces
+
+# import local interfaces
+
+# import Zope3 packages
+
+# import local packages
+
+
+class XMLRPCCookieAuthTransport(xmlrpclib.Transport):
+ """An XML-RPC transport handling authentication via cookies"""
+
+ def __init__(self, user_agent, credentials=(), cookies=None):
+ xmlrpclib.Transport.__init__(self)
+ self.user_agent = user_agent
+ self.credentials = credentials
+ self.cookies = cookies
+
+ # override the send_host hook to also send authentication info
+ def send_host(self, connection, host):
+ xmlrpclib.Transport.send_host(self, connection, host)
+ if (self.cookies is not None) and (len(self.cookies) > 0):
+ for cookie in self.cookies:
+ connection.putheader('Cookie', '%s=%s' % (cookie.name, cookie.value))
+ elif self.credentials:
+ auth = 'Basic %s' % base64.encodestring("%s:%s" % self.credentials).strip()
+ connection.putheader('Authorization', auth)
+
+ def request(self, host, handler, request_body, verbose=False):
+
+ # dummy request class for extracting cookies
+ class CookieRequest(urllib2.Request):
+ pass
+
+ # dummy response class for extracting cookies
+ class CookieResponse:
+ def __init__(self, headers):
+ self.headers = headers
+ def info(self):
+ return self.headers
+
+ # issue XML-RPC request
+ connection = self.make_connection(host)
+ self.verbose = verbose
+ if verbose:
+ connection.set_debuglevel(1)
+ self.send_request(connection, handler, request_body)
+ self.send_host(connection, host)
+ self.send_user_agent(connection)
+ # get response
+ self.send_content(connection, request_body)
+ errcode, errmsg, headers = connection.getreply()
+ # extract cookies from response headers
+ crequest = CookieRequest('http://%s/' % host)
+ cresponse = CookieResponse(headers)
+ if self.cookies is not None:
+ self.cookies.extract_cookies(cresponse, crequest)
+ if errcode != 200:
+ raise xmlrpclib.ProtocolError(host + handler, errcode, errmsg, headers)
+ try:
+ sock = connection._conn.sock
+ except AttributeError:
+ sock = None
+ return self._parse_response(connection.getfile(), sock)
+
+
+def getClient(uri, credentials=(), verbose=False):
+ """Get an XML-RPC client which supports basic authentication"""
+ transport = XMLRPCCookieAuthTransport('Python XML-RPC Client/0.1 (ZTFY basic implementation)', credentials)
+ return xmlrpclib.Server(uri, transport=transport, verbose=verbose)
+
+
+def getClientWithCookies(uri, credentials=(), verbose=False):
+ """Get an XML-RPC client which supports authentication throught cookies"""
+ transport = XMLRPCCookieAuthTransport('Python XML-RPC Client/0.1 (ZTFY cookie implementation)', credentials, cookielib.CookieJar())
+ return xmlrpclib.Server(uri, transport=transport, verbose=verbose)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/request.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,86 @@
+### -*- coding: utf-8 -*- ####################################################
+##############################################################################
+#
+# Copyright (c) 2008 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+
+# import standard packages
+
+# import Zope3 interfaces
+from zope.annotation.interfaces import IAnnotations
+from zope.publisher.interfaces import IRequest
+from zope.security.interfaces import NoInteraction
+
+# import local interfaces
+
+# import Zope3 packages
+from zope.security.management import getInteraction
+
+# import local packages
+
+from ztfy.utils import _
+
+
+def getRequest():
+ """Extract request from current interaction"""
+ interaction = getInteraction()
+ for participation in interaction.participations:
+ if IRequest.providedBy(participation):
+ return participation
+ raise RuntimeError, _("No Request in interaction !")
+
+
+def queryRequest():
+ try:
+ return getRequest()
+ except NoInteraction: # No current interaction
+ return None
+ except RuntimeError: # No request in interaction
+ return None
+
+
+def getRequestPrincipal(request=None):
+ """Get principal from given request"""
+ if request is None:
+ request = getRequest()
+ return request.principal
+
+getPrincipal = getRequestPrincipal
+
+
+def getRequestAnnotations(request=None):
+ """Get annotations from given request"""
+ if request is None:
+ request = getRequest()
+ return IAnnotations(request)
+
+getAnnotations = getRequestAnnotations
+
+
+def getRequestData(key, request=None, default=None):
+ """Get request data, stored into request annotations"""
+ try:
+ annotations = getRequestAnnotations(request)
+ return annotations.get(key, default)
+ except:
+ return default
+
+getData = getRequestData
+
+
+def setRequestData(key, data, request=None):
+ """Define request data, stored into request annotations"""
+ annotations = getRequestAnnotations(request)
+ annotations[key] = data
+
+setData = setRequestData
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/schema.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,110 @@
+### -*- coding: utf-8 -*- ####################################################
+##############################################################################
+#
+# Copyright (c) 2012 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+
+# import standard packages
+import decimal
+import string
+
+# import Zope3 interfaces
+from z3c.form.interfaces import IWidget
+from zope.schema.interfaces import ITextLine, IDecimal
+
+# import local interfaces
+
+# import Zope3 packages
+from z3c.form.converter import BaseDataConverter, FormatterValidationError
+from zope.component import adapts
+from zope.interface import implements
+from zope.schema import TextLine, Decimal
+from zope.schema._bootstrapfields import InvalidValue
+
+# import local packages
+
+from ztfy.utils import _
+
+
+class StringLine(TextLine):
+ """String line field"""
+
+ _type = str
+
+ def fromUnicode(self, value):
+ return str(value)
+
+
+#
+# Color field
+#
+
+class IColorField(ITextLine):
+ """Marker interface for color fields"""
+
+
+class ColorField(TextLine):
+ """Color field"""
+
+ implements(IColorField)
+
+ def __init__(self, *args, **kw):
+ super(ColorField, self).__init__(max_length=6, *args, **kw)
+
+ def _validate(self, value):
+ if len(value) not in (3, 6):
+ raise InvalidValue, _("Color length must be 3 or 6 characters")
+ for v in value:
+ if v not in string.hexdigits:
+ raise InvalidValue, _("Color value must contain only valid color codes (numbers or letters between 'A' end 'F')")
+ super(ColorField, self)._validate(value)
+
+
+#
+# Pointed decimal field
+#
+
+class IDottedDecimalField(IDecimal):
+ """Marker interface for dotted decimal fields"""
+
+
+class DottedDecimalField(Decimal):
+ """Dotted decimal field"""
+
+ implements(IDottedDecimalField)
+
+
+class DottedDecimalDataConverter(BaseDataConverter):
+ """Dotted decimal field data converter"""
+
+ adapts(IDottedDecimalField, IWidget)
+
+ errorMessage = _('The entered value is not a valid decimal literal.')
+
+ def __init__(self, field, widget):
+ super(DottedDecimalDataConverter, self).__init__(field, widget)
+
+ def toWidgetValue(self, value):
+ if not value:
+ return self.field.missing_value
+ return str(value)
+
+ def toFieldValue(self, value):
+ if value is self.field.missing_value:
+ return u''
+ if not value:
+ return None
+ try:
+ return decimal.Decimal(value)
+ except decimal.InvalidOperation:
+ raise FormatterValidationError(self.errorMessage, value)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/security.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,84 @@
+### -*- coding: utf-8 -*- ####################################################
+##############################################################################
+#
+# Copyright (c) 2008 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+
+# import standard packages
+from persistent.list import PersistentList
+from persistent.dict import PersistentDict
+
+# import Zope3 interfaces
+from zope.authentication.interfaces import IAuthentication
+from zope.pluggableauth.interfaces import IPrincipalInfo
+
+# import local interfaces
+
+# import Zope3 packages
+from zc.set import Set
+from zope.component import getUtility
+from zope.deprecation.deprecation import deprecate
+from zope.i18n import translate
+from zope.interface import implements
+from zope.security.proxy import removeSecurityProxy
+
+# import local packages
+from ztfy.utils.request import getRequest
+
+from ztfy.utils import _
+
+
+def unproxied(value):
+ """Remove security proxies from given value ; if value is a list or dict, all elements are unproxied"""
+ if isinstance(value, (list, PersistentList)):
+ result = []
+ for v in value:
+ result.append(unproxied(v))
+ elif isinstance(value, (dict, PersistentDict)):
+ result = value.copy()
+ for v in value:
+ result[v] = unproxied(value[v])
+ elif isinstance(value, (set, Set)):
+ result = []
+ for v in value:
+ result.append(unproxied(v))
+ else:
+ result = removeSecurityProxy(value)
+ return result
+
+
+@deprecate("ztfy.utils.security.MissingPrincipal is deprecated. Use ztfy.security.search.MissingPrincipal class instead.")
+class MissingPrincipal(object):
+
+ implements(IPrincipalInfo)
+
+ def __init__(self, id):
+ self.id = id
+ self.request = getRequest()
+
+ @property
+ def title(self):
+ return translate(_("< missing principal %s >"), context=self.request) % self.id
+
+ @property
+ def description(self):
+ return translate(_("This principal can't be found in any authentication utility..."), context=self.request)
+
+
+@deprecate("ztfy.utils.security.getPrincipal is deprecated. Use ztfy.security.search.getPrincipal function instead.")
+def getPrincipal(uid):
+ principals = getUtility(IAuthentication)
+ try:
+ return principals.getPrincipal(uid)
+ except:
+ return MissingPrincipal(uid)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/session.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,40 @@
+### -*- coding: utf-8 -*- ####################################################
+##############################################################################
+#
+# Copyright (c) 2008-2010 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+__docformat__ = "restructuredtext"
+
+# import standard packages
+
+# import Zope3 interfaces
+from zope.session.interfaces import ISession
+
+# import local interfaces
+
+# import Zope3 packages
+
+# import local packages
+from security import unproxied
+
+
+def getData(request, app, key, default=None):
+ """Get data associated with a given session"""
+ session = ISession(request)[app]
+ return session.get(key, default)
+
+
+def setData(request, app, key, value):
+ """Set data associated to a given session"""
+ session = ISession(request)[app]
+ session[key] = unproxied(value)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/site.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,48 @@
+### -*- coding: utf-8 -*- ####################################################
+##############################################################################
+#
+# Copyright (c) 2008-2010 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+__docformat__ = "restructuredtext"
+
+# import standard packages
+
+# import Zope3 interfaces
+from zope.intid.interfaces import IIntIds
+
+# import local interfaces
+from ztfy.utils.interfaces import INewSiteManagerEvent
+
+# import Zope3 packages
+from zope.component import getUtility
+from zope.interface import implements
+from zope.location import locate
+
+# import local packages
+
+
+class NewSiteManagerEvent(object):
+ """Event notified when a new site manager is created"""
+
+ implements(INewSiteManagerEvent)
+
+ def __init__(self, obj):
+ self.object = obj
+
+
+def locateAndRegister(contained, parent, key, intids=None):
+ locate(contained, parent)
+ if intids is None:
+ intids = getUtility(IIntIds)
+ intids.register(contained)
+ parent[key] = contained
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/tal/__init__.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,1 @@
+#
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/tal/configure.zcml Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,40 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ xmlns:tales="http://namespaces.zope.org/tales"
+ i18n_domain="ztfy.utils">
+
+ <adapter
+ name="start"
+ factory=".text.TextStartTalesAdapter"
+ provides="zope.traversing.interfaces.IPathAdapter"
+ for="*" />
+
+ <adapter
+ name="text"
+ factory=".text.TextOutputTalesAdapter"
+ provides="zope.traversing.interfaces.IPathAdapter"
+ for="*" />
+
+ <adapter
+ name="html"
+ factory=".html.HTMLTalesAdapter"
+ provides="zope.traversing.interfaces.IPathAdapter"
+ for="*" />
+
+ <adapter
+ name="data"
+ factory=".request.RequestDataTalesAdapter"
+ provides="zope.traversing.interfaces.IPathAdapter"
+ for="zope.publisher.interfaces.browser.IBrowserRequest" />
+
+ <adapter
+ name="session"
+ factory=".session.SessionDataTalesAdapter"
+ provides="zope.traversing.interfaces.IPathAdapter"
+ for="zope.publisher.interfaces.browser.IBrowserRequest" />
+
+ <tales:expressiontype
+ name="fanstatic"
+ handler=".fanstatic.FanstaticTalesExpression" />
+
+</configure>
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/tal/fanstatic.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,38 @@
+### -*- coding: utf-8 -*- ####################################################
+##############################################################################
+#
+# Copyright (c) 2008-2011 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+__docformat__ = "restructuredtext"
+
+# import standard packages
+
+# import Zope3 interfaces
+
+# import local interfaces
+
+# import Zope3 packages
+from zope.tales.expressions import StringExpr
+
+# import local packages
+from ztfy.utils.traversing import resolve
+
+
+class FanstaticTalesExpression(StringExpr):
+
+ def __call__(self, econtext):
+ lib, res = self._expr.split('#')
+ module = resolve(lib)
+ resource = getattr(module, res)
+ resource.need()
+ return ''
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/tal/html.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,65 @@
+### -*- coding: utf-8 -*- ####################################################
+##############################################################################
+#
+# Copyright (c) 2008-2010 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+__docformat__ = "restructuredtext"
+
+# import standard packages
+
+# import Zope3 interfaces
+from zope.tales.interfaces import ITALESFunctionNamespace
+
+# import local interfaces
+from ztfy.utils.tal.interfaces import IHTMLTalesAPI
+
+# import Zope3 packages
+from zope.interface import implements
+
+# import local packages
+from ztfy.utils.html import htmlToText
+from ztfy.utils.text import textToHTML, Renderer
+
+
+class HTMLTalesAdapter(object):
+
+ implements(IHTMLTalesAPI, ITALESFunctionNamespace)
+
+ def __init__(self, context):
+ self.context = context
+
+ def setEngine(self, engine):
+ self.request = engine.vars['request']
+
+ def renderer(self):
+ return Renderer(self.context)
+
+ def totext(self):
+ if not self.context:
+ return u''
+ return htmlToText(self.context)
+
+ def text(self):
+ if not self.context:
+ return u''
+ return textToHTML(self.context, 'zope.source.plaintext')
+
+ def stx(self):
+ if not self.context:
+ return u''
+ return textToHTML(self.context, 'zope.source.stx')
+
+ def rest(self):
+ if not self.context:
+ return u''
+ return textToHTML(self.context, 'zope.source.rest')
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/tal/interfaces.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,83 @@
+### -*- coding: utf-8 -*- ####################################################
+##############################################################################
+#
+# Copyright (c) 2008-2010 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+__docformat__ = "restructuredtext"
+
+# import standard packages
+
+# import Zope3 interfaces
+
+# import local interfaces
+
+# import Zope3 packages
+from zope.interface import Interface
+
+# import local packages
+
+
+class ITextStartTalesAPI(Interface):
+ """'start' TALES namespace interface"""
+
+ def __getattr__(attr):
+ """Get first characters of adapted text, without cutting words"""
+
+
+class ITextOutputTalesAPI(Interface):
+ """'text' TALES namespace interface"""
+
+ def js():
+ """Convert adapted text to a JavaScript compatible output"""
+
+ def noquotes():
+ """Remove double quotes from adapted text"""
+
+ def breaks():
+ """Replace '|' by newlines in adapted text"""
+
+ def translate():
+ """Use I18n translation of given text"""
+
+
+class IHTMLTalesAPI(Interface):
+ """'html' TALES namespace interface"""
+
+ def renderer():
+ """Get text to HTML renderer"""
+
+ def totext():
+ """Convert adapted HTML content to text"""
+
+ def text():
+ """Convert adapted text to HTML"""
+
+ def stx():
+ """Convert adapted StructuredText to HTML"""
+
+ def rest():
+ """Convert adapted reStructuredText to HTML"""
+
+
+class IRequestDataTalesAPI(Interface):
+ """Request 'data' TALES namespace interface"""
+
+ def __getattr__(attr):
+ """Get request data for given attribute"""
+
+
+class ISessionDataTalesAPI(Interface):
+ """Session data TALES namespace interface"""
+
+ def __getattr__(attr):
+ """Get session data for given app and key"""
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/tal/request.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,44 @@
+### -*- coding: utf-8 -*- ####################################################
+##############################################################################
+#
+# Copyright (c) 2008-2010 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+__docformat__ = "restructuredtext"
+
+# import standard packages
+
+# import Zope3 interfaces
+from zope.tales.interfaces import ITALESFunctionNamespace
+
+# import local interfaces
+from ztfy.utils.tal.interfaces import IRequestDataTalesAPI
+
+# import Zope3 packages
+from zope.interface import implements
+
+# import local packages
+from ztfy.utils.request import getData
+
+
+class RequestDataTalesAdapter(object):
+
+ implements(IRequestDataTalesAPI, ITALESFunctionNamespace)
+
+ def __init__(self, context):
+ self.context = context
+
+ def setEngine(self, engine):
+ self.request = engine.vars['request']
+
+ def __getattr__(self, attr):
+ return getData(attr, self.context)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/tal/session.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,45 @@
+### -*- coding: utf-8 -*- ####################################################
+##############################################################################
+#
+# Copyright (c) 2008-2010 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+__docformat__ = "restructuredtext"
+
+# import standard packages
+
+# import Zope3 interfaces
+from zope.tales.interfaces import ITALESFunctionNamespace
+
+# import local interfaces
+from ztfy.utils.tal.interfaces import ISessionDataTalesAPI
+
+# import Zope3 packages
+from zope.interface import implements
+
+# import local packages
+from ztfy.utils.session import getData
+
+
+class SessionDataTalesAdapter(object):
+
+ implements(ISessionDataTalesAPI, ITALESFunctionNamespace)
+
+ def __init__(self, context):
+ self.context = context
+
+ def setEngine(self, engine):
+ self.request = engine.vars['request']
+
+ def __getattr__(self, attr):
+ app, key = attr.split(',')
+ return getData(self.context, app, key)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/tal/text.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,76 @@
+### -*- coding: utf-8 -*- ####################################################
+##############################################################################
+#
+# Copyright (c) 2008-2010 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+__docformat__ = "restructuredtext"
+
+# import standard packages
+
+# import Zope3 interfaces
+from zope.tales.interfaces import ITALESFunctionNamespace
+
+# import local interfaces
+from ztfy.utils.tal.interfaces import ITextStartTalesAPI, ITextOutputTalesAPI
+
+# import Zope3 packages
+from zope.i18n import translate
+from zope.interface import implements
+
+# import local packages
+from ztfy.utils.text import textStart
+
+
+class TextStartTalesAdapter(object):
+
+ implements(ITextStartTalesAPI, ITALESFunctionNamespace)
+
+ def __init__(self, context):
+ self.context = context
+
+ def setEngine(self, engine):
+ self.request = engine.vars['request']
+
+ def __getattr__(self, attr):
+ try:
+ if attr.find(',') > 0:
+ length, max = (int(x) for x in attr.split(','))
+ else:
+ length = int(attr)
+ max = 0
+ return textStart(self.context, length, max)
+ except:
+ return ''
+
+
+class TextOutputTalesAdapter(object):
+
+ implements(ITextOutputTalesAPI, ITALESFunctionNamespace)
+
+ def __init__(self, context):
+ self.context = context
+
+ def setEngine(self, engine):
+ self.request = engine.vars['request']
+
+ def js(self):
+ return self.context.replace("'", "'").replace("\n", "
")
+
+ def noquotes(self):
+ return self.context.replace('"', '')
+
+ def breaks(self):
+ return self.context.replace('|', '\n')
+
+ def translate(self):
+ return translate(self.context, context=self.request)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/tests/__init__.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,1 @@
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/tests/test_utilsdocs.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,62 @@
+### -*- coding: utf-8 -*- ####################################################
+##############################################################################
+#
+# Copyright (c) 2008-2010 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+"""
+Generic Test case for ztfy.utils doctest
+"""
+__docformat__ = 'restructuredtext'
+
+import unittest
+import doctest
+import sys
+import os
+
+
+current_dir = os.path.dirname(__file__)
+
+def doc_suite(test_dir, setUp=None, tearDown=None, globs=None):
+ """Returns a test suite, based on doctests found in /doctest."""
+ suite = []
+ if globs is None:
+ globs = globals()
+
+ flags = (doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE |
+ doctest.REPORT_ONLY_FIRST_FAILURE)
+
+ package_dir = os.path.split(test_dir)[0]
+ if package_dir not in sys.path:
+ sys.path.append(package_dir)
+
+ doctest_dir = os.path.join(package_dir, 'doctests')
+
+ # filtering files on extension
+ docs = [os.path.join(doctest_dir, doc) for doc in
+ os.listdir(doctest_dir) if doc.endswith('.txt')]
+
+ for test in docs:
+ suite.append(doctest.DocFileSuite(test, optionflags=flags,
+ globs=globs, setUp=setUp,
+ tearDown=tearDown,
+ module_relative=False))
+
+ return unittest.TestSuite(suite)
+
+def test_suite():
+ """returns the test suite"""
+ return doc_suite(current_dir)
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/tests/test_utilsdocstrings.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,66 @@
+### -*- coding: utf-8 -*- ####################################################
+##############################################################################
+#
+# Copyright (c) 2008-2010 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+"""
+Generic Test case for ztfy.utils doc strings
+"""
+__docformat__ = 'restructuredtext'
+
+import unittest
+import doctest
+import sys
+import os
+
+
+current_dir = os.path.abspath(os.path.dirname(__file__))
+
+def doc_suite(test_dir, globs=None):
+ """Returns a test suite, based on doc tests strings found in /*.py"""
+ suite = []
+ if globs is None:
+ globs = globals()
+
+ flags = (doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE |
+ doctest.REPORT_ONLY_FIRST_FAILURE)
+
+ package_dir = os.path.split(test_dir)[0]
+ if package_dir not in sys.path:
+ sys.path.append(package_dir)
+
+ # filtering files on extension
+ docs = [doc for doc in
+ os.listdir(package_dir) if doc.endswith('.py')]
+ docs = [doc for doc in docs if not doc.startswith('__')]
+
+ for test in docs:
+ fd = open(os.path.join(package_dir, test))
+ content = fd.read()
+ fd.close()
+ if '>>> ' not in content:
+ continue
+ test = test.replace('.py', '')
+ location = 'ztfy.utils.%s' % test
+ suite.append(doctest.DocTestSuite(location, optionflags=flags,
+ globs=globs))
+
+ return unittest.TestSuite(suite)
+
+def test_suite():
+ """returns the test suite"""
+ return doc_suite(current_dir)
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/text.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,68 @@
+### -*- coding: utf-8 -*- ####################################################
+##############################################################################
+#
+# Copyright (c) 2008-2010 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+__docformat__ = "restructuredtext"
+
+# import standard packages
+
+# import Zope3 interfaces
+
+# import local interfaces
+
+# import Zope3 packages
+from zope.component import createObject, queryMultiAdapter
+
+# import local packages
+from ztfy.utils.request import getRequest
+
+
+def textStart(text, length, max=0):
+ """Get first words of given text with maximum given length
+
+ If @max is specified, text is shortened only if remaining text is longer than @max
+
+ @param text: initial text
+ @param length: maximum length of resulting text
+ @param max: if > 0, @text is shortened only if remaining text is longer than max
+ """
+ result = text or ''
+ if length > len(result):
+ return result
+ index = length - 1
+ text_length = len(result)
+ while (index > 0) and (result[index] != ' '):
+ index -= 1
+ if (index > 0) and (text_length > index + max):
+ return result[:index] + '…'
+ return text
+
+
+def textToHTML(text, renderer='zope.source.plaintext', request=None):
+ if request is None:
+ request = getRequest()
+ formatter = createObject(renderer, text)
+ renderer = queryMultiAdapter((formatter, request), name=u'')
+ return renderer.render()
+
+
+class Renderer(object):
+
+ def __init__(self, context):
+ self.context = context
+
+ def render(self, renderer, request=None):
+ if not self.context:
+ return u''
+ return textToHTML(self.context, renderer, request)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/timezone/__init__.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,61 @@
+### -*- coding: utf-8 -*- ####################################################
+##############################################################################
+#
+# Copyright (c) 2008-2010 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+__docformat__ = "restructuredtext"
+
+# import standard packages
+import pytz
+
+# import Zope3 interfaces
+from zope.interface.common.idatetime import ITZInfo
+from zope.publisher.interfaces.browser import IBrowserRequest
+
+# import local interfaces
+from ztfy.utils.timezone.interfaces import IServerTimezone
+
+# import Zope3 packages
+from zope.component import adapter, queryUtility
+from zope.interface import implementer
+
+# import local packages
+
+
+GMT = pytz.timezone('GMT')
+_tz = pytz.timezone('Europe/Paris')
+tz = _tz
+
+@implementer(ITZInfo)
+@adapter(IBrowserRequest)
+def tzinfo(request=None):
+ util = queryUtility(IServerTimezone)
+ if util is not None:
+ return pytz.timezone(util.timezone)
+ return GMT
+
+
+def tztime(value):
+ if not value:
+ return None
+ if not value.tzinfo:
+ value = GMT.localize(value)
+ return value.astimezone(tzinfo())
+
+
+def gmtime(value):
+ if not value:
+ return None
+ if not value.tzinfo:
+ value = GMT.localize(value)
+ return value.astimezone(GMT)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/timezone/configure.zcml Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,39 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ xmlns:browser="http://namespaces.zope.org/browser"
+ i18n_domain="ztfy.utils">
+
+ <adapter
+ factory=".tzinfo" />
+
+ <utility
+ name="ZTFY timezones"
+ component=".schema.TimezonesVocabulary" />
+
+ <class class=".utility.ServerTimezoneUtility">
+ <factory
+ id="ztfy.utils.timezone.ServerTimezone" />
+ <implements
+ interface="zope.annotation.interfaces.IAttributeAnnotatable" />
+ <require
+ interface=".interfaces.IServerTimezone"
+ permission="zope.View" />
+ <require
+ set_schema=".interfaces.IServerTimezone"
+ permission="zope.ManageServices" />
+ </class>
+
+ <browser:addMenuItem
+ title="ZTFY server timezone"
+ description="A server timezone utility is used to define default server timezone"
+ class=".utility.ServerTimezoneUtility"
+ permission="zope.ManageSite" />
+
+ <browser:editform
+ name="properties.html"
+ for=".interfaces.IServerTimezone"
+ schema=".interfaces.IServerTimezone"
+ permission="zope.ManageSite"
+ menu="zmi_views" title="Properties" />
+
+</configure>
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/timezone/interfaces.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,37 @@
+### -*- coding: utf-8 -*- ####################################################
+##############################################################################
+#
+# Copyright (c) 2008-2010 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+__docformat__ = "restructuredtext"
+
+# import standard packages
+
+# import Zope3 interfaces
+
+# import local interfaces
+
+# import Zope3 packages
+from zope.interface import Interface
+
+# import local packages
+from schema import Timezone
+
+from ztfy.utils import _
+
+
+class IServerTimezone(Interface):
+
+ timezone = Timezone(title=_("Server timezone"),
+ description=_("Default server timezone"),
+ required=True)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/timezone/schema.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,57 @@
+### -*- coding: utf-8 -*- ####################################################
+##############################################################################
+#
+# Copyright (c) 2008-2010 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+__docformat__ = "restructuredtext"
+
+# import standard packages
+import pytz
+
+# import Zope3 interfaces
+from zope.schema.interfaces import IChoice, IVocabularyFactory
+
+# import local interfaces
+
+# import Zope3 packages
+from zope.interface import implements, classProvides
+from zope.schema import Choice
+from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary
+
+# import local packages
+
+
+class TimezonesVocabulary(SimpleVocabulary):
+
+ classProvides(IVocabularyFactory)
+
+ def __init__(self, *args, **kw):
+ terms = [SimpleTerm(t, t, t) for t in pytz.all_timezones]
+ super(TimezonesVocabulary, self).__init__(terms)
+
+
+class ITimezone(IChoice):
+ """Marker interface for timezone field"""
+
+
+class Timezone(Choice):
+ """Timezone choice field"""
+
+ implements(ITimezone)
+
+ def __init__(self, **kw):
+ if 'vocabulary' in kw:
+ kw.pop('vocabulary')
+ if 'default' not in kw:
+ kw['default'] = u'GMT'
+ super(Timezone, self).__init__(vocabulary='ZTFY timezones', **kw)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/timezone/utility.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,38 @@
+### -*- coding: utf-8 -*- ####################################################
+##############################################################################
+#
+# Copyright (c) 2008-2010 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+__docformat__ = "restructuredtext"
+
+# import standard packages
+from persistent import Persistent
+
+# import Zope3 interfaces
+
+# import local interfaces
+from ztfy.utils.timezone.interfaces import IServerTimezone
+
+# import Zope3 packages
+from zope.container.contained import Contained
+from zope.interface import implements
+from zope.schema.fieldproperty import FieldProperty
+
+# import local packages
+
+
+class ServerTimezoneUtility(Persistent, Contained):
+
+ implements(IServerTimezone)
+
+ timezone = FieldProperty(IServerTimezone['timezone'])
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/traversing.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,65 @@
+### -*- coding: utf-8 -*- ####################################################
+##############################################################################
+#
+# Copyright (c) 2008 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+
+# import standard packages
+
+# import Zope3 interfaces
+
+# import local interfaces
+
+# import Zope3 packages
+from zope.interface import Interface
+
+# import local packages
+
+
+def getParent(context, interface=Interface, allow_context=True):
+ """Get first parent of the given context that implements given interface"""
+ if allow_context:
+ parent = context
+ else:
+ parent = getattr(context, '__parent__', None)
+ while parent is not None:
+ if interface.providedBy(parent):
+ return interface(parent)
+ parent = getattr(parent, '__parent__', None)
+ return None
+
+
+# copied from fanstatic (which copied it from zope.dottedname !)
+def resolve(name, module=None):
+ name = name.split('.')
+ if not name[0]:
+ if module is None:
+ raise ValueError("relative name without base module")
+ module = module.split('.')
+ name.pop(0)
+ while not name[0]:
+ module.pop()
+ name.pop(0)
+ name = module + name
+
+ used = name.pop(0)
+ found = __import__(used)
+ for n in name:
+ used += '.' + n
+ try:
+ found = getattr(found, n)
+ except AttributeError:
+ __import__(used)
+ found = getattr(found, n)
+
+ return found
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/unicode.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,169 @@
+### -*- coding: utf-8 -*- ####################################################
+##############################################################################
+#
+# Copyright (c) 2008 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+
+# import standard packages
+import codecs
+import string
+
+# import Zope3 interfaces
+
+# import local interfaces
+
+# import Zope3 packages
+
+# import local packages
+
+
+_unicodeTransTable = {}
+def _fillUnicodeTransTable():
+ _corresp = [
+ (u"A", [0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x0100, 0x0102, 0x0104]),
+ (u"AE", [0x00C6]),
+ (u"a", [0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x0101, 0x0103, 0x0105]),
+ (u"ae", [0x00E6]),
+ (u"C", [0x00C7, 0x0106, 0x0108, 0x010A, 0x010C]),
+ (u"c", [0x00E7, 0x0107, 0x0109, 0x010B, 0x010D]),
+ (u"D", [0x00D0, 0x010E, 0x0110]),
+ (u"d", [0x00F0, 0x010F, 0x0111]),
+ (u"E", [0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x0112, 0x0114, 0x0116, 0x0118, 0x011A]),
+ (u"e", [0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x0113, 0x0115, 0x0117, 0x0119, 0x011B]),
+ (u"G", [0x011C, 0x011E, 0x0120, 0x0122]),
+ (u"g", [0x011D, 0x011F, 0x0121, 0x0123]),
+ (u"H", [0x0124, 0x0126]),
+ (u"h", [0x0125, 0x0127]),
+ (u"I", [0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x0128, 0x012A, 0x012C, 0x012E, 0x0130]),
+ (u"i", [0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x0129, 0x012B, 0x012D, 0x012F, 0x0131]),
+ (u"IJ", [0x0132]),
+ (u"ij", [0x0133]),
+ (u"J", [0x0134]),
+ (u"j", [0x0135]),
+ (u"K", [0x0136]),
+ (u"k", [0x0137, 0x0138]),
+ (u"L", [0x0139, 0x013B, 0x013D, 0x013F, 0x0141]),
+ (u"l", [0x013A, 0x013C, 0x013E, 0x0140, 0x0142]),
+ (u"N", [0x00D1, 0x0143, 0x0145, 0x0147, 0x014A]),
+ (u"n", [0x00F1, 0x0144, 0x0146, 0x0148, 0x0149, 0x014B]),
+ (u"O", [0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D8, 0x014C, 0x014E, 0x0150]),
+ (u"o", [0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F8, 0x014D, 0x014F, 0x0151]),
+ (u"OE", [0x0152]),
+ (u"oe", [0x0153]),
+ (u"R", [0x0154, 0x0156, 0x0158]),
+ (u"r", [0x0155, 0x0157, 0x0159]),
+ (u"S", [0x015A, 0x015C, 0x015E, 0x0160]),
+ (u"s", [0x015B, 0x015D, 0x015F, 0x01610, 0x017F]),
+ (u"T", [0x0162, 0x0164, 0x0166]),
+ (u"t", [0x0163, 0x0165, 0x0167]),
+ (u"U", [0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x0168, 0x016A, 0x016C, 0x016E, 0x0170, 0x172]),
+ (u"u", [0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0169, 0x016B, 0x016D, 0x016F, 0x0171]),
+ (u"W", [0x0174]),
+ (u"w", [0x0175]),
+ (u"Y", [0x00DD, 0x0176, 0x0178]),
+ (u"y", [0x00FD, 0x00FF, 0x0177]),
+ (u"Z", [0x0179, 0x017B, 0x017D]),
+ (u"z", [0x017A, 0x017C, 0x017E])
+ ]
+ for char, codes in _corresp:
+ for code in codes :
+ _unicodeTransTable[code] = char
+
+_fillUnicodeTransTable()
+
+
+def translateString(s, escapeSlashes=False, forceLower=True, spaces=' ') :
+ """Remove extended characters from string and replace them with 'basic' ones
+
+ @param s: text to be cleaned.
+ @type s: str or unicode
+ @param escapeSlashes: if True, slashes are also converted
+ @type escapeSlashes: boolean
+ @param forceLower: if True, result is automatically converted to lower case
+ @type forceLower: boolean
+ @return: text without diacritics
+ @rtype: unicode
+ """
+ if escapeSlashes:
+ s = string.replace(s, "\\", "/").split("/")[-1]
+ s = s.strip()
+ if isinstance(s, str):
+ s = unicode(s, "utf8", "replace")
+ s = s.translate(_unicodeTransTable)
+ s = ''.join([a for a in s.translate(_unicodeTransTable) if a.replace(' ', '-') in (string.ascii_letters + string.digits + '_-.')])
+ if forceLower:
+ s = s.lower()
+ if spaces != ' ':
+ s = s.replace(' ', spaces)
+ return s
+
+
+def nvl(value, default=''):
+ """Get specified value, or an empty string if value is empty
+
+ @param value: text to be checked
+ @param default: default value
+ @return: value, or default if value is empty
+ """
+ return value or default
+
+
+def uninvl(value, default=u''):
+ """Get specified value converted to unicode, or an empty unicode string if value is empty
+
+ @param value: text to be checked
+ @type value: str or unicode
+ @param default: default value
+ @return: value, or default if value is empty
+ @rtype: unicode
+ """
+ try:
+ if isinstance(value, unicode):
+ return value
+ return codecs.decode(value or default)
+ except:
+ return codecs.decode(value or default, 'latin1')
+
+
+def unidict(value):
+ """Get specified dict with values converted to unicode
+
+ @param value: input dict of strings which may be converted to unicode
+ @type value: dict
+ @return: input dict converted to unicode
+ @rtype: dict
+ """
+ result = {}
+ for key in value:
+ result[key] = uninvl(value[key])
+ return result
+
+
+def unilist(value):
+ """Get specified list with values converted to unicode
+
+ @param value: input list of strings which may be converted to unicode
+ @type value: list
+ @return: input list converted to unicode
+ @rtype: list
+ """
+ if not isinstance(value, (list, tuple)):
+ return uninvl(value)
+ return [uninvl(v) for v in value]
+
+
+def utf8(value):
+ """Convert given value to UTF-8"""
+ if isinstance(value, unicode):
+ value = value.encode('utf8')
+ return value
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/utils/zodb.py Wed Jun 20 16:29:53 2012 +0200
@@ -0,0 +1,46 @@
+### -*- coding: utf-8 -*- ####################################################
+##############################################################################
+#
+# Copyright (c) 2008-2012 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+__docformat__ = "restructuredtext"
+
+# import standard packages
+
+# import Zope3 interfaces
+from persistent.interfaces import IPersistent
+from transaction.interfaces import ITransactionManager
+from ZODB.interfaces import IConnection
+
+# import local interfaces
+
+# import Zope3 packages
+from zope.component import adapter
+from zope.interface import implementer
+
+# import local packages
+
+
+# IPersistent adapters copied from zc.twist package
+# also register this for adapting from IConnection
+@adapter(IPersistent)
+@implementer(ITransactionManager)
+def transactionManager(obj):
+ conn = IConnection(obj) # typically this will be
+ # zope.app.keyreference.persistent.connectionOfPersistent
+ try:
+ return conn.transaction_manager
+ except AttributeError:
+ return conn._txn_mgr
+ # or else we give up; who knows. transaction_manager is the more
+ # recent spelling.