Skip to content

Commit b0ed026

Browse files
authored
Merge pull request HDInnovations#4009 from Obi-Wana/add-poll-expiry-date
2 parents c98dd69 + 2ef73fa commit b0ed026

File tree

12 files changed

+209
-17
lines changed

12 files changed

+209
-17
lines changed

app/Http/Controllers/HomeController.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,12 @@ public function index(Request $request): \Illuminate\Contracts\View\Factory|\Ill
115115
'user.group',
116116
])->get(),
117117
),
118-
'poll' => cache()->remember('latest_poll', $expiresAt, fn () => Poll::latest()->first()),
118+
'poll' => cache()->remember('latest_poll', $expiresAt, function () {
119+
return Poll::where(function ($query): void {
120+
$query->where('expires_at', '>', now())
121+
->orWhereNull('expires_at');
122+
})->latest()->first();
123+
}),
119124
'freeleech_tokens' => FreeleechToken::where('user_id', $user->id)->get(),
120125
'bookmarks' => Bookmark::where('user_id', $user->id)->get(),
121126
]);

app/Http/Controllers/PollController.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ public function show(Request $request, Poll $poll): \Illuminate\Contracts\View\F
4545
->withInfo(trans('poll.already-voted-result'));
4646
}
4747

48+
if ($poll->expires_at?->isPast()) {
49+
return to_route('polls.votes.index', ['poll' => $poll])
50+
->withInfo(trans('poll.expired-voted-result'));
51+
}
52+
4853
return view('poll.show', ['poll' => $poll]);
4954
}
5055
}

app/Http/Controllers/Staff/PollController.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public function create(): \Illuminate\Contracts\View\Factory|\Illuminate\View\Vi
7070
*/
7171
public function store(StorePoll $request): \Illuminate\Http\RedirectResponse
7272
{
73-
$poll = Poll::create(['user_id' => $request->user()->id] + $request->safe()->only(['title', 'multiple_choice']));
73+
$poll = Poll::create(['user_id' => $request->user()->id] + $request->safe()->only(['title', 'expires_at', 'multiple_choice']));
7474
Option::upsert(array_map(fn ($item) => ['poll_id' => $poll->id] + $item, $request->safe()->only(['options'])['options']), ['id'], []);
7575

7676
$this->chatRepository->systemMessage(
@@ -98,7 +98,7 @@ public function edit(Poll $poll): \Illuminate\Contracts\View\Factory|\Illuminate
9898
*/
9999
public function update(UpdatePollRequest $request, Poll $poll): \Illuminate\Http\RedirectResponse
100100
{
101-
$poll->update($request->safe()->only(['title', 'multiple_choice']));
101+
$poll->update($request->safe()->only(['title', 'expires_at', 'multiple_choice']));
102102

103103
$poll->options()
104104
->whereNotIn('id', Arr::flatten($request->safe()->only(['options.*.id'])))

app/Http/Requests/StorePoll.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ public function rules(): array
4343
'required',
4444
'min:10'
4545
],
46+
'expires_at' => [
47+
'sometimes',
48+
'nullable',
49+
'date',
50+
],
4651
'multiple_choice' => [
4752
'required',
4853
'boolean'

app/Http/Requests/UpdatePollRequest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ public function rules(): array
3232
'required',
3333
'min:10',
3434
],
35+
'expires_at' => [
36+
'sometimes',
37+
'nullable',
38+
'date',
39+
],
3540
'multiple_choice' => [
3641
'required',
3742
'boolean',

app/Models/Poll.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
* @property int $user_id
2828
* @property string $title
2929
* @property int $multiple_choice
30+
* @property \Illuminate\Support\Carbon|null $expires_at
3031
* @property \Illuminate\Support\Carbon|null $created_at
3132
* @property \Illuminate\Support\Carbon|null $updated_at
3233
*/
@@ -44,6 +45,18 @@ class Poll extends Model
4445
*/
4546
protected $guarded = [];
4647

48+
/**
49+
* Get the attributes that should be cast.
50+
*
51+
* @return array{expires_at: 'datetime'}
52+
*/
53+
protected function casts(): array
54+
{
55+
return [
56+
'expires_at' => 'datetime',
57+
];
58+
}
59+
4760
/**
4861
* Belongs To A User.
4962
*
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* NOTICE OF LICENSE.
7+
*
8+
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
9+
* The details is bundled with this project in the file LICENSE.txt.
10+
*
11+
* @project UNIT3D Community Edition
12+
*
13+
* @author HDVinnie <hdinnovations@protonmail.com>
14+
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
15+
*/
16+
17+
use Illuminate\Database\Migrations\Migration;
18+
use Illuminate\Database\Schema\Blueprint;
19+
use Illuminate\Support\Facades\Schema;
20+
21+
return new class () extends Migration {
22+
/**
23+
* Run the migrations.
24+
*/
25+
public function up(): void
26+
{
27+
Schema::table('polls', function (Blueprint $table): void {
28+
$table->dateTime('expires_at')->nullable()->after('multiple_choice');
29+
});
30+
}
31+
};

lang/en/poll.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
'add-option' => 'Add Option',
1818
'already-voted-error' => 'You have already voted on this poll. Your vote has not been counted.',
1919
'already-voted-result' => 'You have already voted on this poll. Here are the results.',
20+
'expired-voted-result' => 'This poll is expired. Here are the results.',
2021
'create-poll' => 'Create Poll',
2122
'current' => 'Current Poll(s)',
2223
'delete-option' => 'Delete Option',
@@ -32,4 +33,5 @@
3233
'vote-counted' => 'Your vote has been counted.',
3334
'vote-now' => 'Get Your Vote In Now!',
3435
'votes' => 'Votes',
36+
'close-date' => 'Close Poll after',
3537
];

resources/views/Staff/poll/create.blade.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,18 @@ class="form__text"
4747
{{ __('common.title') }}
4848
</label>
4949
</p>
50+
<p class="form__group">
51+
<input
52+
id="expires_at"
53+
class="form__text"
54+
name="expires_at"
55+
type="date"
56+
value="{{ old('expires_at') }}"
57+
/>
58+
<label class="form__label form__label--floating" for="expires_at">
59+
{{ __('poll.close-date') }}
60+
</label>
61+
</p>
5062
<template x-for="option in options">
5163
<p class="form__group">
5264
<input

resources/views/Staff/poll/edit.blade.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,18 @@ class="form__text"
5252
{{ __('poll.title') }}
5353
</label>
5454
</p>
55+
<p class="form__group">
56+
<input
57+
id="expires_at"
58+
class="form__text"
59+
name="expires_at"
60+
type="dateTime-local"
61+
value="{{ $poll->expires_at }}"
62+
/>
63+
<label class="form__label form__label--floating" for="expires_at">
64+
{{ __('poll.close-date') }}
65+
</label>
66+
</p>
5567
<template x-for="(option, i) in extraOptions">
5668
<p class="form__group">
5769
<input

resources/views/Staff/poll/index.blade.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
<tr>
3232
<th>{{ __('poll.title') }}</th>
3333
<th>{{ __('common.date') }}</th>
34+
<th>{{ __('forum.state') }}</th>
3435
<th>{{ __('common.action') }}</th>
3536
</tr>
3637
</thead>
@@ -50,6 +51,13 @@
5051
{{ date('d M Y', $poll->created_at->getTimestamp()) }}
5152
</time>
5253
</td>
54+
<td>
55+
@if ($poll->expires_at?->isPast())
56+
{{ __('forum.closed') }}
57+
@else
58+
{{ __('forum.open') }}
59+
@endif
60+
</td>
5361
<td>
5462
<menu class="data-table__actions">
5563
<li class="data-table__action">

tests/Feature/Http/Controllers/Staff/PollControllerTest.php

Lines changed: 108 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,105 @@
1717
use App\Http\Controllers\Staff\PollController;
1818
use App\Http\Requests\StorePoll;
1919
use App\Http\Requests\UpdatePollRequest;
20+
use App\Models\Bot;
21+
use App\Models\Chatroom;
22+
use App\Models\Group;
2023
use App\Models\Poll;
2124
use App\Models\User;
2225

23-
test('create returns an ok response', function (): void {
24-
$this->markTestIncomplete('This test case was generated by Shift. When you are ready, remove this line and complete this test case.');
26+
test('create a poll returns an ok response', function (): void {
27+
// Poll chat announcements needs a system user, a bot and a chatroom
28+
$botUser = User::factory()->create([
29+
'username' => 'System',
30+
]);
31+
$bot = Bot::factory()->create([
32+
'command' => 'Systembot',
33+
]);
34+
$chat = Chatroom::factory()->create([
35+
'name' => config('chat.system_chatroom'),
36+
]);
2537

26-
$user = User::factory()->create();
38+
$group = Group::factory()->create([
39+
'is_modo' => 1,
40+
]);
41+
$staff = User::factory()->create([
42+
'group_id' => $group->id,
43+
'active' => 1,
44+
]);
2745

28-
$response = $this->actingAs($user)->get(route('staff.polls.create'));
46+
$pollTitle = 'Test Poll Title';
47+
$pollOption1 = 'Option 1';
48+
$pollOption2 = 'Option 2';
49+
50+
$response = $this->actingAs($staff)->withSession(['banned' => false])->post(route('staff.polls.store'), [
51+
'title' => $pollTitle,
52+
'options' => [
53+
['id' => 0, 'name' => $pollOption1],
54+
['id' => 1, 'name' => $pollOption2],
55+
],
56+
'multiple_choice' => 0,
57+
]);
2958

30-
$response->assertOk();
31-
$response->assertViewIs('Staff.poll.create');
59+
$response->assertRedirectToRoute('staff.polls.index');
60+
$this->assertDatabaseHas('polls', [
61+
'title' => $pollTitle,
62+
'user_id' => $staff->id,
63+
'expires_at' => null,
64+
]);
65+
$this->assertDatabaseHas('options', [
66+
'name' => $pollOption1,
67+
]);
68+
$this->assertDatabaseHas('options', [
69+
'name' => $pollOption2,
70+
]);
71+
});
3272

33-
// TODO: perform additional assertions
73+
test('create a poll with expiration date returns an ok response', function (): void {
74+
// Poll chat announcements needs a system user, a bot and a chatroom
75+
$botUser = User::factory()->create([
76+
'username' => 'System',
77+
]);
78+
$bot = Bot::factory()->create([
79+
'command' => 'Systembot',
80+
]);
81+
$chat = Chatroom::factory()->create([
82+
'name' => config('chat.system_chatroom'),
83+
]);
84+
85+
$group = Group::factory()->create([
86+
'is_modo' => 1,
87+
]);
88+
$staff = User::factory()->create([
89+
'group_id' => $group->id,
90+
'active' => 1,
91+
]);
92+
93+
$pollTitle = 'Test Poll Title';
94+
$pollUntil = now()->addDays(1);
95+
$pollOption1 = 'Option 1';
96+
$pollOption2 = 'Option 2';
97+
98+
$response = $this->actingAs($staff)->post(route('staff.polls.store'), [
99+
'title' => $pollTitle,
100+
'expires_at' => $pollUntil,
101+
'options' => [
102+
['id' => 0, 'name' => $pollOption1],
103+
['id' => 1, 'name' => $pollOption2],
104+
],
105+
'multiple_choice' => 0,
106+
]);
107+
108+
$this->assertDatabaseHas('polls', [
109+
'title' => $pollTitle,
110+
'user_id' => $staff->id,
111+
'expires_at' => $pollUntil,
112+
]);
113+
$this->assertDatabaseHas('options', [
114+
'name' => $pollOption1,
115+
]);
116+
$this->assertDatabaseHas('options', [
117+
'name' => $pollOption2,
118+
]);
34119
});
35120

36121
test('destroy returns an ok response', function (): void {
@@ -63,18 +148,27 @@
63148
});
64149

65150
test('index returns an ok response', function (): void {
66-
$this->markTestIncomplete('This test case was generated by Shift. When you are ready, remove this line and complete this test case.');
151+
$polls = Poll::factory()->times(3)->create([
152+
'expires_at' => null,
153+
]);
154+
$pollExpired = Poll::factory()->create([
155+
'expires_at' => now()->subDays(1),
156+
]);
67157

68-
$polls = Poll::factory()->times(3)->create();
69-
$user = User::factory()->create();
158+
$group = Group::factory()->create([
159+
'is_modo' => 1,
160+
]);
161+
$staff = User::factory()->create([
162+
'group_id' => $group->id,
163+
'active' => 1,
164+
]);
70165

71-
$response = $this->actingAs($user)->get(route('staff.polls.index'));
166+
$response = $this->actingAs($staff)->get(route('staff.polls.index'));
72167

73168
$response->assertOk();
74169
$response->assertViewIs('Staff.poll.index');
75-
$response->assertViewHas('polls', $polls);
76-
77-
// TODO: perform additional assertions
170+
$response->assertSee('Closed');
171+
$response->assertSee('Open');
78172
});
79173

80174
test('show returns an ok response', function (): void {

0 commit comments

Comments
 (0)