Implemented Pod field.
This commit is contained in:
parent
fc75a42264
commit
ecd9c66ca1
|
@ -460,6 +460,23 @@ class Info(Type):
|
||||||
specificReadPermission, specificWritePermission, width,
|
specificReadPermission, specificWritePermission, width,
|
||||||
height, master, masterValue, focus, historized)
|
height, master, masterValue, focus, historized)
|
||||||
|
|
||||||
|
class Pod(Type):
|
||||||
|
'''A pod is a field allowing to produce a (PDF, ODT, Word, RTF...) document
|
||||||
|
from data contained in Appy class and linked objects or anything you
|
||||||
|
want to put in it. It uses appy.pod.'''
|
||||||
|
def __init__(self, validator=None, index=None, default=None,
|
||||||
|
optional=False, editDefault=False, show='view',
|
||||||
|
page='main', group=None, move=0, indexed=False,
|
||||||
|
searchable=False, specificReadPermission=False,
|
||||||
|
specificWritePermission=False, width=None, height=None,
|
||||||
|
master=None, masterValue=None, focus=False, historized=False,
|
||||||
|
template=None):
|
||||||
|
Type.__init__(self, None, (0,1), index, default, optional,
|
||||||
|
False, show, page, group, move, indexed, searchable,
|
||||||
|
specificReadPermission, specificWritePermission, width,
|
||||||
|
height, master, masterValue, focus, historized)
|
||||||
|
self.template = template # The path to a POD template
|
||||||
|
|
||||||
# Workflow-specific types ------------------------------------------------------
|
# Workflow-specific types ------------------------------------------------------
|
||||||
class State:
|
class State:
|
||||||
def __init__(self, permissions, initial=False, phase='main', show=True):
|
def __init__(self, permissions, initial=False, phase='main', show=True):
|
||||||
|
|
|
@ -193,8 +193,16 @@ class ArchetypeFieldDescriptor:
|
||||||
self.widgetParams['visible'] = False # Archetypes will believe the
|
self.widgetParams['visible'] = False # Archetypes will believe the
|
||||||
# field is invisible; we will display it ourselves (like for Ref fields)
|
# field is invisible; we will display it ourselves (like for Ref fields)
|
||||||
|
|
||||||
|
def walkPod(self):
|
||||||
|
'''How to dump a Pod type?'''
|
||||||
|
self.fieldType = 'FileField'
|
||||||
|
self.widgetType = 'FileWidget'
|
||||||
|
self.fieldParams['storage'] = 'python:AttributeStorage()'
|
||||||
|
# Add the POD-related fields on the Flavour
|
||||||
|
Flavour._appy_addPodRelatedFields(self)
|
||||||
|
|
||||||
alwaysAValidatorFor = ('Ref', 'Integer', 'Float')
|
alwaysAValidatorFor = ('Ref', 'Integer', 'Float')
|
||||||
notToValidateFields = ('Info', 'Computed', 'Action')
|
notToValidateFields = ('Info', 'Computed', 'Action', 'Pod')
|
||||||
def walkAppyType(self):
|
def walkAppyType(self):
|
||||||
'''Walks into the Appy type definition and gathers data about the
|
'''Walks into the Appy type definition and gathers data about the
|
||||||
Archetype elements to generate.'''
|
Archetype elements to generate.'''
|
||||||
|
@ -311,10 +319,12 @@ class ArchetypeFieldDescriptor:
|
||||||
elif self.appyType.type == 'Computed': self.walkComputed()
|
elif self.appyType.type == 'Computed': self.walkComputed()
|
||||||
# Manage things which are specific to Actions
|
# Manage things which are specific to Actions
|
||||||
elif self.appyType.type == 'Action': self.walkAction()
|
elif self.appyType.type == 'Action': self.walkAction()
|
||||||
# Manage things which are specific to reference 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 info types
|
# Manage things which are specific to Info types
|
||||||
elif self.appyType.type == 'Info': self.walkInfo()
|
elif self.appyType.type == 'Info': self.walkInfo()
|
||||||
|
# Manage things which are specific to Pod types
|
||||||
|
elif self.appyType.type == 'Pod': self.walkPod()
|
||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
'''Produces the Archetypes field definition as a string.'''
|
'''Produces the Archetypes field definition as a string.'''
|
||||||
|
@ -524,15 +534,6 @@ class ArchetypesClassDescriptor(ClassDescriptor):
|
||||||
return search
|
return search
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def addGenerateDocMethod(self):
|
|
||||||
m = self.methods
|
|
||||||
spaces = TABS
|
|
||||||
m += '\n' + ' '*spaces + 'def generateDocument(self):\n'
|
|
||||||
spaces += TABS
|
|
||||||
m += ' '*spaces + "'''Generates a document from p_self.'''\n"
|
|
||||||
m += ' '*spaces + 'return self._appy_generateDocument()\n'
|
|
||||||
self.methods = m
|
|
||||||
|
|
||||||
class ToolClassDescriptor(ClassDescriptor):
|
class ToolClassDescriptor(ClassDescriptor):
|
||||||
'''Represents the POD-specific fields that must be added to the tool.'''
|
'''Represents the POD-specific fields that must be added to the tool.'''
|
||||||
predefined = True
|
predefined = True
|
||||||
|
|
|
@ -689,7 +689,6 @@ class Generator(AbstractGenerator):
|
||||||
self.applicationName)
|
self.applicationName)
|
||||||
if classDescr.isAbstract():
|
if classDescr.isAbstract():
|
||||||
register = ''
|
register = ''
|
||||||
classDescr.addGenerateDocMethod() # For POD
|
|
||||||
repls = self.repls.copy()
|
repls = self.repls.copy()
|
||||||
repls.update({
|
repls.update({
|
||||||
'imports': '\n'.join(imports), 'parents': parents,
|
'imports': '\n'.join(imports), 'parents': parents,
|
||||||
|
|
|
@ -35,6 +35,7 @@ class PloneInstaller:
|
||||||
# front page?
|
# front page?
|
||||||
self.showPortlet = showPortlet # Must we show the application portlet?
|
self.showPortlet = showPortlet # Must we show the application portlet?
|
||||||
self.ploneStuff = ploneStuff # A dict of some Plone functions or vars
|
self.ploneStuff = ploneStuff # A dict of some Plone functions or vars
|
||||||
|
self.attributes = ploneStuff['GLOBALS']['attributes']
|
||||||
self.toLog = StringIO()
|
self.toLog = StringIO()
|
||||||
self.typeAliases = {'sharing': '', 'gethtml': '',
|
self.typeAliases = {'sharing': '', 'gethtml': '',
|
||||||
'(Default)': 'skynView', 'edit': 'skyn/edit',
|
'(Default)': 'skynView', 'edit': 'skyn/edit',
|
||||||
|
@ -202,6 +203,7 @@ class PloneInstaller:
|
||||||
def updatePodTemplates(self):
|
def updatePodTemplates(self):
|
||||||
'''Creates or updates the POD templates in flavours according to pod
|
'''Creates or updates the POD templates in flavours according to pod
|
||||||
declarations in the application classes.'''
|
declarations in the application classes.'''
|
||||||
|
# Creates or updates the old-way class-related templates
|
||||||
i = -1
|
i = -1
|
||||||
for klass in self.appClasses:
|
for klass in self.appClasses:
|
||||||
i += 1
|
i += 1
|
||||||
|
@ -232,6 +234,30 @@ class PloneInstaller:
|
||||||
flavour.create(podAttr, id=podId, podTemplate=f,
|
flavour.create(podAttr, id=podId, podTemplate=f,
|
||||||
title=produceNiceMessage(templateName))
|
title=produceNiceMessage(templateName))
|
||||||
f.close()
|
f.close()
|
||||||
|
# Creates the new-way templates for Pod fields if they do not exist.
|
||||||
|
for contentType, attrNames in self.attributes.iteritems():
|
||||||
|
appyClass = self.tool.getAppyClass(contentType)
|
||||||
|
for attrName in attrNames:
|
||||||
|
appyType = getattr(appyClass, attrName)
|
||||||
|
if appyType.type == 'Pod':
|
||||||
|
# For every flavour, find the attribute that stores the
|
||||||
|
# template, and store on it the default one specified in
|
||||||
|
# the appyType if no template is stored yet.
|
||||||
|
for flavour in self.appyTool.flavours:
|
||||||
|
attrName = flavour.getAttributeName(
|
||||||
|
'podTemplate', appyClass, attrName)
|
||||||
|
fileObject = getattr(flavour.o, attrName)
|
||||||
|
if not fileObject or (fileObject.size == 0):
|
||||||
|
# There is no file. Put the one specified in the
|
||||||
|
# appyType.
|
||||||
|
fileName=os.path.join(self.appyTool.getDiskFolder(),
|
||||||
|
appyType.template)
|
||||||
|
if os.path.exists(fileName):
|
||||||
|
setattr(flavour, attrName, fileName)
|
||||||
|
else:
|
||||||
|
self.appyTool.log(
|
||||||
|
'Template "%s" was not found!' % \
|
||||||
|
fileName, type='error')
|
||||||
|
|
||||||
def installTool(self):
|
def installTool(self):
|
||||||
'''Configures the application tool and flavours.'''
|
'''Configures the application tool and flavours.'''
|
||||||
|
|
|
@ -1,10 +1,20 @@
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
import os, os.path
|
import os, os.path, time
|
||||||
|
from StringIO import StringIO
|
||||||
|
from appy.shared import mimeTypes
|
||||||
|
from appy.shared.utils import getOsTempFolder
|
||||||
|
import appy.pod
|
||||||
|
from appy.pod.renderer import Renderer
|
||||||
import appy.gen
|
import appy.gen
|
||||||
from appy.gen import Type
|
from appy.gen import Type
|
||||||
from appy.gen.plone25.mixins import AbstractMixin
|
from appy.gen.plone25.mixins import AbstractMixin
|
||||||
from appy.gen.plone25.descriptors import ArchetypesClassDescriptor
|
from appy.gen.plone25.descriptors import ArchetypesClassDescriptor
|
||||||
|
|
||||||
|
# Errors -----------------------------------------------------------------------
|
||||||
|
DELETE_TEMP_DOC_ERROR = 'A temporary document could not be removed. %s.'
|
||||||
|
POD_ERROR = 'An error occurred while generating the document. Please ' \
|
||||||
|
'contact the system administrator.'
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class FlavourMixin(AbstractMixin):
|
class FlavourMixin(AbstractMixin):
|
||||||
_appy_meta_type = 'flavour'
|
_appy_meta_type = 'flavour'
|
||||||
|
@ -88,7 +98,7 @@ class FlavourMixin(AbstractMixin):
|
||||||
podTemplates = getattr(appySelf, fieldName, [])
|
podTemplates = getattr(appySelf, fieldName, [])
|
||||||
if not isinstance(podTemplates, list):
|
if not isinstance(podTemplates, list):
|
||||||
podTemplates = [podTemplates]
|
podTemplates = [podTemplates]
|
||||||
res = [r.o for r in podTemplates if r.phase==phase]
|
res = [r.o for r in podTemplates if r.podPhase == phase]
|
||||||
hasParents = True
|
hasParents = True
|
||||||
klass = obj.__class__
|
klass = obj.__class__
|
||||||
while hasParents:
|
while hasParents:
|
||||||
|
@ -98,15 +108,106 @@ class FlavourMixin(AbstractMixin):
|
||||||
podTemplates = getattr(appySelf, fieldName, [])
|
podTemplates = getattr(appySelf, fieldName, [])
|
||||||
if not isinstance(podTemplates, list):
|
if not isinstance(podTemplates, list):
|
||||||
podTemplates = [podTemplates]
|
podTemplates = [podTemplates]
|
||||||
res += [r.o for r in podTemplates if r.phase==phase]
|
res += [r.o for r in podTemplates if r.podPhase == phase]
|
||||||
klass = parent
|
klass = parent
|
||||||
else:
|
else:
|
||||||
hasParents = False
|
hasParents = False
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def getMaxShownTemplates(self, obj):
|
def getMaxShownTemplates(self, obj):
|
||||||
attrName = 'podMaxShownTemplatesFor%s' % obj.meta_type
|
attrName = 'getPodMaxShownTemplatesFor%s' % obj.meta_type
|
||||||
return getattr(self, attrName)
|
return getattr(self, attrName)()
|
||||||
|
|
||||||
|
def getPodInfo(self, ploneObj, fieldName):
|
||||||
|
'''Returns POD-related information about Pod field p_fieldName defined
|
||||||
|
on class whose p_ploneObj is an instance of.'''
|
||||||
|
res = {}
|
||||||
|
appyClass = self.getParentNode().getAppyClass(ploneObj.meta_type)
|
||||||
|
appyFlavour = self.appy()
|
||||||
|
n = appyFlavour.getAttributeName('formats', appyClass, fieldName)
|
||||||
|
res['formats'] = getattr(appyFlavour, n)
|
||||||
|
n = appyFlavour.getAttributeName('podTemplate', appyClass, fieldName)
|
||||||
|
res['template'] = getattr(appyFlavour, n)
|
||||||
|
res['title'] = self.translate(getattr(appyClass, fieldName).label)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def generateDocument(self):
|
||||||
|
'''Generates the document:
|
||||||
|
- from a PodTemplate instance if it is a class-wide pod template;
|
||||||
|
- from field-related info on the flavour if it is a Pod field.
|
||||||
|
UID of object that is the template target is given in the request.'''
|
||||||
|
rq = self.REQUEST
|
||||||
|
appyTool = self.getParentNode().appy()
|
||||||
|
# Get the object
|
||||||
|
objectUid = rq.get('objectUid')
|
||||||
|
obj = self.uid_catalog(UID=objectUid)[0].getObject()
|
||||||
|
# Get information about the document to render. Information comes from
|
||||||
|
# a PodTemplate instance or from the flavour itself, depending on
|
||||||
|
# whether we generate a doc from a class-wide template or from a pod
|
||||||
|
# field.
|
||||||
|
templateUid = rq.get('templateUid', None)
|
||||||
|
if templateUid:
|
||||||
|
podTemplate = self.uid_catalog(UID=templateUid)[0].getObject()
|
||||||
|
appyPt = podTemplate.appy()
|
||||||
|
format = podTemplate.getPodFormat()
|
||||||
|
template = appyPt.podTemplate.content
|
||||||
|
podTitle = podTemplate.Title()
|
||||||
|
else:
|
||||||
|
fieldName = rq.get('fieldName')
|
||||||
|
podInfo = self.getPodInfo(obj, fieldName)
|
||||||
|
format = podInfo['formats'][0]
|
||||||
|
template = podInfo['template'].content
|
||||||
|
podTitle = podInfo['title']
|
||||||
|
# Temporary file where to generate the result
|
||||||
|
tempFileName = '%s/%s_%f.%s' % (
|
||||||
|
getOsTempFolder(), obj.UID(), time.time(), format)
|
||||||
|
# Define parameters to pass to the appy.pod renderer
|
||||||
|
currentUser = self.portal_membership.getAuthenticatedMember()
|
||||||
|
podContext = {'tool': appyTool, 'flavour': self.appy(),
|
||||||
|
'user': currentUser,
|
||||||
|
'now': self.getProductConfig().DateTime(),
|
||||||
|
'projectFolder': appyTool.getDiskFolder(),
|
||||||
|
}
|
||||||
|
if templateUid:
|
||||||
|
podContext['podTemplate'] = podContext['self'] = appyPt
|
||||||
|
rendererParams = {'template': StringIO(template),
|
||||||
|
'context': podContext,
|
||||||
|
'result': tempFileName}
|
||||||
|
if appyTool.unoEnabledPython:
|
||||||
|
rendererParams['pythonWithUnoPath'] = appyTool.unoEnabledPython
|
||||||
|
if appyTool.openOfficePort:
|
||||||
|
rendererParams['ooPort'] = appyTool.openOfficePort
|
||||||
|
# Launch the renderer
|
||||||
|
try:
|
||||||
|
renderer = Renderer(**rendererParams)
|
||||||
|
renderer.run()
|
||||||
|
except appy.pod.PodError, pe:
|
||||||
|
if not os.path.exists(tempFileName):
|
||||||
|
# In some (most?) cases, when OO returns an error, the result is
|
||||||
|
# nevertheless generated.
|
||||||
|
appyTool.log(str(pe), type='error')
|
||||||
|
appyTool.say(POD_ERROR)
|
||||||
|
return self.goto(rq.get('HTTP_REFERER'))
|
||||||
|
# Open the temp file on the filesystem
|
||||||
|
f = file(tempFileName, 'rb')
|
||||||
|
res = f.read()
|
||||||
|
# Identify the filename to return
|
||||||
|
fileName = u'%s-%s' % (obj.Title().decode('utf-8'),
|
||||||
|
podTitle.decode('utf-8'))
|
||||||
|
fileName = appyTool.normalize(fileName)
|
||||||
|
response = obj.REQUEST.RESPONSE
|
||||||
|
response.setHeader('Content-Type', mimeTypes[format])
|
||||||
|
response.setHeader('Content-Disposition', 'inline;filename="%s.%s"'\
|
||||||
|
% (fileName, format))
|
||||||
|
f.close()
|
||||||
|
# Returns the doc and removes the temp file
|
||||||
|
try:
|
||||||
|
os.remove(tempFileName)
|
||||||
|
except OSError, oe:
|
||||||
|
appyTool.log(DELETE_TEMP_DOC_ERROR % str(oe), type='warning')
|
||||||
|
except IOError, ie:
|
||||||
|
appyTool.log(DELETE_TEMP_DOC_ERROR % str(ie), type='warning')
|
||||||
|
return res
|
||||||
|
|
||||||
def getAttr(self, attrName):
|
def getAttr(self, attrName):
|
||||||
'''Gets on this flavour attribute named p_attrName. Useful because we
|
'''Gets on this flavour attribute named p_attrName. Useful because we
|
||||||
|
|
|
@ -1,111 +1,7 @@
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
import os, os.path, time, unicodedata
|
|
||||||
from appy.shared import mimeTypes
|
|
||||||
from appy.gen.plone25.mixins import AbstractMixin
|
from appy.gen.plone25.mixins import AbstractMixin
|
||||||
from StringIO import StringIO
|
|
||||||
import appy.pod
|
|
||||||
from appy.pod.renderer import Renderer
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
class PodError(Exception): pass
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
def getOsTempFolder():
|
|
||||||
tmp = '/tmp'
|
|
||||||
if os.path.exists(tmp) and os.path.isdir(tmp):
|
|
||||||
res = tmp
|
|
||||||
elif os.environ.has_key('TMP'):
|
|
||||||
res = os.environ['TMP']
|
|
||||||
elif os.environ.has_key('TEMP'):
|
|
||||||
res = os.environ['TEMP']
|
|
||||||
else:
|
|
||||||
raise "Sorry, I can't find a temp folder on your machine."
|
|
||||||
return res
|
|
||||||
|
|
||||||
# Error-related constants ------------------------------------------------------
|
|
||||||
POD_ERROR = 'An error occurred while generating the document. Please check ' \
|
|
||||||
'the following things if you wanted to generate the document in ' \
|
|
||||||
'PDF, DOC or RTF: (1) OpenOffice is started in server mode on ' \
|
|
||||||
'the port you should have specified in the PloneMeeting ' \
|
|
||||||
'configuration (go to Site setup-> PloneMeeting configuration); ' \
|
|
||||||
'(2) if the Python interpreter running Zope and ' \
|
|
||||||
'Plone is not able to discuss with OpenOffice (it does not have ' \
|
|
||||||
'"uno" installed - check it by typing "import uno" at the Python ' \
|
|
||||||
'prompt) please specify, in the PloneMeeting configuration, ' \
|
|
||||||
'the path to a UNO-enabled Python interpreter (ie, the Python ' \
|
|
||||||
'interpreter included in the OpenOffice distribution, or, if ' \
|
|
||||||
'your server runs Ubuntu, the standard Python interpreter ' \
|
|
||||||
'installed in /usr/bin/python). Here is the error as reported ' \
|
|
||||||
'by the appy.pod library:\n\n %s'
|
|
||||||
DELETE_TEMP_DOC_ERROR = 'A temporary document could not be removed. %s.'
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class PodTemplateMixin(AbstractMixin):
|
class PodTemplateMixin(AbstractMixin):
|
||||||
_appy_meta_type = 'podtemplate'
|
_appy_meta_type = 'podtemplate'
|
||||||
|
|
||||||
unwantedChars = ('\\', '/', ':', '*', '?', '"', '<', '>', '|', ' ')
|
|
||||||
def _getFileName(self, obj):
|
|
||||||
'''Returns a valid, clean fileName for the document generated from
|
|
||||||
p_self for p_obj.'''
|
|
||||||
res = u'%s-%s' % (obj.Title().decode('utf-8'),
|
|
||||||
self.Title().decode('utf-8'))
|
|
||||||
# Remove accents
|
|
||||||
res = unicodedata.normalize('NFKD', res).encode("ascii", "ignore")
|
|
||||||
# Remove unwanted chars (ie, chars that are not valid in file names
|
|
||||||
# under Windows)
|
|
||||||
finalRes = ''
|
|
||||||
for char in res:
|
|
||||||
if char not in self.unwantedChars:
|
|
||||||
finalRes += char
|
|
||||||
return finalRes
|
|
||||||
|
|
||||||
def generateDocument(self, obj):
|
|
||||||
'''Generates a document from this template, for object p_obj.'''
|
|
||||||
appySelf = self.appy()
|
|
||||||
appName = self.getProductConfig().PROJECTNAME
|
|
||||||
appModule = getattr(self.getProductConfig(), appName)
|
|
||||||
# Temporary file where to generate the result
|
|
||||||
tempFileName = '%s/%s_%f.%s' % (
|
|
||||||
getOsTempFolder(), obj.UID(), time.time(), self.getPodFormat())
|
|
||||||
# Define parameters to pass to the appy.pod renderer
|
|
||||||
currentUser = self.portal_membership.getAuthenticatedMember()
|
|
||||||
podContext = {'self': obj.appy(),
|
|
||||||
'user': currentUser,
|
|
||||||
'podTemplate': appySelf,
|
|
||||||
'now': self.getProductConfig().DateTime(),
|
|
||||||
'projectFolder': os.path.dirname(appModule.__file__)
|
|
||||||
}
|
|
||||||
rendererParams = {'template': StringIO(appySelf.podTemplate.content),
|
|
||||||
'context': podContext,
|
|
||||||
'result': tempFileName }
|
|
||||||
if appySelf.tool.unoEnabledPython:
|
|
||||||
rendererParams['pythonWithUnoPath'] = appySelf.tool.unoEnabledPython
|
|
||||||
if appySelf.tool.openOfficePort:
|
|
||||||
rendererParams['ooPort'] = appySelf.tool.openOfficePort
|
|
||||||
# Launch the renderer
|
|
||||||
try:
|
|
||||||
renderer = Renderer(**rendererParams)
|
|
||||||
renderer.run()
|
|
||||||
except appy.pod.PodError, pe:
|
|
||||||
if not os.path.exists(tempFileName):
|
|
||||||
# In some (most?) cases, when OO returns an error, the result is
|
|
||||||
# nevertheless generated.
|
|
||||||
raise PodError(POD_ERROR % str(pe))
|
|
||||||
# Open the temp file on the filesystem
|
|
||||||
f = file(tempFileName, 'rb')
|
|
||||||
res = f.read()
|
|
||||||
fileName = self._getFileName(obj)
|
|
||||||
response = obj.REQUEST.RESPONSE
|
|
||||||
response.setHeader('Content-Type', mimeTypes[self.getPodFormat()])
|
|
||||||
response.setHeader('Content-Disposition', 'inline;filename="%s.%s"'\
|
|
||||||
% (fileName, self.getPodFormat()))
|
|
||||||
f.close()
|
|
||||||
# Returns the doc and removes the temp file
|
|
||||||
try:
|
|
||||||
os.remove(tempFileName)
|
|
||||||
except OSError, oe:
|
|
||||||
self.getProductConfig().logger.warn(DELETE_TEMP_DOC_ERROR % str(oe))
|
|
||||||
except IOError, ie:
|
|
||||||
self.getProductConfig().logger.warn(DELETE_TEMP_DOC_ERROR % str(ie))
|
|
||||||
return res
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
|
@ -577,8 +577,8 @@ class ToolMixin(AbstractMixin):
|
||||||
else:
|
else:
|
||||||
sourceObj = self.uid_catalog(UID=d1)[0].getObject()
|
sourceObj = self.uid_catalog(UID=d1)[0].getObject()
|
||||||
label = '%s_%s' % (sourceObj.meta_type, d2)
|
label = '%s_%s' % (sourceObj.meta_type, d2)
|
||||||
res['backText'] = u'%s : %s' % \
|
res['backText'] = u'%s : %s' % (sourceObj.Title().decode('utf-8'),
|
||||||
(sourceObj.Title(),self.translate(label))
|
self.translate(label))
|
||||||
newNav = '%s.%s.%s.%%d.%s' % (t, d1, d2, totalNumber)
|
newNav = '%s.%s.%s.%%d.%s' % (t, d1, d2, totalNumber)
|
||||||
# Among, first, previous, next and last, which one do I need?
|
# Among, first, previous, next and last, which one do I need?
|
||||||
previousNeeded = False # Previous ?
|
previousNeeded = False # Previous ?
|
||||||
|
|
|
@ -258,6 +258,9 @@ class AbstractMixin:
|
||||||
for e in v]
|
for e in v]
|
||||||
else:
|
else:
|
||||||
return t('%s_%s_list_%s' % (self.meta_type, name, v))
|
return t('%s_%s_list_%s' % (self.meta_type, name, v))
|
||||||
|
if not isinstance(v, basestring):
|
||||||
|
# Archetypes "Description" fields may hold a BaseUnit instance.
|
||||||
|
v = unicode(v)
|
||||||
return v
|
return v
|
||||||
elif vType == 'Boolean':
|
elif vType == 'Boolean':
|
||||||
if v: return self.translate('yes', domain='plone')
|
if v: return self.translate('yes', domain='plone')
|
||||||
|
@ -318,7 +321,7 @@ class AbstractMixin:
|
||||||
If p_startNumber is a number, this method will return x objects,
|
If p_startNumber is a number, this method will return x objects,
|
||||||
starting at p_startNumber, x being appyType.maxPerPage.'''
|
starting at p_startNumber, x being appyType.maxPerPage.'''
|
||||||
appyType = self.getAppyType(fieldName)
|
appyType = self.getAppyType(fieldName)
|
||||||
sortedUids = getattr(self, '_appy_%s' % fieldName)
|
sortedUids = self._appy_getSortedField(fieldName)
|
||||||
batchNeeded = startNumber != None
|
batchNeeded = startNumber != None
|
||||||
exec 'refUids= self.getRaw%s%s()' % (fieldName[0].upper(),fieldName[1:])
|
exec 'refUids= self.getRaw%s%s()' % (fieldName[0].upper(),fieldName[1:])
|
||||||
# There may be too much UIDs in sortedUids because these fields
|
# There may be too much UIDs in sortedUids because these fields
|
||||||
|
@ -370,8 +373,7 @@ class AbstractMixin:
|
||||||
|
|
||||||
def getAppyRefIndex(self, fieldName, obj):
|
def getAppyRefIndex(self, fieldName, obj):
|
||||||
'''Gets the position of p_obj within Ref field named p_fieldName.'''
|
'''Gets the position of p_obj within Ref field named p_fieldName.'''
|
||||||
sortedFieldName = '_appy_%s' % fieldName
|
sortedObjectsUids = self._appy_getSortedField(fieldName)
|
||||||
sortedObjectsUids = getattr(self, sortedFieldName)
|
|
||||||
res = sortedObjectsUids.index(obj.UID())
|
res = sortedObjectsUids.index(obj.UID())
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
@ -627,8 +629,7 @@ class AbstractMixin:
|
||||||
'''This method changes the position of object with uid p_objectUid in
|
'''This method changes the position of object with uid p_objectUid in
|
||||||
reference field p_fieldName to p_newIndex i p_isDelta is False, or
|
reference field p_fieldName to p_newIndex i p_isDelta is False, or
|
||||||
to actualIndex+p_newIndex if p_isDelta is True.'''
|
to actualIndex+p_newIndex if p_isDelta is True.'''
|
||||||
sortedFieldName = '_appy_%s' % fieldName
|
sortedObjectsUids = self._appy_getSortedField(fieldName)
|
||||||
sortedObjectsUids = getattr(self, sortedFieldName)
|
|
||||||
oldIndex = sortedObjectsUids.index(objectUid)
|
oldIndex = sortedObjectsUids.index(objectUid)
|
||||||
sortedObjectsUids.remove(objectUid)
|
sortedObjectsUids.remove(objectUid)
|
||||||
if isDelta:
|
if isDelta:
|
||||||
|
@ -1155,31 +1156,19 @@ class AbstractMixin:
|
||||||
res = self.portal_type
|
res = self.portal_type
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def _appy_generateDocument(self):
|
def _appy_getSortedField(self, fieldName):
|
||||||
'''Generates the document from a template whose UID is specified in the
|
'''Gets, for reference field p_fieldName, the Appy persistent list
|
||||||
request for a given object whose UID is also in the request.'''
|
that contains the sorted list of referred object UIDs. If this list
|
||||||
# Get the object
|
does not exist, it is created.'''
|
||||||
objectUid = self.REQUEST.get('objectUid')
|
sortedFieldName = '_appy_%s' % fieldName
|
||||||
obj = self.uid_catalog(UID=objectUid)[0].getObject()
|
if not hasattr(self.aq_base, sortedFieldName):
|
||||||
# Get the POD template
|
pList = self.getProductConfig().PersistentList
|
||||||
templateUid = self.REQUEST.get('templateUid')
|
exec 'self.%s = pList()' % sortedFieldName
|
||||||
podTemplate = self.uid_catalog(UID=templateUid)[0].getObject()
|
return getattr(self, sortedFieldName)
|
||||||
return podTemplate.generateDocument(obj)
|
|
||||||
|
|
||||||
def _appy_manageSortedRefs(self):
|
|
||||||
'''For every reference field, this method creates the additional
|
|
||||||
reference lists that are ordered (if it did not already exist).'''
|
|
||||||
for field in self.schema.fields():
|
|
||||||
if field.type == 'reference':
|
|
||||||
sortedRefField = '_appy_%s' % field.getName()
|
|
||||||
if not hasattr(self.aq_base, sortedRefField):
|
|
||||||
pList = self.getProductConfig().PersistentList
|
|
||||||
exec 'self.%s = pList()' % sortedRefField
|
|
||||||
|
|
||||||
def _appy_manageRefs(self, created):
|
def _appy_manageRefs(self, created):
|
||||||
'''Every time an object is created or updated, this method updates
|
'''Every time an object is created or updated, this method updates
|
||||||
the Reference fields accordingly.'''
|
the Reference fields accordingly.'''
|
||||||
self._appy_manageSortedRefs()
|
|
||||||
self._appy_manageRefsFromRequest()
|
self._appy_manageRefsFromRequest()
|
||||||
# If the creation was initiated by another object, update the
|
# If the creation was initiated by another object, update the
|
||||||
# reference.
|
# reference.
|
||||||
|
@ -1207,7 +1196,7 @@ class AbstractMixin:
|
||||||
fieldName = requestKey[9:]
|
fieldName = requestKey[9:]
|
||||||
fieldsInRequest.append(fieldName)
|
fieldsInRequest.append(fieldName)
|
||||||
fieldValue = self.REQUEST[requestKey]
|
fieldValue = self.REQUEST[requestKey]
|
||||||
sortedRefField = getattr(self, '_appy_%s' % fieldName)
|
sortedRefField = self._appy_getSortedField(fieldName)
|
||||||
del sortedRefField[:]
|
del sortedRefField[:]
|
||||||
if not fieldValue: fieldValue = []
|
if not fieldValue: fieldValue = []
|
||||||
if isinstance(fieldValue, basestring):
|
if isinstance(fieldValue, basestring):
|
||||||
|
|
|
@ -66,8 +66,8 @@ class PodTemplate(ModelClass):
|
||||||
podTemplate = File(multiplicity=(1,1))
|
podTemplate = File(multiplicity=(1,1))
|
||||||
podFormat = String(validator=['odt', 'pdf', 'rtf', 'doc'],
|
podFormat = String(validator=['odt', 'pdf', 'rtf', 'doc'],
|
||||||
multiplicity=(1,1), default='odt')
|
multiplicity=(1,1), default='odt')
|
||||||
phase = String(default='main')
|
podPhase = String(default='main')
|
||||||
_appy_attributes = ['description', 'podTemplate', 'podFormat', 'phase']
|
_appy_attributes = ['description', 'podTemplate', 'podFormat', 'podPhase']
|
||||||
|
|
||||||
defaultFlavourAttrs = ('number', 'enableNotifications')
|
defaultFlavourAttrs = ('number', 'enableNotifications')
|
||||||
flavourAttributePrefixes = ('optionalFieldsFor', 'defaultValueFor',
|
flavourAttributePrefixes = ('optionalFieldsFor', 'defaultValueFor',
|
||||||
|
@ -147,6 +147,26 @@ class Flavour(ModelClass):
|
||||||
fieldType.page = 'data'
|
fieldType.page = 'data'
|
||||||
fieldType.group = fieldDescr.classDescr.klass.__name__
|
fieldType.group = fieldDescr.classDescr.klass.__name__
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _appy_addPodRelatedFields(klass, fieldDescr):
|
||||||
|
'''Adds the fields needed in the Flavour for configuring a Pod field.
|
||||||
|
The following method, m_appy_addPodField, is the previous way to
|
||||||
|
manage gen-pod integration. For the moment, both approaches coexist.
|
||||||
|
In the future, only this one may subsist.'''
|
||||||
|
className = fieldDescr.classDescr.name
|
||||||
|
# On what page and group to display those fields ?
|
||||||
|
pg = {'page': 'documentGeneration',
|
||||||
|
'group': '%s_2' % fieldDescr.classDescr.klass.__name__}
|
||||||
|
# Add the field that will store the pod template.
|
||||||
|
fieldName = 'podTemplateFor%s_%s' % (className, fieldDescr.fieldName)
|
||||||
|
fieldType = File(**pg)
|
||||||
|
klass._appy_addField(fieldName, fieldType, fieldDescr.classDescr)
|
||||||
|
# Add the field that will store the output format(s)
|
||||||
|
fieldName = 'formatsFor%s_%s' % (className, fieldDescr.fieldName)
|
||||||
|
fieldType = String(validator=('odt', 'pdf', 'doc', 'rtf'),
|
||||||
|
multiplicity=(1,None), default=('odt',), **pg)
|
||||||
|
klass._appy_addField(fieldName, fieldType, fieldDescr.classDescr)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _appy_addPodField(klass, classDescr):
|
def _appy_addPodField(klass, classDescr):
|
||||||
'''Adds a POD field to the flavour and also an integer field that will
|
'''Adds a POD field to the flavour and also an integer field that will
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
<tal:block metal:define-macro="master"
|
<tal:block metal:define-macro="master"
|
||||||
define="contextObj python:context.getParentNode();
|
define="contextObj python:context.getParentNode();
|
||||||
errors request/errors | python:{};
|
errors request/errors | python:{};
|
||||||
Iterator python:modules['Products.Archetypes'].IndexIterator;
|
|
||||||
schematas contextObj/Schemata;
|
schematas contextObj/Schemata;
|
||||||
fieldsets python:[key for key in schematas.keys() if (key != 'metadata') and (schematas[key].editableFields(contextObj, visible_only=True))];
|
fieldsets python:[key for key in schematas.keys() if (key != 'metadata') and (schematas[key].editableFields(contextObj, visible_only=True))];
|
||||||
default_fieldset python:(not schematas or schematas.has_key('default')) and 'default' or fieldsets[0];
|
default_fieldset python:(not schematas or schematas.has_key('default')) and 'default' or fieldsets[0];
|
||||||
|
|
|
@ -1,32 +1,13 @@
|
||||||
<div metal:define-macro="listPodTemplates" class="appyPod" tal:condition="podTemplates"
|
<div metal:define-macro="listPodTemplates" class="appyPod" tal:condition="podTemplates"
|
||||||
tal:define="podTemplates python: flavour.getAvailablePodTemplates(contextObj, phase);">
|
tal:define="podTemplates python: flavour.getAvailablePodTemplates(contextObj, phase);">
|
||||||
<script language="javascript">
|
|
||||||
<!--
|
|
||||||
// Function that allows to generate a meeting document containing selected items.
|
|
||||||
function generatePodDocument(contextUid, templateUid) {
|
|
||||||
var theForm = document.forms["podTemplateForm"];
|
|
||||||
theForm.objectUid.value = contextUid;
|
|
||||||
theForm.templateUid.value = templateUid;
|
|
||||||
theForm.submit();
|
|
||||||
}
|
|
||||||
-->
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<tal:comment replace="nothing">Form submitted when an object needs to be generated as a document.</tal:comment>
|
|
||||||
<form name="podTemplateForm" method="post"
|
|
||||||
tal:attributes="action python: contextObj.absolute_url() + '/generateDocument'">
|
|
||||||
<input type="hidden" name="objectUid"/>
|
|
||||||
<input type="hidden" name="templateUid"/>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<tal:podTemplates define="maxShownTemplates python: flavour.getMaxShownTemplates(contextObj)">
|
<tal:podTemplates define="maxShownTemplates python: flavour.getMaxShownTemplates(contextObj)">
|
||||||
<tal:comment replace="nothing">Display templates as links if a few number of templates must be shown</tal:comment>
|
<tal:comment replace="nothing">Display templates as links if a few number of templates must be shown</tal:comment>
|
||||||
<span class="discreet" tal:condition="python: len(podTemplates)<=maxShownTemplates"
|
<span class="discreet" tal:condition="python: len(podTemplates)<=maxShownTemplates"
|
||||||
tal:repeat="podTemplate podTemplates">
|
tal:repeat="podTemplate podTemplates">
|
||||||
<a style="cursor: pointer"
|
<a style="cursor: pointer"
|
||||||
tal:define="podFormat podTemplate/getPodFormat"
|
tal:define="podFormat podTemplate/getPodFormat"
|
||||||
tal:attributes="onclick python: 'javascript:generatePodDocument(\'%s\',\'%s\')' % (contextObj.UID(), podTemplate.UID())" >
|
tal:attributes="onclick python: 'javascript:generatePodDocument(\'%s\',\'%s\', \'\')' % (contextObj.UID(), podTemplate.UID())" >
|
||||||
<img tal:attributes="src string: $portal_url/$podFormat.png"/>
|
<img tal:attributes="src string: $portal_url/skyn/$podFormat.png"/>
|
||||||
<span tal:replace="podTemplate/Title"/>
|
<span tal:replace="podTemplate/Title"/>
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
|
@ -34,7 +15,7 @@
|
||||||
<select tal:condition="python: len(podTemplates)>maxShownTemplates">
|
<select tal:condition="python: len(podTemplates)>maxShownTemplates">
|
||||||
<option value="" tal:content="python: tool.translate('choose_a_doc')"></option>
|
<option value="" tal:content="python: tool.translate('choose_a_doc')"></option>
|
||||||
<option tal:repeat="podTemplate podTemplates" tal:content="podTemplate/Title"
|
<option tal:repeat="podTemplate podTemplates" tal:content="podTemplate/Title"
|
||||||
tal:attributes="onclick python: 'javascript:generatePodDocument(\'%s\',\'%s\')' % (contextObj.UID(), podTemplate.UID())" />
|
tal:attributes="onclick python: 'javascript:generatePodDocument(\'%s\',\'%s\', \'\')' % (contextObj.UID(), podTemplate.UID())" />
|
||||||
</select>
|
</select>
|
||||||
</tal:podTemplates>
|
</tal:podTemplates>
|
||||||
</div>
|
</div>
|
||||||
|
@ -149,13 +130,22 @@
|
||||||
<tal:formattedString condition="python: fmt not in (0, 3)">
|
<tal:formattedString condition="python: fmt not in (0, 3)">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend tal:condition="showLabel" tal:content="label"></legend>
|
<legend tal:condition="showLabel" tal:content="label"></legend>
|
||||||
<span tal:condition="python: appyType['format'] == 1"
|
<span tal:condition="python: v and (appyType['format'] == 1)"
|
||||||
tal:replace="structure python: v.replace('\n', '<br>')"/>
|
tal:replace="structure python: v.replace('\n', '<br>')"/>
|
||||||
<span tal:condition="python: appyType['format'] == 2" tal:replace="structure v"/>
|
<span tal:condition="python: v and (appyType['format'] == 2)" tal:replace="structure v"/>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</tal:formattedString>
|
</tal:formattedString>
|
||||||
</metal:showString>
|
</metal:showString>
|
||||||
|
|
||||||
|
<metal:showPod define-macro="showPodField"
|
||||||
|
tal:define="fieldName field/getName;
|
||||||
|
format python:flavour.getPodInfo(contextObj, fieldName)['formats'][0]">
|
||||||
|
<img tal:attributes="src string: $portal_url/skyn/${format}.png;
|
||||||
|
title label;
|
||||||
|
onClick python: 'javascript:generatePodDocument(\'%s\',\'\',\'%s\')' % (contextObj.UID(), fieldName)"
|
||||||
|
style="cursor:pointer"/>
|
||||||
|
</metal:showPod>
|
||||||
|
|
||||||
<div metal:define-macro="showArchetypesField"
|
<div metal:define-macro="showArchetypesField"
|
||||||
tal:define="field fieldDescr/atField|widgetDescr/atField;
|
tal:define="field fieldDescr/atField|widgetDescr/atField;
|
||||||
appyType fieldDescr/appyType|widgetDescr/appyType;
|
appyType fieldDescr/appyType|widgetDescr/appyType;
|
||||||
|
@ -225,6 +215,9 @@
|
||||||
<tal:infoField condition="python: (not isEdit) and (appyType['type'] == 'Info')">
|
<tal:infoField condition="python: (not isEdit) and (appyType['type'] == 'Info')">
|
||||||
<metal:af use-macro="here/skyn/macros/macros/showInfoField" />
|
<metal:af use-macro="here/skyn/macros/macros/showInfoField" />
|
||||||
</tal:infoField>
|
</tal:infoField>
|
||||||
|
<tal:podField condition="python: (not isEdit) and (appyType['type'] == 'Pod')">
|
||||||
|
<metal:af use-macro="here/skyn/macros/macros/showPodField" />
|
||||||
|
</tal:podField>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div metal:define-macro="showBackwardField"
|
<div metal:define-macro="showBackwardField"
|
||||||
|
@ -492,6 +485,14 @@
|
||||||
// Inverse the cookie value
|
// Inverse the cookie value
|
||||||
createCookie(cookieId, newState);
|
createCookie(cookieId, newState);
|
||||||
}
|
}
|
||||||
|
// Function that allows to generate a document from a pod template.
|
||||||
|
function generatePodDocument(contextUid, templateUid, fieldName) {
|
||||||
|
var theForm = document.forms["podTemplateForm"];
|
||||||
|
theForm.objectUid.value = contextUid;
|
||||||
|
theForm.templateUid.value = templateUid;
|
||||||
|
theForm.fieldName.value = fieldName;
|
||||||
|
theForm.submit();
|
||||||
|
}
|
||||||
-->
|
-->
|
||||||
</script>
|
</script>
|
||||||
<tal:comment replace="nothing">Global form for deleting an object</tal:comment>
|
<tal:comment replace="nothing">Global form for deleting an object</tal:comment>
|
||||||
|
@ -499,6 +500,14 @@
|
||||||
<input type="hidden" name="action" value="Delete"/>
|
<input type="hidden" name="action" value="Delete"/>
|
||||||
<input type="hidden" name="objectUid"/>
|
<input type="hidden" name="objectUid"/>
|
||||||
</form>
|
</form>
|
||||||
|
<tal:comment replace="nothing">Global form for generating a document from a pod template.</tal:comment>
|
||||||
|
<form name="podTemplateForm" method="post"
|
||||||
|
tal:attributes="action python: flavour.absolute_url() + '/generateDocument'">
|
||||||
|
<input type="hidden" name="objectUid"/>
|
||||||
|
<tal:comment replace="nothing">templateUid is given if class-wide pod, fieldName is given if podField.</tal:comment>
|
||||||
|
<input type="hidden" name="templateUid"/>
|
||||||
|
<input type="hidden" name="fieldName"/>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div metal:define-macro="showPageHeader"
|
<div metal:define-macro="showPageHeader"
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
appName appFolder/id;
|
appName appFolder/id;
|
||||||
tool python: portal.get('portal_%s' % appName.lower());
|
tool python: portal.get('portal_%s' % appName.lower());
|
||||||
contentType python:context.REQUEST.get('type_name');
|
contentType python:context.REQUEST.get('type_name');
|
||||||
|
flavour python: tool.getFlavour(contentType);
|
||||||
flavourNumber python:int(context.REQUEST.get('flavourNumber'));
|
flavourNumber python:int(context.REQUEST.get('flavourNumber'));
|
||||||
searchName python:context.REQUEST.get('search', '')">
|
searchName python:context.REQUEST.get('search', '')">
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
i18n:domain="<!applicationName!>">
|
i18n:domain="<!applicationName!>">
|
||||||
<body>
|
<body>
|
||||||
<div metal:define-macro="portlet"
|
<div metal:define-macro="portlet"
|
||||||
tal:define="tool python: context.<!toolInstanceName!>"
|
tal:define="tool python: context.<!toolInstanceName!>;
|
||||||
|
flavour python: tool.getFlavour(tool);"
|
||||||
tal:condition="tool/showPortlet">
|
tal:condition="tool/showPortlet">
|
||||||
<metal:block metal:use-macro="here/global_defines/macros/defines" />
|
<metal:block metal:use-macro="here/global_defines/macros/defines" />
|
||||||
<metal:prologue use-macro="here/skyn/macros/macros/pagePrologue"/>
|
<metal:prologue use-macro="here/skyn/macros/macros/pagePrologue"/>
|
||||||
|
|
|
@ -37,6 +37,13 @@ class FlavourWrapper:
|
||||||
number of available templates is higher, templates are shown in a
|
number of available templates is higher, templates are shown in a
|
||||||
drop-down list.
|
drop-down list.
|
||||||
|
|
||||||
|
"podTemplate"
|
||||||
|
Stores the pod template for p_attrName.
|
||||||
|
|
||||||
|
"formats"
|
||||||
|
Stores the output format(s) of a given pod template for
|
||||||
|
p_attrName.
|
||||||
|
|
||||||
"resultColumns"
|
"resultColumns"
|
||||||
Stores the list of columns that must be show when displaying
|
Stores the list of columns that must be show when displaying
|
||||||
instances of the a given root p_klass.
|
instances of the a given root p_klass.
|
||||||
|
@ -71,8 +78,7 @@ class FlavourWrapper:
|
||||||
simply show the current state, be it linked to the current phase
|
simply show the current state, be it linked to the current phase
|
||||||
or not.
|
or not.
|
||||||
'''
|
'''
|
||||||
fullClassName = '%s_%s' % (klass.__module__.replace('.', '_'),
|
fullClassName = self.o.getPortalType(klass)
|
||||||
klass.__name__)
|
|
||||||
res = '%sFor%s' % (attributeType, fullClassName)
|
res = '%sFor%s' % (attributeType, fullClassName)
|
||||||
if attrName: res += '_%s' % attrName
|
if attrName: res += '_%s' % attrName
|
||||||
return res
|
return res
|
||||||
|
|
|
@ -121,11 +121,7 @@ class AbstractWrapper:
|
||||||
objs.append(obj)
|
objs.append(obj)
|
||||||
exec 'self.o.s%s(objs)' % postfix
|
exec 'self.o.s%s(objs)' % postfix
|
||||||
# Update the ordered list of references
|
# Update the ordered list of references
|
||||||
sortedRefField = '_appy_%s' % fieldName
|
self.o._appy_getSortedField(fieldName).append(obj.UID())
|
||||||
if not hasattr(self.o.aq_base, sortedRefField):
|
|
||||||
exec 'self.o.%s = self.o.getProductConfig().PersistentList()' % \
|
|
||||||
sortedRefField
|
|
||||||
getattr(self.o, sortedRefField).append(obj.UID())
|
|
||||||
|
|
||||||
def sort(self, fieldName):
|
def sort(self, fieldName):
|
||||||
'''Sorts referred elements linked to p_self via p_fieldName. At
|
'''Sorts referred elements linked to p_self via p_fieldName. At
|
||||||
|
@ -177,7 +173,6 @@ class AbstractWrapper:
|
||||||
ploneObj = getattr(folder, objId)
|
ploneObj = getattr(folder, objId)
|
||||||
appyObj = ploneObj.appy()
|
appyObj = ploneObj.appy()
|
||||||
# Set object attributes
|
# Set object attributes
|
||||||
ploneObj._appy_manageSortedRefs()
|
|
||||||
for attrName, attrValue in kwargs.iteritems():
|
for attrName, attrValue in kwargs.iteritems():
|
||||||
setterName = 'set%s%s' % (attrName[0].upper(), attrName[1:])
|
setterName = 'set%s%s' % (attrName[0].upper(), attrName[1:])
|
||||||
if isinstance(attrValue, AbstractWrapper):
|
if isinstance(attrValue, AbstractWrapper):
|
||||||
|
@ -257,9 +252,21 @@ class AbstractWrapper:
|
||||||
elif mType == 'error': mType = 'stop'
|
elif mType == 'error': mType = 'stop'
|
||||||
self.o.plone_utils.addPortalMessage(message, type=mType)
|
self.o.plone_utils.addPortalMessage(message, type=mType)
|
||||||
|
|
||||||
def normalize(self, s):
|
unwantedChars = ('\\', '/', ':', '*', '?', '"', '<', '>', '|', ' ')
|
||||||
|
def normalize(self, s, usage='fileName'):
|
||||||
'''Returns a version of string p_s whose special chars have been
|
'''Returns a version of string p_s whose special chars have been
|
||||||
replaced with normal chars.'''
|
replaced with normal chars.'''
|
||||||
|
# We work in unicode. Convert p_s to unicode if not unicode.
|
||||||
|
if isinstance(s, str): s = s.decode('utf-8')
|
||||||
|
elif not isinstance(s, unicode): s = unicode(s)
|
||||||
|
if usage == 'fileName':
|
||||||
|
# Remove any char that can't be found within a file name under
|
||||||
|
# Windows.
|
||||||
|
res = ''
|
||||||
|
for char in s:
|
||||||
|
if char not in self.unwantedChars:
|
||||||
|
res += char
|
||||||
|
s = res
|
||||||
return unicodedata.normalize('NFKD', s).encode("ascii","ignore")
|
return unicodedata.normalize('NFKD', s).encode("ascii","ignore")
|
||||||
|
|
||||||
def search(self, klass, sortBy='', maxResults=None,
|
def search(self, klass, sortBy='', maxResults=None,
|
||||||
|
|
Loading…
Reference in a new issue