diff --git a/fields/ref.py b/fields/ref.py index 2df23f7..7ddce93 100644 --- a/fields/ref.py +++ b/fields/ref.py @@ -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: diff --git a/gen/mixins/ToolMixin.py b/gen/mixins/ToolMixin.py index ea494c2..0a90c85 100644 --- a/gen/mixins/ToolMixin.py +++ b/gen/mixins/ToolMixin.py @@ -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 diff --git a/gen/wrappers/UserWrapper.py b/gen/wrappers/UserWrapper.py index 9dce449..120ff8d 100644 --- a/gen/wrappers/UserWrapper.py +++ b/gen/wrappers/UserWrapper.py @@ -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' diff --git a/gen/wrappers/__init__.py b/gen/wrappers/__init__.py index bb90718..92cbddf 100644 --- a/gen/wrappers/__init__.py +++ b/gen/wrappers/__init__.py @@ -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):