From 7beff88f5ffe5a07c2039d96bf4e28ff6e0120ca Mon Sep 17 00:00:00 2001 From: Tim Abbott Date: Sat, 7 Jan 2017 12:19:52 -0800 Subject: [PATCH] views: Extract zerver/views/home.py. This is a significant piece of the remaining effort required to eliminate the catch-all zerver/views/__init__.py. --- templates/zerver/accounts_accept_terms.html | 2 +- zerver/views/__init__.py | 345 +------------------- zerver/views/home.py | 342 +++++++++++++++++++ zproject/urls.py | 7 +- 4 files changed, 357 insertions(+), 339 deletions(-) create mode 100644 zerver/views/home.py diff --git a/templates/zerver/accounts_accept_terms.html b/templates/zerver/accounts_accept_terms.html index 57fa131acc..cfef110562 100644 --- a/templates/zerver/accounts_accept_terms.html +++ b/templates/zerver/accounts_accept_terms.html @@ -16,7 +16,7 @@ the registration flow has its own (nearly identical) copy of the fields below in {% endif %} -
+ {{ csrf_input }}
diff --git a/zerver/views/__init__.py b/zerver/views/__init__.py index ac1f6c4951..4c5d30e16f 100644 --- a/zerver/views/__init__.py +++ b/zerver/views/__init__.py @@ -2,7 +2,6 @@ from __future__ import absolute_import from typing import Any, List, Dict, Optional, Text -from django.utils import translation from django.utils.translation import ugettext as _ from django.conf import settings from django.contrib.auth import authenticate, login, get_backends @@ -11,27 +10,18 @@ from django.http import HttpResponseRedirect, HttpResponseForbidden, HttpRespons from django.shortcuts import redirect from django.template import RequestContext, loader from django.utils.timezone import now -from django.utils.cache import patch_cache_control from django.core.exceptions import ValidationError from django.core import validators from django.core.mail import send_mail -from zerver.models import Message, UserProfile, Stream, Subscription, Huddle, \ - Recipient, Realm, UserMessage, DefaultStream, RealmEmoji, RealmAlias, \ - RealmFilter, \ - PreregistrationUser, get_client, UserActivity, \ - get_stream, UserPresence, get_recipient, name_changes_disabled, email_to_username, \ +from zerver.models import UserProfile, Realm, PreregistrationUser, \ + name_changes_disabled, email_to_username, \ completely_open, get_unique_open_realm, email_allowed_for_realm, \ - get_realm, get_realm_by_email_domain, list_of_domains_for_realm + get_realm, get_realm_by_email_domain from zerver.lib.actions import do_change_password, do_change_full_name, do_change_is_admin, \ do_activate_user, do_create_user, do_create_realm, set_default_streams, \ - update_user_presence, do_events_register, \ - do_change_tos_version, \ - user_email_is_unique, \ - compute_mit_user_fullname, do_set_muted_topics, \ - get_cross_realm_dicts, \ - do_update_pointer, realm_user_count -from zerver.lib.push_notifications import num_push_devices_for_user -from zerver.forms import RegistrationForm, HomepageForm, RealmCreationForm, ToSForm, \ + do_events_register, user_email_is_unique, \ + compute_mit_user_fullname, do_set_muted_topics +from zerver.forms import RegistrationForm, HomepageForm, RealmCreationForm, \ CreateUserForm, FindMyTeamForm from zerver.lib.actions import is_inactive from django_auth_ldap.backend import LDAPBackend, _LDAPUser @@ -40,27 +30,17 @@ from zerver.decorator import require_post, authenticated_json_post_view, \ has_request_variables, \ JsonableError, get_user_profile_by_email, REQ, \ zulip_login_required -from zerver.lib.avatar import avatar_url -from zerver.lib.i18n import get_language_list, get_language_name, \ - get_language_list_for_templates from zerver.lib.response import json_success, json_error -from zerver.lib.utils import statsd, get_subdomain -from version import ZULIP_VERSION +from zerver.lib.utils import get_subdomain from zproject.backends import password_auth_enabled from confirmation.models import Confirmation, RealmCreationKey, check_key_is_valid +import logging import requests import ujson -import calendar -import datetime -import simplejson -import re -from six.moves import urllib, zip_longest, zip, range -import time -import logging - +from six.moves import urllib from zproject.jinja2 import render_to_response def redirect_and_log_into_subdomain(realm, full_name, email_address): @@ -257,7 +237,7 @@ def accounts_register(request): realm.subdomain, user_profile.email,)) return redirect('/') login(request, auth_result) - return HttpResponseRedirect(realm.uri + reverse('zerver.views.home')) + return HttpResponseRedirect(realm.uri + reverse('zerver.views.home.home')) return render_to_response( 'zerver/register.html', @@ -273,28 +253,6 @@ def accounts_register(request): 'realms_have_subdomains': settings.REALMS_HAVE_SUBDOMAINS, 'password_auth_enabled': password_auth_enabled(realm), }, request=request) -@zulip_login_required -def accounts_accept_terms(request): - # type: (HttpRequest) -> HttpResponse - if request.method == "POST": - form = ToSForm(request.POST) - if form.is_valid(): - do_change_tos_version(request.user, settings.TOS_VERSION) - return redirect(home) - else: - form = ToSForm() - - email = request.user.email - special_message_template = None - if request.user.tos_version is None and settings.FIRST_TIME_TOS_TEMPLATE is not None: - special_message_template = 'zerver/' + settings.FIRST_TIME_TOS_TEMPLATE - return render_to_response( - 'zerver/accounts_accept_terms.html', - {'form': form, - 'email': email, - 'special_message_template': special_message_template}, - request=request) - def create_preregistration_user(email, request, realm_creation=False): # type: (Text, HttpRequest, bool) -> HttpResponse realm_str = request.session.pop('realm_str', None) @@ -408,289 +366,6 @@ def accounts_home(request): {'form': form, 'current_url': request.get_full_path}, 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)] - - # TODO: We may want to exclude muted messages from this count. - # It was attempted in the past, but the original attempt - # was broken. When we re-architect muting, we may - # want to to revisit this (see git issue #1019). - return UserMessage.objects.filter( - user_profile=user_profile, message_id__gt=user_profile.pointer).exclude( - message__recipient__type=Recipient.STREAM, - message__recipient__id__in=not_in_home_view_recipients).exclude( - 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 - # We have USE_TZ = True, so our datetime objects are timezone-aware. - # Return the epoch seconds in UTC. - return calendar.timegm(user_message.message.pub_date.utctimetuple()) - -def home(request): - # type: (HttpRequest) -> HttpResponse - if not settings.SUBDOMAINS_HOMEPAGE: - return home_real(request) - - # If settings.SUBDOMAINS_HOMEPAGE, sends the user the landing - # page, not the login form, on the root domain - - subdomain = get_subdomain(request) - if subdomain != "": - return home_real(request) - - return render_to_response('zerver/hello.html', - request=request) - -@zulip_login_required -def home_real(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. - request.session.modified = True - - user_profile = request.user - request._email = request.user.email - request.client = get_client("website") - - # If a user hasn't signed the current Terms of Service, send them there - if settings.TERMS_OF_SERVICE is not None and settings.TOS_VERSION is not None and \ - int(settings.TOS_VERSION.split('.')[0]) > user_profile.major_tos_version(): - return accounts_accept_terms(request) - - narrow = [] # type: List[List[Text]] - narrow_stream = None - narrow_topic = request.GET.get("topic") - if request.GET.get("stream"): - try: - narrow_stream = get_stream(request.GET.get("stream"), user_profile.realm) - assert(narrow_stream is not None) - assert(narrow_stream.is_public()) - narrow = [["stream", narrow_stream.name]] - except Exception: - logging.exception("Narrow parsing") - if narrow_topic is not None: - narrow.append(["topic", narrow_topic]) - - register_ret = do_events_register(user_profile, request.client, - apply_markdown=True, narrow=narrow) - user_has_messages = (register_ret['max_message_id'] != -1) - - # Reset our don't-spam-users-with-email counter since the - # user has since logged in - if not user_profile.last_reminder is None: - user_profile.last_reminder = None - user_profile.save(update_fields=["last_reminder"]) - - # Brand new users get the tutorial - needs_tutorial = settings.TUTORIAL_ENABLED and \ - user_profile.tutorial_status != UserProfile.TUTORIAL_FINISHED - - 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 = first_in_realm and \ - not PreregistrationUser.objects.filter(referred_by=user_profile).count() - - if user_profile.pointer == -1 and user_has_messages: - # Put the new user's pointer at the bottom - # - # This improves performance, because we limit backfilling of messages - # before the pointer. It's also likely that someone joining an - # organization is interested in recent messages more than the very - # first messages on the system. - - register_ret['pointer'] = register_ret['max_message_id'] - user_profile.last_pointer_updater = request.session.session_key - - if user_profile.pointer == -1: - latest_read = None - else: - try: - latest_read = UserMessage.objects.get(user_profile=user_profile, - message__id=user_profile.pointer) - except UserMessage.DoesNotExist: - # Don't completely fail if your saved pointer ID is invalid - logging.warning("%s has invalid pointer %s" % (user_profile.email, user_profile.pointer)) - latest_read = None - - desktop_notifications_enabled = user_profile.enable_desktop_notifications - if narrow_stream is not None: - desktop_notifications_enabled = False - - if user_profile.realm.notifications_stream: - notifications_stream = user_profile.realm.notifications_stream.name - else: - notifications_stream = "" - - # Set default language and make it persist - default_language = register_ret['default_language'] - url_lang = '/{}'.format(request.LANGUAGE_CODE) - if not request.path.startswith(url_lang): - translation.activate(default_language) - - request.session[translation.LANGUAGE_SESSION_KEY] = default_language - - # Pass parameters to the client-side JavaScript code. - # These end up in a global JavaScript Object named 'page_params'. - page_params = dict( - zulip_version = ZULIP_VERSION, - share_the_love = settings.SHARE_THE_LOVE, - development_environment = settings.DEVELOPMENT, - debug_mode = settings.DEBUG, - test_suite = settings.TEST_SUITE, - poll_timeout = settings.POLL_TIMEOUT, - login_page = settings.HOME_NOT_LOGGED_IN, - server_uri = settings.SERVER_URI, - realm_uri = user_profile.realm.uri, - maxfilesize = settings.MAX_FILE_UPLOAD_SIZE, - server_generation = settings.SERVER_GENERATION, - password_auth_enabled = password_auth_enabled(user_profile.realm), - have_initial_messages = user_has_messages, - subbed_info = register_ret['subscriptions'], - unsubbed_info = register_ret['unsubscribed'], - neversubbed_info = register_ret['never_subscribed'], - people_list = register_ret['realm_users'], - bot_list = register_ret['realm_bots'], - initial_pointer = register_ret['pointer'], - initial_presences = register_ret['presences'], - initial_servertime = time.time(), # Used for calculating relative presence age - fullname = user_profile.full_name, - email = user_profile.email, - domain = user_profile.realm.domain, - domains = list_of_domains_for_realm(user_profile.realm), - realm_name = register_ret['realm_name'], - realm_invite_required = register_ret['realm_invite_required'], - realm_invite_by_admins_only = register_ret['realm_invite_by_admins_only'], - realm_authentication_methods = register_ret['realm_authentication_methods'], - realm_create_stream_by_admins_only = register_ret['realm_create_stream_by_admins_only'], - realm_add_emoji_by_admins_only = register_ret['realm_add_emoji_by_admins_only'], - realm_allow_message_editing = register_ret['realm_allow_message_editing'], - realm_message_content_edit_limit_seconds = register_ret['realm_message_content_edit_limit_seconds'], - realm_restricted_to_domain = register_ret['realm_restricted_to_domain'], - realm_default_language = register_ret['realm_default_language'], - realm_waiting_period_threshold = register_ret['realm_waiting_period_threshold'], - enter_sends = user_profile.enter_sends, - user_id = user_profile.id, - left_side_userlist = register_ret['left_side_userlist'], - default_language = register_ret['default_language'], - default_language_name = get_language_name(register_ret['default_language']), - language_list_dbl_col = get_language_list_for_templates(register_ret['default_language']), - language_list = get_language_list(), - referrals = register_ret['referrals'], - realm_emoji = register_ret['realm_emoji'], - needs_tutorial = needs_tutorial, - first_in_realm = first_in_realm, - prompt_for_invites = prompt_for_invites, - notifications_stream = notifications_stream, - cross_realm_bots = list(get_cross_realm_dicts()), - use_websockets = settings.USE_WEBSOCKETS, - - # Stream message notification settings: - stream_desktop_notifications_enabled = user_profile.enable_stream_desktop_notifications, - stream_sounds_enabled = user_profile.enable_stream_sounds, - - # Private message and @-mention notification settings: - desktop_notifications_enabled = desktop_notifications_enabled, - sounds_enabled = user_profile.enable_sounds, - enable_offline_email_notifications = user_profile.enable_offline_email_notifications, - pm_content_in_desktop_notifications = user_profile.pm_content_in_desktop_notifications, - enable_offline_push_notifications = user_profile.enable_offline_push_notifications, - enable_online_push_notifications = user_profile.enable_online_push_notifications, - twenty_four_hour_time = register_ret['twenty_four_hour_time'], - enable_digest_emails = user_profile.enable_digest_emails, - event_queue_id = register_ret['queue_id'], - last_event_id = register_ret['last_event_id'], - max_message_id = register_ret['max_message_id'], - unread_count = approximate_unread_count(user_profile), - furthest_read_time = sent_time_in_epoch_seconds(latest_read), - save_stacktraces = settings.SAVE_FRONTEND_STACKTRACES, - alert_words = register_ret['alert_words'], - muted_topics = register_ret['muted_topics'], - realm_filters = register_ret['realm_filters'], - realm_default_streams = register_ret['realm_default_streams'], - is_admin = user_profile.is_realm_admin, - can_create_streams = user_profile.can_create_streams(), - name_changes_disabled = name_changes_disabled(user_profile.realm), - has_mobile_devices = num_push_devices_for_user(user_profile) > 0, - autoscroll_forever = user_profile.autoscroll_forever, - default_desktop_notifications = user_profile.default_desktop_notifications, - avatar_url = avatar_url(user_profile), - avatar_url_medium = avatar_url(user_profile, medium=True), - avatar_source = user_profile.avatar_source, - mandatory_topics = user_profile.realm.mandatory_topics, - show_digest_email = user_profile.realm.show_digest_email, - presence_disabled = user_profile.realm.presence_disabled, - is_zephyr_mirror_realm = user_profile.realm.is_zephyr_mirror_realm, - ) - - if narrow_stream is not None: - # In narrow_stream context, initial pointer is just latest message - recipient = get_recipient(Recipient.STREAM, narrow_stream.id) - try: - initial_pointer = Message.objects.filter(recipient=recipient).order_by('id').reverse()[0].id - except IndexError: - initial_pointer = -1 - page_params["narrow_stream"] = narrow_stream.name - if narrow_topic is not None: - page_params["narrow_topic"] = narrow_topic - page_params["narrow"] = [dict(operator=term[0], operand=term[1]) for term in narrow] - page_params["max_message_id"] = initial_pointer - page_params["initial_pointer"] = initial_pointer - page_params["have_initial_messages"] = (initial_pointer != -1) - - statsd.incr('views.home') - show_invites = True - - # Some realms only allow admins to invite users - if user_profile.realm.invite_by_admins_only and not user_profile.is_realm_admin: - show_invites = False - - product_name = "Zulip" - page_params['product_name'] = product_name - request._log_data['extra'] = "[%s]" % (register_ret["queue_id"],) - response = render_to_response('zerver/index.html', - {'user_profile': user_profile, - 'page_params': simplejson.encoder.JSONEncoderForHTML().encode(page_params), - 'nofontface': is_buggy_ua(request.META.get("HTTP_USER_AGENT", "Unspecified")), - 'avatar_url': avatar_url(user_profile), - 'show_debug': - settings.DEBUG and ('show_debug' in request.GET), - 'pipeline': settings.PIPELINE_ENABLED, - 'show_invites': show_invites, - 'is_admin': user_profile.is_realm_admin, - 'show_webathena': user_profile.realm.webathena_enabled, - 'enable_feedback': settings.ENABLE_FEEDBACK, - 'embedded': narrow_stream is not None, - 'product_name': product_name - }, - request=request) - patch_cache_control(response, no_cache=True, no_store=True, must_revalidate=True) - return response - -@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. - This may get fixed in the future, but for right now we can - just serve the more conservative CSS to all our desktop apps. - """ - return ("Humbug Desktop/" in agent or "Zulip Desktop/" in agent or "ZulipDesktop/" in agent) and \ - "Mac" not in agent - @authenticated_json_post_view @has_request_variables def json_set_muted_topics(request, user_profile, diff --git a/zerver/views/home.py b/zerver/views/home.py new file mode 100644 index 0000000000..72eec9d20d --- /dev/null +++ b/zerver/views/home.py @@ -0,0 +1,342 @@ +from __future__ import absolute_import +from typing import Any, List, Dict, Optional, Text + +from django.conf import settings +from django.core.urlresolvers import reverse +from django.http import HttpResponseRedirect, HttpResponse, HttpRequest +from django.shortcuts import redirect +from django.utils import translation +from django.utils.cache import patch_cache_control +from six.moves import zip_longest, zip, range + +from version import ZULIP_VERSION +from zerver.decorator import zulip_login_required +from zerver.forms import ToSForm +from zerver.models import Message, UserProfile, Stream, Subscription, Huddle, \ + Recipient, Realm, UserMessage, DefaultStream, RealmEmoji, RealmAlias, \ + RealmFilter, \ + PreregistrationUser, get_client, UserActivity, \ + get_stream, UserPresence, get_recipient, name_changes_disabled, email_to_username, \ + list_of_domains_for_realm +from zerver.lib.actions import update_user_presence, do_change_tos_version, \ + do_events_register, do_update_pointer, get_cross_realm_dicts, realm_user_count +from zerver.lib.avatar import avatar_url +from zerver.lib.i18n import get_language_list, get_language_name, \ + get_language_list_for_templates +from zerver.lib.push_notifications import num_push_devices_for_user +from zerver.lib.utils import statsd, get_subdomain +from zproject.backends import password_auth_enabled +from zproject.jinja2 import render_to_response + +import calendar +import datetime +import logging +import re +import simplejson +import time + +@zulip_login_required +def accounts_accept_terms(request): + # type: (HttpRequest) -> HttpResponse + if request.method == "POST": + form = ToSForm(request.POST) + if form.is_valid(): + do_change_tos_version(request.user, settings.TOS_VERSION) + return redirect(home) + else: + form = ToSForm() + + email = request.user.email + special_message_template = None + if request.user.tos_version is None and settings.FIRST_TIME_TOS_TEMPLATE is not None: + special_message_template = 'zerver/' + settings.FIRST_TIME_TOS_TEMPLATE + return render_to_response( + 'zerver/accounts_accept_terms.html', + {'form': form, + 'email': email, + 'special_message_template': special_message_template}, + 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)] + + # TODO: We may want to exclude muted messages from this count. + # It was attempted in the past, but the original attempt + # was broken. When we re-architect muting, we may + # want to to revisit this (see git issue #1019). + return UserMessage.objects.filter( + user_profile=user_profile, message_id__gt=user_profile.pointer).exclude( + message__recipient__type=Recipient.STREAM, + message__recipient__id__in=not_in_home_view_recipients).exclude( + 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 + # We have USE_TZ = True, so our datetime objects are timezone-aware. + # Return the epoch seconds in UTC. + return calendar.timegm(user_message.message.pub_date.utctimetuple()) + +def home(request): + # type: (HttpRequest) -> HttpResponse + if not settings.SUBDOMAINS_HOMEPAGE: + return home_real(request) + + # If settings.SUBDOMAINS_HOMEPAGE, sends the user the landing + # page, not the login form, on the root domain + + subdomain = get_subdomain(request) + if subdomain != "": + return home_real(request) + + return render_to_response('zerver/hello.html', + request=request) + +@zulip_login_required +def home_real(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. + request.session.modified = True + + user_profile = request.user + request._email = request.user.email + request.client = get_client("website") + + # If a user hasn't signed the current Terms of Service, send them there + if settings.TERMS_OF_SERVICE is not None and settings.TOS_VERSION is not None and \ + int(settings.TOS_VERSION.split('.')[0]) > user_profile.major_tos_version(): + return accounts_accept_terms(request) + + narrow = [] # type: List[List[Text]] + narrow_stream = None + narrow_topic = request.GET.get("topic") + if request.GET.get("stream"): + try: + narrow_stream = get_stream(request.GET.get("stream"), user_profile.realm) + assert(narrow_stream is not None) + assert(narrow_stream.is_public()) + narrow = [["stream", narrow_stream.name]] + except Exception: + logging.exception("Narrow parsing") + if narrow_topic is not None: + narrow.append(["topic", narrow_topic]) + + register_ret = do_events_register(user_profile, request.client, + apply_markdown=True, narrow=narrow) + user_has_messages = (register_ret['max_message_id'] != -1) + + # Reset our don't-spam-users-with-email counter since the + # user has since logged in + if not user_profile.last_reminder is None: + user_profile.last_reminder = None + user_profile.save(update_fields=["last_reminder"]) + + # Brand new users get the tutorial + needs_tutorial = settings.TUTORIAL_ENABLED and \ + user_profile.tutorial_status != UserProfile.TUTORIAL_FINISHED + + 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 = first_in_realm and \ + not PreregistrationUser.objects.filter(referred_by=user_profile).count() + + if user_profile.pointer == -1 and user_has_messages: + # Put the new user's pointer at the bottom + # + # This improves performance, because we limit backfilling of messages + # before the pointer. It's also likely that someone joining an + # organization is interested in recent messages more than the very + # first messages on the system. + + register_ret['pointer'] = register_ret['max_message_id'] + user_profile.last_pointer_updater = request.session.session_key + + if user_profile.pointer == -1: + latest_read = None + else: + try: + latest_read = UserMessage.objects.get(user_profile=user_profile, + message__id=user_profile.pointer) + except UserMessage.DoesNotExist: + # Don't completely fail if your saved pointer ID is invalid + logging.warning("%s has invalid pointer %s" % (user_profile.email, user_profile.pointer)) + latest_read = None + + desktop_notifications_enabled = user_profile.enable_desktop_notifications + if narrow_stream is not None: + desktop_notifications_enabled = False + + if user_profile.realm.notifications_stream: + notifications_stream = user_profile.realm.notifications_stream.name + else: + notifications_stream = "" + + # Set default language and make it persist + default_language = register_ret['default_language'] + url_lang = '/{}'.format(request.LANGUAGE_CODE) + if not request.path.startswith(url_lang): + translation.activate(default_language) + + request.session[translation.LANGUAGE_SESSION_KEY] = default_language + + # Pass parameters to the client-side JavaScript code. + # These end up in a global JavaScript Object named 'page_params'. + page_params = dict( + zulip_version = ZULIP_VERSION, + share_the_love = settings.SHARE_THE_LOVE, + development_environment = settings.DEVELOPMENT, + debug_mode = settings.DEBUG, + test_suite = settings.TEST_SUITE, + poll_timeout = settings.POLL_TIMEOUT, + login_page = settings.HOME_NOT_LOGGED_IN, + server_uri = settings.SERVER_URI, + realm_uri = user_profile.realm.uri, + maxfilesize = settings.MAX_FILE_UPLOAD_SIZE, + server_generation = settings.SERVER_GENERATION, + password_auth_enabled = password_auth_enabled(user_profile.realm), + have_initial_messages = user_has_messages, + subbed_info = register_ret['subscriptions'], + unsubbed_info = register_ret['unsubscribed'], + neversubbed_info = register_ret['never_subscribed'], + people_list = register_ret['realm_users'], + bot_list = register_ret['realm_bots'], + initial_pointer = register_ret['pointer'], + initial_presences = register_ret['presences'], + initial_servertime = time.time(), # Used for calculating relative presence age + fullname = user_profile.full_name, + email = user_profile.email, + domain = user_profile.realm.domain, + domains = list_of_domains_for_realm(user_profile.realm), + realm_name = register_ret['realm_name'], + realm_invite_required = register_ret['realm_invite_required'], + realm_invite_by_admins_only = register_ret['realm_invite_by_admins_only'], + realm_authentication_methods = register_ret['realm_authentication_methods'], + realm_create_stream_by_admins_only = register_ret['realm_create_stream_by_admins_only'], + realm_add_emoji_by_admins_only = register_ret['realm_add_emoji_by_admins_only'], + realm_allow_message_editing = register_ret['realm_allow_message_editing'], + realm_message_content_edit_limit_seconds = register_ret['realm_message_content_edit_limit_seconds'], + realm_restricted_to_domain = register_ret['realm_restricted_to_domain'], + realm_default_language = register_ret['realm_default_language'], + realm_waiting_period_threshold = register_ret['realm_waiting_period_threshold'], + enter_sends = user_profile.enter_sends, + user_id = user_profile.id, + left_side_userlist = register_ret['left_side_userlist'], + default_language = register_ret['default_language'], + default_language_name = get_language_name(register_ret['default_language']), + language_list_dbl_col = get_language_list_for_templates(register_ret['default_language']), + language_list = get_language_list(), + referrals = register_ret['referrals'], + realm_emoji = register_ret['realm_emoji'], + needs_tutorial = needs_tutorial, + first_in_realm = first_in_realm, + prompt_for_invites = prompt_for_invites, + notifications_stream = notifications_stream, + cross_realm_bots = list(get_cross_realm_dicts()), + use_websockets = settings.USE_WEBSOCKETS, + + # Stream message notification settings: + stream_desktop_notifications_enabled = user_profile.enable_stream_desktop_notifications, + stream_sounds_enabled = user_profile.enable_stream_sounds, + + # Private message and @-mention notification settings: + desktop_notifications_enabled = desktop_notifications_enabled, + sounds_enabled = user_profile.enable_sounds, + enable_offline_email_notifications = user_profile.enable_offline_email_notifications, + pm_content_in_desktop_notifications = user_profile.pm_content_in_desktop_notifications, + enable_offline_push_notifications = user_profile.enable_offline_push_notifications, + enable_online_push_notifications = user_profile.enable_online_push_notifications, + twenty_four_hour_time = register_ret['twenty_four_hour_time'], + enable_digest_emails = user_profile.enable_digest_emails, + event_queue_id = register_ret['queue_id'], + last_event_id = register_ret['last_event_id'], + max_message_id = register_ret['max_message_id'], + unread_count = approximate_unread_count(user_profile), + furthest_read_time = sent_time_in_epoch_seconds(latest_read), + save_stacktraces = settings.SAVE_FRONTEND_STACKTRACES, + alert_words = register_ret['alert_words'], + muted_topics = register_ret['muted_topics'], + realm_filters = register_ret['realm_filters'], + realm_default_streams = register_ret['realm_default_streams'], + is_admin = user_profile.is_realm_admin, + can_create_streams = user_profile.can_create_streams(), + name_changes_disabled = name_changes_disabled(user_profile.realm), + has_mobile_devices = num_push_devices_for_user(user_profile) > 0, + autoscroll_forever = user_profile.autoscroll_forever, + default_desktop_notifications = user_profile.default_desktop_notifications, + avatar_url = avatar_url(user_profile), + avatar_url_medium = avatar_url(user_profile, medium=True), + avatar_source = user_profile.avatar_source, + mandatory_topics = user_profile.realm.mandatory_topics, + show_digest_email = user_profile.realm.show_digest_email, + presence_disabled = user_profile.realm.presence_disabled, + is_zephyr_mirror_realm = user_profile.realm.is_zephyr_mirror_realm, + ) + + if narrow_stream is not None: + # In narrow_stream context, initial pointer is just latest message + recipient = get_recipient(Recipient.STREAM, narrow_stream.id) + try: + initial_pointer = Message.objects.filter(recipient=recipient).order_by('id').reverse()[0].id + except IndexError: + initial_pointer = -1 + page_params["narrow_stream"] = narrow_stream.name + if narrow_topic is not None: + page_params["narrow_topic"] = narrow_topic + page_params["narrow"] = [dict(operator=term[0], operand=term[1]) for term in narrow] + page_params["max_message_id"] = initial_pointer + page_params["initial_pointer"] = initial_pointer + page_params["have_initial_messages"] = (initial_pointer != -1) + + statsd.incr('views.home') + show_invites = True + + # Some realms only allow admins to invite users + if user_profile.realm.invite_by_admins_only and not user_profile.is_realm_admin: + show_invites = False + + product_name = "Zulip" + page_params['product_name'] = product_name + request._log_data['extra'] = "[%s]" % (register_ret["queue_id"],) + response = render_to_response('zerver/index.html', + {'user_profile': user_profile, + 'page_params': simplejson.encoder.JSONEncoderForHTML().encode(page_params), + 'nofontface': is_buggy_ua(request.META.get("HTTP_USER_AGENT", "Unspecified")), + 'avatar_url': avatar_url(user_profile), + 'show_debug': + settings.DEBUG and ('show_debug' in request.GET), + 'pipeline': settings.PIPELINE_ENABLED, + 'show_invites': show_invites, + 'is_admin': user_profile.is_realm_admin, + 'show_webathena': user_profile.realm.webathena_enabled, + 'enable_feedback': settings.ENABLE_FEEDBACK, + 'embedded': narrow_stream is not None, + 'product_name': product_name + }, + request=request) + patch_cache_control(response, no_cache=True, no_store=True, must_revalidate=True) + return response + +@zulip_login_required +def desktop_home(request): + # type: (HttpRequest) -> HttpResponse + return HttpResponseRedirect(reverse('zerver.views.home.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. + This may get fixed in the future, but for right now we can + just serve the more conservative CSS to all our desktop apps. + """ + return ("Humbug Desktop/" in agent or "Zulip Desktop/" in agent or "ZulipDesktop/" in agent) and \ + "Mac" not in agent + diff --git a/zproject/urls.py b/zproject/urls.py index 9e0056d471..114dd6f730 100644 --- a/zproject/urls.py +++ b/zproject/urls.py @@ -18,6 +18,7 @@ from django.contrib.auth.views import (login, password_reset, import zerver.tornado.views import zerver.views import zerver.views.auth +import zerver.views.home import zerver.views.zephyr import zerver.views.users import zerver.views.unsubscribe @@ -44,11 +45,11 @@ from zerver.lib.rest import rest_dispatch # If you're adding a new page to the website (as opposed to a new # endpoint for use by code), you should add it here. i18n_urls = [ - url(r'^$', zerver.views.home, name='zerver.views.home'), + url(r'^$', zerver.views.home.home, name='zerver.views.home.home'), # We have a desktop-specific landing page in case we change our / # to not log in in the future. We don't want to require a new # desktop app build for everyone in that case - url(r'^desktop_home/$', zerver.views.desktop_home, name='zerver.views.desktop_home'), + url(r'^desktop_home/$', zerver.views.home.desktop_home, name='zerver.views.home.desktop_home'), url(r'^accounts/login/sso/$', zerver.views.auth.remote_user_sso, name='login-sso'), url(r'^accounts/login/jwt/$', zerver.views.auth.remote_user_jwt, name='login-jwt'), @@ -104,7 +105,7 @@ i18n_urls = [ zerver.views.unsubscribe.email_unsubscribe, name='zerver.views.unsubscribe.email_unsubscribe'), # Portico-styled page used to provide email confirmation of terms acceptance. - url(r'^accounts/accept_terms/$', zerver.views.accounts_accept_terms, name='zerver.views.accounts_accept_terms'), + url(r'^accounts/accept_terms/$', zerver.views.home.accounts_accept_terms, name='zerver.views.home.accounts_accept_terms'), # Realm Creation url(r'^create_realm/$', zerver.views.create_realm, name='zerver.views.create_realm'),