Compare commits

..

No commits in common. "master" and "fix-documents" have entirely different histories.

358 changed files with 21741 additions and 23763 deletions

View file

@ -1,13 +0,0 @@
# Browsers that we support
# https://github.com/browserslist/browserslist#readme
>= 0.5%
last 2 major versions
not dead
Chrome >= 60
Firefox >= 60
Firefox ESR
iOS >= 12
Safari >= 12
not Explorer <= 11

View file

@ -1,16 +0,0 @@
.git
*.md
node_modules/
npm-debug.log
.npmrc
tests/
Dockerfile*
.dockerignore
.git
.gitignore
README.md
LICENSE
.vscode
vendor/
public/build/
auth.json

View file

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

View file

@ -5,6 +5,8 @@ APP_DEBUG=true
APP_URL=http://localhost
API_TOKEN=toto
BRAND=
LOG_CHANNEL=stack
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
@ -31,20 +33,10 @@ MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME="${APP_NAME}"
MAIL_ADMIN_ADDRESS=null
# SENTRY
SENTRY_LARAVEL_DSN=
# SAML
SAML2_DEFAULT_TENANT_UUID=
SAML2_SP_CERT_x509=
SAML2_SP_CERT_PRIVATEKEY=
SAML2_CONTACT_TECHNICAL_EMAIL=
SAML2_CONTACT_SUPPORT_EMAIL=
# UWIL ADMIN SECURE
# - LIST SEPARATED WITH ',' (Required)
UWIL_ADMIN_SECURE_CIDR_LIST=""
# - LIST SEPARATED WITH ',' (Optional)
UWIL_ADMIN_SECURE_CIDR_PROJECT_ALLOWED_LIST=""
# LDC WEB STATUS CHECKER
LDC_STATUS_CHECKER_API_KEY="MY_KEY"
LDC_STATUS_CHECKER_UUID="UUID_PROJECT"

View file

@ -29,14 +29,7 @@
"sourceType": "module"
},
"plugins": ["vue", "@typescript-eslint"],
"ignorePatterns": [
"webpack.mix.js",
"**/*.php",
"**/*.svg",
"**/vendor/*.js",
"**/vendor/**/*.js",
"**/vendor/**/*.scss"
],
"ignorePatterns": ["webpack.mix.js", "**/vendor/*.js", "**/vendor/**/*.js"],
"rules": {
"indent": ["error", 4, { "SwitchCase": 1 }],
"linebreak-style": ["error", "unix"],
@ -44,19 +37,5 @@
"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"]
]
}
}
}
}

3
.gitattributes vendored
View file

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

3
.gitignore vendored
View file

@ -25,6 +25,3 @@ npm-debug.log
yarn-error.log
.vscode/settings.json
.npmrc
.zshrc
.composer/
.npm/

View file

@ -1,23 +0,0 @@
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/docker-multistage.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

View file

@ -1,11 +0,0 @@
## Comportement constaté
## Comportement attendu
## Solution envisagée
## Étapes pour la reproduction du bug
*

View file

@ -1,7 +0,0 @@
## Présentation du besoin
## Implémentation
## Maquettes, documentations...

14
.styleci.yml Normal file
View file

@ -0,0 +1,14 @@
php:
preset: laravel
version: 8
disabled:
- no_unused_imports
finder:
not-name:
- index.php
- server.php
js:
finder:
not-name:
- webpack.mix.js
css: true

View file

