def test_invite_user_signup_initial_history(self): # type: () -> None """ Test that a new user invited to a stream receives some initial history but only from public streams. """ self.login("*****@*****.**") user_profile = get_user_profile_by_email("*****@*****.**") private_stream_name = "Secret" (stream, _) = create_stream_if_needed(user_profile.realm, private_stream_name, invite_only=True) do_add_subscription(user_profile, stream) public_msg_id = self.send_message("*****@*****.**", "Denmark", Recipient.STREAM, "Public topic", "Public message") secret_msg_id = self.send_message("*****@*****.**", private_stream_name, Recipient.STREAM, "Secret topic", "Secret message") invitee = "*****@*****.**" self.assert_json_success(self.invite(invitee, [private_stream_name, "Denmark"])) self.assertTrue(find_key_by_email(invitee)) self.submit_reg_form_for_user("alice-test", "password") invitee_profile = get_user_profile_by_email(invitee) invitee_msg_ids = [um.message_id for um in UserMessage.objects.filter(user_profile=invitee_profile)] self.assertTrue(public_msg_id in invitee_msg_ids) self.assertFalse(secret_msg_id in invitee_msg_ids)
def handle(self, *args, **options): # type: (*Any, **str) -> None old_email = options['old_email'] if options['new_email']: new_email = options['new_email'] else: new_email = old_email gravatar_url = "https://secure.gravatar.com/avatar/%s?d=identicon" % (gravatar_hash(old_email),) gravatar_data = requests.get(gravatar_url).content gravatar_file = SimpleUploadedFile('gravatar.jpg', gravatar_data, 'image/jpeg') try: user_profile = get_user_profile_by_email(old_email) except UserProfile.DoesNotExist: try: user_profile = get_user_profile_by_email(new_email) except UserProfile.DoesNotExist: raise CommandError("Could not find specified user") upload_avatar_image(gravatar_file, user_profile, old_email) if old_email != new_email: gravatar_file.seek(0) upload_avatar_image(gravatar_file, user_profile, new_email) user_profile.avatar_source = UserProfile.AVATAR_FROM_USER user_profile.save(update_fields=['avatar_source'])
def add_bot_backend(request, user_profile, full_name=REQ(), short_name=REQ(), default_sending_stream_name=REQ('default_sending_stream', default=None), default_events_register_stream_name=REQ('default_events_register_stream', default=None), default_all_public_streams=REQ(validator=check_bool, default=None)): # type: (HttpRequest, UserProfile, text_type, text_type, Optional[text_type], Optional[text_type], Optional[bool]) -> HttpResponse short_name += "-bot" email = short_name + "@" + user_profile.realm.domain form = CreateUserForm({'full_name': full_name, 'email': email}) if not form.is_valid(): # We validate client-side as well return json_error(_('Bad name or username')) try: get_user_profile_by_email(email) return json_error(_("Username already in use")) except UserProfile.DoesNotExist: pass if len(request.FILES) == 0: avatar_source = UserProfile.AVATAR_FROM_GRAVATAR elif len(request.FILES) != 1: return json_error(_("You may only upload one file at a time")) else: user_file = list(request.FILES.values())[0] upload_avatar_image(user_file, user_profile, email) avatar_source = UserProfile.AVATAR_FROM_USER default_sending_stream = None if default_sending_stream_name is not None: default_sending_stream = stream_or_none(default_sending_stream_name, user_profile.realm) if default_sending_stream and not default_sending_stream.is_public() and not \ subscribed_to_stream(user_profile, default_sending_stream): return json_error(_('Insufficient permission')) default_events_register_stream = None if default_events_register_stream_name is not None: default_events_register_stream = stream_or_none(default_events_register_stream_name, user_profile.realm) if default_events_register_stream and not default_events_register_stream.is_public() and not \ subscribed_to_stream(user_profile, default_events_register_stream): return json_error(_('Insufficient permission')) bot_profile = do_create_user(email=email, password='', realm=user_profile.realm, full_name=full_name, short_name=short_name, active=True, bot_type=UserProfile.DEFAULT_BOT, bot_owner=user_profile, avatar_source=avatar_source, default_sending_stream=default_sending_stream, default_events_register_stream=default_events_register_stream, default_all_public_streams=default_all_public_streams) json_result = dict( api_key=bot_profile.api_key, avatar_url=avatar_url(bot_profile), default_sending_stream=get_stream_name(bot_profile.default_sending_stream), default_events_register_stream=get_stream_name(bot_profile.default_events_register_stream), default_all_public_streams=bot_profile.default_all_public_streams, ) return json_success(json_result)
def test_mention_shortname(self): sender_user_profile = get_user_profile_by_email("*****@*****.**") user_profile = get_user_profile_by_email("*****@*****.**") msg = Message(sender=sender_user_profile, sending_client=get_client("test")) content = "@**hamlet**" self.assertEqual(msg.render_markdown(content), '<p><span class="user-mention" data-user-email="*****@*****.**">@King Hamlet</span></p>') self.assertEqual(msg.mentions_user_ids, set([user_profile.id]))
def add_bot_backend(request, user_profile, full_name_raw=REQ("full_name"), short_name=REQ(), default_sending_stream_name=REQ('default_sending_stream', default=None), default_events_register_stream_name=REQ('default_events_register_stream', default=None), default_all_public_streams=REQ(validator=check_bool, default=None)): # type: (HttpRequest, UserProfile, Text, Text, Optional[Text], Optional[Text], Optional[bool]) -> HttpResponse short_name += "-bot" full_name = check_full_name(full_name_raw) email = '%s@%s' % (short_name, user_profile.realm.get_bot_domain()) form = CreateUserForm({'full_name': full_name, 'email': email}) if not form.is_valid(): # We validate client-side as well return json_error(_('Bad name or username')) try: get_user_profile_by_email(email) return json_error(_("Username already in use")) except UserProfile.DoesNotExist: pass if len(request.FILES) == 0: avatar_source = UserProfile.AVATAR_FROM_GRAVATAR elif len(request.FILES) != 1: return json_error(_("You may only upload one file at a time")) else: avatar_source = UserProfile.AVATAR_FROM_USER default_sending_stream = None if default_sending_stream_name is not None: (default_sending_stream, ignored_rec, ignored_sub) = access_stream_by_name( user_profile, default_sending_stream_name) default_events_register_stream = None if default_events_register_stream_name is not None: (default_events_register_stream, ignored_rec, ignored_sub) = access_stream_by_name( user_profile, default_events_register_stream_name) bot_profile = do_create_user(email=email, password='', realm=user_profile.realm, full_name=full_name, short_name=short_name, active=True, bot_type=UserProfile.DEFAULT_BOT, bot_owner=user_profile, avatar_source=avatar_source, default_sending_stream=default_sending_stream, default_events_register_stream=default_events_register_stream, default_all_public_streams=default_all_public_streams) if len(request.FILES) == 1: user_file = list(request.FILES.values())[0] upload_avatar_image(user_file, user_profile, bot_profile) json_result = dict( api_key=bot_profile.api_key, avatar_url=avatar_url(bot_profile), default_sending_stream=get_stream_name(bot_profile.default_sending_stream), default_events_register_stream=get_stream_name(bot_profile.default_events_register_stream), default_all_public_streams=bot_profile.default_all_public_streams, ) return json_success(json_result)
def test_invalid_pointer(self): """ Posting json to /json/update_pointer with an invalid pointer returns a 400 and error message. """ self.login("*****@*****.**") self.assertEqual(get_user_profile_by_email("*****@*****.**").pointer, -1) result = self.client.post("/json/update_pointer", {"pointer": "foo"}) self.assert_json_error(result, "Bad value for 'pointer': foo") self.assertEqual(get_user_profile_by_email("*****@*****.**").pointer, -1)
def test_pointer_out_of_range(self): """ Posting json to /json/update_pointer with an out of range (< 0) pointer returns a 400 and error message. """ self.login("*****@*****.**") self.assertEqual(get_user_profile_by_email("*****@*****.**").pointer, -1) result = self.client.post("/json/update_pointer", {"pointer": -2}) self.assert_json_error(result, "Bad value for 'pointer': -2") self.assertEqual(get_user_profile_by_email("*****@*****.**").pointer, -1)
def test_missing_pointer(self): """ Posting json to /json/update_pointer which does not contain a pointer key/value pair returns a 400 and error message. """ self.login("*****@*****.**") self.assertEqual(get_user_profile_by_email("*****@*****.**").pointer, -1) result = self.client.post("/json/update_pointer", {"foo": 1}) self.assert_json_error(result, "Missing 'pointer' argument") self.assertEqual(get_user_profile_by_email("*****@*****.**").pointer, -1)
def export_avatars_local_helper(realm, output_dir, local_dir): # type: (Realm, Path, Path) -> None if not os.path.exists(output_dir): os.makedirs(output_dir) count = 0 records = [] users = list(UserProfile.objects.filter(realm=realm)) users += [ get_user_profile_by_email(settings.NOTIFICATION_BOT), get_user_profile_by_email(settings.EMAIL_GATEWAY_BOT), get_user_profile_by_email(settings.WELCOME_BOT), ] for user in users: if user.avatar_source == UserProfile.AVATAR_FROM_GRAVATAR: continue # NOTE: There is an avatar source called AVATAR_FROM_SYSTEM, # but I'm not sure we support it any more. If we # have system-generated avatars, then arguably we # don't need to export them, but it's probably # expedient to just copy them over. The more # common case is AVATAR_FROM_USER, which is handled # here as well. AVATAR_FROM_GRAVATAR refers to # avatars hosted by gravatar.com, and for them, # we have no files to worry about exporting avatar_hash = user_avatar_hash(user.email) wildcard = os.path.join(local_dir, avatar_hash + '.*') for local_path in glob.glob(wildcard): logging.info('Copying avatar file for user %s from %s' % ( user.email, local_path)) fn = os.path.basename(local_path) output_path = os.path.join(output_dir, fn) mkdir_p(str(os.path.dirname(output_path))) subprocess.check_call(["cp", "-a", str(local_path), str(output_path)]) stat = os.stat(local_path) record = dict(realm_id=realm.id, user_profile_id=user.id, user_profile_email=user.email, s3_path=fn, path=fn, size=stat.st_size, last_modified=stat.st_mtime, content_type=None) records.append(record) count += 1 if (count % 100 == 0): logging.info("Finished %s" % (count,)) with open(os.path.join(output_dir, "records.json"), "w") as records_file: ujson.dump(records, records_file, indent=4)
def test_api_update_pointer(self): """ Same as above, but for the API view """ email = "*****@*****.**" self.assertEqual(get_user_profile_by_email(email).pointer, -1) msg_id = self.send_message("*****@*****.**", "Verona", Recipient.STREAM) result = self.client_put("/api/v1/users/me/pointer", {"pointer": msg_id}, **self.api_auth(email)) self.assert_json_success(result) self.assertEqual(get_user_profile_by_email(email).pointer, msg_id)
def test_update_pointer(self): """ Posting a pointer to /update (in the form {"pointer": pointer}) changes the pointer we store for your UserProfile. """ self.login("*****@*****.**") self.assertEqual(get_user_profile_by_email("*****@*****.**").pointer, -1) msg_id = self.send_message("*****@*****.**", "Verona", Recipient.STREAM) result = self.client.post("/json/update_pointer", {"pointer": msg_id}) self.assert_json_success(result) self.assertEqual(get_user_profile_by_email("*****@*****.**").pointer, msg_id)
def test_do_change_realm_subdomain_clears_user_realm_cache(self) -> None: """The main complicated thing about changing realm subdomains is updating the cache, and we start by populating the cache for Hamlet, and we end by checking the cache to ensure that his realm appears to be deactivated. You can make this test fail by disabling cache.flush_realm().""" user = get_user_profile_by_email('*****@*****.**') realm = get_realm('zulip') do_change_realm_subdomain(realm, "newzulip") user = get_user_profile_by_email('*****@*****.**') self.assertEqual(user.realm.string_id, "newzulip") # This doesn't use a cache right now, but may later. self.assertIsNone(get_realm("zulip"))
def test_register_deactivated(self): """ If you try to register for a deactivated realm, you get a clear error page. """ realm = get_realm("zulip.com") realm.deactivated = True realm.save(update_fields=["deactivated"]) result = self.register("test", "test") self.assertIn("has been deactivated", result.content.replace("\n", " ")) with self.assertRaises(UserProfile.DoesNotExist): get_user_profile_by_email('*****@*****.**')
def test_mention_multiple(self): sender_user_profile = get_user_profile_by_email("*****@*****.**") hamlet = get_user_profile_by_email("*****@*****.**") cordelia = get_user_profile_by_email("*****@*****.**") msg = Message(sender=sender_user_profile, sending_client=get_client("test")) content = "@**King Hamlet** and @**cordelia**, check this out" self.assertEqual(msg.render_markdown(content), '<p>' '<span class="user-mention" ' 'data-user-email="*****@*****.**">@King Hamlet</span> and ' '<span class="user-mention" ' 'data-user-email="*****@*****.**">@Cordelia Lear</span>, ' 'check this out</p>') self.assertEqual(msg.mentions_user_ids, set([hamlet.id, cordelia.id]))
def handle(self, **options): # type: (**Any) -> None if ( options["string_id"] is None or options["streams"] is None or (options["users"] is None and options["all_users"] is None) ): self.print_help("./manage.py", "add_users_to_streams") exit(1) stream_names = set([stream.strip() for stream in options["streams"].split(",")]) realm = get_realm_by_string_id(options["string_id"]) if options["all_users"]: user_profiles = UserProfile.objects.filter(realm=realm) else: emails = set([email.strip() for email in options["users"].split(",")]) user_profiles = [] for email in emails: user_profiles.append(get_user_profile_by_email(email)) for stream_name in set(stream_names): for user_profile in user_profiles: stream, _ = create_stream_if_needed(user_profile.realm, stream_name) _ignore, already_subscribed = bulk_add_subscriptions([stream], [user_profile]) was_there_already = user_profile.id in {tup[0].id for tup in already_subscribed} print( "%s %s to %s" % ("Already subscribed" if was_there_already else "Subscribed", user_profile.email, stream_name) )
def authenticate(self, *args, **kwargs): # type: (*Any, **Any) -> Optional[UserProfile] return_data = kwargs.get('return_data', {}) email_address = self.get_email_address(*args, **kwargs) if not email_address: return None try: user_profile = get_user_profile_by_email(email_address) except UserProfile.DoesNotExist: return_data["valid_attestation"] = True return None if not user_profile.is_active: return_data["inactive_user"] = True return None if user_profile.realm.deactivated: return_data["inactive_realm"] = True return None if not check_subdomain(kwargs.get("realm_subdomain"), user_profile.realm.subdomain): return_data["invalid_subdomain"] = True return None return user_profile
def subscribe_to_stream(self, email, stream_name, realm=None): realm = get_realm(resolve_email_to_domain(email)) stream = get_stream(stream_name, realm) if stream is None: stream, _ = create_stream_if_needed(realm, stream_name) user_profile = get_user_profile_by_email(email) do_add_subscription(user_profile, stream, no_log=True)
def send_mail(self, subject_template_name, email_template_name, context, from_email, to_email, html_email_template_name=None): # type: (str, str, Dict[str, Any], str, str, str) -> None """ Currently we don't support accounts in multiple subdomains using a single email address. We override this function so that we do not send a reset link to an email address if the reset attempt is done on the subdomain which does not match user.realm.subdomain. Once we start supporting accounts with the same email in multiple subdomains, we may be able to refactor this function. A second reason we override this function is so that we can send the mail through the functions in zerver.lib.send_email, to match how we send all other mail in the codebase. """ user = get_user_profile_by_email(to_email) attempted_subdomain = get_subdomain(getattr(self, 'request')) context['attempted_realm'] = False if not check_subdomain(user.realm.subdomain, attempted_subdomain): context['attempted_realm'] = get_realm(attempted_subdomain) send_email('zerver/emails/password_reset', to_user_id=user.id, from_name="Zulip Account Security", from_address=FromAddress.NOREPLY, context=context)
def clean_username(self): # type: () -> str email = self.cleaned_data['username'] try: user_profile = get_user_profile_by_email(email) except UserProfile.DoesNotExist: return email if user_profile.realm.deactivated: error_msg = u"""Sorry for the trouble, but %s has been deactivated. Please contact %s to reactivate this group.""" % ( user_profile.realm.name, FromAddress.SUPPORT) raise ValidationError(mark_safe(error_msg)) if not user_profile.is_active and not user_profile.is_mirror_dummy: error_msg = (u"Sorry for the trouble, but your account has been " u"deactivated. Please contact %s to reactivate " u"it.") % (FromAddress.SUPPORT,) raise ValidationError(mark_safe(error_msg)) if not check_subdomain(get_subdomain(self.request), user_profile.realm.subdomain): logging.warning("User %s attempted to password login to wrong subdomain %s" % (user_profile.email, get_subdomain(self.request))) raise ValidationError(mark_safe(WRONG_SUBDOMAIN_ERROR)) return email
def enqueue_welcome_emails(email, name): sender = {'email': '*****@*****.**', 'name': 'Waseem Daher'} if settings.VOYAGER: sender = {'email': settings.ZULIP_ADMINISTRATOR, 'name': 'Zulip'} user_profile = get_user_profile_by_email(email) unsubscribe_link = one_click_unsubscribe_link(user_profile, "welcome") template_payload = {'name': name, 'not_voyager': not settings.VOYAGER, 'external_host': settings.EXTERNAL_HOST, 'unsubscribe_link': unsubscribe_link} #Send day 1 email send_local_email_template_with_delay([{'email': email, 'name': name}], "zerver/emails/followup/day1", template_payload, datetime.timedelta(hours=1), tags=["followup-emails"], sender=sender) #Send day 2 email tomorrow = datetime.datetime.utcnow() + datetime.timedelta(hours=24) # 11 AM EDT tomorrow_morning = datetime.datetime(tomorrow.year, tomorrow.month, tomorrow.day, 15, 0) assert(datetime.datetime.utcnow() < tomorrow_morning) send_local_email_template_with_delay([{'email': email, 'name': name}], "zerver/emails/followup/day2", template_payload, tomorrow_morning - datetime.datetime.utcnow(), tags=["followup-emails"], sender=sender)
def update_user_backend(request, user_profile, email, full_name=REQ(default="", validator=check_string), is_admin=REQ(default=None, validator=check_bool)): # type: (HttpRequest, UserProfile, text_type, Optional[text_type], Optional[bool]) -> HttpResponse try: target = get_user_profile_by_email(email) except UserProfile.DoesNotExist: return json_error(_('No such user')) if not user_profile.can_admin_user(target): return json_error(_('Insufficient permission')) if is_admin is not None: if not is_admin and check_last_admin(user_profile): return json_error(_('Cannot remove the only organization administrator')) do_change_is_admin(target, is_admin) if (full_name is not None and target.full_name != full_name and full_name.strip() != ""): # We don't respect `name_changes_disabled` here because the request # is on behalf of the administrator. new_full_name = full_name.strip() if len(new_full_name) > UserProfile.MAX_NAME_LENGTH: return json_error(_("Name too long!")) do_change_full_name(target, new_full_name) return json_success()
def enqueue_welcome_emails(email, name): # type: (Text, Text) -> None from zerver.context_processors import common_context if settings.WELCOME_EMAIL_SENDER is not None: sender = settings.WELCOME_EMAIL_SENDER # type: Dict[str, Text] else: sender = {'email': settings.ZULIP_ADMINISTRATOR, 'name': 'Zulip'} user_profile = get_user_profile_by_email(email) unsubscribe_link = one_click_unsubscribe_link(user_profile, "welcome") template_payload = common_context(user_profile) template_payload.update({ 'verbose_support_offers': settings.VERBOSE_SUPPORT_OFFERS, 'unsubscribe_link': unsubscribe_link }) # Send day 1 email send_local_email_template_with_delay([{'email': email, 'name': name}], "zerver/emails/followup/day1", template_payload, datetime.timedelta(hours=1), tags=["followup-emails"], sender=sender) # Send day 2 email send_local_email_template_with_delay([{'email': email, 'name': name}], "zerver/emails/followup/day2", template_payload, datetime.timedelta(days=1), tags=["followup-emails"], sender=sender)
def enqueue_welcome_emails(email, name): # type: (text_type, text_type) -> None if settings.WELCOME_EMAIL_SENDER is not None: sender = settings.WELCOME_EMAIL_SENDER # type: Dict[str, text_type] else: sender = {'email': settings.ZULIP_ADMINISTRATOR, 'name': 'Zulip'} user_profile = get_user_profile_by_email(email) unsubscribe_link = one_click_unsubscribe_link(user_profile, "welcome") template_payload = {'name': name, 'verbose_support_offers': settings.VERBOSE_SUPPORT_OFFERS, 'external_host': settings.EXTERNAL_HOST, 'external_uri_scheme': settings.EXTERNAL_URI_SCHEME, 'server_uri': settings.SERVER_URI, 'realm_uri': user_profile.realm.uri, 'unsubscribe_link': unsubscribe_link} # Send day 1 email send_local_email_template_with_delay([{'email': email, 'name': name}], "zerver/emails/followup/day1", template_payload, datetime.timedelta(hours=1), tags=["followup-emails"], sender=sender) # Send day 2 email send_local_email_template_with_delay([{'email': email, 'name': name}], "zerver/emails/followup/day2", template_payload, datetime.timedelta(days=1), tags=["followup-emails"], sender=sender)
def by_pm_with(self, query, operand, maybe_negate): # type: (Query, str, ConditionTransform) -> Query if ',' in operand: # Huddle try: emails = [e.strip() for e in operand.split(',')] recipient = recipient_for_emails(emails, False, self.user_profile, self.user_profile) except ValidationError: raise BadNarrowOperator('unknown recipient ' + operand) cond = column("recipient_id") == recipient.id return query.where(maybe_negate(cond)) else: # Personal message self_recipient = get_recipient(Recipient.PERSONAL, type_id=self.user_profile.id) if operand == self.user_profile.email: # Personals with self cond = and_(column("sender_id") == self.user_profile.id, column("recipient_id") == self_recipient.id) return query.where(maybe_negate(cond)) # Personals with other user; include both directions. try: narrow_profile = get_user_profile_by_email(operand) except UserProfile.DoesNotExist: raise BadNarrowOperator('unknown user ' + operand) narrow_recipient = get_recipient(Recipient.PERSONAL, narrow_profile.id) cond = or_(and_(column("sender_id") == narrow_profile.id, column("recipient_id") == self_recipient.id), and_(column("sender_id") == self.user_profile.id, column("recipient_id") == narrow_recipient.id)) return query.where(maybe_negate(cond))
def test_digest_unsubscribe(self): # type: () -> None """ We provide one-click unsubscribe links in digest e-mails that you can click even when logged out to stop receiving them. Unsubscribing from these emails also dequeues any digest email jobs that have been queued. """ email = "*****@*****.**" user_profile = get_user_profile_by_email("*****@*****.**") self.assertTrue(user_profile.enable_digest_emails) # Enqueue a fake digest email. send_digest_email(user_profile, "", "") self.assertEqual(1, len(ScheduledJob.objects.filter( type=ScheduledJob.EMAIL, filter_string__iexact=email))) # Simulate unsubscribing from digest e-mails. unsubscribe_link = one_click_unsubscribe_link(user_profile, "digest") result = self.client_get(urllib.parse.urlparse(unsubscribe_link).path) # The setting is toggled off, and scheduled jobs have been removed. self.assertEqual(result.status_code, 200) # Circumvent user_profile caching. user_profile = UserProfile.objects.get(email="*****@*****.**") self.assertFalse(user_profile.enable_digest_emails) self.assertEqual(0, len(ScheduledJob.objects.filter( type=ScheduledJob.EMAIL, filter_string__iexact=email)))
def extract_and_upload_attachments(message, realm): # type: (message.Message, Realm) -> text_type user_profile = get_user_profile_by_email(settings.EMAIL_GATEWAY_BOT) attachment_links = [] payload = message.get_payload() if not isinstance(payload, list): # This is not a multipart message, so it can't contain attachments. return "" for part in payload: content_type = part.get_content_type() filename = part.get_filename() if filename: attachment = part.get_payload(decode=True) if isinstance(attachment, binary_type): s3_url = upload_message_image(filename, content_type, attachment, user_profile, target_realm=realm) formatted_link = u"[%s](%s)" % (filename, s3_url) attachment_links.append(formatted_link) else: logger.warning("Payload is not bytes (invalid attachment %s in message from %s)." % (filename, message.get("From"))) return u"\n".join(attachment_links)
def create_mirrored_message_users(request, user_profile, recipients): # type: (HttpResponse, UserProfile, Iterable[Text]) -> Tuple[bool, Optional[UserProfile]] if "sender" not in request.POST: return (False, None) sender_email = request.POST["sender"].strip().lower() referenced_users = set([sender_email]) if request.POST['type'] == 'private': for email in recipients: referenced_users.add(email.lower()) if request.client.name == "zephyr_mirror": user_check = same_realm_zephyr_user fullname_function = compute_mit_user_fullname elif request.client.name == "irc_mirror": user_check = same_realm_irc_user fullname_function = compute_irc_user_fullname elif request.client.name in ("jabber_mirror", "JabberMirror"): user_check = same_realm_jabber_user fullname_function = compute_jabber_user_fullname else: # Unrecognized mirroring client return (False, None) for email in referenced_users: # Check that all referenced users are in our realm: if not user_check(user_profile, email): return (False, None) # Create users for the referenced users, if needed. for email in referenced_users: create_mirror_user_if_needed(user_profile.realm, email, fullname_function) sender = get_user_profile_by_email(sender_email) return (True, sender)
def test_receive_stream_email_messages_success(self): # build dummy messages for stream # test valid incoming stream message is processed properly self.login("*****@*****.**") user_profile = get_user_profile_by_email("*****@*****.**") self.subscribe_to_stream(user_profile.email, "Denmark") stream = get_stream("Denmark", user_profile.realm) stream_to_address = encode_email_address(stream) incoming_valid_message = MIMEText('TestStreamEmailMessages Body') incoming_valid_message['Subject'] = 'TestStreamEmailMessages Subject' incoming_valid_message['From'] = "*****@*****.**" incoming_valid_message['To'] = stream_to_address incoming_valid_message['Reply-to'] = "*****@*****.**" process_message(incoming_valid_message) # Hamlet is subscribed to this stream so should see the email message from Othello. message = most_recent_message(user_profile) self.assertEqual(message.content, "TestStreamEmailMessages Body") self.assertEqual(get_display_recipient(message.recipient), stream.name) self.assertEqual(message.subject, incoming_valid_message['Subject'])
def handle(self, **options): # type: (*Any, **Any) -> None if options["domain"] is None or options["stream"] is None or \ (options["users"] is None and options["all_users"] is None): self.print_help("./manage.py", "remove_users_from_stream") exit(1) realm = get_realm(options["domain"]) stream_name = options["stream"].strip() stream = get_stream(stream_name, realm) if options["all_users"]: user_profiles = UserProfile.objects.filter(realm=realm) else: emails = set([email.strip() for email in options["users"].split(",")]) user_profiles = [] for email in emails: user_profiles.append(get_user_profile_by_email(email)) result = bulk_remove_subscriptions(user_profiles, [stream]) not_subscribed = result[1] not_subscribed_users = {tup[0] for tup in not_subscribed} for user_profile in user_profiles: if user_profile in not_subscribed_users: print("%s was not subscribed" % (user_profile.email,)) else: print("Removed %s from %s" % (user_profile.email, stream_name))
def test_receive_stream_email_messages_empty_body(self): # build dummy messages for stream # test message with empty body is not sent self.login("*****@*****.**") user_profile = get_user_profile_by_email("*****@*****.**") self.subscribe_to_stream(user_profile.email, "Denmark") stream = get_stream("Denmark", user_profile.realm) stream_to_address = encode_email_address(stream) headers = {} headers['Reply-To'] = '*****@*****.**' # empty body incoming_valid_message = MIMEText('') incoming_valid_message['Subject'] = 'TestStreamEmailMessages Subject' incoming_valid_message['From'] = "*****@*****.**" incoming_valid_message['To'] = stream_to_address incoming_valid_message['Reply-to'] = "*****@*****.**" exception_message = "" debug_info = {} # process_message eats the exception & logs an error which can't be parsed here # so calling process_stream_message directly try: process_stream_message(incoming_valid_message['To'], incoming_valid_message['Subject'], incoming_valid_message, debug_info) except ZulipEmailForwardError as e: # empty body throws exception exception_message = e.message self.assertEqual(exception_message, "Unable to find plaintext or HTML message body")
def common_get_active_user_by_email(email, return_data=None): # type: (text_type, Optional[Dict[str, Any]]) -> Optional[UserProfile] try: user_profile = get_user_profile_by_email(email) except UserProfile.DoesNotExist: return None if not user_profile.is_active: if return_data is not None: return_data['inactive_user'] = True return None if user_profile.realm.deactivated: if return_data is not None: return_data['inactive_realm'] = True return None return user_profile
def test_invite_user_signup_initial_history(self): """ Test that a new user invited to a stream receives some initial history but only from public streams. """ self.login("*****@*****.**") user_profile = get_user_profile_by_email("*****@*****.**") private_stream_name = "Secret" (stream, _) = create_stream_if_needed(user_profile.realm, private_stream_name, invite_only=True) do_add_subscription(user_profile, stream) public_msg_id = self.send_message("*****@*****.**", "Denmark", Recipient.STREAM, "Public topic", "Public message") secret_msg_id = self.send_message("*****@*****.**", private_stream_name, Recipient.STREAM, "Secret topic", "Secret message") invitee = "*****@*****.**" self.assert_json_success(self.invite(invitee, [private_stream_name, "Denmark"])) self.assertTrue(find_key_by_email(invitee)) self.submit_reg_form_for_user("alice-test", "password") invitee_profile = get_user_profile_by_email(invitee) invitee_msg_ids = [um.message_id for um in UserMessage.objects.filter(user_profile=invitee_profile)] self.assertTrue(public_msg_id in invitee_msg_ids) self.assertFalse(secret_msg_id in invitee_msg_ids)
def send_message(self, sender_name, recipient_list, message_type, content="test content", subject="test", **kwargs): sender = get_user_profile_by_email(sender_name) if message_type == Recipient.PERSONAL: message_type_name = "private" else: message_type_name = "stream" if isinstance(recipient_list, six.string_types): recipient_list = [recipient_list] (sending_client, _) = Client.objects.get_or_create(name="test suite") return check_send_message( sender, sending_client, message_type_name, recipient_list, subject, content, forged=False, forged_timestamp=None, forwarder_user_profile=sender, realm=sender.realm, **kwargs)
def create_user_backend(request, user_profile, email=REQ(), password=REQ(), full_name_raw=REQ("full_name"), short_name=REQ()): # type: (HttpRequest, UserProfile, Text, Text, Text, Text) -> HttpResponse full_name = check_full_name(full_name_raw) form = CreateUserForm({'full_name': full_name, 'email': email}) if not form.is_valid(): return json_error(_('Bad name or username')) # Check that the new user's email address belongs to the admin's realm # (Since this is an admin API, we don't require the user to have been # invited first.) realm = user_profile.realm if not email_allowed_for_realm(email, user_profile.realm): return json_error(_("Email '%(email)s' does not belong to domain '%(domain)s'") % {'email': email, 'domain': realm.domain}) try: get_user_profile_by_email(email) return json_error(_("Email '%s' already in use") % (email,)) except UserProfile.DoesNotExist: pass do_create_user(email, password, realm, full_name, short_name) return json_success()
def clean_username(self): email = self.cleaned_data['username'] try: user_profile = get_user_profile_by_email(email) except UserProfile.DoesNotExist: return email if user_profile.realm.deactivated: error_msg = u"""Sorry for the trouble, but %s has been deactivated. Please contact %s to reactivate this group.""" % (user_profile.realm.name, settings.ZULIP_ADMINISTRATOR) raise ValidationError(mark_safe(error_msg)) return email
def ensure_medium_avatar_image(self, email): # type: (Text) -> None user_profile = get_user_profile_by_email(email) file_path = user_avatar_path(user_profile) output_path = os.path.join(settings.LOCAL_UPLOADS_DIR, "avatars", file_path + "-medium.png") if os.path.isfile(output_path): return image_path = os.path.join(settings.LOCAL_UPLOADS_DIR, "avatars", file_path + ".original") image_data = open(image_path, "rb").read() resized_medium = resize_avatar(image_data, MEDIUM_AVATAR_SIZE) write_local_file('avatars', file_path + '-medium.png', resized_medium)
def ensure_medium_avatar_image(self, email): # type: (Text) -> None user_profile = get_user_profile_by_email(email) email_hash = user_avatar_hash(email) s3_file_name = email_hash bucket_name = settings.S3_AVATAR_BUCKET conn = S3Connection(settings.S3_KEY, settings.S3_SECRET_KEY) bucket = get_bucket(conn, force_str(bucket_name)) key = bucket.get_key(email_hash) image_data = key.get_contents_as_string() resized_medium = resize_avatar(image_data, MEDIUM_AVATAR_SIZE) upload_image_to_s3(bucket_name, s3_file_name + "-medium.png", "image/png", user_profile, resized_medium)
def regenerate_bot_api_key(request, user_profile, email): # type: (HttpRequest, UserProfile, Text) -> HttpResponse try: bot = get_user_profile_by_email(email) except: return json_error(_('No such user')) if not user_profile.can_admin_user(bot): return json_error(_('Insufficient permission')) do_regenerate_api_key(bot) json_result = dict( api_key = bot.api_key ) return json_success(json_result)
def authenticate(self, remote_user): if not remote_user: return email = remote_user_to_email(remote_user) try: user_profile = get_user_profile_by_email(email) except UserProfile.DoesNotExist: return None if user_profile.is_mirror_dummy: # mirror dummies can not login, but they can convert to real users return None return user_profile
def authenticate(self, username=None, password=None): """ Authenticate a user based on email address as the user name. """ if username is None or password is None: # Return immediately. Otherwise we will look for a SQL row with # NULL username. While that's probably harmless, it's needless # exposure. return None try: user_profile = get_user_profile_by_email(username) if not password_auth_enabled(user_profile.realm): return None if user_profile.check_password(password): return user_profile except UserProfile.DoesNotExist: return None
def handle(self, *args, **options): # type: (*Any, **str) -> None data_file = options['data_file'] with open(data_file, "r") as f: for line in f: email, new_name = line.strip().split(",", 1) try: user_profile = get_user_profile_by_email(email) old_name = user_profile.full_name print("%s: %s -> %s" % (email, old_name, new_name)) do_change_full_name(user_profile, new_name, None) except UserProfile.DoesNotExist: print( "* E-mail %s doesn't exist in the system, skipping." % (email, ))
def avatar(request, email): # type: (HttpRequest, str) -> HttpResponse try: user_profile = get_user_profile_by_email(email) avatar_source = user_profile.avatar_source except UserProfile.DoesNotExist: avatar_source = 'G' url = get_avatar_url(avatar_source, email) # We can rely on the url already having query parameters. Because # our templates depend on being able to use the ampersand to # add query parameters to our url, get_avatar_url does '?x=x' # hacks to prevent us from having to jump through decode/encode hoops. assert '?' in url url += '&' + request.META['QUERY_STRING'] return redirect(url)
def update_user_backend(request, user_profile, email, is_admin=REQ(default=None, validator=check_bool)): # type: (HttpRequest, UserProfile, text_type, Optional[bool]) -> HttpResponse try: target = get_user_profile_by_email(email) except UserProfile.DoesNotExist: return json_error(_('No such user')) if not user_profile.can_admin_user(target): return json_error(_('Insufficient permission')) if is_admin is not None: do_change_is_admin(target, is_admin) return json_success({})
def test_realm_patterns(self): RealmFilter( realm=get_realm('zulip.com'), pattern=r"#(?P<id>[0-9]{2,8})", url_format_string=r"https://trac.zulip.net/ticket/%(id)s").save() msg = Message(sender=get_user_profile_by_email("*****@*****.**")) content = "We should fix #224 and #115, but not issue#124 or #1124z or [trac #15](https://trac.zulip.net/ticket/16) today." converted = bugdown.convert(content, realm_domain='zulip.com', message=msg) self.assertEqual( converted, '<p>We should fix <a href="https://trac.zulip.net/ticket/224" target="_blank" title="https://trac.zulip.net/ticket/224">#224</a> and <a href="https://trac.zulip.net/ticket/115" target="_blank" title="https://trac.zulip.net/ticket/115">#115</a>, but not issue#124 or #1124z or <a href="https://trac.zulip.net/ticket/16" target="_blank" title="https://trac.zulip.net/ticket/16">trac #15</a> today.</p>' )
def patch_bot_backend(request, user_profile, email, full_name=REQ(default=None), default_sending_stream=REQ(default=None), default_events_register_stream=REQ(default=None), default_all_public_streams=REQ(default=None, validator=check_bool)): # type: (HttpRequest, UserProfile, text_type, Optional[text_type], Optional[text_type], Optional[text_type], Optional[bool]) -> HttpResponse try: bot = get_user_profile_by_email(email) except: return json_error(_('No such user')) if not user_profile.can_admin_user(bot): return json_error(_('Insufficient permission')) if full_name is not None: do_change_full_name(bot, full_name) if default_sending_stream is not None: stream = stream_or_none(default_sending_stream, bot.realm) do_change_default_sending_stream(bot, stream) if default_events_register_stream is not None: stream = stream_or_none(default_events_register_stream, bot.realm) do_change_default_events_register_stream(bot, stream) if default_all_public_streams is not None: do_change_default_all_public_streams(bot, default_all_public_streams) if len(request.FILES) == 0: pass elif len(request.FILES) == 1: user_file = list(request.FILES.values())[0] upload_avatar_image(user_file, user_profile, bot.email) avatar_source = UserProfile.AVATAR_FROM_USER do_change_avatar_source(bot, avatar_source) else: return json_error(_("You may only upload one file at a time")) json_result = dict( full_name=bot.full_name, avatar_url=avatar_url(bot), default_sending_stream=get_stream_name(bot.default_sending_stream), default_events_register_stream=get_stream_name( bot.default_events_register_stream), default_all_public_streams=bot.default_all_public_streams, ) return json_success(json_result)
def send_stream_message(self, sender_email, stream_name, content=u"test content", topic_name=u"test"): # type: (Text, Text, Text, Text) -> int sender = get_user_profile_by_email(sender_email) (sending_client, _) = Client.objects.get_or_create(name="test suite") return check_send_stream_message( sender=sender, client=sending_client, stream_name=stream_name, topic=topic_name, body=content, )
def enqueue_welcome_emails(email, name): # type: (text_type, text_type) -> None if settings.WELCOME_EMAIL_SENDER is not None: sender = settings.WELCOME_EMAIL_SENDER # type: Dict[str, text_type] else: sender = {'email': settings.ZULIP_ADMINISTRATOR, 'name': 'Zulip'} user_profile = get_user_profile_by_email(email) unsubscribe_link = one_click_unsubscribe_link(user_profile, "welcome") template_payload = { 'name': name, 'verbose_support_offers': settings.VERBOSE_SUPPORT_OFFERS, 'external_host': settings.EXTERNAL_HOST, 'external_uri_scheme': settings.EXTERNAL_URI_SCHEME, 'server_uri': settings.SERVER_URI, 'realm_uri': user_profile.realm.uri, 'unsubscribe_link': unsubscribe_link } # Send day 1 email send_local_email_template_with_delay([{ 'email': email, 'name': name }], "zerver/emails/followup/day1", template_payload, datetime.timedelta(hours=1), tags=["followup-emails"], sender=sender) # Send day 2 email tomorrow = datetime.datetime.utcnow() + datetime.timedelta(hours=24) # 11 AM EDT tomorrow_morning = datetime.datetime(tomorrow.year, tomorrow.month, tomorrow.day, 15, 0) assert (datetime.datetime.utcnow() < tomorrow_morning) send_local_email_template_with_delay([{ 'email': email, 'name': name }], "zerver/emails/followup/day2", template_payload, tomorrow_morning - datetime.datetime.utcnow(), tags=["followup-emails"], sender=sender)
def test_invite_with_non_ascii_streams(self): """ Inviting someone to streams with non-ASCII characters succeeds. """ self.login("*****@*****.**") invitee = "*****@*****.**" stream_name = u"hümbüǵ" realm = get_realm("zulip.com") stream, _ = create_stream_if_needed(realm, stream_name) # Make sure we're subscribed before inviting someone. do_add_subscription(get_user_profile_by_email("*****@*****.**"), stream, no_log=True) self.assert_json_success(self.invite(invitee, [stream_name]))
def consume(self, data): invitee = get_prereg_user_by_email(data["email"]) referrer = get_user_profile_by_email(data["referrer_email"]) do_send_confirmation_email(invitee, referrer) # queue invitation reminder for two days from now. link = Confirmation.objects.get_link_for_object(invitee) send_local_email_template_with_delay([{'email': data["email"], 'name': ""}], "zerver/emails/invitation/invitation_reminder_email", {'activate_url': link, 'referrer': referrer, 'verbose_support_offers': settings.VERBOSE_SUPPORT_OFFERS, 'external_host': settings.EXTERNAL_HOST, 'support_email': settings.ZULIP_ADMINISTRATOR}, datetime.timedelta(days=2), tags=["invitation-reminders"], sender={'email': settings.ZULIP_ADMINISTRATOR, 'name': 'Zulip'})
def handle(self, *args, **options): # type: (*Any, **str) -> None if options["to"]: users = [get_user_profile_by_email(options["to"])] elif options["realm"]: realm = get_realm(options["realm"]) users = UserProfile.objects.filter(realm=realm, is_active=True, is_bot=False, is_mirror_dummy=False) elif options["server"] == "YES": users = UserProfile.objects.filter(is_active=True, is_bot=False, is_mirror_dummy=False) else: raise RuntimeError("Missing arguments") self.send(users)
def get_or_create_user(self, username, ldap_user): try: return get_user_profile_by_email(username), False except UserProfile.DoesNotExist: domain = resolve_email_to_domain(username) realm = get_realm(domain) full_name_attr = settings.AUTH_LDAP_USER_ATTR_MAP["full_name"] short_name = full_name = ldap_user.attrs[full_name_attr][0] if "short_name" in settings.AUTH_LDAP_USER_ATTR_MAP: short_name_attr = settings.AUTH_LDAP_USER_ATTR_MAP[ "short_name"] short_name = ldap_user.attrs[short_name_attr][0] user_profile = do_create_user(username, None, realm, full_name, short_name) return user_profile, False
def test_realm_emoji(self): def emoji_img(name, url): return '<img alt="%s" class="emoji" src="%s" title="%s">' % ( name, url, name) zulip_realm = get_realm('zulip.com') url = "https://zulip.com/test_realm_emoji.png" do_add_realm_emoji(zulip_realm, "test", url) # Needs to mock an actual message because that's how bugdown obtains the realm msg = Message(sender=get_user_profile_by_email("*****@*****.**")) converted = bugdown.convert(":test:", "zulip.com", msg) self.assertEqual(converted, '<p>%s</p>' % (emoji_img(':test:', url))) do_remove_realm_emoji(zulip_realm, 'test') converted = bugdown.convert(":test:", "zulip.com", msg) self.assertEqual(converted, '<p>:test:</p>')
def handle(self, *args, **options): # type: (*Any, **Any) -> None if not options["flag"] or not options["op"] or not options["email"]: print("Please specify an operation, a flag and an email") exit(1) op = options['op'] flag = getattr(UserMessage.flags, options['flag']) all_until = options['all_until'] email = options['email'] user_profile = get_user_profile_by_email(email) if all_until: filt = models.Q(id__lte=all_until) else: filt = models.Q(message__id__in=[ mid.strip() for mid in sys.stdin.read().split(',') ]) mids = [ m.id for m in UserMessage.objects.filter( filt, user_profile=user_profile).order_by('-id') ] if options["for_real"]: sys.stdin.close() sys.stdout.close() sys.stderr.close() def do_update(batch): # type: (Iterable[int]) -> None msgs = UserMessage.objects.filter(id__in=batch) if op == 'add': msgs.update(flags=models.F('flags').bitor(flag)) elif op == 'remove': msgs.update(flags=models.F('flags').bitand(~flag)) if not options["for_real"]: logging.info("Updating %s by %s %s" % (mids, op, flag)) logging.info( "Dry run completed. Run with --for-real to change message flags." ) exit(1) utils.run_in_batches(mids, 400, do_update, sleep_time=3) exit(0)
def enqueue_welcome_emails(email, name): # type: (text_type, text_type) -> None sender = { 'email': '*****@*****.**', 'name': 'Waseem Daher' } # type: Dict[str, text_type] if settings.VOYAGER: sender = {'email': settings.ZULIP_ADMINISTRATOR, 'name': 'Zulip'} user_profile = get_user_profile_by_email(email) unsubscribe_link = one_click_unsubscribe_link(user_profile, "welcome") template_payload = { 'name': name, 'not_voyager': not settings.VOYAGER, 'external_host': settings.EXTERNAL_HOST, 'unsubscribe_link': unsubscribe_link } # Send day 1 email send_local_email_template_with_delay([{ 'email': email, 'name': name }], "zerver/emails/followup/day1", template_payload, datetime.timedelta(hours=1), tags=["followup-emails"], sender=sender) # Send day 2 email tomorrow = datetime.datetime.utcnow() + datetime.timedelta(hours=24) # 11 AM EDT tomorrow_morning = datetime.datetime(tomorrow.year, tomorrow.month, tomorrow.day, 15, 0) assert (datetime.datetime.utcnow() < tomorrow_morning) send_local_email_template_with_delay([{ 'email': email, 'name': name }], "zerver/emails/followup/day2", template_payload, tomorrow_morning - datetime.datetime.utcnow(), tags=["followup-emails"], sender=sender)
def test_missedmessage_unsubscribe(self): """ We provide one-click unsubscribe links in missed message e-mails that you can click even when logged out to update your email notification settings. """ user_profile = get_user_profile_by_email("*****@*****.**") user_profile.enable_offline_email_notifications = True user_profile.save() unsubscribe_link = one_click_unsubscribe_link(user_profile, "missed_messages") result = self.client.get(urlparse(unsubscribe_link).path) self.assertEqual(result.status_code, 200) # Circumvent user_profile caching. user_profile = UserProfile.objects.get(email="*****@*****.**") self.assertFalse(user_profile.enable_offline_email_notifications)
def test_non_ascii_login(self): """ You can log in even if your password contain non-ASCII characters. """ email = "*****@*****.**" password = u"hümbüǵ" # Registering succeeds. self.register("test", password) user_profile = get_user_profile_by_email(email) self.assertEqual(get_session_dict_user(self.client.session), user_profile.id) self.client.post('/accounts/logout/') self.assertIsNone(get_session_dict_user(self.client.session)) # Logging in succeeds. self.client.post('/accounts/logout/') self.login(email, password) self.assertEqual(get_session_dict_user(self.client.session), user_profile.id)
def api_auth(self, identifier): # type: (Text) -> Dict[str, Text] """ identifier: Can be an email or a remote server uuid. """ if identifier in API_KEYS: api_key = API_KEYS[identifier] else: if is_remote_server(identifier): api_key = get_remote_server_by_uuid(identifier).api_key else: api_key = get_user_profile_by_email(identifier).api_key API_KEYS[identifier] = api_key credentials = u"%s:%s" % (identifier, api_key) return { 'HTTP_AUTHORIZATION': u'Basic ' + base64.b64encode(credentials.encode('utf-8')).decode('utf-8') }
def send_message(self, sender_name, raw_recipients, message_type, content=u"test content", subject=u"test", **kwargs): # type: (text_type, Union[text_type, List[text_type]], int, text_type, text_type, **Any) -> int sender = get_user_profile_by_email(sender_name) if message_type == Recipient.PERSONAL: message_type_name = "private" else: message_type_name = "stream" if isinstance(raw_recipients, six.string_types): recipient_list = [raw_recipients] else: recipient_list = raw_recipients (sending_client, _) = Client.objects.get_or_create(name="test suite") return check_send_message( sender, sending_client, message_type_name, recipient_list, subject, content, forged=False, forged_timestamp=None, forwarder_user_profile=sender, realm=sender.realm, **kwargs)
def api_beeminder_webhook( request: HttpRequest, user_profile: UserProfile, payload: Dict[str, Any] = REQ(argument_type='body'), stream: Text = REQ(default="beeminder"), email: str = REQ(default='*****@*****.**'), topic: Text = REQ(default='beekeeper') ) -> HttpResponse: secret = payload["goal"]["secret"] goal_name = payload["goal"]["slug"] losedate = payload["goal"]["losedate"] limsum = payload["goal"]["limsum"] pledge = payload["goal"]["pledge"] time_remain = (losedate - current_time) / 3600 # time in hours # To show user's probable reaction by looking at pledge amount if pledge > 0: expression = ':worried:' else: expression = ':relieved:' if not secret: # In this case notifications will be sent to stream name = get_user_name(email) body = u"Hello **{}**! I am the Beeminder bot! :octopus: \n You are going to derail \ from goal **{}** in **{:0.1f} hours** \n You need **{}** to avoid derailing \n * Pledge: **{}$** {}" body = body.format(name, goal_name, time_remain, limsum, pledge, expression) check_send_stream_message(user_profile, request.client, stream, topic, body) return json_success() else: # In this case PM will be sent to user p = get_user_profile_by_email(email) body = u"I am the Beeminder bot! :octopus: \n You are going to derail from \ goal **{}** in **{:0.1f} hours** \n You need **{}** to avoid derailing \n * Pledge: **{}$**{}" body = body.format(goal_name, time_remain, limsum, pledge, expression) check_send_private_message(user_profile, request.client, p, body) return json_success()
def handle(self, *args, **options): try: user_profile = get_user_profile_by_email(options["email"]) except UserProfile.DoesNotExist: raise CommandError("No such user.") output_dir = options["output_dir"] if output_dir is None: output_dir = tempfile.mkdtemp(prefix="/tmp/zulip-export-") if os.path.exists(output_dir): shutil.rmtree(output_dir) os.makedirs(output_dir) print("Exporting user %s" % (user_profile.email, )) do_export_user(user_profile, output_dir) print("Finished exporting to %s; tarring" % (output_dir, )) tarball_path = output_dir.rstrip('/') + '.tar.gz' subprocess.check_call( ["tar", "--strip-components=1", "-czf", tarball_path, output_dir]) print("Tarball written to %s" % (tarball_path, ))