[gen] pod fields now read pod templates directly from disk. Fields 'template' and 'formats' that were generated into the database (and editable through-the-web) are now removed. This simplification will allow in a second step to define several templates for a unique pod field, ie: multiDoc = Pod(template='od/Item*.odt'). [gen] Additionally, fields tool.numberOfSearchColumnsForXXX and tool.searchFieldsForXXX are not generated anymore and are replace by static class attributes class.numberOfSearchColumns and class.searchFields.

This commit is contained in:
Gaetan Delannay 2013-09-21 17:46:42 +02:00
parent c5930edd2d
commit e1b83574c5
8 changed files with 50 additions and 255 deletions

View file

@ -15,7 +15,7 @@
# Appy. If not, see <http://www.gnu.org/licenses/>. # Appy. If not, see <http://www.gnu.org/licenses/>.
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
import time, os, os.path, StringIO import time, os, os.path
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 file import File
@ -43,7 +43,7 @@ class Pod(Field):
<input type="checkbox" name=":doLabel" id=":chekboxId"/> <input type="checkbox" name=":doLabel" id=":chekboxId"/>
<label lfor=":chekboxId" class="discreet">:_(doLabel)"></label> <label lfor=":chekboxId" class="discreet">:_(doLabel)"></label>
</x> </x>
<img for="fmt in field.getToolInfo(obj)[1]" src=":url(fmt)" <img for="fmt in field.getOutputFormats(zobj)" src=":url(fmt)"
onclick=":'generatePodDocument(%s, %s, %s, %s)' % \ onclick=":'generatePodDocument(%s, %s, %s, %s)' % \
(q(zobj.UID()), q(name), q(fmt), q(ztool.getQueryInfo()))" (q(zobj.UID()), q(name), q(fmt), q(ztool.getQueryInfo()))"
title=":fmt.capitalize()" class="clickable"/> title=":fmt.capitalize()" class="clickable"/>
@ -58,7 +58,7 @@ class Pod(Field):
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, context=None, action=None, askAction=False,
stylesMapping={}, freezeFormat='pdf'): stylesMapping={}, formats=None, freezeFormat='pdf'):
# The following param stores the path to a POD template # The following param stores the path to a POD template
self.template = template self.template = template
# 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
@ -72,7 +72,14 @@ class Pod(Field):
self.askAction = askAction 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
# Freeze format is by PDF by default # What are the output formats when generating documents from this pod ?
if not formats:
# Compute default ones
if template.endswith('.ods'):
self.formats = ('xls', 'ods')
else:
self.formats = ('pdf', 'doc', 'odt')
# Freeze format is PDF by default.
self.freezeFormat = freezeFormat self.freezeFormat = freezeFormat
Field.__init__(self, None, (0,1), default, show, page, group, layouts, Field.__init__(self, None, (0,1), default, show, page, group, layouts,
move, indexed, searchable, specificReadPermission, move, indexed, searchable, specificReadPermission,
@ -86,26 +93,13 @@ class Pod(Field):
value = getattr(obj.o.aq_base, self.name, None) value = getattr(obj.o.aq_base, self.name, None)
return isinstance(value, obj.o.getProductConfig().File) return isinstance(value, obj.o.getProductConfig().File)
def getToolInfo(self, obj): def getOutputFormats(self, obj):
'''Gets information related to this field (p_self) that is available in '''Returns self.formats, excepted if there is a frozen document: in
the tool: the POD template and the available output formats. If this this case, only the format of the frozen doc is returned.'''
field is frozen, available output formats are not available anymore: if not self.isFrozen(obj): return self.formats
only the format of the frozen doc is returned.''' # The only available format is the one from the frozen document
tool = obj.tool fileName = getattr(obj.o.aq_base, self.name).filename
appyClass = tool.o.getAppyClass(obj.o.meta_type) return (os.path.splitext(fileName)[1][1:],)
# Get the output format(s)
if self.isFrozen(obj):
# The only available format is the one from the frozen document
fileName = getattr(obj.o.aq_base, self.name).filename
formats = (os.path.splitext(fileName)[1][1:],)
else:
# Available formats are those which are selected in the tool.
name = tool.getAttributeName('formats', appyClass, self.name)
formats = getattr(tool, name)
# Get the POD template
name = tool.getAttributeName('podTemplate', appyClass, self.name)
template = getattr(tool, name)
return (template, formats)
def getValue(self, obj): def getValue(self, obj):
'''Gets, on_obj, the value conforming to self's type definition. For a '''Gets, on_obj, the value conforming to self's type definition. For a
@ -121,17 +115,18 @@ class Pod(Field):
# A Pod field differs from other field types because there can be # 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 # 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 # odt, pdf,...; self.action can be executed or not...). We get those
# precisions about the way to produce the file from the request object # precisions about the way to produce the file from the request object.
# and from the tool. If we don't find the request object (or if it does # If we don't find the request object (or if it does not exist, ie,
# not exist, ie, when Zope runs in test mode), we use default values. # when Zope runs in test mode), we use default values.
obj = obj.appy() obj = obj.appy()
tool = obj.tool tool = obj.tool
# Get POD template and available formats from the tool. diskFolder = tool.getDiskFolder()
template, availFormats = self.getToolInfo(obj) # Get the path to the pod template.
templatePath = os.path.join(diskFolder, self.template)
if not os.path.isfile(templatePath):
raise Exception('Pod template not found at %s.' % templatePath)
# Get the output format # Get the output format
defaultFormat = 'pdf' outputFormat = getattr(rq, 'podFormat', 'odt')
if defaultFormat not in availFormats: defaultFormat = availFormats[0]
outputFormat = getattr(rq, 'podFormat', defaultFormat)
# 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):
@ -144,7 +139,7 @@ class Pod(Field):
# Define parameters to give to the appy.pod renderer # Define parameters to give to the appy.pod renderer
podContext = {'tool': tool, 'user': obj.user, 'self': obj, 'field':self, podContext = {'tool': tool, 'user': obj.user, 'self': obj, 'field':self,
'now': obj.o.getProductConfig().DateTime(), 'now': obj.o.getProductConfig().DateTime(),
'_': obj.translate, 'projectFolder': tool.getDiskFolder()} '_': obj.translate, 'projectFolder': diskFolder}
# If the POD document is related to a query, get it from the request, # If the POD document is related to a query, get it from the request,
# execute it and put the result in the context. # execute it and put the result in the context.
isQueryRelated = rq.get('queryData', None) isQueryRelated = rq.get('queryData', None)
@ -175,9 +170,8 @@ class Pod(Field):
stylesMapping = self.callMethod(obj, self.stylesMapping) stylesMapping = self.callMethod(obj, self.stylesMapping)
else: else:
stylesMapping = self.stylesMapping stylesMapping = self.stylesMapping
rendererParams = {'template': StringIO.StringIO(template.content), rendererParams = {'template': templatePath, 'context': podContext,
'context': podContext, 'result': tempFileName, 'result': tempFileName, 'stylesMapping':stylesMapping,
'stylesMapping': stylesMapping,
'imageResolver': tool.o.getApp()} 'imageResolver': tool.o.getApp()}
if tool.unoEnabledPython: if tool.unoEnabledPython:
rendererParams['pythonWithUnoPath'] = tool.unoEnabledPython rendererParams['pythonWithUnoPath'] = tool.unoEnabledPython
@ -191,7 +185,7 @@ class Pod(Field):
if not os.path.exists(tempFileName): if not os.path.exists(tempFileName):
# In some (most?) cases, when OO returns an error, the result is # In some (most?) cases, when OO returns an error, the result is
# nevertheless generated. # nevertheless generated.
obj.log(str(pe), 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 = obj.translate(self.labelId)
@ -209,9 +203,9 @@ class Pod(Field):
try: try:
os.remove(tempFileName) os.remove(tempFileName)
except OSError, oe: except OSError, oe:
obj.log(Pod.DELETE_TEMP_DOC_ERROR % str(oe), type='warning') obj.log(Pod.DELETE_TEMP_DOC_ERROR % str(oe).strip(), type='warning')
except IOError, ie: except IOError, ie:
obj.log(Pod.DELETE_TEMP_DOC_ERROR % str(ie), type='warning') 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

@ -6,7 +6,7 @@
import types, copy import types, copy
import appy.gen as gen import appy.gen as gen
import po import po
from model import ModelClass, toolFieldPrefixes from model import ModelClass
from utils import produceNiceMessage, getClassName from utils import produceNiceMessage, getClassName
TABS = 4 # Number of blanks in a Python indentation. TABS = 4 # Number of blanks in a Python indentation.
@ -260,21 +260,6 @@ class FieldDescriptor:
def __repr__(self): def __repr__(self):
return '<Field %s, %s>' % (self.fieldName, self.classDescr) return '<Field %s, %s>' % (self.fieldName, self.classDescr)
def getToolFieldMessage(self, fieldName):
'''Some attributes generated on the Tool class need a specific
default message, returned by this method.'''
res = fieldName
for prefix in toolFieldPrefixes:
fullPrefix = prefix + 'For'
if fieldName.startswith(fullPrefix):
messageId = 'MSG_%s' % prefix
res = getattr(po, messageId)
if res.find('%s') != -1:
# I must complete the message with the field name.
res = res % fieldName.split('_')[-1]
break
return res
def produceMessage(self, msgId, isLabel=True): def produceMessage(self, msgId, isLabel=True):
'''Gets the default label, description or help (depending on p_msgType) '''Gets the default label, description or help (depending on p_msgType)
for i18n message p_msgId.''' for i18n message p_msgId.'''
@ -283,10 +268,6 @@ class FieldDescriptor:
if isLabel: if isLabel:
niceDefault = True niceDefault = True
default = self.fieldName default = self.fieldName
# Some attributes need a specific predefined message
if isinstance(self.classDescr, ToolClassDescriptor):
default = self.getToolFieldMessage(self.fieldName)
if default != self.fieldName: niceDefault = False
return msgId, default, niceDefault return msgId, default, niceDefault
def walkString(self): def walkString(self):
@ -318,8 +299,6 @@ class FieldDescriptor:
if self.appyType.askAction: if self.appyType.askAction:
label = '%s_%s_askaction' % (self.classDescr.name, self.fieldName) label = '%s_%s_askaction' % (self.classDescr.name, self.fieldName)
self.i18n(label, po.POD_ASKACTION, nice=False) self.i18n(label, po.POD_ASKACTION, nice=False)
# Add the POD-related fields on the Tool
self.generator.tool.addPodRelatedFields(self)
def walkList(self): def walkList(self):
# Add i18n-specific messages # Add i18n-specific messages
@ -417,43 +396,6 @@ class ToolClassDescriptor(ClassDescriptor):
def isFolder(self, klass=None): return True def isFolder(self, klass=None): return True
def isRoot(self): return False def isRoot(self): return False
def addPodRelatedFields(self, fieldDescr):
'''Adds the fields needed in the Tool for configuring a Pod field.'''
className = fieldDescr.classDescr.name
# On what page and group to display those fields ?
pg = {'page': 'documents',
'group':gen.Group(fieldDescr.classDescr.klass.__name__,['50%']*2)}
# Add the field that will store the pod template.
fieldName = 'podTemplateFor%s_%s' % (className, fieldDescr.fieldName)
fieldType = gen.File(**pg)
self.addField(fieldName, fieldType)
# Add the field that will store the output format(s)
fieldName = 'formatsFor%s_%s' % (className, fieldDescr.fieldName)
fieldType = gen.String(validator=gen.Selection('getPodOutputFormats'),
multiplicity=(1,None), default=('odt',), **pg)
self.addField(fieldName, fieldType)
def addSearchRelatedFields(self, classDescr):
'''Adds, for class p_classDescr, attributes related to the search
functionality for class p_classDescr.'''
className = classDescr.name
# Field that defines how many columns are shown on the custom search
# screen.
fieldName = 'numberOfSearchColumnsFor%s' % className
fieldType = gen.Integer(default=3, page='userInterface',
group=classDescr.klass.__name__)
self.addField(fieldName, fieldType)
# Field that allows to select, among all indexed fields, what fields
# must really be used in the search screen.
fieldName = 'searchFieldsFor%s' % className
defaultValue = [a[0] for a in classDescr.getOrderedAppyAttributes(
condition='attrValue.indexed')]
if 'title' not in defaultValue: defaultValue.insert(0, 'title')
fieldType = gen.String(multiplicity=(0,None), validator=gen.Selection(
'_appy_getSearchableFields*%s' % className), default=defaultValue,
page='userInterface', group=classDescr.klass.__name__)
self.addField(fieldName, fieldType)
def addImportRelatedFields(self, classDescr): def addImportRelatedFields(self, classDescr):
'''Adds, for class p_classDescr, attributes related to the import '''Adds, for class p_classDescr, attributes related to the import
functionality for class p_classDescr.''' functionality for class p_classDescr.'''

View file

@ -716,8 +716,6 @@ class ZopeGenerator(Generator):
# import-related fields. # import-related fields.
for classDescr in self.getClasses(include='allButTool'): for classDescr in self.getClasses(include='allButTool'):
if not classDescr.isRoot(): continue if not classDescr.isRoot(): continue
# Add the search-related fields.
self.tool.addSearchRelatedFields(classDescr)
importMean = classDescr.getCreateMean('Import') importMean = classDescr.getCreateMean('Import')
if importMean: if importMean:
self.tool.addImportRelatedFields(classDescr) self.tool.addImportRelatedFields(classDescr)

View file

@ -189,37 +189,6 @@ class ZopeInstaller:
appyTool.log('Group "%s", related to global role "%s", was ' \ appyTool.log('Group "%s", related to global role "%s", was ' \
'created.' % (groupId, role)) 'created.' % (groupId, role))
# Create POD templates within the tool if required
for contentType in self.config.attributes.iterkeys():
appyClass = tool.getAppyClass(contentType)
if not appyClass: continue # May be an abstract class
wrapperClass = tool.getAppyClass(contentType, wrapper=True)
for appyType in wrapperClass.__fields__:
if appyType.type != 'Pod': continue
# Find the attribute that stores the template, and store on
# it the default one specified in the appyType if no
# template is stored yet.
attrName = appyTool.getAttributeName('podTemplate', appyClass,
appyType.name)
fileObject = getattr(appyTool, attrName)
if not fileObject or (fileObject.size == 0):
# There is no file. Put the one specified in the appyType.
fileName = os.path.join(appyTool.getDiskFolder(),
appyType.template)
if os.path.exists(fileName):
setattr(appyTool, attrName, fileName)
# If the template is ods, set the default format to ods
# (because default is odt)
if fileName.endswith('.ods'):
formats = appyTool.getAttributeName('formats',
appyClass, appyType.name)
setattr(appyTool, formats, ['ods'])
appyTool.log('Imported "%s" in the tool in ' \
'attribute "%s"'% (fileName, attrName))
else:
appyTool.log('Template "%s" was not found!' % \
fileName, type='error')
# Create or update Translation objects # Create or update Translation objects
translations = [t.o.id for t in appyTool.translations] translations = [t.o.id for t in appyTool.translations]
# We browse the languages supported by this application and check # We browse the languages supported by this application and check

View file

@ -203,15 +203,6 @@ class ToolMixin(BaseMixin):
cfg = self.getProductConfig() cfg = self.getProductConfig()
return [self.getAppyClass(k) for k in cfg.rootClasses] return [self.getAppyClass(k) for k in cfg.rootClasses]
def _appy_getSearchableFields(self, className):
'''Returns the (translated) names of fields that may be searched on
objects of type p_className (=indexed fields).'''
res = []
for field in self.getAllAppyTypes(className=className):
if field.indexed:
res.append((field.name, self.translate(field.labelId)))
return res
def getSearchInfo(self, className, refInfo=None): def getSearchInfo(self, className, refInfo=None):
'''Returns, as an object: '''Returns, as an object:
- the list of searchable fields (some among all indexed fields); - the list of searchable fields (some among all indexed fields);
@ -225,9 +216,13 @@ class ToolMixin(BaseMixin):
nbOfColumns = refField.queryNbCols nbOfColumns = refField.queryNbCols
else: else:
# The search is triggered from an app-wide search. # The search is triggered from an app-wide search.
tool = self.appy() klass = self.getAppyClass(className)
fieldNames = getattr(tool, 'searchFieldsFor%s' % className,()) fieldNames = getattr(klass, 'searchFields', None)
nbOfColumns = getattr(tool, 'numberOfSearchColumnsFor%s' %className) if not fieldNames:
# Gather all the indexed fields on this class.
fieldNames = [f.name for f in self.getAllAppyTypes(className) \
if f.indexed]
nbOfColumns = getattr(klass, 'numberOfSearchColumns', 3)
for name in fieldNames: for name in fieldNames:
field = self.getAppyType(name, className=className) field = self.getAppyType(name, className=className)
fields.append(field) fields.append(field)
@ -250,8 +245,7 @@ class ToolMixin(BaseMixin):
'''Must we show, on pxQueryResult, instances of p_className as a list or '''Must we show, on pxQueryResult, instances of p_className as a list or
as a grid?''' as a grid?'''
klass = self.getAppyClass(className) klass = self.getAppyClass(className)
if hasattr(klass, 'resultMode'): return klass.resultMode return getattr(klass, 'resultMode', 'list')
return 'list' # The default mode
def getImportElements(self, className): def getImportElements(self, className):
'''Returns the list of elements that can be imported from p_path for '''Returns the list of elements that can be imported from p_path for

View file

@ -214,13 +214,12 @@ setattr(Page, Page.pages.back.attribute, Page.pages.back)
# The Tool class --------------------------------------------------------------- # The Tool class ---------------------------------------------------------------
# Prefixes of the fields generated on the Tool. # Prefixes of the fields generated on the Tool.
toolFieldPrefixes = ('podTemplate', 'formats', 'numberOfSearchColumns',
'searchFields')
defaultToolFields = ('title', 'mailHost', 'mailEnabled', 'mailFrom', defaultToolFields = ('title', 'mailHost', 'mailEnabled', 'mailFrom',
'appyVersion', 'dateFormat', 'hourFormat', 'users', 'appyVersion', 'dateFormat', 'hourFormat',
'connectedUsers', 'groups', 'translations', 'unoEnabledPython', 'openOfficePort',
'loadTranslationsAtStartup', 'pages', 'unoEnabledPython', 'numberOfResultsPerPage', 'users', 'connectedUsers',
'openOfficePort', 'numberOfResultsPerPage') 'groups', 'translations', 'loadTranslationsAtStartup',
'pages')
class Tool(ModelClass): class Tool(ModelClass):
# In a ModelClass we need to declare attributes in the following list. # In a ModelClass we need to declare attributes in the following list.
@ -239,6 +238,9 @@ class Tool(ModelClass):
appyVersion = gen.String(**lf) appyVersion = gen.String(**lf)
dateFormat = gen.String(default='%d/%m/%Y', **lf) dateFormat = gen.String(default='%d/%m/%Y', **lf)
hourFormat = gen.String(default='%H:%M', **lf) hourFormat = gen.String(default='%H:%M', **lf)
unoEnabledPython = gen.String(default='/usr/bin/python', **lf)
openOfficePort = gen.Integer(default=2002, **lf)
numberOfResultsPerPage = gen.Integer(default=30, **lf)
# Ref(User) will maybe be transformed into Ref(CustomUserClass). # Ref(User) will maybe be transformed into Ref(CustomUserClass).
userPage = gen.Page('users', show=isManager) userPage = gen.Page('users', show=isManager)
@ -265,16 +267,6 @@ class Tool(ModelClass):
show='view', back=gen.Ref(attribute='toTool3', show=False), show='view', back=gen.Ref(attribute='toTool3', show=False),
page=gen.Page('pages', show=isManager)) page=gen.Page('pages', show=isManager))
# Document generation page
dgp = {'page': gen.Page('documents', show=isManagerEdit)}
def validPythonWithUno(self, value): pass # Real method in the wrapper
unoEnabledPython = gen.String(default='/usr/bin/python', show=False,
validator=validPythonWithUno, **dgp)
openOfficePort = gen.Integer(default=2002, show=False, **dgp)
# User interface page
numberOfResultsPerPage = gen.Integer(default=30,
page=gen.Page('userInterface', show=False))
@classmethod @classmethod
def _appy_clean(klass): def _appy_clean(klass):
toClean = [] toClean = []

View file

@ -216,13 +216,6 @@ appyLabels = [
# Some default values for labels whose ids are not fixed (so they can't be # Some default values for labels whose ids are not fixed (so they can't be
# included in the previous variable). # included in the previous variable).
CONFIG = "Configuration panel for product '%s'" CONFIG = "Configuration panel for product '%s'"
# The following messages (starting with MSG_) correspond to tool
# attributes added for every gen-class (warning: the message IDs correspond
# to MSG_<attributePrefix>).
MSG_podTemplate = "POD template for field '%s'"
MSG_formats = "Output format(s) for field '%s'"
MSG_numberOfSearchColumns = "Number of search columns"
MSG_searchFields = "Search fields"
POD_ASKACTION = 'Trigger related action' 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}\\".'

View file

@ -6,17 +6,6 @@ from appy.shared.utils import executeCommand
from appy.gen.wrappers import AbstractWrapper from appy.gen.wrappers import AbstractWrapper
from appy.px import Px from appy.px import Px
# ------------------------------------------------------------------------------
_PY = 'Please specify a file corresponding to a Python interpreter ' \
'(ie "/usr/bin/python").'
FILE_NOT_FOUND = 'Path "%s" was not found.'
VALUE_NOT_FILE = 'Path "%s" is not a file. ' + _PY
NO_PYTHON = "Name '%s' does not starts with 'python'. " + _PY
NOT_UNO_ENABLED_PYTHON = '"%s" is not a UNO-enabled Python interpreter. ' \
'To check if a Python interpreter is UNO-enabled, ' \
'launch it and type "import uno". If you have no ' \
'ImportError exception it is ok.'
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
class ToolWrapper(AbstractWrapper): class ToolWrapper(AbstractWrapper):
@ -649,19 +638,6 @@ class ToolWrapper(AbstractWrapper):
</p> </p>
</x>''', template=AbstractWrapper.pxTemplate, hook='content') </x>''', template=AbstractWrapper.pxTemplate, hook='content')
def validPythonWithUno(self, value):
'''This method represents the validator for field unoEnabledPython.'''
if value:
if not os.path.exists(value):
return FILE_NOT_FOUND % value
if not os.path.isfile(value):
return VALUE_NOT_FILE % value
if not os.path.basename(value).startswith('python'):
return NO_PYTHON % value
if os.system('%s -c "import uno"' % value):
return NOT_UNO_ENABLED_PYTHON % value
return True
def isManager(self): def isManager(self):
'''Some pages on the tool can only be accessed by managers.''' '''Some pages on the tool can only be accessed by managers.'''
if self.user.has_role('Manager'): return 'view' if self.user.has_role('Manager'): return 'view'
@ -686,11 +662,6 @@ class ToolWrapper(AbstractWrapper):
(user.o.absolute_url(), user.title,access)) (user.o.absolute_url(), user.title,access))
return res + '\n'.join(rows) + '</table>' return res + '\n'.join(rows) + '</table>'
podOutputFormats = ('odt', 'pdf', 'doc', 'rtf', 'ods', 'xls')
def getPodOutputFormats(self):
'''Gets the available output formats for POD documents.'''
return [(of, self.translate(of)) for of in self.podOutputFormats]
def getInitiator(self, field=False): def getInitiator(self, field=False):
'''Retrieves the object that triggered the creation of the object '''Retrieves the object that triggered the creation of the object
being currently created (if any), or the name of the field in this being currently created (if any), or the name of the field in this
@ -712,32 +683,6 @@ class ToolWrapper(AbstractWrapper):
'''Gets the Appy class corresponding to technical p_zopeName.''' '''Gets the Appy class corresponding to technical p_zopeName.'''
return self.o.getAppyClass(zopeName) return self.o.getAppyClass(zopeName)
def getAttributeName(self, attributeType, klass, attrName=None):
'''Some names of Tool attributes are not easy to guess. This method
generates the attribute name based on p_attributeType, a p_klass from
the application, and a p_attrName (given only if needed).
p_attributeType may be:
"podTemplate"
Stores the pod template for p_attrName.
"formats"
Stores the output format(s) of a given pod template for
p_attrName.
"numberOfSearchColumns"
Determines in how many columns the search screen for p_klass
is rendered.
"searchFields"
Determines, among all indexed fields for p_klass, which one will
really be used in the search screen.
'''
fullClassName = self.o.getPortalType(klass)
res = '%sFor%s' % (attributeType, fullClassName)
if attrName: res += '_%s' % attrName
return res
def getAvailableLanguages(self): def getAvailableLanguages(self):
'''Returns the list of available languages for this application.''' '''Returns the list of available languages for this application.'''
return [(t.id, t.title) for t in self.translations] return [(t.id, t.title) for t in self.translations]
@ -801,36 +746,4 @@ class ToolWrapper(AbstractWrapper):
except Exception, e: except Exception, e:
failed.append(startObject) failed.append(startObject)
return nb, failed return nb, failed
def validate(self, new, errors):
'''Validates that uploaded POD templates and output types are
compatible.'''
page = self.request.get('page', 'main')
if page == 'documents':
# Check that uploaded templates and output formats are compatible.
for fieldName in dir(new):
# Ignore fields which are not POD templates.
if not fieldName.startswith('podTemplate'): continue
# Get the file name, either from the newly uploaded file or
# from the existing file stored in the database.
if getattr(new, fieldName):
fileName = getattr(new, fieldName).filename
else:
fileName = getattr(self, fieldName).name
# Get the extension of the uploaded file.
ext = os.path.splitext(fileName)[1][1:]
# Get the chosen output formats for this template.
formatsFieldName = 'formatsFor%s' % fieldName[14:]
formats = getattr(new, formatsFieldName)
error = False
if ext == 'odt':
error = ('ods' in formats) or ('xls' in formats)
elif ext == 'ods':
error = ('odt' in formats) or ('pdf' in formats) or \
('doc' in formats) or ('rtf' in formats)
if error:
msg = 'This (these) format(s) cannot be used with ' \
'this template.'
setattr(errors, formatsFieldName, msg)
return self._callCustom('validate', new, errors)
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------