[gen] Pod fields can now be configured with several templates.

This commit is contained in:
Gaetan Delannay 2014-03-19 23:13:31 +01:00
parent 889289407f
commit ecc3a8c39b
10 changed files with 175 additions and 122 deletions

View file

@ -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.

View file

@ -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'):
# 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 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):

View file

@ -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

View file

@ -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.'''

View file

@ -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('*')

View file

@ -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}.'

View file

@ -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 }

View file

@ -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();
} }

View file

@ -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)"

View file

@ -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.