Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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 extend.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
}),

(new Extend\ApiResource(Resource\PostResource::class))
->fields(Api\PostAttributes::class)
->endpoint(Endpoint\Index::class, function (Endpoint\Index $endpoint) {
return $endpoint
->addDefaultInclude(['discussion.bestAnswerPost', 'discussion.bestAnswerUser', 'discussion.bestAnswerPost.user']); // @todo: same
Expand Down
7 changes: 6 additions & 1 deletion js/src/@types/shims.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ declare module 'flarum/common/models/Discussion' {
hasBestAnswer(): boolean | undefined;
bestAnswerPost(): Post | null;
bestAnswerUser(): User | null;
canSelectBestAnswer(): boolean;
bestAnswerSetAt(): Date | null;
}
}
Expand All @@ -37,3 +36,9 @@ declare module 'flarum/common/models/User' {
bestAnswerCount(): number;
}
}

declare module 'flarum/common/models/Post' {
export default interface Post {
canSelectAsBestAnswer(): boolean;
}
}
6 changes: 0 additions & 6 deletions js/src/admin/components/BestAnswerSettingsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,6 @@ export default class BestAnswerSettingsPage extends ExtensionPage {
</div>
<h3>{app.translator.trans('fof-best-answer.admin.settings.label.general')}</h3>
<div className="Section">
{this.buildSettingComponent({
type: 'boolean',
setting: 'fof-best-answer.allow_select_own_post',
label: app.translator.trans('fof-best-answer.admin.settings.allow_select_own_post'),
help: app.translator.trans('fof-best-answer.admin.settings.allow_select_own_post_help'),
})}
{this.buildSettingComponent({
type: 'boolean',
setting: 'fof-best-answer.use_alternative_ui',
Expand Down
9 changes: 9 additions & 0 deletions js/src/admin/extend.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import app from 'flarum/admin/app';
import Extend from 'flarum/common/extenders';
import BestAnswerSettingsPage from './components/BestAnswerSettingsPage';
import commonExtend from '../common/extend';
Expand All @@ -22,5 +23,13 @@ export default [
permission: 'discussion.selectBestAnswerNotOwnDiscussion',
}),
'reply'
)
.permission(
() => ({
icon: 'fas fa-check',
label: app.translator.trans('fof-best-answer.admin.permissions.allow_select_own_post'),
permission: 'discussion.fof-best-answer.allow_select_own_post',
}),
'reply'
),
];
15 changes: 5 additions & 10 deletions js/src/forum/addBestAnswerAction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,13 @@ import extractText from 'flarum/common/utils/extractText';

export default function addBestAnswerAction() {
const ineligible = (discussion: Discussion, post: Post) => {
return post.isHidden() || post.number() === 1 || !discussion.canSelectBestAnswer() || !app.session.user;
};

const blockSelectOwnPost = (post: Post): boolean => {
const user = post.user();
return !app.forum.attribute<boolean>('canSelectBestAnswerOwnPost') && user !== false && user.id() === app.session.user?.id();
return post.isHidden() || post.number() === 1 || !post.canSelectAsBestAnswer() || !app.session.user;
};

const isThisBestAnswer = (discussion: Discussion, post: Post): boolean => {
const bAPost = discussion.bestAnswerPost();
const bAPost = discussion.bestAnswerPost?.();
const hasBestAnswer = discussion.hasBestAnswer();
return hasBestAnswer !== undefined && hasBestAnswer && bAPost !== null && bAPost.id() === post.id();
return hasBestAnswer !== undefined && hasBestAnswer && bAPost !== null && bAPost.id?.() === post.id();
};

const actionLabel = (isBestAnswer: boolean): string => {
Expand Down Expand Up @@ -72,7 +67,7 @@ export default function addBestAnswerAction() {

if (post.contentType() !== 'comment') return;

if (ineligible(discussion, post) || blockSelectOwnPost(post) || !app.current.matches(DiscussionPage)) return;
if (ineligible(discussion, post) || !app.current.matches(DiscussionPage)) return;

items.add(
'bestAnswer',
Expand Down Expand Up @@ -101,7 +96,7 @@ export default function addBestAnswerAction() {

post.pushAttributes({ isBestAnswer });

if (ineligible(discussion, post) || blockSelectOwnPost(post) || !app.current.matches(DiscussionPage)) return;
if (ineligible(discussion, post) || !app.current.matches(DiscussionPage)) return;

items.add(
'bestAnswer',
Expand Down
6 changes: 4 additions & 2 deletions js/src/forum/extend.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Discussion from 'flarum/common/models/Discussion';
import commonExtend from '../common/extend';
import Extend from 'flarum/common/extenders';
import type Post from 'flarum/common/models/Post';
import Post from 'flarum/common/models/Post';
import User from 'flarum/common/models/User';
import Model from 'flarum/common/Model';

Expand All @@ -12,9 +12,11 @@ export default [
.hasOne<Post>('bestAnswerPost')
.hasOne<User>('bestAnswerUser')
.attribute<boolean | number>('hasBestAnswer')
.attribute<boolean>('canSelectBestAnswer')
.attribute('bestAnswerSetAt', Model.transformDate),

new Extend.Model(User) //
.attribute<number>('bestAnswerCount'),

new Extend.Model(Post) //
.attribute<boolean>('canSelectAsBestAnswer'),
];
3 changes: 1 addition & 2 deletions resources/locale/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@ fof-best-answer:
permissions:
best_answer: Select Best Answer (own Discussion)
best_answer_not_own_discussion: Select Best Answer (not own Discussion)
allow_select_own_post: Select own post as Best Answer
settings:
label:
tags: Best Answer Tags
general: General
reminders: Reminders
advanced: Advanced
reminders_notice: For reminders to function, you must have set up the Flarum scheduler correctly.
allow_select_own_post: Select own post
allow_select_own_post_help: Allow a user to select their own post as a best answer to a discussion
show_max_lines_label: Max lines to show in post preview
show_max_lines_help: Set to 0 to disable. If a post is longer than the configured amount of lines, it will be truncated in the post preview with a fade out effect.
select_best_answer_reminder_days: Reminder frequency
Expand Down
3 changes: 0 additions & 3 deletions src/Api/ForumAttributes.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@ public function __construct(
public function __invoke(): array
{
return [
Schema\Boolean::make('canSelectBestAnswerOwnPost')
->get(fn () => (bool) $this->settings->get('fof-best-answer.allow_select_own_post')),

Schema\Boolean::make('bestAnswerDiscussionSidebarJumpButton')
->get(fn () => (bool) $this->settings->get('fof-best-answer.discussion_sidebar_jump_button')),

Expand Down
33 changes: 33 additions & 0 deletions src/Api/PostAttributes.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

/*
* This file is part of fof/best-answer.
*
* Copyright (c) FriendsOfFlarum.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace FoF\BestAnswer\Api;

use Flarum\Api\Context;
use Flarum\Api\Schema;
use Flarum\Post\Post;
use FoF\BestAnswer\Repository\BestAnswerRepository;

class PostAttributes
{
public function __construct(
protected BestAnswerRepository $bestAnswerRepository,
) {
}

public function __invoke(): array
{
return [
Schema\Boolean::make('canSelectAsBestAnswer')
->get(fn (Post $post, Context $context) => $this->bestAnswerRepository->canSelectPostAsBestAnswer($context->getActor(), $post)),
];
}
}
2 changes: 1 addition & 1 deletion src/Repository/BestAnswerRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public function canSelectPostAsBestAnswer(User $user, Post $post): bool
}

if ($user->id === $post->user_id) {
return (bool) $this->settings->get('fof-best-answer.allow_select_own_post');
return $user->can('fof-best-answer.allow_select_own_post', $post->discussion);
}

return true;
Expand Down
29 changes: 23 additions & 6 deletions tests/integration/api/SetBestAnswerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public function setUp(): void
],
'group_permission' => [
['group_id' => 4, 'permission' => 'discussion.selectBestAnswerNotOwnDiscussion', 'created_at' => Carbon::now()],
['group_id' => 4, 'permission' => 'discussion.fof-best-answer.allow_select_own_post', 'created_at' => Carbon::now()],
],
'group_user' => [
['user_id' => 4, 'group_id' => 4],
Expand Down Expand Up @@ -166,20 +167,36 @@ public function user_without_permission_cannot_set_best_answer(int $userId)
$this->assertEquals(403, $response->getStatusCode());
}

public static function unauthorizedUsersOwnPostProvider(): array
{
return [
[2],
[3],
];
}

#[Test]
public function user_cannot_set_own_post_as_best_answer_if_not_permitted()
#[DataProvider('unauthorizedUsersOwnPostProvider')]
public function user_cannot_set_own_post_as_best_answer_if_not_permitted(int $userId)
{
$response = $this->setBestAnswer(3, 5, 2);
$response = $this->setBestAnswer($userId, 5, 2);

$this->assertEquals(403, $response->getStatusCode());
}

#[Test]
public function user_can_set_own_post_as_best_answer_if_permitted()
public static function permittedUsersOwnPostProvider(): array
{
$this->setting('fof-best-answer.allow_select_own_post', true);
return [
[1],
[4],
];
}

$response = $this->setBestAnswer(3, 5, 2);
#[Test]
#[DataProvider('permittedUsersOwnPostProvider')]
public function user_can_set_own_post_as_best_answer_if_permitted(int $userId)
{
$response = $this->setBestAnswer($userId, 5, 2);

$this->assertEquals(200, $response->getStatusCode());

Expand Down
Loading