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:
parent
cbc7d257d4
commit
4c4b2d0f87
17 changed files with 502 additions and 181 deletions
|
@ -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
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue