mirror of
https://github.com/zulip/zulip.git
synced 2025-11-04 22:13:26 +00:00
Annotate zerver/views/__init__.py.
Also fix typing errors in a few related files. [with tweaks from tabbott]
This commit is contained in:
committed by
Tim Abbott
parent
c9bb93b0d2
commit
a261a6bbac
@@ -2842,7 +2842,7 @@ def apply_events(state, events, user_profile):
|
||||
def do_events_register(user_profile, user_client, apply_markdown=True,
|
||||
event_types=None, queue_lifespan_secs=0, all_public_streams=False,
|
||||
narrow=[]):
|
||||
# type: (UserProfile, Client, bool, Optional[Iterable[str]], int, bool, Iterable[Sequence[str]]) -> Dict[str, Any]
|
||||
# type: (UserProfile, Client, bool, Optional[Iterable[str]], int, bool, Iterable[Sequence[text_type]]) -> Dict[str, Any]
|
||||
# Technically we don't need to check this here because
|
||||
# build_narrow_filter will check it, but it's nicer from an error
|
||||
# handling perspective to do it before contacting Tornado
|
||||
@@ -3113,7 +3113,7 @@ def do_set_alert_words(user_profile, alert_words):
|
||||
notify_alert_words(user_profile, alert_words)
|
||||
|
||||
def do_set_muted_topics(user_profile, muted_topics):
|
||||
# type: (UserProfile, List[Union[List[text_type], Tuple[text_type, text_type]]]) -> None
|
||||
# type: (UserProfile, Union[List[List[text_type]], List[Tuple[text_type, text_type]]]) -> None
|
||||
user_profile.muted_topics = ujson.dumps(muted_topics)
|
||||
user_profile.save(update_fields=['muted_topics'])
|
||||
event = dict(type="muted_topics", muted_topics=muted_topics)
|
||||
|
||||
@@ -383,7 +383,7 @@ class EventsRegisterTest(AuthedTestCase):
|
||||
('type', equals('muted_topics')),
|
||||
('muted_topics', check_list(check_list(check_string, 2))),
|
||||
])
|
||||
events = self.do_test(lambda: do_set_muted_topics(self.user_profile, [["Denmark", "topic"]]))
|
||||
events = self.do_test(lambda: do_set_muted_topics(self.user_profile, [[u"Denmark", u"topic"]]))
|
||||
error = muted_topics_checker('events[0]', events[0])
|
||||
self.assert_on_error(error)
|
||||
|
||||
@@ -958,22 +958,22 @@ class TestEventsRegisterNarrowDefaults(TestCase):
|
||||
# type: () -> None
|
||||
self.user_profile.default_events_register_stream_id = None
|
||||
self.user_profile.save()
|
||||
result = _default_narrow(self.user_profile, [('stream', 'my_stream')])
|
||||
self.assertEqual(result, [('stream', 'my_stream')])
|
||||
result = _default_narrow(self.user_profile, [[u'stream', u'my_stream']])
|
||||
self.assertEqual(result, [[u'stream', u'my_stream']])
|
||||
|
||||
def test_use_passed_narrow_with_default(self):
|
||||
# type: () -> None
|
||||
self.user_profile.default_events_register_stream_id = self.stream.id
|
||||
self.user_profile.save()
|
||||
result = _default_narrow(self.user_profile, [('stream', 'my_stream')])
|
||||
self.assertEqual(result, [('stream', 'my_stream')])
|
||||
result = _default_narrow(self.user_profile, [[u'stream', u'my_stream']])
|
||||
self.assertEqual(result, [[u'stream', u'my_stream']])
|
||||
|
||||
def test_use_default_if_narrow_is_empty(self):
|
||||
# type: () -> None
|
||||
self.user_profile.default_events_register_stream_id = self.stream.id
|
||||
self.user_profile.save()
|
||||
result = _default_narrow(self.user_profile, [])
|
||||
self.assertEqual(result, [('stream', 'Verona')])
|
||||
self.assertEqual(result, [[u'stream', u'Verona']])
|
||||
|
||||
def test_use_narrow_if_default_is_none(self):
|
||||
# type: () -> None
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
from __future__ import absolute_import
|
||||
from typing import Any
|
||||
from typing import Any, List, Dict, Optional, Callable, Tuple
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import authenticate, login, get_backends
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.http import HttpResponseRedirect, HttpResponseForbidden, HttpResponse
|
||||
from django.http import HttpResponseRedirect, HttpResponseForbidden, HttpResponse, HttpRequest
|
||||
from django.shortcuts import redirect
|
||||
from django.template import RequestContext, loader
|
||||
from django.utils.timezone import now
|
||||
@@ -61,6 +61,7 @@ import datetime
|
||||
import ujson
|
||||
import simplejson
|
||||
import re
|
||||
from six import text_type
|
||||
from six.moves import urllib
|
||||
import base64
|
||||
import time
|
||||
@@ -74,10 +75,12 @@ 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 name_changes_disabled(realm):
|
||||
# type: (Realm) -> bool
|
||||
return settings.NAME_CHANGES_DISABLED or realm.name_changes_disabled
|
||||
|
||||
@require_post
|
||||
def accounts_register(request):
|
||||
# type: (HttpRequest) -> HttpResponse
|
||||
key = request.POST['key']
|
||||
confirmation = Confirmation.objects.get(confirmation_key=key)
|
||||
prereg_user = confirmation.content_object
|
||||
@@ -267,6 +270,7 @@ from zerver.lib.ccache import make_ccache
|
||||
@has_request_variables
|
||||
def webathena_kerberos_login(request, user_profile,
|
||||
cred=REQ(default=None)):
|
||||
# type (HttpRequest, UserProfile, str) -> HttpResponse
|
||||
if cred is None:
|
||||
return json_error(_("Could not find Kerberos credential"))
|
||||
if not user_profile.realm.domain == "mit.edu":
|
||||
@@ -298,6 +302,7 @@ def webathena_kerberos_login(request, user_profile,
|
||||
return json_success()
|
||||
|
||||
def api_endpoint_docs(request):
|
||||
# type: (HttpRequest) -> HttpResponse
|
||||
raw_calls = open('templates/zerver/api_content.json', 'r').read()
|
||||
calls = ujson.loads(raw_calls)
|
||||
langs = set()
|
||||
@@ -323,11 +328,12 @@ def api_endpoint_docs(request):
|
||||
|
||||
@authenticated_json_post_view
|
||||
@has_request_variables
|
||||
def json_invite_users(request, user_profile, invitee_emails=REQ()):
|
||||
if not invitee_emails:
|
||||
def json_invite_users(request, user_profile, invitee_emails_raw=REQ("invitee_emails")):
|
||||
# type: (HttpRequest, UserProfile, str) -> HttpResponse
|
||||
if not invitee_emails_raw:
|
||||
return json_error(_("You must specify at least one email address."))
|
||||
|
||||
invitee_emails = set(re.split(r'[, \n]', invitee_emails))
|
||||
invitee_emails = set(re.split(r'[, \n]', invitee_emails_raw))
|
||||
|
||||
stream_names = request.POST.getlist('stream')
|
||||
if not stream_names:
|
||||
@@ -354,6 +360,7 @@ def json_invite_users(request, user_profile, invitee_emails=REQ()):
|
||||
return json_success()
|
||||
|
||||
def create_homepage_form(request, user_info=None):
|
||||
# type: (HttpRequest, Optional[Dict[str, Any]]) -> HomepageForm
|
||||
if user_info:
|
||||
return HomepageForm(user_info, domain=request.session.get("domain"))
|
||||
# An empty fields dict is not treated the same way as not
|
||||
@@ -361,6 +368,7 @@ def create_homepage_form(request, user_info=None):
|
||||
return HomepageForm(domain=request.session.get("domain"))
|
||||
|
||||
def maybe_send_to_registration(request, email, full_name=''):
|
||||
# type: (HttpRequest, text_type, text_type) -> HttpResponse
|
||||
form = create_homepage_form(request, user_info={'email': email})
|
||||
request.verified_email = None
|
||||
if form.is_valid():
|
||||
@@ -390,6 +398,7 @@ def maybe_send_to_registration(request, email, full_name=''):
|
||||
request=request)
|
||||
|
||||
def login_or_register_remote_user(request, remote_username, user_profile, full_name=''):
|
||||
# type: (HttpRequest, str, UserProfile, text_type) -> HttpResponse
|
||||
if user_profile is None or user_profile.is_mirror_dummy:
|
||||
# Since execution has reached here, the client specified a remote user
|
||||
# but no associated user account exists. Send them over to the
|
||||
@@ -401,6 +410,7 @@ def login_or_register_remote_user(request, remote_username, user_profile, full_n
|
||||
request.get_host()))
|
||||
|
||||
def remote_user_sso(request):
|
||||
# type: (HttpRequest) -> HttpResponse
|
||||
try:
|
||||
remote_user = request.META["REMOTE_USER"]
|
||||
except KeyError:
|
||||
@@ -411,6 +421,7 @@ def remote_user_sso(request):
|
||||
|
||||
@csrf_exempt
|
||||
def remote_user_jwt(request):
|
||||
# type: (HttpRequest) -> HttpResponse
|
||||
try:
|
||||
json_web_token = request.POST["json_web_token"]
|
||||
payload, signing_input, header, signature = jwt.load(json_web_token)
|
||||
@@ -445,9 +456,11 @@ def remote_user_jwt(request):
|
||||
return login_or_register_remote_user(request, email, user_profile, remote_user)
|
||||
|
||||
def google_oauth2_csrf(request, value):
|
||||
# type: (HttpRequest, str) -> HttpResponse
|
||||
return hmac.new(get_token(request).encode('utf-8'), value, hashlib.sha256).hexdigest()
|
||||
|
||||
def start_google_oauth2(request):
|
||||
# type: (HttpRequest) -> HttpResponse
|
||||
uri = 'https://accounts.google.com/o/oauth2/auth?'
|
||||
cur_time = str(int(time.time()))
|
||||
csrf_state = '{}:{}'.format(
|
||||
@@ -471,12 +484,14 @@ def start_google_oauth2(request):
|
||||
# from a property to a function
|
||||
requests_json_is_function = callable(requests.Response.json)
|
||||
def extract_json_response(resp):
|
||||
# type: (HttpResponse) -> Dict[str, Any]
|
||||
if requests_json_is_function:
|
||||
return resp.json()
|
||||
else:
|
||||
return resp.json
|
||||
|
||||
def finish_google_oauth2(request):
|
||||
# type: (HttpRequest) -> HttpResponse
|
||||
error = request.GET.get('error')
|
||||
if error == 'access_denied':
|
||||
return redirect('/')
|
||||
@@ -538,6 +553,7 @@ def finish_google_oauth2(request):
|
||||
return login_or_register_remote_user(request, email_address, user_profile, full_name)
|
||||
|
||||
def login_page(request, **kwargs):
|
||||
# type: (HttpRequest, **Any) -> HttpResponse
|
||||
extra_context = kwargs.pop('extra_context', {})
|
||||
if dev_auth_enabled():
|
||||
# Development environments usually have only a few users, but
|
||||
@@ -559,6 +575,7 @@ def login_page(request, **kwargs):
|
||||
return template_response
|
||||
|
||||
def dev_direct_login(request, **kwargs):
|
||||
# type: (HttpRequest, **Any) -> HttpResponse
|
||||
# This function allows logging in without a password and should only be called in development environments.
|
||||
# It may be called if the DevAuthBackend is included in settings.AUTHENTICATION_BACKENDS
|
||||
if (not dev_auth_enabled()) or settings.PRODUCTION:
|
||||
@@ -575,8 +592,10 @@ def dev_direct_login(request, **kwargs):
|
||||
@authenticated_json_post_view
|
||||
@has_request_variables
|
||||
def json_bulk_invite_users(request, user_profile,
|
||||
invitee_emails=REQ(validator=check_list(check_string))):
|
||||
invitee_emails = set(invitee_emails)
|
||||
invitee_emails_list=REQ('invitee_emails',
|
||||
validator=check_list(check_string))):
|
||||
# type: (HttpRequest, UserProfile, List[str]) -> HttpResponse
|
||||
invitee_emails = set(invitee_emails_list)
|
||||
streams = get_default_subs(user_profile)
|
||||
|
||||
ret_error, error_data = do_invite_users(user_profile, invitee_emails, streams)
|
||||
@@ -594,6 +613,7 @@ def json_bulk_invite_users(request, user_profile,
|
||||
|
||||
@zulip_login_required
|
||||
def initial_invite_page(request):
|
||||
# type: (HttpRequest) -> HttpResponse
|
||||
user = request.user
|
||||
# Only show the bulk-invite page for the first user in a realm
|
||||
domain_count = len(UserProfile.objects.filter(realm=user.realm))
|
||||
@@ -610,9 +630,11 @@ def initial_invite_page(request):
|
||||
|
||||
@require_post
|
||||
def logout_then_login(request, **kwargs):
|
||||
# type: (HttpRequest, **Any) -> HttpResponse
|
||||
return django_logout_then_login(request, kwargs)
|
||||
|
||||
def create_preregistration_user(email, request):
|
||||
# type: (text_type, HttpRequest) -> HttpResponse
|
||||
domain = request.session.get("domain")
|
||||
if completely_open(domain):
|
||||
# Clear the "domain" from the session object; it's no longer needed
|
||||
@@ -631,6 +653,7 @@ def create_preregistration_user(email, request):
|
||||
return PreregistrationUser.objects.create(email=email)
|
||||
|
||||
def accounts_home_with_domain(request, domain):
|
||||
# type: (HttpRequest, str) -> HttpResponse
|
||||
if completely_open(domain):
|
||||
# You can sign up for a completely open realm through a
|
||||
# special registration path that contains the domain in the
|
||||
@@ -643,6 +666,7 @@ def accounts_home_with_domain(request, domain):
|
||||
return HttpResponseRedirect(reverse('zerver.views.accounts_home'))
|
||||
|
||||
def send_registration_completion_email(email, request):
|
||||
# type: (str, HttpRequest) -> HttpResponse
|
||||
"""
|
||||
Send an email with a confirmation link to the provided e-mail so the user
|
||||
can complete their registration.
|
||||
@@ -654,6 +678,7 @@ def send_registration_completion_email(email, request):
|
||||
additional_context=context)
|
||||
|
||||
def accounts_home(request):
|
||||
# type: (HttpRequest) -> HttpResponse
|
||||
if request.method == 'POST':
|
||||
form = create_homepage_form(request, user_info=request.POST)
|
||||
if form.is_valid():
|
||||
@@ -673,6 +698,7 @@ def accounts_home(request):
|
||||
request=request)
|
||||
|
||||
def approximate_unread_count(user_profile):
|
||||
# type: (UserProfile) -> int
|
||||
not_in_home_view_recipients = [sub.recipient.id for sub in \
|
||||
Subscription.objects.filter(
|
||||
user_profile=user_profile, in_home_view=False)]
|
||||
@@ -692,6 +718,7 @@ def approximate_unread_count(user_profile):
|
||||
flags=UserMessage.flags.read).count()
|
||||
|
||||
def sent_time_in_epoch_seconds(user_message):
|
||||
# type: (UserMessage) -> float
|
||||
# user_message is a UserMessage object.
|
||||
if not user_message:
|
||||
return None
|
||||
@@ -701,6 +728,7 @@ def sent_time_in_epoch_seconds(user_message):
|
||||
|
||||
@zulip_login_required
|
||||
def home(request):
|
||||
# type: (HttpRequest) -> HttpResponse
|
||||
# We need to modify the session object every two weeks or it will expire.
|
||||
# This line makes reloading the page a sufficient action to keep the
|
||||
# session alive.
|
||||
@@ -892,9 +920,11 @@ def home(request):
|
||||
|
||||
@zulip_login_required
|
||||
def desktop_home(request):
|
||||
# type: (HttpRequest) -> HttpResponse
|
||||
return HttpResponseRedirect(reverse('zerver.views.home'))
|
||||
|
||||
def is_buggy_ua(agent):
|
||||
# type: (str) -> bool
|
||||
"""Discrimiate CSS served to clients based on User Agent
|
||||
|
||||
Due to QTBUG-3467, @font-face is not supported in QtWebKit.
|
||||
@@ -905,11 +935,13 @@ def is_buggy_ua(agent):
|
||||
"Mac" not in agent
|
||||
|
||||
def get_pointer_backend(request, user_profile):
|
||||
# type: (HttpRequest, UserProfile) -> HttpResponse
|
||||
return json_success({'pointer': user_profile.pointer})
|
||||
|
||||
@has_request_variables
|
||||
def update_pointer_backend(request, user_profile,
|
||||
pointer=REQ(converter=to_non_negative_int)):
|
||||
# type: (HttpRequest, UserProfile, int) -> HttpResponse
|
||||
if pointer <= user_profile.pointer:
|
||||
return json_success()
|
||||
|
||||
@@ -928,12 +960,14 @@ def update_pointer_backend(request, user_profile,
|
||||
return json_success()
|
||||
|
||||
def generate_client_id():
|
||||
# type: () -> str
|
||||
return generate_random_token(32)
|
||||
|
||||
# The order of creation of the various dictionaries are important.
|
||||
# We filter on {userprofile,stream,subscription_recipient}_ids.
|
||||
@require_realm_admin
|
||||
def export(request, user_profile):
|
||||
# type: (HttpRequest, UserProfile) -> HttpResponse
|
||||
if (Message.objects.filter(sender__realm=user_profile.realm).count() > 1000000 or
|
||||
UserMessage.objects.filter(user_profile__realm=user_profile.realm).count() > 3000000):
|
||||
return json_error(_("Realm has too much data for non-batched export."))
|
||||
@@ -1000,6 +1034,7 @@ def export(request, user_profile):
|
||||
return json_success(response)
|
||||
|
||||
def get_profile_backend(request, user_profile):
|
||||
# type: (HttpRequest, UserProfile) -> HttpResponse
|
||||
result = dict(pointer = user_profile.pointer,
|
||||
client_id = generate_client_id(),
|
||||
max_message_id = -1)
|
||||
@@ -1017,8 +1052,9 @@ def update_realm(request, user_profile, name=REQ(validator=check_string, default
|
||||
invite_required=REQ(validator=check_bool, default=None),
|
||||
invite_by_admins_only=REQ(validator=check_bool, default=None),
|
||||
create_stream_by_admins_only=REQ(validator=check_bool, default=None)):
|
||||
# type: (HttpRequest, UserProfile, Optional[str], Optional[bool], Optional[bool], Optional[bool], Optional[bool]) -> HttpResponse
|
||||
realm = user_profile.realm
|
||||
data = {}
|
||||
data = {} # type: Dict[str, Any]
|
||||
if name is not None and realm.name != name:
|
||||
do_set_realm_name(realm, name)
|
||||
data['name'] = 'updated'
|
||||
@@ -1039,6 +1075,7 @@ def update_realm(request, user_profile, name=REQ(validator=check_string, default
|
||||
@authenticated_json_post_view
|
||||
@has_request_variables
|
||||
def json_upload_file(request, user_profile):
|
||||
# type: (HttpRequest, UserProfile) -> HttpResponse
|
||||
if len(request.FILES) == 0:
|
||||
return json_error(_("You must specify a file to upload"))
|
||||
if len(request.FILES) != 1:
|
||||
@@ -1053,15 +1090,16 @@ def json_upload_file(request, user_profile):
|
||||
|
||||
@zulip_login_required
|
||||
@has_request_variables
|
||||
def get_uploaded_file(request, realm_id, filename,
|
||||
def get_uploaded_file(request, realm_id_str, filename,
|
||||
redir=REQ(validator=check_bool, default=True)):
|
||||
# type: (HttpRequest, str, str, bool) -> HttpResponse
|
||||
if settings.LOCAL_UPLOADS_DIR is not None:
|
||||
return HttpResponseForbidden() # Should have been served by nginx
|
||||
|
||||
user_profile = request.user
|
||||
url_path = "%s/%s" % (realm_id, filename)
|
||||
url_path = "%s/%s" % (realm_id_str, filename)
|
||||
|
||||
if realm_id == "unk":
|
||||
if realm_id_str == "unk":
|
||||
realm_id = get_realm_for_filename(url_path)
|
||||
if realm_id is None:
|
||||
# File does not exist
|
||||
@@ -1081,7 +1119,7 @@ def get_uploaded_file(request, realm_id, filename,
|
||||
@require_post
|
||||
@has_request_variables
|
||||
def api_fetch_api_key(request, username=REQ(), password=REQ()):
|
||||
# type: (Any, Any, Any) -> Any
|
||||
# type: (HttpRequest, str, str) -> HttpResponse
|
||||
return_data = {} # type: Dict[str, bool]
|
||||
if username == "google-oauth2-token":
|
||||
user_profile = authenticate(google_oauth2_token=password, return_data=return_data)
|
||||
@@ -1103,23 +1141,27 @@ def api_fetch_api_key(request, username=REQ(), password=REQ()):
|
||||
@authenticated_json_post_view
|
||||
@has_request_variables
|
||||
def json_fetch_api_key(request, user_profile, password=REQ(default='')):
|
||||
# type: (HttpRequest, UserProfile, str) -> HttpResponse
|
||||
if password_auth_enabled(user_profile.realm) and not user_profile.check_password(password):
|
||||
return json_error(_("Your username or password is incorrect."))
|
||||
return json_success({"api_key": user_profile.api_key})
|
||||
|
||||
@csrf_exempt
|
||||
def api_fetch_google_client_id(request):
|
||||
# type: (HttpRequest) -> HttpResponse
|
||||
if not settings.GOOGLE_CLIENT_ID:
|
||||
return json_error(_("GOOGLE_CLIENT_ID is not configured"), status=400)
|
||||
return json_success({"google_client_id": settings.GOOGLE_CLIENT_ID})
|
||||
|
||||
def get_status_list(requesting_user_profile):
|
||||
# type: (UserProfile) -> Dict[str, Any]
|
||||
return {'presences': get_status_dict(requesting_user_profile),
|
||||
'server_timestamp': time.time()}
|
||||
|
||||
@has_request_variables
|
||||
def update_active_status_backend(request, user_profile, status=REQ(),
|
||||
new_user_input=REQ(validator=check_bool, default=False)):
|
||||
# type: (HttpRequest, UserProfile, str, bool) -> HttpResponse
|
||||
status_val = UserPresence.status_from_string(status)
|
||||
if status_val is None:
|
||||
raise JsonableError(_("Invalid presence status: %s") % (status,))
|
||||
@@ -1144,6 +1186,7 @@ def update_active_status_backend(request, user_profile, status=REQ(),
|
||||
|
||||
@authenticated_json_post_view
|
||||
def json_get_active_statuses(request, user_profile):
|
||||
# type: (HttpRequest, UserProfile) -> HttpResponse
|
||||
return json_success(get_status_list(user_profile))
|
||||
|
||||
# Does not need to be authenticated because it's called from rest_dispatch
|
||||
@@ -1151,20 +1194,23 @@ def json_get_active_statuses(request, user_profile):
|
||||
def api_events_register(request, user_profile,
|
||||
apply_markdown=REQ(default=False, validator=check_bool),
|
||||
all_public_streams=REQ(default=None, validator=check_bool)):
|
||||
# type: (HttpRequest, UserProfile, bool, Optional[bool]) -> HttpResponse
|
||||
return events_register_backend(request, user_profile,
|
||||
apply_markdown=apply_markdown,
|
||||
all_public_streams=all_public_streams)
|
||||
|
||||
def _default_all_public_streams(user_profile, all_public_streams):
|
||||
# type: (UserProfile, Optional[bool]) -> bool
|
||||
if all_public_streams is not None:
|
||||
return all_public_streams
|
||||
else:
|
||||
return user_profile.default_all_public_streams
|
||||
|
||||
def _default_narrow(user_profile, narrow):
|
||||
# type: (UserProfile, List[List[text_type]]) -> List[List[text_type]]
|
||||
default_stream = user_profile.default_events_register_stream
|
||||
if not narrow and user_profile.default_events_register_stream is not None:
|
||||
narrow = [('stream', default_stream.name)]
|
||||
narrow = [['stream', default_stream.name]]
|
||||
return narrow
|
||||
|
||||
@has_request_variables
|
||||
@@ -1173,7 +1219,7 @@ def events_register_backend(request, user_profile, apply_markdown=True,
|
||||
event_types=REQ(validator=check_list(check_string), default=None),
|
||||
narrow=REQ(validator=check_list(check_list(check_string, length=2)), default=[]),
|
||||
queue_lifespan_secs=REQ(converter=int, default=0)):
|
||||
|
||||
# type: (HttpRequest, UserProfile, bool, Optional[bool], Optional[List[str]], List[List[text_type]], int) -> HttpResponse
|
||||
all_public_streams = _default_all_public_streams(user_profile, all_public_streams)
|
||||
narrow = _default_narrow(user_profile, narrow)
|
||||
|
||||
@@ -1186,6 +1232,7 @@ def events_register_backend(request, user_profile, apply_markdown=True,
|
||||
@authenticated_json_post_view
|
||||
@has_request_variables
|
||||
def json_refer_friend(request, user_profile, email=REQ()):
|
||||
# type: (HttpRequest, UserProfile, str) -> HttpResponse
|
||||
if not email:
|
||||
return json_error(_("No email address specified"))
|
||||
if user_profile.invites_granted - user_profile.invites_used <= 0:
|
||||
@@ -1199,20 +1246,22 @@ def json_refer_friend(request, user_profile, email=REQ()):
|
||||
@has_request_variables
|
||||
def json_set_muted_topics(request, user_profile,
|
||||
muted_topics=REQ(validator=check_list(check_list(check_string, length=2)), default=[])):
|
||||
# type: (HttpRequest, UserProfile, List[List[text_type]]) -> HttpResponse
|
||||
do_set_muted_topics(user_profile, muted_topics)
|
||||
return json_success()
|
||||
|
||||
def add_push_device_token(request, user_profile, token, kind, ios_app_id=None):
|
||||
if token == '' or len(token) > 4096:
|
||||
def add_push_device_token(request, user_profile, token_str, kind, ios_app_id=None):
|
||||
# type: (HttpRequest, UserProfile, str, int, Optional[str]) -> HttpResponse
|
||||
if token_str == '' or len(token_str) > 4096:
|
||||
return json_error(_('Empty or invalid length token'))
|
||||
|
||||
# If another user was previously logged in on the same device and didn't
|
||||
# properly log out, the token will still be registered to the wrong account
|
||||
PushDeviceToken.objects.filter(token=token).delete()
|
||||
PushDeviceToken.objects.filter(token=token_str).delete()
|
||||
|
||||
# Overwrite with the latest value
|
||||
token, created = PushDeviceToken.objects.get_or_create(user=user_profile,
|
||||
token=token,
|
||||
token=token_str,
|
||||
kind=kind,
|
||||
ios_app_id=ios_app_id)
|
||||
if not created:
|
||||
@@ -1223,18 +1272,21 @@ def add_push_device_token(request, user_profile, token, kind, ios_app_id=None):
|
||||
|
||||
@has_request_variables
|
||||
def add_apns_device_token(request, user_profile, token=REQ(), appid=REQ(default=settings.ZULIP_IOS_APP_ID)):
|
||||
# type: (HttpRequest, UserProfile, str, str) -> HttpResponse
|
||||
return add_push_device_token(request, user_profile, token, PushDeviceToken.APNS, ios_app_id=appid)
|
||||
|
||||
@has_request_variables
|
||||
def add_android_reg_id(request, user_profile, token=REQ()):
|
||||
return add_push_device_token(request, user_profile, token, PushDeviceToken.GCM)
|
||||
def add_android_reg_id(request, user_profile, token_str=REQ("token")):
|
||||
# type: (HttpRequest, UserProfile, str) -> HttpResponse
|
||||
return add_push_device_token(request, user_profile, token_str, PushDeviceToken.GCM)
|
||||
|
||||
def remove_push_device_token(request, user_profile, token, kind):
|
||||
if token == '' or len(token) > 4096:
|
||||
def remove_push_device_token(request, user_profile, token_str, kind):
|
||||
# type: (HttpRequest, UserProfile, str, int) -> HttpResponse
|
||||
if token_str == '' or len(token_str) > 4096:
|
||||
return json_error(_('Empty or invalid length token'))
|
||||
|
||||
try:
|
||||
token = PushDeviceToken.objects.get(token=token, kind=kind)
|
||||
token = PushDeviceToken.objects.get(token=token_str, kind=kind)
|
||||
token.delete()
|
||||
except PushDeviceToken.DoesNotExist:
|
||||
return json_error(_("Token does not exist"))
|
||||
@@ -1243,17 +1295,21 @@ def remove_push_device_token(request, user_profile, token, kind):
|
||||
|
||||
@has_request_variables
|
||||
def remove_apns_device_token(request, user_profile, token=REQ()):
|
||||
# type: (HttpRequest, UserProfile, str) -> HttpResponse
|
||||
return remove_push_device_token(request, user_profile, token, PushDeviceToken.APNS)
|
||||
|
||||
@has_request_variables
|
||||
def remove_android_reg_id(request, user_profile, token=REQ()):
|
||||
# type: (HttpRequest, UserProfile, str) -> HttpResponse
|
||||
return remove_push_device_token(request, user_profile, token, PushDeviceToken.GCM)
|
||||
|
||||
|
||||
def generate_204(request):
|
||||
# type: (HttpRequest) -> HttpResponse
|
||||
return HttpResponse(content=None, status=204)
|
||||
|
||||
def process_unsubscribe(token, type, unsubscribe_function):
|
||||
def process_unsubscribe(token, subscription_type, unsubscribe_function):
|
||||
# type: (HttpRequest, str, Callable[[UserProfile], None]) -> HttpResponse
|
||||
try:
|
||||
confirmation = Confirmation.objects.get(confirmation_key=token)
|
||||
except Confirmation.DoesNotExist:
|
||||
@@ -1262,19 +1318,22 @@ def process_unsubscribe(token, type, unsubscribe_function):
|
||||
user_profile = confirmation.content_object
|
||||
unsubscribe_function(user_profile)
|
||||
return render_to_response('zerver/unsubscribe_success.html',
|
||||
{"subscription_type": type,
|
||||
{"subscription_type": subscription_type,
|
||||
"external_host": settings.EXTERNAL_HOST})
|
||||
|
||||
# Email unsubscribe functions. All have the function signature
|
||||
# processor(user_profile).
|
||||
|
||||
def do_missedmessage_unsubscribe(user_profile):
|
||||
# type: (UserProfile) -> None
|
||||
do_change_enable_offline_email_notifications(user_profile, False)
|
||||
|
||||
def do_welcome_unsubscribe(user_profile):
|
||||
# type: (UserProfile) -> None
|
||||
clear_followup_emails_queue(user_profile.email)
|
||||
|
||||
def do_digest_unsubscribe(user_profile):
|
||||
# type: (UserProfile) -> None
|
||||
do_change_enable_digest_emails(user_profile, False)
|
||||
|
||||
# The keys are part of the URL for the unsubscribe link and must be valid
|
||||
@@ -1289,6 +1348,7 @@ email_unsubscribers = {
|
||||
|
||||
# Login NOT required. These are for one-click unsubscribes.
|
||||
def email_unsubscribe(request, type, token):
|
||||
# type: (HttpRequest, str, str) -> HttpResponse
|
||||
if type in email_unsubscribers:
|
||||
display_name, unsubscribe_function = email_unsubscribers[type]
|
||||
return process_unsubscribe(token, display_name, unsubscribe_function)
|
||||
|
||||
Reference in New Issue
Block a user