[gen] Pod fields can now be configured with several templates.
This commit is contained in:
parent
889289407f
commit
ecc3a8c39b
105
fields/file.py
105
fields/file.py
|
@ -28,32 +28,76 @@ WRONG_FILE_TUPLE = 'This is not the way to set a file. You can specify a ' \
|
||||||
'mimeType).'
|
'mimeType).'
|
||||||
CONVERSION_ERROR = 'An error occurred. %s'
|
CONVERSION_ERROR = 'An error occurred. %s'
|
||||||
|
|
||||||
|
def guessMimeType(fileName):
|
||||||
|
'''Try to find the MIME type of file p_fileName.'''
|
||||||
|
return mimetypes.guess_type(fileName)[0] or File.defaultMimeType
|
||||||
|
|
||||||
|
def osPathJoin(*pathElems):
|
||||||
|
'''Version of os.path.elems that takes care of path elems being empty
|
||||||
|
strings.'''
|
||||||
|
return os.path.join(*pathElems).rstrip(os.sep)
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class FileInfo:
|
class FileInfo:
|
||||||
'''For a "file" field, its binary content is stored on the filesystem.
|
'''A FileInfo instance holds metadata about a file on the filesystem.
|
||||||
Within the database, we store a FileInfo instance that only stores some
|
|
||||||
metadata.'''
|
For every File field, we will store a FileInfo instance in the dabatase;
|
||||||
|
the real file will be stored in the Appy/ZODB database-managed
|
||||||
|
filesystem.
|
||||||
|
|
||||||
|
This is the primary usage of FileInfo instances. FileInfo instances can
|
||||||
|
also be used every time we need to manipulate a file. For example, when
|
||||||
|
getting the content of a Pod field, a temporary file may be generated and
|
||||||
|
you will get a FileInfo that represents it.
|
||||||
|
'''
|
||||||
BYTES = 5000
|
BYTES = 5000
|
||||||
def __init__(self, fsPath):
|
|
||||||
# The path on disk (from the root DB folder) where the file will be
|
def __init__(self, fsPath, inDb=True, uploadName=None):
|
||||||
# stored.
|
'''p_fsPath is the path of the file on disk.
|
||||||
|
- If p_inDb is True, this FileInfo will be stored in the database and
|
||||||
|
will hold metadata about a File field whose content will lie in the
|
||||||
|
database-controlled filesystem. In this case, p_fsPath is the path
|
||||||
|
of the file *relative* to the root DB folder. We avoid storing
|
||||||
|
absolute paths in order to ease the transfer of databases from one
|
||||||
|
place to the other. Moreover, p_fsPath does not include the
|
||||||
|
filename, that will be computed later, from the field name.
|
||||||
|
|
||||||
|
- If p_inDb is False, this FileInfo is a simple temporary object
|
||||||
|
representing any file on the filesystem (not necessarily in the
|
||||||
|
db-controlled filesystem). For instance, it could represent a temp
|
||||||
|
file generated from a Pod field in the OS temp folder. In this
|
||||||
|
case, p_fsPath is the absolute path to the file, including the
|
||||||
|
filename. If you manipulate such a FileInfo instance, please avoid
|
||||||
|
using methods that are used by Appy to manipulate
|
||||||
|
database-controlled files (like methods getFilePath, removeFile,
|
||||||
|
writeFile or copyFile).'''
|
||||||
self.fsPath = fsPath
|
self.fsPath = fsPath
|
||||||
self.fsName = None # The name of the file in fsPath
|
self.fsName = None # The name of the file in fsPath
|
||||||
self.uploadName = None # The name of the uploaded file
|
self.uploadName = uploadName # The name of the uploaded file
|
||||||
self.size = 0 # Its size, in bytes
|
self.size = 0 # Its size, in bytes
|
||||||
self.mimeType = None # Its MIME type
|
self.mimeType = None # Its MIME type
|
||||||
self.modified = None # The last modification date for this file.
|
self.modified = None # The last modification date for this file.
|
||||||
|
# Complete metadata if p_inDb is False
|
||||||
|
if not inDb:
|
||||||
|
self.fsName = '' # Already included in self.fsPath.
|
||||||
|
# We will not store p_inDb. Checking if self.fsName is the empty
|
||||||
|
# string is equivalent.
|
||||||
|
fileInfo = os.stat(self.fsPath)
|
||||||
|
self.size = fileInfo.st_size
|
||||||
|
self.mimeType = guessMimeType(self.fsPath)
|
||||||
|
from DateTime import DateTime
|
||||||
|
self.modified = DateTime(fileInfo.st_mtime)
|
||||||
|
|
||||||
def getFilePath(self, obj):
|
def getFilePath(self, obj):
|
||||||
'''Returns the absolute file name of the file on disk that corresponds
|
'''Returns the absolute file name of the file on disk that corresponds
|
||||||
to this FileInfo instance.'''
|
to this FileInfo instance.'''
|
||||||
dbFolder, folder = obj.o.getFsFolder()
|
dbFolder, folder = obj.o.getFsFolder()
|
||||||
return os.path.join(dbFolder, folder, self.fsName)
|
return osPathJoin(dbFolder, folder, self.fsName)
|
||||||
|
|
||||||
def removeFile(self, dbFolder, removeEmptyFolders=False):
|
def removeFile(self, dbFolder='', removeEmptyFolders=False):
|
||||||
'''Removes the file from the filesystem.'''
|
'''Removes the file from the filesystem.'''
|
||||||
try:
|
try:
|
||||||
os.remove(os.path.join(dbFolder, self.fsPath, self.fsName))
|
os.remove(osPathJoin(dbFolder, self.fsPath, self.fsName))
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
# If the current ZODB transaction is re-triggered, the file may
|
# If the current ZODB transaction is re-triggered, the file may
|
||||||
# already have been deleted.
|
# already have been deleted.
|
||||||
|
@ -62,7 +106,7 @@ class FileInfo:
|
||||||
# if this removal leaves them empty (unless p_removeEmptyFolders is
|
# if this removal leaves them empty (unless p_removeEmptyFolders is
|
||||||
# False).
|
# False).
|
||||||
if removeEmptyFolders:
|
if removeEmptyFolders:
|
||||||
sutils.FolderDeleter.deleteEmpty(os.path.join(dbFolder,self.fsPath))
|
sutils.FolderDeleter.deleteEmpty(osPathJoin(dbFolder,self.fsPath))
|
||||||
|
|
||||||
def normalizeFileName(self, name):
|
def normalizeFileName(self, name):
|
||||||
'''Normalizes file p_name.'''
|
'''Normalizes file p_name.'''
|
||||||
|
@ -118,7 +162,7 @@ class FileInfo:
|
||||||
self.uploadName = name
|
self.uploadName = name
|
||||||
self.fsName = '%s%s' % (fieldName, os.path.splitext(name)[1].lower())
|
self.fsName = '%s%s' % (fieldName, os.path.splitext(name)[1].lower())
|
||||||
# Write the file on disk (and compute/get its size in bytes)
|
# Write the file on disk (and compute/get its size in bytes)
|
||||||
fsName = os.path.join(dbFolder, self.fsPath, self.fsName)
|
fsName = osPathJoin(dbFolder, self.fsPath, self.fsName)
|
||||||
f = file(fsName, 'wb')
|
f = file(fsName, 'wb')
|
||||||
if fileType == 'FileUpload':
|
if fileType == 'FileUpload':
|
||||||
# Write the FileUpload instance on disk.
|
# Write the FileUpload instance on disk.
|
||||||
|
@ -150,21 +194,22 @@ class FileInfo:
|
||||||
self.modified = DateTime()
|
self.modified = DateTime()
|
||||||
|
|
||||||
def copyFile(self, fieldName, filePath, dbFolder):
|
def copyFile(self, fieldName, filePath, dbFolder):
|
||||||
'''Copies the "external" file stored at _filePath in the db-controlled
|
'''Copies the "external" file stored at p_filePath in the db-controlled
|
||||||
file system, for storing a value for p_fieldName.'''
|
file system, for storing a value for p_fieldName.'''
|
||||||
# Set names for the file
|
# Set names for the file
|
||||||
name = self.normalizeFileName(filePath)
|
name = self.normalizeFileName(filePath)
|
||||||
self.uploadName = name
|
self.uploadName = name
|
||||||
self.fsName = '%s%s' % (fieldName, os.path.splitext(name)[1])
|
self.fsName = '%s%s' % (fieldName, os.path.splitext(name)[1])
|
||||||
# Set mimeType
|
# Set mimeType
|
||||||
self.mimeType= mimetypes.guess_type(filePath)[0] or File.defaultMimeType
|
self.mimeType = guessMimeType(filePath)
|
||||||
# Copy the file
|
# Copy the file
|
||||||
shutil.copyfile(filePath, self.fsName)
|
fsName = osPathJoin(dbFolder, self.fsPath, self.fsName)
|
||||||
|
shutil.copyfile(filePath, fsName)
|
||||||
from DateTime import DateTime
|
from DateTime import DateTime
|
||||||
self.modified = DateTime()
|
self.modified = DateTime()
|
||||||
self.size = os.stat(self.fsName).st_size
|
self.size = os.stat(fsName).st_size
|
||||||
|
|
||||||
def writeResponse(self, response, dbFolder):
|
def writeResponse(self, response, dbFolder=''):
|
||||||
'''Writes this file in the HTTP p_response object.'''
|
'''Writes this file in the HTTP p_response object.'''
|
||||||
# As a preamble, initialise response headers.
|
# As a preamble, initialise response headers.
|
||||||
header = response.setHeader
|
header = response.setHeader
|
||||||
|
@ -176,7 +221,7 @@ class FileInfo:
|
||||||
#sh('Cachecontrol', 'no-cache')
|
#sh('Cachecontrol', 'no-cache')
|
||||||
#sh('Expires', 'Thu, 11 Dec 1975 12:05:05 GMT')
|
#sh('Expires', 'Thu, 11 Dec 1975 12:05:05 GMT')
|
||||||
# Write the file in the response
|
# Write the file in the response
|
||||||
fsName = os.path.join(dbFolder, self.fsPath, self.fsName)
|
fsName = osPathJoin(dbFolder, self.fsPath, self.fsName)
|
||||||
f = file(fsName, 'rb')
|
f = file(fsName, 'rb')
|
||||||
while True:
|
while True:
|
||||||
chunk = f.read(self.BYTES)
|
chunk = f.read(self.BYTES)
|
||||||
|
@ -282,28 +327,6 @@ class File(Field):
|
||||||
historized, mapping, label, sdefault, scolspan, swidth,
|
historized, mapping, label, sdefault, scolspan, swidth,
|
||||||
sheight, True)
|
sheight, True)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def getFileObject(filePath, fileName=None, zope=False):
|
|
||||||
'''Returns a File instance as can be stored in the database or
|
|
||||||
manipulated in code, filled with content from a file on disk,
|
|
||||||
located at p_filePath. If you want to give it a name that is more
|
|
||||||
sexy than the actual basename of p_filePath, specify it in
|
|
||||||
p_fileName.
|
|
||||||
|
|
||||||
If p_zope is True, it will be the raw Zope object = an instance of
|
|
||||||
OFS.Image.File. Else, it will be a FileWrapper instance from Appy.'''
|
|
||||||
f = file(filePath, 'rb')
|
|
||||||
if not fileName:
|
|
||||||
fileName = os.path.basename(filePath)
|
|
||||||
fileId = 'file.%f' % time.time()
|
|
||||||
import OFS.Image
|
|
||||||
res = OFS.Image.File(fileId, fileName, f)
|
|
||||||
res.filename = fileName
|
|
||||||
res.content_type = mimetypes.guess_type(fileName)[0]
|
|
||||||
f.close()
|
|
||||||
if not zope: res = sutils.FileWrapper(res)
|
|
||||||
return res
|
|
||||||
|
|
||||||
def getRequestValue(self, request, requestName=None):
|
def getRequestValue(self, request, requestName=None):
|
||||||
name = requestName or self.name
|
name = requestName or self.name
|
||||||
return request.get('%s_file' % name)
|
return request.get('%s_file' % name)
|
||||||
|
@ -387,7 +410,7 @@ class File(Field):
|
||||||
fileName, fileContent, mimeType = value
|
fileName, fileContent, mimeType = value
|
||||||
if not fileName:
|
if not fileName:
|
||||||
raise Exception(WRONG_FILE_TUPLE)
|
raise Exception(WRONG_FILE_TUPLE)
|
||||||
mimeType = mimeType or mimetypes.guess_type(fileName)[0]
|
mimeType = mimeType or guessMimeType(fileName)
|
||||||
info.writeFile(self.name, (fileName, fileContent, mimeType),
|
info.writeFile(self.name, (fileName, fileContent, mimeType),
|
||||||
dbFolder)
|
dbFolder)
|
||||||
# Store the FileInfo instance in the database.
|
# Store the FileInfo instance in the database.
|
||||||
|
|
149
fields/pod.py
149
fields/pod.py
|
@ -16,10 +16,12 @@
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
import time, os, os.path
|
import time, os, os.path
|
||||||
|
from file import FileInfo
|
||||||
|
from appy import Object
|
||||||
from appy.fields import Field
|
from appy.fields import Field
|
||||||
from appy.px import Px
|
from appy.px import Px
|
||||||
from file import File
|
|
||||||
from appy.gen.layout import Table
|
from appy.gen.layout import Table
|
||||||
|
from appy.gen import utils as gutils
|
||||||
from appy.pod import PodError
|
from appy.pod import PodError
|
||||||
from appy.pod.renderer import Renderer
|
from appy.pod.renderer import Renderer
|
||||||
from appy.shared import utils as sutils
|
from appy.shared import utils as sutils
|
||||||
|
@ -34,19 +36,28 @@ class Pod(Field):
|
||||||
POD_ERROR = 'An error occurred while generating the document. Please ' \
|
POD_ERROR = 'An error occurred while generating the document. Please ' \
|
||||||
'contact the system administrator.'
|
'contact the system administrator.'
|
||||||
DELETE_TEMP_DOC_ERROR = 'A temporary document could not be removed. %s.'
|
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.'
|
||||||
|
|
||||||
pxView = pxCell = Px('''
|
pxView = pxCell = Px('''
|
||||||
<!-- Ask action -->
|
<table cellpadding="0" cellspacing="0">
|
||||||
<x if="field.askAction"
|
<tr>
|
||||||
var2="doLabel='%s_askaction' % field.labelId;
|
<td for="template in field.getVisibleTemplates(obj)">
|
||||||
chekboxId='%s_%s_cb' % (zobj.UID(), name)">
|
<table cellpadding="0" cellspacing="0" class="podTable">
|
||||||
<input type="checkbox" name=":doLabel" id=":chekboxId"/>
|
<tr>
|
||||||
<label lfor=":chekboxId" class="discreet">:_(doLabel)"></label>
|
<td for="fmt in field.getOutputFormats(obj)">
|
||||||
</x>
|
<img src=":url(fmt)" title=":fmt.upper()" class="clickable"
|
||||||
<img for="fmt in field.getOutputFormats(zobj)" src=":url(fmt)"
|
onclick=":'generatePodDocument(%s,%s,%s,%s,%s)' % \
|
||||||
onclick=":'generatePodDocument(%s, %s, %s, %s)' % \
|
(q(obj.uid), q(name), q(template), q(fmt), \
|
||||||
(q(zobj.UID()), q(name), q(fmt), q(ztool.getQueryInfo()))"
|
q(ztool.getQueryInfo()))"/>
|
||||||
title=":fmt.capitalize()" class="clickable"/>''')
|
</td>
|
||||||
|
<td class="podName">:field.getTemplateName(obj, template)</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>''')
|
||||||
|
|
||||||
pxEdit = pxSearch = ''
|
pxEdit = pxSearch = ''
|
||||||
|
|
||||||
|
@ -56,26 +67,43 @@ class Pod(Field):
|
||||||
specificWritePermission=False, width=None, height=None,
|
specificWritePermission=False, width=None, height=None,
|
||||||
maxChars=None, colspan=1, master=None, masterValue=None,
|
maxChars=None, colspan=1, master=None, masterValue=None,
|
||||||
focus=False, historized=False, mapping=None, label=None,
|
focus=False, historized=False, mapping=None, label=None,
|
||||||
template=None, context=None, action=None, askAction=False,
|
template=None, templateName=None, showTemplate=None,
|
||||||
stylesMapping={}, formats=None, freezeFormat='pdf'):
|
context=None, stylesMapping={}, formats=None,
|
||||||
# The following param stores the path to a POD template
|
freezeFormat='pdf'):
|
||||||
self.template = template
|
# Param "template" stores the path to the pod template(s).
|
||||||
|
if not template: raise Exception(Pod.NO_TEMPLATE)
|
||||||
|
if isinstance(template, basestring):
|
||||||
|
self.template = [template]
|
||||||
|
else:
|
||||||
|
self.template = template
|
||||||
|
# Param "templateName", if specified, is a method that will be called
|
||||||
|
# with the current template (from self.template) as single arg and must
|
||||||
|
# return the name of this template. If self.template stores a single
|
||||||
|
# template, you have no need to use param "templateName". Simply use the
|
||||||
|
# field label to name the template. But if you have a multi-pod field
|
||||||
|
# (with several templates specified as a list or tuple in param
|
||||||
|
# "template"), you will probably choose to hide the field label and use
|
||||||
|
# param "templateName" to give a specific name to every template. If
|
||||||
|
# "template" contains several templates and "templateName" is None, Appy
|
||||||
|
# will produce names from template filenames.
|
||||||
|
self.templateName = templateName
|
||||||
|
# "showTemplate", if specified, must be a method that will be called
|
||||||
|
# with the current template as single arg and that must return True if
|
||||||
|
# the template can be seen by the current user. "showTemplate" comes in
|
||||||
|
# addition to self.show. self.show dictates the visibility of the whole
|
||||||
|
# field (ie, all templates from self.template) while "showTemplate"
|
||||||
|
# dictates the visiblity of a specific template within self.template.
|
||||||
|
self.showTemplate = showTemplate
|
||||||
# The context is a dict containing a specific pod context, or a method
|
# The context is a dict containing a specific pod context, or a method
|
||||||
# that returns such a dict.
|
# that returns such a dict.
|
||||||
self.context = context
|
self.context = context
|
||||||
# Next one is a method that will be triggered after the document has
|
|
||||||
# been generated.
|
|
||||||
self.action = action
|
|
||||||
# If askAction is True, the action will be triggered only if the user
|
|
||||||
# checks a checkbox, which, by default, will be unchecked.
|
|
||||||
self.askAction = askAction
|
|
||||||
# A global styles mapping that would apply to the whole template
|
# A global styles mapping that would apply to the whole template
|
||||||
self.stylesMapping = stylesMapping
|
self.stylesMapping = stylesMapping
|
||||||
# What are the output formats when generating documents from this pod ?
|
# What are the output formats when generating documents from this pod ?
|
||||||
self.formats = formats
|
self.formats = formats
|
||||||
if not formats:
|
if not formats:
|
||||||
# Compute default ones
|
# Compute default ones
|
||||||
if template.endswith('.ods'):
|
if self.template[0].endswith('.ods'):
|
||||||
self.formats = ('xls', 'ods')
|
self.formats = ('xls', 'ods')
|
||||||
else:
|
else:
|
||||||
self.formats = ('pdf', 'doc', 'odt')
|
self.formats = ('pdf', 'doc', 'odt')
|
||||||
|
@ -103,32 +131,53 @@ class Pod(Field):
|
||||||
fileName = getattr(obj.o.aq_base, self.name).filename
|
fileName = getattr(obj.o.aq_base, self.name).filename
|
||||||
return (os.path.splitext(fileName)[1][1:],)
|
return (os.path.splitext(fileName)[1][1:],)
|
||||||
|
|
||||||
|
def getTemplateName(self, obj, fileName):
|
||||||
|
'''Gets the name of a template given its p_fileName.'''
|
||||||
|
res = None
|
||||||
|
if self.templateName:
|
||||||
|
# Use the method specified in self.templateName.
|
||||||
|
res = self.templateName(obj, fileName)
|
||||||
|
# Else, deduce a nice name from p_fileName.
|
||||||
|
if not res:
|
||||||
|
name = os.path.splitext(os.path.basename(fileName))[0]
|
||||||
|
res = gutils.produceNiceMessage(name)
|
||||||
|
return res
|
||||||
|
|
||||||
|
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.
|
||||||
|
res = []
|
||||||
|
for template in self.template:
|
||||||
|
if self.showTemplate(obj, template):
|
||||||
|
res.append(template)
|
||||||
|
return res
|
||||||
|
|
||||||
def getValue(self, obj):
|
def getValue(self, obj):
|
||||||
'''Gets, on_obj, the value conforming to self's type definition. For a
|
'''For a pod field, getting its value means computing a pod document or
|
||||||
Pod field, if a file is stored in the field, it means that the
|
returning a frozen one. A pod field differs from other field types
|
||||||
field has been frozen. Else, it means that the value must be
|
because there can be several ways to produce the field value (ie:
|
||||||
retrieved by calling pod to compute the result.'''
|
self.template can hold various templates; output file format can be
|
||||||
rq = getattr(obj, 'REQUEST', None)
|
odt, pdf,.... We get those precisions about the way to produce the
|
||||||
res = getattr(obj.aq_base, self.name, None)
|
file from the request object. If we don't find the request object (or
|
||||||
if res and res.size:
|
if it does not exist, ie, when Zope runs in test mode), we use
|
||||||
# Return the frozen file.
|
default values.'''
|
||||||
return sutils.FileWrapper(res)
|
rq = getattr(obj, 'REQUEST') or Object()
|
||||||
# If we are here, it means that we must call pod to compute the file.
|
|
||||||
# A Pod field differs from other field types because there can be
|
|
||||||
# several ways to produce the field value (ie: output file format can be
|
|
||||||
# odt, pdf,...; self.action can be executed or not...). 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.
|
|
||||||
obj = obj.appy()
|
obj = obj.appy()
|
||||||
|
template = rq.get('template') or self.template[0]
|
||||||
|
# Security check.
|
||||||
|
if not self.showTemplate(obj, template):
|
||||||
|
raise Exception(self.UNAVAILABLE_TEMPLATE)
|
||||||
|
# Return the frozen document if frozen.
|
||||||
|
# if ...
|
||||||
|
# We must call pod to compute a pod document from "template".
|
||||||
tool = obj.tool
|
tool = obj.tool
|
||||||
diskFolder = tool.getDiskFolder()
|
diskFolder = tool.getDiskFolder()
|
||||||
# Get the path to the pod template.
|
# Get the path to the pod template.
|
||||||
templatePath = os.path.join(diskFolder, self.template)
|
templatePath = os.path.join(diskFolder, template)
|
||||||
if not os.path.isfile(templatePath):
|
if not os.path.isfile(templatePath):
|
||||||
raise Exception('Pod template not found at %s.' % templatePath)
|
raise Exception(self.TEMPLATE_NOT_FOUND % templatePath)
|
||||||
# Get the output format
|
# Get the output format
|
||||||
outputFormat = getattr(rq, 'podFormat', 'odt')
|
outputFormat = rq.get('podFormat', 'odt')
|
||||||
# Get or compute the specific POD context
|
# Get or compute the specific POD context
|
||||||
specificContext = None
|
specificContext = None
|
||||||
if callable(self.context):
|
if callable(self.context):
|
||||||
|
@ -190,24 +239,20 @@ class Pod(Field):
|
||||||
obj.log(str(pe).strip(), type='error')
|
obj.log(str(pe).strip(), type='error')
|
||||||
return Pod.POD_ERROR
|
return Pod.POD_ERROR
|
||||||
# Give a friendly name for this file
|
# Give a friendly name for this file
|
||||||
fileName = obj.translate(self.labelId)
|
fileName = self.getTemplateName(obj, template)
|
||||||
if not isQueryRelated:
|
if not isQueryRelated:
|
||||||
# This is a POD for a single object: personalize the file name with
|
# This is a POD for a single object: personalize the file name with
|
||||||
# the object title.
|
# the object title.
|
||||||
fileName = '%s-%s' % (obj.title, fileName)
|
fileName = '%s-%s' % (obj.title, fileName)
|
||||||
fileName = tool.normalize(fileName) + '.' + outputFormat
|
fileName = tool.normalize(fileName) + '.' + outputFormat
|
||||||
# Get a FileWrapper instance from the temp file on the filesystem
|
# Get a FileInfo instance to manipulate the temp file on the filesystem.
|
||||||
res = File.getFileObject(tempFileName, fileName)
|
return FileInfo(tempFileName, inDb=False, uploadName=fileName)
|
||||||
# Execute the related action if relevant
|
|
||||||
doAction = getattr(rq, 'askAction', False) in ('True', True)
|
|
||||||
if doAction and self.action: self.action(obj, podContext)
|
|
||||||
# Returns the doc and removes the temp file
|
# Returns the doc and removes the temp file
|
||||||
try:
|
try:
|
||||||
os.remove(tempFileName)
|
os.remove(tempFileName)
|
||||||
except OSError, oe:
|
except Exception, e:
|
||||||
obj.log(Pod.DELETE_TEMP_DOC_ERROR % str(oe).strip(), type='warning')
|
obj.log(Pod.DELETE_TEMP_DOC_ERROR % str(e).strip(), type='warning')
|
||||||
except IOError, ie:
|
|
||||||
obj.log(Pod.DELETE_TEMP_DOC_ERROR % str(ie).strip(), type='warning')
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def store(self, obj, value):
|
def store(self, obj, value):
|
||||||
|
|
|
@ -296,12 +296,6 @@ class FieldDescriptor:
|
||||||
label = '%s_%s_addConfirm' % (self.classDescr.name, self.fieldName)
|
label = '%s_%s_addConfirm' % (self.classDescr.name, self.fieldName)
|
||||||
self.i18n(label, po.CONFIRM, nice=False)
|
self.i18n(label, po.CONFIRM, nice=False)
|
||||||
|
|
||||||
def walkPod(self):
|
|
||||||
# Add i18n-specific messages
|
|
||||||
if self.appyType.askAction:
|
|
||||||
label = '%s_%s_askaction' % (self.classDescr.name, self.fieldName)
|
|
||||||
self.i18n(label, po.POD_ASKACTION, nice=False)
|
|
||||||
|
|
||||||
def walkList(self):
|
def walkList(self):
|
||||||
# Add i18n-specific messages
|
# Add i18n-specific messages
|
||||||
for name, field in self.appyType.fields:
|
for name, field in self.appyType.fields:
|
||||||
|
@ -361,8 +355,6 @@ class FieldDescriptor:
|
||||||
elif self.appyType.type == 'Action': self.walkAction()
|
elif self.appyType.type == 'Action': self.walkAction()
|
||||||
# Manage things which are specific to Ref types
|
# Manage things which are specific to Ref types
|
||||||
elif self.appyType.type == 'Ref': self.walkRef()
|
elif self.appyType.type == 'Ref': self.walkRef()
|
||||||
# Manage things which are specific to Pod types
|
|
||||||
elif self.appyType.type == 'Pod': self.walkPod()
|
|
||||||
# Manage things which are specific to List types
|
# Manage things which are specific to List types
|
||||||
elif self.appyType.type == 'List': self.walkList()
|
elif self.appyType.type == 'List': self.walkList()
|
||||||
# Manage things which are specific to Calendar types
|
# Manage things which are specific to Calendar types
|
||||||
|
|
|
@ -120,12 +120,10 @@ class ToolMixin(BaseMixin):
|
||||||
# An error has occurred, and p_res contains the error message
|
# An error has occurred, and p_res contains the error message
|
||||||
obj.say(res)
|
obj.say(res)
|
||||||
return self.goto(rq.get('HTTP_REFERER'))
|
return self.goto(rq.get('HTTP_REFERER'))
|
||||||
# res contains a FileWrapper instance.
|
# res contains a FileInfo instance.
|
||||||
response = rq.RESPONSE
|
res.writeResponse(rq.RESPONSE)
|
||||||
response.setHeader('Content-Type', res.mimeType)
|
# (Try to) delete the temp file on disk.
|
||||||
response.setHeader('Content-Disposition',
|
res.removeFile()
|
||||||
'inline;filename="%s"' % res.name)
|
|
||||||
return res.content
|
|
||||||
|
|
||||||
def getAppName(self):
|
def getAppName(self):
|
||||||
'''Returns the name of the application.'''
|
'''Returns the name of the application.'''
|
||||||
|
|
|
@ -689,6 +689,7 @@ class BaseMixin:
|
||||||
'''Returns the database value of field named p_name for p_self.'''
|
'''Returns the database value of field named p_name for p_self.'''
|
||||||
if layoutType == 'search': return # No object in search screens.
|
if layoutType == 'search': return # No object in search screens.
|
||||||
field = self.getAppyType(name)
|
field = self.getAppyType(name)
|
||||||
|
if field.type == 'Pod': return
|
||||||
if '*' not in name: return field.getValue(self)
|
if '*' not in name: return field.getValue(self)
|
||||||
# The field is an inner field from a List.
|
# The field is an inner field from a List.
|
||||||
listName, name, i = name.split('*')
|
listName, name, i = name.split('*')
|
||||||
|
|
|
@ -23,7 +23,6 @@ fallbacks = {'en': 'en-us en-ca',
|
||||||
|
|
||||||
# Default values for i18n labels whose ids are not fixed.
|
# Default values for i18n labels whose ids are not fixed.
|
||||||
CONFIG = "Configuration panel for product '%s'"
|
CONFIG = "Configuration panel for product '%s'"
|
||||||
POD_ASKACTION = 'Trigger related action'
|
|
||||||
EMAIL_SUBJECT = '${siteTitle} - Action \\"${transitionName}\\" has been ' \
|
EMAIL_SUBJECT = '${siteTitle} - Action \\"${transitionName}\\" has been ' \
|
||||||
'performed on element entitled \\"${objectTitle}\\".'
|
'performed on element entitled \\"${objectTitle}\\".'
|
||||||
EMAIL_BODY = 'You can consult this element at ${objectUrl}.'
|
EMAIL_BODY = 'You can consult this element at ${objectUrl}.'
|
||||||
|
|
|
@ -156,3 +156,5 @@ td.search { padding-top: 8px }
|
||||||
.homeTable th { padding-top: 5px; font-size: 105% }
|
.homeTable th { padding-top: 5px; font-size: 105% }
|
||||||
.first { margin-top: 0px }
|
.first { margin-top: 0px }
|
||||||
.error { margin: 5px }
|
.error { margin: 5px }
|
||||||
|
.podName { font-size: 90%; padding-left: 3px }
|
||||||
|
.podTable { margin-left: 15px }
|
||||||
|
|
|
@ -521,20 +521,16 @@ function toggleCookie(cookieId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function that allows to generate a document from a pod template.
|
// Function that allows to generate a document from a pod template.
|
||||||
function generatePodDocument(contextUid, fieldName, podFormat, queryData,
|
function generatePodDocument(uid, fieldName, template, podFormat, queryData,
|
||||||
customParams) {
|
customParams) {
|
||||||
var theForm = document.getElementById("podTemplateForm");
|
var theForm = document.getElementById("podTemplateForm");
|
||||||
theForm.objectUid.value = contextUid;
|
theForm.objectUid.value = uid;
|
||||||
theForm.fieldName.value = fieldName;
|
theForm.fieldName.value = fieldName;
|
||||||
|
theForm.template.value = template;
|
||||||
theForm.podFormat.value = podFormat;
|
theForm.podFormat.value = podFormat;
|
||||||
theForm.askAction.value = "False";
|
|
||||||
theForm.queryData.value = queryData;
|
theForm.queryData.value = queryData;
|
||||||
if (customParams) { theForm.customParams.value = customParams; }
|
if (customParams) { theForm.customParams.value = customParams; }
|
||||||
else { theForm.customParams.value = ''; }
|
else { theForm.customParams.value = ''; }
|
||||||
var askActionWidget = document.getElementById(contextUid + '_' + fieldName + '_cb');
|
|
||||||
if (askActionWidget && askActionWidget.checked) {
|
|
||||||
theForm.askAction.value = "True";
|
|
||||||
}
|
|
||||||
theForm.submit();
|
theForm.submit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -93,7 +93,7 @@ class ToolWrapper(AbstractWrapper):
|
||||||
# PXs for graphical elements shown on every page
|
# PXs for graphical elements shown on every page
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
# Global elements included in every page.
|
# Global elements included in every page.
|
||||||
pxPagePrologue = Px('''<x>
|
pxPagePrologue = Px('''
|
||||||
<!-- Include type-specific CSS and JS. -->
|
<!-- Include type-specific CSS and JS. -->
|
||||||
<x if="cssJs">
|
<x if="cssJs">
|
||||||
<link for="cssFile in cssJs['css']" rel="stylesheet" type="text/css"
|
<link for="cssFile in cssJs['css']" rel="stylesheet" type="text/css"
|
||||||
|
@ -133,12 +133,11 @@ class ToolWrapper(AbstractWrapper):
|
||||||
action=":ztool.absolute_url() + '/generateDocument'">
|
action=":ztool.absolute_url() + '/generateDocument'">
|
||||||
<input type="hidden" name="objectUid"/>
|
<input type="hidden" name="objectUid"/>
|
||||||
<input type="hidden" name="fieldName"/>
|
<input type="hidden" name="fieldName"/>
|
||||||
|
<input type="hidden" name="template"/>
|
||||||
<input type="hidden" name="podFormat"/>
|
<input type="hidden" name="podFormat"/>
|
||||||
<input type="hidden" name="askAction"/>
|
|
||||||
<input type="hidden" name="queryData"/>
|
<input type="hidden" name="queryData"/>
|
||||||
<input type="hidden" name="customParams"/>
|
<input type="hidden" name="customParams"/>
|
||||||
</form>
|
</form>''')
|
||||||
</x>''')
|
|
||||||
|
|
||||||
pxPageBottom = Px('''
|
pxPageBottom = Px('''
|
||||||
<script var="info=zobj.getSlavesRequestInfo(page)"
|
<script var="info=zobj.getSlavesRequestInfo(page)"
|
||||||
|
|
|
@ -797,14 +797,12 @@ class AbstractWrapper(object):
|
||||||
zopeObj.reindex()
|
zopeObj.reindex()
|
||||||
return appyObj
|
return appyObj
|
||||||
|
|
||||||
def freeze(self, fieldName, doAction=False):
|
def freeze(self, fieldName):
|
||||||
'''This method freezes a POD document. TODO: allow to freeze Computed
|
'''This method freezes a POD document. TODO: allow to freeze Computed
|
||||||
fields.'''
|
fields.'''
|
||||||
rq = self.request
|
rq = self.request
|
||||||
field = self.o.getAppyType(fieldName)
|
field = self.o.getAppyType(fieldName)
|
||||||
if field.type != 'Pod': raise 'Cannot freeze non-Pod field.'
|
if field.type != 'Pod': raise 'Cannot freeze non-Pod field.'
|
||||||
# Perform the related action if required.
|
|
||||||
if doAction: self.request.set('askAction', True)
|
|
||||||
# Set the freeze format
|
# Set the freeze format
|
||||||
rq.set('podFormat', field.freezeFormat)
|
rq.set('podFormat', field.freezeFormat)
|
||||||
# Generate the document.
|
# Generate the document.
|
||||||
|
|
Loading…
Reference in a new issue