@ -18,7 +18,8 @@
],
"customSyntax": "postcss-scss",
"rules": {
"declaration-property-value-no-unknown": true,
"indentation": 4,
"string-quotes": "double",
"selector-max-id": null,
"color-no-invalid-hex": true,
"order/properties-alphabetical-order": null,

View file

@ -1,46 +0,0 @@
FROM composer:2.8 AS vendor
ARG GITLAB_USER
ARG GITLAB_PASSWORD
ARG PHP_8_5_TAG=latest
COPY database/ database/
COPY composer.json composer.json
COPY composer.lock composer.lock
RUN composer config gitlab-token.lab.s-web.fr ${GITLAB_USER} ${GITLAB_PASSWORD}
RUN composer install \
--ignore-platform-reqs \
--no-interaction \
--no-plugins \
--no-scripts \
--no-dev \
--prefer-dist
FROM node:20.18 AS frontend
ARG GITLAB_PASSWORD
RUN mkdir -p /app/public/build
COPY package*.json tsconfig.json vite.config.ts .eslintrc.json .stylelintrc.json /app/
COPY resources/ /app/resources/
COPY --from=vendor /app/vendor/ /app/vendor/
WORKDIR /app
RUN npm config set @ldc-web:registry https://lab.s-web.fr/api/v4/packages/npm/
RUN npm config set -- '//lab.s-web.fr/api/v4/packages/npm/:_authToken' "${GITLAB_PASSWORD}"
RUN npm ci && npm run prod
FROM registry.s-web.fr/ldc-web/projets/ldc-web-images-docker/uwil-production-8.5:${PHP_8_5_TAG}
ARG USER=www-data
COPY --chown=${USER}:${USER} . /app/srcs
COPY --chown=${USER}:${USER} ./resources/favicon /app/srcs/public
COPY --from=vendor --chown=${USER}:${USER} /app/vendor/ /app/srcs/vendor/
COPY --from=frontend --chown=${USER}:${USER} /app/public/build/ /app/srcs/public/build/
USER root
RUN chmod -R 755 /app/srcs/storage
USER ${USER}

View file

@ -8,6 +8,3 @@
&nbsp;
- continue migrations :
`php artisan migrate`
TODO:
- sur les méthodes GET le token est visible dans les logs server..

View file

@ -6,42 +6,44 @@ use App\Enums\Formularies\InputTypeEnum;
use App\Lib\Inputs\Definition\Input;
use App\Lib\Inputs\Definition\InputData;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Database\Eloquent\Model;
class AsInput implements CastsAttributes
{
/**
* Cast the given value.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $key
* @param mixed $value
* @param array $attributes
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $key
* @param mixed $value
* @param array $attributes
* @return \App\Lib\Inputs\Definition\Input
* @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceBeforeLastUsed
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceAfterLastUsed
*/
public function get(Model $model, string $key, mixed $value, array $attributes): Input
public function get($model, string $key, $value, array $attributes): Input
{
// phpcs:enable
/** @var array<string, integer|string|boolean> $inputData */
/** @var string $class */
/** @var array<integer|string|boolean> $data */
$inputData = \json_decode($value, true);
$inputClass = InputTypeEnum::make($inputData['type'])->getInputClass();
$inputClass = InputTypeEnum::make($inputData['type'])->inputClass;
return $inputClass::from(InputData::make(...\array_values($inputData)), $inputData['required'] ?? false);
}
/**
* Prepare the given value for storage.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $key
* @param mixed $value
* @param array $attributes
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $key
* @param \App\Lib\Inputs\Definition\Input $value
* @param array $attributes
* @return string Json encoded \App\Lib\Inputs\Definition\Input
* @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceBeforeLastUsed
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceAfterLastUsed
*/
public function set(Model $model, string $key, mixed $value, array $attributes): string
public function set($model, string $key, $value, array $attributes)
{
// phpcs:enable
$this->assertIsInput($value);
@ -51,12 +53,11 @@ class AsInput implements CastsAttributes
/**
* Assert is instance of \App\Lib\Inputs\Definition\Input
*
* @param mixed $value
* @return void
* @throws \RuntimeException If the value is not an input.
*/
private function assertIsInput(mixed $value): void
private function assertIsInput($value): void
{
if (!\is_object($value) or !\in_array(Input::class, \class_parents($value))) {
$value = \json_encode($value);

View file

@ -28,30 +28,37 @@ class CreateAdmin extends Command
protected $description = 'Create a backend admin user';
/**
* The first name of the new user.
* The firstname of the new user.
*
* @var string|null
* @var string
*/
protected $first_name;
protected $firstname;
/**
* The last name of the new user.
* The lastname of the new user.
*
* @var string|null
* @var string
*/
protected $last_name;
protected $lastname;
/**
* The email of the new user.
*
* @var string|null
* @var string
*/
protected $email;
/**
* The password of the new user.
*
* @var string
*/
protected $password;
/**
* The role of the new user.
*
* @var string|null
* @var string
*/
protected $role;
@ -60,26 +67,30 @@ class CreateAdmin extends Command
*
* @return integer
*/
public function handle(): int
public function handle()
{
$this->first_name = null;
$this->firstname = null;
$this->addFirstname();
$this->last_name = null;
$this->lastname = 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,
'firstname' => $this->firstname,
'lastname' => $this->lastname,
'email' => $this->email,
'email_verified_at' => now(),
'role' => $this->role,
'password' => $this->password,
'role' => $this->role
]);
$user->saveOrFail();
@ -95,14 +106,14 @@ class CreateAdmin extends Command
*/
private function addFirstname(): void
{
while (is_null($this->first_name)) {
while (is_null($this->firstname)) {
try {
$tmp = $this->ask('Type the wanted user firstname', 'Admin');
$validator = Validator::make(['firstname' => $tmp], [
'firstname' => 'required|min:3|max:255'
]);
$validator->validate();
$this->first_name = $tmp;
$this->firstname = $tmp;
} catch (ValidationException $e) {
$this->error(sprintf(
'Please provide a valid firstname %s',
@ -120,14 +131,14 @@ class CreateAdmin extends Command
*/
private function addLastname(): void
{
while (is_null($this->last_name)) {
while (is_null($this->lastname)) {
try {
$tmp = $this->ask('Type the wanted user lastname', 'Saïtama');
$validator = Validator::make(['lastname' => $tmp], [
'lastname' => 'required|min:3|max:255'
]);
$validator->validate();
$this->last_name = $tmp;
$this->lastname = $tmp;
} catch (ValidationException $e) {
$this->error(sprintf(
'Please provide a valid lastname %s',
@ -163,6 +174,22 @@ class CreateAdmin extends Command
}; //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.
*
@ -174,16 +201,16 @@ class CreateAdmin extends Command
try {
$tmp = $this->choice(
'Select his role',
[UserRolesEnum::admin->label(), UserRolesEnum::client->label()],
UserRolesEnum::client->label(),
[UserRolesEnum::admin()->label, UserRolesEnum::client()->label],
UserRolesEnum::client()->label,
$maxAttempts = null,
$allowMultipleSelections = false
);
if ($tmp === UserRolesEnum::admin->label()) {
$tmp = UserRolesEnum::admin;
if ($tmp === UserRolesEnum::admin()->label) {
$tmp = UserRolesEnum::admin();
}
if ($tmp === UserRolesEnum::client->label()) {
$tmp = UserRolesEnum::client;
if ($tmp === UserRolesEnum::client()->label) {
$tmp = UserRolesEnum::client();
}
$validator = Validator::make(['role' => $tmp], [
'role' => 'required'
@ -192,7 +219,7 @@ class CreateAdmin extends Command
$this->role = $tmp;
} catch (ValidationException $e) {
$this->error(sprintf(
'Please choose from the selection %s',
'Please choose from the selection',
\implode(',', collect($e->errors())->flatten()->all())
));
continue;

View file

@ -2,17 +2,30 @@
namespace App\Console;
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)
{
}
/**
* Register the commands for the application.
*
* @return void
*/
protected function commands(): void
protected function commands()
{
$this->load(__DIR__ . '/Commands');
require base_path('routes/console.php');
}
}

View file

@ -2,22 +2,37 @@
namespace App\Enums;
use Uwil\LaravelCommon\Traits\Enums\BaseEnum;
use Kwaadpepper\Enum\BaseEnumRoutable;
enum AppUserRolesEnum: int
/**
* This defines a data type.
*
* @method static self total() total role
* @method static self user() user role
*/
final class AppUserRolesEnum extends BaseEnumRoutable
{
use BaseEnum;
case total = 1;
case user = 2;
/**
* enum values
* @return array
*/
protected static function values(): array
{
return [
'total' => 1,
'user' => 2,
];
}
/**
* Optionnal labels definition.
*
* @phpstan-ignore-next-line
* enum labels
* @return array
*/
private const LABELS = [
self::total->name => 'Total',
self::user->name => 'Utilisateur',
];
protected static function labels(): array
{
return [
'total' => 'Total',
'user' => 'Utilisateur',
];
}
}

View file

@ -2,26 +2,43 @@
namespace App\Enums\Documents;
use Uwil\LaravelCommon\Traits\Enums\BaseEnum;
use Kwaadpepper\Enum\BaseEnumRoutable;
enum LangEnum: int
/**
* This defines a data type.
*
* @method static self fr()
* @method static self en()
* @method static self de()
* @method static self nl()
*/
final class LangEnum extends BaseEnumRoutable
{
use BaseEnum;
case fr = 1;
case en = 2;
case de = 3;
case nl = 4;
/**
* enum values
* @return array
*/
protected static function values(): array
{
return [
'fr' => 1,
'en' => 2,
'de' => 3,
'nl' => 4,
];
}
/**
* Optionnal labels definition.
*
* @phpstan-ignore-next-line
* enum labels
* @return array
*/
private const LABELS = [
self::fr->name => 'Français (FR)',
self::en->name => 'Anglais (EN)',
self::de->name => 'Allemand (DE)',
self::nl->name => 'Néerlandais (NL)',
];
protected static function labels(): array
{
return [
'fr' => 'Français (FR)',
'en' => 'Anglais (EN)',
'de' => 'Allemand (DE)',
'nl' => 'Néerlandais (NL)',
];
}
}

View file

@ -2,36 +2,37 @@
namespace App\Enums\Documents;
use Uwil\LaravelCommon\Traits\Enums\BaseEnum;
use Kwaadpepper\Enum\BaseEnumRoutable;
enum TypeEnum: int
/**
* This defines a data type.
*
* @method static self pdf()
* @method static self mp4()
*/
final class TypeEnum extends BaseEnumRoutable
{
use BaseEnum;
case pdf = 1;
case mp4 = 2;
/**
* Optionnal labels definition.
*
* @phpstan-ignore-next-line
* enum values
* @return array
*/
private const LABELS = [
self::pdf->name => 'PDF',
self::mp4->name => 'MP4',
];
/**
* Get translation key.
*
* @return string
*/
public function icon(): string
protected static function values(): array
{
return match ($this) {
self::pdf => 'file',
self::mp4 => 'video',
};
return [
'pdf' => 'pdf',
'mp4' => 'mp4',
];
}
/**
* enum labels
* @return array
*/
protected static function labels(): array
{
return [
'pdf' => 'PDF',
'mp4' => 'MP4',
];
}
}

View file

@ -2,24 +2,40 @@
namespace App\Enums\Formularies;
use Uwil\LaravelCommon\Traits\Enums\BaseEnum;
use Kwaadpepper\Enum\BaseEnumRoutable;
enum InputStatusEnum: int
/**
* This defines a data type.
*
* @method static self system() input system
* @method static self locked() input locked
* @method static self default() input default
*/
final class InputStatusEnum extends BaseEnumRoutable
{
use BaseEnum;
case system = 1;
case locked = 2;
case default = 3;
/**
* enum values
* @return array
*/
protected static function values(): array
{
return [
'system' => 1,
'locked' => 2,
'default' => 3,
];
}
/**
* Optionnal labels definition.
*
* @phpstan-ignore-next-line
* enum labels
* @return array
*/
private const LABELS = [
self::system->name => 'system',
self::locked->name => 'verrouillé',
self::default->name => 'default',
];
protected static function labels(): array
{
return [
'system' => __('system'),
'locked' => __('verrouillé'),
'default' => __('default'),
];
}
}

View file

@ -9,50 +9,87 @@ use App\Lib\Inputs\InputRadio;
use App\Lib\Inputs\InputSelect;
use App\Lib\Inputs\InputSelectMultiple;
use App\Lib\Inputs\InputText;
use Uwil\LaravelCommon\Traits\Enums\BaseEnum;
use Kwaadpepper\Enum\BaseEnumRoutable;
enum InputTypeEnum: int
/**
* This defines a data type.
*
* @property-read integer value
* @property-read string label
* @property-read string inputClass InputType class.
*
* @method static self text() input text
* @method static self email() input email
* @method static self phone() input phone
* @method static self radio() input radio
* @method static self select() input select
* @method static self selectMultiple() input selectMultiple
* @method static self checkbox() input checkbox
*/
final class InputTypeEnum extends BaseEnumRoutable
{
use BaseEnum;
case text = 1;
case email = 2;
case phone = 3;
case radio = 4;
case select = 5;
case selectMultiple = 6;
case checkbox = 7;
/**
* Optionnal labels definition.
*
* @phpstan-ignore-next-line
* enum values
* @return array
*/
private const LABELS = [
self::text->name => 'texte',
self::email->name => 'email',
self::phone->name => 'téléphone',
self::radio->name => 'radio',
self::select->name => 'liste déroulante',
self::selectMultiple->name => 'liste déroulante à choix multiple',
self::checkbox->name => 'case à cocher',
];
/**
* Get InputType class based on the enum value.
*
* @return string
*/
public function getInputClass(): string
protected static function values(): array
{
return match ($this) {
self::text => InputText::class,
self::email => InputEmail::class,
self::phone => InputPhone::class,
self::radio => InputRadio::class,
self::select => InputSelect::class,
self::selectMultiple => InputSelectMultiple::class,
self::checkbox => InputCheckbox::class,
};
return [
'text' => 1,
'email' => 2,
'phone' => 3,
'radio' => 4,
'select' => 5,
'selectMultiple' => 6,
'checkbox' => 7,
];
}
/**
* enum labels
* @return array
*/
protected static function labels(): array
{
return [
'text' => __('texte'),
'email' => __('e-mail'),
'phone' => __('téléphone'),
'radio' => __('radio'),
'select' => __('liste déroulante'),
'selectMultiple' => __('liste déroulante à choix multiple'),
'checkbox' => __('case à cocher'),
];
}
/**
* Enum input Types
*
* @return array
*/
protected static function inputTypes(): array
{
return [
'text' => InputText::class,
'email' => InputEmail::class,
'phone' => InputPhone::class,
'radio' => InputRadio::class,
'select' => InputSelect::class,
'selectMultiple' => InputSelectMultiple::class,
'checkbox' => InputCheckbox::class,
];
}
/**
* @param string $attribute
* @return integer|string
* @throws \Kwaadpepper\Enum\Exceptions\UnknownEnumProperty If the prop cannot be found.
*/
public function __get(string $attribute)
{
if ($attribute === 'inputClass') {
return $this->inputTypes()[$this->getDefinition()];
}
return parent::__get($attribute);
}
}

View file

@ -1,21 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Enums;
use Uwil\LaravelCommon\Traits\Enums\BaseEnum;
/**
* Defines the available pagination page size options.
*/
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,30 @@
<?php
namespace App\Enums\Pagination;
use Kwaadpepper\Enum\BaseEnumRoutable;
/**
* @method static self five()
* @method static self twelve()
* @method static self twentyfive()
* @method static self fifty()
* @method static self onehundred()
*/
final class ItemsPerPaginationEnum extends BaseEnumRoutable
{
/**
* enum values
* @return array
*/
protected static function values(): array
{
return [
'five' => 5,
'twelve' => 12,
'twentyfive' => 25,
'fifty' => 50,
'onehundred' => 100
];
}
}

View file

@ -2,32 +2,52 @@
namespace App\Enums;
use Uwil\LaravelCommon\Traits\Enums\BaseEnum;
use Kwaadpepper\Enum\BaseEnumRoutable;
enum SettingTypesEnum: int
/**
* This defines a data type.
*
* @method static self string() string type
* @method static self numeric() numeric type
* @method static self picture() picture type
* @method static self json() string type
* @method static self boolean() boolean type
* @method static self color() color type
* @method static self directory() directory type
*/
final class SettingTypesEnum extends BaseEnumRoutable
{
use BaseEnum;
case string = 1;
case numeric = 2;
case picture = 3;
case json = 4;
case boolean = 5;
case color = 6;
case directory = 7;
/**
* enum values
* @return array
*/
protected static function values(): array
{
return [
'string' => 1,
'numeric' => 2,
'picture' => 3,
'json' => 4,
'boolean' => 5,
'color' => 6,
'directory' => 7
];
}
/**
* Optionnal labels definition.
*
* @phpstan-ignore-next-line
* enum labels
* @return array
*/
private const LABELS = [
self::string->name => 'String (Hexa)',
self::numeric->name => 'Numeric',
self::picture->name => 'Picture',
self::json->name => 'Json',
self::boolean->name => 'Boolean',
self::color->name => 'Color',
self::directory->name => 'Directory',
];
protected static function labels(): array
{
return [
'string' => 'String (Hexa)',
'numeric' => 'Numeric',
'picture' => 'Picture',
'json' => 'Json',
'boolean' => 'Boolean',
'color' => 'Color',
'directory' => 'Directory',
];
}
}

View file

@ -2,22 +2,37 @@
namespace App\Enums;
use Uwil\LaravelCommon\Traits\Enums\BaseEnum;
use Kwaadpepper\Enum\BaseEnumRoutable;
enum UserRolesEnum: int
/**
* This defines a data type.
*
* @method static self admin() admin role
* @method static self client() client role
*/
final class UserRolesEnum extends BaseEnumRoutable
{
use BaseEnum;
case admin = 1;
case client = 2;
/**
* enum values
* @return array
*/
protected static function values(): array
{
return [
'admin' => 1,
'client' => 2,
];
}
/**
* Optionnal labels definition.
*
* @phpstan-ignore-next-line
* enum labels
* @return array
*/
private const LABELS = [
self::admin->name => 'Administrateur',
self::client->name => 'Client',
];
protected static function labels(): array
{
return [
'admin' => 'Administrateur',
'client' => 'Client',
];
}
}

View file

@ -17,7 +17,7 @@ class Handler extends ExceptionHandler
/**
* A list of the inputs that are never flashed for validation exceptions.
*
* @var list<string>
* @var array<int, string>
*/
protected $dontFlash = [
'current_password',
@ -30,12 +30,9 @@ class Handler extends ExceptionHandler
*
* @return void
*/
public function register(): void
public function register()
{
$this->reportable(function (Throwable $e) {
if (app()->bound('sentry')) {
app('sentry')->captureException($e);
}
});
}
}

View file

@ -11,21 +11,12 @@ class AppController extends Controller
{
/**
* Get App Config.
*
* @return \App\Http\Resources\ApiResponseResource
* @return ApiResponseResource
*/
public function get(): \App\Http\Resources\ApiResponseResource
public function get()
{
/** @var string $tenantKey */
$tenantKey = request()->tenant;
$response = [];
$settings = Setting::query()
->where('tenant_id', $tenantKey)
->orWhere('key', 'Button1')
->orWhere('key', 'Button2')
->orWhere('key', 'Button3')
->get();
$settings = Setting::where('key', 'Button1')->orWhere('key', 'Button2')->orWhere('key', 'Button3')->get();
foreach ($settings as $setting) {
$directory = Directory::find($setting->value)->only('id', 'name');

View file

@ -12,17 +12,17 @@ class SettingsController extends Controller
/**
* Get Settings.
*
* @return \App\Http\Resources\ApiResponseResource
* @return \Illuminate\Http\JsonResponse
*/
public function get(): \App\Http\Resources\ApiResponseResource
public function get()
{
$response = [];
$settings = Setting::all();
foreach ($settings as $setting) {
$response[$setting->key] =
SettingTypesEnum::numeric->equals($setting->type) ||
SettingTypesEnum::boolean->equals($setting->type) ||
SettingTypesEnum::directory->equals($setting->type)
SettingTypesEnum::numeric()->equals($setting->type) ||
SettingTypesEnum::boolean()->equals($setting->type) ||
SettingTypesEnum::directory()->equals($setting->type)
? \intval($setting->value)
: $setting->value;
}

View file

@ -19,6 +19,7 @@ use App\Models\Directory;
use App\Models\Document;
use App\Models\Formulary;
use App\Models\Setting;
use Google\Cloud\Vision\V1\ImageAnnotatorClient;
use GuzzleHttp\Exception\ClientException;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
@ -31,13 +32,13 @@ use Illuminate\Support\Facades\URL;
class SyncController extends Controller
{
/**
* @return \Illuminate\Http\Resources\Json\AnonymousResourceCollection
* @param \Illuminate\Http\Request $request
* @return \App\Http\Resources\AppUserCollection
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
*/
public function getUsers(): \Illuminate\Http\Resources\Json\AnonymousResourceCollection
public function getUsers(Request $request)
{
return AppUserResource::collection(
AppUser::query()->where('tenant_id', \tenant()->getKey())->where('status', 1)->get()
);
return AppUserResource::collection(AppUser::where('status', 1)->get());
}
// *** --- START CONTACTS ---
@ -48,11 +49,11 @@ class SyncController extends Controller
* @param \App\Models\AppUser $app_user
* @return \Illuminate\Http\Resources\Json\AnonymousResourceCollection
*/
public function getContacts(AppUser $app_user): \Illuminate\Http\Resources\Json\AnonymousResourceCollection
public function getContacts(AppUser $app_user)
{
return ContactResource::collection(
Contact::query()->whereHas('appUser', function (Builder $q) use ($app_user) {
$q->where('tenant_id', \tenant()->getKey())->where('id', $app_user->id);
$q->where('id', $app_user->id);
})->get()
);
}
@ -62,49 +63,43 @@ class SyncController extends Controller
*
* @param \App\Http\Requests\Api\Contacts\UpsertContactsRequest $request
* @param \App\Models\AppUser $app_user
* @return \Illuminate\Http\JsonResponse
* @return \Illuminate\Http\Resources\Json\AnonymousResourceCollection
*/
public function upsertContact(
UpsertContactsRequest $request,
AppUser $app_user
): \Illuminate\Http\JsonResponse {
public function upsertContact(UpsertContactsRequest $request, AppUser $app_user)
{
/** @var \Illuminate\Support\Collection<string,integer> */
$formularies = Formulary::query()->pluck('slug')->flip();
$contactQuery = Contact::query()->whereHas('appUser', function (Builder $q) use ($app_user) {
$q->where('tenant_id', \tenant()->getKey())->where('id', $app_user->getKey());
$q->where('id', $app_user->id);
});
\collect($request->contacts)->each(
function (array $contactRequestData) use ($contactQuery, $formularies, $app_user) {
/** @var \App\Models\Contact $model */
$model = (clone $contactQuery)->where('id', Arr::get($contactRequestData, 'id'))->firstOrNew();
\collect($request->contacts)->each(function (array $contactRequestData) use ($contactQuery, $formularies) {
/** @var \App\Models\Contact */
$model = (clone $contactQuery)->where('id', Arr::get($contactRequestData, 'id'))->firstOrNew();
$contactRequestData['timestamp'] = Carbon::parse($contactRequestData['timestamp'], 'UTC')
->setTimezone(\now()->getTimezone());
// @phpstan-ignore logicalAnd.leftAlwaysTrue
if ($model->timestamp and $model->timestamp->gte($contactRequestData['timestamp'])) {
return;
}
// * Set parse values on data prop to fill in model.
$contactRequestData['data'] = \collect($contactRequestData['dataParsed'])
// * FILTER CONTACT DATA WITH ACTUAL FORMULARY
->filter(function (Input $input) use ($formularies) {
return $formularies->has($input->data->formSlug);
});
$model->fill($contactRequestData);
$model->app_user_id = $app_user->getKey();
// * Send contact to Feature
if (
!empty($contactRequestData['send_to_app_user_id']) and
($destUser = AppUser::query()->where('id', $contactRequestData['send_to_app_user_id'])->first())
) {
$model->appUser()->associate($destUser);
}
$model->saveOrFail();
$contactRequestData['timestamp'] = Carbon::parse($contactRequestData['timestamp'], 'UTC')
->setTimezone(\now()->getTimezone());
if ($model->timestamp and $model->timestamp->gte($contactRequestData['timestamp'])) {
return;
}
);
// * Set parse values on data prop to fill in model.
$contactRequestData['data'] = \collect($contactRequestData['dataParsed'])
// * FILTER CONTACT DATA WITH ACTUAL FORMULARY
->filter(function (Input $input) use ($formularies) {
return $formularies->has($input->data->formSlug);
});
$model->fill($contactRequestData);
// * Send contact to Feature
if (
!empty($contactRequestData['send_to_app_user_id']) and
($destUser = AppUser::query()->where('id', $contactRequestData['send_to_app_user_id'])->first())
) {
$model->appUser()->associate($destUser);
}
$model->saveOrFail();
});
// * HTTP 200 is ok.
return \response()->json();
@ -115,13 +110,13 @@ class SyncController extends Controller
*
* @param \App\Http\Requests\Api\Contacts\DestroyContactsRequest $request
* @param \App\Models\AppUser $app_user
* @return \Illuminate\Http\JsonResponse
* @return \Illuminate\Http\Resources\Json
*/
public function destroyContacts(DestroyContactsRequest $request, AppUser $app_user): \Illuminate\Http\JsonResponse
public function destroyContacts(DestroyContactsRequest $request, AppUser $app_user)
{
Contact::query()->whereIn('id', $request->validated()['contactUuidList'])
Contact::query()->whereIn('id', $request->contactUuidList)
->whereHas('appUser', function (Builder $q) use ($app_user) {
$q->where('tenant_id', \tenant()->getKey())->where('id', $app_user->id);
$q->where('id', $app_user->id);
})->delete();
// * HTTP 200 is ok.
@ -131,42 +126,72 @@ class SyncController extends Controller
// *** --- END CONTACTS ---
/**
* @return \Illuminate\Http\Resources\Json\AnonymousResourceCollection
* @param \Illuminate\Http\Request $request
* @return \App\Http\Resources\DirectoriesCollection
*/
public function getDirectories(): \Illuminate\Http\Resources\Json\AnonymousResourceCollection
public function getDirectories(Request $request)
{
return DirectoryResource::collection(
Directory::query()->where('tenant_id', \tenant()->getKey())->orderBy('order')->get()
);
return DirectoryResource::collection(Directory::query()->orderBy('order')->get());
}
/**
* @return \Illuminate\Http\Resources\Json\AnonymousResourceCollection
* @param \Illuminate\Http\Request $request
* @return \App\Http\Resources\DocumentsCollection
*/
public function getDocuments(): \Illuminate\Http\Resources\Json\AnonymousResourceCollection
public function getDocuments(Request $request)
{
return DocumentResource::collection(
Document::query()->where('tenant_id', \tenant()->getKey())->where('status', 1)->get()
);
return DocumentResource::collection(Document::where('status', 1)->get());
}
/**
* @return \Illuminate\Http\Resources\Json\AnonymousResourceCollection
* @param \Illuminate\Http\Request $request
* @return array
*/
public function getFormularies(): \Illuminate\Http\Resources\Json\AnonymousResourceCollection
public function getVersion(Request $request)
{
return FormularyResource::collection(
Formulary::query()->where('tenant_id', \tenant()->getKey())->orderBy('order')->get()
);
$app_uuid = $request->app_uuid;
$app_version = $request->app_version;
$version = Setting::select('value AS version')->where('key', 'version')->first();
$ShowIsActive = Setting::select('value AS ShowIsActive')->where('key', 'ShowIsActive')->first();
try {
$client = new \GuzzleHttp\Client();
$response = $client->request(
'GET',
'https://store.ldc.fr/api/get-version?token=@P$oi6or!bL2-r%3C&app_uuid=' . $app_uuid
);
$body = $response->getBody();
$storeVersion = json_decode($body)->version;
} catch (ClientException $e) {
$storeVersion = $app_version;
}
if ($storeVersion > $app_version) {
$newApp = true;
} else {
$newApp = false;
}
return ['version' => $version->version, 'ShowIsActive' => $ShowIsActive->ShowIsActive, 'newApp' => $newApp];
}
/**
* @param \App\Http\Requests\Api\SendFlyerRequest $request
* @return \App\Http\Resources\ContactsCollection
*/
public function getFormularies()
{
return FormularyResource::collection(Formulary::query()->orderBy('order')->get());
}
/**
* NOTE: This should be refactored, on app side also.
* @param \Illuminate\Http\Request $request
* @return string
*/
public function sendFlyer(Request $request): string
public function sendFlyer(Request $request)
{
$requestData = $request->emails;
// FIXME: This should be validated OMG.
$requestData = \json_decode($request->emails, true);
$email = $requestData['email'];
$subject = $requestData['subject'];
$message = $requestData['message'];
@ -179,14 +204,12 @@ class SyncController extends Controller
$filesName = [];
$noExists = [];
foreach ($docs as $doc) {
$exists = Storage::disk('public')
->exists(sprintf('docs/%s/%s.%s', \tenant()->getKey(), $doc['uuid'], $doc['type']));
$exists = Storage::disk('public')->exists('docs/' . $doc['uuid'] . '.' . $doc['type']);
if ($exists) {
array_push($attach, config('app.url') . URL::temporarySignedRoute(
array_push($attach, URL::temporarySignedRoute(
'docs.download',
now()->addDays(30),
['tenant' => \tenant()->getKey(), 'uuid' => $doc['uuid']],
false
['uuid' => $doc['uuid']]
));
array_push($filesName, $doc['title']);
} else {
@ -194,26 +217,56 @@ class SyncController extends Controller
}
}
if (count($noExists) === 0) {
$mail = new SendFlyer([
'body' => $message,
'fromName' => $user->firstname . ' ' . $user->lastname,
'subject' => $subject,
'attach' => $attach,
$message = new SendFlyer([
'body' => $message,
'from' => $user->email,
'fromName' => $user->firstname . ' ' . $user->lastname,
'subject' => '[' . config('app.BRAND') . '] - ' . $subject,
'attach' => $attach,
'filesName' => $filesName,
]);
Mail::to($email)->cc($user->email)->send($mail);
Mail::to($email)
->cc($user->email)
->queue($message);
} else {
$mail = new SendFlyerFailed([
'email' => $email,
'message' => $message,
'subject' => $subject,
$message = new SendFlyerFailed([
'email' => $email,
'message' => $message,
'subject' => $subject,
'filesName' => $filesName,
'noExists' => $noExists
'noExists' => $noExists
]);
Mail::to($user->email)->send($mail);
Mail::to($user->email)
->queue($message);
} //end if
return 'OK';
}
/**
* @param \Illuminate\Http\Request $request
* @return void
*/
public function processOcr(Request $request)
{
// Instantiates a client.
$imageAnnotator = new ImageAnnotatorClient([
'credentials' => public_path() . '/credentials.json'
]);
// The name of the image file to annotate.
$fileName = 'test.jpg';
// Prepare the image to be annotated.
$image = file_get_contents(public_path() . "/" . $fileName);
// Performs label detection on the image file.
$response = $imageAnnotator->textDetection($image);
$labels = $response->getTextAnnotations();
foreach ($labels as $label) {
echo $label->getDescription() . "<br>";
}
}
}

View file

@ -12,31 +12,25 @@ use App\Models\Directory;
use App\Traits\Controllers\ChangesModelOrder;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Maatwebsite\Excel\Facades\Excel;
class AppUserController extends Controller
{
use ChangesModelOrder;
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(AppUser::class);
}
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
* @return \Illuminate\Http\Response
*/
public function index(Request $request): \Illuminate\View\View
public function index(Request $request)
{
/** @var \Illuminate\Database\Eloquent\Builder $query */
$query = AppUser::query();
/** @var string $search Search field */
if ($request->search) {
$this->searchQuery(
$query,
@ -47,45 +41,36 @@ class AppUserController extends Controller
'email'
);
}
$searchFields = \implode(', ', [
trans('validation.attributes.last_name'),
trans('validation.attributes.first_name'),
trans('validation.attributes.email'),
]);
$searchFields = trans('Nom, Prénom, Email');
/** Sort columns with a query */
$this->sortQuery($query);
$appUserModels = $this->paginate($query);
$app_users = $this->paginate($query);
return view('bo.pages.app_users.index', compact('appUserModels', 'searchFields'));
return view('bo.pages.app-users.index', compact('app_users', 'searchFields'));
}
/**
* Show the form for creating a new resource.
*
* @param \App\Models\AppUser $app_user
* @return \Illuminate\View\View
* @return \Illuminate\Http\Response
*/
public function create(AppUser $app_user): \Illuminate\View\View
public function create()
{
$directories = Directory::renderDirectories(
Directory::query()->where('needAuthorization', 1)->orderBy('name', 'asc')->get()
);
$directories = Directory::renderDirectories(Directory::where('needAuthorization', 1)
->orderBy('name', 'ASC')->get());
return view('bo.pages.app_users.create', [
'appUserModel' => $app_user,
'directories' => $directories
]);
return view('bo.pages.app-users.create', compact('directories'));
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\AppUser\StoreAppUserRequest $request
* @return \Illuminate\Http\RedirectResponse
* @param \App\Http\Requests\Bo\AppUser\StoreAppUserRequest $request
* @return \Illuminate\Http\Response
*/
public function store(StoreAppUserRequest $request): \Illuminate\Http\RedirectResponse
public function store(StoreAppUserRequest $request)
{
return DB::transaction(function () use ($request) {
$app_user = new AppUser();
@ -94,10 +79,10 @@ class AppUserController extends Controller
if ($app_user->saveOrFail()) {
$app_user->directories()->sync(collect($request->directories)->pluck('id'));
return redirect()
->route('bo.app_users.edit', ['tenant' => \tenant(), 'app_user' => $app_user])
->route('bo.app-users.edit', $app_user)
->with('success', trans(
':model a été créé',
['model' => trans('models.classes.app_user')]
['model' => Str::singular(trans('models.classes.app_users'))]
));
}
@ -105,7 +90,7 @@ class AppUserController extends Controller
->back()
->with('error', trans(
':model n\'a pas été créé',
['model' => trans('models.classes.app_user')]
['model' => Str::singular(trans('models.classes.app_users'))]
));
});
}
@ -114,19 +99,17 @@ class AppUserController extends Controller
* Show the form for editing the specified resource.
*
* @param \App\Models\AppUser $app_user
* @return \Illuminate\View\View
* @return \Illuminate\Http\Response
*/
public function edit(AppUser $app_user): \Illuminate\View\View
public function edit(AppUser $app_user)
{
$directories = Directory::renderDirectories(
Directory::query()->where('needAuthorization', 1)->orderBy('name', 'asc')->get()
);
// @phpstan-ignore assign.propertyReadOnly
$directories = Directory::renderDirectories(Directory::query()->where('needAuthorization', 1)
->orderBy('name', 'ASC')->get());
$app_user->directories = Directory::renderDirectories($app_user->directories);
return view('bo.pages.app_users.edit', [
'appUserModel' => $app_user,
'directories' => $directories
return view('bo.pages.app-users.edit', [
'app_userModel' => $app_user,
'directories' => $directories
]);
}
@ -135,9 +118,9 @@ class AppUserController extends Controller
*
* @param \App\Http\Requests\Bo\AppUser\UpdateAppUserRequest $request
* @param \App\Models\AppUser $app_user
* @return \Illuminate\Http\RedirectResponse
* @return \Illuminate\Http\Response
*/
public function update(UpdateAppUserRequest $request, AppUser $app_user): \Illuminate\Http\RedirectResponse
public function update(UpdateAppUserRequest $request, AppUser $app_user)
{
$app_user->directories()->sync(collect($request->directories)->pluck('id'));
$params = $request->validated();
@ -149,17 +132,17 @@ class AppUserController extends Controller
if ($app_user->saveOrFail()) {
return redirect()
->route('bo.app_users.edit', ['tenant' => \tenant(), 'app_user' => $app_user])
->route('bo.app-users.edit', $app_user)
->with('success', trans(
':model a été mis à jour',
['model' => trans('models.classes.app_user')]
['model' => Str::singular(trans('models.classes.app_users'))]
));
}
return redirect()
->back()
->with('error', trans(
':model n\'a pas été mis à jour',
['model' => trans('models.classes.app_user')]
['model' => Str::singular(trans('models.classes.app_users'))]
));
});
}
@ -167,17 +150,17 @@ class AppUserController extends Controller
/**
* Remove the specified resource from storage.
*
* @param \App\Models\AppUser $app_user
* @return \Illuminate\Http\RedirectResponse
* @param \App\Models\AppUser $app_user
* @return \Illuminate\Http\Response
*/
public function destroy(AppUser $app_user): \Illuminate\Http\RedirectResponse
public function destroy(AppUser $app_user)
{
if ($app_user->deleteOrFail()) {
return redirect()
->route('bo.app_users.index', ['tenant' => \tenant()])
->route('bo.app-users.index')
->with('warning', trans(
':model a bien été supprimé',
['model' => trans('models.classes.app_user')]
['model' => Str::singular(trans('models.classes.app_users'))]
));
}
@ -185,21 +168,10 @@ class AppUserController extends Controller
->back()
->with('error', trans(
':model n\'a pas été supprimé',
['model' => trans('models.classes.app_user')]
['model' => Str::singular(trans('models.classes.app_users'))]
));
}
/**
* Duplicate the specified resource.
*
* @param \App\Models\AppUser $app_user
* @return \Illuminate\Contracts\View\View
*/
public function duplicate(AppUser $app_user): \Illuminate\Contracts\View\View
{
return $this->create($app_user->replicate());
}
/**
* Mass import by CSV.
* matricule;lastname;firstname;email
@ -208,11 +180,10 @@ class AppUserController extends Controller
* @param \App\Http\Requests\Bo\ImportRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function import(ImportRequest $request): \Illuminate\Http\RedirectResponse
public function import(ImportRequest $request)
{
Excel::import(new AppUserImport(), $request->import, null, \Maatwebsite\Excel\Excel::CSV);
return redirect()->route('bo.app_users.index', ['tenant' => \tenant()])
->with('success', 'Import terminé.');
return redirect()->route('bo.app-users.index')->with('success', 'Import terminé.');
}
}

View file

@ -25,7 +25,7 @@ class LoginController extends Controller
*
* @return \Illuminate\Contracts\Auth\StatefulGuard
*/
protected function guard(): \Illuminate\Contracts\Auth\StatefulGuard
protected function guard()
{
return Auth::guard('backend');
}
@ -35,14 +35,13 @@ class LoginController extends Controller
*
* @return \Illuminate\View\View
*/
public function showLoginForm(): \Illuminate\View\View
public function showLoginForm()
{
return view('bo.auth.login');
}
/**
* Where to redirect users after login.
*
* @return string
*/
protected function redirectTo(): string

View file

@ -15,14 +15,13 @@ class ResetPasswordController extends Controller
*
* @return \Illuminate\Contracts\Auth\StatefulGuard
*/
protected function guard(): \Illuminate\Contracts\Auth\StatefulGuard
protected function guard()
{
return Auth::guard('backend');
}
/**
* Where to redirect users after login.
*
* @return string
*/
protected function redirectTo(): string

View file

@ -11,27 +11,20 @@ use Illuminate\Database\Eloquent\Builder;
class ContactController extends Controller
{
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(Contact::class);
}
/**
* Display a listing of the resource.
*
* @param \App\Http\Requests\Bo\FilterCustomerRequest $request
* @return \Illuminate\View\View
* @return \Illuminate\Http\Response
*/
public function index(FilterCustomerRequest $request): \Illuminate\View\View
public function index(FilterCustomerRequest $request)
{
/** @var \Illuminate\Database\Eloquent\Builder $query */
$query = Contact::query()->with('appUser')->whereHas('appUser', function ($query) {
$query->where('tenant_id', tenant('id'));
});
$query = Contact::query();
$query->with('appUser');
/** @var string $search Search field */
if ($request->search) {
$this->searchQuery(
$query,
@ -42,11 +35,7 @@ class ContactController extends Controller
'email',
);
}
$searchFields = \implode(', ', [
trans('validation.attributes.last_name'),
trans('validation.attributes.email'),
trans('validation.attributes.phone'),
]);
$searchFields = \implode(',', [trans('Nom'), trans('Prénom'), trans('Email')]);
/** Sort columns with a query */
$this->sortQuery($query);
@ -57,26 +46,26 @@ class ContactController extends Controller
});
}
$contactModels = $this->paginate($query);
$contactModelList = $this->paginate($query);
$appUsers = AppUser::query()->select(['id', 'lastname', 'firstname'])->orderBy('lastname')->get();
$appUsers = AppUser::query()->select(['id', 'lastname', 'firstname'])->get();
return view('bo.pages.contacts.index', compact('contactModels', 'searchFields', 'appUsers'));
return view('bo.pages.contacts.index', compact('contactModelList', 'searchFields', 'appUsers'));
}
/**
* Display the specified resource.
*
* @param \App\Models\Contact $contact
* @return \Illuminate\View\View
* @return \Illuminate\Http\Response
*/
public function show(Contact $contact): \Illuminate\View\View
public function show(Contact $contact)
{
$contact->load('appUser.directories');
$fomularies = Formulary::all()->keyBy('name');
return view('bo.pages.contacts.show', [
return \view('bo.pages.contacts.show', [
'contactModel' => $contact,
'fomularies' => $fomularies
'fomularies' => $fomularies
]);
}
}

View file

@ -11,7 +11,7 @@ class DashboardController extends Controller
*
* @return \Illuminate\Contracts\View\View
*/
public function index(): \Illuminate\Contracts\View\View
public function index()
{
return view('bo.pages.dashboard');
}

View file

@ -6,55 +6,51 @@ use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\Directories\StoreDirectoryRequest;
use App\Http\Requests\Bo\Directories\UpdateDirectoryRequest;
use App\Models\Directory;
use App\Models\Document;
use App\Models\Setting;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Str;
class DirectoriesController extends Controller
{
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(Directory::class);
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Contracts\View\View
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
public function index(): \Illuminate\Contracts\View\View
public function index()
{
$directoryModels = $this->getDirectoriesForComponent();
return \view('bo.pages.directories.index', \compact('directoryModels'));
$directories = $this->getDirectoriesForComponent();
return \view('bo.pages.directories.index', \compact('directories'));
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\Directories\StoreDirectoryRequest $request
* @return \Illuminate\Http\RedirectResponse
* @param \App\Http\Requests\Bo\Directory\StoreDirectoryRequest $request
* @return \Illuminate\Http\Response
*/
public function store(StoreDirectoryRequest $request): \Illuminate\Http\RedirectResponse
public function store(StoreDirectoryRequest $request)
{
return DB::transaction(function () use ($request) {
$directory = new Directory();
$directory->fill($request->validated());
if ($directory->saveOrFail()) {
return redirect()
->route('bo.directories.index', ['tenant' => \tenant()])
->route('bo.directories.index')
->with('success', trans(
':model a été créé',
['model' => trans('models.classes.directory')]
['model' => Str::singular(trans('models.classes.directories'))]
));
}
return redirect()
->back()
->with('error', trans(
':model n\'a pas été créé',
['model' => trans('models.classes.directory')]
['model' => Str::singular(trans('models.classes.directories'))]
));
});
}
@ -63,10 +59,11 @@ class DirectoriesController extends Controller
* Show the form for editing the specified resource.
*
* @param \App\Models\Directory $directory
* @return \Illuminate\View\View
* @return \Illuminate\Http\Response
*/
public function edit(Directory $directory): \Illuminate\View\View
public function edit(Directory $directory)
{
$this->authorize('edit', $directory);
return view('bo.pages.directories.edit', ['directoryModel' => $directory]);
}
@ -75,26 +72,27 @@ class DirectoriesController extends Controller
*
* @param \App\Http\Requests\Bo\Directories\UpdateDirectoryRequest $request
* @param \App\Models\Directory $directory
* @return \Illuminate\Http\RedirectResponse
* @return \Illuminate\Http\Response
*/
public function update(UpdateDirectoryRequest $request, Directory $directory): \Illuminate\Http\RedirectResponse
public function update(UpdateDirectoryRequest $request, Directory $directory)
{
$this->authorize('edit', $directory);
return DB::transaction(function () use ($request, $directory) {
$directory->fill($request->validated());
if ($directory->saveOrFail()) {
return redirect()
->route('bo.directories.edit', ['tenant' => \tenant(), 'directory' => $directory])
->route('bo.directories.edit', $directory)
->with('success', trans(
':model a été mis à jour',
['model' => trans('models.classes.directory')]
['model' => Str::singular(trans('models.classes.directories'))]
));
}
return redirect()
->back()
->with('error', trans(
':model n\'a pas été mis à jour',
['model' => trans('models.classes.directory')]
['model' => Str::singular(trans('models.classes.directories'))]
));
});
}
@ -103,12 +101,12 @@ class DirectoriesController extends Controller
* Remove the specified resource from storage.
*
* @param \App\Models\Directory $directory
* @return \Illuminate\Database\Eloquent\Collection
* @return \Illuminate\Http\Response
*/
public function destroy(Directory $directory): \Illuminate\Database\Eloquent\Collection
public function destroy(Directory $directory)
{
$this->authorize('delete', $directory);
return DB::transaction(function () use ($directory) {
/** @var \App\Models\Directory $directoryChild */
foreach ($directory->children as $directoryChild) {
$directoryChild->directory_id = null;
$directoryChild->saveOrFail();
@ -116,8 +114,7 @@ class DirectoriesController extends Controller
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Document> */
$documents = $directory->documents()->get()->sortBy('lang', \SORT_REGULAR, true);
if ($documents->count()) {
$documents->each(function (\Illuminate\Database\Eloquent\Model $document) {
/** @var \App\Models\Document $document */
$documents->each(function (Document $document) {
$document->deleteOrFail();
});
}
@ -130,7 +127,7 @@ class DirectoriesController extends Controller
':model :name n\'a pas été supprimé,
un bouton est encore associé à ce dernier dans les paramètres :',
[
'model' => trans('models.classes.directory'),
'model' => Str::singular(trans('models.classes.directories')),
'name' => $directory->name['fr']
]
)
@ -149,7 +146,7 @@ class DirectoriesController extends Controller
* @param \Illuminate\Http\Request $request
* @return void
*/
public function saveOrder(Request $request): void
public function saveOrder(Request $request)
{
$this->assignOrder($request->directories, null);
}
@ -157,11 +154,11 @@ class DirectoriesController extends Controller
/**
* Assign the new order and directory id to each directory.
*
* @param array $directories
* @param integer|null $directoryId
* @param array $directories
* @param integer $directoryId
* @return void
*/
public function assignOrder(array $directories, int|null $directoryId = null): void
public function assignOrder(array $directories, int $directoryId = null)
{
foreach ($directories as $newDirectory) {
$directory = Directory::where('id', $newDirectory['id'])->first();
@ -178,27 +175,27 @@ class DirectoriesController extends Controller
* Save the new order/structure of directories.
*
* @param \Illuminate\Http\Request $request
* @return \App\Models\Directory
* @return \Illuminate\Http\Response
*/
public function needAuthorization(Request $request): \App\Models\Directory
public function needAuthorization(Request $request)
{
$directory = Directory::query()->where('id', $request->id)->firstOrFail();
$directory = Directory::where('id', $request->id)->firstOrFail();
$directory->needAuthorization = $directory->needAuthorization ? false : true;
$directory->saveOrFail();
return $directory;
if ($directory->saveOrFail()) {
return $directory;
}
}
/**
* Get directories for component.
* Get directories for component
*
* @return \Illuminate\Database\Eloquent\Collection
* @return \Illuminate\Database\Eloquent\Collection<array-key,\App\Models\Directory>
*/
private function getDirectoriesForComponent(): \Illuminate\Database\Eloquent\Collection
private function getDirectoriesForComponent()
{
return Directory::query()
->where('tenant_id', \tenant()->getKey())
->withCount('documents')
->orderBy('order', 'asc')
->get();
->orderBy('order', 'ASC')->get();
}
}

View file

@ -2,7 +2,6 @@
namespace App\Http\Controllers\Bo;
use App\Enums\Documents\LangEnum;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\Documents\StoreDocumentRequest;
use App\Http\Requests\Bo\Documents\UpdateDocumentRequest;
@ -12,6 +11,7 @@ use App\Models\Document;
use App\Models\Tag;
use App\Traits\Controllers\ChangesModelOrder;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\DB;
@ -25,26 +25,19 @@ class DocumentController extends Controller
{
use ChangesModelOrder;
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(Document::class);
}
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
* @return \Illuminate\Http\Response
*/
public function index(Request $request): \Illuminate\View\View
public function index(Request $request)
{
/** @var \Illuminate\Database\Eloquent\Builder $query */
$query = Document::query()->with(['directory', 'parentDocument']);
$query = Document::query();
$tags = Tag::get();
/** @var string $search Search field */
if ($request->search) {
$this->searchQuery(
$query,
@ -53,7 +46,7 @@ class DocumentController extends Controller
'title',
);
}
$searchFields = trans('validation.attributes.title');
$searchFields = trans('Titre');
/** Search by tags */
$query->when($request->tag, function ($query) use ($request) {
@ -65,43 +58,37 @@ class DocumentController extends Controller
/** Sort columns with a query */
$this->sortQuery($query);
$documentModels = $this->paginate($query);
$documents = $this->paginate($query);
return view('bo.pages.documents.index', [
'documentModels' => $documentModels,
'searchFields' => $searchFields,
'searchTag' => $request->tag,
'tags' => $tags
'documents' => $documents,
'searchFields' => $searchFields,
'searchTag' => $request->tag,
'tags' => $tags
]);
}
/**
* Show the form for creating a new resource.
*
* @param \App\Models\Document $document
* @return \Illuminate\View\View
* @return \Illuminate\Http\Response
*/
public function create(Document $document): \Illuminate\View\View
public function create()
{
$documentsList = Document::query()->where('lang', LangEnum::fr)->get();
$directories = Directory::getChildrenDirectories();
$tags = Tag::get();
$documents = Document::where('lang', \App\Enums\Documents\LangEnum::fr())->get();
$directories = Directory::getChildrenDirectories();
$tags = Tag::get();
return view('bo.pages.documents.create', [
'documentModel' => $document,
'documentsList' => $documentsList,
'directories' => $directories,
'tags' => $tags
]);
return view('bo.pages.documents.create', compact('documents', 'directories', 'tags'));
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\Documents\StoreDocumentRequest $request
* @return \Illuminate\Http\RedirectResponse
* @param \App\Http\Requests\Bo\Documents\StoreDocumentRequest $request
* @return \Illuminate\Http\Response
*/
public function store(StoreDocumentRequest $request): \Illuminate\Http\RedirectResponse
public function store(StoreDocumentRequest $request)
{
return DB::transaction(function () use ($request) {
$document = new Document();
@ -109,17 +96,17 @@ class DocumentController extends Controller
if ($document->saveOrFail()) {
$document->tags()->sync(collect($request->tags)->pluck('id'));
return redirect()
->route('bo.documents.edit', ['tenant' => \tenant(), 'document' => $document])
->route('bo.documents.edit', $document)
->with('success', trans(
':model a été créé',
['model' => trans('models.classes.document')]
['model' => Str::singular(trans('models.classes.documents'))]
));
}
return redirect()
->back()
->with('error', trans(
':model n\'a pas été créé',
['model' => trans('models.classes.document')]
['model' => Str::singular(trans('models.classes.documents'))]
));
});
}
@ -128,47 +115,47 @@ class DocumentController extends Controller
* Show the form for editing the specified resource.
*
* @param \App\Models\Document $document
* @return \Illuminate\View\View
* @return \Illuminate\Http\Response
*/
public function edit(Document $document): \Illuminate\View\View
public function edit(Document $document)
{
$documentsList = Document::query()->where('lang', LangEnum::fr)->get();
$directories = Directory::getChildrenDirectories();
$tags = Tag::get();
$documents = Document::where('lang', \App\Enums\Documents\LangEnum::fr())->get();
$directories = Directory::getChildrenDirectories();
$tags = Tag::get();
return view('bo.pages.documents.edit', [
'documents' => $documents,
'documentModel' => $document,
'documentsList' => $documentsList,
'directories' => $directories,
'tags' => $tags
'directories' => $directories,
'tags' => $tags
]);
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\Documents\UpdateDocumentRequest $request
* @param \App\Models\Document $document
* @return \Illuminate\Http\RedirectResponse
* @param \App\Http\Requests\Bo\Document\UpdateDocumentRequest $request
* @param \App\Models\Document $document
* @return \Illuminate\Http\Response
*/
public function update(UpdateDocumentRequest $request, Document $document): \Illuminate\Http\RedirectResponse
public function update(UpdateDocumentRequest $request, Document $document)
{
return DB::transaction(function () use ($request, $document) {
$document->fill($request->validated());
$document->tags()->sync(collect($request->tags)->pluck('id'));
if ($document->saveOrFail()) {
return redirect()
->route('bo.documents.edit', ['tenant' => \tenant(), 'document' => $document])
->route('bo.documents.edit', $document)
->with('success', trans(
':model a été mis à jour',
['model' => trans('models.classes.document')]
['model' => Str::singular(trans('models.classes.documents'))]
));
}
return redirect()
->back()
->with('error', trans(
':model n\'a pas été mis à jour',
['model' => trans('models.classes.document')]
['model' => Str::singular(trans('models.classes.documents'))]
));
});
}
@ -176,49 +163,42 @@ class DocumentController extends Controller
/**
* Remove the specified resource from storage.
*
* @param \App\Models\Document $document
* @return \Illuminate\Http\RedirectResponse
* @param \App\Models\Document $document
* @return \Illuminate\Http\Response
*/
public function destroy(Document $document): \Illuminate\Http\RedirectResponse
public function destroy(Document $document)
{
/** @var array<string,string> */
$previousQueries = [];
\parse_str(\parse_url(url()->previous(), \PHP_URL_QUERY), $previousQueries);
$document->tags()->sync([]);
/** @var \App\Models\Document $documentChild */
foreach ($document->children as $documentChild) {
Document::removeDocument($documentChild);
$documentChild->deleteOrFail();
}
if ($document->deleteOrFail()) {
return redirect()
->route('bo.documents.index', array_merge(['tenant' => \tenant()], $previousQueries))
->route('bo.documents.index')
->with('warning', trans(
':model a bien été supprimé',
['model' => trans('models.classes.document')]
['model' => Str::singular(trans('models.classes.documents'))]
));
}
return redirect()
->back()
->with('error', trans(
':model n\'a pas été supprimé',
['model' => trans('models.classes.document')]
['model' => Str::singular(trans('models.classes.documents'))]
));
}
/**
* Download the document
*
* @param string $tenant
* @param string $uuid
* @return \Symfony\Component\HttpFoundation\BinaryFileResponse
*/
public function download(string $tenant, string $uuid): \Symfony\Component\HttpFoundation\BinaryFileResponse
public function download(string $uuid)
{
$doc = Document::where('uuid', $uuid)->firstOrFail();
$pathToFile = public_path() . Storage::url(
sprintf('docs/%s/%s.%s', $tenant, $doc->uuid, $doc->type)
);
$pathToFile = public_path() . Storage::url('docs/' . $doc->uuid . '.' . $doc->type);
return response()->download($pathToFile, $doc->path);
}
@ -244,9 +224,10 @@ class DocumentController extends Controller
return $this->saveFile($save->getFile(), $request->uuid);
}
// We are in chunk mode, lets send the current progress.
/** @var AbstractHandler $handler */
$handler = $save->handler();
return response()->json([
"done" => $handler->getPercentageDone(),
"done" => $handler->getPercentageDone(),
'status' => true
]);
}
@ -256,14 +237,14 @@ class DocumentController extends Controller
* @param string|null $uuid
* @return \Illuminate\Http\JsonResponse
*/
protected function saveFile(UploadedFile $file, ?string $uuid): \Illuminate\Http\JsonResponse
protected function saveFile(UploadedFile $file, ?string $uuid): JsonResponse
{
/** @var \Illuminate\Filesystem\FilesystemAdapter */
$disk = Storage::disk('docs');
$uuid = $uuid ?: Str::uuid();
/** @var \Illuminate\Http\UploadedFile $file */
$filename = sprintf('%s/%s.%s', \tenant()->getKey(), $uuid, $file->guessExtension());
$filename = $uuid . '.' . $file->guessExtension();
$disk->putFileAs('', $file, $filename);
return response()->json([

View file

@ -2,38 +2,33 @@
namespace App\Http\Controllers\Bo;
use App\Enums\Formularies\InputStatusEnum;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\Formularies\StoreFormularyRequest;
use App\Http\Requests\Bo\Formularies\UpdateFormularyRequest;
use App\Models\Contact;
use App\Lib\Helpers\ToolboxHelper;
use App\Models\Formulary;
use App\Traits\Controllers\ChangesModelOrder;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class FormularyController extends Controller
{
use ChangesModelOrder;
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(Formulary::class);
}
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
* @return \Illuminate\Http\Response
*/
public function index(Request $request): \Illuminate\View\View
public function index(Request $request)
{
/** @var \Illuminate\Database\Eloquent\Builder $query */
$query = Formulary::query();
/** @var string $search Search field */
if ($request->search) {
$this->searchQuery(
$query,
@ -42,34 +37,33 @@ class FormularyController extends Controller
'name',
);
}
$searchFields = trans('validation.attributes.name');
$searchFields = trans('Nom');
/** Sort columns with a query */
$this->sortQuery($query);
$formularyModels = $this->paginate($query);
$formulariesModel = $this->paginate($query);
return view('bo.pages.formularies.index', compact('formularyModels', 'searchFields'));
return view('bo.pages.formularies.index', compact('formulariesModel', 'searchFields'));
}
/**
* Show the form for creating a new resource.
*
* @param \App\Models\Formulary $formulary
* @return \Illuminate\View\View
* @return \Illuminate\Http\Response
*/
public function create(Formulary $formulary): \Illuminate\View\View
public function create()
{
return view('bo.pages.formularies.create', ['formularyModel' => $formulary]);
return view('bo.pages.formularies.create');
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\Formularies\StoreFormularyRequest $request
* @return \Illuminate\Http\RedirectResponse
* @param \App\Http\Requests\Bo\Formularies\StoreFormularyRequest $request
* @return \Illuminate\Http\Response
*/
public function store(StoreFormularyRequest $request): \Illuminate\Http\RedirectResponse
public function store(StoreFormularyRequest $request)
{
return DB::transaction(function () use ($request) {
$formulary = new Formulary();
@ -77,17 +71,17 @@ class FormularyController extends Controller
if ($formulary->saveOrFail()) {
return redirect()
->route('bo.formularies.edit', ['tenant' => \tenant(), 'formulary' => $formulary])
->route('bo.formularies.edit', $formulary)
->with('success', trans(
':model a été créé',
['model' => trans('models.classes.formulary')]
['model' => Str::singular(trans('models.classes.formularies'))]
));
}
return redirect()
->back()
->with('error', trans(
':model n\'a pas été créé',
['model' => trans('models.classes.formulary')]
['model' => Str::singular(trans('models.classes.formularies'))]
));
});
}
@ -96,40 +90,48 @@ class FormularyController extends Controller
* Show the form for editing the specified resource.
*
* @param \App\Models\Formulary $formulary
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Contracts\View\View
* @return \Illuminate\Http\Response
*/
public function edit(Formulary $formulary): \Illuminate\Http\RedirectResponse|\Illuminate\Contracts\View\View
public function edit(Formulary $formulary)
{
return view('bo.pages.formularies.edit', [
'formularyModel' => $formulary
]);
if ($formulary->status->value === InputStatusEnum::system()->value) {
return redirect()->route('bo.formularies.index')
->with('error', trans(
'Administration du champs :name impossible car il est vérouillé !',
['name' => $formulary->name]
));
} else {
return view('bo.pages.formularies.edit', [
'formularyModel' => $formulary
]);
}
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\Formularies\UpdateFormularyRequest $request
* @param \App\Models\Formulary $formulary
* @return \Illuminate\Http\RedirectResponse
* @param \App\Http\Requests\Bo\Formulary\UpdateFormularyRequest $request
* @param \App\Models\Formulary $formulary
* @return \Illuminate\Http\Response
*/
public function update(UpdateFormularyRequest $request, Formulary $formulary): \Illuminate\Http\RedirectResponse
public function update(UpdateFormularyRequest $request, Formulary $formulary)
{
return DB::transaction(function () use ($request, $formulary) {
$formulary->fill($request->validated());
if ($formulary->saveOrFail()) {
return redirect()
->route('bo.formularies.edit', ['tenant' => \tenant(), 'formulary' => $formulary])
->route('bo.formularies.edit', $formulary)
->with('success', trans(
':model a été mis à jour',
['model' => trans('models.classes.formulary')]
['model' => Str::singular(trans('models.classes.formularies'))]
));
}
return redirect()
->back()
->with('error', trans(
':model n\'a pas été mis à jour',
['model' => trans('models.classes.formulary')]
['model' => Str::singular(trans('models.classes.formularies'))]
));
});
}
@ -137,41 +139,29 @@ class FormularyController extends Controller
/**
* Remove the specified resource from storage.
*
* @param \App\Models\Formulary $formulary
* @return \Illuminate\Http\RedirectResponse
* @param \App\Models\Formulary $formulary
* @return \Illuminate\Http\Response
*/
public function destroy(Formulary $formulary): \Illuminate\Http\RedirectResponse
public function destroy(Formulary $formulary)
{
/** @var array<string,string> */
$previousQueries = [];
\parse_str(\parse_url(url()->previous(), \PHP_URL_QUERY), $previousQueries);
return DB::transaction(function () use ($formulary, $previousQueries) {
return DB::transaction(function () use ($formulary) {
// * Remove the input on all contacts.
Contact::removeDataInputOnAllContacts($formulary);
ToolboxHelper::removeDataInputOnAllContacts($formulary);
if ($formulary->deleteOrFail()) {
return redirect()
->route('bo.formularies.index', array_merge(['tenant' => \tenant()], $previousQueries))
->route('bo.formularies.index')
->with('warning', trans(
':model a bien été supprimé',
['model' => trans('models.classes.formulary')]
['model' => Str::singular(trans('models.classes.formularies'))]
));
}
return redirect()->back()->with('error', trans(
':model n\'a pas été supprimé',
['model' => trans('models.classes.formulary')]
));
return redirect()
->back()
->with('error', trans(
':model n\'a pas été supprimé',
['model' => Str::singular(trans('models.classes.formularies'))]
));
});
}
/**
* Duplicate the specified resource.
*
* @param \App\Models\Formulary $formulary
* @return \Illuminate\Contracts\View\View
*/
public function duplicate(Formulary $formulary): \Illuminate\Contracts\View\View
{
return $this->create($formulary->replicate());
}
}

View file

@ -6,62 +6,38 @@ use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\Settings\UpdateSettingRequest;
use App\Models\Directory;
use App\Models\Setting;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class SettingController extends Controller
{
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(Setting::class);
}
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
* @return \Illuminate\Http\Response
*/
public function index(Request $request): \Illuminate\Contracts\View\View
public function index()
{
$directories = Directory::renderDirectories(
Directory::query()->whereNull('directory_id')->with('parent')->get(),
);
$directories = Directory::renderDirectories(Directory::whereNull('directory_id')->get());
/** @var \Illuminate\Database\Eloquent\Builder $query */
$query = Setting::query();
/** @var \Illuminate\Database\Eloquent\Builder $settings */
$settings = Setting::query();
if ($request->search) {
$this->searchQuery(
$query,
$request->search,
null,
'key',
);
}
$searchFields = trans('validation.custom.key');
$this->sortQuery($settings);
$settings = $settings->get();
$this->sortQuery($query);
$settingModels = $query->get();
return view('bo.pages.settings.index', compact('settingModels', 'searchFields', 'directories'));
return view('bo.pages.settings.index', compact('settings', 'directories'));
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\Setting $setting
* @return \Illuminate\Contracts\View\View
* @return \Illuminate\Http\Response
*/
public function edit(Setting $setting): \Illuminate\Contracts\View\View
public function edit(Setting $setting)
{
$directories = Directory::renderDirectories(
Directory::query()->whereNull('directory_id')->with('parent')->get(),
);
$directories = Directory::renderDirectories(Directory::whereNull('directory_id')->get());
return view('bo.pages.settings.edit', ['settingModel' => $setting, 'directories' => $directories]);
}
@ -69,27 +45,27 @@ class SettingController extends Controller
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\Settings\UpdateSettingRequest $request
* @param \App\Models\Setting $setting
* @return \Illuminate\Http\RedirectResponse
* @param \App\Http\Requests\Bo\Setting\UpdateSettingRequest $request
* @param \App\Models\Setting $setting
* @return \Illuminate\Http\Response
*/
public function update(UpdateSettingRequest $request, Setting $setting): \Illuminate\Http\RedirectResponse
public function update(UpdateSettingRequest $request, Setting $setting)
{
return DB::transaction(function () use ($request, $setting) {
$setting->fill($request->validated());
if ($setting->saveOrFail()) {
return redirect()
->route('bo.settings.edit', ['tenant' => \tenant(), 'setting' => $setting])
->route('bo.settings.edit', $setting)
->with('success', trans(
':model a été mis à jour',
['model' => trans('models.classes.setting')]
['model' => Str::singular(trans('models.classes.settings'))]
));
}
return redirect()
->back()
->with('error', trans(
':model n\'a pas été mis à jour',
['model' => trans('models.classes.setting')]
['model' => Str::singular(trans('models.classes.settings'))]
));
});
}
@ -97,26 +73,24 @@ class SettingController extends Controller
/**
* Remove the specified resource from storage.
*
* @param \App\Models\Setting $setting
* @return \Illuminate\Http\RedirectResponse
* @param \App\Models\Setting $setting
* @return \Illuminate\Http\Response
*/
public function destroy(Setting $setting): \Illuminate\Http\RedirectResponse
public function destroy(Setting $setting)
{
return DB::transaction(function () use ($setting) {
if ($setting->deleteOrFail()) {
return redirect()
->route('bo.settings.index', ['tenant' => \tenant()])
->with('warning', trans(
':model a bien été supprimé',
['model' => trans('models.classes.setting')]
));
}
if ($setting->deleteOrFail()) {
return redirect()
->back()
->with('error', trans(
':model n\'a pas été supprimé',
['model' => trans('models.classes.setting')]
->route('bo.settings.index')
->with('warning', trans(
':model a bien été supprimé',
['model' => Str::singular(trans('models.classes.settings'))]
));
});
}
return redirect()
->back()
->with('error', trans(
':model n\'a pas été supprimé',
['model' => Str::singular(trans('models.classes.settings'))]
));
}
}

View file

@ -9,30 +9,24 @@ use App\Models\Tag;
use App\Traits\Controllers\ChangesModelOrder;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class TagController extends Controller
{
use ChangesModelOrder;
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(Tag::class);
}
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
* @return \Illuminate\Http\Response
*/
public function index(Request $request): \Illuminate\Contracts\View\View
public function index(Request $request)
{
/** @var \Illuminate\Database\Eloquent\Builder $query */
$query = Tag::query();
/** @var string $search Search field */
if ($request->search) {
$this->searchQuery(
$query,
@ -41,51 +35,50 @@ class TagController extends Controller
'name',
);
}
$searchFields = trans('validation.attributes.name');
$searchFields = trans('Nom');
/** Sort columns with a query */
$this->sortQuery($query);
$tagModels = $this->paginate($query);
$tags = $this->paginate($query);
return view('bo.pages.tags.index', compact('tagModels', 'searchFields'));
return view('bo.pages.tags.index', compact('tags', 'searchFields'));
}
/**
* Show the form for creating a new resource.
*
* @param \App\Models\Tag $tag
* @return \Illuminate\Contracts\View\View
* @return \Illuminate\Http\Response
*/
public function create(Tag $tag): \Illuminate\Contracts\View\View
public function create()
{
return view('bo.pages.tags.create', ['tagModel' => $tag]);
return view('bo.pages.tags.create');
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\Tags\StoreTagRequest $request
* @return \Illuminate\Http\RedirectResponse
* @param \App\Http\Requests\Bo\Tag\StoreTagRequest $request
* @return \Illuminate\Http\Response
*/
public function store(StoreTagRequest $request): \Illuminate\Http\RedirectResponse
public function store(StoreTagRequest $request)
{
return DB::transaction(function () use ($request) {
$tag = new Tag();
$tag->fill($request->validated());
if ($tag->saveOrFail()) {
return redirect()
->route('bo.tags.edit', ['tenant' => \tenant(), 'tag' => $tag])
->route('bo.tags.edit', $tag)
->with('success', trans(
':model a été créé',
['model' => trans('models.classes.tag')]
['model' => Str::singular(trans('models.classes.tags'))]
));
}
return redirect()
->back()
->with('error', trans(
':model n\'a pas été créé',
['model' => trans('models.classes.tag')]
['model' => Str::singular(trans('models.classes.tags'))]
));
});
}
@ -94,9 +87,9 @@ class TagController extends Controller
* Show the form for editing the specified resource.
*
* @param \App\Models\Tag $tag
* @return \Illuminate\Contracts\View\View
* @return \Illuminate\Http\Response
*/
public function edit(Tag $tag): \Illuminate\Contracts\View\View
public function edit(Tag $tag)
{
return view('bo.pages.tags.edit', ['tagModel' => $tag]);
}
@ -104,28 +97,28 @@ class TagController extends Controller
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\Tags\UpdateTagRequest $request
* @param \App\Models\Tag $tag
* @return \Illuminate\Http\RedirectResponse
* @param \App\Http\Requests\Bo\Tag\UpdateTagRequest $request
* @param \App\Models\Tag $tag
* @return \Illuminate\Http\Response
*/
public function update(UpdateTagRequest $request, Tag $tag): \Illuminate\Http\RedirectResponse
public function update(UpdateTagRequest $request, Tag $tag)
{
return DB::transaction(function () use ($request, $tag) {
$tag->fill($request->validated());
if ($tag->saveOrFail()) {
return redirect()
->route('bo.tags.edit', ['tenant' => \tenant(), 'tag' => $tag])
->route('bo.tags.edit', $tag)
->with('success', trans(
':model a été mis à jour',
['model' => trans('models.classes.tag')]
['model' => Str::singular(trans('models.classes.tags'))]
));
}
return redirect()
->back()
->with('error', trans(
':model n\'a pas été mis à jour',
['model' => trans('models.classes.tag')]
['model' => Str::singular(trans('models.classes.tags'))]
));
});
}
@ -133,40 +126,24 @@ class TagController extends Controller
/**
* Remove the specified resource from storage.
*
* @param \App\Models\Tag $tag
* @return \Illuminate\Http\RedirectResponse
* @param \App\Models\Tag $tag
* @return \Illuminate\Http\Response
*/
public function destroy(Tag $tag): \Illuminate\Http\RedirectResponse
public function destroy(Tag $tag)
{
/** @var array<string,string> */
$previousQueries = [];
\parse_str(\parse_url(url()->previous(), \PHP_URL_QUERY), $previousQueries);
return DB::transaction(function () use ($tag, $previousQueries) {
if ($tag->deleteOrFail()) {
return redirect()
->route('bo.tags.index', array_merge(['tenant' => \tenant()], $previousQueries))
->with('warning', trans(
':model a bien été supprimé',
['model' => trans('models.classes.tag')]
));
}
if ($tag->deleteOrFail()) {
return redirect()
->back()
->with('error', trans(
':model n\'a pas été supprimé',
['model' => trans('models.classes.tag')]
->route('bo.tags.index')
->with('warning', trans(
':model a bien été supprimé',
['model' => Str::singular(trans('models.classes.tags'))]
));
});
}
/**
* Duplicate the specified resource.
*
* @param \App\Models\Tag $tag
* @return \Illuminate\Contracts\View\View
*/
public function duplicate(Tag $tag): \Illuminate\Contracts\View\View
{
return $this->create($tag->replicate());
}
return redirect()
->back()
->with('error', trans(
':model n\'a pas été supprimé',
['model' => Str::singular(trans('models.classes.tags'))]
));
}
}

View file

@ -1,174 +0,0 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\Tenants\StoreTenantRequest;
use App\Http\Requests\Bo\Tenants\UpdateTenantRequest;
use App\Models\Tenant;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
class TenantController extends Controller
{
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(Tenant::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 = Tenant::query();
if ($request->search) {
$this->searchQuery(
$query,
$request->search,
null,
'name',
);
}
$searchFields = trans('validation.attributes.name');
/** Sort columns with a query */
$this->sortQuery($query);
$tenantModels = $this->paginate($query);
return view('bo.pages.tenants.index', compact('tenantModels', 'searchFields'));
}
/**
* Show the form for creating a new resource.
*
* @param \App\Models\Tenant $tenant
* @return \Illuminate\Contracts\View\View
*/
public function create(Tenant $tenant): \Illuminate\Contracts\View\View
{
return view('bo.pages.tenants.create', ['tenantModel' => $tenant]);
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\Tenants\StoreTenantRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(StoreTenantRequest $request): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request) {
$tenant = new Tenant();
$tenant->fill($request->validated());
if ($tenant->saveOrFail()) {
return redirect()
->route('bo.tenants.edit', ['tenant' => $tenant])
->with('success', trans(
':model a été créé',
['model' => trans('models.classes.tenant')]
));
}
return redirect()
->back()
->with('error', trans(
':model n\'a pas été créé',
['model' => trans('models.classes.tenant')]
));
});
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\Tenant $tenant
* @return \Illuminate\Contracts\View\View
*/
public function edit(Tenant $tenant): \Illuminate\Contracts\View\View
{
return view('bo.pages.tenants.edit', ['tenantModel' => $tenant]);
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\Tenants\UpdateTenantRequest $request
* @param \App\Models\Tenant $tenant
* @return \Illuminate\Http\RedirectResponse
*/
public function update(UpdateTenantRequest $request, Tenant $tenant): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request, $tenant) {
$tenant->fill($request->validated());
if ($tenant->saveOrFail()) {
return redirect()
->route('bo.tenants.edit', ['tenant' => $tenant])
->with('success', trans(
':model a été mis à jour',
['model' => trans('models.classes.tenant')]
));
}
return redirect()
->back()
->with('error', trans(
':model n\'a pas été mis à jour',
['model' => trans('models.classes.tenant')]
));
});
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\Tenant $tenant
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(Tenant $tenant): \Illuminate\Http\RedirectResponse
{
/** @var array<string,string> */
$previousQueries = [];
\parse_str(\parse_url(url()->previous(), \PHP_URL_QUERY), $previousQueries);
return DB::transaction(function () use ($tenant, $previousQueries) {
if ($tenant->deleteOrFail()) {
/** @var string $tenantKey */
$tenantKey = $tenant->getKey();
Storage::disk('public')->deleteDirectory("modelfiles/{$tenantKey}");
Storage::disk('public')->deleteDirectory("docs/{$tenantKey}");
return redirect()
->route('bo.tenants.index', $previousQueries)
->with('warning', trans(
':model a bien été supprimé',
['model' => trans('models.classes.tenant')]
));
}
return redirect()
->back()
->with('error', trans(
':model n\'a pas été supprimé',
['model' => trans('models.classes.tenant')]
));
});
}
/**
* Duplicate the specified resource.
*
* @param \App\Models\Tenant $tenant
* @return \Illuminate\Contracts\View\View
*/
public function duplicate(Tenant $tenant): \Illuminate\Contracts\View\View
{
return $this->create($tenant->replicate());
}
}

View file

@ -1,170 +0,0 @@
<?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\Tenant;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class UserController extends Controller
{
/**
* 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()->with('tenants');
/** @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
{
$tenants = Tenant::query()->orderBy('name', 'asc')->get();
return view('bo.pages.users.create', ['userModel' => $user, 'tenants' => $tenants]);
}
/**
* 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();
$user->fill($request->validated());
if ($user->saveOrFail()) {
$user->tenants()->sync(collect($request->validated('tenants'))->pluck('id'));
return redirect()->route('bo.users.edit', ['tenant' => tenant(), 'user' => $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
{
$tenants = Tenant::query()->orderBy('name', 'asc')->get();
return view('bo.pages.users.edit', ['userModel' => $user, 'tenants' => $tenants]);
}
/**
* 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) {
$user->fill($request->validated());
if ($user->saveOrFail()) {
$user->tenants()->sync(collect($request->validated('tenants'))->pluck('id'));
return redirect()->route('bo.users.edit', ['tenant' => tenant(), 'user' => $user])
->with('success', __('crud.messages.has_been_updated', [
'model' => Str::ucfirst(__('models.classes.user'))
]));
}
return redirect()->route('bo.users.edit', ['tenant' => tenant(), 'user' => $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', ['tenant' => tenant()])
->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

@ -2,7 +2,9 @@
namespace App\Http\Controllers;
use App\Enums\ItemsPerPaginationEnum;
use App\Enums\Pagination\ItemsPerPaginationEnum;
use App\Lib\Helpers\ToolboxHelper;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
@ -12,7 +14,6 @@ use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Str;
use Uwil\LaravelCommon\Helpers\ToolboxHelper;
class Controller extends BaseController
{
@ -26,7 +27,7 @@ class Controller extends BaseController
*
* @param \Illuminate\Database\Eloquent\Builder $query The eloquent query builder.
* @param string $search The query string.
* @param callable|null $searchQuery 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.
@ -35,7 +36,7 @@ class Controller extends BaseController
protected function searchQuery(
Builder $query,
string $search,
callable|null $searchQuery = null,
callable $searchQuery = null,
string ...$fields
): Builder {
if (count($fields)) {
@ -95,16 +96,16 @@ class Controller extends BaseController
* @param array $ignore
* @return \Illuminate\Database\Eloquent\Builder
*/
protected function sortQuery(Builder $query, array $ignore = []): \Illuminate\Database\Eloquent\Builder
protected function sortQuery(Builder $query, array $ignore = []): Builder
{
$rName = request()->route()->getName();
$table = $query->getModel()->getTable();
if (request()->input('sort_col')) {
Session::put("$rName.sort_col", request()->input('sort_col'));
if (request()->sort_col) {
Session::put("$rName.sort_col", request()->sort_col);
Session::put("$rName.sorted", true);
}
if (request()->input('sort_way')) {
Session::put("$rName.sort_way", request()->input('sort_way'));
if (request()->sort_way) {
Session::put("$rName.sort_way", request()->sort_way);
Session::put("$rName.sorted", true);
}
if (request()->rst) {
@ -112,29 +113,17 @@ class Controller extends BaseController
Session::remove("$rName.sort_col");
Session::remove("$rName.sort_way");
}
if (
request()->input('sort_col') and
request()->input('sort_way') and
Schema::hasColumn($table, request()->input('sort_col'))
) {
if (!in_array(request()->input('sort_col'), $ignore)) {
$query = $query->orderBy(
\sprintf("$table.%s", request()->input('sort_col')),
request()->input('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');
$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;
}
@ -142,21 +131,17 @@ class Controller extends BaseController
/**
* Customize pagination with cache or config (default).
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param \App\Enums\ItemsPerPaginationEnum|null $pagination
* @param \Illuminate\Database\Eloquent\Builder $query
* @param \App\Enums\Pagination\ItemsPerPaginationEnum|null $pagination
* @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
*/
protected function paginate(
Builder $query,
ItemsPerPaginationEnum|null $pagination = null
): \Illuminate\Contracts\Pagination\LengthAwarePaginator {
protected function paginate(Builder $query, ItemsPerPaginationEnum $pagination = null): 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\ItemsPerPaginationEnum',
$pagination = $pagination ?? ItemsPerPaginationEnum::twelve();
$pagination = ToolboxHelper::getValidatedPaginate(
Cache::get($cacheKey, $pagination->value)
);
if (Cache::get($cacheKey) !== $pagination) {
Cache::put($cacheKey, $pagination);

View file

@ -16,6 +16,7 @@ class Kernel extends HttpKernel
protected $middleware = [
\App\Http\Middleware\TrustHosts::class,
\App\Http\Middleware\TrustProxies::class,
\Fruitcake\Cors\HandleCors::class,
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
@ -36,13 +37,7 @@ class Kernel extends HttpKernel
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\SecureHeaders::class,
],
'saml' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\App\Http\Middleware\SecureHeaders::class
],
'api' => [
@ -59,17 +54,16 @@ class Kernel extends HttpKernel
* @var array<string, class-string|string>
*/
protected $routeMiddleware = [
'api.token' => \App\Http\Middleware\CheckApiToken::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'auth' => \App\Http\Middleware\Authenticate::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'htmlsanitize' => \App\Http\Middleware\HtmlSanitizer::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'restrict_tenant_access' => \App\Http\Middleware\RestrictTenantAccess::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::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' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
'htmlsanitize' => \App\Http\Middleware\HtmlSanitizer::class,
'api.token' => \App\Http\Middleware\CheckApiToken::class,
];
}

View file

@ -13,7 +13,7 @@ class Authenticate extends Middleware
* @param mixed $request
* @return string|null
*/
protected function redirectTo(mixed $request): string|null
protected function redirectTo($request)
{
$logins = [
'backend' => route('bo.login')

View file

@ -14,7 +14,7 @@ class CheckApiToken
* @param \Closure $next
* @return mixed
*/
public function handle(Request $request, Closure $next): mixed
public function handle(Request $request, Closure $next)
{
if (isset($request->token) && $request->token == config('auth.API_TOKEN')) {
return $next($request);

View file

@ -11,5 +11,6 @@ class EncryptCookies extends Middleware
*
* @var array<int, string>
*/
protected $except = [];
protected $except = [
];
}

View file

@ -2,8 +2,8 @@
namespace App\Http\Middleware;
use App\Lib\Helpers\ToolboxHelper;
use Illuminate\Http\Request;
use Uwil\LaravelCommon\Helpers\ToolboxHelper;
class HtmlSanitizer
{
@ -15,7 +15,7 @@ class HtmlSanitizer
* @param mixed ...$params
* @return mixed
*/
public function handle(Request $request, \Closure $next, mixed ...$params): mixed
public function handle(Request $request, \Closure $next, ...$params)
{
$inputs = $request->all();
/** @var callable|null */

View file

@ -12,7 +12,6 @@ class RedirectIfAuthenticated
* Handle an incoming request.
*
* @phpcs:disable Generic.Files.LineLength.TooLong
*
* @param \Illuminate\Http\Request $request
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
* @param string|null ...$guards
@ -20,11 +19,8 @@ class RedirectIfAuthenticated
* @throws \RuntimeException If guard is unhandled.
* @phpcs:enable
*/
public function handle(
Request $request,
Closure $next,
string|null ...$guards
): \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse {
public function handle(Request $request, Closure $next, ...$guards)
{
$guards = empty($guards) ? [null] : $guards;
foreach ($guards as $guard) {

View file

@ -1,48 +0,0 @@
<?php
namespace App\Http\Middleware;
use App\Models\Tenant;
use Closure;
use Illuminate\Http\Request;
class RestrictTenantAccess
{
/**
* 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): \Symfony\Component\HttpFoundation\Response
{
/** @var \App\Models\User|null $user */
$user = \auth('backend')->user();
if ($user) {
if (
!$request->routeIs(['bo.dashboard', 'bo.users.*', 'bo.tenants.*']) &&
$request->tenant &&
!\in_array($request->tenant, Tenant::get()->pluck('id')->toArray())
) {
// @phpstan-ignore argument.type
abort('404');
}
if (Tenant::count() && $request->routeIs(['bo.tenants.*', 'bo.users.*']) && $user->can('isAdmin')) {
$tenant = Tenant::first()->getKey();
tenancy()->initialize($tenant);
return $next($request);
}
if (
Tenant::count() && ($request->routeIs('bo.dashboard')
|| $request->routeIs('bo.users.edit', $user->getKey()))
) {
$tenant = ($user->tenants->count()) ? $user->tenants->first() : Tenant::first()->getKey();
tenancy()->initialize($tenant);
return $next($request);
}
} //end if
return $next($request);
}
}

View file

@ -6,7 +6,6 @@ use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\View;
use Illuminate\Support\Facades\Vite;
class SecureHeaders
{
@ -27,13 +26,14 @@ class SecureHeaders
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return \Symfony\Component\HttpFoundation\Response
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle(Request $request, Closure $next): \Symfony\Component\HttpFoundation\Response
public function handle(Request $request, Closure $next)
{
View::share('nonce', $nonce = Vite::useCspNonce());
View::share('nonce', $nonce = \uniqid());
// * 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);
@ -49,18 +49,19 @@ class SecureHeaders
// * CKEDITOR Specific https://ckeditor.com/docs/ckeditor5/latest/installation/advanced/csp.html
"connect-src 'self' blob:;",
"img-src * data: blob:;",
"object-src 'self' " . implode(' ', []) . ';',
'frame-src ' . implode(' ', []) . ';',
"font-src 'self' data: ;",
// ! Using CKEditor will must have unsafe-inline style
// phpcs:ignore Generic.Files.LineLength.TooLong
"style-src 'self' 'unsafe-inline' " . \implode(' ', [
// * File Manager
'https://cdn.jsdelivr.net',
'https://use.fontawesome.com',
'https://fonts.googleapis.com',
'https://fonts.googleapis.com'
]) . ';',
// ! 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:',
]) . ';'
@ -75,12 +76,12 @@ class SecureHeaders
}
/**
* Remove unwanted headers.
* Remove unwanted headers
*
* @param array $headerList
* @return void
*/
private function removeUnwantedHeaders(array $headerList): void
private function removeUnwantedHeaders(array $headerList)
{
foreach ($headerList as $header) {
header_remove($header);

View file

@ -11,7 +11,7 @@ class TrustHosts extends Middleware
*
* @return array<int, string|null>
*/
public function hosts(): array
public function hosts()
{
return [
$this->allSubdomainsOfApplicationUrl(),

View file

@ -6,15 +6,26 @@ use Illuminate\Foundation\Http\FormRequest;
class DestroyContactsRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize()
{
// * Check is done with api.token middleware
return true;
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation(): void
protected function prepareForValidation()
{
$this->merge([
'contactUuidList' => \collect($this->contactUuidList)->filter()->all(),
'contactUuidList' => \collect($this->contactUuidList)->filter()->all()
]);
}
@ -23,10 +34,10 @@ class DestroyContactsRequest extends FormRequest
*
* @return array
*/
public function rules(): array
public function rules()
{
return [
'contactUuidList' => 'present|array|min:0',
'contactUuidList' => 'present|array|min:0',
'contactUuidList.*' => 'uuid',
];
}

View file

@ -6,16 +6,27 @@ use App\Enums\Formularies\InputTypeEnum;
use App\Lib\Inputs\Definition\InputData;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\ValidationException;
use Illuminate\Validation\Rules\Enum;
use Kwaadpepper\Enum\Rules\EnumIsValidRule;
class UpsertContactsRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize()
{
// * Check is done with api.token middleware
return true;
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation(): void
protected function prepareForValidation()
{
$this->merge([
'contacts' => \collect($this->contacts)->filter()
@ -50,35 +61,35 @@ class UpsertContactsRequest extends FormRequest
*
* @return array
*/
public function rules(): array
public function rules()
{
return [
'contacts' => 'present|array',
'contacts.*' => 'array',
'contacts.*.id' => 'uuid',
'contacts.*.lastname' => 'nullable|string|between:1,255',
'contacts.*.firstname' => 'nullable|string|between:1,255',
'contacts' => 'present|array',
'contacts.*' => 'array',
'contacts.*.id' => 'uuid',
'contacts.*.lastname' => 'nullable|string|max:255',
'contacts.*.firstname' => 'nullable|string|max:255',
// * Dot not validate email here (soft validate), if it does not pass, app will be blocked.
'contacts.*.email' => 'required|string|between:1,255',
'contacts.*.phone' => 'nullable|string|between:1,255',
'contacts.*.email' => 'required|string|max:255',
'contacts.*.phone' => 'nullable|string|max:255',
'contacts.*.timestamp' => 'required|date',
// * DATA
'contacts.*.data' => 'present|array',
'contacts.*.dataParsed' => 'present',
'contacts.*.data.*' => 'array',
'contacts.*.data.*.title' => 'present|string|min:1|max:255',
'contacts.*.data.*.formSlug' => 'present|string|min:1|max:255',
'contacts.*.data.*.type' => ['present', new Enum(InputTypeEnum::class)],
'contacts.*.data.*.value' => 'present',
'contacts.*.data.*.required' => 'present|boolean',
'contacts.*.data' => 'present|array',
'contacts.*.dataParsed' => 'present',
'contacts.*.data.*' => 'array',
'contacts.*.data.*.title' => 'present|string|min:1|max:255',
'contacts.*.data.*.formSlug' => 'present|string|min:1|max:255',
'contacts.*.data.*.type' => ['present', new EnumIsValidRule(InputTypeEnum::class)],
'contacts.*.data.*.value' => 'present',
'contacts.*.data.*.required' => 'present|boolean',
'contacts.*.data.*.send_to_app_user_id' => 'nullable|numeric',
'contacts.*.notes' => 'nullable|string',
'contacts.*.visits' => 'present|array',
'contacts.*.visits.*' => 'required|array',
'contacts.*.notes' => 'nullable|string',
'contacts.*.visits' => 'present|array',
'contacts.*.visits.*' => 'required|array',
'contacts.*.visits.*.title' => 'present|string',
'contacts.*.visits.*.note' => 'present|string',
'contacts.*.visits.*.note' => 'present|string',
];
}
@ -90,7 +101,7 @@ class UpsertContactsRequest extends FormRequest
* @return mixed
* @throws \Illuminate\Validation\ValidationException In case of parse failure.
*/
private function filterDataLine(array $data, mixed $index): mixed
private function filterDataLine(array $data, $index)
{
$data = \collect($data);
try {
@ -106,15 +117,10 @@ class UpsertContactsRequest extends FormRequest
'error' => "wrong formSlug on data index '{$index}'"
]);
}
$type = InputTypeEnum::make($data->get('type') ? $data->get('type') : null);
if (\is_null($type)) {
throw ValidationException::withMessages([
'error' => "wrong inputType on data index '{$index}'"
]);
}
$type = InputTypeEnum::make($data->get('type') ? $data->get('type') : null);
$value = $data['value'] ?? null;
$required = $data->get('required') ? true : false;
$inputClass = $type->getInputClass();
$inputClass = $type->inputClass;
return $inputClass::from(InputData::make(
$title,
$formSlug,

View file

@ -1,56 +0,0 @@
<?php
namespace App\Http\Requests\Api;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
/** FIXME: currently not used but will be in the future. */
class SendFlyerRequest extends FormRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
return [
'emails.email' => [
'required',
'email:rfc,strict,dns,spoof,filter',
Rule::exists('contacts', 'email')->where(function ($query) {
$query->whereIn('app_user_id', function ($subQuery) {
$subQuery->select('id')->from('app_users')->where('tenant_id', $this->tenant);
});
}),
],
'emails.subject' => 'required|string|between:3,255',
'emails.message' => 'required|string|min:3',
'emails.app_user_id' => [
'required',
'integer',
Rule::exists('app_users', 'id')->where('tenant_id', $this->tenant)
],
'emails.documents' => 'required|array|min:1',
'emails.documents.*.title' => 'required|string|min:3',
'emails.documents.*.uuid' => 'required|string|min:3',
'emails.documents.*.type' => 'required|string|min:1',
];
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'emails.email' => trans('validation.attributes.email'),
'emails.subject' => trans('validation.attributes.subject'),
'emails.message' => trans('validation.attributes.message'),
'emails.app_user_id' => trans('models.classes.app_user'),
];
}
}

View file

@ -3,13 +3,8 @@
namespace App\Http\Requests\Bo\AppUser;
use App\Enums\AppUserRolesEnum;
use App\Models\AppUser;
use Illuminate\Database\Query\Builder;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Rules\Enum;
use Kwaadpepper\Enum\Rules\EnumIsValidRule;
use Illuminate\Validation\Rules\Password;
class StoreAppUserRequest extends FormRequest
@ -19,9 +14,9 @@ class StoreAppUserRequest extends FormRequest
*
* @return boolean
*/
public function authorize(): bool
public function authorize()
{
return Gate::check('create', AppUser::class);
return auth('backend')->check();
}
/**
@ -29,10 +24,10 @@ class StoreAppUserRequest extends FormRequest
*
* @return void
*/
protected function prepareForValidation(): void
protected function prepareForValidation()
{
$this->merge([
'status' => $this->status ? true : false,
'status' => $this->status ? true : false,
'authorizedFolders' => $this->authorizedFolders ? $this->authorizedFolders : [],
]);
}
@ -42,22 +37,14 @@ class StoreAppUserRequest extends FormRequest
*
* @return array
*/
public function rules(): array
public function rules()
{
return [
'lastname' => 'required|min:3|max:255',
'firstname' => 'required|min:3|max:255',
'email' => [
'required',
'email:rfc,strict,dns,spoof,filter',
Rule::unique('app_users', 'email')
->where(fn(Builder $query) => $query->where('tenant_id', tenant('id')))
],
'password' => [
'required',
'required_with:password_confirmation',
'confirmed',
'max:255',
'lastname' => 'required|min:3|max:255',
'firstname' => 'required|min:3|max:255',
'email' => 'required|min:3|max:255|email:rfc,dns|unique:app_users,email',
'password' => [
'required', 'required_with:password_confirmation', 'confirmed', 'max:255',
Password::min(8)
->letters()
->mixedCase()
@ -65,11 +52,11 @@ class StoreAppUserRequest extends FormRequest
->symbols()
],
'authorizedFolders' => 'present|array',
'role' => [
'role' => [
'required',
new Enum(AppUserRolesEnum::class)
new EnumIsValidRule(AppUserRolesEnum::class)
],
'status' => 'required|boolean'
'status' => 'required|boolean'
];
}
@ -78,16 +65,16 @@ class StoreAppUserRequest extends FormRequest
*
* @return array
*/
public function attributes(): array
public function attributes()
{
return [
'lastname' => \trans('validation.attributes.last_name'),
'firstname' => \trans('validation.attributes.first_name'),
'email' => \trans('validation.attributes.email'),
'password' => \trans('validation.attributes.password'),
'authorizedFolders' => Str::of(\trans('models.classes.directory'))->plural(),
'role' => \trans('validation.custom.role'),
'status' => \trans('validation.custom.status')
'lastname' => \trans('nom'),
'firstname' => \trans('prénom'),
'email' => \trans('email'),
'password' => \trans('mot de passe'),
'authorizedFolders' => \trans('dossiers autorisés'),
'role' => \trans('rôle'),
'status' => \trans('statut')
];
}
}

View file

@ -2,29 +2,17 @@
namespace App\Http\Requests\Bo\AppUser;
use Illuminate\Support\Facades\Gate;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Rules\Password;
use Illuminate\Database\Query\Builder;
class UpdateAppUserRequest extends StoreAppUserRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return Gate::check('update', $this->route('app_user'));
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
public function rules()
{
$rules = [
'email' => [
@ -32,9 +20,7 @@ class UpdateAppUserRequest extends StoreAppUserRequest
'min:3',
'max:255',
'email:rfc,dns',
Rule::unique('app_users', 'email')
->where(fn (Builder $query) => $query->where('tenant_id', tenant('id')))
->ignore($this->route('app_user')->getKey())
Rule::unique('app_users', 'email')->ignore($this->route('app_user')->id),
],
'password' => [
'sometimes', 'nullable', 'required_with:password_confirmation', 'confirmed', 'max:255',

View file

@ -4,14 +4,14 @@ namespace App\Http\Requests\Bo;
use Illuminate\Foundation\Http\FormRequest;
class UpdateChangeOrderRequest extends FormRequest
class ChangeOrderRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
public function authorize()
{
return auth('backend')->check();
}
@ -21,10 +21,10 @@ class UpdateChangeOrderRequest extends FormRequest
*
* @return void
*/
protected function prepareForValidation(): void
protected function prepareForValidation()
{
$this->merge([
'direction' => $this->route('direction') === 'up' ? true : false
'direction' => $this->action === 'up' ? true : false
]);
}
@ -33,9 +33,10 @@ class UpdateChangeOrderRequest extends FormRequest
*
* @return array
*/
public function rules(): array
public function rules()
{
return [
'action' => 'required|string|in:up,down',
'direction' => 'required|boolean',
];
}

View file

@ -3,9 +3,7 @@
namespace App\Http\Requests\Bo\Directories;
use App\Enums\Documents\LangEnum;
use App\Models\Directory;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Gate;
class StoreDirectoryRequest extends FormRequest
{
@ -14,9 +12,9 @@ class StoreDirectoryRequest extends FormRequest
*
* @return boolean
*/
public function authorize(): bool
public function authorize()
{
return Gate::check('create', Directory::class);
return auth('backend')->check();
}
/**
@ -24,7 +22,7 @@ class StoreDirectoryRequest extends FormRequest
*
* @return void
*/
protected function prepareForValidation(): void
protected function prepareForValidation()
{
$this->merge([
'needAuthorization' => $this->needAuthorization ? true : false,
@ -39,8 +37,8 @@ class StoreDirectoryRequest extends FormRequest
public function rules()
{
return [
'name' => 'required|array',
'name.*' => 'required|string|min:3|max:255',
'name' => 'required|array',
'name.*' => 'required|string|min:3|max:255',
'needAuthorization' => 'required|boolean'
];
}
@ -50,15 +48,17 @@ class StoreDirectoryRequest extends FormRequest
*
* @return array
*/
public function attributes(): array
public function attributes()
{
$rules = [
'name' => \trans('validation.attributes.name'),
'needAuthorization' => \trans('validation.custom.authorization')
'name' => \trans('nom du dossier'),
'needAuthorization' => \trans('autorisation du dossier')
];
/** @var Object $langEnum */
foreach (LangEnum::toArray() as $langEnum) {
$rules["name.{$langEnum->name}"] = sprintf('%s %s', trans('validation.attributes.name'), $langEnum->label);
$def = $langEnum->getDefinition();
$rules["name.{$def}"] = \trans('nom du dossier :lang', [
'lang' => $langEnum->label
]);
}
return $rules;
}

View file

@ -2,17 +2,6 @@
namespace App\Http\Requests\Bo\Directories;
use Illuminate\Support\Facades\Gate;
class UpdateDirectoryRequest extends StoreDirectoryRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return Gate::check('update', $this->route('directory'));
}
}

View file

@ -4,13 +4,11 @@ namespace App\Http\Requests\Bo\Documents;
use App\Enums\Documents\LangEnum;
use App\Enums\Documents\TypeEnum;
use App\Lib\Helpers\FileStorageHelper;
use App\Models\Document;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Storage;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Rules\Enum;
use Uwil\LaravelCommon\Helpers\FileHelper;
use Kwaadpepper\Enum\Rules\EnumIsValidRule;
class StoreDocumentRequest extends FormRequest
{
@ -19,9 +17,9 @@ class StoreDocumentRequest extends FormRequest
*
* @return boolean
*/
public function authorize(): bool
public function authorize()
{
return Gate::check('create', Document::class);
return auth('backend')->check();
}
/**
@ -29,15 +27,21 @@ class StoreDocumentRequest extends FormRequest
*
* @return void
*/
protected function prepareForValidation(): void
protected function prepareForValidation()
{
$this->merge([
'status' => $this->status ? true : false,
'status' => $this->status ? true : false
]);
if (Document::query()->where('id', $this->document_id)->exists()) {
$this->merge([
'directory_id' => Document::query()->where('id', $this->document_id)
->value('directory_id')
]);
}
// * Sanitize Path
if (!empty($this->path)) {
$filename = $this->path;
$refMethod = new \ReflectionMethod(FileHelper::class, 'sanitizeFileName');
$refMethod = new \ReflectionMethod(FileStorageHelper::class, 'sanitizeFileName');
$refMethod->setAccessible(true);
$filename = \pathinfo(
$refMethod->invoke(null, htmlspecialchars($filename), false),
@ -47,12 +51,6 @@ class StoreDocumentRequest extends FormRequest
'path' => $filename
]);
}
if (Document::query()->where('id', $this->document_id)->exists() && $this->type) {
$this->merge([
'directory_id' => Document::query()->where('id', $this->document_id)->value('directory_id'),
'type' => TypeEnum::make($this->type, 'name')->name(),
]);
}
}
/**
@ -60,23 +58,20 @@ class StoreDocumentRequest extends FormRequest
*
* @return array
*/
public function rules(): array
public function rules()
{
/** @var \Illuminate\Filesystem\FilesystemAdapter */
$disk = Storage::disk('docs');
return [
'directory_id' => 'required|integer',
'document_id' => 'sometimes:directory_id|nullable|integer',
'uuid' => [
'required',
'uuid',
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
function ($attribute, $value, $fail) use ($disk) {
if ($this->type and \array_column(TypeEnum::toArray(), 'name')) {
$extension = TypeEnum::make($this->type, 'name')->name();
$value = "{$value}.{$extension}";
'document_id' => 'sometimes:directory_id|nullable|integer',
'uuid' => [
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
'required', 'uuid', function ($attribute, $value, $fail) use ($disk) {
if ($this->type and \in_array($this->type, TypeEnum::toValues())) {
$value = "{$value}.{$this->type}";
}
if (!$disk->has(sprintf('%s/%s', \tenant()->getKey(), $value))) {
if (!$disk->has($value)) {
$path = $this->path ?? $this->value ?? 'not found';
$fail(\trans('Le fichier :filename n\'est pas présent dans le système de fichier', [
'filename' => $path
@ -84,11 +79,17 @@ class StoreDocumentRequest extends FormRequest
}
}
],
'title' => 'required|min:3|max:255',
'type' => ['required', 'string', Rule::in(array_column(TypeEnum::toArray(), 'name'))],
'path' => 'required|min:3|max:255',
'lang' => ['required', new Enum(LangEnum::class)],
'status' => 'required|boolean'
'title' => 'required|min:3|max:255',
'type' => [
'required',
new EnumIsValidRule(TypeEnum::class)
],
'path' => 'required|min:3|max:255',
'lang' => [
'required',
new EnumIsValidRule(LangEnum::class)
],
'status' => 'required|boolean'
];
}
@ -97,17 +98,17 @@ class StoreDocumentRequest extends FormRequest
*
* @return array
*/
public function attributes(): array
public function attributes()
{
return [
'directory_id' => \trans('models.classes.directory'),
'document_id' => \trans('models.classes.document'),
'title' => \trans('validation.attributes.title'),
'uuid' => \trans('models.classes.document'),
'type' => \trans('validation.custom.type'),
'path' => \trans('validation.custom.path'),
'lang' => \trans('validation.custom.lang'),
'status' => \trans('validation.custom.status'),
'directory_id' => \trans('dossier à associer'),
'document_id' => \trans('document (FR) à associer'),
'title' => \trans('titre'),
'uuid' => \trans('document'),
'type' => \trans('type du document'),
'path' => \trans('chemin du document'),
'lang' => \trans('langue du document'),
'status' => \trans('état de publication du document'),
];
}
}

View file

@ -2,17 +2,6 @@
namespace App\Http\Requests\Bo\Documents;
use Illuminate\Support\Facades\Gate;
class UpdateDocumentRequest extends StoreDocumentRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return Gate::check('update', $this->route('document'));
}
}

View file

@ -3,7 +3,6 @@
namespace App\Http\Requests\Bo;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class FilterCustomerRequest extends FormRequest
{
@ -12,9 +11,9 @@ class FilterCustomerRequest extends FormRequest
*
* @return boolean
*/
public function authorize(): bool
public function authorize()
{
return \auth('backend')->check();
return \auth()->check();
}
/**
@ -22,15 +21,10 @@ class FilterCustomerRequest extends FormRequest
*
* @return array
*/
public function rules(): array
public function rules()
{
return [
'customer' => [
'sometimes',
'nullable',
'numeric',
Rule::unique('app_users', 'id')->where('tenant_id', tenant('id'))
],
'customer' => 'sometimes|nullable|numeric|exists:app_users,id'
];
}
}

View file

@ -4,10 +4,8 @@ namespace App\Http\Requests\Bo\Formularies;
use App\Enums\Formularies\InputStatusEnum;
use App\Enums\Formularies\InputTypeEnum;
use App\Models\Formulary;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Gate;
use Illuminate\Validation\Rules\Enum;
use Kwaadpepper\Enum\Rules\EnumIsValidRule;
class StoreFormularyRequest extends FormRequest
{
@ -17,15 +15,14 @@ class StoreFormularyRequest extends FormRequest
* @var string[]
*/
private $mobileAppLocales = ['fr', 'en', 'de', 'nl'];
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
public function authorize()
{
return Gate::check('create', Formulary::class);
return auth('backend')->check();
}
/**
@ -33,10 +30,10 @@ class StoreFormularyRequest extends FormRequest
*
* @return void
*/
protected function prepareForValidation(): void
protected function prepareForValidation()
{
$this->merge([
'required' => $this->required ? true : false,
'required' => $this->required ? true : false,
'is_visible' => $this->is_visible ? true : false,
]);
}
@ -46,21 +43,21 @@ class StoreFormularyRequest extends FormRequest
*
* @return array
*/
public function rules(): array
public function rules()
{
$rules = [
'type' => ['required', new Enum(InputTypeEnum::class)],
'type' => ['required', new EnumIsValidRule(InputTypeEnum::class)],
'placeholder' => 'present|array',
'details' => 'nullable|array',
'details.*' => 'required|array',
'required' => 'required|boolean',
'is_visible' => 'required|boolean',
'details' => 'nullable|array',
'details.*' => 'required|array',
'required' => 'required|boolean',
'is_visible' => 'required|boolean',
];
foreach ($this->mobileAppLocales as $locale) {
$rules["placeholder.{$locale}"] = 'required|string|min:3|max:100';
$rules["details.*.{$locale}"] = 'required|string|min:3|max:100';
}
if (intval($this->status) !== InputStatusEnum::locked->value()) {
if (intval($this->status) !== InputStatusEnum::locked()->value) {
$rules = array_merge($rules, ['name' => 'required|string|min:3|max:70']);
}
return $rules;
@ -74,18 +71,17 @@ class StoreFormularyRequest extends FormRequest
public function attributes(): array
{
$attributes = [
'type' => trans('validation.custom.type'),
'name' => trans('validation.attributes.name'),
'placeholder' => trans('validation.custom.placeholder'),
'details' => trans('validation.custom.detail'),
'details.*' => trans('validation.custom.detail'),
'details.*.value' => trans('validation.custom.valeur'),
'required' => trans('validation.custom.required'),
'is_visible' => trans('validation.custom.visibility'),
'type' => trans('type'),
'name' => trans('nom'),
'placeholder' => trans('placeholder'),
'details' => trans('détail'),
'details.*' => trans('détail 2'),
'details.*.value' => trans('valeur d\'un élément dans la liste des détails du contenu'),
'required' => trans('l\'état d\'obligation'),
'is_visible' => trans('l\'état de visibilité'),
];
foreach ($this->mobileAppLocales as $locale) {
$attributes["placeholder.{$locale}"] = trans('placeholder (:lang)', ['lang' => $locale]);
$attributes["details.*.{$locale}"] = trans('details (:lang)', ['lang' => $locale]);
}
return $attributes;
}

View file

@ -2,26 +2,14 @@
namespace App\Http\Requests\Bo\Formularies;
use Illuminate\Support\Facades\Gate;
class UpdateFormularyRequest extends StoreFormularyRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return Gate::check('update', $this->route('formulary'));
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
public function rules()
{
$rules = [
'type' => 'nullable',

View file

@ -11,7 +11,7 @@ class ImportRequest extends FormRequest
*
* @return boolean
*/
public function authorize(): bool
public function authorize()
{
return auth('backend')->check();
}
@ -21,7 +21,7 @@ class ImportRequest extends FormRequest
*
* @return array
*/
public function rules(): array
public function rules()
{
return [
'import' => [
@ -30,7 +30,7 @@ class ImportRequest extends FormRequest
'mimes:csv,txt',
// phpcs:ignore Generic.Files.LineLength.TooLong
'mimetypes:text/csv,text/plain,application/csv,text/comma-separated-values,text/anytext,application/octet-stream,application/txt',
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function ($attribute, $value, $fail) {
if (
$this->import->getClientMimeType() !== 'text/csv'

View file

@ -2,11 +2,11 @@
namespace App\Http\Requests\Bo\Settings;
use App\Enums\UserRolesEnum;
use App\Models\Setting;
use App\Traits\Requests\HasPicture;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Gate;
use Uwil\LaravelCommon\Traits\Requests\HasPicture;
class UpdateSettingRequest extends FormRequest
{
@ -17,18 +17,17 @@ class UpdateSettingRequest extends FormRequest
*
* @return boolean
*/
public function authorize(): bool
public function authorize()
{
if (
$this->key === "Button1" ||
$this->key === "Button2" ||
$this->key === "Button3"
$this->key === "Button3" ||
$this->key === "ShowIsActive"
) {
return Gate::check('update', $this->route('setting'));
return auth('backend')->check();
} else {
/** @var \App\Models\User $user */
$user = Auth::guard('backend')->user();
return $user->can('isAdmin');
return (auth('backend')->check() && Auth::user()->role === UserRolesEnum::admin()->value);
}
}
@ -37,9 +36,11 @@ class UpdateSettingRequest extends FormRequest
*
* @return void
*/
protected function prepareForValidation(): void
protected function prepareForValidation()
{
if (
if ($this->key === "ShowIsActive") {
$this->merge(['value' => intval($this->value) ? "true" : "false"]);
} elseif (
$this->key === "Button1" ||
$this->key === "Button2" ||
$this->key === "Button3"
@ -60,7 +61,7 @@ class UpdateSettingRequest extends FormRequest
*
* @return array
*/
public function rules(): array
public function rules()
{
if (
$this->key === "Button1" ||
@ -68,11 +69,10 @@ class UpdateSettingRequest extends FormRequest
$this->key === "Button3"
) {
$buttonsNullable = 0;
$buttons = Setting::query()
->where('key', 'Button1')
->orWhere('key', 'Button2')
->orWhere('key', 'Button3')
->get();
$buttons = Setting::where('key', 'Button1')
->orWhere('key', 'Button2')
->orWhere('key', 'Button3')
->get();
foreach ($buttons as $button) {
if ($button->value === null) {
if ($button->key !== $this->key) {
@ -82,7 +82,7 @@ class UpdateSettingRequest extends FormRequest
}
return [
'value' => [
//phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
//phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function ($attribute, $value, $fail) use ($buttonsNullable) {
if (!isset($value) && $buttonsNullable >= 2) {
$fail(\trans('Au moins 1 bouton doît être défini.'));
@ -96,7 +96,7 @@ class UpdateSettingRequest extends FormRequest
$this->key === "BackgroundImage" ||
$this->key === "HomeButtonImage"
) {
return $this->pictureRules('value', 100, 100, 1920, 1200, true);
return $this->pictureRules('value', true, 100, 100, 1920, 1200);
} else {
return ['value' => 'required|string|min:1'];
} //end if
@ -107,12 +107,12 @@ class UpdateSettingRequest extends FormRequest
*
* @return array
*/
public function attributes(): array
public function attributes()
{
return [
'key' => \trans('validation.custom.key'),
'value' => \trans('validation.custom.value'),
'type' => \trans('validation.custom.type'),
'key' => \trans('clé'),
'value' => \trans('valeur'),
'type' => \trans('type'),
];
}
}

View file

@ -2,9 +2,8 @@
namespace App\Http\Requests\Bo\Tags;
use App\Models\Tag;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Str;
class StoreTagRequest extends FormRequest
{
@ -13,9 +12,21 @@ class StoreTagRequest extends FormRequest
*
* @return boolean
*/
public function authorize(): bool
public function authorize()
{
return Gate::check('create', Tag::class);
return auth('backend')->check();
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
$this->merge([
'slug' => Str::slug($this->name),
]);
}
/**
@ -23,7 +34,7 @@ class StoreTagRequest extends FormRequest
*
* @return array
*/
public function rules(): array
public function rules()
{
return [
'name' => 'required|string|min:2|max:25',
@ -38,7 +49,8 @@ class StoreTagRequest extends FormRequest
public function attributes(): array
{
return [
'name' => trans('models.classes.tag'),
'name' => trans('name of the tag'),
'slug' => trans('slug of the tag'),
];
}
}

View file

@ -2,17 +2,6 @@
namespace App\Http\Requests\Bo\Tags;
use Illuminate\Support\Facades\Gate;
class UpdateTagRequest extends StoreTagRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return Gate::check('update', $this->route('tag'));
}
}

View file

@ -1,44 +0,0 @@
<?php
namespace App\Http\Requests\Bo\Tenants;
use App\Models\Tenant;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Gate;
class StoreTenantRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return Gate::allows('create', Tenant::class);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
return [
'name' => 'required|string|min:2|max:25',
];
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'name' => trans('validation.attributes.name'),
];
}
}

View file

@ -1,18 +0,0 @@
<?php
namespace App\Http\Requests\Bo\Tenants;
use Illuminate\Support\Facades\Gate;
class UpdateTenantRequest extends StoreTenantRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return Gate::check('update', $this->route('tenant'));
}
}

View file

@ -11,9 +11,9 @@ class UploadRequest extends FormRequest
*
* @return boolean
*/
public function authorize(): bool
public function authorize()
{
return \auth('backend')->check();
return \auth()->check();
}
/**
@ -21,7 +21,7 @@ class UploadRequest extends FormRequest
*
* @return array
*/
public function rules(): array
public function rules()
{
return [
'uuid' => 'nullable|uuid'

View file

@ -1,77 +0,0 @@
<?php
namespace App\Http\Requests\Bo\Users;
use App\Enums\UserRolesEnum;
use App\Models\User;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Rules\Enum;
class StoreUserRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return Gate::check('create', User::class);
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation(): void
{
if ($this->input('role') == UserRolesEnum::admin->value()) {
$this->merge([
'tenants' => null
]);
}
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
$isClient = $this->input('role') == UserRolesEnum::client->value();
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',
Rule::unique('users', 'email')
],
'role' => ['required', new Enum(UserRolesEnum::class)],
'tenants' => $isClient ? 'required|array' : 'nullable|array',
'tenants.*' => $isClient ? 'required|array' : 'nullable|array',
'tenants.*.id' => $isClient ? 'required|exists:tenants,id' : 'nullable|exists:tenants,id',
];
}
/**
* 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'),
'role' => trans('validation.attributes.role'),
'tenants' => Str::of(trans('models.classes.tenant'))->plural(),
];
}
}

View file

@ -1,42 +0,0 @@
<?php
namespace App\Http\Requests\Bo\Users;
use App\Enums\UserRolesEnum;
use Illuminate\Support\Facades\Gate;
use Illuminate\Validation\Rules\Enum;
class UpdateUserRequest extends StoreUserRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return Gate::check('update', $this->route('user'));
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
/** @var \App\Models\User $userModel */
$userModel = request()->route()->parameter('user');
$rules = [
'email' => [
'required',
'string',
'unique:users,email,' . $userModel->getKey(),
'email:rfc,strict,dns,spoof,filter',
'max:255'
],
'role' => ['sometimes', new Enum(UserRolesEnum::class)],
];
return \array_merge(parent::rules(), $rules);
}
}

View file

@ -8,14 +8,12 @@ class ApiResponseResource extends JsonResource
{
/**
* Status
*
* @var string
*/
private $status;
/**
* Datas
*
* @var array
*/
private $datas;
@ -40,11 +38,11 @@ class ApiResponseResource extends JsonResource
* @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
*/
public function toArray($request): array
public function toArray($request)
{
return [
'status' => $this->status,
'datas' => $this->datas
'datas' => $this->datas
];
}
}

View file

@ -2,7 +2,6 @@
namespace App\Http\Resources;
use App\Enums\AppUserRolesEnum;
use App\Models\Directory;
use Illuminate\Http\Resources\Json\JsonResource;
@ -12,34 +11,26 @@ class AppUserResource extends JsonResource
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
* @return array
* @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
*/
public function toArray($request)
{
/**
* @var \App\Models\AppUser
* @phpstan-ignore varTag.nativeType
*/
$appUser = $this;
//phpcs:enable
return [
'id' => $appUser->id,
'email' => $appUser->email,
'surname' => $appUser->lastname,
'firstname' => $appUser->firstname,
'password' => $appUser->password,
'activated' => $appUser->status,
'role' => ($appUser->role->equals(AppUserRolesEnum::total)) ? "TOTAL" : "USER",
'authorizedFolders' => json_encode(
$appUser->directories->each(
// @phpstan-ignore argument.type
function (Directory $directory) use ($request) {
return (new DirectoryResource($directory))->toArray($request);
}
)
),
'created_at' => $appUser->created_at,
'updated_at' => $appUser->updated_at,
'id' => $this->id,
'email' => $this->email,
'surname' => $this->lastname,
'firstname' => $this->firstname,
'password' => $this->password,
'activated' => $this->status,
'role' => ($this->role === 1) ? "TOTAL" : "USER",
'authorizedFolders' => json_encode($this->directories->each(function (Directory $directory) use ($request) {
return (new DirectoryResource($directory))->toArray($request);
})),
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
];
}
}

View file

@ -16,25 +16,23 @@ class ContactResource extends JsonResource
*/
public function toArray($request)
{
/**
* @var \App\Models\Contact
* @phpstan-ignore varTag.nativeType
*/
//phpcs:enable
/** @var \App\Models\Contact */
$contact = $this;
return [
'id' => $contact->id,
'lastname' => $contact->lastname,
'firstname' => $contact->firstname,
'email' => $contact->email,
'phone' => $contact->phone,
'data' => $contact->data,
'notes' => $contact->notes,
'visits' => $contact->visits,
'timestamp' => $contact->timestamp->toISOString(),
'app_user_id' => $contact->app_user_id,
'id' => $contact->id,
'lastname' => $contact->lastname,
'firstname' => $contact->firstname,
'email' => $contact->email,
'phone' => $contact->phone,
'data' => $contact->data,
'notes' => $contact->notes,
'visits' => $contact->visits,
'timestamp' => $contact->timestamp->toISOString(),
'app_user_id' => $contact->app_user_id,
'send_to_app_user_id' => null,
'created_at' => $contact->created_at,
'updated_at' => $contact->updated_at,
'created_at' => $contact->created_at,
'updated_at' => $contact->updated_at,
];
}
}

View file

@ -16,19 +16,15 @@ class DirectoryResource extends JsonResource
*/
public function toArray($request)
{
/**
* @var \App\Models\Directory
* @phpstan-ignore varTag.nativeType
*/
$directory = $this;
//phpcs:enable
return [
'id' => $directory->id,
'name' => json_encode($directory->name),
'parent' => $directory->directory_id,
'order' => $directory->order,
'needAuthorization' => $directory->needAuthorization,
'created_at' => $directory->created_at,
'updated_at' => $directory->updated_at,
'id' => $this->id,
'name' => json_encode($this->name),
'parent' => $this->directory_id,
'order' => $this->order,
'needAuthorization' => $this->needAuthorization,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
];
}
}

View file

@ -16,23 +16,32 @@ class DocumentResource extends JsonResource
*/
public function toArray($request)
{
/**
* @var \App\Models\Document
* @phpstan-ignore varTag.nativeType
*/
$document = $this;
switch ($this->lang) {
case 1:
$tmpLang = "fr";
break;
case 2:
$tmpLang = "en";
break;
case 3:
$tmpLang = "de";
break;
case 4:
$tmpLang = "nl";
break;
}
return [
'id' => $document->id,
'uuid' => $document->uuid,
'title' => $document->title,
'type' => $document->type,
'directory' => $document->directory_id,
'path' => $document->path,
'lang' => $document->lang->name,
'published' => ($document->status) ? 1 : 0,
'parent' => $document->document_id,
'created_at' => $document->created_at,
'updated_at' => $document->updated_at,
'id' => $this->id,
'uuid' => $this->uuid,
'title' => $this->title,
'type' => $this->type,
'directory' => $this->directory_id,
'path' => $this->path,
'lang' => $tmpLang,
'published' => ($this->status) ? 1 : 0,
'parent' => $this->document_id,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
];
}
}

View file

@ -11,30 +11,27 @@ class FormularyResource extends JsonResource
*
* @param \Illuminate\Http\Request $request
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
* @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
* @phpcs:disable: Squiz.Commenting.FunctionComment.TypeHintMissing
* @phpcs:disable: Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
*/
public function toArray($request)
{
/**
* @var \App\Models\Formulary
* @phpstan-ignore varTag.nativeType
*/
/** @var \App\Models\Formulary */
$formulary = $this;
return [
'id' => $formulary->id,
'type' => $formulary->type->value,
'name' => $formulary->name,
'slug' => $formulary->slug,
'id' => $formulary->id,
'type' => $formulary->type->value,
'name' => $formulary->name,
'slug' => $formulary->slug,
'placeholder' => $formulary->placeholder,
'details' => \collect($formulary->details)->filter()->map(function (array $detail) {
'details' => \collect($formulary->details)->filter()->map(function (array $detail) {
// * Realm can't hold more deeper dans Map<mixed,primitive>
return \json_encode($detail);
})->all(),
'is_visible' => $formulary->is_visible,
'required' => $formulary->required,
'status' => $formulary->status->value,
'order' => $formulary->order,
'is_visible' => $formulary->is_visible,
'required' => $formulary->required,
'status' => $formulary->status->value,
'order' => $formulary->order,
];
}
}

View file

@ -17,7 +17,7 @@ class AppUserImport implements ToModel, WithUpserts, WithCustomCsvSettings
/**
* @return string|array
*/
public function uniqueBy(): string|array
public function uniqueBy()
{
return 'email';
}
@ -37,27 +37,29 @@ class AppUserImport implements ToModel, WithUpserts, WithCustomCsvSettings
*
* @return \Illuminate\Database\Eloquent\Model|null
*/
public function model(array $row): \Illuminate\Database\Eloquent\Model|null
public function model(array $row)
{
if (!isset($row[0]) || !isset($row[1]) || !isset($row[2]) || !isset($row[3])) {
return null;
}
$password = Hash::make(Str::random(10));
$user = new AppUser([
'email' => $row[3],
'lastname' => ucfirst(strtolower($row[1])),
$user = new AppUser([
'email' => $row[3],
'lastname' => ucfirst(strtolower($row[1])),
'firstname' => ucfirst(strtolower($row[2])),
'password' => $password,
'status' => 1,
'role' => AppUserRolesEnum::user,
'password' => '',
'status' => 1,
'role' => AppUserRolesEnum::user(),
]);
if ($user) {
$password = Str::random(10);
$user->password = Hash::make($password);
$user->save();
Mail::to($user->email)->send(new AccountCreated($user, $password));
}
$user->saveOrFail();
Mail::to($user->email)->send(new AccountCreated([
'email' => $user->email,
'lastname' => $user->lastname,
'firstname' => $user->firstname,
'password' => $user->password,
]));
return $user;
}

View file

@ -1,46 +0,0 @@
<?php
namespace App\Jobs\Mails;
use App\Mail\TenantDeleted;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Facades\Mail;
class SendTenantDeletedMail implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
/** @var string $author */
private $author;
/** @var string $name */
private $name;
/**
* Create a new job instance.
*
* @param string $author
* @param string $name
* @return void
*/
public function __construct(string $author, string $name)
{
$this->author = $author;
$this->name = $name;
}
/**
* Execute the job.
*
* @return void
*/
public function handle(): void
{
foreach (config('mail.admin.address') as $address) {
Mail::to($address)->send(new TenantDeleted($this->author, $this->name));
}
}
}

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($param))[0] ?? null;
}
switch ($type) {
case 'Lambda':
case 'Object':
continue 2;
case 'array':
$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,444 @@
<?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} === $model->getOriginal($attribute)) {
return;
}
$originalPath = \ltrim(\urldecode($model->getOriginal($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.
*/
public static function storeFile(
Model $model,
$file,
bool $slugify = false,
string $filename = null,
bool $private = false
): string {
$tableName = $model->getTable();
if (\is_object($file) and $file instanceof \Illuminate\Http\UploadedFile) {
if ($private) {
throw new \RuntimeException('Private storage for uploaded files is unhandled');
}
/** @var \Illuminate\Http\UploadedFile $file */
$filename = $filename ?: $file->getClientOriginalName();
$filename = $slugify ? self::slugifyFileName($filename) : $filename;
$newPath = self::prepareMoveFile($tableName, $filename);
$newPath = str_replace(storage_path('app'), '', $newPath);
$file->storePubliclyAs(
pathinfo($newPath, \PATHINFO_DIRNAME),
pathinfo($newPath, \PATHINFO_BASENAME)
);
return 'storage' . str_replace('/public', '', $newPath);
}
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 + beautifier
$filename = self::sanitizeFileName($filename);
// * Security filter
$filename = \filter_var($filename, \FILTER_SANITIZE_STRING);
// * 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 has 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;
}
}

View file

@ -0,0 +1,298 @@
<?php
namespace App\Lib\Helpers;
use App\Enums\Pagination\ItemsPerPaginationEnum;
use App\Lib\Inputs\Definition\Input;
use App\Models\Contact;
use App\Models\Formulary;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
use Kwaadpepper\Enum\Rules\EnumIsValidRule;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class ToolboxHelper
{
/**
* 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 => ':attribute : Il est interdit de coller une url du back office dans ce champ'
]);
}
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 => sprintf(
':attribute : Il est interdit de coller une url de %s dans ce champ',
config('app.name')
)
]);
}
return $match;
} catch (NotFoundHttpException $e) {
return $match;
}
},
$text
);
return $text;
}
/**
* Mutltibyte string replace
* @param mixed $search
* @param mixed $replace
* @param mixed $subject
* @param integer $count
* @return mixed
*/
public static function mbReplace($search, $replace, $subject, int &$count = 0)
{
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 mixed $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,
$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('La valeur :value pour le champ :attribute est déjà utilisé', [
'attribute' => $field,
'value' => $value
])
])->validate();
return;
}
Validator::make([$field => $value], [
$field => Rule::unique($table)->where($query)->ignore($id),
], [
$field . '.unique' => trans('La valeur pour le champ :attribute est déjà utilisé', [
'attribute' => $field
])
])->validate();
}
/**
* 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));
}
}
/**
* Get validated pagination from request
*
* @param integer $default
* @return integer
*/
public static function getValidatedPaginate(int $default): int
{
try {
return \intval(Validator::make(
['pagination' => \request()->get('pagination')],
['pagination' => new EnumIsValidRule(ItemsPerPaginationEnum::class)]
)->validated()['pagination']);
} catch (ValidationException $e) {
return $default;
}
}
/**
* Assert contact data is matching actual Formularies
*
* @param \App\Models\Contact $contact
* @return void
*/
public static function assertContactDataIsMatchingFormulary(Contact $contact)
{
/** @var \Illuminate\Support\Collection<string,integer> */
$formularies = Formulary::query()->pluck('slug')->flip();
\collect($contact->data)->each(function (Input $input) use ($formularies) {
if (!$formularies->has($input->data->formSlug)) {
throw new \ErrorException(
"Formulary {$input->data->formSlug} does not exist, do not store this contact data prop"
);
}
});
}
/**
* Remove Data Input on All contacts
*
* @param \App\Models\Formulary $formulary
* @return void
*/
public static function removeDataInputOnAllContacts(Formulary $formulary): void
{
DB::transaction(function () use ($formulary) {
Contact::query()->select(['id', 'data'])->each(function (Contact $contact) use ($formulary) {
$contact->data = \collect($contact->data)->filter(function (Input $input) use ($formulary) {
return $input->data->formSlug !== $formulary->slug;
});
$contact->saveOrFail();
});
});
}
}

View file

@ -3,6 +3,7 @@
namespace App\Lib\Inputs\Definition;
use App\Models\Formulary;
use Illuminate\Database\Eloquent\Builder;
/**
* To manipulate Input
@ -62,11 +63,10 @@ abstract class Input implements InputInterface
/**
* Get the matching formulary for this object
*
* @return \App\Models\Formulary|null
* @return \App\Models/Formulary|null
*/
public function getFormulary(): \App\Models\Formulary|null
public function getFormulary(): ?Formulary
{
// @phpstan-ignore return.type
return $this->formularyQuery()->first();
}
@ -85,7 +85,7 @@ abstract class Input implements InputInterface
*
* @return \Illuminate\Database\Eloquent\Builder
*/
private function formularyQuery(): \Illuminate\Database\Eloquent\Builder
private function formularyQuery(): Builder
{
return Formulary::query()
->where('slug', $this->data->formSlug)
@ -102,12 +102,10 @@ abstract class Input implements InputInterface
* @return static
* @throws \RuntimeException If the input type is not recognized, or data is invalid.
*/
public static function from(InputData $data, bool $required = true): static
public static function from(InputData $data, bool $required = true)
{
$inputTypeDataClass = static::class;
$type = "";
try {
// @phpstan-ignore function.alreadyNarrowedType
$type = \is_numeric($data->type) ? intval($data->type) : strval($data->type);
return new static(
InputData::make($data->title, $data->formSlug, $type, $data->value, $required),
@ -133,7 +131,7 @@ abstract class Input implements InputInterface
* @return static
* @throws \RuntimeException If the input type is not recognized, or data is invalid.
*/
public static function make(string $title, string $formSlug, $data, bool $required = true): static
public static function make(string $title, string $formSlug, $data, bool $required = true)
{
return static::from(
InputData::make(
@ -167,9 +165,9 @@ abstract class Input implements InputInterface
*
* @return array
*/
public function toArray(): array
public function toArray()
{
return (array) $this->data;
return (array)$this->data;
}
/**
@ -179,7 +177,7 @@ abstract class Input implements InputInterface
* @return string
* @phpcs:disable Squiz.Commenting.FunctionComment.ScalarTypeHintMissing
*/
public function toJson($options = 0): string
public function toJson($options = 0)
{
// phpcs:enable
return \json_encode($this->jsonSerialize(), $options);
@ -190,7 +188,8 @@ abstract class Input implements InputInterface
*
* @return array
*/
public function jsonSerialize(): array
#[\ReturnTypeWillChange]
public function jsonSerialize()
{
return $this->toArray();
}

View file

@ -5,13 +5,13 @@ namespace App\Lib\Inputs\Definition;
use App\Enums\Formularies\InputTypeEnum;
/**
* @property string $title The input type placeholder
* @property string $formSlug The original Formulary slug.
* @property integer $type The input type value
* @property string|array<integer,string> $value The input value, either a string or an array
* @property-read string $title The input type placeholder
* @property-read string $formSlug The original Formulary slug.
* @property-read integer $type The input type value
* @property-read string|array<integer,string> $value The input value, either a string or an array
* with the value index and label
* (for Multiselect and checkboxes)
* @property boolean $required The input is required ?
* @property-read boolean $required The input is required ?
* @package App\Lib\Inputs
*/
class InputData extends \stdClass
@ -62,7 +62,7 @@ class InputData extends \stdClass
* @return void
* @throws \RuntimeException If $data is wrong.
*/
public static function assertIsInputTypeData(InputData $data): void
public static function assertIsInputTypeData(InputData $data)
{
$title = $data->title;
$type = $data->type;
@ -70,19 +70,15 @@ class InputData extends \stdClass
$value = $data->value;
$required = $data->required;
// @phpstan-ignore function.alreadyNarrowedType
if (!\is_string($title)) {
throw new \RuntimeException("InputTypeData 'title' should be a string");
}
// @phpstan-ignore function.alreadyNarrowedType
if (!\is_string($formSlug)) {
throw new \RuntimeException("InputTypeData 'formSlug' should be a string");
}
// @phpstan-ignore function.alreadyNarrowedType
if (!\is_integer($type)) {
throw new \RuntimeException("InputTypeData 'type' should be an integer");
}
// @phpstan-ignore function.alreadyNarrowedType
if (!\is_bool($required)) {
throw new \RuntimeException("InputTypeData 'required' should be a boolean");
}
@ -93,7 +89,6 @@ class InputData extends \stdClass
throw new \RuntimeException($e->getMessage());
}
// @phpstan-ignore function.impossibleType, function.alreadyNarrowedType, booleanAnd.alwaysFalse
if (!\is_null($value) && !\is_string($value) && !\is_array($value)) {
$type = \gettype($value);
$value = \json_encode($value);

View file

@ -31,7 +31,7 @@ interface InputInterface extends Jsonable, Arrayable, \JsonSerializable
* @return static
* @throws \RuntimeException If the input type is not recognized, or data is invalid.
*/
public static function from(InputData $data, bool $required = true): static;
public static function from(InputData $data, bool $required = true);
/**
* Parse DB line to Object
@ -45,7 +45,7 @@ interface InputInterface extends Jsonable, Arrayable, \JsonSerializable
* @return static
* @throws \RuntimeException If the input type is not recognized, or data is invalid.
*/
public static function make(string $title, string $formSlug, $data, bool $required = true): static;
public static function make(string $title, string $formSlug, $data, bool $required = true);
// * --- CHILDS Should implement ---
@ -66,7 +66,7 @@ interface InputInterface extends Jsonable, Arrayable, \JsonSerializable
/**
* Get the matching formulary for this object
*
* @return \App\Models\Formulary|null
* @return \App\Models/Formulary|null
*/
public function getFormulary(): ?Formulary;
@ -91,8 +91,8 @@ interface InputInterface extends Jsonable, Arrayable, \JsonSerializable
*
* @param string|array $data The line with data.
* @param boolean $required The line is required.
* @return void
* @return string|array<integer,string>
* @throws \Illuminate\Validation\ValidationException If the data is invalid.
*/
public static function validateData($data, bool $required): void;
public static function validateData($data, bool $required);
}

View file

@ -4,6 +4,7 @@ namespace App\Lib\Inputs;
use App\Enums\Formularies\InputTypeEnum;
use App\Lib\Inputs\Definition\Input;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Support\Facades\Validator as FacadesValidator;
/**
@ -17,9 +18,9 @@ class InputCheckbox extends Input
*
* @return \App\Enums\Formularies\InputTypeEnum
*/
public static function getType(): \App\Enums\Formularies\InputTypeEnum
public static function getType(): InputTypeEnum
{
return InputTypeEnum::checkbox;
return InputTypeEnum::checkbox();
}
@ -30,12 +31,12 @@ class InputCheckbox extends Input
* @param boolean $required The line is required.
* @return \Illuminate\Contracts\Validation\Validator
*/
public static function getValidator($data, bool $required): \Illuminate\Contracts\Validation\Validator
public static function getValidator($data, bool $required): Validator
{
return FacadesValidator::make(
['value' => $data],
[
'value' => 'array' . ($required ? '|min:1' : ''),
'value' => 'array' . ($required ? '|min:1' : ''),
'value.*' => 'required|string|max:255'
]
);

View file

@ -4,6 +4,7 @@ namespace App\Lib\Inputs;
use App\Enums\Formularies\InputTypeEnum;
use App\Lib\Inputs\Definition\Input;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Support\Facades\Validator as FacadesValidator;
/**
@ -17,9 +18,9 @@ class InputEmail extends Input
*
* @return \App\Enums\Formularies\InputTypeEnum
*/
public static function getType(): \App\Enums\Formularies\InputTypeEnum
public static function getType(): InputTypeEnum
{
return InputTypeEnum::email;
return InputTypeEnum::email();
}
@ -30,7 +31,7 @@ class InputEmail extends Input
* @param boolean $required The line is required.
* @return \Illuminate\Contracts\Validation\Validator
*/
public static function getValidator($data, bool $required): \Illuminate\Contracts\Validation\Validator
public static function getValidator($data, bool $required): Validator
{
return FacadesValidator::make(
['value' => $data],

View file

@ -4,6 +4,7 @@ namespace App\Lib\Inputs;
use App\Enums\Formularies\InputTypeEnum;
use App\Lib\Inputs\Definition\Input;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Support\Facades\Validator as FacadesValidator;
/**
@ -17,9 +18,9 @@ class InputPhone extends Input
*
* @return \App\Enums\Formularies\InputTypeEnum
*/
public static function getType(): \App\Enums\Formularies\InputTypeEnum
public static function getType(): InputTypeEnum
{
return InputTypeEnum::phone;
return InputTypeEnum::phone();
}
@ -30,7 +31,7 @@ class InputPhone extends Input
* @param boolean $required The line is required.
* @return \Illuminate\Contracts\Validation\Validator
*/
public static function getValidator($data, bool $required): \Illuminate\Contracts\Validation\Validator
public static function getValidator($data, bool $required): Validator
{
return FacadesValidator::make(
['value' => $data],

View file

@ -4,6 +4,7 @@ namespace App\Lib\Inputs;
use App\Enums\Formularies\InputTypeEnum;
use App\Lib\Inputs\Definition\Input;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Support\Facades\Validator as FacadesValidator;
/**
@ -17,9 +18,9 @@ class InputRadio extends Input
*
* @return \App\Enums\Formularies\InputTypeEnum
*/
public static function getType(): \App\Enums\Formularies\InputTypeEnum
public static function getType(): InputTypeEnum
{
return InputTypeEnum::radio;
return InputTypeEnum::radio();
}
@ -30,7 +31,7 @@ class InputRadio extends Input
* @param boolean $required The line is required.
* @return \Illuminate\Contracts\Validation\Validator
*/
public static function getValidator($data, bool $required): \Illuminate\Contracts\Validation\Validator
public static function getValidator($data, bool $required): Validator
{
return FacadesValidator::make(
['value' => $data],

View file

@ -4,6 +4,7 @@ namespace App\Lib\Inputs;
use App\Enums\Formularies\InputTypeEnum;
use App\Lib\Inputs\Definition\Input;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Support\Facades\Validator as FacadesValidator;
/**
@ -17,9 +18,9 @@ class InputSelect extends Input
*
* @return \App\Enums\Formularies\InputTypeEnum
*/
public static function getType(): \App\Enums\Formularies\InputTypeEnum
public static function getType(): InputTypeEnum
{
return InputTypeEnum::select;
return InputTypeEnum::select();
}
@ -30,7 +31,7 @@ class InputSelect extends Input
* @param boolean $required The line is required.
* @return \Illuminate\Contracts\Validation\Validator
*/
public static function getValidator($data, bool $required): \Illuminate\Contracts\Validation\Validator
public static function getValidator($data, bool $required): Validator
{
return FacadesValidator::make(
['value' => $data],

View file

@ -15,8 +15,8 @@ class InputSelectMultiple extends InputCheckbox
*
* @return \App\Enums\Formularies\InputTypeEnum
*/
public static function getType(): \App\Enums\Formularies\InputTypeEnum
public static function getType(): InputTypeEnum
{
return InputTypeEnum::selectMultiple;
return InputTypeEnum::selectMultiple();
}
}

View file

@ -4,6 +4,7 @@ namespace App\Lib\Inputs;
use App\Enums\Formularies\InputTypeEnum;
use App\Lib\Inputs\Definition\Input;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Support\Facades\Validator as FacadesValidator;
/**
@ -16,9 +17,9 @@ class InputText extends Input
*
* @return \App\Enums\Formularies\InputTypeEnum
*/
public static function getType(): \App\Enums\Formularies\InputTypeEnum
public static function getType(): InputTypeEnum
{
return InputTypeEnum::text;
return InputTypeEnum::text();
}
@ -29,7 +30,7 @@ class InputText extends Input
* @param boolean $required The line is required.
* @return \Illuminate\Contracts\Validation\Validator
*/
public static function getValidator($data, bool $required): \Illuminate\Contracts\Validation\Validator
public static function getValidator($data, bool $required): Validator
{
return FacadesValidator::make(
['value' => $data],

View file

@ -2,6 +2,7 @@
namespace App\Mail;
use App\Models\AppUser;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
@ -11,30 +12,36 @@ class AccountCreated extends Mailable
use Queueable;
use SerializesModels;
/** @var object */
public $data;
/**
* The app user instance
*
* @var \App\Models\AppUser;
*/
public $user;
/** @var string */
public $password;
/**
* Create a new message instance.
*
* @param mixed $data
* @param \App\Models\AppUser $user
* @param string $password
* @return void
*/
public function __construct(mixed $data)
public function __construct(AppUser $user, string $password)
{
$this->data = (object) $data;
$this->user = $user;
$this->password = $password;
}
/**
* Build the message.
*
* @return mixed
* @return $this
*/
public function build(): mixed
public function build()
{
return $this
->from($this->data->email, sprintf('%s %s', $this->data->lastname, $this->data->firstname))
->subject(__("Votre compte a été créé"))
->markdown('bo.mails.account-created');
return $this->subject(__("Votre compte a été créé"))->view('emails.account-created');
}
}

View file

@ -3,11 +3,10 @@
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class SendFlyer extends Mailable implements ShouldQueue
class SendFlyer extends Mailable
{
use Queueable;
use SerializesModels;
@ -21,20 +20,20 @@ class SendFlyer extends Mailable implements ShouldQueue
* @param mixed $data
* @return void
*/
public function __construct(mixed $data)
public function __construct($data)
{
$this->data = (object) $data;
$this->data = (object)$data;
}
/**
* Build the message.
*
* @return mixed
* @return $this
*/
public function build(): mixed
public function build()
{
return $this
->from(config('mail.from.address'), $this->data->fromName)
->from($this->data->from, $this->data->fromName)
->subject($this->data->subject)
->markdown('bo.mails.sendflyer');
}

View file

@ -3,11 +3,10 @@
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class SendFlyerFailed extends Mailable implements ShouldQueue
class SendFlyerFailed extends Mailable
{
use Queueable;
use SerializesModels;
@ -21,19 +20,20 @@ class SendFlyerFailed extends Mailable implements ShouldQueue
* @param mixed $data
* @return void
*/
public function __construct(mixed $data)
public function __construct($data)
{
$this->data = (object) $data;
$this->data = (object)$data;
}
/**
* Build the message.
*
* @return mixed
* @return $this
*/
public function build(): mixed
public function build()
{
return $this
->from('noreply@ldc.fr', '[APP] ' . config('app.name'))
->subject('[MAIL] Une erreur est survenue')
->view('bo.mails.sendflyerfailed', ['data' => $this->data]);
}

View file

@ -1,48 +0,0 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class TenantDeleted extends Mailable
{
use Queueable;
use SerializesModels;
/** @var string $author */
private $author;
/** @var string $name */
private $name;
/**
* Create a new message instance.
*
* @param string $author
* @param string $name
* @return void
*/
public function __construct(string $author, string $name)
{
$this->author = $author;
$this->name = $name;
}
/**
* Build the message.
*
* @return mixed
*/
public function build(): mixed
{
return $this
->from(config('mail.from.address'), config('mail.from.name'))
->subject(sprintf('Suppression du tenant %s', $this->name))
->markdown('bo.mails.tenant-deleted', [
'author' => $this->author,
'name' => $this->name,
]);
}
}

View file

@ -3,35 +3,30 @@
namespace App\Models;
use App\Enums\AppUserRolesEnum;
use App\Traits\Models\IsRelatedToTenant;
use App\Lib\Helpers\ToolboxHelper;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Hash;
use Stancl\Tenancy\Database\Concerns\BelongsToTenant;
use Kwaadpepper\Enum\Traits\CastsEnums;
/**
* @property integer $id App user id.
* @property string $lastname App user last name.
* @property string $firstname App user first name.
* @property string $email App user email.
* @property string $password App user password.
* @property string|null $tenant_id Associated tenant.
* @property boolean $status App user status.
* @property \App\Enums\AppUserRolesEnum $role App user role.
* @property integer $order App user order.
* @property-read \Carbon\Carbon $created_at Created date.
* @property-read \Carbon\Carbon $updated_at Updated date.
* @property integer $id
* @property string $lastname
* @property string $firstname
* @property string $email
* @property string $password
* @property \App\Enums\AppUserRolesEnum $role
* @property boolean $status
* @property integer $order
*/
class AppUser extends Model
{
use BelongsToTenant;
use IsRelatedToTenant;
use HasFactory;
use CastsEnums;
/**
* The attributes that are mass assignable.
*
* @var list<string>
* fillable
* @var string[]
*/
protected $fillable = [
'lastname',
@ -46,18 +41,17 @@ class AppUser extends Model
/**
* The attributes that should be hidden for serialization.
*
* @var list<string>
* @var array<int, string>
*/
protected $hidden = [
'password',
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
* cast enum
* @var string[]
*/
protected $casts = [
protected $enumCasts = [
'role' => AppUserRolesEnum::class
];
@ -68,14 +62,15 @@ class AppUser extends Model
*
* @return void
*/
protected static function booted(): void
protected static function booted()
{
static::creating(function (self $model) {
self::assertFieldIsUnique('email', $model->email);
self::setOrder($model);
self::updatePassword($model);
static::updatePassword($model);
});
static::updating(function (self $model) {
self::updatePassword($model);
static::updatePassword($model);
});
}
@ -95,16 +90,28 @@ class AppUser extends Model
}
/**
* Set order after the last element of the list.
* Asserts using validation that the field is unique.
*
* @param self $model
* @param string $nameField
* @param string $valueField
* @param integer|null $id
* @return void
*/
private static function setOrder(self $model): void
private static function assertFieldIsUnique(string $nameField, string $valueField, ?int $id = null)
{
$model->order = \intval(
self::query()->where(self::whereTenantQuery())->max('order')
) + 1;
$table = (new self())->getTable();
ToolboxHelper::assertFieldIsUnique($table, $nameField, $valueField, $id);
}
/**
* Set order after the last element of the list.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return void
*/
private static function setOrder(Model $model): void
{
$model->order = \intval(self::query()->max('order')) + 1;
}
// * RELATIONSHIPS
@ -114,7 +121,7 @@ class AppUser extends Model
*
* @return \Illuminate\Database\Eloquent\Relations\MorphToMany
*/
public function directories(): \Illuminate\Database\Eloquent\Relations\MorphToMany
public function directories()
{
return $this->morphToMany(Directory::class, 'directorable');
}

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