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

301 lines
13 KiB
Python

# ------------------------------------------------------------------------------
import re, os, os.path
from appy.gen.utils import FieldDescr
from appy.gen.plone25.mixins import AbstractMixin
from appy.gen.plone25.mixins.FlavourMixin import FlavourMixin
from appy.gen.plone25.wrappers import AbstractWrapper
_PY = 'Please specify a file corresponding to a Python interpreter ' \
'(ie "/usr/bin/python").'
FILE_NOT_FOUND = 'Path "%s" was not found.'
VALUE_NOT_FILE = 'Path "%s" is not a file. ' + _PY
NO_PYTHON = "Name '%s' does not starts with 'python'. " + _PY
NOT_UNO_ENABLED_PYTHON = '"%s" is not a UNO-enabled Python interpreter. ' \
'To check if a Python interpreter is UNO-enabled, ' \
'launch it and type "import uno". If you have no ' \
'ImportError exception it is ok.'
jsMessages = ('no_elem_selected', 'delete_confirm')
# ------------------------------------------------------------------------------
class ToolMixin(AbstractMixin):
_appy_meta_type = 'tool'
def _appy_validateUnoEnabledPython(self, value):
'''This method represents the validator for field unoEnabledPython.
This field is present on the Tool only if POD is needed.'''
if value:
if not os.path.exists(value):
return FILE_NOT_FOUND % value
if not os.path.isfile(value):
return VALUE_NOT_FILE % value
if not os.path.basename(value).startswith('python'):
return NO_PYTHON % value
if os.system('%s -c "import uno"' % value):
return NOT_UNO_ENABLED_PYTHON % value
return None
def getFlavour(self, contextObjOrPortalType, appy=False):
'''Gets the flavour that corresponds to p_contextObjOrPortalType.'''
if isinstance(contextObjOrPortalType, basestring):
portalTypeName = contextObjOrPortalType
else:
# It is the contextObj, not a portal type name
portalTypeName = contextObjOrPortalType.portal_type
res = None
appyTool = self._appy_getWrapper(force=True)
flavourNumber = None
nameElems = portalTypeName.split('_')
if len(nameElems) > 1:
try:
flavourNumber = int(nameElems[-1])
except ValueError:
pass
appName = self.getProductConfig().PROJECTNAME
if flavourNumber != None:
for flavour in appyTool.flavours:
if flavourNumber == flavour.number:
res = flavour
elif portalTypeName == ('%sFlavour' % appName):
# Current object is the Flavour itself. In this cas we simply
# return the wrapped contextObj. Here we are sure that
# contextObjOrPortalType is an object, not a portal type.
res = contextObjOrPortalType._appy_getWrapper(force=True)
if not res and appyTool.flavours:
res = appyTool.flavours[0]
# If appy=False, return the Plone object and not the Appy wrapper
# (this way, we avoid Zope security/access-related problems while
# using this object in Zope Page Templates)
if res and not appy:
res = res.o
return res
def getFlavoursInfo(self):
'''Returns information about flavours.'''
res = []
appyTool = self._appy_getWrapper(force=True)
for flavour in appyTool.flavours:
if isinstance(flavour.o, FlavourMixin):
# This is a bug: sometimes other objects are associated as
# flavours.
res.append({'title': flavour.title, 'number':flavour.number})
return res
def getAppName(self):
'''Returns the name of this application.'''
return self.getProductConfig().PROJECTNAME
def getAppFolder(self):
'''Returns the folder at the root of the Plone site that is dedicated
to this application.'''
cfg = self.getProductConfig()
portal = cfg.getToolByName(self, 'portal_url').getPortalObject()
return getattr(portal, self.getAppName())
def getRootClasses(self):
'''Returns the list of root classes for this application.'''
return self.getProductConfig().rootClasses
def showPortlet(self):
return not self.portal_membership.isAnonymousUser()
def executeQuery(self, queryName, flavourNumber):
if queryName.find(',') != -1:
# Several content types are specified
portalTypes = queryName.split(',')
if flavourNumber != 1:
portalTypes = ['%s_%d' % (pt, flavourNumber) \
for pt in portalTypes]
else:
portalTypes = queryName
params = {'portal_type': portalTypes, 'batch': True}
res = self.portal_catalog.searchResults(**params)
batchStart = self.REQUEST.get('b_start', 0)
res = self.getProductConfig().Batch(res,
self.getNumberOfResultsPerPage(), int(batchStart), orphan=0)
return res
def getResultColumnsNames(self, queryName):
contentTypes = queryName.strip(',').split(',')
resSet = None # Temporary set for computing intersections.
res = [] # Final, sorted result.
flavour = None
fieldNames = None
for cType in contentTypes:
# Get the flavour tied to those content types
if not flavour:
flavour = self.getFlavour(cType, appy=True)
if flavour.number != 1:
cType = cType.rsplit('_', 1)[0]
fieldNames = getattr(flavour, 'resultColumnsFor%s' % cType)
if not resSet:
resSet = set(fieldNames)
else:
resSet = resSet.intersection(fieldNames)
# By converting to set, we've lost order. Let's put things in the right
# order.
for fieldName in fieldNames:
if fieldName in resSet:
res.append(fieldName)
return res
def getResultColumns(self, anObject, queryName):
'''What columns must I show when displaying a list of root class
instances? Result is a list of tuples containing the name of the
column (=name of the field) and a FieldDescr instance.'''
res = []
for fieldName in self.getResultColumnsNames(queryName):
if fieldName == 'workflowState':
# We do not return a FieldDescr instance if the attributes is
# not a *real* attribute but the workfow state.
res.append(fieldName)
else:
# Create a FieldDescr instance
appyType = anObject.getAppyType(fieldName)
if not appyType:
res.append({'atField': None, 'name': fieldName})
# The field name is wrong.
# We return it so we can show it in an error message.
else:
atField = anObject.schema.get(fieldName)
fieldDescr = FieldDescr(atField, appyType, None)
res.append(fieldDescr.get())
return res
xhtmlToText = re.compile('<.*?>', re.S)
def getReferenceLabel(self, brain, appyType):
'''p_appyType is a Ref with link=True. I need to display, on an edit
view, the referenced object p_brain in the listbox that will allow
the user to choose which object(s) to link through the Ref.
According to p_appyType, the label may only be the object title,
or more if parameter appyType.shownInfo is used.'''
res = brain.Title
if 'title' in appyType['shownInfo']:
# We may place it at another place
res = ''
appyObj = brain.getObject()._appy_getWrapper(force=True)
for fieldName in appyType['shownInfo']:
value = getattr(appyObj, fieldName)
if isinstance(value, AbstractWrapper):
value = value.title.decode('utf-8')
elif isinstance(value, basestring):
value = value.decode('utf-8')
refAppyType = appyObj.o.getAppyType(fieldName)
if refAppyType and (refAppyType['type'] == 'String') and \
(refAppyType['format'] == 2):
value = self.xhtmlToText.sub(' ', value)
else:
value = str(value)
prefix = ''
if res:
prefix = ' | '
res += prefix + value.encode('utf-8')
maxWidth = self.getListBoxesMaximumWidth()
if len(res) > maxWidth:
res = res[:maxWidth-2] + '...'
return res
translationMapping = {'portal_path': ''}
def translateWithMapping(self, label):
'''Translates p_label in the application domain, with a default
translation mapping.'''
if not self.translationMapping['portal_path']:
self.translationMapping['portal_path'] = \
self.portal_url.getPortalPath()
appName = self.getProductConfig().PROJECTNAME
return self.utranslate(label, self.translationMapping, domain=appName)
def getPublishedObject(self):
'''Gets the currently published object.'''
rq = self.REQUEST
obj = rq['PUBLISHED']
parent = obj.getParentNode()
if parent.id == 'skyn': return parent.getParentNode()
return rq['PUBLISHED']
def getAppyClass(self, contentType):
'''Gets the Appy Python class that is related to p_contentType.'''
# Retrieve first the Archetypes class corresponding to p_ContentType
portalType = self.portal_types.get(contentType)
atClassName = portalType.getProperty('content_meta_type')
appName = self.getProductConfig().PROJECTNAME
exec 'from Products.%s.%s import %s as atClass' % \
(appName, atClassName, atClassName)
# Get then the Appy Python class
return atClass.wrapperClass.__bases__[-1]
def getCreateMeans(self, contentTypeOrAppyClass):
'''Gets the different ways objects of p_contentTypeOrAppyClass (which
can be a Plone content type or a Appy class) can be created
(via a web form, by importing external data, etc). Result is a
dict whose keys are strings (ie "form", "import"...) and whose
values are additional data bout the particular mean.'''
pythonClass = contentTypeOrAppyClass
if isinstance(contentTypeOrAppyClass, basestring):
pythonClass = self.getAppyClass(pythonClass)
res = {}
if not pythonClass.__dict__.has_key('create'):
res['form'] = None
# No additional data for this means, which is the default one.
else:
means = pythonClass.create
if means:
if isinstance(means, basestring): res[means] = None
elif isinstance(means, list) or isinstance(means, tuple):
for mean in means:
if isinstance(mean, basestring):
res[mean] = None
else:
res[mean.id] = mean.__dict__
else:
res[means.id] = means.__dict__
return res
def getImportElements(self, contentType):
'''Returns the list of elements that can be imported from p_path for
p_contentType.'''
appyClass = self.getAppyClass(contentType)
importParams = self.getCreateMeans(appyClass)['import']
columnMethod = importParams['columnMethod'].__get__('')
sortMethod = importParams['sortMethod']
if sortMethod: sortMethod = sortMethod.__get__('')
elems = []
for elem in os.listdir(importParams['path']):
elemFullPath = os.path.join(importParams['path'], elem)
niceElem = columnMethod(elemFullPath)
niceElem.insert(0, elemFullPath) # To the result, I add the full
# path of the elem, which will not be shown.
elems.append(niceElem)
if sortMethod:
elems = sortMethod(elems)
return [importParams['columnHeaders'], elems]
def onImportObjects(self):
'''This method is called when the user wants to create objects from
external data.'''
rq = self.REQUEST
appyClass = self.getAppyClass(rq.get('type_name'))
importPaths = rq.get('importPath').split('|')
appFolder = self.getAppFolder()
for importPath in importPaths:
if not importPath: continue
objectId = os.path.basename(importPath)
self.appy().create(appyClass, id=objectId, _data=importPath)
self.plone_utils.addPortalMessage(self.translate('import_done'))
return self.goto(rq['HTTP_REFERER'])
def isAlreadyImported(self, contentType, importPath):
appFolder = self.getAppFolder()
objectId = os.path.basename(importPath)
if hasattr(appFolder.aq_base, objectId):
return True
else:
return False
def getJavascriptMessages(self):
'''Returns the translated version of messages that must be shown in
Javascript popups.'''
res = ''
for msg in jsMessages:
res += 'var %s = "%s";\n' % (msg, self.translate(msg))
return res
# ------------------------------------------------------------------------------