def do_change_full_name(user_profile: UserProfile, full_name: str, acting_user: Optional[UserProfile]) -> None: old_name = user_profile.full_name user_profile.full_name = full_name user_profile.save(update_fields=["full_name"]) event_time = timezone_now() RealmAuditLog.objects.create( realm=user_profile.realm, acting_user=acting_user, modified_user=user_profile, event_type=RealmAuditLog.USER_FULL_NAME_CHANGED, event_time=event_time, extra_data=old_name, ) payload = dict(user_id=user_profile.id, full_name=user_profile.full_name) send_event( user_profile.realm, dict(type="realm_user", op="update", person=payload), active_user_ids(user_profile.realm_id), ) if user_profile.is_bot: send_event( user_profile.realm, dict(type="realm_bot", op="update", bot=payload), bot_owner_user_ids(user_profile), )
def do_create_stripe_customer(user: UserProfile, stripe_token: Optional[str] = None) -> Customer: realm = user.realm # We could do a better job of handling race conditions here, but if two # people from a realm try to upgrade at exactly the same time, the main # bad thing that will happen is that we will create an extra stripe # customer that we can delete or ignore. stripe_customer = stripe.Customer.create(description="%s (%s)" % (realm.string_id, realm.name), email=user.delivery_email, metadata={ 'realm_id': realm.id, 'realm_str': realm.string_id }, source=stripe_token) event_time = timestamp_to_datetime(stripe_customer.created) with transaction.atomic(): RealmAuditLog.objects.create( realm=user.realm, acting_user=user, event_type=RealmAuditLog.STRIPE_CUSTOMER_CREATED, event_time=event_time) if stripe_token is not None: RealmAuditLog.objects.create( realm=user.realm, acting_user=user, event_type=RealmAuditLog.STRIPE_CARD_CHANGED, event_time=event_time) customer, created = Customer.objects.update_or_create( realm=realm, defaults={'stripe_customer_id': stripe_customer.id}) user.is_billing_admin = True user.save(update_fields=["is_billing_admin"]) return customer
def do_create_customer(user: UserProfile, stripe_token: Optional[str]=None, coupon: Optional[Coupon]=None) -> stripe.Customer: realm = user.realm stripe_coupon_id = None if coupon is not None: stripe_coupon_id = coupon.stripe_coupon_id # We could do a better job of handling race conditions here, but if two # people from a realm try to upgrade at exactly the same time, the main # bad thing that will happen is that we will create an extra stripe # customer that we can delete or ignore. stripe_customer = stripe.Customer.create( description="%s (%s)" % (realm.string_id, realm.name), email=user.email, metadata={'realm_id': realm.id, 'realm_str': realm.string_id}, source=stripe_token, coupon=stripe_coupon_id) if PRINT_STRIPE_FIXTURE_DATA: print(''.join(['"create_customer": ', str(stripe_customer), ','])) # nocoverage event_time = timestamp_to_datetime(stripe_customer.created) with transaction.atomic(): RealmAuditLog.objects.create( realm=user.realm, acting_user=user, event_type=RealmAuditLog.STRIPE_CUSTOMER_CREATED, event_time=event_time) if stripe_token is not None: RealmAuditLog.objects.create( realm=user.realm, acting_user=user, event_type=RealmAuditLog.STRIPE_CARD_ADDED, event_time=event_time) Customer.objects.create(realm=realm, stripe_customer_id=stripe_customer.id) user.is_billing_admin = True user.save(update_fields=["is_billing_admin"]) return stripe_customer
def do_create_stripe_customer(user: UserProfile, stripe_token: Optional[str]=None) -> Customer: realm = user.realm # We could do a better job of handling race conditions here, but if two # people from a realm try to upgrade at exactly the same time, the main # bad thing that will happen is that we will create an extra stripe # customer that we can delete or ignore. stripe_customer = stripe.Customer.create( description="%s (%s)" % (realm.string_id, realm.name), email=user.email, metadata={'realm_id': realm.id, 'realm_str': realm.string_id}, source=stripe_token) event_time = timestamp_to_datetime(stripe_customer.created) with transaction.atomic(): RealmAuditLog.objects.create( realm=user.realm, acting_user=user, event_type=RealmAuditLog.STRIPE_CUSTOMER_CREATED, event_time=event_time) if stripe_token is not None: RealmAuditLog.objects.create( realm=user.realm, acting_user=user, event_type=RealmAuditLog.STRIPE_CARD_CHANGED, event_time=event_time) customer, created = Customer.objects.update_or_create(realm=realm, defaults={ 'stripe_customer_id': stripe_customer.id}) user.is_billing_admin = True user.save(update_fields=["is_billing_admin"]) return customer
def get_next_hotspots(user: UserProfile) -> List[Dict[str, object]]: # For manual testing, it can be convenient to set # ALWAYS_SEND_ALL_HOTSPOTS=True in `zproject/dev_settings.py` to # make it easy to click on all of the hotspots. if settings.ALWAYS_SEND_ALL_HOTSPOTS: return [{ 'name': hotspot, 'title': ALL_HOTSPOTS[hotspot]['title'], 'description': ALL_HOTSPOTS[hotspot]['description'], 'delay': 0, } for hotspot in ALL_HOTSPOTS] if user.tutorial_status == UserProfile.TUTORIAL_FINISHED: return [] seen_hotspots = frozenset(UserHotspot.objects.filter(user=user).values_list('hotspot', flat=True)) for hotspot in ['intro_reply', 'intro_streams', 'intro_topics', 'intro_compose']: if hotspot not in seen_hotspots: return [{ 'name': hotspot, 'title': ALL_HOTSPOTS[hotspot]['title'], 'description': ALL_HOTSPOTS[hotspot]['description'], 'delay': 0.5, }] user.tutorial_status = UserProfile.TUTORIAL_FINISHED user.save(update_fields=['tutorial_status']) return []
def get_next_hotspots(user: UserProfile) -> List[Dict[str, object]]: # For manual testing, it can be convenient to set # ALWAYS_SEND_ALL_HOTSPOTS=True in `zproject/dev_settings.py` to # make it easy to click on all of the hotspots. Note that # ALWAYS_SEND_ALL_HOTSPOTS has some bugs; see ReadTheDocs (link # above) for details. if settings.ALWAYS_SEND_ALL_HOTSPOTS: return [{ "name": hotspot, "title": str(ALL_HOTSPOTS[hotspot]["title"]), "description": str(ALL_HOTSPOTS[hotspot]["description"]), "delay": 0, } for hotspot in ALL_HOTSPOTS] if user.tutorial_status == UserProfile.TUTORIAL_FINISHED: return [] seen_hotspots = frozenset( UserHotspot.objects.filter(user=user).values_list("hotspot", flat=True)) for hotspot in [ "intro_reply", "intro_streams", "intro_topics", "intro_gear", "intro_compose" ]: if hotspot not in seen_hotspots: return [{ "name": hotspot, "title": str(ALL_HOTSPOTS[hotspot]["title"]), "description": str(ALL_HOTSPOTS[hotspot]["description"]), "delay": 0.5, }] user.tutorial_status = UserProfile.TUTORIAL_FINISHED user.save(update_fields=["tutorial_status"]) return []
def get_next_hotspots(user: UserProfile) -> List[Dict[str, object]]: # For manual testing, it can be convenient to set # ALWAYS_SEND_ALL_HOTSPOTS=True in `zproject/dev_settings.py` to # make it easy to click on all of the hotspots. if settings.ALWAYS_SEND_ALL_HOTSPOTS: return [{ 'name': hotspot, 'title': ALL_HOTSPOTS[hotspot]['title'], 'description': ALL_HOTSPOTS[hotspot]['description'], 'delay': 0, } for hotspot in ALL_HOTSPOTS] if user.tutorial_status == UserProfile.TUTORIAL_FINISHED: return [] seen_hotspots = frozenset( UserHotspot.objects.filter(user=user).values_list('hotspot', flat=True)) for hotspot in [ 'intro_reply', 'intro_streams', 'intro_topics', 'intro_compose' ]: if hotspot not in seen_hotspots: return [{ 'name': hotspot, 'title': ALL_HOTSPOTS[hotspot]['title'], 'description': ALL_HOTSPOTS[hotspot]['description'], 'delay': 0.5, }] user.tutorial_status = UserProfile.TUTORIAL_FINISHED user.save(update_fields=['tutorial_status']) return []
def do_make_user_billing_admin(user_profile: UserProfile) -> None: user_profile.is_billing_admin = True user_profile.save(update_fields=["is_billing_admin"]) event = dict( type="realm_user", op="update", person=dict(user_id=user_profile.id, is_billing_admin=True) ) send_event(user_profile.realm, event, active_user_ids(user_profile.realm_id))
def do_change_default_all_public_streams( user_profile: UserProfile, value: bool, *, acting_user: Optional[UserProfile]) -> None: old_value = user_profile.default_all_public_streams user_profile.default_all_public_streams = value user_profile.save(update_fields=["default_all_public_streams"]) event_time = timezone_now() RealmAuditLog.objects.create( realm=user_profile.realm, event_type=RealmAuditLog.USER_DEFAULT_ALL_PUBLIC_STREAMS_CHANGED, event_time=event_time, modified_user=user_profile, acting_user=acting_user, extra_data=orjson.dumps({ RealmAuditLog.OLD_VALUE: old_value, RealmAuditLog.NEW_VALUE: value, }).decode(), ) if user_profile.is_bot: event = dict( type="realm_bot", op="update", bot=dict( user_id=user_profile.id, default_all_public_streams=user_profile. default_all_public_streams, ), ) transaction.on_commit(lambda: send_event( user_profile.realm, event, bot_owner_user_ids(user_profile), ))
def get_next_hotspots(user: UserProfile) -> List[Dict[str, object]]: # Only used for manual testing SEND_ALL = False if settings.DEVELOPMENT and SEND_ALL: return [{ 'name': hotspot, 'title': ALL_HOTSPOTS[hotspot]['title'], 'description': ALL_HOTSPOTS[hotspot]['description'], 'delay': 0, } for hotspot in ALL_HOTSPOTS] if user.tutorial_status == UserProfile.TUTORIAL_FINISHED: return [] seen_hotspots = frozenset(UserHotspot.objects.filter(user=user).values_list('hotspot', flat=True)) for hotspot in ['intro_reply', 'intro_streams', 'intro_topics', 'intro_compose']: if hotspot not in seen_hotspots: return [{ 'name': hotspot, 'title': ALL_HOTSPOTS[hotspot]['title'], 'description': ALL_HOTSPOTS[hotspot]['description'], 'delay': 0.5, }] user.tutorial_status = UserProfile.TUTORIAL_FINISHED user.save(update_fields=['tutorial_status']) return []
def copy_user_settings(source_profile: UserProfile, target_profile: UserProfile) -> None: # Important note: Code run from here to configure the user's # settings should not call send_event, as that would cause clients # to throw an exception (we haven't sent the realm_user/add event # yet, so that event will include the updated details of target_profile). # # Note that this function will do at least one save() on target_profile. for settings_name in UserProfile.property_types: value = getattr(source_profile, settings_name) setattr(target_profile, settings_name, value) for settings_name in UserProfile.notification_setting_types: value = getattr(source_profile, settings_name) setattr(target_profile, settings_name, value) setattr(target_profile, "full_name", source_profile.full_name) setattr(target_profile, "enter_sends", source_profile.enter_sends) target_profile.save() if source_profile.avatar_source == UserProfile.AVATAR_FROM_USER: from zerver.lib.actions import do_change_avatar_fields do_change_avatar_fields( target_profile, UserProfile.AVATAR_FROM_USER, skip_notify=True, acting_user=target_profile, ) copy_avatar(source_profile, target_profile) copy_hotpots(source_profile, target_profile)
def get_next_hotspots(user: UserProfile) -> List[Dict[str, object]]: # For manual testing, it can be convenient to set # ALWAYS_SEND_ALL_HOTSPOTS=True in `zproject/dev_settings.py` to # make it easy to click on all of the hotspots. Note that # ALWAYS_SEND_ALL_HOTSPOTS has some bugs; see ReadTheDocs (link # above) for details. # # Since this is just for development purposes, it's convinient for us to send # all the hotspots rather than any specific category. if settings.ALWAYS_SEND_ALL_HOTSPOTS: return [{ "name": hotspot, "title": str(ALL_HOTSPOTS[hotspot]["title"]), "description": str(ALL_HOTSPOTS[hotspot]["description"]), "delay": 0, } for hotspot in ALL_HOTSPOTS] if user.tutorial_status == UserProfile.TUTORIAL_FINISHED: return [] seen_hotspots = frozenset( UserHotspot.objects.filter(user=user).values_list("hotspot", flat=True)) for hotspot in INTRO_HOTSPOTS.keys(): if hotspot not in seen_hotspots: return [{ "name": hotspot, "title": str(INTRO_HOTSPOTS[hotspot]["title"]), "description": str(INTRO_HOTSPOTS[hotspot]["description"]), "delay": 0.5, }] user.tutorial_status = UserProfile.TUTORIAL_FINISHED user.save(update_fields=["tutorial_status"]) return []
def do_create_customer(user: UserProfile, stripe_token: Optional[str]=None, coupon: Optional[Coupon]=None) -> stripe.Customer: realm = user.realm stripe_coupon_id = None if coupon is not None: stripe_coupon_id = coupon.stripe_coupon_id # We could do a better job of handling race conditions here, but if two # people from a realm try to upgrade at exactly the same time, the main # bad thing that will happen is that we will create an extra stripe # customer that we can delete or ignore. stripe_customer = stripe.Customer.create( description="%s (%s)" % (realm.string_id, realm.name), email=user.email, metadata={'realm_id': realm.id, 'realm_str': realm.string_id}, source=stripe_token, coupon=stripe_coupon_id) if PRINT_STRIPE_FIXTURE_DATA: print(''.join(['"create_customer": ', str(stripe_customer), ','])) # nocoverage event_time = timestamp_to_datetime(stripe_customer.created) with transaction.atomic(): RealmAuditLog.objects.create( realm=user.realm, acting_user=user, event_type=RealmAuditLog.STRIPE_CUSTOMER_CREATED, event_time=event_time) if stripe_token is not None: RealmAuditLog.objects.create( realm=user.realm, acting_user=user, event_type=RealmAuditLog.STRIPE_CARD_CHANGED, event_time=event_time) Customer.objects.create(realm=realm, stripe_customer_id=stripe_customer.id) user.is_billing_admin = True user.save(update_fields=["is_billing_admin"]) return stripe_customer
def sync_avatar_from_ldap(self, user: UserProfile, ldap_user: _LDAPUser) -> None: if 'avatar' in settings.AUTH_LDAP_USER_ATTR_MAP: # We do local imports here to avoid import loops from zerver.lib.upload import upload_avatar_image from zerver.lib.actions import do_change_avatar_fields from io import BytesIO avatar_attr_name = settings.AUTH_LDAP_USER_ATTR_MAP['avatar'] if avatar_attr_name not in ldap_user.attrs: # nocoverage # If this specific user doesn't have e.g. a # thumbnailPhoto set in LDAP, just skip that user. return ldap_avatar = ldap_user.attrs[avatar_attr_name][0] avatar_changed = is_avatar_new(ldap_avatar, user) if not avatar_changed: # Don't do work to replace the avatar with itself. return io = BytesIO(ldap_avatar) # Structurally, to make the S3 backend happy, we need to # provide a Content-Type; since that isn't specified in # any metadata, we auto-detect it. content_type = magic.from_buffer(copy.deepcopy(io).read()[0:1024], mime=True) if content_type.startswith("image/"): upload_avatar_image(io, user, user, content_type=content_type) do_change_avatar_fields(user, UserProfile.AVATAR_FROM_USER) # Update avatar hash. user.avatar_hash = user_avatar_content_hash(ldap_avatar) user.save(update_fields=["avatar_hash"]) else: logging.warning("Could not parse %s field for user %s" % (avatar_attr_name, user.id))
def copy_hotpots(source_profile: UserProfile, target_profile: UserProfile) -> None: for userhotspot in frozenset(UserHotspot.objects.filter(user=source_profile)): UserHotspot.objects.create(user=target_profile, hotspot=userhotspot.hotspot, timestamp=userhotspot.timestamp) target_profile.tutorial_status = source_profile.tutorial_status target_profile.onboarding_steps = source_profile.onboarding_steps target_profile.save(update_fields=['tutorial_status', 'onboarding_steps'])
def do_soft_deactivate_user(user_profile: UserProfile) -> None: user_profile.last_active_message_id = UserMessage.objects.filter( user_profile=user_profile).order_by('-message__id')[0].message_id user_profile.long_term_idle = True user_profile.save( update_fields=['long_term_idle', 'last_active_message_id']) logger.info('Soft Deactivated user %s (%s)' % (user_profile.id, user_profile.email))
def do_set_zoom_token(user: UserProfile, token: Optional[Dict[str, object]]) -> None: user.zoom_token = token user.save(update_fields=["zoom_token"]) send_event( user.realm, dict(type="has_zoom_token", value=token is not None), [user.id], )
def set_tutorial_status(request: HttpRequest, user_profile: UserProfile, status: str=REQ(validator=check_string)) -> HttpResponse: if status == 'started': user_profile.tutorial_status = UserProfile.TUTORIAL_STARTED elif status == 'finished': user_profile.tutorial_status = UserProfile.TUTORIAL_FINISHED user_profile.save(update_fields=["tutorial_status"]) return json_success()
def do_soft_deactivate_user(user_profile: UserProfile) -> None: user_profile.last_active_message_id = UserMessage.objects.filter( user_profile=user_profile).order_by( '-message__id')[0].message_id user_profile.long_term_idle = True user_profile.save(update_fields=[ 'long_term_idle', 'last_active_message_id']) logger.info('Soft Deactivated user %s (%s)' % (user_profile.id, user_profile.email))
def do_change_tos_version(user_profile: UserProfile, tos_version: str) -> None: user_profile.tos_version = tos_version user_profile.save(update_fields=["tos_version"]) event_time = timezone_now() RealmAuditLog.objects.create( realm=user_profile.realm, acting_user=user_profile, modified_user=user_profile, event_type=RealmAuditLog.USER_TERMS_OF_SERVICE_VERSION_CHANGED, event_time=event_time, )
def change_user_is_active(user_profile: UserProfile, value: bool) -> None: """ Helper function for changing the .is_active field. Not meant as a standalone function in production code as properly activating/deactivating users requires more steps. This changes the is_active value and saves it, while ensuring Subscription.is_user_active values are updated in the same db transaction. """ with transaction.atomic(savepoint=False): user_profile.is_active = value user_profile.save(update_fields=["is_active"]) Subscription.objects.filter(user_profile=user_profile).update(is_user_active=value)
def do_soft_deactivate_user(user_profile: UserProfile) -> None: try: user_profile.last_active_message_id = UserMessage.objects.filter( user_profile=user_profile).order_by('-message__id')[0].message_id except IndexError: # nocoverage # In the unlikely event that a user somehow has never received # a message, we just use the overall max message ID. user_profile.last_active_message_id = Message.objects.max().id user_profile.long_term_idle = True user_profile.save( update_fields=['long_term_idle', 'last_active_message_id']) logger.info('Soft Deactivated user %s' % (user_profile.id, ))
def do_soft_deactivate_user(user_profile: UserProfile) -> None: try: user_profile.last_active_message_id = (UserMessage.objects.filter( user_profile=user_profile).order_by("-message_id")[0].message_id) except IndexError: # nocoverage # In the unlikely event that a user somehow has never received # a message, we just use the overall max message ID. user_profile.last_active_message_id = Message.objects.last().id user_profile.long_term_idle = True user_profile.save( update_fields=["long_term_idle", "last_active_message_id"]) logger.info("Soft deactivated user %s", user_profile.id)
def reactivate_user_if_soft_deactivated( user_profile: UserProfile) -> Union[UserProfile, None]: if user_profile.long_term_idle: add_missing_messages(user_profile) user_profile.long_term_idle = False user_profile.save(update_fields=['long_term_idle']) RealmAuditLog.objects.create( realm=user_profile.realm, modified_user=user_profile, event_type=RealmAuditLog.USER_SOFT_ACTIVATED, event_time=timezone_now()) logger.info('Soft Reactivated user %s' % (user_profile.id, )) return user_profile return None
def maybe_catch_up_soft_deactivated_user( user_profile: UserProfile) -> Union[UserProfile, None]: if user_profile.long_term_idle: add_missing_messages(user_profile) user_profile.long_term_idle = False user_profile.save(update_fields=['long_term_idle']) RealmAuditLog.objects.create(realm=user_profile.realm, modified_user=user_profile, event_type='user_soft_activated', event_time=timezone_now()) logger.info('Soft Reactivated user %s (%s)' % (user_profile.id, user_profile.email)) return user_profile return None
def do_soft_deactivate_user(user_profile: UserProfile) -> None: try: user_profile.last_active_message_id = UserMessage.objects.filter( user_profile=user_profile).order_by( '-message__id')[0].message_id except IndexError: # nocoverage # In the unlikely event that a user somehow has never received # a message, we just use the overall max message ID. user_profile.last_active_message_id = Message.objects.max().id user_profile.long_term_idle = True user_profile.save(update_fields=[ 'long_term_idle', 'last_active_message_id']) logger.info('Soft Deactivated user %s (%s)' % (user_profile.id, user_profile.email))
def maybe_catch_up_soft_deactivated_user(user_profile: UserProfile) -> Union[UserProfile, None]: if user_profile.long_term_idle: add_missing_messages(user_profile) user_profile.long_term_idle = False user_profile.save(update_fields=['long_term_idle']) RealmAuditLog.objects.create( realm=user_profile.realm, modified_user=user_profile, event_type=RealmAuditLog.USER_SOFT_ACTIVATED, event_time=timezone_now() ) logger.info('Soft Reactivated user %s (%s)' % (user_profile.id, user_profile.email)) return user_profile return None
def sponsorship( request: HttpRequest, user: UserProfile, organization_type: str = REQ("organization-type", validator=check_string), website: str = REQ("website", validator=check_string), description: str = REQ("description", validator=check_string) ) -> HttpResponse: realm = user.realm requested_by = user.full_name role_id_to_name_map = { UserProfile.ROLE_REALM_OWNER: "Realm owner", UserProfile.ROLE_REALM_ADMINISTRATOR: "Realm adminstrator", UserProfile.ROLE_MEMBER: "Member", UserProfile.ROLE_GUEST: "Guest" } user_role = role_id_to_name_map[user.role] support_realm_uri = get_realm(settings.STAFF_SUBDOMAIN).uri support_url = urljoin( support_realm_uri, urlunsplit(("", "", reverse('analytics.views.support'), urlencode({"q": realm.string_id}), ""))) context = { "requested_by": requested_by, "user_role": user_role, "string_id": realm.string_id, "support_url": support_url, "organization_type": organization_type, "website": website, "description": description, } send_email( "zerver/emails/sponsorship_request", to_emails=[FromAddress.SUPPORT], from_name="Zulip sponsorship", from_address=FromAddress.tokenized_no_reply_address(), reply_to_email=user.delivery_email, context=context, ) update_sponsorship_status(realm, True) user.is_billing_admin = True user.save(update_fields=["is_billing_admin"]) return json_success()
def do_activate_mirror_dummy_user(user_profile: UserProfile, *, acting_user: Optional[UserProfile]) -> None: """Called to have a user "take over" a "mirror dummy" user (i.e. is_mirror_dummy=True) account when they sign up with the same email address. Essentially, the result should be as though we had created the UserProfile just now with do_create_user, except that the mirror dummy user may appear as the recipient or sender of messages from before their account was fully created. TODO: This function likely has bugs resulting from this being a parallel code path to do_create_user; e.g. it likely does not handle preferences or default streams properly. """ with transaction.atomic(): change_user_is_active(user_profile, True) user_profile.is_mirror_dummy = False user_profile.set_unusable_password() user_profile.date_joined = timezone_now() user_profile.tos_version = settings.TERMS_OF_SERVICE_VERSION user_profile.save(update_fields=[ "date_joined", "password", "is_mirror_dummy", "tos_version" ]) event_time = user_profile.date_joined RealmAuditLog.objects.create( realm=user_profile.realm, modified_user=user_profile, acting_user=acting_user, event_type=RealmAuditLog.USER_ACTIVATED, 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, ) if settings.BILLING_ENABLED: update_license_ledger_if_needed(user_profile.realm, event_time) notify_created_user(user_profile)
def copy_user_settings(source_profile: UserProfile, target_profile: UserProfile) -> None: """Warning: Does not save, to avoid extra database queries""" for settings_name in UserProfile.property_types: value = getattr(source_profile, settings_name) setattr(target_profile, settings_name, value) for settings_name in UserProfile.notification_setting_types: value = getattr(source_profile, settings_name) setattr(target_profile, settings_name, value) setattr(target_profile, "full_name", source_profile.full_name) target_profile.save() if source_profile.avatar_source == UserProfile.AVATAR_FROM_USER: from zerver.lib.actions import do_change_avatar_fields do_change_avatar_fields(target_profile, UserProfile.AVATAR_FROM_USER) copy_avatar(source_profile, target_profile)
def sponsorship( request: HttpRequest, user: UserProfile, organization_type: str = REQ("organization-type", json_validator=check_string), website: str = REQ("website", json_validator=check_string), description: str = REQ("description", json_validator=check_string), ) -> HttpResponse: realm = user.realm requested_by = user.full_name user_role = user.get_role_name() support_realm_uri = get_realm(settings.STAFF_SUBDOMAIN).uri support_url = urljoin( support_realm_uri, urlunsplit( ("", "", reverse("support"), urlencode({"q": realm.string_id}), "")), ) context = { "requested_by": requested_by, "user_role": user_role, "string_id": realm.string_id, "support_url": support_url, "organization_type": organization_type, "website": website, "description": description, } send_email( "zerver/emails/sponsorship_request", to_emails=[FromAddress.SUPPORT], from_name="Zulip sponsorship", from_address=FromAddress.tokenized_no_reply_address(), reply_to_email=user.delivery_email, context=context, ) update_sponsorship_status(realm, True, acting_user=user) user.is_billing_admin = True user.save(update_fields=["is_billing_admin"]) return json_success()
def do_change_user_role( user_profile: UserProfile, value: int, *, acting_user: Optional[UserProfile] ) -> None: old_value = user_profile.role old_system_group = get_system_user_group_for_user(user_profile) user_profile.role = value user_profile.save(update_fields=["role"]) RealmAuditLog.objects.create( realm=user_profile.realm, modified_user=user_profile, acting_user=acting_user, event_type=RealmAuditLog.USER_ROLE_CHANGED, event_time=timezone_now(), extra_data=orjson.dumps( { RealmAuditLog.OLD_VALUE: old_value, RealmAuditLog.NEW_VALUE: value, RealmAuditLog.ROLE_COUNT: realm_user_count_by_role(user_profile.realm), } ).decode(), ) event = dict( type="realm_user", op="update", person=dict(user_id=user_profile.id, role=user_profile.role) ) transaction.on_commit( lambda: send_event(user_profile.realm, event, active_user_ids(user_profile.realm_id)) ) UserGroupMembership.objects.filter( user_profile=user_profile, user_group=old_system_group ).delete() system_group = get_system_user_group_for_user(user_profile) UserGroupMembership.objects.create(user_profile=user_profile, user_group=system_group) do_send_user_group_members_update_event("remove_members", old_system_group, [user_profile.id]) do_send_user_group_members_update_event("add_members", system_group, [user_profile.id]) if UserProfile.ROLE_MEMBER in [old_value, value]: update_users_in_full_members_system_group(user_profile.realm, [user_profile.id])
def do_regenerate_api_key(user_profile: UserProfile, acting_user: UserProfile) -> str: old_api_key = user_profile.api_key new_api_key = generate_api_key() user_profile.api_key = new_api_key user_profile.save(update_fields=["api_key"]) # We need to explicitly delete the old API key from our caches, # because the on-save handler for flushing the UserProfile object # in zerver/lib/cache.py only has access to the new API key. cache_delete(user_profile_by_api_key_cache_key(old_api_key)) event_time = timezone_now() RealmAuditLog.objects.create( realm=user_profile.realm, acting_user=acting_user, modified_user=user_profile, event_type=RealmAuditLog.USER_API_KEY_CHANGED, event_time=event_time, ) if user_profile.is_bot: send_event( user_profile.realm, dict( type="realm_bot", op="update", bot=dict( user_id=user_profile.id, api_key=new_api_key, ), ), bot_owner_user_ids(user_profile), ) event = { "type": "clear_push_device_tokens", "user_profile_id": user_profile.id } queue_json_publish("deferred_work", event) return new_api_key
def do_change_password(user_profile: UserProfile, password: str, commit: bool = True) -> None: user_profile.set_password(password) if commit: user_profile.save(update_fields=["password"]) # Imported here to prevent import cycles from zproject.backends import RateLimitedAuthenticationByUsername RateLimitedAuthenticationByUsername( user_profile.delivery_email).clear_history() event_time = timezone_now() RealmAuditLog.objects.create( realm=user_profile.realm, acting_user=user_profile, modified_user=user_profile, event_type=RealmAuditLog.USER_PASSWORD_CHANGED, event_time=event_time, )
def do_change_default_events_register_stream( user_profile: UserProfile, stream: Optional[Stream], *, acting_user: Optional[UserProfile]) -> None: old_value = user_profile.default_events_register_stream_id user_profile.default_events_register_stream = stream user_profile.save(update_fields=["default_events_register_stream"]) event_time = timezone_now() RealmAuditLog.objects.create( realm=user_profile.realm, event_type=RealmAuditLog.USER_DEFAULT_REGISTER_STREAM_CHANGED, event_time=event_time, modified_user=user_profile, acting_user=acting_user, extra_data=orjson.dumps({ RealmAuditLog.OLD_VALUE: old_value, RealmAuditLog.NEW_VALUE: None if stream is None else stream.id, }).decode(), ) if user_profile.is_bot: if stream: stream_name: Optional[str] = stream.name else: stream_name = None event = dict( type="realm_bot", op="update", bot=dict( user_id=user_profile.id, default_events_register_stream=stream_name, ), ) transaction.on_commit(lambda: send_event( user_profile.realm, event, bot_owner_user_ids(user_profile), ))
def do_change_avatar_fields( user_profile: UserProfile, avatar_source: str, skip_notify: bool = False, *, acting_user: Optional[UserProfile], ) -> None: user_profile.avatar_source = avatar_source user_profile.avatar_version += 1 user_profile.save(update_fields=["avatar_source", "avatar_version"]) event_time = timezone_now() RealmAuditLog.objects.create( realm=user_profile.realm, modified_user=user_profile, event_type=RealmAuditLog.USER_AVATAR_SOURCE_CHANGED, extra_data={"avatar_source": avatar_source}, event_time=event_time, acting_user=acting_user, ) if not skip_notify: notify_avatar_url_change(user_profile)
def set_user_alert_words(user_profile: UserProfile, alert_words: List[str]) -> None: user_profile.alert_words = ujson.dumps(alert_words) user_profile.save(update_fields=['alert_words'])
def do_send_missedmessage_events_reply_in_zulip(user_profile: UserProfile, missed_messages: List[Message], message_count: int) -> None: """ Send a reminder email to a user if she's missed some PMs by being offline. The email will have its reply to address set to a limited used email address that will send a zulip message to the correct recipient. This allows the user to respond to missed PMs, huddles, and @-mentions directly from the email. `user_profile` is the user to send the reminder to `missed_messages` is a list of Message objects to remind about they should all have the same recipient and subject """ from zerver.context_processors import common_context # Disabled missedmessage emails internally if not user_profile.enable_offline_email_notifications: return recipients = set((msg.recipient_id, msg.subject) for msg in missed_messages) if len(recipients) != 1: raise ValueError( 'All missed_messages must have the same recipient and subject %r' % recipients ) unsubscribe_link = one_click_unsubscribe_link(user_profile, "missed_messages") context = common_context(user_profile) context.update({ 'name': user_profile.full_name, 'message_count': message_count, 'mention': missed_messages[0].is_stream_message(), 'unsubscribe_link': unsubscribe_link, 'realm_name_in_notifications': user_profile.realm_name_in_notifications, 'show_message_content': user_profile.message_content_in_email_notifications, }) # If this setting (email mirroring integration) is enabled, only then # can users reply to email to send message to Zulip. Thus, one must # ensure to display warning in the template. if settings.EMAIL_GATEWAY_PATTERN: context.update({ 'reply_warning': False, 'reply_to_zulip': True, }) else: context.update({ 'reply_warning': True, 'reply_to_zulip': False, }) from zerver.lib.email_mirror import create_missed_message_address reply_to_address = create_missed_message_address(user_profile, missed_messages[0]) if reply_to_address == FromAddress.NOREPLY: reply_to_name = None else: reply_to_name = "Zulip" senders = list(set(m.sender for m in missed_messages)) if (missed_messages[0].recipient.type == Recipient.HUDDLE): display_recipient = get_display_recipient(missed_messages[0].recipient) # Make sure that this is a list of strings, not a string. assert not isinstance(display_recipient, str) other_recipients = [r['full_name'] for r in display_recipient if r['id'] != user_profile.id] context.update({'group_pm': True}) if len(other_recipients) == 2: huddle_display_name = "%s" % (" and ".join(other_recipients)) context.update({'huddle_display_name': huddle_display_name}) elif len(other_recipients) == 3: huddle_display_name = "%s, %s, and %s" % ( other_recipients[0], other_recipients[1], other_recipients[2]) context.update({'huddle_display_name': huddle_display_name}) else: huddle_display_name = "%s, and %s others" % ( ', '.join(other_recipients[:2]), len(other_recipients) - 2) context.update({'huddle_display_name': huddle_display_name}) elif (missed_messages[0].recipient.type == Recipient.PERSONAL): context.update({'private_message': True}) else: # Keep only the senders who actually mentioned the user # # TODO: When we add wildcard mentions that send emails, add # them to the filter here. senders = list(set(m.sender for m in missed_messages if UserMessage.objects.filter(message=m, user_profile=user_profile, flags=UserMessage.flags.mentioned).exists())) context.update({'at_mention': True}) # If message content is disabled, then flush all information we pass to email. if not user_profile.message_content_in_email_notifications: context.update({ 'reply_to_zulip': False, 'messages': [], 'sender_str': "", 'realm_str': user_profile.realm.name, 'huddle_display_name': "", }) else: context.update({ 'messages': build_message_list(user_profile, missed_messages), 'sender_str': ", ".join(sender.full_name for sender in senders), 'realm_str': user_profile.realm.name, }) from_name = "Zulip missed messages" # type: str from_address = FromAddress.NOREPLY if len(senders) == 1 and settings.SEND_MISSED_MESSAGE_EMAILS_AS_USER: # If this setting is enabled, you can reply to the Zulip # missed message emails directly back to the original sender. # However, one must ensure the Zulip server is in the SPF # record for the domain, or there will be spam/deliverability # problems. sender = senders[0] from_name, from_address = (sender.full_name, sender.email) context.update({ 'reply_warning': False, 'reply_to_zulip': False, }) email_dict = { 'template_prefix': 'zerver/emails/missed_message', 'to_user_id': user_profile.id, 'from_name': from_name, 'from_address': from_address, 'reply_to_email': formataddr((reply_to_name, reply_to_address)), 'context': context} queue_json_publish("email_senders", email_dict) user_profile.last_reminder = timezone_now() user_profile.save(update_fields=['last_reminder'])