[gen] pod field: one of the listed templates in field.template can simply be a pointer to another template from the list. For example, template=('Item.odt', 'Item.odt.variant'). The second file is an empty file and gen will use the first one for the second template. It allows to have similar files for templates that, in the UI, are different, ie, can have different names and appear under different conditions. Note that in the default context of every template, variable 'template' contains the name of the template file. It allows the template to know if he is 'called' under the name 'Item.odt' or 'Item.odt.variant'.

This commit is contained in:
Gaetan Delannay 2014-09-10 16:26:19 +02:00
parent 71bc58a8b0
commit 194b455816
4 changed files with 76 additions and 14 deletions

View file

@ -34,6 +34,7 @@ class Pod(Field):
# Layout for rendering a POD field for exporting query results. # Layout for rendering a POD field for exporting query results.
rLayouts = {'view': 'fl!'} rLayouts = {'view': 'fl!'}
allFormats = {'.odt': ('pdf', 'doc', 'odt'), '.ods': ('xls', 'ods')} allFormats = {'.odt': ('pdf', 'doc', 'odt'), '.ods': ('xls', 'ods')}
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.'
NO_TEMPLATE = 'Please specify a pod template in field "template".' NO_TEMPLATE = 'Please specify a pod template in field "template".'
@ -119,10 +120,17 @@ class Pod(Field):
template=None, templateName=None, showTemplate=None, template=None, templateName=None, showTemplate=None,
freezeTemplate=None, context=None, stylesMapping={}, freezeTemplate=None, context=None, stylesMapping={},
formats=None, getChecked=None): formats=None, getChecked=None):
# Param "template" stores the path to the pod template(s). # Param "template" stores the path to the pod template(s). If there is
# a single template, a string is expected. Else, a list or tuple of
# strings is expected. Every such path must be relative to your
# application. A pod template name Test.odt that is stored at the root
# of your app will be referred as "Test.odt" in self.template. If it is
# stored within sub-folder "pod", it will be referred as "pod/Test.odt".
if not template: raise Exception(Pod.NO_TEMPLATE) if not template: raise Exception(Pod.NO_TEMPLATE)
if isinstance(template, basestring): if isinstance(template, basestring):
self.template = [template] self.template = [template]
elif isinstance(template, tuple):
self.template = list(template)
else: else:
self.template = template self.template = template
# Param "templateName", if specified, is a method that will be called # Param "templateName", if specified, is a method that will be called
@ -194,7 +202,8 @@ class Pod(Field):
self.getChecked = getChecked self.getChecked = getChecked
if not formats: if not formats:
# Compute default ones # Compute default ones
if self.template[0].endswith('.ods'): ext = self.getExtension(self.template[0])
if ext == '.ods':
self.formats = ('xls', 'ods') self.formats = ('xls', 'ods')
else: else:
self.formats = ('pdf', 'doc', 'odt') self.formats = ('pdf', 'doc', 'odt')
@ -207,11 +216,46 @@ class Pod(Field):
# field is determined by freezing. # field is determined by freezing.
self.validable = False self.validable = False
def getExtension(self, template):
'''Gets a p_template's extension (".odt" or ".ods"). Because a template
can simply be a pointer to another template (ie, "Item.odt.variant"),
the logic for getting the extension is a bit more tricky.'''
elems = os.path.splitext(template)
if elems[1] in Pod.allFormats: return elems[1]
# p_template must be a pointer to another template and has one more
# extension.
return os.path.splitext(elems[0])[1]
def getAllFormats(self, template): def getAllFormats(self, template):
'''Gets all the outputy formats that are available for a given '''Gets all the output formats that are available for a given
p_template.''' p_template.'''
ext = os.path.splitext(template)[1] return self.allFormats[self.getExtension(template)]
return self.allFormats[ext]
def setTemplateFolder(self, folder):
'''This methods adds a prefix to every template name in
self.template. This can be useful if a plug-in module needs to
replace an application template by its own templates. Here is an
example: imagine a base application has a pod field with:
self.templates = ["Item.odt", "Decision.odt"]
The plug-in module, named "PlugInApp", wants to replace it with its
own templates Item.odt, Decision.odt and Other.odt, stored in its
sub-folder "pod". Suppose the base pod field is in <podField>. The
plug-in will write:
<podField>.templates = ["Item.odt", "Decision.odt", "Other.odt"]
<podField>.setTemplateFolder('../PlugInApp/pod')
The following code is equivalent, will work, but is precisely the
kind of things we want to avoid.
<podField>.templates = ["../PlugInApp/pod/Item.odt",
"../PlugInApp/pod/Decision.odt",
"../PlugInApp/pod/Other.odt"]
'''
for i in range(len(self.template)):
self.template[i] = os.path.join(folder, self.template[i])
def getTemplateName(self, obj, fileName): def getTemplateName(self, obj, fileName):
'''Gets the name of a template given its p_fileName.''' '''Gets the name of a template given its p_fileName.'''
@ -225,6 +269,19 @@ class Pod(Field):
res = gutils.produceNiceMessage(name) res = gutils.produceNiceMessage(name)
return res return res
def getTemplatePath(self, diskFolder, template):
'''Return the absolute path to some pod p_template, by prefixing it with
the application path. p_template can be a pointer to another
template.'''
res = sutils.resolvePath(os.path.join(diskFolder, template))
if not os.path.isfile(res):
raise Exception(self.TEMPLATE_NOT_FOUND % templatePath)
# Unwrap the path if the file is simply a pointer to another one.
elems = os.path.splitext(res)
if elems[1] not in Pod.allFormats:
res = self.getTemplatePath(diskFolder, elems[0])
return res
def getDownloadName(self, obj, template, format, queryRelated): def getDownloadName(self, obj, template, format, queryRelated):
'''Gets the name of the pod result as will be seen by the user that will '''Gets the name of the pod result as will be seen by the user that will
download it. Ensure the returned name is not too long for the OS that download it. Ensure the returned name is not too long for the OS that
@ -295,9 +352,7 @@ class Pod(Field):
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 = sutils.resolvePath(os.path.join(diskFolder, template)) templatePath = self.getTemplatePath(diskFolder, template)
if not os.path.isfile(templatePath):
raise Exception(self.TEMPLATE_NOT_FOUND % templatePath)
# 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):
@ -311,7 +366,8 @@ 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': diskFolder} '_': obj.translate, 'projectFolder': diskFolder,
'template': template}
# If the pod document is related to a query, re-trigger it and put the # If the pod document is related to a query, re-trigger it and put the
# result in the pod context. # result in the pod context.
if queryData: if queryData:

