Implemented Pod field.
This commit is contained in:
parent
fc75a42264
commit
ecd9c66ca1
15 changed files with 260 additions and 188 deletions
|
@ -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
|
||||
from appy.gen import Type
|
||||
from appy.gen.plone25.mixins import AbstractMixin
|
||||
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):
|
||||
_appy_meta_type = 'flavour'
|
||||
|
@ -88,7 +98,7 @@ class FlavourMixin(AbstractMixin):
|
|||
podTemplates = getattr(appySelf, fieldName, [])
|
||||
if not isinstance(podTemplates, list):
|
||||
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
|
||||
klass = obj.__class__
|
||||
while hasParents:
|
||||
|
@ -98,15 +108,106 @@ class FlavourMixin(AbstractMixin):
|
|||
podTemplates = getattr(appySelf, fieldName, [])
|
||||
if not isinstance(podTemplates, list):
|
||||
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
|
||||
else:
|
||||
hasParents = False
|
||||
return res
|
||||
|
||||
def getMaxShownTemplates(self, obj):
|
||||
attrName = 'podMaxShownTemplatesFor%s' % obj.meta_type
|
||||
return getattr(self, attrName)
|
||||
attrName = 'getPodMaxShownTemplatesFor%s' % obj.meta_type
|
||||
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):
|
||||
'''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 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):
|
||||
_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:
|
||||
sourceObj = self.uid_catalog(UID=d1)[0].getObject()
|
||||
label = '%s_%s' % (sourceObj.meta_type, d2)
|
||||
res['backText'] = u'%s : %s' % \
|
||||
(sourceObj.Title(),self.translate(label))
|
||||
res['backText'] = u'%s : %s' % (sourceObj.Title().decode('utf-8'),
|
||||
self.translate(label))
|
||||
newNav = '%s.%s.%s.%%d.%s' % (t, d1, d2, totalNumber)
|
||||
# Among, first, previous, next and last, which one do I need?
|
||||
previousNeeded = False # Previous ?
|
||||
|
|
|
@ -258,6 +258,9 @@ class AbstractMixin:
|
|||
for e in v]
|
||||
else:
|
||||
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
|
||||
elif vType == 'Boolean':
|
||||
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,
|
||||
starting at p_startNumber, x being appyType.maxPerPage.'''
|
||||
appyType = self.getAppyType(fieldName)
|
||||
sortedUids = getattr(self, '_appy_%s' % fieldName)
|
||||
sortedUids = self._appy_getSortedField(fieldName)
|
||||
batchNeeded = startNumber != None
|
||||
exec 'refUids= self.getRaw%s%s()' % (fieldName[0].upper(),fieldName[1:])
|
||||
# There may be too much UIDs in sortedUids because these fields
|
||||
|
@ -370,8 +373,7 @@ class AbstractMixin:
|
|||
|
||||
def getAppyRefIndex(self, fieldName, obj):
|
||||
'''Gets the position of p_obj within Ref field named p_fieldName.'''
|
||||
sortedFieldName = '_appy_%s' % fieldName
|
||||
sortedObjectsUids = getattr(self, sortedFieldName)
|
||||
sortedObjectsUids = self._appy_getSortedField(fieldName)
|
||||
res = sortedObjectsUids.index(obj.UID())
|
||||
return res
|
||||
|
||||
|
@ -627,8 +629,7 @@ class AbstractMixin:
|
|||
'''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
|
||||
to actualIndex+p_newIndex if p_isDelta is True.'''
|
||||
sortedFieldName = '_appy_%s' % fieldName
|
||||
sortedObjectsUids = getattr(self, sortedFieldName)
|
||||
sortedObjectsUids = self._appy_getSortedField(fieldName)
|
||||
oldIndex = sortedObjectsUids.index(objectUid)
|
||||
sortedObjectsUids.remove(objectUid)
|
||||
if isDelta:
|
||||
|
@ -1155,31 +1156,19 @@ class AbstractMixin:
|
|||
res = self.portal_type
|
||||
return res
|
||||
|
||||
def _appy_generateDocument(self):
|
||||
'''Generates the document from a template whose UID is specified in the
|
||||
request for a given object whose UID is also in the request.'''
|
||||
# Get the object
|
||||
objectUid = self.REQUEST.get('objectUid')
|
||||
obj = self.uid_catalog(UID=objectUid)[0].getObject()
|
||||
# Get the POD template
|
||||
templateUid = self.REQUEST.get('templateUid')
|
||||
podTemplate = self.uid_catalog(UID=templateUid)[0].getObject()
|
||||
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_getSortedField(self, fieldName):
|
||||
'''Gets, for reference field p_fieldName, the Appy persistent list
|
||||
that contains the sorted list of referred object UIDs. If this list
|
||||
does not exist, it is created.'''
|
||||
sortedFieldName = '_appy_%s' % fieldName
|
||||
if not hasattr(self.aq_base, sortedFieldName):
|
||||
pList = self.getProductConfig().PersistentList
|
||||
exec 'self.%s = pList()' % sortedFieldName
|
||||
return getattr(self, sortedFieldName)
|
||||
|
||||
def _appy_manageRefs(self, created):
|
||||
'''Every time an object is created or updated, this method updates
|
||||
the Reference fields accordingly.'''
|
||||
self._appy_manageSortedRefs()
|
||||
self._appy_manageRefsFromRequest()
|
||||
# If the creation was initiated by another object, update the
|
||||
# reference.
|
||||
|
@ -1207,7 +1196,7 @@ class AbstractMixin:
|
|||
fieldName = requestKey[9:]
|
||||
fieldsInRequest.append(fieldName)
|
||||
fieldValue = self.REQUEST[requestKey]
|
||||
sortedRefField = getattr(self, '_appy_%s' % fieldName)
|
||||
sortedRefField = self._appy_getSortedField(fieldName)
|
||||
del sortedRefField[:]
|
||||
if not fieldValue: fieldValue = []
|
||||
if isinstance(fieldValue, basestring):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue