36 |
37 |
38 |
41 |
--------------------------------------------------------------------------------
/tests/Pest.php:
--------------------------------------------------------------------------------
1 | in('Feature');
18 |
19 | /*
20 | |--------------------------------------------------------------------------
21 | | Expectations
22 | |--------------------------------------------------------------------------
23 | |
24 | | When you're writing tests, you often need to check that values meet certain conditions. The
25 | | "expect()" function gives you access to a set of "expectations" methods that you can use
26 | | to assert different things. Of course, you may extend the Expectation API at any time.
27 | |
28 | */
29 |
30 | expect()->extend('toBeOne', function () {
31 | return $this->toBe(1);
32 | });
33 |
34 | /*
35 | |--------------------------------------------------------------------------
36 | | Functions
37 | |--------------------------------------------------------------------------
38 | |
39 | | While Pest is very powerful out-of-the-box, you may have some testing code specific to your
40 | | project that you don't want to repeat in every file. Here you can also expose helpers as
41 | | global functions to help you to reduce the number of lines of code in your test files.
42 | |
43 | */
44 |
45 | function something()
46 | {
47 | // ..
48 | }
49 |
--------------------------------------------------------------------------------
/resources/js/Pages/Auth/ConfirmPassword.vue:
--------------------------------------------------------------------------------
1 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | This is a secure area of the application. Please confirm your password before continuing.
26 |
27 |
28 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/app/Notifications/CommentCreated.php:
--------------------------------------------------------------------------------
1 |
30 | */
31 | public function via(object $notifiable): array
32 | {
33 | return ['mail'];
34 | }
35 |
36 | /**
37 | * Get the mail representation of the notification.
38 | */
39 | public function toMail(object $notifiable): MailMessage
40 | {
41 | return ( new MailMessage )
42 | // ->greeting('Hello My Friend')
43 | ->line('User "'.$this->comment->user->username.'" has made a comment on your post. Please see comment bellow.')
44 | ->line('"' . $this->comment->comment . '"')
45 | ->action('View Post', url(route('post.view', $this->post->id)))
46 | ->line('Thank you for using our application!')
47 | // ->salutation("My salutation")
48 | ;
49 | }
50 |
51 | /**
52 | * Get the array representation of the notification.
53 | *
54 | * @return array
55 | */
56 | public function toArray(object $notifiable): array
57 | {
58 | return [
59 | //
60 | ];
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/tests/Feature/Auth/PasswordResetTest.php:
--------------------------------------------------------------------------------
1 | get('/forgot-password');
9 |
10 | $response->assertStatus(200);
11 | });
12 |
13 | test('reset password link can be requested', function () {
14 | Notification::fake();
15 |
16 | $user = User::factory()->create();
17 |
18 | $this->post('/forgot-password', ['email' => $user->email]);
19 |
20 | Notification::assertSentTo($user, ResetPassword::class);
21 | });
22 |
23 | test('reset password screen can be rendered', function () {
24 | Notification::fake();
25 |
26 | $user = User::factory()->create();
27 |
28 | $this->post('/forgot-password', ['email' => $user->email]);
29 |
30 | Notification::assertSentTo($user, ResetPassword::class, function ($notification) {
31 | $response = $this->get('/reset-password/'.$notification->token);
32 |
33 | $response->assertStatus(200);
34 |
35 | return true;
36 | });
37 | });
38 |
39 | test('password can be reset with valid token', function () {
40 | Notification::fake();
41 |
42 | $user = User::factory()->create();
43 |
44 | $this->post('/forgot-password', ['email' => $user->email]);
45 |
46 | Notification::assertSentTo($user, ResetPassword::class, function ($notification) use ($user) {
47 | $response = $this->post('/reset-password', [
48 | 'token' => $notification->token,
49 | 'email' => $user->email,
50 | 'password' => 'password',
51 | 'password_confirmation' => 'password',
52 | ]);
53 |
54 | $response->assertSessionHasNoErrors();
55 |
56 | return true;
57 | });
58 | });
59 |
--------------------------------------------------------------------------------
/bootstrap/app.php:
--------------------------------------------------------------------------------
1 | singleton(
30 | Illuminate\Contracts\Http\Kernel::class,
31 | App\Http\Kernel::class
32 | );
33 |
34 | $app->singleton(
35 | Illuminate\Contracts\Console\Kernel::class,
36 | App\Console\Kernel::class
37 | );
38 |
39 | $app->singleton(
40 | Illuminate\Contracts\Debug\ExceptionHandler::class,
41 | App\Exceptions\Handler::class
42 | );
43 |
44 | /*
45 | |--------------------------------------------------------------------------
46 | | Return The Application
47 | |--------------------------------------------------------------------------
48 | |
49 | | This script returns the application instance. The instance is given to
50 | | the calling script so we can separate the building of the instances
51 | | from the actual running of the application and sending responses.
52 | |
53 | */
54 |
55 | return $app;
56 |
--------------------------------------------------------------------------------
/config/hashing.php:
--------------------------------------------------------------------------------
1 | 'bcrypt',
19 |
20 | /*
21 | |--------------------------------------------------------------------------
22 | | Bcrypt Options
23 | |--------------------------------------------------------------------------
24 | |
25 | | Here you may specify the configuration options that should be used when
26 | | passwords are hashed using the Bcrypt algorithm. This will allow you
27 | | to control the amount of time it takes to hash the given password.
28 | |
29 | */
30 |
31 | 'bcrypt' => [
32 | 'rounds' => env('BCRYPT_ROUNDS', 12),
33 | 'verify' => true,
34 | ],
35 |
36 | /*
37 | |--------------------------------------------------------------------------
38 | | Argon Options
39 | |--------------------------------------------------------------------------
40 | |
41 | | Here you may specify the configuration options that should be used when
42 | | passwords are hashed using the Argon algorithm. These will allow you
43 | | to control the amount of time it takes to hash the given password.
44 | |
45 | */
46 |
47 | 'argon' => [
48 | 'memory' => 65536,
49 | 'threads' => 1,
50 | 'time' => 4,
51 | 'verify' => true,
52 | ],
53 |
54 | ];
55 |
--------------------------------------------------------------------------------
/resources/js/Pages/Post/View.vue:
--------------------------------------------------------------------------------
1 |
41 |
42 |
43 |
44 |
45 |
48 |
49 |
50 |
53 |
54 |
55 |
56 |
59 |
--------------------------------------------------------------------------------
/app/Http/Requests/InviteUsersRequest.php:
--------------------------------------------------------------------------------
1 | group = $this->route('group');
25 |
26 | return $this->group->isAdmin(Auth::id());
27 | }
28 |
29 | /**
30 | * Get the validation rules that apply to the request.
31 | *
32 | * @return array|string>
33 | */
34 | public function rules(): array
35 | {
36 | return [
37 | 'email' => ['required', function ($attribute, $value, \Closure $fail) {
38 | $this->user = User::query()->where('email', $value)
39 | ->orWhere('username', $value)
40 | ->first();
41 |
42 | if (!$this->user) {
43 | $fail('User does not exist');
44 | }
45 |
46 | $this->groupUser = GroupUser::where('user_id', $this->user->id)
47 | ->where('group_id', $this->group->id)
48 | ->first();
49 |
50 | if ($this->groupUser && $this->groupUser->status === GroupUserStatus::APPROVED->value) {
51 | $fail('User is already joined to the group');
52 | }
53 | }]
54 | ];
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/artisan:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | make(Illuminate\Contracts\Console\Kernel::class);
34 |
35 | $status = $kernel->handle(
36 | $input = new Symfony\Component\Console\Input\ArgvInput,
37 | new Symfony\Component\Console\Output\ConsoleOutput
38 | );
39 |
40 | /*
41 | |--------------------------------------------------------------------------
42 | | Shutdown The Application
43 | |--------------------------------------------------------------------------
44 | |
45 | | Once Artisan has finished running, we will fire off the shutdown events
46 | | so that any final work may be done by the application before we shut
47 | | down the process. This is the last thing to happen to the request.
48 | |
49 | */
50 |
51 | $kernel->terminate($input, $status);
52 |
53 | exit($status);
54 |
--------------------------------------------------------------------------------
/app/Providers/TelescopeServiceProvider.php:
--------------------------------------------------------------------------------
1 | hideSensitiveRequestDetails();
20 |
21 | Telescope::filter(function (IncomingEntry $entry) {
22 | if ($this->app->environment('local')) {
23 | return true;
24 | }
25 |
26 | return $entry->isReportableException() ||
27 | $entry->isFailedRequest() ||
28 | $entry->isFailedJob() ||
29 | $entry->isScheduledTask() ||
30 | $entry->hasMonitoredTag();
31 | });
32 | }
33 |
34 | /**
35 | * Prevent sensitive request details from being logged by Telescope.
36 | */
37 | protected function hideSensitiveRequestDetails(): void
38 | {
39 | if ($this->app->environment('local')) {
40 | return;
41 | }
42 |
43 | Telescope::hideRequestParameters(['_token']);
44 |
45 | Telescope::hideRequestHeaders([
46 | 'cookie',
47 | 'x-csrf-token',
48 | 'x-xsrf-token',
49 | ]);
50 | }
51 |
52 | /**
53 | * Register the Telescope gate.
54 | *
55 | * This gate determines who can access Telescope in non-local environments.
56 | */
57 | protected function gate(): void
58 | {
59 | Gate::define('viewTelescope', function ($user) {
60 | return in_array($user->email, [
61 | //
62 | ]);
63 | });
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/public/index.php:
--------------------------------------------------------------------------------
1 | make(Kernel::class);
50 |
51 | $response = $kernel->handle(
52 | $request = Request::capture()
53 | )->send();
54 |
55 | $kernel->terminate($request, $response);
56 |
--------------------------------------------------------------------------------
/app/Models/Comment.php:
--------------------------------------------------------------------------------
1 | belongsTo(User::class);
24 | }
25 |
26 | public function post(): BelongsTo
27 | {
28 | return $this->belongsTo(Post::class);
29 | }
30 |
31 | public function reactions(): MorphMany
32 | {
33 | return $this->morphMany(Reaction::class, 'object');
34 | }
35 |
36 | public function comments(): HasMany
37 | {
38 | return $this->hasMany(self::class, 'parent_id');
39 | }
40 |
41 | public function isOwner($userId)
42 | {
43 | return $this->user_id == $userId;
44 | }
45 |
46 | public static function getAllChildrenComments($comment): array
47 | {
48 | $comments = Comment::query()->where('post_id', $comment->post_id)->get();
49 | $result = [$comment];
50 | self::_getAllChildrenComments($comments, $comment->id, $result);
51 |
52 | return $result;
53 | }
54 |
55 | private static function _getAllChildrenComments($comments, $parentId, &$result = []): void
56 | {
57 | foreach ($comments as $comment) {
58 | if ($comment->parent_id === $parentId) {
59 | $result[] = $comment;
60 | // Find all comment which has parentId as $comment->id
61 | self::_getAllChildrenComments($comments, $comment->id, $result);
62 | }
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/app/Models/User.php:
--------------------------------------------------------------------------------
1 |
24 | */
25 | protected $fillable = [
26 | 'name',
27 | 'username',
28 | 'email',
29 | 'password',
30 | 'cover_path',
31 | 'avatar_path',
32 | 'pinned_post_id'
33 | ];
34 |
35 | /**
36 | * The attributes that should be hidden for serialization.
37 | *
38 | * @var array
39 | */
40 | protected $hidden = [
41 | 'password',
42 | 'remember_token',
43 | ];
44 |
45 | /**
46 | * The attributes that should be cast.
47 | *
48 | * @var array
49 | */
50 | protected $casts = [
51 | 'email_verified_at' => 'datetime',
52 | 'password' => 'hashed',
53 | ];
54 |
55 | public function getSlugOptions(): SlugOptions
56 | {
57 | return SlugOptions::create()
58 | ->generateSlugsFrom('name')
59 | ->saveSlugsTo('username')
60 | ->doNotGenerateSlugsOnUpdate();
61 | }
62 |
63 | public function followers(): BelongsToMany
64 | {
65 | return $this->belongsToMany(User::class, 'followers', 'user_id', 'follower_id');
66 | }
67 |
68 | public function followings(): BelongsToMany
69 | {
70 | return $this->belongsToMany(User::class, 'followers', 'follower_id', 'user_id');
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/resources/js/Pages/Auth/ForgotPassword.vue:
--------------------------------------------------------------------------------
1 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | Forgot your password? No problem. Just let us know your email address and we will email you a password reset
30 | link that will allow you to choose a new one.
31 |
27 | Thanks for signing up! Before getting started, could you verify your email address by clicking on the link
28 | we just emailed to you? If you didn't receive the email, we will gladly send you another.
29 |
30 |
31 |
32 | A new verification link has been sent to the email address you provided during registration.
33 |