appypod-rattail/gen/plone25/mixins/FlavourMixin.py

287 lines
13 KiB
Python

# ------------------------------------------------------------------------------
import os, os.path, time, types
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'
def getPortalType(self, metaTypeOrAppyType):
'''Returns the name of the portal_type that is based on
p_metaTypeOrAppyType in this flavour.'''
res = metaTypeOrAppyType
isPredefined = False
isAppy = False
appName = self.getProductConfig().PROJECTNAME
if not isinstance(res, basestring):
res = ArchetypesClassDescriptor.getClassName(res)
isAppy = True
if res.find('Extensions_appyWrappers') != -1:
isPredefined = True
elems = res.split('_')
res = '%s%s' % (elems[1], elems[4])
elif isAppy and issubclass(metaTypeOrAppyType, appy.gen.Tool):
# This is the custom tool
isPredefined = True
res = '%sTool' % appName
elif isAppy and issubclass(metaTypeOrAppyType, appy.gen.Flavour):
# This is the custom Flavour
isPredefined = True
res = '%sFlavour' % appName
if not isPredefined:
if self.getNumber() != 1:
res = '%s_%d' % (res, self.number)
return res
def registerPortalTypes(self):
'''Registers, into portal_types, the portal types which are specific
to this flavour.'''
i = -1
registeredFactoryTypes = self.portal_factory.getFactoryTypes().keys()
factoryTypesToRegister = []
appName = self.getProductConfig().PROJECTNAME
for metaTypeName in self.allMetaTypes:
i += 1
portalTypeName = '%s_%d' % (metaTypeName, self.number)
# If the portal type corresponding to the meta type is
# registered in portal_factory (in the model:
# use_portal_factory=True), we must also register the new
# portal_type we are currently creating.
if metaTypeName in registeredFactoryTypes:
factoryTypesToRegister.append(portalTypeName)
if not hasattr(self.portal_types, portalTypeName) and \
hasattr(self.portal_types, metaTypeName):
# Indeed abstract meta_types have no associated portal_type
typeInfoName = "%s: %s (%s)" % (appName, metaTypeName,
metaTypeName)
self.portal_types.manage_addTypeInformation(
getattr(self.portal_types, metaTypeName).meta_type,
id=portalTypeName, typeinfo_name=typeInfoName)
# Set the human readable title explicitly
portalType = getattr(self.portal_types, portalTypeName)
portalType.title = portalTypeName
# Associate a workflow for this new portal type.
pf = self.portal_workflow
workflowChain = pf.getChainForPortalType(metaTypeName)
pf.setChainForPortalTypes([portalTypeName],workflowChain)
# Copy actions from the base portal type
basePortalType = getattr(self.portal_types, metaTypeName)
portalType._actions = tuple(basePortalType._cloneActions())
# Copy aliases from the base portal type
portalType.setMethodAliases(basePortalType.getMethodAliases())
# Update the factory tool with the list of types to register
self.portal_factory.manage_setPortalFactoryTypes(
listOfTypeIds=factoryTypesToRegister+registeredFactoryTypes)
def getClassFolder(self, className):
'''Return the folder related to p_className.'''
return getattr(self, className)
def getAvailablePodTemplates(self, obj, phase='main'):
'''Returns the POD templates which are available for generating a
document from p_obj.'''
appySelf = self.appy()
fieldName = 'podTemplatesFor%s' % obj.meta_type
res = []
podTemplates = getattr(appySelf, fieldName, [])
if not isinstance(podTemplates, list):
podTemplates = [podTemplates]
res = [r.o for r in podTemplates if r.podPhase == phase]
hasParents = True
klass = obj.__class__
while hasParents:
parent = klass.__bases__[-1]
if hasattr(parent, 'wrapperClass'):
fieldName = 'podTemplatesFor%s' % parent.meta_type
podTemplates = getattr(appySelf, fieldName, [])
if not isinstance(podTemplates, list):
podTemplates = [podTemplates]
res += [r.o for r in podTemplates if r.podPhase == phase]
klass = parent
else:
hasParents = False
return res
def getMaxShownTemplates(self, obj):
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)
appyType = ploneObj.getAppyType(fieldName)
res['title'] = self.translate(appyType['label'])
res['context'] = appyType['context']
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()
appyObj = obj.appy()
# 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)
specificPodContext = 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']
if podInfo['context']:
if type(podInfo['context']) == types.FunctionType:
specificPodContext = podInfo['context'](appyObj)
else:
specificPodContext = podInfo['context']
# 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, 'self': appyObj,
'now': self.getProductConfig().DateTime(),
'projectFolder': appyTool.getDiskFolder(),
}
if specificPodContext:
podContext.update(specificPodContext)
if templateUid:
podContext['podTemplate'] = 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
can't use getattr directly in Zope Page Templates.'''
return getattr(self, attrName, None)
def _appy_getAllFields(self, contentType):
'''Returns the (translated) names of fields of p_contentType.'''
res = []
for attrName in self.getProductConfig().attributes[contentType]:
if attrName != 'title': # Will be included by default.
label = '%s_%s' % (contentType, attrName)
res.append((attrName, self.translate(label)))
# Add object state
res.append(('workflowState', self.translate('workflow_state')))
return res
def _appy_getSearchableFields(self, contentType):
'''Returns the (translated) names of fields that may be searched on
objects of type p_contentType (=indexed fields).'''
tool = self.getParentNode()
appyClass = tool.getAppyClass(contentType)
attrNames = self.getProductConfig().attributes[contentType]
res = []
for attrName in attrNames:
attr = getattr(appyClass, attrName)
if isinstance(attr, Type) and attr.indexed:
label = '%s_%s' % (contentType, attrName)
res.append((attrName, self.translate(label)))
return res
def getSearchableFields(self, contentType):
'''Returns, among the list of all searchable fields (see method above),
the list of fields that the user has configured in the flavour as
being effectively used in the search screen.'''
res = []
appyClass = self.getAppyClass(contentType)
for attrName in getattr(self, 'searchFieldsFor%s' % contentType):
attr = getattr(appyClass, attrName)
dAttr = self._appy_getTypeAsDict(attrName, attr, appyClass)
res.append((attrName, dAttr))
return res
def getImportElements(self, contentType):
'''Returns the list of elements that can be imported from p_path for
p_contentType.'''
tool = self.getParentNode()
appyClass = tool.getAppyClass(contentType)
importParams = tool.getCreateMeans(appyClass)['import']
onElement = importParams['onElement'].__get__('')
sortMethod = importParams['sort']
if sortMethod: sortMethod = sortMethod.__get__('')
elems = []
importPath = getattr(self, 'importPathFor%s' % contentType)
for elem in os.listdir(importPath):
elemFullPath = os.path.join(importPath, elem)
elemInfo = onElement(elemFullPath)
if elemInfo:
elemInfo.insert(0, elemFullPath) # To the result, I add the full
# path of the elem, which will not be shown.
elems.append(elemInfo)
if sortMethod:
elems = sortMethod(elems)
return [importParams['headers'], elems]
# ------------------------------------------------------------------------------