├── .editorconfig
├── .env.example
├── .env.testing
├── .eslintrc.cjs
├── .gitattributes
├── .github
├── FUNDING.yml
└── ISSUE_TEMPLATE
│ └── bug_report.md
├── .gitignore
├── .kamal
└── secrets
├── .node-version
├── .yarnrc.yml
├── Dockerfile
├── LICENSE
├── README.md
├── app
├── Broadcasting
│ └── PostChannel.php
├── Concern
│ └── Likeable.php
├── Console
│ └── Kernel.php
├── Events
│ └── CommentPosted.php
├── Exceptions
│ └── Handler.php
├── Helpers
│ └── date.php
├── Http
│ ├── Controllers
│ │ ├── Admin
│ │ │ ├── CommentController.php
│ │ │ ├── MediaLibraryController.php
│ │ │ ├── PostController.php
│ │ │ ├── PostThumbnailController.php
│ │ │ ├── ShowDashboard.php
│ │ │ └── UserController.php
│ │ ├── Api
│ │ │ └── V1
│ │ │ │ ├── Auth
│ │ │ │ └── AuthenticateController.php
│ │ │ │ ├── CommentController.php
│ │ │ │ ├── MediaController.php
│ │ │ │ ├── PostCommentController.php
│ │ │ │ ├── PostController.php
│ │ │ │ ├── PostLikeController.php
│ │ │ │ ├── UserCommentController.php
│ │ │ │ ├── UserController.php
│ │ │ │ └── UserPostController.php
│ │ ├── Auth
│ │ │ ├── AuthenticatedSessionController.php
│ │ │ ├── EmailVerificationNotificationController.php
│ │ │ ├── EmailVerificationPromptController.php
│ │ │ ├── NewPasswordController.php
│ │ │ ├── PasswordController.php
│ │ │ ├── PasswordResetLinkController.php
│ │ │ ├── RegisteredUserController.php
│ │ │ └── VerifyEmailController.php
│ │ ├── CommentController.php
│ │ ├── Controller.php
│ │ ├── NewsletterSubscriptionController.php
│ │ ├── PostCommentController.php
│ │ ├── PostController.php
│ │ ├── PostFeedController.php
│ │ ├── PostLikeController.php
│ │ ├── UserController.php
│ │ └── UserPasswordController.php
│ ├── Middleware
│ │ ├── Authenticate.php
│ │ ├── EncryptCookies.php
│ │ ├── PreventRequestsDuringMaintenance.php
│ │ ├── RedirectIfAuthenticated.php
│ │ ├── RoleMiddleware.php
│ │ ├── TrimStrings.php
│ │ ├── TrustProxies.php
│ │ └── VerifyCsrfToken.php
│ ├── Requests
│ │ ├── Admin
│ │ │ ├── CommentsRequest.php
│ │ │ ├── MediaLibraryRequest.php
│ │ │ ├── PostsRequest.php
│ │ │ └── UsersRequest.php
│ │ ├── Api
│ │ │ └── CommentsRequest.php
│ │ ├── Auth
│ │ │ └── LoginRequest.php
│ │ ├── CommentsRequest.php
│ │ ├── NewsletterSubscriptionRequest.php
│ │ ├── UserPasswordsRequest.php
│ │ └── UsersRequest.php
│ └── Resources
│ │ ├── Comment.php
│ │ ├── Media.php
│ │ ├── Post.php
│ │ ├── Role.php
│ │ └── User.php
├── Jobs
│ ├── PrepareNewsletterSubscriptionEmail.php
│ ├── SendNewsletterSubscriptionEmail.php
│ └── UnsubscribeEmailNewsletter.php
├── Mail
│ └── Newsletter.php
├── Models
│ ├── Comment.php
│ ├── Like.php
│ ├── Media.php
│ ├── MediaLibrary.php
│ ├── NewsletterSubscription.php
│ ├── Post.php
│ ├── Role.php
│ └── User.php
├── Observers
│ ├── CommentObserver.php
│ ├── MediaObserver.php
│ ├── PostObserver.php
│ └── UserObserver.php
├── Policies
│ ├── CommentPolicy.php
│ ├── MediaPolicy.php
│ ├── PostPolicy.php
│ └── UserPolicy.php
├── Providers
│ ├── AppServiceProvider.php
│ ├── AuthServiceProvider.php
│ ├── BladeServiceProvider.php
│ ├── BroadcastServiceProvider.php
│ ├── EventServiceProvider.php
│ ├── HorizonServiceProvider.php
│ ├── ObserverServiceProvider.php
│ ├── RouteServiceProvider.php
│ └── TelescopeServiceProvider.php
├── Rules
│ ├── AlphaName.php
│ ├── CanBeAuthor.php
│ └── CurrentPassword.php
├── Scopes
│ └── PostedScope.php
└── View
│ └── Components
│ ├── Alert.php
│ ├── Card.php
│ └── Icon.php
├── artisan
├── bootstrap
├── app.php
└── cache
│ └── .gitignore
├── composer.json
├── composer.lock
├── config
├── app.php
├── auth.php
├── broadcasting.php
├── cache.php
├── cors.php
├── database.php
├── deploy.yml
├── filesystems.php
├── hashing.php
├── horizon.php
├── logging.php
├── mail.php
├── media-library.php
├── queue.php
├── sanctum.php
├── services.php
├── session.php
├── telescope.php
└── view.php
├── data
└── .gitkeep
├── database
├── .gitignore
├── factories
│ ├── CommentFactory.php
│ ├── LikeFactory.php
│ ├── NewsletterSubscriptionFactory.php
│ ├── PostFactory.php
│ ├── RoleFactory.php
│ └── UserFactory.php
├── migrations
│ ├── .gitkeep
│ ├── 2014_10_12_000000_create_users_table.php
│ ├── 2014_10_12_100000_create_password_resets_table.php
│ ├── 2016_12_19_080506_create_posts_table.php
│ ├── 2016_12_19_201351_create_comments_table.php
│ ├── 2016_12_30_125504_create_newsletter_subscriptions_table.php
│ ├── 2016_12_30_171448_create_jobs_table.php
│ ├── 2017_01_08_205244_add_provider_and_provider_id_to_users.php
│ ├── 2017_01_09_191917_update_email_and_password_to_nullable_to_users.php
│ ├── 2017_01_23_210625_create_roles_table.php
│ ├── 2017_01_29_003732_add_registered_at_to_users_table.php
│ ├── 2017_03_12_213124_create_media_table.php
│ ├── 2017_03_19_102521_add_thumbnail_id_to_posts_table.php
│ ├── 2017_03_25_194948_add_api_token_to_users_table.php
│ ├── 2017_04_16_092512_add_slug_to_posts.php
│ ├── 2017_11_14_001056_add_index_on_title_to_posts.php
│ ├── 2017_11_15_003340_create_likes_table.php
│ ├── 2018_03_22_214952_drop_media_table.php
│ ├── 2018_03_22_215224_create_media_table_with_media_library.php
│ ├── 2018_03_22_230046_create_media_libraries_table.php
│ ├── 2018_09_05_220100_add_email_verified_at_to_users.php
│ ├── 2019_09_09_000456_create_failed_jobs_table.php
│ ├── 2019_12_14_000001_create_personal_access_tokens_table.php
│ ├── 2020_10_06_231328_add_fields_to_media.php
│ ├── 2022_04_30_011124_add_generated_conversions_to_media_table.php
│ └── 2024_10_19_150013_create_telescope_entries_table.php
└── seeders
│ ├── DatabaseSeeder.php
│ └── dev
│ └── DevDatabaseSeeder.php
├── lang
├── en
│ ├── auth.php
│ ├── comments.php
│ ├── dashboard.php
│ ├── forms.php
│ ├── likes.php
│ ├── media.php
│ ├── newsletter.php
│ ├── pagination.php
│ ├── passwords.php
│ ├── posts.php
│ ├── roles.php
│ ├── tokens.php
│ ├── users.php
│ └── validation.php
└── fr
│ ├── auth.php
│ ├── comments.php
│ ├── dashboard.php
│ ├── forms.php
│ ├── likes.php
│ ├── media.php
│ ├── newsletter.php
│ ├── pagination.php
│ ├── passwords.php
│ ├── posts.php
│ ├── roles.php
│ ├── tokens.php
│ ├── users.php
│ └── validation.php
├── package.json
├── phpunit.xml
├── public
├── .htaccess
├── favicon.ico
├── index.php
├── robots.txt
└── web.config
├── resources
├── js
│ ├── admin.js
│ ├── app.js
│ └── bootstrap.js
├── sass
│ ├── _variables.scss
│ ├── admin.scss
│ ├── app.scss
│ ├── base
│ │ └── _utilities.scss
│ ├── layouts
│ │ ├── _administration.scss
│ │ └── _posts.scss
│ ├── modules
│ │ └── _form.scss
│ └── shared
│ │ └── _sidebar.scss
└── views
│ ├── admin
│ ├── comments
│ │ ├── _form.blade.php
│ │ ├── _list.blade.php
│ │ ├── edit.blade.php
│ │ └── index.blade.php
│ ├── dashboard
│ │ ├── _comments.blade.php
│ │ ├── _posts.blade.php
│ │ ├── _users.blade.php
│ │ └── index.blade.php
│ ├── layouts
│ │ └── app.blade.php
│ ├── media
│ │ ├── _list.blade.php
│ │ ├── create.blade.php
│ │ └── index.blade.php
│ ├── posts
│ │ ├── _form.blade.php
│ │ ├── _list.blade.php
│ │ ├── _thumbnail.blade.php
│ │ ├── create.blade.php
│ │ ├── edit.blade.php
│ │ └── index.blade.php
│ ├── shared
│ │ ├── navbar.blade.php
│ │ └── sidebar.blade.php
│ └── users
│ │ ├── _form.blade.php
│ │ ├── _list.blade.php
│ │ ├── edit.blade.php
│ │ └── index.blade.php
│ ├── auth
│ ├── forgot-password.blade.php
│ ├── login.blade.php
│ ├── register.blade.php
│ ├── reset-password.blade.php
│ └── verify-email.blade.php
│ ├── comments
│ ├── _comment.blade.php
│ ├── _form.blade.php
│ ├── _list.blade.php
│ └── index.blade.php
│ ├── components
│ ├── alert.blade.php
│ ├── card.blade.php
│ └── icon.blade.php
│ ├── emails
│ └── newsletter.blade.php
│ ├── layouts
│ ├── app.blade.php
│ └── rss.blade.php
│ ├── likes
│ ├── _like.blade.php
│ └── _likes.blade.php
│ ├── newsletters
│ └── unsubscribed.blade.php
│ ├── posts
│ ├── _empty.blade.php
│ ├── _post.blade.php
│ ├── _search_form.blade.php
│ ├── index.blade.php
│ └── show.blade.php
│ ├── posts_feed
│ ├── _show.blade.php
│ └── index.blade.php
│ ├── shared
│ ├── alerts.blade.php
│ ├── footer.blade.php
│ ├── navbar.blade.php
│ └── newsletter-form.blade.php
│ └── users
│ ├── _comment.blade.php
│ ├── _post.blade.php
│ ├── edit.blade.php
│ ├── layout.blade.php
│ ├── password.blade.php
│ └── show.blade.php
├── routes
├── admin.php
├── api.php
├── auth.php
├── channels.php
├── console.php
└── web.php
├── storage
├── app
│ ├── .gitignore
│ └── public
│ │ └── .gitignore
├── framework
│ ├── .gitignore
│ ├── cache
│ │ └── .gitignore
│ ├── sessions
│ │ └── .gitignore
│ └── views
│ │ └── .gitignore
└── logs
│ └── .gitignore
├── tests
├── Bootstrap.php
├── CreatesApplication.php
├── Feature
│ ├── Admin
│ │ ├── CommentTest.php
│ │ ├── DashboardTest.php
│ │ ├── PostTest.php
│ │ └── UserTest.php
│ ├── Api
│ │ └── V1
│ │ │ ├── Auth
│ │ │ └── AuthenticateTest.php
│ │ │ ├── CommentTest.php
│ │ │ ├── PostLikeTest.php
│ │ │ ├── PostTest.php
│ │ │ └── UserTest.php
│ ├── NewsletterSubscriptionTest.php
│ ├── PostTest.php
│ └── UserTest.php
├── TestCase.php
└── Unit
│ ├── CheckRoleMiddlewareTest.php
│ ├── CommentTest.php
│ ├── DateHelperTest.php
│ ├── LikeableTest.php
│ ├── NewsletterMailTest.php
│ ├── PostTest.php
│ └── UserTest.php
├── vite.config.js
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | insert_final_newline = true
7 | indent_style = space
8 | indent_size = 4
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
14 | [*.{yml,yaml,js}]
15 | indent_size = 2
16 |
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | APP_NAME=Laravel
2 | APP_ENV=local
3 | APP_KEY=
4 | APP_DEBUG=true
5 | APP_URL=http://laravel-blog.test
6 | LOG_CHANNEL=stack
7 |
8 | DB_CONNECTION=sqlite
9 |
10 | BROADCAST_DRIVER=pusher
11 | CACHE_DRIVER=file
12 | QUEUE_CONNECTION=redis
13 | SESSION_DRIVER=file
14 | SESSION_LIFETIME=120
15 |
16 | REDIS_HOST=127.0.0.1
17 | REDIS_PASSWORD=null
18 | REDIS_PORT=6379
19 |
20 | MAIL_MAILER=smtp
21 | MAIL_HOST=localhost
22 | MAIL_PORT=1025
23 | MAIL_USERNAME=null
24 | MAIL_PASSWORD=null
25 | MAIL_ENCRYPTION=null
26 |
27 | AWS_ACCESS_KEY_ID=
28 | AWS_SECRET_ACCESS_KEY=
29 |
30 | PUSHER_APP_ID=
31 | PUSHER_APP_KEY=
32 | PUSHER_APP_SECRET=
33 | PUSHER_APP_CLUSTER=
34 |
35 | VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
36 | VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
37 |
--------------------------------------------------------------------------------
/.env.testing:
--------------------------------------------------------------------------------
1 | APP_KEY=base64:HGT19Mfm6j77W2N6K3GXqJqqNgUromHg41lRFHesEJc=
2 | TELESCOPE_ENABLED=false
3 |
4 | DB_CONNECTION=pgsql
5 | DB_HOST=127.0.0.1
6 | DB_DATABASE=laravel_blog_test
7 | DB_USERNAME=
8 | DB_PASSWORD=
9 |
--------------------------------------------------------------------------------
/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | // https://eslint.org/docs/user-guide/configuring
2 |
3 | module.exports = {
4 | env: {
5 | browser: true,
6 | es6: true
7 | },
8 | extends: [
9 | 'standard'
10 | ],
11 | globals: {
12 | Atomics: 'readonly',
13 | SharedArrayBuffer: 'readonly',
14 | $: false
15 | },
16 | parserOptions: {
17 | sourceType: 'module'
18 | },
19 | rules: {
20 | 'no-new': 'off'
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto eol=lf
2 | *.css linguist-vendored
3 | *.scss linguist-vendored
4 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: guillaumebriday
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: "[BUG]"
5 | labels: ''
6 | assignees: guillaumebriday
7 |
8 | ---
9 |
10 | **Please read the README before considering opening an issue and be sure to check that the bug is not related to your development environment.**
11 |
12 | ## Describe the bug
13 | A clear and concise description of what the bug is.
14 |
15 | ## To Reproduce
16 | Steps to reproduce the behavior:
17 | 1. Go to '...'
18 | 2. Click on '....'
19 | 3. Scroll down to '....'
20 | 4. See error
21 |
22 | ## Expected behavior
23 | A clear and concise description of what you expected to happen.
24 |
25 | ## Screenshots
26 | If applicable, add screenshots to help explain your problem.
27 |
28 | ## Additional context
29 | Add any other context about the problem here.
30 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.phpunit.cache
2 | /node_modules
3 | /public/storage
4 | /public/hot
5 | /public/css
6 | /public/fonts
7 | /public/js
8 | /public/mix-manifest.json
9 | public/media/
10 | public/build/
11 | /vendor
12 | /.idea
13 | /.vagrant
14 | .env
15 | .php_cs.cache
16 | /storage/debugbar
17 | /storage/tmp
18 | /storage/medialibrary
19 | /log
20 | .phpunit.result.cache
21 | pint.json
22 | storage/media-library/temp
23 | .pnp.*
24 | .yarn/*
25 | !.yarn/patches
26 | !.yarn/plugins
27 | !.yarn/releases
28 | !.yarn/sdks
29 | !.yarn/versions
30 |
--------------------------------------------------------------------------------
/.kamal/secrets:
--------------------------------------------------------------------------------
1 | # Secrets defined here are available for reference under registry/password, env/secret, builder/secrets,
2 | # and accessories/*/env/secret in config/deploy.yml. All secrets should be pulled from either
3 | # password manager, ENV, or a file. DO NOT ENTER RAW CREDENTIALS HERE! This file needs to be safe for git.
4 |
5 | # Option 1: Read secrets from the environment
6 | KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD
7 | APP_KEY=$APP_KEY
8 |
--------------------------------------------------------------------------------
/.node-version:
--------------------------------------------------------------------------------
1 | 20
2 |
--------------------------------------------------------------------------------
/.yarnrc.yml:
--------------------------------------------------------------------------------
1 | nodeLinker: node-modules
2 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM serversideup/php:8.3-fpm-nginx AS base
2 |
3 | # Switch to root so we can do root things
4 | USER root
5 |
6 | # Install the exif extension with root permissions
7 | RUN install-php-extensions exif
8 |
9 | # Install JavaScript dependencies
10 | ARG NODE_VERSION=20.18.0
11 | ENV PATH=/usr/local/node/bin:$PATH
12 | RUN curl -sL https://github.com/nodenv/node-build/archive/master.tar.gz | tar xz -C /tmp/ && \
13 | /tmp/node-build-master/bin/node-build "${NODE_VERSION}" /usr/local/node && \
14 | corepack enable && \
15 | rm -rf /tmp/node-build-master
16 |
17 | # Drop back to our unprivileged user
18 | USER www-data
19 |
20 | FROM base
21 |
22 | ENV SSL_MODE="off"
23 | ENV AUTORUN_ENABLED="true"
24 | ENV PHP_OPCACHE_ENABLE="1"
25 | ENV HEALTHCHECK_PATH="/up"
26 |
27 | # Copy the app files...
28 | COPY --chown=www-data:www-data . /var/www/html
29 |
30 | # Re-run install, but now with scripts and optimizing the autoloader (should be faster)...
31 | RUN composer install --no-interaction --prefer-dist --optimize-autoloader
32 |
33 | # Precompiling assets for production
34 | RUN yarn install --immutable && \
35 | yarn build && \
36 | rm -rf node_modules
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Guillaume Briday
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/app/Broadcasting/PostChannel.php:
--------------------------------------------------------------------------------
1 | $resource->likes->each->delete());
16 | }
17 |
18 | /**
19 | * Get all of the resource's likes.
20 | */
21 | public function likes(): morphMany
22 | {
23 | return $this->morphMany(Like::class, 'likeable');
24 | }
25 |
26 | /**
27 | * Create a like if it does not exist yet.
28 | */
29 | public function like()
30 | {
31 | if ($this->likes()->where('author_id', auth()->id())->doesntExist()) {
32 | return $this->likes()->create(['author_id' => auth()->id()]);
33 | }
34 | }
35 |
36 | /**
37 | * Check if the resource is liked by the current user
38 | */
39 | public function isLiked(): bool
40 | {
41 | return $this->likes->where('author_id', auth()->id())->isNotEmpty();
42 | }
43 |
44 | /**
45 | * Delete like for a resource.
46 | */
47 | public function dislike()
48 | {
49 | return $this->likes()->where('author_id', auth()->id())->get()->each->delete();
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/app/Console/Kernel.php:
--------------------------------------------------------------------------------
1 | command('backup:clean')->daily()->at('01:00');
16 | $schedule->command('backup:run')->daily()->at('02:00');
17 | $schedule->command('telescope:prune')->daily()->at('03:00');
18 | }
19 |
20 | /**
21 | * Register the commands for the application.
22 | */
23 | protected function commands(): void
24 | {
25 | $this->load(__DIR__ . '/Commands');
26 |
27 | require base_path('routes/console.php');
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/Events/CommentPosted.php:
--------------------------------------------------------------------------------
1 | comment = $comment;
25 | $this->post = $post;
26 | }
27 |
28 | /**
29 | * Get the channels the event should broadcast on.
30 | */
31 | public function broadcastOn(): Channel
32 | {
33 | return new Channel('post.' . $this->post->id);
34 | }
35 |
36 | /**
37 | * The event's broadcast name.
38 | */
39 | public function broadcastAs(): string
40 | {
41 | return 'comment.posted';
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/app/Exceptions/Handler.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | protected $dontFlash = [
16 | 'current_password',
17 | 'password',
18 | 'password_confirmation',
19 | ];
20 |
21 | /**
22 | * Register the exception handling callbacks for the application.
23 | */
24 | public function register(): void
25 | {
26 | $this->reportable(function (Throwable $e) {
27 | //
28 | });
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/Helpers/date.php:
--------------------------------------------------------------------------------
1 | format($format);
19 | }
20 |
--------------------------------------------------------------------------------
/app/Http/Controllers/Admin/CommentController.php:
--------------------------------------------------------------------------------
1 | Comment::with('post', 'author')->latest()->paginate(50)
21 | ]);
22 | }
23 |
24 | /**
25 | * Display the specified resource edit form.
26 | */
27 | public function edit(Comment $comment): View
28 | {
29 | return view('admin.comments.edit', [
30 | 'comment' => $comment,
31 | 'users' => User::pluck('name', 'id')
32 | ]);
33 | }
34 |
35 | /**
36 | * Update the specified resource in storage.
37 | */
38 | public function update(CommentsRequest $request, Comment $comment): RedirectResponse
39 | {
40 | $comment->update($request->validated());
41 |
42 | return redirect()->route('admin.comments.edit', $comment)->withSuccess(__('comments.updated'));
43 | }
44 |
45 | /**
46 | * Remove the specified resource from storage.
47 | */
48 | public function destroy(Comment $comment): RedirectResponse
49 | {
50 | $comment->delete();
51 |
52 | return redirect()->route('admin.comments.index')->withSuccess(__('comments.deleted'));
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/app/Http/Controllers/Admin/PostThumbnailController.php:
--------------------------------------------------------------------------------
1 | update(['thumbnail_id' => null]);
17 |
18 | return redirect()->route('admin.posts.edit', $post)->withSuccess(__('posts.updated'));
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/Http/Controllers/Admin/ShowDashboard.php:
--------------------------------------------------------------------------------
1 | Comment::lastWeek()->get(),
20 | 'posts' => Post::lastWeek()->get(),
21 | 'users' => User::lastWeek()->get(),
22 | ]);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/Http/Controllers/Admin/UserController.php:
--------------------------------------------------------------------------------
1 | User::latest()->paginate(50)
22 | ]);
23 | }
24 |
25 | /**
26 | * Display the specified resource edit form.
27 | */
28 | public function edit(User $user): View
29 | {
30 | return view('admin.users.edit', [
31 | 'user' => $user,
32 | 'roles' => Role::all()
33 | ]);
34 | }
35 |
36 | /**
37 | * Update the specified resource in storage.
38 | */
39 | public function update(UsersRequest $request, User $user): RedirectResponse
40 | {
41 | if ($request->filled('password')) {
42 | $request->merge([
43 | 'password' => Hash::make($request->input('password'))
44 | ]);
45 | }
46 |
47 | $user->update(array_filter($request->only(['name', 'email', 'password'])));
48 |
49 | $role_ids = array_values($request->get('roles', []));
50 | $user->roles()->sync($role_ids);
51 |
52 | return redirect()->route('admin.users.edit', $user)->withSuccess(__('users.updated'));
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/app/Http/Controllers/Api/V1/Auth/AuthenticateController.php:
--------------------------------------------------------------------------------
1 | $request->input('email'), 'password' => $request->input('password')])) {
19 | $user = User::where('email', $request->input('email'))->first();
20 |
21 | return (new UserResource($user))
22 | ->additional(['meta' => [
23 | 'access_token' => $user->createToken('api_token')->plainTextToken
24 | ]]);
25 | }
26 |
27 | return response()->json(['message' => 'This action is unauthorized.'], 401);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/Http/Controllers/Api/V1/CommentController.php:
--------------------------------------------------------------------------------
1 | paginate($request->input('limit', 20))
21 | );
22 | }
23 |
24 | /**
25 | * Return the specified resource.
26 | */
27 | public function show(Comment $comment): CommentResource
28 | {
29 | return new CommentResource($comment);
30 | }
31 |
32 | /**
33 | * Remove the specified resource from storage.
34 | */
35 | public function destroy(Comment $comment): Response
36 | {
37 | $this->authorize('delete', $comment);
38 |
39 | $comment->delete();
40 |
41 | return response()->noContent();
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/app/Http/Controllers/Api/V1/MediaController.php:
--------------------------------------------------------------------------------
1 | media()->paginate($request->input('limit', 20))
23 | );
24 | }
25 |
26 | /**
27 | * Store a newly created resource in storage.
28 | */
29 | public function store(MediaLibraryRequest $request): MediaResource
30 | {
31 | $this->authorize('store', Media::class);
32 |
33 | $image = $request->file('image');
34 | $name = $image->getClientOriginalName();
35 |
36 | if ($request->filled('name')) {
37 | $name = $request->input('name');
38 | }
39 |
40 | return new MediaResource(
41 | MediaLibrary::first()
42 | ->addMedia($image)
43 | ->usingName($name)
44 | ->toMediaCollection()
45 | );
46 | }
47 |
48 | /**
49 | * Remove the specified resource from storage.
50 | */
51 | public function destroy(Media $medium): Response
52 | {
53 | $this->authorize('delete', $medium);
54 |
55 | $medium->delete();
56 |
57 | return response()->noContent();
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/app/Http/Controllers/Api/V1/PostCommentController.php:
--------------------------------------------------------------------------------
1 | comments()->with('author')->latest()->paginate($request->input('limit', 20))
23 | );
24 | }
25 |
26 | /**
27 | * Store a newly created resource in storage.
28 | */
29 | public function store(CommentsRequest $request, Post $post): CommentResource
30 | {
31 | $comment = new CommentResource(
32 | Auth::user()->comments()->create([
33 | 'post_id' => $post->id,
34 | 'content' => $request->input('content')
35 | ])
36 | );
37 |
38 | broadcast(new CommentPosted($comment, $post))->toOthers();
39 |
40 | return $comment;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/app/Http/Controllers/Api/V1/PostLikeController.php:
--------------------------------------------------------------------------------
1 | like();
19 | }
20 |
21 | /**
22 | * Remove the specified resource from storage.
23 | *
24 | * @return \Illuminate\Http\Response
25 | */
26 | public function destroy(Post $post)
27 | {
28 | return $post->dislike();
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/Http/Controllers/Api/V1/UserCommentController.php:
--------------------------------------------------------------------------------
1 | comments()->latest()->paginate($request->input('limit', 20))
20 | );
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/Http/Controllers/Api/V1/UserController.php:
--------------------------------------------------------------------------------
1 | with('roles')->latest()->paginate($request->input('limit', 20))
22 | );
23 | }
24 |
25 | /**
26 | * Return the specified resource.
27 | */
28 | public function show(User $user): UserResource
29 | {
30 | return new UserResource($user);
31 | }
32 |
33 | /**
34 | * Update the specified resource in storage.
35 | */
36 | public function update(UsersRequest $request, User $user): UserResource
37 | {
38 | $this->authorize('update', $user);
39 |
40 | if ($request->filled('password')) {
41 | $request->merge([
42 | 'password' => Hash::make($request->input('password'))
43 | ]);
44 | }
45 |
46 | $user->update(array_filter($request->only(['name', 'email', 'password'])));
47 |
48 | return new UserResource($user);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/app/Http/Controllers/Api/V1/UserPostController.php:
--------------------------------------------------------------------------------
1 | posts()->latest()->paginate($request->input('limit', 20))
20 | );
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/Http/Controllers/Auth/AuthenticatedSessionController.php:
--------------------------------------------------------------------------------
1 | authenticate();
29 |
30 | $request->session()->regenerate();
31 |
32 | return redirect()->intended(RouteServiceProvider::HOME);
33 | }
34 |
35 | /**
36 | * Destroy an authenticated session.
37 | */
38 | public function destroy(Request $request): RedirectResponse
39 | {
40 | Auth::guard('web')->logout();
41 |
42 | $request->session()->invalidate();
43 |
44 | $request->session()->regenerateToken();
45 |
46 | return redirect('/');
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/app/Http/Controllers/Auth/EmailVerificationNotificationController.php:
--------------------------------------------------------------------------------
1 | user()->hasVerifiedEmail()) {
18 | return redirect()->intended(RouteServiceProvider::HOME);
19 | }
20 |
21 | $request->user()->sendEmailVerificationNotification();
22 |
23 | return back()->with('status', 'verification-link-sent');
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/Http/Controllers/Auth/EmailVerificationPromptController.php:
--------------------------------------------------------------------------------
1 | user()->hasVerifiedEmail()
19 | ? redirect()->intended(RouteServiceProvider::HOME)
20 | : view('auth.verify-email');
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/Http/Controllers/Auth/PasswordController.php:
--------------------------------------------------------------------------------
1 | validateWithBag('updatePassword', [
19 | 'current_password' => ['required', 'current_password'],
20 | 'password' => ['required', Password::defaults(), 'confirmed'],
21 | ]);
22 |
23 | $request->user()->update([
24 | 'password' => Hash::make($validated['password']),
25 | ]);
26 |
27 | return back()->with('status', 'password-updated');
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/Http/Controllers/Auth/PasswordResetLinkController.php:
--------------------------------------------------------------------------------
1 | validate([
29 | 'email' => ['required', 'email'],
30 | ]);
31 |
32 | // We will send the password reset link to this user. Once we have attempted
33 | // to send the link, we will examine the response then see the message we
34 | // need to show to the user. Finally, we'll send out a proper response.
35 | $status = Password::sendResetLink(
36 | $request->only('email')
37 | );
38 |
39 | return $status == Password::RESET_LINK_SENT
40 | ? back()->with('status', __($status))
41 | : back()->withInput($request->only('email'))
42 | ->withErrors(['email' => __($status)]);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/app/Http/Controllers/Auth/RegisteredUserController.php:
--------------------------------------------------------------------------------
1 | validate([
34 | 'name' => ['required', 'string', 'max:255'],
35 | 'email' => ['required', 'string', 'email', 'max:255', 'unique:' . User::class],
36 | 'password' => ['required', 'confirmed', Rules\Password::defaults()],
37 | ]);
38 |
39 | $user = User::create([
40 | 'name' => $request->name,
41 | 'email' => $request->email,
42 | 'password' => Hash::make($request->password),
43 | ]);
44 |
45 | event(new Registered($user));
46 |
47 | Auth::login($user);
48 |
49 | return redirect(RouteServiceProvider::HOME);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/app/Http/Controllers/Auth/VerifyEmailController.php:
--------------------------------------------------------------------------------
1 | user()->hasVerifiedEmail()) {
19 | return redirect()->intended(RouteServiceProvider::HOME . '?verified=1');
20 | }
21 |
22 | if ($request->user()->markEmailAsVerified()) {
23 | event(new Verified($request->user()));
24 | }
25 |
26 | return redirect()->intended(RouteServiceProvider::HOME . '?verified=1');
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/Http/Controllers/CommentController.php:
--------------------------------------------------------------------------------
1 | comments()->create($request->validated());
18 |
19 | return response()->turboStream([
20 | response()->turboStream()->prepend('comments')->view('comments._comment', ['comment' => $comment]),
21 | response()->turboStream()->replace('comments_form')->view('comments._form', ['post' => $comment->post]),
22 | response()->turboStream()->update('comments_count', trans_choice('comments.count', $comment->post->comments()->count()))
23 | ]);
24 | }
25 |
26 | /**
27 | * Remove the specified resource from storage.
28 | */
29 | public function destroy(Comment $comment): MultiplePendingTurboStreamResponse
30 | {
31 | $this->authorize('delete', $comment);
32 |
33 | $comment->delete();
34 |
35 | return response()->turboStream([
36 | response()->turboStream()->remove($comment),
37 | response()->turboStream()->update('comments_count', trans_choice('comments.count', $comment->post->comments()->count()))
38 | ]);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/Http/Controllers/Controller.php:
--------------------------------------------------------------------------------
1 | validated());
22 |
23 | return back()->withSuccess(__('newsletter.created'));
24 | }
25 |
26 | /**
27 | * Remove the specified resource from storage.
28 | */
29 | public function unsubscribe(Request $request)
30 | {
31 | $validator = Validator::make($request->all(), [
32 | 'email' => 'required|email|exists:newsletter_subscriptions,email'
33 | ]);
34 |
35 | if ($validator->fails()) {
36 | $errors = $validator->errors()->all();
37 | $route = 'login';
38 |
39 | if (Auth::check()) {
40 | $route = 'home';
41 | }
42 |
43 | return redirect()->route($route)->withErrors($errors);
44 | }
45 |
46 | UnsubscribeEmailNewsletter::dispatch($request->input('email'));
47 |
48 | Session::flash('success', __('newsletter.unsubscribed'));
49 |
50 | return view('newsletters.unsubscribed');
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/Http/Controllers/PostCommentController.php:
--------------------------------------------------------------------------------
1 | $post->comments()->with('author')->latest()->get()
17 | ]);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/Http/Controllers/PostController.php:
--------------------------------------------------------------------------------
1 | Post::search($request->input('q'))
18 | ->with('author', 'likes')
19 | ->withCount('comments', 'thumbnail', 'likes')
20 | ->latest()
21 | ->paginate(20)
22 | ]);
23 | }
24 |
25 | /**
26 | * Display the specified resource.
27 | */
28 | public function show(Request $request, Post $post): View
29 | {
30 | $post->comments_count = $post->comments()->count();
31 | $post->likes_count = $post->likes()->count();
32 |
33 | return view('posts.show', [
34 | 'post' => $post
35 | ]);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/Http/Controllers/PostFeedController.php:
--------------------------------------------------------------------------------
1 | addHour(), fn () => Post::latest()->limit(20)->get());
17 |
18 | return response()->view('posts_feed.index', [
19 | 'posts' => $posts
20 | ], 200)->header('Content-Type', 'text/xml');
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/Http/Controllers/PostLikeController.php:
--------------------------------------------------------------------------------
1 | like();
19 |
20 | return response()->turboStream([
21 | response()->turboStream()->replace(dom_id($post, 'like'))->view('likes._like', ['post' => $post]),
22 | response()->turboStream()->update(dom_id($post, 'likes_count'), Str::of($post->likes()->count()))
23 | ]);
24 | }
25 |
26 | /**
27 | * Remove the specified resource from storage.
28 | */
29 | public function destroy(Post $post): MultiplePendingTurboStreamResponse
30 | {
31 | $post->dislike();
32 |
33 | return response()->turboStream([
34 | response()->turboStream()->replace(dom_id($post, 'like'))->view('likes._like', ['post' => $post]),
35 | response()->turboStream()->update(dom_id($post, 'likes_count'), Str::of($post->likes()->count()))
36 | ]);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/Http/Controllers/UserController.php:
--------------------------------------------------------------------------------
1 | $user,
21 | 'posts_count' => $user->posts()->count(),
22 | 'comments_count' => $user->comments()->count(),
23 | 'likes_count' => $user->likes()->count(),
24 | 'posts' => $user->posts()->withCount('likes', 'comments')->latest()->limit(5)->get(),
25 | 'comments' => $user->comments()->with('post.author')->latest()->limit(5)->get()
26 | ]);
27 | }
28 |
29 | /**
30 | * Show the form for editing the specified resource.
31 | */
32 | public function edit(): View
33 | {
34 | $user = auth()->user();
35 |
36 | $this->authorize('update', $user);
37 |
38 | return view('users.edit', [
39 | 'user' => $user,
40 | 'roles' => Role::all()
41 | ]);
42 | }
43 |
44 | /**
45 | * Update the specified resource in storage.
46 | */
47 | public function update(UsersRequest $request): RedirectResponse
48 | {
49 | $user = auth()->user();
50 |
51 | $this->authorize('update', $user);
52 |
53 | $user->update($request->validated());
54 |
55 | return redirect()->route('users.edit')->withSuccess(__('users.updated'));
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/app/Http/Controllers/UserPasswordController.php:
--------------------------------------------------------------------------------
1 | user();
18 |
19 | $this->authorize('update', $user);
20 |
21 | return view('users.password', ['user' => $user]);
22 | }
23 |
24 | /**
25 | * Update password for the specified resource in storage.
26 | */
27 | public function update(UserPasswordsRequest $request): RedirectResponse
28 | {
29 | $user = auth()->user();
30 |
31 | $this->authorize('update', $user);
32 |
33 | $request->merge([
34 | 'password' => Hash::make($request->input('password'))
35 | ]);
36 |
37 | $user->update($request->only('password'));
38 |
39 | return redirect()->route('users.password')->withSuccess(__('users.password_updated'));
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/Http/Middleware/Authenticate.php:
--------------------------------------------------------------------------------
1 | expectsJson() ? null : route('login');
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/Http/Middleware/EncryptCookies.php:
--------------------------------------------------------------------------------
1 | check()) {
24 | return redirect(RouteServiceProvider::HOME);
25 | }
26 | }
27 |
28 | return $next($request);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/Http/Middleware/RoleMiddleware.php:
--------------------------------------------------------------------------------
1 | user()->hasRole($role)) {
16 | return redirect()->route('home')->withErrors(__('auth.not_authorized'));
17 | }
18 |
19 | return $next($request);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/Http/Middleware/TrimStrings.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | protected $except = [
15 | 'current_password',
16 | 'password',
17 | 'password_confirmation',
18 | ];
19 | }
20 |
--------------------------------------------------------------------------------
/app/Http/Middleware/TrustProxies.php:
--------------------------------------------------------------------------------
1 | |string|null
14 | */
15 | protected $proxies;
16 |
17 | /**
18 | * The headers that should be used to detect proxies.
19 | *
20 | * @var int
21 | */
22 | protected $headers =
23 | Request::HEADER_X_FORWARDED_FOR |
24 | Request::HEADER_X_FORWARDED_HOST |
25 | Request::HEADER_X_FORWARDED_PORT |
26 | Request::HEADER_X_FORWARDED_PROTO |
27 | Request::HEADER_X_FORWARDED_AWS_ELB;
28 | }
29 |
--------------------------------------------------------------------------------
/app/Http/Middleware/VerifyCsrfToken.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | protected $except = [
15 | //
16 | ];
17 | }
18 |
--------------------------------------------------------------------------------
/app/Http/Requests/Admin/CommentsRequest.php:
--------------------------------------------------------------------------------
1 | merge([
24 | 'posted_at' => Carbon::parse($this->input('posted_at'))
25 | ]);
26 | }
27 |
28 | /**
29 | * Get the validation rules that apply to the request.
30 | */
31 | public function rules(): array
32 | {
33 | return [
34 | 'content' => 'required',
35 | 'posted_at' => 'required|after_or_equal:' . $this->comment->post->posted_at,
36 | 'author_id' => 'required|exists:users,id'
37 | ];
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/app/Http/Requests/Admin/MediaLibraryRequest.php:
--------------------------------------------------------------------------------
1 | 'required|image',
24 | 'name' => 'nullable|string|max:255'
25 | ];
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/Http/Requests/Admin/PostsRequest.php:
--------------------------------------------------------------------------------
1 | merge([
26 | 'slug' => Str::slug($this->input('title'))
27 | ]);
28 |
29 | $this->merge([
30 | 'posted_at' => Carbon::parse($this->input('posted_at'))
31 | ]);
32 | }
33 |
34 | /**
35 | * Get the validation rules that apply to the request.
36 | */
37 | public function rules(): array
38 | {
39 | return [
40 | 'title' => 'required',
41 | 'content' => 'required',
42 | 'posted_at' => 'required|date',
43 | 'thumbnail_id' => 'nullable|exists:media,id',
44 | 'author_id' => ['required', 'exists:users,id', new CanBeAuthor],
45 | 'slug' => 'unique:posts,slug,' . (optional($this->post)->id ?: 'NULL'),
46 | ];
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/app/Http/Requests/Admin/UsersRequest.php:
--------------------------------------------------------------------------------
1 | ['required', 'string', 'max:255', new AlphaName],
25 | 'email' => 'required|email|unique:users,email,' . $this->user->id,
26 | 'password' => 'nullable|confirmed',
27 | 'roles.*' => 'exists:roles,id'
28 | ];
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/Http/Requests/Api/CommentsRequest.php:
--------------------------------------------------------------------------------
1 | 'required'
24 | ];
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/Http/Requests/CommentsRequest.php:
--------------------------------------------------------------------------------
1 | 'required',
24 | 'post_id' => 'required|exists:posts,id'
25 | ];
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/Http/Requests/NewsletterSubscriptionRequest.php:
--------------------------------------------------------------------------------
1 | 'required|email|unique:newsletter_subscriptions',
24 | ];
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/Http/Requests/UserPasswordsRequest.php:
--------------------------------------------------------------------------------
1 | ['required', new CurrentPassword],
25 | 'password' => 'required|confirmed'
26 | ];
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/Http/Requests/UsersRequest.php:
--------------------------------------------------------------------------------
1 | ['required', 'string', 'max:255', new AlphaName],
25 | 'email' => 'required|email|unique:users,email,' . auth()->user()->id,
26 | ];
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/Http/Resources/Comment.php:
--------------------------------------------------------------------------------
1 | user();
16 |
17 | return [
18 | 'id' => $this->id,
19 | 'content' => $this->content,
20 | 'posted_at' => $this->posted_at->toIso8601String(),
21 | 'humanized_posted_at' => humanize_date($this->posted_at),
22 | 'author_id' => $this->author_id,
23 | 'post_id' => $this->post_id,
24 | 'author_name' => $this->author->name,
25 | 'author_url' => route('users.show', $this->author),
26 | 'can_delete' => $user ? $user->can('delete', $this->resource) : false
27 | ];
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/Http/Resources/Media.php:
--------------------------------------------------------------------------------
1 | $this->id,
16 | 'name' => $this->name,
17 | 'url' => url($this->getUrl()),
18 | 'thumb_url' => url($this->getUrl('thumb')),
19 | ];
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/Http/Resources/Post.php:
--------------------------------------------------------------------------------
1 | $this->id,
16 | 'title' => $this->title,
17 | 'slug' => $this->slug,
18 | 'content' => $this->content,
19 | 'posted_at' => $this->posted_at->toIso8601String(),
20 | 'author_id' => $this->author_id,
21 | 'comments_count' => $this->comments_count ?? $this->comments()->count(),
22 | 'thumbnail_url' => $this->when($this->hasThumbnail(), url(optional($this->thumbnail)->getUrl())),
23 | 'thumb_thumbnail_url' => $this->when($this->hasThumbnail(), url(optional($this->thumbnail)->getUrl('thumb')))
24 | ];
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/Http/Resources/Role.php:
--------------------------------------------------------------------------------
1 | $this->id,
16 | 'name' => $this->name
17 | ];
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/Http/Resources/User.php:
--------------------------------------------------------------------------------
1 | $this->id,
16 | 'name' => $this->name,
17 | 'email' => $this->email,
18 | 'provider' => $this->provider,
19 | 'provider_id' => $this->provider_id,
20 | 'registered_at' => $this->registered_at->toIso8601String(),
21 | 'comments_count' => $this->comments_count ?? $this->comments()->count(),
22 | 'posts_count' => $this->posts_count ?? $this->posts()->count(),
23 | 'roles' => Role::collection($this->roles),
24 | ];
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/Jobs/PrepareNewsletterSubscriptionEmail.php:
--------------------------------------------------------------------------------
1 | each(fn ($newsletterSubscription) => SendNewsletterSubscriptionEmail::dispatch($newsletterSubscription->email));
24 |
25 | PrepareNewsletterSubscriptionEmail::dispatch()->delay(carbon('next month'));
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/Jobs/SendNewsletterSubscriptionEmail.php:
--------------------------------------------------------------------------------
1 | email = $email;
25 | }
26 |
27 | /**
28 | * Execute the job.
29 | */
30 | public function handle(): void
31 | {
32 | $posts = Post::lastMonth()->get();
33 | $email = $this->email;
34 |
35 | Mail::to($this->email)->send(new Newsletter($posts, $email));
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/Jobs/UnsubscribeEmailNewsletter.php:
--------------------------------------------------------------------------------
1 | email = $email;
23 | }
24 |
25 | /**
26 | * Execute the job.
27 | */
28 | public function handle(): void
29 | {
30 | $email = $this->email;
31 |
32 | $newsletterSubscription = NewsletterSubscription::where('email', $email)->first();
33 | if ($newsletterSubscription) {
34 | $newsletterSubscription->delete();
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/Mail/Newsletter.php:
--------------------------------------------------------------------------------
1 | posts = $posts;
21 | $this->email = $email;
22 | }
23 |
24 | /**
25 | * Build the message.
26 | */
27 | public function build(): Newsletter
28 | {
29 | return $this->from('hello@app.com', config('app.name', 'Laravel'))
30 | ->subject(__('newsletter.email.subject'))
31 | ->view('emails.newsletter')
32 | ->with([
33 | 'posts' => $this->posts,
34 | 'email' => $this->email
35 | ]);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/Models/Comment.php:
--------------------------------------------------------------------------------
1 | 'datetime'
34 | ];
35 |
36 | /**
37 | * Scope a query to only include comments posted last week.
38 | */
39 | public function scopeLastWeek(Builder $query): Builder
40 | {
41 | return $query->whereBetween('posted_at', [carbon('1 week ago'), now()])
42 | ->latest();
43 | }
44 |
45 | /**
46 | * Scope a query to order comments by latest posted.
47 | */
48 | public function scopeLatest(Builder $query): Builder
49 | {
50 | return $query->orderBy('posted_at', 'desc');
51 | }
52 |
53 | /**
54 | * Return the comment's author
55 | */
56 | public function author(): BelongsTo
57 | {
58 | return $this->belongsTo(User::class, 'author_id');
59 | }
60 |
61 | /**
62 | * Return the comment's post
63 | */
64 | public function post(): BelongsTo
65 | {
66 | return $this->belongsTo(Post::class);
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/app/Models/Like.php:
--------------------------------------------------------------------------------
1 | morphTo();
31 | }
32 |
33 | /**
34 | * Return the like's author
35 | */
36 | public function author(): BelongsTo
37 | {
38 | return $this->belongsTo(User::class, 'author_id');
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/Models/Media.php:
--------------------------------------------------------------------------------
1 | 'datetime',
16 | 'manipulations' => 'array',
17 | 'custom_properties' => 'array',
18 | 'generated_conversions' => 'array',
19 | 'responsive_images' => 'array',
20 | ];
21 | }
22 |
--------------------------------------------------------------------------------
/app/Models/MediaLibrary.php:
--------------------------------------------------------------------------------
1 | addMediaConversion('thumb')
17 | ->width(350)
18 | ->height(250);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/Models/NewsletterSubscription.php:
--------------------------------------------------------------------------------
1 | posted_at = now();
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/app/Observers/MediaObserver.php:
--------------------------------------------------------------------------------
1 | posted_at = now();
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/app/Observers/PostObserver.php:
--------------------------------------------------------------------------------
1 | slug = Str::slug($post->title, '-');
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/Observers/UserObserver.php:
--------------------------------------------------------------------------------
1 | registered_at = now();
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/app/Policies/CommentPolicy.php:
--------------------------------------------------------------------------------
1 | isAdmin()) {
19 | return true;
20 | }
21 | }
22 |
23 | /**
24 | * Determine whether the user can delete the comment.
25 | */
26 | public function delete(User $user, Comment $comment): bool
27 | {
28 | return $user->id === $comment->author_id;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/Policies/MediaPolicy.php:
--------------------------------------------------------------------------------
1 | isAdmin()) {
19 | return true;
20 | }
21 | }
22 |
23 | /**
24 | * Determine whether the user can store a medium.
25 | */
26 | public function store(User $user): bool
27 | {
28 | return $user->isAdmin();
29 | }
30 |
31 | /**
32 | * Determine whether the user can delete the medium.
33 | */
34 | public function delete(User $user, Media $medium): bool
35 | {
36 | return $user->isAdmin();
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/Policies/PostPolicy.php:
--------------------------------------------------------------------------------
1 | isAdmin()) {
19 | return true;
20 | }
21 | }
22 |
23 | /**
24 | * Determine whether the user can update the post.
25 | */
26 | public function update(User $user, Post $post): bool
27 | {
28 | return $user->isAdmin();
29 | }
30 |
31 | /**
32 | * Determine whether the user can store a post.
33 | */
34 | public function store(User $user): bool
35 | {
36 | return $user->isAdmin();
37 | }
38 |
39 | /**
40 | * Determine whether the user can delete the post.
41 | */
42 | public function delete(User $user, Post $post): bool
43 | {
44 | return $user->isAdmin();
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/app/Policies/UserPolicy.php:
--------------------------------------------------------------------------------
1 | isAdmin()) {
18 | return true;
19 | }
20 | }
21 |
22 | /**
23 | * Determine whether the user can update the user.
24 | */
25 | public function update(User $current_user, User $user): bool
26 | {
27 | return $current_user->id === $user->id;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/Providers/AppServiceProvider.php:
--------------------------------------------------------------------------------
1 | ";
26 | });
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/Providers/AuthServiceProvider.php:
--------------------------------------------------------------------------------
1 | 'App\Policies\CommentPolicy',
16 | 'App\Models\Post' => 'App\Policies\PostPolicy',
17 | 'App\Models\User' => 'App\Policies\UserPolicy',
18 | 'App\Models\Media' => 'App\Policies\MediaPolicy',
19 | ];
20 |
21 | /**
22 | * Register any authentication / authorization services.
23 | */
24 | public function boot(): void
25 | {
26 | $this->registerPolicies();
27 |
28 | //
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/Providers/BladeServiceProvider.php:
--------------------------------------------------------------------------------
1 | Auth::check() && Auth::user()->isAdmin());
17 |
18 | Blade::if('profile', fn ($user) => Auth::check() && Auth::user()->id == $user->id);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/Providers/BroadcastServiceProvider.php:
--------------------------------------------------------------------------------
1 | >
16 | */
17 | protected $listen = [
18 | Registered::class => [
19 | SendEmailVerificationNotification::class,
20 | ],
21 | ];
22 |
23 | /**
24 | * Register any events for your application.
25 | */
26 | public function boot(): void
27 | {
28 | //
29 | }
30 |
31 | /**
32 | * Determine if events and listeners should be automatically discovered.
33 | */
34 | public function shouldDiscoverEvents(): bool
35 | {
36 | return false;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/Providers/HorizonServiceProvider.php:
--------------------------------------------------------------------------------
1 | environment('local')) {
16 | Horizon::auth(fn ($request) => auth()->check() && auth()->user()->isAdmin());
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/Providers/ObserverServiceProvider.php:
--------------------------------------------------------------------------------
1 | app->isLocal()) {
21 | return true;
22 | }
23 |
24 | return $entry->isReportableException() ||
25 | $entry->isFailedJob() ||
26 | $entry->isScheduledTask() ||
27 | $entry->hasMonitoredTag();
28 | });
29 | }
30 |
31 | /**
32 | * Register the Telescope gate.
33 | *
34 | * This gate determines who can access Telescope in non-local environments.
35 | */
36 | protected function gate(): void
37 | {
38 | Gate::define('viewTelescope', fn ($user) => $user->isAdmin());
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/Rules/AlphaName.php:
--------------------------------------------------------------------------------
1 | 0;
21 | }
22 |
23 | /**
24 | * Get the validation error message.
25 | */
26 | public function message(): string
27 | {
28 | return trans('validation.alpha_name');
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/Rules/CanBeAuthor.php:
--------------------------------------------------------------------------------
1 | canBeAuthor();
18 | }
19 |
20 | /**
21 | * Get the validation error message.
22 | */
23 | public function message(): string
24 | {
25 | return trans('validation.can_be_author');
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/Rules/CurrentPassword.php:
--------------------------------------------------------------------------------
1 | user()->password);
16 | }
17 |
18 | /**
19 | * Get the validation error message.
20 | */
21 | public function message(): string
22 | {
23 | return trans('validation.current_password');
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/Scopes/PostedScope.php:
--------------------------------------------------------------------------------
1 | user();
18 |
19 | // if not connected or if connected but not admin
20 | if (!$user || !$user->isAdmin()) {
21 | $builder->where('posted_at', '<=', now());
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/View/Components/Alert.php:
--------------------------------------------------------------------------------
1 | type = $type;
20 | $this->dismissible = isset($dismissible) && $dismissible;
21 | }
22 |
23 | /**
24 | * Get the view / contents that represent the component.
25 | */
26 | public function render(): View
27 | {
28 | return view('components.alert');
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/View/Components/Card.php:
--------------------------------------------------------------------------------
1 | name = $name;
20 | $this->prefix = $prefix;
21 | }
22 |
23 | /**
24 | * Get the view / contents that represent the component.
25 | */
26 | public function render(): View
27 | {
28 | return view('components.icon');
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/bootstrap/cache/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/config/cors.php:
--------------------------------------------------------------------------------
1 | ['api/*', 'sanctum/csrf-cookie'],
19 |
20 | 'allowed_methods' => ['*'],
21 |
22 | 'allowed_origins' => ['*'],
23 |
24 | 'allowed_origins_patterns' => [],
25 |
26 | 'allowed_headers' => ['*'],
27 |
28 | 'exposed_headers' => [],
29 |
30 | 'max_age' => 0,
31 |
32 | 'supports_credentials' => false,
33 |
34 | ];
35 |
--------------------------------------------------------------------------------
/config/deploy.yml:
--------------------------------------------------------------------------------
1 | <% require "dotenv"; Dotenv.load(".env") %>
2 |
3 | # Name of your application. Used to uniquely configure containers.
4 | service: laravel-blog
5 |
6 | # Name of the container image.
7 | image: guillaumebriday/laravel-blog
8 |
9 | # Deploy to these servers.
10 | servers:
11 | web:
12 | - 51.159.152.188
13 |
14 | proxy:
15 | app_port: 8080
16 | ssl: true
17 | host: laravel-blog.guillaumebriday.fr
18 |
19 | # Credentials for your image host.
20 | registry:
21 | username: guillaumebriday
22 |
23 | # Always use an access token rather than real password (pulled from .kamal/secrets).
24 | password:
25 | - KAMAL_REGISTRY_PASSWORD
26 |
27 | builder:
28 | arch: amd64
29 |
30 | env:
31 | clear:
32 | APP_NAME: "Laravel blog"
33 | APP_ENV: "production"
34 | APP_DEBUG: false
35 | APP_URL: "https://laravel-blog.guillaumebriday.fr"
36 | ASSET_URL: "https://laravel-blog.guillaumebriday.fr"
37 | DB_CONNECTION: "sqlite"
38 | DB_DATABASE: "/var/www/html/data/production_laravel_blog.sqlite3"
39 | MAIL_MAILER: "log"
40 | SESSION_DRIVER: "file"
41 | CACHE_STORE: "file"
42 | secret:
43 | - APP_KEY
44 |
45 | volumes:
46 | - "data:/var/www/html/data"
47 |
48 | aliases:
49 | console: app exec --reuse -i "bash"
50 | tinker: app exec --reuse -i "php artisan tinker"
51 |
--------------------------------------------------------------------------------
/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', 10),
33 | ],
34 |
35 | /*
36 | |--------------------------------------------------------------------------
37 | | Argon Options
38 | |--------------------------------------------------------------------------
39 | |
40 | | Here you may specify the configuration options that should be used when
41 | | passwords are hashed using the Argon algorithm. These will allow you
42 | | to control the amount of time it takes to hash the given password.
43 | |
44 | */
45 |
46 | 'argon' => [
47 | 'memory' => 65536,
48 | 'threads' => 1,
49 | 'time' => 4,
50 | ],
51 |
52 | ];
53 |
--------------------------------------------------------------------------------
/config/services.php:
--------------------------------------------------------------------------------
1 | [
18 | 'token' => env('POSTMARK_TOKEN'),
19 | ],
20 |
21 | 'ses' => [
22 | 'key' => env('AWS_ACCESS_KEY_ID'),
23 | 'secret' => env('AWS_SECRET_ACCESS_KEY'),
24 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
25 | ],
26 |
27 | 'resend' => [
28 | 'key' => env('RESEND_KEY'),
29 | ],
30 |
31 | 'slack' => [
32 | 'notifications' => [
33 | 'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'),
34 | 'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'),
35 | ],
36 | ],
37 |
38 | ];
39 |
--------------------------------------------------------------------------------
/config/view.php:
--------------------------------------------------------------------------------
1 | [
17 | resource_path('views'),
18 | ],
19 |
20 | /*
21 | |--------------------------------------------------------------------------
22 | | Compiled View Path
23 | |--------------------------------------------------------------------------
24 | |
25 | | This option determines where all the compiled Blade templates will be
26 | | stored for your application. Typically, this is within the storage
27 | | directory. However, as usual, you are free to change this value.
28 | |
29 | */
30 |
31 | 'compiled' => env(
32 | 'VIEW_COMPILED_PATH',
33 | realpath(storage_path('framework/views'))
34 | ),
35 |
36 | ];
37 |
--------------------------------------------------------------------------------
/data/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guillaumebriday/laravel-blog/c88e307c833708665b0c2d486a8fe633eb4cefd7/data/.gitkeep
--------------------------------------------------------------------------------
/database/.gitignore:
--------------------------------------------------------------------------------
1 | *.sqlite
2 |
--------------------------------------------------------------------------------
/database/factories/CommentFactory.php:
--------------------------------------------------------------------------------
1 | $this->faker->paragraph(),
26 | 'author_id' => User::factory(),
27 | 'post_id' => Post::factory()
28 | ];
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/database/factories/LikeFactory.php:
--------------------------------------------------------------------------------
1 | 'App\Models\Post',
26 | 'likeable_id' => Post::factory(),
27 | 'author_id' => User::factory()
28 | ];
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/database/factories/NewsletterSubscriptionFactory.php:
--------------------------------------------------------------------------------
1 | $this->faker->unique()->safeEmail()
24 | ];
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/database/factories/PostFactory.php:
--------------------------------------------------------------------------------
1 | $this->faker->sentence(),
25 | 'content' => $this->faker->paragraph(),
26 | 'posted_at' => now(),
27 | 'author_id' => User::factory()
28 | ];
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/database/factories/RoleFactory.php:
--------------------------------------------------------------------------------
1 | $this->faker->word()
24 | ];
25 | }
26 |
27 | /**
28 | * Indicate that the user is admin.
29 | */
30 | public function admin(): Factory
31 | {
32 | return $this->state(function () {
33 | return [
34 | 'name' => 'admin'
35 | ];
36 | });
37 | }
38 |
39 | /**
40 | * Indicate that the user is editor.
41 | */
42 | public function editor(): Factory
43 | {
44 | return $this->state(function () {
45 | return [
46 | 'name' => 'editor'
47 | ];
48 | });
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/database/factories/UserFactory.php:
--------------------------------------------------------------------------------
1 | $this->faker->name(),
27 | 'email' => $this->faker->unique()->safeEmail(),
28 | 'password' => $password ?: $password = 'password',
29 | 'remember_token' => Str::random(10),
30 | 'email_verified_at' => now(),
31 | ];
32 | }
33 |
34 | /**
35 | * Indicate that the user is anakin.
36 | */
37 | public function anakin(): Factory
38 | {
39 | return $this->state(function () {
40 | return [
41 | 'name' => 'Anakin',
42 | 'email' => 'anakin@skywalker.st'
43 | ];
44 | });
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/database/migrations/.gitkeep:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/database/migrations/2014_10_12_000000_create_users_table.php:
--------------------------------------------------------------------------------
1 | increments('id');
16 | $table->string('name');
17 | $table->string('email')->unique();
18 | $table->string('password');
19 | $table->rememberToken();
20 | $table->timestamps();
21 | });
22 | }
23 |
24 | /**
25 | * Reverse the migrations.
26 | */
27 | public function down(): void
28 | {
29 | Schema::drop('users');
30 | }
31 | };
32 |
--------------------------------------------------------------------------------
/database/migrations/2014_10_12_100000_create_password_resets_table.php:
--------------------------------------------------------------------------------
1 | string('email')->index();
16 | $table->string('token')->index();
17 | $table->timestamp('created_at')->nullable();
18 | });
19 | }
20 |
21 | /**
22 | * Reverse the migrations.
23 | */
24 | public function down(): void
25 | {
26 | Schema::drop('password_resets');
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/database/migrations/2016_12_19_080506_create_posts_table.php:
--------------------------------------------------------------------------------
1 | increments('id');
16 | $table->integer('author_id')->unsigned()->default(0);
17 | $table->foreign('author_id')->references('id')->on('users')->onDelete('cascade');
18 | $table->string('title');
19 | $table->text('content');
20 | $table->datetime('posted_at');
21 | $table->timestamps();
22 | });
23 | }
24 |
25 | /**
26 | * Reverse the migrations.
27 | */
28 | public function down(): void
29 | {
30 | Schema::drop('posts');
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/database/migrations/2016_12_19_201351_create_comments_table.php:
--------------------------------------------------------------------------------
1 | increments('id');
16 |
17 | $table->integer('author_id')->unsigned()->default(0);
18 | $table->foreign('author_id')->references('id')->on('users')->onDelete('cascade');
19 |
20 | $table->integer('post_id')->unsigned();
21 | $table->foreign('post_id')->references('id')->on('posts')->onDelete('cascade');
22 |
23 | $table->text('content');
24 | $table->datetime('posted_at');
25 | $table->timestamps();
26 | });
27 | }
28 |
29 | /**
30 | * Reverse the migrations.
31 | */
32 | public function down(): void
33 | {
34 | Schema::drop('comments');
35 | }
36 | };
37 |
--------------------------------------------------------------------------------
/database/migrations/2016_12_30_125504_create_newsletter_subscriptions_table.php:
--------------------------------------------------------------------------------
1 | increments('id');
16 | $table->string('email')->unique();
17 | $table->timestamps();
18 | });
19 | }
20 |
21 | /**
22 | * Reverse the migrations.
23 | */
24 | public function down(): void
25 | {
26 | Schema::dropIfExists('newsletter_subscriptions');
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/database/migrations/2016_12_30_171448_create_jobs_table.php:
--------------------------------------------------------------------------------
1 | bigIncrements('id');
16 | $table->string('queue');
17 | $table->longText('payload');
18 | $table->tinyInteger('attempts')->unsigned();
19 | $table->unsignedInteger('reserved_at')->nullable();
20 | $table->unsignedInteger('available_at');
21 | $table->unsignedInteger('created_at');
22 | $table->index(['queue', 'reserved_at']);
23 | });
24 | }
25 |
26 | /**
27 | * Reverse the migrations.
28 | */
29 | public function down(): void
30 | {
31 | Schema::dropIfExists('jobs');
32 | }
33 | };
34 |
--------------------------------------------------------------------------------
/database/migrations/2017_01_08_205244_add_provider_and_provider_id_to_users.php:
--------------------------------------------------------------------------------
1 | string('provider')->nullable();
16 | $table->string('provider_id')->unique()->nullable();
17 | });
18 | }
19 |
20 | /**
21 | * Reverse the migrations.
22 | */
23 | public function down(): void
24 | {
25 | Schema::table('users', function (Blueprint $table) {
26 | $table->dropUnique('users_provider_id_unique');
27 | $table->dropColumn('provider');
28 | $table->dropColumn('provider_id');
29 | });
30 | }
31 | };
32 |
--------------------------------------------------------------------------------
/database/migrations/2017_01_09_191917_update_email_and_password_to_nullable_to_users.php:
--------------------------------------------------------------------------------
1 | string('email')->nullable()->change();
16 | $table->string('password')->nullable()->change();
17 | });
18 | }
19 |
20 | /**
21 | * Reverse the migrations.
22 | */
23 | public function down(): void
24 | {
25 | Schema::table('users', function (Blueprint $table) {
26 | $table->string('email')->nullable(false)->change();
27 | $table->string('password')->nullable(false)->change();
28 | });
29 | }
30 | };
31 |
--------------------------------------------------------------------------------
/database/migrations/2017_01_23_210625_create_roles_table.php:
--------------------------------------------------------------------------------
1 | increments('id');
16 |
17 | $table->string('name');
18 |
19 | $table->timestamps();
20 | });
21 |
22 | Schema::create('role_user', function (Blueprint $table) {
23 | $table->increments('id');
24 |
25 | $table->integer('user_id')->unsigned();
26 | $table->foreign('user_id')->references('id')->on('users');
27 |
28 | $table->integer('role_id')->unsigned();
29 | $table->foreign('role_id')->references('id')->on('roles');
30 |
31 | $table->timestamps();
32 | });
33 | }
34 |
35 | /**
36 | * Reverse the migrations.
37 | */
38 | public function down(): void
39 | {
40 | Schema::dropIfExists('role_user');
41 | Schema::dropIfExists('roles');
42 | }
43 | };
44 |
--------------------------------------------------------------------------------
/database/migrations/2017_01_29_003732_add_registered_at_to_users_table.php:
--------------------------------------------------------------------------------
1 | datetime('registered_at')->nullable();
16 | });
17 | }
18 |
19 | /**
20 | * Reverse the migrations.
21 | */
22 | public function down(): void
23 | {
24 | Schema::table('users', function (Blueprint $table) {
25 | $table->dropColumn('registered_at');
26 | });
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/database/migrations/2017_03_12_213124_create_media_table.php:
--------------------------------------------------------------------------------
1 | increments('id');
16 |
17 | $table->string('filename');
18 | $table->string('original_filename');
19 | $table->string('mime_type');
20 | $table->nullableMorphs('mediable');
21 |
22 | $table->timestamps();
23 | });
24 | }
25 |
26 | /**
27 | * Reverse the migrations.
28 | */
29 | public function down(): void
30 | {
31 | Schema::dropIfExists('media');
32 | }
33 | };
34 |
--------------------------------------------------------------------------------
/database/migrations/2017_03_19_102521_add_thumbnail_id_to_posts_table.php:
--------------------------------------------------------------------------------
1 | integer('thumbnail_id')->unsigned()->nullable();
16 | $table->foreign('thumbnail_id')->references('id')->on('media');
17 | });
18 | }
19 |
20 | /**
21 | * Reverse the migrations.
22 | */
23 | public function down(): void
24 | {
25 | Schema::table('posts', function (Blueprint $table) {
26 | $table->dropForeign(['thumbnail_id']);
27 | $table->dropColumn('thumbnail_id');
28 | });
29 | }
30 | };
31 |
--------------------------------------------------------------------------------
/database/migrations/2017_03_25_194948_add_api_token_to_users_table.php:
--------------------------------------------------------------------------------
1 | string('api_token', 60)->unique()->nullable();
16 | });
17 | }
18 |
19 | /**
20 | * Reverse the migrations.
21 | */
22 | public function down(): void
23 | {
24 | Schema::table('users', function (Blueprint $table) {
25 | $table->dropColumn('api_token');
26 | });
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/database/migrations/2017_04_16_092512_add_slug_to_posts.php:
--------------------------------------------------------------------------------
1 | string('slug')->unique()->after('title');
16 | });
17 | }
18 |
19 | /**
20 | * Reverse the migrations.
21 | */
22 | public function down(): void
23 | {
24 | Schema::table('posts', function (Blueprint $table) {
25 | $table->dropColumn('slug');
26 | });
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/database/migrations/2017_11_14_001056_add_index_on_title_to_posts.php:
--------------------------------------------------------------------------------
1 | index('title');
16 | });
17 | }
18 |
19 | /**
20 | * Reverse the migrations.
21 | */
22 | public function down(): void
23 | {
24 | Schema::table('posts', function (Blueprint $table) {
25 | $table->dropIndex('posts_title_index');
26 | });
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/database/migrations/2017_11_15_003340_create_likes_table.php:
--------------------------------------------------------------------------------
1 | increments('id');
16 | $table->integer('author_id')->unsigned();
17 | $table->foreign('author_id')->references('id')->on('users');
18 |
19 | $table->nullableMorphs('likeable');
20 |
21 | $table->timestamps();
22 | });
23 | }
24 |
25 | /**
26 | * Reverse the migrations.
27 | */
28 | public function down(): void
29 | {
30 | Schema::dropIfExists('likes');
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/database/migrations/2018_03_22_214952_drop_media_table.php:
--------------------------------------------------------------------------------
1 | dropForeign(['thumbnail_id']);
16 | $table->dropColumn('thumbnail_id');
17 | });
18 |
19 | Schema::dropIfExists('media');
20 | }
21 |
22 | /**
23 | * Reverse the migrations.
24 | */
25 | public function down(): void
26 | {
27 | Schema::create('media', function (Blueprint $table) {
28 | $table->increments('id');
29 |
30 | $table->string('filename');
31 | $table->string('original_filename');
32 | $table->string('mime_type');
33 | $table->nullableMorphs('mediable');
34 |
35 | $table->timestamps();
36 | });
37 |
38 | Schema::table('posts', function (Blueprint $table) {
39 | $table->integer('thumbnail_id')->unsigned()->nullable();
40 | $table->foreign('thumbnail_id')->references('id')->on('media');
41 | });
42 | }
43 | };
44 |
--------------------------------------------------------------------------------
/database/migrations/2018_03_22_215224_create_media_table_with_media_library.php:
--------------------------------------------------------------------------------
1 | increments('id');
16 | $table->morphs('model');
17 | $table->string('collection_name');
18 | $table->string('name');
19 | $table->string('file_name');
20 | $table->string('mime_type')->nullable();
21 | $table->string('disk');
22 | $table->unsignedBigInteger('size');
23 | $table->json('manipulations');
24 | $table->json('custom_properties');
25 | $table->json('responsive_images');
26 | $table->datetime('posted_at');
27 | $table->unsignedInteger('order_column')->nullable();
28 | $table->nullableTimestamps();
29 | });
30 |
31 | Schema::table('posts', function (Blueprint $table) {
32 | $table->integer('thumbnail_id')->unsigned()->nullable();
33 | $table->foreign('thumbnail_id')->references('id')->on('media')->onDelete('set null');
34 | });
35 | }
36 |
37 | /**
38 | * Reverse the migrations.
39 | */
40 | public function down()
41 | {
42 | Schema::table('posts', function (Blueprint $table) {
43 | $table->dropForeign(['thumbnail_id']);
44 | $table->dropColumn('thumbnail_id');
45 | });
46 |
47 | Schema::dropIfExists('media');
48 | }
49 | };
50 |
--------------------------------------------------------------------------------
/database/migrations/2018_03_22_230046_create_media_libraries_table.php:
--------------------------------------------------------------------------------
1 | increments('id');
16 | $table->timestamps();
17 | });
18 | }
19 |
20 | /**
21 | * Reverse the migrations.
22 | */
23 | public function down(): void
24 | {
25 | Schema::dropIfExists('media_libraries');
26 | }
27 | };
28 |
--------------------------------------------------------------------------------
/database/migrations/2018_09_05_220100_add_email_verified_at_to_users.php:
--------------------------------------------------------------------------------
1 | timestamp('email_verified_at')->nullable();
16 | });
17 | }
18 |
19 | /**
20 | * Reverse the migrations.
21 | */
22 | public function down(): void
23 | {
24 | Schema::table('users', function (Blueprint $table) {
25 | $table->dropColumn('email_verified_at');
26 | });
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/database/migrations/2019_09_09_000456_create_failed_jobs_table.php:
--------------------------------------------------------------------------------
1 | bigIncrements('id');
16 | $table->string('uuid')->unique();
17 | $table->text('connection');
18 | $table->text('queue');
19 | $table->longText('payload');
20 | $table->longText('exception');
21 | $table->timestamp('failed_at')->useCurrent();
22 | });
23 | }
24 |
25 | /**
26 | * Reverse the migrations.
27 | */
28 | public function down(): void
29 | {
30 | Schema::dropIfExists('failed_jobs');
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php:
--------------------------------------------------------------------------------
1 | id();
16 | $table->morphs('tokenable');
17 | $table->string('name');
18 | $table->string('token', 64)->unique();
19 | $table->text('abilities')->nullable();
20 | $table->timestamp('last_used_at')->nullable();
21 | $table->timestamp('expires_at')->nullable();
22 | $table->timestamps();
23 | });
24 | }
25 |
26 | /**
27 | * Reverse the migrations.
28 | */
29 | public function down(): void
30 | {
31 | Schema::dropIfExists('personal_access_tokens');
32 |
33 | Schema::table('users', function (Blueprint $table) {
34 | $table->string('api_token', 60)->unique()->nullable();
35 | });
36 | }
37 | };
38 |
--------------------------------------------------------------------------------
/database/migrations/2020_10_06_231328_add_fields_to_media.php:
--------------------------------------------------------------------------------
1 | uuid('uuid')->nullable();
16 | $table->string('conversions_disk')->nullable();
17 | });
18 | }
19 |
20 | /**
21 | * Reverse the migrations.
22 | */
23 | public function down(): void
24 | {
25 | Schema::table('media', function (Blueprint $table) {
26 | $table->dropColumn('uuid');
27 | $table->dropColumn('conversions_disk');
28 | });
29 | }
30 | };
31 |
--------------------------------------------------------------------------------
/database/migrations/2022_04_30_011124_add_generated_conversions_to_media_table.php:
--------------------------------------------------------------------------------
1 | json('generated_conversions')->nullable();
19 | });
20 | }
21 | }
22 |
23 | /**
24 | * Reverse the migrations.
25 | */
26 | public function down(): void
27 | {
28 | /* Restore the 'generated_conversions' field in the 'custom_properties' column if you removed them in this migration
29 | Media::query()
30 | ->whereRaw("JSON_TYPE(generated_conversions) != 'NULL'")
31 | ->update([
32 | 'custom_properties' => DB::raw("JSON_SET(custom_properties, '$.generated_conversions', generated_conversions)")
33 | ]);
34 | */
35 |
36 | Schema::table('media', function (Blueprint $table) {
37 | $table->dropColumn('generated_conversions');
38 | });
39 | }
40 | };
41 |
--------------------------------------------------------------------------------
/database/seeders/dev/DevDatabaseSeeder.php:
--------------------------------------------------------------------------------
1 | count(20)
17 | ->create()
18 | ->each(function ($post) {
19 | Comment::factory()
20 | ->count(5)
21 | ->create([
22 | 'post_id' => $post->id
23 | ]);
24 | });
25 |
26 | NewsletterSubscription::factory()->count(5)->create();
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/lang/en/auth.php:
--------------------------------------------------------------------------------
1 | 'These credentials do not match our records.',
17 | 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
18 | 'login' => 'Login',
19 | 'logout' => 'Logout',
20 | 'register' => 'Register',
21 | 'remember_me' => 'Remember me',
22 | 'forgotten_password' => 'Forgot password ?',
23 | 'reset_password' => 'Reset password',
24 | 'send_password_reset_link' => 'Send password reset link',
25 | 'logged_in' => 'You are now logged in',
26 | 'logged_in_provider' => 'You are now logged in with :Provider.',
27 | 'not_authorized' => 'Not authorized.',
28 |
29 | ];
30 |
--------------------------------------------------------------------------------
/lang/en/comments.php:
--------------------------------------------------------------------------------
1 | ':count comment|:count comments',
5 | 'comment' => 'Comment',
6 | 'comments' => 'Comments',
7 | 'delete' => 'Delete',
8 | 'last_comments' => 'Latest comments',
9 | 'updated' => 'Comment updated',
10 | 'deleted' => 'Comment deleted',
11 | 'new_comments' => 'new comment|new comments',
12 | 'sign_in_to_comment' => 'You must be signed in to comment.',
13 | 'loading_comments' => 'Load comments',
14 | 'posted_on' => 'Posted on',
15 |
16 | 'attributes' => [
17 | 'content' => 'Content',
18 | 'author' => 'Author',
19 | 'post' => 'Post',
20 | 'posted_at' => 'Posted at',
21 | ],
22 |
23 | 'placeholder' => [
24 | 'content' => 'Your comment'
25 | ]
26 | ];
27 |
--------------------------------------------------------------------------------
/lang/en/dashboard.php:
--------------------------------------------------------------------------------
1 | 'Dashboard',
5 | 'this_week' => 'This week',
6 | 'details' => 'Details',
7 | 'posts' => 'Posts',
8 | 'comments' => 'Comments',
9 | 'users' => 'Users',
10 | 'media' => 'Gallery',
11 | ];
12 |
--------------------------------------------------------------------------------
/lang/en/forms.php:
--------------------------------------------------------------------------------
1 | [
6 | 'save' => 'Save',
7 | 'update' => 'Update',
8 | 'back' => 'Back',
9 | 'generate' => 'Generate',
10 | 'delete' => 'Delete',
11 | 'add' => 'Add',
12 | ],
13 |
14 | 'comments' => [
15 | 'delete' => 'Are you sure you want to delete this comment?'
16 | ],
17 |
18 | 'posts' => [
19 | 'delete' => 'Are you sure you want to delete this article?',
20 | 'delete_thumbnail' => 'Are you sure you want to delete the image?'
21 | ],
22 |
23 | 'media' => [
24 | 'delete' => 'Are you sure you want to delete this thumbnail?',
25 | ],
26 |
27 | ];
28 |
--------------------------------------------------------------------------------
/lang/en/likes.php:
--------------------------------------------------------------------------------
1 | 'Likes',
5 | ];
6 |
--------------------------------------------------------------------------------
/lang/en/media.php:
--------------------------------------------------------------------------------
1 | 'Media',
5 | 'create' => 'Add an image',
6 | 'created' => 'Image posted',
7 | 'updated' => 'Image updated',
8 | 'deleted' => 'Image deleted',
9 | 'download' => 'Download',
10 | 'show' => 'Show',
11 | 'delete' => 'Delete',
12 | 'count' => ':count image|:count images',
13 |
14 | 'attributes' => [
15 | 'image' => 'Image',
16 | 'name' => 'Name',
17 | 'posted_at' => 'Posted at',
18 | 'url' => 'URL',
19 | ],
20 |
21 | ];
22 |
--------------------------------------------------------------------------------
/lang/en/newsletter.php:
--------------------------------------------------------------------------------
1 | 'Subscribe',
5 | 'placeholder' => 'Your email',
6 | 'created' => 'Email added to the newsletter successfully',
7 | 'unsubscribed' => 'The request for unsubscription has been taken into account.',
8 |
9 | 'email' => [
10 | 'subject' => 'Newsletter of the month',
11 | 'welcome' => 'Welcome to the newsletter of the month!',
12 | 'description' => 'Here is the list of: count last articles published during the month',
13 | 'thanks' => 'Thank you for subscribing to our newsletter',
14 | 'unsubscribe' => 'Unsubscribe'
15 | ]
16 | ];
17 |
--------------------------------------------------------------------------------
/lang/en/pagination.php:
--------------------------------------------------------------------------------
1 | '« Previous',
17 | 'next' => 'Next »',
18 |
19 | ];
20 |
--------------------------------------------------------------------------------
/lang/en/passwords.php:
--------------------------------------------------------------------------------
1 | 'Passwords must be at least eight characters and match the confirmation.',
17 | 'reset' => 'Your password has been reset!',
18 | 'sent' => 'We have emailed your password reset link!',
19 | 'token' => 'This password reset token is invalid.',
20 | 'user' => "We can't find a user with that email address.",
21 |
22 | ];
23 |
--------------------------------------------------------------------------------
/lang/en/posts.php:
--------------------------------------------------------------------------------
1 | 'Publish',
5 | 'posts' => 'Posts',
6 | 'delete' => 'Delete',
7 | 'delete_thumbnail' => 'Delete the picture',
8 | 'edit' => 'Edit',
9 | 'last_posts' => 'Latest posts',
10 | 'create' => 'Create post',
11 | 'created' => 'Post created',
12 | 'updated' => 'Post updated',
13 | 'deleted' => 'Post deleted',
14 | 'empty' => 'There is no post yet.',
15 | 'new_posts' => 'new post|new posts',
16 | 'count' => ':count post|:count posts',
17 | 'show' => 'Show post',
18 | 'search' => 'Search ...',
19 | 'search_results' => ':count post found|:count posts found',
20 |
21 | 'attributes' => [
22 | 'title' => 'Title',
23 | 'content' => 'Content',
24 | 'author' => 'Author',
25 | 'thumbnail' => 'Post picture',
26 | 'posted_at' => 'Posted at',
27 | 'thumbnail' => 'Thumbnail image',
28 | ],
29 |
30 | 'placeholder' => [
31 | 'title' => 'Your title',
32 | 'content' => 'Your content',
33 | 'thumbnail' => 'Choose a thumbnail'
34 | ]
35 | ];
36 |
--------------------------------------------------------------------------------
/lang/en/roles.php:
--------------------------------------------------------------------------------
1 | 'None',
5 | 'admin' => 'Administrator',
6 | 'editor' => 'Editor',
7 | ];
8 |
--------------------------------------------------------------------------------
/lang/en/tokens.php:
--------------------------------------------------------------------------------
1 | 'The API key has been successfully generated'
5 | ];
6 |
--------------------------------------------------------------------------------
/lang/en/users.php:
--------------------------------------------------------------------------------
1 | 'My profile',
5 | 'public_profile' => 'My public profile',
6 | 'settings' => 'Settings',
7 | 'edit' => 'Edit profile',
8 | 'show' => 'Show profile',
9 | 'updated' => 'Profile updated',
10 | 'password_updated' => 'Password updated',
11 | 'new_users' => 'new user|new users',
12 | 'count' => ':count user|:count users',
13 | 'security' => 'Security',
14 |
15 | 'attributes' => [
16 | 'name' => 'Name',
17 | 'email' => 'Email',
18 | 'current_password' => 'Current password',
19 | 'password' => 'Password',
20 | 'password_confirmation' => 'Password confirmation',
21 | 'roles' => 'Roles',
22 | 'registered_at' => 'Registered at',
23 | ],
24 |
25 | 'placeholder' => [
26 | 'name' => 'Your name',
27 | 'email' => 'Your email',
28 | 'current_password' => 'Your current password',
29 | 'password' => 'Your new password',
30 | 'password_confirmation' => 'Password confirmation'
31 | ]
32 | ];
33 |
--------------------------------------------------------------------------------
/lang/fr/auth.php:
--------------------------------------------------------------------------------
1 | 'Ces identifiants ne correspondent pas à nos enregistrements',
17 | 'throttle' => 'Trop de tentatives de connexion. Veuillez essayer de nouveau dans :seconds secondes.',
18 | 'login' => 'Se connecter',
19 | 'logout' => 'Se déconnecter',
20 | 'register' => "S'inscrire",
21 | 'remember_me' => 'Se souvenir de moi',
22 | 'forgotten_password' => 'Mot de passe oublié ?',
23 | 'reset_password' => 'Réinitialiser le mot de passe',
24 | 'send_password_reset_link' => 'Envoyer un lien de réinitialisation du mot de passe',
25 | 'logged_in' => 'Vous êtes désormais connecté.',
26 | 'logged_in_provider' => 'Vous êtes désormais connecté avec :Provider.',
27 | 'not_authorized' => "Cette opération n'est pas autorisée.",
28 |
29 | ];
30 |
--------------------------------------------------------------------------------
/lang/fr/comments.php:
--------------------------------------------------------------------------------
1 | ':count commentaire|:count commentaires',
5 | 'comment' => 'Commenter',
6 | 'comments' => 'Commentaires',
7 | 'delete' => 'Supprimer',
8 | 'last_comments' => 'Les derniers commentaires',
9 | 'updated' => 'Commentaire mis à jour avec succès',
10 | 'deleted' => 'Commentaire supprimé avec succès',
11 | 'new_comments' => 'nouveau commentaire|nouveaux commentaires',
12 | 'sign_in_to_comment' => 'Vous devez vous connecter pour commenter.',
13 | 'loading_comments' => 'Charger les commentaires',
14 | 'posted_on' => 'Posté sur',
15 |
16 | 'attributes' => [
17 | 'content' => 'Contenu',
18 | 'author' => 'Auteur',
19 | 'post' => 'Article',
20 | 'posted_at' => 'Posté le',
21 | ],
22 |
23 | 'placeholder' => [
24 | 'content' => 'Votre commentaire'
25 | ]
26 | ];
27 |
--------------------------------------------------------------------------------
/lang/fr/dashboard.php:
--------------------------------------------------------------------------------
1 | 'Dashboard',
5 | 'this_week' => 'Cette semaine',
6 | 'details' => 'Voir en détails',
7 | 'posts' => 'Articles',
8 | 'comments' => 'Commentaires',
9 | 'users' => 'Utilisateurs',
10 | 'media' => 'Galerie',
11 | ];
12 |
--------------------------------------------------------------------------------
/lang/fr/forms.php:
--------------------------------------------------------------------------------
1 | [
6 | 'save' => 'Sauvegarder',
7 | 'update' => 'Mettre à jour',
8 | 'back' => 'Retour',
9 | 'generate' => 'Générer',
10 | 'delete' => 'Supprimer',
11 | 'add' => 'Ajouter',
12 | ],
13 |
14 | 'comments' => [
15 | 'delete' => 'Êtes-vous sûr de vouloir supprimer ce commentaires ?'
16 | ],
17 |
18 | 'posts' => [
19 | 'delete' => 'Êtes-vous sûr de vouloir supprimer cet article ?',
20 | 'delete_thumbnail' => "Êtes-vous sûr de vouloir supprimer l'image à la une ?"
21 | ],
22 |
23 | 'media' => [
24 | 'delete' => 'Êtes-vous sûr de vouloir supprimer cette image ?',
25 | ],
26 |
27 | ];
28 |
--------------------------------------------------------------------------------
/lang/fr/likes.php:
--------------------------------------------------------------------------------
1 | "J'aime",
5 | ];
6 |
--------------------------------------------------------------------------------
/lang/fr/media.php:
--------------------------------------------------------------------------------
1 | 'Media',
5 | 'create' => 'Ajouter une image',
6 | 'created' => 'Image créée avec succès',
7 | 'updated' => 'Image mise à jour avec succès',
8 | 'deleted' => 'Image supprimée avec succès',
9 | 'download' => 'Télécharger',
10 | 'show' => 'Voir',
11 | 'delete' => 'Supprimer',
12 | 'count' => ':count image|:count images',
13 |
14 | 'attributes' => [
15 | 'image' => 'Image',
16 | 'name' => 'Nom',
17 | 'posted_at' => 'Postée le',
18 | 'url' => 'URL',
19 | ],
20 |
21 | ];
22 |
--------------------------------------------------------------------------------
/lang/fr/newsletter.php:
--------------------------------------------------------------------------------
1 | "S'inscrire",
5 | 'placeholder' => 'Votre email',
6 | 'created' => 'Email ajouté à la newsletter avec succès',
7 | 'unsubscribed' => 'La demande de désabonnement a bien été prise en compte.',
8 |
9 | 'email' => [
10 | 'subject' => 'Newsletter du mois',
11 | 'welcome' => 'Bienvenue sur la Newsletter du mois !',
12 | 'description' => 'Voici la liste des :count derniers articles publiés au cours du mois',
13 | 'thanks' => 'Merci de vous être abonné à notre newsletter',
14 | 'unsubscribe' => 'Se désabonner'
15 | ]
16 | ];
17 |
--------------------------------------------------------------------------------
/lang/fr/pagination.php:
--------------------------------------------------------------------------------
1 | '« Précédent',
17 | 'next' => 'Suivant »',
18 |
19 | ];
20 |
--------------------------------------------------------------------------------
/lang/fr/passwords.php:
--------------------------------------------------------------------------------
1 | 'Les mots de passe doivent contenir au moins huit caractères et doivent être identiques.',
17 | 'reset' => 'Votre mot de passe a été réinitialisé !',
18 | 'sent' => 'Nous vous avons envoyé par courriel le lien de réinitialisation du mot de passe !',
19 | 'token' => "Ce jeton de réinitialisation du mot de passe n'est pas valide.",
20 | 'user' => "Aucun utilisateur n'a été trouvé avec cette adresse e-mail.",
21 |
22 | ];
23 |
--------------------------------------------------------------------------------
/lang/fr/posts.php:
--------------------------------------------------------------------------------
1 | 'Publier',
5 | 'posts' => 'Articles',
6 | 'delete' => 'Supprimer',
7 | 'delete_thumbnail' => "Supprimer l'image à la une",
8 | 'edit' => 'Éditer',
9 | 'last_posts' => 'Les derniers articles',
10 | 'create' => 'Ajouter un article',
11 | 'created' => 'Article créé avec succès',
12 | 'updated' => 'Article mis à jour avec succès',
13 | 'deleted' => 'Article supprimé avec succès',
14 | 'empty' => "Il n'y a aucun article pour le moment.",
15 | 'new_posts' => 'nouvel article|nouveaux articles',
16 | 'count' => ':count article|:count articles',
17 | 'show' => "Voir l'article",
18 | 'search' => 'Recherche ...',
19 | 'search_results' => ':count article trouvé|:count articles trouvés',
20 |
21 | 'attributes' => [
22 | 'title' => 'Titre',
23 | 'content' => 'Contenu',
24 | 'author' => 'Auteur',
25 | 'thumbnail' => 'Image à la une',
26 | 'posted_at' => 'Posté le',
27 | 'thumbnail' => "L'image de couverture",
28 | ],
29 |
30 | 'placeholder' => [
31 | 'title' => 'Votre titre',
32 | 'content' => 'Votre contenu',
33 | 'thumbnail' => 'Choisissez une image à la une...'
34 | ]
35 | ];
36 |
--------------------------------------------------------------------------------
/lang/fr/roles.php:
--------------------------------------------------------------------------------
1 | 'Aucun',
5 | 'admin' => 'Administrateur',
6 | 'editor' => 'Éditeur',
7 | ];
8 |
--------------------------------------------------------------------------------
/lang/fr/tokens.php:
--------------------------------------------------------------------------------
1 | "La clé d'API a bien été générée"
5 | ];
6 |
--------------------------------------------------------------------------------
/lang/fr/users.php:
--------------------------------------------------------------------------------
1 | 'Mon profil',
5 | 'public_profile' => 'Mon profil public',
6 | 'settings' => 'Réglages',
7 | 'edit' => 'Éditer le profil',
8 | 'show' => 'Voir le profil',
9 | 'updated' => 'Le profil a bien été mis à jour',
10 | 'password_updated' => 'Le mot de passe a bien été mis à jour',
11 | 'new_users' => 'nouvel utilisateur|nouveaux utilisateurs',
12 | 'count' => ':count utilisateur|:count utilisateurs',
13 | 'security' => 'Sécurité',
14 |
15 | 'attributes' => [
16 | 'name' => 'Nom',
17 | 'email' => 'Email',
18 | 'current_password' => 'Mot de passe actuel',
19 | 'password' => 'Mot de passe',
20 | 'password_confirmation' => 'Confirmation du mot de passe',
21 | 'roles' => 'Rôles',
22 | 'registered_at' => 'Enregistré le',
23 | ],
24 |
25 | 'placeholder' => [
26 | 'name' => 'Votre nom',
27 | 'email' => 'Votre email',
28 | 'current_password' => 'Votre mot de passe actuel',
29 | 'password' => 'Votre nouveau mot de passe',
30 | 'password_confirmation' => 'Confirmation du nouveau mot de passe'
31 | ]
32 | ];
33 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "type": "module",
4 | "scripts": {
5 | "lint": "eslint --ext .js resources/js",
6 | "dev": "vite",
7 | "build": "vite build"
8 | },
9 | "dependencies": {
10 | "@fortawesome/fontawesome-free": "^6.6.0",
11 | "@hotwired/turbo": "^8.0.12",
12 | "@popperjs/core": "^2.11.8",
13 | "bootstrap": "^5.3.3",
14 | "clipboard": "2.0.11",
15 | "cross-env": "7.0.3",
16 | "jquery": "^3.7.1",
17 | "laravel-echo": "1.16.1",
18 | "laravel-vite-plugin": "^1.0.5",
19 | "pusher-js": "7.6.0",
20 | "trumbowyg": "2.28.0",
21 | "vite": "^5.4.9"
22 | },
23 | "devDependencies": {
24 | "eslint": "8.57.0",
25 | "eslint-config-standard": "17.1.0",
26 | "eslint-plugin-import": "2.29.1",
27 | "eslint-plugin-n": "16.6.2",
28 | "eslint-plugin-node": "11.1.0",
29 | "eslint-plugin-promise": "6.6.0",
30 | "eslint-plugin-standard": "5.0.0",
31 | "sass": "1.80.3"
32 | },
33 | "packageManager": "yarn@4.5.1"
34 | }
35 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | @lang('posts.show') : 6 | 7 | {{ route('posts.show', $comment->post) }} 8 | 9 |
10 | 11 | @include('admin/comments/_form') 12 | @endsection 13 | -------------------------------------------------------------------------------- /resources/views/admin/comments/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('admin.layouts.app') 2 | 3 | @section('content') 4 |5 | @lang('posts.show') : 6 | 7 | 8 | {{ route('posts.show', $post) }} 9 | 10 |
11 | 12 | @include('admin/posts/_thumbnail') 13 | 14 | 34 | @endsection 35 | -------------------------------------------------------------------------------- /resources/views/admin/posts/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('admin.layouts.app') 2 | 3 | @section('content') 4 |@lang('users.attributes.name') | 6 |@lang('users.attributes.email') | 7 |@lang('users.attributes.registered_at') | 8 |9 | |
---|---|---|---|
15 | 16 | {{ $user->fullname }} 17 | 18 | | 19 |{{ $user->email }} | 20 |@humanize_date($user->registered_at, 'd/m/Y H:i:s') | 21 |
22 |
23 | |
26 |
5 | @lang('users.show') : 6 | 7 | 8 | {{ route('users.show', $user) }} 9 | 10 |
11 | 12 | @include('admin/users/_form') 13 | @endsection 14 | -------------------------------------------------------------------------------- /resources/views/admin/users/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('admin.layouts.app') 2 | 3 | @section('content') 4 |4 | @humanize_date($comment->posted_at) 5 |
6 | 7 | @can('delete', $comment) 8 | 14 | @endcan 15 |24 | {{ $comment->content }} 25 |
26 |4 | @lang('newsletter.email.description', ['count' => $posts->count()]) : 5 |
6 | 7 |18 | @lang('newsletter.email.thanks') 19 |
20 | 21 |22 | 23 | @lang('newsletter.email.unsubscribe') 24 | 25 |
26 | -------------------------------------------------------------------------------- /resources/views/layouts/app.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |22 | {{ Str::words(strip_tags($post->content), 10) }} 23 |
24 | 25 |26 | 27 | 28 | {{ $post->author->fullname }} 29 | 30 | 31 |
32 | 33 |{{ $comment->content }}
7 | 8 |9 | @humanize_date($comment->posted_at) 10 |
11 |
17 | @humanize_date($post->posted_at)
18 |
19 |