Compare commits

...
Sign in to create a new pull request.

91 commits

Author SHA1 Message Date
Vincent BRIDIER
314ef2dff7 Fix vite config for CI 2023-11-08 09:07:00 +00:00
Alexis Gousseau
f80f43a0a0 fix phpcs 2023-11-07 13:12:08 +01:00
Alexis Gousseau
6dc813e366 update social media icons 2023-11-07 13:06:17 +01:00
Alexis Gousseau
fc7dad207e add pagination on news 2023-11-07 13:06:04 +01:00
Alexis Gousseau
bdbbb9ff9a add news.index + news.show pages 2023-11-06 16:05:48 +01:00
Alexis Gousseau
705aa7c522 lot of fix (translations, docblocks, saveQuietly seeder) 2023-11-06 12:57:17 +01:00
Alexis Gousseau
3c63d85bd9 update bo joinus: remove index page and redirect to the edit 2023-11-06 11:19:08 +01:00
Alexis Gousseau
2648256177 update bo conviction: remove index page and redirect to the edit 2023-11-06 11:13:14 +01:00
Alexis Gousseau
5f90384be4 update bo explorer: remove index page and redirect to the edit 2023-11-06 11:04:33 +01:00
Alexis Gousseau
35f6d98dbc update bo homepage: remove index page and redirect to the edit 2023-11-06 11:03:49 +01:00
Alexis Gousseau
480482f5b2 update contact pdf validation 2023-11-06 09:28:50 +01:00
Alexis Gousseau
7bf122def5 fix joinus->video_id 2023-11-06 09:28:34 +01:00
Alexis Gousseau
f374271268 fix/conformity 2023-11-02 17:17:54 +01:00
Sebastien Hattry
7e9292e27e Fix title display 2023-11-02 11:26:10 +01:00
Sebastien Hattry
54b3d22d40 About page responsive 2/2 2023-11-02 10:52:56 +01:00
Alexis Gousseau
480d6394b4 WIP: add joinus page 2023-11-02 09:36:22 +01:00
Noah Lancien
f693e2a195 Added 'nos-convictions page 2023-11-02 09:34:29 +01:00
Sebastien Hattry
58518d0238 About page responsive 1/2 2023-10-31 17:26:08 +01:00
Sebastien Hattry
acd7e13c81 fix bug 2023-10-31 11:03:12 +01:00
Sebastien Hattry
404b022a17 Add about us page 1/2 2023-10-31 10:47:58 +01:00
Noah Lancien
a65d855acc Added products.show page 2023-10-30 17:31:15 +01:00
Alexis Gousseau
aba83a5c91 add ranges.show page 2023-10-30 16:51:12 +01:00
Alexis Gousseau
9481b4ca70 add ranges.index page 2023-10-30 15:39:31 +01:00
Alexis Gousseau
45ff265caf fix contact page 2023-10-30 12:14:43 +01:00
Noah Lancien
87b2ca2368 Fixed 404 and 503 pages 2023-10-27 16:52:28 +02:00
Noah Lancien
50c743f728 Fixed 404 and 503 pages 2023-10-27 16:43:37 +02:00
Noah Lancien
91094d9120 Added 'Explorateur & Gastronome' page 2023-10-27 16:29:31 +02:00
Jérémy Munsch
3d959fd37c
Upgrade using base project bump 2023-10-27 15:13:51 +02:00
Alexis Gousseau
0f4fe5eaac add contact page 2023-10-27 11:41:57 +02:00
Alexis Gousseau
58111ca5dd add homepage 2023-10-26 10:32:22 +02:00
Sebastien Hattry
97817caaaa Add Bootstrap font-size 2023-10-25 10:52:54 +02:00
Nicolas Demangeat
066fd0c8b3 fix blade trad 2023-10-24 17:05:45 +02:00
Sebastien Hattry
97dc1acf2a Change cookies behaviour 2023-10-24 17:04:52 +02:00
Sebastien Hattry
608803bd0c Add StaticPage controller instead of legals and policies 2023-10-24 16:36:31 +02:00
Sebastien Hattry
bd5be3fce1 Add legals and polices pages 2023-10-24 16:16:35 +02:00
Sebastien Hattry
8668c6a38c Add CSP execptions rules for cookies 2023-10-24 16:11:24 +02:00
Nicolas Demangeat
6e01470b5c add crud testimonial 2023-10-24 15:15:55 +02:00
Sebastien Hattry
f9a7728375 Add cookies notice settings in env.example 2023-10-24 12:18:22 +02:00
Sebastien Hattry
4d5dc72b11 issue-30 - Add cookie notice 2023-10-24 12:12:23 +02:00
Nicolas Demangeat
b01c6cc577 add crud join us page 2023-10-24 11:53:24 +02:00
Nicolas Demangeat
d97071b740 add commitments to explorer page selection 2023-10-24 10:08:32 +02:00
Noah Lancien
a17953deb1 Added commitments select in product CRUD 2023-10-23 16:49:40 +02:00
nicolas.demangeat
6e433628b5 Merge branch 'crud-convictionpage' into '2.0.0'
added conviction page

See merge request ldc-web/projets/agis!11
2023-10-23 14:33:16 +00:00
Nicolas Demangeat
3f15d48355 added conviction page 2023-10-23 16:29:49 +02:00
Noah Lancien
d61f05640d Added Product CRUD 2023-10-23 12:24:37 +02:00
nicolas.demangeat
3e64820fb8 Merge branch 'crud-explorerpage' into '2.0.0'
Crud explorerpage

See merge request ldc-web/projets/agis!10
2023-10-23 10:18:11 +00:00
Nicolas Demangeat
21d0ec429f added explorer page 2023-10-23 12:14:52 +02:00
Jérémy Munsch
ca54e892b5
Closes #3
inputs ok
2023-10-23 11:41:52 +02:00
Jérémy Munsch
608852e80a
inputs ok 2023-10-23 11:32:13 +02:00
Jérémy Munsch
425a7b223d
Fix footer layout for desktop 2023-10-23 11:04:04 +02:00
nicolas.demangeat
6ceb64a50c Merge branch 'crud-homepage' into '2.0.0'
added homepage

See merge request ldc-web/projets/agis!9
2023-10-23 08:54:49 +00:00
Nicolas Demangeat
6d6729285b fix conflicts 2023-10-23 10:52:00 +02:00
Jérémy Munsch
4cc8134e96
footer desktop ok 2023-10-23 10:51:39 +02:00
Nicolas Demangeat
e1994588e1 added homepage 2023-10-23 10:42:57 +02:00
Jérémy Munsch
5220fc130f
footer mobile ok 2023-10-23 10:09:45 +02:00
Noah Lancien
70e05dd6fa Fixed duplicated imports and phpcs 2023-10-23 07:15:30 +00:00
Noah Lancien
3ceaceacb6 Resolved conflicts on crud-stamps 2023-10-20 17:18:46 +02:00
Noah Lancien
abc6dd8484 Added News CRUD 2023-10-20 15:16:13 +00:00
Noah Lancien
b63450f905 Added stamps CRUD 2023-10-20 17:11:08 +02:00
nicolas.demangeat
34235baa16 Merge branch 'crud-commitments' into '2.0.0'
added commitments crud

See merge request ldc-web/projets/agis!8
2023-10-20 15:09:24 +00:00
Nicolas Demangeat
1242d4893f fix breadcrumbs 2023-10-20 17:06:59 +02:00
Nicolas Demangeat
e7ce6aef02 fix conflicts 2023-10-20 17:04:02 +02:00
Nicolas Demangeat
7d689d30fb added commitments crud 2023-10-20 16:56:44 +02:00
Noah Lancien
219eba96ec Resolved conflicts in crud-news 2023-10-20 16:23:04 +02:00
Noah Lancien
0d7bdd5901 Merge branch 'crud-seasonalsections' into 2.0.0 2023-10-20 16:17:19 +02:00
Noah Lancien
3712af4419 Added seasonal sections CRUD 2023-10-20 16:16:36 +02:00
Jérémy Munsch
fe494a24cf
Add run vite config with ssl 2023-10-20 15:01:42 +02:00
Noah Lancien
f3a7994154 Added News CRUD 2023-10-20 14:35:57 +02:00
Vincent BRIDIER
ff10196da7 Merge branch 'crud-ranges' into '2.0.0'
Added ranges CRUD + missing HasPicture Trait

See merge request ldc-web/projets/agis!6
2023-10-20 10:42:53 +00:00
noah.lancien
3343e18a11 Added ranges CRUD + missing HasPicture Trait 2023-10-20 10:42:53 +00:00
Jérémy Munsch
ed92ed2d94
Fix BS btn colors 2023-10-20 12:04:18 +02:00
Jérémy Munsch
f581a25ba7
Theme preparation TEST 2023-10-20 11:58:45 +02:00
Jérémy Munsch
c2b5daa67c
npm audit fix 2023-10-20 11:58:45 +02:00
Vincent BRIDIER
2a46a25165 Merge branch 'issue-11-static-page-crud' into '2.0.0'
Issue 11 static page crud

See merge request ldc-web/projets/agis!5
2023-10-20 08:53:04 +00:00
Noah Lancien
e346e65d7d Fixed static_pages translations 2023-10-20 10:45:11 +02:00
Noah Lancien
a807c9656c Lock content modificiation for pages that don't need content 2023-10-20 10:40:44 +02:00
Noah Lancien
ba73f40554 Added static pages crud from base project 2023-10-20 10:29:38 +02:00
Noah Lancien
037b859066 Fixed commands name 2023-10-20 10:27:33 +02:00
Vincent BRIDIER
0e4d84cc6f Merge branch 'issue-5-seeders-factories' into '2.0.0'
Issue 5 seeders factories

See merge request ldc-web/projets/agis!4
2023-10-20 07:39:18 +00:00
noah.lancien
57e84ab247 Close #5 seeders factories 2023-10-20 07:39:18 +00:00
Jérémy Munsch
656562afe5
Peacock color 2023-10-19 14:26:51 +02:00
Jérémy Munsch
76114a0d59
composer update 2023-10-19 14:24:11 +02:00
Jérémy Munsch
5f16a9e77f
Add missing bootstrap elements 2023-10-19 14:22:48 +02:00
Vincent BRIDIER
78931c7a46 Merge branch 'last-base-project-updates' into '2.0.0'
Updated with the last version of base project

See merge request ldc-web/projets/agis!3
2023-10-19 09:22:22 +00:00
Vincent BRIDIER
96ed23b5c9 Merge branch 'issue-2-migrations' into '2.0.0'
Issue 2 migrations

See merge request ldc-web/projets/agis!2
2023-10-19 09:21:36 +00:00
noah.lancien
f8eb6f481e Issue 2 migrations Close #2 2023-10-19 09:21:36 +00:00
Noah Lancien
1564eb8bc3 Updated with the last version of base project 2023-10-18 16:25:43 +02:00
Vincent BRIDIER
f47d3f6fd1
Update composer.json + Fix PSR4 2023-10-18 12:22:36 +02:00
Noah Lancien
338715d9eb Updated bo footer and README 2023-10-17 16:02:25 +02:00
Noah Lancien
b25444263a Init laravel 10 2023-10-17 14:58:21 +02:00
Noah Lancien
dc41c9aa82 Reset project 2023-10-17 14:52:32 +02:00
8876 changed files with 197976 additions and 619177 deletions

5
.browserslistrc Normal file
View file

@ -0,0 +1,5 @@
# Browsers that we support
last 3 version
> 0.3%
not dead # no browsers without security updates

2
.dockerignore Normal file
View file

@ -0,0 +1,2 @@
.git
*.md

21
.editorconfig Normal file
View file

@ -0,0 +1,21 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
[*.{yml,yaml}]
indent_size = 2
[docker-compose.yml]
indent_size = 4
[*.vue]
indent_size = 2

71
.env.example Normal file
View file

@ -0,0 +1,71 @@
APP_NAME=Laravel
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost
ASSET_URL=http://localhost
LOG_CHANNEL=stack
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=
BROADCAST_DRIVER=log
CACHE_DRIVER=file
FILESYSTEM_DISK=local
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120
MEMCACHED_HOST=127.0.0.1
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=smtp
MAIL_HOST=mailpit
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_HOST=
PUSHER_PORT=443
PUSHER_SCHEME=https
PUSHER_APP_CLUSTER=mt1
VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
VITE_PUSHER_HOST="${PUSHER_HOST}"
VITE_PUSHER_PORT="${PUSHER_PORT}"
VITE_PUSHER_SCHEME="${PUSHER_SCHEME}"
VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
# Sentry
SENTRY_LARAVEL_DSN=
# MATOMO
MATOMO_SITE_ID=
# GOOGLE
# - RECAPTCHA
GOOGLE_RECAPTCHA_PUBLIC=
GOOGLE_RECAPTCHA_PRIVATE=
GOOGLE_RECAPTCHA_VERSION=

62
.eslintrc.json Normal file
View file

@ -0,0 +1,62 @@
{
"env": {
"browser": true,
"es2021": true
},
"extends": [
"eslint:recommended",
"plugin:vue/vue3-strongly-recommended",
"@vue/eslint-config-typescript"
],
"overrides": [
{
"files": ["*.ts", "*.vue"],
"rules": {
"no-undef": "off"
}
},
{
"files": ["*.vue"],
"rules": {
"indent": "off"
}
}
],
"parser": "vue-eslint-parser",
"parserOptions": {
"parser": "@typescript-eslint/parser",
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": ["vue", "@typescript-eslint"],
"ignorePatterns": [
"webpack.mix.js",
"**/*.php",
"**/*.svg",
"**/vendor/*.js",
"**/vendor/**/*.js",
"**/vendor/**/*.scss"
],
"rules": {
"indent": ["error", 4, { "SwitchCase": 1 }],
"linebreak-style": ["error", "unix"],
"quotes": ["error", "double"],
"semi": ["error", "always"],
"vue/script-indent": ["error", 2, { "baseIndent": 0, "switchCase": 1 }],
"vue/one-component-per-file": "off"
},
"settings": {
"import/resolver": {
"node": {
"extensions": [".js", ".vue", ".ts", ".d.ts"]
},
"alias": {
"extensions": [".vue", ".js", ".ts", ".scss", ".d.ts"],
"map": [
["~", "./resources/assets"],
["@", "./node_modules"]
]
}
}
}
}

11
.gitattributes vendored Normal file
View file

@ -0,0 +1,11 @@
* text=auto eol=lf
*.blade.php diff=html
*.css diff=css
*.html diff=html
*.md diff=markdown
*.php diff=php
/.github export-ignore
CHANGELOG.md export-ignore
.styleci.yml export-ignore

45
.gitignore vendored Executable file → Normal file
View file

@ -1,20 +1,25 @@
app/cache/*
app/logs/*
app/config/parameters.yml
*.orig
*.bak
*.zip
.buildpath
.project
.settings
web/bundles/*
web/js/*
web/css/*
web/img-cache/*
web/uploads/offres/
web/uploads/images/original/*
*.php\~
/*.gitmodules
.idea/*
.DS_Store
web/.htaccess
.env
.env.backup
.env.production
.phpunit.result.cache
.stylelintcache
/.fleet
/.idea
/.phpunit.cache
/.vscode
/node_modules
/bootstrap
/public/build
/public/cache
/public/hot
/public/sitemap.xml
/public/storage
/storage/*.key
/vendor
auth.json
docker-compose.override.yml
Homestead.json
Homestead.yaml
npm-debug.log
yarn-error.log
.npmrc

24
.gitlab-ci.yml Normal file
View file

@ -0,0 +1,24 @@
include:
- project: 'ldc-web/shared-ci'
ref: 'main'
file: 'lint/lint.yml'
- project: 'ldc-web/shared-ci'
ref: 'main'
file: 'security/security.yml'
- project: 'ldc-web/shared-ci'
ref: 'main'
file: 'build/build.yml'
- project: 'ldc-web/shared-ci'
ref: 'main'
file: 'test/test.yml'
- project: 'ldc-web/shared-ci'
ref: 'main'
file: 'deploy/deploy.yml'
stages:
- lint
- security
- build
- test
- deploy

45
.stylelintrc.json Normal file
View file

@ -0,0 +1,45 @@
{
"extends": [
"stylelint-config-standard-scss",
"stylelint-config-sass-guidelines",
"stylelint-config-idiomatic-order"
],
"plugins": [
"stylelint-scss"
],
"files": [
"**/*.css",
"**/*.scss"
],
"ignoreFiles": [
"**/vendor/*.css",
"**/vendor/**/*.css",
"**/vendor/*.scss",
"**/vendor/**/*.scss",
"**/vendor/*.sass",
"**/vendor/**/*.sass",
"**/vendor/*.less",
"**/vendor/**/*.less"
],
"customSyntax": "postcss-scss",
"rules": {
"declaration-property-value-no-unknown": true,
"selector-max-id": null,
"color-no-invalid-hex": true,
"order/properties-alphabetical-order": null,
"max-nesting-depth": 5,
"selector-max-compound-selectors": 5,
"declaration-colon-newline-after": null,
"value-list-comma-newline-after": null,
"scss/double-slash-comment-empty-line-before": [
"always",
{
"ignore": [
"between-comments",
"stylelint-commands",
"inside-block"
]
}
]
}
}

View file

@ -1,10 +0,0 @@
language: php
php:
- 5.3.3
- 5.3
- 5.4
before_script: composer install
script: phpunit -c app

22
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,22 @@
{
"workbench.colorCustomizations": {
"activityBar.activeBackground": "#9898da",
"activityBar.background": "#9898da",
"activityBar.foreground": "#15202b",
"activityBar.inactiveForeground": "#15202b99",
"activityBarBadge.background": "#f7e8e8",
"activityBarBadge.foreground": "#15202b",
"commandCenter.border": "#e7e7e799",
"sash.hoverBorder": "#9898da",
"statusBar.background": "#7373cc",
"statusBar.foreground": "#e7e7e7",
"statusBarItem.hoverBackground": "#9898da",
"statusBarItem.remoteBackground": "#7373cc",
"statusBarItem.remoteForeground": "#e7e7e7",
"titleBar.activeBackground": "#7373cc",
"titleBar.activeForeground": "#e7e7e7",
"titleBar.inactiveBackground": "#7373cc99",
"titleBar.inactiveForeground": "#e7e7e799"
},
"peacock.remoteColor": "#7373cc"
}

19
LICENSE
View file

@ -1,19 +0,0 @@
Copyright (c) 2004-2013 Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

177
README.md Executable file → Normal file
View file

@ -1,176 +1 @@
Symfony Standard Edition
========================
Welcome to the Symfony Standard Edition - a fully-functional Symfony2
application that you can use as the skeleton for your new applications.
This document contains information on how to download, install, and start
using Symfony. For a more detailed explanation, see the [Installation][1]
chapter of the Symfony Documentation.
1) Installing the Standard Edition
----------------------------------
When it comes to installing the Symfony Standard Edition, you have the
following options.
### Use Composer (*recommended*)
As Symfony uses [Composer][2] to manage its dependencies, the recommended way
to create a new project is to use it.
If you don't have Composer yet, download it following the instructions on
http://getcomposer.org/ or just run the following command:
curl -s https://getcomposer.org/installer | php
Then, use the `create-project` command to generate a new Symfony application:
php composer.phar create-project symfony/framework-standard-edition path/to/install 2.1.x-dev
For an exact version, replace 2.1.x-dev with the latest Symfony version (e.g. 2.1.1).
Composer will install Symfony and all its dependencies under the
`path/to/install` directory.
### Download an Archive File
To quickly test Symfony, you can also download an [archive][3] of the Standard
Edition and unpack it somewhere under your web server root directory.
If you downloaded an archive "without vendors", you also need to install all
the necessary dependencies. Download composer (see above) and run the
following command:
php composer.phar install
2) Checking your System Configuration
-------------------------------------
Before starting coding, make sure that your local system is properly
configured for Symfony.
Execute the `check.php` script from the command line:
php app/check.php
Access the `config.php` script from a browser:
http://localhost/path/to/symfony/app/web/config.php
If you get any warnings or recommendations, fix them before moving on.
3) Browsing the Demo Application
--------------------------------
Congratulations! You're now ready to use Symfony.
From the `config.php` page, click the "Bypass configuration and go to the
Welcome page" link to load up your first Symfony page.
You can also use a web-based configurator by clicking on the "Configure your
Symfony Application online" link of the `config.php` page.
To see a real-live Symfony page in action, access the following page:
web/app_dev.php/demo/hello/Fabien
4) Getting started with Symfony
-------------------------------
This distribution is meant to be the starting point for your Symfony
applications, but it also contains some sample code that you can learn from
and play with.
A great way to start learning Symfony is via the [Quick Tour][4], which will
take you through all the basic features of Symfony2.
Once you're feeling good, you can move onto reading the official
[Symfony2 book][5].
A default bundle, `AcmeDemoBundle`, shows you Symfony2 in action. After
playing with it, you can remove it by following these steps:
* delete the `src/Acme` directory;
* remove the routing entries referencing AcmeBundle in
`app/config/routing_dev.yml`;
* remove the AcmeBundle from the registered bundles in `app/AppKernel.php`;
* remove the `web/bundles/acmedemo` directory;
* remove the `security.providers`, `security.firewalls.login` and
`security.firewalls.secured_area` entries in the `security.yml` file or
tweak the security configuration to fit your needs.
What's inside?
---------------
The Symfony Standard Edition is configured with the following defaults:
* Twig is the only configured template engine;
* Doctrine ORM/DBAL is configured;
* Swiftmailer is configured;
* Annotations for everything are enabled.
It comes pre-configured with the following bundles:
* **FrameworkBundle** - The core Symfony framework bundle
* [**SensioFrameworkExtraBundle**][6] - Adds several enhancements, including
template and routing annotation capability
* [**DoctrineBundle**][7] - Adds support for the Doctrine ORM
* [**TwigBundle**][8] - Adds support for the Twig templating engine
* [**SecurityBundle**][9] - Adds security by integrating Symfony's security
component
* [**SwiftmailerBundle**][10] - Adds support for Swiftmailer, a library for
sending emails
* [**MonologBundle**][11] - Adds support for Monolog, a logging library
* [**AsseticBundle**][12] - Adds support for Assetic, an asset processing
library
* [**JMSSecurityExtraBundle**][13] - Allows security to be added via
annotations
* [**JMSDiExtraBundle**][14] - Adds more powerful dependency injection
features
* **WebProfilerBundle** (in dev/test env) - Adds profiling functionality and
the web debug toolbar
* **SensioDistributionBundle** (in dev/test env) - Adds functionality for
configuring and working with Symfony distributions
* [**SensioGeneratorBundle**][15] (in dev/test env) - Adds code generation
capabilities
* **AcmeDemoBundle** (in dev/test env) - A demo bundle with some example
code
Enjoy!
[1]: http://symfony.com/doc/2.1/book/installation.html
[2]: http://getcomposer.org/
[3]: http://symfony.com/download
[4]: http://symfony.com/doc/2.1/quick_tour/the_big_picture.html
[5]: http://symfony.com/doc/2.1/index.html
[6]: http://symfony.com/doc/2.1/bundles/SensioFrameworkExtraBundle/index.html
[7]: http://symfony.com/doc/2.1/book/doctrine.html
[8]: http://symfony.com/doc/2.1/book/templating.html
[9]: http://symfony.com/doc/2.1/book/security.html
[10]: http://symfony.com/doc/2.1/cookbook/email.html
[11]: http://symfony.com/doc/2.1/cookbook/logging/monolog.html
[12]: http://symfony.com/doc/2.1/cookbook/assetic/asset_management.html
[13]: http://jmsyst.com/bundles/JMSSecurityExtraBundle/master
[14]: http://jmsyst.com/bundles/JMSDiExtraBundle/master
[15]: http://symfony.com/doc/2.1/bundles/SensioGeneratorBundle/index.html
# Agis

View file

