mirror of https://github.com/zulip/zulip.git
uploads: Implement 5GB/user quota for paid orgs on Zulip Cloud.
Fixes #28621 Till now, this was actually a flat 50GB despite what the /plans/ page says and was adjusted flexibly when somebody asked for a higher limit. This actually implements the advertised formula, but changing it to 5GB/user since that's a more reasonable limit. Keeps the 50GB limit for sponsored Standard Free organizations and also places it as the floor for the quota for paid orgs, to not lower this for tiny orgs with less than 5 users.
This commit is contained in:
parent
4eacd25ad5
commit
066de96a86
|
@ -304,7 +304,7 @@
|
|||
<hr />
|
||||
<ul class="feature-list">
|
||||
<li>Unlimited search history</li>
|
||||
<li>File storage up to 10 GB per user</li>
|
||||
<li>File storage up to 5 GB per user</li>
|
||||
<li><a href="/help/message-retention-policy">Message retention policies</a></li>
|
||||
<li>Brand Zulip with your logo</li>
|
||||
<li>Priority commercial support</li>
|
||||
|
|
|
@ -67,7 +67,7 @@
|
|||
<h2>Standard</h2>
|
||||
<ul class="feature-list">
|
||||
<li><span>Unlimited search history</span></li>
|
||||
<li><span>File storage up to 10 GB per user</span></li>
|
||||
<li><span>File storage up to 5 GB per user</span></li>
|
||||
<li><span><a href="/help/message-retention-policy">Message retention policies</a></span></li>
|
||||
<li><span>Brand Zulip with your logo</span></li>
|
||||
<li><span>Priority commercial support</span></li>
|
||||
|
|
|
@ -511,7 +511,7 @@ class Realm(models.Model): # type: ignore[django-manager-missing] # django-stub
|
|||
]
|
||||
|
||||
UPLOAD_QUOTA_LIMITED = 5
|
||||
UPLOAD_QUOTA_STANDARD = 50
|
||||
UPLOAD_QUOTA_STANDARD_FREE = 50
|
||||
custom_upload_quota_gb = models.IntegerField(null=True)
|
||||
|
||||
VIDEO_CHAT_PROVIDERS = {
|
||||
|
@ -836,17 +836,24 @@ class Realm(models.Model): # type: ignore[django-manager-missing] # django-stub
|
|||
if self.custom_upload_quota_gb is not None:
|
||||
return self.custom_upload_quota_gb
|
||||
|
||||
plan_type = self.plan_type
|
||||
if plan_type == Realm.PLAN_TYPE_PLUS:
|
||||
return Realm.UPLOAD_QUOTA_STANDARD
|
||||
elif plan_type == Realm.PLAN_TYPE_STANDARD:
|
||||
return Realm.UPLOAD_QUOTA_STANDARD
|
||||
elif plan_type == Realm.PLAN_TYPE_SELF_HOSTED:
|
||||
if not settings.CORPORATE_ENABLED:
|
||||
return None
|
||||
elif plan_type == Realm.PLAN_TYPE_STANDARD_FREE:
|
||||
return Realm.UPLOAD_QUOTA_STANDARD
|
||||
elif plan_type == Realm.PLAN_TYPE_LIMITED:
|
||||
|
||||
plan_type = self.plan_type
|
||||
if plan_type == Realm.PLAN_TYPE_SELF_HOSTED: # nocoverage
|
||||
return None
|
||||
if plan_type == Realm.PLAN_TYPE_LIMITED:
|
||||
return Realm.UPLOAD_QUOTA_LIMITED
|
||||
elif plan_type == Realm.PLAN_TYPE_STANDARD_FREE:
|
||||
return Realm.UPLOAD_QUOTA_STANDARD_FREE
|
||||
elif plan_type in [Realm.PLAN_TYPE_STANDARD, Realm.PLAN_TYPE_PLUS]:
|
||||
from corporate.lib.stripe import get_seat_count
|
||||
|
||||
# Paying customers with few users should get a reasonable minimum quota.
|
||||
return max(
|
||||
get_seat_count(self) * settings.UPLOAD_QUOTA_PER_USER_GB,
|
||||
Realm.UPLOAD_QUOTA_STANDARD_FREE,
|
||||
)
|
||||
else:
|
||||
raise AssertionError("Invalid plan type")
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import re
|
|||
import string
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Any, Dict, List, Union
|
||||
from unittest import mock
|
||||
from unittest import mock, skipUnless
|
||||
|
||||
import orjson
|
||||
from django.conf import settings
|
||||
|
@ -15,6 +15,7 @@ from typing_extensions import override
|
|||
|
||||
from confirmation.models import Confirmation, create_confirmation_link
|
||||
from zerver.actions.create_realm import do_change_realm_subdomain, do_create_realm
|
||||
from zerver.actions.create_user import do_create_user
|
||||
from zerver.actions.message_send import (
|
||||
internal_send_huddle_message,
|
||||
internal_send_private_message,
|
||||
|
@ -60,6 +61,9 @@ from zerver.models.realms import get_realm
|
|||
from zerver.models.streams import get_stream
|
||||
from zerver.models.users import get_system_bot, get_user_profile_by_id
|
||||
|
||||
if settings.ZILENCER_ENABLED:
|
||||
from corporate.lib.stripe import get_seat_count
|
||||
|
||||
|
||||
class RealmTest(ZulipTestCase):
|
||||
def assert_user_profile_cache_gets_new_name(
|
||||
|
@ -909,8 +913,22 @@ class RealmTest(ZulipTestCase):
|
|||
self.assertEqual(realm_audit_log.acting_user, iago)
|
||||
self.assertEqual(realm.org_type, Realm.ORG_TYPES["government"]["id"])
|
||||
|
||||
@skipUnless(settings.ZILENCER_ENABLED, "requires zilencer")
|
||||
def test_change_realm_plan_type(self) -> None:
|
||||
realm = get_realm("zulip")
|
||||
|
||||
# Create additional user, so that the realm has a lot of seats for the purposes
|
||||
# of upload quota calculation.
|
||||
for count in range(10):
|
||||
do_create_user(
|
||||
f"email{count}@example.com",
|
||||
f"password {count}",
|
||||
realm,
|
||||
"name",
|
||||
role=UserProfile.ROLE_MEMBER,
|
||||
acting_user=None,
|
||||
)
|
||||
|
||||
iago = self.example_user("iago")
|
||||
self.assertEqual(realm.plan_type, Realm.PLAN_TYPE_SELF_HOSTED)
|
||||
self.assertEqual(realm.max_invites, settings.INVITES_DEFAULT_REALM_DAILY_MAX)
|
||||
|
@ -938,7 +956,9 @@ class RealmTest(ZulipTestCase):
|
|||
self.assertEqual(realm.plan_type, Realm.PLAN_TYPE_STANDARD)
|
||||
self.assertEqual(realm.max_invites, Realm.INVITES_STANDARD_REALM_DAILY_MAX)
|
||||
self.assertEqual(realm.message_visibility_limit, None)
|
||||
self.assertEqual(realm.upload_quota_gb, Realm.UPLOAD_QUOTA_STANDARD)
|
||||
self.assertEqual(
|
||||
realm.upload_quota_gb, get_seat_count(realm) * settings.UPLOAD_QUOTA_PER_USER_GB
|
||||
)
|
||||
everyone_system_group = UserGroup.objects.get(name=SystemGroups.EVERYONE, realm=realm)
|
||||
self.assertEqual(realm.can_access_all_users_group_id, everyone_system_group.id)
|
||||
|
||||
|
@ -956,7 +976,7 @@ class RealmTest(ZulipTestCase):
|
|||
self.assertEqual(realm.plan_type, Realm.PLAN_TYPE_STANDARD_FREE)
|
||||
self.assertEqual(realm.max_invites, Realm.INVITES_STANDARD_REALM_DAILY_MAX)
|
||||
self.assertEqual(realm.message_visibility_limit, None)
|
||||
self.assertEqual(realm.upload_quota_gb, Realm.UPLOAD_QUOTA_STANDARD)
|
||||
self.assertEqual(realm.upload_quota_gb, Realm.UPLOAD_QUOTA_STANDARD_FREE)
|
||||
|
||||
do_change_realm_plan_type(realm, Realm.PLAN_TYPE_LIMITED, acting_user=iago)
|
||||
do_change_realm_plan_type(realm, Realm.PLAN_TYPE_PLUS, acting_user=iago)
|
||||
|
@ -964,7 +984,9 @@ class RealmTest(ZulipTestCase):
|
|||
self.assertEqual(realm.plan_type, Realm.PLAN_TYPE_PLUS)
|
||||
self.assertEqual(realm.max_invites, Realm.INVITES_STANDARD_REALM_DAILY_MAX)
|
||||
self.assertEqual(realm.message_visibility_limit, None)
|
||||
self.assertEqual(realm.upload_quota_gb, Realm.UPLOAD_QUOTA_STANDARD)
|
||||
self.assertEqual(
|
||||
realm.upload_quota_gb, get_seat_count(realm) * settings.UPLOAD_QUOTA_PER_USER_GB
|
||||
)
|
||||
|
||||
do_change_realm_permission_group_setting(
|
||||
realm, "can_access_all_users_group", members_system_group, acting_user=None
|
||||
|
@ -974,7 +996,9 @@ class RealmTest(ZulipTestCase):
|
|||
self.assertEqual(realm.plan_type, Realm.PLAN_TYPE_STANDARD)
|
||||
self.assertEqual(realm.max_invites, Realm.INVITES_STANDARD_REALM_DAILY_MAX)
|
||||
self.assertEqual(realm.message_visibility_limit, None)
|
||||
self.assertEqual(realm.upload_quota_gb, Realm.UPLOAD_QUOTA_STANDARD)
|
||||
self.assertEqual(
|
||||
realm.upload_quota_gb, get_seat_count(realm) * settings.UPLOAD_QUOTA_PER_USER_GB
|
||||
)
|
||||
self.assertEqual(realm.can_access_all_users_group_id, everyone_system_group.id)
|
||||
|
||||
# Test that custom_upload_quota_gb overrides the default upload_quota_gb
|
||||
|
|
|
@ -167,6 +167,9 @@ LOCAL_UPLOADS_DIR: Optional[str] = None
|
|||
LOCAL_AVATARS_DIR: Optional[str] = None
|
||||
LOCAL_FILES_DIR: Optional[str] = None
|
||||
MAX_FILE_UPLOAD_SIZE = 25
|
||||
# How many GB an organization on a paid plan can upload per user,
|
||||
# on zulipchat.com.
|
||||
UPLOAD_QUOTA_PER_USER_GB = 5
|
||||
|
||||
# Jitsi Meet video call integration; set to None to disable integration.
|
||||
JITSI_SERVER_URL: Optional[str] = "https://meet.jit.si"
|
||||
|
|
Loading…
Reference in New Issue