def test_clear_scheduled_emails_with_multiple_user_ids(self) -> None: hamlet = self.example_user('hamlet') iago = self.example_user('iago') send_future_email('zerver/emails/followup_day1', iago.realm, to_user_ids=[hamlet.id, iago.id], delay=datetime.timedelta(hours=1)) self.assertEqual(ScheduledEmail.objects.count(), 1) clear_scheduled_emails([hamlet.id, iago.id]) self.assertEqual(ScheduledEmail.objects.count(), 0)
def do_welcome_unsubscribe(user_profile: UserProfile) -> None: clear_scheduled_emails([user_profile.id], ScheduledEmail.WELCOME)
def do_change_user_setting( user_profile: UserProfile, setting_name: str, setting_value: Union[bool, str, int], *, acting_user: Optional[UserProfile], ) -> None: old_value = getattr(user_profile, setting_name) event_time = timezone_now() if setting_name == "timezone": assert isinstance(setting_value, str) setting_value = canonicalize_timezone(setting_value) else: property_type = UserProfile.property_types[setting_name] assert isinstance(setting_value, property_type) setattr(user_profile, setting_name, setting_value) # TODO: Move these database actions into a transaction.atomic block. user_profile.save(update_fields=[setting_name]) if setting_name in UserProfile.notification_setting_types: # Prior to all personal settings being managed by property_types, # these were only created for notification settings. # # TODO: Start creating these for all settings, and do a # backfilled=True migration. RealmAuditLog.objects.create( realm=user_profile.realm, event_type=RealmAuditLog.USER_SETTING_CHANGED, event_time=event_time, acting_user=acting_user, modified_user=user_profile, extra_data=orjson.dumps({ RealmAuditLog.OLD_VALUE: old_value, RealmAuditLog.NEW_VALUE: setting_value, "property": setting_name, }).decode(), ) # Disabling digest emails should clear a user's email queue if setting_name == "enable_digest_emails" and not setting_value: clear_scheduled_emails(user_profile.id, ScheduledEmail.DIGEST) if setting_name == "email_notifications_batching_period_seconds": assert isinstance(old_value, int) assert isinstance(setting_value, int) update_scheduled_email_notifications_time(user_profile, old_value, setting_value) event = { "type": "user_settings", "op": "update", "property": setting_name, "value": setting_value, } if setting_name == "default_language": assert isinstance(setting_value, str) event["language_name"] = get_language_name(setting_value) transaction.on_commit( lambda: send_event(user_profile.realm, event, [user_profile.id])) if setting_name in UserProfile.notification_settings_legacy: # This legacy event format is for backwards-compatibility with # clients that don't support the new user_settings event type. # We only send this for settings added before Feature level 89. legacy_event = { "type": "update_global_notifications", "user": user_profile.email, "notification_name": setting_name, "setting": setting_value, } transaction.on_commit(lambda: send_event( user_profile.realm, legacy_event, [user_profile.id])) if setting_name in UserProfile.display_settings_legacy or setting_name == "timezone": # This legacy event format is for backwards-compatibility with # clients that don't support the new user_settings event type. # We only send this for settings added before Feature level 89. legacy_event = { "type": "update_display_settings", "user": user_profile.email, "setting_name": setting_name, "setting": setting_value, } if setting_name == "default_language": assert isinstance(setting_value, str) legacy_event["language_name"] = get_language_name(setting_value) transaction.on_commit(lambda: send_event( user_profile.realm, legacy_event, [user_profile.id])) # Updates to the time zone display setting are sent to all users if setting_name == "timezone": payload = dict( email=user_profile.email, user_id=user_profile.id, timezone=canonicalize_timezone(user_profile.timezone), ) timezone_event = dict(type="realm_user", op="update", person=payload) transaction.on_commit(lambda: send_event( user_profile.realm, timezone_event, active_user_ids(user_profile.realm_id), )) if setting_name == "enable_drafts_synchronization" and setting_value is False: # Delete all of the drafts from the backend but don't send delete events # for them since all that's happened is that we stopped syncing changes, # not deleted every previously synced draft - to do that use the DELETE # endpoint. Draft.objects.filter(user_profile=user_profile).delete()
def do_deactivate_user( user_profile: UserProfile, _cascade: bool = True, *, acting_user: Optional[UserProfile] ) -> None: if not user_profile.is_active: return if _cascade: # We need to deactivate bots before the target user, to ensure # that a failure partway through this function cannot result # in only the user being deactivated. bot_profiles = get_active_bots_owned_by_user(user_profile) for profile in bot_profiles: do_deactivate_user(profile, _cascade=False, acting_user=acting_user) with transaction.atomic(): if user_profile.realm.is_zephyr_mirror_realm: # nocoverage # For zephyr mirror users, we need to make them a mirror dummy # again; otherwise, other users won't get the correct behavior # when trying to send messages to this person inside Zulip. # # Ideally, we need to also ensure their zephyr mirroring bot # isn't running, but that's a separate issue. user_profile.is_mirror_dummy = True user_profile.save(update_fields=["is_mirror_dummy"]) change_user_is_active(user_profile, False) clear_scheduled_emails(user_profile.id) revoke_invites_generated_by_user(user_profile) event_time = timezone_now() RealmAuditLog.objects.create( realm=user_profile.realm, modified_user=user_profile, acting_user=acting_user, event_type=RealmAuditLog.USER_DEACTIVATED, event_time=event_time, extra_data=orjson.dumps( { RealmAuditLog.ROLE_COUNT: realm_user_count_by_role(user_profile.realm), } ).decode(), ) do_increment_logging_stat( user_profile.realm, COUNT_STATS["active_users_log:is_bot:day"], user_profile.is_bot, event_time, increment=-1, ) if settings.BILLING_ENABLED: update_license_ledger_if_needed(user_profile.realm, event_time) delete_user_sessions(user_profile) event = dict( type="realm_user", op="remove", person=dict(user_id=user_profile.id, full_name=user_profile.full_name), ) send_event(user_profile.realm, event, active_user_ids(user_profile.realm_id)) if user_profile.is_bot: event = dict( type="realm_bot", op="remove", bot=dict(user_id=user_profile.id, full_name=user_profile.full_name), ) send_event(user_profile.realm, event, bot_owner_user_ids(user_profile))