@ -1,262 +0,0 @@
Symfony Standard Edition Upgrade
================================
From Symfony 2.0 to Symfony 2.1
-------------------------------
### Project Dependencies
As of Symfony 2.1, project dependencies are managed by
[Composer](http://getcomposer.org/):
* The `bin/vendors` script can be removed as `composer.phar` does all the work
now (it is recommended to install it globally on your machine).
* The `deps` file need to be replaced with the `composer.json` one.
* The `composer.lock` is the equivalent of the generated `deps.lock` file and
it is automatically generated by Composer.
Download the default
[`composer.json`](https://raw.github.com/symfony/symfony-standard/2.1/composer.json)
and
[`composer.lock`](https://raw.github.com/symfony/symfony-standard/2.1/composer.lock)
files for Symfony 2.1 and put them into the main directory of your project. If
you have customized your `deps` file, move the added dependencies to the
`composer.json` file (many bundles and PHP libraries are already available as
Composer packages -- search for them on [Packagist](http://packagist.org/)).
Remove your current `vendor` directory.
Finally, run Composer:
$ composer.phar install
Note: You must complete the upgrade steps below so composer can successfully generate the autoload files.
### `app/autoload.php`
The default `autoload.php` reads as follows (it has been simplified a lot as
autoloading for libraries and bundles declared in your `composer.json` file is
automatically managed by the Composer autoloader):
<?php
use Doctrine\Common\Annotations\AnnotationRegistry;
$loader = include __DIR__.'/../vendor/autoload.php';
// intl
if (!function_exists('intl_get_error_code')) {
require_once __DIR__.'/../vendor/symfony/symfony/src/Symfony/Component/Locale/Resources/stubs/functions.php';
$loader->add('', __DIR__.'/../vendor/symfony/symfony/src/Symfony/Component/Locale/Resources/stubs');
}
AnnotationRegistry::registerLoader(array($loader, 'loadClass'));
return $loader;
### `app/config/config.yml`
The `framework.charset` setting must be removed. If you are not using `UTF-8`
for your application, override the `getCharset()` method in your `AppKernel`
class instead:
class AppKernel extends Kernel
{
public function getCharset()
{
return 'ISO-8859-1';
}
// ...
}
You might want to add the new `strict_requirements` parameter to
`framework.router` (it avoids fatal errors in the production environment when
a link cannot be generated):
framework:
router:
strict_requirements: "%kernel.debug%"
The `default_locale` parameter is now a setting of the main `framework`
configuration (it was under the `framework.session` in 2.0):
framework:
default_locale: "%locale%"
The `auto_start` setting under `framework.session` must be removed as it is
not used anymore (the session is now always started on-demand). If
`auto_start` was the only setting under the `framework.session` entry, don't
remove it entirely, but set its value to `~` (`~` means `null` in YAML)
instead:
framework:
session: ~
The `trust_proxy_headers` setting was added in the default configuration file
(as it should be set to `true` when you install your application behind a
reverse proxy):
framework:
trust_proxy_headers: false
An empty `bundles` entry was added to the `assetic` configuration:
assetic:
bundles: []
The default `swiftmailer` configuration now has the `spool` setting configured
to the `memory` type to defer email sending after the response is sent to the
user (recommended for better end-user performance):
swiftmailer:
spool: { type: memory }
The `jms_security_extra` configuration was moved to the `security.yml`
configuration file.
### `app/config/config_dev.yml`
An example of how to send all emails to a unique address was added:
#swiftmailer:
# delivery_address: me@example.com
### `app/config/config_test.yml`
The `storage_id` setting must be changed to `session.storage.mock_file`:
framework:
session:
storage_id: session.storage.mock_file
### `app/config/parameters.ini`
The file has been converted to a YAML file which reads as follows:
parameters:
database_driver: pdo_mysql
database_host: localhost
database_port: ~
database_name: symfony
database_user: root
database_password: ~
mailer_transport: smtp
mailer_host: localhost
mailer_user: ~
mailer_password: ~
locale: en
secret: ThisTokenIsNotSoSecretChangeIt
Note that if you convert your parameters file to YAML, you must also change
its reference in `app/config/config.yml`.
### `app/config/routing_dev.yml`
The `_assetic` entry was removed:
#_assetic:
# resource: .
# type: assetic
### `app/config/security.yml`
Under `security.access_control`, the default rule for internal routes was changed:
security:
access_control:
#- { path: ^/_internal/secure, roles: IS_AUTHENTICATED_ANONYMOUSLY, ip: 127.0.0.1 }
Under `security.providers`, the `in_memory` example was updated to the following:
security:
providers:
in_memory:
memory:
users:
user: { password: userpass, roles: [ 'ROLE_USER' ] }
admin: { password: adminpass, roles: [ 'ROLE_ADMIN' ] }
### `app/AppKernel.php`
The following bundles have been added to the list of default registered bundles:
new JMS\AopBundle\JMSAopBundle(),
new JMS\DiExtraBundle\JMSDiExtraBundle($this),
You must also rename the DoctrineBundle from:
new Symfony\Bundle\DoctrineBundle\DoctrineBundle(),
to:
new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
### `web/app.php`
The default `web/app.php` file now reads as follows:
<?php
use Symfony\Component\ClassLoader\ApcClassLoader;
use Symfony\Component\HttpFoundation\Request;
$loader = require_once __DIR__.'/../app/bootstrap.php.cache';
// Use APC for autoloading to improve performance
// Change 'sf2' by the prefix you want in order to prevent key conflict with another application
/*
$loader = new ApcClassLoader('sf2', $loader);
$loader->register(true);
*/
require_once __DIR__.'/../app/AppKernel.php';
//require_once __DIR__.'/../app/AppCache.php';
$kernel = new AppKernel('prod', false);
$kernel->loadClassCache();
//$kernel = new AppCache($kernel);
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);
### `web/app_dev.php`
The default `web/app_dev.php` file now reads as follows:
<?php
use Symfony\Component\HttpFoundation\Request;
// If you don't want to setup permissions the proper way, just uncomment the following PHP line
// read http://symfony.com/doc/current/book/installation.html#configuration-and-setup for more information
//umask(0000);
// This check prevents access to debug front controllers that are deployed by accident to production servers.
// Feel free to remove this, extend it, or make something more sophisticated.
if (isset($_SERVER['HTTP_CLIENT_IP'])
|| isset($_SERVER['HTTP_X_FORWARDED_FOR'])
|| !in_array(@$_SERVER['REMOTE_ADDR'], array(
'127.0.0.1',
'::1',
))
) {
header('HTTP/1.0 403 Forbidden');
exit('You are not allowed to access this file. Check '.basename(__FILE__).' for more information.');
}
$loader = require_once __DIR__.'/../app/bootstrap.php.cache';
require_once __DIR__.'/../app/AppKernel.php';
$kernel = new AppKernel('dev', true);
$kernel->loadClassCache();
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);

View file

@ -1 +0,0 @@
deny from all

View file

@ -1,9 +0,0 @@
<?php
require_once __DIR__.'/AppKernel.php';
use Symfony\Bundle\FrameworkBundle\HttpCache\HttpCache;
class AppCache extends HttpCache
{
}

View file

@ -1,43 +0,0 @@
<?php
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Config\Loader\LoaderInterface;
class AppKernel extends Kernel
{
public function registerBundles()
{
$bundles = array(
new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
new Symfony\Bundle\SecurityBundle\SecurityBundle(),
new Symfony\Bundle\TwigBundle\TwigBundle(),
new Symfony\Bundle\MonologBundle\MonologBundle(),
new Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle(),
new Symfony\Bundle\AsseticBundle\AsseticBundle(),
new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),
new JMS\AopBundle\JMSAopBundle(),
new JMS\DiExtraBundle\JMSDiExtraBundle($this),
new JMS\SecurityExtraBundle\JMSSecurityExtraBundle(),
new FOS\UserBundle\FOSUserBundle(),
new Agis\FO\AgisBundle\AgisFOAgisBundle(),
new Agis\BO\AgisBundle\AgisBOAgisBundle(),
new Agis\BO\UserBundle\AgisBoUserBundle(),
new Agis\Commun\CommunBundle\AgisCommunCommunBundle(),
new Arson\elFinderBundle\ArsonElFinderBundle()
);
if (in_array($this->getEnvironment(), array('dev', 'test', 'local'))) {
$bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
$bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle();
$bundles[] = new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle();
}
return $bundles;
}
public function registerContainerConfiguration(LoaderInterface $loader)
{
$loader->load(__DIR__.'/config/config_'.$this->getEnvironment().'.yml');
}
}

View file

@ -0,0 +1,35 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Mail;
class TestMail extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'uwil:mail:test';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Send test email to equipe.web@ldc.fr';
/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
Mail::raw(sprintf('UWIL mail-test from %s %s', \config('app.name'), \config('app.url')), function ($msg) {
$msg->to('equipe.web@ldc.fr')->subject('Test Email');
});
}
}

View file

@ -0,0 +1,33 @@
<?php
namespace App\Console\Commands;
use App\Jobs\NewsStatusUpdate;
use Illuminate\Console\Command;
class UpdateNewsStatus extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'uwil:updatestatus:news';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Update news status according to the publish and unpublish dates';
/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
\dispatch(new NewsStatusUpdate());
}
}

View file

@ -0,0 +1,33 @@
<?php
namespace App\Console\Commands;
use App\Jobs\SeasonalSectionStatusUpdate;
use Illuminate\Console\Command;
class UpdateSeasonalSectionsStatus extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'uwil:updatestatus:seasonalsections';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Update seasonal sections status according to the publish and unpublish dates';
/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
\dispatch(new SeasonalSectionStatusUpdate());
}
}

View file

@ -0,0 +1,229 @@
<?php
namespace App\Console\Commands;
use App\Enums\Users\RoleEnum;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
class UserCreate extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'uwil:user:create';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Create a backend admin user';
/**
* The first name of the new user.
*
* @var string
*/
protected $first_name;
/**
* The last name of the new user.
*
* @var string
*/
protected $last_name;
/**
* The email of the new user.
*
* @var string
*/
protected $email;
/**
* The password of the new user.
*
* @var string
*/
protected $password;
/**
* The role of the new user.
*
* @var string
*/
protected $role;
/**
* Execute the console command.
*
* @return integer
*/
public function handle(): int
{
$this->first_name = null;
$this->addFirstName();
$this->last_name = null;
$this->addLastName();
$this->email = null;
$this->addEmail();
$this->password = null;
$this->addPassword();
$this->role = null;
$this->addRole();
$user = new User([
'first_name' => $this->first_name,
'last_name' => $this->last_name,
'email' => $this->email,
'password' => $this->password,
'role' => $this->role,
'published' => true,
'published_at' => Carbon::now(),
]);
$user->saveOrFail();
$this->info('User created 👌');
return 0;
}
/**
* Add a first name to the user.
*
* @return void
*/
private function addFirstName(): void
{
while (is_null($this->first_name)) {
try {
$tmp = $this->ask('Type the wanted user first name', 'Admin');
$validator = Validator::make(['first_name' => $tmp], [
'first_name' => 'required|min:3|max:255'
]);
$validator->validate();
$this->first_name = $tmp;
} catch (ValidationException $e) {
$this->error(sprintf(
'Please provide a valid first name %s',
\implode(',', collect($e->errors())->flatten()->all())
));
continue;
} //end try
}; //end while
}
/**
* Add a last name to the user.
*
* @return void
*/
private function addLastName(): void
{
while (is_null($this->last_name)) {
try {
$tmp = $this->ask('Type the wanted user last name', 'Saïtama');
$validator = Validator::make(['last_name' => $tmp], [
'last_name' => 'required|min:3|max:255'
]);
$validator->validate();
$this->last_name = $tmp;
} catch (ValidationException $e) {
$this->error(sprintf(
'Please provide a valid last_name %s',
\implode(',', collect($e->errors())->flatten()->all())
));
continue;
} //end try
}; //end while
}
/**
* Add an email to the user.
*
* @return void
*/
private function addEmail(): void
{
while (is_null($this->email)) {
try {
$tmp = $this->ask('Type the wanted user email', 'equipe.web@ldc.fr');
$validator = Validator::make(['email' => $tmp], [
'email' => 'required|unique:users,email|email:rfc,dns'
]);
$validator->validate();
$this->email = $tmp;
} catch (ValidationException $e) {
$this->error(sprintf(
'Please provide a valid email %s',
\implode(',', collect($e->errors())->flatten()->all())
));
continue;
} //end try
}; //end while
}
/**
* Add a password to the user.
*
* @return void
*/
private function addPassword(): void
{
while (
($this->password = $this->secret(
(!\is_null($this->password) ? 'Failed to confirm, ' : '') . 'Type the wanted user password'
)) !== $this->secret('Confirm the password')
) {
continue;
}; //end while
}
/**
* Add a role to the user.
*
* @return void
*/
private function addRole(): void
{
while (is_null($this->role)) {
try {
$tmp = $this->choice(
'Select his role',
[RoleEnum::conceptor->label(), RoleEnum::admin->label(), RoleEnum::visitor->label()],
RoleEnum::visitor->label(),
$maxAttempts = null,
$allowMultipleSelections = false
);
if ($tmp === RoleEnum::conceptor->label()) {
$tmp = RoleEnum::conceptor;
} elseif ($tmp === RoleEnum::admin->label()) {
$tmp = RoleEnum::admin;
} elseif ($tmp === RoleEnum::visitor->label()) {
$tmp = RoleEnum::visitor;
}
$validator = Validator::make(['role' => $tmp], [
'role' => 'required'
]);
$validator->validate();
$this->role = $tmp;
} catch (ValidationException $e) {
$this->error(sprintf(
'Please choose from the selection',
\implode(',', collect($e->errors())->flatten()->all())
));
continue;
} //end try
}; //end while
}
}

35
app/Console/Kernel.php Normal file
View file

@ -0,0 +1,35 @@
<?php
namespace App\Console;
use App\Jobs\NewsStatusUpdate;
use App\Jobs\SeasonalSectionStatusUpdate;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
/**
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
$schedule->job(NewsStatusUpdate::class)->everyMinute();
$schedule->job(SeasonalSectionStatusUpdate::class)->everyMinute();
}
/**
* Register the commands for the application.
*
* @return void
*/
protected function commands()
{
$this->load(__DIR__ . '/Commands');
require base_path('routes/console.php');
}
}

View file

