[gen] Ref.linkObject and unlinkObject (and caller methods like wrapper.create) can now be called with attr 'executeMethods' being False: in this case, all methods defined in attrs like 'insert', 'beforelink', 'afterLink' will not be called. Can be useful while migrating data or duplicating objects.

This commit is contained in:
Gaetan Delannay 2014-09-29 10:06:40 +02:00
parent 1d81dc768b
commit abe56a5add
4 changed files with 72 additions and 50 deletions

View file

@ -982,15 +982,25 @@ class Ref(Field):
elif nbOfRefs > maxRef: elif nbOfRefs > maxRef:
return obj.translate('max_ref_violated') return obj.translate('max_ref_violated')
def linkObject(self, obj, value, back=False, noSecurity=True): def linkObject(self, obj, value, back=False, noSecurity=True,
executeMethods=True):
'''This method links p_value (which can be a list of objects) to p_obj '''This method links p_value (which can be a list of objects) to p_obj
through this Ref field.''' through this Ref field. When linking 2 objects via a Ref,
p_linkObject must be called twice: once on the forward Ref and once
on the backward ref. p_back indicates if we are calling it on the
forward or backward Ref. If p_noSecurity is True, we bypass security
checks (has the logged user the right to modify this Ref field?).
If p_executeMethods is False, we do not execute methods that
customize the object insertion (parameters insert, beforeLink,
afterLink...). This can be useful while migrating data or duplicating
an object.'''
zobj = obj.o zobj = obj.o
# Security check # Security check
if not noSecurity: zobj.mayEdit(self.writePermission, raiseError=True) if not noSecurity: zobj.mayEdit(self.writePermission, raiseError=True)
# p_value can be a list of objects # p_value can be a list of objects
if type(value) in sutils.sequenceTypes: if type(value) in sutils.sequenceTypes:
for v in value: self.linkObject(obj, v, back=back) for v in value:
self.linkObject(obj, v, back, noSecurity, executeMethods)
return return
# Gets the list of referred objects (=list of uids), or create it. # Gets the list of referred objects (=list of uids), or create it.
refs = getattr(zobj.aq_base, self.name, None) refs = getattr(zobj.aq_base, self.name, None)
@ -1001,9 +1011,9 @@ class Ref(Field):
uid = value.o.id uid = value.o.id
if uid in refs: return if uid in refs: return
# Execute self.beforeLink if present # Execute self.beforeLink if present
if self.beforeLink: self.beforeLink(obj, value) if executeMethods and self.beforeLink: self.beforeLink(obj, value)
# Where must we insert the object? # Where must we insert the object?
if not self.insert: if not self.insert or not executeMethods:
refs.append(uid) refs.append(uid)
elif self.insert == 'start': elif self.insert == 'start':
refs.insert(0, uid) refs.insert(0, uid)
@ -1032,21 +1042,27 @@ class Ref(Field):
tool.getObject(uid, appy=True))) tool.getObject(uid, appy=True)))
refs._p_changed = 1 refs._p_changed = 1
# Execute self.afterLink if present # Execute self.afterLink if present
if self.afterLink: self.afterLink(obj, value) if executeMethods and self.afterLink: self.afterLink(obj, value)
# Update the back reference # Update the back reference
if not back: self.back.linkObject(value, obj, back=True) if not back:
self.back.linkObject(value, obj, True, noSecurity, executeMethods)
def unlinkObject(self, obj, value, back=False, noSecurity=True): def unlinkObject(self, obj, value, back=False, noSecurity=True,
executeMethods=True):
'''This method unlinks p_value (which can be a list of objects) from '''This method unlinks p_value (which can be a list of objects) from
p_obj through this Ref field.''' p_obj through this Ref field. For an explanation about parameters
p_back, p_noSecurity and p_executeMethods, check m_linkObject's doc
above.'''
zobj = obj.o zobj = obj.o
# Security check # Security check
if not noSecurity: if not noSecurity:
zobj.mayEdit(self.writePermission, raiseError=True) zobj.mayEdit(self.writePermission, raiseError=True)
if executeMethods:
self.mayUnlinkElement(obj, value, raiseError=True) self.mayUnlinkElement(obj, value, raiseError=True)
# p_value can be a list of objects # p_value can be a list of objects
if type(value) in sutils.sequenceTypes: if type(value) in sutils.sequenceTypes:
for v in value: self.unlinkObject(obj, v, back=back) for v in value:
self.unlinkObject(obj, v, back, noSecurity, executeMethods)
return return
refs = getattr(zobj.aq_base, self.name, None) refs = getattr(zobj.aq_base, self.name, None)
if not refs: return if not refs: return
@ -1055,9 +1071,10 @@ class Ref(Field):
if uid in refs: if uid in refs:
refs.remove(uid) refs.remove(uid)
# Execute self.afterUnlink if present # Execute self.afterUnlink if present
if self.afterUnlink: self.afterUnlink(obj, value) if executeMethods and self.afterUnlink: self.afterUnlink(obj, value)
# Update the back reference # Update the back reference
if not back: self.back.unlinkObject(value, obj, back=True) if not back:
self.back.unlinkObject(value,obj,True,noSecurity,executeMethods)
def store(self, obj, value): def store(self, obj, value):
'''Stores on p_obj, the p_value, which can be: '''Stores on p_obj, the p_value, which can be:

View file

@ -1199,27 +1199,11 @@ class ToolMixin(BaseMixin):
anymore. If p_normalized is True, special chars in the first and last anymore. If p_normalized is True, special chars in the first and last
names are normalized.''' names are normalized.'''
tool = self.appy() tool = self.appy()
if not login: login = tool.user.login if not login:
# Manage the special case of an anonymous user. user = tool.user
if login == 'anon': else:
name = self.translate('anonymous')
if normalized: name = sutils.normalizeString(name)
return name
# Manage the case of any other user.
user = tool.search1('User', noSecurity=True, login=login) user = tool.search1('User', noSecurity=True, login=login)
if not user: return login return user.getTitle(normalized=normalized)
firstName = user.firstName
name = user.name
res = ''
if firstName:
if normalized: firstName = sutils.normalizeString(firstName)
res += firstName
if name:
if normalized: name = sutils.normalizeString(name)
if res: res += ' ' + name
else: res = name
if not res: res = login
return res
def tempFile(self): def tempFile(self):
'''A temp file has been created in a temp folder. This method returns '''A temp file has been created in a temp folder. This method returns

View file

@ -4,6 +4,7 @@ from appy.gen import WorkflowOwner
from appy.gen.layout import summaryPageLayouts from appy.gen.layout import summaryPageLayouts
from appy.gen.wrappers import AbstractWrapper from appy.gen.wrappers import AbstractWrapper
from appy.gen import utils as gutils from appy.gen import utils as gutils
from appy.shared import utils as sutils
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
class UserWrapper(AbstractWrapper): class UserWrapper(AbstractWrapper):
@ -11,6 +12,19 @@ class UserWrapper(AbstractWrapper):
specialUsers = ('system', 'anon', 'admin') specialUsers = ('system', 'anon', 'admin')
layouts = summaryPageLayouts layouts = summaryPageLayouts
def getTitle(self, normalized=False):
'''Returns a nice name for this user, based on available information:
name/first name or title or login. If p_normalized is True, special
chars (like accents) are converted to ascii chars.'''
# Manage the special case of an anonymous user.
login = self.login
if login == 'anon':
res = self.translate('anonymous')
else:
res = self.title or login
if not normalized: return res
return sutils.normalizeString(name)
def showLogin(self): def showLogin(self):
'''When must we show the login field?''' '''When must we show the login field?'''
if self.o.isTemporary(): return 'edit' if self.o.isTemporary(): return 'edit'

View file

