From 08343564874ef561c6db97fb418b02ee4f6cd0de Mon Sep 17 00:00:00 2001 From: Gaetan Delannay Date: Fri, 21 Mar 2014 16:50:48 +0100 Subject: [PATCH] [gen] Pod field can now freeze and unfreeze any of its multiple templates. --- fields/pod.py | 149 ++++++++++++++++++++++++++------------- gen/mixins/ToolMixin.py | 3 +- gen/wrappers/__init__.py | 45 +++--------- 3 files changed, 111 insertions(+), 86 deletions(-) diff --git a/fields/pod.py b/fields/pod.py index 807bf23..25f9c60 100644 --- a/fields/pod.py +++ b/fields/pod.py @@ -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(''' @@ -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) # ------------------------------------------------------------------------------ diff --git a/gen/mixins/ToolMixin.py b/gen/mixins/ToolMixin.py index 6607524..f16daf5 100644 --- a/gen/mixins/ToolMixin.py +++ b/gen/mixins/ToolMixin.py @@ -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.''' diff --git a/gen/wrappers/__init__.py b/gen/wrappers/__init__.py index 6d588a8..0e7ac92 100644 --- a/gen/wrappers/__init__.py +++ b/gen/wrappers/__init__.py @@ -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.'''