@ -0,0 +1,45 @@
<?php
namespace App\Enums\ActivityLogs;
use App\Traits\Enums\BaseEnum;
enum ActivityLogsEventEnum: int
{
use BaseEnum;
case created = 1;
case updated = 2;
case duplicated = 3;
case deleted = 4;
/**
* Optionnal labels definition
*/
private const LABELS = [
self::created->name => 'Création',
self::updated->name => 'Mise à jour',
self::duplicated->name => 'Duplication',
self::deleted->name => 'Suppression',
];
/**
* Custom added classes definition
*/
private const BOOTSTRAPCLASS = [
self::created->name => 'success',
self::updated->name => 'warning',
self::duplicated->name => 'secondary',
self::deleted->name => 'danger',
];
/**
* Get Class
*
* @return string
*/
public function bootstrapClass(): string
{
return self::BOOTSTRAPCLASS[$this->name];
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace App\Enums\Contact;
use App\Traits\Enums\BaseEnum;
enum CivilityEnum: int
{
use BaseEnum;
case man = 1;
case woman = 2;
/**
* Optionnal labels definition
*/
private const LABELS = [
self::man->name => 'Homme',
self::woman->name => 'Femme',
];
}

View file

@ -0,0 +1,25 @@
<?php
namespace App\Enums\Contact;
use App\Traits\Enums\BaseEnum;
enum ObjectEnum: int
{
use BaseEnum;
case customerService = 1;
case ideasSuggestions = 2;
case informationRequest = 3;
case other = 4;
/**
* Optionnal labels definition
*/
private const LABELS = [
self::customerService->name => 'Service consommateurs',
self::ideasSuggestions->name => 'Idées & suggestions',
self::informationRequest->name => 'Demande d’informations',
self::other->name => 'Divers',
];
}

623
app/Enums/Country.php Normal file
View file

@ -0,0 +1,623 @@
<?php
namespace App\Enums;
use App\Traits\Enums\BaseEnum;
enum Country: int
{
use BaseEnum;
case afghanistan = 1;
case alandIslands = 2;
case albania = 3;
case algeria = 4;
case americanSamoa = 5;
case andorra = 6;
case angola = 7;
case anguilla = 8;
case antarctica = 9;
case antiguaAndBarbuda = 10;
case argentina = 11;
case armenia = 12;
case aruba = 13;
case australia = 14;
case austria = 15;
case azerbaijan = 16;
case bahrain = 17;
case bahamas = 18;
case bangladesh = 19;
case barbados = 20;
case belarus = 21;
case belgium = 22;
case belize = 23;
case benin = 24;
case bermuda = 25;
case bhutan = 26;
case boliviaPlurinationalStateOf = 27;
case bonaireSintEustatiusAndSaba = 28;
case bosniaAndHerzegovina = 29;
case botswana = 30;
case bouvetIsland = 31;
case brazil = 32;
case britishIndianOceanTerritory = 33;
case bruneiDarussalam = 34;
case bulgaria = 35;
case burkinaFaso = 36;
case burundi = 37;
case cambodia = 38;
case cameroon = 39;
case canada = 40;
case capeVerde = 41;
case caymanIslands = 42;
case centralAfricanRepublic = 43;
case chad = 44;
case chile = 45;
case china = 46;
case christmasIsland = 47;
case cocosKeelingIslands = 48;
case colombia = 49;
case comoros = 50;
case congo = 51;
case congoTheDemocraticRepublicOfThe = 52;
case cookIslands = 53;
case costaRica = 54;
case coteDivoire = 55;
case croatia = 56;
case cuba = 57;
case curacao = 58;
case cyprus = 59;
case czechRepublic = 60;
case denmark = 61;
case djibouti = 62;
case dominica = 63;
case dominicanRepublic = 64;
case ecuador = 65;
case egypt = 66;
case elSalvador = 67;
case equatorialGuinea = 68;
case eritrea = 69;
case estonia = 70;
case ethiopia = 71;
case falklandIslandsMalvinas = 72;
case faroeIslands = 73;
case fiji = 74;
case finland = 75;
case france = 76;
case frenchGuiana = 77;
case frenchPolynesia = 78;
case frenchSouthernTerritories = 79;
case gabon = 80;
case gambia = 81;
case georgia = 82;
case germany = 83;
case ghana = 84;
case gibraltar = 85;
case greece = 86;
case greenland = 87;
case grenada = 88;
case guadeloupe = 89;
case guam = 90;
case guatemala = 91;
case guernsey = 92;
case guinea = 93;
case guineaBissau = 94;
case guyana = 95;
case haiti = 96;
case heardIslandAndMcdonaldIslands = 97;
case holySeeVaticanCityState = 98;
case honduras = 99;
case hongKong = 100;
case hungary = 101;
case iceland = 102;
case india = 103;
case indonesia = 104;
case iranIslamicRepublicOf = 105;
case iraq = 106;
case ireland = 107;
case isleOfMan = 108;
case israel = 109;
case italy = 110;
case jamaica = 111;
case japan = 112;
case jersey = 113;
case jordan = 114;
case kazakhstan = 115;
case kenya = 116;
case kiribati = 117;
case koreaDemocraticPeoplesRepublicOf = 118;
case koreaRepublicOf = 119;
case kuwait = 120;
case kyrgyzstan = 121;
case laoPeoplesDemocraticRepublic = 122;
case latvia = 123;
case lebanon = 124;
case lesotho = 125;
case liberia = 126;
case libya = 127;
case liechtenstein = 128;
case lithuania = 129;
case luxembourg = 130;
case macao = 131;
case macedoniaTheFormerYugoslavRepublicOf = 132;
case madagascar = 133;
case malawi = 134;
case malaysia = 135;
case maldives = 136;
case mali = 137;
case malta = 138;
case marshallIslands = 139;
case martinique = 140;
case mauritania = 141;
case mauritius = 142;
case mayotte = 143;
case mexico = 144;
case micronesiaFederatedStatesOf = 145;
case moldovaRepublicOf = 146;
case monaco = 147;
case mongolia = 148;
case montenegro = 149;
case montserrat = 150;
case morocco = 151;
case mozambique = 152;
case myanmar = 153;
case namibia = 154;
case nauru = 155;
case nepal = 156;
case netherlands = 157;
case newCaledonia = 158;
case newZealand = 159;
case nicaragua = 160;
case niger = 161;
case nigeria = 162;
case niue = 163;
case norfolkIsland = 164;
case northernMarianaIslands = 165;
case norway = 166;
case oman = 167;
case pakistan = 168;
case palau = 169;
case palestineStateOf = 170;
case panama = 171;
case papuaNewGuinea = 172;
case paraguay = 173;
case peru = 174;
case philippines = 175;
case pitcairn = 176;
case poland = 177;
case portugal = 178;
case puertoRico = 179;
case qatar = 180;
case reunion = 181;
case romania = 182;
case russianFederation = 183;
case rwanda = 184;
case saintBarthelemy = 185;
case saintHelenaAscensionAndTristanDaCunha = 186;
case saintKittsAndNevis = 187;
case saintLucia = 188;
case saintMartinFrenchPart = 189;
case saintPierreAndMiquelon = 190;
case saintVincentAndTheGrenadines = 191;
case samoa = 192;
case sanMarino = 193;
case saoTomeAndPrincipe = 194;
case saudiArabia = 195;
case senegal = 196;
case serbia = 197;
case seychelles = 198;
case sierraLeone = 199;
case singapore = 200;
case sintMaartenDutchPart = 201;
case slovakia = 202;
case slovenia = 203;
case solomonIslands = 204;
case somalia = 205;
case southAfrica = 206;
case southGeorgiaAndTheSouthSandwichIslands = 207;
case southSudan = 208;
case spain = 209;
case sriLanka = 210;
case sudan = 211;
case suriname = 212;
case svalbardAndJanMayen = 213;
case swaziland = 214;
case sweden = 215;
case switzerland = 216;
case syrianArabRepublic = 217;
case taiwanProvinceOfChina = 218;
case tajikistan = 219;
case tanzaniaUnitedRepublicOf = 220;
case thailand = 221;
case timorLeste = 222;
case togo = 223;
case tokelau = 224;
case tonga = 225;
case trinidadAndTobago = 226;
case tunisia = 227;
case turkey = 228;
case turkmenistan = 229;
case turksAndCaicosIslands = 230;
case tuvalu = 231;
case uganda = 232;
case ukraine = 233;
case unitedArabEmirates = 234;
case unitedKingdom = 235;
case unitedStates = 236;
case unitedStatesMinorOutlyingIslands = 237;
case uruguay = 238;
case uzbekistan = 239;
case vanuatu = 240;
case venezuelaBolivarianRepublicOf = 241;
case vietNam = 242;
case virginIslandsBritish = 243;
case virginIslandsUs = 244;
case wallisAndFutuna = 245;
case westernSahara = 246;
case yemen = 247;
case zambia = 248;
case zimbabwe = 249;
/**
* Optionnal labels definition
*/
private const LABELS = [
self::afghanistan->name => 'Afghanistan',
self::alandIslands->name => 'Åland Islands',
self::albania->name => 'Albania',
self::algeria->name => 'Algeria',
self::americanSamoa->name => 'American Samoa',
self::andorra->name => 'Andorra',
self::angola->name => 'Angola',
self::anguilla->name => 'Anguilla',
self::antarctica->name => 'Antarctica',
self::antiguaAndBarbuda->name => 'Antigua and Barbuda',
self::argentina->name => 'Argentina',
self::armenia->name => 'Armenia',
self::aruba->name => 'Aruba',
self::australia->name => 'Australia',
self::austria->name => 'Austria',
self::azerbaijan->name => 'Azerbaijan',
self::bahrain->name => 'Bahrain',
self::bahamas->name => 'Bahamas',
self::bangladesh->name => 'Bangladesh',
self::barbados->name => 'Barbados',
self::belarus->name => 'Belarus',
self::belgium->name => 'Belgium',
self::belize->name => 'Belize',
self::benin->name => 'Benin',
self::bermuda->name => 'Bermuda',
self::bhutan->name => 'Bhutan',
self::boliviaPlurinationalStateOf->name => 'Bolivia, Plurinational State of',
self::bonaireSintEustatiusAndSaba->name => 'Bonaire, Sint Eustatius and Saba',
self::bosniaAndHerzegovina->name => 'Bosnia and Herzegovina',
self::botswana->name => 'Botswana',
self::bouvetIsland->name => 'Bouvet Island',
self::brazil->name => 'Brazil',
self::britishIndianOceanTerritory->name => 'British Indian Ocean Territory',
self::bruneiDarussalam->name => 'Brunei Darussalam',
self::bulgaria->name => 'Bulgaria',
self::burkinaFaso->name => 'Burkina Faso',
self::burundi->name => 'Burundi',
self::cambodia->name => 'Cambodia',
self::cameroon->name => 'Cameroon',
self::canada->name => 'Canada',
self::capeVerde->name => 'Cape Verde',
self::caymanIslands->name => 'Cayman Islands',
self::centralAfricanRepublic->name => 'Central African Republic',
self::chad->name => 'Chad',
self::chile->name => 'Chile',
self::china->name => 'China',
self::christmasIsland->name => 'Christmas Island',
self::cocosKeelingIslands->name => 'Cocos (Keeling) Islands',
self::colombia->name => 'Colombia',
self::comoros->name => 'Comoros',
self::congo->name => 'Congo',
self::congoTheDemocraticRepublicOfThe->name => 'Congo, the Democratic Republic of the',
self::cookIslands->name => 'Cook Islands',
self::costaRica->name => 'Costa Rica',
self::coteDivoire->name => 'Côte d\'Ivoire',
self::croatia->name => 'Croatia',
self::cuba->name => 'Cuba',
self::curacao->name => 'Curaçao',
self::cyprus->name => 'Cyprus',
self::czechRepublic->name => 'Czech Republic',
self::denmark->name => 'Denmark',
self::djibouti->name => 'Djibouti',
self::dominica->name => 'Dominica',
self::dominicanRepublic->name => 'Dominican Republic',
self::ecuador->name => 'Ecuador',
self::egypt->name => 'Egypt',
self::elSalvador->name => 'El Salvador',
self::equatorialGuinea->name => 'Equatorial Guinea',
self::eritrea->name => 'Eritrea',
self::estonia->name => 'Estonia',
self::ethiopia->name => 'Ethiopia',
self::falklandIslandsMalvinas->name => 'Falkland Islands (Malvinas)',
self::faroeIslands->name => 'Faroe Islands',
self::fiji->name => 'Fiji',
self::finland->name => 'Finland',
self::france->name => 'France',
self::frenchGuiana->name => 'French Guiana',
self::frenchPolynesia->name => 'French Polynesia',
self::frenchSouthernTerritories->name => 'French Southern Territories',
self::gabon->name => 'Gabon',
self::gambia->name => 'Gambia',
self::georgia->name => 'Georgia',
self::germany->name => 'Germany',
self::ghana->name => 'Ghana',
self::gibraltar->name => 'Gibraltar',
self::greece->name => 'Greece',
self::greenland->name => 'Greenland',
self::grenada->name => 'Grenada',
self::guadeloupe->name => 'Guadeloupe',
self::guam->name => 'Guam',
self::guatemala->name => 'Guatemala',
self::guernsey->name => 'Guernsey',
self::guinea->name => 'Guinea',
self::guineaBissau->name => 'GuineaBissau',
self::guyana->name => 'Guyana',
self::haiti->name => 'Haiti',
self::heardIslandAndMcdonaldIslands->name => 'Heard Island and McDonald Islands',
self::holySeeVaticanCityState->name => 'Holy See (Vatican City State)',
self::honduras->name => 'Honduras',
self::hongKong->name => 'Hong Kong',
self::hungary->name => 'Hungary',
self::iceland->name => 'Iceland',
self::india->name => 'India',
self::indonesia->name => 'Indonesia',
self::iranIslamicRepublicOf->name => 'Iran, Islamic Republic of',
self::iraq->name => 'Iraq',
self::ireland->name => 'Ireland',
self::isleOfMan->name => 'Isle of Man',
self::israel->name => 'Israel',
self::italy->name => 'Italy',
self::jamaica->name => 'Jamaica',
self::japan->name => 'Japan',
self::jersey->name => 'Jersey',
self::jordan->name => 'Jordan',
self::kazakhstan->name => 'Kazakhstan',
self::kenya->name => 'Kenya',
self::kiribati->name => 'Kiribati',
self::koreaDemocraticPeoplesRepublicOf->name => 'Korea, Democratic People\'s Republic of',
self::koreaRepublicOf->name => 'Korea, Republic of',
self::kuwait->name => 'Kuwait',
self::kyrgyzstan->name => 'Kyrgyzstan',
self::laoPeoplesDemocraticRepublic->name => 'Lao People\'s Democratic Republic',
self::latvia->name => 'Latvia',
self::lebanon->name => 'Lebanon',
self::lesotho->name => 'Lesotho',
self::liberia->name => 'Liberia',
self::libya->name => 'Libya',
self::liechtenstein->name => 'Liechtenstein',
self::lithuania->name => 'Lithuania',
self::luxembourg->name => 'Luxembourg',
self::macao->name => 'Macao',
self::macedoniaTheFormerYugoslavRepublicOf->name => 'Macedonia, the Former Yugoslav Republic of',
self::madagascar->name => 'Madagascar',
self::malawi->name => 'Malawi',
self::malaysia->name => 'Malaysia',
self::maldives->name => 'Maldives',
self::mali->name => 'Mali',
self::malta->name => 'Malta',
self::marshallIslands->name => 'Marshall Islands',
self::martinique->name => 'Martinique',
self::mauritania->name => 'Mauritania',
self::mauritius->name => 'Mauritius',
self::mayotte->name => 'Mayotte',
self::mexico->name => 'Mexico',
self::micronesiaFederatedStatesOf->name => 'Micronesia, Federated States of',
self::moldovaRepublicOf->name => 'Moldova, Republic of',
self::monaco->name => 'Monaco',
self::mongolia->name => 'Mongolia',
self::montenegro->name => 'Montenegro',
self::montserrat->name => 'Montserrat',
self::morocco->name => 'Morocco',
self::mozambique->name => 'Mozambique',
self::myanmar->name => 'Myanmar',
self::namibia->name => 'Namibia',
self::nauru->name => 'Nauru',
self::nepal->name => 'Nepal',
self::netherlands->name => 'Netherlands',
self::newCaledonia->name => 'New Caledonia',
self::newZealand->name => 'New Zealand',
self::nicaragua->name => 'Nicaragua',
self::niger->name => 'Niger',
self::nigeria->name => 'Nigeria',
self::niue->name => 'Niue',
self::norfolkIsland->name => 'Norfolk Island',
self::northernMarianaIslands->name => 'Northern Mariana Islands',
self::norway->name => 'Norway',
self::oman->name => 'Oman',
self::pakistan->name => 'Pakistan',
self::palau->name => 'Palau',
self::palestineStateOf->name => 'Palestine, State of',
self::panama->name => 'Panama',
self::papuaNewGuinea->name => 'Papua New Guinea',
self::paraguay->name => 'Paraguay',
self::peru->name => 'Peru',
self::philippines->name => 'Philippines',
self::pitcairn->name => 'Pitcairn',
self::poland->name => 'Poland',
self::portugal->name => 'Portugal',
self::puertoRico->name => 'Puerto Rico',
self::qatar->name => 'Qatar',
self::reunion->name => 'Réunion',
self::romania->name => 'Romania',
self::russianFederation->name => 'Russian Federation',
self::rwanda->name => 'Rwanda',
self::saintBarthelemy->name => 'Saint Barthélemy',
self::saintHelenaAscensionAndTristanDaCunha->name => 'Saint Helena, Ascension and Tristan da Cunha',
self::saintKittsAndNevis->name => 'Saint Kitts and Nevis',
self::saintLucia->name => 'Saint Lucia',
self::saintMartinFrenchPart->name => 'Saint Martin (French part)',
self::saintPierreAndMiquelon->name => 'Saint Pierre and Miquelon',
self::saintVincentAndTheGrenadines->name => 'Saint Vincent and the Grenadines',
self::samoa->name => 'Samoa',
self::sanMarino->name => 'San Marino',
self::saoTomeAndPrincipe->name => 'Sao Tome and Principe',
self::saudiArabia->name => 'Saudi Arabia',
self::senegal->name => 'Senegal',
self::serbia->name => 'Serbia',
self::seychelles->name => 'Seychelles',
self::sierraLeone->name => 'Sierra Leone',
self::singapore->name => 'Singapore',
self::sintMaartenDutchPart->name => 'Sint Maarten (Dutch part)',
self::slovakia->name => 'Slovakia',
self::slovenia->name => 'Slovenia',
self::solomonIslands->name => 'Solomon Islands',
self::somalia->name => 'Somalia',
self::southAfrica->name => 'South Africa',
self::southGeorgiaAndTheSouthSandwichIslands->name => 'South Georgia and the South Sandwich Islands',
self::southSudan->name => 'South Sudan',
self::spain->name => 'Spain',
self::sriLanka->name => 'Sri Lanka',
self::sudan->name => 'Sudan',
self::suriname->name => 'Suriname',
self::svalbardAndJanMayen->name => 'Svalbard and Jan Mayen',
self::swaziland->name => 'Swaziland',
self::sweden->name => 'Sweden',
self::switzerland->name => 'Switzerland',
self::syrianArabRepublic->name => 'Syrian Arab Republic',
self::taiwanProvinceOfChina->name => 'Taiwan, Province of China',
self::tajikistan->name => 'Tajikistan',
self::tanzaniaUnitedRepublicOf->name => 'Tanzania, United Republic of',
self::thailand->name => 'Thailand',
self::timorLeste->name => 'TimorLeste',
self::togo->name => 'Togo',
self::tokelau->name => 'Tokelau',
self::tonga->name => 'Tonga',
self::trinidadAndTobago->name => 'Trinidad and Tobago',
self::tunisia->name => 'Tunisia',
self::turkey->name => 'Turkey',
self::turkmenistan->name => 'Turkmenistan',
self::turksAndCaicosIslands->name => 'Turks and Caicos Islands',
self::tuvalu->name => 'Tuvalu',
self::uganda->name => 'Uganda',
self::ukraine->name => 'Ukraine',
self::unitedArabEmirates->name => 'United Arab Emirates',
self::unitedKingdom->name => 'United Kingdom',
self::unitedStates->name => 'United States',
self::unitedStatesMinorOutlyingIslands->name => 'United States Minor Outlying Islands',
self::uruguay->name => 'Uruguay',
self::uzbekistan->name => 'Uzbekistan',
self::vanuatu->name => 'Vanuatu',
self::venezuelaBolivarianRepublicOf->name => 'Venezuela, Bolivarian Republic of',
self::vietNam->name => 'Viet Nam',
self::virginIslandsBritish->name => 'Virgin Islands, British',
self::virginIslandsUs->name => 'Virgin Islands, U.S.',
self::wallisAndFutuna->name => 'Wallis and Futuna',
self::westernSahara->name => 'Western Sahara',
self::yemen->name => 'Yemen',
self::zambia->name => 'Zambia',
self::zimbabwe->name => 'Zimbabwe',
];
/**
* Get Valid Shipping Countries
*
* @return array
*/
public static function validShippingCountries(): array
{
return [
self::france,
/**
* // * DROM COM .
// self::guadeloupe,
// self::martinique,
// self::frenchGuiana,
// self::reunion,
// self::saintPierreAndMiquelon,
// self::mayotte,
// self::saintBarthelemy,
// self::saintMartinFrenchPart,
// self::wallisAndFutuna,
// self::frenchPolynesia,
// self::newCaledonia
*/
];
}
/**
* Get Valid Shipping Countries
*
* @return array
*/
public static function validBillingCountries(): array
{
return [
self::france,
// * DROM COM.
self::guadeloupe,
self::martinique,
self::frenchGuiana,
self::reunion,
self::saintPierreAndMiquelon,
self::mayotte,
self::saintBarthelemy,
self::saintMartinFrenchPart,
self::wallisAndFutuna,
self::frenchPolynesia,
self::newCaledonia
];
}
/**
* Get Alpha 2 ISO code of valid shipping countries
*
* @return array
*/
public static function getValidShippingCountriesIsoAplha2Codes(): array
{
return ['FR'];
}
/**
* Get Alpha 2 ISO code of valid billing countries
*
* @return array
*/
public static function getValidBillingCountriesIsoAplha2Codes(): array
{
return \array_values(self::ISOCODES);
}
/**
* Custom added ISOCODES definition
*/
private const ISOCODES = [
self::france->name => 'FR',
self::guadeloupe->name => 'GP',
self::martinique->name => 'MQ',
self::frenchGuiana->name => 'GF',
self::reunion->name => 'RE',
self::saintPierreAndMiquelon->name => 'PM',
self::mayotte->name => 'YT',
self::saintBarthelemy->name => 'BL',
self::saintMartinFrenchPart->name => 'MF',
self::wallisAndFutuna->name => 'WF',
self::frenchPolynesia->name => 'PF',
self::newCaledonia->name => 'NC',
];
/**
* Get ISOCODE
*
* @return string
*/
public function iso(): string
{
return self::ISOCODES[$this->name];
}
/**
* Get enum ISOCODES
*
* @return array<string>
*/
public static function toIsoCodes(): array
{
return self::ISOCODES;
}
}

30
app/Enums/InputType.php Normal file
View file

@ -0,0 +1,30 @@
<?php
namespace App\Enums;
use App\Traits\Enums\BaseEnum;
enum InputType: int
{
use BaseEnum;
case boolean = 1;
case dropdownArray = 2;
case email = 3;
case image = 4;
case imageArray = 5;
case labelArray = 6;
case miniblocArray = 7;
case number = 8;
case selectEnum = 9;
case password = 10;
case string = 11;
case text = 12;
case url = 13;
/** Vimeo only */
case videoVimeo = 14;
/** Youtube only */
case videoYoutube = 15;
/** Youtube or Vimeo. */
case video = 16;
}

View file

@ -0,0 +1,27 @@
<?php
namespace App\Enums\News;
use App\Traits\Enums\BaseEnum;
enum CategoryEnum: int
{
use BaseEnum;
case enterprise = 1;
case convictions = 2;
case recipes = 3;
case jobs = 4;
case communication = 5;
/**
* Optionnal labels definition.
*/
private const LABELS = [
self::enterprise->name => 'La vie d\'Entreprise',
self::convictions->name => 'Nos convictions',
self::recipes->name => 'Nos recettes',
self::jobs->name => 'Nos métiers',
self::communication->name => 'Communication',
];
}

View file

@ -0,0 +1,16 @@
<?php
namespace App\Enums\Pagination;
use App\Traits\Enums\BaseEnum;
enum ItemsPerPaginationEnum: int
{
use BaseEnum;
case five = 5;
case twelve = 12;
case twentyfive = 25;
case fifty = 50;
case onehundred = 100;
}

View file

@ -0,0 +1,70 @@
<?php
namespace App\Enums\Products;
use App\Traits\Enums\BaseEnum;
enum CookingModeEnum: int
{
use BaseEnum;
case microwave = 1;
case furnace = 2;
case waterbath = 3;
case pan = 4;
case fresh = 5;
/**
* Optionnal labels definition.
*/
private const LABELS = [
self::microwave->name => 'Micro-ondes',
self::furnace->name => 'Four traditionnel',
self::waterbath->name => 'Bain-marie',
self::pan->name => 'Poêle',
self::fresh->name => 'Se consomme frais',
];
/**
* Pictures paths.
*/
private const PICTURES = [
self::microwave->name => 'resources/assets/images/pictos/micro-wave.png',
self::furnace->name => 'resources/assets/images/pictos/furnace.png',
self::waterbath->name => 'resources/assets/images/pictos/water-bath.png',
self::pan->name => 'resources/assets/images/pictos/pan.png',
self::fresh->name => 'resources/assets/images/pictos/fresh.png',
];
/**
* Get picture path
*
* @return string
*/
public function picture(): string
{
return self::PICTURES[$this->name];
}
/**
* Alt attributes for pictures.
*/
private const ALTS = [
self::microwave->name => 'Pictogramme d\'un micro-ondes',
self::furnace->name => 'Pictogramme d\'un four traditionnel',
self::waterbath->name => 'Pictogramme d\'un bain-marie',
self::pan->name => 'Pictogramme d\'une poêle',
self::fresh->name => 'Pictogramme d\'une goutte d\'eau',
];
/**
* Get picture alt attribute
*
* @return string
*/
public function alt(): string
{
return self::ALTS[$this->name];
}
}

View file

@ -0,0 +1,42 @@
<?php
namespace App\Enums\SeasonalSections;
use App\Traits\Enums\BaseEnum;
enum BackgroundEnum: int
{
use BaseEnum;
case candlemas = 1;
case chinesenewyear = 2;
case endofyear = 3;
/**
* Optionnal labels definition.
*/
private const LABELS = [
self::candlemas->name => 'Chandeleur',
self::chinesenewyear->name => 'Nouvel an chinois',
self::endofyear->name => 'Fin d\'année',
];
/**
* Custom added classes definition
*/
private const BOOTSTRAPCLASS = [
self::candlemas->name => 'seasonal-sections-candlemas',
self::chinesenewyear->name => 'seasonal-sections-chinesenewyear',
self::endofyear->name => 'seasonal-sections-endofyear',
];
/**
* Get Class
*
* @return string
*/
public function bootstrapClass(): string
{
return self::BOOTSTRAPCLASS[$this->name];
}
}

View file

@ -0,0 +1,37 @@
<?php
namespace App\Enums;
use App\Traits\Enums\BaseEnum;
enum StaticPageType: int
{
use BaseEnum;
case contact = 1;
case legals = 2;
case confidentiality = 3;
case home = 4;
case conviction = 5;
case explorer = 6;
case joinus = 7;
case ranges = 8;
case aboutus = 9;
case news = 10;
/**
* Optionnal labels definition.
*/
private const LABELS = [
self::contact->name => 'Contact',
self::legals->name => 'Mentions légales',
self::confidentiality->name => 'Confidentialité',
self::home->name => 'Accueil',
self::conviction->name => 'Nos Convictions',
self::explorer->name => 'Explorateur & Gastronome',
self::joinus->name => 'Rejoignez-nous',
self::ranges->name => 'Nos Gammes de Produits',
self::aboutus->name => 'Nous Connaître',
self::news->name => 'Actualités',
];
}

View file

@ -0,0 +1,21 @@
<?php
namespace App\Enums\Theme;
use App\Traits\Enums\BaseEnum;
enum BootstrapThemeEnum: int
{
use BaseEnum;
case light = 1;
case dark = 2;
/**
* Optionnal labels definition
*/
private const LABELS = [
self::light->name => 'Light',
self::dark->name => 'Dark',
];
}

View file

@ -0,0 +1,23 @@
<?php
namespace App\Enums\Users;
use App\Traits\Enums\BaseEnum;
enum RoleEnum: int
{
use BaseEnum;
case conceptor = 1;
case admin = 2;
case visitor = 3;
/**
* Optionnal labels definition.
*/
private const LABELS = [
self::conceptor->name => 'Concepteur',
self::admin->name => 'Administrateur',
self::visitor->name => 'Visiteur',
];
}

View file

@ -0,0 +1,48 @@
<?php
namespace App\Exceptions;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Throwable;
class Handler extends ExceptionHandler
{
/**
* A list of exception types with their corresponding custom log levels.
*
* @var array<class-string<\Throwable>, \Psr\Log\LogLevel::*>
*/
protected $levels = [];
/**
* A list of the exception types that are not reported.
*
* @var array<int, class-string<\Throwable>>
*/
protected $dontReport = [];
/**
* A list of the inputs that are never flashed to the session on validation exceptions.
*
* @var array<int, string>
*/
protected $dontFlash = [
'current_password',
'password',
'password_confirmation',
];
/**
* Register the exception handling callbacks for the application.
*
* @return void
*/
public function register()
{
$this->reportable(function (Throwable $e) {
if (app()->bound('sentry')) {
app('sentry')->captureException($e);
}
});
}
}

View file

@ -0,0 +1,71 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Models\ActivityLog;
use App\Traits\Controllers\ChangesModelOrder;
use App\Traits\Controllers\UpdateModelPublished;
use Illuminate\Http\Request;
class ActivityLogsController extends Controller
{
use ChangesModelOrder;
use UpdateModelPublished;
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/
public function index(Request $request): \Illuminate\Contracts\View\View
{
$this->authorize('isConceptor');
/** @var \Illuminate\Database\Eloquent\Builder $query */
$query = ActivityLog::query()->with('user');
/** @var string $search Search field */
$search = $request->search;
if ($search) {
$this->searchQuery(
$query,
$search,
null,
'model_class',
);
}
$searchFields = \implode(', ', [
trans('validation.custom.target'),
]);
/** Sort columns with a query */
$this->sortQuery($query);
/** Custom pagination */
$activitylogModels = $this->paginate($query);
return view('bo.pages.activity_logs.index', compact('activitylogModels', 'search', 'searchFields'));
}
/**
* Show the form for editing the specified resource.
*
* @param App\Models\ActivityLog $activity_log
* @return \Illuminate\Contracts\View\View
*/
public function show(ActivityLog $activity_log): \Illuminate\Contracts\View\View
{
$this->authorize('isConceptor');
$targetModelClass = $activity_log->model_class;
$targetModel = $activity_log->model_class::where(
(new $targetModelClass())->getRouteKeyName(),
$activity_log->model_id
)->first();
return view('bo.pages.activity_logs.show', [
'activitylogModel' => $activity_log,
'targetModel' => $targetModel,
]);
}
}

View file

@ -0,0 +1,72 @@
<?php
namespace App\Http\Controllers\Bo\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Contracts\Auth\StatefulGuard;
use Illuminate\Contracts\View\View;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class LoginController extends Controller
{
use AuthenticatesUsers;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest:backend')->except('logout');
}
/**
* Get the guard to be used during authentication.
*
* @return \Illuminate\Contracts\Auth\StatefulGuard
*/
protected function guard(): StatefulGuard
{
return Auth::guard('backend');
}
/**
* Show the application's login form.
*
* @return \Illuminate\Contracts\View\View
*/
public function showLoginForm(): View
{
return view('bo.auth.login');
}
/**
* Where to redirect users after login.
*
* @return string
*/
protected function redirectTo(): string
{
return response()->redirectToIntended()->getTargetUrl();
}
/**
* Attempt to log the user into the application.
*
* @param \Illuminate\Http\Request $request
* @return boolean
*/
protected function attemptLogin(Request $request)
{
$credentials = $this->credentials($request);
// * Attempt login only using published users.
$credentials['published'] = true;
return $this->guard()->attempt(
$credentials,
$request->boolean('remember')
);
}
}

View file

@ -0,0 +1,33 @@
<?php
namespace App\Http\Controllers\Bo\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Contracts\Auth\StatefulGuard;
use Illuminate\Foundation\Auth\ResetsPasswords;
use Illuminate\Support\Facades\Auth;
class ResetPasswordController extends Controller
{
use ResetsPasswords;
/**
* Get the guard to be used during authentication.
*
* @return \Illuminate\Contracts\Auth\StatefulGuard
*/
protected function guard(): StatefulGuard
{
return Auth::guard('backend');
}
/**
* Where to redirect users after login.
*
* @return string
*/
protected function redirectTo(): string
{
return \route('bo.login');
}
}

View file

@ -0,0 +1,166 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\Commitments\StoreCommitmentRequest;
use App\Http\Requests\Bo\Commitments\UpdateCommitmentRequest;
use App\Models\Commitment;
use App\Traits\Controllers\ChangesModelOrder;
use App\Traits\Controllers\UpdateModelPublished;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class CommitmentController extends Controller
{
use UpdateModelPublished;
use ChangesModelOrder;
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(Commitment::class);
}
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/
public function index(Request $request): \Illuminate\Contracts\View\View
{
/** @var \Illuminate\Database\Eloquent\Builder $commitmentModels */
$commitmentModels = Commitment::query();
/** @var string $search Search field */
$search = $request->search;
if ($search) {
$this->searchQuery(
$commitmentModels,
$search,
null,
'title',
);
}
$searchFields = \implode(', ', [
Str::ucfirst(trans('validation.attributes.title'))
]);
/** Sort columns with a query */
$this->sortQuery($commitmentModels);
/** Custom pagination */
$commitmentModels = $this->paginate($commitmentModels);
return view('bo.pages.commitments.index', compact('commitmentModels', 'search', 'searchFields'));
}
/**
* Show the form for creating a new resource.
*
* @param \App\Models\Commitment $commitment
* @return \Illuminate\Contracts\View\View
*/
public function create(Commitment $commitment): \Illuminate\Contracts\View\View
{
return view('bo.pages.commitments.create', ['commitmentModel' => $commitment]);
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\Commitments\StoreCommitmentRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(StoreCommitmentRequest $request): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request) {
$commitment = new Commitment();
if ($commitment->fill($request->validated())->saveOrFail()) {
return redirect()->route('bo.commitments.edit', $commitment)
->with('success', __('crud.messages.has_been_created', [
'model' => Str::ucfirst(__('models.classes.commitment'))
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_created', [
'model' => Str::ucfirst(__('models.classes.commitment'))
]));
});
}
/**
* Show the form for editing the specified resource.
*
* @param App\Models\Commitment $commitment
* @return \Illuminate\Contracts\View\View
*/
public function edit(Commitment $commitment): \Illuminate\Contracts\View\View
{
return view('bo.pages.commitments.edit', ['commitmentModel' => $commitment]);
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\Commitments\UpdateCommitmentRequest $request
* @param \App\Models\Commitment $commitment
* @return \Illuminate\Http\RedirectResponse
*/
public function update(UpdateCommitmentRequest $request, Commitment $commitment): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request, $commitment) {
if ($commitment->fill($request->validated())->saveOrFail()) {
return redirect()->route('bo.commitments.edit', $commitment)
->with('success', __('crud.messages.has_been_updated', [
'model' => Str::ucfirst(__('models.classes.commitment'))
]));
}
return redirect()->route('bo.commitments.edit', $commitment)
->with('error', __('crud.messages.cannot_be_updated', [
'model' => Str::ucfirst(__('models.classes.commitment'))
]));
});
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\Commitment $commitment
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(Commitment $commitment): \Illuminate\Http\RedirectResponse
{
$commitment->products()->detach();
if ($commitment->explorerpage()->count() > 3) {
if ($commitment->deleteOrFail()) {
return redirect()->route('bo.commitments.index')
->with('success', __('crud.messages.has_been_deleted', [
'model' => Str::ucfirst(__('models.classes.commitment'))
]));
}
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_deleted', [
'model' => Str::ucfirst(__('models.classes.commitment'))
]));
}
/**
* Duplicate the specified resource.
*
* @param \App\Models\Commitment $commitment
* @return \Illuminate\Contracts\View\View
*/
public function duplicate(Commitment $commitment): \Illuminate\Contracts\View\View
{
return $this->create($commitment->replicate());
}
}

View file

