Merge branch 'app-users' into 'directories'
update CRUD app-users #3 See merge request ldc-web/projets/com-app-bo!2
This commit is contained in:
commit
ec94594cea
140 changed files with 9163 additions and 10695 deletions
|
|
@ -3,6 +3,9 @@ APP_ENV=local
|
|||
APP_KEY=
|
||||
APP_DEBUG=true
|
||||
APP_URL=http://localhost
|
||||
API_TOKEN=toto
|
||||
|
||||
BRAND=
|
||||
|
||||
LOG_CHANNEL=stack
|
||||
LOG_DEPRECATIONS_CHANNEL=null
|
||||
|
|
|
|||
4
CHANGELOG.md
Normal file
4
CHANGELOG.md
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
#### **21/04/23 ► v2.1**
|
||||
- Refonte totale du back-office
|
||||
- Ajout des étiquettes sur les documents
|
||||
- Ajout d'une section administration des étiquettes
|
||||
15
README.md
15
README.md
|
|
@ -1,7 +1,10 @@
|
|||
## reste à faire
|
||||
## Migration v2.1
|
||||
|
||||
FO :
|
||||
ts
|
||||
view
|
||||
route
|
||||
les fichiers à la racine
|
||||
- add default table of laravel named "Migrations" :
|
||||
`php artisan migrate:install`
|
||||
|
||||
- seed migrations already passed :
|
||||
`php artisan db:seed --class=MigrationSeeder --force`
|
||||
|
||||
- continue migrations :
|
||||
`php artisan migrate`
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Enums\AppUserRolesEnum;
|
||||
use App\Enums\UserRolesEnum;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
|
@ -201,16 +201,16 @@ class CreateAdmin extends Command
|
|||
try {
|
||||
$tmp = $this->choice(
|
||||
'Select his role',
|
||||
[AppUserRolesEnum::total()->label, AppUserRolesEnum::user()->label],
|
||||
AppUserRolesEnum::user()->label,
|
||||
[UserRolesEnum::admin()->label, UserRolesEnum::client()->label],
|
||||
UserRolesEnum::client()->label,
|
||||
$maxAttempts = null,
|
||||
$allowMultipleSelections = false
|
||||
);
|
||||
if ($tmp === AppUserRolesEnum::total()->label) {
|
||||
$tmp = AppUserRolesEnum::total();
|
||||
if ($tmp === UserRolesEnum::admin()->label) {
|
||||
$tmp = UserRolesEnum::admin();
|
||||
}
|
||||
if ($tmp === AppUserRolesEnum::user()->label) {
|
||||
$tmp = AppUserRolesEnum::user();
|
||||
if ($tmp === UserRolesEnum::client()->label) {
|
||||
$tmp = UserRolesEnum::client();
|
||||
}
|
||||
$validator = Validator::make(['role' => $tmp], [
|
||||
'role' => 'required'
|
||||
|
|
|
|||
44
app/Enums/Documents/LangEnum.php
Normal file
44
app/Enums/Documents/LangEnum.php
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Documents;
|
||||
|
||||
use Kwaadpepper\Enum\BaseEnumRoutable;
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
/**
|
||||
* enum values
|
||||
* @return array
|
||||
*/
|
||||
protected static function values(): array
|
||||
{
|
||||
return [
|
||||
'fr' => 1,
|
||||
'en' => 2,
|
||||
'de' => 3,
|
||||
'nl' => 4,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* enum labels
|
||||
* @return array
|
||||
*/
|
||||
protected static function labels(): array
|
||||
{
|
||||
return [
|
||||
'fr' => 'Français (FR)',
|
||||
'en' => 'Anglais (EN)',
|
||||
'de' => 'Allemand (DE)',
|
||||
'nl' => 'Néerlandais (NL)',
|
||||
];
|
||||
}
|
||||
}
|
||||
38
app/Enums/Documents/TypeEnum.php
Normal file
38
app/Enums/Documents/TypeEnum.php
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Documents;
|
||||
|
||||
use Kwaadpepper\Enum\BaseEnumRoutable;
|
||||
|
||||
/**
|
||||
* This defines a data type.
|
||||
*
|
||||
* @method static self pdf()
|
||||
* @method static self mp4()
|
||||
*/
|
||||
final class TypeEnum extends BaseEnumRoutable
|
||||
{
|
||||
/**
|
||||
* enum values
|
||||
* @return array
|
||||
*/
|
||||
protected static function values(): array
|
||||
{
|
||||
return [
|
||||
'pdf' => 1,
|
||||
'mp4' => 2,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* enum labels
|
||||
* @return array
|
||||
*/
|
||||
protected static function labels(): array
|
||||
{
|
||||
return [
|
||||
'pdf' => 'PDF',
|
||||
'mp4' => 'MP4',
|
||||
];
|
||||
}
|
||||
}
|
||||
53
app/Enums/SettingTypesEnum.php
Normal file
53
app/Enums/SettingTypesEnum.php
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
use Kwaadpepper\Enum\BaseEnumRoutable;
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
/**
|
||||
* enum values
|
||||
* @return array
|
||||
*/
|
||||
protected static function values(): array
|
||||
{
|
||||
return [
|
||||
'string' => 1,
|
||||
'numeric' => 2,
|
||||
'picture' => 3,
|
||||
'json' => 4,
|
||||
'boolean' => 5,
|
||||
'color' => 6,
|
||||
'directory' => 7
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* enum labels
|
||||
* @return array
|
||||
*/
|
||||
protected static function labels(): array
|
||||
{
|
||||
return [
|
||||
'string' => 'String (Hexa)',
|
||||
'numeric' => 'Numeric',
|
||||
'picture' => 'Picture',
|
||||
'json' => 'Json',
|
||||
'boolean' => 'Boolean',
|
||||
'color' => 'Color',
|
||||
'directory' => 'Directory',
|
||||
];
|
||||
}
|
||||
}
|
||||
38
app/Enums/UserRolesEnum.php
Normal file
38
app/Enums/UserRolesEnum.php
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
use Kwaadpepper\Enum\BaseEnumRoutable;
|
||||
|
||||
/**
|
||||
* This defines a data type.
|
||||
*
|
||||
* @method static self admin() admin role
|
||||
* @method static self client() client role
|
||||
*/
|
||||
final class UserRolesEnum extends BaseEnumRoutable
|
||||
{
|
||||
/**
|
||||
* enum values
|
||||
* @return array
|
||||
*/
|
||||
protected static function values(): array
|
||||
{
|
||||
return [
|
||||
'admin' => 1,
|
||||
'client' => 2,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* enum labels
|
||||
* @return array
|
||||
*/
|
||||
protected static function labels(): array
|
||||
{
|
||||
return [
|
||||
'admin' => 'Administrateur',
|
||||
'client' => 'Client',
|
||||
];
|
||||
}
|
||||
}
|
||||
27
app/Http/Controllers/API/AppController.php
Normal file
27
app/Http/Controllers/API/AppController.php
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\ApiResponseResource;
|
||||
use App\Models\Directory;
|
||||
use App\Models\Setting;
|
||||
|
||||
class AppController extends Controller
|
||||
{
|
||||
/**
|
||||
* Get App Config.
|
||||
* @return ApiResponseResource
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
$response = [];
|
||||
$settings = Setting::where('key', 'Button1')->orWhere('key', 'Button2')->orWhere('key', 'Button3')->get();
|
||||
|
||||
foreach ($settings as $setting) {
|
||||
$response['folders'][$setting->key] = Directory::find($setting->value)->only('id', 'name');
|
||||
}
|
||||
|
||||
return new ApiResponseResource("OK", $response);
|
||||
}
|
||||
}
|
||||
31
app/Http/Controllers/API/SettingsController.php
Normal file
31
app/Http/Controllers/API/SettingsController.php
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Enums\SettingTypesEnum;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Setting;
|
||||
use App\Http\Resources\ApiResponseResource;
|
||||
|
||||
class SettingsController extends Controller
|
||||
{
|
||||
/**
|
||||
* Get Settings.
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
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)
|
||||
? \intval($setting->value)
|
||||
: $setting->value;
|
||||
}
|
||||
return new ApiResponseResource("OK", $response);
|
||||
}
|
||||
}
|
||||
246
app/Http/Controllers/API/SyncController.php
Normal file
246
app/Http/Controllers/API/SyncController.php
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Mail\SendFlyer;
|
||||
use App\Mail\SendFlyerFailed;
|
||||
use App\Models\AppUser;
|
||||
use App\Models\Contact;
|
||||
use App\Models\Directory;
|
||||
use App\Models\Document;
|
||||
use App\Models\Setting;
|
||||
use Google\Cloud\Vision\V1\ImageAnnotatorClient;
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
|
||||
class SyncController extends Controller
|
||||
{
|
||||
/**
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \App\Http\Resources\AppUserCollection
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
|
||||
*/
|
||||
public function getUsers(Request $request)
|
||||
{
|
||||
return new \App\Http\Resources\AppUserCollection(AppUser::where('status', 1)->get());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \App\Http\Resources\ContactsCollection
|
||||
*/
|
||||
public function getContacts(Request $request)
|
||||
{
|
||||
$userId = $request->userId;
|
||||
$this->syncContacts($request->contacts, $userId);
|
||||
return new \App\Http\Resources\ContactsCollection(Contact::where('app_user_id', $userId)->get());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \App\Http\Resources\DirectoriesCollection
|
||||
*/
|
||||
public function getDirectories(Request $request)
|
||||
{
|
||||
return new \App\Http\Resources\DirectoriesCollection(Directory::all());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \App\Http\Resources\DocumentsCollection
|
||||
*/
|
||||
public function getDocuments(Request $request)
|
||||
{
|
||||
return new \App\Http\Resources\DocumentsCollection(Document::where('status', 1)->get());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function getVersion(Request $request)
|
||||
{
|
||||
$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 \Illuminate\Http\Request $request
|
||||
* @return string
|
||||
*/
|
||||
public function sendFlyer(Request $request)
|
||||
{
|
||||
$email = $request->emails['email'];
|
||||
$subject = $request->emails['subject'];
|
||||
$message = $request->emails['message'];
|
||||
$app_user_id = $request->emails['app_user_id'];
|
||||
$docs = $request->emails['documents'];
|
||||
|
||||
$user = AppUser::find($app_user_id);
|
||||
|
||||
$attach = [];
|
||||
$filesName = [];
|
||||
$noExists = [];
|
||||
foreach ($docs as $doc) {
|
||||
$exists = Storage::disk('public')->exists('docs/' . $doc['uuid'] . '.' . $doc['type']);
|
||||
if ($exists) {
|
||||
array_push($attach, URL::temporarySignedRoute(
|
||||
'bo.docs.download',
|
||||
now()->addDays(30),
|
||||
['uuid' => $doc['uuid']]
|
||||
));
|
||||
array_push($filesName, $doc['title']);
|
||||
} else {
|
||||
array_push($noExists, $doc['title']);
|
||||
}
|
||||
}
|
||||
if (count($noExists) === 0) {
|
||||
$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)
|
||||
->queue($message);
|
||||
} else {
|
||||
$message = new SendFlyerFailed([
|
||||
'email' => $email,
|
||||
'message' => $message,
|
||||
'subject' => $subject,
|
||||
'filesName' => $filesName,
|
||||
'noExists' => $noExists
|
||||
]);
|
||||
|
||||
Mail::to($user->email)
|
||||
->queue($message);
|
||||
} //end if
|
||||
return 'OK';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $contacts
|
||||
* @param mixed $userId
|
||||
* @return void
|
||||
*/
|
||||
private function syncContacts(mixed $contacts, mixed $userId)
|
||||
{
|
||||
$contacts = json_decode(json_encode($contacts), false);
|
||||
foreach ($contacts as $contact) {
|
||||
if ($contact->to_delete === true) {
|
||||
$tmp = Contact::where([
|
||||
['id', '=', $contact->uniq_id],
|
||||
['app_user_id', '=', $contact->app_user_id]
|
||||
])->first();
|
||||
if ($tmp) {
|
||||
$tmp->delete();
|
||||
}
|
||||
} else {
|
||||
if ($contact->app_user_id != $userId) {
|
||||
$tmp = Contact::where([
|
||||
['id', '=', $contact->uniq_id],
|
||||
['app_user_id', '=', $userId]
|
||||
])->first();
|
||||
if ($tmp) {
|
||||
$tmp->delete();
|
||||
}
|
||||
}
|
||||
$tmp = Contact::where([
|
||||
['id', '=', $contact->uniq_id],
|
||||
['app_user_id', '=', $contact->app_user_id]
|
||||
])->first();
|
||||
if ($tmp) {
|
||||
$tmp->email = $contact->email;
|
||||
$tmp->type = $contact->type;
|
||||
$tmp->other = $contact->other;
|
||||
$tmp->select1 = $contact->select1;
|
||||
$tmp->select2 = $contact->select2;
|
||||
$tmp->lastname = $contact->lastname;
|
||||
$tmp->firstname = $contact->firstname;
|
||||
$tmp->company = $contact->company;
|
||||
$tmp->address = $contact->address;
|
||||
$tmp->phone = empty($contact->phone) ? "" : $contact->phone;
|
||||
$tmp->products = $contact->products;
|
||||
$tmp->notes = $contact->notes;
|
||||
$tmp->visits = $contact->visits;
|
||||
$tmp->app_user_id = $contact->app_user_id;
|
||||
$tmp->save();
|
||||
} else {
|
||||
$tmp = new Contact();
|
||||
$tmp->id = $contact->uniq_id;
|
||||
$tmp->email = $contact->email;
|
||||
$tmp->type = $contact->type;
|
||||
$tmp->other = $contact->other;
|
||||
$tmp->select1 = $contact->select1;
|
||||
$tmp->select2 = $contact->select2;
|
||||
$tmp->lastname = $contact->lastname;
|
||||
$tmp->firstname = $contact->firstname;
|
||||
$tmp->company = $contact->company;
|
||||
$tmp->address = $contact->address;
|
||||
$tmp->phone = empty($contact->phone) ? "" : $contact->phone;
|
||||
$tmp->products = json_encode($contact->products);
|
||||
$tmp->notes = $contact->notes;
|
||||
$tmp->visits = $contact->visits;
|
||||
$tmp->app_user_id = $contact->app_user_id;
|
||||
$tmp->save();
|
||||
} //end if
|
||||
} //end if
|
||||
} //end foreach
|
||||
}
|
||||
|
||||
/**
|
||||
* @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>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,13 +5,20 @@ namespace App\Http\Controllers\Bo;
|
|||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Bo\AppUser\StoreAppUserRequest;
|
||||
use App\Http\Requests\Bo\AppUser\UpdateAppUserRequest;
|
||||
use App\Http\Requests\Bo\ImportRequest;
|
||||
use App\Imports\AppUserImport;
|
||||
use App\Models\AppUser;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
|
|
@ -49,7 +56,10 @@ class AppUserController extends Controller
|
|||
*/
|
||||
public function create()
|
||||
{
|
||||
return view('bo.pages.app-users.create');
|
||||
$directories = Directory::renderDirectories(Directory::where('needAuthorization', 1)
|
||||
->orderBy('name', 'ASC')->get());
|
||||
|
||||
return view('bo.pages.app-users.create', compact('directories'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -63,11 +73,9 @@ class AppUserController extends Controller
|
|||
return DB::transaction(function () use ($request) {
|
||||
$app_user = new AppUser();
|
||||
$app_user->fill($request->validated());
|
||||
if (!isset($request->validated()['status'])) {
|
||||
$app_user->status = 0;
|
||||
}
|
||||
|
||||
if ($app_user->saveOrFail()) {
|
||||
$app_user->directories()->sync(collect($request->directories)->pluck('id'));
|
||||
return redirect()
|
||||
->route('bo.app-users.edit', $app_user)
|
||||
->with('success', trans(
|
||||
|
|
@ -93,8 +101,13 @@ class AppUserController extends Controller
|
|||
*/
|
||||
public function edit(AppUser $app_user)
|
||||
{
|
||||
$directories = Directory::renderDirectories(Directory::where('needAuthorization', 1)
|
||||
->orderBy('name', 'ASC')->get());
|
||||
$app_user->directories = Directory::renderDirectories($app_user->directories);
|
||||
|
||||
return view('bo.pages.app-users.edit', [
|
||||
'app_userModel' => $app_user,
|
||||
'directories' => $directories
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
@ -107,15 +120,14 @@ class AppUserController extends Controller
|
|||
*/
|
||||
public function update(UpdateAppUserRequest $request, AppUser $app_user)
|
||||
{
|
||||
$app_user->directories()->sync(collect($request->directories)->pluck('id'));
|
||||
$params = $request->validated();
|
||||
if (\is_null($params['password'])) {
|
||||
unset($params['password']);
|
||||
}
|
||||
return DB::transaction(function () use ($params, $app_user) {
|
||||
$app_user->fill($params);
|
||||
if (!isset($params['status'])) {
|
||||
$app_user->status = !$app_user->status;
|
||||
}
|
||||
|
||||
if ($app_user->saveOrFail()) {
|
||||
return redirect()
|
||||
->route('bo.app-users.edit', $app_user)
|
||||
|
|
@ -153,8 +165,23 @@ class AppUserController extends Controller
|
|||
return redirect()
|
||||
->back()
|
||||
->with('error', trans(
|
||||
':model n\'a pas été supprimée',
|
||||
':model n\'a pas été supprimé',
|
||||
['model' => Str::singular(trans('models.classes.app_users'))]
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Mass import by CSV.
|
||||
* matricule;lastname;firstname;email
|
||||
* default role = user
|
||||
*
|
||||
* @param \App\Http\Requests\Bo\ImportRequest $request
|
||||
* @return \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')->with('success', 'Import terminé.');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,11 +3,15 @@
|
|||
namespace App\Http\Controllers\Bo;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Contact;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Parsedown;
|
||||
use Illuminate\Support\Facades\File;
|
||||
|
||||
class DashboardController extends Controller
|
||||
{
|
||||
/**
|
||||
* Dashboard Page
|
||||
* Show the application dashboard.
|
||||
*
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
|
|
@ -15,4 +19,48 @@ class DashboardController extends Controller
|
|||
{
|
||||
return view('bo.pages.dashboard');
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupérer les contacts (fichier .csv) et les fichiers.
|
||||
*
|
||||
* @return \Illuminate\Http\Response|\Illuminate\Contracts\Routing\ResponseFactory
|
||||
*/
|
||||
public function exportContact()
|
||||
{
|
||||
$csv_file = 'export-contacts.csv';
|
||||
$csvExporter = new \Laracsv\Export();
|
||||
$contacts = Contact::all();
|
||||
|
||||
$csvExporter->beforeEach(function ($contact) {
|
||||
$contact->app_user_id = $contact->firstname . " " . $contact->lastname;
|
||||
$contact->phone = "$contact->phone";
|
||||
});
|
||||
|
||||
$csvExporter->getWriter()->setDelimiter(';');
|
||||
$csvExporter->getWriter()->setEnclosure('"');
|
||||
$csvExporter->getWriter()->setOutputBOM(\League\Csv\Reader::BOM_UTF8);
|
||||
$csvExporter->build($contacts, [
|
||||
'app_user_id' => 'Propriétaire',
|
||||
'email' => 'Email',
|
||||
'type' => 'Type du contact',
|
||||
'other' => 'Type autre',
|
||||
'select1' => 'Select n°1',
|
||||
'select2' => 'Select n°2',
|
||||
'name' => 'Nom',
|
||||
'firstname' => 'Prénom',
|
||||
'company' => 'Entreprise',
|
||||
'address' => 'Adresse',
|
||||
'phone' => 'Téléphone',
|
||||
'notes' => 'Notes',
|
||||
'visits' => 'Visites',
|
||||
]);
|
||||
|
||||
Storage::disk('public')->put($csv_file, $csvExporter->getWriter()->getContent());
|
||||
|
||||
return response()->download(
|
||||
storage_path('app/public/') . "$csv_file",
|
||||
'contacts.csv',
|
||||
['Content-Type' => 'text/csv; charset=utf-8']
|
||||
)->deleteFileAfterSend();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ 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\Setting;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
|
|
@ -27,7 +28,7 @@ class DirectoriesController extends Controller
|
|||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param \App\Http\Requests\Bo\Directory\StoreDirectoryRequest $request
|
||||
* @param \App\Http\Requests\Bo\Directory\StoreDirectoryRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(StoreDirectoryRequest $request)
|
||||
|
|
@ -35,7 +36,6 @@ class DirectoriesController extends Controller
|
|||
return DB::transaction(function () use ($request) {
|
||||
$directory = new Directory();
|
||||
$directory->fill($request->validated());
|
||||
|
||||
if ($directory->saveOrFail()) {
|
||||
return redirect()
|
||||
->route('bo.directories.index')
|
||||
|
|
@ -44,7 +44,6 @@ class DirectoriesController extends Controller
|
|||
['model' => Str::singular(trans('models.classes.directories'))]
|
||||
));
|
||||
}
|
||||
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', trans(
|
||||
|
|
@ -102,26 +101,28 @@ class DirectoriesController extends Controller
|
|||
*/
|
||||
public function destroy(Directory $directory)
|
||||
{
|
||||
return DB::transaction(function () use ($directory) {
|
||||
foreach ($directory->children as $directoryChild) {
|
||||
$directoryChild->parent_id = null;
|
||||
$directoryChild->saveOrFail();
|
||||
foreach ($directory->children as $directoryChild) {
|
||||
$directoryChild->directory_id = null;
|
||||
$directoryChild->saveOrFail();
|
||||
}
|
||||
$buttons = Setting::where('key', 'Button1')->orWhere('key', 'Button2')->orWhere('key', 'Button3')->get();
|
||||
foreach ($buttons as $button) {
|
||||
if (isset($button->value) && intval($button->value) === $directory->id) {
|
||||
return [
|
||||
$button->id => trans(
|
||||
':model :name n\'a pas été supprimé,
|
||||
un bouton est encore associé à ce dernier dans les paramètres :',
|
||||
[
|
||||
'model' => Str::singular(trans('models.classes.directories')),
|
||||
'name' => $directory->name['fr']
|
||||
]
|
||||
)
|
||||
];
|
||||
}
|
||||
if ($directory->deleteOrFail()) {
|
||||
return redirect()
|
||||
->route('bo.directories.index')
|
||||
->with('warning', trans(
|
||||
':model a bien été supprimé',
|
||||
['model' => Str::singular(trans('models.classes.directories'))]
|
||||
));
|
||||
}
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', trans(
|
||||
':model n\'a pas été supprimé',
|
||||
['model' => Str::singular(trans('models.classes.directories'))]
|
||||
));
|
||||
});
|
||||
}
|
||||
if ($directory->deleteOrFail()) {
|
||||
return Directory::orderBy('order', 'ASC')->get();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -136,18 +137,18 @@ class DirectoriesController extends Controller
|
|||
}
|
||||
|
||||
/**
|
||||
* Assign the new order and parent id to each directory.
|
||||
* Assign the new order and directory id to each directory.
|
||||
*
|
||||
* @param array $directories
|
||||
* @param mixed $parentId
|
||||
* @param array $directories
|
||||
* @param integer $directoryId
|
||||
* @return void
|
||||
*/
|
||||
public function assignOrder(array $directories, $parentId = null)
|
||||
public function assignOrder(array $directories, int $directoryId = null)
|
||||
{
|
||||
foreach ($directories as $newDirectory) {
|
||||
$directory = Directory::where('id', $newDirectory['id'])->first();
|
||||
$directory['order'] = $newDirectory['order'];
|
||||
$directory['parent_id'] = $parentId;
|
||||
$directory = Directory::where('id', $newDirectory['id'])->first();
|
||||
$directory['order'] = $newDirectory['order'];
|
||||
$directory['directory_id'] = $directoryId;
|
||||
$directory->saveOrFail();
|
||||
if (isset($newDirectory['children']) && count($newDirectory['children']) > 0) {
|
||||
$this->assignOrder($newDirectory['children'], $newDirectory['id']);
|
||||
|
|
@ -167,18 +168,7 @@ class DirectoriesController extends Controller
|
|||
$directory->needAuthorization = $directory->needAuthorization ? false : true;
|
||||
|
||||
if ($directory->saveOrFail()) {
|
||||
return redirect()
|
||||
->route('bo.directories.index')
|
||||
->with('success', trans(
|
||||
':model a été mis à jour',
|
||||
['model' => Str::singular(trans('models.classes.directories'))]
|
||||
));
|
||||
return $directory;
|
||||
}
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', trans(
|
||||
':model n\'a été mis à jour',
|
||||
['model' => Str::singular(trans('models.classes.directories'))]
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
262
app/Http/Controllers/Bo/DocumentController.php
Normal file
262
app/Http/Controllers/Bo/DocumentController.php
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Bo;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Bo\Documents\StoreDocumentRequest;
|
||||
use App\Http\Requests\Bo\Documents\UpdateDocumentRequest;
|
||||
use App\Models\Directory;
|
||||
use App\Models\Document;
|
||||
use App\Models\Tag;
|
||||
use App\Traits\Controllers\ChangesModelOrder;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use Pion\Laravel\ChunkUpload\Exceptions\UploadMissingFileException;
|
||||
use Pion\Laravel\ChunkUpload\Handler\HandlerFactory;
|
||||
use Pion\Laravel\ChunkUpload\Receiver\FileReceiver;
|
||||
|
||||
class DocumentController extends Controller
|
||||
{
|
||||
use ChangesModelOrder;
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
/** @var \Illuminate\Database\Eloquent\Builder $documents */
|
||||
$documents = Document::query();
|
||||
$tags = Tag::get();
|
||||
|
||||
if ($request->search) {
|
||||
$this->searchQuery(
|
||||
$documents,
|
||||
$request->search,
|
||||
null,
|
||||
'title',
|
||||
);
|
||||
}
|
||||
|
||||
$documents->when($request->tag, function ($query) use ($request) {
|
||||
$query->whereHas('tags', function (Builder $query) use ($request) {
|
||||
$query->where('id', $request->tag);
|
||||
});
|
||||
});
|
||||
|
||||
$this->sortQuery($documents);
|
||||
$searchFields = trans('Titre');
|
||||
|
||||
$documents = $documents->paginate(10);
|
||||
|
||||
return view('bo.pages.documents.index', [
|
||||
'documents' => $documents,
|
||||
'searchFields' => $searchFields,
|
||||
'searchTag' => $request->tag,
|
||||
'tags' => $tags
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$documents = Document::where('lang', \App\Enums\Documents\LangEnum::fr())->get();
|
||||
$directories = Directory::getChildrenDirectories();
|
||||
$tags = Tag::get();
|
||||
|
||||
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\Response
|
||||
*/
|
||||
public function store(StoreDocumentRequest $request)
|
||||
{
|
||||
return DB::transaction(function () use ($request) {
|
||||
$document = new Document();
|
||||
$document->fill($request->validated());
|
||||
if ($document->saveOrFail()) {
|
||||
$document->tags()->sync(collect($request->tags)->pluck('id'));
|
||||
return redirect()
|
||||
->route('bo.documents.edit', $document)
|
||||
->with('success', trans(
|
||||
':model a été créé',
|
||||
['model' => Str::singular(trans('models.classes.documents'))]
|
||||
));
|
||||
}
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', trans(
|
||||
':model n\'a pas été créé',
|
||||
['model' => Str::singular(trans('models.classes.documents'))]
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*
|
||||
* @param \App\Models\Document $document
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function edit(Document $document)
|
||||
{
|
||||
$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,
|
||||
'directories' => $directories,
|
||||
'tags' => $tags
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param \App\Http\Requests\Bo\Document\UpdateDocumentRequest $request
|
||||
* @param \App\Models\Document $document
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
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', $document)
|
||||
->with('success', trans(
|
||||
':model a été mis à jour',
|
||||
['model' => Str::singular(trans('models.classes.documents'))]
|
||||
));
|
||||
}
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', trans(
|
||||
':model n\'a pas été mis à jour',
|
||||
['model' => Str::singular(trans('models.classes.documents'))]
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param \App\Models\Document $document
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function destroy(Document $document)
|
||||
{
|
||||
foreach ($document->children as $documentChild) {
|
||||
Document::removeDocument($documentChild);
|
||||
$documentChild->deleteOrFail();
|
||||
}
|
||||
if ($document->deleteOrFail()) {
|
||||
return redirect()
|
||||
->route('bo.documents.index')
|
||||
->with('warning', trans(
|
||||
':model a bien été supprimé',
|
||||
['model' => Str::singular(trans('models.classes.documents'))]
|
||||
));
|
||||
}
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', trans(
|
||||
':model n\'a pas été supprimé',
|
||||
['model' => Str::singular(trans('models.classes.documents'))]
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Download the document
|
||||
*
|
||||
* @param mixed $uuid
|
||||
* @return \Symfony\Component\HttpFoundation\BinaryFileResponse
|
||||
*/
|
||||
public function download(mixed $uuid)
|
||||
{
|
||||
$doc = Document::where('uuid', $uuid)->firstOrFail();
|
||||
$pathToFile = public_path() . Storage::url('docs/' . $doc->uuid . '.' . $doc->type);
|
||||
return response()->download($pathToFile, $doc->path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
* @throws \Pion\Laravel\ChunkUpload\Exceptions\UploadMissingFileException Bla.
|
||||
*/
|
||||
public function upload(Request $request)
|
||||
{
|
||||
if (isset($request->uuid)) {
|
||||
$uuid = $request->uuid;
|
||||
} else {
|
||||
$uuid = false;
|
||||
}
|
||||
// Create the file receiver.
|
||||
$receiver = new FileReceiver("file", $request, HandlerFactory::classFromRequest($request));
|
||||
// Check if the upload is success, throw exception or return response you need.
|
||||
if ($receiver->isUploaded() === false) {
|
||||
throw new UploadMissingFileException();
|
||||
}
|
||||
// Receive the file.
|
||||
$save = $receiver->receive();
|
||||
// Check if the upload has finished (in chunk mode it will send smaller files).
|
||||
if ($save->isFinished()) {
|
||||
// Save the file and return any response you need, current example uses `move` function.
|
||||
// If you are not using move, you need to manually delete the file: unlink($save->getFile()->getPathname()).
|
||||
return $this->saveFile($save->getFile(), $uuid);
|
||||
}
|
||||
// We are in chunk mode, lets send the current progress.
|
||||
/** @var AbstractHandler $handler */
|
||||
$handler = $save->handler();
|
||||
return response()->json([
|
||||
"done" => $handler->getPercentageDone(),
|
||||
'status' => true
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Illuminate\Http\UploadedFile $file
|
||||
* @param mixed $uuid
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
* @throws \Illuminate\Contracts\Container\BindingResolutionException Bla.
|
||||
*/
|
||||
protected function saveFile(UploadedFile $file, mixed $uuid)
|
||||
{
|
||||
if ($uuid === false) {
|
||||
$uuid = sprintf(
|
||||
'%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
|
||||
mt_rand(0, 0xffff),
|
||||
mt_rand(0, 0xffff),
|
||||
mt_rand(0, 0xffff),
|
||||
mt_rand(0, 0x0fff) | 0x4000,
|
||||
mt_rand(0, 0x3fff) | 0x8000,
|
||||
mt_rand(0, 0xffff),
|
||||
mt_rand(0, 0xffff),
|
||||
mt_rand(0, 0xffff)
|
||||
);
|
||||
}
|
||||
$finalPath = storage_path('app/public/docs/');
|
||||
// Move the file name.
|
||||
$file->move($finalPath, $uuid . '.' . $file->getClientOriginalExtension());
|
||||
return response()->json([
|
||||
'uid' => $uuid,
|
||||
]);
|
||||
}
|
||||
}
|
||||
96
app/Http/Controllers/Bo/SettingController.php
Normal file
96
app/Http/Controllers/Bo/SettingController.php
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Bo;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Bo\Settings\UpdateSettingRequest;
|
||||
use App\Models\Directory;
|
||||
use App\Models\Setting;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class SettingController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$directories = Directory::renderDirectories(Directory::whereNull('directory_id')->get());
|
||||
|
||||
/** @var \Illuminate\Database\Eloquent\Builder $settings */
|
||||
$settings = Setting::query();
|
||||
|
||||
$this->sortQuery($settings);
|
||||
$settings = $settings->get();
|
||||
|
||||
return view('bo.pages.settings.index', compact('settings', 'directories'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*
|
||||
* @param \App\Models\Setting $setting
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function edit(Setting $setting)
|
||||
{
|
||||
$directories = Directory::renderDirectories(Directory::whereNull('directory_id')->get());
|
||||
|
||||
return view('bo.pages.settings.edit', ['settingModel' => $setting, 'directories' => $directories]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param \App\Http\Requests\Bo\Setting\UpdateSettingRequest $request
|
||||
* @param \App\Models\Setting $setting
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
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', $setting)
|
||||
->with('success', trans(
|
||||
':model a été mis à jour',
|
||||
['model' => Str::singular(trans('models.classes.settings'))]
|
||||
));
|
||||
}
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', trans(
|
||||
':model n\'a pas été mis à jour',
|
||||
['model' => Str::singular(trans('models.classes.settings'))]
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param \App\Models\Setting $setting
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function destroy(Setting $setting)
|
||||
{
|
||||
if ($setting->deleteOrFail()) {
|
||||
return redirect()
|
||||
->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'))]
|
||||
));
|
||||
}
|
||||
}
|
||||
147
app/Http/Controllers/Bo/TagController.php
Normal file
147
app/Http/Controllers/Bo/TagController.php
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Bo;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Bo\Tags\StoreTagRequest;
|
||||
use App\Http\Requests\Bo\Tags\UpdateTagRequest;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
/** @var \Illuminate\Database\Eloquent\Builder $tags */
|
||||
$tags = Tag::query();
|
||||
|
||||
if ($request->search) {
|
||||
$this->searchQuery(
|
||||
$tags,
|
||||
$request->search,
|
||||
null,
|
||||
'name',
|
||||
);
|
||||
}
|
||||
|
||||
$this->sortQuery($tags);
|
||||
$searchFields = trans('Nom');
|
||||
|
||||
$tags = $tags->paginate(10);
|
||||
|
||||
return view('bo.pages.tags.index', compact('tags', 'searchFields'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
return view('bo.pages.tags.create');
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param \App\Http\Requests\Bo\Tag\StoreTagRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
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', $tag)
|
||||
->with('success', trans(
|
||||
':model a été créé',
|
||||
['model' => Str::singular(trans('models.classes.tags'))]
|
||||
));
|
||||
}
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', trans(
|
||||
':model n\'a pas été créé',
|
||||
['model' => Str::singular(trans('models.classes.tags'))]
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*
|
||||
* @param \App\Models\Tag $tag
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function edit(Tag $tag)
|
||||
{
|
||||
return view('bo.pages.tags.edit', ['tagModel' => $tag]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param \App\Http\Requests\Bo\Tag\UpdateTagRequest $request
|
||||
* @param \App\Models\Tag $tag
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
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', $tag)
|
||||
->with('success', trans(
|
||||
':model a été mis à jour',
|
||||
['model' => Str::singular(trans('models.classes.tags'))]
|
||||
));
|
||||
}
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', trans(
|
||||
':model n\'a pas été mis à jour',
|
||||
['model' => Str::singular(trans('models.classes.tags'))]
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param \App\Models\Tag $tag
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function destroy(Tag $tag)
|
||||
{
|
||||
if ($tag->deleteOrFail()) {
|
||||
return redirect()
|
||||
->route('bo.tags.index')
|
||||
->with('warning', trans(
|
||||
':model a bien été supprimé',
|
||||
['model' => Str::singular(trans('models.classes.tags'))]
|
||||
));
|
||||
}
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', trans(
|
||||
':model n\'a pas été supprimé',
|
||||
['model' => Str::singular(trans('models.classes.tags'))]
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
@ -64,5 +64,6 @@ class Kernel extends HttpKernel
|
|||
'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,
|
||||
];
|
||||
}
|
||||
|
|
|
|||
24
app/Http/Middleware/CheckApiToken.php
Normal file
24
app/Http/Middleware/CheckApiToken.php
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class CheckApiToken
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
if (isset($request->token) && $request->token == config('auth.API_TOKEN')) {
|
||||
return $next($request);
|
||||
}
|
||||
return response()->json(['message' => 'Nope'], 401);
|
||||
}
|
||||
}
|
||||
|
|
@ -18,6 +18,19 @@ class StoreAppUserRequest extends FormRequest
|
|||
return auth('backend')->check();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the data for validation.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function prepareForValidation()
|
||||
{
|
||||
$this->merge([
|
||||
'status' => $this->status ? true : false,
|
||||
'authorizedFolders' => $this->authorizedFolders ? $this->authorizedFolders : [],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
|
|
@ -29,13 +42,13 @@ class StoreAppUserRequest extends FormRequest
|
|||
'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' => 'min:8',
|
||||
'authorizedFolders' => 'json|required',
|
||||
'password' => 'required|nullable|required_with:password_confirmation|confirmed|min:8|max:255',
|
||||
'authorizedFolders' => 'present|array',
|
||||
'role' => [
|
||||
'required',
|
||||
new EnumIsValidRule(AppUserRolesEnum::class)
|
||||
],
|
||||
'status' => 'sometimes'
|
||||
'status' => 'required|boolean'
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,8 +25,7 @@ class UpdateAppUserRequest extends StoreAppUserRequest
|
|||
'email:rfc,dns',
|
||||
Rule::unique('app_users', 'email')->ignore($this->route('app_user')->id),
|
||||
],
|
||||
'password' => 'nullable|min:8',
|
||||
'authorizedFolders' => 'json|required',
|
||||
'password' => 'sometimes|nullable|required_with:password_confirmation|confirmed|min:8|max:255',
|
||||
'role' => [
|
||||
'required',
|
||||
new EnumIsValidRule(AppUserRolesEnum::class)
|
||||
|
|
|
|||
73
app/Http/Requests/Bo/Documents/StoreDocumentRequest.php
Normal file
73
app/Http/Requests/Bo/Documents/StoreDocumentRequest.php
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Bo\Documents;
|
||||
|
||||
use App\Enums\Documents\LangEnum;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Kwaadpepper\Enum\Rules\EnumIsValidRule;
|
||||
|
||||
class StoreDocumentRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return auth('backend')->check();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the data for validation.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function prepareForValidation()
|
||||
{
|
||||
$this->merge([
|
||||
'status' => $this->status ? true : false
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'directory_id' => 'sometimes|nullable|integer',
|
||||
'document_id' => 'sometimes|nullable|integer',
|
||||
'uuid' => 'required|min:3|max:255',
|
||||
'title' => 'required|min:3|max:255',
|
||||
'type' => 'required|min:3|max:255',
|
||||
'path' => 'required|min:3|max:255',
|
||||
'lang' => [
|
||||
'required',
|
||||
new EnumIsValidRule(LangEnum::class)
|
||||
],
|
||||
'status' => 'required|boolean'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get custom attributes for validator errors.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function attributes()
|
||||
{
|
||||
return [
|
||||
'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'),
|
||||
];
|
||||
}
|
||||
}
|
||||
7
app/Http/Requests/Bo/Documents/UpdateDocumentRequest.php
Normal file
7
app/Http/Requests/Bo/Documents/UpdateDocumentRequest.php
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Bo\Documents;
|
||||
|
||||
class UpdateDocumentRequest extends StoreDocumentRequest
|
||||
{
|
||||
}
|
||||
45
app/Http/Requests/Bo/ImportRequest.php
Normal file
45
app/Http/Requests/Bo/ImportRequest.php
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Bo;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class ImportRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return auth('backend')->check();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'import' => [
|
||||
'required',
|
||||
'file',
|
||||
'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.FoundInExtendedClassBeforeLastUsed
|
||||
function ($attribute, $value, $fail) {
|
||||
if (
|
||||
$this->import->getClientMimeType() !== 'text/csv'
|
||||
&& $this->import->getClientMimeType() !== 'application/vnd.ms-excel'
|
||||
) {
|
||||
$fail(__('Le fichier doit être d\'un mime type text/csv ou application/vnd.ms-excel'));
|
||||
}
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
118
app/Http/Requests/Bo/Settings/UpdateSettingRequest.php
Normal file
118
app/Http/Requests/Bo/Settings/UpdateSettingRequest.php
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
<?php
|
||||
|
||||
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;
|
||||
|
||||
class UpdateSettingRequest extends FormRequest
|
||||
{
|
||||
use HasPicture;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
if (
|
||||
$this->key === "Button1" ||
|
||||
$this->key === "Button2" ||
|
||||
$this->key === "Button3" ||
|
||||
$this->key === "ShowIsActive"
|
||||
) {
|
||||
return auth('backend')->check();
|
||||
} else {
|
||||
return (auth('backend')->check() && Auth::user()->role === UserRolesEnum::admin()->value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the data for validation.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function prepareForValidation()
|
||||
{
|
||||
if ($this->key === "ShowIsActive") {
|
||||
$this->merge(['value' => intval($this->value) ? "true" : "false"]);
|
||||
} elseif (
|
||||
$this->key === "Button1" ||
|
||||
$this->key === "Button2" ||
|
||||
$this->key === "Button3"
|
||||
) {
|
||||
$this->merge(['value' => $this->value === "0" ? null : $this->value]);
|
||||
} elseif (
|
||||
$this->key === "SquaredLogo" ||
|
||||
$this->key === "RectangleLogo" ||
|
||||
$this->key === "BackgroundImage" ||
|
||||
$this->key === "HomeButtonImage"
|
||||
) {
|
||||
$this->mergePicture('value');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
if (
|
||||
$this->key === "Button1" ||
|
||||
$this->key === "Button2" ||
|
||||
$this->key === "Button3"
|
||||
) {
|
||||
$buttonsNullable = 0;
|
||||
$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) {
|
||||
$buttonsNullable++;
|
||||
}
|
||||
};
|
||||
}
|
||||
return [
|
||||
'value' => [
|
||||
//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.'));
|
||||
}
|
||||
}
|
||||
]
|
||||
];
|
||||
} elseif (
|
||||
$this->key === "SquaredLogo" ||
|
||||
$this->key === "RectangleLogo" ||
|
||||
$this->key === "BackgroundImage" ||
|
||||
$this->key === "HomeButtonImage"
|
||||
) {
|
||||
return $this->pictureRules('value', true, 100, 100, 1920, 1200);
|
||||
} else {
|
||||
return ['value' => 'required|string|min:1'];
|
||||
} //end if
|
||||
}
|
||||
|
||||
/**
|
||||
* Get custom attributes for validator errors.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function attributes()
|
||||
{
|
||||
return [
|
||||
'key' => \trans('clé'),
|
||||
'value' => \trans('valeur'),
|
||||
'type' => \trans('type'),
|
||||
];
|
||||
}
|
||||
}
|
||||
56
app/Http/Requests/Bo/Tags/StoreTagRequest.php
Normal file
56
app/Http/Requests/Bo/Tags/StoreTagRequest.php
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Bo\Tags;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class StoreTagRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return auth('backend')->check();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the data for validation.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function prepareForValidation()
|
||||
{
|
||||
$this->merge([
|
||||
'slug' => Str::slug($this->name),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'name' => 'required|string|min:2|max:25',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get custom attributes for validator errors.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function attributes(): array
|
||||
{
|
||||
return [
|
||||
'name' => trans('name of the tag'),
|
||||
'slug' => trans('slug of the tag'),
|
||||
];
|
||||
}
|
||||
}
|
||||
7
app/Http/Requests/Bo/Tags/UpdateTagRequest.php
Normal file
7
app/Http/Requests/Bo/Tags/UpdateTagRequest.php
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Bo\Tags;
|
||||
|
||||
class UpdateTagRequest extends StoreTagRequest
|
||||
{
|
||||
}
|
||||
46
app/Http/Resources/ApiResponseResource.php
Normal file
46
app/Http/Resources/ApiResponseResource.php
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class ApiResponseResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Status
|
||||
* @var string
|
||||
*/
|
||||
private $status;
|
||||
|
||||
/**
|
||||
* Datas
|
||||
* @var array
|
||||
*/
|
||||
private $datas;
|
||||
/**
|
||||
* Create a new resource instance.
|
||||
*
|
||||
* @param string $status
|
||||
* @param array $datas
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(string $status, array $datas)
|
||||
{
|
||||
$this->status = $status;
|
||||
$this->datas = $datas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
'status' => $this->status,
|
||||
'datas' => $this->datas
|
||||
];
|
||||
}
|
||||
}
|
||||
19
app/Http/Resources/AppUserCollection.php
Normal file
19
app/Http/Resources/AppUserCollection.php
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
|
||||
class AppUserCollection extends ResourceCollection
|
||||
{
|
||||
/**
|
||||
* Transform the resource collection into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return parent::toArray($request);
|
||||
}
|
||||
}
|
||||
19
app/Http/Resources/ContactsCollection.php
Normal file
19
app/Http/Resources/ContactsCollection.php
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
|
||||
class ContactsCollection extends ResourceCollection
|
||||
{
|
||||
/**
|
||||
* Transform the resource collection into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return parent::toArray($request);
|
||||
}
|
||||
}
|
||||
19
app/Http/Resources/DirectoriesCollection.php
Normal file
19
app/Http/Resources/DirectoriesCollection.php
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
|
||||
class DirectoriesCollection extends ResourceCollection
|
||||
{
|
||||
/**
|
||||
* Transform the resource collection into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return parent::toArray($request);
|
||||
}
|
||||
}
|
||||
19
app/Http/Resources/DocumentsCollection.php
Normal file
19
app/Http/Resources/DocumentsCollection.php
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
|
||||
class DocumentsCollection extends ResourceCollection
|
||||
{
|
||||
/**
|
||||
* Transform the resource collection into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return parent::toArray($request);
|
||||
}
|
||||
}
|
||||
66
app/Imports/AppUserImport.php
Normal file
66
app/Imports/AppUserImport.php
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
namespace App\Imports;
|
||||
|
||||
use App\Mail\AccountCreated;
|
||||
use App\Enums\AppUserRolesEnum;
|
||||
use App\Models\AppUser;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Str;
|
||||
use Maatwebsite\Excel\Concerns\ToModel;
|
||||
use Maatwebsite\Excel\Concerns\WithUpserts;
|
||||
use Maatwebsite\Excel\Concerns\WithCustomCsvSettings;
|
||||
|
||||
class AppUserImport implements ToModel, WithUpserts, WithCustomCsvSettings
|
||||
{
|
||||
/**
|
||||
* @return string|array
|
||||
*/
|
||||
public function uniqueBy()
|
||||
{
|
||||
return 'email';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getCsvSettings(): array
|
||||
{
|
||||
return [
|
||||
'delimiter' => ";"
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $row
|
||||
*
|
||||
* @return \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;
|
||||
}
|
||||
$user = new AppUser([
|
||||
'email' => $row[3],
|
||||
'lastname' => ucfirst(strtolower($row[1])),
|
||||
'firstname' => ucfirst(strtolower($row[2])),
|
||||
'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();
|
||||
|
||||
return $user;
|
||||
}
|
||||
}
|
||||
47
app/Mail/AccountCreated.php
Normal file
47
app/Mail/AccountCreated.php
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use App\Models\AppUser;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class AccountCreated extends Mailable
|
||||
{
|
||||
use Queueable;
|
||||
use SerializesModels;
|
||||
|
||||
/**
|
||||
* The app user instance
|
||||
*
|
||||
* @var \App\Models\AppUser;
|
||||
*/
|
||||
public $user;
|
||||
|
||||
/** @var string */
|
||||
public $password;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*
|
||||
* @param \App\Models\AppUser $user
|
||||
* @param string $password
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(AppUser $user, string $password)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->password = $password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the message.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
return $this->subject(__("Votre compte a été créé"))->view('emails.account-created');
|
||||
}
|
||||
}
|
||||
40
app/Mail/SendFlyer.php
Normal file
40
app/Mail/SendFlyer.php
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class SendFlyer extends Mailable
|
||||
{
|
||||
use Queueable;
|
||||
use SerializesModels;
|
||||
|
||||
/** @var object */
|
||||
public $data;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*
|
||||
* @param mixed $data
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(mixed $data)
|
||||
{
|
||||
$this->data = (object)$data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the message.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
return $this
|
||||
->from($this->data->from, $this->data->fromName)
|
||||
->subject($this->data->subject)
|
||||
->markdown('bo.mails.sendflyer');
|
||||
}
|
||||
}
|
||||
40
app/Mail/SendFlyerFailed.php
Normal file
40
app/Mail/SendFlyerFailed.php
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class SendFlyerFailed extends Mailable
|
||||
{
|
||||
use Queueable;
|
||||
use SerializesModels;
|
||||
|
||||
/** @var object */
|
||||
private $data;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*
|
||||
* @param mixed $data
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(mixed $data)
|
||||
{
|
||||
$this->data = (object)$data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the message.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
|
@ -3,24 +3,27 @@
|
|||
namespace App\Models;
|
||||
|
||||
use App\Enums\AppUserRolesEnum;
|
||||
use App\Lib\Helpers\ToolboxHelper;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Kwaadpepper\Enum\Traits\CastsEnums;
|
||||
|
||||
/**
|
||||
* @property integer $id
|
||||
* @property string $lastname
|
||||
* @property string $firstname
|
||||
* @property string $email
|
||||
* @property string $password
|
||||
* @property integer $role
|
||||
* @property boolean $status
|
||||
* @property integer $order
|
||||
*/
|
||||
class AppUser extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
use CastsEnums;
|
||||
|
||||
/**
|
||||
* cast enum
|
||||
* @var string[]
|
||||
*/
|
||||
protected $enumCasts = [
|
||||
'role' => AppUserRolesEnum::class
|
||||
];
|
||||
|
||||
/**
|
||||
* fillable
|
||||
* @var string[]
|
||||
|
|
@ -30,11 +33,21 @@ class AppUser extends Model
|
|||
'firstname',
|
||||
'email',
|
||||
'password',
|
||||
'authorizedFolders',
|
||||
'role',
|
||||
'status',
|
||||
'order',
|
||||
];
|
||||
|
||||
/**
|
||||
* cast enum
|
||||
* @var string[]
|
||||
*/
|
||||
protected $enumCasts = [
|
||||
'role' => AppUserRolesEnum::class
|
||||
];
|
||||
|
||||
// * EVENTS
|
||||
|
||||
/**
|
||||
* Perform any actions required after the model boots.
|
||||
*
|
||||
|
|
@ -43,16 +56,53 @@ class AppUser extends Model
|
|||
protected static function booted()
|
||||
{
|
||||
static::creating(function (self $model) {
|
||||
$model->lastname = strtoupper($model->lastname);
|
||||
$model->firstname = ucfirst(strtolower($model->firstname));
|
||||
$model->password = Hash::make($model->password);
|
||||
self::assertFieldIsUnique('email', $model->email);
|
||||
self::setOrder($model);
|
||||
$model->password = Hash::make($model->password);
|
||||
});
|
||||
static::updating(function (self $model) {
|
||||
$model->lastname = strtoupper($model->lastname);
|
||||
$model->firstname = ucfirst(strtolower($model->firstname));
|
||||
if (Hash::needsRehash($model->password)) {
|
||||
$model->password = Hash::make($model->password);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// * METHODS
|
||||
|
||||
/**
|
||||
* Asserts using validation that the field is unique.
|
||||
*
|
||||
* @param string $nameField
|
||||
* @param string $valueField
|
||||
* @param integer|null $id
|
||||
* @return void
|
||||
*/
|
||||
private static function assertFieldIsUnique(string $nameField, string $valueField, ?int $id = null)
|
||||
{
|
||||
$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
|
||||
|
||||
/**
|
||||
* MorphToMany Directories
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\MorphToMany
|
||||
*/
|
||||
public function directories()
|
||||
{
|
||||
return $this->morphToMany(Directory::class, 'directorable');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
63
app/Models/Contact.php
Normal file
63
app/Models/Contact.php
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Kwaadpepper\Enum\Traits\CastsEnums;
|
||||
|
||||
/**
|
||||
* @property integer $app_user_id
|
||||
* @property integer $id
|
||||
* @property string $email
|
||||
* @property string $type
|
||||
* @property string $other
|
||||
* @property string $select1
|
||||
* @property string $select2
|
||||
* @property string $name
|
||||
* @property string $firstname
|
||||
* @property string $company
|
||||
* @property string $address
|
||||
* @property string $phone
|
||||
* @property string $products
|
||||
* @property string $card_url
|
||||
* @property string $notes
|
||||
* @property string $visits
|
||||
*/
|
||||
class Contact extends Model
|
||||
{
|
||||
use CastsEnums;
|
||||
|
||||
/**
|
||||
* fillable
|
||||
* @var string[]
|
||||
*/
|
||||
protected $fillable = [
|
||||
'app_user_id',
|
||||
'email',
|
||||
'type',
|
||||
'other',
|
||||
'select1',
|
||||
'select2',
|
||||
'name',
|
||||
'firstname',
|
||||
'company',
|
||||
'address',
|
||||
'phone',
|
||||
'products',
|
||||
'card_url',
|
||||
'notes',
|
||||
'visits',
|
||||
];
|
||||
|
||||
// * RELATIONSHIPS
|
||||
|
||||
/**
|
||||
* MorphToMany Directories
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function appUser()
|
||||
{
|
||||
return $this->belongsTo(AppUser::class, 'id', 'app_user_id');
|
||||
}
|
||||
}
|
||||
|
|
@ -2,14 +2,13 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Lib\Helpers\ToolboxHelper;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @property integer $id
|
||||
* @property string $name
|
||||
* @property integer $parent_id
|
||||
* @property integer $directory_id
|
||||
* @property integer $order
|
||||
* @property boolean $needAuthorization
|
||||
*/
|
||||
|
|
@ -17,13 +16,6 @@ class Directory extends Model
|
|||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The accessors to append to the model's array form.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $appends = ['is_system'];
|
||||
|
||||
/**
|
||||
* Fillable;
|
||||
*
|
||||
|
|
@ -31,9 +23,10 @@ class Directory extends Model
|
|||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'parent_id',
|
||||
'directory_id',
|
||||
'order',
|
||||
'needAuthorization'
|
||||
'needAuthorization',
|
||||
'is_system'
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
@ -53,29 +46,19 @@ class Directory extends Model
|
|||
protected static function booted()
|
||||
{
|
||||
static::creating(function (self $model) {
|
||||
self::setIsSystem($model);
|
||||
self::setOrder($model);
|
||||
});
|
||||
static::deleting(function (self $tag) {
|
||||
$tag->appusers()->detach();
|
||||
static::deleting(function (self $model) {
|
||||
$model->appusers()->detach();
|
||||
foreach ($model->documents as $key => $document) {
|
||||
$document->delete();
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// * METHODS
|
||||
|
||||
/**
|
||||
* Asserts using validation that the field is unique.
|
||||
*
|
||||
* @param string $nameField
|
||||
* @param string $valueField
|
||||
* @param integer|null $id
|
||||
* @return void
|
||||
*/
|
||||
private static function assertFieldIsUnique(string $nameField, string $valueField, ?int $id = null)
|
||||
{
|
||||
$table = (new self())->getTable();
|
||||
ToolboxHelper::assertFieldIsUnique($table, $nameField, $valueField, $id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set order after the last element of the list.
|
||||
*
|
||||
|
|
@ -88,14 +71,64 @@ class Directory extends Model
|
|||
}
|
||||
|
||||
/**
|
||||
* Check if is directory system.
|
||||
* 1 = Salon
|
||||
* Set order after the last element of the list.
|
||||
*
|
||||
* @return boolean
|
||||
* @param \Illuminate\Database\Eloquent\Model $model
|
||||
* @return void
|
||||
*/
|
||||
public function getIsSystemAttribute()
|
||||
private static function setIsSystem(Model $model): void
|
||||
{
|
||||
return $this->id === 1;
|
||||
$model->is_system = ($model->is_system) ? $model->is_system : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render directories.
|
||||
*
|
||||
* @param mixed $directories
|
||||
* @return array
|
||||
*/
|
||||
public static function renderDirectories(mixed $directories)
|
||||
{
|
||||
foreach ($directories as $k => $directory) {
|
||||
$directories[$k]->name = Directory::renameDirectory($directory, "");
|
||||
}
|
||||
return $directories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename directory.
|
||||
*
|
||||
* @param Directory $model
|
||||
* @param string $nameDirectory
|
||||
* @return array
|
||||
*/
|
||||
private static function renameDirectory(Directory $model, string $nameDirectory)
|
||||
{
|
||||
if (strlen($nameDirectory)) {
|
||||
$nameDirectory = $model->name['fr'] . " / " . $nameDirectory;
|
||||
} else {
|
||||
$nameDirectory = $model->name['fr'];
|
||||
}
|
||||
if (isset($model->parent)) {
|
||||
$nameDirectory = $model->renameDirectory($model->parent, $nameDirectory);
|
||||
}
|
||||
return $nameDirectory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename directory.
|
||||
*
|
||||
* @return LaravelModelList
|
||||
*/
|
||||
public static function getChildrenDirectories()
|
||||
{
|
||||
$directories = [];
|
||||
foreach (Directory::orderBy('order', 'ASC')->get()->toArray() as $key => $value) {
|
||||
if (count(Directory::get()[$key]->children) === 0) {
|
||||
array_push($directories, Directory::get()[$key]);
|
||||
}
|
||||
}
|
||||
return Directory::renderDirectories($directories);
|
||||
}
|
||||
|
||||
// * RELATIONSHIPS
|
||||
|
|
@ -107,7 +140,7 @@ class Directory extends Model
|
|||
*/
|
||||
public function parent()
|
||||
{
|
||||
return $this->belongsTo('App\Models\Directory', 'parent_id');
|
||||
return $this->belongsTo('App\Models\Directory', 'directory_id');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -117,7 +150,7 @@ class Directory extends Model
|
|||
*/
|
||||
public function children()
|
||||
{
|
||||
return $this->hasMany('App\Models\Directory', 'parent_id');
|
||||
return $this->hasMany('App\Models\Directory', 'directory_id');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -127,6 +160,16 @@ class Directory extends Model
|
|||
*/
|
||||
public function appusers()
|
||||
{
|
||||
return $this->morphedByMany(AppUser::class, 'taggable');
|
||||
return $this->morphedByMany(AppUser::class, 'directorable');
|
||||
}
|
||||
|
||||
/**
|
||||
* Has Many Relationship Method.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function documents()
|
||||
{
|
||||
return $this->hasMany('App\Models\Document', 'directory_id');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
181
app/Models/Document.php
Normal file
181
app/Models/Document.php
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\TypeEnum;
|
||||
use App\Lib\Helpers\ToolboxHelper;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
/**
|
||||
* @property integer $id
|
||||
* @property integer $uuid
|
||||
* @property string $title
|
||||
* @property string $type
|
||||
* @property integer $directory_id
|
||||
* @property integer $document_id
|
||||
* @property string $path
|
||||
* @property string $lang
|
||||
* @property boolean $status
|
||||
* @property integer $order
|
||||
*/
|
||||
class Document extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* Fillable;
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $fillable = [
|
||||
'uuid',
|
||||
'title',
|
||||
'type',
|
||||
'directory_id',
|
||||
'document_id',
|
||||
'path',
|
||||
'lang',
|
||||
'status',
|
||||
'order',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'status' => 'bool'
|
||||
];
|
||||
|
||||
/**
|
||||
* Map attribute names to enum classes.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $enumCasts = [
|
||||
'lang' => LangEnum::class,
|
||||
'type' => TypeEnum::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Perform any actions required after the model boots.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function booted()
|
||||
{
|
||||
static::creating(function (self $model) {
|
||||
self::assertFieldIsUnique('title', $model->title);
|
||||
self::setOrder($model);
|
||||
});
|
||||
static::updating(function (self $model) {
|
||||
$path = \ltrim(\urldecode("docs/" . $model->getOriginal('uuid') . $model->getOriginal('type')), '/');
|
||||
if (Storage::disk('public')->exists($path)) {
|
||||
Storage::disk('public')->delete($path);
|
||||
}
|
||||
});
|
||||
static::deleting(function (self $model) {
|
||||
self::removeDocument($model);
|
||||
});
|
||||
}
|
||||
|
||||
// * METHODS
|
||||
|
||||
/**
|
||||
* Asserts using validation that the field is unique.
|
||||
*
|
||||
* @param string $nameField
|
||||
* @param string $valueField
|
||||
* @param integer|null $id
|
||||
* @return void
|
||||
*/
|
||||
private static function assertFieldIsUnique(string $nameField, string $valueField, ?int $id = null)
|
||||
{
|
||||
$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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set order after the last element of the list.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Model $model
|
||||
* @return void
|
||||
*/
|
||||
public static function removeDocument(Model $model): void
|
||||
{
|
||||
$path = \ltrim(\urldecode("storage/docs/" . $model->uuid . $model->type), '/');
|
||||
if (\strpos($path, 'storage') !== 0) {
|
||||
return;
|
||||
}
|
||||
$path = ToolboxHelper::mbReplace('storage/', '', $path);
|
||||
if (Storage::disk('public')->exists($path)) {
|
||||
Storage::disk('public')->delete($path);
|
||||
}
|
||||
}
|
||||
|
||||
// * RELATIONSHIPS
|
||||
|
||||
/**
|
||||
* One To Many Relationship Method.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function directory()
|
||||
{
|
||||
return $this->belongsTo('App\Models\Directory', 'directory_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* One To Many Relationship Method.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function parent()
|
||||
{
|
||||
return $this->belongsTo('App\Models\Document', 'document_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Has Many Relationship Method.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function children()
|
||||
{
|
||||
return $this->hasMany('App\Models\Document', 'document_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* MorphedByMany appusers relation
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\MorphedByMany
|
||||
*/
|
||||
public function appusers()
|
||||
{
|
||||
return $this->morphedByMany(AppUser::class, 'taggable');
|
||||
}
|
||||
|
||||
/**
|
||||
* MorphToMany tags relation.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\MorphToMany
|
||||
*/
|
||||
public function tags()
|
||||
{
|
||||
return $this->morphToMany(Tag::class, 'taggable');
|
||||
}
|
||||
}
|
||||
74
app/Models/Setting.php
Normal file
74
app/Models/Setting.php
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\SettingTypesEnum;
|
||||
use App\Lib\Helpers\FileStorageHelper;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Kwaadpepper\Enum\Traits\CastsEnums;
|
||||
|
||||
/**
|
||||
* @property integer $id
|
||||
* @property string $key
|
||||
* @property string $value
|
||||
* @property integer $type
|
||||
* @property timestamp $created_at
|
||||
* @property timestamp $updated_at
|
||||
*/
|
||||
class Setting extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
use CastsEnums;
|
||||
|
||||
/**
|
||||
* fillable
|
||||
* @var string[]
|
||||
*/
|
||||
protected $fillable = [
|
||||
'key',
|
||||
'value',
|
||||
'type',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
];
|
||||
|
||||
/**
|
||||
* cast enum
|
||||
* @var string[]
|
||||
*/
|
||||
protected $enumCasts = [
|
||||
'type' => SettingTypesEnum::class
|
||||
];
|
||||
|
||||
// * EVENTS
|
||||
|
||||
/**
|
||||
* Perform any actions required after the model boots.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function booted()
|
||||
{
|
||||
static::updating(function (self $model) {
|
||||
if (
|
||||
$model->key === "SquaredLogo" ||
|
||||
$model->key === "RectangleLogo" ||
|
||||
$model->key === "BackgroundImage" ||
|
||||
$model->key === "HomeButtonImage"
|
||||
) {
|
||||
$model->value = FileStorageHelper::storeFile($model, $model->value, true);
|
||||
}
|
||||
});
|
||||
static::deleted(function (self $model) {
|
||||
if (
|
||||
$model->key === "SquaredLogo" ||
|
||||
$model->key === "RectangleLogo" ||
|
||||
$model->key === "BackgroundImage" ||
|
||||
$model->key === "HomeButtonImage"
|
||||
) {
|
||||
FileStorageHelper::removeFile($model, 'value');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
107
app/Models/Tag.php
Normal file
107
app/Models/Tag.php
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Lib\Helpers\ToolboxHelper;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @property integer $id
|
||||
* @property string $name
|
||||
* @property string $slug
|
||||
* @property integer $order
|
||||
*/
|
||||
class Tag extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* Indicates if the model should be timestamped.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $timestamps = false;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'slug',
|
||||
];
|
||||
|
||||
/**
|
||||
* Perform any actions required after the model boots.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::creating(function (self $tag) {
|
||||
static::setSlug($tag);
|
||||
static::assertFieldIsUnique($tag->slug);
|
||||
static::setOrder($tag);
|
||||
});
|
||||
static::updating(function (self $tag) {
|
||||
static::assertFieldIsUnique($tag->slug, $tag->id);
|
||||
});
|
||||
static::deleting(function (self $tag) {
|
||||
$tag->documents()->detach();
|
||||
});
|
||||
}
|
||||
|
||||
// * METHODS
|
||||
|
||||
/**
|
||||
* Set the slug.
|
||||
*
|
||||
* @param \App\Models\Tag $tag
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function setSlug(Tag $tag)
|
||||
{
|
||||
$tag->slug = Str::slug($tag->name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts using validation that the field is unique.
|
||||
*
|
||||
* @param string $slug
|
||||
* @param integer|null $id
|
||||
* @return void
|
||||
* @throws \Illuminate\Validation\ValidationException If field already exists.
|
||||
*/
|
||||
private static function assertFieldIsUnique(string $slug, ?int $id = null)
|
||||
{
|
||||
$table = (new self())->getTable();
|
||||
ToolboxHelper::assertFieldIsUnique($table, 'slug', $slug, $id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set order after the last element of the list.
|
||||
*
|
||||
* @param \App\Models\Tag $tag
|
||||
* @return void
|
||||
*/
|
||||
private static function setOrder(Tag $tag): void
|
||||
{
|
||||
$tag->order = \intval(self::query()->max('order')) + 1;
|
||||
}
|
||||
|
||||
// * RELATIONSHIPS
|
||||
|
||||
/**
|
||||
* MorphToMany documents relation.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\MorphToMany
|
||||
*/
|
||||
public function documents()
|
||||
{
|
||||
return $this->morphedByMany(Document::class, 'taggable');
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use App\Enums\UserRolesEnum;
|
||||
|
||||
class User extends Authenticatable
|
||||
{
|
||||
|
|
@ -45,6 +46,14 @@ class User extends Authenticatable
|
|||
'email_verified_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* cast enum
|
||||
* @var string[]
|
||||
*/
|
||||
protected $enumCasts = [
|
||||
'role' => UserRolesEnum::class
|
||||
];
|
||||
|
||||
/**
|
||||
* Perform any actions required after the model boots.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
|
|
@ -36,5 +37,7 @@ class AppServiceProvider extends ServiceProvider
|
|||
\strtoupper(config('app.locale'))
|
||||
));
|
||||
Carbon::setLocale(config('app.locale'));
|
||||
|
||||
JsonResource::withoutWrapping();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Models\AppUser;
|
||||
use App\Models\Document;
|
||||
use App\Models\Tag;
|
||||
use Illuminate\Cache\RateLimiting\Limit;
|
||||
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
|
||||
use Illuminate\Http\Request;
|
||||
|
|
@ -34,7 +37,16 @@ class RouteServiceProvider extends ServiceProvider
|
|||
Route::middleware('web')
|
||||
->namespace($this->namespace)
|
||||
->group(base_path('routes/web-bo.php'));
|
||||
|
||||
Route::prefix('api')
|
||||
->middleware('api')
|
||||
->namespace($this->namespace)
|
||||
->group(base_path('routes/api.php'));
|
||||
});
|
||||
|
||||
Route::model('appuser', AppUser::class);
|
||||
Route::model('document', Document::class);
|
||||
Route::model('tag', Tag::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
26
app/Traits/Controllers/UpdateModelStatus.php
Normal file
26
app/Traits/Controllers/UpdateModelStatus.php
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace App\Traits\Controllers;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* This trait works with Route::bind in App service provider
|
||||
* to resolve the model and the DB row
|
||||
* It does not work with binder Service
|
||||
*/
|
||||
trait UpdateModelStatus
|
||||
{
|
||||
/**
|
||||
* Change model published.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Model $model
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function changePublished(Model $model)
|
||||
{
|
||||
$model->status = !$model->status;
|
||||
$model->saveOrFail();
|
||||
return redirect()->back();
|
||||
}
|
||||
}
|
||||
192
app/Traits/Requests/HasPicture.php
Normal file
192
app/Traits/Requests/HasPicture.php
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
<?php
|
||||
|
||||
namespace App\Traits\Requests;
|
||||
|
||||
use Illuminate\Support\Facades\File;
|
||||
|
||||
trait HasPicture
|
||||
{
|
||||
/**
|
||||
* Minimum width
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $minWith = 400;
|
||||
|
||||
/**
|
||||
* Maximum width
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $maxWith = 1800;
|
||||
|
||||
/**
|
||||
* Minimum height
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $minHeight = 300;
|
||||
|
||||
/**
|
||||
* Maximum height
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $maxHeight = 1800;
|
||||
|
||||
/**
|
||||
* Image rules
|
||||
*
|
||||
* @param string $name
|
||||
* @param boolean $required
|
||||
* @param integer|null $minWith
|
||||
* @param integer|null $minHeight
|
||||
* @param integer|null $maxWith
|
||||
* @param integer|null $maxHeight
|
||||
* @return array
|
||||
*/
|
||||
public function pictureRules(
|
||||
string $name = 'picture',
|
||||
bool $required = true,
|
||||
?int $minWith = null,
|
||||
?int $minHeight = null,
|
||||
?int $maxWith = null,
|
||||
?int $maxHeight = null
|
||||
): array {
|
||||
$sizes = (object)[
|
||||
'minWith' => $minWith ?? $this->minWith,
|
||||
'minHeight' => $minHeight ?? $this->minHeight,
|
||||
'maxWith' => $maxWith ?? $this->maxWith,
|
||||
'maxHeight' => $maxHeight ?? $this->maxHeight
|
||||
];
|
||||
return [
|
||||
$name => [
|
||||
$required ? 'required' : 'nullable',
|
||||
function (string $attribute, $value, callable $fail) use ($sizes) {
|
||||
return $this->validatePicture($attribute, $value, $fail, $sizes);
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Images rules
|
||||
*
|
||||
* @param string $name
|
||||
* @param boolean $required
|
||||
* @param integer|null $minWith
|
||||
* @param integer|null $minHeight
|
||||
* @param integer|null $maxWith
|
||||
* @param integer|null $maxHeight
|
||||
* @param integer|null $limitMin
|
||||
* @param integer|null $limitMax
|
||||
* @return array
|
||||
*/
|
||||
public function picturesRules(
|
||||
string $name = 'pictures',
|
||||
bool $required = true,
|
||||
?int $minWith = null,
|
||||
?int $minHeight = null,
|
||||
?int $maxWith = null,
|
||||
?int $maxHeight = null,
|
||||
?int $limitMin = null,
|
||||
?int $limitMax = null
|
||||
): array {
|
||||
$sizes = (object)[
|
||||
'required' => $required ?? $this->required,
|
||||
'minWith' => $minWith ?? $this->minWith,
|
||||
'minHeight' => $minHeight ?? $this->minHeight,
|
||||
'maxWith' => $maxWith ?? $this->maxWith,
|
||||
'maxHeight' => $maxHeight ?? $this->maxHeight,
|
||||
'limitMin' => $limitMin ?? $this->limitMin,
|
||||
'limitMax' => $limitMax ?? $this->limitMax
|
||||
];
|
||||
return [
|
||||
"{$name}" => ($required ? 'required' : 'nullable') . "|array|between:$limitMin,$limitMax",
|
||||
"{$name}.*" => [
|
||||
'required',
|
||||
function (string $attribute, $value, callable $fail) use ($sizes) {
|
||||
return $this->validatePicture($attribute, $value, $fail, $sizes);
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate picture
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param callback $fail
|
||||
* @param object $sizes
|
||||
* @return void
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
|
||||
*/
|
||||
private function validatePicture(string $attribute, mixed $value, callable $fail, object $sizes)
|
||||
{
|
||||
if (!File::exists($value)) {
|
||||
$fail(trans(":attribute n'existe pas dans le système de fichier"));
|
||||
return;
|
||||
}
|
||||
if (!\in_array(File::mimeType($value), ['image/jpeg', 'image/jpg', 'image/png', 'image/gif'])) {
|
||||
$fail(trans(":attribute doit être une image de type 'image/jpeg,image/jpg,image/png,image/gif'"));
|
||||
}
|
||||
if (!($dimensions = \getimagesize($value)) or !isset($dimensions[0]) or !isset($dimensions[1])) {
|
||||
$fail(trans(":attribute impossible de valider les dimensions"));
|
||||
} else {
|
||||
$width = intval($dimensions[0]);
|
||||
$height = intval($dimensions[1]);
|
||||
|
||||
$xMessage = trans(
|
||||
":attribute la largeur doit être comprise entre {$sizes->minWith}px et {$sizes->maxWith}px"
|
||||
);
|
||||
if ($sizes->minWith === $sizes->maxWith) {
|
||||
$xMessage = trans(":attribute la largeur doit être de {$sizes->minWith}px");
|
||||
}
|
||||
$yMessage = trans(
|
||||
":attribute la hauteur doit être comprise entre {$sizes->minWith}px et {$sizes->maxWith}px"
|
||||
);
|
||||
if ($sizes->minHeight === $sizes->maxHeight) {
|
||||
$yMessage = trans(":attribute la hauteur doit être de {$sizes->minWith}px");
|
||||
}
|
||||
|
||||
if (($width < $sizes->minWith) or ($width > $sizes->maxWith)) {
|
||||
$fail($xMessage);
|
||||
}
|
||||
if (($height < $sizes->minHeight) or ($height > $sizes->maxHeight)) {
|
||||
$fail($yMessage);
|
||||
}
|
||||
} //end if
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges products pictures fields with request data
|
||||
*
|
||||
* @param string $name
|
||||
* @return void
|
||||
*/
|
||||
protected function mergePicture(string $name = 'picture'): void
|
||||
{
|
||||
$picturePath = $this->{$name};
|
||||
if (is_string($picturePath)) {
|
||||
$this->merge(["{$name}Validate" => []]);
|
||||
// ! Empty picture.
|
||||
if ($picturePath === 'false') {
|
||||
// * Empty picture and exit
|
||||
$this->merge([$name => []]);
|
||||
return;
|
||||
}
|
||||
$decodedPath = \urldecode($picturePath);
|
||||
$this->merge([
|
||||
"{$name}Validate" => array_merge(
|
||||
$this->{"{$name}Validate"},
|
||||
[\public_path(ltrim($decodedPath, '/'))]
|
||||
)
|
||||
]);
|
||||
$changedPicture = ltrim($decodedPath, '/');
|
||||
$this->merge([
|
||||
$name => $changedPicture
|
||||
]);
|
||||
} //end if
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,7 @@
|
|||
{
|
||||
"name": "laravel/laravel",
|
||||
"name": "uwil/com-app-bo",
|
||||
"version": "1.0.0",
|
||||
"type": "project",
|
||||
"time": "2023-03-02",
|
||||
"description": "The Laravel Framework.",
|
||||
"support": {
|
||||
"email": "equipe.web@ldc.fr"
|
||||
},
|
||||
"description": "Back-Office for Commercial App",
|
||||
"keywords": [
|
||||
"framework",
|
||||
"laravel"
|
||||
|
|
@ -15,7 +10,9 @@
|
|||
"require": {
|
||||
"php": "^7.3|^8.0",
|
||||
"diglactic/laravel-breadcrumbs": "^6.1",
|
||||
"erusev/parsedown": "^1.7",
|
||||
"fruitcake/laravel-cors": "^2.0",
|
||||
"google/cloud-vision": "^0.25.0",
|
||||
"guzzlehttp/guzzle": "^7.0.1",
|
||||
"kwaadpepper/enum": "^2.1",
|
||||
"kwaadpepper/image-resizer": "^0.1.2",
|
||||
|
|
@ -24,9 +21,12 @@
|
|||
"laravel/ui": "^3.4",
|
||||
"ldc/web-admin-secure-laravel": "^1.0",
|
||||
"ldc/web-status-checker-laravel": "^1.0",
|
||||
"maatwebsite/excel": "^3.1",
|
||||
"mews/purifier": "^3.3",
|
||||
"pion/laravel-chunk-upload": "^1.3",
|
||||
"sentry/sentry-laravel": "^3.2",
|
||||
"spatie/laravel-mix-preload": "^1.3"
|
||||
"spatie/laravel-mix-preload": "^1.3",
|
||||
"usmanhalalit/laracsv": "^2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"facade/ignition": "^2.5",
|
||||
|
|
@ -39,7 +39,7 @@
|
|||
"slevomat/coding-standard": "^8.8"
|
||||
},
|
||||
"repositories": {
|
||||
"lab.s-web.fr/23": {
|
||||
"lab.s-web.fr": {
|
||||
"type": "composer",
|
||||
"url": "https://lab.s-web.fr/api/v4/group/23/-/packages/composer/packages.json"
|
||||
}
|
||||
|
|
@ -89,7 +89,8 @@
|
|||
"preferred-install": "dist",
|
||||
"sort-packages": true,
|
||||
"allow-plugins": {
|
||||
"dealerdirect/phpcodesniffer-composer-installer": true
|
||||
"dealerdirect/phpcodesniffer-composer-installer": true,
|
||||
"php-http/discovery": true
|
||||
},
|
||||
"gitlab-domains": [
|
||||
"lab.s-web.fr"
|
||||
|
|
|
|||
2333
composer.lock
generated
2333
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
return [
|
||||
|
||||
'BRAND' => env('BRAND', 'LDC'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Name
|
||||
|
|
@ -167,6 +169,8 @@ return [
|
|||
* Package Service Providers...
|
||||
*/
|
||||
|
||||
Maatwebsite\Excel\ExcelServiceProvider::class,
|
||||
|
||||
/*
|
||||
* Application Service Providers...
|
||||
*/
|
||||
|
|
@ -230,7 +234,6 @@ return [
|
|||
'URL' => Illuminate\Support\Facades\URL::class,
|
||||
'Validator' => Illuminate\Support\Facades\Validator::class,
|
||||
'View' => Illuminate\Support\Facades\View::class,
|
||||
|
||||
'Excel' => Maatwebsite\Excel\Facades\Excel::class,
|
||||
],
|
||||
|
||||
];
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ return [
|
|||
|
|
||||
*/
|
||||
|
||||
'API_TOKEN' => env('API_TOKEN'),
|
||||
|
||||
'defaults' => [
|
||||
'guard' => 'frontend',
|
||||
'passwords' => 'users',
|
||||
|
|
|
|||
52
config/chunk-upload.php
Normal file
52
config/chunk-upload.php
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/pionl/laravel-chunk-upload
|
||||
*/
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
* The storage config
|
||||
*/
|
||||
|
||||
'storage' => [
|
||||
|
||||
/*
|
||||
* Returns the folder name of the chunks. The location is in storage/app/{folder_name}
|
||||
*/
|
||||
|
||||
'chunks' => 'chunks',
|
||||
'disk' => 'local',
|
||||
],
|
||||
'clear' => [
|
||||
|
||||
/*
|
||||
* How old chunks we should delete
|
||||
*/
|
||||
|
||||
'timestamp' => '-3 HOURS',
|
||||
'schedule' => [
|
||||
'enabled' => true,
|
||||
// Run every hour on the 25th minute.
|
||||
'cron' => '25 * * * *',
|
||||
],
|
||||
],
|
||||
'chunk' => [
|
||||
// Setup for the chunk naming setup to ensure same name upload at same time.
|
||||
'name' => [
|
||||
'use' => [
|
||||
// Should the chunk name use the session id? The uploader must send cookie!
|
||||
'session' => true,
|
||||
// Instead of session we can use the ip and browser?
|
||||
'browser' => false,
|
||||
],
|
||||
],
|
||||
],
|
||||
'handlers' => [
|
||||
// A list of handlers/providers that will be appended to existing list of handlers.
|
||||
'custom' => [],
|
||||
// Overrides the list of handlers - use only what you really want.
|
||||
'override' => [],
|
||||
],
|
||||
];
|
||||
349
config/excel.php
Normal file
349
config/excel.php
Normal file
|
|
@ -0,0 +1,349 @@
|
|||
<?php
|
||||
|
||||
use Maatwebsite\Excel\Excel;
|
||||
|
||||
return [
|
||||
'exports' => [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Chunk size
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When using FromQuery, the query is automatically chunked.
|
||||
| Here you can specify how big the chunk should be.
|
||||
|
|
||||
*/
|
||||
|
||||
'chunk_size' => 1000,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Pre-calculate formulas during export
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'pre_calculate_formulas' => false,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Enable strict null comparison
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When enabling strict null comparison empty cells ('') will
|
||||
| be added to the sheet.
|
||||
*/
|
||||
|
||||
'strict_null_comparison' => false,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| CSV Settings
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Configure e.g. delimiter, enclosure and line ending for CSV exports.
|
||||
|
|
||||
*/
|
||||
|
||||
'csv' => [
|
||||
'delimiter' => ',',
|
||||
'enclosure' => '"',
|
||||
'line_ending' => PHP_EOL,
|
||||
'use_bom' => false,
|
||||
'include_separator_line' => false,
|
||||
'excel_compatibility' => false,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Worksheet properties
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Configure e.g. default title, creator, subject,...
|
||||
|
|
||||
*/
|
||||
|
||||
'properties' => [
|
||||
'creator' => '',
|
||||
'lastModifiedBy' => '',
|
||||
'title' => '',
|
||||
'description' => '',
|
||||
'subject' => '',
|
||||
'keywords' => '',
|
||||
'category' => '',
|
||||
'manager' => '',
|
||||
'company' => '',
|
||||
],
|
||||
],
|
||||
|
||||
'imports' => [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Read Only
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When dealing with imports, you might only be interested in the
|
||||
| data that the sheet exists. By default we ignore all styles,
|
||||
| however if you want to do some logic based on style data
|
||||
| you can enable it by setting read_only to false.
|
||||
|
|
||||
*/
|
||||
|
||||
'read_only' => true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Ignore Empty
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When dealing with imports, you might be interested in ignoring
|
||||
| rows that have null values or empty strings. By default rows
|
||||
| containing empty strings or empty values are not ignored but can be
|
||||
| ignored by enabling the setting ignore_empty to true.
|
||||
|
|
||||
*/
|
||||
|
||||
'ignore_empty' => false,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Heading Row Formatter
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Configure the heading row formatter.
|
||||
| Available options: none|slug|custom
|
||||
|
|
||||
*/
|
||||
|
||||
'heading_row' => [
|
||||
'formatter' => 'slug',
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| CSV Settings
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Configure e.g. delimiter, enclosure and line ending for CSV imports.
|
||||
|
|
||||
*/
|
||||
|
||||
'csv' => [
|
||||
'delimiter' => ',',
|
||||
'enclosure' => '"',
|
||||
'escape_character' => '\\',
|
||||
'contiguous' => false,
|
||||
'input_encoding' => 'UTF-8',
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Worksheet properties
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Configure e.g. default title, creator, subject,...
|
||||
|
|
||||
*/
|
||||
|
||||
'properties' => [
|
||||
'creator' => '',
|
||||
'lastModifiedBy' => '',
|
||||
'title' => '',
|
||||
'description' => '',
|
||||
'subject' => '',
|
||||
'keywords' => '',
|
||||
'category' => '',
|
||||
'manager' => '',
|
||||
'company' => '',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Extension detector
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Configure here which writer/reader type should be used when the package
|
||||
| needs to guess the correct type based on the extension alone.
|
||||
|
|
||||
*/
|
||||
|
||||
'extension_detector' => [
|
||||
'xlsx' => Excel::XLSX,
|
||||
'xlsm' => Excel::XLSX,
|
||||
'xltx' => Excel::XLSX,
|
||||
'xltm' => Excel::XLSX,
|
||||
'xls' => Excel::XLS,
|
||||
'xlt' => Excel::XLS,
|
||||
'ods' => Excel::ODS,
|
||||
'ots' => Excel::ODS,
|
||||
'slk' => Excel::SLK,
|
||||
'xml' => Excel::XML,
|
||||
'gnumeric' => Excel::GNUMERIC,
|
||||
'htm' => Excel::HTML,
|
||||
'html' => Excel::HTML,
|
||||
'csv' => Excel::CSV,
|
||||
'tsv' => Excel::TSV,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| PDF Extension
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Configure here which Pdf driver should be used by default.
|
||||
| Available options: Excel::MPDF | Excel::TCPDF | Excel::DOMPDF
|
||||
|
|
||||
*/
|
||||
|
||||
'pdf' => Excel::DOMPDF,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Value Binder
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| PhpSpreadsheet offers a way to hook into the process of a value being
|
||||
| written to a cell. In there some assumptions are made on how the
|
||||
| value should be formatted. If you want to change those defaults,
|
||||
| you can implement your own default value binder.
|
||||
|
|
||||
| Possible value binders:
|
||||
|
|
||||
| [x] Maatwebsite\Excel\DefaultValueBinder::class
|
||||
| [x] PhpOffice\PhpSpreadsheet\Cell\StringValueBinder::class
|
||||
| [x] PhpOffice\PhpSpreadsheet\Cell\AdvancedValueBinder::class
|
||||
|
|
||||
*/
|
||||
|
||||
'value_binder' => [
|
||||
'default' => Maatwebsite\Excel\DefaultValueBinder::class,
|
||||
],
|
||||
|
||||
'cache' => [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default cell caching driver
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| By default PhpSpreadsheet keeps all cell values in memory, however when
|
||||
| dealing with large files, this might result into memory issues. If you
|
||||
| want to mitigate that, you can configure a cell caching driver here.
|
||||
| When using the illuminate driver, it will store each value in a the
|
||||
| cache store. This can slow down the process, because it needs to
|
||||
| store each value. You can use the "batch" store if you want to
|
||||
| only persist to the store when the memory limit is reached.
|
||||
|
|
||||
| Drivers: memory|illuminate|batch
|
||||
|
|
||||
*/
|
||||
|
||||
'driver' => 'memory',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Batch memory caching
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When dealing with the "batch" caching driver, it will only
|
||||
| persist to the store when the memory limit is reached.
|
||||
| Here you can tweak the memory limit to your liking.
|
||||
|
|
||||
*/
|
||||
|
||||
'batch' => [
|
||||
'memory_limit' => 60000,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Illuminate cache
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When using the "illuminate" caching driver, it will automatically use
|
||||
| your default cache store. However if you prefer to have the cell
|
||||
| cache on a separate store, you can configure the store name here.
|
||||
| You can use any store defined in your cache config. When leaving
|
||||
| at "null" it will use the default store.
|
||||
|
|
||||
*/
|
||||
|
||||
'illuminate' => [
|
||||
'store' => null,
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Transaction Handler
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| By default the import is wrapped in a transaction. This is useful
|
||||
| for when an import may fail and you want to retry it. With the
|
||||
| transactions, the previous import gets rolled-back.
|
||||
|
|
||||
| You can disable the transaction handler by setting this to null.
|
||||
| Or you can choose a custom made transaction handler here.
|
||||
|
|
||||
| Supported handlers: null|db
|
||||
|
|
||||
*/
|
||||
|
||||
'transactions' => [
|
||||
'handler' => 'db',
|
||||
],
|
||||
|
||||
'temporary_files' => [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Local Temporary Path
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When exporting and importing files, we use a temporary file, before
|
||||
| storing reading or downloading. Here you can customize that path.
|
||||
|
|
||||
*/
|
||||
|
||||
'local_path' => storage_path('framework/laravel-excel'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Remote Temporary Disk
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When dealing with a multi server setup with queues in which you
|
||||
| cannot rely on having a shared local temporary path, you might
|
||||
| want to store the temporary file on a shared disk. During the
|
||||
| queue executing, we'll retrieve the temporary file from that
|
||||
| location instead. When left to null, it will always use
|
||||
| the local path. This setting only has effect when using
|
||||
| in conjunction with queued imports and exports.
|
||||
|
|
||||
*/
|
||||
|
||||
'remote_disk' => null,
|
||||
'remote_prefix' => null,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Force Resync
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When dealing with a multi server setup as above, it's possible
|
||||
| for the clean up that occurs after entire queue has been run to only
|
||||
| cleanup the server that the last AfterImportJob runs on. The rest of the server
|
||||
| would still have the local temporary file stored on it. In this case your
|
||||
| local storage limits can be exceeded and future imports won't be processed.
|
||||
| To mitigate this you can set this config value to be true, so that after every
|
||||
| queued chunk is processed the local temporary file is deleted on the server that
|
||||
| processed it.
|
||||
|
|
||||
*/
|
||||
|
||||
'force_resync_remote' => null,
|
||||
],
|
||||
];
|
||||
37
database/factories/AppUserFactory.php
Normal file
37
database/factories/AppUserFactory.php
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Enums\AppUserRolesEnum;
|
||||
use App\Models\AppUser;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends Factory<\App\Models\AppUser>
|
||||
*/
|
||||
class AppUserFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The name of the factory's corresponding model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $model = AppUser::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function definition()
|
||||
{
|
||||
return [
|
||||
'firstname' => $this->faker->unique()->firstname(),
|
||||
'lastname' => $this->faker->unique()->lastName(),
|
||||
'email' => $this->faker->unique()->safeEmail(),
|
||||
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi',
|
||||
'status' => $this->faker->boolean(75),
|
||||
'role' => collect(AppUserRolesEnum::toArray())->random(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -31,8 +31,9 @@ class DirectoryFactory extends Factory
|
|||
"de" => $this->faker->unique()->word,
|
||||
"nl" => $this->faker->unique()->word,
|
||||
],
|
||||
'parent_id' => null,
|
||||
'directory_id' => null,
|
||||
'needAuthorization' => $this->faker->boolean(50),
|
||||
'is_system' => 0,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
34
database/factories/TagFactory.php
Normal file
34
database/factories/TagFactory.php
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Tag;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @extends Factory<\App\Models\Tag>
|
||||
*/
|
||||
final class TagFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The name of the factory's corresponding model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $model = Tag::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function definition()
|
||||
{
|
||||
$name = $this->faker->unique()->word;
|
||||
return [
|
||||
'name' => $name,
|
||||
'slug' => Str::slug($name),
|
||||
];
|
||||
}
|
||||
}
|
||||
BIN
database/factories/assets/settings/background-image-default.png
Normal file
BIN
database/factories/assets/settings/background-image-default.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
BIN
database/factories/assets/settings/home-button-image-default.png
Normal file
BIN
database/factories/assets/settings/home-button-image-default.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 581 B |
BIN
database/factories/assets/settings/rectangle-logo-default.png
Normal file
BIN
database/factories/assets/settings/rectangle-logo-default.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
BIN
database/factories/assets/settings/squared-logo-default.png
Normal file
BIN
database/factories/assets/settings/squared-logo-default.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
|
|
@ -14,7 +14,7 @@ class CreateFailedJobsTable extends Migration
|
|||
public function up()
|
||||
{
|
||||
Schema::create('failed_jobs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->bigIncrements('id');
|
||||
$table->string('uuid')->unique();
|
||||
$table->text('connection');
|
||||
$table->text('queue');
|
||||
|
|
|
|||
|
|
@ -1,32 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class RenameActivatedAppUsersTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('app_users', function (Blueprint $table) {
|
||||
$table->renameColumn('activated', 'status');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('app_users', function (Blueprint $table) {
|
||||
$table->renameColumn('status', 'activated');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
<?php
|
||||
|
||||
use App\Enums\AppUserRolesEnum;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class UpdateRoleAppUsersTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
DB::table('app_users')->where('role', 'USER')->update(['role' => AppUserRolesEnum::user()->value]);
|
||||
DB::table('app_users')->where('role', 'TOTAL')->update(['role' => AppUserRolesEnum::total()->value]);
|
||||
|
||||
DB::statement("ALTER TABLE app_users CHANGE COLUMN role role TINYINT NOT NULL");
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
DB::statement("ALTER TABLE app_users CHANGE COLUMN role role VARCHAR(50) NOT NULL");
|
||||
|
||||
DB::table('app_users')->where('role', strval(AppUserRolesEnum::user()->value))->update(['role' => 'USER']);
|
||||
DB::table('app_users')->where('role', strval(AppUserRolesEnum::total()->value))->update(['role' => 'TOTAL']);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class UpdatePasswordAppUsersTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
DB::statement("ALTER TABLE app_users CHANGE COLUMN password password VARCHAR(255) NOT NULL");
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
DB::statement("ALTER TABLE app_users CHANGE COLUMN password password BLOB NOT NULL");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Directory;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class UpdateDirectoryTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
// * APP_USER->UPDATED_AT
|
||||
//phpcs:ignore Generic.Files.LineLength.TooLong
|
||||
DB::statement('ALTER TABLE `directories` CHANGE `updated_at` `updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP');
|
||||
|
||||
Schema::table('directories', function (Blueprint $table) {
|
||||
$table->renameColumn('parent', 'directory_id');
|
||||
$table->json('name')->change();
|
||||
$table->boolean('is_system')->after('needAuthorization')->comment('Le dossier est-il administrable ?');
|
||||
$table->dateTime('created_at')->default('CURRENT_TIMESTAMP')->nullable()->change();
|
||||
});
|
||||
|
||||
Directory::each(function (Directory $model) {
|
||||
if ($model->name['fr'] === "Le Salon") {
|
||||
$model->is_system = 1;
|
||||
$model->saveOrFail();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
// * APP_USER->CREATED_AT
|
||||
//phpcs:ignore Generic.Files.LineLength.TooLong
|
||||
DB::statement('ALTER TABLE `directories` CHANGE `created_at` `created_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP');
|
||||
|
||||
Schema::table('directories', function (Blueprint $table) {
|
||||
$table->renameColumn('directory_id', 'parent');
|
||||
$table->string('name')->change();
|
||||
$table->dropColumn('is_system');
|
||||
$table->dateTime('updated_at')->default('0000-00-00 00:00:00')->nullable()->change();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -13,10 +13,10 @@ class AddPivotDirectoryTable extends Migration
|
|||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('taggables', function (Blueprint $table) {
|
||||
Schema::create('directorables', function (Blueprint $table) {
|
||||
$table->foreignId('directory_id')->references('id')->on('directories');
|
||||
$table->bigInteger('taggable_id')->comment('Id du model associé');
|
||||
$table->string('taggable_type', 60)->comment('Type du model associé');
|
||||
$table->bigInteger('directorable_id')->comment('Id du model associé');
|
||||
$table->string('directorable_type', 60)->comment('Type du model associé');
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -27,6 +27,6 @@ class AddPivotDirectoryTable extends Migration
|
|||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('taggables');
|
||||
Schema::dropIfExists('directorables');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
|
||||
use App\Enums\AppUserRolesEnum;
|
||||
use App\Models\AppUser;
|
||||
use App\Models\Directory;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class UpdateAppUsersTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
// * APP_USER->UPDATED_AT
|
||||
//phpcs:ignore Generic.Files.LineLength.TooLong
|
||||
DB::statement('ALTER TABLE `app_users` CHANGE `updated_at` `updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP');
|
||||
|
||||
// * APP_USER->ROLE
|
||||
DB::table('app_users')->where('role', 'USER')->update(['role' => AppUserRolesEnum::user()->value]);
|
||||
DB::table('app_users')->where('role', 'TOTAL')->update(['role' => AppUserRolesEnum::total()->value]);
|
||||
DB::statement("ALTER TABLE app_users CHANGE COLUMN role role TINYINT NOT NULL");
|
||||
|
||||
// * APP_USER->PASSWORD
|
||||
DB::statement("ALTER TABLE app_users CHANGE COLUMN password password VARCHAR(255) NOT NULL");
|
||||
|
||||
$appUsersOld = AppUser::get();
|
||||
|
||||
// * OTHERS
|
||||
Schema::table('app_users', function (Blueprint $table) {
|
||||
$table->dropUnique('email');
|
||||
$table->unique(['email']);
|
||||
$table->renameColumn('surname', 'lastname');
|
||||
$table->renameColumn('activated', 'status');
|
||||
$table->integer('order')->after('role')->comment('Ordonnance des utilisateurs de l\'applications.');
|
||||
$table->dropColumn('authorizedFolders');
|
||||
$table->dateTime('created_at')->default('CURRENT_TIMESTAMP')->nullable()->change();
|
||||
});
|
||||
|
||||
AppUser::each(function (AppUser $appUserModel, int $key) use ($appUsersOld) {
|
||||
foreach (json_decode($appUsersOld[$key]->authorizedFolders) as $directory) {
|
||||
Directory::each(function (Directory $directoryModel) use ($appUserModel, $directory) {
|
||||
if ($directoryModel->id == $directory->id) {
|
||||
$appUserModel->directories()->save($directoryModel);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
// * APP_USER->ROLE
|
||||
DB::statement("ALTER TABLE app_users CHANGE COLUMN role role VARCHAR(50) NOT NULL");
|
||||
DB::table('app_users')->where('role', strval(AppUserRolesEnum::user()->value))->update(['role' => 'USER']);
|
||||
DB::table('app_users')->where('role', strval(AppUserRolesEnum::total()->value))->update(['role' => 'TOTAL']);
|
||||
|
||||
// * APP_USER->CREATED_AT
|
||||
//phpcs:ignore Generic.Files.LineLength.TooLong
|
||||
DB::statement('ALTER TABLE `app_users` CHANGE `created_at` `created_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP');
|
||||
|
||||
// * APP_USER->PASSWORD
|
||||
DB::statement("ALTER TABLE app_users CHANGE COLUMN password password BLOB NOT NULL");
|
||||
|
||||
// * OTHERS
|
||||
Schema::table('app_users', function (Blueprint $table) {
|
||||
$table->dropUnique(['email']);
|
||||
$table->unique('email');
|
||||
$table->renameColumn('lastname', 'surname');
|
||||
$table->renameColumn('status', 'activated');
|
||||
$table->dropColumn('order');
|
||||
$table->text('authorizedFolders')->nullable();
|
||||
$table->dateTime('updated_at')->default('0000-00-00 00:00:00')->nullable()->change();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
<?php
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class UpdateUsersTable extends Migration
|
||||
|
|
@ -13,10 +15,23 @@ class UpdateUsersTable extends Migration
|
|||
*/
|
||||
public function up()
|
||||
{
|
||||
$usersOld = User::get();
|
||||
|
||||
//phpcs:ignore Generic.Files.LineLength.TooLong
|
||||
DB::statement('ALTER TABLE `users` CHANGE `updated_at` `updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP');
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->renameColumn('name', 'firstname');
|
||||
$table->string('lastname')->after('name');
|
||||
$table->string('lastname')->nullable()->after('name');
|
||||
$table->dateTime('created_at')->default('CURRENT_TIMESTAMP')->nullable()->change();
|
||||
});
|
||||
|
||||
$usersNew = User::get();
|
||||
foreach ($usersNew as $key => $userNew) {
|
||||
$userName = explode(" ", $usersOld[$key]->name);
|
||||
$userNew->firstname = $userName[0];
|
||||
$userNew->lastname = $userName[1] ?? null;
|
||||
$userNew->saveOrFail();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -26,9 +41,22 @@ class UpdateUsersTable extends Migration
|
|||
*/
|
||||
public function down()
|
||||
{
|
||||
// * APP_USER->CREATED_AT
|
||||
//phpcs:ignore Generic.Files.LineLength.TooLong
|
||||
DB::statement('ALTER TABLE `users` CHANGE `created_at` `created_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP');
|
||||
|
||||
$usersOld = User::get();
|
||||
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->renameColumn('firstname', 'name');
|
||||
$table->dropColumn('lastname');
|
||||
$table->dateTime('updated_at')->default('0000-00-00 00:00:00')->nullable()->change();
|
||||
});
|
||||
|
||||
$usersNew = User::get();
|
||||
foreach ($usersNew as $key => $userNew) {
|
||||
$userNew->name = $usersOld[$key]->firstname . " " . $userNew[$key]->lastname;
|
||||
$userNew->saveOrFail();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,38 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class UpdateDirectoryTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('directories', function (Blueprint $table) {
|
||||
$table->dropForeign('directories_parent_foreign');
|
||||
$table->renameColumn('parent', 'parent_id');
|
||||
$table->foreign(['parent_id'])->references(['id'])->on('directories');
|
||||
$table->json('name')->change();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('directories', function (Blueprint $table) {
|
||||
$table->dropForeign('directories_parent_id_foreign');
|
||||
$table->renameColumn('parent_id', 'parent');
|
||||
$table->foreign(['parent'])->references('id')->on('directories');
|
||||
$table->string('name')->change();
|
||||
});
|
||||
}
|
||||
}
|
||||
126
database/migrations/2023_04_05_093932_update_document_table.php
Normal file
126
database/migrations/2023_04_05_093932_update_document_table.php
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
<?php
|
||||
|
||||
use App\Enums\Documents\LangEnum;
|
||||
use App\Models\Document;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class UpdateDocumentTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Collection of old Documents.
|
||||
* @var object[]
|
||||
*/
|
||||
private $documentsOld;
|
||||
|
||||
/**
|
||||
* Collection of new Documents.
|
||||
* @var object[]
|
||||
*/
|
||||
private $documentsNew;
|
||||
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
$this->documentsOld = Document::get();
|
||||
$this->documentsNew = Document::get();
|
||||
|
||||
// * APP_USER->UPDATED_AT
|
||||
//phpcs:ignore Generic.Files.LineLength.TooLong
|
||||
DB::statement('ALTER TABLE `documents` CHANGE `updated_at` `updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP');
|
||||
|
||||
self::updateData();
|
||||
Schema::table('documents', function (Blueprint $table) {
|
||||
$table->bigInteger('parent')->unsigned()->change();
|
||||
$table->renameColumn('directory', 'directory_id');
|
||||
$table->dropColumn('parent');
|
||||
$table->unsignedBigInteger('document_id')->index()->nullable()->after('directory');
|
||||
$table->foreign(['document_id'])->references(['id'])->on('documents')->constrained();
|
||||
$table->integer('order')->after('published')->comment('Ordonnance des documents.');
|
||||
$table->string('type', 255)->change();
|
||||
$table->integer('lang')->nullable()->change();
|
||||
$table->renameColumn('published', 'status');
|
||||
$table->dateTime('created_at')->default('CURRENT_TIMESTAMP')->nullable()->change();
|
||||
});
|
||||
foreach ($this->documentsNew as $key => $documentNew) {
|
||||
$documentNew->document_id =
|
||||
($this->documentsOld[$key]->parent !== null) ? intval($this->documentsOld[$key]->parent) : null;
|
||||
$documentNew->saveOrFail();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
$documentsOld = Document::get();
|
||||
|
||||
// * APP_USER->CREATED_AT
|
||||
//phpcs:ignore Generic.Files.LineLength.TooLong
|
||||
DB::statement('ALTER TABLE `documents` CHANGE `created_at` `created_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP');
|
||||
|
||||
Schema::table('documents', function (Blueprint $table) {
|
||||
$table->renameColumn('directory_id', 'directory');
|
||||
$table->renameColumn('document_id', 'parent');
|
||||
$table->dropColumn('order');
|
||||
$table->string('type', 3)->change();
|
||||
$table->renameColumn('status', 'published');
|
||||
$table->dateTime('updated_at')->default('0000-00-00 00:00:00')->nullable()->change();
|
||||
});
|
||||
|
||||
$documentsNew = Document::get();
|
||||
foreach ($documentsNew as $key => $documentNew) {
|
||||
switch ($documentNew->lang) {
|
||||
case LangEnum::fr()->value:
|
||||
$documentNew->lang = 'fr';
|
||||
break;
|
||||
case LangEnum::en()->value:
|
||||
$documentNew->lang = 'en';
|
||||
break;
|
||||
case LangEnum::de()->value:
|
||||
$documentNew->lang = 'de';
|
||||
break;
|
||||
case LangEnum::nl()->value:
|
||||
$documentNew->lang = 'nl';
|
||||
break;
|
||||
}
|
||||
$documentNew->parent = strval($documentsOld[$key]->document_id);
|
||||
$documentNew->saveOrFail();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update data migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function updateData()
|
||||
{
|
||||
foreach ($this->documentsNew as $documentNew) {
|
||||
switch ($documentNew->lang) {
|
||||
case "fr":
|
||||
$documentNew->lang = LangEnum::fr()->value;
|
||||
break;
|
||||
case "en":
|
||||
$documentNew->lang = LangEnum::en()->value;
|
||||
break;
|
||||
case "de":
|
||||
$documentNew->lang = LangEnum::de()->value;
|
||||
break;
|
||||
case "nl":
|
||||
$documentNew->lang = LangEnum::nl()->value;
|
||||
break;
|
||||
}
|
||||
$documentNew->saveOrFail();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Setting;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class UpdateSettingsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
// * APP_USER->UPDATED_AT
|
||||
//phpcs:ignore Generic.Files.LineLength.TooLong
|
||||
DB::statement('ALTER TABLE `settings` CHANGE `updated_at` `updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP');
|
||||
|
||||
Schema::table('settings', function (Blueprint $table) {
|
||||
$table->dropUnique('data_key_unique');
|
||||
$table->unique(['key']);
|
||||
$table->text('value')->nullable()->default(null)->change();
|
||||
$table->dateTime('created_at')->default('CURRENT_TIMESTAMP')->nullable()->change();
|
||||
});
|
||||
|
||||
$settings = Setting::get();
|
||||
foreach ($settings as $key => $setting) {
|
||||
if ($setting->key === "ShowIsActive") {
|
||||
$setting->type = 5;
|
||||
} elseif ($setting->key === "MainColor") {
|
||||
$setting->type = 6;
|
||||
} elseif ($setting->key === "Button1" || $setting->key === "Button2" || $setting->key === "Button3") {
|
||||
$setting->type = 7;
|
||||
} elseif (
|
||||
$setting->key === "FormSelect1" ||
|
||||
$setting->key === "FormSelect2" ||
|
||||
$setting->key === "FormCheck"
|
||||
) {
|
||||
$setting->type = 4;
|
||||
}
|
||||
$setting->saveOrFail();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
// * APP_USER->CREATED_AT
|
||||
//phpcs:ignore Generic.Files.LineLength.TooLong
|
||||
DB::statement('ALTER TABLE `settings` CHANGE `created_at` `created_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP');
|
||||
|
||||
Schema::table('settings', function (Blueprint $table) {
|
||||
$table->dropUnique('settings_key_unique');
|
||||
$table->unique('data_key_unique');
|
||||
$table->text('value')->change();
|
||||
$table->dateTime('updated_at')->default('0000-00-00 00:00:00')->nullable()->change();
|
||||
});
|
||||
|
||||
$settings = Setting::get();
|
||||
foreach ($settings as $key => $setting) {
|
||||
if ($setting->key === "ShowIsActive") {
|
||||
$setting->type = 2;
|
||||
} elseif ($setting->key === "MainColor") {
|
||||
$setting->type = 1;
|
||||
} elseif ($setting->key === "Button1" || $setting->key === "Button2" || $setting->key === "Button3") {
|
||||
$setting->type = 2;
|
||||
} elseif (
|
||||
$setting->key === "FormSelect1" ||
|
||||
$setting->key === "FormSelect2" ||
|
||||
$setting->key === "FormCheck"
|
||||
) {
|
||||
$setting->type = 1;
|
||||
}
|
||||
$setting->saveOrFail();
|
||||
}
|
||||
}
|
||||
}
|
||||
34
database/migrations/2023_04_18_153753_create_tags_table.php
Normal file
34
database/migrations/2023_04_18_153753_create_tags_table.php
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateTagsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('tags', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('slug')->unique()->comment('Slug du nom de l\'étiquette.');
|
||||
$table->string('name')->comment('Nom du tag qui sera unique.');
|
||||
$table->integer('order')->comment('L\'ordre des étiquette.');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('tags');
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration;
|
|||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class UpdateSurnameAppUsersTable extends Migration
|
||||
class CreateTaggablesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
|
|
@ -13,8 +13,10 @@ class UpdateSurnameAppUsersTable extends Migration
|
|||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('app_users', function (Blueprint $table) {
|
||||
$table->renameColumn('surname', 'lastname');
|
||||
Schema::create('taggables', function (Blueprint $table) {
|
||||
$table->foreignId('tag_id')->references('id')->on('tags');
|
||||
$table->foreignId('taggable_id')->references('id')->on('documents');
|
||||
$table->string('taggable_type', 60);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -25,8 +27,6 @@ class UpdateSurnameAppUsersTable extends Migration
|
|||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('app_users', function (Blueprint $table) {
|
||||
$table->renameColumn('lastname', 'surname');
|
||||
});
|
||||
Schema::dropIfExists('taggables');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class UpdateContactsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
// * APP_USER->UPDATED_AT
|
||||
//phpcs:ignore Generic.Files.LineLength.TooLong
|
||||
DB::statement('ALTER TABLE `contacts` CHANGE `updated_at` `updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP');
|
||||
|
||||
Schema::table('contacts', function (Blueprint $table) {
|
||||
$table->renameColumn('user_id', 'app_user_id');
|
||||
$table->renameColumn('name', 'lastname');
|
||||
$table->dateTime('created_at')->default('CURRENT_TIMESTAMP')->nullable()->change();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
// * APP_USER->CREATED_AT
|
||||
//phpcs:ignore Generic.Files.LineLength.TooLong
|
||||
DB::statement('ALTER TABLE `contacts` CHANGE `created_at` `created_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP');
|
||||
|
||||
Schema::table('contacts', function (Blueprint $table) {
|
||||
$table->renameColumn('app_user_id', 'user_id');
|
||||
$table->renameColumn('lastname', 'name');
|
||||
$table->dateTime('updated_at')->default('0000-00-00 00:00:00')->nullable()->change();
|
||||
});
|
||||
}
|
||||
}
|
||||
24
database/seeders/AppUserSeeder.php
Normal file
24
database/seeders/AppUserSeeder.php
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\AppUser;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class AppUserSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$i = 1;
|
||||
AppUser::factory(15)->make()->each(function (AppUser $directory) use (&$i) {
|
||||
$directory->order = $i;
|
||||
$directory->saveOrFail();
|
||||
$i = $i + 1;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -14,7 +14,10 @@ class DatabaseSeeder extends Seeder
|
|||
public function run()
|
||||
{
|
||||
$this->call([
|
||||
AppUserSeeder::class,
|
||||
DirectorySeeder::class,
|
||||
SettingSeeder::class,
|
||||
TagSeeder::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@
|
|||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\AppUser;
|
||||
use App\Models\Directory;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class DirectorySeeder extends Seeder
|
||||
{
|
||||
|
|
@ -14,11 +16,33 @@ class DirectorySeeder extends Seeder
|
|||
*/
|
||||
public function run()
|
||||
{
|
||||
$i = 1;
|
||||
Directory::factory(15)->make()->each(function (Directory $directory) use (&$i) {
|
||||
|
||||
$directory = new Directory();
|
||||
$directory->name = [
|
||||
"fr" => "Salon",
|
||||
"en" => "Salon",
|
||||
"de" => "Wohnzimmer",
|
||||
"nl" => "Tentoonstelling",
|
||||
];
|
||||
$directory->directory_id = null;
|
||||
$directory->needAuthorization = 0;
|
||||
$directory->is_system = 1;
|
||||
$directory->order = 1;
|
||||
$directory->saveOrFail();
|
||||
|
||||
$i = 2;
|
||||
Directory::factory(14)->make()->each(function (Directory $directory) use (&$i) {
|
||||
$directory->order = $i;
|
||||
$directory->saveOrFail();
|
||||
$i = $i + 1;
|
||||
});
|
||||
|
||||
// Seeder for directory_authorizations table.
|
||||
$directories = Directory::query()->where('needAuthorization', true)->get();
|
||||
AppUser::each(function (AppUser $model) use ($directories) {
|
||||
$offset = rand(DB::table('directories')->min('id') ?? 1, DB::table('directories')->max('id') ?? 15);
|
||||
$length = rand(1, 2);
|
||||
$model->directories()->saveMany($directories->slice($offset, $length));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
31
database/seeders/MigrationSeeder.php
Normal file
31
database/seeders/MigrationSeeder.php
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class MigrationSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
DB::table('migrations')->truncate();
|
||||
DB::table('migrations')->insert([
|
||||
['id' => 1, 'migration' => '2023_03_14_150900_create_app_users_table', 'batch' => 1],
|
||||
['id' => 2, 'migration' => '2023_03_14_150900_create_contacts_table', 'batch' => 1],
|
||||
['id' => 3, 'migration' => '2023_03_14_150900_create_directories_table', 'batch' => 1],
|
||||
['id' => 4, 'migration' => '2023_03_14_150900_create_documents_table', 'batch' => 1],
|
||||
['id' => 5, 'migration' => '2023_03_14_150900_create_settings_table', 'batch' => 1],
|
||||
['id' => 6, 'migration' => '2023_03_14_150900_create_users_table', 'batch' => 1],
|
||||
['id' => 7, 'migration' => '2023_03_14_150901_add_foreign_keys_to_contacts_table', 'batch' => 1],
|
||||
['id' => 8, 'migration' => '2023_03_14_150901_add_foreign_keys_to_directories_table', 'batch' => 1],
|
||||
['id' => 9, 'migration' => '2023_03_14_150901_add_foreign_keys_to_documents_table', 'batch' => 1]
|
||||
]);
|
||||
$this->command->warn('Seeding default Migration table.');
|
||||
}
|
||||
}
|
||||
137
database/seeders/SettingSeeder.php
Normal file
137
database/seeders/SettingSeeder.php
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Lib\Helpers\FileStorageHelper;
|
||||
use App\Models\Directory;
|
||||
use App\Models\Setting;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class SettingSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$setting = new Setting();
|
||||
$setting->id = 1;
|
||||
$setting->key = "version";
|
||||
$setting->value = 1;
|
||||
$setting->type = 3;
|
||||
$setting->saveOrFail();
|
||||
|
||||
$setting = new Setting();
|
||||
$setting->id = 2;
|
||||
$setting->key = "ShowIsActive";
|
||||
$setting->value = 0;
|
||||
$setting->type = 4;
|
||||
$setting->saveOrFail();
|
||||
|
||||
$setting = new Setting();
|
||||
$setting->id = 3;
|
||||
$setting->key = "MainColor";
|
||||
$setting->value = "#000000";
|
||||
$setting->type = 6;
|
||||
$setting->saveOrFail();
|
||||
|
||||
$setting = new Setting();
|
||||
$setting->id = 4;
|
||||
$setting->key = "SquaredLogo";
|
||||
$setting->value = FileStorageHelper::storeFile(
|
||||
$setting,
|
||||
new \SplFileInfo(\resource_path(
|
||||
'../database/factories/assets/settings/squared-logo-default.png'
|
||||
))
|
||||
);
|
||||
$setting->type = 5;
|
||||
$setting->saveOrFail();
|
||||
|
||||
$setting = new Setting();
|
||||
$setting->id = 5;
|
||||
$setting->key = "RectangleLogo";
|
||||
$setting->value = FileStorageHelper::storeFile(
|
||||
$setting,
|
||||
new \SplFileInfo(\resource_path(
|
||||
'../database/factories/assets/settings/rectangle-logo-default.png'
|
||||
))
|
||||
);
|
||||
$setting->type = 5;
|
||||
$setting->saveOrFail();
|
||||
|
||||
$setting = new Setting();
|
||||
$setting->id = 6;
|
||||
$setting->key = "BackgroundImage";
|
||||
$setting->value = FileStorageHelper::storeFile(
|
||||
$setting,
|
||||
new \SplFileInfo(\resource_path(
|
||||
'../database/factories/assets/settings/background-image-default.png'
|
||||
))
|
||||
);
|
||||
$setting->type = 5;
|
||||
$setting->saveOrFail();
|
||||
|
||||
$setting = new Setting();
|
||||
$setting->id = 7;
|
||||
$setting->key = "HomeButtonImage";
|
||||
$setting->value = FileStorageHelper::storeFile(
|
||||
$setting,
|
||||
new \SplFileInfo(\resource_path(
|
||||
'../database/factories/assets/settings/home-button-image-default.png'
|
||||
))
|
||||
);
|
||||
$setting->type = 5;
|
||||
$setting->saveOrFail();
|
||||
|
||||
$setting = new Setting();
|
||||
$setting->id = 8;
|
||||
$setting->key = "Button1";
|
||||
$setting->value = collect(Directory::whereNull('directory_id')->pluck('id'))->random();
|
||||
$setting->type = 7;
|
||||
$setting->saveOrFail();
|
||||
|
||||
$setting = new Setting();
|
||||
$setting->id = 9;
|
||||
$setting->key = "Button2";
|
||||
$setting->value = collect(Directory::whereNull('directory_id')->pluck('id'))->random();
|
||||
$setting->type = 7;
|
||||
$setting->saveOrFail();
|
||||
|
||||
$setting = new Setting();
|
||||
$setting->id = 10;
|
||||
$setting->key = "Button3";
|
||||
$setting->value = collect(Directory::whereNull('directory_id')->pluck('id'))->random();
|
||||
$setting->type = 7;
|
||||
$setting->saveOrFail();
|
||||
|
||||
$setting = new Setting();
|
||||
$setting->id = 11;
|
||||
$setting->key = "AppStoreUUID";
|
||||
$setting->value = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX";
|
||||
$setting->type = 1;
|
||||
$setting->saveOrFail();
|
||||
|
||||
$setting = new Setting();
|
||||
$setting->id = 12;
|
||||
$setting->key = "FormSelect1";
|
||||
$setting->value = "[{}]";
|
||||
$setting->type = 2;
|
||||
$setting->saveOrFail();
|
||||
|
||||
$setting = new Setting();
|
||||
$setting->id = 13;
|
||||
$setting->key = "FormSelect2";
|
||||
$setting->value = "[{}]";
|
||||
$setting->type = 2;
|
||||
$setting->saveOrFail();
|
||||
|
||||
$setting = new Setting();
|
||||
$setting->id = 14;
|
||||
$setting->key = "FormCheck";
|
||||
$setting->value = "[{}]";
|
||||
$setting->type = 2;
|
||||
$setting->saveOrFail();
|
||||
}
|
||||
}
|
||||
24
database/seeders/TagSeeder.php
Normal file
24
database/seeders/TagSeeder.php
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\Tag;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class TagSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$i = 1;
|
||||
Tag::factory(15)->make()->each(function (Tag $tag) use (&$i) {
|
||||
$tag->order = $i;
|
||||
$tag->saveOrFail();
|
||||
$i = $i + 1;
|
||||
});
|
||||
}
|
||||
}
|
||||
9443
package-lock.json
generated
9443
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -14,6 +14,7 @@
|
|||
"stylelint-ci": "npx stylelint '**/*.scss' -f json"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ckpack/vue-color": "^1.1.9",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.1.2",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.1.2",
|
||||
"@fortawesome/free-regular-svg-icons": "^6.1.2",
|
||||
|
|
@ -43,6 +44,7 @@
|
|||
"postcss": "^8.1.14",
|
||||
"prettier": "^2.7.1",
|
||||
"resolve-url-loader": "^5.0.0",
|
||||
"resumablejs": "^1.1.0",
|
||||
"sass": "^1.54.8",
|
||||
"sass-loader": "^12.6.0",
|
||||
"slugify": "^1.6.5",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,11 @@
|
|||
return [
|
||||
'classes' => [
|
||||
'dashboard' => 'Dashboard',
|
||||
'app_users' => 'Application\'s users',
|
||||
'directories' => 'Directories',
|
||||
'users' => 'Users'
|
||||
'documents' => 'Documents',
|
||||
'users' => 'Users',
|
||||
'settings' => 'Settings',
|
||||
'tags' => 'Tags'
|
||||
]
|
||||
];
|
||||
|
|
|
|||
|
|
@ -3,7 +3,11 @@
|
|||
return [
|
||||
'classes' => [
|
||||
'dashboard' => 'Tableau de bord',
|
||||
'app_users' => 'Application utilisateurs',
|
||||
'directories' => 'Dossiers',
|
||||
'users' => 'Utilisateurs'
|
||||
'documents' => 'Documents',
|
||||
'users' => 'Utilisateurs',
|
||||
'settings' => 'Paramètres',
|
||||
'tags' => 'Étiquettes'
|
||||
]
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,5 +1,14 @@
|
|||
.need-word-counter {
|
||||
position: relative;
|
||||
border-radius: 0 0 0.375rem 0.375rem;
|
||||
|
||||
&-password {
|
||||
border-radius: 0 0 0 0.375rem !important;
|
||||
|
||||
&-btn {
|
||||
border-radius: 0 0 0.375rem !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.table {
|
||||
|
|
@ -22,3 +31,10 @@
|
|||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.preview-image-index {
|
||||
width: auto;
|
||||
max-width: 5rem;
|
||||
height: auto;
|
||||
max-height: 5rem;
|
||||
}
|
||||
|
|
|
|||
1
resources/sass/bo/vendor/_bootstrap.scss
vendored
1
resources/sass/bo/vendor/_bootstrap.scss
vendored
|
|
@ -37,6 +37,7 @@
|
|||
@import "~bootstrap/scss/close";
|
||||
@import "~bootstrap/scss/card";
|
||||
@import "~bootstrap/scss/breadcrumb";
|
||||
@import "~bootstrap/scss/progress";
|
||||
|
||||
// 6. Optionally include utilities API last to generate classes based on the Sass map in `_utilities.scss`
|
||||
@import "~bootstrap/scss/utilities/api";
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<Transition name="fade">
|
||||
<p
|
||||
v-if="message"
|
||||
class="message"
|
||||
class="text-danger mb-2"
|
||||
>
|
||||
{{ message }}
|
||||
</p>
|
||||
|
|
@ -76,6 +76,33 @@ export default defineComponent({
|
|||
components: {
|
||||
VueTagsInputCls,
|
||||
},
|
||||
data(): {
|
||||
name: string;
|
||||
item: string;
|
||||
selectedItems: CustomTag[];
|
||||
allItems: CustomTag[];
|
||||
placeholder: string;
|
||||
min: number;
|
||||
max: number | null;
|
||||
fieldName: string;
|
||||
waitingListObjects: VueTagsInput.IAddArgs[];
|
||||
allErrors: Record<string, string[]>;
|
||||
message: string | null;
|
||||
} {
|
||||
return {
|
||||
name: "",
|
||||
item: "",
|
||||
selectedItems: [],
|
||||
allItems: [],
|
||||
placeholder: "",
|
||||
min: 0,
|
||||
max: null,
|
||||
fieldName: "",
|
||||
waitingListObjects: [],
|
||||
allErrors: {},
|
||||
message: null,
|
||||
};
|
||||
},
|
||||
beforeMount() {
|
||||
if (!this.$attrs.json) {
|
||||
throw new Error("this component requires json");
|
||||
|
|
@ -96,36 +123,9 @@ export default defineComponent({
|
|||
// * Error parsing
|
||||
this.allErrors = data.errors;
|
||||
},
|
||||
data(): {
|
||||
name: string;
|
||||
item: string;
|
||||
selectedItems: Array<CustomTag>;
|
||||
allItems: Array<CustomTag>;
|
||||
placeholder: string;
|
||||
min: number;
|
||||
max: number | null;
|
||||
fieldName: string;
|
||||
waitingListObjects: Array<VueTagsInput.IAddArgs>;
|
||||
allErrors: Record<string, Array<string>>;
|
||||
message: string | null;
|
||||
} {
|
||||
return {
|
||||
name: "",
|
||||
item: "",
|
||||
selectedItems: [],
|
||||
allItems: [],
|
||||
placeholder: "",
|
||||
min: 0,
|
||||
max: null,
|
||||
fieldName: "",
|
||||
waitingListObjects: [],
|
||||
allErrors: {},
|
||||
message: null,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
parseTags(selectedItems: Array<CustomTag>): Array<CustomTag> {
|
||||
return selectedItems.map((item: CustomTag) => {
|
||||
parseTags(selectedItems: CustomTag[]): CustomTag[] {
|
||||
return selectedItems.map((item) => {
|
||||
item.text = String(item.label ?? item.name ?? item.title);
|
||||
item.slug = String(item.slug ?? slugify(item.text));
|
||||
return item;
|
||||
|
|
@ -153,12 +153,12 @@ export default defineComponent({
|
|||
* Delete item.
|
||||
* @param newItems
|
||||
*/
|
||||
changedTags(newItems: Array<CustomTag>): void {
|
||||
changedTags(newItems: CustomTag[]): void {
|
||||
// * Si on enleve un élement
|
||||
if (this.selectedItems.length > newItems.length) {
|
||||
// * On respecte la validation minimale
|
||||
if (newItems.length >= this.min) {
|
||||
this.selectedItems = newItems as Array<CustomTag>;
|
||||
this.selectedItems = newItems as CustomTag[];
|
||||
} else {
|
||||
// * Non respect de la validation minimale
|
||||
this.message = this.__("Nombre d'élements minimal atteint.");
|
||||
|
|
@ -169,7 +169,7 @@ export default defineComponent({
|
|||
}
|
||||
},
|
||||
tagExists(
|
||||
items: Array<CustomTag>,
|
||||
items: CustomTag[],
|
||||
tagToCheck: VueTagsInput.ITag
|
||||
): CustomTag | null {
|
||||
let customTag: CustomTag | null = null;
|
||||
|
|
@ -249,10 +249,6 @@ export default defineComponent({
|
|||
}
|
||||
}
|
||||
|
||||
.message {
|
||||
color: #ff0000;
|
||||
}
|
||||
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.5s ease;
|
||||
|
|
|
|||
309
resources/ts/bo/components/ColorPickerComponent.vue
Normal file
309
resources/ts/bo/components/ColorPickerComponent.vue
Normal file
|
|
@ -0,0 +1,309 @@
|
|||
<template>
|
||||
<div
|
||||
:class="`input-group color-picker-component ${
|
||||
intRgbaMode ? '' : 'no-transparent'
|
||||
}`"
|
||||
ref="colorpicker"
|
||||
>
|
||||
<div
|
||||
:id="`colorPickerFakeInput${intId}`"
|
||||
class="d-inline-block"
|
||||
ref="colorPickerFakeInput"
|
||||
>
|
||||
<div class="d-inline-block">
|
||||
<b v-if="!intSimple">
|
||||
<label
|
||||
v-if="intLabel.length"
|
||||
:for="intId ? intId : ''"
|
||||
class="form-label"
|
||||
>
|
||||
{{ intLabel }}
|
||||
</label>
|
||||
</b>
|
||||
<input
|
||||
:id="intId"
|
||||
type="color"
|
||||
class="form-control form-control-color"
|
||||
:value="intInternalHex"
|
||||
:title="intTitle"
|
||||
data-bs-toggle="tooltip"
|
||||
:aria-describedby="intAriaDescribedby"
|
||||
@click.prevent="togglePicker()"
|
||||
:disabled="intDisabled ? true : false"
|
||||
>
|
||||
<input
|
||||
:name="intName"
|
||||
type="text"
|
||||
class="d-none"
|
||||
:value="intValue"
|
||||
>
|
||||
<div
|
||||
:class="`chrome-picker position-absolute left-0 ${
|
||||
intDisplayPicker ? 'd-inline-block' : 'd-none'
|
||||
}`"
|
||||
>
|
||||
<chrome-picker
|
||||
ref="picker"
|
||||
v-model="intInternalValue"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
:class="`form-check form-switch m-0 ${
|
||||
intNullable ? 'd-inline-block' : 'd-none'
|
||||
}`"
|
||||
>
|
||||
<input
|
||||
ref="nullableInput"
|
||||
:id="`checkbox${intId}`"
|
||||
class="form-check-input me-1"
|
||||
type="checkbox"
|
||||
data-bs-toggle="tooltip"
|
||||
:title="__('Rendre la couleur transparente ?')"
|
||||
:checked="intValue === null ? true : false"
|
||||
@change="updateValue"
|
||||
role="button"
|
||||
>
|
||||
<label
|
||||
class="form-check-label user-select-none"
|
||||
:for="`checkbox${intId}`"
|
||||
role="button"
|
||||
>
|
||||
{{ __("Transparent ?") }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from "vue";
|
||||
// @ts-ignore
|
||||
import { Chrome } from "@ckpack/vue-color";
|
||||
import trans from "./../../modules/trans";
|
||||
import Tooltip from "bootstrap/js/dist/tooltip";
|
||||
|
||||
export default defineComponent({
|
||||
inheritAttrs: false,
|
||||
mixins: [trans],
|
||||
components: {
|
||||
"chrome-picker": Chrome,
|
||||
},
|
||||
props: {
|
||||
id: {
|
||||
type: String as PropType<string | null>,
|
||||
default: null,
|
||||
},
|
||||
name: {
|
||||
type: String as PropType<string | undefined>,
|
||||
default: undefined,
|
||||
},
|
||||
value: {
|
||||
type: String as PropType<string | null>,
|
||||
default: null,
|
||||
},
|
||||
title: {
|
||||
type: String as PropType<string | undefined>,
|
||||
default: undefined,
|
||||
},
|
||||
label: {
|
||||
type: String as PropType<string | undefined>,
|
||||
default: undefined,
|
||||
},
|
||||
nullable: {
|
||||
type: Boolean as PropType<boolean | undefined>,
|
||||
default: undefined,
|
||||
},
|
||||
rgbaMode: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: false,
|
||||
},
|
||||
ariaDescribedby: {
|
||||
type: String as PropType<string | undefined>,
|
||||
default: undefined,
|
||||
},
|
||||
simple: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data(): {
|
||||
intId: string;
|
||||
intName: string;
|
||||
intValue: string | null;
|
||||
intTitle: string;
|
||||
intLabel: string;
|
||||
intNullable: boolean;
|
||||
intRgbaMode: boolean;
|
||||
intAriaDescribedby: string;
|
||||
intInternalValue:
|
||||
| string
|
||||
| { hex: string; rgba: { a: string; b: string; g: string; r: string } };
|
||||
intInternalHex: string;
|
||||
intDisplayPicker: boolean;
|
||||
intNullableInput: HTMLInputElement | null;
|
||||
intColorPickerFakeInput: HTMLElement | null;
|
||||
intPicker: typeof Chrome | null;
|
||||
intSimple: boolean;
|
||||
intDisabled: boolean;
|
||||
} {
|
||||
return {
|
||||
intId: "",
|
||||
intName: "",
|
||||
intValue: "",
|
||||
intTitle: "",
|
||||
intLabel: "",
|
||||
intNullable: false,
|
||||
intRgbaMode: false,
|
||||
intAriaDescribedby: "",
|
||||
intInternalValue: "",
|
||||
intInternalHex: "",
|
||||
intDisplayPicker: false,
|
||||
intNullableInput: null,
|
||||
intColorPickerFakeInput: null,
|
||||
intPicker: null,
|
||||
intSimple: false,
|
||||
intDisabled: false,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.intNullableInput = this.$refs.nullableInput as HTMLInputElement;
|
||||
this.intColorPickerFakeInput = this.$refs
|
||||
.colorPickerFakeInput as HTMLElement;
|
||||
this.intPicker = this.$refs.picker as typeof Chrome;
|
||||
|
||||
const json = String(this.$attrs.json ?? "{}"),
|
||||
data = JSON.parse(json);
|
||||
this.intId = this.id ? this.id : data.id;
|
||||
this.intName = this.name ? this.name : data.name;
|
||||
this.intValue = this.value ? this.value : data.value;
|
||||
this.intTitle = this.title ? this.title : data.title;
|
||||
this.intLabel = this.label ? this.label : data.label;
|
||||
this.intAriaDescribedby = this.ariaDescribedby
|
||||
? this.ariaDescribedby
|
||||
: data.ariaDescribedby;
|
||||
this.intNullable = this.nullable
|
||||
? this.nullable
|
||||
: data.nullable
|
||||
? true
|
||||
: false;
|
||||
this.intRgbaMode = this.rgbaMode
|
||||
? this.rgbaMode
|
||||
: data.rgbaMode
|
||||
? true
|
||||
: false;
|
||||
this.intSimple = this.simple ? this.simple : data.simple;
|
||||
this.intDisabled = this.disabled ? this.disabled : data.disabled;
|
||||
|
||||
if (!this.intNullable) {
|
||||
this.intInternalValue = this.intValue
|
||||
? this.intValue
|
||||
: this.intRgbaMode
|
||||
? "rgba(0,0,0,0)"
|
||||
: "#000000";
|
||||
} else {
|
||||
this.intInternalValue = this.intValue ?? "";
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
this.intInternalHex = this.intPicker?.$data.val.hex;
|
||||
this.initTooltips();
|
||||
});
|
||||
},
|
||||
watch: {
|
||||
intInternalValue() {
|
||||
this.updateValue();
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
updateValue() {
|
||||
if (this.intNullableInput?.checked) {
|
||||
this.hidePicker();
|
||||
this.intValue = "";
|
||||
this.intInternalHex = "";
|
||||
return;
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
this.intInternalHex = this.intPicker?.$data.val.hex;
|
||||
});
|
||||
if (typeof this.intInternalValue === "string") {
|
||||
this.intValue = this.intInternalValue;
|
||||
return;
|
||||
}
|
||||
const isNull = this.intNullableInput?.checked ? true : false;
|
||||
if (this.intRgbaMode) {
|
||||
const rgba = this.intInternalValue?.rgba;
|
||||
this.intValue =
|
||||
!isNull && rgba
|
||||
? `rgba(${rgba.r}, ${rgba.g}, ${rgba.b}, ${rgba.a})`
|
||||
: this.intNullable
|
||||
? null
|
||||
: "";
|
||||
return;
|
||||
}
|
||||
this.intValue =
|
||||
!isNull && this.intInternalValue
|
||||
? this.intInternalValue.hex
|
||||
: this.intNullable
|
||||
? null
|
||||
: "";
|
||||
},
|
||||
showPicker() {
|
||||
document.addEventListener("click", this.documentClick);
|
||||
this.intDisplayPicker = true;
|
||||
const input = this.intNullableInput;
|
||||
if (!input) {
|
||||
throw new Error("input missing");
|
||||
}
|
||||
input.checked = false;
|
||||
},
|
||||
hidePicker() {
|
||||
document.removeEventListener("click", this.documentClick);
|
||||
this.intDisplayPicker = false;
|
||||
},
|
||||
togglePicker() {
|
||||
this.intDisplayPicker ? this.hidePicker() : this.showPicker();
|
||||
},
|
||||
documentClick(e: Event) {
|
||||
const colorpickerComponent = this.intColorPickerFakeInput,
|
||||
target = e.target;
|
||||
if (
|
||||
colorpickerComponent !== target &&
|
||||
colorpickerComponent &&
|
||||
(target as HTMLElement).closest(`#colorPickerFakeInput${this.intId}`)
|
||||
?.id !== colorpickerComponent.id
|
||||
) {
|
||||
this.hidePicker();
|
||||
}
|
||||
},
|
||||
initTooltips() {
|
||||
// * INIT Bootstrap tooltips
|
||||
let tooltipTriggerList = [].slice.call(
|
||||
document.querySelectorAll(
|
||||
".color-picker-component [data-bs-toggle=\"tooltip\"]"
|
||||
)
|
||||
);
|
||||
tooltipTriggerList.map(function (tooltipTriggerEl) {
|
||||
return new Tooltip(tooltipTriggerEl, {
|
||||
delay: { show: 1000, hide: 300 },
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.color-picker-component {
|
||||
position: relative;
|
||||
.chrome-picker {
|
||||
z-index: 9;
|
||||
}
|
||||
&.no-transparent .vc-chrome-alpha-wrap {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
409
resources/ts/bo/components/CrudDocumentInputComponent.vue
Normal file
409
resources/ts/bo/components/CrudDocumentInputComponent.vue
Normal file
|
|
@ -0,0 +1,409 @@
|
|||
<template>
|
||||
<div :class="`crud-document-input-${intId} row`">
|
||||
<!-- FILE LANGUAGE -->
|
||||
<div class="col-12 col-md-6 mb-3">
|
||||
<label
|
||||
for="selectLanguageDocument"
|
||||
class="col-form-label"
|
||||
>
|
||||
<b>{{ __("Langue du document") }} *</b>
|
||||
</label>
|
||||
<select
|
||||
id="selectLanguageDocument"
|
||||
name="lang"
|
||||
class="form-select"
|
||||
aria-label="Select language for the document"
|
||||
@click="languageDocument($event)"
|
||||
role="button"
|
||||
>
|
||||
<option
|
||||
v-for="(language, languageIndex) in intLanguages"
|
||||
:key="languageIndex"
|
||||
:value="language.value"
|
||||
:selected="Number(intModel?.lang) === language.value ? true : false"
|
||||
>
|
||||
{{ language.label }}
|
||||
</option>
|
||||
</select>
|
||||
<small class="form-text text-muted">
|
||||
{{ __("Choisissez une langue parmis la liste.") }}
|
||||
</small>
|
||||
</div>
|
||||
<!-- INPUT NAME -->
|
||||
<div class="col-12 col-md-6 mb-3">
|
||||
<label
|
||||
for="documentTitle"
|
||||
class="col-form-label"
|
||||
>
|
||||
<b>{{ __("Titre du document") }} *</b>
|
||||
</label>
|
||||
<WordCounter
|
||||
id="documentTitle"
|
||||
class="ck-word-count"
|
||||
/>
|
||||
<input
|
||||
id="documentTitle"
|
||||
type="text"
|
||||
name="title"
|
||||
class="form-control need-word-counter"
|
||||
:placeholder="__('Un beau titre de document')"
|
||||
:value="intModel?.title"
|
||||
>
|
||||
<small class="form-text text-muted">
|
||||
{{ __("Doit posséder 3 caractères minimum, 255 maximum.") }}
|
||||
</small>
|
||||
</div>
|
||||
<!-- SELECT DIRECTORY -->
|
||||
<div
|
||||
v-if="intIsDocFR"
|
||||
class="col-12 col-md-6 mb-3"
|
||||
>
|
||||
<label
|
||||
for="directoryDirectoryId"
|
||||
class="col-form-label"
|
||||
>
|
||||
<b>{{ __("Dossier à associer") }} *</b>
|
||||
</label>
|
||||
<select
|
||||
id="directoryDirectoryId"
|
||||
class="form-select"
|
||||
name="directory_id"
|
||||
aria-describedby="directoryDirectoryIdHelp"
|
||||
role="button"
|
||||
>
|
||||
<option
|
||||
v-for="(directory, directoryIndex) in intDirectories"
|
||||
:key="directoryIndex"
|
||||
:value="directory.id"
|
||||
:selected="intModel?.directory_id === directory.id ? true : false"
|
||||
>
|
||||
{{ directory.name }}
|
||||
</option>
|
||||
</select>
|
||||
<small class="form-text text-muted">
|
||||
{{ __("Choisissez un dossier à associer parmis la liste.") }}
|
||||
</small>
|
||||
</div>
|
||||
<!-- SELECT FRENCH DOCUMENT -->
|
||||
<div
|
||||
v-else
|
||||
class="col-12 col-md-6 mb-3"
|
||||
>
|
||||
<label
|
||||
for="documentParentId"
|
||||
class="col-form-label"
|
||||
>
|
||||
<b>{{ __("Document français à associer") }} *</b>
|
||||
</label>
|
||||
<select
|
||||
v-if="intDocuments.length > 0"
|
||||
id="documentParentId"
|
||||
class="form-select"
|
||||
name="document_id"
|
||||
aria-describedby="documentParentIdHelp"
|
||||
role="button"
|
||||
>
|
||||
<option
|
||||
v-for="(document, documentIndex) in intDocuments"
|
||||
:key="documentIndex"
|
||||
:value="document.id"
|
||||
:selected="intModel?.document_id === document.id ? true : false"
|
||||
>
|
||||
{{ document.title }}
|
||||
</option>
|
||||
</select>
|
||||
<p
|
||||
v-else
|
||||
class="text-muted"
|
||||
>
|
||||
{{ __("Aucun documents français n'est disponible") }}
|
||||
</p>
|
||||
<small class="form-text text-muted">
|
||||
{{ __("Choisissez un document français à associer parmis la liste.") }}
|
||||
</small>
|
||||
</div>
|
||||
<!-- CHOOSE FILE -->
|
||||
<div class="col-12 col-md-6 mb-3">
|
||||
<label
|
||||
for="documentInputFile"
|
||||
class="col-form-label"
|
||||
>
|
||||
<b>{{ __("Document à ajouter") }} *</b>
|
||||
</label>
|
||||
<div :class="intIsUploading ? 'd-none' : ''">
|
||||
<div class="input-group">
|
||||
<button
|
||||
class="btn btn-outline-secondary"
|
||||
type="button"
|
||||
data-bs="tooltip"
|
||||
:title="__('Modifier le document source')"
|
||||
>
|
||||
<FontAwesomeIcon icon="fa-solid fa-folder-open" />
|
||||
</button>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
role="button"
|
||||
ref="inputFile"
|
||||
:value="
|
||||
intDocumentDatas.path
|
||||
? intDocumentDatas.path
|
||||
: 'Sélectionnez un fichier'
|
||||
"
|
||||
data-bs="tooltip"
|
||||
:title="__('Modifier le document source')"
|
||||
readonly
|
||||
>
|
||||
</div>
|
||||
<div v-if="intMessage">
|
||||
<p
|
||||
class="m-0"
|
||||
:class="intSuccess ? 'text-success' : 'text-danger'"
|
||||
>
|
||||
{{ intMessage }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="progress"
|
||||
:class="!intIsUploading ? 'd-none' : ''"
|
||||
>
|
||||
<div
|
||||
class="progress-bar progress-bar-striped progress-bar-animated"
|
||||
ref="progressBar"
|
||||
role="progressbar"
|
||||
aria-label="Progress bar"
|
||||
aria-valuenow="0"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100"
|
||||
>
|
||||
{{ intFilePercent }}%
|
||||
</div>
|
||||
</div>
|
||||
<small class="form-text text-muted">
|
||||
{{ __("Doit être au format .pdf ou .mp4.") }}
|
||||
</small>
|
||||
</div>
|
||||
<input
|
||||
class="d-none"
|
||||
name="path"
|
||||
:value="intDocumentDatas.path"
|
||||
>
|
||||
<input
|
||||
class="d-none"
|
||||
name="uuid"
|
||||
:value="intDocumentDatas.uuid"
|
||||
>
|
||||
<input
|
||||
class="d-none"
|
||||
name="type"
|
||||
:value="intDocumentDatas.type"
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
import { Tooltip } from "bootstrap";
|
||||
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
||||
import WordCounter from "./WordCounterComponent.vue";
|
||||
import route from "./../../modules/route";
|
||||
import trans from "./../../modules/trans";
|
||||
import Resumable from "resumablejs";
|
||||
|
||||
export default defineComponent({
|
||||
name: "AddDocumentComponent",
|
||||
mixins: [route, trans],
|
||||
components: {
|
||||
WordCounter,
|
||||
FontAwesomeIcon,
|
||||
},
|
||||
data(): {
|
||||
intId: string;
|
||||
intMessage: string;
|
||||
intSuccess: boolean;
|
||||
intIsUploading: boolean;
|
||||
intFilePercent: number | null;
|
||||
intProgressbar: HTMLDivElement | null;
|
||||
intLanguages: {
|
||||
value: number;
|
||||
label: string;
|
||||
}[];
|
||||
intDocuments: { id: number; title: string }[];
|
||||
intDirectories: LaravelModel[];
|
||||
intIsDocFR: boolean;
|
||||
intModel: LaravelModel | null;
|
||||
intR: Resumable | null;
|
||||
intDocumentDatas: {
|
||||
directory_id: number;
|
||||
document_id: number | null;
|
||||
uuid: string;
|
||||
title: string;
|
||||
type: string;
|
||||
path: string;
|
||||
lang: string;
|
||||
published: boolean;
|
||||
};
|
||||
intCsrf: string;
|
||||
intTooltipList: HTMLButtonElement[];
|
||||
} {
|
||||
return {
|
||||
intId: "",
|
||||
intR: null,
|
||||
intMessage: "",
|
||||
intSuccess: false,
|
||||
intIsUploading: false,
|
||||
intFilePercent: 0,
|
||||
intProgressbar: null,
|
||||
intDocuments: [],
|
||||
intDirectories: [],
|
||||
intLanguages: [],
|
||||
intIsDocFR: true,
|
||||
intModel: null,
|
||||
intDocumentDatas: {
|
||||
directory_id: 0,
|
||||
document_id: null,
|
||||
uuid: "",
|
||||
title: "",
|
||||
type: "",
|
||||
path: "",
|
||||
lang: "",
|
||||
published: false,
|
||||
},
|
||||
intCsrf: "",
|
||||
intTooltipList: [],
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
const json = String(this.$attrs.json ?? "{}"),
|
||||
data = JSON.parse(json);
|
||||
this.intId = data.id;
|
||||
this.intLanguages = data.languages;
|
||||
this.intDocuments = data.documents;
|
||||
this.intDirectories = data.directories;
|
||||
this.intModel = data.model;
|
||||
if (this.intModel != null) {
|
||||
this.intDocumentDatas.path = String(this.intModel.path);
|
||||
this.intDocumentDatas.uuid = String(this.intModel.uuid);
|
||||
this.intDocumentDatas.type = String(this.intModel.type);
|
||||
this.intIsDocFR = this.intModel.lang === "1" ? true : false;
|
||||
}
|
||||
this.intCsrf = data.csrf;
|
||||
this.$nextTick(() => {
|
||||
this.initResumable();
|
||||
this.updateBootstrapTooltip();
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
languageDocument(e: Event) {
|
||||
const target = e.target as HTMLSelectElement;
|
||||
this.intIsDocFR =
|
||||
Number(target.value) === this.intLanguages[0].value ? true : false;
|
||||
},
|
||||
initResumable() {
|
||||
// Route.
|
||||
const uploadFileRoute = route.methods.route("bo.documents.upload");
|
||||
if (!uploadFileRoute) {
|
||||
throw new Error("Undefined route bo.documents.upload");
|
||||
}
|
||||
this.intR = new Resumable({
|
||||
chunkSize: 1024 * 1024, // 1MB
|
||||
simultaneousUploads: 1,
|
||||
maxFiles: 1,
|
||||
testChunks: false,
|
||||
target: uploadFileRoute,
|
||||
query: {
|
||||
_token: this.intCsrf,
|
||||
},
|
||||
});
|
||||
this.intR.on("fileAdded", this.fileAdded);
|
||||
this.intR.on("fileSuccess", this.fileSuccess);
|
||||
this.intR.on("fileError", this.fileError);
|
||||
this.intR.on("fileProgress", () => {
|
||||
if (this.intR !== null) {
|
||||
this.intFilePercent = Math.floor(this.intR.progress() * 100);
|
||||
if (this.intProgressbar !== null)
|
||||
this.intProgressbar.style.width = this.intFilePercent + "%";
|
||||
}
|
||||
});
|
||||
this.intR.assignBrowse(this.$refs.inputFile as Element, false);
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param file The file would like to store.
|
||||
*/
|
||||
fileAdded(file: any) {
|
||||
if (
|
||||
file.file.type !== "application/pdf" &&
|
||||
file.file.type !== "video/mp4"
|
||||
) {
|
||||
return !(this.intMessage = "Extension du fichier invalide.");
|
||||
}
|
||||
this.intIsUploading = true;
|
||||
setTimeout(() => {
|
||||
this.intProgressbar = this.$refs.progressBar as HTMLDivElement;
|
||||
this.intProgressbar.style.width = this.intFilePercent + "%";
|
||||
}, 10);
|
||||
this.closeBootstrapTooltip();
|
||||
if (this.intR !== null) this.intR.upload();
|
||||
},
|
||||
/**
|
||||
* Run method if the file is stored.
|
||||
* @param file The file stored.
|
||||
* @param message Set a new successful message.
|
||||
*/
|
||||
fileSuccess(file: any, message: string) {
|
||||
let result = JSON.parse(message);
|
||||
this.intDocumentDatas.path = file.fileName;
|
||||
this.intDocumentDatas.uuid = result.uid;
|
||||
if (file.file.type === "application/pdf") {
|
||||
this.intDocumentDatas.type = "pdf";
|
||||
} else {
|
||||
this.intDocumentDatas.type = "mp4";
|
||||
}
|
||||
this.intSuccess = true;
|
||||
this.intMessage = "Fichier chargé.";
|
||||
const intInputFileResult = this.$refs.progressBar as HTMLInputElement;
|
||||
intInputFileResult.value =
|
||||
this.intDocumentDatas.uuid + this.intDocumentDatas.type;
|
||||
setTimeout(() => {
|
||||
this.intIsUploading = false;
|
||||
this.$nextTick(() => {
|
||||
this.updateBootstrapTooltip();
|
||||
});
|
||||
}, 800);
|
||||
},
|
||||
/**
|
||||
* Set a new error message.
|
||||
*/
|
||||
fileError() {
|
||||
this.intMessage = "Une erreur est survenue, veuillez recharger la page.";
|
||||
},
|
||||
/**
|
||||
* Update Bootstrap tooltips.
|
||||
*/
|
||||
updateBootstrapTooltip() {
|
||||
let newTooltipList = [].slice.call(
|
||||
document.querySelectorAll(
|
||||
".crud-document-input-" + this.intId + " [data-bs=\"tooltip\"]"
|
||||
)
|
||||
) as HTMLButtonElement[];
|
||||
let tmp = newTooltipList.filter((x) => !this.intTooltipList.includes(x));
|
||||
tmp.map((tooltip) => {
|
||||
return new Tooltip(tooltip);
|
||||
});
|
||||
this.intTooltipList = newTooltipList;
|
||||
this.closeBootstrapTooltip();
|
||||
},
|
||||
/**
|
||||
* Close all Bootstrap tooltips.
|
||||
*/
|
||||
closeBootstrapTooltip() {
|
||||
this.intTooltipList.forEach((tooltip) => {
|
||||
tooltip.blur();
|
||||
Tooltip.getInstance(tooltip)?.hide();
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
@ -1,5 +1,21 @@
|
|||
<template>
|
||||
<div :class="`directories-${intId}`">
|
||||
<div :class="`directories-${intId} position-relative`">
|
||||
<Transition name="fade">
|
||||
<p
|
||||
v-if="intMessage"
|
||||
class="d-flex align-items-center text-danger mb-2"
|
||||
>
|
||||
<span>{{ Object.values(intMessage)[0] }} </span>
|
||||
<a
|
||||
class="btn btn-sm btn-primary"
|
||||
:href="getEditButtonRoute(Number(Object.keys(intMessage)[0]))"
|
||||
:title="__('Modifier ce paramètre')"
|
||||
data-bs="tooltip"
|
||||
>
|
||||
<FontAwesomeIcon icon="fa-solid fa-pencil" />
|
||||
</a>
|
||||
</p>
|
||||
</Transition>
|
||||
<vue-nestable
|
||||
:value="intDirectories"
|
||||
@change="updateOrderDirectories"
|
||||
|
|
@ -47,16 +63,10 @@
|
|||
<FontAwesomeIcon icon="fa-solid fa-arrow-up" />
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
v-if="!item.is_system"
|
||||
class="btn btn-sm border-0 disabled opacity-100"
|
||||
>
|
||||
<button class="btn btn-sm border-0 disabled opacity-100">
|
||||
<FontAwesomeIcon icon="fa-solid fa-grip-vertical" />
|
||||
</button>
|
||||
<p
|
||||
class="m-0"
|
||||
:class="item.is_system ? 'ms-2' : ''"
|
||||
>
|
||||
<p class="m-0">
|
||||
{{ item.name["fr" as keyof typeof item.name] }}
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -65,33 +75,24 @@
|
|||
class="d-flex justify-content-end align-items-center"
|
||||
>
|
||||
<div class="input-group">
|
||||
<form
|
||||
:action="getAuthorizationDirectoryRoute(item.id)"
|
||||
method="POST"
|
||||
class="d-inline btn-group"
|
||||
<button
|
||||
v-if="item.needAuthorization"
|
||||
@click="updateAuthorization(item)"
|
||||
class="btn btn-sm btn-warning"
|
||||
:title="__('L\'accès à ce dossier est restreint')"
|
||||
data-bs="tooltip"
|
||||
>
|
||||
<button
|
||||
v-if="item.needAuthorization"
|
||||
class="btn btn-sm btn-warning"
|
||||
:title="__('L\'accès à ce dossier est restreint')"
|
||||
data-bs="tooltip"
|
||||
>
|
||||
<FontAwesomeIcon icon="fa-solid fa-lock" />
|
||||
</button>
|
||||
<button
|
||||
v-else
|
||||
class="btn btn-sm btn-success"
|
||||
:title="__('L\'accès à ce dossier est tout public')"
|
||||
data-bs="tooltip"
|
||||
>
|
||||
<FontAwesomeIcon icon="fa-solid fa-lock-open" />
|
||||
</button>
|
||||
<input
|
||||
type="hidden"
|
||||
name="_token"
|
||||
:value="intCsrf"
|
||||
>
|
||||
</form>
|
||||
<FontAwesomeIcon icon="fa-solid fa-lock" />
|
||||
</button>
|
||||
<button
|
||||
v-else
|
||||
@click="updateAuthorization(item)"
|
||||
class="btn btn-sm btn-success"
|
||||
:title="__('L\'accès à ce dossier est tout public')"
|
||||
data-bs="tooltip"
|
||||
>
|
||||
<FontAwesomeIcon icon="fa-solid fa-lock-open" />
|
||||
</button>
|
||||
<a
|
||||
:href="getEditDirectoryRoute(item.id)"
|
||||
class="btn btn-sm btn-primary"
|
||||
|
|
@ -100,29 +101,33 @@
|
|||
>
|
||||
<FontAwesomeIcon icon="fa-solid fa-pencil" />
|
||||
</a>
|
||||
<form
|
||||
:action="getDestroyDirectoryRoute(item.id)"
|
||||
method="POST"
|
||||
class="d-inline btn-group confirmDeleteJS"
|
||||
<button
|
||||
@click="deleteDirectory($event, item)"
|
||||
class="btn btn-sm btn-danger confirmDelete"
|
||||
:title="__('Supprimer le Dossier')"
|
||||
data-bs="tooltip"
|
||||
ref="confirmDelete"
|
||||
>
|
||||
<input
|
||||
type="hidden"
|
||||
name="_token"
|
||||
:value="intCsrf"
|
||||
>
|
||||
<button
|
||||
class="btn btn-sm btn-danger"
|
||||
:title="__('Supprimer le Dossier')"
|
||||
data-bs="tooltip"
|
||||
>
|
||||
<FontAwesomeIcon icon="fa-solid fa-trash" />
|
||||
</button>
|
||||
</form>
|
||||
<FontAwesomeIcon icon="fa-solid fa-trash" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</vue-nestable-handle>
|
||||
</template>
|
||||
</vue-nestable>
|
||||
<div
|
||||
v-if="intLoading"
|
||||
class="loading position-absolute top-0 start-0 d-flex justify-content-center align-items-center rounded-1 w-100 h-100"
|
||||
>
|
||||
<div
|
||||
class="spinner-border text-secondary"
|
||||
role="status"
|
||||
>
|
||||
<span class="visually-hidden">
|
||||
{{ __("form.image_input_viewer_loading") }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -133,11 +138,12 @@ import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
|||
import { Tooltip } from "bootstrap";
|
||||
import route from "../../modules/route";
|
||||
import trans from "../../modules/trans";
|
||||
import sweetalertConfirm from "../../modules/sweetalertConfirm";
|
||||
|
||||
type DirectoryObject = {
|
||||
id: number;
|
||||
name: string;
|
||||
parent_id: number | null;
|
||||
directory_id: number | null;
|
||||
text: string | null;
|
||||
order: number;
|
||||
needAuthorization: number;
|
||||
|
|
@ -153,13 +159,15 @@ export default defineComponent({
|
|||
VueNestableHandle,
|
||||
FontAwesomeIcon,
|
||||
},
|
||||
mixins: [route, trans],
|
||||
mixins: [route, trans, sweetalertConfirm],
|
||||
data(): {
|
||||
intCsrf: string | null | undefined;
|
||||
intId: String;
|
||||
intDirectories: DirectoryObject[];
|
||||
intMessage: object | object[] | null;
|
||||
intDisplay: boolean[];
|
||||
tooltipList: HTMLButtonElement[];
|
||||
intLoading: boolean;
|
||||
intTooltipList: HTMLButtonElement[];
|
||||
} {
|
||||
return {
|
||||
intCsrf: document
|
||||
|
|
@ -167,8 +175,10 @@ export default defineComponent({
|
|||
?.getAttribute("content"),
|
||||
intId: "",
|
||||
intDirectories: [],
|
||||
intMessage: null,
|
||||
intDisplay: [],
|
||||
tooltipList: [],
|
||||
intLoading: false,
|
||||
intTooltipList: [],
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
|
|
@ -189,7 +199,7 @@ export default defineComponent({
|
|||
* @param parent
|
||||
*/
|
||||
recursiveDir(list: DirectoryObject[], parent: number | null = null) {
|
||||
let root = list.filter((e: DirectoryObject) => e.parent_id == parent);
|
||||
let root = list.filter((e: DirectoryObject) => e.directory_id == parent);
|
||||
for (let i = 0; i < root.length; i++) {
|
||||
let childs = this.recursiveDir(list, root[i].id);
|
||||
root[i].children = [];
|
||||
|
|
@ -253,7 +263,7 @@ export default defineComponent({
|
|||
assignOrder(children: DirectoryObject[], parentId: number | null) {
|
||||
children.forEach((element, index) => {
|
||||
element.order = index;
|
||||
element.parent_id = parentId;
|
||||
element.directory_id = parentId;
|
||||
if (element.children && element.children.length) {
|
||||
element.children = this.assignOrder(element.children, element.id);
|
||||
} else {
|
||||
|
|
@ -277,30 +287,16 @@ export default defineComponent({
|
|||
return route;
|
||||
},
|
||||
/**
|
||||
* Return destroy route for a directory.
|
||||
* Return edit route for a directory.
|
||||
* @param id
|
||||
* @return string
|
||||
*/
|
||||
getDestroyDirectoryRoute(id: number): string {
|
||||
const route = this.route("bo.directories.destroy", {
|
||||
getEditButtonRoute(id: number): string {
|
||||
const route = this.route("bo.settings.edit", {
|
||||
ID: id,
|
||||
});
|
||||
if (!route) {
|
||||
throw new Error("Undefined route bo.directories.destroy");
|
||||
}
|
||||
return route;
|
||||
},
|
||||
/**
|
||||
* Return authorization route for a directory.
|
||||
* @param id
|
||||
* @return string
|
||||
*/
|
||||
getAuthorizationDirectoryRoute(id: number): string {
|
||||
const route = this.route("bo.directories.need-authorization", {
|
||||
ID: id,
|
||||
});
|
||||
if (!route) {
|
||||
throw new Error("Undefined route bo.directories.need-authorization");
|
||||
throw new Error("Undefined route bo.settings.edit");
|
||||
}
|
||||
return route;
|
||||
},
|
||||
|
|
@ -317,6 +313,101 @@ export default defineComponent({
|
|||
this.updateBootstrapTooltip();
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Update authorization for a specific directory.
|
||||
* @param LaravelModel model
|
||||
*/
|
||||
updateAuthorization(model: LaravelModel) {
|
||||
this.intLoading = true;
|
||||
const route = this.route("bo.directories.need-authorization", {
|
||||
ID: model.id as number,
|
||||
});
|
||||
if (!route) {
|
||||
throw new Error("Undefined route bo.directories.need-authorization");
|
||||
}
|
||||
window.axios
|
||||
.post(route, { id: model.id })
|
||||
.then((reponse) => {
|
||||
this.intDirectories.forEach((directory, directoryindex) => {
|
||||
if (directory.id === reponse.data.id) {
|
||||
this.intDirectories[directoryindex] = reponse.data;
|
||||
}
|
||||
});
|
||||
this.intLoading = false;
|
||||
this.$nextTick(() => {
|
||||
this.updateBootstrapTooltip();
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error.response) {
|
||||
console.error(error.response.data);
|
||||
} else if (error.request) {
|
||||
console.error(error.request);
|
||||
} else {
|
||||
// Something happened in setting up the request that triggered an Error.
|
||||
console.error("Error", error.message);
|
||||
}
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Update authorization for a specific directory.
|
||||
* @param LaravelModel model
|
||||
*/
|
||||
deleteDirectory(e: Event, model: LaravelModel) {
|
||||
const btnConfirmDelete = this.$refs.confirmDelete as HTMLButtonElement;
|
||||
(async () => {
|
||||
const promise = new Promise((resolve: (value: boolean) => void) => {
|
||||
sweetalertConfirm.methods.confirm(
|
||||
"Confirmez",
|
||||
"Êtes vous sure ?",
|
||||
btnConfirmDelete,
|
||||
function (response) {
|
||||
resolve(response.isConfirmed);
|
||||
},
|
||||
{ icon: "warning" }
|
||||
);
|
||||
return false;
|
||||
});
|
||||
if (await promise) {
|
||||
this.intLoading = true;
|
||||
const route = this.route("bo.directories.destroy", {
|
||||
ID: model.id as number,
|
||||
});
|
||||
if (!route) {
|
||||
throw new Error("Undefined route bo.directories.destroy");
|
||||
}
|
||||
window.axios
|
||||
.post(route, { id: model.id, _method: "DELETE" })
|
||||
.then((reponse) => {
|
||||
if (reponse.data[0] !== undefined) {
|
||||
this.intDirectories = this.recursiveDir(reponse.data);
|
||||
} else {
|
||||
this.intMessage = reponse.data;
|
||||
setTimeout(() => {
|
||||
this.intMessage = null;
|
||||
this.$nextTick(() => {
|
||||
this.updateBootstrapTooltip();
|
||||
});
|
||||
}, 6000);
|
||||
}
|
||||
this.intLoading = false;
|
||||
this.$nextTick(() => {
|
||||
this.updateBootstrapTooltip();
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error.response) {
|
||||
console.error(error.response.data);
|
||||
} else if (error.request) {
|
||||
console.error(error.request);
|
||||
} else {
|
||||
// Something happened in setting up the request that triggered an Error.
|
||||
console.error("Error", error.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
})();
|
||||
},
|
||||
/**
|
||||
* Update Bootstrap tooltips.
|
||||
*/
|
||||
|
|
@ -326,18 +417,18 @@ export default defineComponent({
|
|||
".directories-" + this.intId + " [data-bs=\"tooltip\"]"
|
||||
)
|
||||
) as HTMLButtonElement[];
|
||||
let tmp = newTooltipList.filter((x) => !this.tooltipList.includes(x));
|
||||
let tmp = newTooltipList.filter((x) => !this.intTooltipList.includes(x));
|
||||
tmp.map((tooltip) => {
|
||||
return new Tooltip(tooltip);
|
||||
});
|
||||
this.closeBootstrapTooltip();
|
||||
this.tooltipList = newTooltipList;
|
||||
this.intTooltipList = newTooltipList;
|
||||
},
|
||||
/**
|
||||
* Close all Bootstrap tooltips.
|
||||
*/
|
||||
closeBootstrapTooltip() {
|
||||
this.tooltipList.forEach((tooltip) => {
|
||||
this.intTooltipList.forEach((tooltip) => {
|
||||
tooltip.blur();
|
||||
Tooltip.getInstance(tooltip)?.hide();
|
||||
});
|
||||
|
|
@ -345,3 +436,19 @@ export default defineComponent({
|
|||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.loading {
|
||||
background-color: rgba(0, 0, 0, 0.15);
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.5s ease;
|
||||
}
|
||||
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,108 +1,108 @@
|
|||
<template>
|
||||
<WordCounter
|
||||
:id="intId"
|
||||
class="ck-word-count"
|
||||
/>
|
||||
<textarea
|
||||
ref="textarea"
|
||||
:id="intId"
|
||||
:class="`input-text-auto-component need-word-counter ${intClass}`"
|
||||
:name="intName"
|
||||
:aria-describedby="intAriaDescribedby"
|
||||
:placeholder="intPlaceholder"
|
||||
:required="intRequired"
|
||||
@input="onInput"
|
||||
@blur="onBlur"
|
||||
v-model="intValue"
|
||||
/>
|
||||
</template>
|
||||
<WordCounter
|
||||
:id="intId"
|
||||
class="ck-word-count"
|
||||
/>
|
||||
<textarea
|
||||
ref="textarea"
|
||||
:id="intId"
|
||||
:class="`input-text-auto-component need-word-counter ${intClass}`"
|
||||
:name="intName"
|
||||
:aria-describedby="intAriaDescribedby"
|
||||
:placeholder="intPlaceholder"
|
||||
:required="intRequired"
|
||||
@input="onInput"
|
||||
@blur="onBlur"
|
||||
v-model="intValue"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from "vue";
|
||||
import trans from "../../modules/trans";
|
||||
import WordCounter from "./WordCounterComponent.vue";
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from "vue";
|
||||
import trans from "../../modules/trans";
|
||||
import WordCounter from "./WordCounterComponent.vue";
|
||||
|
||||
const textarea = ref<HTMLTextAreaElement | null>(null);
|
||||
const textarea = ref<HTMLTextAreaElement | null>(null);
|
||||
|
||||
export default defineComponent({
|
||||
inheritAttrs: false,
|
||||
mixins: [trans],
|
||||
components: {
|
||||
WordCounter,
|
||||
},
|
||||
setup() {
|
||||
return { textarea };
|
||||
},
|
||||
beforeMount() {
|
||||
if (!this.$attrs.json) {
|
||||
throw new Error("this component requires json");
|
||||
export default defineComponent({
|
||||
inheritAttrs: false,
|
||||
mixins: [trans],
|
||||
components: {
|
||||
WordCounter,
|
||||
},
|
||||
setup() {
|
||||
return { textarea };
|
||||
},
|
||||
beforeMount() {
|
||||
if (!this.$attrs.json) {
|
||||
throw new Error("this component requires json");
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
const json = String(this.$attrs.json ?? "{}"),
|
||||
data = JSON.parse(json);
|
||||
this.intId = String(data.id);
|
||||
this.intClass = String(data.class);
|
||||
this.intName = String(data.name);
|
||||
this.intPlaceholder = String(data.placeholder);
|
||||
this.intAriaDescribedby = String(data.ariaDescribedby);
|
||||
this.intValue = String(data.value);
|
||||
if (data.required) {
|
||||
this.intRequired = true;
|
||||
}
|
||||
if (this.textarea) {
|
||||
// Default input init
|
||||
this.textarea.setAttribute("style", "height: 1rem;overflow-y: hidden;");
|
||||
}
|
||||
// * Init input
|
||||
this.$nextTick(this.onInput);
|
||||
},
|
||||
data(): {
|
||||
intId: string;
|
||||
intClass: string;
|
||||
intName: string;
|
||||
intPlaceholder: string;
|
||||
intAriaDescribedby: string;
|
||||
intRequired: boolean;
|
||||
intValue: string;
|
||||
} {
|
||||
return {
|
||||
intId: "",
|
||||
intClass: "",
|
||||
intName: "",
|
||||
intPlaceholder: "",
|
||||
intAriaDescribedby: "",
|
||||
intRequired: false,
|
||||
intValue: "",
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
onBlur() {
|
||||
const el = textarea.value;
|
||||
if (!el) {
|
||||
throw new Error("undefined textarea element");
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
const json = String(this.$attrs.json ?? "{}"),
|
||||
data = JSON.parse(json);
|
||||
this.intId = String(data.id);
|
||||
this.intClass = String(data.class);
|
||||
this.intName = String(data.name);
|
||||
this.intPlaceholder = String(data.placeholder);
|
||||
this.intAriaDescribedby = String(data.ariaDescribedby);
|
||||
this.intValue = String(data.value);
|
||||
if (data.required) {
|
||||
this.intRequired = true;
|
||||
}
|
||||
if (this.textarea) {
|
||||
// Default input init
|
||||
this.textarea.setAttribute("style", "height: 1rem;overflow-y: hidden;");
|
||||
}
|
||||
// * Init input
|
||||
this.$nextTick(this.onInput);
|
||||
},
|
||||
data(): {
|
||||
intId: string;
|
||||
intClass: string;
|
||||
intName: string;
|
||||
intPlaceholder: string;
|
||||
intAriaDescribedby: string;
|
||||
intRequired: boolean;
|
||||
intValue: string;
|
||||
} {
|
||||
return {
|
||||
intId: "",
|
||||
intClass: "",
|
||||
intName: "",
|
||||
intPlaceholder: "",
|
||||
intAriaDescribedby: "",
|
||||
intRequired: false,
|
||||
intValue: "",
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
onBlur() {
|
||||
const el = textarea.value;
|
||||
if (!el) {
|
||||
throw new Error("undefined textarea element");
|
||||
}
|
||||
// * Remove every new line char
|
||||
el.value = el.value.replace(/[\n\r]/g, "");
|
||||
this.$nextTick(() => {
|
||||
el.style.height = "auto";
|
||||
el.style.height = `${el.scrollHeight}px`;
|
||||
});
|
||||
},
|
||||
onInput() {
|
||||
const el = textarea.value;
|
||||
if (!el) {
|
||||
throw new Error("undefined textarea element");
|
||||
}
|
||||
// * Remove every new line char
|
||||
el.value = el.value.replace(/[\n\r]/g, "");
|
||||
this.$nextTick(() => {
|
||||
el.style.height = "auto";
|
||||
el.style.height = `${el.scrollHeight}px`;
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
</script>
|
||||
onInput() {
|
||||
const el = textarea.value;
|
||||
if (!el) {
|
||||
throw new Error("undefined textarea element");
|
||||
}
|
||||
el.style.height = "auto";
|
||||
el.style.height = `${el.scrollHeight}px`;
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.input-text-auto-component {
|
||||
resize: none;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss">
|
||||
.input-text-auto-component {
|
||||
resize: none;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
124
resources/ts/bo/components/PasswordVisibility.vue
Normal file
124
resources/ts/bo/components/PasswordVisibility.vue
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
<template>
|
||||
<div :class="`password-visibility-${inputId} h-100`">
|
||||
<span
|
||||
v-if="visible"
|
||||
class="text-bg-primary reveal d-flex justify-content-center align-items-center h-100"
|
||||
@click.prevent="toggleVisibility()"
|
||||
type="button"
|
||||
:title="__('Cacher le mot de passe')"
|
||||
data-bs="tooltip"
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon="fas fa-eye-slash"
|
||||
:aria-hidden="true"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
v-else
|
||||
class="text-bg-primary reveal d-flex justify-content-center align-items-center h-100"
|
||||
@click.prevent="toggleVisibility()"
|
||||
type="button"
|
||||
:title="__('Dévoiler le mot de passe')"
|
||||
data-bs="tooltip"
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon="fas fa-eye"
|
||||
:aria-hidden="true"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
||||
import { Tooltip } from "bootstrap";
|
||||
import trans from "./../../modules/trans";
|
||||
|
||||
export default defineComponent({
|
||||
name: "PasswordVisibility",
|
||||
mixins: [trans],
|
||||
components: { FontAwesomeIcon },
|
||||
data(): {
|
||||
inputId: string;
|
||||
input: HTMLInputElement;
|
||||
visible: boolean;
|
||||
tooltipList: HTMLButtonElement[];
|
||||
} {
|
||||
return {
|
||||
inputId: "",
|
||||
input: document.createElement("input"),
|
||||
visible: false,
|
||||
tooltipList: [],
|
||||
};
|
||||
},
|
||||
computed: {},
|
||||
mounted() {
|
||||
const json = String(this.$attrs.json ?? "{}"),
|
||||
data = JSON.parse(json);
|
||||
this.inputId = data.inputId;
|
||||
this.input = document.getElementById(this.inputId) as HTMLInputElement;
|
||||
if (!this.input) {
|
||||
throw new Error(`Cannot find input id '${this.inputId}'`);
|
||||
}
|
||||
if (this.input.tagName !== "INPUT") {
|
||||
throw new Error(`id '${this.inputId}' is not an input`);
|
||||
}
|
||||
if ((this.input as HTMLInputElement).type !== "password") {
|
||||
throw new Error(`id '${this.inputId}' is not an input password`);
|
||||
}
|
||||
this.input = this.input as HTMLInputElement;
|
||||
this.$nextTick(() => {
|
||||
this.updateBootstrapTooltip();
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* Update password status (visible, hidden).
|
||||
*/
|
||||
toggleVisibility(): void {
|
||||
if (this.input.type === "password") {
|
||||
this.input.type = "text";
|
||||
this.visible = true;
|
||||
} else {
|
||||
this.input.type = "password";
|
||||
this.visible = false;
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
this.updateBootstrapTooltip();
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Update Bootstrap tooltips.
|
||||
*/
|
||||
updateBootstrapTooltip() {
|
||||
let newTooltipList = [].slice.call(
|
||||
document.querySelectorAll(
|
||||
".password-visibility-" + this.inputId + " [data-bs=\"tooltip\"]"
|
||||
)
|
||||
) as HTMLButtonElement[];
|
||||
let tmp = newTooltipList.filter((x) => !this.tooltipList.includes(x));
|
||||
tmp.map((tooltip) => {
|
||||
return new Tooltip(tooltip);
|
||||
});
|
||||
this.closeBootstrapTooltip();
|
||||
this.tooltipList = newTooltipList;
|
||||
},
|
||||
/**
|
||||
* Close all Bootstrap tooltips.
|
||||
*/
|
||||
closeBootstrapTooltip() {
|
||||
this.tooltipList.forEach((tooltip) => {
|
||||
tooltip.blur();
|
||||
Tooltip.getInstance(tooltip)?.hide();
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.reveal {
|
||||
width: 52px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -40,7 +40,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||
});
|
||||
setupApp(app);
|
||||
app.mount(grpEl);
|
||||
}, 500);
|
||||
}, 10);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -37,8 +37,12 @@ import {
|
|||
faGripVertical,
|
||||
faLock,
|
||||
faLockOpen,
|
||||
faCircleCheck,
|
||||
faCircleXmark,
|
||||
faFloppyDisk,
|
||||
faVideo,
|
||||
faFile,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { faCircleXmark } from "@fortawesome/free-regular-svg-icons";
|
||||
|
||||
config.autoReplaceSvg = false;
|
||||
library.add(faEraser);
|
||||
|
|
@ -74,11 +78,15 @@ library.add(faArrowAltCircleUp);
|
|||
library.add(faGripVertical);
|
||||
library.add(faLock);
|
||||
library.add(faLockOpen);
|
||||
library.add(faCircleCheck);
|
||||
library.add(faCircleXmark);
|
||||
library.add(faFloppyDisk);
|
||||
library.add(faFile);
|
||||
library.add(faVideo);
|
||||
|
||||
// Front
|
||||
library.add(faChevronLeft);
|
||||
library.add(faChevronRight);
|
||||
library.add(faCircleXmark);
|
||||
library.add(faCircleNotch);
|
||||
library.add(faSliders);
|
||||
|
||||
|
|
|
|||
|
|
@ -7,14 +7,14 @@
|
|||
|
||||
<body>
|
||||
@include('bo.layouts.header')
|
||||
<main class="container-fluid pt-2 @yield('bg-color')">
|
||||
<main class="container-fluid pt-3 @yield('bg-color')">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col col-md-6">
|
||||
@include('modules.flash-message')
|
||||
</div>
|
||||
</div>
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-12 col-md-10 col-lg-8 mt-3">
|
||||
<div class="col-12 col-md-10 col-lg-8">
|
||||
@include('modules.breadcrumb.breadcrumb-body')
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<nav class="navbar navbar-expand-md bg-light text-bg-light shadow-sm">
|
||||
<nav class="navbar navbar-expand-lg bg-light text-bg-light shadow-sm">
|
||||
<div class="container">
|
||||
<a class="navbar-brand rounded py-1" href="{{ url('/') }}">
|
||||
{{ config('app.name', 'Laravel') }}
|
||||
|
|
|
|||
|
|
@ -9,11 +9,11 @@
|
|||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link @if(request()->routeIs('bo.app-users')) active @endif"
|
||||
href="{{ route('bo.app-users.index') }}"
|
||||
title="{{ __('Utilisateurs') }}"
|
||||
<a class="nav-link @if(request()->routeIs('bo.app-users.*')) active @endif"
|
||||
href="{{ route('bo.app-users.index') }}?rst=1"
|
||||
title="{{ __('Liste des :model', ['model' => __('models.classes.users')]) }}"
|
||||
data-bs-toggle="tooltip">
|
||||
{{ __('Utilisateurs') }}
|
||||
{{ __(':model', ['model' => __('models.classes.users')]) }}
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
|
|
@ -24,5 +24,29 @@
|
|||
{{ __(':model', ['model' => __('models.classes.directories')]) }}
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link @if(request()->routeIs('bo.documents.*')) active @endif"
|
||||
href="{{ route('bo.documents.index') }}"
|
||||
title="{{ __('Liste des :model', ['model' => __('models.classes.documents')]) }}"
|
||||
data-bs-toggle="tooltip">
|
||||
{{ __(':model', ['model' => __('models.classes.documents')]) }}
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link @if(request()->routeIs('bo.tags.*')) active @endif"
|
||||
href="{{ route('bo.tags.index') }}"
|
||||
title="{{ __('Liste des :model', ['model' => __('models.classes.tags')]) }}"
|
||||
data-bs-toggle="tooltip">
|
||||
{{ __(':model', ['model' => __('models.classes.tags')]) }}
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link @if(request()->routeIs('bo.settings.*')) active @endif"
|
||||
href="{{ route('bo.settings.index') }}"
|
||||
title="{{ __('Liste des :model', ['model' => __('models.classes.settings')]) }}"
|
||||
data-bs-toggle="tooltip">
|
||||
{{ __(':model', ['model' => __('models.classes.settings')]) }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
@endauth
|
||||
|
|
|
|||
17
resources/views/bo/mails/sendflyer.blade.php
Normal file
17
resources/views/bo/mails/sendflyer.blade.php
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
@component('mail::message')
|
||||
|
||||
{{ $data->body }}
|
||||
|
||||
@if (count($data->attach) === 1)
|
||||
<p>{{ __('Brochure :') }}</p>
|
||||
@else
|
||||
<p>{{ __('Brochures :') }}</p>
|
||||
@endif
|
||||
|
||||
@for ($i = 0; $i < count($data->attach); $i++)
|
||||
@component('mail::button', ['url' => $data->attach[$i], 'color' => 'success'])
|
||||
{{ $data->filesName[$i] }}
|
||||
@endcomponent
|
||||
@endfor
|
||||
|
||||
@endcomponent
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue