More input and output formats for OO conversion in converter.py and bugfix in skyn edit.
This commit is contained in:
parent
fff2b6a329
commit
e89eda4838
|
@ -355,7 +355,7 @@ class Ref(Type):
|
|||
|
||||
class Computed(Type):
|
||||
def __init__(self, validator=None, multiplicity=(0,1), index=None,
|
||||
default=None, optional=False, editDefault=False, show=True,
|
||||
default=None, optional=False, editDefault=False, show='view',
|
||||
page='main', group=None, move=0, indexed=False,
|
||||
searchable=False, specificReadPermission=False,
|
||||
specificWritePermission=False, width=None, height=None,
|
||||
|
|
|
@ -33,9 +33,13 @@ class AbstractMixin:
|
|||
# creates the final object from the temp object.
|
||||
previousData = None
|
||||
if not created: previousData = self.rememberPreviousData()
|
||||
# We do not process form data (=real update on the object) if the tool
|
||||
# itself is being created.
|
||||
if obj._appy_meta_type != 'tool': obj.processForm()
|
||||
# Perform the change on the object, unless self is a tool being created.
|
||||
if (obj._appy_meta_type == 'tool') and created:
|
||||
# We do not process form data (=real update on the object) if the
|
||||
# tool itself is being created.
|
||||
pass
|
||||
else:
|
||||
obj.processForm()
|
||||
if previousData:
|
||||
# Keep in history potential changes on historized fields
|
||||
self.historizeData(previousData)
|
||||
|
@ -389,7 +393,7 @@ class AbstractMixin:
|
|||
fieldDescr = fieldDescr.__dict__
|
||||
appyType = fieldDescr['appyType']
|
||||
if isEdit and (appyType['type']=='Ref') and appyType['add']:return False
|
||||
if isEdit and (appyType['type'] in ('Action', 'Computed')): return False
|
||||
if isEdit and (appyType['type'] == 'Action'): return False
|
||||
if (fieldDescr['widgetType'] == 'backField') and \
|
||||
not self.getBRefs(fieldDescr['fieldRel']): return False
|
||||
# Do not show field if it is optional and not selected in flavour
|
||||
|
|
|
@ -3,23 +3,19 @@
|
|||
errors request/errors | python:{};
|
||||
Iterator python:modules['Products.Archetypes'].IndexIterator;
|
||||
schematas contextObj/Schemata;
|
||||
fieldsets python:[key for key in schematas.keys() if (key != 'metadata') and (schematas[key].editableFields(here, visible_only=True))];
|
||||
fieldsets python:[key for key in schematas.keys() if (key != 'metadata') and (schematas[key].editableFields(contextObj, visible_only=True))];
|
||||
default_fieldset python:(not schematas or schematas.has_key('default')) and 'default' or fieldsets[0];
|
||||
fieldset request/fieldset|options/fieldset|default_fieldset;
|
||||
fields python:schematas[fieldset].editableFields(here);
|
||||
dummy python:here.at_isEditable(fields);
|
||||
portal_type python:here.getPortalTypeName().lower().replace(' ', '_');
|
||||
type_name here/getPortalTypeName|here/archetype_name;
|
||||
lockable python:hasattr(here, 'wl_isLocked');
|
||||
isLocked python:lockable and here.wl_isLocked();
|
||||
tabindex tabindex|python:Iterator(pos=7000);
|
||||
fields python:schematas[fieldset].editableFields(contextObj);
|
||||
lockable python:hasattr(contextObj, 'wl_isLocked');
|
||||
isLocked python:lockable and contextObj.wl_isLocked();
|
||||
isEdit python:True;
|
||||
tool contextObj/getTool;
|
||||
flavour python: tool.getFlavour(contextObj);
|
||||
appFolder tool/getAppFolder;
|
||||
appName appFolder/id;
|
||||
css python:here.getUniqueWidgetAttr(fields, 'helper_css');
|
||||
js python:here.getUniqueWidgetAttr(fields, 'helper_js');
|
||||
css python:contextObj.getUniqueWidgetAttr(fields, 'helper_css');
|
||||
js python:contextObj.getUniqueWidgetAttr(fields, 'helper_js');
|
||||
phaseInfo python: contextObj.getAppyPhases(fieldset=fieldset, forPlone=True);
|
||||
phase request/phase|phaseInfo/name;
|
||||
pageName python: contextObj.getAppyPage(isEdit, phaseInfo);">
|
||||
|
@ -91,24 +87,20 @@
|
|||
<tal:previousButton condition="python: pageIndex > 0">
|
||||
<input class="context" type="submit" name="buttonPrevious"
|
||||
i18n:attributes="value label_previous;" i18n:domain="plone"
|
||||
tal:attributes="tabindex tabindex/next;
|
||||
disabled python:test(isLocked, 'disabled', None);"/>
|
||||
tal:attributes="disabled python:test(isLocked, 'disabled', None);"/>
|
||||
<input type="hidden" name="previousPage" tal:attributes="value python: pages[pageIndex-1]"/>
|
||||
</tal:previousButton>
|
||||
<tal:nextButton condition="python: pageIndex < numberOfPages - 1">
|
||||
<input class="context" type="submit" name="buttonNext" value="Next"
|
||||
i18n:attributes="value label_next;" i18n:domain="plone"
|
||||
tal:attributes="tabindex tabindex/next;
|
||||
disabled python:test(isLocked, 'disabled', None);"/>
|
||||
tal:attributes="disabled python:test(isLocked, 'disabled', None);"/>
|
||||
<input type="hidden" name="nextPage" tal:attributes="value python: pages[pageIndex+1]"/>
|
||||
</tal:nextButton>
|
||||
<input class="context" type="submit" name="buttonOk"
|
||||
i18n:attributes="value label_save;" i18n:domain="plone"
|
||||
tal:attributes="tabindex tabindex/next;
|
||||
disabled python:test(isLocked, 'disabled', None);"/>
|
||||
tal:attributes="disabled python:test(isLocked, 'disabled', None);"/>
|
||||
<input class="standalone" type="submit" name="buttonCancel"
|
||||
i18n:attributes="value label_cancel;" i18n:domain="plone"
|
||||
tal:attributes="tabindex tabindex/next"/>
|
||||
i18n:attributes="value label_cancel;" i18n:domain="plone"/>
|
||||
</metal:block>
|
||||
</form>
|
||||
<div metal:use-macro="here/skyn/macros/macros/showPageFooter"/>
|
||||
|
|
|
@ -202,7 +202,7 @@
|
|||
<metal:editRef use-macro="here/skyn/ref/macros/editReference" />
|
||||
</tal:ref>
|
||||
</tal:editRef>
|
||||
<tal:computedField condition="python: (not isEdit) and (appyType['type'] == 'Computed')">
|
||||
<tal:computedField condition="python: appyType['type'] == 'Computed'">
|
||||
<metal:cf use-macro="here/skyn/macros/macros/showComputedField" />
|
||||
</tal:computedField>
|
||||
<tal:actionField condition="python: appyType['type'] == 'Action'">
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
developer the real classes used by the underlying web framework.'''
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
import time, os.path, mimetypes, unicodedata, random
|
||||
import os, os.path, time, mimetypes, unicodedata, random
|
||||
import appy.pod
|
||||
from appy.gen import Search
|
||||
from appy.gen.utils import sequenceTypes
|
||||
from appy.shared.utils import getOsTempFolder
|
||||
|
@ -236,15 +237,23 @@ class AbstractWrapper:
|
|||
wfTool.doActionFor(self.o, transitionName, comment=comment)
|
||||
del self.o._v_appy_do
|
||||
|
||||
def log(self, message, logLevel='info'):
|
||||
def log(self, message, type='info'):
|
||||
'''Logs a message in the log file. p_logLevel may be "info", "warning"
|
||||
or "error".'''
|
||||
logger = self.o.getProductConfig().logger
|
||||
if logLevel == 'warning': logMethod = logger.warn
|
||||
elif logLevel == 'error': logMethod = logger.error
|
||||
if type == 'warning': logMethod = logger.warn
|
||||
elif type == 'error': logMethod = logger.error
|
||||
else: logMethod = logger.info
|
||||
logMethod(message)
|
||||
|
||||
def say(self, message, type='info'):
|
||||
'''Prints a message in the user interface. p_logLevel may be "info",
|
||||
"warning" or "error".'''
|
||||
mType = type
|
||||
if mType == 'warning': mType = 'warn'
|
||||
elif mType == 'error': mType = 'stop'
|
||||
self.o.plone_utils.addPortalMessage(message, type=mType)
|
||||
|
||||
def normalize(self, s):
|
||||
'''Returns a version of string p_s whose special chars have been
|
||||
replaced with normal chars.'''
|
||||
|
@ -338,11 +347,16 @@ class FileWrapper:
|
|||
raise 'Impossible to set attribute %s. "Settable" attributes ' \
|
||||
'are "name", "content" and "mimeType".' % name
|
||||
|
||||
def dump(self, filePath=None):
|
||||
def dump(self, filePath=None, format=None, tool=None):
|
||||
'''Writes the file on disk. If p_filePath is specified, it is the
|
||||
path name where the file will be dumped; folders mentioned in it
|
||||
must exist. If not, the file will be dumped in the OS temp folder.
|
||||
The absolute path name of the dumped file is returned.'''
|
||||
The absolute path name of the dumped file is returned.
|
||||
If an error occurs, the method returns None. If p_format is
|
||||
specified, OpenOffice will be called for converting the dumped file
|
||||
to the desired format. In this case, p_tool, a Appy tool, must be
|
||||
provided. Indeed, any Appy tool contains parameters for contacting
|
||||
OpenOffice in server mode.'''
|
||||
if not filePath:
|
||||
filePath = '%s/file%f.%s' % (getOsTempFolder(), time.time(),
|
||||
self.name)
|
||||
|
@ -358,5 +372,20 @@ class FileWrapper:
|
|||
# Only one chunk
|
||||
f.write(self.content)
|
||||
f.close()
|
||||
if format:
|
||||
if not tool: return
|
||||
# Convert the dumped file using OpenOffice
|
||||
convScript = '%s/converter.py' % os.path.dirname(appy.pod.__file__)
|
||||
cmd = '%s %s "%s" %s -p%d' % (tool.unoEnabledPython, convScript,
|
||||
filePath, format, tool.openOfficePort)
|
||||
res = os.system(cmd)
|
||||
os.remove(filePath)
|
||||
if res != 0: return
|
||||
# Return the name of the converted file.
|
||||
baseName, ext = os.path.splitext(filePath)
|
||||
if (ext == '.%s' % format):
|
||||
filePath = '%s.res.%s' % (baseName, format)
|
||||
else:
|
||||
filePath = '%s.%s' % (baseName, format)
|
||||
return filePath
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
162
pod/converter.py
162
pod/converter.py
|
@ -20,15 +20,26 @@
|
|||
import sys, os, os.path, time, signal
|
||||
from optparse import OptionParser
|
||||
|
||||
ODT_FILE_TYPES = {'doc': 'MS Word 97', # Could be 'MS Word 2003 XML'
|
||||
'pdf': 'writer_pdf_Export',
|
||||
htmlFilters = {'odt': 'HTML (StarWriter)',
|
||||
'ods': 'HTML (StarCalc)',
|
||||
'odp': 'impress_html_Export'}
|
||||
|
||||
FILE_TYPES = {'odt': 'writer8',
|
||||
'ods': 'calc8',
|
||||
'odp': 'impress8',
|
||||
'htm': htmlFilters, 'html': htmlFilters,
|
||||
'rtf': 'Rich Text Format',
|
||||
'txt': 'Text',
|
||||
'html': 'HTML (StarWriter)',
|
||||
'htm': 'HTML (StarWriter)',
|
||||
'odt': 'ODT'}
|
||||
# Conversion to ODT does not make any conversion; it simply updates indexes and
|
||||
# linked documents.
|
||||
'csv': 'Text - txt - csv (StarCalc)',
|
||||
'pdf': {'odt': 'writer_pdf_Export', 'ods': 'calc_pdf_Export',
|
||||
'odp': 'impress_pdf_Export', 'odg': 'draw_pdf_Export'},
|
||||
'swf': 'impress_flash_Export',
|
||||
'doc': 'MS Word 97',
|
||||
'xls': 'MS Excel 97',
|
||||
'ppt': 'MS PowerPoint 97',
|
||||
}
|
||||
# Conversion from odt to odt does not make any conversion, but updates indexes
|
||||
# and linked documents.
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
class ConverterError(Exception): pass
|
||||
|
@ -46,7 +57,7 @@ DEFAULT_PORT = 2002
|
|||
|
||||
# ------------------------------------------------------------------------------
|
||||
class Converter:
|
||||
'''Converts an ODT document into pdf, doc, txt or rtf.'''
|
||||
'''Converts an document readable by OpenOffice into pdf, doc, txt or rtf.'''
|
||||
exeVariants = ('soffice.exe', 'soffice')
|
||||
pathReplacements = {'program files': 'progra~1',
|
||||
'openoffice.org 1': 'openof~1',
|
||||
|
@ -54,41 +65,64 @@ class Converter:
|
|||
}
|
||||
def __init__(self, docPath, resultType, port=DEFAULT_PORT):
|
||||
self.port = port
|
||||
self.docUrl, self.docPath = self.getDocUrls(docPath)
|
||||
self.resultFilter = self.getResultFilter(resultType)
|
||||
self.resultUrl = self.getResultUrl(resultType)
|
||||
self.docUrl, self.docPath = self.getInputUrls(docPath)
|
||||
self.inputType = os.path.splitext(docPath)[1][1:].lower()
|
||||
self.resultType = resultType
|
||||
self.resultFilter = self.getResultFilter()
|
||||
self.resultUrl = self.getResultUrl()
|
||||
self.ooContext = None
|
||||
self.oo = None # OpenOffice application object
|
||||
self.doc = None # OpenOffice loaded document
|
||||
def getDocUrls(self, docPath):
|
||||
self.oo = None # The OpenOffice application object
|
||||
self.doc = None # The OpenOffice loaded document
|
||||
|
||||
def getInputUrls(self, docPath):
|
||||
'''Returns the absolute path of the input file. In fact, it returns a
|
||||
tuple with some URL version of the path for OO as the first element
|
||||
and the absolute path as the second element.'''
|
||||
import uno
|
||||
if not os.path.exists(docPath) and not os.path.isfile(docPath):
|
||||
raise ConverterError(DOC_NOT_FOUND % docPath)
|
||||
docAbsPath = os.path.abspath(docPath)
|
||||
# Return one path for OO, one path for me.
|
||||
return uno.systemPathToFileUrl(docAbsPath), docAbsPath
|
||||
def getResultFilter(self, resultType):
|
||||
if ODT_FILE_TYPES.has_key(resultType):
|
||||
res = ODT_FILE_TYPES[resultType]
|
||||
|
||||
def getResultFilter(self):
|
||||
'''Based on the result type, identifies which OO filter to use for the
|
||||
document conversion.'''
|
||||
if FILE_TYPES.has_key(self.resultType):
|
||||
res = FILE_TYPES[self.resultType]
|
||||
if isinstance(res, dict):
|
||||
res = res[self.inputType]
|
||||
else:
|
||||
raise ConverterError(BAD_RESULT_TYPE % (resultType,
|
||||
ODT_FILE_TYPES.keys()))
|
||||
raise ConverterError(BAD_RESULT_TYPE % (self.resultType,
|
||||
FILE_TYPES.keys()))
|
||||
return res
|
||||
def getResultUrl(self, resultType):
|
||||
|
||||
def getResultUrl(self):
|
||||
'''Returns the path of the result file in the format needed by OO. If
|
||||
the result type and the input type are the same (ie the user wants to
|
||||
refresh indexes or some other action and not perform a real
|
||||
conversion), the result file is named
|
||||
<inputFileName>.res.<resultType>.
|
||||
|
||||
Else, the result file is named like the input file but with a
|
||||
different extension:
|
||||
<inputFileName>.<resultType>
|
||||
'''
|
||||
import uno
|
||||
baseName = os.path.splitext(self.docPath)[0]
|
||||
if resultType != 'odt':
|
||||
res = '%s.%s' % (baseName, resultType)
|
||||
if self.resultType != self.inputType:
|
||||
res = '%s.%s' % (baseName, self.resultType)
|
||||
else:
|
||||
res = '%s.res.%s' % (baseName, resultType)
|
||||
res = '%s.res.%s' % (baseName, self.resultType)
|
||||
try:
|
||||
f = open(res, 'w')
|
||||
f.write('Hello')
|
||||
f.close()
|
||||
os.remove(res)
|
||||
return uno.systemPathToFileUrl(res)
|
||||
except OSError, oe:
|
||||
raise ConverterError(CANNOT_WRITE_RESULT % (res, oe))
|
||||
except (OSError, IOError), ioe:
|
||||
raise ConverterError(CANNOT_WRITE_RESULT % (res, ioe))
|
||||
|
||||
def connect(self):
|
||||
'''Connects to OpenOffice'''
|
||||
if os.name == 'nt':
|
||||
|
@ -115,30 +149,18 @@ class Converter:
|
|||
'com.sun.star.frame.Desktop', self.ooContext)
|
||||
except NoConnectException, nce:
|
||||
raise ConverterError(CONNECT_ERROR % (self.port, nce))
|
||||
def disconnect(self):
|
||||
self.doc.close(True)
|
||||
# Do a nasty thing before exiting the python process. In case the
|
||||
# last call is a oneway call (e.g. see idl-spec of insertString),
|
||||
# it must be forced out of the remote-bridge caches before python
|
||||
# exits the process. Otherwise, the oneway call may or may not reach
|
||||
# the target object.
|
||||
# I do this here by calling a cheap synchronous call (getPropertyValue).
|
||||
self.ooContext.ServiceManager
|
||||
def loadDocument(self):
|
||||
from com.sun.star.lang import IllegalArgumentException, \
|
||||
IndexOutOfBoundsException
|
||||
|
||||
def updateOdtDocument(self):
|
||||
'''If the input file is an ODT document, we will perform 2 tasks:
|
||||
1) Update all annexes;
|
||||
2) Update sections (if sections refer to external content, we try to
|
||||
include the content within the result file)
|
||||
'''
|
||||
from com.sun.star.lang import IndexOutOfBoundsException
|
||||
# I need to use IndexOutOfBoundsException because sometimes, when
|
||||
# using sections.getCount, UNO returns a number that is bigger than
|
||||
# the real number of sections (this is because it also counts the
|
||||
# sections that are present within the sub-documents to integrate)
|
||||
from com.sun.star.beans import PropertyValue
|
||||
try:
|
||||
# Load the document to convert in a new hidden frame
|
||||
prop = PropertyValue()
|
||||
prop.Name = 'Hidden'
|
||||
prop.Value = True
|
||||
self.doc = self.oo.loadComponentFromURL(self.docUrl, "_blank", 0,
|
||||
(prop,))
|
||||
# Update all indexes
|
||||
indexes = self.doc.getDocumentIndexes()
|
||||
indexesCount = indexes.getCount()
|
||||
|
@ -165,23 +187,52 @@ class Converter:
|
|||
# of the section. Else, it won't appear.
|
||||
except IndexOutOfBoundsException:
|
||||
pass
|
||||
|
||||
def loadDocument(self):
|
||||
from com.sun.star.lang import IllegalArgumentException, \
|
||||
IndexOutOfBoundsException
|
||||
from com.sun.star.beans import PropertyValue
|
||||
try:
|
||||
# Loads the document to convert in a new hidden frame
|
||||
prop = PropertyValue()
|
||||
prop.Name = 'Hidden'
|
||||
prop.Value = True
|
||||
self.doc = self.oo.loadComponentFromURL(self.docUrl, "_blank", 0,
|
||||
(prop,))
|
||||
if self.inputType == 'odt':
|
||||
# Perform additional tasks for odt documents
|
||||
self.updateOdtDocument()
|
||||
try:
|
||||
self.doc.refresh()
|
||||
except AttributeError:
|
||||
pass
|
||||
except IllegalArgumentException, iae:
|
||||
raise ConverterError(URL_NOT_FOUND % (self.docPath, iae))
|
||||
|
||||
def convertDocument(self):
|
||||
if self.resultFilter != 'ODT':
|
||||
# I must really perform a conversion
|
||||
'''Calls OO to perform a document conversion. Note that the conversion
|
||||
is not really done if the source and target documents have the same
|
||||
type.'''
|
||||
properties = []
|
||||
from com.sun.star.beans import PropertyValue
|
||||
prop = PropertyValue()
|
||||
prop.Name = 'FilterName'
|
||||
prop.Value = self.resultFilter
|
||||
self.doc.storeToURL(self.resultUrl, (prop,))
|
||||
else:
|
||||
self.doc.storeToURL(self.resultUrl, ())
|
||||
properties.append(prop)
|
||||
if self.resultType == 'csv':
|
||||
# For CSV export, add options (separator, etc)
|
||||
optionsProp = PropertyValue()
|
||||
optionsProp.Name = 'FilterOptions'
|
||||
optionsProp.Value = '59,34,76,1'
|
||||
properties.append(optionsProp)
|
||||
self.doc.storeToURL(self.resultUrl, tuple(properties))
|
||||
|
||||
def run(self):
|
||||
'''Connects to OO, does the job and disconnects.'''
|
||||
self.connect()
|
||||
self.loadDocument()
|
||||
self.convertDocument()
|
||||
self.disconnect()
|
||||
self.doc.close(True)
|
||||
|
||||
# ConverterScript-related messages ---------------------------------------------
|
||||
WRONG_NB_OF_ARGS = 'Wrong number of arguments.'
|
||||
|
@ -191,12 +242,13 @@ ERROR_CODE = 1
|
|||
class ConverterScript:
|
||||
usage = 'usage: python converter.py fileToConvert outputType [options]\n' \
|
||||
' where fileToConvert is the absolute or relative pathname of\n' \
|
||||
' the ODT file you want to convert;\n'\
|
||||
' the file you want to convert (or whose content like\n' \
|
||||
' indexes need to be refreshed);\n'\
|
||||
' and outputType is the output format, that must be one of\n' \
|
||||
' %s.\n' \
|
||||
' "python" should be a UNO-enabled Python interpreter (ie the one\n' \
|
||||
' which is included in the OpenOffice.org distribution).' % \
|
||||
str(ODT_FILE_TYPES.keys())
|
||||
' "python" should be a UNO-enabled Python interpreter (ie the ' \
|
||||
' one which is included in the OpenOffice.org distribution).' % \
|
||||
str(FILE_TYPES.keys())
|
||||
def run(self):
|
||||
optParser = OptionParser(usage=ConverterScript.usage)
|
||||
optParser.add_option("-p", "--port", dest="port",
|
||||
|
|
|
@ -25,7 +25,7 @@ import appy.pod
|
|||
from appy.pod import PodError
|
||||
from appy.shared.xml_parser import XmlElement
|
||||
from appy.pod.pod_parser import PodParser, PodEnvironment, OdInsert
|
||||
from appy.pod.converter import ODT_FILE_TYPES
|
||||
from appy.pod.converter import FILE_TYPES
|
||||
from appy.pod.buffers import FileBuffer
|
||||
from appy.pod.xhtml2odt import Xhtml2OdtConverter
|
||||
from appy.pod.doc_importers import OdtImporter, ImageImporter, PdfImporter
|
||||
|
@ -423,9 +423,9 @@ class Renderer:
|
|||
os.rename(resultOdtName, self.result)
|
||||
else:
|
||||
if resultType.startswith('.'): resultType = resultType[1:]
|
||||
if not resultType in ODT_FILE_TYPES.keys():
|
||||
if not resultType in FILE_TYPES.keys():
|
||||
raise PodError(BAD_RESULT_TYPE % (
|
||||
self.result, ODT_FILE_TYPES.keys()))
|
||||
self.result, FILE_TYPES.keys()))
|
||||
# Call OpenOffice to perform the conversion or document update
|
||||
self.callOpenOffice(resultOdtName, resultType)
|
||||
# I have the result. Move it to the correct name
|
||||
|
|
Loading…
Reference in a new issue