Skip to content

Commit

Permalink
feat: allow creation of polls without post id (global poll)
Browse files Browse the repository at this point in the history
  • Loading branch information
imorland committed Jan 31, 2024
1 parent 7abeebe commit c8e60a0
Show file tree
Hide file tree
Showing 9 changed files with 167 additions and 9 deletions.
8 changes: 8 additions & 0 deletions js/src/admin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ app.initializers.add('fof/polls', () => {
},
'start'
)
.registerPermission(
{
icon: 'fas fa-signal',
label: app.translator.trans('fof-polls.admin.permissions.start_global'),
permission: 'startGlobalPoll',
},
'start'
)
.registerPermission(
{
icon: 'fas fa-pencil-alt',
Expand Down
17 changes: 17 additions & 0 deletions migrations/2024_01_31_update_polls_set_post_id_nullable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Schema\Builder;

return [
'up' => function (Builder $schema) {
$schema->table('polls', function (Blueprint $table) {
$table->integer('post_id')->unsigned()->nullable()->change();
});
},
'down' => function (Builder $schema) {
$schema->table('polls', function (Blueprint $table) {
$table->integer('post_id')->unsigned()->nullable(false)->change();
});
},
];
1 change: 1 addition & 0 deletions resources/locale/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ fof-polls:
permissions:
view_results_without_voting: View results without voting
start: Start a poll
start_global: Start a global poll
self_edit: Edit created polls (requires post edit permission)
self_post_edit: Edit *all* polls on own posts (requires post edit permission)
vote: Vote on polls
Expand Down
6 changes: 3 additions & 3 deletions src/Access/PollPolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public function seeVoteCount(User $actor, Poll $poll)
return $this->deny();
}

if ($poll->myVotes($actor)->count() || $actor->can('polls.viewResultsWithoutVoting', $poll->post->discussion)) {
if ($poll->myVotes($actor)->count() || $actor->can('polls.viewResultsWithoutVoting', $poll->post ? $poll->post->discussion : null)) {

Check failure on line 26 in src/Access/PollPolicy.php

View workflow job for this annotation

GitHub Actions / run / PHPStan PHP 7.3

Ternary operator condition is always true.

Check failure on line 26 in src/Access/PollPolicy.php

View workflow job for this annotation

GitHub Actions / run / PHPStan PHP 7.4

Ternary operator condition is always true.

Check failure on line 26 in src/Access/PollPolicy.php

View workflow job for this annotation

GitHub Actions / run / PHPStan PHP 8.0

Ternary operator condition is always true.

Check failure on line 26 in src/Access/PollPolicy.php

View workflow job for this annotation

GitHub Actions / run / PHPStan PHP 8.1

Ternary operator condition is always true.

Check failure on line 26 in src/Access/PollPolicy.php

View workflow job for this annotation

GitHub Actions / run / PHPStan PHP 8.2

Ternary operator condition is always true.

Check failure on line 26 in src/Access/PollPolicy.php

View workflow job for this annotation

GitHub Actions / run / PHPStan PHP 8.3

Ternary operator condition is always true.
return $this->allow();
}
}
Expand All @@ -48,7 +48,7 @@ public function view(User $actor, Poll $poll)

public function vote(User $actor, Poll $poll)
{
if ($actor->can('polls.vote', $poll->post->discussion) && !$poll->hasEnded()) {
if ($actor->can('polls.vote', $poll->post ? $poll->post->discussion : null) && !$poll->hasEnded()) {

Check failure on line 51 in src/Access/PollPolicy.php

View workflow job for this annotation

GitHub Actions / run / PHPStan PHP 7.3

Ternary operator condition is always true.

Check failure on line 51 in src/Access/PollPolicy.php

View workflow job for this annotation

GitHub Actions / run / PHPStan PHP 7.4

Ternary operator condition is always true.

Check failure on line 51 in src/Access/PollPolicy.php

View workflow job for this annotation

GitHub Actions / run / PHPStan PHP 8.0

Ternary operator condition is always true.

Check failure on line 51 in src/Access/PollPolicy.php

View workflow job for this annotation

GitHub Actions / run / PHPStan PHP 8.1

Ternary operator condition is always true.

Check failure on line 51 in src/Access/PollPolicy.php

View workflow job for this annotation

GitHub Actions / run / PHPStan PHP 8.2

Ternary operator condition is always true.

Check failure on line 51 in src/Access/PollPolicy.php

View workflow job for this annotation

GitHub Actions / run / PHPStan PHP 8.3

Ternary operator condition is always true.
return $this->allow();
}
}
Expand All @@ -62,7 +62,7 @@ public function changeVote(User $actor, Poll $poll)

public function edit(User $actor, Poll $poll)
{
if ($actor->can('polls.moderate', $poll->post->discussion)) {
if ($actor->can('polls.moderate', $poll->post ? $poll->post->discussion : null)) {

Check failure on line 65 in src/Access/PollPolicy.php

View workflow job for this annotation

GitHub Actions / run / PHPStan PHP 7.3

Ternary operator condition is always true.

Check failure on line 65 in src/Access/PollPolicy.php

View workflow job for this annotation

GitHub Actions / run / PHPStan PHP 7.4

Ternary operator condition is always true.

Check failure on line 65 in src/Access/PollPolicy.php

View workflow job for this annotation

GitHub Actions / run / PHPStan PHP 8.0

Ternary operator condition is always true.

Check failure on line 65 in src/Access/PollPolicy.php

View workflow job for this annotation

GitHub Actions / run / PHPStan PHP 8.1

Ternary operator condition is always true.

Check failure on line 65 in src/Access/PollPolicy.php

View workflow job for this annotation

GitHub Actions / run / PHPStan PHP 8.2

Ternary operator condition is always true.

Check failure on line 65 in src/Access/PollPolicy.php

View workflow job for this annotation

GitHub Actions / run / PHPStan PHP 8.3

Ternary operator condition is always true.
return $this->allow();
}

Expand Down
3 changes: 2 additions & 1 deletion src/Access/ScopePollVisibility.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ public function __invoke(User $actor, Builder $query)
$query->whereExists(function ($query) use ($actor) {
$query->selectRaw('1')
->from('posts')
->whereColumn('posts.id', 'polls.post_id');
->whereColumn('posts.id', 'polls.post_id')
->orWhere('polls.post_id', null);
Post::query()->setQuery($query)->whereVisibleTo($actor);
});
}
Expand Down
8 changes: 7 additions & 1 deletion src/Api/Controllers/CreatePollController.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,16 @@ protected function data(ServerRequestInterface $request, Document $document)
$postId = Arr::get($request->getParsedBody(), 'data.relationships.post.data.id');
$actor = RequestUtil::getActor($request);

$post = null;

if ($postId !== null) {
$post = $this->posts->findOrFail($postId, $actor);
}

return $this->bus->dispatch(
new CreatePoll(
$actor,
$this->posts->findOrFail($postId, $actor),
$post,
Arr::get($request->getParsedBody(), 'data.attributes')
)
);
Expand Down
4 changes: 2 additions & 2 deletions src/Commands/CreatePoll.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class CreatePoll
public $actor;

/**
* @var Post
* @var ?Post
*/
public $post;

Expand All @@ -42,7 +42,7 @@ class CreatePoll
* @param array $data
* @param callable|null $savePollOn
*/
public function __construct(User $actor, Post $post, array $data, callable $savePollOn = null)
public function __construct(User $actor, ?Post $post, array $data, callable $savePollOn = null)
{
$this->actor = $actor;
$this->post = $post;
Expand Down
8 changes: 6 additions & 2 deletions src/Commands/CreatePollHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,11 @@ public function __construct(PostRepository $posts, PollValidator $validator, Pol

public function handle(CreatePoll $command)
{
$command->actor->assertCan('startPoll', $command->post);
if ($command->post) {
$command->actor->assertCan('startPoll', $command->post);
} else {
$command->actor->assertCan('startGlobalPoll');
}

$attributes = $command->data;

Expand Down Expand Up @@ -98,7 +102,7 @@ public function handle(CreatePoll $command)

$poll = Poll::build(
Arr::get($attributes, 'question'),
$command->post->id,
$command->post ? $command->post->id : null,
$command->actor->id,
$carbonDate != null ? $carbonDate->utc() : null,
Arr::get($attributes, 'publicPoll'),
Expand Down
121 changes: 121 additions & 0 deletions tests/integration/api/CreatePollTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public function setUp(): void
],
'group_permission' => [
['permission' => 'discussion.polls.start', 'group_id' => 4],
['permission' => 'startGlobalPoll', 'group_id' => 4],
],
]);
}
Expand Down Expand Up @@ -314,4 +315,124 @@ public function unauthorized_user_cannot_create_post_poll_on_api(int $userId)

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

/**
* @dataProvider authorizedUserProvider
*
* @test
*/
public function authorized_user_cannot_create_post_poll_with_invalid_postId(int $userId)
{
$response = $this->send(
$this->request(
'POST',
'/api/fof/polls',
[
'authenticatedAs' => $userId,
'json' => [
'data' => [
'attributes' => [
'question' => 'Add a poll to an existing post',
'publicPoll' => false,
'hideVotes' => false,
'allowChangeVote' => true,
'allowMultipleVotes' => false,
'maxVotes' => 0,
'endDate' => false,
'options' => [
[
'answer' => 'Yes',
],
[
'answer' => 'No',
],
],
],
'relationships' => [
'post' => [
'data' => [
'type' => 'posts',
'id' => 299,
],
],
],
],
],
]
)
);

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

/**
* @dataProvider authorizedUserProvider
*
* @test
*/
public function authorized_user_can_create_global_poll_on_api(int $userId)
{
$response = $this->send(
$this->request(
'POST',
'/api/fof/polls',
[
'authenticatedAs' => $userId,
'json' => [
'data' => [
'attributes' => [
'question' => 'Add a global poll',
'publicPoll' => false,
'hideVotes' => false,
'allowChangeVote' => true,
'allowMultipleVotes' => false,
'maxVotes' => 0,
'endDate' => false,
'options' => [
[
'answer' => 'Yes',
],
[
'answer' => 'No',
],
],
],
],
],
]
)
);

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

$json = json_decode($response->getBody()->getContents(), true);

$data = $json['data'];
$attributes = $data['attributes'];

$this->assertEquals('Add a global poll', $attributes['question']);

$pollId = $data['id'];
$this->assertNotNull($pollId);

$poll = Poll::find($pollId);
$this->assertNotNull($poll);
$this->assertNull($poll->post_id);

$response = $this->send(
$this->request(
'GET',
'/api/fof/polls/'.$pollId,
[
'authenticatedAs' => $userId,
]
)
);

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

$json = json_decode($response->getBody()->getContents(), true);

$this->assertTrue($json['data']['attributes']['isGlobal']);
}
}

0 comments on commit c8e60a0

Please sign in to comment.