2020-06-11 00:54:34 +02:00
|
|
|
import logging
|
2020-09-05 04:02:13 +02:00
|
|
|
import secrets
|
2021-06-27 13:25:55 +02:00
|
|
|
from typing import List, Optional, Tuple
|
2017-01-07 21:19:52 +01:00
|
|
|
|
|
|
|
from django.conf import settings
|
2021-09-20 08:05:40 +02:00
|
|
|
from django.http import HttpRequest, HttpResponse
|
2017-03-16 14:14:31 +01:00
|
|
|
from django.shortcuts import redirect, render
|
2017-01-07 21:19:52 +01:00
|
|
|
from django.utils.cache import patch_cache_control
|
|
|
|
|
2023-04-24 16:51:02 +02:00
|
|
|
from zerver.actions.user_settings import do_change_tos_version, do_change_user_setting
|
2022-04-19 11:47:26 +02:00
|
|
|
from zerver.context_processors import get_realm_from_request, get_valid_realm_from_request
|
2021-10-03 14:16:07 +02:00
|
|
|
from zerver.decorator import web_public_view, zulip_login_required
|
2017-01-07 21:19:52 +01:00
|
|
|
from zerver.forms import ToSForm
|
2021-06-04 10:19:50 +02:00
|
|
|
from zerver.lib.compatibility import is_outdated_desktop_app, is_unsupported_browser
|
2021-06-14 12:38:43 +02:00
|
|
|
from zerver.lib.home import build_page_params_for_home_page_load, get_user_permission_info
|
2023-06-30 02:48:22 +02:00
|
|
|
from zerver.lib.narrow_helpers import NarrowTerm
|
2021-08-21 19:24:20 +02:00
|
|
|
from zerver.lib.request import RequestNotes
|
2017-01-30 03:11:00 +01:00
|
|
|
from zerver.lib.streams import access_stream_by_name
|
2017-10-19 07:21:57 +02:00
|
|
|
from zerver.lib.subdomains import get_subdomain
|
2022-04-14 23:28:32 +02:00
|
|
|
from zerver.lib.user_counts import realm_user_count
|
2023-04-24 16:51:02 +02:00
|
|
|
from zerver.models import PreregistrationUser, Realm, RealmUserDefault, Stream, UserProfile
|
2017-01-07 21:19:52 +01:00
|
|
|
|
|
|
|
|
2019-09-14 01:38:28 +02:00
|
|
|
def need_accept_tos(user_profile: Optional[UserProfile]) -> bool:
|
2020-09-27 06:49:16 +02:00
|
|
|
if user_profile is None:
|
2019-09-14 01:38:28 +02:00
|
|
|
return False
|
|
|
|
|
2023-04-24 16:51:02 +02:00
|
|
|
if user_profile.tos_version == UserProfile.TOS_VERSION_BEFORE_FIRST_LOGIN:
|
|
|
|
return True
|
|
|
|
|
2021-12-07 02:23:24 +01:00
|
|
|
if settings.TERMS_OF_SERVICE_VERSION is None:
|
2019-09-14 01:38:28 +02:00
|
|
|
return False
|
|
|
|
|
2021-12-07 02:23:24 +01:00
|
|
|
return int(settings.TERMS_OF_SERVICE_VERSION.split(".")[0]) > user_profile.major_tos_version()
|
2019-09-14 01:38:28 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2017-01-07 21:19:52 +01:00
|
|
|
@zulip_login_required
|
2017-11-27 09:28:57 +01:00
|
|
|
def accounts_accept_terms(request: HttpRequest) -> HttpResponse:
|
2021-07-24 20:37:35 +02:00
|
|
|
assert request.user.is_authenticated
|
|
|
|
|
2017-01-07 21:19:52 +01:00
|
|
|
if request.method == "POST":
|
|
|
|
form = ToSForm(request.POST)
|
|
|
|
if form.is_valid():
|
2023-04-24 16:51:02 +02:00
|
|
|
assert (
|
|
|
|
settings.TERMS_OF_SERVICE_VERSION is not None
|
|
|
|
or request.user.tos_version == UserProfile.TOS_VERSION_BEFORE_FIRST_LOGIN
|
|
|
|
)
|
2021-12-07 02:23:24 +01:00
|
|
|
do_change_tos_version(request.user, settings.TERMS_OF_SERVICE_VERSION)
|
2023-04-24 16:51:02 +02:00
|
|
|
|
|
|
|
email_address_visibility = form.cleaned_data["email_address_visibility"]
|
|
|
|
if (
|
|
|
|
email_address_visibility is not None
|
|
|
|
and email_address_visibility != request.user.email_address_visibility
|
|
|
|
):
|
|
|
|
do_change_user_setting(
|
|
|
|
request.user,
|
|
|
|
"email_address_visibility",
|
|
|
|
email_address_visibility,
|
|
|
|
acting_user=request.user,
|
|
|
|
)
|
2017-01-07 21:19:52 +01:00
|
|
|
return redirect(home)
|
|
|
|
else:
|
|
|
|
form = ToSForm()
|
|
|
|
|
2023-04-24 16:51:02 +02:00
|
|
|
default_email_address_visibility = None
|
|
|
|
first_time_login = request.user.tos_version == UserProfile.TOS_VERSION_BEFORE_FIRST_LOGIN
|
|
|
|
if first_time_login:
|
|
|
|
realm_user_default = RealmUserDefault.objects.get(realm=request.user.realm)
|
|
|
|
default_email_address_visibility = realm_user_default.email_address_visibility
|
|
|
|
|
2022-01-28 22:45:56 +01:00
|
|
|
context = {
|
|
|
|
"form": form,
|
|
|
|
"email": request.user.delivery_email,
|
|
|
|
# Text displayed when updating TERMS_OF_SERVICE_VERSION.
|
|
|
|
"terms_of_service_message": settings.TERMS_OF_SERVICE_MESSAGE,
|
2023-04-24 16:51:02 +02:00
|
|
|
"terms_of_service_version": settings.TERMS_OF_SERVICE_VERSION,
|
2022-01-28 22:45:56 +01:00
|
|
|
# HTML template used when agreeing to terms of service the
|
|
|
|
# first time, e.g. after data import.
|
|
|
|
"first_time_terms_of_service_message_template": None,
|
2023-04-24 16:51:02 +02:00
|
|
|
"first_time_login": first_time_login,
|
|
|
|
"default_email_address_visibility": default_email_address_visibility,
|
|
|
|
"email_address_visibility_options_dict": UserProfile.EMAIL_ADDRESS_VISIBILITY_ID_TO_NAME_MAP,
|
|
|
|
"email_address_visibility_admins_only": UserProfile.EMAIL_ADDRESS_VISIBILITY_ADMINS,
|
|
|
|
"email_address_visibility_moderators": UserProfile.EMAIL_ADDRESS_VISIBILITY_MODERATORS,
|
|
|
|
"email_address_visibility_nobody": UserProfile.EMAIL_ADDRESS_VISIBILITY_NOBODY,
|
2022-01-28 22:45:56 +01:00
|
|
|
}
|
|
|
|
|
2021-12-10 20:16:42 +01:00
|
|
|
if (
|
2023-05-08 09:17:57 +02:00
|
|
|
request.user.tos_version == UserProfile.TOS_VERSION_BEFORE_FIRST_LOGIN
|
2021-12-10 20:16:42 +01:00
|
|
|
and settings.FIRST_TIME_TERMS_OF_SERVICE_TEMPLATE is not None
|
|
|
|
):
|
2022-01-28 22:45:56 +01:00
|
|
|
context[
|
|
|
|
"first_time_terms_of_service_message_template"
|
|
|
|
] = settings.FIRST_TIME_TERMS_OF_SERVICE_TEMPLATE
|
|
|
|
|
2017-03-16 14:14:31 +01:00
|
|
|
return render(
|
|
|
|
request,
|
2021-02-12 08:20:45 +01:00
|
|
|
"zerver/accounts_accept_terms.html",
|
2022-01-28 22:45:56 +01:00
|
|
|
context,
|
2017-03-16 14:14:31 +01:00
|
|
|
)
|
2017-01-07 21:19:52 +01:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
|
|
|
def detect_narrowed_window(
|
|
|
|
request: HttpRequest, user_profile: Optional[UserProfile]
|
2023-06-30 00:50:04 +02:00
|
|
|
) -> Tuple[List[NarrowTerm], Optional[Stream], Optional[str]]:
|
2019-09-14 01:53:42 +02:00
|
|
|
"""This function implements Zulip's support for a mini Zulip window
|
|
|
|
that just handles messages from a single narrow"""
|
2020-09-27 06:49:16 +02:00
|
|
|
if user_profile is None:
|
2019-09-14 01:53:42 +02:00
|
|
|
return [], None, None
|
|
|
|
|
2023-06-30 00:50:04 +02:00
|
|
|
narrow: List[NarrowTerm] = []
|
2019-09-14 01:53:42 +02:00
|
|
|
narrow_stream = None
|
|
|
|
narrow_topic = request.GET.get("topic")
|
|
|
|
|
2022-05-31 01:32:29 +02:00
|
|
|
if "stream" in request.GET:
|
2019-09-14 01:53:42 +02:00
|
|
|
try:
|
2023-06-19 16:42:11 +02:00
|
|
|
# TODO: We should support stream IDs and direct messages here as well.
|
2019-09-14 01:53:42 +02:00
|
|
|
narrow_stream_name = request.GET.get("stream")
|
2022-05-31 01:32:29 +02:00
|
|
|
assert narrow_stream_name is not None
|
2021-02-12 08:19:30 +01:00
|
|
|
(narrow_stream, ignored_sub) = access_stream_by_name(user_profile, narrow_stream_name)
|
2023-06-30 00:50:04 +02:00
|
|
|
narrow = [NarrowTerm(operator="stream", operand=narrow_stream.name)]
|
2019-09-14 01:53:42 +02:00
|
|
|
except Exception:
|
|
|
|
logging.warning("Invalid narrow requested, ignoring", extra=dict(request=request))
|
|
|
|
if narrow_stream is not None and narrow_topic is not None:
|
2023-06-30 00:50:04 +02:00
|
|
|
narrow.append(NarrowTerm(operator="topic", operand=narrow_topic))
|
2019-09-14 01:53:42 +02:00
|
|
|
return narrow, narrow_stream, narrow_topic
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2019-09-14 01:56:29 +02:00
|
|
|
def update_last_reminder(user_profile: Optional[UserProfile]) -> None:
|
|
|
|
"""Reset our don't-spam-users-with-email counter since the
|
|
|
|
user has since logged in
|
|
|
|
"""
|
2020-09-27 06:49:16 +02:00
|
|
|
if user_profile is None:
|
2019-09-14 01:56:29 +02:00
|
|
|
return
|
|
|
|
|
|
|
|
if user_profile.last_reminder is not None: # nocoverage
|
|
|
|
# TODO: Look into the history of last_reminder; we may have
|
|
|
|
# eliminated that as a useful concept for non-bot users.
|
|
|
|
user_profile.last_reminder = None
|
|
|
|
user_profile.save(update_fields=["last_reminder"])
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2017-11-27 09:28:57 +01:00
|
|
|
def home(request: HttpRequest) -> HttpResponse:
|
2017-01-07 21:19:52 +01:00
|
|
|
subdomain = get_subdomain(request)
|
|
|
|
|
2021-10-03 14:16:07 +02:00
|
|
|
# If settings.ROOT_DOMAIN_LANDING_PAGE and this is the root
|
|
|
|
# domain, send the user the landing page.
|
2022-08-17 19:06:07 +02:00
|
|
|
if (
|
|
|
|
settings.ROOT_DOMAIN_LANDING_PAGE
|
|
|
|
and subdomain == Realm.SUBDOMAIN_FOR_ROOT_DOMAIN
|
|
|
|
and settings.CORPORATE_ENABLED
|
|
|
|
):
|
2022-08-17 19:24:10 +02:00
|
|
|
from corporate.views.portico import hello_view
|
|
|
|
|
2021-10-03 14:16:07 +02:00
|
|
|
return hello_view(request)
|
|
|
|
|
2022-04-19 11:47:26 +02:00
|
|
|
realm = get_realm_from_request(request)
|
|
|
|
if realm is None:
|
|
|
|
return render(request, "zerver/invalid_realm.html", status=404)
|
|
|
|
if realm.allow_web_public_streams_access():
|
2021-10-03 14:16:07 +02:00
|
|
|
return web_public_view(home_real)(request)
|
|
|
|
return zulip_login_required(home_real)(request)
|
2017-01-07 21:19:52 +01:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2017-11-27 09:28:57 +01:00
|
|
|
def home_real(request: HttpRequest) -> HttpResponse:
|
2020-03-25 02:00:28 +01:00
|
|
|
# Before we do any real work, check if the app is banned.
|
2022-05-12 06:54:12 +02:00
|
|
|
client_user_agent = request.headers.get("User-Agent", "")
|
2020-03-25 02:00:28 +01:00
|
|
|
(insecure_desktop_app, banned_desktop_app, auto_update_broken) = is_outdated_desktop_app(
|
2021-02-12 08:19:30 +01:00
|
|
|
client_user_agent
|
|
|
|
)
|
2020-03-25 02:00:28 +01:00
|
|
|
if banned_desktop_app:
|
|
|
|
return render(
|
|
|
|
request,
|
2021-02-12 08:20:45 +01:00
|
|
|
"zerver/insecure_desktop_app.html",
|
2020-03-25 02:00:28 +01:00
|
|
|
context={
|
|
|
|
"auto_update_broken": auto_update_broken,
|
python: Use trailing commas consistently.
Automatically generated by the following script, based on the output
of lint with flake8-comma:
import re
import sys
last_filename = None
last_row = None
lines = []
for msg in sys.stdin:
m = re.match(
r"\x1b\[35mflake8 \|\x1b\[0m \x1b\[1;31m(.+):(\d+):(\d+): (\w+)", msg
)
if m:
filename, row_str, col_str, err = m.groups()
row, col = int(row_str), int(col_str)
if filename == last_filename:
assert last_row != row
else:
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
with open(filename) as f:
lines = f.readlines()
last_filename = filename
last_row = row
line = lines[row - 1]
if err in ["C812", "C815"]:
lines[row - 1] = line[: col - 1] + "," + line[col - 1 :]
elif err in ["C819"]:
assert line[col - 2] == ","
lines[row - 1] = line[: col - 2] + line[col - 1 :].lstrip(" ")
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-10 05:23:40 +02:00
|
|
|
},
|
2020-03-25 02:00:28 +01:00
|
|
|
)
|
2020-04-20 14:00:03 +02:00
|
|
|
(unsupported_browser, browser_name) = is_unsupported_browser(client_user_agent)
|
|
|
|
if unsupported_browser:
|
|
|
|
return render(
|
|
|
|
request,
|
2021-02-12 08:20:45 +01:00
|
|
|
"zerver/unsupported_browser.html",
|
2020-04-20 14:00:03 +02:00
|
|
|
context={
|
|
|
|
"browser_name": browser_name,
|
python: Use trailing commas consistently.
Automatically generated by the following script, based on the output
of lint with flake8-comma:
import re
import sys
last_filename = None
last_row = None
lines = []
for msg in sys.stdin:
m = re.match(
r"\x1b\[35mflake8 \|\x1b\[0m \x1b\[1;31m(.+):(\d+):(\d+): (\w+)", msg
)
if m:
filename, row_str, col_str, err = m.groups()
row, col = int(row_str), int(col_str)
if filename == last_filename:
assert last_row != row
else:
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
with open(filename) as f:
lines = f.readlines()
last_filename = filename
last_row = row
line = lines[row - 1]
if err in ["C812", "C815"]:
lines[row - 1] = line[: col - 1] + "," + line[col - 1 :]
elif err in ["C819"]:
assert line[col - 2] == ","
lines[row - 1] = line[: col - 2] + line[col - 1 :].lstrip(" ")
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-10 05:23:40 +02:00
|
|
|
},
|
2020-04-20 14:00:03 +02:00
|
|
|
)
|
2020-03-25 02:00:28 +01:00
|
|
|
|
2017-01-07 21:19:52 +01:00
|
|
|
# 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.
|
|
|
|
request.session.modified = True
|
|
|
|
|
2019-09-14 01:38:28 +02:00
|
|
|
if request.user.is_authenticated:
|
|
|
|
user_profile = request.user
|
2020-10-02 00:00:28 +02:00
|
|
|
realm = user_profile.realm
|
2020-09-27 06:49:16 +02:00
|
|
|
else:
|
2020-10-02 00:00:28 +02:00
|
|
|
realm = get_valid_realm_from_request(request)
|
2022-04-19 11:47:26 +02:00
|
|
|
# We load the spectator experience. We fall through to the shared code
|
2020-10-07 07:10:02 +02:00
|
|
|
# for loading the application, with user_profile=None encoding
|
|
|
|
# that we're a spectator, not a logged-in user.
|
|
|
|
user_profile = None
|
|
|
|
|
2020-07-18 19:00:04 +02:00
|
|
|
update_last_reminder(user_profile)
|
|
|
|
|
2017-01-07 21:19:52 +01:00
|
|
|
# If a user hasn't signed the current Terms of Service, send them there
|
2019-09-14 01:38:28 +02:00
|
|
|
if need_accept_tos(user_profile):
|
2017-01-07 21:19:52 +01:00
|
|
|
return accounts_accept_terms(request)
|
|
|
|
|
2019-09-14 01:53:42 +02:00
|
|
|
narrow, narrow_stream, narrow_topic = detect_narrowed_window(request, user_profile)
|
2017-01-07 21:19:52 +01:00
|
|
|
|
2019-09-14 02:09:27 +02:00
|
|
|
if user_profile is not None:
|
|
|
|
first_in_realm = realm_user_count(user_profile.realm) == 1
|
|
|
|
# If you are the only person in the realm and you didn't invite
|
|
|
|
# anyone, we'll continue to encourage you to do so on the frontend.
|
|
|
|
prompt_for_invites = (
|
2021-02-12 08:19:30 +01:00
|
|
|
first_in_realm
|
|
|
|
and not PreregistrationUser.objects.filter(referred_by=user_profile).count()
|
2019-09-14 02:09:27 +02:00
|
|
|
)
|
|
|
|
needs_tutorial = user_profile.tutorial_status == UserProfile.TUTORIAL_WAITING
|
2020-02-19 21:59:26 +01:00
|
|
|
|
2020-09-27 06:49:16 +02:00
|
|
|
else:
|
2019-09-14 02:09:27 +02:00
|
|
|
first_in_realm = False
|
|
|
|
prompt_for_invites = False
|
|
|
|
# The current tutorial doesn't super make sense for logged-out users.
|
|
|
|
needs_tutorial = False
|
2017-01-07 21:19:52 +01:00
|
|
|
|
2020-07-18 20:33:28 +02:00
|
|
|
queue_id, page_params = build_page_params_for_home_page_load(
|
|
|
|
request=request,
|
|
|
|
user_profile=user_profile,
|
2020-10-02 00:00:28 +02:00
|
|
|
realm=realm,
|
2020-07-18 20:33:28 +02:00
|
|
|
insecure_desktop_app=insecure_desktop_app,
|
|
|
|
narrow=narrow,
|
|
|
|
narrow_stream=narrow_stream,
|
|
|
|
narrow_topic=narrow_topic,
|
|
|
|
first_in_realm=first_in_realm,
|
|
|
|
prompt_for_invites=prompt_for_invites,
|
|
|
|
needs_tutorial=needs_tutorial,
|
2017-01-07 21:19:52 +01:00
|
|
|
)
|
|
|
|
|
2021-08-21 19:24:20 +02:00
|
|
|
log_data = RequestNotes.get_notes(request).log_data
|
2021-07-09 10:06:04 +02:00
|
|
|
assert log_data is not None
|
2021-08-02 23:36:06 +02:00
|
|
|
log_data["extra"] = f"[{queue_id}]"
|
2018-05-03 11:08:50 +02:00
|
|
|
|
2020-09-05 04:02:13 +02:00
|
|
|
csp_nonce = secrets.token_hex(24)
|
2020-07-18 18:13:59 +02:00
|
|
|
|
|
|
|
user_permission_info = get_user_permission_info(user_profile)
|
2019-03-02 18:23:57 +01:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
response = render(
|
|
|
|
request,
|
2021-02-12 08:20:45 +01:00
|
|
|
"zerver/app/index.html",
|
2021-02-12 08:19:30 +01:00
|
|
|
context={
|
2021-02-12 08:20:45 +01:00
|
|
|
"user_profile": user_profile,
|
|
|
|
"page_params": page_params,
|
|
|
|
"csp_nonce": csp_nonce,
|
|
|
|
"color_scheme": user_permission_info.color_scheme,
|
2021-02-12 08:19:30 +01:00
|
|
|
},
|
|
|
|
)
|
2017-01-07 21:19:52 +01:00
|
|
|
patch_cache_control(response, no_cache=True, no_store=True, must_revalidate=True)
|
|
|
|
return response
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2017-01-07 21:19:52 +01:00
|
|
|
@zulip_login_required
|
2017-11-27 09:28:57 +01:00
|
|
|
def desktop_home(request: HttpRequest) -> HttpResponse:
|
2021-09-20 08:05:40 +02:00
|
|
|
return redirect(home)
|