Implemented Pod field.

This commit is contained in:
Gaetan Delannay 2010-02-12 10:59:42 +01:00
parent fc75a42264
commit ecd9c66ca1
15 changed files with 260 additions and 188 deletions

View file

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

View file

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

View file

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

View file

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