Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/api/model/events.dart
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,7 @@ class ChannelUpdateEvent extends ChannelEvent {
case ChannelPropertyName.canAddSubscribersGroup:
case ChannelPropertyName.canDeleteAnyMessageGroup:
case ChannelPropertyName.canDeleteOwnMessageGroup:
case ChannelPropertyName.canSendMessageGroup:
case ChannelPropertyName.canSubscribeGroup:
return GroupSettingValue.fromJson(value);
case ChannelPropertyName.streamWeeklyTraffic:
Expand Down
1 change: 1 addition & 0 deletions lib/api/model/events.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion lib/api/model/model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -649,12 +649,13 @@ class ZulipStream {
bool historyPublicToSubscribers;
int? messageRetentionDays;
@JsonKey(name: 'stream_post_policy')
ChannelPostPolicy channelPostPolicy;
ChannelPostPolicy? channelPostPolicy; // TODO(server-10) remove
// final bool isAnnouncementOnly; // deprecated for `channelPostPolicy`; ignore

GroupSettingValue? canAddSubscribersGroup; // TODO(server-10)
GroupSettingValue? canDeleteAnyMessageGroup; // TODO(server-11)
GroupSettingValue? canDeleteOwnMessageGroup; // TODO(server-11)
GroupSettingValue? canSendMessageGroup; // TODO(server-10)
GroupSettingValue? canSubscribeGroup; // TODO(server-10)

// TODO(server-8): added in FL 199, was previously only on [Subscription] objects
Expand All @@ -676,6 +677,7 @@ class ZulipStream {
required this.canAddSubscribersGroup,
required this.canDeleteAnyMessageGroup,
required this.canDeleteOwnMessageGroup,
required this.canSendMessageGroup,
required this.canSubscribeGroup,
required this.streamWeeklyTraffic,
});
Expand All @@ -698,6 +700,7 @@ class ZulipStream {
canAddSubscribersGroup: subscription.canAddSubscribersGroup,
canDeleteAnyMessageGroup: subscription.canDeleteAnyMessageGroup,
canDeleteOwnMessageGroup: subscription.canDeleteOwnMessageGroup,
canSendMessageGroup: subscription.canSendMessageGroup,
canSubscribeGroup: subscription.canSubscribeGroup,
streamWeeklyTraffic: subscription.streamWeeklyTraffic,
);
Expand Down Expand Up @@ -733,6 +736,7 @@ enum ChannelPropertyName {
canAddSubscribersGroup,
canDeleteAnyMessageGroup,
canDeleteOwnMessageGroup,
canSendMessageGroup,
canSubscribeGroup,
streamWeeklyTraffic;

Expand Down Expand Up @@ -817,6 +821,7 @@ class Subscription extends ZulipStream {
required super.canAddSubscribersGroup,
required super.canDeleteAnyMessageGroup,
required super.canDeleteOwnMessageGroup,
required super.canSendMessageGroup,
required super.canSubscribeGroup,
required super.streamWeeklyTraffic,
required this.desktopNotifications,
Expand Down
13 changes: 11 additions & 2 deletions lib/api/model/model.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 26 additions & 5 deletions lib/model/channel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -192,17 +192,36 @@ mixin ChannelStore on UserStore {
required ZulipStream inChannel,
required DateTime byDate,
}) {
// (selfHasPermissionForGroupSetting isn't equipped to handle the old-server
// fallback logic for this specific permission; it's dynamic and depends on
// channelPostPolicy, so we do our own null check here.)
if (inChannel.canSendMessageGroup != null) {
return selfHasPermissionForGroupSetting(inChannel.canSendMessageGroup!,
GroupSettingType.stream, 'can_send_message_group');
} else if (inChannel.channelPostPolicy != null) {
return _selfPassesLegacyChannelPostPolicy(inChannel: inChannel, atDate: byDate);
} else {
assert(false); // TODO(log)
return true;
}
}

bool _selfPassesLegacyChannelPostPolicy({
required ZulipStream inChannel,
required DateTime atDate,
}) {
assert(inChannel.channelPostPolicy != null);
final role = selfUser.role;
// We let the users with [unknown] role to send the message, then the server
// will decide to accept it or not based on its actual role.
if (role == UserRole.unknown) return true;

switch (inChannel.channelPostPolicy) {
// (Could early-return true on [UserRole.unknown],
// but pre-333 servers shouldn't be giving us an unknown role.)

switch (inChannel.channelPostPolicy!) {
case ChannelPostPolicy.any: return true;
case ChannelPostPolicy.fullMembers: {
if (!role.isAtLeast(UserRole.member)) return false;
if (role == UserRole.member) {
return selfHasPassedWaitingPeriod(byDate: byDate);
return selfHasPassedWaitingPeriod(byDate: atDate);
}
return true;
}
Expand Down Expand Up @@ -405,6 +424,8 @@ class ChannelStoreImpl extends HasUserStore with ChannelStore {
stream.canDeleteAnyMessageGroup = event.value as GroupSettingValue;
case ChannelPropertyName.canDeleteOwnMessageGroup:
stream.canDeleteOwnMessageGroup = event.value as GroupSettingValue;
case ChannelPropertyName.canSendMessageGroup:
stream.canSendMessageGroup = event.value as GroupSettingValue;
case ChannelPropertyName.canSubscribeGroup:
stream.canSubscribeGroup = event.value as GroupSettingValue;
case ChannelPropertyName.streamWeeklyTraffic:
Expand Down
10 changes: 10 additions & 0 deletions test/example_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -461,9 +461,16 @@ ZulipStream stream({
GroupSettingValue? canAddSubscribersGroup,
GroupSettingValue? canDeleteAnyMessageGroup,
GroupSettingValue? canDeleteOwnMessageGroup,
GroupSettingValue? canSendMessageGroup,
GroupSettingValue? canSubscribeGroup,
int? streamWeeklyTraffic,
}) {
if (channelPostPolicy == null) {
// Set a default for realmCanDeleteOwnMessageGroup, but only if we're
// not trying to test legacy behavior with channelPostPolicy.
canSendMessageGroup ??= groupSetting(members: [selfUser.userId]);
}

_checkPositive(streamId, 'stream ID');
_checkPositive(firstMessageId, 'message ID');
var effectiveStreamId = streamId ?? _nextStreamId();
Expand All @@ -485,6 +492,7 @@ ZulipStream stream({
canAddSubscribersGroup: canAddSubscribersGroup ?? GroupSettingValueNamed(nobodyGroup.id),
canDeleteAnyMessageGroup: canDeleteAnyMessageGroup ?? GroupSettingValueNamed(nobodyGroup.id),
canDeleteOwnMessageGroup: canDeleteOwnMessageGroup ?? GroupSettingValueNamed(nobodyGroup.id),
canSendMessageGroup: canSendMessageGroup,
canSubscribeGroup: canSubscribeGroup ?? GroupSettingValueNamed(nobodyGroup.id),
streamWeeklyTraffic: streamWeeklyTraffic,
);
Expand Down Expand Up @@ -528,6 +536,7 @@ Subscription subscription(
canAddSubscribersGroup: stream.canAddSubscribersGroup,
canDeleteAnyMessageGroup: stream.canDeleteAnyMessageGroup,
canDeleteOwnMessageGroup: stream.canDeleteOwnMessageGroup,
canSendMessageGroup: stream.canSendMessageGroup,
canSubscribeGroup: stream.canSubscribeGroup,
streamWeeklyTraffic: stream.streamWeeklyTraffic,
desktopNotifications: desktopNotifications ?? false,
Expand Down Expand Up @@ -1206,6 +1215,7 @@ ChannelUpdateEvent channelUpdateEvent(
case ChannelPropertyName.canAddSubscribersGroup:
case ChannelPropertyName.canDeleteAnyMessageGroup:
case ChannelPropertyName.canDeleteOwnMessageGroup:
case ChannelPropertyName.canSendMessageGroup:
case ChannelPropertyName.canSubscribeGroup:
assert(value is GroupSettingValue);
case ChannelPropertyName.streamWeeklyTraffic:
Expand Down
36 changes: 30 additions & 6 deletions test/model/channel_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@ import 'package:zulip/model/channel.dart';
import '../api/model/model_checks.dart';
import '../example_data.dart' as eg;
import '../stdlib_checks.dart';
import 'binding.dart';
import 'test_store.dart';

void main() {
TestZulipBinding.ensureInitialized();

group('Unified stream/sub data', () {
/// Check that `streams`, `streamsByName`, and `subscriptions` all agree
/// and point to the same objects where applicable.
Expand Down Expand Up @@ -455,34 +458,55 @@ void main() {
});
});

group('hasPostingPermission', () {
group('selfCanSendMessage', () {
test('in group', () {
addTearDown(testBinding.reset);
final now = testBinding.utcNow();

final canSendMessageGroup = eg.groupSetting(members: [eg.selfUser.userId]);
final channel = eg.stream(canSendMessageGroup: canSendMessageGroup);
final store = eg.store(
initialSnapshot: eg.initialSnapshot(streams: [channel]));
check(store.selfCanSendMessage(inChannel: channel, byDate: now))
.isTrue();
});

test('not in group', () {
addTearDown(testBinding.reset);
final now = testBinding.utcNow();

final canSendMessageGroup = eg.groupSetting(members: []);
final channel = eg.stream(canSendMessageGroup: canSendMessageGroup);
final store = eg.store(
initialSnapshot: eg.initialSnapshot(streams: [channel]));
check(store.selfCanSendMessage(inChannel: channel, byDate: now))
.isFalse();
});
});

group('selfCanSendMessage, legacy', () {
final testCases = [
(ChannelPostPolicy.unknown, UserRole.unknown, true),
(ChannelPostPolicy.unknown, UserRole.guest, true),
(ChannelPostPolicy.unknown, UserRole.member, true),
(ChannelPostPolicy.unknown, UserRole.moderator, true),
(ChannelPostPolicy.unknown, UserRole.administrator, true),
(ChannelPostPolicy.unknown, UserRole.owner, true),
(ChannelPostPolicy.any, UserRole.unknown, true),
(ChannelPostPolicy.any, UserRole.guest, true),
(ChannelPostPolicy.any, UserRole.member, true),
(ChannelPostPolicy.any, UserRole.moderator, true),
(ChannelPostPolicy.any, UserRole.administrator, true),
(ChannelPostPolicy.any, UserRole.owner, true),
(ChannelPostPolicy.fullMembers, UserRole.unknown, true),
(ChannelPostPolicy.fullMembers, UserRole.guest, false),
// The fullMembers/member case gets its own tests further below.
// (ChannelPostPolicy.fullMembers, UserRole.member, /* complicated */),
(ChannelPostPolicy.fullMembers, UserRole.moderator, true),
(ChannelPostPolicy.fullMembers, UserRole.administrator, true),
(ChannelPostPolicy.fullMembers, UserRole.owner, true),
(ChannelPostPolicy.moderators, UserRole.unknown, true),
(ChannelPostPolicy.moderators, UserRole.guest, false),
(ChannelPostPolicy.moderators, UserRole.member, false),
(ChannelPostPolicy.moderators, UserRole.moderator, true),
(ChannelPostPolicy.moderators, UserRole.administrator, true),
(ChannelPostPolicy.moderators, UserRole.owner, true),
(ChannelPostPolicy.administrators, UserRole.unknown, true),
(ChannelPostPolicy.administrators, UserRole.guest, false),
(ChannelPostPolicy.administrators, UserRole.member, false),
(ChannelPostPolicy.administrators, UserRole.moderator, false),
Expand Down