@ -0,0 +1,54 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\Convictionpages\UpdateConvictionpageRequest;
use App\Models\Convictionpage;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class ConvictionpageController extends Controller
{
/**
* Show the form for editing the specified resource.
*
* @return \Illuminate\Contracts\View\View
*/
public function edit(): \Illuminate\Contracts\View\View
{
$this->authorize('update', \App\Models\Convictionpage::class);
/** @var \App\Models\Convictionpage $convictionpageModel */
$convictionpageModel = Convictionpage::query()->firstOrFail();
return view('bo.pages.convictionpages.edit', compact('convictionpageModel'));
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\Convictionpages\UpdateConvictionpageRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function update(UpdateConvictionpageRequest $request): \Illuminate\Http\RedirectResponse
{
$this->authorize('update', \App\Models\Convictionpage::class);
return DB::transaction(function () use ($request) {
/** @var \App\Models\Convictionpage $convictionpageModel */
$convictionpageModel = Convictionpage::query()->firstOrFail();
if (($convictionpageModel->fill($request->validated()))->saveOrFail()) {
return redirect()->route('bo.convictionpages.edit')
->with('success', __('crud.messages.has_been_updated', [
'model' => Str::ucfirst(__('models.classes.convictionpage'))
]));
}
return redirect()->route('bo.convictionpages.edit')
->with('error', __('crud.messages.cannot_be_updated', [
'model' => Str::ucfirst(__('models.classes.convictionpage'))
]));
});
}
}

View file

@ -0,0 +1,19 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use Illuminate\Contracts\View\View;
class DashboardController extends Controller
{
/**
* Dashboard Page
*
* @return \Illuminate\Contracts\View\View
*/
public function index(): View
{
return view('bo.pages.dashboard');
}
}

View file

@ -0,0 +1,61 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\Explorerpage\UpdateExplorerpageRequest;
use App\Models\Commitment;
use App\Models\Explorerpage;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class ExplorerpageController extends Controller
{
/**
* Show the form for editing the specified resource.
*
* @return \Illuminate\Contracts\View\View
*/
public function edit(): \Illuminate\Contracts\View\View
{
$this->authorize('update', \App\Models\Explorerpage::class);
/** @var \App\Models\Explorerpage $explorerpageModel */
$explorerpageModel = Explorerpage::query()->firstOrFail();
/** @var \Illuminate\Database\Eloquent\Collection<array-key, \App\Models\Commitment> */
$commitmentModels = Commitment::all();
return view('bo.pages.explorerpages.edit', compact('explorerpageModel', 'commitmentModels'));
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\Explorerpage\UpdateExplorerpageRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function update(UpdateExplorerpageRequest $request): \Illuminate\Http\RedirectResponse
{
$this->authorize('update', \App\Models\Explorerpage::class);
return DB::transaction(function () use ($request) {
/** @var \App\Models\Explorerpage $explorerpageModel */
$explorerpageModel = Explorerpage::query()->firstOrFail();
$explorerpageModel->fill($request->safe()->except('commitments'));
$explorerpageModel->commitments()->sync(\collect($request->validated('commitments'))->pluck('id'));
if ($explorerpageModel->saveOrFail()) {
return redirect()->route('bo.explorerpages.edit')
->with('success', __('crud.messages.has_been_updated', [
'model' => Str::ucfirst(__('models.classes.explorerpage'))
]));
}
return redirect()->route('bo.explorerpages.edit')
->with('error', __('crud.messages.cannot_be_updated', [
'model' => Str::ucfirst(__('models.classes.explorerpage'))
]));
});
}
}

View file

@ -0,0 +1,54 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\Homepage\UpdateHomepageRequest;
use App\Models\Homepage;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class HomepageController extends Controller
{
/**
* Show the form for editing the specified resource.
*
* @return \Illuminate\Contracts\View\View
*/
public function edit(): \Illuminate\Contracts\View\View
{
$this->authorize('update', \App\Models\Homepage::class);
/** @var \App\Models\Homepage $homepageModel */
$homepageModel = Homepage::query()->firstOrFail();
return view('bo.pages.homepages.edit', compact('homepageModel'));
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\Homepage\UpdateHomepageRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function update(UpdateHomepageRequest $request): \Illuminate\Http\RedirectResponse
{
$this->authorize('update', \App\Models\Homepage::class);
return DB::transaction(function () use ($request) {
/** @var \App\Models\Homepage $homepageModel */
$homepageModel = Homepage::query()->firstOrFail();
if ($homepageModel->fill($request->validated())->saveOrFail()) {
return redirect()->route('bo.homepages.edit')
->with('success', __('crud.messages.has_been_updated', [
'model' => Str::ucfirst(__('models.classes.homepage'))
]));
}
return redirect()->route('bo.homepages.edit')
->with('error', __('crud.messages.cannot_be_updated', [
'model' => Str::ucfirst(__('models.classes.homepage'))
]));
});
}
}

View file

@ -0,0 +1,54 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\Joinuspage\UpdateJoinuspageRequest;
use App\Models\Joinuspage;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class JoinuspageController extends Controller
{
/**
* Show the form for editing the specified resource.
*
* @return \Illuminate\Contracts\View\View
*/
public function edit(): \Illuminate\Contracts\View\View
{
$this->authorize('update', \App\Models\Joinuspage::class);
/** @var \App\Models\Joinuspage $joinuspageModel */
$joinuspageModel = Joinuspage::query()->firstOrFail();
return view('bo.pages.joinuspages.edit', compact('joinuspageModel'));
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\Joinuspages\UpdateJoinuspageRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function update(UpdateJoinuspageRequest $request): \Illuminate\Http\RedirectResponse
{
$this->authorize('update', \App\Models\Joinuspage::class);
return DB::transaction(function () use ($request) {
/** @var \App\Models\Joinuspage $joinuspageModel */
$joinuspageModel = Joinuspage::query()->firstOrFail();
if ($joinuspageModel->fill($request->validated())->saveOrFail()) {
return redirect()->route('bo.joinuspages.edit')
->with('success', __('crud.messages.has_been_updated', [
'model' => Str::ucfirst(__('models.classes.joinuspage'))
]));
}
return redirect()->route('bo.joinuspages.edit')
->with('error', __('crud.messages.cannot_be_updated', [
'model' => Str::ucfirst(__('models.classes.joinuspage'))
]));
});
}
}

View file

@ -0,0 +1,168 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\News\StoreNewsRequest;
use App\Http\Requests\Bo\News\UpdateNewsRequest;
use App\Models\News;
use App\Traits\Controllers\ChangesModelOrder;
use App\Traits\Controllers\UpdateModelPublished;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class NewsController extends Controller
{
use UpdateModelPublished;
use ChangesModelOrder;
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(News::class);
}
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/
public function index(Request $request): \Illuminate\Contracts\View\View
{
/** @var \Illuminate\Database\Eloquent\Builder $newsModels */
$newsModels = News::query();
/** @var string $search Search field */
$search = $request->search;
if ($search) {
$this->searchQuery(
$newsModels,
$search,
null,
'title',
'subtitle',
'content',
'category',
);
}
$searchFields = \implode(', ', [
Str::ucfirst(trans('validation.attributes.title')),
trans('validation.custom.subtitle'),
trans('validation.attributes.content'),
trans('validation.custom.category'),
]);
/** Sort columns with a query */
$this->sortQuery($newsModels);
/** Custom pagination */
$newsModels = $this->paginate($newsModels);
return view('bo.pages.news.index', compact('newsModels', 'search', 'searchFields'));
}
/**
* Show the form for creating a new resource.
*
* @param \App\Models\News $news
* @return \Illuminate\Contracts\View\View
*/
public function create(News $news): \Illuminate\Contracts\View\View
{
return view('bo.pages.news.create', ['newsModel' => $news]);
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\News\StoreNewsRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(StoreNewsRequest $request): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request) {
$news = new News();
if ($news->fill($request->validated())->saveOrFail()) {
return redirect()->route('bo.news.edit', $news)
->with('success', __('crud.messages.has_been_created', [
'model' => Str::ucfirst(__('models.classes.news'))
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_created', [
'model' => Str::ucfirst(__('models.classes.news'))
]));
});
}
/**
* Show the form for editing the specified resource.
*
* @param App\Models\News $news
* @return \Illuminate\Contracts\View\View
*/
public function edit(News $news): \Illuminate\Contracts\View\View
{
return view('bo.pages.news.edit', ['newsModel' => $news]);
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\News\UpdateNewsRequest $request
* @param \App\Models\News $news
* @return \Illuminate\Http\RedirectResponse
*/
public function update(UpdateNewsRequest $request, News $news): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request, $news) {
if ($news->fill($request->validated())->saveOrFail()) {
return redirect()->route('bo.news.edit', $news)
->with('success', __('crud.messages.has_been_updated', [
'model' => Str::ucfirst(__('models.classes.news'))
]));
}
return redirect()->route('bo.news.edit', $news)
->with('error', __('crud.messages.cannot_be_updated', [
'model' => Str::ucfirst(__('models.classes.news'))
]));
});
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\News $news
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(News $news): \Illuminate\Http\RedirectResponse
{
if ($news->deleteOrFail()) {
return redirect()->route('bo.news.index')
->with('success', __('crud.messages.has_been_deleted', [
'model' => Str::ucfirst(__('models.classes.news'))
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_deleted', [
'model' => Str::ucfirst(__('models.classes.news'))
]));
}
/**
* Duplicate the specified resource.
*
* @param \App\Models\News $news
* @return \Illuminate\Contracts\View\View
*/
public function duplicate(News $news): \Illuminate\Contracts\View\View
{
return $this->create($news->replicate());
}
}

View file

@ -0,0 +1,191 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\Products\StoreProductRequest;
use App\Http\Requests\Bo\Products\UpdateProductRequest;
use App\Models\Commitment;
use App\Models\Product;
use App\Models\Range;
use App\Traits\Controllers\ChangesModelOrder;
use App\Traits\Controllers\UpdateModelPublished;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class ProductController extends Controller
{
use UpdateModelPublished;
use ChangesModelOrder;
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(Product::class);
}
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/
public function index(Request $request): \Illuminate\Contracts\View\View
{
/** @var \Illuminate\Database\Eloquent\Builder $productModels */
$productModels = Product::query();
/** @var string $search Search field */
$search = $request->search;
if ($search) {
$this->searchQuery(
$productModels,
$search,
null,
'title',
'subtitle',
'description',
);
}
$searchFields = \implode(', ', [
Str::ucfirst(trans('validation.attributes.title')),
trans('validation.custom.subtitle'),
trans('validation.custom.description'),
]);
/** Sort columns with a query */
$this->sortQuery($productModels);
/** Custom pagination */
$productModels = $this->paginate($productModels);
return view('bo.pages.products.index', compact('productModels', 'search', 'searchFields'));
}
/**
* Show the form for creating a new resource.
*
* @param \App\Models\Product $product
* @return \Illuminate\Contracts\View\View
*/
public function create(Product $product): \Illuminate\Contracts\View\View
{
$rangeModels = Range::all();
$commitmentModels = Commitment::all();
return view('bo.pages.products.create', [
'productModel' => $product,
'rangeModels' => $rangeModels,
'commitmentModels' => $commitmentModels,
]);
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\Products\StoreProductRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(StoreProductRequest $request): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request) {
$product = new Product();
// Method safe() don't return formatted nutritionnal values, but we need this method to exclude commitments.
$product->fill($request->safe()->except(['commitments', 'nutritionnal_values']));
// We don't take $request->validated('nutritionnal_values') because it returned undesired datas.
$product->nutritionnal_values = $request->validated()['nutritionnal_values'];
if ($product->saveOrFail()) {
$product->commitments()->attach(collect($request->validated('commitments'))->pluck('id'));
return redirect()->route('bo.products.edit', $product)
->with('success', __('crud.messages.has_been_created', [
'model' => Str::ucfirst(__('models.classes.product'))
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_created', [
'model' => Str::ucfirst(__('models.classes.product'))
]));
});
}
/**
* Show the form for editing the specified resource.
*
* @param App\Models\Product $product
* @return \Illuminate\Contracts\View\View
*/
public function edit(Product $product): \Illuminate\Contracts\View\View
{
$rangeModels = Range::all();
$commitmentModels = Commitment::all();
return view('bo.pages.products.edit', [
'productModel' => $product,
'rangeModels' => $rangeModels,
'commitmentModels' => $commitmentModels,
]);
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\Products\UpdateProductRequest $request
* @param \App\Models\Product $product
* @return \Illuminate\Http\RedirectResponse
*/
public function update(UpdateProductRequest $request, Product $product): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request, $product) {
$product->fill($request->safe()->except('commitments'));
$product->commitments()->sync(collect($request->validated('commitments'))->pluck('id'));
$product->nutritionnal_values = $request->validated()['nutritionnal_values'];
if ($product->saveOrFail()) {
return redirect()->route('bo.products.edit', $product)
->with('success', __('crud.messages.has_been_updated', [
'model' => Str::ucfirst(__('models.classes.product'))
]));
}
return redirect()->route('bo.products.edit', $product)
->with('error', __('crud.messages.cannot_be_updated', [
'model' => Str::ucfirst(__('models.classes.product'))
]));
});
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\Product $product
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(Product $product): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($product) {
$product->commitments()->detach();
if ($product->deleteOrFail()) {
return redirect()->route('bo.products.index')
->with('success', __('crud.messages.has_been_deleted', [
'model' => Str::ucfirst(__('models.classes.product'))
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_deleted', [
'model' => Str::ucfirst(__('models.classes.product'))
]));
});
}
/**
* Duplicate the specified resource.
*
* @param \App\Models\Product $product
* @return \Illuminate\Contracts\View\View
*/
public function duplicate(Product $product): \Illuminate\Contracts\View\View
{
return $this->create($product->replicate());
}
}

View file

@ -0,0 +1,175 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\Ranges\StoreRangeRequest;
use App\Http\Requests\Bo\Ranges\UpdateRangeRequest;
use App\Models\Range;
use App\Traits\Controllers\ChangesModelOrder;
use App\Traits\Controllers\UpdateModelPublished;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class RangeController extends Controller
{
use UpdateModelPublished;
use ChangesModelOrder;
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(Range::class);
}
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/
public function index(Request $request): \Illuminate\Contracts\View\View
{
/** @var \Illuminate\Database\Eloquent\Builder $rangeModels */
$rangeModels = Range::query();
/** @var string $search Search field */
$search = $request->search;
if ($search) {
$this->searchQuery(
$rangeModels,
$search,
null,
'title',
'subtitle',
'tagline',
'description',
);
}
$searchFields = \implode(', ', [
Str::ucfirst(trans('validation.attributes.title')),
trans('validation.custom.subtitle'),
trans('validation.custom.tagline'),
trans('validation.custom.description'),
]);
/** Sort columns with a query */
$this->sortQuery($rangeModels);
/** Custom pagination */
$rangeModels = $this->paginate($rangeModels);
return view('bo.pages.ranges.index', compact('rangeModels', 'search', 'searchFields'));
}
/**
* Show the form for creating a new resource.
*
* @param \App\Models\Range $range
* @return \Illuminate\Contracts\View\View
*/
public function create(Range $range): \Illuminate\Contracts\View\View
{
return view('bo.pages.ranges.create', ['rangeModel' => $range]);
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\Ranges\StoreRangeRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(StoreRangeRequest $request): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request) {
$range = new Range();
if ($range->fill($request->validated())->saveOrFail()) {
return redirect()->route('bo.ranges.edit', $range)
->with('success', __('crud.messages.has_been_created', [
'model' => Str::ucfirst(__('models.classes.range'))
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_created', [
'model' => Str::ucfirst(__('models.classes.range'))
]));
});
}
/**
* Show the form for editing the specified resource.
*
* @param App\Models\Range $range
* @return \Illuminate\Contracts\View\View
*/
public function edit(Range $range): \Illuminate\Contracts\View\View
{
return view('bo.pages.ranges.edit', ['rangeModel' => $range]);
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\Ranges\UpdateRangeRequest $request
* @param \App\Models\Range $range
* @return \Illuminate\Http\RedirectResponse
*/
public function update(UpdateRangeRequest $request, Range $range): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request, $range) {
if ($range->fill($request->validated())->saveOrFail()) {
return redirect()->route('bo.ranges.edit', $range)
->with('success', __('crud.messages.has_been_updated', [
'model' => Str::ucfirst(__('models.classes.range'))
]));
}
return redirect()->route('bo.ranges.edit', $range)
->with('error', __('crud.messages.cannot_be_updated', [
'model' => Str::ucfirst(__('models.classes.range'))
]));
});
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\Range $range
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(Range $range): \Illuminate\Http\RedirectResponse
{
if (!$range->products->isEmpty()) {
return redirect()->back()
->with('error', __('crud.messages.cannot_be_deleted_relation', [
'model' => Str::ucfirst(__('models.classes.range')),
'relations' => Str::plural(__('models.classes.product'))
]));
}
if ($range->deleteOrFail()) {
return redirect()->route('bo.ranges.index')
->with('success', __('crud.messages.has_been_deleted', [
'model' => Str::ucfirst(__('models.classes.range'))
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_deleted', [
'model' => Str::ucfirst(__('models.classes.range'))
]));
}
/**
* Duplicate the specified resource.
*
* @param \App\Models\Range $range
* @return \Illuminate\Contracts\View\View
*/
public function duplicate(Range $range): \Illuminate\Contracts\View\View
{
return $this->create($range->replicate());
}
}

View file

@ -0,0 +1,164 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\SeasonalSections\StoreSeasonalSectionRequest;
use App\Http\Requests\Bo\SeasonalSections\UpdateSeasonalSectionRequest;
use App\Models\SeasonalSection;
use App\Traits\Controllers\UpdateModelPublished;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class SeasonalSectionController extends Controller
{
use UpdateModelPublished;
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(SeasonalSection::class);
}
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/
public function index(Request $request): \Illuminate\Contracts\View\View
{
/** @var \Illuminate\Database\Eloquent\Builder $seasonalSectionModels */
$seasonalSectionModels = SeasonalSection::query();
/** @var string $search Search field */
$search = $request->search;
if ($search) {
$this->searchQuery(
$seasonalSectionModels,
$search,
null,
'title',
'text',
);
}
$searchFields = \implode(', ', [
Str::ucfirst(trans('validation.attributes.title')),
trans('validation.attributes.text'),
]);
/** Sort columns with a query */
$this->sortQuery($seasonalSectionModels);
/** Custom pagination */
$seasonalSectionModels = $this->paginate($seasonalSectionModels);
return view('bo.pages.seasonal_sections.index', compact('seasonalSectionModels', 'search', 'searchFields'));
}
/**
* Show the form for creating a new resource.
*
* @param \App\Models\SeasonalSection $seasonalSection
* @return \Illuminate\Contracts\View\View
*/
public function create(SeasonalSection $seasonalSection): \Illuminate\Contracts\View\View
{
return view('bo.pages.seasonal_sections.create', ['seasonalSectionModel' => $seasonalSection]);
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\SeasonalSections\StoreSeasonalSectionRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(StoreSeasonalSectionRequest $request): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request) {
$seasonalSection = new SeasonalSection();
if ($seasonalSection->fill($request->validated())->saveOrFail()) {
return redirect()->route('bo.seasonalsections.edit', $seasonalSection)
->with('success', __('crud.messages.has_been_created', [
'model' => Str::ucfirst(trans_choice('models.classes.seasonal_section', 1))
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_created', [
'model' => Str::ucfirst(trans_choice('models.classes.seasonal_section', 1))
]));
});
}
/**
* Show the form for editing the specified resource.
*
* @param App\Models\SeasonalSection $seasonalSection
* @return \Illuminate\Contracts\View\View
*/
public function edit(SeasonalSection $seasonalSection): \Illuminate\Contracts\View\View
{
return view('bo.pages.seasonal_sections.edit', ['seasonalSectionModel' => $seasonalSection]);
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\SeasonalSections\UpdateSeasonalSectionRequest $request
* @param \App\Models\SeasonalSection $seasonalSection
* @return \Illuminate\Http\RedirectResponse
*/
public function update(
UpdateSeasonalSectionRequest $request,
SeasonalSection $seasonalSection
): \Illuminate\Http\RedirectResponse {
return DB::transaction(function () use ($request, $seasonalSection) {
if ($seasonalSection->fill($request->validated())->saveOrFail()) {
return redirect()->route('bo.seasonalsections.edit', $seasonalSection)
->with('success', __('crud.messages.has_been_updated', [
'model' => Str::ucfirst(trans_choice('models.classes.seasonal_section', 1))
]));
}
return redirect()->route('bo.seasonalsections.edit', $seasonalSection)
->with('error', __('crud.messages.cannot_be_updated', [
'model' => Str::ucfirst(trans_choice('models.classes.seasonal_section', 1))
]));
});
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\SeasonalSection $seasonalSection
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(SeasonalSection $seasonalSection): \Illuminate\Http\RedirectResponse
{
if ($seasonalSection->deleteOrFail()) {
return redirect()->route('bo.seasonalsections.index')
->with('success', __('crud.messages.has_been_deleted', [
'model' => Str::ucfirst(trans_choice('models.classes.seasonal_section', 1))
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_deleted', [
'model' => Str::ucfirst(trans_choice('models.classes.seasonal_section', 1))
]));
}
/**
* Duplicate the specified resource.
*
* @param \App\Models\SeasonalSection $seasonalSection
* @return \Illuminate\Contracts\View\View
*/
public function duplicate(SeasonalSection $seasonalSection): \Illuminate\Contracts\View\View
{
return $this->create($seasonalSection->replicate());
}
}

View file

@ -0,0 +1,162 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\Stamps\StoreStampRequest;
use App\Http\Requests\Bo\Stamps\UpdateStampRequest;
use App\Models\Stamp;
use App\Traits\Controllers\UpdateModelPublished;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class StampController extends Controller
{
use UpdateModelPublished;
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(Stamp::class);
}
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/
public function index(Request $request): \Illuminate\Contracts\View\View
{
/** @var \Illuminate\Database\Eloquent\Builder $stampModels */
$stampModels = Stamp::query();
/** @var string $search Search field */
$search = $request->search;
if ($search) {
$this->searchQuery(
$stampModels,
$search,
null,
'picture_alt',
'picture_title',
);
}
$searchFields = \implode(', ', [
Str::ucfirst(trans('validation.custom.attribute_alt')),
trans('validation.custom.attribute_title'),
]);
/** Sort columns with a query */
$this->sortQuery($stampModels);
/** Custom pagination */
$stampModels = $this->paginate($stampModels);
return view('bo.pages.stamps.index', compact('stampModels', 'search', 'searchFields'));
}
/**
* Show the form for creating a new resource.
*
* @param \App\Models\Stamp $stamp
* @return \Illuminate\Contracts\View\View
*/
public function create(Stamp $stamp): \Illuminate\Contracts\View\View
{
return view('bo.pages.stamps.create', ['stampModel' => $stamp]);
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\Stamps\StoreStampRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(StoreStampRequest $request): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request) {
$stamp = new Stamp();
if ($stamp->fill($request->validated())->saveOrFail()) {
return redirect()->route('bo.stamps.edit', $stamp)
->with('success', __('crud.messages.has_been_created', [
'model' => Str::ucfirst(__('models.classes.stamp'))
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_created', [
'model' => Str::ucfirst(__('models.classes.stamp'))
]));
});
}
/**
* Show the form for editing the specified resource.
*
* @param App\Models\Stamp $stamp
* @return \Illuminate\Contracts\View\View
*/
public function edit(Stamp $stamp): \Illuminate\Contracts\View\View
{
return view('bo.pages.stamps.edit', ['stampModel' => $stamp]);
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\Stamps\UpdateStampRequest $request
* @param \App\Models\Stamp $stamp
* @return \Illuminate\Http\RedirectResponse
*/
public function update(UpdateStampRequest $request, Stamp $stamp): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request, $stamp) {
if ($stamp->fill($request->validated())->saveOrFail()) {
return redirect()->route('bo.stamps.edit', $stamp)
->with('success', __('crud.messages.has_been_updated', [
'model' => Str::ucfirst(__('models.classes.stamp'))
]));
}
return redirect()->route('bo.stamps.edit', $stamp)
->with('error', __('crud.messages.cannot_be_updated', [
'model' => Str::ucfirst(__('models.classes.stamp'))
]));
});
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\Stamp $stamp
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(Stamp $stamp): \Illuminate\Http\RedirectResponse
{
if ($stamp->deleteOrFail()) {
return redirect()->route('bo.stamps.index')
->with('success', __('crud.messages.has_been_deleted', [
'model' => Str::ucfirst(__('models.classes.stamp'))
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_deleted', [
'model' => Str::ucfirst(__('models.classes.stamp'))
]));
}
/**
* Duplicate the specified resource.
*
* @param \App\Models\Stamp $stamp
* @return \Illuminate\Contracts\View\View
*/
public function duplicate(Stamp $stamp): \Illuminate\Contracts\View\View
{
return $this->create($stamp->replicate());
}
}

View file

@ -0,0 +1,89 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\StaticPages\UpdateStaticPageRequest;
use App\Models\StaticPage;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class StaticPageController extends Controller
{
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(StaticPage::class);
}
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/
public function index(Request $request): \Illuminate\Contracts\View\View
{
/** @var \Illuminate\Database\Eloquent\Builder $query */
$query = StaticPage::query();
/** @var string $search Search field */
$search = $request->search;
if ($search) {
$this->searchQuery(
$query,
$search,
null,
'title',
);
}
$searchFields = \implode(', ', [
Str::ucfirst(trans('validation.attributes.title'))
]);
/** Sort columns with a query */
$this->sortQuery($query);
/** Custom pagination */
$staticPageModelList = $this->paginate($query);
return view('bo.pages.static_pages.index', compact('staticPageModelList', 'search', 'searchFields'));
}
/**
* Show the form for editing the specified resource.
*
* @param App\Models\StaticPage $static_page
* @return \Illuminate\Contracts\View\View
*/
public function edit(StaticPage $static_page): \Illuminate\Contracts\View\View
{
return view('bo.pages.static_pages.edit', ['staticPageModel' => $static_page]);
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\StaticPages\UpdateStaticPageRequest $request
* @param \App\Models\StaticPage $static_page
* @return \Illuminate\Http\RedirectResponse
*/
public function update(UpdateStaticPageRequest $request, StaticPage $static_page): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request, $static_page) {
if ($static_page->fill($request->validated())->saveOrFail()) {
return redirect()->route('bo.static_pages.edit', $static_page)
->with('success', __('crud.messages.has_been_updated', [
'model' => Str::ucfirst(\trans_choice('models.classes.static_page', 1))
]));
}
return redirect()->route('bo.static_pages.edit', $static_page)
->with('error', __('crud.messages.cannot_be_updated', [
'model' => Str::ucfirst(\trans_choice('models.classes.static_page', 1))
]));
});
}
}

View file

@ -0,0 +1,167 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\Testimonials\StoreTestimonialRequest;
use App\Http\Requests\Bo\Testimonials\UpdateTestimonialRequest;
use App\Models\Testimonial;
use App\Traits\Controllers\ChangesModelOrder;
use App\Traits\Controllers\UpdateModelPublished;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class TestimonialController extends Controller
{
use UpdateModelPublished;
use ChangesModelOrder;
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(Testimonial::class);
}
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/
public function index(Request $request): \Illuminate\Contracts\View\View
{
/** @var \Illuminate\Database\Eloquent\Builder $testimonialModels */
$testimonialModels = Testimonial::query();
/** @var string $search Search field */
$search = $request->search;
if ($search) {
$this->searchQuery(
$testimonialModels,
$search,
null,
'description',
'job'
);
}
$searchFields = \implode(', ', [
Str::ucfirst(trans('validation.custom.description')),
Str::ucfirst(trans('validation.custom.job'))
]);
/** Sort columns with a query */
$this->sortQuery($testimonialModels);
/** Custom pagination */
$testimonialModels = $this->paginate($testimonialModels);
return view('bo.pages.testimonials.index', compact('testimonialModels', 'search', 'searchFields'));
}
/**
* Show the form for creating a new resource.
*
* @param \App\Models\Testimonial $testimonial
* @return \Illuminate\Contracts\View\View
*/
public function create(Testimonial $testimonial): \Illuminate\Contracts\View\View
{
return view('bo.pages.testimonials.create', ['testimonialModel' => $testimonial]);
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\Testimonials\StoreTestimonialRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(StoreTestimonialRequest $request): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request) {
$testimonial = new Testimonial();
if ($testimonial->fill($request->validated())->saveOrFail()) {
return redirect()->route('bo.testimonials.edit', $testimonial)
->with('success', __('crud.messages.has_been_created', [
'model' => Str::ucfirst(__('models.classes.testimonial'))
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_created', [
'model' => Str::ucfirst(__('models.classes.testimonial'))
]));
});
}
/**
* Show the form for editing the specified resource.
*
* @param App\Models\Testimonial $testimonial
* @return \Illuminate\Contracts\View\View
*/
public function edit(Testimonial $testimonial): \Illuminate\Contracts\View\View
{
return view('bo.pages.testimonials.edit', ['testimonialModel' => $testimonial]);
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\Testimonials\UpdateTestimonialRequest $request
* @param \App\Models\Testimonial $testimonial
* @return \Illuminate\Http\RedirectResponse
*/
public function update(
UpdateTestimonialRequest $request,
Testimonial $testimonial
): \Illuminate\Http\RedirectResponse {
return DB::transaction(function () use ($request, $testimonial) {
if ($testimonial->fill($request->validated())->saveOrFail()) {
return redirect()->route('bo.testimonials.edit', $testimonial)
->with('success', __('crud.messages.has_been_updated', [
'model' => Str::ucfirst(__('models.classes.testimonial'))
]));
}
return redirect()->route('bo.testimonials.edit', $testimonial)
->with('error', __('crud.messages.cannot_be_updated', [
'model' => Str::ucfirst(__('models.classes.testimonial'))
]));
});
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\Testimonial $testimonial
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(Testimonial $testimonial): \Illuminate\Http\RedirectResponse
{
if ($testimonial->deleteOrFail()) {
return redirect()->route('bo.testimonials.index')
->with('success', __('crud.messages.has_been_deleted', [
'model' => Str::ucfirst(__('models.classes.testimonial'))
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_deleted', [
'model' => Str::ucfirst(__('models.classes.testimonial'))
]));
}
/**
* Duplicate the specified resource.
*
* @param \App\Models\Testimonial $testimonial
* @return \Illuminate\Contracts\View\View
*/
public function duplicate(Testimonial $testimonial): \Illuminate\Contracts\View\View
{
return $this->create($testimonial->replicate());
}
}

View file

@ -0,0 +1,166 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\Users\StoreUserRequest;
use App\Http\Requests\Bo\Users\UpdateUserRequest;
use App\Models\User;
use App\Traits\Controllers\UpdateModelPublished;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class UsersController extends Controller
{
use UpdateModelPublished;
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(User::class);
}
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/
public function index(Request $request): \Illuminate\Contracts\View\View
{
/** @var \Illuminate\Database\Eloquent\Builder $userModels */
$userModels = User::query();
/** @var string $search Search field */
$search = $request->search;
if ($search) {
$this->searchQuery(
$userModels,
$search,
null,
'first_name',
'last_name',
'email',
'role',
);
}
$searchFields = \implode(', ', [
trans('validation.attributes.first_name'),
trans('validation.attributes.last_name'),
trans('validation.attributes.email'),
trans('validation.attributes.role'),
]);
/** Sort columns with a query */
$this->sortQuery($userModels);
/** Custom pagination */
$userModels = $this->paginate($userModels);
return view('bo.pages.users.index', compact('userModels', 'search', 'searchFields'));
}
/**
* Show the form for creating a new resource.
*
* @param \App\Models\User $user
* @return \Illuminate\Contracts\View\View
*/
public function create(User $user): \Illuminate\Contracts\View\View
{
return view('bo.pages.users.create', ['userModel' => $user]);
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\Users\StoreUserRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(StoreUserRequest $request): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request) {
$user = new User();
if ($user->fill($request->validated())->saveOrFail()) {
return redirect()->route('bo.users.edit', $user)
->with('success', __('crud.messages.has_been_created', [
'model' => Str::ucfirst(__('models.classes.user'))
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_created', [
'model' => Str::ucfirst(__('models.classes.user'))
]));
});
}
/**
* Show the form for editing the specified resource.
*
* @param App\Models\User $user
* @return \Illuminate\Contracts\View\View
*/
public function edit(User $user): \Illuminate\Contracts\View\View
{
return view('bo.pages.users.edit', ['userModel' => $user]);
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\Users\UpdateUserRequest $request
* @param \App\Models\User $user
* @return \Illuminate\Http\RedirectResponse
*/
public function update(UpdateUserRequest $request, User $user): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request, $user) {
if ($user->fill($request->validated())->saveOrFail()) {
return redirect()->route('bo.users.edit', $user)
->with('success', __('crud.messages.has_been_updated', [
'model' => Str::ucfirst(__('models.classes.user'))
]));
}
return redirect()->route('bo.users.edit', $user)
->with('error', __('crud.messages.cannot_be_updated', [
'model' => Str::ucfirst(__('models.classes.user'))
]));
});
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\User $user
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(User $user): \Illuminate\Http\RedirectResponse
{
if ($user->deleteOrFail()) {
return redirect()->route('bo.users.index')
->with('success', __('crud.messages.has_been_deleted', [
'model' => Str::ucfirst(__('models.classes.user'))
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_deleted', [
'model' => Str::ucfirst(__('models.classes.user'))
]));
}
/**
* Duplicate the specified resource.
*
* @param \App\Models\User $user
* @return \Illuminate\Contracts\View\View
*/
public function duplicate(User $user): \Illuminate\Contracts\View\View
{
return $this->create($user->replicate());
}
}

View file

@ -0,0 +1,231 @@
<?php
namespace App\Http\Controllers;
use App\Enums\Pagination\ItemsPerPaginationEnum;
use App\Enums\Theme\BootstrapThemeEnum;
use App\Lib\Helpers\ToolboxHelper;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Str;
class Controller extends BaseController
{
use AuthorizesRequests;
use ValidatesRequests;
/**
* Build Search query on specified fields
* splitting search into words or using whole sentence
*
* @param \Illuminate\Database\Eloquent\Builder $query The eloquent query builder.
* @param string $search The query string.
* @param callable $searchQuery The query string.
* @param string ...$fields Either a simple array or a string
* in case of a string the two fields
* would be concatenate to make the search.
* @return \Illuminate\Database\Eloquent\Builder
*/
protected function searchQuery(
Builder $query,
string $search,
callable $searchQuery = null,
string ...$fields
): \Illuminate\Database\Eloquent\Builder {
if (count($fields)) {
// Search using words.
$words = explode(' ', $search);
$query->where(function ($query) use ($searchQuery, $words, $search, $fields) {
foreach ($words as $word) {
if (!strlen($word)) {
continue;
}
$this->searchOnFields($query, $word, $fields);
} //end foreach
// Search whole using whole sentence.
$this->searchOnFields($query, $search, $fields);
if ($searchQuery) {
$searchQuery($query);
}
});
} //end if
return $query;
}
/**
* Build query search on all fields
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param string $search
* @param array $fields
* @return \Illuminate\Database\Eloquent\Builder
*/
private function searchOnFields(
Builder $query,
string $search,
array $fields
): \Illuminate\Database\Eloquent\Builder {
$table = $query->getModel()->getTable();
foreach ($fields as $field) {
$field = str_replace('?', '\?', $field);
if (is_array($field)) {
$query->orWhereRaw(
sprintf("UPPER(CONCAT(%s)) LIKE UPPER(?)", collect($field)->map(function ($q) use ($table) {
return "`$table`.`$q`";
})->implode(', \' \', ')),
['%' . htmlspecialchars($search) . '%']
);
return $query;
}
// Get models woth enum's label.
if (is_array($this->getSearchValue($query, $search, $field))) {
foreach ($this->getSearchValue($query, $search, $field) as $array) {
$query->orWhereRaw(
"UPPER(`$table`.`$field`) LIKE UPPER(?)",
['%' . htmlspecialchars($array->value) . '%']
);
}
return $query;
}
// Get models with relation's name/label.
if (Str::endsWith($field, '_id') && $query->getModel()->has(Str::remove('_id', $field))) {
$query->orWhereHas(Str::remove('_id', $field), function ($queryRelation) use ($field, $search) {
if (Schema::hasColumn(Str::remove('_id', $field) . 's', 'name')) {
$queryRelation->where('name', 'like', '%' . $search . '%');
} elseif (Schema::hasColumn(Str::remove('_id', $field) . 's', 'label')) {
$queryRelation->where('label', 'like', '%' . $search . '%');
}
});
return $query;
}
$query->orWhereRaw(
"UPPER(`$table`.`$field`) LIKE UPPER(?)",
['%' . htmlspecialchars($this->getSearchValue($query, $search, $field)) . '%']
);
} //end foreach
return $query;
}
/**
* Get the search value (on string or Enum).
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param string $search
* @param string $field
* @return string|array
*/
protected function getSearchValue(Builder $query, string $search, string $field): string|array
{
if (
count($query->getModel()->getCasts()) and // Check if the model has casts.
isset($query->getModel()->getCasts()[$field]) and // Check if there is cast about specific field.
enum_exists($query->getModel()->getCasts()[$field]) // Check if the cast is an enum.
) {
$cases = collect($query->getModel()->getCasts()[$field]::toArray());
$enum = $cases->filter(function ($item) use ($search) {
return preg_match('/' . Str::lower($search) . '/', Str::lower($item->label));
});
return $enum->toArray();
} else {
return $search;
}
}
/**
* Sort columns with a query.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param array $ignore
* @return \Illuminate\Database\Eloquent\Builder
*/
protected function sortQuery(Builder $query, array $ignore = []): \Illuminate\Database\Eloquent\Builder
{
$rName = request()->route()->getName();
$table = $query->getModel()->getTable();
if (request()->sort_col) {
Session::put("$rName.sort_col", request()->sort_col);
Session::put("$rName.sorted", true);
}
if (request()->sort_way) {
Session::put("$rName.sort_way", request()->sort_way);
Session::put("$rName.sorted", true);
}
if (request()->rst) {
Session::remove("$rName.sorted");
Session::remove("$rName.sort_col");
Session::remove("$rName.sort_way");
}
if (request()->sort_col and request()->sort_way and Schema::hasColumn($table, request()->sort_col)) {
if (
!isset($ignore) or
(isset($ignore) and !in_array(request()->sort_col, $ignore))
) {
$query = $query->orderBy(\sprintf("$table.%s", request()->sort_col), request()->sort_way);
}
} elseif (Schema::hasColumn($table, 'order')) {
$query = $query->orderBy('order', 'ASC');
Session::put("$rName.sort_col", 'order');
Session::put("$rName.sort_way", 'asc');
} elseif (Schema::hasColumn($table, 'updated_at')) {
$query = $query->orderBy('updated_at', 'DESC');
Session::put("$rName.sort_col", 'updated_at');
Session::put("$rName.sort_way", 'desc');
} elseif (Schema::hasColumn($table, 'created_at')) {
$query = $query->orderBy('created_at', 'DESC');
Session::put("$rName.sort_col", 'created_at');
Session::put("$rName.sort_way", 'desc');
}
return $query;
}
/**
* Customize pagination with cache or config (default).
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param \App\Enums\Pagination\ItemsPerPaginationEnum|null $pagination
* @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
*/
protected function paginate(
Builder $query,
ItemsPerPaginationEnum $pagination = null
): \Illuminate\Contracts\Pagination\LengthAwarePaginator {
$currentRoutePath = Str::slug(request()->route()->getName());
$cacheKey = "pagination.{$currentRoutePath}";
$pagination = $pagination ?? ItemsPerPaginationEnum::twelve;
$pagination = ToolboxHelper::getValidatedEnum(
Cache::get($cacheKey, $pagination->value()),
'pagination',
'\App\Enums\Pagination\ItemsPerPaginationEnum',
);
if (Cache::get($cacheKey) !== $pagination) {
Cache::put($cacheKey, $pagination);
}
$query = $query->paginate($pagination);
return $query;
}
/**
* Set the bootstrap theme or light (default).
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*/
protected function setTheme(Request $request): \Illuminate\Http\RedirectResponse
{
$theme = ToolboxHelper::getValidatedEnum(
$request->theme ?: Cache::get('theme', BootstrapThemeEnum::light->value()),
'theme',
'\App\Http\Controllers\BootstrapThemeEnum',
);
if (Cache::get("theme") !== $theme) {
Cache::put("theme", $theme);
}
return redirect()->back()->with('success', __('crud.messages.theme_updated'));
}
}

View file

@ -0,0 +1,68 @@
<?php
namespace App\Http\Controllers\Fo;
use App\Enums\StaticPageType;
use App\Http\Controllers\Controller;
use App\Lib\Helpers\CollectionHelper;
use App\Lib\Helpers\ToolboxHelper;
use App\Models\News;
use App\Models\StaticPage;
use Illuminate\Contracts\Database\Query\Builder;
class NewsController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Contracts\View\View
*/
public function index(): \Illuminate\Contracts\View\View
{
/** @var \App\Models\StaticPage $staticPageModel */
$staticPageModel = StaticPage::query()->where('type', StaticPageType::news)->firstOrFail();
/** @var \App\Models\News $newsModels */
$newsModels = News::query()->where('published', true)->orderBy('publish_at', 'desc')->paginate(12);
return view('fo.pages.news.index', \compact('staticPageModel', 'newsModels'));
}
/**
* Display a news articles
*
* @param string $slug
* @return \Illuminate\Contracts\View\View
*/
public function show(string $slug): \Illuminate\Contracts\View\View
{
/** @var \App\Models\News $newsModel */
$newsModel = News::query()
->where('published', true)
->where('slug', $slug)
->firstOrFail();
return view('fo.pages.news.show', \compact('newsModel'));
}
/**
* Get filtered news.
*
* @return \Illuminate\Http\JsonResponse
*/
public function newsFiltered(): \Illuminate\Http\JsonResponse
{
$categoryId = ToolboxHelper::getValidatedEnum(
0,
'category',
'\App\Enums\News\CategoryEnum',
);
$newsModels = News::query()
->where('published', true)
->when($categoryId != 0, function (Builder $query) use ($categoryId) {
$query->where('category', $categoryId);
})
->orderBy('publish_at', 'desc')
->get();
$newsModels = CollectionHelper::paginateManually($newsModels, 12, request()->page);
return response()->json($newsModels);
}
}

View file

@ -0,0 +1,25 @@
<?php
namespace App\Http\Controllers\Fo;
use App\Http\Controllers\Controller;
use App\Models\Product;
class ProductController extends Controller
{
/**
* Display the product page.
*
* @param \App\Models\Product $product
* @return \Illuminate\Contracts\View\View
*/
public function show(Product $product): \Illuminate\Contracts\View\View
{
$otherProductModels = $product->range->products()
->where('id', '!=', $product->id)
->where('published', true)
->inRandomOrder()
->get();
return view('fo.pages.products.show', compact('product', 'otherProductModels'));
}
}

View file

@ -0,0 +1,45 @@
<?php
namespace App\Http\Controllers\Fo;
use App\Enums\StaticPageType;
use App\Http\Controllers\Controller;
use App\Models\Product;
use App\Models\Range;
use App\Models\StaticPage;
class RangeController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Contracts\View\View
*/
public function index(): \Illuminate\Contracts\View\View
{
/** @var \App\Models\Range $rangeModels */
$rangeModels = Range::query()->where('published', true)->orderBy('order')->get();
/** @var \App\Models\StaticPage $staticPageModel */
$staticPageModel = StaticPage::query()->where('type', StaticPageType::ranges)->firstOrFail();
return view('fo.pages.ranges.index', compact('rangeModels', 'staticPageModel'));
}
/**
* Show a specific resource.
*
* @param string $slug
* @return \Illuminate\Contracts\View\View
*/
public function show(string $slug): \Illuminate\Contracts\View\View
{
/** @var \App\Models\Range $rangeModel */
$rangeModel = Range::query()->where('published', true)->where('slug', $slug)->firstOrFail();
/** @var \App\Models\Product $productModels */
$productModels = Product::query()->where('range_id', $rangeModel->id)->where('published', true)->get();
return view('fo.pages.ranges.show', compact('rangeModel', 'productModels'));
}
}

View file

@ -0,0 +1,195 @@
<?php
namespace App\Http\Controllers\Fo;
use App\Enums\StaticPageType;
use App\Http\Controllers\Controller;
use App\Http\Requests\Fo\ContactRequest;
use App\Http\Requests\Fo\JoinusRequest;
use App\Mail\SendContactMail;
use App\Mail\SendJoinusMail;
use App\Models\Convictionpage;
use App\Models\Explorerpage;
use App\Models\Homepage;
use App\Models\Joinuspage;
use App\Models\News;
use App\Models\Range;
use App\Models\SeasonalSection;
use App\Models\StaticPage;
use App\Models\Testimonial;
use Illuminate\Support\Facades\Mail;
class StaticPageController extends Controller
{
/**
* About us show
*
* @param \App\Models\StaticPage $aboutUsPage
* @return \Illuminate\Contracts\View\View
*/
public function aboutUs(StaticPage $aboutUsPage)
{
$aboutUsPage = StaticPage::query()->where('type', StaticPageType::aboutus)
->get();
return view('fo.pages.about-us', ['staticPageModel' => $aboutUsPage[0]]);
}
/**
* Legal show
*
* @param \App\Models\StaticPage $legalPage
* @return \Illuminate\Contracts\View\View
*/
public function legals(StaticPage $legalPage)
{
$legalPage = StaticPage::query()->where('type', StaticPageType::legals)->get();
return view('fo.pages.legals', ['staticPageModel' => $legalPage[0]]);
}
/**
* Policies show
*
* @param \App\Models\StaticPage $policiesPage
* @return \Illuminate\Contracts\View\View
*/
public function policies(StaticPage $policiesPage)
{
$policiesPage = StaticPage::query()->where('type', StaticPageType::confidentiality)->get();
return view('fo.pages.legals', ['staticPageModel' => $policiesPage[0]]);
}
/**
* Display a listing of the resource.
*
* @return \Illuminate\Contracts\View\View
*/
public function homepage(): \Illuminate\Contracts\View\View
{
/** @var \App\Models\Homepage $homepageModel */
$homepageModel = Homepage::query()->firstOrFail();
/** @var \App\Models\StaticPage $staticPageModel */
$staticPageModel = StaticPage::query()->where('type', StaticPageType::home)->firstOrFail();
/** @var \App\Models\SeasonalSection $seasonalSectionsModel */
$seasonalSectionsModel = SeasonalSection::query()->where('published', true)->first();
/** @var \App\Models\News $newsModels */
$newsModels = News::query()->where('published', true)->latest('publish_at')->limit(2)->get();
/** @var \App\Models\Range $rangeModels */
$rangeModels = Range::query()->where('published', true)->orderBy('order')->get();
return view('fo.pages.home', compact(
'homepageModel',
'staticPageModel',
'seasonalSectionsModel',
'newsModels',
'rangeModels',
));
}
/**
* Display a listing of the resource.
*
* @return \Illuminate\Contracts\View\View
*/
public function contact(): \Illuminate\Contracts\View\View
{
/** @var \App\Models\StaticPage $contactModel */
$contactModel = StaticPage::query()->where('type', StaticPageType::contact)->firstOrFail();
return view('fo.pages.contact', compact('contactModel'));
}
/**
* Display a listing of the resource.
*
* @param \App\Http\Requests\Fo\ContactRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function contactForm(ContactRequest $request): \Illuminate\Http\RedirectResponse
{
Mail::to(\config('mail.from.address'))->send(new SendContactMail((object)$request->validated()));
return redirect()->route('fo.contact.show')
->with(
'success',
'Bonjour, votre message a bien été transmis. nous vous en remercions. Cordialement, l’équipe Agis.'
);
}
/**
* Display the explorer and gastronome page.
*
* @return \Illuminate\Contracts\View\View
*/
public function explorer(): \Illuminate\Contracts\View\View
{
/** @var \App\Models\Explorerpage $explorerpageModel */
$explorerpageModel = Explorerpage::query()->firstOrFail();
/** @var \App\Models\StaticPage $staticPageModel */
$staticPageModel = StaticPage::query()->where('type', StaticPageType::explorer)->firstOrFail();
return view('fo.pages.explorer-and-gastronome', compact(
'explorerpageModel',
'staticPageModel',
));
}
/**
* Display the convictions page.
*
* @return \Illuminate\Contracts\View\View
*/
public function convictions(): \Illuminate\Contracts\View\View
{
/** @var \App\Models\Convictionpage $convictionpageModel */
$convictionpageModel = Convictionpage::query()->firstOrFail();
/** @var \App\Models\StaticPage $staticPageModel */
$staticPageModel = StaticPage::query()->where('type', StaticPageType::conviction)->firstOrFail();
return view('fo.pages.convictions', compact(
'convictionpageModel',
'staticPageModel',
));
}
/**
* Display the explorer and gastonome page.
*
* @return \Illuminate\Contracts\View\View
*/
public function joinus(): \Illuminate\Contracts\View\View
{
/** @var \App\Models\StaticPage $staticPageModel */
$staticPageModel = StaticPage::query()->where('type', StaticPageType::joinus)->firstOrFail();
/** @var \App\Models\Joinuspage $joinuspageModel */
$joinuspageModel = Joinuspage::query()->firstOrFail();
/** @var \App\Models\Testimonial $testimonialModels */
$testimonialModels = Testimonial::query()->get();
return view('fo.pages.join-us', compact('staticPageModel', 'joinuspageModel', 'testimonialModels'));
}
/**
* Display a listing of the resource.
*
* @param \App\Http\Requests\Fo\JoinusRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function joinusForm(JoinusRequest $request): \Illuminate\Http\RedirectResponse
{
Mail::to(\config('mail.from.address'))->send(new SendJoinusMail((object)$request->validated()));
return redirect()->route('fo.joinus.show')
->with(
'success',
'Bonjour, votre candidature a bien été transmise. nous vous en remercions. Cordialement, l’équipe Agis.'
);
}
}

69
app/Http/Kernel.php Normal file
View file

@ -0,0 +1,69 @@
<?php
namespace App\Http;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel
{
/**
* The application's global HTTP middleware stack.
*
* These middleware are run during every request to your application.
*
* @var array<int, class-string|string>
*/
protected $middleware = [
\App\Http\Middleware\TrustHosts::class,
\App\Http\Middleware\TrustProxies::class,
\Illuminate\Http\Middleware\HandleCors::class,
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
];
/**
* The application's route middleware groups.
*
* @var array<string, array<int, class-string|string>>
*/
protected $middlewareGroups = [
'web' => [
'htmlsanitize',
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\SecureHeaders::class
],
'api' => [
\Illuminate\Routing\Middleware\ThrottleRequests::class . ':api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
/**
* The application's route middleware.
*
* These middleware may be assigned to groups or used individually.
*
* @var array<string, class-string|string>
*/
protected $middlewareAliases = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'signed' => \App\Http\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
'htmlsanitize' => \App\Http\Middleware\HtmlSanitizer::class,
];
}

View file

@ -0,0 +1,39 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Session;
class Authenticate extends Middleware
{
/**
* Get the path the user should be redirected to when they are not authenticated.
*
* @param \Illuminate\Http\Request $request
* @return string|null
*/
protected function redirectTo(Request $request): ?string
{
$logins = [
'backend' => route('bo.login')
];
if (!$request->expectsJson()) {
// * Notify must login
Session::flash('warning', trans('Veuillez vous connecter pour continuer'));
foreach ($request->route()->gatherMiddleware() as $middleware) {
if (\is_string($middleware) and strpos($middleware, 'auth:') !== false) {
$route = $logins[str_replace('auth:', '', $middleware)] ?? null;
if ($route) {
return $route;
}
}
}
return route('login');
}
return null;
}
}

View file

@ -0,0 +1,15 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
class EncryptCookies extends Middleware
{
/**
* The names of the cookies that should not be encrypted.
*
* @var array<int, string>
*/
protected $except = [];
}

View file

@ -0,0 +1,61 @@
<?php
namespace App\Http\Middleware;
use App\Lib\Helpers\ToolboxHelper;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class HtmlSanitizer
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param string|array ...$params
* @return \Symfony\Component\HttpFoundation\Response
*/
public function handle(Request $request, \Closure $next, string|array ...$params): Response
{
$inputs = $request->all();
/** @var callable|null */
$cleanInput = null;
$cleanInput = function (
null|string|array &$input,
string $name,
?string $rootName = null
) use (
$params,
&$cleanInput
): void {
// * Nested element array
if (is_array($input)) {
array_walk_recursive($input, $cleanInput, $name);
}
if (!is_string($input)) {
return;
}
if (in_array($name, $params) or in_array($rootName ?? $name, $params)) {
// ? Is a json input ?
if ($isJson = \json_decode($input, true) and is_array($isJson)) {
// ! Decode each json field recursive.
\array_walk_recursive($isJson, function (string|array &$item) {
if (\is_string($item)) {
$item = clean($item);
$item = ToolboxHelper::stripCkEditorSchemesOnMediaInputs($item);
}
});
} else {
$input = clean($input);
$input = ToolboxHelper::stripCkEditorSchemesOnMediaInputs($input);
}
} else {
$input = strip_tags($input);
}
};
\array_walk($inputs, $cleanInput);
$request->merge($inputs);
return $next($request);
}
}

View file

@ -0,0 +1,15 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance as Middleware;
class PreventRequestsDuringMaintenance extends Middleware
{
/**
* The URIs that should be reachable while maintenance mode is enabled.
*
* @var array<int, string>
*/
protected $except = [];
}

View file

@ -0,0 +1,43 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class RedirectIfAuthenticated
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
* @param string|null ...$guards
* @return \Symfony\Component\HttpFoundation\Response
* @throws \RuntimeException If guard is unhandled.
* @phpcs:disable Squiz.Commenting.FunctionComment.IncorrectTypeHint
*/
public function handle(Request $request, \Closure $next, ...$guards): Response
{
// phpcs:enable phpcs:disable
$guards = empty($guards) ? [null] : $guards;
foreach ($guards as $guard) {
if (auth($guard)->check()) {
switch ($guard) {
case 'frontend':
return \redirect()->route('fo.homepage');
case 'backend':
if (!$request->routeIs('bo.*')) {
continue;
}
return \redirect()->route('bo.dashboard');
default:
throw new \RuntimeException("Unhandled guard `{$guard}` .");
}
}
}
return $next($request);
}
}

View file

@ -0,0 +1,124 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\View;
use Illuminate\Support\Facades\Vite;
use Symfony\Component\HttpFoundation\Response;
class SecureHeaders
{
/**
* Enumerate headers which you do not want in your application's responses.
* Great starting point would be to go check out @Scott_Helme's:
* https://securityheaders.com/
*
* @var array
*/
private $unwantedHeaderList = [
'X-Powered-By',
'Server',
];
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
* @return \Symfony\Component\HttpFoundation\Response
* @phpcs:disable Squiz.Commenting.FunctionComment.IncorrectTypeHint
*/
public function handle(Request $request, Closure $next): Response
{
// phpcs:enable
// * For Vite DEV
$host = \parse_url(\config('app.url'), \PHP_URL_HOST);
View::share('nonce', $nonce = Vite::useCspNonce());
// * For VueJS dev tool to execute
$jsDev = config('app.debug') ? "'unsafe-eval' 'unsafe-inline'" : "'nonce-{$nonce}' 'strict-dynamic'";
$scheme = config('app.env') === 'local' ? 'http' : 'https';
$reportUri = Route::has('cspReportUri') ?
\sprintf(' report-uri %s ;', \parse_url(route('cspReportUri'), \PHP_URL_PATH)) : '';
$this->removeUnwantedHeaders($this->unwantedHeaderList);
$response = $next($request);
$response->headers->set('Referrer-Policy', 'no-referrer-when-downgrade');
$response->headers->set('X-Content-Type-Options', 'nosniff');
$response->headers->set('X-XSS-Protection', '1; mode=block');
$response->headers->set('X-Frame-Options', 'SAMEORIGIN');
$response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains;');
$cspPolicies = [
"default-src 'none';",
"manifest-src 'self';",
// * CKEDITOR Specific https://ckeditor.com/docs/ckeditor5/latest/installation/advanced/csp.html
"connect-src 'self' " . implode(' ', [
// * VITE DEV
config('app.debug') ? "wss://{$host}:4443" : '',
config('app.debug') ? "https://{$host}:4443" : '',
'https://yl.s-web.fr',
]) . ';',
"img-src * https://yl.s-web.fr data: blob:;",
'frame-src ' . implode(' ', [
'https://www.google.com/recaptcha/',
'https://recaptcha.google.com/recaptcha/',
'https://www.google.com/maps/',
'https://www.youtube.com',
]) . ';',
"font-src 'self' data: " . \implode(' ', [
// * VITE DEV
config('app.debug') ? "https://{$host}:4443" : '',
'https://fonts.gstatic.com;'
]) . ';',
// ! Using CKEditor will must have unsafe-inline style
// phpcs:ignore Generic.Files.LineLength.TooLong
"style-src 'self' 'unsafe-inline' " . \implode(' ', [
'https://cdnjs.cloudflare.com',
// * File Manager
'https://fonts.googleapis.com',
'https://cdn.jsdelivr.net',
'https://use.fontawesome.com',
'https://fonts.googleapis.com',
// * VITE DEV
config('app.debug') ? "https://{$host}:4443" : '',
]) . ';',
// ! If you dont use ckeditor use this
// "style-src 'self' 'nonce-$nonce';",
// phpcs:ignore Generic.Files.LineLength.TooLong
"script-src 'self' {$jsDev} " . \implode(' ', [
'https: blob:',
'https://www.googletagmanager.com',
'https://cdnjs.cloudflare.com',
"$scheme://www.google-analytics.com",
'https://www.google.com/recaptcha/',
'https://www.gstatic.com/recaptcha/',
// * VITE DEV
config('app.debug') ? "https://{$host}:4443" : '',
]) . ';'
];
$response->headers->set(
'Content-Security-Policy',
implode(' ', $cspPolicies) . $reportUri
);
$response->headers->set('Feature-Policy', \implode(',', [
'fullscreen', 'accelerometer', 'encrypted-media',
'gyroscope', 'picture-in-picture'
]) . ';');
return $response;
}
/**
* Remove unwanted headers
*
* @param array $headerList
* @return void
*/
private function removeUnwantedHeaders(array $headerList): void
{
foreach ($headerList as $header) {
header_remove($header);
}
}
}

View file

@ -0,0 +1,19 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
class TrimStrings extends Middleware
{
/**
* The names of the attributes that should not be trimmed.
*
* @var array<int, string>
*/
protected $except = [
'current_password',
'password',
'password_confirmation',
];
}

View file

@ -0,0 +1,20 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Middleware\TrustHosts as Middleware;
class TrustHosts extends Middleware
{
/**
* Get the host patterns that should be trusted.
*
* @return array<int, string|null>
*/
public function hosts()
{
return [
$this->allSubdomainsOfApplicationUrl(),
];
}
}

View file

@ -0,0 +1,28 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Middleware\TrustProxies as Middleware;
use Illuminate\Http\Request;
class TrustProxies extends Middleware
{
/**
* The trusted proxies for this application.
*
* @var array<int, string>|string|null
*/
protected $proxies;
/**
* The headers that should be used to detect proxies.
*
* @var integer
*/
protected $headers =
Request::HEADER_X_FORWARDED_FOR |
Request::HEADER_X_FORWARDED_HOST |
Request::HEADER_X_FORWARDED_PORT |
Request::HEADER_X_FORWARDED_PROTO |
Request::HEADER_X_FORWARDED_AWS_ELB;
}

View file

@ -0,0 +1,15 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Routing\Middleware\ValidateSignature as Middleware;
class ValidateSignature extends Middleware
{
/**
* The names of the query string parameters that should be ignored.
*
* @var array<int, string>
*/
protected $except = [];
}

View file

@ -0,0 +1,17 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
class VerifyCsrfToken extends Middleware
{
/**
* The URIs that should be excluded from CSRF verification.
*
* @var array<int, string>
*/
protected $except = [
'/csp-report'
];
}

View file

@ -0,0 +1,68 @@
<?php
namespace App\Http\Requests\Bo\Commitments;
use App\Traits\Requests\HasPicture;
use Illuminate\Foundation\Http\FormRequest;
class StoreCommitmentRequest extends FormRequest
{
use HasPicture;
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth('backend')->check();
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
$this->merge([
'published' => $this->published ? true : false
]);
$this->mergePicture('picture');
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
$rules = [
'title' => 'required|string|min:3|max:255',
'picture_alt' => 'required|string|min:3|max:255',
'picture_title' => 'required|string|min:3|max:255',
'published' => 'required|boolean',
];
return \array_merge(
$rules,
$this->pictureRules('picture', 185, 185, 185, 185, false),
);
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'title' => trans('validation.attributes.title'),
'picture_alt' => trans('validation.custom.picture_alt', ['field' => __('validation.custom.picture')]),
'picture_title' => trans('validation.custom.picture_title', ['field' => __('validation.custom.picture')]),
'published' => trans('validation.attributes.published'),
];
}
}

View file

@ -0,0 +1,7 @@
<?php
namespace App\Http\Requests\Bo\Commitments;
class UpdateCommitmentRequest extends StoreCommitmentRequest
{
}

View file

@ -0,0 +1,59 @@
<?php
namespace App\Http\Requests\Bo\Convictionpages;
use App\Traits\Requests\HasPicture;
use Illuminate\Foundation\Http\FormRequest;
class UpdateConvictionpageRequest extends FormRequest
{
use HasPicture;
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth('backend')->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'key_feed' => 'required|numeric',
'key_earth_1' => 'required|numeric',
'key_earth_2' => 'required|numeric',
'key_together_1' => 'required|numeric',
'key_together_2' => 'required|numeric',
'key_together_3' => 'required|numeric',
'key_together_4' => 'required|numeric',
'key_territory' => 'required|numeric',
];
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'key_feed' => trans('validation.custom.key_feed'),
'key_earth_1' => trans('validation.custom.key_earth_1'),
'key_earth_2' => trans('validation.custom.key_earth_2'),
'key_together_1' => trans('validation.custom.key_together_1'),
'key_together_2' => trans('validation.custom.key_together_2'),
'key_together_3' => trans('validation.custom.key_together_3'),
'key_together_4' => trans('validation.custom.key_together_4'),
'key_territory' => trans('validation.custom.key_territory'),
];
}
}

View file

@ -0,0 +1,91 @@
<?php
namespace App\Http\Requests\Bo\Explorerpage;
use App\Traits\Requests\HasPicture;
use Illuminate\Foundation\Http\FormRequest;
class UpdateExplorerpageRequest extends FormRequest
{
use HasPicture;
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth('backend')->check();
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
$this->mergePicture('picture_1');
$this->mergePicture('picture_2');
$this->mergePicture('picture_3');
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
$rules = [
'picture_1_alt' => 'required|string|between:3,255',
'picture_1_title' => 'required|string|between:3,255',
'picture_2_alt' => 'required|string|between:3,255',
'picture_2_title' => 'required|string|between:3,255',
'picture_3_alt' => 'required|string|between:3,255',
'picture_3_title' => 'required|string|between:3,255',
'commitments' => 'required|array',
'commitments.*.id' => 'required|exists:commitments,id',
];
return \array_merge(
$rules,
$this->pictureRules('picture_1', 265, 364, 265, 364, false),
$this->pictureRules('picture_2', 265, 364, 265, 364, false),
$this->pictureRules('picture_3', 265, 364, 265, 364, false),
);
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'picture_1_alt' => trans('validation.custom.picture_alt', [
'field' => __('validation.custom.picture_1')
]),
'picture_1_title' => trans('validation.custom.picture_title', [
'field' => __('validation.custom.picture_1')
]),
'picture_2_alt' => trans('validation.custom.picture_alt', [
'field' => __('validation.custom.picture_2')
]),
'picture_2_title' => trans('validation.custom.picture_title', [
'field' => __('validation.custom.picture_2')
]),
'picture_3_alt' => trans('validation.custom.picture_alt', [
'field' => __('validation.custom.picture_3')
]),
'picture_3_title' => trans('validation.custom.picture_title', [
'field' => __('validation.custom.picture_3')
]),
'commitments' => trans('validation.custom.commitments'),
'commitments.*.id' => trans('validation.custom.commitments'),
];
}
}

View file

@ -0,0 +1,75 @@
<?php
namespace App\Http\Requests\Bo\Homepage;
use App\Traits\Requests\HasPicture;
use Illuminate\Foundation\Http\FormRequest;
class UpdateHomepageRequest extends FormRequest
{
use HasPicture;
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth('backend')->check();
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
$this->mergePicture('picture');
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
$rules = [
'title_part1' => 'required|string|between:3,255',
'title_part2' => 'required|string|between:3,255',
'title_part3' => 'required|string|between:3,255',
'picture_alt' => 'required|string|between:3,255',
'picture_title' => 'required|string|between:3,255',
'presentation_title' => 'required|string|between:3,255',
'presentation_content' => 'required|string',
];
return \array_merge(
$rules,
$this->pictureRules('picture', 640, 390, 640, 390, false),
);
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'title_part1' => trans('validation.custom.title_part1'),
'title_part2' => trans('validation.custom.title_part2'),
'title_part3' => trans('validation.custom.title_part3'),
'picture_alt' => trans('validation.custom.picture_alt', [
'field' => __('validation.custom.picture')
]),
'picture_title' => trans('validation.custom.picture_title', [
'field' => __('validation.custom.picture')
]),
'presentation_title' => trans('validation.custom.presentation_title'),
'presentation_content' => trans('validation.custom.presentation_content'),
];
}
}

View file

@ -0,0 +1,42 @@
<?php
namespace App\Http\Requests\Bo\Joinuspage;
use Illuminate\Foundation\Http\FormRequest;
class UpdateJoinuspageRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth('backend')->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'video_id' => 'required|string|regex:%([^"&?\/\s]{11})%i',
];
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'video_id' => trans('validation.custom.video_id'),
];
}
}

View file

@ -0,0 +1,91 @@
<?php
namespace App\Http\Requests\Bo\News;
use App\Enums\News\CategoryEnum;
use App\Traits\Requests\HasPicture;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rules\Enum;
class StoreNewsRequest extends FormRequest
{
use HasPicture;
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth('backend')->check();
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
$this->merge([
'published' => $this->published ? true : false
]);
$this->mergePicture('picture');
$this->mergePicture('picture_thumbnail');
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
$rules = [
'seo_title' => 'required|between:45,70',
'seo_description' => 'required|between:50,160',
'title' => 'required|string|between:3,100',
'category' => ['required', new Enum(CategoryEnum::class)],
'picture_alt' => 'required|string|between:3,255',
'picture_title' => 'required|string|between:3,255',
'picture_thumbnail_alt' => 'required|string|between:3,255',
'picture_thumbnail_title' => 'required|string|between:3,255',
'subtitle' => 'required|string|between:3,255',
'content' => 'required|string|min:15',
'published' => 'required|boolean',
'publish_at' => 'nullable|date',
'unpublish_at' => 'nullable|date|after:publish_at',
];
return \array_merge(
$rules,
$this->pictureRules('picture', 1002, 565, 1002, 565, false),
$this->pictureRules('picture_thumbnail', 265, 354, 265, 354, false),
);
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'seo_title' => trans('validation.custom.seo_title'),
'seo_description' => trans('validation.custom.seo_description'),
'title' => trans('validation.attributes.title'),
'category' => trans('validation.custom.category'),
'subtitle' => trans('validation.custom.subtitle'),
'content' => trans('validation.attributes.content'),
'picture_alt' => trans('validation.custom.picture_alt'),
'picture_title' => trans('validation.custom.picture_title'),
'picture_thumbnail_alt' => trans('validation.custom.picture_thumbnail_alt'),
'picture_thumbnail_title' => trans('validation.custom.picture_thumbnail_title'),
'published' => trans('validation.custom.activated'),
'publish_at' => trans('validation.custom.publish_at'),
'unpublish_at' => trans('validation.custom.unpublish_at'),
];
}
}

View file

@ -0,0 +1,7 @@
<?php
namespace App\Http\Requests\Bo\News;
class UpdateNewsRequest extends StoreNewsRequest
{
}

View file

@ -0,0 +1,163 @@
<?php
namespace App\Http\Requests\Bo\Products;
use App\Enums\Products\CookingModeEnum;
use App\Traits\Requests\HasPicture;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rules\Enum;
class StoreProductRequest extends FormRequest
{
use HasPicture;
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth('backend')->check();
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
$this->merge([
'published' => $this->published ? true : false
]);
$this->mergePicture('picture_thumbnail');
$this->mergePicture('picture_ambiance');
$this->mergePicture('picture_packshot');
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
$rules = [
'seo_title' => 'required|between:45,70',
'seo_description' => 'required|between:50,160',
'title' => 'required|string|between:3,100',
'subtitle' => 'required|string|between:3,255',
'picture_thumbnail_alt' => 'required|string|between:3,255',
'picture_thumbnail_title' => 'required|string|between:3,255',
'picture_ambiance_alt' => 'required|string|between:3,255',
'picture_ambiance_title' => 'required|string|between:3,255',
'picture_packshot_alt' => 'required|string|between:3,255',
'picture_packshot_title' => 'required|string|between:3,255',
'description' => 'required|string|min:15',
'cooking_mode' => ['required', new Enum(CookingModeEnum::class)],
'cooking_advice' => 'required|string|min:15',
'ingredients' => 'required|string|min:15',
'nutritionnal_values' => 'required|array',
'nutritionnal_values.*' => 'required|numeric|min:0',
'range_id' => 'required|exists:ranges,id',
'commitments' => 'nullable|array',
'commitments.*.id' => 'required|exists:commitments,id',
'published' => 'required|boolean',
];
return \array_merge(
$rules,
$this->pictureRules('picture_thumbnail', 332, 252, 332, 252, false),
$this->pictureRules('picture_ambiance', 774, 515, 774, 515, false),
$this->pictureRules('picture_packshot', 327, 252, 327, 252, false),
);
}
/**
* Override validated() function from FormRequest.
*
* @param array|integer|string|null $key
* @param mixed $default
* @return array|null
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassAfterLastUsed
*/
public function validated($key = null, mixed $default = null): array|null
{
if ($key === 'commitments') {
return parent::validated($key, $default);
}
// phpcs:enable
$nutritionnal_values = json_encode([
"energyKj" => [
"label" => "Energie en Kj",
"value" => $this->nutritionnal_values['energy_kj'],
],
"energyKcal" => [
"label" => "Energie en kcal",
"value" => $this->nutritionnal_values['energy_kcal'],
],
"fat" => [
"label" => "Matières grasses",
"value" => $this->nutritionnal_values['fat'],
],
"saturated-fat" => [
"label" => "dont acides gras saturés",
"value" => $this->nutritionnal_values['saturated_fat'],
],
"carbohydrate" => [
"label" => "Glucides",
"value" => $this->nutritionnal_values['carbohydrate'],
],
"sugar" => [
"label" => "dont sucres",
"value" => $this->nutritionnal_values['sugar'],
],
"proteins" => [
"label" => "Protéines",
"value" => $this->nutritionnal_values['proteins'],
],
"salt" => [
"label" => "Sel",
"value" => $this->nutritionnal_values['salt'],
]
]);
return array_merge(
parent::validated($key, $default),
[
'nutritionnal_values' => $nutritionnal_values
]
);
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'seo_title' => trans('validation.custom.seo_title'),
'seo_description' => trans('validation.custom.seo_description'),
'title' => trans('validation.attributes.title'),
'subtitle' => trans('validation.custom.subtitle'),
'description' => trans('validation.custom.description'),
'picture_thumbnail_alt' => trans('validation.custom.picture_thumbnail_alt'),
'picture_thumbnail_title' => trans('validation.custom.picture_thumbnail_title'),
'picture_ambiance_alt' => trans('validation.custom.picture_ambiance_alt'),
'picture_ambiance_title' => trans('validation.custom.picture_ambiance_title'),
'picture_packshot_alt' => trans('validation.custom.picture_packshot_alt'),
'picture_packshot_title' => trans('validation.custom.picture_packshot_title'),
'cooking_mode' => trans('validation.custom.cooking_mode'),
'cooking_advice' => trans('validation.custom.cooking_advice'),
'ingredients' => trans('validation.custom.ingredients'),
'nutritionnal_values' => trans('validation.custom.nutritionnal_values'),
'range_id' => trans('models.classes.range'),
'commitments' => trans('Engagements associés au produit'),
'commitments.*.id' => trans('Élément parmis les engagements associés au produit'),
'published' => trans('validation.custom.activated'),
];
}
}

View file

@ -0,0 +1,7 @@
<?php
namespace App\Http\Requests\Bo\Products;
class UpdateProductRequest extends StoreProductRequest
{
}

View file

@ -0,0 +1,90 @@
<?php
namespace App\Http\Requests\Bo\Ranges;
use App\Traits\Requests\HasPicture;
use Illuminate\Foundation\Http\FormRequest;
class StoreRangeRequest extends FormRequest
{
use HasPicture;
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth('backend')->check();
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
$this->merge([
'published' => $this->published ? true : false
]);
$this->mergePicture('picture');
$this->mergePicture('picture_productsheet');
$this->mergePicture('picture_thumbnail');
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
$rules = [
'seo_title' => 'required|between:45,70',
'seo_description' => 'required|between:50,160',
'title' => 'required|string|between:3,100',
'subtitle' => 'required|string|between:3,255',
'tagline' => 'required|string|between:3,255',
'description' => 'required|string|min:15',
'picture_alt' => 'required|string|between:3,255',
'picture_title' => 'required|string|between:3,255',
'picture_productsheet_alt' => 'required|string|between:3,255',
'picture_productsheet_title' => 'required|string|between:3,255',
'picture_thumbnail_alt' => 'required|string|between:3,255',
'picture_thumbnail_title' => 'required|string|between:3,255',
'published' => 'required|boolean',
];
return \array_merge(
$rules,
$this->pictureRules('picture', 466, 440, 466, 440, false),
$this->pictureRules('picture_productsheet', 332, 442, 332, 442, false),
$this->pictureRules('picture_thumbnail', 246, 337, 246, 337, false),
);
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'seo_title' => trans('validation.custom.seo_title'),
'seo_description' => trans('validation.custom.seo_description'),
'title' => trans('validation.attributes.title'),
'subtitle' => trans('validation.custom.subtitle'),
'tagline' => trans('validation.custom.tagline'),
'description' => trans('validation.custom.description'),
'picture_alt' => trans('validation.custom.picture_alt'),
'picture_title' => trans('validation.custom.picture_title'),
'picture_productsheet_alt' => trans('validation.custom.picture_productsheet_alt'),
'picture_productsheet_title' => trans('validation.custom.picture_productsheet_title'),
'picture_thumbnail_alt' => trans('validation.custom.picture_thumbnail_alt'),
'picture_thumbnail_title' => trans('validation.custom.picture_thumbnail_title'),
'published' => trans('validation.custom.activated'),
];
}
}

View file

@ -0,0 +1,7 @@
<?php
namespace App\Http\Requests\Bo\Ranges;
class UpdateRangeRequest extends StoreRangeRequest
{
}

View file

@ -0,0 +1,80 @@
<?php
namespace App\Http\Requests\Bo\SeasonalSections;
use App\Enums\SeasonalSections\BackgroundEnum;
use App\Traits\Requests\HasPicture;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rules\Enum;
class StoreSeasonalSectionRequest extends FormRequest
{
use HasPicture;
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth('backend')->check();
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
$this->merge([
'published' => $this->published ? true : false
]);
$this->mergePicture('picture');
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
$rules = [
'title' => 'required|string|between:3,100',
'text' => 'required|string|min:15',
'picture_alt' => 'required|string|between:3,255',
'picture_title' => 'required|string|between:3,255',
'background' => ['required', new Enum(BackgroundEnum::class)],
'published' => 'required|boolean',
'publish_at' => 'nullable|date',
'unpublish_at' => 'nullable|date|after:publish_at',
];
return \array_merge(
$rules,
$this->pictureRules('picture', 362, 542, 362, 542, false),
);
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'title' => trans('validation.attributes.title'),
'text' => trans('validation.attributes.content'),
'picture_alt' => trans('validation.custom.picture_alt'),
'picture_title' => trans('validation.custom.picture_title'),
'background' => trans('validation.custom.background'),
'published' => trans('validation.custom.activated'),
'publish_at' => trans('validation.custom.publish_at'),
'unpublish_at' => trans('validation.custom.unpublish_at'),
];
}
}

View file

@ -0,0 +1,7 @@
<?php
namespace App\Http\Requests\Bo\SeasonalSections;
class UpdateSeasonalSectionRequest extends StoreSeasonalSectionRequest
{
}

View file

@ -0,0 +1,66 @@
<?php
namespace App\Http\Requests\Bo\Stamps;
use App\Traits\Requests\HasPicture;
use Illuminate\Foundation\Http\FormRequest;
class StoreStampRequest extends FormRequest
{
use HasPicture;
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth('backend')->check();
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
$this->merge([
'published' => $this->published ? true : false
]);
$this->mergePicture('picture');
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
$rules = [
'picture_alt' => 'required|string|between:3,255',
'picture_title' => 'required|string|between:3,255',
'published' => 'required|boolean',
];
return \array_merge(
$rules,
$this->pictureRules('picture', 120, 120, 120, 120, false),
);
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'picture_alt' => trans('validation.custom.picture_alt'),
'picture_title' => trans('validation.custom.picture_title'),
'published' => trans('validation.custom.activated'),
];
}
}

View file

@ -0,0 +1,7 @@
<?php
namespace App\Http\Requests\Bo\Stamps;
class UpdateStampRequest extends StoreStampRequest
{
}

View file

@ -0,0 +1,72 @@
<?php
namespace App\Http\Requests\Bo\StaticPages;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Str;
class UpdateStaticPageRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth('backend')->check();
}
/**
* Prepare the data for validation.
*
* @return void
*/
public function prepareForValidation(): void
{
if (
\in_array($this->route('static_page')->type, [
\App\Enums\StaticPageType::confidentiality,
\App\Enums\StaticPageType::legals
])
) {
$this->merge([
'content' => (string)Str::of($this->post('content'))
]);
} else {
$this->merge([
'content' => ''
]);
}
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'seo_title' => 'required|min:45|max:70',
'seo_description' => 'required|min:50|max:160',
'title' => 'required|min:3|max:255',
'content' => 'present',
];
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'seo_title' => trans('validation.custom.seo_title'),
'seo_description' => trans('validation.custom.seo_description'),
'title' => trans('validation.attributes.title'),
'content' => trans('validation.attributes.content'),
];
}
}

View file

@ -0,0 +1,67 @@
<?php
namespace App\Http\Requests\Bo\Testimonials;
use App\Traits\Requests\HasPicture;
use Illuminate\Foundation\Http\FormRequest;
class StoreTestimonialRequest extends FormRequest
{
use HasPicture;
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth('backend')->check();
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
$this->mergePicture('picture');
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
$rules = [
'picture_alt' => 'required|string|between:3,255',
'picture_title' => 'required|string|between:3,255',
'description' => 'required|string|between:3,255',
'job' => 'required|string|between:3,255',
'statut' => 'required|string|between:3,255',
];
return \array_merge(
$rules,
$this->pictureRules('picture', 292, 417, 292, 417, false),
);
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'picture_alt' => trans('validation.custom.picture_alt', ['field' => __('validation.custom.picture')]),
'picture_title' => trans('validation.custom.picture_title', ['field' => __('validation.custom.picture')]),
'description' => trans('validation.attributes.description'),
'job' => trans('validation.custom.job'),
'statut' => trans('validation.custom.statut'),
];
}
}

View file

@ -0,0 +1,7 @@
<?php
namespace App\Http\Requests\Bo\Testimonials;
class UpdateTestimonialRequest extends StoreTestimonialRequest
{
}

View file

@ -0,0 +1,54 @@
<?php
namespace App\Http\Requests\Bo;
use Illuminate\Foundation\Http\FormRequest;
class UpdateChangeOrderRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth()->check();
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation(): void
{
$this->merge([
'direction' => $this->direction === 'up' ? true : false
]);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
return [
'direction' => 'required|boolean',
];
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'direction' => trans('validation.custom.direction'),
];
}
}

View file

@ -0,0 +1,229 @@
<?php
namespace App\Http\Requests\Bo;
use App\Enums\InputType;
use App\Lib\Helpers\ToolboxHelper;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\ValidationException;
abstract class UpdateMiniCrudRequest extends FormRequest
{
/**
* The model parameter name
*
* @var string
*/
protected $modelName = null;
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize()
{
return auth('backend')->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
* @phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh
* @phpcs:disable Generic.Metrics.NestingLevel.TooHigh
*/
public function rules()
{
// phpcs:enable
$rules = [];
foreach ($this->{$this->modelName}->data as $iptName => $input) {
$input = \json_decode(\json_encode($input), true);
switch ($input['inputType']) {
case InputType::number->value:
$rules = array_merge($rules, [
"data.$iptName.value" => $this->compileValidate('required|numeric', $input)
]);
break;
case InputType::videoYoutube->value:
$rules = array_merge($rules, [
"data.$iptName.value" => $this->compileValidate('regex:%([^"&?\/\s]{11})%i', $input)
]);
break;
case InputType::videoVimeo->value:
$rules = array_merge($rules, [
"data.$iptName.value" => $this->compileValidate('regex:%([0-9]{8,10})%i', $input)
]);
break;
case InputType::video->value:
$rules = array_merge($rules, [
"data.$iptName.value" => $this->compileValidate([
'regex:%([0-9]{8,10}|[^"&?\/\s]{11})%i'
], $input)
]);
break;
case InputType::string->value:
$rules = array_merge($rules, [
"data.$iptName.value" => $this->compileValidate('required|string|min:3', $input)
]);
break;
case InputType::text->value:
$mergeRules = $this->compileValidate('required|string|min:15', $input);
$mergeRules = collect($mergeRules)
->map(function ($rule) {
if (\strpos($rule, 'max:') !== false) {
$max = \intval(str_replace('max:', '', $rule));
return function ($attribute, $value, $fail) use ($max) {
if (\strlen(\strip_tags($value)) > $max) {
$fail("$attribute ne doit pas excéder $max caractères");
}
};
}
return $rule;
});
$rules = array_merge($rules, [
"data.$iptName.value" => $mergeRules
]);
break;
case InputType::image->value:
$required = $this->isUpdate() ? 'sometimes' : 'required';
$ipt = $this->data[$iptName];
$max = isset($ipt['dimensionsMax']);
$dim = \is_string($input['dimensions']) ? collect(explode(
',',
(isset($ipt['dimensionsMax']) and is_string($ipt['dimensionsMax'])) ?
$ipt['dimensionsMax'] : $input['dimensions']
))->mapWithKeys(function ($v) {
$v = explode(' ', $v);
return [$v[0] => $v[1]];
}) : $input['dimensions'];
// * Adapt dimensions
$sizes = (object)[
'minHeight' => $dim['height'],
'maxHeight' => $dim['height'],
'minWith' => $dim['width'],
'maxWith' => $dim['width'],
];
$rules = array_merge($rules, [
"data.$iptName.value" => [
function (string $attribute, $value, callable $fail) use ($sizes) {
return ToolboxHelper::validatePicture($attribute, $value, $fail, $sizes);
}
],
$this->compileValidate(
implode('|', [
$required,
'image',
'mimes:png,jpg,jpeg',
sprintf(
'dimensions:%s=%d,%s=%d',
$max ? 'max_height' : 'height',
$dim['height'],
$max ? 'max_width' : 'width',
$dim['width']
)
]),
$input
),
"data.$iptName.alt" => $this->compileValidate("$required|string|min:5", $input),
"data.$iptName.title" => $this->compileValidate("$required|string|min:5", $input)
]);
break;
case InputType::url->value:
$rules = array_merge($rules, [
"data.$iptName.value" => [
'required', 'url',
// @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function ($attribute, $value, $fail) {
try {
ToolboxHelper::assertNoInternalUrls($attribute, $value);
ToolboxHelper::assertNoInternalBoUrls($attribute, $value);
} catch (ValidationException $e) {
$fail(collect($e->errors())->first()[0]);
}
}
]
]);
break;
case InputType::boolean->value:
$all = $this->all();
$nested = ToolboxHelper::dotStringToNestedArray(
"data.$iptName.value",
$this->input("data.$iptName.value") ? true : false
);
$all = ToolboxHelper::arrayMergeRecursiveDistinct($all, $nested);
$this->merge($all);
$rules = array_merge($rules, [
"data.$iptName.value" => 'boolean'
]);
break;
} //end switch
} //end foreach
return $rules;
}
/**
* Undocumented function
*
* @param string|array $inputTypeValidate
* @param array $input
* @return array
*/
public function compileValidate(string|array $inputTypeValidate, array $input): array
{
if (\is_string($inputTypeValidate)) {
$inputTypeValidate = explode('|', $inputTypeValidate);
}
/** @var array|string */
$inputValidate = \collect($input)->get('validate', $input);
$inputValidate = \is_string($inputValidate) ? explode('|', $inputValidate) : $inputValidate;
foreach ($inputValidate as $validate) {
if (!\in_array($validate, $inputTypeValidate)) {
$inputTypeValidate[] = $validate;
}
}
$test = \collect($inputTypeValidate)->all();
$test = \collect($inputTypeValidate)->sort(function ($valueA, $valueB) {
$valueA = $this->ruleHasToComeFirst($valueA) ? 0 : 2;
$valueB = $this->ruleHasToComeFirst($valueB) ? 1 : 2;
return $valueA - $valueB;
})->unique()->all();
return \collect($inputTypeValidate)->unique()->sort(function ($valueA, $valueB) {
$valueA = $this->ruleHasToComeFirst($valueA) ? 0 : 2;
$valueB = $this->ruleHasToComeFirst($valueB) ? 1 : 2;
return $valueA - $valueB;
})->all();
}
/**
* Does the rulle need to come first ?
*
* @param string $rule
* @return boolean
*/
private function ruleHasToComeFirst(string $rule): bool
{
$rule = \collect(\explode(':', $rule, 1))->filter()->first(null, '');
return \collect([
'nullable', 'present', 'required', 'required_if',
'required_unless', 'required_with', 'required_with_all',
'required_without', 'required_without_all', 'required_with_all',
'missing', 'missing_if', 'missing_unless',
'missing_with', 'missing_with_all',
])->first(fn (string $firstRule) => \strpos($rule, $firstRule) === 0) ? true : false;
}
/**
* Is this request an update request ?
*
* @return boolean
*/
private function isUpdate(): bool
{
return $this->isMethod('PUT');
}
}

View file

@ -0,0 +1,74 @@
<?php
namespace App\Http\Requests\Bo\Users;
use App\Enums\Users\RoleEnum;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rules\Enum;
use Illuminate\Validation\Rules\Password;
class StoreUserRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth('backend')->check();
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
$this->merge([
'published' => $this->published ? true : false
]);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
return [
'first_name' => 'required|string|min:3|max:255',
'last_name' => 'required|string|min:3|max:255',
'email' => 'required|email:rfc,strict,dns,spoof,filter|unique:users,email',
'password' => [
'required', 'required_with:password_confirmation', 'confirmed', 'max:255', Password::min(8)
->letters()
->mixedCase()
->numbers()
->symbols()
->uncompromised()
],
'role' => ['required', new Enum(RoleEnum::class)],
'published' => 'required|boolean',
];
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'first_name' => trans('validation.attributes.first_name'),
'last_name' => trans('validation.attributes.last_name'),
'email' => trans('validation.attributes.email'),
'password' => trans('validation.attributes.password'),
'role' => trans('validation.attributes.role'),
'published' => trans('validation.custom.publication')
];
}
}

View file

@ -0,0 +1,48 @@
<?php
namespace App\Http\Requests\Bo\Users;
use Illuminate\Validation\Rules\Password;
class UpdateUserRequest extends StoreUserRequest
{
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
$this->merge([
'published' => $this->published ? true : false,
'password' => $this->password ? $this->password : $this->route('user')->password,
'password_confirmation' => $this->password_confirmation ?
$this->password_confirmation :
$this->route('user')->password
]);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
return array_merge(parent::rules(), [
'email' => [
'required',
'email:rfc,strict,dns,spoof,filter',
'unique:users,email,' . $this->route('user')->getKey() . ',id',
],
'password' => [
'required', 'required_with:password_confirmation', 'confirmed', 'max:255', Password::min(8)
->letters()
->mixedCase()
->numbers()
->symbols()
->uncompromised()
],
]);
}
}

View file

@ -0,0 +1,198 @@
<?php
namespace App\Http\Requests\Fo;
use App\Enums\Contact\CivilityEnum;
use App\Enums\Contact\ObjectEnum;
use App\Lib\Helpers\ToolboxHelper;
use App\Traits\Requests\HasPicture;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rules\Enum;
class ContactRequest extends FormRequest
{
use HasPicture;
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
$this->merge([
'accept_rules' => $this->accept_rules ? true : false,
'send_data' => $this->send_data ? true : false,
]);
$this->mergePicture('file');
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
ToolboxHelper::validateRecaptcha($this, \config('services.google.recaptcha.version'));
$rules = [
'civility' => ['sometimes', 'nullable', new Enum(CivilityEnum::class)],
'last_name' => 'sometimes|nullable|string|between:3,255',
'first_name' => 'sometimes|nullable|string|between:3,255',
'society' => 'sometimes|nullable|string|between:3,255',
'email' => 'required|email:rfc,strict,dns,spoof,filter|max:255',
'phone' => 'sometimes|nullable|numeric|min:0',
'address' => 'sometimes|nullable|string|between:3,255',
'postal_code' => 'sometimes|nullable|numeric|min:0',
'city' => 'sometimes|nullable|string|between:3,255',
'object' => ['required', new Enum(ObjectEnum::class)],
'product_name' => [
'nullable', 'string', 'between:3,255',
// @phpcs:ignore Generic.Files.LineLength.TooLong
'required_with:product_date_limit,product_barcode,product_lot,product_date_consumption,product_location',
// @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function ($attribute, $value, $fail) {
if ($this->get("object") != ObjectEnum::customerService->value()) {
$fail(\trans('le Champs :field doit être vide lorsque le champs objet
n\'est pas égale à :enumValue.', [
'field' => trans('validation.custom.product_name'),
'enumValue' => ObjectEnum::customerService->label(),
]));
}
}
],
'product_date_limit' => [
'sometimes', 'nullable', 'string', 'between:3,255',
// @phpcs:ignore Generic.Files.LineLength.TooLong
'required_with:product_name,product_barcode,product_lot,product_date_consumption,product_location',
// @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function ($attribute, $value, $fail) {
if ($this->get("object") != ObjectEnum::customerService->value()) {
$fail(\trans('le Champs :field doit être vide lorsque le champs objet
n\'est pas égale à :enumValue.', [
'field' => trans('validation.custom.product_date_limit'),
'enumValue' => ObjectEnum::customerService->label(),
]));
}
}
],
'product_barcode' => [
'sometimes', 'nullable', 'numeric', 'min:0',
// @phpcs:ignore Generic.Files.LineLength.TooLong
'required_with:product_name,product_date_limit,product_lot,product_date_consumption,product_location',
// @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function ($attribute, $value, $fail) {
if ($this->get("object") != ObjectEnum::customerService->value()) {
$fail(\trans('le Champs :field doit être vide lorsque le champs objet
n\'est pas égale à :enumValue.', [
'field' => trans('validation.custom.product_barcode'),
'enumValue' => ObjectEnum::customerService->label(),
]));
}
}
],
'product_lot' => [
'sometimes', 'nullable', 'numeric', 'min:0',
// @phpcs:ignore Generic.Files.LineLength.TooLong
'required_with:product_name,product_date_limit,product_barcode,product_date_consumption,product_location',
// @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function ($attribute, $value, $fail) {
if ($this->get("object") != ObjectEnum::customerService->value()) {
$fail(\trans('le Champs :field doit être vide lorsque le champs objet
n\'est pas égale à :enumValue.', [
'field' => trans('validation.custom.product_lot'),
'enumValue' => ObjectEnum::customerService->label(),
]));
}
}
],
'product_date_consumption' => [
'sometimes', 'nullable', 'string', 'between:3,255',
// @phpcs:ignore Generic.Files.LineLength.TooLong
'required_with:product_name,product_date_limit,product_barcode,product_lot,product_location',
// @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function ($attribute, $value, $fail) {
if ($this->get("object") != ObjectEnum::customerService->value()) {
$fail(\trans('le Champs :field doit être vide lorsque le champs objet
n\'est pas égale à :enumValue.', [
'field' => trans('validation.custom.product_date_consumption'),
'enumValue' => ObjectEnum::customerService->label(),
]));
}
}
],
'product_location' => [
'sometimes', 'nullable', 'string', 'between:3,255',
// @phpcs:ignore Generic.Files.LineLength.TooLong
'required_with:product_name,product_date_limit,product_barcode,product_lot,product_date_consumption',
// @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function ($attribute, $value, $fail) {
if ($this->get("object") != ObjectEnum::customerService->value()) {
$fail(\trans('le Champs :field doit être vide lorsque le champs objet
n\'est pas égale à :enumValue.', [
'field' => trans('validation.custom.product_location'),
'enumValue' => ObjectEnum::customerService->label(),
]));
}
}
],
'message' => 'required|string|between:3,255',
'accept_rules' => 'required|boolean',
'send_data' => 'required|boolean',
];
if (
isset(request()->file) and
(request()->file->getClientMimeType() === "image/jpeg" ||
request()->file->getClientMimeType() === "image/png")
) {
$rules = \array_merge($rules, $this->pictureRules(
'file',
0,
0,
1920,
1080,
3000,
true,
'product_name,product_date_limit,product_barcode,product_lot,product_date_consumption,product_location'
));
} elseif (isset(request()->file) and (request()->file->getClientMimeType() === "application/pdf")) {
$rules = \array_merge($rules, [
//phpcs:ignore Generic.Files.LineLength.TooLong
'file' => 'sometimes|nullable|mimes:pdf|required_with:product_name,product_date_limit,product_barcode,product_lot,product_date_consumption,product_location'
]);
} //end if
return $rules;
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'civility' => trans('validation.custom.civility'),
'first_name' => trans('validation.attributes.first_name'),
'last_name' => trans('validation.attributes.last_name'),
'society' => trans('validation.custom.society'),
'email' => trans('validation.attributes.email'),
'phone' => trans('validation.attributes.phone'),
'address' => trans('validation.attributes.address'),
'postal_code' => trans('validation.attributes.postal_code'),
'city' => trans('validation.attributes.city'),
'object' => trans('validation.custom.object'),
'product_name' => trans('validation.custom.product_name'),
'product_date_limit' => trans('validation.custom.product_date_limit'),
'product_barcode' => trans('validation.custom.product_barcode'),
'product_lot' => trans('validation.custom.product_lot'),
'product_date_consumption' => trans('validation.custom.product_date_consumption'),
'product_location' => trans('validation.custom.product_location'),
'message' => trans('validation.attributes.message'),
'accept_rules' => trans('validation.custom.accept_rules'),
'send_data' => trans('validation.custom.send_data'),
'file' => trans('validation.custom.file'),
];
}
}

View file

@ -0,0 +1,45 @@
<?php
namespace App\Http\Requests\Fo;
use App\Lib\Helpers\ToolboxHelper;
use Illuminate\Foundation\Http\FormRequest;
class JoinusRequest extends FormRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
ToolboxHelper::validateRecaptcha($this, \config('services.google.recaptcha.version'));
return [
'last_name' => 'required|string|between:3,255',
'first_name' => 'required|string|between:3,255',
'email' => 'required|email:rfc,strict,dns,spoof,filter|max:255',
'phone' => 'required|numeric|min:0',
'message' => 'required|string|between:3,255',
"file" => "sometimes|nullable|mimes:pdf|max:10000"
];
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'first_name' => trans('validation.attributes.first_name'),
'last_name' => trans('validation.attributes.last_name'),
'email' => trans('validation.attributes.email'),
'phone' => trans('validation.attributes.phone'),
'message' => trans('validation.attributes.message'),
'file' => trans('validation.attributes.file'),
];
}
}

View file

@ -0,0 +1,46 @@
<?php
namespace App\Jobs;
use App\Models\News;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Log;
/**
* Update published attribute reading its
* publish_at and unpublish_at attributes.
*/
class NewsStatusUpdate
{
/**
* Called when the job will be executed.
*
* @return void
*/
public function handle()
{
/** @var \Illuminate\Database\Eloquent\Collection<array-key, \App\Models\News> */
$newsListToPublish = News::query()
->where('publish_at', '<=', Carbon::now()->toDateTime())
->whereNotNull('publish_at')
->where('published', false)->get();
Log::debug(__CLASS__ . " news to publish " . $newsListToPublish->count());
foreach ($newsListToPublish as $news) {
$news->publish_at = null;
$news->published = true;
$news->saveOrFail();
}
/** @var \Illuminate\Database\Eloquent\Collection<array-key, \App\Models\News> */
$newsListToUnPublish = News::query()
->where('unpublish_at', '<=', Carbon::now()->toDateTime())
->whereNotNull('unpublish_at')
->where('published', true)->get();
Log::debug(__CLASS__ . " news to unpublish " . $newsListToPublish->count());
foreach ($newsListToUnPublish as $news) {
$news->publish_at = null;
$news->unpublish_at = null;
$news->published = false;
$news->saveOrFail();
}
}
}

View file

@ -0,0 +1,46 @@
<?php
namespace App\Jobs;
use App\Models\SeasonalSection;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Log;
/**
* Update published attribute reading its
* publish_at and unpublish_at attributes.
*/
class SeasonalSectionStatusUpdate
{
/**
* Called when the job will be executed.
*
* @return void
*/
public function handle()
{
/** @var \Illuminate\Database\Eloquent\Collection<array-key, \App\Models\SeasonalSections> */
$seasonalsectionsListToPublish = SeasonalSection::query()
->where('publish_at', '<=', Carbon::now()->toDateTime())
->whereNotNull('publish_at')
->where('published', false)->get();
Log::debug(__CLASS__ . " seasonalsections to publish " . $seasonalsectionsListToPublish->count());
foreach ($seasonalsectionsListToPublish as $seasonalsections) {
$seasonalsections->publish_at = null;
$seasonalsections->published = true;
$seasonalsections->saveOrFail();
}
/** @var \Illuminate\Database\Eloquent\Collection<array-key, \App\Models\SeasonalSections> */
$seasonalsectionsListToUnPublish = SeasonalSection::query()
->where('unpublish_at', '<=', Carbon::now()->toDateTime())
->whereNotNull('unpublish_at')
->where('published', true)->get();
Log::debug(__CLASS__ . " seasonalsections to unpublish " . $seasonalsectionsListToPublish->count());
foreach ($seasonalsectionsListToUnPublish as $seasonalsections) {
$seasonalsections->publish_at = null;
$seasonalsections->unpublish_at = null;
$seasonalsections->published = false;
$seasonalsections->saveOrFail();
}
}
}

View file

@ -0,0 +1,64 @@
<?php
namespace App\Lib\Helpers;
use Illuminate\Container\Container;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Collection;
class CollectionHelper
{
/**
* Paginate a collection
*
* @param \Illuminate\Support\Collection $results
* @param integer $pageSize
* @return \Illuminate\Pagination\LengthAwarePaginator
*/
public static function paginate(Collection $results, int $pageSize): LengthAwarePaginator
{
$page = Paginator::resolveCurrentPage('page');
return self::paginateManually($results, $pageSize, $page);
}
/**
* Paginate a collection
*
* @param \Illuminate\Support\Collection $results
* @param integer $pageSize
* @param integer $page
* @return \Illuminate\Pagination\LengthAwarePaginator
*/
public static function paginateManually(Collection $results, int $pageSize, int $page = 1): LengthAwarePaginator
{
$total = $results->count();
return self::paginator($results->forPage($page, $pageSize), $total, $pageSize, $page, [
'path' => Paginator::resolveCurrentPath(),
'pageName' => 'page',
]);
}
/**
* Create a new length-aware paginator instance.
*
* @param \Illuminate\Support\Collection $items
* @param integer $total
* @param integer $perPage
* @param integer $currentPage
* @param array $options
* @return \Illuminate\Pagination\LengthAwarePaginator
*/
protected static function paginator(Collection $items, int $total, int $perPage, int $currentPage, array $options)
{
return Container::getInstance()->makeWith(LengthAwarePaginator::class, [
'items' => $items,
'total' => $total,
'perPage' => $perPage,
'currentPage' => $currentPage,
'options' => $options
]);
}
}

View file

@ -0,0 +1,190 @@
<?php
namespace App\Lib\Helpers;
class ExceptionsHelper
{
/**
* Handle sentry event
*
* @param \Sentry\Event $event
* @return null|\Sentry\Event
*/
public static function sentryEventHandler(\Sentry\Event $event): ?\Sentry\Event
{
// Remove unwanted reports.
$event->setModules([]);
$event->setOsContext(null);
/** @var \Sentry\ExceptionDataBag[] */
$exceptionBag = [];
foreach ($event->getExceptions() as $exceptionDataBag) {
$exceptionBag[] = self::getAnonymizedSentryException($exceptionDataBag);
}
$event->setExceptions($exceptionBag);
return $event;
}
/**
* Anonimize Sentry Exception bag
*
* @param \Sentry\ExceptionDataBag $exceptionDataBag
* @return \Sentry\ExceptionDataBag
*/
private static function getAnonymizedSentryException(
\Sentry\ExceptionDataBag $exceptionDataBag
): \Sentry\ExceptionDataBag {
$exceptionDataBag->setValue(self::getAnonymizedMessage($exceptionDataBag->getValue()));
if ($stackTrace = $exceptionDataBag->getStacktrace()) {
$exceptionDataBag->setStacktrace(self::getAnonymizedSentryStackTrace($stackTrace));
}
return $exceptionDataBag;
}
/**
* Anonymise sentry stacktrace
*
* @param \Sentry\Stacktrace $stackTrace
* @return \Sentry\Stacktrace
*/
private static function getAnonymizedSentryStackTrace(\Sentry\Stacktrace $stackTrace): \Sentry\Stacktrace
{
/** @var \Sentry\Frame[] */
$frames = \array_reverse($stackTrace->getFrames(), true);
$i = \count($frames) - 1;
// * Remove all but keep first (index.php).
while ($i-- > 0) {
$stackTrace->removeFrame(count($stackTrace->getFrames()) - 1);
}
foreach ($frames as $index => $frame) {
$frame->setPostContext([]);
$frame->setPreContext([]);
$frame->setContextLine('');
$frame->setVars(self::getAnonymizedSentryParams($frame->getVars()));
$stackTrace->addFrame($frame);
if ($index === 0) {
$stackTrace->removeFrame(count($stackTrace->getFrames()) - 1);
}
}
return $stackTrace;
}
/**
* Get anonymized sentry params
*
* @param mixed[] $params
* @return string[]
*/
private static function getAnonymizedSentryParams(array $params): array
{
foreach ($params as &$param) {
$type = \strtolower(\gettype($param));
if (\is_string($param)) {
$type = \explode(' ', \trim(strtolower($param)))[0] ?? null;
}
switch (true) {
case $type === 'lambda':
case $type === 'object':
continue 2;
case $type === 'array' and \is_array($param):
$param = sprintf('Array(%d)', \count($param));
continue 2;
default:
if (\is_numeric($param)) {
$param = \ucfirst($type);
continue 2;
}
if (\is_string($param)) {
$param = sprintf('String(%d)', \strlen($param));
continue 2;
}
} //end switch
} //end foreach
return $params;
}
/**
* Anonymize at least Sql queries
*
* @param string $message
* @return string
*/
public static function getAnonymizedMessage(string $message): string
{
$message = \preg_replace('/^(.*)( \(SQL:.*\))$/', '$1', $message) ?? $message;
if ($sqlPos = \strpos($message, 'to use near')) {
$message = \mb_strcut($message, 0, $sqlPos);
}
return $message;
}
/**
* Anonymize stack trace
*
* @param \Throwable $e
* @return string
*/
public static function getAnonymizedStackTrace(\Throwable $e): string
{
$projectPath = \realpath(\sprintf('%s/../', \app_path()));
$traceStack = $e->getTrace();
$traceStr = '';
$maxStackStage = 0;
foreach ($traceStack as $stackStage => $stack) {
$maxStackStage = $stackStage;
$traceStr .= \sprintf(
'#%s %s(%d): ',
$stackStage,
str_replace($projectPath, '', $stack['file'] ?? ''),
$stack['line'] ?? ''
);
if (isset($stack['class'])) {
$traceStr .= "{$stack['class']}->{$stack['function']}(";
if (isset($stack['args'])) {
$traceStr .= self::handleArgs($stack);
}
$traceStr .= ")\n";
} elseif (isset($stack['function'])) {
$traceStr .= "{$stack['function']}(";
if (isset($stack['args'])) {
$traceStr .= self::handleArgs($stack);
}
$traceStr .= ")\n";
}
} //end foreach
$maxStackStage++;
$traceStr .= "#$maxStackStage {main}";
return $traceStr;
}
/**
* Handle Args
*
* @param array $stack
* @return string
*/
public static function handleArgs(array $stack): string
{
$traceArgs = '';
$argsCount = count($stack['args']);
for ($i = 0; $i < $argsCount; $i++) {
$type = \gettype($stack['args'][$i]);
switch ($type) {
case 'object':
$traceArgs .= sprintf('Object(%s)', \get_class($stack['args'][$i]));
break;
case 'array':
$traceArgs .= sprintf('Array(%d)', \count($stack['args'][$i]));
break;
case 'string':
$traceArgs .= sprintf('String(%d)', \strlen($stack['args'][$i]));
break;
default:
$traceArgs .= \gettype($stack['args'][$i]);
}
$traceArgs .= ($i !== ($argsCount - 1) ? ',' : '');
}
return $traceArgs;
}
}

View file