View file

@ -265,6 +265,7 @@ class String(Field):
'view': {LINE:pxViewLine, TEXT:pxViewText, XHTML:pxViewRich, 'view': {LINE:pxViewLine, TEXT:pxViewText, XHTML:pxViewRich,
PASSWORD:pxViewLine, CAPTCHA:pxViewLine} PASSWORD:pxViewLine, CAPTCHA:pxViewLine}
} }
subPx['cell'] = subPx['view']
# Some predefined functions that may also be used as validators # Some predefined functions that may also be used as validators
@staticmethod @staticmethod

View file

@ -156,7 +156,7 @@ class UserWrapper(AbstractWrapper):
if self.firstName and self.name: if self.firstName and self.name:
self.title = '%s %s' % (self.name, self.firstName) self.title = '%s %s' % (self.name, self.firstName)
else: else:
self.title = self.login self.title = self.title or self.login
def ensureAdminIsManager(self): def ensureAdminIsManager(self):
'''User 'admin' must always have role 'Manager'.''' '''User 'admin' must always have role 'Manager'.'''

View file

@ -776,14 +776,19 @@ class AbstractWrapper(object):
def getField(self, name): return self.o.getAppyType(name) def getField(self, name): return self.o.getAppyType(name)
def getValue(self, name, formatted=False, language=None): def getValue(self, name, formatted=False, language=None):
'''Gets the formatted value of field p_name. If this formatting implies '''Gets the possibly p_formatted value of field p_name. If this
translating some element, translate them in p_langue, or in the user formatting implies translating something, it will be done in
language if not specified.''' p_language, or in the user language if not specified. If the "shown"
value is required instead of the "formatted" value (see methods
getFormattedValue and getShownValue from class appy.fields.Field),
use p_formatted="shown" instead of p_formatted=True.'''
field = self.o.getAppyType(name) field = self.o.getAppyType(name)
obj = self.o obj = self.o
val = field.getValue(obj) val = field.getValue(obj)
if not formatted: return val if not formatted: return val
return field.getFormattedValue(obj, val, language=language) method = (formatted == 'shown') and 'getShownValue' or \
'getFormattedValue'
return getattr(field, method)(obj, val, language=language)
def getLabel(self, name, type='field'): def getLabel(self, name, type='field'):
'''Gets the translated label of field named p_name. If p_type is '''Gets the translated label of field named p_name. If p_type is