Commit 35cde2d7 authored by Yusei Tahara's avatar Yusei Tahara
Browse files

import PortalTransforms

git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@24294 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 63a35fb1
DONT USE ChangeLog USE HISTORY.txt instead.
2004-07-24 Christian Heimes <heimes@faho.rwth-aachen.de>
* Changed version to stick to Archetypes version.
2004-05-25 Christian Heimes <heimes@faho.rwth-aachen.de>
* Seperate MimetypesRegistry to a new product
2004-04-20 Christian Heimes <heimes@faho.rwth-aachen.de>
* transforms/rest.py: rest transform is now using the zope implementation if
available
2004-04-07 Christian Heimes <heimes@faho.rwth-aachen.de>
* transforms/text_pre_to_html.py: new transform for preformatted plain text
* transforms/text_to_html.py: changed <br/> to <br />
2004-03-17 Christian Heimes <heimes@faho.rwth-aachen.de>
* transforms/pdf_to_text.py: return text utf-8 encoded
2004-02-04 Sylvain Thénault <syt@logilab.fr>
* transforms/office_com.py: fix wrong import
2003-12-03 Sidnei da Silva <sidnei@awkly.org>
* mime_types/magic.py (guessMime): Don't try to be so magic :)
2003-11-18 Andreas Jung <andreas@andreas-jung.com)
* commandtransform.py: fixed sick cleanDir() implementation
2003-11-17 Andreas Jung <andreas@andreas-jung.com)
* added rtf_to_html.py converter
* added rtf to as mimetypes to mime_types/__init__.py
* added rtf_to_xml.py converter
* added pdf_to_text.py converter
* removed dependency from CMFDefault.utils for misc converters
(integrated code into libtransforms/utils.py)
2003-11-14 Sidnei da Silva <sidnei@plone.org>
* MimeTypesRegistry.py (MimeTypesRegistry.classify): If no results
this far, use magic.py module, written by Jason Petrone, and
updated by Gabriel Wicke with the data from gnome-vfs-mime-magic.
2003-11-07 Sylvain Thénault <syt@logilab.fr>
* use the same license as Archetypes (BSD like instead of GPL)
* www/tr_widgets.zpt: fix bug in the list widget (space before the
parameter's name, making it unrecognized)
* zope/Transform.py: fix set parameters to correctly remap
transform if editable inputs or output. (fix #837244)
* TransformEngine.py: better error messages, a few lines wrapping
* zope/__init__.py: use pt_globals instead of globals for variable
handling the product globals, making it reloadable
* Extensions/Install.py: use pt_globals
* www/listMimeTypes.zpt: use mt/normalized as id instead of mt/name
2003-11-05 Sylvain Thénault <syt@logilab.fr>
* unsafe_tranforms/command.py: added dummy output mime type to avoid
error when added via the ZMI (fix #837252)
2003-10-30 Sylvain Thénault <syt@logilab.fr>
* fixed addMimeType, editMimeType and tr_widget templates (fix #832958)
2003-10-03 Sidnei da Silva <sidnei@dreamcatcher.homeunix.org>
* utils.py (TransformException.getToolByName): Modified
getToolByName to have a fallback mimetypes_registry, so we can
simplify BaseUnit.
2003-09-23 Sylvain Thénault <syt@logilab.fr>
* MimesTypesRegistry.py: make unicode error handling configurable
* zope/MimesTypesTool.py: add a property for unicode error handling
* zope/Transform.py: make tests working
2003-08-19 Sylvain Thénault <syt@logilab.fr>
* transforms/rest.py: override "traceback" setting to avoid
sys.exit !
* transforms/text_to_html.py: use html_quote
2003-08-12 Sylvain Thénault <syt@logilab.fr>
* TransformEngine.py: set "encoding" in datastream metadata if
tranform provides a "output_encoding" attribute. Fix access to
"id" instead of "name()"
* zope/Transform.py: add some code to handle output encoding...
2003-08-08 Sylvain Thénault <syt@logilab.fr>
* MimeTypesRegistry.py: use suffix map has the standard mime types
module, hopefully correct behaviour of classify
* unsafe_transforms/build_transforms.py: fix inputs and output
mime type of ps_to_text transform
2003-08-07 Sylvain Thenault <sylvain.thenault@logilab.fr>
* encoding.py: new module which aims to detect encoding of text
files
* MimeTypesRegistry.py: use the encoding module in iadapter
2003-08-06 Sylvain Thenault <sylvain.thenault@logilab.fr>
* MimeTypesRegistry.py (classify): return
'application/octet-stream' instead of None
* transforms/text_to_html.py: replace '\n' with <br/> instead of
<pre> wrapping
* unsafe_transforms/build_transforms.py: create a ps_to_text
transform if ps2ascii is available
* tests/test_transforms.py: handle name of transforms to test on
command line
* transforms/__init__.py: do not print traceback on missing binary
exception
2003-08-01 Sylvain Thenault <sylvain.thenault@logilab.fr>
* transforms/text_to_html.py: new transform to wrap plain text in
<pre> for html
* transforms/test_transforms.py: add test for text_to_html
2003-07-28 Sylvain Thenault <sylvain.thenault@logilab.fr>
* zope/TransformsChain.py: fixes to make it works within Zope.
* www/editTransformsChain.zpt: add inputs / output information.
2003-07-28 Sylvain Thenault <sylvain.thenault@logilab.fr>
* transforms/rest.py: remove class="document"
* tests/test_transforms.py: added missing output for the identity
transform's test, fix initialize method.
2003-07-21 Sylvain Thenault <sylvain.thenault@logilab.fr>
* transforms/identity.py: added identity transform (used for instance
to convert text/x-rest to text/plain).
* tests/test_transforms.py: added test for the identity transform.
2003-07-11 Sylvain Thenault <sylvain.thenault@logilab.fr>
* unsafe_transforms/xml.py: just make it working.
* unsafe_transforms/command.py: add missing "name" argument to the
constructor. Use popen3 instead of popen4.
* unsafe_transforms/build_transforms.py: create an xml_to_html
transform if an xslt processor is available (however this transform
is not configured for any doctypes / dtds). Create tidy_html
transform if the tidy command is available.
* tests/test_transforms.py: add test cases for the xml and
html_tidy transform.
* transform.py: added transform_customize hook.
* docs/user_manual.rst: explain difference between python distro
and zope product. Added notice about archetypes integration.
* docs/dev_manual.rst: minor fixes.
003-07-10 Sylvain Thenault <sylvain.thenault@logilab.fr>
* refactoring to permit use of this package outside zope :)
Zope mode is triggered when "import Zope" doesn't fail
* fix bug in word_to_html / office_wvware transform
* add a generic test for transforms. It's much more easier now to
add a test for a transform :)
* add licensing information
* interfaces.py: complete / cleanup interfaces
* bin/tranform: add command line tool
* unsafe_transforms/command.py: bug fix
* addTransformsChain.zpt: fix typo
* fix #768927
2003-07-09 Sylvain Thenault <sylvain.thenault@logilab.fr>
* code cleaning:
- moved Transform and TransformsChain in their own files
- removed no more used bindingmixin and sourceAdapter
- merged transform and chain classes together
- generic cache and misc utilities in the new utils.py.
* ready for 1.0 alpha1 :)
2003-07-05 Sylvain Thenault <sylvain.thenault@logilab.fr>
* make the PortalTransforms product from the original transform
package and the mimetypes / transforms tools originaly defined in
Archetypes.
* drop the ability to use it as a standalone python package, since
there was too much duplicated code to make it works.
* some works on tests to make them succeed :)
* MimeTypesTool.py (MimeTypesTool.lookup): return an empty list
instead of None when no matching mime types is found.
2003-05-14 Sidnei da Silva <sidnei@x3ng.com>
* interface.py: Trying to normalize the way interfaces are
imported in different versions of Zope.
2003-04-21 Sidnei da Silva <sidnei@x3ng.com>
* __init__.py: Fixed lots of things here and there to make it work
with the new BaseUnit in Archetypes.
2003-04-20 Sidnei da Silva <sidnei@x3ng.com>
* tests/output/rest3.out: Fixed subtitle and added a test.
2003-04-19 Sidnei da Silva <sidnei@x3ng.com>
* tests/test_rest.py (BaseReSTFileTest.testSame): Added tests
based on input/output dirs to make it easy to add new tests for reST.
* transforms/rest.py (rest.convert): Rendering of
reST was broken. It was not rendering references the right way,
and it didnt seem like it was doing the right thing with
titles. Updated to use docutils.core.publish_string.
* tests/test_all.py (test_suite): Added lynx_dump to transform
html -> text. With tests.
2003-04-18 Sidnei da Silva <sidnei@x3ng.com>
* tests/test_all.py (test_suite): Removed dependencies from
CMFCore on testsuite.
* __init__.py: Made it work without being inside Products. We
eventually need to make a distutils setup, and then this can be
removed. If someone knows a better way to do this, please do.
lynx
pdftohtml
python-docutils
import os
from Products.CMFCore.DirectoryView import addDirectoryViews
from Products.CMFCore.DirectoryView import registerDirectory
from Products.CMFCore.DirectoryView import createDirectoryView
from Products.CMFCore.DirectoryView import manage_listAvailableDirectories
from Products.CMFCore.utils import getToolByName
from Products.CMFCore.utils import minimalpath
from Globals import package_home
from Acquisition import aq_base
from OFS.ObjectManager import BadRequestException
from Products.PortalTransforms import GLOBALS
from Products.PortalTransforms import skins_dir
from StringIO import StringIO
def install(self):
out = StringIO()
qi=getToolByName(self, 'portal_quickinstaller')
qi.installProduct('MimetypesRegistry',)
id = 'portal_transforms'
if hasattr(aq_base(self), id):
pt = getattr(self, id)
if not getattr(aq_base(pt), '_new_style_pt', None) == 1:
print >>out, 'Removing old portal transforms tool'
self.manage_delObjects([id,])
if not hasattr(aq_base(self), id):
addTool = self.manage_addProduct['PortalTransforms'].manage_addTool
addTool('Portal Transforms')
print >>out, 'Installing portal transforms tool'
updateSafeHtml(self, out)
correctMapping(self, out)
# not required right now
# installSkin(self)
return out.getvalue()
def correctMapping(self, out):
pt = getToolByName(self, 'portal_transforms')
pt_ids = pt.objectIds()
for m_in, m_out_dict in pt._mtmap.items():
for m_out, transforms in m_out_dict.items():
for transform in transforms:
if transform.id not in pt_ids:
#error, mapped transform is no object in portal_transforms. correct it!
print >>out, "have to unmap transform (%s) cause its not in portal_transforms ..." % transform.id
try:
pt._unmapTransform(transform)
except:
raise
else:
print >>out, "...ok"
def updateSafeHtml(self, out):
print >>out, 'Update safe_html...'
safe_html_id = 'safe_html'
safe_html_module = "Products.PortalTransforms.transforms.safe_html"
pt = getToolByName(self, 'portal_transforms')
for id in pt.objectIds():
transform = getattr(pt, id)
if transform.id == safe_html_id and transform.module == safe_html_module:
try:
disable_transform = transform.get_parameter_value('disable_transform')
except KeyError:
print >>out, ' replace safe_html (%s, %s) ...' % (transform.name(), transform.module)
try:
pt.unregisterTransform(id)
pt.manage_addTransform(id, safe_html_module)
except:
raise
else:
print >>out, ' ...done'
print >>out, '...done'
def installSkin(self):
skinstool=getToolByName(self, 'portal_skins')
fullProductSkinsPath = os.path.join(package_home(GLOBALS), skins_dir)
productSkinsPath = minimalpath(fullProductSkinsPath)
registered_directories = manage_listAvailableDirectories()
if productSkinsPath not in registered_directories:
registerDirectory(skins_dir, GLOBALS)
try:
addDirectoryViews(skinstool, skins_dir, GLOBALS)
except BadRequestException, e:
pass # directory view has already been added
files = os.listdir(fullProductSkinsPath)
for productSkinName in files:
if os.path.isdir(os.path.join(fullProductSkinsPath, productSkinName)) \
and productSkinName != 'CVS':
for skinName in skinstool.getSkinSelections():
path = skinstool.getSkinPath(skinName)
path = [i.strip() for i in path.split(',')]
try:
if productSkinName not in path:
path.insert(path.index('custom') +1, productSkinName)
except ValueError:
if productSkinName not in path:
path.append(productSkinName)
path = ','.join(path)
skinstool.addSkinSelection(skinName, path)
#poof, you're a product
1.4.0-final - 2006-06-16
========================
* Shut down a noisy logging message to DEBUG level.
[hannosch]
* Converted logging infrastructure from zLOG usage to Python's logging module.
[hannosch]
* Avoid DeprecationWarning for manageAddDelete.
[hannosch]
* Spring-cleaning of tests infrastructure.
[hannosch]
1.4.0-beta1 - 2006-03-26
========================
* removed odd archetypes 1.3 style version checking
[jensens]
* Removed BBB code for CMFCorePermissions import location.
[hannosch]
* removed deprecation-warning for ToolInit
[jensens]
1.3.9-final02 - 2006-01-15
==========================
* nothing - the odd version checking needs a version change to stick to
Archetypes version.
[yenzenz]
1.3.9-RC1 - 2005-12-29
======================
* Fixed [ 1293684 ], unregistered Transforms are not unmaped,
Transformation was deleted from portal_transforms, but remained
active.
http://sourceforge.net/tracker/index.php?func=detail&aid=1293684&group_id=75272&atid=543430
Added a cleanup that unmaps deleted transforms on reinstall
[csenger]
* Replaced the safe_html transformation with a configurable version
with the same functionality. Migration is handled on reinstall.
http://trac.plone.org/plone/ticket/4538
[csenger] [dreamcatcher]
* Removed CoUnInitialize call. According to Mark Hammond: The
right thing to do is call that function, although almost noone
does (including pywin32 itself, which does CoInitialize the main
thread) and I've never heard of problem caused by this
omission.
[sidnei]
* Fix a long outstanding issue with improper COM thread model
initialization. Initialize COM for multi-threading, ignoring any
errors when someone else has already initialized differently.
https://trac.plone.org/plone/ticket/4712
[sidnei]
* Correct some wrong security settings.
[hannosch]
* Fixed the requirements look-up from the policy
(#1358085)
1.3.8-final02 - 2005-10-11
==========================
* nothing - the odd version checking needs a version change to stick to
Archetypes version.
[yenzenz]
1.3.7-final01 - 2005-08-30
==========================
* nothing - the odd version checking needs a version change to stick to
Archetypes version.
[yenzenz]
1.3.6-final02 - 2005-08-07
==========================
* nothing - the odd version checking needs a version change to stick to
Archetypes version.
[yenzenz]
1.3.6-final - 2005-08-01
========================
* Added q to the list of valid and safe html tags by limi's request.
Wrote test for safe_html parsing.
[hannosch]
* Added ins and del to the list of valid and safe html tags.
[ 1199917 ] XHTML DEL tag is removed during the safe_html conversion
[tiran]
1.3.5-final02 - 2005-07-17
==========================
* changed version to stick to appropiate Archetypes Version.
[yenzenz]
1.3.5-final - 2005-07-06
========================
* pdf_to_html can show images now. Revert it to command transformer and
make it work under windows.
[panjunyong]
* refined command based unsafe transform to make it work with windows.
[panjunyong]
* Disabled office_uno by default because it doesn't support multithread yet
[panjunyong]
* Rewrote office_uno to make it work for the recent PyUNO.
[panjunyong]
1.3.4-final01 - 2005-05-20
==========================
* nothing (I hate to write this. But the odd version checking needs it).
[yenzenz]
1.3.4-rc1 - 2005-03-25
======================
* Better error handling for safe html transformation
[tiran]
1.3.3-final - 2005-03-05
========================
* Updated link to rtf converter to http://freshmeat.net/projects/rtfconverter/
[tiran]
* Small fix for the com office converter. COM could crash if word is
invisible. Also a pop up might appeare when quitting word.
[gogo]
* Fixed [ 1053846 ] Charset problem with wvware word_to_html conversion
[flacoste]
* Fixed python and test pre transforms to use html quote special characters.
Thx to stain. [ 1091670 ] Python source code does not escape HTML.
[tiran]
* Fixed [ 1121812 ] fix PortalTransforms unregisterTransformation()
unregisterTransformation() misses to remove from the zodb the persistance
wrapper added to the trasformation
[dan_t]
* Fixed [ 1118739 ] popentransform does not work on windows
[duncanb]
* Fixed [ 1122175 ] extra indnt sytax error in office_uno.py
[ryuuguu]
* fixed bug with some transformers' temp filename: it tried to use original filename
which is encoded in utf8 and may contrain invalid charset for my Windows server.
Just use filename as: unknown.suffix
[panjunyong]
* STX header level is set to 2 instead of using zope.conf. Limi forced me to
change it.
[tiran]
* fixed bug: word_to_html uses office_com under windows
1.3.2-5 - 2004-10-17
====================
* Fixed [ 1041637 ] RichWidget: STX level should be set to 3 instead 1. The
structured text transform is now using the zope.conf option or has an
optional level paramenter in the convert method.
[tiran]
* Added win32api.GetShortPathName to libtransforms/commandtransform
so binaries found in directories which have spaces in their names
will work as expected
[runyaga]
1.3.2-4 - 2004-09-30
====================
* nothing changed
1.3.2-3 - 2004-09-25
====================
* Fixed more unit tests
[tiran]
1.3.2-2 - 2004-09-17
====================
* Fixed [ 1025066 ] Serious persistency bug
[dmaurer]
* Fixed some unit tests failurs. Some unit tests did fail because the reST
and STX output has changed slightly.
[tiran]
* Don't include the first three lines of the lynx output which are url,
title and a blank line. This fixed also a unit test because the url
which was a file in the fs did change every time.
[tiran]
* Fixed a bug in make_unpersistent. It seemed that this method touched values
inside the mapping.
[dreamcatcher]
1.3.2-1 - 2004-09-04
====================
* Disabled filters that were introduced in 1.3.1-1. The currently used
transform path algo is broken took too long to find a path.
[tiran]
* Cleaned up major parts of PT by removing the python only implementation which
was broken anyway
* Fixed [ 1019632 ] current svn bundle (rev 2942) broken
1.3.1-1 - 2004-08-16
====================
* Introduce the concept of filters (one-hop transforms where the source and
destination are the same mimetype).
[dreamcatcher]
* Add a html filter to extract the content of the body tag (so we don't get a
double <body> when uploading full html files).
[dreamcatcher]
* Change base class for Transform to SimpleItem which is equivalent to the
previous base classes and provides a nice __repr__.
[dreamcatcher]
* Lower log levels.
[dreamcatcher]
* cache.py: Added purgeCache, fixed has cache test.
[tiran]
* Fixed non critical typo in error message: Unvalid -> Invalid
[tirna]
1.3.0-3 - 2004-08-06
====================
* Added context to the convert, convertTo and __call__ methods. The context is
the object on which the transform was called.
[tiran]
* Added isCacheable flag and setCacheable to idatastream (data.py). Now you can
disable the caching of the result of a transformation.
[tiran]
* Added __setstate__ to load new transformations from the file system.
[tiran]
* Fixed [ 1002014 ] Add policy screen doesn't accept single entry
[tiran]
1.3.0-2 - 2004-07-29
====================
* Added workaround for [ 997998 ] PT breaks ZMI/Find [tiran]
Copyright (c) 2002-2003, Benjamin Saller <bcsaller@ideasuite.com>, and
the respective authors.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Archetypes nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
include ChangeLog
include README
include TODO
include version.txt
include bin/transform
include bin/transform.bat
recursive-include docs *.txt
recursive-include docs *.rst
recursive-include docs *.html
recursive-include tests/input *
recursive-include tests/output *
NAME=PortalTransforms
MAJOR_VERSION=1.0
MINOR_VERSION=4
RELEASE_TAG=
PACKAGE_NAME=${NAME}-${MAJOR_VERSION}.${MINOR_VERSION}${RELEASE_TAG}
PYTHON="/usr/bin/python"
TMPDIR=~/tmp
CURDIR=~/src/archetypes/head/PortalTransforms
BASE_DIR=${CURDIR}/..
SOFTWARE_HOME=~/src/zope/2_7/lib/python
INSTANCE_HOME=~/src/instance/shellex
PACKAGES=PortalTransforms
RM=rm -f
RMRF=rm -rf
FIND=find
XARGS=xargs
CD=cd
LN=ln -sfn
CP=cp
TAR=tar
MKDIR=mkdir -p
.PHONY : clean test reindent reindent_clean sdist
.PHONY : default
# default: The default step (invoked when make is called without a target)
default: clean test
clean :
find . \( -name '*~' -o -name '*.py[co]' -o -name '*.bak' \) -exec rm {} \; -print
reindent :
~/src/reindent.py -r -v .
test :
export INSTANCE_HOME=${INSTANCE_HOME}; export SOFTWARE_HOME=${SOFTWARE_HOME}; \
cd ${CURDIR}/tests && ${PYTHON} runalltests.py
# sdist: Create a source distribution file (implies clean).
#
sdist: reindent clean sdist_tgz
# sdist_tgz: Create a tgz archive file as a source distribution.
#
sdist_tgz:
echo -n "${MAJOR_VERSION}.${MINOR_VERSION}${RELEASE_TAG}" >\
${CURDIR}/version.txt
${MKDIR} ${TMPDIR}/${PACKAGE_NAME}
${CD} ${TMPDIR}/${PACKAGE_NAME} && \
for package in ${PACKAGES}; do ${LN} ${BASE_DIR}/$$package .; done && \
${CD} ${TMPDIR} && ${TAR} czfh ${BASE_DIR}/${PACKAGE_NAME}.tgz ${PACKAGE_NAME} \
--exclude=${PACKAGE_NAME}.tgz\
--exclude=CVS \
--exclude=.cvsignore \
--exclude=makefile \
--exclude=Makefile \
--exclude=*.pyc \
--exclude=TAGS \
--exclude=*~ \
--exclude=.#*
${RMRF} ${TMPDIR}/${PACKAGE_NAME}
Portal Transforms
=================
This Zope product provides two new tools for the CMF in order to make MIME
types based transformations on the portal contents, and so an easy to way to
plugin some new transformations for previously unsupported content types. The
provided tools are :
* portal_transform (the transform tool) : handle transformation of data from a
mime type to another
A bunch of ready to use transformations are also provided. Look at the
documentation for more information.
Notice this package can also be used as a standalone Python package. If
you've downloaded the Python distribution, you can't make it a Zope
product since Zope files have been removed from this distribution.
This product is an off-spring of the Archetypes project.
Installation
------------
WARNING : The two installation methods may conflict, choose the one adapted to
your need.
Zope
````
* Put this package in your Zope's Products directory and restart Zope
* either use the QuickInstaller to add this product to your CMF site or add an
external method to the root of your CMF site with the following information :
:module: PortalTransforms.Install
:method: install
and click the test tab to run it.
Python
``````
* Extract the tarball
* Run "python setup.py install". See "python setup.py install --help" for
installation options.
* That's it, you should have the library and the *transform* command line tool
installed.
Documentation
-------------
See the *docs* directory in this package.
Mailing-list
------------
Discussion about this products occurs to the archetypes mailing list :
http://sourceforge.net/mail/?group_id=75272
or on the #plone channel of irc.freenode.net.
Authors
-------
Benjamin Saller <bcsaller@yahoo.com>
Sidnei da Silva <sidnei@x3ng.com>
Sylvain Thénault <sylvain.thenault@logilab.fr>
wv
xsltproc
tidy
unrtf
ppthtml
xlhtml
gs-common
TODO list for the Portal Transforms product
-------------------------------------------
* enhance unsafe_transforms/build_transforms to provide a bunch of
transformations using command/xml with various configuration
* iencoding_classifier ?
* make more transforms :)
from logging import ERROR
from UserDict import UserDict
from Products.PageTemplates.PageTemplateFile import PageTemplateFile
from Globals import InitializeClass
from Globals import PersistentMapping
try:
from ZODB.PersistentList import PersistentList
except ImportError:
from persistent.list import PersistentList
from OFS.SimpleItem import SimpleItem
from AccessControl import ClassSecurityInfo
from Products.CMFCore.permissions import ManagePortal
from Products.CMFCore.utils import getToolByName
from Products.PortalTransforms.interfaces import itransform
from Products.PortalTransforms.utils import TransformException, log, _www
from Products.PortalTransforms.transforms.broken import BrokenTransform
__revision__ = '$Id: Transform.py 6255 2006-04-11 15:29:29Z hannosch $'
def import_from_name(module_name):
""" import and return a module by its name """
__traceback_info__ = (module_name, )
m = __import__(module_name)
try:
for sub in module_name.split('.')[1:]:
m = getattr(m, sub)
except AttributeError, e:
raise ImportError(str(e))
return m
def make_config_persistent(kwargs):
""" iterates on the given dictionnary and replace list by persistent list,
dictionary by persistent mapping.
"""
for key, value in kwargs.items():
if type(value) == type({}):
p_value = PersistentMapping(value)
kwargs[key] = p_value
elif type(value) in (type(()), type([])):
p_value = PersistentList(value)
kwargs[key] = p_value
def make_config_nonpersistent(kwargs):
""" iterates on the given dictionary and replace ListClass by python List,
and DictClass by python Dict
"""
for key, value in kwargs.items():
if isinstance(value, PersistentMapping):
p_value = dict(value)
kwargs[key] = p_value
elif isinstance(value, PersistentList):
p_value = list(value)
kwargs[key] = p_value
VALIDATORS = {
'int' : int,
'string' : str,
'list' : PersistentList,
'dict' : PersistentMapping,
}
class Transform(SimpleItem):
"""A transform is an external method with
additional configuration information
"""
__implements__ = itransform
meta_type = 'Transform'
meta_types = all_meta_types = ()
manage_options = (
({'label':'Configure',
'action':'manage_main'},
{'label':'Reload',
'action':'manage_reloadTransform'},) +
SimpleItem.manage_options
)
manage_main = PageTemplateFile('configureTransform', _www)
manage_reloadTransform = PageTemplateFile('reloadTransform', _www)
tr_widgets = PageTemplateFile('tr_widgets', _www)
security = ClassSecurityInfo()
__allow_access_to_unprotected_subobjects__ = 1
def __init__(self, id, module, transform=None):
self.id = id
self.module = module
# DM 2004-09-09: 'Transform' instances are stored as
# part of a module level configuration structure
# Therefore, they must not contain persistent objects
self._config = UserDict()
self._config.__allow_access_to_unprotected_subobjects__ = 1
self._config_metadata = UserDict()
self._tr_init(1, transform)
def __setstate__(self, state):
""" __setstate__ is called whenever the instance is loaded
from the ZODB, like when Zope is restarted.
We should reload the wrapped transform at this time
"""
Transform.inheritedAttribute('__setstate__')(self, state)
self._tr_init()
def _tr_init(self, set_conf=0, transform=None):
""" initialize the zope transform by loading the wrapped transform """
__traceback_info__ = (self.module, )
if transform is None:
transform = self._load_transform()
else:
self._v_transform = transform
# check this is a valid transform
if not hasattr(transform, '__class__'):
raise TransformException('Invalid transform : transform is not a class')
if not itransform.isImplementedBy(transform):
raise TransformException('Invalid transform : itransform is not implemented by %s' % transform.__class__)
if not hasattr(transform, 'inputs'):
raise TransformException('Invalid transform : missing required "inputs" attribute')
if not hasattr(transform, 'output'):
raise TransformException('Invalid transform : missing required "output" attribute')
# manage configuration
if set_conf and hasattr(transform, 'config'):
conf = dict(transform.config)
self._config.update(conf)
make_config_persistent(self._config)
if hasattr(transform, 'config_metadata'):
conf = dict(transform.config_metadata)
self._config_metadata.update(conf)
make_config_persistent(self._config_metadata)
transform.config = dict(self._config)
make_config_nonpersistent(transform.config)
transform.config_metadata = dict(self._config_metadata)
make_config_nonpersistent(transform.config_metadata)
self.inputs = transform.inputs
self.output = transform.output
self.output_encoding = getattr(transform, 'output_encoding', None)
return transform
def _load_transform(self):
m = import_from_name(self.module)
if not hasattr(m, 'register'):
msg = 'Invalid transform module %s: no register function defined' % self.module
raise TransformException(msg)
try:
transform = m.register()
except Exception, err:
transform = BrokenTransform(self.id, self.module, err)
msg = "Cannot register transform %s, using BrokenTransform: Error\n %s" % (self.id, err)
self.title = 'BROKEN'
log(msg, severity=ERROR)
else:
self.title = ''
self._v_transform = transform
return transform
security.declarePrivate('manage_beforeDelete')
def manage_beforeDelete(self, item, container):
SimpleItem.manage_beforeDelete(self, item, container)
if self is item:
# unregister self from catalog on deletion
# While building business template, transform tool will be
# copied and this transform will be removed from copied one
# so that this must not use getToolByName to retrive the tool.
tr_tool = self.aq_parent
tr_tool._unmapTransform(self)
security.declarePublic('get_documentation')
def get_documentation(self):
""" return transform documentation """
if not hasattr(self, '_v_transform'):
self._load_transform()
return self._v_transform.__doc__
security.declarePublic('get_documentation')
def convert(self, *args, **kwargs):
""" return apply the transform and return the result """
if not hasattr(self, '_v_transform'):
self._load_transform()
return self._v_transform.convert(*args, **kwargs)
security.declarePublic('name')
def name(self):
"""return the name of the transform instance"""
return self.id
security.declareProtected(ManagePortal, 'get_parameters')
def get_parameters(self):
""" get transform's parameters names """
if not hasattr(self, '_v_transform'):
self._load_transform()
keys = self._v_transform.config.keys()
keys.sort()
return keys
security.declareProtected(ManagePortal, 'get_parameter_value')
def get_parameter_value(self, key):
""" get value of a transform's parameter """
value = self._config[key]
type = self.get_parameter_infos(key)[0]
if type == 'dict':
result = {}
for key, val in value.items():
result[key] = val
elif type == 'list':
result = list(value)
else:
result = value
return result
security.declareProtected(ManagePortal, 'get_parameter_infos')
def get_parameter_infos(self, key):
""" get informations about a parameter
return a tuple (type, label, description [, type specific data])
where type in (string, int, list, dict)
label and description are two string describing the field
there may be some additional elements specific to the type :
(key label, value label) for the dict type
"""
try:
return tuple(self._config_metadata[key])
except KeyError:
return 'string', '', ''
security.declareProtected(ManagePortal, 'set_parameters')
def set_parameters(self, REQUEST=None, **kwargs):
""" set transform's parameters """
if not kwargs:
kwargs = REQUEST.form
self.preprocess_param(kwargs)
for param, value in kwargs.items():
try:
self.get_parameter_value(param)
except KeyError:
log('Warning: ignored parameter %r' % param)
continue
meta = self.get_parameter_infos(param)
self._config[param] = VALIDATORS[meta[0]](value)
tr_tool = getToolByName(self, 'portal_transforms')
# need to remap transform if necessary (i.e. configurable inputs / output)
if kwargs.has_key('inputs') or kwargs.has_key('output'):
tr_tool._unmapTransform(self)
if not hasattr(self, '_v_transform'):
self._load_transform()
self.inputs = kwargs.get('inputs', self._v_transform.inputs)
self.output = kwargs.get('output', self._v_transform.output)
tr_tool._mapTransform(self)
# track output encoding
if kwargs.has_key('output_encoding'):
self.output_encoding = kwargs['output_encoding']
if REQUEST is not None:
REQUEST['RESPONSE'].redirect(tr_tool.absolute_url()+'/manage_main')
security.declareProtected(ManagePortal, 'reload')
def reload(self):
""" reload the module where the transformation class is defined """
log('Reloading transform %s' % self.module)
m = import_from_name(self.module)
reload(m)
self._tr_init()
def preprocess_param(self, kwargs):
""" preprocess param fetched from an http post to handle optional dictionary
"""
for param in self.get_parameters():
if self.get_parameter_infos(param)[0] == 'dict':
try:
keys = kwargs[param + '_key']
del kwargs[param + '_key']
except:
keys = ()
try:
values = kwargs[param + '_value']
del kwargs[param + '_value']
except:
values = ()
kwargs[param] = dict = {}
for key, value in zip(keys, values):
key = key.strip()
if key:
value = value.strip()
if value:
dict[key] = value
InitializeClass(Transform)
from AccessControl.Role import RoleManager
from AccessControl import ClassSecurityInfo
from Acquisition import Implicit
from Acquisition import aq_parent
from Acquisition import aq_base
from Globals import Persistent
from Globals import InitializeClass
from Globals import PersistentMapping
try:
from ZODB.PersistentList import PersistentList
except ImportError:
from persistent.list import PersistentList
from OFS.Folder import Folder
from OFS.SimpleItem import Item
from Products.PageTemplates.PageTemplateFile import PageTemplateFile
from Products.CMFCore.ActionProviderBase import ActionProviderBase
from Products.CMFCore.permissions import ManagePortal, View
from Products.CMFCore.utils import UniqueObject
from Products.CMFCore.utils import getToolByName
from Products.PortalTransforms.libtransforms.utils import MissingBinary
from Products.PortalTransforms import transforms
from Products.PortalTransforms.interfaces import iengine
from Products.PortalTransforms.interfaces import idatastream
from Products.PortalTransforms.interfaces import itransform
from Products.PortalTransforms.data import datastream
from Products.PortalTransforms.chain import TransformsChain
from Products.PortalTransforms.chain import chain
from Products.PortalTransforms.cache import Cache
from Products.PortalTransforms.Transform import Transform
from Products.PortalTransforms.utils import log
from Products.PortalTransforms.utils import TransformException
from Products.PortalTransforms.utils import BadRequest
from Products.PortalTransforms.utils import _www
__revision__ = '$Id: TransformEngine.py 6255 2006-04-11 15:29:29Z hannosch $'
from logging import DEBUG
class TransformTool(UniqueObject, ActionProviderBase, Folder):
id = 'portal_transforms'
meta_type = id.title().replace('_', ' ')
isPrincipiaFolderish = 1 # Show up in the ZMI
__implements__ = iengine
meta_types = all_meta_types = (
{ 'name' : 'Transform',
'action' : 'manage_addTransformForm'},
{ 'name' : 'TransformsChain',
'action' : 'manage_addTransformsChainForm'},
)
manage_addTransformForm = PageTemplateFile('addTransform', _www)
manage_addTransformsChainForm = PageTemplateFile('addTransformsChain', _www)
manage_cacheForm = PageTemplateFile('setCacheTime', _www)
manage_editTransformationPolicyForm = PageTemplateFile('editTransformationPolicy', _www)
manage_reloadAllTransforms = PageTemplateFile('reloadAllTransforms', _www)
manage_options = ((Folder.manage_options[0],) + Folder.manage_options[2:] +
(
{ 'label' : 'Caches',
'action' : 'manage_cacheForm'},
{ 'label' : 'Policy',
'action' : 'manage_editTransformationPolicyForm'},
{ 'label' : 'Reload transforms',
'action' : 'manage_reloadAllTransforms'},
)
)
security = ClassSecurityInfo()
def __init__(self, policies=None, max_sec_in_cache=3600):
self._mtmap = PersistentMapping()
self._policies = policies or PersistentMapping()
self.max_sec_in_cache = max_sec_in_cache
self._new_style_pt = 1
# mimetype oriented conversions (iengine interface) ########################
def unregisterTransform(self, name):
""" unregister a transform
name is the name of a registered transform
"""
self._unmapTransform(getattr(self, name))
if name in self.objectIds():
self._delObject(name)
def convertTo(self, target_mimetype, orig, data=None, object=None,
usedby=None, context=None, **kwargs):
"""Convert orig to a given mimetype
* orig is an encoded string
* data an optional idatastream object. If None a new datastream will be
created and returned
* optional object argument is the object on which is bound the data.
If present that object will be used by the engine to bound cached data.
* additional arguments (kwargs) will be passed to the transformations.
Some usual arguments are : filename, mimetype, encoding
return an object implementing idatastream or None if no path has been
found.
"""
target_mimetype = str(target_mimetype)
if object is not None:
cache = Cache(object)
data = cache.getCache(target_mimetype)
if data is not None:
time, data = data
if self.max_sec_in_cache == 0 or time < self.max_sec_in_cache:
return data
if data is None:
data = self._wrap(target_mimetype)
registry = getToolByName(self, 'mimetypes_registry')
if not getattr(aq_base(registry), 'classify', None):
# avoid problems when importing a site with an old mimetype registry
# XXX return None or orig?
return None
orig_mt = registry.classify(orig,
mimetype=kwargs.get('mimetype'),
filename=kwargs.get('filename'))
orig_mt = str(orig_mt)
if not orig_mt:
log('Unable to guess input mime type (filename=%s, mimetype=%s)' %(
kwargs.get('mimetype'), kwargs.get('filename')), severity=DEBUG)
return None
target_mt = registry.lookup(target_mimetype)
if target_mt:
target_mt = target_mt[0]
else:
log('Unable to match target mime type %s'% str(target_mimetype),
severity=DEBUG)
return None
## fastpath
# If orig_mt and target_mt are the same, we only allow
# a one-hop transform, a.k.a. filter.
# XXX disabled filtering for now
filter_only = False
if orig_mt == str(target_mt):
filter_only = True
data.setData(orig)
md = data.getMetadata()
md['mimetype'] = str(orig_mt)
if object is not None:
cache.setCache(str(target_mimetype), data)
return data
## get a path to output mime type
requirements = self._policies.get(str(target_mt), [])
path = self._findPath(orig_mt, target_mt, list(requirements))
if not path and requirements:
log('Unable to satisfy requirements %s' % ', '.join(requirements),
severity=DEBUG)
path = self._findPath(orig_mt, target_mt)
if not path:
log('NO PATH FROM %s TO %s : %s' % (orig_mt, target_mimetype, path),
severity=DEBUG)
return None #XXX raise TransformError
if len(path) > 1:
## create a chain on the fly (sly)
transform = chain()
for t in path:
transform.registerTransform(t)
else:
transform = path[0]
result = transform.convert(orig, data, context=context, usedby=usedby, **kwargs)
assert(idatastream.isImplementedBy(result),
'result doesn\'t implemented idatastream')
self._setMetaData(result, transform)
# set cache if possible
if object is not None and result.isCacheable():
cache.setCache(str(target_mimetype), result)
# return idatastream object
return result
security.declarePublic('convertToData')
def convertToData(self, target_mimetype, orig, data=None, object=None,
usedby=None, context=None, **kwargs):
"""Convert to a given mimetype and return the raw data
ignoring subobjects. see convertTo for more information
"""
data =self.convertTo(target_mimetype, orig, data, object, usedby,
context, **kwargs)
if data:
return data.getData()
return None
security.declarePublic('convert')
def convert(self, name, orig, data=None, context=None, **kwargs):
"""run a tranform of a given name on data
* name is the name of a registered transform
see convertTo docstring for more info
"""
if not data:
data = self._wrap(name)
try:
transform = getattr(self, name)
except AttributeError:
raise Exception('No such transform "%s"' % name)
data = transform.convert(orig, data, context=context, **kwargs)
self._setMetaData(data, transform)
return data
def __call__(self, name, orig, data=None, context=None, **kwargs):
"""run a transform by its name, returning the raw data product
* name is the name of a registered transform.
return an encoded string.
see convert docstring for more info on additional arguments.
"""
data = self.convert(name, orig, data, context, **kwargs)
return data.getData()
# utilities ###############################################################
def _setMetaData(self, datastream, transform):
"""set metadata on datastream according to the given transform
(mime type and optionaly encoding)
"""
md = datastream.getMetadata()
if hasattr(transform, 'output_encoding'):
md['encoding'] = transform.output_encoding
md['mimetype'] = transform.output
def _wrap(self, name):
"""wrap a data object in an icache"""
return datastream(name)
def _unwrap(self, data):
"""unwrap data from an icache"""
if idatastream.isImplementedBy(data):
data = data.getData()
return data
def _mapTransform(self, transform):
"""map transform to internal structures"""
registry = getToolByName(self, 'mimetypes_registry')
inputs = getattr(transform, 'inputs', None)
if not inputs:
raise TransformException('Bad transform %s : no input MIME type' %
(transform))
for i in inputs:
mts = registry.lookup(i)
if not mts:
msg = 'Input MIME type %r for transform %s is not registered '\
'in the MIME types registry' % (i, transform.name())
raise TransformException(msg)
for mti in mts:
for mt in mti.mimetypes:
mt_in = self._mtmap.setdefault(mt, PersistentMapping())
output = getattr(transform, 'output', None)
if not output:
msg = 'Bad transform %s : no output MIME type'
raise TransformException(msg % transform.name())
mto = registry.lookup(output)
if not mto:
msg = 'Output MIME type %r for transform %s is not '\
'registered in the MIME types registry' % \
(output, transform.name())
raise TransformException(msg)
if len(mto) > 1:
msg = 'Wildcarding not allowed in transform\'s output '\
'MIME type'
raise TransformException(msg)
for mt2 in mto[0].mimetypes:
try:
if not transform in mt_in[mt2]:
mt_in[mt2].append(transform)
except KeyError:
mt_in[mt2] = PersistentList([transform])
def _unmapTransform(self, transform):
"""unmap transform from internal structures"""
registry = getToolByName(self, 'mimetypes_registry')
for i in transform.inputs:
for mti in registry.lookup(i):
for mt in mti.mimetypes:
mt_in = self._mtmap.get(mt, {})
output = transform.output
mto = registry.lookup(output)
for mt2 in mto[0].mimetypes:
l = mt_in[mt2]
for i in range(len(l)):
if transform.name() == l[i].name():
l.pop(i)
break
else:
log('Can\'t find transform %s from %s to %s' % (
transform.name(), mti, mt),
severity=DEBUG)
def _findPath(self, orig, target, required_transforms=()):
"""return the shortest path for transformation from orig mimetype to
target mimetype
"""
path = []
if not self._mtmap:
return None
# naive algorithm :
# find all possible paths with required transforms
# take the shortest
#
# it should be enough since we should not have so much possible paths
shortest, winner = 9999, None
for path in self._getPaths(str(orig), str(target), required_transforms):
if len(path) < shortest:
winner = path
shortest = len(path)
return winner
def _getPaths(self, orig, target, requirements, path=None, result=None):
"""return a all path for transformation from orig mimetype to
target mimetype
"""
if path is None:
result = []
path = []
requirements = list(requirements)
outputs = self._mtmap.get(orig)
if outputs is None:
return result
path.append(None)
for o_mt, transforms in outputs.items():
for transform in transforms:
required = 0
name = transform.name()
if name in requirements:
requirements.remove(name)
required = 1
if transform in path:
# avoid infinite loop...
continue
path[-1] = transform
if o_mt == target:
if not requirements:
result.append(path[:])
else:
self._getPaths(o_mt, target, requirements, path, result)
if required:
requirements.append(name)
path.pop()
return result
security.declarePrivate('manage_afterAdd')
def manage_afterAdd(self, item, container):
""" overload manage_afterAdd to finish initialization when the
transform tool is added
"""
Folder.manage_afterAdd(self, item, container)
transforms.initialize(self)
# XXX required?
#try:
# # first initialization
# transforms.initialize(self)
#except:
# # may fail on copy
# pass
security.declareProtected(ManagePortal, 'manage_addTransform')
def manage_addTransform(self, id, module, REQUEST=None):
""" add a new transform to the tool """
transform = Transform(id, module)
self._setObject(id, transform)
self._mapTransform(transform)
if REQUEST is not None:
REQUEST['RESPONSE'].redirect(self.absolute_url()+'/manage_main')
security.declareProtected(ManagePortal, 'manage_addTransform')
def manage_addTransformsChain(self, id, description, REQUEST=None):
""" add a new transform to the tool """
transform = TransformsChain(id, description)
self._setObject(id, transform)
self._mapTransform(transform)
if REQUEST is not None:
REQUEST['RESPONSE'].redirect(self.absolute_url()+'/manage_main')
security.declareProtected(ManagePortal, 'manage_addTransform')
def manage_setCacheValidityTime(self, seconds, REQUEST=None):
"""set the lifetime of cached data in seconds"""
self.max_sec_in_cache = int(seconds)
if REQUEST is not None:
REQUEST['RESPONSE'].redirect(self.absolute_url()+'/manage_main')
security.declareProtected(ManagePortal, 'reloadTransforms')
def reloadTransforms(self, ids=()):
""" reload transforms with the given ids
if no ids, reload all registered transforms
return a list of (transform_id, transform_module) describing reloaded
transforms
"""
if not ids:
ids = self.objectIds()
reloaded = []
for id in ids:
o = getattr(self, id)
o.reload()
reloaded.append((id, o.module))
return reloaded
# Policy handling methods #################################################
def manage_addPolicy(self, output_mimetype, required_transforms, REQUEST=None):
""" add a policy for a given output mime types"""
registry = getToolByName(self, 'mimetypes_registry')
if not registry.lookup(output_mimetype):
raise TransformException('Unknown MIME type')
if self._policies.has_key(output_mimetype):
msg = 'A policy for output %s is yet defined' % output_mimetype
raise TransformException(msg)
required_transforms = tuple(required_transforms)
self._policies[output_mimetype] = required_transforms
if REQUEST is not None:
REQUEST['RESPONSE'].redirect(self.absolute_url()+'/manage_editTransformationPolicyForm')
def manage_delPolicies(self, outputs, REQUEST=None):
""" remove policies for given output mime types"""
for mimetype in outputs:
del self._policies[mimetype]
if REQUEST is not None:
REQUEST['RESPONSE'].redirect(self.absolute_url()+'/manage_editTransformationPolicyForm')
def listPolicies(self):
""" return the list of defined policies
a policy is a 2-uple (output_mime_type, [list of required transforms])
"""
# XXXFIXME: backward compat, should be removed latter
if not hasattr(self, '_policies'):
self._policies = PersistentMapping()
return self._policies.items()
# mimetype oriented conversions (iengine interface) ########################
def registerTransform(self, transform):
"""register a new transform
transform isn't a Zope Transform (the wrapper) but the wrapped transform
the persistence wrapper will be created here
"""
# needed when call from transform.transforms.initialize which
# register non zope transform
module = str(transform.__module__)
transform = Transform(transform.name(), module, transform)
if not itransform.isImplementedBy(transform):
raise TransformException('%s does not implement itransform' % transform)
name = transform.name()
__traceback_info__ = (name, transform)
if name not in self.objectIds():
self._setObject(name, transform)
self._mapTransform(transform)
security.declareProtected(ManagePortal, 'ZopeFind')
def ZopeFind(self, *args, **kwargs):
"""Don't break ZopeFind feature when a transform can't be loaded
"""
try:
return Folder.ZopeFind(self, *args, **kwargs)
except MissingBinary:
log('ZopeFind: catched MissingBinary exception')
security.declareProtected(View, 'objectItems')
def objectItems(self, *args, **kwargs):
"""Don't break ZopeFind feature when a transform can't be loaded
"""
try:
return Folder.objectItems(self, *args, **kwargs)
except MissingBinary:
log('objectItems: catched MissingBinary exception')
return []
InitializeClass(TransformTool)
from Products.PortalTransforms.TransformEngine import TransformTool
"""FIXME: backward compat, remove later
"""
from Products.PortalTransforms.chain import Chain as TransformsChain
import os.path
__version__ = open(os.path.join(__path__[0], 'version.txt')).read().strip()
from Products.PortalTransforms.utils import skins_dir
from Products.PortalTransforms.TransformEngine import TransformTool
GLOBALS = globals()
PKG_NAME = 'PortalTransforms'
tools = (
TransformTool,
)
# XXX backward compatibility tricks to make old PortalTransform based Mimetypes
# running (required)
import sys
this_module = sys.modules[__name__]
from Products.MimetypesRegistry import mime_types
setattr(this_module, 'mime_types', mime_types)
from Products.MimetypesRegistry import MimeTypeItem
setattr(this_module, 'MimeTypeItem', MimeTypeItem)
from Products.MimetypesRegistry import MimeTypeItem
sys.modules['Products.PortalTransforms.zope.MimeTypeItem'] = MimeTypeItem
def initialize(context):
from Products.CMFCore.DirectoryView import registerDirectory
#registerDirectory(skins_dir, GLOBALS)
from Products.CMFCore import utils
utils.ToolInit("%s Tool" % PKG_NAME,
tools=tools,
icon="tool.gif",
).initialize(context)
from Products import PortalTransforms as PRODUCT
import os.path
version=PRODUCT.__version__
modname=PRODUCT.__name__
# (major, minor, patchlevel, release info) where release info is:
# -99 for alpha, -49 for beta, -19 for rc and 0 for final
# increment the release info number by one e.g. -98 for alpha2
major, minor, bugfix = version.split('.')[:3]
bugfix, release = bugfix.split('-')[:2]
relinfo=-99 #alpha
if 'beta' in release:
relinfo=-49
if 'rc' in release:
relinfo=-19
if 'final' in release:
relinfo=0
numversion = (int(major), int(minor), int(bugfix), relinfo)
license = 'BSD like'
license_text = open(os.path.join(PRODUCT.__path__[0], 'LICENSE.txt')).read()
copyright = '''Copyright (c) 2003 LOGILAB S.A. (Paris, FRANCE)'''
author = "Archetypes developement team"
author_email = "archetypes-devel@lists.sourceforge.net"
short_desc = "MIME types based transformations for the CMF"
long_desc = """This package provides two new CMF tools in order to
make MIME types based transformations on the portal contents and so an
easy to way to plugin some new transformations for previously
unsupported content types. You will find more info in the package's
README and docs directory.
.
It's part of the Archetypes project, but the only requirement to use it
is to have a CMF based site. If you are using Archetypes, this package
replaces the transform package.
.
Notice this package can also be used as a standalone Python package. If
you've downloaded the Python distribution, you can't make it a Zope
product since Zope files have been removed from this distribution.
"""
web = "http://plone.org/products/archetypes"
ftp = ""
mailing_list = "archetypes-devel@lists.sourceforge.net"
debian_name = "zope-cmftransforms"
debian_maintainer = "Sylvain Thenault"
debian_maintainer_email = "sylvain.thenault@logilab.fr"
debian_handler = "zope"
<configure
xmlns="http://namespaces.zope.org/five"
>
<bridge
zope2=".interfaces.idatastream"
package=".z3.interfaces"
name="IDataStream"
/>
<bridge
zope2=".interfaces.itransform"
package=".z3.interfaces"
name="ITransform"
/>
<bridge
zope2=".interfaces.ichain"
package=".z3.interfaces"
name="IChain"
/>
<bridge
zope2=".interfaces.iengine"
package=".z3.interfaces"
name="IEngine"
/>
</configure>
"""Cache
"""
from time import time
from Acquisition import aq_base
class Cache:
def __init__(self, context, _id='_v_transform_cache'):
self.context = context
self._id =_id
def _genCacheKey(self, identifier, *args):
key = identifier
for arg in args:
key = '%s_%s' % (key, arg)
key = key.replace('/', '_')
key = key.replace('+', '_')
key = key.replace('-', '_')
key = key.replace(' ', '_')
return key
def setCache(self, key, value):
"""cache a value indexed by key"""
if not value.isCacheable():
return
context = self.context
key = self._genCacheKey(key)
if getattr(aq_base(context), self._id, None) is None:
setattr(context, self._id, {})
getattr(context, self._id)[key] = (time(), value)
return key
def getCache(self, key):
"""try to get a cached value for key
return None if not present
else return a tuple (time spent in cache, value)
"""
context = self.context
key = self._genCacheKey(key)
dict = getattr(context, self._id, None)
if dict is None :
return None
try:
orig_time, value = dict.get(key, None)
return time() - orig_time, value
except TypeError:
return None
def purgeCache(self, key=None):
"""Remove cache
"""
context = self.context
id = self._id
if not shasattr(context, id):
return
if key is None:
delattr(context, id)
else:
cache = getattr(context, id)
key = self._genCacheKey(key)
if cache.has_key(key):
del cache[key]
from Products.PageTemplates.PageTemplateFile import PageTemplateFile
from Globals import Persistent
from Globals import InitializeClass
from Acquisition import Implicit
from OFS.SimpleItem import Item
from AccessControl.Role import RoleManager
from AccessControl import ClassSecurityInfo
from Products.CMFCore.permissions import ManagePortal, ManageProperties
from Products.CMFCore.utils import getToolByName
from Products.PortalTransforms.utils import TransformException, _www
from Products.PortalTransforms.interfaces import ichain
from Products.PortalTransforms.interfaces import itransform
from UserList import UserList
class chain(UserList):
"""A chain of transforms used to transform data"""
__implements__ = (ichain, itransform)
def __init__(self, name='',*args):
UserList.__init__(self, *args)
self.__name__ = name
if args:
self._update()
def name(self):
return self.__name__
def registerTransform(self, transform):
self.append(transform)
def unregisterTransform(self, name):
for i in range(len(self)):
tr = self[i]
if tr.name() == name:
self.pop(i)
break
else:
raise Exception('No transform named %s registered' % name)
def convert(self, orig, data, **kwargs):
for transform in self:
data = transform.convert(orig, data, **kwargs)
orig = data.getData()
md = data.getMetadata()
md['mimetype'] = self.output
return data
def __setitem__(self, key, value):
UserList.__setitem__(self, key, value)
self._update()
def append(self, value):
UserList.append(self, value)
self._update()
def insert(self, *args):
UserList.insert(*args)
self._update()
def remove(self, *args):
UserList.remove(*args)
self._update()
def pop(self, *args):
UserList.pop(*args)
self._update()
def _update(self):
self.inputs = self[0].inputs
self.output = self[-1].output
for i in range(len(self)):
if hasattr(self[-i-1], 'output_encoding'):
self.output_encoding = self[-i-1].output_encoding
break
else:
try:
del self.output_encoding
except:
pass
class TransformsChain(Implicit, Item, RoleManager, Persistent):
""" a transforms chain is suite of transforms to apply in order.
It follows the transform API so that a chain is itself a transform.
"""
meta_type = 'TransformsChain'
meta_types = all_meta_types = ()
manage_options = (
({'label':'Configure',
'action':'manage_main'},
{'label':'Reload',
'action':'manage_reloadTransform'},) +
Item.manage_options
)
manage_main = PageTemplateFile('editTransformsChain', _www)
manage_reloadTransform = PageTemplateFile('reloadTransform', _www)
security = ClassSecurityInfo()
def __init__(self, id, description, ids=()):
self.id = id
self.description = description
self._object_ids = list(ids)
self.inputs = ('application/octet-stream',)
self.output = 'application/octet-stream'
self._chain = None
def __setstate__(self, state):
""" __setstate__ is called whenever the instance is loaded
from the ZODB, like when Zope is restarted.
We should rebuild the chain at this time
"""
TransformsChain.inheritedAttribute('__setstate__')(self, state)
self._chain = None
def _chain_init(self):
""" build the transforms chain """
tr_tool = getToolByName(self, 'portal_transforms')
self._chain = c = chain()
for id in self._object_ids:
object = getattr(tr_tool, id)
c.registerTransform(object)
self.inputs = c.inputs or ('application/octet-stream',)
self.output = c.output or 'application/octet-stream'
security.declarePublic('convert')
def convert(self, *args, **kwargs):
""" return apply the transform and return the result """
if self._chain is None:
self._chain_init()
return self._chain.convert(*args, **kwargs)
security.declarePublic('name')
def name(self):
"""return the name of the transform instance"""
return self.id
security.declarePrivate('manage_beforeDelete')
def manage_beforeDelete(self, item, container):
Item.manage_beforeDelete(self, item, container)
if self is item:
# unregister self from catalog on deletion
tr_tool = getToolByName(self, 'portal_transforms')
tr_tool.unregisterTransform(self.id)
security.declareProtected(ManagePortal, 'manage_addObject')
def manage_addObject(self, id, REQUEST=None):
""" add a new transform or chain to the chain """
assert id not in self._object_ids
self._object_ids.append(id)
self._chain_init()
if REQUEST is not None:
REQUEST['RESPONSE'].redirect(self.absolute_url()+'/manage_main')
security.declareProtected(ManagePortal, 'manage_delObjects')
def manage_delObjects(self, ids, REQUEST=None):
""" delete the selected mime types """
for id in ids:
self._object_ids.remove(id)
self._chain_init()
if REQUEST is not None:
REQUEST['RESPONSE'].redirect(self.absolute_url()+'/manage_main')
# transforms order handling #
security.declareProtected(ManagePortal, 'move_object_to_position')
def move_object_to_position(self, id, newpos):
""" overriden from OrderedFolder to store id instead of objects
"""
oldpos = self._object_ids.index(id)
if (newpos < 0 or newpos == oldpos or newpos >= len(self._object_ids)):
return 0
self._object_ids.pop(oldpos)
self._object_ids.insert(newpos, id)
self._chain_init()
return 1
security.declareProtected(ManageProperties, 'move_object_up')
def move_object_up(self, id, REQUEST=None):
""" move object with the given id up in the list """
newpos = self._object_ids.index(id) - 1
self.move_object_to_position(id, newpos)
if REQUEST is not None:
REQUEST['RESPONSE'].redirect(self.absolute_url()+'/manage_main')
security.declareProtected(ManageProperties, 'move_object_down')
def move_object_down(self, id, REQUEST=None):
""" move object with the given id down in the list """
newpos = self._object_ids.index(id) + 1
self.move_object_to_position(id, newpos)
if REQUEST is not None:
REQUEST['RESPONSE'].redirect(self.absolute_url()+'/manage_main')
# Z transform interface #
security.declareProtected(ManagePortal, 'reload')
def reload(self):
""" reload the module where the transformation class is defined """
for tr in self.objectValues():
tr.reload()
# utilities #
security.declareProtected(ManagePortal, 'listAddableObjectIds')
def listAddableObjectIds(self):
""" return a list of addable transform """
tr_tool = getToolByName(self, 'portal_transforms')
return [id for id in tr_tool.objectIds() if not (id == self.id or id in self._object_ids)]
security.declareProtected(ManagePortal, 'objectIds')
def objectIds(self):
""" return a list of addable transform """
return tuple(self._object_ids)
security.declareProtected(ManagePortal, 'objectValues')
def objectValues(self):
""" return a list of addable transform """
tr_tool = getToolByName(self, 'portal_transforms')
return [getattr(tr_tool, id) for id in self.objectIds()]
InitializeClass(TransformsChain)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment