Added a new feature allowing to create objects by importing them from external sources instead of from filling a web form.

This commit is contained in:
Gaetan Delannay 2009-10-20 16:57:00 +02:00
parent cbc7d257d4
commit 4c4b2d0f87
17 changed files with 502 additions and 181 deletions

View file

@ -14,6 +14,7 @@ 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):
@ -206,4 +207,91 @@ class ToolMixin(AbstractMixin):
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)
self.plone_utils.addPortalMessage(self.translate('import_done'))
return rq.RESPONSE.redirect(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
# ------------------------------------------------------------------------------

View file

@ -20,6 +20,10 @@ class AbstractMixin:
inherits from this class. It contains basic functions allowing to
minimize the amount of generated code.'''
def getAppyAttribute(self, name):
'''Returns method or attribute value corresponding to p_name.'''
return eval('self.%s' % name)
def createOrUpdate(self, created):
'''This method creates (if p_created is True) or updates an object.
In the case of an object creation, p_self is a temporary object
@ -53,7 +57,31 @@ class AbstractMixin:
if obj._appy_meta_type == 'tool': self.unindexObject()
else: obj.reindexObject()
return obj
def delete(self):
'''This methods is self's suicide.'''
self.getParentNode().manage_delObjects([self.id])
def onCreate(self):
'''This method is called when a user wants to create a root object in
the application folder or an object through a reference field.'''
rq = self.REQUEST
if rq.get('initiator', None):
# The object to create will be linked to an initiator object through
# a ref field.
initiatorRes=self.uid_catalog.searchResults(UID=rq.get('initiator'))
rq.SESSION['initiator'] = rq.get('initiator')
rq.SESSION['initiatorField'] = rq.get('field')
rq.SESSION['initiatorTarget'] = rq.get('type_name')
if self._appy_meta_type == 'tool':
baseUrl = self.getAppFolder().absolute_url()
else:
baseUrl = self.absolute_url()
objId = self.generateUniqueId(rq.get('type_name'))
urlBack = '%s/portal_factory/%s/%s/skyn/edit' % \
(baseUrl, rq.get('type_name'), objId)
return rq.RESPONSE.redirect(urlBack)
def onUpdate(self):
'''This method is executed when a user wants to update an object.
The object may be a temporary object created by portal_factory in
@ -106,6 +134,13 @@ class AbstractMixin:
rq.set('fieldset', rq.get('nextPage'))
return obj.skyn.edit(obj)
def onDelete(self):
rq = self.REQUEST
msg = self.translate('delete_done')
self.delete()
self.plone_utils.addPortalMessage(msg)
rq.RESPONSE.redirect(rq['HTTP_REFERER'])
def getAppyType(self, fieldName):
'''Returns the Appy type corresponding to p_fieldName.'''
res = None
@ -407,7 +442,7 @@ class AbstractMixin:
else:
return res
def changeAppyRefOrder(self, fieldName, objectUid, newIndex, isDelta):
def changeRefOrder(self, fieldName, objectUid, newIndex, isDelta):
'''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.'''
@ -421,6 +456,27 @@ class AbstractMixin:
pass # To implement later on
sortedObjectsUids.insert(newIndex, objectUid)
def onChangeRefOrder(self):
'''This method is called when the user wants to change order of an
item in a reference field.'''
rq = self.REQUEST
# Move the item up (-1), down (+1) or at a given position ?
move = -1 # Move up
isDelta = True
if rq.get('moveDown.x', None) != None:
move = 1 # Move down
elif rq.get('moveSeveral.x', None) != None:
try:
move = int(rq.get('moveValue'))
# In this case, it is not a delta value; it is the new position
# where the item must be moved.
isDelta = False
except ValueError:
self.plone_utils.addPortalMessage(
self.translate('ref_invalid_index'))
self.changeRefOrder(rq['fieldName'], rq['refObjectUid'], move, isDelta)
return rq.RESPONSE.redirect(rq['HTTP_REFERER'])
def getWorkflow(self, appy=True):
'''Returns the Appy workflow instance that is relevant for this
object. If p_appy is False, it returns the DC workflow.'''
@ -511,6 +567,38 @@ class AbstractMixin:
self.reindexObject()
return res
def onExecuteAppyAction(self):
'''This method is called every time a user wants to execute an Appy
action on an object.'''
rq = self.REQUEST
res, msg = self.executeAppyAction(rq['fieldName'])
if not msg:
# Use the default i18n messages
suffix = 'ko'
if res:
suffix = 'ok'
label='%s_action_%s' % (self.getLabelPrefix(rq['fieldName']),suffix)
msg = self.translate(label)
self.plone_utils.addPortalMessage(msg)
return rq.RESPONSE.redirect(rq['HTTP_REFERER'])
def onTriggerTransition(self):
'''This method is called whenever a user wants to trigger a workflow
transition on an object.'''
rq = self.REQUEST
self.portal_workflow.doActionFor(self, rq['workflow_action'],
comment = rq.get('comment', ''))
# Where to redirect the user back ?
urlBack = rq['HTTP_REFERER']
if urlBack.find('?') != -1:
# Remove params; this way, the user may be redirected to correct
# phase when relevant.
urlBack = urlBack[:urlBack.find('?')]
msg = self.translate(u'Your content\'s status has been modified.',
domain='plone')
self.plone_utils.addPortalMessage(msg)
return rq.RESPONSE.redirect(urlBack)
def callAppySelect(self, selectMethod, brains):
'''Selects objects from a Reference field.'''
if selectMethod: