diff --git a/framework/core/js/src/admin/components/PermissionGrid.tsx b/framework/core/js/src/admin/components/PermissionGrid.tsx index 407552cd2..da88b3d4f 100644 --- a/framework/core/js/src/admin/components/PermissionGrid.tsx +++ b/framework/core/js/src/admin/components/PermissionGrid.tsx @@ -288,6 +288,31 @@ export default class PermissionGrid { + const minutes = parseInt(app.data.settings.allow_hide_own_posts, 10); + + return SettingDropdown.component({ + defaultLabel: minutes + ? app.translator.trans('core.admin.permissions_controls.allow_some_minutes_button', { count: minutes }) + : app.translator.trans('core.admin.permissions_controls.allow_indefinitely_button'), + key: 'allow_hide_own_posts', + options: [ + { value: '-1', label: app.translator.trans('core.admin.permissions_controls.allow_indefinitely_button') }, + { value: '10', label: app.translator.trans('core.admin.permissions_controls.allow_ten_minutes_button') }, + { value: 'reply', label: app.translator.trans('core.admin.permissions_controls.allow_until_reply_button') }, + { value: '0', label: app.translator.trans('core.admin.permissions_controls.allow_never_button') }, + ], + }); + }, + }, + 80 + ); + items.merge(app.extensionData.getAllExtensionPermissions('reply')); return items; diff --git a/framework/core/locale/core.yml b/framework/core/locale/core.yml index c35d0964e..c021fa1f9 100644 --- a/framework/core/locale/core.yml +++ b/framework/core/locale/core.yml @@ -194,6 +194,7 @@ core: # These translations are used in the Permissions page of the admin interface. permissions: + allow_hide_own_posts_label: Allow deleting own posts allow_post_editing_label: Allow post editing allow_renaming_label: Allow renaming create_access_token_label: Create access token @@ -228,6 +229,7 @@ core: # These translations are used in the dropdown menus on the Permissions page. permissions_controls: allow_indefinitely_button: Indefinitely + allow_never_button: => core.ref.never allow_some_minutes_button: "{count, plural, one {For # minute} other {For # minutes}}" allow_ten_minutes_button: For 10 minutes allow_until_reply_button: Until next reply @@ -489,7 +491,7 @@ core: log_out_button: => core.ref.log_out hide_access_token: Hide Token last_activity: Last activity - never: Never + never: => core.ref.never new_access_token_button: => core.ref.new_token new_access_token_modal: submit_button: Create Token @@ -802,6 +804,7 @@ core: log_in: Log In log_out: Log Out mark_all_as_read: Mark All as Read + never: Never new_token: New Token next_page: Next Page notifications: Notifications diff --git a/framework/core/src/Install/Steps/WriteSettings.php b/framework/core/src/Install/Steps/WriteSettings.php index 2d4dfedec..c23ca005d 100644 --- a/framework/core/src/Install/Steps/WriteSettings.php +++ b/framework/core/src/Install/Steps/WriteSettings.php @@ -56,6 +56,7 @@ class WriteSettings implements Step private function getDefaults() { return [ + 'allow_hide_own_posts' => 'reply', 'allow_post_editing' => 'reply', 'allow_renaming' => '10', 'allow_sign_up' => '1', diff --git a/framework/core/src/Post/Access/PostPolicy.php b/framework/core/src/Post/Access/PostPolicy.php index 9c5b5e812..88fb041de 100644 --- a/framework/core/src/Post/Access/PostPolicy.php +++ b/framework/core/src/Post/Access/PostPolicy.php @@ -71,6 +71,14 @@ class PostPolicy extends AbstractPolicy */ public function hide(User $actor, Post $post) { - return $this->edit($actor, $post); + if ($post->user_id == $actor->id && (! $post->hidden_at || $post->hidden_user_id == $actor->id) && $actor->can('reply', $post->discussion)) { + $allowHiding = $this->settings->get('allow_hide_own_posts'); + + if ($allowHiding === '-1' + || ($allowHiding === 'reply' && $post->number >= $post->discussion->last_post_number) + || (is_numeric($allowHiding) && $post->created_at->diffInMinutes(new Carbon) < $allowHiding)) { + return $this->allow(); + } + } } } diff --git a/framework/core/tests/integration/policy/PostPolicyTest.php b/framework/core/tests/integration/policy/PostPolicyTest.php index a9d5f5417..046cbf39e 100644 --- a/framework/core/tests/integration/policy/PostPolicyTest.php +++ b/framework/core/tests/integration/policy/PostPolicyTest.php @@ -121,4 +121,104 @@ class PostPolicyTest extends TestCase $this->assertFalse($user->can('edit', $earlierPost)); $this->assertFalse($user->can('edit', $lastPost)); } + + /** + * @test + */ + public function hide_indefinitely() + { + $this->setting('allow_hide_own_posts', '-1'); + $this->app(); + + $user = User::findOrFail(2); + $earlierPost = Post::findOrFail(1); + $lastPost = Post::findOrFail(2); + + // Date close to "now" + Carbon::setTestNow('2021-11-01 13:00:05'); + + $this->assertTrue($user->can('hide', $earlierPost)); + $this->assertTrue($user->can('hide', $lastPost)); + + // Date further into the future + Carbon::setTestNow('2025-01-01 13:00:00'); + + $this->assertTrue($user->can('hide', $earlierPost)); + $this->assertTrue($user->can('hide', $lastPost)); + } + + /** + * @test + */ + public function hide_until_reply() + { + $this->setting('allow_hide_own_posts', 'reply'); + $this->app(); + + $user = User::findOrFail(2); + $earlierPost = Post::findOrFail(1); + $lastPost = Post::findOrFail(2); + + // Date close to "now" + Carbon::setTestNow('2021-11-01 13:00:05'); + + $this->assertFalse($user->can('hide', $earlierPost)); + $this->assertTrue($user->can('hide', $lastPost)); + + // Date further into the future + Carbon::setTestNow('2025-01-01 13:00:00'); + + $this->assertFalse($user->can('hide', $earlierPost)); + $this->assertTrue($user->can('hide', $lastPost)); + } + + /** + * @test + */ + public function hide_10_minutes() + { + $this->setting('allow_hide_own_posts', '10'); + $this->app(); + + $user = User::findOrFail(2); + $earlierPost = Post::findOrFail(1); + $lastPost = Post::findOrFail(2); + + // Date close to "now" + Carbon::setTestNow('2021-11-01 13:00:05'); + + $this->assertTrue($user->can('hide', $earlierPost)); + $this->assertTrue($user->can('hide', $lastPost)); + + // Date further into the future + Carbon::setTestNow('2025-01-01 13:00:00'); + + $this->assertFalse($user->can('hide', $earlierPost)); + $this->assertFalse($user->can('hide', $lastPost)); + } + + /** + * @test + */ + public function hide_never() + { + $this->setting('allow_hide_own_posts', '0'); + $this->app(); + + $user = User::findOrFail(2); + $earlierPost = Post::findOrFail(1); + $lastPost = Post::findOrFail(2); + + // Date close to "now" + Carbon::setTestNow('2021-11-01 13:00:05'); + + $this->assertFalse($user->can('hide', $earlierPost)); + $this->assertFalse($user->can('hide', $lastPost)); + + // Date further into the future + Carbon::setTestNow('2025-01-01 13:00:00'); + + $this->assertFalse($user->can('hide', $earlierPost)); + $this->assertFalse($user->can('hide', $lastPost)); + } }