mirror of https://github.com/zulip/zulip.git
models: Extract zerver.models.streams.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
parent
856063f143
commit
776a0eeae8
|
@ -49,7 +49,7 @@ rules:
|
|||
- pattern-not: from zerver.lib.utils import generate_api_key
|
||||
- pattern-not: from zerver.models.linkifiers import filter_pattern_validator
|
||||
- pattern-not: from zerver.models.linkifiers import url_template_validator
|
||||
- pattern-not: from zerver.models import generate_email_token_for_stream
|
||||
- pattern-not: from zerver.models.streams import generate_email_token_for_stream
|
||||
- pattern-not: from zerver.models.realms import generate_realm_uuid_owner_secret
|
||||
- pattern-either:
|
||||
- pattern: from zerver import $X
|
||||
|
|
|
@ -1006,7 +1006,7 @@ export const stream_privacy_policy_values = {
|
|||
|
||||
export const stream_post_policy_values = {
|
||||
// These strings should match the strings in the
|
||||
// Stream.POST_POLICIES object in zerver/models/__init__.py.
|
||||
// Stream.POST_POLICIES object in zerver/models/streams.py.
|
||||
everyone: {
|
||||
code: StreamPostPolicy.EVERYONE,
|
||||
description: $t({defaultMessage: "Everyone"}),
|
||||
|
|
|
@ -8,13 +8,8 @@ from zerver.lib.default_streams import (
|
|||
get_default_streams_for_realm_as_dicts,
|
||||
)
|
||||
from zerver.lib.exceptions import JsonableError
|
||||
from zerver.models import (
|
||||
DefaultStream,
|
||||
DefaultStreamGroup,
|
||||
Realm,
|
||||
Stream,
|
||||
get_default_stream_groups,
|
||||
)
|
||||
from zerver.models import DefaultStream, DefaultStreamGroup, Realm, Stream
|
||||
from zerver.models.streams import get_default_stream_groups
|
||||
from zerver.models.users import active_non_guest_user_ids
|
||||
from zerver.tornado.django_api import send_event_on_commit
|
||||
|
||||
|
|
|
@ -76,8 +76,8 @@ from zerver.models import (
|
|||
UserMessage,
|
||||
UserProfile,
|
||||
UserTopic,
|
||||
get_stream_by_id_in_realm,
|
||||
)
|
||||
from zerver.models.streams import get_stream_by_id_in_realm
|
||||
from zerver.models.users import get_system_bot
|
||||
from zerver.tornado.django_api import send_event
|
||||
|
||||
|
|
|
@ -103,12 +103,11 @@ from zerver.models import (
|
|||
UserProfile,
|
||||
UserTopic,
|
||||
get_client,
|
||||
get_stream,
|
||||
get_stream_by_id_in_realm,
|
||||
query_for_ids,
|
||||
)
|
||||
from zerver.models.groups import SystemGroups
|
||||
from zerver.models.recipients import get_huddle_user_ids
|
||||
from zerver.models.streams import get_stream, get_stream_by_id_in_realm
|
||||
from zerver.models.users import get_system_bot, get_user_by_delivery_email, is_cross_realm_bot_email
|
||||
from zerver.tornado.django_api import send_event
|
||||
|
||||
|
|
|
@ -182,7 +182,7 @@ def convert_channel_data(
|
|||
# should be allowed to post in the converted Zulip stream.
|
||||
# For more details: https://zulip.com/help/stream-sending-policy
|
||||
#
|
||||
# See `Stream` model in `zerver/models/__init__.py` to know about what each
|
||||
# See `Stream` model in `zerver/models/streams.py` to know about what each
|
||||
# number represent.
|
||||
stream_post_policy = 4 if channel_dict.get("ro", False) else 1
|
||||
|
||||
|
|
|
@ -639,7 +639,7 @@ def realm_text_description_cache_key(realm: "Realm") -> str:
|
|||
return f"realm_text_description:{realm.string_id}"
|
||||
|
||||
|
||||
# Called by models/__init__.py to flush the stream cache whenever we save a stream
|
||||
# Called by models/streams.py to flush the stream cache whenever we save a stream
|
||||
# object.
|
||||
def flush_stream(
|
||||
*,
|
||||
|
|
|
@ -29,8 +29,8 @@ from zerver.models import (
|
|||
Subscription,
|
||||
UserActivityInterval,
|
||||
UserProfile,
|
||||
get_active_streams,
|
||||
)
|
||||
from zerver.models.streams import get_active_streams
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
log_to_file(logger, settings.DIGEST_LOG_PATH)
|
||||
|
|
|
@ -37,8 +37,8 @@ from zerver.models import (
|
|||
Stream,
|
||||
UserProfile,
|
||||
get_client,
|
||||
get_stream_by_id_in_realm,
|
||||
)
|
||||
from zerver.models.streams import get_stream_by_id_in_realm
|
||||
from zerver.models.users import get_system_bot, get_user_profile_by_id
|
||||
from zproject.backends import is_user_active
|
||||
|
||||
|
|
|
@ -80,13 +80,13 @@ from zerver.models import (
|
|||
UserStatus,
|
||||
UserTopic,
|
||||
custom_profile_fields_for_realm,
|
||||
get_default_stream_groups,
|
||||
)
|
||||
from zerver.models.constants import MAX_TOPIC_NAME_LENGTH
|
||||
from zerver.models.linkifiers import linkifiers_for_realm
|
||||
from zerver.models.realm_emoji import get_all_custom_emoji_for_realm
|
||||
from zerver.models.realm_playgrounds import get_realm_playgrounds
|
||||
from zerver.models.realms import get_realm_domains
|
||||
from zerver.models.streams import get_default_stream_groups
|
||||
from zerver.tornado.django_api import get_user_events, request_event_queue
|
||||
from zproject.backends import email_auth_enabled, password_auth_enabled
|
||||
|
||||
|
|
|
@ -7,7 +7,8 @@ from django.conf import settings
|
|||
from django.db.models import Q
|
||||
|
||||
from zerver.lib.users import get_inaccessible_user_ids
|
||||
from zerver.models import UserGroup, UserProfile, get_linkable_streams
|
||||
from zerver.models import UserGroup, UserProfile
|
||||
from zerver.models.streams import get_linkable_streams
|
||||
|
||||
BEFORE_MENTION_ALLOWED_REGEX = r"(?<![^\s\'\"\(\{\[\/<])"
|
||||
|
||||
|
|
|
@ -75,15 +75,8 @@ from zerver.lib.validator import (
|
|||
check_string_or_int,
|
||||
check_string_or_int_list,
|
||||
)
|
||||
from zerver.models import (
|
||||
Realm,
|
||||
Recipient,
|
||||
Stream,
|
||||
Subscription,
|
||||
UserMessage,
|
||||
UserProfile,
|
||||
get_active_streams,
|
||||
)
|
||||
from zerver.models import Realm, Recipient, Stream, Subscription, UserMessage, UserProfile
|
||||
from zerver.models.streams import get_active_streams
|
||||
from zerver.models.users import (
|
||||
get_user_by_id_in_realm_including_cross_realm,
|
||||
get_user_including_cross_realm,
|
||||
|
|
|
@ -30,12 +30,14 @@ from zerver.models import (
|
|||
Subscription,
|
||||
UserGroup,
|
||||
UserProfile,
|
||||
)
|
||||
from zerver.models.groups import SystemGroups
|
||||
from zerver.models.streams import (
|
||||
bulk_get_streams,
|
||||
get_realm_stream,
|
||||
get_stream,
|
||||
get_stream_by_id_in_realm,
|
||||
)
|
||||
from zerver.models.groups import SystemGroups
|
||||
from zerver.models.users import active_non_guest_user_ids, active_user_ids, is_cross_realm_bot_email
|
||||
from zerver.tornado.django_api import send_event
|
||||
|
||||
|
|
|
@ -25,7 +25,8 @@ from zerver.lib.types import (
|
|||
SubscriptionInfo,
|
||||
SubscriptionStreamDict,
|
||||
)
|
||||
from zerver.models import Realm, Stream, Subscription, UserProfile, get_active_streams
|
||||
from zerver.models import Realm, Stream, Subscription, UserProfile
|
||||
from zerver.models.streams import get_active_streams
|
||||
|
||||
|
||||
def get_web_public_subs(realm: Realm) -> SubscriptionInfo:
|
||||
|
|
|
@ -107,11 +107,10 @@ from zerver.models import (
|
|||
UserMessage,
|
||||
UserProfile,
|
||||
UserStatus,
|
||||
get_realm_stream,
|
||||
get_stream,
|
||||
)
|
||||
from zerver.models.groups import SystemGroups
|
||||
from zerver.models.realms import clear_supported_auth_backends_cache, get_realm
|
||||
from zerver.models.streams import get_realm_stream, get_stream
|
||||
from zerver.models.users import get_system_bot, get_user, get_user_by_delivery_email
|
||||
from zerver.openapi.openapi import validate_against_openapi_schema, validate_request
|
||||
from zerver.tornado.event_queue import clear_client_event_queues_for_testing
|
||||
|
|
|
@ -60,9 +60,9 @@ from zerver.models import (
|
|||
UserMessage,
|
||||
UserProfile,
|
||||
get_client,
|
||||
get_stream,
|
||||
)
|
||||
from zerver.models.realms import get_realm
|
||||
from zerver.models.streams import get_stream
|
||||
from zerver.tornado.handlers import AsyncDjangoHandler, allocate_handler_id
|
||||
from zilencer.models import RemoteZulipServer
|
||||
from zproject.backends import ExternalAuthDataDict, ExternalAuthResult
|
||||
|
|
|
@ -12,7 +12,8 @@ from sqlalchemy.types import Integer
|
|||
from zerver.lib.timestamp import datetime_to_timestamp
|
||||
from zerver.lib.topic import topic_match_sa
|
||||
from zerver.lib.types import UserTopicDict
|
||||
from zerver.models import UserProfile, UserTopic, get_stream
|
||||
from zerver.models import UserProfile, UserTopic
|
||||
from zerver.models.streams import get_stream
|
||||
|
||||
|
||||
def get_user_topics(
|
||||
|
|
|
@ -5,7 +5,7 @@ from typing_extensions import override
|
|||
|
||||
from zerver.actions.streams import merge_streams
|
||||
from zerver.lib.management import ZulipBaseCommand
|
||||
from zerver.models import get_stream
|
||||
from zerver.models.streams import get_stream
|
||||
|
||||
|
||||
class Command(ZulipBaseCommand):
|
||||
|
|
|
@ -5,7 +5,7 @@ from typing_extensions import override
|
|||
|
||||
from zerver.actions.streams import bulk_remove_subscriptions
|
||||
from zerver.lib.management import ZulipBaseCommand
|
||||
from zerver.models import get_stream
|
||||
from zerver.models.streams import get_stream
|
||||
|
||||
|
||||
class Command(ZulipBaseCommand):
|
||||
|
|
|
@ -13,8 +13,9 @@ from typing_extensions import override
|
|||
from zerver.lib.email_mirror import mirror_email_message
|
||||
from zerver.lib.email_mirror_helpers import encode_email_address
|
||||
from zerver.lib.management import ZulipBaseCommand
|
||||
from zerver.models import Realm, get_stream
|
||||
from zerver.models import Realm
|
||||
from zerver.models.realms import get_realm
|
||||
from zerver.models.streams import get_stream
|
||||
|
||||
# This command loads an email from a specified file and sends it
|
||||
# to the email mirror. Simple emails can be passed in a JSON file,
|
||||
|
|
|
@ -12,7 +12,7 @@ from django.db.backends.base.schema import BaseDatabaseSchemaEditor
|
|||
from django.db.migrations.state import StateApps
|
||||
from django.db.models.functions import Upper
|
||||
|
||||
from zerver.models import generate_email_token_for_stream
|
||||
from zerver.models.streams import generate_email_token_for_stream
|
||||
|
||||
|
||||
def migrate_existing_attachment_data(
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
from django.db import migrations, models
|
||||
|
||||
from zerver.models import generate_email_token_for_stream
|
||||
from zerver.models.streams import generate_email_token_for_stream
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
|
|
@ -2,10 +2,9 @@
|
|||
# mypy: disable-error-code="explicit-override"
|
||||
|
||||
import hashlib
|
||||
import secrets
|
||||
import time
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Any, Callable, Dict, List, Optional, Set, Tuple, TypedDict, TypeVar, Union
|
||||
from typing import Any, Callable, Dict, List, Optional, Tuple, TypedDict, TypeVar, Union
|
||||
|
||||
import orjson
|
||||
from bitfield import BitField
|
||||
|
@ -34,7 +33,6 @@ from zerver.lib.cache import (
|
|||
cache_with_key,
|
||||
flush_message,
|
||||
flush_muting_users_cache,
|
||||
flush_stream,
|
||||
flush_submessage,
|
||||
flush_used_upload_space_cache,
|
||||
realm_alert_words_automaton_cache_key,
|
||||
|
@ -44,11 +42,9 @@ from zerver.lib.display_recipient import get_recipient_ids
|
|||
from zerver.lib.exceptions import RateLimitedError
|
||||
from zerver.lib.timestamp import datetime_to_timestamp
|
||||
from zerver.lib.types import (
|
||||
DefaultStreamDict,
|
||||
ExtendedFieldElement,
|
||||
ExtendedValidator,
|
||||
FieldElement,
|
||||
GroupPermissionSetting,
|
||||
ProfileDataElementBase,
|
||||
ProfileDataElementValue,
|
||||
RealmUserValidator,
|
||||
|
@ -66,7 +62,6 @@ from zerver.lib.validator import (
|
|||
)
|
||||
from zerver.models.constants import MAX_TOPIC_NAME_LENGTH
|
||||
from zerver.models.groups import GroupGroupMembership as GroupGroupMembership
|
||||
from zerver.models.groups import SystemGroups
|
||||
from zerver.models.groups import UserGroup as UserGroup
|
||||
from zerver.models.groups import UserGroupMembership as UserGroupMembership
|
||||
from zerver.models.linkifiers import RealmFilter as RealmFilter
|
||||
|
@ -84,6 +79,10 @@ from zerver.models.realms import RealmAuthenticationMethod as RealmAuthenticatio
|
|||
from zerver.models.realms import RealmDomain as RealmDomain
|
||||
from zerver.models.recipients import Huddle as Huddle
|
||||
from zerver.models.recipients import Recipient as Recipient
|
||||
from zerver.models.streams import DefaultStream as DefaultStream
|
||||
from zerver.models.streams import DefaultStreamGroup as DefaultStreamGroup
|
||||
from zerver.models.streams import Stream as Stream
|
||||
from zerver.models.streams import Subscription as Subscription
|
||||
from zerver.models.users import RealmUserDefault as RealmUserDefault
|
||||
from zerver.models.users import UserBaseSettings as UserBaseSettings
|
||||
from zerver.models.users import UserProfile as UserProfile
|
||||
|
@ -143,202 +142,6 @@ def query_for_ids(
|
|||
return query
|
||||
|
||||
|
||||
def generate_email_token_for_stream() -> str:
|
||||
return secrets.token_hex(16)
|
||||
|
||||
|
||||
class Stream(models.Model):
|
||||
MAX_NAME_LENGTH = 60
|
||||
MAX_DESCRIPTION_LENGTH = 1024
|
||||
|
||||
name = models.CharField(max_length=MAX_NAME_LENGTH, db_index=True)
|
||||
realm = models.ForeignKey(Realm, db_index=True, on_delete=CASCADE)
|
||||
date_created = models.DateTimeField(default=timezone_now)
|
||||
deactivated = models.BooleanField(default=False)
|
||||
description = models.CharField(max_length=MAX_DESCRIPTION_LENGTH, default="")
|
||||
rendered_description = models.TextField(default="")
|
||||
|
||||
# Foreign key to the Recipient object for STREAM type messages to this stream.
|
||||
recipient = models.ForeignKey(Recipient, null=True, on_delete=models.SET_NULL)
|
||||
|
||||
# Various permission policy configurations
|
||||
PERMISSION_POLICIES: Dict[str, Dict[str, Any]] = {
|
||||
"web_public": {
|
||||
"invite_only": False,
|
||||
"history_public_to_subscribers": True,
|
||||
"is_web_public": True,
|
||||
"policy_name": gettext_lazy("Web-public"),
|
||||
},
|
||||
"public": {
|
||||
"invite_only": False,
|
||||
"history_public_to_subscribers": True,
|
||||
"is_web_public": False,
|
||||
"policy_name": gettext_lazy("Public"),
|
||||
},
|
||||
"private_shared_history": {
|
||||
"invite_only": True,
|
||||
"history_public_to_subscribers": True,
|
||||
"is_web_public": False,
|
||||
"policy_name": gettext_lazy("Private, shared history"),
|
||||
},
|
||||
"private_protected_history": {
|
||||
"invite_only": True,
|
||||
"history_public_to_subscribers": False,
|
||||
"is_web_public": False,
|
||||
"policy_name": gettext_lazy("Private, protected history"),
|
||||
},
|
||||
# Public streams with protected history are currently only
|
||||
# available in Zephyr realms
|
||||
"public_protected_history": {
|
||||
"invite_only": False,
|
||||
"history_public_to_subscribers": False,
|
||||
"is_web_public": False,
|
||||
"policy_name": gettext_lazy("Public, protected history"),
|
||||
},
|
||||
}
|
||||
invite_only = models.BooleanField(default=False)
|
||||
history_public_to_subscribers = models.BooleanField(default=True)
|
||||
|
||||
# Whether this stream's content should be published by the web-public archive features
|
||||
is_web_public = models.BooleanField(default=False)
|
||||
|
||||
STREAM_POST_POLICY_EVERYONE = 1
|
||||
STREAM_POST_POLICY_ADMINS = 2
|
||||
STREAM_POST_POLICY_RESTRICT_NEW_MEMBERS = 3
|
||||
STREAM_POST_POLICY_MODERATORS = 4
|
||||
# TODO: Implement policy to restrict posting to a user group or admins.
|
||||
|
||||
# Who in the organization has permission to send messages to this stream.
|
||||
stream_post_policy = models.PositiveSmallIntegerField(default=STREAM_POST_POLICY_EVERYONE)
|
||||
POST_POLICIES: Dict[int, StrPromise] = {
|
||||
# These strings should match the strings in the
|
||||
# stream_post_policy_values object in stream_data.js.
|
||||
STREAM_POST_POLICY_EVERYONE: gettext_lazy("All stream members can post"),
|
||||
STREAM_POST_POLICY_ADMINS: gettext_lazy("Only organization administrators can post"),
|
||||
STREAM_POST_POLICY_MODERATORS: gettext_lazy(
|
||||
"Only organization administrators and moderators can post"
|
||||
),
|
||||
STREAM_POST_POLICY_RESTRICT_NEW_MEMBERS: gettext_lazy(
|
||||
"Only organization full members can post"
|
||||
),
|
||||
}
|
||||
STREAM_POST_POLICY_TYPES = list(POST_POLICIES.keys())
|
||||
|
||||
# The unique thing about Zephyr public streams is that we never list their
|
||||
# users. We may try to generalize this concept later, but for now
|
||||
# we just use a concrete field. (Zephyr public streams aren't exactly like
|
||||
# invite-only streams--while both are private in terms of listing users,
|
||||
# for Zephyr we don't even list users to stream members, yet membership
|
||||
# is more public in the sense that you don't need a Zulip invite to join.
|
||||
# This field is populated directly from UserProfile.is_zephyr_mirror_realm,
|
||||
# and the reason for denormalizing field is performance.
|
||||
is_in_zephyr_realm = models.BooleanField(default=False)
|
||||
|
||||
# Used by the e-mail forwarder. The e-mail RFC specifies a maximum
|
||||
# e-mail length of 254, and our max stream length is 30, so we
|
||||
# have plenty of room for the token.
|
||||
email_token = models.CharField(
|
||||
max_length=32,
|
||||
default=generate_email_token_for_stream,
|
||||
unique=True,
|
||||
)
|
||||
|
||||
# For old messages being automatically deleted.
|
||||
# Value NULL means "use retention policy of the realm".
|
||||
# Value -1 means "disable retention policy for this stream unconditionally".
|
||||
# Non-negative values have the natural meaning of "archive messages older than <value> days".
|
||||
MESSAGE_RETENTION_SPECIAL_VALUES_MAP = {
|
||||
"unlimited": -1,
|
||||
"realm_default": None,
|
||||
}
|
||||
message_retention_days = models.IntegerField(null=True, default=None)
|
||||
|
||||
# on_delete field here is set to RESTRICT because we don't want to allow
|
||||
# deleting a user group in case it is referenced by this setting.
|
||||
# We are not using PROTECT since we want to allow deletion of user groups
|
||||
# when realm itself is deleted.
|
||||
can_remove_subscribers_group = models.ForeignKey(UserGroup, on_delete=models.RESTRICT)
|
||||
|
||||
# The very first message ID in the stream. Used to help clients
|
||||
# determine whether they might need to display "more topics" for a
|
||||
# stream based on what messages they have cached.
|
||||
first_message_id = models.IntegerField(null=True, db_index=True)
|
||||
|
||||
stream_permission_group_settings = {
|
||||
"can_remove_subscribers_group": GroupPermissionSetting(
|
||||
require_system_group=True,
|
||||
allow_internet_group=False,
|
||||
allow_owners_group=False,
|
||||
allow_nobody_group=False,
|
||||
allow_everyone_group=True,
|
||||
default_group_name=SystemGroups.ADMINISTRATORS,
|
||||
id_field_name="can_remove_subscribers_group_id",
|
||||
),
|
||||
}
|
||||
|
||||
class Meta:
|
||||
indexes = [
|
||||
models.Index(Upper("name"), name="upper_stream_name_idx"),
|
||||
]
|
||||
|
||||
@override
|
||||
def __str__(self) -> str:
|
||||
return self.name
|
||||
|
||||
def is_public(self) -> bool:
|
||||
# All streams are private in Zephyr mirroring realms.
|
||||
return not self.invite_only and not self.is_in_zephyr_realm
|
||||
|
||||
def is_history_realm_public(self) -> bool:
|
||||
return self.is_public()
|
||||
|
||||
def is_history_public_to_subscribers(self) -> bool:
|
||||
return self.history_public_to_subscribers
|
||||
|
||||
# Stream fields included whenever a Stream object is provided to
|
||||
# Zulip clients via the API. A few details worth noting:
|
||||
# * "id" is represented as "stream_id" in most API interfaces.
|
||||
# * "email_token" is not realm-public and thus is not included here.
|
||||
# * is_in_zephyr_realm is a backend-only optimization.
|
||||
# * "deactivated" streams are filtered from the API entirely.
|
||||
# * "realm" and "recipient" are not exposed to clients via the API.
|
||||
API_FIELDS = [
|
||||
"date_created",
|
||||
"description",
|
||||
"first_message_id",
|
||||
"history_public_to_subscribers",
|
||||
"id",
|
||||
"invite_only",
|
||||
"is_web_public",
|
||||
"message_retention_days",
|
||||
"name",
|
||||
"rendered_description",
|
||||
"stream_post_policy",
|
||||
"can_remove_subscribers_group_id",
|
||||
]
|
||||
|
||||
def to_dict(self) -> DefaultStreamDict:
|
||||
return DefaultStreamDict(
|
||||
can_remove_subscribers_group=self.can_remove_subscribers_group_id,
|
||||
date_created=datetime_to_timestamp(self.date_created),
|
||||
description=self.description,
|
||||
first_message_id=self.first_message_id,
|
||||
history_public_to_subscribers=self.history_public_to_subscribers,
|
||||
invite_only=self.invite_only,
|
||||
is_web_public=self.is_web_public,
|
||||
message_retention_days=self.message_retention_days,
|
||||
name=self.name,
|
||||
rendered_description=self.rendered_description,
|
||||
stream_id=self.id,
|
||||
stream_post_policy=self.stream_post_policy,
|
||||
is_announcement_only=self.stream_post_policy == Stream.STREAM_POST_POLICY_ADMINS,
|
||||
)
|
||||
|
||||
|
||||
post_save.connect(flush_stream, sender=Stream)
|
||||
post_delete.connect(flush_stream, sender=Stream)
|
||||
|
||||
|
||||
class UserTopic(models.Model):
|
||||
user_profile = models.ForeignKey(UserProfile, on_delete=CASCADE)
|
||||
stream = models.ForeignKey(Stream, on_delete=CASCADE)
|
||||
|
@ -489,61 +292,6 @@ def get_client_remote_cache(name: str) -> Client:
|
|||
return client
|
||||
|
||||
|
||||
def get_realm_stream(stream_name: str, realm_id: int) -> Stream:
|
||||
return Stream.objects.get(name__iexact=stream_name.strip(), realm_id=realm_id)
|
||||
|
||||
|
||||
def get_active_streams(realm: Realm) -> QuerySet[Stream]:
|
||||
"""
|
||||
Return all streams (including invite-only streams) that have not been deactivated.
|
||||
"""
|
||||
return Stream.objects.filter(realm=realm, deactivated=False)
|
||||
|
||||
|
||||
def get_linkable_streams(realm_id: int) -> QuerySet[Stream]:
|
||||
"""
|
||||
This returns the streams that we are allowed to linkify using
|
||||
something like "#frontend" in our markup. For now the business
|
||||
rule is that you can link any stream in the realm that hasn't
|
||||
been deactivated (similar to how get_active_streams works).
|
||||
"""
|
||||
return Stream.objects.filter(realm_id=realm_id, deactivated=False)
|
||||
|
||||
|
||||
def get_stream(stream_name: str, realm: Realm) -> Stream:
|
||||
"""
|
||||
Callers that don't have a Realm object already available should use
|
||||
get_realm_stream directly, to avoid unnecessarily fetching the
|
||||
Realm object.
|
||||
"""
|
||||
return get_realm_stream(stream_name, realm.id)
|
||||
|
||||
|
||||
def get_stream_by_id_in_realm(stream_id: int, realm: Realm) -> Stream:
|
||||
return Stream.objects.select_related("realm", "recipient").get(id=stream_id, realm=realm)
|
||||
|
||||
|
||||
def bulk_get_streams(realm: Realm, stream_names: Set[str]) -> Dict[str, Any]:
|
||||
def fetch_streams_by_name(stream_names: Set[str]) -> QuerySet[Stream]:
|
||||
#
|
||||
# This should be just
|
||||
#
|
||||
# Stream.objects.select_related().filter(name__iexact__in=stream_names,
|
||||
# realm_id=realm_id)
|
||||
#
|
||||
# But chaining __in and __iexact doesn't work with Django's
|
||||
# ORM, so we have the following hack to construct the relevant where clause
|
||||
where_clause = (
|
||||
"upper(zerver_stream.name::text) IN (SELECT upper(name) FROM unnest(%s) AS name)"
|
||||
)
|
||||
return get_active_streams(realm).extra(where=[where_clause], params=(list(stream_names),))
|
||||
|
||||
if not stream_names:
|
||||
return {}
|
||||
streams = list(fetch_streams_by_name(stream_names))
|
||||
return {stream.name.lower(): stream for stream in streams}
|
||||
|
||||
|
||||
class AbstractMessage(models.Model):
|
||||
sender = models.ForeignKey(UserProfile, on_delete=CASCADE)
|
||||
|
||||
|
@ -1432,91 +1180,6 @@ def get_old_unclaimed_attachments(
|
|||
return old_attachments, old_archived_attachments
|
||||
|
||||
|
||||
class Subscription(models.Model):
|
||||
"""Keeps track of which users are part of the
|
||||
audience for a given Recipient object.
|
||||
|
||||
For 1:1 and group direct message Recipient objects, only the
|
||||
user_profile and recipient fields have any meaning, defining the
|
||||
immutable set of users who are in the audience for that Recipient.
|
||||
|
||||
For Recipient objects associated with a Stream, the remaining
|
||||
fields in this model describe the user's subscription to that stream.
|
||||
"""
|
||||
|
||||
user_profile = models.ForeignKey(UserProfile, on_delete=CASCADE)
|
||||
recipient = models.ForeignKey(Recipient, on_delete=CASCADE)
|
||||
|
||||
# Whether the user has since unsubscribed. We mark Subscription
|
||||
# objects as inactive, rather than deleting them, when a user
|
||||
# unsubscribes, so we can preserve user customizations like
|
||||
# notification settings, stream color, etc., if the user later
|
||||
# resubscribes.
|
||||
active = models.BooleanField(default=True)
|
||||
# This is a denormalization designed to improve the performance of
|
||||
# bulk queries of Subscription objects, Whether the subscribed user
|
||||
# is active tends to be a key condition in those queries.
|
||||
# We intentionally don't specify a default value to promote thinking
|
||||
# about this explicitly, as in some special cases, such as data import,
|
||||
# we may be creating Subscription objects for a user that's deactivated.
|
||||
is_user_active = models.BooleanField()
|
||||
|
||||
# Whether this user had muted this stream.
|
||||
is_muted = models.BooleanField(default=False)
|
||||
|
||||
DEFAULT_STREAM_COLOR = "#c2c2c2"
|
||||
color = models.CharField(max_length=10, default=DEFAULT_STREAM_COLOR)
|
||||
pin_to_top = models.BooleanField(default=False)
|
||||
|
||||
# These fields are stream-level overrides for the user's default
|
||||
# configuration for notification, configured in UserProfile. The
|
||||
# default, None, means we just inherit the user-level default.
|
||||
desktop_notifications = models.BooleanField(null=True, default=None)
|
||||
audible_notifications = models.BooleanField(null=True, default=None)
|
||||
push_notifications = models.BooleanField(null=True, default=None)
|
||||
email_notifications = models.BooleanField(null=True, default=None)
|
||||
wildcard_mentions_notify = models.BooleanField(null=True, default=None)
|
||||
|
||||
class Meta:
|
||||
unique_together = ("user_profile", "recipient")
|
||||
indexes = [
|
||||
models.Index(
|
||||
fields=("recipient", "user_profile"),
|
||||
name="zerver_subscription_recipient_id_user_profile_id_idx",
|
||||
condition=Q(active=True, is_user_active=True),
|
||||
),
|
||||
]
|
||||
|
||||
@override
|
||||
def __str__(self) -> str:
|
||||
return f"{self.user_profile!r} -> {self.recipient!r}"
|
||||
|
||||
# Subscription fields included whenever a Subscription object is provided to
|
||||
# Zulip clients via the API. A few details worth noting:
|
||||
# * These fields will generally be merged with Stream.API_FIELDS
|
||||
# data about the stream.
|
||||
# * "user_profile" is usually implied as full API access to Subscription
|
||||
# is primarily done for the current user; API access to other users'
|
||||
# subscriptions is generally limited to boolean yes/no.
|
||||
# * "id" and "recipient_id" are not included as they are not used
|
||||
# in the Zulip API; it's an internal implementation detail.
|
||||
# Subscription objects are always looked up in the API via
|
||||
# (user_profile, stream) pairs.
|
||||
# * "active" is often excluded in API use cases where it is implied.
|
||||
# * "is_muted" often needs to be copied to not "in_home_view" for
|
||||
# backwards-compatibility.
|
||||
API_FIELDS = [
|
||||
"audible_notifications",
|
||||
"color",
|
||||
"desktop_notifications",
|
||||
"email_notifications",
|
||||
"is_muted",
|
||||
"pin_to_top",
|
||||
"push_notifications",
|
||||
"wildcard_mentions_notify",
|
||||
]
|
||||
|
||||
|
||||
class UserActivity(models.Model):
|
||||
"""Data table recording the last time each user hit Zulip endpoints
|
||||
via which Clients; unlike UserPresence, these data are not exposed
|
||||
|
@ -1627,38 +1290,6 @@ class UserStatus(AbstractEmoji):
|
|||
status_text = models.CharField(max_length=255, default="")
|
||||
|
||||
|
||||
class DefaultStream(models.Model):
|
||||
realm = models.ForeignKey(Realm, on_delete=CASCADE)
|
||||
stream = models.ForeignKey(Stream, on_delete=CASCADE)
|
||||
|
||||
class Meta:
|
||||
unique_together = ("realm", "stream")
|
||||
|
||||
|
||||
class DefaultStreamGroup(models.Model):
|
||||
MAX_NAME_LENGTH = 60
|
||||
|
||||
name = models.CharField(max_length=MAX_NAME_LENGTH, db_index=True)
|
||||
realm = models.ForeignKey(Realm, on_delete=CASCADE)
|
||||
streams = models.ManyToManyField("zerver.Stream")
|
||||
description = models.CharField(max_length=1024, default="")
|
||||
|
||||
class Meta:
|
||||
unique_together = ("realm", "name")
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
return dict(
|
||||
name=self.name,
|
||||
id=self.id,
|
||||
description=self.description,
|
||||
streams=[stream.to_dict() for stream in self.streams.all().order_by("name")],
|
||||
)
|
||||
|
||||
|
||||
def get_default_stream_groups(realm: Realm) -> QuerySet[DefaultStreamGroup]:
|
||||
return DefaultStreamGroup.objects.filter(realm=realm)
|
||||
|
||||
|
||||
class AbstractScheduledJob(models.Model):
|
||||
scheduled_timestamp = models.DateTimeField(db_index=True)
|
||||
# JSON representation of arguments to consumer
|
||||
|
|
|
@ -0,0 +1,387 @@
|
|||
import secrets
|
||||
from typing import Any, Dict, Set
|
||||
|
||||
from django.db import models
|
||||
from django.db.models import CASCADE, Q, QuerySet
|
||||
from django.db.models.functions import Upper
|
||||
from django.db.models.signals import post_delete, post_save
|
||||
from django.utils.timezone import now as timezone_now
|
||||
from django.utils.translation import gettext_lazy
|
||||
from django_stubs_ext import StrPromise
|
||||
from typing_extensions import override
|
||||
|
||||
from zerver.lib.cache import flush_stream
|
||||
from zerver.lib.timestamp import datetime_to_timestamp
|
||||
from zerver.lib.types import DefaultStreamDict, GroupPermissionSetting
|
||||
from zerver.models.groups import SystemGroups, UserGroup
|
||||
from zerver.models.realms import Realm
|
||||
from zerver.models.recipients import Recipient
|
||||
from zerver.models.users import UserProfile
|
||||
|
||||
|
||||
def generate_email_token_for_stream() -> str:
|
||||
return secrets.token_hex(16)
|
||||
|
||||
|
||||
class Stream(models.Model):
|
||||
MAX_NAME_LENGTH = 60
|
||||
MAX_DESCRIPTION_LENGTH = 1024
|
||||
|
||||
name = models.CharField(max_length=MAX_NAME_LENGTH, db_index=True)
|
||||
realm = models.ForeignKey(Realm, db_index=True, on_delete=CASCADE)
|
||||
date_created = models.DateTimeField(default=timezone_now)
|
||||
deactivated = models.BooleanField(default=False)
|
||||
description = models.CharField(max_length=MAX_DESCRIPTION_LENGTH, default="")
|
||||
rendered_description = models.TextField(default="")
|
||||
|
||||
# Foreign key to the Recipient object for STREAM type messages to this stream.
|
||||
recipient = models.ForeignKey(Recipient, null=True, on_delete=models.SET_NULL)
|
||||
|
||||
# Various permission policy configurations
|
||||
PERMISSION_POLICIES: Dict[str, Dict[str, Any]] = {
|
||||
"web_public": {
|
||||
"invite_only": False,
|
||||
"history_public_to_subscribers": True,
|
||||
"is_web_public": True,
|
||||
"policy_name": gettext_lazy("Web-public"),
|
||||
},
|
||||
"public": {
|
||||
"invite_only": False,
|
||||
"history_public_to_subscribers": True,
|
||||
"is_web_public": False,
|
||||
"policy_name": gettext_lazy("Public"),
|
||||
},
|
||||
"private_shared_history": {
|
||||
"invite_only": True,
|
||||
"history_public_to_subscribers": True,
|
||||
"is_web_public": False,
|
||||
"policy_name": gettext_lazy("Private, shared history"),
|
||||
},
|
||||
"private_protected_history": {
|
||||
"invite_only": True,
|
||||
"history_public_to_subscribers": False,
|
||||
"is_web_public": False,
|
||||
"policy_name": gettext_lazy("Private, protected history"),
|
||||
},
|
||||
# Public streams with protected history are currently only
|
||||
# available in Zephyr realms
|
||||
"public_protected_history": {
|
||||
"invite_only": False,
|
||||
"history_public_to_subscribers": False,
|
||||
"is_web_public": False,
|
||||
"policy_name": gettext_lazy("Public, protected history"),
|
||||
},
|
||||
}
|
||||
invite_only = models.BooleanField(default=False)
|
||||
history_public_to_subscribers = models.BooleanField(default=True)
|
||||
|
||||
# Whether this stream's content should be published by the web-public archive features
|
||||
is_web_public = models.BooleanField(default=False)
|
||||
|
||||
STREAM_POST_POLICY_EVERYONE = 1
|
||||
STREAM_POST_POLICY_ADMINS = 2
|
||||
STREAM_POST_POLICY_RESTRICT_NEW_MEMBERS = 3
|
||||
STREAM_POST_POLICY_MODERATORS = 4
|
||||
# TODO: Implement policy to restrict posting to a user group or admins.
|
||||
|
||||
# Who in the organization has permission to send messages to this stream.
|
||||
stream_post_policy = models.PositiveSmallIntegerField(default=STREAM_POST_POLICY_EVERYONE)
|
||||
POST_POLICIES: Dict[int, StrPromise] = {
|
||||
# These strings should match the strings in the
|
||||
# stream_post_policy_values object in stream_data.js.
|
||||
STREAM_POST_POLICY_EVERYONE: gettext_lazy("All stream members can post"),
|
||||
STREAM_POST_POLICY_ADMINS: gettext_lazy("Only organization administrators can post"),
|
||||
STREAM_POST_POLICY_MODERATORS: gettext_lazy(
|
||||
"Only organization administrators and moderators can post"
|
||||
),
|
||||
STREAM_POST_POLICY_RESTRICT_NEW_MEMBERS: gettext_lazy(
|
||||
"Only organization full members can post"
|
||||
),
|
||||
}
|
||||
STREAM_POST_POLICY_TYPES = list(POST_POLICIES.keys())
|
||||
|
||||
# The unique thing about Zephyr public streams is that we never list their
|
||||
# users. We may try to generalize this concept later, but for now
|
||||
# we just use a concrete field. (Zephyr public streams aren't exactly like
|
||||
# invite-only streams--while both are private in terms of listing users,
|
||||
# for Zephyr we don't even list users to stream members, yet membership
|
||||
# is more public in the sense that you don't need a Zulip invite to join.
|
||||
# This field is populated directly from UserProfile.is_zephyr_mirror_realm,
|
||||
# and the reason for denormalizing field is performance.
|
||||
is_in_zephyr_realm = models.BooleanField(default=False)
|
||||
|
||||
# Used by the e-mail forwarder. The e-mail RFC specifies a maximum
|
||||
# e-mail length of 254, and our max stream length is 30, so we
|
||||
# have plenty of room for the token.
|
||||
email_token = models.CharField(
|
||||
max_length=32,
|
||||
default=generate_email_token_for_stream,
|
||||
unique=True,
|
||||
)
|
||||
|
||||
# For old messages being automatically deleted.
|
||||
# Value NULL means "use retention policy of the realm".
|
||||
# Value -1 means "disable retention policy for this stream unconditionally".
|
||||
# Non-negative values have the natural meaning of "archive messages older than <value> days".
|
||||
MESSAGE_RETENTION_SPECIAL_VALUES_MAP = {
|
||||
"unlimited": -1,
|
||||
"realm_default": None,
|
||||
}
|
||||
message_retention_days = models.IntegerField(null=True, default=None)
|
||||
|
||||
# on_delete field here is set to RESTRICT because we don't want to allow
|
||||
# deleting a user group in case it is referenced by this setting.
|
||||
# We are not using PROTECT since we want to allow deletion of user groups
|
||||
# when realm itself is deleted.
|
||||
can_remove_subscribers_group = models.ForeignKey(UserGroup, on_delete=models.RESTRICT)
|
||||
|
||||
# The very first message ID in the stream. Used to help clients
|
||||
# determine whether they might need to display "more topics" for a
|
||||
# stream based on what messages they have cached.
|
||||
first_message_id = models.IntegerField(null=True, db_index=True)
|
||||
|
||||
stream_permission_group_settings = {
|
||||
"can_remove_subscribers_group": GroupPermissionSetting(
|
||||
require_system_group=True,
|
||||
allow_internet_group=False,
|
||||
allow_owners_group=False,
|
||||
allow_nobody_group=False,
|
||||
allow_everyone_group=True,
|
||||
default_group_name=SystemGroups.ADMINISTRATORS,
|
||||
id_field_name="can_remove_subscribers_group_id",
|
||||
),
|
||||
}
|
||||
|
||||
class Meta:
|
||||
indexes = [
|
||||
models.Index(Upper("name"), name="upper_stream_name_idx"),
|
||||
]
|
||||
|
||||
@override
|
||||
def __str__(self) -> str:
|
||||
return self.name
|
||||
|
||||
def is_public(self) -> bool:
|
||||
# All streams are private in Zephyr mirroring realms.
|
||||
return not self.invite_only and not self.is_in_zephyr_realm
|
||||
|
||||
def is_history_realm_public(self) -> bool:
|
||||
return self.is_public()
|
||||
|
||||
def is_history_public_to_subscribers(self) -> bool:
|
||||
return self.history_public_to_subscribers
|
||||
|
||||
# Stream fields included whenever a Stream object is provided to
|
||||
# Zulip clients via the API. A few details worth noting:
|
||||
# * "id" is represented as "stream_id" in most API interfaces.
|
||||
# * "email_token" is not realm-public and thus is not included here.
|
||||
# * is_in_zephyr_realm is a backend-only optimization.
|
||||
# * "deactivated" streams are filtered from the API entirely.
|
||||
# * "realm" and "recipient" are not exposed to clients via the API.
|
||||
API_FIELDS = [
|
||||
"date_created",
|
||||
"description",
|
||||
"first_message_id",
|
||||
"history_public_to_subscribers",
|
||||
"id",
|
||||
"invite_only",
|
||||
"is_web_public",
|
||||
"message_retention_days",
|
||||
"name",
|
||||
"rendered_description",
|
||||
"stream_post_policy",
|
||||
"can_remove_subscribers_group_id",
|
||||
]
|
||||
|
||||
def to_dict(self) -> DefaultStreamDict:
|
||||
return DefaultStreamDict(
|
||||
can_remove_subscribers_group=self.can_remove_subscribers_group_id,
|
||||
date_created=datetime_to_timestamp(self.date_created),
|
||||
description=self.description,
|
||||
first_message_id=self.first_message_id,
|
||||
history_public_to_subscribers=self.history_public_to_subscribers,
|
||||
invite_only=self.invite_only,
|
||||
is_web_public=self.is_web_public,
|
||||
message_retention_days=self.message_retention_days,
|
||||
name=self.name,
|
||||
rendered_description=self.rendered_description,
|
||||
stream_id=self.id,
|
||||
stream_post_policy=self.stream_post_policy,
|
||||
is_announcement_only=self.stream_post_policy == Stream.STREAM_POST_POLICY_ADMINS,
|
||||
)
|
||||
|
||||
|
||||
post_save.connect(flush_stream, sender=Stream)
|
||||
post_delete.connect(flush_stream, sender=Stream)
|
||||
|
||||
|
||||
def get_realm_stream(stream_name: str, realm_id: int) -> Stream:
|
||||
return Stream.objects.get(name__iexact=stream_name.strip(), realm_id=realm_id)
|
||||
|
||||
|
||||
def get_active_streams(realm: Realm) -> QuerySet[Stream]:
|
||||
"""
|
||||
Return all streams (including invite-only streams) that have not been deactivated.
|
||||
"""
|
||||
return Stream.objects.filter(realm=realm, deactivated=False)
|
||||
|
||||
|
||||
def get_linkable_streams(realm_id: int) -> QuerySet[Stream]:
|
||||
"""
|
||||
This returns the streams that we are allowed to linkify using
|
||||
something like "#frontend" in our markup. For now the business
|
||||
rule is that you can link any stream in the realm that hasn't
|
||||
been deactivated (similar to how get_active_streams works).
|
||||
"""
|
||||
return Stream.objects.filter(realm_id=realm_id, deactivated=False)
|
||||
|
||||
|
||||
def get_stream(stream_name: str, realm: Realm) -> Stream:
|
||||
"""
|
||||
Callers that don't have a Realm object already available should use
|
||||
get_realm_stream directly, to avoid unnecessarily fetching the
|
||||
Realm object.
|
||||
"""
|
||||
return get_realm_stream(stream_name, realm.id)
|
||||
|
||||
|
||||
def get_stream_by_id_in_realm(stream_id: int, realm: Realm) -> Stream:
|
||||
return Stream.objects.select_related("realm", "recipient").get(id=stream_id, realm=realm)
|
||||
|
||||
|
||||
def bulk_get_streams(realm: Realm, stream_names: Set[str]) -> Dict[str, Any]:
|
||||
def fetch_streams_by_name(stream_names: Set[str]) -> QuerySet[Stream]:
|
||||
#
|
||||
# This should be just
|
||||
#
|
||||
# Stream.objects.select_related().filter(name__iexact__in=stream_names,
|
||||
# realm_id=realm_id)
|
||||
#
|
||||
# But chaining __in and __iexact doesn't work with Django's
|
||||
# ORM, so we have the following hack to construct the relevant where clause
|
||||
where_clause = (
|
||||
"upper(zerver_stream.name::text) IN (SELECT upper(name) FROM unnest(%s) AS name)"
|
||||
)
|
||||
return get_active_streams(realm).extra(where=[where_clause], params=(list(stream_names),))
|
||||
|
||||
if not stream_names:
|
||||
return {}
|
||||
streams = list(fetch_streams_by_name(stream_names))
|
||||
return {stream.name.lower(): stream for stream in streams}
|
||||
|
||||
|
||||
class Subscription(models.Model):
|
||||
"""Keeps track of which users are part of the
|
||||
audience for a given Recipient object.
|
||||
|
||||
For 1:1 and group direct message Recipient objects, only the
|
||||
user_profile and recipient fields have any meaning, defining the
|
||||
immutable set of users who are in the audience for that Recipient.
|
||||
|
||||
For Recipient objects associated with a Stream, the remaining
|
||||
fields in this model describe the user's subscription to that stream.
|
||||
"""
|
||||
|
||||
user_profile = models.ForeignKey(UserProfile, on_delete=CASCADE)
|
||||
recipient = models.ForeignKey(Recipient, on_delete=CASCADE)
|
||||
|
||||
# Whether the user has since unsubscribed. We mark Subscription
|
||||
# objects as inactive, rather than deleting them, when a user
|
||||
# unsubscribes, so we can preserve user customizations like
|
||||
# notification settings, stream color, etc., if the user later
|
||||
# resubscribes.
|
||||
active = models.BooleanField(default=True)
|
||||
# This is a denormalization designed to improve the performance of
|
||||
# bulk queries of Subscription objects, Whether the subscribed user
|
||||
# is active tends to be a key condition in those queries.
|
||||
# We intentionally don't specify a default value to promote thinking
|
||||
# about this explicitly, as in some special cases, such as data import,
|
||||
# we may be creating Subscription objects for a user that's deactivated.
|
||||
is_user_active = models.BooleanField()
|
||||
|
||||
# Whether this user had muted this stream.
|
||||
is_muted = models.BooleanField(default=False)
|
||||
|
||||
DEFAULT_STREAM_COLOR = "#c2c2c2"
|
||||
color = models.CharField(max_length=10, default=DEFAULT_STREAM_COLOR)
|
||||
pin_to_top = models.BooleanField(default=False)
|
||||
|
||||
# These fields are stream-level overrides for the user's default
|
||||
# configuration for notification, configured in UserProfile. The
|
||||
# default, None, means we just inherit the user-level default.
|
||||
desktop_notifications = models.BooleanField(null=True, default=None)
|
||||
audible_notifications = models.BooleanField(null=True, default=None)
|
||||
push_notifications = models.BooleanField(null=True, default=None)
|
||||
email_notifications = models.BooleanField(null=True, default=None)
|
||||
wildcard_mentions_notify = models.BooleanField(null=True, default=None)
|
||||
|
||||
class Meta:
|
||||
unique_together = ("user_profile", "recipient")
|
||||
indexes = [
|
||||
models.Index(
|
||||
fields=("recipient", "user_profile"),
|
||||
name="zerver_subscription_recipient_id_user_profile_id_idx",
|
||||
condition=Q(active=True, is_user_active=True),
|
||||
),
|
||||
]
|
||||
|
||||
@override
|
||||
def __str__(self) -> str:
|
||||
return f"{self.user_profile!r} -> {self.recipient!r}"
|
||||
|
||||
# Subscription fields included whenever a Subscription object is provided to
|
||||
# Zulip clients via the API. A few details worth noting:
|
||||
# * These fields will generally be merged with Stream.API_FIELDS
|
||||
# data about the stream.
|
||||
# * "user_profile" is usually implied as full API access to Subscription
|
||||
# is primarily done for the current user; API access to other users'
|
||||
# subscriptions is generally limited to boolean yes/no.
|
||||
# * "id" and "recipient_id" are not included as they are not used
|
||||
# in the Zulip API; it's an internal implementation detail.
|
||||
# Subscription objects are always looked up in the API via
|
||||
# (user_profile, stream) pairs.
|
||||
# * "active" is often excluded in API use cases where it is implied.
|
||||
# * "is_muted" often needs to be copied to not "in_home_view" for
|
||||
# backwards-compatibility.
|
||||
API_FIELDS = [
|
||||
"audible_notifications",
|
||||
"color",
|
||||
"desktop_notifications",
|
||||
"email_notifications",
|
||||
"is_muted",
|
||||
"pin_to_top",
|
||||
"push_notifications",
|
||||
"wildcard_mentions_notify",
|
||||
]
|
||||
|
||||
|
||||
class DefaultStream(models.Model):
|
||||
realm = models.ForeignKey(Realm, on_delete=CASCADE)
|
||||
stream = models.ForeignKey(Stream, on_delete=CASCADE)
|
||||
|
||||
class Meta:
|
||||
unique_together = ("realm", "stream")
|
||||
|
||||
|
||||
class DefaultStreamGroup(models.Model):
|
||||
MAX_NAME_LENGTH = 60
|
||||
|
||||
name = models.CharField(max_length=MAX_NAME_LENGTH, db_index=True)
|
||||
realm = models.ForeignKey(Realm, on_delete=CASCADE)
|
||||
streams = models.ManyToManyField("zerver.Stream")
|
||||
description = models.CharField(max_length=1024, default="")
|
||||
|
||||
class Meta:
|
||||
unique_together = ("realm", "name")
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
return dict(
|
||||
name=self.name,
|
||||
id=self.id,
|
||||
description=self.description,
|
||||
streams=[stream.to_dict() for stream in self.streams.all().order_by("name")],
|
||||
)
|
||||
|
||||
|
||||
def get_default_stream_groups(realm: Realm) -> QuerySet[DefaultStreamGroup]:
|
||||
return DefaultStreamGroup.objects.filter(realm=realm)
|
|
@ -81,13 +81,13 @@ from zerver.models import (
|
|||
Subscription,
|
||||
UserGroup,
|
||||
UserProfile,
|
||||
get_stream,
|
||||
)
|
||||
from zerver.models.groups import SystemGroups
|
||||
from zerver.models.linkifiers import linkifiers_for_realm
|
||||
from zerver.models.realm_emoji import EmojiInfo, get_all_custom_emoji_for_realm
|
||||
from zerver.models.realm_playgrounds import get_realm_playgrounds
|
||||
from zerver.models.realms import RealmDomainDict, get_realm, get_realm_domains
|
||||
from zerver.models.streams import get_stream
|
||||
|
||||
|
||||
class TestRealmAuditLog(ZulipTestCase):
|
||||
|
|
|
@ -24,9 +24,9 @@ from zerver.models import (
|
|||
Subscription,
|
||||
UserProfile,
|
||||
get_bot_services,
|
||||
get_stream,
|
||||
)
|
||||
from zerver.models.realms import get_realm
|
||||
from zerver.models.streams import get_stream
|
||||
from zerver.models.users import get_user, is_cross_realm_bot_email
|
||||
|
||||
|
||||
|
|
|
@ -26,16 +26,9 @@ from zerver.lib.digest import (
|
|||
from zerver.lib.message import get_last_message_id
|
||||
from zerver.lib.streams import create_stream_if_needed
|
||||
from zerver.lib.test_classes import ZulipTestCase
|
||||
from zerver.models import (
|
||||
Message,
|
||||
Realm,
|
||||
RealmAuditLog,
|
||||
Stream,
|
||||
UserActivityInterval,
|
||||
UserProfile,
|
||||
get_stream,
|
||||
)
|
||||
from zerver.models import Message, Realm, RealmAuditLog, Stream, UserActivityInterval, UserProfile
|
||||
from zerver.models.realms import get_realm
|
||||
from zerver.models.streams import get_stream
|
||||
|
||||
|
||||
class TestDigestEmailMessages(ZulipTestCase):
|
||||
|
|
|
@ -37,8 +37,9 @@ from zerver.lib.send_email import FromAddress
|
|||
from zerver.lib.streams import ensure_stream
|
||||
from zerver.lib.test_classes import ZulipTestCase
|
||||
from zerver.lib.test_helpers import mock_queue_publish, most_recent_message, most_recent_usermessage
|
||||
from zerver.models import Attachment, Recipient, Stream, UserProfile, get_stream
|
||||
from zerver.models import Attachment, Recipient, Stream, UserProfile
|
||||
from zerver.models.realms import get_realm
|
||||
from zerver.models.streams import get_stream
|
||||
from zerver.models.users import get_system_bot
|
||||
from zerver.worker.queue_processors import MirrorWorker
|
||||
|
||||
|
|
|
@ -15,7 +15,8 @@ from zerver.actions.user_topics import do_set_user_topic_visibility_policy
|
|||
from zerver.lib.cache import cache_delete, get_muting_users_cache_key
|
||||
from zerver.lib.test_classes import ZulipTestCase
|
||||
from zerver.lib.test_helpers import HostRequestMock, dummy_handler, mock_queue_publish
|
||||
from zerver.models import Recipient, Subscription, UserProfile, UserTopic, get_stream
|
||||
from zerver.models import Recipient, Subscription, UserProfile, UserTopic
|
||||
from zerver.models.streams import get_stream
|
||||
from zerver.tornado.event_queue import (
|
||||
ClientDescriptor,
|
||||
access_client_descriptor,
|
||||
|
|
|
@ -28,15 +28,9 @@ from zerver.lib.test_helpers import (
|
|||
stub_event_queue_user_events,
|
||||
)
|
||||
from zerver.lib.users import get_api_key, get_users_for_api
|
||||
from zerver.models import (
|
||||
CustomProfileField,
|
||||
UserMessage,
|
||||
UserPresence,
|
||||
UserProfile,
|
||||
get_client,
|
||||
get_stream,
|
||||
)
|
||||
from zerver.models import CustomProfileField, UserMessage, UserPresence, UserProfile, get_client
|
||||
from zerver.models.realms import get_realm
|
||||
from zerver.models.streams import get_stream
|
||||
from zerver.models.users import get_system_bot
|
||||
from zerver.tornado.event_queue import (
|
||||
allocate_client_descriptor,
|
||||
|
|
|
@ -241,9 +241,9 @@ from zerver.models import (
|
|||
UserStatus,
|
||||
UserTopic,
|
||||
get_client,
|
||||
get_stream,
|
||||
)
|
||||
from zerver.models.groups import SystemGroups
|
||||
from zerver.models.streams import get_stream
|
||||
from zerver.models.users import get_user_by_delivery_email
|
||||
from zerver.openapi.openapi import validate_against_openapi_schema
|
||||
from zerver.tornado.django_api import send_event
|
||||
|
|
|
@ -11,8 +11,9 @@ from zerver.lib.streams import access_stream_for_send_message
|
|||
from zerver.lib.test_classes import ZulipTestCase
|
||||
from zerver.lib.test_helpers import most_recent_message
|
||||
from zerver.lib.users import is_administrator_role
|
||||
from zerver.models import UserProfile, UserStatus, get_stream
|
||||
from zerver.models import UserProfile, UserStatus
|
||||
from zerver.models.realms import get_realm
|
||||
from zerver.models.streams import get_stream
|
||||
from zerver.models.users import get_user_by_delivery_email
|
||||
|
||||
|
||||
|
|
|
@ -24,8 +24,9 @@ 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.timestamp import datetime_to_timestamp
|
||||
from zerver.models import DefaultStream, Draft, Realm, UserActivity, UserProfile, get_stream
|
||||
from zerver.models import DefaultStream, Draft, Realm, UserActivity, UserProfile
|
||||
from zerver.models.realms import get_realm
|
||||
from zerver.models.streams import get_stream
|
||||
from zerver.models.users import get_system_bot, get_user
|
||||
from zerver.worker.queue_processors import UserActivityWorker
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ from zerver.lib.i18n import get_browser_language_code
|
|||
from zerver.lib.test_classes import ZulipTestCase
|
||||
from zerver.lib.test_helpers import HostRequestMock
|
||||
from zerver.management.commands import makemessages
|
||||
from zerver.models import get_realm_stream
|
||||
from zerver.models.streams import get_realm_stream
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from django.test.client import _MonkeyPatchedWSGIResponse as TestHttpResponse
|
||||
|
|
|
@ -83,13 +83,12 @@ from zerver.models import (
|
|||
UserProfile,
|
||||
UserStatus,
|
||||
UserTopic,
|
||||
get_active_streams,
|
||||
get_client,
|
||||
get_stream,
|
||||
)
|
||||
from zerver.models.groups import SystemGroups
|
||||
from zerver.models.realms import get_realm
|
||||
from zerver.models.recipients import get_huddle_hash
|
||||
from zerver.models.streams import get_active_streams, get_stream
|
||||
from zerver.models.users import get_system_bot, get_user_by_delivery_email
|
||||
|
||||
|
||||
|
|
|
@ -63,10 +63,10 @@ from zerver.models import (
|
|||
UserGroup,
|
||||
UserMessage,
|
||||
UserProfile,
|
||||
get_stream,
|
||||
)
|
||||
from zerver.models.groups import SystemGroups
|
||||
from zerver.models.realms import get_realm
|
||||
from zerver.models.streams import get_stream
|
||||
from zerver.models.users import get_user_by_delivery_email
|
||||
from zerver.views.invite import INVITATION_LINK_VALIDITY_MINUTES, get_invitee_emails_set
|
||||
from zerver.views.registration import accounts_home
|
||||
|
|
|
@ -20,8 +20,9 @@ from zerver.actions.reactions import do_add_reaction
|
|||
from zerver.lib.management import ZulipBaseCommand, check_config
|
||||
from zerver.lib.test_classes import ZulipTestCase
|
||||
from zerver.lib.test_helpers import most_recent_message, stdout_suppressed
|
||||
from zerver.models import Message, Reaction, Realm, Recipient, UserProfile, get_stream
|
||||
from zerver.models import Message, Reaction, Realm, Recipient, UserProfile
|
||||
from zerver.models.realms import get_realm
|
||||
from zerver.models.streams import get_stream
|
||||
from zerver.models.users import get_user_profile_by_email
|
||||
|
||||
|
||||
|
|
|
@ -68,11 +68,11 @@ from zerver.models import (
|
|||
UserMessage,
|
||||
UserProfile,
|
||||
get_client,
|
||||
get_stream,
|
||||
)
|
||||
from zerver.models.groups import SystemGroups
|
||||
from zerver.models.linkifiers import linkifiers_for_realm
|
||||
from zerver.models.realms import get_realm
|
||||
from zerver.models.streams import get_stream
|
||||
|
||||
|
||||
class SimulatedFencedBlockPreprocessor(FencedBlockPreprocessor):
|
||||
|
|
|
@ -12,17 +12,9 @@ from zerver.lib.test_classes import ZulipTestCase
|
|||
from zerver.lib.test_helpers import make_client
|
||||
from zerver.lib.topic import TOPIC_LINKS
|
||||
from zerver.lib.types import DisplayRecipientT, UserDisplayRecipient
|
||||
from zerver.models import (
|
||||
Message,
|
||||
Reaction,
|
||||
Realm,
|
||||
RealmFilter,
|
||||
Recipient,
|
||||
Stream,
|
||||
UserProfile,
|
||||
get_stream,
|
||||
)
|
||||
from zerver.models import Message, Reaction, Realm, RealmFilter, Recipient, Stream, UserProfile
|
||||
from zerver.models.realms import get_realm
|
||||
from zerver.models.streams import get_stream
|
||||
|
||||
|
||||
class MessageDictTest(ZulipTestCase):
|
||||
|
|
|
@ -30,19 +30,11 @@ from zerver.lib.user_topics import (
|
|||
topic_has_visibility_policy,
|
||||
)
|
||||
from zerver.lib.utils import assert_is_not_none
|
||||
from zerver.models import (
|
||||
Message,
|
||||
Realm,
|
||||
Stream,
|
||||
UserGroup,
|
||||
UserMessage,
|
||||
UserProfile,
|
||||
UserTopic,
|
||||
get_stream,
|
||||
)
|
||||
from zerver.models import Message, Realm, Stream, UserGroup, UserMessage, UserProfile, UserTopic
|
||||
from zerver.models.constants import MAX_TOPIC_NAME_LENGTH
|
||||
from zerver.models.groups import SystemGroups
|
||||
from zerver.models.realms import get_realm
|
||||
from zerver.models.streams import get_stream
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from django.test.client import _MonkeyPatchedWSGIResponse as TestHttpResponse
|
||||
|
|
|
@ -8,7 +8,8 @@ from zerver.actions.user_topics import do_set_user_topic_visibility_policy
|
|||
from zerver.lib.push_notifications import get_apns_badge_count, get_apns_badge_count_future
|
||||
from zerver.lib.test_classes import ZulipTestCase
|
||||
from zerver.lib.test_helpers import mock_queue_publish
|
||||
from zerver.models import NotificationTriggers, Subscription, UserPresence, UserTopic, get_stream
|
||||
from zerver.models import NotificationTriggers, Subscription, UserPresence, UserTopic
|
||||
from zerver.models.streams import get_stream
|
||||
from zerver.tornado.event_queue import maybe_enqueue_notifications
|
||||
|
||||
|
||||
|
|
|
@ -58,9 +58,9 @@ from zerver.models import (
|
|||
UserMessage,
|
||||
UserProfile,
|
||||
UserTopic,
|
||||
get_stream,
|
||||
)
|
||||
from zerver.models.realms import get_realm
|
||||
from zerver.models.streams import get_stream
|
||||
from zerver.views.message_fetch import get_messages_backend
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
|
|
@ -33,9 +33,9 @@ from zerver.models import (
|
|||
UserMessage,
|
||||
UserProfile,
|
||||
UserTopic,
|
||||
get_stream,
|
||||
)
|
||||
from zerver.models.realms import get_realm
|
||||
from zerver.models.streams import get_stream
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from django.test.client import _MonkeyPatchedWSGIResponse as TestHttpResponse
|
||||
|
|
|
@ -27,9 +27,10 @@ from zerver.lib.email_notifications import (
|
|||
)
|
||||
from zerver.lib.send_email import FromAddress
|
||||
from zerver.lib.test_classes import ZulipTestCase
|
||||
from zerver.models import NotificationTriggers, UserMessage, UserProfile, UserTopic, get_stream
|
||||
from zerver.models import NotificationTriggers, UserMessage, UserProfile, UserTopic
|
||||
from zerver.models.realm_emoji import get_name_keyed_dict_for_active_realm_emoji
|
||||
from zerver.models.realms import get_realm
|
||||
from zerver.models.streams import get_stream
|
||||
|
||||
|
||||
class TestMessageNotificationEmails(ZulipTestCase):
|
||||
|
|
|
@ -56,12 +56,12 @@ from zerver.models import (
|
|||
UserGroup,
|
||||
UserMessage,
|
||||
UserProfile,
|
||||
get_stream,
|
||||
)
|
||||
from zerver.models.constants import MAX_TOPIC_NAME_LENGTH
|
||||
from zerver.models.groups import SystemGroups
|
||||
from zerver.models.realms import get_realm
|
||||
from zerver.models.recipients import get_or_create_huddle
|
||||
from zerver.models.streams import get_stream
|
||||
from zerver.models.users import get_system_bot, get_user
|
||||
from zerver.views.message_send import InvalidMirrorInputError
|
||||
|
||||
|
|
|
@ -6,8 +6,9 @@ from zerver.actions.streams import do_change_stream_permission
|
|||
from zerver.lib.test_classes import ZulipTestCase
|
||||
from zerver.lib.test_helpers import timeout_mock
|
||||
from zerver.lib.timeout import TimeoutExpiredError
|
||||
from zerver.models import Message, UserMessage, get_client, get_stream
|
||||
from zerver.models import Message, UserMessage, get_client
|
||||
from zerver.models.realms import get_realm
|
||||
from zerver.models.streams import get_stream
|
||||
|
||||
|
||||
class TopicHistoryTest(ZulipTestCase):
|
||||
|
|
|
@ -12,8 +12,9 @@ from zerver.lib.outgoing_webhook import get_service_interface_class, process_suc
|
|||
from zerver.lib.test_classes import ZulipTestCase
|
||||
from zerver.lib.timestamp import datetime_to_timestamp
|
||||
from zerver.lib.topic import TOPIC_NAME
|
||||
from zerver.models import SLACK_INTERFACE, Message, NotificationTriggers, get_stream
|
||||
from zerver.models import SLACK_INTERFACE, Message, NotificationTriggers
|
||||
from zerver.models.realms import get_realm
|
||||
from zerver.models.streams import get_stream
|
||||
from zerver.models.users import get_user
|
||||
from zerver.openapi.openapi import validate_against_openapi_schema
|
||||
|
||||
|
|
|
@ -19,8 +19,9 @@ from zerver.lib.test_classes import ZulipTestCase
|
|||
from zerver.lib.topic import TOPIC_NAME
|
||||
from zerver.lib.url_encoding import near_message_url
|
||||
from zerver.lib.users import add_service
|
||||
from zerver.models import Recipient, Service, UserProfile, get_stream
|
||||
from zerver.models import Recipient, Service, UserProfile
|
||||
from zerver.models.realms import get_realm
|
||||
from zerver.models.streams import get_stream
|
||||
|
||||
|
||||
class ResponseMock:
|
||||
|
|
|
@ -91,9 +91,9 @@ from zerver.models import (
|
|||
UserProfile,
|
||||
UserTopic,
|
||||
get_client,
|
||||
get_stream,
|
||||
)
|
||||
from zerver.models.realms import get_realm
|
||||
from zerver.models.streams import get_stream
|
||||
from zilencer.models import RemoteZulipServerAuditLog
|
||||
from zilencer.views import DevicesToCleanUpDict
|
||||
|
||||
|
|
|
@ -32,9 +32,9 @@ from zerver.models import (
|
|||
UserActivity,
|
||||
UserProfile,
|
||||
get_client,
|
||||
get_stream,
|
||||
)
|
||||
from zerver.models.realms import get_realm
|
||||
from zerver.models.streams import get_stream
|
||||
from zerver.tornado.event_queue import build_offline_notification
|
||||
from zerver.worker import queue_processors
|
||||
from zerver.worker.queue_processors import (
|
||||
|
|
|
@ -53,10 +53,10 @@ from zerver.models import (
|
|||
UserGroupMembership,
|
||||
UserMessage,
|
||||
UserProfile,
|
||||
get_stream,
|
||||
)
|
||||
from zerver.models.groups import SystemGroups
|
||||
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
|
||||
|
||||
|
||||
|
|
|
@ -38,9 +38,9 @@ from zerver.models import (
|
|||
SubMessage,
|
||||
UserMessage,
|
||||
get_client,
|
||||
get_stream,
|
||||
)
|
||||
from zerver.models.realms import get_realm
|
||||
from zerver.models.streams import get_stream
|
||||
from zerver.models.users import get_system_bot
|
||||
|
||||
# Class with helper functions useful for testing archiving of reactions:
|
||||
|
|
|
@ -76,9 +76,9 @@ from zerver.models import (
|
|||
Subscription,
|
||||
UserMessage,
|
||||
UserProfile,
|
||||
get_stream,
|
||||
)
|
||||
from zerver.models.realms import get_realm
|
||||
from zerver.models.streams import get_stream
|
||||
from zerver.models.users import get_system_bot, get_user, get_user_by_delivery_email
|
||||
from zerver.views.auth import redirect_and_log_into_subdomain, start_two_factor_auth
|
||||
from zerver.views.development.registration import confirmation_key
|
||||
|
|
|
@ -27,9 +27,9 @@ from zerver.models import (
|
|||
UserActivity,
|
||||
UserMessage,
|
||||
UserProfile,
|
||||
get_stream,
|
||||
)
|
||||
from zerver.models.realms import get_realm
|
||||
from zerver.models.streams import get_stream
|
||||
|
||||
logger_string = "zulip.soft_deactivation"
|
||||
|
||||
|
|
|
@ -104,12 +104,11 @@ from zerver.models import (
|
|||
UserGroup,
|
||||
UserMessage,
|
||||
UserProfile,
|
||||
get_default_stream_groups,
|
||||
get_stream,
|
||||
validate_attachment_request,
|
||||
validate_attachment_request_for_spectator_access,
|
||||
)
|
||||
from zerver.models.realms import get_realm
|
||||
from zerver.models.streams import get_default_stream_groups, get_stream
|
||||
from zerver.models.users import active_non_guest_user_ids, get_user, get_user_profile_by_id_in_realm
|
||||
from zerver.views.streams import compose_views
|
||||
|
||||
|
|
|
@ -12,7 +12,8 @@ from zerver.lib.stream_topic import StreamTopicTarget
|
|||
from zerver.lib.test_classes import ZulipTestCase
|
||||
from zerver.lib.test_helpers import get_subscription
|
||||
from zerver.lib.user_topics import get_topic_mutes, topic_has_visibility_policy
|
||||
from zerver.models import UserProfile, UserTopic, get_stream
|
||||
from zerver.models import UserProfile, UserTopic
|
||||
from zerver.models.streams import get_stream
|
||||
|
||||
|
||||
class MutedTopicsTestsDeprecated(ZulipTestCase):
|
||||
|
|
|
@ -74,11 +74,11 @@ from zerver.models import (
|
|||
UserTopic,
|
||||
check_valid_user_ids,
|
||||
get_client,
|
||||
get_stream,
|
||||
)
|
||||
from zerver.models.groups import SystemGroups
|
||||
from zerver.models.prereg_users import filter_to_valid_prereg_users
|
||||
from zerver.models.realms import InvalidFakeEmailDomainError, get_fake_email_domain, get_realm
|
||||
from zerver.models.streams import get_stream
|
||||
from zerver.models.users import (
|
||||
get_source_profile,
|
||||
get_system_bot,
|
||||
|
|
|
@ -16,8 +16,9 @@ from zerver.actions.users import change_user_is_active
|
|||
from zerver.lib.email_notifications import enqueue_welcome_emails, send_account_registered_email
|
||||
from zerver.lib.request import REQ, has_request_variables
|
||||
from zerver.lib.response import json_success
|
||||
from zerver.models import Realm, get_realm_stream
|
||||
from zerver.models import Realm
|
||||
from zerver.models.realms import get_realm
|
||||
from zerver.models.streams import get_realm_stream
|
||||
from zerver.models.users import get_user_by_delivery_email
|
||||
from zerver.views.invite import INVITATION_LINK_VALIDITY_MINUTES
|
||||
from zproject.email_backends import get_forward_address, set_forward_address
|
||||
|
|
|
@ -80,7 +80,6 @@ from zerver.models import (
|
|||
RealmUserDefault,
|
||||
Stream,
|
||||
UserProfile,
|
||||
get_default_stream_groups,
|
||||
)
|
||||
from zerver.models.constants import MAX_LANGUAGE_ID_LENGTH
|
||||
from zerver.models.realms import (
|
||||
|
@ -91,6 +90,7 @@ from zerver.models.realms import (
|
|||
get_realm,
|
||||
name_changes_disabled,
|
||||
)
|
||||
from zerver.models.streams import get_default_stream_groups
|
||||
from zerver.models.users import get_source_profile, get_user_by_delivery_email
|
||||
from zerver.views.auth import (
|
||||
create_preregistration_realm,
|
||||
|
|
|
@ -66,10 +66,10 @@ from zerver.models import (
|
|||
UserProfile,
|
||||
flush_alert_word,
|
||||
get_client,
|
||||
get_stream,
|
||||
)
|
||||
from zerver.models.realms import get_realm
|
||||
from zerver.models.recipients import get_or_create_huddle
|
||||
from zerver.models.streams import get_stream
|
||||
from zerver.models.users import get_user, get_user_by_delivery_email, get_user_profile_by_id
|
||||
from zilencer.models import RemoteRealm, RemoteZulipServer
|
||||
from zilencer.views import update_remote_realm_data_for_server
|
||||
|
|
Loading…
Reference in New Issue