diff --git a/static/js/panels.js b/static/js/panels.js
index 8c2daf87e1..7110ee0b94 100644
--- a/static/js/panels.js
+++ b/static/js/panels.js
@@ -80,6 +80,8 @@ export function initialize() {
const ls = localstorage();
if (page_params.insecure_desktop_app) {
open($("[data-process='insecure-desktop-app']"));
+ } else if (page_params.server_needs_upgrade) {
+ open($("[data-process='server-needs-upgrade']"));
} else if (page_params.warn_no_email === true && page_params.is_admin) {
// if email has not been set up and the user is the admin,
// display a warning to tell them to set up an email server.
diff --git a/templates/zerver/app/navbar_alerts.html b/templates/zerver/app/navbar_alerts.html
index c71281144e..25444d78f5 100644
--- a/templates/zerver/app/navbar_alerts.html
+++ b/templates/zerver/app/navbar_alerts.html
@@ -45,6 +45,15 @@
×
+
{% trans count=page_params.unread_msgs.count %}
diff --git a/zerver/lib/home.py b/zerver/lib/home.py
index 4890f66102..d3eddafc31 100644
--- a/zerver/lib/home.py
+++ b/zerver/lib/home.py
@@ -1,11 +1,15 @@
import calendar
+import datetime
+import os
import time
from dataclasses import dataclass
from typing import Any, Dict, List, Optional, Tuple
+import pytz
from django.conf import settings
from django.http import HttpRequest
from django.utils import translation
+from django.utils.timezone import now as timezone_now
from two_factor.utils import default_device
from zerver.lib.events import do_events_register
@@ -35,6 +39,33 @@ class UserPermissionInfo:
show_webathena: bool
+# LAST_SERVER_UPGRADE_TIME is the last time the server had a version deployed.
+if settings.PRODUCTION: # nocoverage
+ timestamp = os.path.basename(os.path.abspath(settings.DEPLOY_ROOT))
+ LAST_SERVER_UPGRADE_TIME = datetime.datetime.strptime(timestamp, "%Y-%m-%d-%H-%M-%S").replace(
+ tzinfo=pytz.utc
+ )
+else:
+ LAST_SERVER_UPGRADE_TIME = timezone_now()
+
+
+def is_outdated_server(user_profile: Optional[UserProfile]) -> bool:
+ # TODO: We should ideally be using the minimum of this calculation
+ # and the date the release tarball was generated.
+ tzaware_last_upgrade_time = LAST_SERVER_UPGRADE_TIME
+ deadline = tzaware_last_upgrade_time + datetime.timedelta(
+ days=settings.SERVER_UPGRADE_NAG_DEADLINE
+ )
+
+ if user_profile is None or not user_profile.is_realm_admin:
+ # Administrators get warned at the deadline; all users 30 days later.
+ deadline = deadline + datetime.timedelta(days=30)
+
+ if timezone_now() > deadline:
+ return True
+ return False
+
+
def get_furthest_read_time(user_profile: Optional[UserProfile]) -> Optional[float]:
if user_profile is None:
return time.time()
@@ -174,6 +205,7 @@ def build_page_params_for_home_page_load(
test_suite=settings.TEST_SUITE,
poll_timeout=settings.POLL_TIMEOUT,
insecure_desktop_app=insecure_desktop_app,
+ server_needs_upgrade=is_outdated_server(user_profile),
login_page=settings.HOME_NOT_LOGGED_IN,
root_domain_uri=settings.ROOT_DOMAIN_URI,
save_stacktraces=settings.SAVE_FRONTEND_STACKTRACES,
diff --git a/zerver/tests/test_home.py b/zerver/tests/test_home.py
index 1fc26246e5..176d0fb1cc 100644
--- a/zerver/tests/test_home.py
+++ b/zerver/tests/test_home.py
@@ -5,6 +5,7 @@ from typing import Any
from unittest.mock import patch
import orjson
+import pytz
from django.conf import settings
from django.http import HttpResponse
from django.utils.timezone import now as timezone_now
@@ -17,10 +18,10 @@ from zerver.lib.actions import (
do_create_user,
)
from zerver.lib.events import add_realm_logo_fields
-from zerver.lib.home import get_furthest_read_time
+from zerver.lib.home import LAST_SERVER_UPGRADE_TIME, get_furthest_read_time, is_outdated_server
from zerver.lib.soft_deactivation import do_soft_deactivate_users
from zerver.lib.test_classes import ZulipTestCase
-from zerver.lib.test_helpers import get_user_messages, queries_captured
+from zerver.lib.test_helpers import get_user_messages, override_settings, queries_captured
from zerver.lib.users import compute_show_invites_and_add_streams
from zerver.models import (
DefaultStream,
@@ -211,6 +212,7 @@ class HomeTest(ZulipTestCase):
"server_inline_image_preview",
"server_inline_url_embed_preview",
"server_name_changes_disabled",
+ "server_needs_upgrade",
"settings_send_digest_emails",
"starred_message_counts",
"starred_messages",
@@ -874,6 +876,27 @@ class HomeTest(ZulipTestCase):
compute_navbar_logo_url(page_params), "/static/images/logo/zulip-org-logo.svg?version=0"
)
+ @override_settings(SERVER_UPGRADE_NAG_DEADLINE=365)
+ def test_is_outdated_server(self) -> None:
+ # Check when server_upgrade_nag_deadline > last_server_upgrade_time
+ hamlet = self.example_user("hamlet")
+ iago = self.example_user("iago")
+ now = LAST_SERVER_UPGRADE_TIME.replace(tzinfo=pytz.utc)
+ with patch("zerver.lib.home.timezone_now", return_value=now + timedelta(days=10)):
+ self.assertEqual(is_outdated_server(iago), False)
+ self.assertEqual(is_outdated_server(hamlet), False)
+ self.assertEqual(is_outdated_server(None), False)
+
+ with patch("zerver.lib.home.timezone_now", return_value=now + timedelta(days=397)):
+ self.assertEqual(is_outdated_server(iago), True)
+ self.assertEqual(is_outdated_server(hamlet), True)
+ self.assertEqual(is_outdated_server(None), True)
+
+ with patch("zerver.lib.home.timezone_now", return_value=now + timedelta(days=380)):
+ self.assertEqual(is_outdated_server(iago), True)
+ self.assertEqual(is_outdated_server(hamlet), False)
+ self.assertEqual(is_outdated_server(None), False)
+
def test_furthest_read_time(self) -> None:
msg_id = self.send_test_message("hello!", sender_name="iago")
diff --git a/zproject/default_settings.py b/zproject/default_settings.py
index 541285558c..ce29143fb6 100644
--- a/zproject/default_settings.py
+++ b/zproject/default_settings.py
@@ -441,3 +441,6 @@ NAGIOS_BOT_HOST = EXTERNAL_HOST
# Use half of the available CPUs for data import purposes.
DEFAULT_DATA_EXPORT_IMPORT_PARALLELISM = (len(os.sched_getaffinity(0)) // 2) or 1
+
+# How long after the last upgrade to nag users that the server is insecure
+SERVER_UPGRADE_NAG_DEADLINE = 365