Skip to content

Commit 7aaff57

Browse files
committed
store: Implement selfCanDeleteMessage
1 parent 9132b25 commit 7aaff57

File tree

3 files changed

+641
-1
lines changed

3 files changed

+641
-1
lines changed

lib/model/store.dart

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,128 @@ class PerAccountStore extends PerAccountStoreBase with
673673

674674
final AutocompleteViewManager autocompleteViewManager = AutocompleteViewManager();
675675

676+
/// Whether the user has permission to delete the message, as of [byDate].
677+
///
678+
/// For a value of [byDate], use [ZulipBinding.instance.utcNow].
679+
// There might be a better place for this to live…but it uses
680+
// message, channel, self-user, and user-group data, so seemed simplest to
681+
// put it here where all that is available.
682+
bool selfCanDeleteMessage(int messageId, {required DateTime byDate}) {
683+
// Compare web's message_delete.get_deletability.
684+
685+
final message = messages[messageId];
686+
if (message == null) return false; // TODO(log)
687+
688+
final ZulipStream? channel;
689+
if (message is StreamMessage) {
690+
channel = streams[message.streamId];
691+
// TODO(log) if channel null here?
692+
} else {
693+
channel = null;
694+
}
695+
696+
if (channel != null && channel.isArchived == true) {
697+
return false;
698+
}
699+
700+
if (realmCanDeleteAnyMessageGroup != null
701+
&& selfHasPermissionForGroupSetting(realmCanDeleteAnyMessageGroup!,
702+
GroupSettingType.realm, 'can_delete_any_message_group')) {
703+
// realmCanDeleteAnyMessageGroup always present as of FL 281.
704+
// TODO(server-10) make required, simplify, delete this FL-281 comment
705+
return true;
706+
}
707+
708+
if (channel != null) {
709+
if (channel.canDeleteAnyMessageGroup != null
710+
&& selfHasPermissionForGroupSetting(channel.canDeleteAnyMessageGroup!,
711+
GroupSettingType.stream, 'can_delete_any_message_group')) {
712+
// channel.canDeleteAnyMessageGroup always present as of FL 407.
713+
// TODO(server-11) make required, simplify, delete this FL-407 comment
714+
return true;
715+
}
716+
}
717+
718+
final sender = getUser(message.senderId);
719+
if (sender == null) return false;
720+
721+
if (
722+
sender.userId != selfUserId
723+
&& !(sender.isBot && sender.botOwnerId == selfUserId)
724+
) {
725+
return false;
726+
}
727+
728+
// Web returns false here for local-echoed message objects;
729+
// that's impossible here because `message` can't be an [OutboxMessage]
730+
// (it's a [Message] from [MessageStore.messages]).
731+
732+
if (realmCanDeleteOwnMessageGroup != null) {
733+
// realmCanDeleteOwnMessageGroup always present as of FL 291.
734+
// TODO(server-10) make required, simplify, delete this FL-291 comment
735+
if (!selfHasPermissionForGroupSetting(realmCanDeleteOwnMessageGroup!,
736+
GroupSettingType.realm, 'can_delete_own_message_group')) {
737+
if (channel == null) {
738+
// i.e. this is a DM
739+
return false;
740+
}
741+
742+
if (
743+
channel.canDeleteOwnMessageGroup == null
744+
|| !selfHasPermissionForGroupSetting(channel.canDeleteOwnMessageGroup!,
745+
GroupSettingType.stream, 'can_delete_own_message_group')
746+
) {
747+
// channel.canDeleteOwnMessageGroup always present as of FL 407.
748+
// TODO(server-11) make required, simplify, delete this FL-407 comment
749+
return false;
750+
}
751+
}
752+
}
753+
754+
if (
755+
realmDeleteOwnMessagePolicy != null
756+
&& !_selfPassesLegacyDeleteMessagePolicy(messageId, byDate: byDate)
757+
) {
758+
// realmDeleteOwnMessagePolicy is removed in FL 291
759+
// TODO(server-10) remove that, simplify, delete this FL-291 comment
760+
return false;
761+
}
762+
763+
if (realmMessageContentDeleteLimitSeconds == null) {
764+
// i.e., no limit
765+
return true;
766+
}
767+
768+
return byDate.millisecondsSinceEpoch ~/ 1000 - message.timestamp
769+
<= realmMessageContentDeleteLimitSeconds!;
770+
}
771+
772+
bool _selfPassesLegacyDeleteMessagePolicy(int messageId, {required DateTime byDate}) {
773+
assert(realmDeleteOwnMessagePolicy != null);
774+
final role = selfUser.role;
775+
776+
// (Could early-return true on [UserRole.unknown],
777+
// but pre-291 servers shouldn't be giving us an unknown role.)
778+
779+
switch (realmDeleteOwnMessagePolicy!) {
780+
case RealmDeleteOwnMessagePolicy.members:
781+
return true;
782+
case RealmDeleteOwnMessagePolicy.admins:
783+
return role.isAtLeast(UserRole.administrator);
784+
case RealmDeleteOwnMessagePolicy.fullMembers: {
785+
if (!role.isAtLeast(UserRole.member)) return false;
786+
if (role == UserRole.member) {
787+
return hasPassedWaitingPeriod(selfUser, byDate: byDate);
788+
}
789+
return true;
790+
}
791+
case RealmDeleteOwnMessagePolicy.moderators:
792+
return role.isAtLeast(UserRole.moderator);
793+
case RealmDeleteOwnMessagePolicy.everyone:
794+
return true;
795+
}
796+
}
797+
676798
// End of data.
677799
//|//////////////////////////////////////////////////////////////
678800

test/example_data.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ User user({
271271
String? dateJoined,
272272
bool? isActive,
273273
bool? isBot,
274+
int? botOwnerId,
274275
UserRole? role,
275276
String? avatarUrl,
276277
Map<int, ProfileFieldUserData>? profileData,
@@ -286,7 +287,7 @@ User user({
286287
isActive: isActive ?? true,
287288
isBot: isBot ?? false,
288289
botType: null,
289-
botOwnerId: null,
290+
botOwnerId: botOwnerId,
290291
role: role ?? UserRole.member,
291292
timezone: 'UTC',
292293
avatarUrl: avatarUrl,

0 commit comments

Comments
 (0)