mirror of https://github.com/zulip/zulip.git
Move streams views into their own file.
This commit is contained in:
parent
6a8318ddcd
commit
37d4a11610
|
@ -13,44 +13,35 @@ from django.core.exceptions import ValidationError
|
|||
from django.core import validators
|
||||
from django.contrib.auth.views import login as django_login_page, \
|
||||
logout_then_login as django_logout_then_login
|
||||
from django.db.models import Q, F
|
||||
from django.forms.models import model_to_dict
|
||||
from django.core.mail import send_mail
|
||||
from django.middleware.csrf import get_token
|
||||
from django.db import transaction
|
||||
from zerver.models import Message, UserProfile, Stream, Subscription, Huddle, \
|
||||
Recipient, Realm, UserMessage, DefaultStream, RealmEmoji, RealmAlias, \
|
||||
RealmFilter, bulk_get_recipients, \
|
||||
RealmFilter, \
|
||||
PreregistrationUser, get_client, MitUser, UserActivity, PushDeviceToken, \
|
||||
get_stream, bulk_get_streams, UserPresence, \
|
||||
get_recipient, valid_stream_name, \
|
||||
get_stream, UserPresence, get_recipient, \
|
||||
split_email_to_domain, resolve_email_to_domain, email_to_username, get_realm, \
|
||||
completely_open, get_unique_open_realm, get_active_user_dicts_in_realm, remote_user_to_email
|
||||
from zerver.lib.actions import bulk_remove_subscriptions, do_change_password, \
|
||||
completely_open, get_unique_open_realm, remote_user_to_email
|
||||
from zerver.lib.actions import do_change_password, \
|
||||
do_change_full_name, do_change_enable_desktop_notifications, do_change_is_admin, \
|
||||
do_change_enter_sends, do_change_enable_sounds, do_activate_user, do_create_user, \
|
||||
do_change_subscription_property, internal_send_message, \
|
||||
create_stream_if_needed, gather_subscriptions, subscribed_to_stream, \
|
||||
update_user_presence, bulk_add_subscriptions, do_events_register, \
|
||||
internal_send_message, subscribed_to_stream, update_user_presence, do_events_register, \
|
||||
get_status_dict, do_change_enable_offline_email_notifications, \
|
||||
do_change_enable_digest_emails, do_set_realm_name, do_set_realm_restricted_to_domain, \
|
||||
do_set_realm_invite_required, do_set_realm_invite_by_admins_only, internal_prep_message, \
|
||||
do_send_messages, get_default_subs, do_deactivate_user, do_reactivate_user, \
|
||||
do_set_realm_invite_required, do_set_realm_invite_by_admins_only, \
|
||||
get_default_subs, do_deactivate_user, do_reactivate_user, \
|
||||
user_email_is_unique, do_invite_users, do_refer_friend, compute_mit_user_fullname, \
|
||||
do_add_alert_words, do_remove_alert_words, do_set_alert_words, get_subscriber_emails, \
|
||||
do_set_muted_topics, do_rename_stream, clear_followup_emails_queue, \
|
||||
do_change_enable_offline_push_notifications, \
|
||||
do_deactivate_stream, do_change_autoscroll_forever, do_make_stream_public, \
|
||||
do_add_default_stream, do_change_default_all_public_streams, \
|
||||
do_change_default_desktop_notifications, \
|
||||
do_add_alert_words, do_remove_alert_words, do_set_alert_words, \
|
||||
do_set_muted_topics, clear_followup_emails_queue, \
|
||||
do_change_enable_offline_push_notifications, do_change_autoscroll_forever, \
|
||||
do_change_default_all_public_streams, do_change_default_desktop_notifications, \
|
||||
do_change_default_events_register_stream, do_change_default_sending_stream, \
|
||||
do_change_enable_stream_desktop_notifications, do_change_enable_stream_sounds, \
|
||||
do_change_stream_description, do_get_streams, do_make_stream_private, \
|
||||
do_regenerate_api_key, do_remove_default_stream, do_update_pointer, \
|
||||
do_regenerate_api_key, do_update_pointer, \
|
||||
do_change_avatar_source, do_change_twenty_four_hour_time, do_change_left_side_userlist, \
|
||||
realm_user_count
|
||||
|
||||
from zerver.lib.create_user import random_api_key
|
||||
from zerver.lib.push_notifications import num_push_devices_for_user
|
||||
from zerver.forms import RegistrationForm, HomepageForm, ToSForm, \
|
||||
CreateUserForm, is_inactive, OurAuthenticationForm
|
||||
|
@ -59,16 +50,14 @@ from django_auth_ldap.backend import LDAPBackend, _LDAPUser
|
|||
from zerver.lib import bugdown
|
||||
from zerver.lib.alert_words import user_alert_words
|
||||
from zerver.lib.validator import check_string, check_list, check_dict, \
|
||||
check_int, check_bool, check_variable_type
|
||||
from zerver.decorator import require_post, \
|
||||
authenticated_api_view, authenticated_json_post_view, \
|
||||
check_bool
|
||||
from zerver.decorator import require_post, authenticated_json_post_view, \
|
||||
has_request_variables, authenticated_json_view, to_non_negative_int, \
|
||||
JsonableError, get_user_profile_by_email, REQ, require_realm_admin, \
|
||||
RequestVariableConversionError
|
||||
JsonableError, get_user_profile_by_email, REQ, require_realm_admin
|
||||
from zerver.lib.avatar import avatar_url, get_avatar_url
|
||||
from zerver.lib.upload import upload_message_image_through_web_client, upload_avatar_image, \
|
||||
get_signed_upload_url, get_realm_for_filename
|
||||
from zerver.lib.response import json_success, json_error, json_response
|
||||
from zerver.lib.response import json_success, json_error
|
||||
from zerver.lib.unminify import SourceMap
|
||||
from zerver.lib.queue import queue_json_publish
|
||||
from zerver.lib.utils import statsd, generate_random_token, statsd_key
|
||||
|
@ -92,86 +81,11 @@ import os
|
|||
import jwt
|
||||
import hashlib
|
||||
import hmac
|
||||
from collections import defaultdict
|
||||
|
||||
from zerver.lib.rest import rest_dispatch as _rest_dispatch
|
||||
from six.moves import map
|
||||
rest_dispatch = csrf_exempt((lambda request, *args, **kwargs: _rest_dispatch(request, globals(), *args, **kwargs)))
|
||||
|
||||
def list_to_streams(streams_raw, user_profile, autocreate=False, invite_only=False):
|
||||
"""Converts plaintext stream names to a list of Streams, validating input in the process
|
||||
|
||||
For each stream name, we validate it to ensure it meets our
|
||||
requirements for a proper stream name: that is, that it is shorter
|
||||
than Stream.MAX_NAME_LENGTH characters and passes
|
||||
valid_stream_name.
|
||||
|
||||
This function in autocreate mode should be atomic: either an exception will be raised
|
||||
during a precheck, or all the streams specified will have been created if applicable.
|
||||
|
||||
@param streams_raw The list of stream names to process
|
||||
@param user_profile The user for whom we are retreiving the streams
|
||||
@param autocreate Whether we should create streams if they don't already exist
|
||||
@param invite_only Whether newly created streams should have the invite_only bit set
|
||||
"""
|
||||
existing_streams = []
|
||||
created_streams = []
|
||||
# Validate all streams, getting extant ones, then get-or-creating the rest.
|
||||
stream_set = set(stream_name.strip() for stream_name in streams_raw)
|
||||
rejects = []
|
||||
for stream_name in stream_set:
|
||||
if len(stream_name) > Stream.MAX_NAME_LENGTH:
|
||||
raise JsonableError("Stream name (%s) too long." % (stream_name,))
|
||||
if not valid_stream_name(stream_name):
|
||||
raise JsonableError("Invalid stream name (%s)." % (stream_name,))
|
||||
|
||||
existing_stream_map = bulk_get_streams(user_profile.realm, stream_set)
|
||||
|
||||
for stream_name in stream_set:
|
||||
stream = existing_stream_map.get(stream_name.lower())
|
||||
if stream is None:
|
||||
rejects.append(stream_name)
|
||||
else:
|
||||
existing_streams.append(stream)
|
||||
if autocreate:
|
||||
for stream_name in rejects:
|
||||
stream, created = create_stream_if_needed(user_profile.realm,
|
||||
stream_name,
|
||||
invite_only=invite_only)
|
||||
if created:
|
||||
created_streams.append(stream)
|
||||
else:
|
||||
existing_streams.append(stream)
|
||||
elif rejects:
|
||||
raise JsonableError("Stream(s) (%s) do not exist" % ", ".join(rejects))
|
||||
|
||||
return existing_streams, created_streams
|
||||
|
||||
class PrincipalError(JsonableError):
|
||||
def __init__(self, principal):
|
||||
self.principal = principal
|
||||
|
||||
def to_json_error_msg(self):
|
||||
return ("User not authorized to execute queries on behalf of '%s'"
|
||||
% (self.principal,))
|
||||
|
||||
def principal_to_user_profile(agent, principal):
|
||||
principal_doesnt_exist = False
|
||||
try:
|
||||
principal_user_profile = get_user_profile_by_email(principal)
|
||||
except UserProfile.DoesNotExist:
|
||||
principal_doesnt_exist = True
|
||||
|
||||
if (principal_doesnt_exist
|
||||
or agent.realm != principal_user_profile.realm):
|
||||
# We have to make sure we don't leak information about which users
|
||||
# are registered for Zulip in a different realm. We could do
|
||||
# something a little more clever and check the domain part of the
|
||||
# principal to maybe give a better error message
|
||||
raise PrincipalError(principal)
|
||||
|
||||
return principal_user_profile
|
||||
|
||||
def name_changes_disabled(realm):
|
||||
return settings.NAME_CHANGES_DISABLED or realm.name_changes_disabled
|
||||
|
||||
|
@ -1139,26 +1053,6 @@ def json_tutorial_status(request, user_profile, status=REQ('status')):
|
|||
|
||||
return json_success()
|
||||
|
||||
@authenticated_json_post_view
|
||||
def json_get_public_streams(request, user_profile):
|
||||
return get_public_streams_backend(request, user_profile)
|
||||
|
||||
# By default, lists all streams that the user has access to --
|
||||
# i.e. public streams plus invite-only streams that the user is on
|
||||
@has_request_variables
|
||||
def get_streams_backend(request, user_profile,
|
||||
include_public=REQ(validator=check_bool, default=True),
|
||||
include_subscribed=REQ(validator=check_bool, default=True),
|
||||
include_all_active=REQ(validator=check_bool, default=False)):
|
||||
|
||||
streams = do_get_streams(user_profile, include_public, include_subscribed,
|
||||
include_all_active)
|
||||
return json_success({"streams": streams})
|
||||
|
||||
def get_public_streams_backend(request, user_profile):
|
||||
return get_streams_backend(request, user_profile, include_public=True,
|
||||
include_subscribed=False, include_all_active=False)
|
||||
|
||||
@require_realm_admin
|
||||
@has_request_variables
|
||||
def update_realm(request, user_profile, name=REQ(validator=check_string, default=None),
|
||||
|
@ -1181,260 +1075,6 @@ def update_realm(request, user_profile, name=REQ(validator=check_string, default
|
|||
data['invite_by_admins_only'] = invite_by_admins_only
|
||||
return json_success(data)
|
||||
|
||||
@require_realm_admin
|
||||
@has_request_variables
|
||||
def add_default_stream(request, user_profile, stream_name=REQ):
|
||||
return json_success(do_add_default_stream(user_profile.realm, stream_name))
|
||||
|
||||
@require_realm_admin
|
||||
@has_request_variables
|
||||
def remove_default_stream(request, user_profile, stream_name=REQ):
|
||||
return json_success(do_remove_default_stream(user_profile.realm, stream_name))
|
||||
|
||||
@authenticated_json_post_view
|
||||
@require_realm_admin
|
||||
@has_request_variables
|
||||
def json_rename_stream(request, user_profile, old_name=REQ, new_name=REQ):
|
||||
return json_success(do_rename_stream(user_profile.realm, old_name, new_name))
|
||||
|
||||
@authenticated_json_post_view
|
||||
@require_realm_admin
|
||||
@has_request_variables
|
||||
def json_make_stream_public(request, user_profile, stream_name=REQ):
|
||||
return json_success(do_make_stream_public(user_profile, user_profile.realm, stream_name))
|
||||
|
||||
@authenticated_json_post_view
|
||||
@require_realm_admin
|
||||
@has_request_variables
|
||||
def json_make_stream_private(request, user_profile, stream_name=REQ):
|
||||
return json_success(do_make_stream_private(user_profile.realm, stream_name))
|
||||
|
||||
@require_realm_admin
|
||||
@has_request_variables
|
||||
def update_stream_backend(request, user_profile, stream_name,
|
||||
description=REQ(validator=check_string, default=None)):
|
||||
if description is not None:
|
||||
do_change_stream_description(user_profile.realm, stream_name, description)
|
||||
return json_success({})
|
||||
|
||||
def list_subscriptions_backend(request, user_profile):
|
||||
return json_success({"subscriptions": gather_subscriptions(user_profile)[0]})
|
||||
|
||||
@transaction.atomic
|
||||
@has_request_variables
|
||||
def update_subscriptions_backend(request, user_profile,
|
||||
delete=REQ(validator=check_list(check_string), default=[]),
|
||||
add=REQ(validator=check_list(check_dict([['name', check_string]])), default=[])):
|
||||
if not add and not delete:
|
||||
return json_error('Nothing to do. Specify at least one of "add" or "delete".')
|
||||
|
||||
json_dict = {}
|
||||
for method, items in ((add_subscriptions_backend, add), (remove_subscriptions_backend, delete)):
|
||||
response = method(request, user_profile, streams_raw=items)
|
||||
if response.status_code != 200:
|
||||
transaction.rollback()
|
||||
return response
|
||||
json_dict.update(ujson.loads(response.content))
|
||||
return json_success(json_dict)
|
||||
|
||||
@authenticated_json_post_view
|
||||
def json_remove_subscriptions(request, user_profile):
|
||||
return remove_subscriptions_backend(request, user_profile)
|
||||
|
||||
@has_request_variables
|
||||
def remove_subscriptions_backend(request, user_profile,
|
||||
streams_raw = REQ("subscriptions", validator=check_list(check_string)),
|
||||
principals = REQ(validator=check_list(check_string), default=None)):
|
||||
|
||||
removing_someone_else = principals and \
|
||||
set(principals) != set((user_profile.email,))
|
||||
if removing_someone_else and not user_profile.is_admin():
|
||||
# You can only unsubscribe other people from a stream if you are a realm
|
||||
# admin.
|
||||
return json_error("This action requires administrative rights")
|
||||
|
||||
streams, _ = list_to_streams(streams_raw, user_profile)
|
||||
|
||||
for stream in streams:
|
||||
if removing_someone_else and stream.invite_only and \
|
||||
not subscribed_to_stream(user_profile, stream):
|
||||
# Even as an admin, you can't remove other people from an
|
||||
# invite-only stream you're not on.
|
||||
return json_error("Cannot administer invite-only streams this way")
|
||||
|
||||
if principals:
|
||||
people_to_unsub = set(principal_to_user_profile(
|
||||
user_profile, principal) for principal in principals)
|
||||
else:
|
||||
people_to_unsub = [user_profile]
|
||||
|
||||
result = dict(removed=[], not_subscribed=[])
|
||||
(removed, not_subscribed) = bulk_remove_subscriptions(people_to_unsub, streams)
|
||||
|
||||
for (subscriber, stream) in removed:
|
||||
result["removed"].append(stream.name)
|
||||
for (subscriber, stream) in not_subscribed:
|
||||
result["not_subscribed"].append(stream.name)
|
||||
|
||||
return json_success(result)
|
||||
|
||||
@authenticated_json_post_view
|
||||
def json_add_subscriptions(request, user_profile):
|
||||
return add_subscriptions_backend(request, user_profile)
|
||||
|
||||
def filter_stream_authorization(user_profile, streams):
|
||||
streams_subscribed = set()
|
||||
recipients_map = bulk_get_recipients(Recipient.STREAM, [stream.id for stream in streams])
|
||||
subs = Subscription.objects.filter(user_profile=user_profile,
|
||||
recipient__in=recipients_map.values(),
|
||||
active=True)
|
||||
|
||||
for sub in subs:
|
||||
streams_subscribed.add(sub.recipient.type_id)
|
||||
|
||||
unauthorized_streams = []
|
||||
for stream in streams:
|
||||
# The user is authorized for his own streams
|
||||
if stream.id in streams_subscribed:
|
||||
continue
|
||||
|
||||
# The user is not authorized for invite_only streams
|
||||
if stream.invite_only:
|
||||
unauthorized_streams.append(stream)
|
||||
|
||||
streams = [stream for stream in streams if
|
||||
stream.id not in set(stream.id for stream in unauthorized_streams)]
|
||||
return streams, unauthorized_streams
|
||||
|
||||
def stream_link(stream_name):
|
||||
"Escapes a stream name to make a #narrow/stream/stream_name link"
|
||||
return "#narrow/stream/%s" % (urllib.quote(stream_name.encode('utf-8')),)
|
||||
|
||||
def stream_button(stream_name):
|
||||
stream_name = stream_name.replace('\\', '\\\\')
|
||||
stream_name = stream_name.replace(')', '\\)')
|
||||
return '!_stream_subscribe_button(%s)' % (stream_name,)
|
||||
|
||||
@has_request_variables
|
||||
def add_subscriptions_backend(request, user_profile,
|
||||
streams_raw = REQ("subscriptions",
|
||||
validator=check_list(check_dict([['name', check_string]]))),
|
||||
invite_only = REQ(validator=check_bool, default=False),
|
||||
announce = REQ(validator=check_bool, default=False),
|
||||
principals = REQ(validator=check_list(check_string), default=None),
|
||||
authorization_errors_fatal = REQ(validator=check_bool, default=True)):
|
||||
|
||||
if not user_profile.can_create_streams():
|
||||
return json_error('User cannot create streams.')
|
||||
|
||||
stream_names = []
|
||||
for stream in streams_raw:
|
||||
stream_name = stream["name"].strip()
|
||||
if len(stream_name) > Stream.MAX_NAME_LENGTH:
|
||||
return json_error("Stream name (%s) too long." % (stream_name,))
|
||||
if not valid_stream_name(stream_name):
|
||||
return json_error("Invalid stream name (%s)." % (stream_name,))
|
||||
stream_names.append(stream_name)
|
||||
|
||||
existing_streams, created_streams = \
|
||||
list_to_streams(stream_names, user_profile, autocreate=True, invite_only=invite_only)
|
||||
authorized_streams, unauthorized_streams = \
|
||||
filter_stream_authorization(user_profile, existing_streams)
|
||||
if len(unauthorized_streams) > 0 and authorization_errors_fatal:
|
||||
return json_error("Unable to access stream (%s)." % unauthorized_streams[0].name)
|
||||
# Newly created streams are also authorized for the creator
|
||||
streams = authorized_streams + created_streams
|
||||
|
||||
if principals is not None:
|
||||
if user_profile.realm.domain == 'mit.edu' and not all(stream.invite_only for stream in streams):
|
||||
return json_error("You can only invite other mit.edu users to invite-only streams.")
|
||||
subscribers = set(principal_to_user_profile(user_profile, principal) for principal in principals)
|
||||
else:
|
||||
subscribers = [user_profile]
|
||||
|
||||
(subscribed, already_subscribed) = bulk_add_subscriptions(streams, subscribers)
|
||||
|
||||
result = dict(subscribed=defaultdict(list), already_subscribed=defaultdict(list))
|
||||
for (subscriber, stream) in subscribed:
|
||||
result["subscribed"][subscriber.email].append(stream.name)
|
||||
for (subscriber, stream) in already_subscribed:
|
||||
result["already_subscribed"][subscriber.email].append(stream.name)
|
||||
|
||||
private_streams = dict((stream.name, stream.invite_only) for stream in streams)
|
||||
bots = dict((subscriber.email, subscriber.is_bot) for subscriber in subscribers)
|
||||
|
||||
# Inform the user if someone else subscribed them to stuff,
|
||||
# or if a new stream was created with the "announce" option.
|
||||
notifications = []
|
||||
if principals and result["subscribed"]:
|
||||
for email, subscriptions in result["subscribed"].iteritems():
|
||||
if email == user_profile.email:
|
||||
# Don't send a Zulip if you invited yourself.
|
||||
continue
|
||||
if bots[email]:
|
||||
# Don't send invitation Zulips to bots
|
||||
continue
|
||||
|
||||
if len(subscriptions) == 1:
|
||||
msg = ("Hi there! We thought you'd like to know that %s just "
|
||||
"subscribed you to the%s stream [%s](%s)."
|
||||
% (user_profile.full_name,
|
||||
" **invite-only**" if private_streams[subscriptions[0]] else "",
|
||||
subscriptions[0],
|
||||
stream_link(subscriptions[0]),
|
||||
))
|
||||
else:
|
||||
msg = ("Hi there! We thought you'd like to know that %s just "
|
||||
"subscribed you to the following streams: \n\n"
|
||||
% (user_profile.full_name,))
|
||||
for stream in subscriptions:
|
||||
msg += "* [%s](%s)%s\n" % (
|
||||
stream,
|
||||
stream_link(stream),
|
||||
" (**invite-only**)" if private_streams[stream] else "")
|
||||
|
||||
if len([s for s in subscriptions if not private_streams[s]]) > 0:
|
||||
msg += "\nYou can see historical content on a non-invite-only stream by narrowing to it."
|
||||
notifications.append(internal_prep_message(settings.NOTIFICATION_BOT,
|
||||
"private", email, "", msg))
|
||||
|
||||
if announce and len(created_streams) > 0:
|
||||
notifications_stream = user_profile.realm.notifications_stream
|
||||
if notifications_stream is not None:
|
||||
if len(created_streams) > 1:
|
||||
stream_msg = "the following streams: %s" % \
|
||||
(", ".join('`%s`' % (s.name,) for s in created_streams),)
|
||||
else:
|
||||
stream_msg = "a new stream `%s`" % (created_streams[0].name)
|
||||
|
||||
stream_buttons = ' '.join(stream_button(s.name) for s in created_streams)
|
||||
msg = ("%s just created %s. %s" % (user_profile.full_name,
|
||||
stream_msg, stream_buttons))
|
||||
notifications.append(internal_prep_message(settings.NOTIFICATION_BOT,
|
||||
"stream",
|
||||
notifications_stream.name, "Streams", msg,
|
||||
realm=notifications_stream.realm))
|
||||
else:
|
||||
msg = ("Hi there! %s just created a new stream '%s'. %s"
|
||||
% (user_profile.full_name, created_streams[0].name, stream_button(created_streams[0].name)))
|
||||
for realm_user_dict in get_active_user_dicts_in_realm(user_profile.realm):
|
||||
# Don't announce to yourself or to people you explicitly added
|
||||
# (who will get the notification above instead).
|
||||
if realm_user_dict['email'] in principals or realm_user_dict['email'] == user_profile.email:
|
||||
continue
|
||||
notifications.append(internal_prep_message(settings.NOTIFICATION_BOT,
|
||||
"private",
|
||||
realm_user_dict['email'], "", msg))
|
||||
|
||||
if len(notifications) > 0:
|
||||
do_send_messages(notifications)
|
||||
|
||||
result["subscribed"] = dict(result["subscribed"])
|
||||
result["already_subscribed"] = dict(result["already_subscribed"])
|
||||
if not authorization_errors_fatal:
|
||||
result["unauthorized"] = [stream.name for stream in unauthorized_streams]
|
||||
return json_success(result)
|
||||
|
||||
def get_members_backend(request, user_profile):
|
||||
realm = user_profile.realm
|
||||
admins = set(user_profile.realm.get_admin_users())
|
||||
|
@ -1455,10 +1095,6 @@ def get_members_backend(request, user_profile):
|
|||
members.append(member)
|
||||
return json_success({'members': members})
|
||||
|
||||
@authenticated_json_post_view
|
||||
def json_get_subscribers(request, user_profile):
|
||||
return get_subscribers_backend(request, user_profile)
|
||||
|
||||
@authenticated_json_post_view
|
||||
@has_request_variables
|
||||
def json_upload_file(request, user_profile):
|
||||
|
@ -1497,16 +1133,6 @@ def get_uploaded_file(request, realm_id, filename,
|
|||
else:
|
||||
return HttpResponseForbidden()
|
||||
|
||||
@has_request_variables
|
||||
def get_subscribers_backend(request, user_profile, stream_name=REQ('stream')):
|
||||
stream = get_stream(stream_name, user_profile.realm)
|
||||
if stream is None:
|
||||
raise JsonableError("Stream does not exist: %s" % (stream_name,))
|
||||
|
||||
subscribers = get_subscriber_emails(stream, user_profile)
|
||||
|
||||
return json_success({'subscribers': subscribers})
|
||||
|
||||
@authenticated_json_post_view
|
||||
@has_request_variables
|
||||
def json_change_settings(request, user_profile,
|
||||
|
@ -1667,89 +1293,6 @@ def json_change_ui_settings(request, user_profile,
|
|||
|
||||
return json_success(result)
|
||||
|
||||
@authenticated_json_post_view
|
||||
@has_request_variables
|
||||
def json_stream_exists(request, user_profile, stream=REQ,
|
||||
autosubscribe=REQ(default=False)):
|
||||
return stream_exists_backend(request, user_profile, stream, autosubscribe)
|
||||
|
||||
def stream_exists_backend(request, user_profile, stream_name, autosubscribe):
|
||||
if not valid_stream_name(stream_name):
|
||||
return json_error("Invalid characters in stream name")
|
||||
stream = get_stream(stream_name, user_profile.realm)
|
||||
result = {"exists": bool(stream)}
|
||||
if stream is not None:
|
||||
recipient = get_recipient(Recipient.STREAM, stream.id)
|
||||
if autosubscribe:
|
||||
bulk_add_subscriptions([stream], [user_profile])
|
||||
result["subscribed"] = Subscription.objects.filter(user_profile=user_profile,
|
||||
recipient=recipient,
|
||||
active=True).exists()
|
||||
return json_success(result) # results are ignored for HEAD requests
|
||||
return json_response(data=result, status=404)
|
||||
|
||||
def get_subscription_or_die(stream_name, user_profile):
|
||||
stream = get_stream(stream_name, user_profile.realm)
|
||||
if not stream:
|
||||
raise JsonableError("Invalid stream %s" % (stream.name,))
|
||||
recipient = get_recipient(Recipient.STREAM, stream.id)
|
||||
subscription = Subscription.objects.filter(user_profile=user_profile,
|
||||
recipient=recipient, active=True)
|
||||
|
||||
if not subscription.exists():
|
||||
raise JsonableError("Not subscribed to stream %s" % (stream_name,))
|
||||
|
||||
return subscription
|
||||
|
||||
@authenticated_json_view
|
||||
@has_request_variables
|
||||
def json_subscription_property(request, user_profile, subscription_data=REQ(
|
||||
validator=check_list(
|
||||
check_dict([["stream", check_string],
|
||||
["property", check_string],
|
||||
["value", check_variable_type(
|
||||
[check_string, check_bool])]])))):
|
||||
"""
|
||||
This is the entry point to changing subscription properties. This
|
||||
is a bulk endpoint: requestors always provide a subscription_data
|
||||
list containing dictionaries for each stream of interest.
|
||||
|
||||
Requests are of the form:
|
||||
|
||||
[{"stream": "devel", "property": "in_home_view", "value": False},
|
||||
{"stream": "devel", "property": "color", "value": "#c2c2c2"}]
|
||||
"""
|
||||
if request.method != "POST":
|
||||
return json_error("Invalid verb")
|
||||
|
||||
property_converters = {"color": check_string, "in_home_view": check_bool,
|
||||
"desktop_notifications": check_bool,
|
||||
"audible_notifications": check_bool}
|
||||
response_data = []
|
||||
|
||||
for change in subscription_data:
|
||||
stream_name = change["stream"]
|
||||
property = change["property"]
|
||||
value = change["value"]
|
||||
|
||||
if property not in property_converters:
|
||||
return json_error("Unknown subscription property: %s" % (property,))
|
||||
|
||||
sub = get_subscription_or_die(stream_name, user_profile)[0]
|
||||
|
||||
property_conversion = property_converters[property](property, value)
|
||||
if property_conversion:
|
||||
return json_error(property_conversion)
|
||||
|
||||
do_change_subscription_property(user_profile, sub, stream_name,
|
||||
property, value)
|
||||
|
||||
response_data.append({'stream': stream_name,
|
||||
'property': property,
|
||||
'value': value})
|
||||
|
||||
return json_success({"subscription_data": response_data})
|
||||
|
||||
@csrf_exempt
|
||||
@require_post
|
||||
@has_request_variables
|
||||
|
@ -1997,18 +1540,6 @@ def update_user_backend(request, user_profile, email,
|
|||
do_change_is_admin(target, is_admin)
|
||||
return json_success({})
|
||||
|
||||
@require_realm_admin
|
||||
def deactivate_stream_backend(request, user_profile, stream_name):
|
||||
target = get_stream(stream_name, user_profile.realm)
|
||||
if not target:
|
||||
return json_error('No such stream name')
|
||||
|
||||
if target.invite_only and not subscribed_to_stream(user_profile, target):
|
||||
return json_error('Cannot administer invite-only streams this way')
|
||||
|
||||
do_deactivate_stream(target)
|
||||
return json_success({})
|
||||
|
||||
def avatar(request, email):
|
||||
try:
|
||||
user_profile = get_user_profile_by_email(email)
|
||||
|
|
|
@ -0,0 +1,486 @@
|
|||
from __future__ import absolute_import
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import transaction
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
|
||||
from zerver.decorator import authenticated_json_post_view, \
|
||||
has_request_variables, authenticated_json_view, \
|
||||
JsonableError, get_user_profile_by_email, REQ, require_realm_admin
|
||||
from zerver.lib.actions import bulk_remove_subscriptions, \
|
||||
do_change_subscription_property, internal_prep_message, \
|
||||
create_stream_if_needed, gather_subscriptions, subscribed_to_stream, \
|
||||
bulk_add_subscriptions, do_send_messages, get_subscriber_emails, do_rename_stream, \
|
||||
do_deactivate_stream, do_make_stream_public, do_add_default_stream, \
|
||||
do_change_stream_description, do_get_streams, do_make_stream_private, \
|
||||
do_remove_default_stream
|
||||
from zerver.lib.response import json_success, json_error, json_response
|
||||
from zerver.lib.validator import check_string, check_list, check_dict, \
|
||||
check_bool, check_variable_type
|
||||
from zerver.models import UserProfile, Stream, Subscription, \
|
||||
Recipient, get_recipient, get_stream, bulk_get_streams, \
|
||||
bulk_get_recipients, valid_stream_name, get_active_user_dicts_in_realm
|
||||
|
||||
from collections import defaultdict
|
||||
import ujson
|
||||
import urllib
|
||||
|
||||
from zerver.lib.rest import rest_dispatch as _rest_dispatch
|
||||
rest_dispatch = csrf_exempt((lambda request, *args, **kwargs: _rest_dispatch(request, globals(), *args, **kwargs)))
|
||||
|
||||
def list_to_streams(streams_raw, user_profile, autocreate=False, invite_only=False):
|
||||
"""Converts plaintext stream names to a list of Streams, validating input in the process
|
||||
|
||||
For each stream name, we validate it to ensure it meets our
|
||||
requirements for a proper stream name: that is, that it is shorter
|
||||
than Stream.MAX_NAME_LENGTH characters and passes
|
||||
valid_stream_name.
|
||||
|
||||
This function in autocreate mode should be atomic: either an exception will be raised
|
||||
during a precheck, or all the streams specified will have been created if applicable.
|
||||
|
||||
@param streams_raw The list of stream names to process
|
||||
@param user_profile The user for whom we are retreiving the streams
|
||||
@param autocreate Whether we should create streams if they don't already exist
|
||||
@param invite_only Whether newly created streams should have the invite_only bit set
|
||||
"""
|
||||
existing_streams = []
|
||||
created_streams = []
|
||||
# Validate all streams, getting extant ones, then get-or-creating the rest.
|
||||
stream_set = set(stream_name.strip() for stream_name in streams_raw)
|
||||
rejects = []
|
||||
for stream_name in stream_set:
|
||||
if len(stream_name) > Stream.MAX_NAME_LENGTH:
|
||||
raise JsonableError("Stream name (%s) too long." % (stream_name,))
|
||||
if not valid_stream_name(stream_name):
|
||||
raise JsonableError("Invalid stream name (%s)." % (stream_name,))
|
||||
|
||||
existing_stream_map = bulk_get_streams(user_profile.realm, stream_set)
|
||||
|
||||
for stream_name in stream_set:
|
||||
stream = existing_stream_map.get(stream_name.lower())
|
||||
if stream is None:
|
||||
rejects.append(stream_name)
|
||||
else:
|
||||
existing_streams.append(stream)
|
||||
if autocreate:
|
||||
for stream_name in rejects:
|
||||
stream, created = create_stream_if_needed(user_profile.realm,
|
||||
stream_name,
|
||||
invite_only=invite_only)
|
||||
if created:
|
||||
created_streams.append(stream)
|
||||
else:
|
||||
existing_streams.append(stream)
|
||||
elif rejects:
|
||||
raise JsonableError("Stream(s) (%s) do not exist" % ", ".join(rejects))
|
||||
|
||||
return existing_streams, created_streams
|
||||
|
||||
class PrincipalError(JsonableError):
|
||||
def __init__(self, principal):
|
||||
self.principal = principal
|
||||
|
||||
def to_json_error_msg(self):
|
||||
return ("User not authorized to execute queries on behalf of '%s'"
|
||||
% (self.principal,))
|
||||
|
||||
def principal_to_user_profile(agent, principal):
|
||||
principal_doesnt_exist = False
|
||||
try:
|
||||
principal_user_profile = get_user_profile_by_email(principal)
|
||||
except UserProfile.DoesNotExist:
|
||||
principal_doesnt_exist = True
|
||||
|
||||
if (principal_doesnt_exist
|
||||
or agent.realm != principal_user_profile.realm):
|
||||
# We have to make sure we don't leak information about which users
|
||||
# are registered for Zulip in a different realm. We could do
|
||||
# something a little more clever and check the domain part of the
|
||||
# principal to maybe give a better error message
|
||||
raise PrincipalError(principal)
|
||||
|
||||
return principal_user_profile
|
||||
|
||||
@require_realm_admin
|
||||
def deactivate_stream_backend(request, user_profile, stream_name):
|
||||
target = get_stream(stream_name, user_profile.realm)
|
||||
if not target:
|
||||
return json_error('No such stream name')
|
||||
|
||||
if target.invite_only and not subscribed_to_stream(user_profile, target):
|
||||
return json_error('Cannot administer invite-only streams this way')
|
||||
|
||||
do_deactivate_stream(target)
|
||||
return json_success({})
|
||||
|
||||
@require_realm_admin
|
||||
@has_request_variables
|
||||
def add_default_stream(request, user_profile, stream_name=REQ):
|
||||
return json_success(do_add_default_stream(user_profile.realm, stream_name))
|
||||
|
||||
@require_realm_admin
|
||||
@has_request_variables
|
||||
def remove_default_stream(request, user_profile, stream_name=REQ):
|
||||
return json_success(do_remove_default_stream(user_profile.realm, stream_name))
|
||||
|
||||
@authenticated_json_post_view
|
||||
@require_realm_admin
|
||||
@has_request_variables
|
||||
def json_rename_stream(request, user_profile, old_name=REQ, new_name=REQ):
|
||||
return json_success(do_rename_stream(user_profile.realm, old_name, new_name))
|
||||
|
||||
@authenticated_json_post_view
|
||||
@require_realm_admin
|
||||
@has_request_variables
|
||||
def json_make_stream_public(request, user_profile, stream_name=REQ):
|
||||
return json_success(do_make_stream_public(user_profile, user_profile.realm, stream_name))
|
||||
|
||||
@authenticated_json_post_view
|
||||
@require_realm_admin
|
||||
@has_request_variables
|
||||
def json_make_stream_private(request, user_profile, stream_name=REQ):
|
||||
return json_success(do_make_stream_private(user_profile.realm, stream_name))
|
||||
|
||||
@require_realm_admin
|
||||
@has_request_variables
|
||||
def update_stream_backend(request, user_profile, stream_name,
|
||||
description=REQ(validator=check_string, default=None)):
|
||||
if description is not None:
|
||||
do_change_stream_description(user_profile.realm, stream_name, description)
|
||||
return json_success({})
|
||||
|
||||
def list_subscriptions_backend(request, user_profile):
|
||||
return json_success({"subscriptions": gather_subscriptions(user_profile)[0]})
|
||||
|
||||
@transaction.atomic
|
||||
@has_request_variables
|
||||
def update_subscriptions_backend(request, user_profile,
|
||||
delete=REQ(validator=check_list(check_string), default=[]),
|
||||
add=REQ(validator=check_list(check_dict([['name', check_string]])), default=[])):
|
||||
if not add and not delete:
|
||||
return json_error('Nothing to do. Specify at least one of "add" or "delete".')
|
||||
|
||||
json_dict = {}
|
||||
for method, items in ((add_subscriptions_backend, add), (remove_subscriptions_backend, delete)):
|
||||
response = method(request, user_profile, streams_raw=items)
|
||||
if response.status_code != 200:
|
||||
transaction.rollback()
|
||||
return response
|
||||
json_dict.update(ujson.loads(response.content))
|
||||
return json_success(json_dict)
|
||||
|
||||
@authenticated_json_post_view
|
||||
def json_remove_subscriptions(request, user_profile):
|
||||
return remove_subscriptions_backend(request, user_profile)
|
||||
|
||||
@has_request_variables
|
||||
def remove_subscriptions_backend(request, user_profile,
|
||||
streams_raw = REQ("subscriptions", validator=check_list(check_string)),
|
||||
principals = REQ(validator=check_list(check_string), default=None)):
|
||||
|
||||
removing_someone_else = principals and \
|
||||
set(principals) != set((user_profile.email,))
|
||||
if removing_someone_else and not user_profile.is_admin():
|
||||
# You can only unsubscribe other people from a stream if you are a realm
|
||||
# admin.
|
||||
return json_error("This action requires administrative rights")
|
||||
|
||||
streams, _ = list_to_streams(streams_raw, user_profile)
|
||||
|
||||
for stream in streams:
|
||||
if removing_someone_else and stream.invite_only and \
|
||||
not subscribed_to_stream(user_profile, stream):
|
||||
# Even as an admin, you can't remove other people from an
|
||||
# invite-only stream you're not on.
|
||||
return json_error("Cannot administer invite-only streams this way")
|
||||
|
||||
if principals:
|
||||
people_to_unsub = set(principal_to_user_profile(
|
||||
user_profile, principal) for principal in principals)
|
||||
else:
|
||||
people_to_unsub = [user_profile]
|
||||
|
||||
result = dict(removed=[], not_subscribed=[])
|
||||
(removed, not_subscribed) = bulk_remove_subscriptions(people_to_unsub, streams)
|
||||
|
||||
for (subscriber, stream) in removed:
|
||||
result["removed"].append(stream.name)
|
||||
for (subscriber, stream) in not_subscribed:
|
||||
result["not_subscribed"].append(stream.name)
|
||||
|
||||
return json_success(result)
|
||||
|
||||
@authenticated_json_post_view
|
||||
def json_add_subscriptions(request, user_profile):
|
||||
return add_subscriptions_backend(request, user_profile)
|
||||
|
||||
def filter_stream_authorization(user_profile, streams):
|
||||
streams_subscribed = set()
|
||||
recipients_map = bulk_get_recipients(Recipient.STREAM, [stream.id for stream in streams])
|
||||
subs = Subscription.objects.filter(user_profile=user_profile,
|
||||
recipient__in=recipients_map.values(),
|
||||
active=True)
|
||||
|
||||
for sub in subs:
|
||||
streams_subscribed.add(sub.recipient.type_id)
|
||||
|
||||
unauthorized_streams = []
|
||||
for stream in streams:
|
||||
# The user is authorized for his own streams
|
||||
if stream.id in streams_subscribed:
|
||||
continue
|
||||
|
||||
# The user is not authorized for invite_only streams
|
||||
if stream.invite_only:
|
||||
unauthorized_streams.append(stream)
|
||||
|
||||
streams = [stream for stream in streams if
|
||||
stream.id not in set(stream.id for stream in unauthorized_streams)]
|
||||
return streams, unauthorized_streams
|
||||
|
||||
def stream_link(stream_name):
|
||||
"Escapes a stream name to make a #narrow/stream/stream_name link"
|
||||
return "#narrow/stream/%s" % (urllib.quote(stream_name.encode('utf-8')),)
|
||||
|
||||
def stream_button(stream_name):
|
||||
stream_name = stream_name.replace('\\', '\\\\')
|
||||
stream_name = stream_name.replace(')', '\\)')
|
||||
return '!_stream_subscribe_button(%s)' % (stream_name,)
|
||||
|
||||
@has_request_variables
|
||||
def add_subscriptions_backend(request, user_profile,
|
||||
streams_raw = REQ("subscriptions",
|
||||
validator=check_list(check_dict([['name', check_string]]))),
|
||||
invite_only = REQ(validator=check_bool, default=False),
|
||||
announce = REQ(validator=check_bool, default=False),
|
||||
principals = REQ(validator=check_list(check_string), default=None),
|
||||
authorization_errors_fatal = REQ(validator=check_bool, default=True)):
|
||||
|
||||
if not user_profile.can_create_streams():
|
||||
return json_error('User cannot create streams.')
|
||||
|
||||
stream_names = []
|
||||
for stream in streams_raw:
|
||||
stream_name = stream["name"].strip()
|
||||
if len(stream_name) > Stream.MAX_NAME_LENGTH:
|
||||
return json_error("Stream name (%s) too long." % (stream_name,))
|
||||
if not valid_stream_name(stream_name):
|
||||
return json_error("Invalid stream name (%s)." % (stream_name,))
|
||||
stream_names.append(stream_name)
|
||||
|
||||
existing_streams, created_streams = \
|
||||
list_to_streams(stream_names, user_profile, autocreate=True, invite_only=invite_only)
|
||||
authorized_streams, unauthorized_streams = \
|
||||
filter_stream_authorization(user_profile, existing_streams)
|
||||
if len(unauthorized_streams) > 0 and authorization_errors_fatal:
|
||||
return json_error("Unable to access stream (%s)." % unauthorized_streams[0].name)
|
||||
# Newly created streams are also authorized for the creator
|
||||
streams = authorized_streams + created_streams
|
||||
|
||||
if principals is not None:
|
||||
if user_profile.realm.domain == 'mit.edu' and not all(stream.invite_only for stream in streams):
|
||||
return json_error("You can only invite other mit.edu users to invite-only streams.")
|
||||
subscribers = set(principal_to_user_profile(user_profile, principal) for principal in principals)
|
||||
else:
|
||||
subscribers = [user_profile]
|
||||
|
||||
(subscribed, already_subscribed) = bulk_add_subscriptions(streams, subscribers)
|
||||
|
||||
result = dict(subscribed=defaultdict(list), already_subscribed=defaultdict(list))
|
||||
for (subscriber, stream) in subscribed:
|
||||
result["subscribed"][subscriber.email].append(stream.name)
|
||||
for (subscriber, stream) in already_subscribed:
|
||||
result["already_subscribed"][subscriber.email].append(stream.name)
|
||||
|
||||
private_streams = dict((stream.name, stream.invite_only) for stream in streams)
|
||||
bots = dict((subscriber.email, subscriber.is_bot) for subscriber in subscribers)
|
||||
|
||||
# Inform the user if someone else subscribed them to stuff,
|
||||
# or if a new stream was created with the "announce" option.
|
||||
notifications = []
|
||||
if principals and result["subscribed"]:
|
||||
for email, subscriptions in result["subscribed"].iteritems():
|
||||
if email == user_profile.email:
|
||||
# Don't send a Zulip if you invited yourself.
|
||||
continue
|
||||
if bots[email]:
|
||||
# Don't send invitation Zulips to bots
|
||||
continue
|
||||
|
||||
if len(subscriptions) == 1:
|
||||
msg = ("Hi there! We thought you'd like to know that %s just "
|
||||
"subscribed you to the%s stream [%s](%s)."
|
||||
% (user_profile.full_name,
|
||||
" **invite-only**" if private_streams[subscriptions[0]] else "",
|
||||
subscriptions[0],
|
||||
stream_link(subscriptions[0]),
|
||||
))
|
||||
else:
|
||||
msg = ("Hi there! We thought you'd like to know that %s just "
|
||||
"subscribed you to the following streams: \n\n"
|
||||
% (user_profile.full_name,))
|
||||
for stream in subscriptions:
|
||||
msg += "* [%s](%s)%s\n" % (
|
||||
stream,
|
||||
stream_link(stream),
|
||||
" (**invite-only**)" if private_streams[stream] else "")
|
||||
|
||||
if len([s for s in subscriptions if not private_streams[s]]) > 0:
|
||||
msg += "\nYou can see historical content on a non-invite-only stream by narrowing to it."
|
||||
notifications.append(internal_prep_message(settings.NOTIFICATION_BOT,
|
||||
"private", email, "", msg))
|
||||
|
||||
if announce and len(created_streams) > 0:
|
||||
notifications_stream = user_profile.realm.notifications_stream
|
||||
if notifications_stream is not None:
|
||||
if len(created_streams) > 1:
|
||||
stream_msg = "the following streams: %s" % \
|
||||
(", ".join('`%s`' % (s.name,) for s in created_streams),)
|
||||
else:
|
||||
stream_msg = "a new stream `%s`" % (created_streams[0].name)
|
||||
|
||||
stream_buttons = ' '.join(stream_button(s.name) for s in created_streams)
|
||||
msg = ("%s just created %s. %s" % (user_profile.full_name,
|
||||
stream_msg, stream_buttons))
|
||||
notifications.append(internal_prep_message(settings.NOTIFICATION_BOT,
|
||||
"stream",
|
||||
notifications_stream.name, "Streams", msg,
|
||||
realm=notifications_stream.realm))
|
||||
else:
|
||||
msg = ("Hi there! %s just created a new stream '%s'. %s"
|
||||
% (user_profile.full_name, created_streams[0].name, stream_button(created_streams[0].name)))
|
||||
for realm_user_dict in get_active_user_dicts_in_realm(user_profile.realm):
|
||||
# Don't announce to yourself or to people you explicitly added
|
||||
# (who will get the notification above instead).
|
||||
if realm_user_dict['email'] in principals or realm_user_dict['email'] == user_profile.email:
|
||||
continue
|
||||
notifications.append(internal_prep_message(settings.NOTIFICATION_BOT,
|
||||
"private",
|
||||
realm_user_dict['email'], "", msg))
|
||||
|
||||
if len(notifications) > 0:
|
||||
do_send_messages(notifications)
|
||||
|
||||
result["subscribed"] = dict(result["subscribed"])
|
||||
result["already_subscribed"] = dict(result["already_subscribed"])
|
||||
if not authorization_errors_fatal:
|
||||
result["unauthorized"] = [stream.name for stream in unauthorized_streams]
|
||||
return json_success(result)
|
||||
|
||||
@has_request_variables
|
||||
def get_subscribers_backend(request, user_profile, stream_name=REQ('stream')):
|
||||
stream = get_stream(stream_name, user_profile.realm)
|
||||
if stream is None:
|
||||
raise JsonableError("Stream does not exist: %s" % (stream_name,))
|
||||
|
||||
subscribers = get_subscriber_emails(stream, user_profile)
|
||||
|
||||
return json_success({'subscribers': subscribers})
|
||||
|
||||
@authenticated_json_post_view
|
||||
def json_get_subscribers(request, user_profile):
|
||||
return get_subscribers_backend(request, user_profile)
|
||||
|
||||
@authenticated_json_post_view
|
||||
def json_get_public_streams(request, user_profile):
|
||||
return get_public_streams_backend(request, user_profile)
|
||||
|
||||
# By default, lists all streams that the user has access to --
|
||||
# i.e. public streams plus invite-only streams that the user is on
|
||||
@has_request_variables
|
||||
def get_streams_backend(request, user_profile,
|
||||
include_public=REQ(validator=check_bool, default=True),
|
||||
include_subscribed=REQ(validator=check_bool, default=True),
|
||||
include_all_active=REQ(validator=check_bool, default=False)):
|
||||
|
||||
streams = do_get_streams(user_profile, include_public, include_subscribed,
|
||||
include_all_active)
|
||||
return json_success({"streams": streams})
|
||||
|
||||
def get_public_streams_backend(request, user_profile):
|
||||
return get_streams_backend(request, user_profile, include_public=True,
|
||||
include_subscribed=False, include_all_active=False)
|
||||
|
||||
@authenticated_json_post_view
|
||||
@has_request_variables
|
||||
def json_stream_exists(request, user_profile, stream=REQ,
|
||||
autosubscribe=REQ(default=False)):
|
||||
return stream_exists_backend(request, user_profile, stream, autosubscribe)
|
||||
|
||||
def stream_exists_backend(request, user_profile, stream_name, autosubscribe):
|
||||
if not valid_stream_name(stream_name):
|
||||
return json_error("Invalid characters in stream name")
|
||||
stream = get_stream(stream_name, user_profile.realm)
|
||||
result = {"exists": bool(stream)}
|
||||
if stream is not None:
|
||||
recipient = get_recipient(Recipient.STREAM, stream.id)
|
||||
if autosubscribe:
|
||||
bulk_add_subscriptions([stream], [user_profile])
|
||||
result["subscribed"] = Subscription.objects.filter(user_profile=user_profile,
|
||||
recipient=recipient,
|
||||
active=True).exists()
|
||||
return json_success(result) # results are ignored for HEAD requests
|
||||
return json_response(data=result, status=404)
|
||||
|
||||
def get_subscription_or_die(stream_name, user_profile):
|
||||
stream = get_stream(stream_name, user_profile.realm)
|
||||
if not stream:
|
||||
raise JsonableError("Invalid stream %s" % (stream.name,))
|
||||
recipient = get_recipient(Recipient.STREAM, stream.id)
|
||||
subscription = Subscription.objects.filter(user_profile=user_profile,
|
||||
recipient=recipient, active=True)
|
||||
|
||||
if not subscription.exists():
|
||||
raise JsonableError("Not subscribed to stream %s" % (stream_name,))
|
||||
|
||||
return subscription
|
||||
|
||||
@authenticated_json_view
|
||||
@has_request_variables
|
||||
def json_subscription_property(request, user_profile, subscription_data=REQ(
|
||||
validator=check_list(
|
||||
check_dict([["stream", check_string],
|
||||
["property", check_string],
|
||||
["value", check_variable_type(
|
||||
[check_string, check_bool])]])))):
|
||||
"""
|
||||
This is the entry point to changing subscription properties. This
|
||||
is a bulk endpoint: requestors always provide a subscription_data
|
||||
list containing dictionaries for each stream of interest.
|
||||
|
||||
Requests are of the form:
|
||||
|
||||
[{"stream": "devel", "property": "in_home_view", "value": False},
|
||||
{"stream": "devel", "property": "color", "value": "#c2c2c2"}]
|
||||
"""
|
||||
if request.method != "POST":
|
||||
return json_error("Invalid verb")
|
||||
|
||||
property_converters = {"color": check_string, "in_home_view": check_bool,
|
||||
"desktop_notifications": check_bool,
|
||||
"audible_notifications": check_bool}
|
||||
response_data = []
|
||||
|
||||
for change in subscription_data:
|
||||
stream_name = change["stream"]
|
||||
property = change["property"]
|
||||
value = change["value"]
|
||||
|
||||
if property not in property_converters:
|
||||
return json_error("Unknown subscription property: %s" % (property,))
|
||||
|
||||
sub = get_subscription_or_die(stream_name, user_profile)[0]
|
||||
|
||||
property_conversion = property_converters[property](property, value)
|
||||
if property_conversion:
|
||||
return json_error(property_conversion)
|
||||
|
||||
do_change_subscription_property(user_profile, sub, stream_name,
|
||||
property, value)
|
||||
|
||||
response_data.append({'stream': stream_name,
|
||||
'property': property,
|
||||
'value': value})
|
||||
|
||||
return json_success({"subscription_data": response_data})
|
|
@ -106,21 +106,21 @@ urlpatterns += patterns('zerver.views',
|
|||
# These are json format views used by the web client. They require a logged in browser.
|
||||
url(r'^json/update_pointer$', 'json_update_pointer'),
|
||||
url(r'^json/get_old_messages$', 'messages.json_get_old_messages'),
|
||||
url(r'^json/get_public_streams$', 'json_get_public_streams'),
|
||||
url(r'^json/rename_stream$', 'json_rename_stream'),
|
||||
url(r'^json/make_stream_public$', 'json_make_stream_public'),
|
||||
url(r'^json/make_stream_private$', 'json_make_stream_private'),
|
||||
url(r'^json/get_public_streams$', 'streams.json_get_public_streams'),
|
||||
url(r'^json/rename_stream$', 'streams.json_rename_stream'),
|
||||
url(r'^json/make_stream_public$', 'streams.json_make_stream_public'),
|
||||
url(r'^json/make_stream_private$', 'streams.json_make_stream_private'),
|
||||
url(r'^json/send_message$', 'messages.json_send_message'),
|
||||
url(r'^json/invite_users$', 'json_invite_users'),
|
||||
url(r'^json/bulk_invite_users$', 'json_bulk_invite_users'),
|
||||
url(r'^json/settings/change$', 'json_change_settings'),
|
||||
url(r'^json/notify_settings/change$', 'json_change_notify_settings'),
|
||||
url(r'^json/ui_settings/change$', 'json_change_ui_settings'),
|
||||
url(r'^json/subscriptions/remove$', 'json_remove_subscriptions'),
|
||||
url(r'^json/subscriptions/add$', 'json_add_subscriptions'),
|
||||
url(r'^json/subscriptions/exists$', 'json_stream_exists'),
|
||||
url(r'^json/subscriptions/property$', 'json_subscription_property'),
|
||||
url(r'^json/get_subscribers$', 'json_get_subscribers'),
|
||||
url(r'^json/subscriptions/remove$', 'streams.json_remove_subscriptions'),
|
||||
url(r'^json/subscriptions/add$', 'streams.json_add_subscriptions'),
|
||||
url(r'^json/subscriptions/exists$', 'streams.json_stream_exists'),
|
||||
url(r'^json/subscriptions/property$', 'streams.json_subscription_property'),
|
||||
url(r'^json/get_subscribers$', 'streams.json_get_subscribers'),
|
||||
url(r'^json/fetch_api_key$', 'json_fetch_api_key'),
|
||||
url(r'^json/update_active_status$', 'json_update_active_status'),
|
||||
url(r'^json/get_active_statuses$', 'json_get_active_statuses'),
|
||||
|
@ -176,16 +176,6 @@ urlpatterns += patterns('zerver.views',
|
|||
v1_api_and_json_patterns = patterns('zerver.views',
|
||||
url(r'^export$', 'rest_dispatch',
|
||||
{'GET': 'export'}),
|
||||
url(r'^streams$', 'rest_dispatch',
|
||||
{'GET': 'get_streams_backend'}),
|
||||
# GET returns "stream info" (undefined currently?), HEAD returns whether stream exists (200 or 404)
|
||||
url(r'^streams/(?P<stream_name>.*)/members$', 'rest_dispatch',
|
||||
{'GET': 'get_subscribers_backend'}),
|
||||
url(r'^streams/(?P<stream_name>.*)$', 'rest_dispatch',
|
||||
{'HEAD': 'stream_exists_backend',
|
||||
'GET': 'stream_exists_backend',
|
||||
'PATCH': 'update_stream_backend',
|
||||
'DELETE': 'deactivate_stream_backend'}),
|
||||
url(r'^users$', 'rest_dispatch',
|
||||
{'GET': 'get_members_backend',
|
||||
'POST': 'create_user_backend'}),
|
||||
|
@ -196,19 +186,11 @@ v1_api_and_json_patterns = patterns('zerver.views',
|
|||
url(r'^users/me/pointer$', 'rest_dispatch',
|
||||
{'GET': 'get_pointer_backend',
|
||||
'PUT': 'update_pointer_backend'}),
|
||||
# GET lists your streams, POST bulk adds, PATCH bulk modifies/removes
|
||||
url(r'^users/me/subscriptions$', 'rest_dispatch',
|
||||
{'GET': 'list_subscriptions_backend',
|
||||
'POST': 'add_subscriptions_backend',
|
||||
'PATCH': 'update_subscriptions_backend'}),
|
||||
url(r'^users/me/alert_words$', 'rest_dispatch',
|
||||
{'GET': 'list_alert_words',
|
||||
'PUT': 'set_alert_words',
|
||||
'PATCH': 'add_alert_words',
|
||||
'DELETE': 'remove_alert_words'}),
|
||||
url(r'^default_streams$', 'rest_dispatch',
|
||||
{'PATCH': 'add_default_stream',
|
||||
'DELETE': 'remove_default_stream'}),
|
||||
url(r'^realm$', 'rest_dispatch',
|
||||
{'PATCH': 'update_realm'}),
|
||||
url(r'^users/me/api_key/regenerate$', 'rest_dispatch',
|
||||
|
@ -225,7 +207,7 @@ v1_api_and_json_patterns = patterns('zerver.views',
|
|||
'DELETE': 'remove_android_reg_id'}),
|
||||
url(r'^users/(?P<email>.*)/reactivate$', 'rest_dispatch',
|
||||
{'POST': 'reactivate_user_backend'}),
|
||||
url(r'^users/(?P<email>.*)$', 'rest_dispatch',
|
||||
url(r'^users/(?P<email>[^/]*)$', 'rest_dispatch',
|
||||
{'PATCH': 'update_user_backend',
|
||||
'DELETE': 'deactivate_user_backend'}),
|
||||
url(r'^bots$', 'rest_dispatch',
|
||||
|
@ -253,6 +235,26 @@ v1_api_and_json_patterns = patterns('zerver.views',
|
|||
url(r'^messages/flags$', 'rest_dispatch',
|
||||
{'POST': 'update_message_flags'}),
|
||||
|
||||
) + patterns('zerver.views.streams',
|
||||
url(r'^streams$', 'rest_dispatch',
|
||||
{'GET': 'get_streams_backend'}),
|
||||
# GET returns "stream info" (undefined currently?), HEAD returns whether stream exists (200 or 404)
|
||||
url(r'^streams/(?P<stream_name>.*)/members$', 'rest_dispatch',
|
||||
{'GET': 'get_subscribers_backend'}),
|
||||
url(r'^streams/(?P<stream_name>.*)$', 'rest_dispatch',
|
||||
{'HEAD': 'stream_exists_backend',
|
||||
'GET': 'stream_exists_backend',
|
||||
'PATCH': 'update_stream_backend',
|
||||
'DELETE': 'deactivate_stream_backend'}),
|
||||
url(r'^default_streams$', 'rest_dispatch',
|
||||
{'PATCH': 'add_default_stream',
|
||||
'DELETE': 'remove_default_stream'}),
|
||||
# GET lists your streams, POST bulk adds, PATCH bulk modifies/removes
|
||||
url(r'^users/me/subscriptions$', 'rest_dispatch',
|
||||
{'GET': 'list_subscriptions_backend',
|
||||
'POST': 'add_subscriptions_backend',
|
||||
'PATCH': 'update_subscriptions_backend'}),
|
||||
|
||||
) + patterns('zerver.tornadoviews',
|
||||
url(r'^events$', 'rest_dispatch',
|
||||
{'GET': 'get_events_backend',
|
||||
|
|
Loading…
Reference in New Issue