# HG changeset patch # User Thierry Florac # Date 1340202593 -7200 # Node ID d3668ecd9137a911fee06a2ee376f16f07689d68 # Parent 044dc196ec8aeed4205b06982d3cae23a058c85d Changed package source layout diff -r 044dc196ec8a -r d3668ecd9137 docs/HISTORY.txt --- /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 diff -r 044dc196ec8a -r d3668ecd9137 docs/README.txt --- /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 diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy.utils.egg-info/PKG-INFO --- /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 diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy.utils.egg-info/SOURCES.txt --- /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 diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy.utils.egg-info/dependency_links.txt --- /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 @@ + diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy.utils.egg-info/entry_points.txt --- /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 diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy.utils.egg-info/namespace_packages.txt --- /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 diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy.utils.egg-info/not-zip-safe --- /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 @@ + diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy.utils.egg-info/requires.txt --- /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 diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy.utils.egg-info/top_level.txt --- /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 diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/__init__.py --- /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__) diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/__init__.py --- /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 +# 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 diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/browser/__init__.py --- /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 @@ +# diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/browser/color.py --- /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 +# 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)) diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/browser/configure.zcml --- /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 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/browser/encoding.py --- /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 +# 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)) diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/browser/templates/color_display.pt --- /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 @@ +
+ +
+ +
\ No newline at end of file diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/browser/templates/color_input.pt --- /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 @@ +
+ +
+ +
\ No newline at end of file diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/catalog/__init__.py --- /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 +# 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) diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/catalog/configure.zcml --- /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 @@ + + + + + + + + \ No newline at end of file diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/catalog/index.py --- /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 +# 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)) diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/configure.zcml --- /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 @@ + + + + + + + + + + + + + + + + + + + + + + diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/date.py --- /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 +# 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 diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/doctests/README.txt --- /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=) + +'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=) + >>> 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=) + +'gmtime' function can be used to convert a datetime to GMT: + + >>> timezone.gmtime(now) + datetime.datetime(2008, 3, 8, 19, 13, 20, tzinfo=) diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/encoding.py --- /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 +# 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) diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/file.py --- /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 +# 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 '' diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/html.py --- /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 +# 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 diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/interfaces.py --- /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 +# 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""" diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/locales/en/LC_MESSAGES/ztfy.utils.mo Binary file src/ztfy/utils/locales/en/LC_MESSAGES/ztfy.utils.mo has changed diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/locales/en/LC_MESSAGES/ztfy.utils.po --- /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 \n" +"Language-Team: French \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" diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/locales/fr/LC_MESSAGES/ztfy.utils.mo Binary file src/ztfy/utils/locales/fr/LC_MESSAGES/ztfy.utils.mo has changed diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/locales/fr/LC_MESSAGES/ztfy.utils.po --- /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 , 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 \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" + diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/locales/ztfy.utils.pot --- /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 \n" +"Language-Team: Zope 3 Developers \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 "" + diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/overrides.zcml --- /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 @@ + + + + + \ No newline at end of file diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/profilehooks.py --- /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 + +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 + diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/property.py --- /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 +# 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 diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/protocol/__init__.py --- /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 @@ +# diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/protocol/http.py --- /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 +# 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) diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/protocol/xmlrpc.py --- /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 +# 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) diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/request.py --- /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 +# 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 diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/schema.py --- /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 +# 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) diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/security.py --- /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 +# 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) diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/session.py --- /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 +# 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) diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/site.py --- /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 +# 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 diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/tal/__init__.py --- /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 @@ +# diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/tal/configure.zcml --- /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 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/tal/fanstatic.py --- /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 +# 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 '' diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/tal/html.py --- /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 +# 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') diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/tal/interfaces.py --- /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 +# 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""" diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/tal/request.py --- /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 +# 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) diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/tal/session.py --- /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 +# 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) diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/tal/text.py --- /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 +# 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) diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/tests/__init__.py --- /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 @@ + diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/tests/test_utilsdocs.py --- /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 +# 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') + diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/tests/test_utilsdocstrings.py --- /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 +# 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') + diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/text.py --- /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 +# 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) diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/timezone/__init__.py --- /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 +# 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) diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/timezone/configure.zcml --- /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 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/timezone/interfaces.py --- /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 +# 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) diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/timezone/schema.py --- /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 +# 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) diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/timezone/utility.py --- /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 +# 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']) diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/traversing.py --- /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 +# 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 diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/unicode.py --- /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 +# 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 diff -r 044dc196ec8a -r d3668ecd9137 src/ztfy/utils/zodb.py --- /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 +# 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.