[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:
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
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
# Security check
if not noSecurity: zobj.mayEdit(self.writePermission, raiseError=True)
# p_value can be a list of objects
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
# Gets the list of referred objects (=list of uids), or create it.
refs = getattr(zobj.aq_base, self.name, None)
@ -1001,9 +1011,9 @@ class Ref(Field):
uid = value.o.id
if uid in refs: return
# 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?
if not self.insert:
if not self.insert or not executeMethods:
refs.append(uid)
elif self.insert == 'start':
refs.insert(0, uid)
@ -1032,21 +1042,27 @@ class Ref(Field):
tool.getObject(uid, appy=True)))
refs._p_changed = 1
# 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
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
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
# Security check
if not noSecurity:
zobj.mayEdit(self.writePermission, raiseError=True)
self.mayUnlinkElement(obj, value, raiseError=True)
if executeMethods:
self.mayUnlinkElement(obj, value, raiseError=True)
# p_value can be a list of objects
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
refs = getattr(zobj.aq_base, self.name, None)
if not refs: return
@ -1055,9 +1071,10 @@ class Ref(Field):
if uid in refs:
refs.remove(uid)
# 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
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):
'''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
names are normalized.'''
tool = self.appy()
if not login: login = tool.user.login
# Manage the special case of an anonymous user.
if login == 'anon':
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)
if not user: return login
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
if not login:
user = tool.user
else:
user = tool.search1('User', noSecurity=True, login=login)
return user.getTitle(normalized=normalized)
def tempFile(self):
'''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.wrappers import AbstractWrapper
from appy.gen import utils as gutils
from appy.shared import utils as sutils
# ------------------------------------------------------------------------------
class UserWrapper(AbstractWrapper):
@ -11,6 +12,19 @@ class UserWrapper(AbstractWrapper):
specialUsers = ('system', 'anon', 'admin')
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):
'''When must we show the login field?'''
if self.o.isTemporary(): return 'edit'

View file

@ -818,15 +818,21 @@ class AbstractWrapper(object):
'''Is this object a temporary object being created?'''
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
through reference field p_fieldName.'''
return self.getField(fieldName).linkObject(self, obj)
through reference field p_fieldName. For understanding the 2 last
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
one through reference field p_fieldName.'''
return self.getField(fieldName).unlinkObject(self, obj)
one through reference field p_fieldName. For understanding the 2 last
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):
'''Sorts referred elements linked to p_self via p_fieldName according
@ -843,7 +849,7 @@ class AbstractWrapper(object):
refs._p_changed = 1
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.
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
be raised. Else, the value will be silently ignored.
If p_executeOnEdit is False, the gen-class's onEdit method, if
present, will not be called.
If p_executeMethods is False, the gen-class's onEdit method, if
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)
tool = self.tool.o
# Determine the class of the object to create
if isField:
fieldName = fieldNameOrClass
appyType = self.o.getAppyType(fieldName)
portalType = tool.getPortalType(appyType.klass)
field = self.o.getAppyType(fieldName)
portalType = tool.getPortalType(field.klass)
else:
klass = fieldNameOrClass
portalType = tool.getPortalType(klass)
@ -885,7 +892,7 @@ class AbstractWrapper(object):
folder = self.o.getCreateFolder()
if not noSecurity:
# Check that the user can edit this field.
appyType.checkAdd(self.o)
field.checkAdd(self.o)
# Create the object
zopeObj = createObject(folder, objId, portalType, tool.getAppName(),
noSecurity=noSecurity)
@ -898,14 +905,14 @@ class AbstractWrapper(object):
if raiseOnWrongAttribute: raise ae
if isField:
# Link the object to this one
appyType.linkObject(self, appyObj)
field.linkObject(self, appyObj, executeMethods=executeMethods)
# Call custom initialization
if executeOnEdit and hasattr(appyObj, 'onEdit'): appyObj.onEdit(True)
if executeMethods and hasattr(appyObj, 'onEdit'): appyObj.onEdit(True)
zopeObj.reindex()
return appyObj
def createFrom(self, fieldNameOrClass, other, noSecurity=False,
executeOnEdit=True):
executeMethods=True):
'''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.'''
# 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)
return self.create(fieldNameOrClass, noSecurity=noSecurity,
raiseOnWrongAttribute=False,
executeOnEdit=executeOnEdit, **params)
executeMethods=executeMethods, **params)
def freeze(self, fieldName, template=None, format='pdf', noSecurity=True,
freezeOdtOnError=True):