[gen] Pod field can now freeze and unfreeze any of its multiple templates.

This commit is contained in:
Gaetan Delannay 2014-03-21 16:50:48 +01:00
parent ecc3a8c39b
commit 0834356487
3 changed files with 111 additions and 86 deletions

View file

@ -33,12 +33,15 @@ class Pod(Field):
want to put in it. It is the way gen uses pod.'''
# Layout for rendering a POD field for exporting query results.
rLayouts = {'view': Table('fl', width=None)}
allFormats = ('pdf', 'doc', 'odt')
POD_ERROR = 'An error occurred while generating the document. Please ' \
'contact the system administrator.'
DELETE_TEMP_DOC_ERROR = 'A temporary document could not be removed. %s.'
NO_TEMPLATE = 'Please specify a pod template in field "template".'
UNAVAILABLE_TEMPLATE = 'You are not allow to perform this action.'
TEMPLATE_NOT_FOUND = 'Template not found at %s.'
FREEZE_ERROR = 'Error while trying to freeze a "%s" file in pod field ' \
'"%s" (%s).'
FREEZE_FATAL_ERROR = 'Server error. Please contact the administrator.'
pxView = pxCell = Px('''
<table cellpadding="0" cellspacing="0">
@ -68,8 +71,7 @@ class Pod(Field):
maxChars=None, colspan=1, master=None, masterValue=None,
focus=False, historized=False, mapping=None, label=None,
template=None, templateName=None, showTemplate=None,
context=None, stylesMapping={}, formats=None,
freezeFormat='pdf'):
context=None, stylesMapping={}, formats=None):
# Param "template" stores the path to the pod template(s).
if not template: raise Exception(Pod.NO_TEMPLATE)
if isinstance(template, basestring):
@ -107,8 +109,6 @@ class Pod(Field):
self.formats = ('xls', 'ods')
else:
self.formats = ('pdf', 'doc', 'odt')
# Freeze format is PDF by default.
self.freezeFormat = freezeFormat
Field.__init__(self, None, (0,1), default, show, page, group, layouts,
move, indexed, searchable, specificReadPermission,
specificWritePermission, width, height, None, colspan,
@ -118,18 +118,12 @@ class Pod(Field):
# field is determined by freezing.
self.validable = False
def isFrozen(self, obj):
'''Is there a frozen document for p_self on p_obj?'''
value = getattr(obj.o.aq_base, self.name, None)
return isinstance(value, obj.o.getProductConfig().File)
def getOutputFormats(self, obj):
'''Returns self.formats, excepted if there is a frozen document: in
this case, only the format of the frozen doc is returned.'''
if not self.isFrozen(obj): return self.formats
# The only available format is the one from the frozen document
fileName = getattr(obj.o.aq_base, self.name).filename
return (os.path.splitext(fileName)[1][1:],)
if not obj.user.has_role('Manager'): return self.formats
# A manager can have all formats
return self.allFormats
def getTemplateName(self, obj, fileName):
'''Gets the name of a template given its p_fileName.'''
@ -143,6 +137,16 @@ class Pod(Field):
res = gutils.produceNiceMessage(name)
return res
def getDownloadName(self, obj, template, format, queryRelated):
'''Gets the name of the pod result as will be seen by the user that will
download it.'''
fileName = self.getTemplateName(obj, template)
if not queryRelated:
# This is a POD for a single object: personalize the file name with
# the object title.
fileName = '%s-%s' % (obj.title, fileName)
return obj.tool.normalize(fileName) + '.' + format
def getVisibleTemplates(self, obj):
'''Returns, among self.template, the template(s) that can be shown.'''
if not self.showTemplate: return self.template # Show them all.
@ -152,23 +156,30 @@ class Pod(Field):
res.append(template)
return res
def getValue(self, obj):
def getValue(self, obj, template=None, format=None, result=None):
'''For a pod field, getting its value means computing a pod document or
returning a frozen one. A pod field differs from other field types
because there can be several ways to produce the field value (ie:
self.template can hold various templates; output file format can be
odt, pdf,.... We get those precisions about the way to produce the
file from the request object. If we don't find the request object (or
if it does not exist, ie, when Zope runs in test mode), we use
default values.'''
rq = getattr(obj, 'REQUEST') or Object()
file, either:
- from params p_template and p_format;
- from the request object;
- from default values (the request object may not be present, ie,
when Zope runs in test mode).'''
obj = obj.appy()
template = rq.get('template') or self.template[0]
rq = obj.request
template = template or rq.get('template') or self.template[0]
format = format or rq.get('podFormat') or 'odt'
# Security check.
if not self.showTemplate(obj, template):
raise Exception(self.UNAVAILABLE_TEMPLATE)
# Return the frozen document if frozen.
# if ...
frozen = self.isFrozen(obj, template, format)
if frozen:
print 'RETURN FROZEN'
fileName = self.getDownloadName(obj, template, format, False)
return FileInfo(frozen, inDb=False, uploadName=fileName)
# We must call pod to compute a pod document from "template".
tool = obj.tool
diskFolder = tool.getDiskFolder()
@ -176,17 +187,16 @@ class Pod(Field):
templatePath = os.path.join(diskFolder, template)
if not os.path.isfile(templatePath):
raise Exception(self.TEMPLATE_NOT_FOUND % templatePath)
# Get the output format
outputFormat = rq.get('podFormat', 'odt')
# Get or compute the specific POD context
specificContext = None
if callable(self.context):
specificContext = self.callMethod(obj, self.context)
else:
specificContext = self.context
# Temporary file where to generate the result
tempFileName = '%s/%s_%f.%s' % (
sutils.getOsTempFolder(), obj.uid, time.time(), outputFormat)
# Compute the name of the result file.
if not result:
result = '%s/%s_%f.%s' % (sutils.getOsTempFolder(),
obj.uid, time.time(), format)
# Define parameters to give to the appy.pod renderer
podContext = {'tool': tool, 'user': obj.user, 'self': obj, 'field':self,
'now': obj.o.getProductConfig().DateTime(),
@ -209,7 +219,7 @@ class Pod(Field):
if specificContext:
podContext.update(specificContext)
# If a custom param comes from the request, add it to the context. A
# custom param must have format "name:value". Custom params override any
# custom param must have form "name:value". Custom params override any
# other value in the request, including values from the field-specific
# context.
customParams = rq.get('customParams', None)
@ -222,8 +232,9 @@ class Pod(Field):
else:
stylesMapping = self.stylesMapping
rendererParams = {'template': templatePath, 'context': podContext,
'result': tempFileName, 'stylesMapping':stylesMapping,
'imageResolver': tool.o.getApp()}
'result': result, 'stylesMapping': stylesMapping,
'imageResolver': tool.o.getApp(),
'overwriteExisting': True}
if tool.unoEnabledPython:
rendererParams['pythonWithUnoPath'] = tool.unoEnabledPython
if tool.openOfficePort:
@ -233,31 +244,71 @@ class Pod(Field):
renderer = Renderer(**rendererParams)
renderer.run()
except PodError, pe:
if not os.path.exists(tempFileName):
if not os.path.exists(result):
# In some (most?) cases, when OO returns an error, the result is
# nevertheless generated.
obj.log(str(pe).strip(), type='error')
return Pod.POD_ERROR
# Give a friendly name for this file
fileName = self.getTemplateName(obj, template)
if not isQueryRelated:
# This is a POD for a single object: personalize the file name with
# the object title.
fileName = '%s-%s' % (obj.title, fileName)
fileName = tool.normalize(fileName) + '.' + outputFormat
# Get a FileInfo instance to manipulate the temp file on the filesystem.
return FileInfo(tempFileName, inDb=False, uploadName=fileName)
fileName = self.getDownloadName(obj, template, format, isQueryRelated)
# Get a FileInfo instance to manipulate the file on the filesystem.
return FileInfo(result, inDb=False, uploadName=fileName)
# Returns the doc and removes the temp file
try:
os.remove(tempFileName)
except Exception, e:
obj.log(Pod.DELETE_TEMP_DOC_ERROR % str(e).strip(), type='warning')
return res
def getFreezeName(self, template=None, format='pdf'):
'''Gets the name on disk on the frozen document corresponding to this
pod field, p_template and p_format.'''
template = template or self.template[0]
templateName = os.path.splitext(template)[0].replace(os.sep, '_')
return '%s_%s.%s' % (self.name, templateName, format)
def store(self, obj, value):
'''Stores (=freezes) a document (in p_value) in the field.'''
if isinstance(value, sutils.FileWrapper):
value = value._zopeFile
setattr(obj, self.name, value)
def isFrozen(self, obj, template=None, format='pdf'):
'''Is there a frozen document for thid pod field, on p_obj, for
p_template in p_format? If yes, it returns the absolute path to the
frozen doc.'''
template = template or self.template[0]
dbFolder, folder = obj.o.getFsFolder()
fileName = self.getFreezeName(template, format)
res = os.path.join(dbFolder, folder, fileName)
if os.path.exists(res): return res
def freeze(self, obj, template=None, format='pdf'):
'''Freezes, on p_obj, a document for this pod field, for p_template in
p_format.'''
# Compute the absolute path where to store the frozen document in the
# database.
dbFolder, folder = obj.o.getFsFolder(create=True)
fileName = self.getFreezeName(template, format)
result = os.path.join(dbFolder, folder, fileName)
if os.path.exists(result):
obj.log('Freeze: overwriting %s...' % result)
# Generate the document.
doc = self.getValue(obj, template=template, format=format,
result=result)
if isinstance(doc, basestring):
# An error occurred, the document was not generated.
obj.log(self.FREEZE_ERROR % (format, self.name, doc), type='error')
if format == 'odt': raise Exception(self.FREEZE_FATAL_ERROR)
obj.log('Trying to freeze the ODT version...')
# Try to freeze the ODT version of the document, which does not
# require to call LibreOffice: the risk of error is smaller.
fileName = self.getFreezeName(template, 'odt')
result = os.path.join(dbFolder, folder, fileName)
if os.path.exists(result):
obj.log('Freeze: overwriting %s...' % result)
doc = self.getValue(obj, template=template, format='odt',
result=result)
if isinstance(doc, basestring):
self.log(self.FREEZE_ERROR % ('odt', self.name, doc),
type='error')
raise Exception(self.FREEZE_FATAL_ERROR)
return doc
def unfreeze(self, obj, template=None, format='pdf'):
'''Unfreezes, on p_obj, the document for this pod field, for p_template
in p_format.'''
# Compute the absolute path to the frozen doc.
dbFolder, folder = obj.o.getFsFolder()
fileName = self.getFreezeName(template, format)
frozenName = os.path.join(dbFolder, folder, fileName)
if os.path.exists(frozenName): os.remove(frozenName)
# ------------------------------------------------------------------------------

View file

@ -115,6 +115,7 @@ class ToolMixin(BaseMixin):
# Get the object on which a document must be generated.
obj = self.getObject(rq.get('objectUid'), appy=True)
fieldName = rq.get('fieldName')
# Get the document by accessing the value of the pod field.
res = getattr(obj, fieldName)
if isinstance(res, basestring):
# An error has occurred, and p_res contains the error message
@ -122,8 +123,6 @@ class ToolMixin(BaseMixin):
return self.goto(rq.get('HTTP_REFERER'))
# res contains a FileInfo instance.
res.writeResponse(rq.RESPONSE)
# (Try to) delete the temp file on disk.
res.removeFile()
def getAppName(self):
'''Returns the name of the application.'''

View file

@ -13,12 +13,6 @@ from appy.shared.utils import getOsTempFolder, executeCommand, \
from appy.shared.xml_parser import XmlMarshaller
from appy.shared.csv_parser import CsvMarshaller
# Some error messages ----------------------------------------------------------
FREEZE_ERROR = 'Error while trying to freeze a "%s" file in POD field ' \
'"%s" (%s).'
FREEZE_FATAL_ERROR = 'A server error occurred. Please contact the system ' \
'administrator.'
# ------------------------------------------------------------------------------
class AbstractWrapper(object):
'''Any real Appy-managed Zope object has a companion object that is an
@ -797,38 +791,19 @@ class AbstractWrapper(object):
zopeObj.reindex()
return appyObj
def freeze(self, fieldName):
'''This method freezes a POD document. TODO: allow to freeze Computed
fields.'''
rq = self.request
def freeze(self, fieldName, template=None, format='pdf'):
'''This method freezes the content of pod field named p_fieldName, for
the given p_template (several templates can be given in
podField.template), in the given p_format ("pdf" by default).'''
field = self.o.getAppyType(fieldName)
if field.type != 'Pod': raise 'Cannot freeze non-Pod field.'
# Set the freeze format
rq.set('podFormat', field.freezeFormat)
# Generate the document.
doc = field.getValue(self.o)
if isinstance(doc, basestring):
self.log(FREEZE_ERROR % (field.freezeFormat, field.name, doc),
type='error')
if field.freezeFormat == 'odt': raise FREEZE_FATAL_ERROR
self.log('Trying to freeze the ODT version...')
# Try to freeze the ODT version of the document, which does not
# require to call OpenOffice/LibreOffice, so the risk of error is
# smaller.
self.request.set('podFormat', 'odt')
doc = field.getValue(self.o)
if isinstance(doc, basestring):
self.log(FREEZE_ERROR % ('odt', field.name, doc), type='error')
raise FREEZE_FATAL_ERROR
field.store(self.o, doc)
if field.type!= 'Pod': raise Exception('Cannot freeze non-Pod field.')
return field.freeze(self, template, format)
def unFreeze(self, fieldName):
'''This method un freezes a POD document. TODO: allow to unfreeze
Computed fields.'''
rq = self.request
def unfreeze(self, fieldName, template=None, format='pdf'):
'''This method unfreezes a pod field.'''
field = self.o.getAppyType(fieldName)
if field.type != 'Pod': raise 'Cannot unFreeze non-Pod field.'
field.store(self.o, None)
if field.type!= 'Pod': raise Exception('Cannot unfreeze non-Pod field.')
field.unfreeze(self, template, format)
def delete(self):
'''Deletes myself.'''