@ -0,0 +1,472 @@
<?php
namespace App\Lib\Helpers;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
/**
* This class is used to handle files in LDC Laravel stack
*
* * The storage strategy will be using date.
* The file name may contain sufix timestamp highresolution time (hrtime or microtime) if is duplicate.
* (function() { return time() . ' ' . Str::slug(microtime(true)) . ' ' . hrtime(true); })();
*
* posts/ - The model table name
* 10/ - Year
* 04/ - Month
* 19/ - Day
* 5f/ - First two letters of md5 file
* black-cat-under-red-car.png - Slugified file name
* black-cat-under-red-car-286071690833400.png - Slugified file name that is duplicated
*
* The final path will be storage/modelfiles/posts/10/04/19/5f/black-cat-under-red-car-286071690833400.png
*
* ! No limits of file per folder will be handled.
*
* @author Munsch Jérémy <jeremy.munsch@ldc.fr>
*/
class FileStorageHelper
{
/**
* Destroy the file associate with the attribute.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $attribute
* @return void
*/
public static function removeFile(Model $model, string $attribute): void
{
$path = \ltrim(\urldecode($model->{$attribute}), '/');
if (\strpos($path, 'storage') !== 0) {
return;
}
$path = ToolboxHelper::mbReplace('storage/', '', $path);
if (Storage::disk('public')->exists($path)) {
Storage::disk('public')->delete($path);
}
}
/**
* Destroy the file associate with the attribute old value (before updated to DB).
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $attribute
* @param boolean $onlyIfUpdated
* @return void
*/
public static function removeOldFile(Model $model, string $attribute, bool $onlyIfUpdated = true): void
{
// * Ignore If removal has to be done only if the field has changed and it actually hasnt.
if ($onlyIfUpdated and $model->{$attribute} === ToolboxHelper::getOriginal($model, $attribute)) {
return;
}
$originalPath = \ltrim(\urldecode(ToolboxHelper::getOriginal($model, $attribute)), '/');
if (\strpos($originalPath, 'storage') !== 0) {
return;
}
$originalPath = ToolboxHelper::mbReplace('storage/', '', $originalPath);
if (Storage::disk('public')->exists($originalPath)) {
Storage::disk('public')->delete($originalPath);
}
}
/**
* Store a file using optimized storage strategy
*
* You can use filename param to overload file name.
*
* @param \Illuminate\Database\Eloquent\Model $model The model that will hold the picture.
* @param \Illuminate\Http\UploadedFile|\SplFileInfo|string $file The file, will just return itself is string.
* @param boolean $slugify Force filename slugification.
* @param string|null $filename This string may be used to store the file.
* @param boolean $private To store the file in a private space.
* @return string
* @throws \RuntimeException If file copy fails.
* @phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh
*/
public static function storeFile(
Model $model,
$file,
bool $slugify = false,
string $filename = null,
bool $private = false
): string {
// phpcs:enable
$tableName = $model->getTable();
if (\is_object($file) and $file instanceof \Illuminate\Http\UploadedFile) {
/** @var \Illuminate\Http\UploadedFile $file */
$filename = $filename ?: $file->getClientOriginalName();
$filename = $slugify ? self::slugifyFileName($filename) : $filename;
$newPath = self::prepareMoveFile($tableName, $filename, $private);
$newPath = str_replace(storage_path('app'), '', $newPath);
if (!$private) {
// * Store public file.
$file->storePubliclyAs(
pathinfo($newPath, \PATHINFO_DIRNAME),
pathinfo($newPath, \PATHINFO_BASENAME)
);
return 'storage' . str_replace('/public', '', $newPath);
}
$file->storeAs(
pathinfo($newPath, \PATHINFO_DIRNAME),
pathinfo($newPath, \PATHINFO_BASENAME)
);
return str_replace('/private/', '', $newPath);
}//end if
if (\is_object($file) and $file instanceof \SplFileInfo) {
/** @var \SplFileInfo $file */
$filename = $filename ?: $file->getBaseName();
$filename = $slugify ? self::slugifyFileName($filename) : $filename;
$newPath = self::prepareMoveFile($tableName, $filename, $private);
if (!File::copy($file->getRealPath(), $newPath)) {
throw new \RuntimeException(sprintf(
'Failed to copy file %s to %s',
$file->getRealPath(),
$newPath
));
}
return $private ?
\ltrim(str_replace(\storage_path('app/private'), '/storage/app/private', $newPath), '/') :
\ltrim(str_replace(\storage_path('app/public'), '/storage', $newPath), '/');
}
return strval($file);
}
/**
* Store all files from string attribute html
* If oldHtmlString is passed, a diff is done with new and old files
* to keep only files that are present in the new html string.
*
* @param \Illuminate\Database\Eloquent\Model $model The model that will hold the picture.
* @param string $htmlString The html string to parse and modify files.
* @param string|null $oldHtmlString The old html string with alreadyParsed
* files using storeModelFilesFromHtml.
* @return string Html with files pathes replaced using storage strategy.
*/
public static function storeModelFilesFromHtml(
Model $model,
string $htmlString,
string $oldHtmlString = null
): string {
\collect(self::onlyUploadedFilesWithFileManager(self::findFilesInHtml($htmlString)))
->each(function (string $path) use ($model, &$htmlString) {
/** @var \Illuminate\Filesystem\FilesystemAdapter */
$disk = Storage::disk('uploads');
$fileInfo = new \SplFileInfo($disk->path(
ToolboxHelper::mbReplace('/uploads/', '', \urldecode($path))
));
$htmlString = ToolboxHelper::mbReplace(
$path,
'/' . self::storeFile($model, $fileInfo, true),
$htmlString
);
});
if ($oldHtmlString) {
self::removeOldModelFilesFromHtml($htmlString, $oldHtmlString);
}
return $htmlString;
}
/**
* Remove all old files from string attribute html that are stored in public that
* are nove present anymore on new html string
*
* @param string $newHtmlString The html string with alreadyParsed files using storeModelFilesFromHtml.
* @param string $oldHtmlString The old html string with alreadyParsed files using storeModelFilesFromHtml.
* @return void
*/
public static function removeOldModelFilesFromHtml(string $newHtmlString, string $oldHtmlString): void
{
self::removeOldFiles(self::findFilesInHtml($oldHtmlString), self::findFilesInHtml($newHtmlString));
}
/**
* Remove all old files that are stored in public that
* are not present anymore on new array files
*
* @param string[] $oldFiles The old array files.
* @param string[] $newFiles The new array files.
* @return void
*/
public static function removeOldFiles(array $oldFiles, array $newFiles): void
{
$oldFiles = \collect(self::onlyUploadedFilesWithFileStorageHelper($oldFiles));
$filesToKeep = \collect(self::onlyUploadedFilesWithFileStorageHelper($newFiles));
$filesToRemove = $oldFiles->diff($filesToKeep);
$filesToRemove->each(function (string $publicStorageFilePath) {
$path = ToolboxHelper::mbReplace('storage/', '', \ltrim(\urldecode($publicStorageFilePath), '/'));
if (Storage::disk('public')->exists($path)) {
Storage::disk('public')->delete($path);
}
});
}
/**
* Remove all files from string attribute html that are stored in public
*
* @param string $htmlString The html string with alreadyParsed files using storeModelFilesFromHtml.
* @return void
*/
public static function removeModelFilesOfHtml(string $htmlString): void
{
$filesToRemove = \collect(self::onlyUploadedFilesWithFileStorageHelper(self::findFilesInHtml($htmlString)));
$filesToRemove->each(function (string $publicStorageFilePath) {
$path = ToolboxHelper::mbReplace('storage/', '', \ltrim(\urldecode($publicStorageFilePath), '/'));
if (Storage::disk('public')->exists($path)) {
Storage::disk('public')->delete($path);
}
});
}
/**
* Find all occurence of files in html
*
* @param string $html
* @return array
*/
public static function findFilesInHtml(string $html): array
{
preg_match_all('/href="(?P<href>.*)"|src="(?P<src>.*)"|url="(?P<url>.*)"/Um', $html, $m, PREG_SET_ORDER, 0);
return collect($m)->pluck('href')->filter()
->concat(collect($m)->pluck('url')->filter())
->concat(collect($m)->pluck('src')->filter())
->all();
}
/**
* Filter array to get only uploaded files that exists
* on disk 'public' (using helper storage strategy)
*
* @param array $urls
* @return array
*/
public static function onlyUploadedFilesWithFileStorageHelper(array $urls): array
{
return \collect($urls)->unique()
// * Filter to get only /uploads and using app url or empty
->filter(function (string $url) {
$path = \parse_url($url, \PHP_URL_PATH);
return \strpos(\ltrim($path, '/'), 'storage/modelfiles') === 0;
})->filter(function (string $url) {
$path = \parse_url($url, \PHP_URL_PATH);
return Storage::disk('public')
->exists(ToolboxHelper::mbReplace('storage/', '', \ltrim(\urldecode($path), '/')));
})->all();
}
/**
* Filter array to get only uploaded files that exists
* on disk 'uploads'
*
* @param array $urls
* @return array
*/
private static function onlyUploadedFilesWithFileManager(array $urls): array
{
return \collect($urls)->unique()
// * Filter to get only /uploads and using app url or empty
->filter(function (string $url) {
$host = \parse_url($url, \PHP_URL_HOST);
$path = \parse_url($url, \PHP_URL_PATH);
return ($host === \parse_url(config('app.url'), PHP_URL_HOST) or \is_null($host)) and
\strpos(\ltrim($path, '/'), 'uploads') === 0;
})->filter(function (string $url) {
$path = \parse_url($url, \PHP_URL_PATH);
return Storage::disk('uploads')
->exists(ToolboxHelper::mbReplace('uploads/', '', \ltrim(\urldecode($path), '/')));
})->all();
}
/**
* Prepare storage path and return
* the file full path
*
* @param string $tableName Model table name.
* @param string $filename The basename for the file.
* @param boolean $private May the file be stored in a private dir.
* @return string
*/
private static function prepareMoveFile(string $tableName, string $filename, bool $private = false): string
{
// * Security filter
$filename = \htmlspecialchars($filename);
// * Security filter + beautifier
$filename = self::sanitizeFileName($filename);
// * Slugify filename
$ext = \pathinfo($filename, \PATHINFO_EXTENSION);
$filename = Str::slug(\pathinfo($filename, \PATHINFO_FILENAME)) .
(\strlen($ext) ? ".{$ext}" : '');
$folderPath = self::getStoragePath(
$private ? 'app/private/modelfiles' : 'app/public/modelfiles',
$tableName,
$filename,
$private
);
$filename = self::getFileUniqueName($folderPath, $filename);
return "{$folderPath}/{$filename}";
}
/**
* Create sub folders to store files using pattern
* storage/modelfiles/posts/10/04/19/5f
* year/month/day/md5 first 2 letters
*
* @param string $storage_dir
* @param string $table
* @param string $filename
* @return string
*/
private static function getStoragePath(string $storage_dir, string $table, string $filename): string
{
$filenameHash = \md5($filename);
$pathFolder = \storage_path(sprintf(
'%s/%s/%s/%s/%s/%s',
$storage_dir,
$table,
gmdate('y'),
gmdate('m'),
gmdate('d'),
substr($filenameHash, 0, 2)
));
if (!File::exists($pathFolder)) {
File::makeDirectory($pathFolder, 0755, true);
}
return $pathFolder;
}
/**
* Get unique filename if the path is already occupied
*
* @param string $folderPath
* @param string $filename
* @return string
* @throws \RuntimeException If a unique filename cannot be obtained.
*/
private static function getFileUniqueName(string $folderPath, string $filename): string
{
$attempts = 0;
$maxAttempts = 20;
while (File::exists("{$folderPath}/{$filename}")) {
if ($attempts++ >= $maxAttempts) {
throw new \RuntimeException("Failed to have a unique filename for {$folderPath}/{$filename}");
}
$filename = pathinfo($filename, \PATHINFO_FILENAME) .
\uniqid('-') . '.' .
pathinfo($filename, \PATHINFO_EXTENSION);
}
return $filename;
}
/**
* Slugify the filename
*
* @param string $filename
* @return string
*/
public static function slugifyFileName(string $filename): string
{
return Str::slug(pathinfo($filename, \PATHINFO_FILENAME)) . '.' . pathinfo($filename, \PATHINFO_EXTENSION);
}
/**
* Sanitize File name for uploaded files
*
* @param string $filename The original uploaded client file name.
* @param boolean $beautify Beautify the filename.
* @return string The sanitized file name.
*/
public static function sanitizeFileName(string $filename, bool $beautify = true): string
{
// * Sanitize filename
// phpcs:disable Generic.Files.LineLength.TooLong
$filename = preg_replace(
'~
[<>:"/\\\|?*]| # file system reserved https://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words
[\x00-\x1F]| # control characters http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
[\x7F\xA0\xAD]| # non-printing characters DEL, NO-BREAK SPACE, SOFT HYPHEN
[#\[\]@!$&\'()+,;=]| # URI reserved https://tools.ietf.org/html/rfc3986#section-2.2
[{}^\~`] # URL unsafe characters https://www.ietf.org/rfc/rfc1738.txt
~x',
'-',
$filename
);
// phpcs:enable
// * Avoids '.' '..' or '.hiddenFiles' .
$filename = Str::of($filename)->ltrim('.-');
// * Optional beautification
if ($beautify) {
$filename = static::beautifyFilename($filename);
}
// * Maximize filename length to 255 bytes http://serverfault.com/a/9548/44086
/** @var string */
$extension = pathinfo($filename, \PATHINFO_EXTENSION);
/** @var string */
$filename = pathinfo($filename, \PATHINFO_FILENAME);
$encoding = mb_detect_encoding($filename);
$encoding = $encoding ? $encoding : null;
return \mb_strcut(
$filename,
0,
255 - (!empty($extension) ? strlen($extension) + 1 : 0),
$encoding
) . (!empty($extension) ? ".$extension" : '');
}
/**
* Beautify a filename
*
* @param string $filename
* @return string
*/
private static function beautifyFilename(string $filename): string
{
// Reduce consecutive characters.
$filename = preg_replace([
// * "file name.zip" becomes 'file-name.zip'
'/ +/',
// * "file___name.zip" becomes 'file-name.zip'
'/_+/',
// * "file---name.zip" becomes 'file-name.zip'
'/-+/'
], '-', $filename);
$filename = preg_replace([
// * "file--.--.-.--name.zip" becomes "file.name.zip"
'/-*\.-*/',
// * "file...name..zip" becomes "file.name.zip"
'/\.{2,}/'
], '.', $filename);
// Lowercase for windows/unix interoperability http://support.microsoft.com/kb/100625 .
$filename = mb_strtolower($filename, mb_detect_encoding($filename));
// * ".file-name.-" becomes "file-name"
$filename = trim($filename, '.-');
return $filename;
}
/**
* Remove all model files for the given Model.
*
* @param Model $model
* @return void
*/
public static function removeModelFiles(Model $model): void
{
$tableName = $model::query()->getModel()->getTable();
if (!empty(Storage::disk('public')->allFiles("modelfiles/{$tableName}"))) {
Storage::disk('public')->deleteDirectory("modelfiles/{$tableName}");
}
if (!empty(Storage::disk('private')->allFiles("modelfiles/{$tableName}"))) {
Storage::disk('private')->deleteDirectory("modelfiles/{$tableName}");
}
}
}

View file

@ -0,0 +1,540 @@
<?php
namespace App\Lib\Helpers;
use Carbon\Carbon;
use Illuminate\Contracts\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Rules\Enum;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Finder\SplFileInfo;
class ToolboxHelper
{
/**
* @param array<int|string, mixed> $array1
* @param array<int|string, mixed> $array2
*
* @return array<int|string, mixed>
*/
public static function arrayMergeRecursiveDistinct(array &$array1, array &$array2): array
{
$merged = $array1;
foreach ($array2 as $key => &$value) {
if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {
$merged[$key] = self::arrayMergeRecursiveDistinct($merged[$key], $value);
} else {
$merged[$key] = $value;
}
}
return $merged;
}
/**
* Validate recaptcha
*
* @param \Illuminate\Http\Request $request
* @param boolean $isV3
* @return void
*/
public static function validateRecaptcha(Request $request, bool $isV3 = true)
{
Validator::validate($request->all(), [
'g-recaptcha-response' => [
'required', 'string',
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed, Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
function ($attribute, $value, $fail) use ($isV3) {
try {
$response = Http::asForm()->post('https://www.google.com/recaptcha/api/siteverify', [
'secret' => config('services.google.recaptcha.private'),
'response' => \strval($value),
'remoteip' => app('clientIp')
]);
if ($e = $response->toException()) {
throw $e;
}
if ($isV3) {
if (
!$response->json('success') or
!$response->json('challenge_ts') or
Carbon::parse($response->json('challenge_ts'))->lessThan(\now('utc')->subMinutes(5)) or
$response->json('hostname') !== \parse_url(config('app.url'), \PHP_URL_HOST) or
!$response->json('score')
) {
throw new \RuntimeException();
}
if (\floatval($response->json('score')) < 0.8) {
$fail(trans('recaptcha.not_enough_score'));
}
return;
}
if (
!$response->json('success') or
!$response->json('challenge_ts') or
Carbon::parse($response->json('challenge_ts'))->lessThan(\now('utc')->subMinutes(5)) or
$response->json('hostname') !== \parse_url(config('app.url'), \PHP_URL_HOST)
) {
throw new \RuntimeException();
}
} catch (\Exception $e) {
$fail(trans('recaptcha.error'));
} //end try
}
]
], [], ['g-recaptcha-response' => 'reCaptcha']);
}
/**
* Strip scheme (https) on oembed urls
* Only embed allowed are for MP4 self hosted videos right now
*
* https is force by ckeditor MediaElements which natively supports
* only cloud hosted medias (youtube, spotify etc..)
*
* @param string $input
* @return string
*/
public static function stripCkEditorSchemesOnMediaInputs(string $input): string
{
$input = \preg_replace('/(?<=<oembed url=")(https:\/\/)(?=.*?">)/m', '', $input);
return $input;
}
/**
* Assert That no Back Office url is present on txt
*
* @param string $field
* @param string $text
* @return string
* @throws \Illuminate\Validation\ValidationException If url is a bo url.
*/
public static function assertNoInternalBoUrls(string $field, string $text): string
{
$routes = Route::getRoutes();
// * Url replace and email replace.
$text = preg_replace_callback(
// phpcs:ignore Generic.Files.LineLength.TooLong
'/(?P<url>(?:http[s]?:\/\/.)?(?:www\.)?[-a-zA-Z0-9@%._\+~#=]{2,256}\.[a-z]{2,6}\b(?:[-a-zA-Z0-9@:%_\+.~#?&\/\/=]*))/',
function ($matches) use ($routes, $field) {
$match = collect($matches)->first() ?? '';
$url = $match;
try {
$request = Request::create($url);
// If a route is match then it do not throws exception.
if (
\parse_url(config('app.url'), \PHP_URL_HOST) === \parse_url($url, \PHP_URL_HOST) &&
trim($routes->match($request)->getPrefix() ?? '', '/') === 'bo'
) {
// Removes the matched url.
throw ValidationException::withMessages([
$field => trans(
'crud.libs.toolbox.assertNoInternalBoUrls',
['attribute' => $field]
)
]);
}
return $match;
} catch (NotFoundHttpException $e) {
return $match;
}
},
$text
);
return $text;
}
/**
* Assert That only external url is present on txt
*
* @param string $field
* @param string $text
* @return string
* @throws \Illuminate\Validation\ValidationException If url is a bo url.
*/
public static function assertNoInternalUrls(string $field, string $text): string
{
self::assertNoInternalBoUrls($field, $text);
$routes = Route::getRoutes();
// * Url replace and email replace.
$text = preg_replace_callback(
// phpcs:ignore Generic.Files.LineLength.TooLong
'/(?P<url>(?:http[s]?:\/\/.)?(?:www\.)?[-a-zA-Z0-9@%._\+~#=]{2,256}\.[a-z]{2,6}\b(?:[-a-zA-Z0-9@:%_\+.~#?&\/\/=]*))/',
function ($matches) use ($routes, $field) {
$match = collect($matches)->first() ?? '';
$url = $match;
try {
$request = Request::create($url);
// If a route is match then it do not throws exception.
if (
\parse_url(config('app.url'), \PHP_URL_HOST) === \parse_url($url, \PHP_URL_HOST) &&
$routes->match($request)
) {
// Removes the matched url.
throw ValidationException::withMessages([
$field => trans(
'crud.libs.toolbox.assertNoInternalUrls',
['attribute' => $field, 'appname' => config('app.name')]
)
]);
}
return $match;
} catch (NotFoundHttpException $e) {
return $match;
}
},
$text
);
return $text;
}
/**
* Mutltibyte string replace
*
* @param array|string $search
* @param array|string $replace
* @param array|string $subject
* @param integer $count
* @return array|string
*/
public static function mbReplace(
array|string $search,
array|string $replace,
array|string $subject,
int &$count = 0
): array|string {
if (!is_array($search) && is_array($replace)) {
return false;
}
if (is_array($subject)) {
// Call mb_replace for each single string in $subject .
foreach ($subject as &$string) {
$string = &self::mbReplace($search, $replace, $string, $count);
}
} elseif (is_array($search)) {
if (!is_array($replace)) {
foreach ($search as &$string) {
$subject = self::mbReplace($string, $replace, $subject, $count);
}
} else {
$n = max(count($search), count($replace));
while ($n--) {
$subject = self::mbReplace(current($search), current($replace), $subject, $count);
next($search);
next($replace);
}
}
} else {
$parts = mb_split(preg_quote($search), $subject);
$count = count($parts) - 1;
$subject = implode($replace, $parts);
} //end if
return $subject;
}
/**
* Asserts that the field is unique
*
* @param string $table
* @param string $field
* @param string|integer|float|boolean|null|array $value
* @param integer|null $id
* @param callable|null $query
* @return void
* @throws \Illuminate\Validation\ValidationException If the field is not unique.
*/
public static function assertFieldIsUnique(
string $table,
string $field,
string|int|float|bool|null|array $value,
?int $id = null,
?callable $query = null
) {
if (!$query) {
Validator::make([$field => $value], [
self::mbReplace('.', '\.', $field) => Rule::unique($table, $field)->ignore($id),
], [
self::mbReplace('.', '\.', "{$field}.unique")
=> trans('crud.libs.toolbox.assertFieldIsUnique', ['attribute' => $field, 'value' => $value])
])->validate();
return;
}
Validator::make([$field => $value], [
$field => Rule::unique($table)->where($query)->ignore($id),
], [
$field . '.unique' => trans(
'crud.libs.toolbox.assertFieldIsUnique',
['attribute' => $field, 'value' => $value]
)
])->validate();
}
/**
* Get original polyfill with autotranslate behavior
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $attribute
* @return mixed
*/
public static function getOriginal(Model $model, string $attribute): mixed
{
if (\in_array('Spatie\Translatable\HasTranslations', \class_uses_recursive($model))) {
return $model->getTranslation(
$attribute,
$model->getLocale(),
$model->useFallbackLocale()
);
}
return $model->getOriginal($attribute);
}
/**
* Dynamic model query
*
* @param string $defaultLocale
* @param \Illuminate\Contracts\Database\Eloquent\Builder $q
* @param string $column
* @param string $value
* @return \Illuminate\Contracts\Database\Eloquent\Builder
*/
public static function queryColumnWithLocales(
string $defaultLocale,
Builder $q,
string $column,
string $value
): Builder {
$currentLocale = app()->currentLocale();
$locales = collect(config('app.locales'))
->mapWithKeys(fn (string $locale) => [$locale => $locale]);
if (!$locales->has($defaultLocale)) {
// * Remove default locale.
$locales->pull($defaultLocale);
}
if (!$locales->has($currentLocale)) {
// * Remove current locale.
$locales->pull($currentLocale);
}
return $q->where(function (Builder $q) use ($defaultLocale, $currentLocale, $locales, $column, $value) {
// * Query default locale
$q->whereRaw("JSON_EXTRACT({$column}, '$.{$defaultLocale}') = '{$value}'");
if ($defaultLocale !== $currentLocale) {
// * Query current locale
$q->whereRaw("JSON_EXTRACT({$column}, '$.{$currentLocale}') = '{$value}'");
}
// * Query any other locale
$locales->each(fn ($locale) => $q->orWhereRaw(
"JSON_EXTRACT({$column}, '$.{$locale}') = '{$value}'"
));
});
}
/**
* Generates an array from a dot separated string
*
* @param string $path
* @param mixed $value
* @param string $separator
* @return array
*/
public static function dotStringToNestedArray(string $path, mixed $value, string $separator = '.'): array
{
$arr = $value;
$keys = array_reverse(explode($separator, $path));
foreach ($keys as $key) {
$arr = [$key => $arr];
}
return $arr;
}
/**
* Convert deeply an object to array
*
* @param array|object $input
* @return array
* @throws \RuntimeException If $input var is a scalar value.
*/
public static function objectToArray($input): array
{
// Only process if it's an object or array being passed to the function.
if (is_object($input) or is_array($input)) {
$ret = (array)$input;
foreach ($ret as &$item) {
// Recursively process EACH element only if not scalar.
$item = (is_object($item) or is_array($item)) ?
self::objectToArray($item) : $item;
}
return $ret;
} else {
throw new \RuntimeException('Cannot handle $input of type ' . \gettype($input));
}
}
/**
* Convert deeply an array to object
*
* @param array|object $input
* @return object
* @throws \RuntimeException If $input var is a scalar value.
*/
public static function arrayToObject($input): object
{
// Only process if it's an object or array being passed to the function.
if (is_object($input) or is_array($input)) {
$ret = (object)$input;
foreach ($ret as &$item) {
// Recursively process EACH element only if not scalar.
$item = (is_object($item) or is_array($item)) ?
self::arrayToObject($item) : $item;
}
return $ret;
} else {
throw new \RuntimeException('Cannot handle $input of type ' . \gettype($input));
}
}
/**
* Validate picture
*
* @param string $attribute
* @param string|null $value
* @param callback $fail
* @param object $sizes
* @return void
*/
public static function validatePicture(string $attribute, string|null $value, callable $fail, object $sizes)
{
if (!($useStorage = Storage::exists($value)) and !File::exists($value)) {
$fail(trans(':attribute n\'existe pas dans le système de fichier', [
'attribute' => $attribute
]));
return;
}
/** @var string */
$fileFullPath = self::getUploadedFileFullPath($value, $useStorage);
if (
!\in_array(self::getUploadedFileMimeType($value, $useStorage), [
'image/jpeg', 'image/jpg', 'image/png', 'image/gif'
])
) {
$fail(trans(':attribute doit être une image de type \'image/jpeg,image/jpg,image/png,image/gif\'', [
'attribute' => $attribute
]));
}
if (!($dimensions = \getimagesize($fileFullPath)) or !isset($dimensions[0]) or !isset($dimensions[1])) {
$fail(trans(':attribute impossible de valider les dimensions', [
'attribute' => $attribute
]));
} else {
$width = intval($dimensions[0]);
$height = intval($dimensions[1]);
$xMessage = trans(
":attribute la largeur doit être comprise entre {$sizes->minWith}px et {$sizes->maxWith}px"
);
if ($sizes->minWith === $sizes->maxWith) {
$xMessage = trans(":attribute la largeur doit être de {$sizes->minWith}px");
}
$yMessage = trans(
":attribute la hauteur doit être comprise entre {$sizes->minWith}px et {$sizes->maxWith}px"
);
if ($sizes->minHeight === $sizes->maxHeight) {
$yMessage = trans(":attribute la hauteur doit être de {$sizes->minWith}px");
}
if (($width < $sizes->minWith) or ($width > $sizes->maxWith)) {
$fail($xMessage);
}
if (($height < $sizes->minHeight) or ($height > $sizes->maxHeight)) {
$fail($yMessage);
}
} //end if
}
/**
* Get the file full path
*
* @param string $value
* @param boolean $useStorage
* @return string
*/
private static function getUploadedFileFullPath(string $value, bool $useStorage): string
{
/** @var \Illuminate\Filesystem\FilesystemAdapter */
$disk = Storage::disk();
return !$useStorage ? (realpath($value) ?: $value) : $disk->path($value);
}
/**
* Get the file mimetype
*
* @param string $value
* @param boolean $useStorage
* @return string|false
*/
private static function getUploadedFileMimeType(string $value, bool $useStorage)
{
return !$useStorage ? File::mimeType($value) : Storage::mimeType($value);
}
/**
* Fetch class list from a specific namespace
*
* @param string $namespace
* @return array<string> An array of classe namespaces
*/
public static function getClassesUsingNamespace(string $namespace): array
{
return collect(File::files(\app_path(Str::of($namespace)->replaceFirst('\App', ''))))
->filter(fn (SplFileInfo $splFileInfo) => $splFileInfo->getExtension() === 'php')
->map(fn (SplFileInfo $splFileInfo) => Str::of($splFileInfo->getBasename())
->replace('.' . $splFileInfo->getExtension(), '')->prepend("{$namespace}\\"))->all();
}
/**
* Get all classes from a specific namespace that implement a specific interface
*
* @param string $namespace
* @param string $interface
* @return array<string> An array of classe namespaces
*/
public static function getClassesUsingInterface(string $namespace, string $interface): array
{
return Arr::where(
static::getClassesUsingNamespace($namespace),
fn (string $class_namespace) => \collect(class_implements($class_namespace, true))->filter()
->contains(fn (string $class_interface) => $class_interface === $interface)
);
}
/**
* Get validated pagination from request.
*
* @param integer $default
* @param string $field
* @param string $enumPath
* @return integer
*/
public static function getValidatedEnum(int $default, string $field, string $enumPath): int
{
try {
return \intval(Validator::make(
[$field => \request()->get($field)],
[$field => new Enum($enumPath)]
)->validated()[$field]);
} catch (ValidationException $e) {
return $default;
}
}
}

Some files were not shown because too many files have changed in this diff Show more