def json_stream_exists( request: HttpRequest, user_profile: UserProfile, stream_name: str = REQ("stream"), autosubscribe: bool = REQ(json_validator=check_bool, default=False), ) -> HttpResponse: check_stream_name(stream_name) try: (stream, sub) = access_stream_by_name(user_profile, stream_name) except JsonableError as e: raise ResourceNotFoundError(e.msg) # access_stream functions return a subscription if and only if we # are already subscribed. result = {"subscribed": sub is not None} # If we got here, we're either subscribed or the stream is public. # So if we're not yet subscribed and autosubscribe is enabled, we # should join. if sub is None and autosubscribe: bulk_add_subscriptions(user_profile.realm, [stream], [user_profile], acting_user=user_profile) result["subscribed"] = True return json_success(request, data=result) # results are ignored for HEAD requests
def check_stream_name_available(realm: Realm, name: str) -> None: check_stream_name(name) try: get_stream(name, realm) raise JsonableError( _("Stream name '{}' is already taken.").format(name)) except Stream.DoesNotExist: pass
def list_to_streams( streams_raw: Collection[StreamDict], user_profile: UserProfile, autocreate: bool = False, admin_access_required: bool = False, ) -> Tuple[List[Stream], List[Stream]]: """Converts list of dicts to a list of Streams, validating input in the process For each stream name, we validate it to ensure it meets our requirements for a proper stream name using check_stream_name. This function in autocreate mode should be atomic: either an exception will be raised during a precheck, or all the streams specified will have been created if applicable. @param streams_raw The list of stream dictionaries to process; names should already be stripped of whitespace by the caller. @param user_profile The user for whom we are retrieving the streams @param autocreate Whether we should create streams if they don't already exist """ # Validate all streams, getting extant ones, then get-or-creating the rest. stream_set = {stream_dict["name"] for stream_dict in streams_raw} for stream_name in stream_set: # Stream names should already have been stripped by the # caller, but it makes sense to verify anyway. assert stream_name == stream_name.strip() check_stream_name(stream_name) existing_streams: List[Stream] = [] missing_stream_dicts: List[StreamDict] = [] existing_stream_map = bulk_get_streams(user_profile.realm, stream_set) if admin_access_required: existing_recipient_ids = [ stream.recipient_id for stream in existing_stream_map.values() ] subs = Subscription.objects.filter( user_profile=user_profile, recipient_id__in=existing_recipient_ids, active=True) sub_map = {sub.recipient_id: sub for sub in subs} for stream in existing_stream_map.values(): sub = sub_map.get(stream.recipient_id, None) check_stream_access_for_delete_or_update(user_profile, stream, sub) message_retention_days_not_none = False web_public_stream_requested = False for stream_dict in streams_raw: stream_name = stream_dict["name"] stream = existing_stream_map.get(stream_name.lower()) if stream is None: if stream_dict.get("message_retention_days", None) is not None: message_retention_days_not_none = True missing_stream_dicts.append(stream_dict) if autocreate and stream_dict["is_web_public"]: web_public_stream_requested = True else: existing_streams.append(stream) if len(missing_stream_dicts) == 0: # This is the happy path for callers who expected all of these # streams to exist already. created_streams: List[Stream] = [] else: # autocreate=True path starts here for stream_dict in missing_stream_dicts: invite_only = stream_dict.get("invite_only", False) if invite_only and not user_profile.can_create_private_streams(): raise JsonableError(_("Insufficient permission")) if not invite_only and not user_profile.can_create_public_streams( ): raise JsonableError(_("Insufficient permission")) if not autocreate: raise JsonableError( _("Stream(s) ({}) do not exist").format( ", ".join(stream_dict["name"] for stream_dict in missing_stream_dicts), )) if web_public_stream_requested: if not user_profile.realm.web_public_streams_enabled(): raise JsonableError(_("Web public streams are not enabled.")) if not user_profile.can_create_web_public_streams(): # We set create_web_public_stream_policy to allow only organization owners # to create web-public streams, because of their sensitive nature. raise JsonableError(_("Insufficient permission")) if message_retention_days_not_none: if not user_profile.is_realm_owner: raise OrganizationOwnerRequired() user_profile.realm.ensure_not_on_limited_plan() # We already filtered out existing streams, so dup_streams # will normally be an empty list below, but we protect against somebody # else racing to create the same stream. (This is not an entirely # paranoid approach, since often on Zulip two people will discuss # creating a new stream, and both people eagerly do it.) created_streams, dup_streams = create_streams_if_needed( realm=user_profile.realm, stream_dicts=missing_stream_dicts, acting_user=user_profile) existing_streams += dup_streams return existing_streams, created_streams