@ -818,15 +818,21 @@ class AbstractWrapper(object):
'''Is this object a temporary object being created?''' '''Is this object a temporary object being created?'''
return self.o.isTemporary() return self.o.isTemporary()
def link(self, fieldName, obj): def link(self, fieldName, obj, noSecurity=True, executeMethods=True):
'''This method links p_obj (which can be a list of objects) to this one '''This method links p_obj (which can be a list of objects) to this one
through reference field p_fieldName.''' through reference field p_fieldName. For understanding the 2 last
return self.getField(fieldName).linkObject(self, obj) params, check Ref's m_linkObject's doc.'''
field = self.getField(fieldName)
return field.linkObject(self, obj, noSecurity=noSecurity,
executeMethods=executeMethods)
def unlink(self, fieldName, obj): def unlink(self, fieldName, obj, noSecurity=True, executeMethods=True):
'''This method unlinks p_obj (which can be a list of objects) from this '''This method unlinks p_obj (which can be a list of objects) from this
one through reference field p_fieldName.''' one through reference field p_fieldName. For understanding the 2 last
return self.getField(fieldName).unlinkObject(self, obj) params, check Ref's m_unlinkObject's doc.'''
field = self.getField(fieldName)
return field.unlinkObject(self, obj, noSecurity=noSecurity,
executeMethods=executeMethods)
def sort(self, fieldName, sortKey='title', reverse=False): def sort(self, fieldName, sortKey='title', reverse=False):
'''Sorts referred elements linked to p_self via p_fieldName according '''Sorts referred elements linked to p_self via p_fieldName according
@ -843,7 +849,7 @@ class AbstractWrapper(object):
refs._p_changed = 1 refs._p_changed = 1
def create(self, fieldNameOrClass, noSecurity=False, def create(self, fieldNameOrClass, noSecurity=False,
raiseOnWrongAttribute=True, executeOnEdit=True, **kwargs): raiseOnWrongAttribute=True, executeMethods=True, **kwargs):
'''This method creates a new instance of a gen-class. '''This method creates a new instance of a gen-class.
If p_fieldNameOrClass is the name of a field, the created object will If p_fieldNameOrClass is the name of a field, the created object will
@ -859,16 +865,17 @@ class AbstractWrapper(object):
correspond to a field on the created object, an AttributeError will correspond to a field on the created object, an AttributeError will
be raised. Else, the value will be silently ignored. be raised. Else, the value will be silently ignored.
If p_executeOnEdit is False, the gen-class's onEdit method, if If p_executeMethods is False, the gen-class's onEdit method, if
present, will not be called. present, will not be called; any other defined method will not be
called neither (ie, Ref.insert, Ref.beforeLink, Ref.afterLink...).
''' '''
isField = isinstance(fieldNameOrClass, basestring) isField = isinstance(fieldNameOrClass, basestring)
tool = self.tool.o tool = self.tool.o
# Determine the class of the object to create # Determine the class of the object to create
if isField: if isField:
fieldName = fieldNameOrClass fieldName = fieldNameOrClass
appyType = self.o.getAppyType(fieldName) field = self.o.getAppyType(fieldName)
portalType = tool.getPortalType(appyType.klass) portalType = tool.getPortalType(field.klass)
else: else:
klass = fieldNameOrClass klass = fieldNameOrClass
portalType = tool.getPortalType(klass) portalType = tool.getPortalType(klass)
@ -885,7 +892,7 @@ class AbstractWrapper(object):
folder = self.o.getCreateFolder() folder = self.o.getCreateFolder()
if not noSecurity: if not noSecurity:
# Check that the user can edit this field. # Check that the user can edit this field.
appyType.checkAdd(self.o) field.checkAdd(self.o)
# Create the object # Create the object
zopeObj = createObject(folder, objId, portalType, tool.getAppName(), zopeObj = createObject(folder, objId, portalType, tool.getAppName(),
noSecurity=noSecurity) noSecurity=noSecurity)
@ -898,14 +905,14 @@ class AbstractWrapper(object):
if raiseOnWrongAttribute: raise ae if raiseOnWrongAttribute: raise ae
if isField: if isField:
# Link the object to this one # Link the object to this one
appyType.linkObject(self, appyObj) field.linkObject(self, appyObj, executeMethods=executeMethods)
# Call custom initialization # Call custom initialization
if executeOnEdit and hasattr(appyObj, 'onEdit'): appyObj.onEdit(True) if executeMethods and hasattr(appyObj, 'onEdit'): appyObj.onEdit(True)
zopeObj.reindex() zopeObj.reindex()
return appyObj return appyObj
def createFrom(self, fieldNameOrClass, other, noSecurity=False, def createFrom(self, fieldNameOrClass, other, noSecurity=False,
executeOnEdit=True): executeMethods=True):
'''Similar to m_create above, excepted that we will use another object '''Similar to m_create above, excepted that we will use another object
(p_other) as base for filling in data for the object to create.''' (p_other) as base for filling in data for the object to create.'''
# Get the field values to set from p_other and store it in a dict. # Get the field values to set from p_other and store it in a dict.
@ -919,7 +926,7 @@ class AbstractWrapper(object):
params[field.name] = field.getCopyValue(other.o) params[field.name] = field.getCopyValue(other.o)
return self.create(fieldNameOrClass, noSecurity=noSecurity, return self.create(fieldNameOrClass, noSecurity=noSecurity,
raiseOnWrongAttribute=False, raiseOnWrongAttribute=False,
executeOnEdit=executeOnEdit, **params) executeMethods=executeMethods, **params)
def freeze(self, fieldName, template=None, format='pdf', noSecurity=True, def freeze(self, fieldName, template=None, format='pdf', noSecurity=True,
freezeOdtOnError=True): freezeOdtOnError=True):