def test_temporary_disconnect(self): self.signal_.connect(self.listener) with util.disconnected_from(self.signal_, self.listener): self.signal_.send() assert_false(self.mock_listener.called)
def merge_user(self, user): """Merge a registered user into this account. This user will be a contributor on any project. if the registered user and this account are both contributors of the same project. Then it will remove the registered user and set this account to the highest permission of the two and set this account to be visible if either of the two are visible on the project. :param user: A User object to be merged. """ # Fail if the other user has conflicts. if not user.can_be_merged: raise MergeConflictError("Users cannot be merged") # Move over the other user's attributes # TODO: confirm for system_tag in user.system_tags: if system_tag not in self.system_tags: self.system_tags.append(system_tag) self.is_claimed = self.is_claimed or user.is_claimed self.is_invited = self.is_invited or user.is_invited # copy over profile only if this user has no profile info if user.jobs and not self.jobs: self.jobs = user.jobs if user.schools and not self.schools: self.schools = user.schools if user.social and not self.social: self.social = user.social unclaimed = user.unclaimed_records.copy() unclaimed.update(self.unclaimed_records) self.unclaimed_records = unclaimed # - unclaimed records should be connected to only one user user.unclaimed_records = {} security_messages = user.security_messages.copy() security_messages.update(self.security_messages) self.security_messages = security_messages for key, value in user.mailchimp_mailing_lists.iteritems(): # subscribe to each list if either user was subscribed subscription = value or self.mailchimp_mailing_lists.get(key) signals.user_merged.send(self, list_name=key, subscription=subscription) # clear subscriptions for merged user signals.user_merged.send(user, list_name=key, subscription=False, send_goodbye=False) for node_id, timestamp in user.comments_viewed_timestamp.iteritems(): if not self.comments_viewed_timestamp.get(node_id): self.comments_viewed_timestamp[node_id] = timestamp elif timestamp > self.comments_viewed_timestamp[node_id]: self.comments_viewed_timestamp[node_id] = timestamp self.emails.extend(user.emails) user.emails = [] for k, v in user.email_verifications.iteritems(): email_to_confirm = v['email'] if k not in self.email_verifications and email_to_confirm != user.username: self.email_verifications[k] = v user.email_verifications = {} # FOREIGN FIELDS for watched in user.watched: if watched not in self.watched: self.watched.append(watched) user.watched = [] for account in user.external_accounts: if account not in self.external_accounts: self.external_accounts.append(account) user.external_accounts = [] # - addons # Note: This must occur before the merged user is removed as a # contributor on the nodes, as an event hook is otherwise fired # which removes the credentials. for addon in user.get_addons(): user_settings = self.get_or_add_addon(addon.config.short_name) user_settings.merge(addon) user_settings.save() # Disconnect signal to prevent emails being sent about being a new contributor when merging users # be sure to reconnect it at the end of this code block. Import done here to prevent circular import error. from website.project.signals import contributor_added from website.project.views.contributor import notify_added_contributor from website.util import disconnected_from # - projects where the user was a contributor with disconnected_from(signal=contributor_added, listener=notify_added_contributor): for node in user.node__contributed: # Skip dashboard node if node.is_dashboard: continue # if both accounts are contributor of the same project if node.is_contributor(self) and node.is_contributor(user): if node.permissions[user._id] > node.permissions[self._id]: permissions = node.permissions[user._id] else: permissions = node.permissions[self._id] node.set_permissions(user=self, permissions=permissions) visible1 = self._id in node.visible_contributor_ids visible2 = user._id in node.visible_contributor_ids if visible1 != visible2: node.set_visible(user=self, visible=True, log=True, auth=Auth(user=self)) else: node.add_contributor( contributor=self, permissions=node.get_permissions(user), visible=node.get_visible(user), log=False, ) try: node.remove_contributor( contributor=user, auth=Auth(user=self), log=False, ) except ValueError: logger.error( 'Contributor {0} not in list on node {1}'.format( user._id, node._id)) node.save() # - projects where the user was the creator for node in user.node__created: node.creator = self node.save() # - file that the user has checked_out, import done here to prevent import error from website.files.models.base import FileNode for file_node in FileNode.files_checked_out(user=user): file_node.checkout = self file_node.save() # finalize the merge remove_sessions_for_user(user) # - username is set to None so the resultant user can set it primary # in the future. user.username = None user.password = None user.verification_key = None user.osf_mailing_lists = {} user.merged_by = self user.save()
def test_temporary_disconnect(self): self.signal_.connect(self.listener) with util.disconnected_from(self.signal_, self.listener): self.signal_.send() self.mock_listener.assert_not_called()
def merge_user(self, user): """Merge a registered user into this account. This user will be a contributor on any project. if the registered user and this account are both contributors of the same project. Then it will remove the registered user and set this account to the highest permission of the two and set this account to be visible if either of the two are visible on the project. :param user: A User object to be merged. """ # Fail if the other user has conflicts. if not user.can_be_merged: raise MergeConflictError("Users cannot be merged") # Move over the other user's attributes # TODO: confirm for system_tag in user.system_tags: if system_tag not in self.system_tags: self.system_tags.append(system_tag) self.is_claimed = self.is_claimed or user.is_claimed self.is_invited = self.is_invited or user.is_invited # copy over profile only if this user has no profile info if user.jobs and not self.jobs: self.jobs = user.jobs if user.schools and not self.schools: self.schools = user.schools if user.social and not self.social: self.social = user.social unclaimed = user.unclaimed_records.copy() unclaimed.update(self.unclaimed_records) self.unclaimed_records = unclaimed # - unclaimed records should be connected to only one user user.unclaimed_records = {} security_messages = user.security_messages.copy() security_messages.update(self.security_messages) self.security_messages = security_messages for key, value in user.mailchimp_mailing_lists.iteritems(): # subscribe to each list if either user was subscribed subscription = value or self.mailchimp_mailing_lists.get(key) signals.user_merged.send(self, list_name=key, subscription=subscription) # clear subscriptions for merged user signals.user_merged.send(user, list_name=key, subscription=False, send_goodbye=False) for node_id, timestamp in user.comments_viewed_timestamp.iteritems(): if not self.comments_viewed_timestamp.get(node_id): self.comments_viewed_timestamp[node_id] = timestamp elif timestamp > self.comments_viewed_timestamp[node_id]: self.comments_viewed_timestamp[node_id] = timestamp self.emails.extend(user.emails) user.emails = [] for k, v in user.email_verifications.iteritems(): email_to_confirm = v['email'] if k not in self.email_verifications and email_to_confirm != user.username: self.email_verifications[k] = v user.email_verifications = {} # FOREIGN FIELDS for watched in user.watched: if watched not in self.watched: self.watched.append(watched) user.watched = [] for account in user.external_accounts: if account not in self.external_accounts: self.external_accounts.append(account) user.external_accounts = [] # - addons # Note: This must occur before the merged user is removed as a # contributor on the nodes, as an event hook is otherwise fired # which removes the credentials. for addon in user.get_addons(): user_settings = self.get_or_add_addon(addon.config.short_name) user_settings.merge(addon) user_settings.save() # Disconnect signal to prevent emails being sent about being a new contributor when merging users # be sure to reconnect it at the end of this code block. Import done here to prevent circular import error. from website.project.signals import contributor_added from website.project.views.contributor import notify_added_contributor from website.util import disconnected_from # - projects where the user was a contributor with disconnected_from(signal=contributor_added, listener=notify_added_contributor): for node in user.node__contributed: # Skip dashboard node if node.is_dashboard: continue # if both accounts are contributor of the same project if node.is_contributor(self) and node.is_contributor(user): if node.permissions[user._id] > node.permissions[self._id]: permissions = node.permissions[user._id] else: permissions = node.permissions[self._id] node.set_permissions(user=self, permissions=permissions) visible1 = self._id in node.visible_contributor_ids visible2 = user._id in node.visible_contributor_ids if visible1 != visible2: node.set_visible(user=self, visible=True, log=True, auth=Auth(user=self)) else: node.add_contributor( contributor=self, permissions=node.get_permissions(user), visible=node.get_visible(user), log=False, ) try: node.remove_contributor( contributor=user, auth=Auth(user=self), log=False, ) except ValueError: logger.error('Contributor {0} not in list on node {1}'.format( user._id, node._id )) node.save() # - projects where the user was the creator for node in user.node__created: node.creator = self node.save() # - file that the user has checked_out, import done here to prevent import error from website.files.models.base import FileNode for file_node in FileNode.files_checked_out(user=user): file_node.checkout = self file_node.save() # finalize the merge remove_sessions_for_user(user) # - username is set to None so the resultant user can set it primary # in the future. user.username = None user.password = None user.verification_key = None user.osf_mailing_lists = {} user.merged_by = self user.save()