diff --git a/app/Enums/CodeType.php b/app/Enums/CodeType.php new file mode 100644 index 0000000..e2424d3 --- /dev/null +++ b/app/Enums/CodeType.php @@ -0,0 +1,25 @@ +name => 'Codes uniques', + self::ean->name => 'Codes EAN', + self::both->name => 'Codes uniques et codes EAN' + ]; +} diff --git a/app/Http/Controllers/Bo/ParticipationController.php b/app/Http/Controllers/Bo/ParticipationController.php index a129f8b..9bba139 100644 --- a/app/Http/Controllers/Bo/ParticipationController.php +++ b/app/Http/Controllers/Bo/ParticipationController.php @@ -27,7 +27,7 @@ class ParticipationController extends Controller $this->authorize('viewWinnerList', Participation::class); /** @var \Illuminate\Database\Eloquent\Builder $participationModels */ - $participationModels = Participation::query()->with(['customer', 'code', 'instantWin'])->whereHas('instantWin'); + $participationModels = Participation::query()->with(['customer', 'code', 'instantWin', 'product'])->whereHas('instantWin'); /** Sort columns with a query */ $this->sortQuery($participationModels); diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index 133454c..3f1dd61 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -37,7 +37,7 @@ class Controller extends BaseController protected function getRandomWeightedElementIndex(array $weightedValues): int { mt_srand(time()); - $rand = mt_rand(1, max((int)collect($weightedValues)->sum('percentage'), 1)); + $rand = mt_rand(1, max((int) collect($weightedValues)->sum('percentage'), 1)); foreach ($weightedValues as $key => $value) { $rand -= $value->percentage; diff --git a/app/Http/Controllers/Fo/Auth/LoginController.php b/app/Http/Controllers/Fo/Auth/LoginController.php index 01d94eb..7de2e19 100644 --- a/app/Http/Controllers/Fo/Auth/LoginController.php +++ b/app/Http/Controllers/Fo/Auth/LoginController.php @@ -3,10 +3,11 @@ namespace App\Http\Controllers\Fo\Auth; use App\Http\Controllers\Controller; +use App\Models\Customer; +use App\Models\Settings; use App\Providers\RouteServiceProvider; use Illuminate\Contracts\Auth\StatefulGuard; use Illuminate\Contracts\View\View; -use Illuminate\Database\Eloquent\Model; use Illuminate\Foundation\Auth\AuthenticatesUsers; use Illuminate\Foundation\Auth\ThrottlesLogins; use Illuminate\Http\Request; @@ -56,19 +57,30 @@ class LoginController extends Controller } /** - * The user has been authenticated. + * Check if the authenticated user can access the game * - * @param \Illuminate\Http\Request $request - * @param \Illuminate\Database\Eloquent\Model $user + * @param \Illuminate\Http\Request $request + * @param \App\Models\Customer $user * @return void * @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing, Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed */ - protected function authenticated(Request $request, Model $user): void + protected function authenticated(Request $request, Customer $user): void { - /** @var \App\Models\Customer $user */ - $user->last_connection = \now(); - $user->saveOrFail(); - Session::flash('info', trans('Vous êtes à présent connecté')); + /** @var \LDC\LDCCRM\Models\Customer $crmCustomer */ + $crmCustomer = $user->api_data; + + /** @var \App\Models\Settings */ + $settings = app(Settings::class); + $countries = $settings->authorized_countries; + + if (count($countries) && !in_array($crmCustomer->country->value, $countries)) { + $this->logout($request); + Session::flash('info', trans('Ce jeu n\'est pas disponible dans votre pays')); + } else { + $user->last_connection = \now(); + $user->saveOrFail(); + Session::flash('info', trans('Vous êtes à présent connecté')); + } } /** diff --git a/app/Http/Controllers/Fo/Auth/VerificationController.php b/app/Http/Controllers/Fo/Auth/VerificationController.php index b5683f2..809f6c9 100644 --- a/app/Http/Controllers/Fo/Auth/VerificationController.php +++ b/app/Http/Controllers/Fo/Auth/VerificationController.php @@ -60,11 +60,11 @@ class VerificationController extends Controller /** @var \App\Models\Customer */ $user = $request->user('frontend'); - if (!hash_equals((string)$request->route('id'), (string)$user->getKey())) { + if (!hash_equals((string) $request->route('id'), (string) $user->getKey())) { throw new AuthorizationException(); } - if (!hash_equals((string)$request->route('hash'), sha1($user->getEmailForVerification()))) { + if (!hash_equals((string) $request->route('hash'), sha1($user->getEmailForVerification()))) { throw new AuthorizationException(); } diff --git a/app/Http/Controllers/Fo/ParticipateController.php b/app/Http/Controllers/Fo/ParticipateController.php index 106fa38..e7894d9 100644 --- a/app/Http/Controllers/Fo/ParticipateController.php +++ b/app/Http/Controllers/Fo/ParticipateController.php @@ -78,7 +78,7 @@ class ParticipateController extends Controller /** @var \App\Models\Customer */ $customer = auth('frontend')->user(); - /** @var \App\Models\Code */ + /** @var \App\Models\Code|null */ $code = $request->post('unique_code'); /** @var \App\Models\Product|null */ $product = $request->post('product'); @@ -150,7 +150,7 @@ class ParticipateController extends Controller $participation->created_at, $instantWin->prize->label, $crmCustomer, - $code->code_unique, + optional($code)->code_unique, optional($product)->ean_code )); diff --git a/app/Http/Requests/Bo/Ranges/StoreRangeRequest.php b/app/Http/Requests/Bo/Ranges/StoreRangeRequest.php index bf6f70f..8814f2e 100644 --- a/app/Http/Requests/Bo/Ranges/StoreRangeRequest.php +++ b/app/Http/Requests/Bo/Ranges/StoreRangeRequest.php @@ -39,7 +39,9 @@ class StoreRangeRequest extends FormRequest public function rules() { $rules = [ - 'name' => 'required|string|min:2|max:255', + 'name' => 'required|string|min:2|max:255', + 'picture_alt' => 'nullable|required_with:picture|string|between:3,255', + 'picture_title' => 'nullable|required_with:picture|string|between:3,255', ]; $width = config('theme.models.ranges.picture_max_width'); $height = config('theme.models.ranges.picture_max_height'); diff --git a/app/Http/Requests/Bo/Settings/UpdateSettingsRequest.php b/app/Http/Requests/Bo/Settings/UpdateSettingsRequest.php index f9b265c..fb570dc 100644 --- a/app/Http/Requests/Bo/Settings/UpdateSettingsRequest.php +++ b/app/Http/Requests/Bo/Settings/UpdateSettingsRequest.php @@ -2,10 +2,14 @@ namespace App\Http\Requests\Bo\Settings; +use App\Enums\CodeType; use App\Lib\Helpers\ToolboxHelper; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Arr; use Illuminate\Support\Facades\Gate; +use Illuminate\Validation\Rule; +use LDC\LDCCRM\Enums\Country; +use Illuminate\Validation\Rules\Enum; class UpdateSettingsRequest extends FormRequest { @@ -14,7 +18,7 @@ class UpdateSettingsRequest extends FormRequest * * @return boolean */ - public function authorize() + public function authorize(): bool { return Gate::check('update', $this->route('settings')); } @@ -23,28 +27,28 @@ class UpdateSettingsRequest extends FormRequest * Prepare the data for validation. * * @return void - * @phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh */ - protected function prepareForValidation() + protected function prepareForValidation(): void { // phpcs:enable $this->merge([ // Checkboxes. - 'notify_prize_sent' => $this->boolean('notify_prize_sent'), - 'use_ean_codes' => $this->boolean('use_ean_codes'), - 'activated_teaser' => $this->boolean('activated_teaser'), - 'activated_close_page' => $this->boolean('activated_close_page'), - 'discounts_activated' => $this->boolean('discounts_activated'), - 'winning_proof' => $this->boolean('winning_proof'), + 'notify_prize_sent' => $this->boolean('notify_prize_sent'), + 'use_ean_codes' => $this->boolean('use_ean_codes'), + 'activated_teaser' => $this->boolean('activated_teaser'), + 'activated_close_page' => $this->boolean('activated_close_page'), + 'discounts_activated' => $this->boolean('discounts_activated'), + 'winning_proof' => $this->boolean('winning_proof'), // Arrays. - 'notification_emails' => collect($this->input('notification_emails'))->filter()->all(), + 'notification_emails' => collect($this->input('notification_emails'))->filter()->all(), + 'authorized_countries' => collect($this->input('authorized_countries'))->filter()->all(), // Remove some dates if the checkbox is uncheck. 'close_page_active_date' => $this->input('activated_close_page') ? $this->input('close_page_active_date') : null, // Remove discounts datas if the discount checkbox is uncheck. - 'discounts_site' => $this->input('discounts_activated') ? $this->input('discounts_site') : null, - 'discounts_campaign' => $this->input('discounts_activated') ? $this->input('discounts_campaign') : null, - 'web_coupons' => collect($this->input('web_coupons'))->filter()->all(), + 'discounts_site' => $this->input('discounts_activated') ? $this->input('discounts_site') : null, + 'discounts_campaign' => $this->input('discounts_activated') ? $this->input('discounts_campaign') : null, + 'web_coupons' => collect($this->input('web_coupons'))->filter()->all(), ]); } @@ -53,29 +57,30 @@ class UpdateSettingsRequest extends FormRequest * * @return array */ - public function rules() + public function rules(): array { return [ 'name' => 'required|string|min:3|max:255', 'company' => 'required|string|min:3|max:255', - 'start_date' => 'required|date', 'end_date' => 'required|date', 'nb_participation_player' => 'required|numeric|min:0', 'nb_participation_ip' => 'required|numeric|min:0', 'nb_unique_code_use' => 'required|numeric|min:0', - 'use_ean_codes' => 'required|boolean', + 'code_type' => ['required', new Enum(CodeType::class)], 'activated_teaser' => 'required|boolean', 'activated_close_page' => 'required|boolean', - // phpcs:ignore Generic.Files.LineLength.TooLong - 'close_page_active_date' => 'nullable|required_if:activated_close_page,true,1|date|after_or_equal:end_date', + 'close_page_active_date' => [ + 'nullable', + 'required_if:activated_close_page,true,1', + 'date', + 'after_or_equal:end_date' + ], 'redirect_close' => 'required|url:http,https', - 'notify_prize_sent' => 'required|boolean', 'discounts_activated' => 'required|boolean', 'discounts_site' => 'nullable|required_if:discounts_activated,true,1|string|min:3|max:255', 'discounts_campaign' => 'nullable|required_if:discounts_activated,true,1|string|min:3|max:255', - 'web_coupons' => [ 'present', 'array', function ($attribute, $value, $fail) { if ($this->post('discounts_activated') and !collect($value)->filter()->count()) { @@ -103,18 +108,22 @@ class UpdateSettingsRequest extends FormRequest $attribute, $value, $fail, - (object)[ + (object) [ 'maxHeight' => 409, - 'maxWith' => 920, + 'maxWith' => 920, 'minHeight' => 409, - 'minWith' => 920 + 'minWith' => 920 ] ); } ], - 'notification_emails' => 'required|array|min:1', 'notification_emails.*' => 'email:rfc,spoof,dns', + 'authorized_countries' => 'sometimes|array|max:' . count(Country::getValidCountries()), + 'authorized_countries.*' => [ + 'sometimes', + Rule::in(Country::getValidCountries()) + ], 'winning_proof' => 'required|boolean', ]; @@ -128,33 +137,33 @@ class UpdateSettingsRequest extends FormRequest public function attributes(): array { return [ - 'name' => trans('validation.attributes.name'), - 'company' => trans('validation.custom.company'), - 'start_date' => trans('validation.custom.start_date'), - 'end_date' => trans('validation.custom.end_date'), - 'nb_participation_player' => trans('validation.custom.nb_participation_player'), - 'nb_participation_ip' => trans('validation.custom.nb_participation_ip'), - 'nb_unique_code_use' => trans('validation.custom.nb_unique_code_use'), - 'use_ean_codes' => trans('validation.custom.use_ean_codes'), - 'activated_teaser' => trans('validation.custom.activated_teaser'), - 'activated_close_page' => trans('validation.custom.activated_close_page'), - 'close_page_active_date' => trans('validation.custom.close_page_active_date'), - 'redirect_close' => trans('validation.custom.redirect_close'), - 'winning_proof' => trans('validation.custom.winning_proof'), - 'discounts_activated' => trans('validation.custom.discounts_activated'), - 'discounts_site' => trans('validation.custom.discounts_site'), - 'discounts_campaign' => trans('validation.custom.discounts_campaign'), - 'notify_prize_sent' => trans('validation.custom.notify_prize_sent'), - - 'web_coupons' => trans('Tableau des coupons'), + 'name' => trans('validation.attributes.name'), + 'company' => trans('validation.custom.company'), + 'start_date' => trans('validation.custom.start_date'), + 'end_date' => trans('validation.custom.end_date'), + 'nb_participation_player' => trans('validation.custom.nb_participation_player'), + 'nb_participation_ip' => trans('validation.custom.nb_participation_ip'), + 'nb_unique_code_use' => trans('validation.custom.nb_unique_code_use'), + 'code_type' => trans('validation.custom.code_type'), + 'activated_teaser' => trans('validation.custom.activated_teaser'), + 'activated_close_page' => trans('validation.custom.activated_close_page'), + 'close_page_active_date' => trans('validation.custom.close_page_active_date'), + 'redirect_close' => trans('validation.custom.redirect_close'), + 'winning_proof' => trans('validation.custom.winning_proof'), + 'discounts_activated' => trans('validation.custom.discounts_activated'), + 'discounts_site' => trans('validation.custom.discounts_site'), + 'discounts_campaign' => trans('validation.custom.discounts_campaign'), + 'notify_prize_sent' => trans('validation.custom.notify_prize_sent'), + 'web_coupons' => trans('Tableau des coupons'), 'web_coupons.*.web_coupon_identifiant' => trans('Identifiant coupon :index'), 'web_coupons.*.percentage' => trans('Pourcentage :index'), 'web_coupons.*.face_value' => trans('Valeur faciale :index'), 'web_coupons.*.valid_on' => trans('Valable sur :index'), 'web_coupons.*.img_name' => trans('Nom image :index'), - - 'notification_emails' => trans('Tableau des email à notifier'), - 'notification_emails.*.email' => trans('validation.attributes.email') + 'notification_emails' => trans('Tableau des email à notifier'), + 'notification_emails.*.email' => trans('validation.attributes.email'), + 'authorized_countries' => trans('Tableau des pays éligibles au jeu'), + 'authorized_countries.*' => trans('validation.custom.authorized_countries'), ]; } } diff --git a/app/Http/Requests/Bo/StaticPages/UpdateStaticPageRequest.php b/app/Http/Requests/Bo/StaticPages/UpdateStaticPageRequest.php index 6f6e9cb..b4624d7 100644 --- a/app/Http/Requests/Bo/StaticPages/UpdateStaticPageRequest.php +++ b/app/Http/Requests/Bo/StaticPages/UpdateStaticPageRequest.php @@ -26,7 +26,7 @@ class UpdateStaticPageRequest extends FormRequest public function prepareForValidation(): void { $this->merge([ - 'content' => (string)Str::of($this->input('content')) + 'content' => (string) Str::of($this->input('content')) ]); } @@ -38,10 +38,10 @@ class UpdateStaticPageRequest extends FormRequest public function rules(): array { return [ - 'seo_title' => 'required|min:45|max:70', + 'seo_title' => 'required|min:45|max:70', 'seo_description' => 'required|min:50|max:160', - 'title' => 'required|min:3|max:255', - 'content' => 'present', + 'title' => 'required|min:3|max:255', + 'content' => 'present', ]; } @@ -53,10 +53,10 @@ class UpdateStaticPageRequest extends FormRequest public function attributes(): array { return [ - 'seo_title' => trans('validation.custom.seo_title'), + 'seo_title' => trans('validation.custom.seo_title'), 'seo_description' => trans('validation.custom.seo_description'), - 'name' => trans('validation.attributes.title'), - 'content' => trans('validation.attributes.content'), + 'name' => trans('validation.attributes.title'), + 'content' => trans('validation.attributes.content'), ]; } } diff --git a/app/Http/Requests/Fo/Customers/CreateAccountRequest.php b/app/Http/Requests/Fo/Customers/CreateAccountRequest.php index 6ece94e..c7af314 100644 --- a/app/Http/Requests/Fo/Customers/CreateAccountRequest.php +++ b/app/Http/Requests/Fo/Customers/CreateAccountRequest.php @@ -2,6 +2,7 @@ namespace App\Http\Requests\Fo\Customers; +use App\Models\Settings; use App\Repositories\Fo\CrmRepository; use App\Rules\IsAdult; use Illuminate\Foundation\Http\FormRequest; @@ -21,7 +22,7 @@ class CreateAccountRequest extends FormRequest * * @return boolean */ - public function authorize() + public function authorize(): bool { return Auth::guard('frontend')->check(); } @@ -31,10 +32,10 @@ class CreateAccountRequest extends FormRequest * * @return void */ - public function prepareForValidation() + public function prepareForValidation(): void { $this->merge([ - 'phone' => $this->input('phone'), + 'phone' => $this->input('phone'), 'opt_in' => $this->boolean('opt_in'), 'ok_cgv' => $this->boolean('ok_cgv') ]); @@ -45,9 +46,12 @@ class CreateAccountRequest extends FormRequest * * @return array */ - public function rules() + public function rules(): array { - /** @var \App\Models\Customer */ + /** @var \App\Models\Settings $settings */ + $settings = app(Settings::class); + + /** @var \App\Models\Customer|null */ $authUser = auth('frontend')->user(); $countryCode = null; @@ -56,56 +60,56 @@ class CreateAccountRequest extends FormRequest (new CrmRepository())->fetchCrmCustomer($authUser)->country->value : 0); $countryRules = match ($countryCode) { Country::belgium()->value => [ - 'phone' => 'phone:BE', + 'phone' => 'phone:BE', 'postal_code' => 'postal_code:BE', ], Country::luxembourg()->value => [ - 'phone' => 'phone:LU', + 'phone' => 'phone:LU', 'postal_code' => 'postal_code:LU', ], default => [ - 'phone' => 'phone:FR', + 'phone' => 'phone:FR', 'postal_code' => 'postal_code:FR', ] }; return [ - 'lastname' => 'required|string|max:255', - 'firstname' => 'required|string|max:255', - 'phone' => [ + 'lastname' => 'required|string|max:255', + 'firstname' => 'required|string|max:255', + 'phone' => [ 'nullable', 'max:255', $countryRules['phone'], ], - 'address' => 'required|string|min:1|max:255', - 'address2' => 'nullable|string|min:1|max:255', + 'address' => 'required|string|min:1|max:255', + 'address2' => 'nullable|string|min:1|max:255', 'postal_code' => [ 'required', 'string', 'min:1', $countryRules['postal_code'], ], - 'city' => 'required|string|min:2|max:255', - 'birth_date' => ['required', new IsAdult(trans('Vous devez être majeur pour vous inscrire.'))], - 'country' => [ + 'city' => 'required|string|min:2|max:255', + 'birth_date' => ['required', new IsAdult(trans('Vous devez être majeur pour vous inscrire.'))], + 'country' => [ 'required', new EnumIsValidRule(Country::class), - Rule::in(Country::getValidCountries()) + Rule::in($settings->getAuthorizedCountries()), ], - 'email' => [ + 'email' => [ 'required', 'email:rfc,dns,spoof', 'max:255', 'unique:customers,email', ], - 'password' => [ + 'password' => [ 'required', 'confirmed', Password::min(8)->mixedCase() ->numbers()->symbols()->uncompromised() ], - 'ok_cgv' => 'accepted', - 'opt_in' => 'required|boolean', + 'ok_cgv' => 'accepted', + 'opt_in' => 'required|boolean', ]; } @@ -133,23 +137,23 @@ class CreateAccountRequest extends FormRequest * * @return array */ - public function attributes() + public function attributes(): array { return [ - 'lastname' => trans('validation.attributes.first_name'), - 'firstname' => trans('validation.attributes.first_name'), - 'phone' => trans('validation.attributes.phone'), - 'address' => trans('validation.attributes.line_address_1'), - 'address2' => trans('validation.attributes.line_address_2'), - 'country' => trans('validation.attributes.country'), - 'postal_code' => trans('validation.attributes.postal_code'), - 'city' => trans('validation.attributes.city'), - 'birth_date' => trans('validation.attributes.date_of_birth'), - 'email' => trans('validation.attributes.email'), - 'password' => trans('validation.attributes.password'), + 'lastname' => trans('validation.attributes.first_name'), + 'firstname' => trans('validation.attributes.first_name'), + 'phone' => trans('validation.attributes.phone'), + 'address' => trans('validation.attributes.line_address_1'), + 'address2' => trans('validation.attributes.line_address_2'), + 'country' => trans('validation.attributes.country'), + 'postal_code' => trans('validation.attributes.postal_code'), + 'city' => trans('validation.attributes.city'), + 'birth_date' => trans('validation.attributes.date_of_birth'), + 'email' => trans('validation.attributes.email'), + 'password' => trans('validation.attributes.password'), 'password_confirmation' => trans('validation.attributes.password_confirmation'), - 'ok_cgv' => trans('conditions d\'utilisation'), - 'opt_in' => trans('newsletter'), + 'ok_cgv' => trans('conditions d\'utilisation'), + 'opt_in' => trans('newsletter'), ]; } @@ -158,17 +162,17 @@ class CreateAccountRequest extends FormRequest * * @return array */ - public function messages() + public function messages(): array { $messages = [ - 'postal_code' => trans('validation.postal_code', [ + 'postal_code' => trans('validation.postal_code', [ 'countries' => \implode(', ', Country::getValidCountriesLabel()), - 'examples' => 72000 + 'examples' => 72000 ]), - 'phone.phone' => trans('validation.phone', [ + 'phone.phone' => trans('validation.phone', [ 'countries' => \implode(', ', Country::getValidCountriesLabel()) ]), - 'email.unique' => trans('Un compte existe déjà avec cette adresse.'), + 'email.unique' => trans('Un compte existe déjà avec cette adresse.'), 'ok_cgv.accepted' => trans('Vous devez accepter les conditions d\'utilisation du site pour continuer.') ]; @@ -178,7 +182,7 @@ class CreateAccountRequest extends FormRequest [ 'postal_code' => trans('validation.postal_code', [ 'countries' => Country::belgium()->label, - 'examples' => 1000 + 'examples' => 1000 ]), ] ), @@ -187,7 +191,7 @@ class CreateAccountRequest extends FormRequest [ 'postal_code' => trans('validation.postal_code', [ 'countries' => Country::luxembourg()->label, - 'examples' => 1111 + 'examples' => 1111 ]), ] ), @@ -202,7 +206,7 @@ class CreateAccountRequest extends FormRequest array_search(Country::luxembourg()->label, Country::getValidCountriesLabel()), ] )), - 'examples' => 72000 + 'examples' => 72000 ]), ] ), diff --git a/app/Http/Requests/Fo/ParticipateRequest.php b/app/Http/Requests/Fo/ParticipateRequest.php index 13b0b9e..515474a 100644 --- a/app/Http/Requests/Fo/ParticipateRequest.php +++ b/app/Http/Requests/Fo/ParticipateRequest.php @@ -2,11 +2,12 @@ namespace App\Http\Requests\Fo; +use App\Enums\CodeType; use App\Models\Code; use App\Models\Participation; use App\Models\Product; use App\Models\Settings; -use Illuminate\Contracts\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Gate; @@ -40,29 +41,27 @@ class ParticipateRequest extends FormRequest */ protected function prepareForValidation(): void { - try { - $this->merge([ - 'unique_code' => Code::query()->where('id', Code::serialToInt( - Str::upper(\strval($this->input('unique_code'))) - ))->first() ?? false, - ]); - } catch (SerialCasterException) { - // * Provoquer l'affichage code incorrect. - $this->merge([ - 'unique_code' => false, - ]); - } finally { - $this->merge([ - 'ean_code' => (string)$this->string('ean_code')->padLeft(13, '0'), - 'game_rules' => $this->boolean('game_rules') - ]); - } - /** @var \App\Models\Settings */ $settings = app(Settings::class); - - // ? Does the game use EAN Codes ? - if ($settings->use_ean_codes) { + if ($settings->code_type->equals(CodeType::unique) || $settings->code_type->equals(CodeType::both)) { + try { + $this->merge([ + 'unique_code' => Code::query()->where('id', Code::serialToInt( + Str::upper(\strval($this->input('unique_code'))) + ))->first() ?? false, + ]); + } catch (SerialCasterException) { + // * Provoquer l'affichage code incorrect. + $this->merge([ + 'unique_code' => false, + ]); + } finally { + $this->merge([ + 'game_rules' => $this->boolean('game_rules'), + ]); + } + } + if ($settings->code_type->equals(CodeType::ean) || $settings->code_type->equals(CodeType::both)) { /** * * Check si les codes ean sont utilisés, auquel cas * * on test celui envoyé pour savoir s'il est correct. @@ -75,12 +74,14 @@ class ParticipateRequest extends FormRequest * càd que les code ean 8 sont de longueur 8, 7 de longueur7 et 13 de longueur 13. */ $this->merge([ + 'ean_code' => (string) $this->string('ean_code')->padLeft(13, '0'), // * Union pour faire des recherches séparée en une seule requête. // ! ean_code is already str_padded to 13 - 'product' => Product::query()->where('ean_code', $this->post('ean_code')) + 'product' => Product::query()->where('ean_code', $this->post('ean_code')) ->union(Product::query()->where('ean_code', Str::limit($this->post('ean_code'), 8, ''))) ->union(Product::query()->where('ean_code', Str::limit($this->post('ean_code'), 7, ''))) - ->first() + ->first(), + 'game_rules' => $this->boolean('game_rules'), ]); } //end if } @@ -96,10 +97,14 @@ class ParticipateRequest extends FormRequest $settings = app(Settings::class); $rules = [ - 'game_rules' => 'required|boolean|accepted', - 'unique_code' => [ + 'game_rules' => 'required|boolean|accepted', + ]; + + // ? Does the game use unique Codes ? + if ($settings->code_type->equals(CodeType::unique) || $settings->code_type->equals(CodeType::both)) { + $rules = \array_merge($rules, ['unique_code' => [ 'required', - // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed + // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed function (string $attribute, $value, callable $fail) { // ? Le Code unique est resolved ? if ($value === false or !\is_object($value) or \get_class($value) !== Code::class) { @@ -129,15 +134,14 @@ class ParticipateRequest extends FormRequest return; } } - ] - ]; - + ]]); + } //end if // ? Does the game use EAN Codes ? - if ($settings->use_ean_codes) { + if ($settings->code_type->equals(CodeType::ean) || $settings->code_type->equals(CodeType::both)) { $rules = \array_merge($rules, ['ean_code' => [ 'required', - // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed + // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed function ($attribute, $value, $fail) { // phpcs:enable /** @var \App\Models\Produt|mixed */ @@ -162,9 +166,9 @@ class ParticipateRequest extends FormRequest public function attributes(): array { return [ - 'unique_code' => \trans('validation.custom.unique_code'), - 'ean_code' => \trans('validation.custom.ean_code'), - 'game_rules' => \trans('validation.custom.game_rules'), + 'unique_code' => \trans('validation.custom.unique_code'), + 'ean_code' => \trans('validation.custom.ean_code'), + 'game_rules' => \trans('validation.custom.game_rules'), ]; } } diff --git a/app/Lib/Helpers/ToolboxHelper.php b/app/Lib/Helpers/ToolboxHelper.php index c6a9fd2..55fec50 100644 --- a/app/Lib/Helpers/ToolboxHelper.php +++ b/app/Lib/Helpers/ToolboxHelper.php @@ -4,7 +4,7 @@ namespace App\Lib\Helpers; use App\Models\Settings; use Carbon\Carbon; -use Illuminate\Contracts\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Http\Request; use Illuminate\Support\Str; @@ -348,11 +348,11 @@ class ToolboxHelper /** * Dynamic model query * - * @param string $defaultLocale - * @param \Illuminate\Contracts\Database\Eloquent\Builder $q - * @param string $column - * @param string $value - * @return \Illuminate\Contracts\Database\Eloquent\Builder + * @param string $defaultLocale + * @param \Illuminate\Database\Eloquent\Builder $q + * @param string $column + * @param string $value + * @return \Illuminate\Database\Eloquent\Builder */ public static function queryColumnWithLocales( string $defaultLocale, @@ -418,7 +418,7 @@ class ToolboxHelper if (!(is_object($input) or is_array($input))) { throw new \RuntimeException('Cannot handle $input of type ' . \gettype($input)); } - $ret = (array)$input; + $ret = (array) $input; foreach ($ret as &$item) { // Recursively process EACH element only if not scalar. $item = (is_object($item) or is_array($item)) ? @@ -440,7 +440,7 @@ class ToolboxHelper if (!(is_object($input) or is_array($input))) { throw new \RuntimeException('Cannot handle $input of type ' . \gettype($input)); } - $ret = (object)$input; + $ret = (object) $input; foreach ($ret as &$item) { // Recursively process EACH element only if not scalar. $item = (is_object($item) or is_array($item)) ? diff --git a/app/Mail/Won/PrizeWonAdminMail.php b/app/Mail/Won/PrizeWonAdminMail.php index 66474f1..b7dc124 100644 --- a/app/Mail/Won/PrizeWonAdminMail.php +++ b/app/Mail/Won/PrizeWonAdminMail.php @@ -24,7 +24,7 @@ class PrizeWonAdminMail extends Mailable implements ShouldQueue * @param \Carbon\Carbon $participation_date The customer participation date. * @param string $prize_label The won prize label. * @param \LDC\LDCCRM\Models\Customer $customer The customer who won. - * @param string $unique_code The unique code used to participate. + * @param string|null $unique_code The unique code used to participate if any. * @param string|null $ean_code The product code used to participate if any. * * @return void @@ -34,8 +34,8 @@ class PrizeWonAdminMail extends Mailable implements ShouldQueue protected Carbon $participation_date, protected string $prize_label, protected Customer $customer, - protected string $unique_code, - protected string|null $ean_code + protected ?string $unique_code, + protected ?string $ean_code ) { } @@ -60,12 +60,14 @@ class PrizeWonAdminMail extends Mailable implements ShouldQueue */ public function content(): \Illuminate\Mail\Mailables\Content { - $participation = [ - trans('Participation :'), - "Code Unique : {$this->unique_code}", - ]; - if ($this->ean_code) { - $participation[] = "Code EAN : {$this->ean_code}"; + $participation = []; + if (!is_null($this->unique_code)) { + $participation = [ + trans('Code Unique : :code', ['code' => $this->unique_code]), + ]; + } + if (!is_null($this->ean_code)) { + $participation[] = trans('Code EAN : :code', ['code' => $this->ean_code]); } return new Content( htmlString: (new MailMessage()) diff --git a/app/Mail/Won/WinningProofAdminMail.php b/app/Mail/Won/WinningProofAdminMail.php index 1d15a11..d635ba3 100644 --- a/app/Mail/Won/WinningProofAdminMail.php +++ b/app/Mail/Won/WinningProofAdminMail.php @@ -26,7 +26,7 @@ class WinningProofAdminMail extends Mailable implements ShouldQueue * @param \Carbon\Carbon $participation_date The customer participation date. * @param string $prize_label The won prize label. * @param \LDC\LDCCRM\Models\Customer $customer The customer who won. - * @param string $unique_code The unique code used to participate. + * @param string|null $unique_code The unique code used to participate if any. * @param string|null $ean_code The product code used to participate if any. * @param string $sticker Fichier image du sticker utilisé. * @param string $ticket Fichier image du ticket de caisse prouvant @@ -40,8 +40,8 @@ class WinningProofAdminMail extends Mailable implements ShouldQueue protected Carbon $participation_date, protected string $prize_label, protected Customer $customer, - protected string $unique_code, - protected string|null $ean_code, + protected ?string $unique_code, + protected ?string $ean_code, protected string $sticker, protected string $ticket, protected string $barcode, @@ -70,11 +70,13 @@ class WinningProofAdminMail extends Mailable implements ShouldQueue */ public function content(): \Illuminate\Mail\Mailables\Content { - $participation = [ - trans('Participation :'), - trans('Code Unique : :code', ['code' => $this->unique_code]), - ]; - if ($this->ean_code) { + $participation = []; + if (!is_null($this->ean_code)) { + $participation = [ + trans('Code Unique : :code', ['code' => $this->unique_code]), + ]; + } + if (!is_null($this->ean_code)) { $participation[] = trans('Code EAN : :code', ['code' => $this->ean_code]); } return new Content( diff --git a/app/Models/BannedIp.php b/app/Models/BannedIp.php index 31602a9..3def840 100644 --- a/app/Models/BannedIp.php +++ b/app/Models/BannedIp.php @@ -31,7 +31,7 @@ class BannedIp extends Model /** * The attributes that are mass assignable. * - * @var array + * @var array */ protected $fillable = ['ip']; diff --git a/app/Models/Code.php b/app/Models/Code.php index f6e9a87..ef15fa4 100644 --- a/app/Models/Code.php +++ b/app/Models/Code.php @@ -41,7 +41,7 @@ class Code extends Model /** * The attributes that are mass assignable. * - * @var array + * @var array */ protected $fillable = [ 'code_exported', diff --git a/app/Models/Customer.php b/app/Models/Customer.php index d9a26dc..b9bfa70 100644 --- a/app/Models/Customer.php +++ b/app/Models/Customer.php @@ -8,7 +8,7 @@ use App\Notifications\Customer\ResetPassword; use App\Notifications\Customer\VerifyEmail; use App\Traits\Models\ActivityLog; use Illuminate\Contracts\Auth\MustVerifyEmail; -use Illuminate\Contracts\Database\Query\Builder; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Prunable; use Illuminate\Database\Eloquent\Relations\HasMany; @@ -17,6 +17,7 @@ use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Validator; +use Illuminate\Support\Str; use Illuminate\Validation\Rule; use Illuminate\Validation\ValidationException; use LDC\LDCCRM\Enums\ApiError; @@ -101,6 +102,17 @@ class Customer extends Authenticatable implements MustVerifyEmail // * RELATIONSHIPS + /** + * Set email attribute in lower case. + * + * @param string $value + * @return void + */ + public function setEmailAttribute(string $value): void + { + $this->attributes['email'] = Str::lower($value); + } + /** * Has Many Participation relation * @@ -365,19 +377,19 @@ class Customer extends Authenticatable implements MustVerifyEmail if ($this->hashcode) { return collect(array_merge( parent::toArray(), - (array)$this->api_data + (array) $this->api_data ))->intersectByKeys([ - 'email' => '', - 'firstname' => '', - 'lastname' => '', - 'address' => '', - 'address2' => '', - 'postcode' => '', + 'email' => '', + 'firstname' => '', + 'lastname' => '', + 'address' => '', + 'address2' => '', + 'postcode' => '', 'birth_date' => '', - 'city' => '', - 'country' => '', - 'opt_in' => '', - 'phone' => '' + 'city' => '', + 'country' => '', + 'opt_in' => '', + 'phone' => '' ])->toArray(); } else { return parent::toArray(); diff --git a/app/Models/FaqQuestion.php b/app/Models/FaqQuestion.php index a9480bc..cafcfa5 100644 --- a/app/Models/FaqQuestion.php +++ b/app/Models/FaqQuestion.php @@ -29,7 +29,7 @@ class FaqQuestion extends Model /** * The attributes that are mass assignable. * - * @var array + * @var array */ protected $fillable = [ 'question', @@ -40,7 +40,7 @@ class FaqQuestion extends Model /** * The relations to eager load on every query. * - * @var array + * @var array */ protected $with = ['theme']; diff --git a/app/Models/FaqTheme.php b/app/Models/FaqTheme.php index 44f9f20..63af913 100644 --- a/app/Models/FaqTheme.php +++ b/app/Models/FaqTheme.php @@ -31,7 +31,7 @@ class FaqTheme extends Model /** * The attributes that are mass assignable. * - * @var array + * @var array */ protected $fillable = ['name']; diff --git a/app/Models/InstantWin.php b/app/Models/InstantWin.php index 1ab8216..f5ccdf3 100644 --- a/app/Models/InstantWin.php +++ b/app/Models/InstantWin.php @@ -32,7 +32,7 @@ class InstantWin extends Model /** * The relations to eager load on every query. * - * @var array + * @var array */ protected $with = ['prize']; diff --git a/app/Models/Participation.php b/app/Models/Participation.php index 91a5715..090d00a 100644 --- a/app/Models/Participation.php +++ b/app/Models/Participation.php @@ -6,7 +6,7 @@ use App\Casts\Ip; use App\Lib\Helpers\FileStorageHelper; use App\Notifications\Customer\PrizeHasBeenSent; use App\Traits\Models\ActivityLog; -use Illuminate\Contracts\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -42,7 +42,7 @@ class Participation extends Model /** * The attributes that are mass assignable. * - * @var array + * @var array */ protected $fillable = [ 'barcode', @@ -56,18 +56,18 @@ class Participation extends Model * @var array */ protected $casts = [ - 'ip' => Ip::class, + 'ip' => Ip::class, 'price_sent' => 'bool' ]; /** * The model's attributes. * - * @var array + * @var array */ protected $attributes = [ 'sticker' => null, - 'ticket' => null, + 'ticket' => null, 'barcode' => null, ]; @@ -76,7 +76,7 @@ class Participation extends Model * * @return void */ - protected static function booted() + protected static function booted(): void { static::creating(function (self $model) { self::manageFields($model); @@ -192,21 +192,23 @@ class Participation extends Model * * @param \IPLib\Address\AddressInterface $ip Ip for the participation. * @param \App\Models\Customer $customer The Customer playing the game. - * @param \App\Models\Code $code The unique code used to play. - * @param null|\App\Models\Product $product The product that holds the sticker if codes are actived. + * @param \App\Models\Code|null $code The unique code used to play. + * @param \App\Models\Product|null $product The product that holds the sticker if codes are actived. * @return \App\Models\Participation */ public static function makeNew( AddressInterface $ip, Customer $customer, - Code $code, - Product|null $product = null + ?Code $code = null, + ?Product $product = null ): self { $self = new self(); $self->ip = $ip; $self->customer()->associate($customer); - $self->code()->associate($code); - if ($product) { + if (!is_null($code)) { + $self->code()->associate($code); + } + if (!is_null($product)) { $self->product()->associate($product); } return $self; diff --git a/app/Models/Prize.php b/app/Models/Prize.php index ffbc6dc..e76ab18 100644 --- a/app/Models/Prize.php +++ b/app/Models/Prize.php @@ -39,7 +39,7 @@ class Prize extends Model /** * The attributes that are mass assignable. * - * @var array + * @var array */ protected $fillable = [ 'label', diff --git a/app/Models/Product.php b/app/Models/Product.php index ffaed68..b174b5c 100644 --- a/app/Models/Product.php +++ b/app/Models/Product.php @@ -36,7 +36,7 @@ class Product extends Model /** * The attributes that are mass assignable. * - * @var array + * @var array */ protected $fillable = [ 'name', diff --git a/app/Models/Range.php b/app/Models/Range.php index d4cb2b2..a29b524 100644 --- a/app/Models/Range.php +++ b/app/Models/Range.php @@ -11,10 +11,12 @@ use Illuminate\Database\Eloquent\Relations\HasMany; /** * Range. * - * @property string $name Name - * @property string $picture Path of the picture - * @property-read \Illuminate\Support\Carbon $created_at Creation Date. - * @property-read \Illuminate\Support\Carbon $updated_at Last update Date. + * @property string $name Name + * @property string $picture Path of the picture + * @property string $picture_alt Alt text for the picture + * @property string $picture_title Title text for the picture + * @property-read \Illuminate\Support\Carbon $created_at Creation Date. + * @property-read \Illuminate\Support\Carbon $updated_at Last update Date. * * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Product[] $products * Associated products. @@ -29,11 +31,13 @@ class Range extends Model /** * The attributes that are mass assignable. * - * @var array + * @var array */ protected $fillable = [ 'name', - 'picture' + 'picture', + 'picture_alt', + 'picture_title', ]; // * EVENTS diff --git a/app/Models/Settings.php b/app/Models/Settings.php index 2cafd35..99552cc 100644 --- a/app/Models/Settings.php +++ b/app/Models/Settings.php @@ -3,6 +3,7 @@ namespace App\Models; use App\Casts\AsArrayOfDiscount; +use App\Enums\CodeType; use App\Lib\Discount; use App\Lib\Helpers\FileStorageHelper; use App\Traits\Models\IsCachedAsCollection; @@ -11,6 +12,7 @@ use Carbon\CarbonInterval; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Cache; +use LDC\LDCCRM\Enums\Country; /** * Settings. @@ -26,7 +28,7 @@ use Illuminate\Support\Facades\Cache; * qu'une seule fois 1 code donné * @property boolean $notify_prize_sent Should the game send notifications * when the prize is sent ? - * @property boolean $use_ean_codes Should the game use ean codes ? + * @property \App\Enums\CodeType $code_type Wich codes must be used. * @property boolean $activated_teaser Is the teaser active ? * @property boolean $activated_close_page Is the close page active ? * @property \Illuminate\Support\Carbon $close_page_active_date End date of the close page @@ -36,6 +38,7 @@ use Illuminate\Support\Facades\Cache; * @property string $discounts_campaign Discount MesBr campaign * @property array<\App\Lib\Discount> $web_coupons Array of web coupons * @property array $notification_emails Array of email to notify when there's a winnner + * @property array $authorized_countries All countries eligible to participate. * @property boolean $winning_proof Is the winning proof needed * @property-read \Illuminate\Support\Carbon $created_at Creation Date. * @property-read \Illuminate\Support\Carbon $updated_at Last update Date. @@ -56,7 +59,7 @@ class Settings extends Model /** * The attributes that are mass assignable. * - * @var array + * @var array */ protected $fillable = [ 'name', @@ -66,7 +69,7 @@ class Settings extends Model 'nb_participation_player', 'nb_participation_ip', 'nb_unique_code_use', - 'use_ean_codes', + 'code_type', 'activated_teaser', 'activated_close_page', 'close_page_active_date', @@ -75,6 +78,7 @@ class Settings extends Model 'discounts_site', 'discounts_campaign', 'web_coupons', + 'authorized_countries', 'notification_emails', 'winning_proof', 'analytics', @@ -87,17 +91,18 @@ class Settings extends Model * @var array */ protected $casts = [ - 'notify_prize_sent' => 'bool', - 'use_ean_codes' => 'bool', - 'activated_teaser' => 'bool', - 'activated_close_page' => 'bool', - 'discounts_activated' => 'bool', - 'winning_proof' => 'bool', - 'start_date' => 'datetime', - 'end_date' => 'datetime', + 'notify_prize_sent' => 'bool', + 'code_type' => CodeType::class, + 'activated_teaser' => 'bool', + 'activated_close_page' => 'bool', + 'discounts_activated' => 'bool', + 'winning_proof' => 'bool', + 'start_date' => 'datetime', + 'end_date' => 'datetime', 'close_page_active_date' => 'datetime', - 'web_coupons' => AsArrayOfDiscount::class, - 'notification_emails' => 'array', + 'web_coupons' => AsArrayOfDiscount::class, + 'authorized_countries' => 'array', + 'notification_emails' => 'array', ]; // * EVENTS @@ -144,14 +149,13 @@ class Settings extends Model /** * Check if the game can be play and report if it cannot. + * Methods to check via policy if game can be played * - * Demande Vincent: Methods to check via policy if game can be played - * - * Besoin: - * - Min 1 code unique - * - Min 1 Instant gagnant - * - Min 1 Product si Settings use_ean_codes - * - Min 1 WebCoupon si Settings discounts_activated + * Needed: + * - Min 1 code unique if Settings code_type is unique or both codes. + * - Min 1 Instant gagnant. + * - Min 1 Product if Settings code_type is ean or both codes. + * - Min 1 WebCoupon if Settings discounts_activated * * @return boolean */ @@ -159,23 +163,29 @@ class Settings extends Model { /** @var \App\Models\Settings */ $settings = app(Settings::class); - if (!(bool)Prize::getCached()->count()) { + if (!(bool) Prize::getCached()->count()) { report(new \RuntimeException('Need prizes to enable the game')); return false; } - if (!(bool)Cache::remember('codes.count', CarbonInterval::day(), fn () => Code::query()->count())) { + if ( + ($settings->code_type->equals(CodeType::unique) || $settings->code_type->equals(CodeType::both)) + and !(bool) Cache::remember('codes.count', CarbonInterval::day(), fn() => Code::query()->count()) + ) { report(new \RuntimeException('Need codes to enable the game')); return false; } - if (!(bool)InstantWin::getCached()->count()) { + if (!(bool) InstantWin::getCached()->count()) { report(new \RuntimeException('Need instant win to enable the game')); return false; } - if ($settings->use_ean_codes and !(bool)Product::getCached()->count()) { + if ( + ($settings->code_type->equals(CodeType::ean) || $settings->code_type->equals(CodeType::both)) + and !(bool) Product::getCached()->count() + ) { report(new \RuntimeException('Need products to enable the game using ean codes')); return false; } - if ($settings->discounts_activated and !(bool)collect($this->web_coupons)->count()) { + if ($settings->discounts_activated and !(bool) collect($this->web_coupons)->count()) { report(new \RuntimeException('Need MesBr coupons to enable the game using discounts')); return false; } @@ -259,6 +269,30 @@ class Settings extends Model ); } + /** + * Get all authorized countries. + * + * @return \Illuminate\Support\Collection + */ + public function getAuthorizedCountries(): \Illuminate\Support\Collection + { + /** @var \App\Models\Settings $settings */ + $settings = app(Settings::class); + if (count($settings->authorized_countries)) { + // * Authorized countries defined in settings. + $countries = collect(Country::toArray()) + ->filter(fn($enum) => in_array($enum->value, $settings->authorized_countries)); + } else { + // * Only DROM-COM + France. + $franceCountries = collect(Country::getValidCountries()) + ->filter(fn($enum) => !in_array($enum, [Country::belgium(), Country::luxembourg()])); + $otherCountries = collect(Country::getValidCountries()) + ->filter(fn($enum) => !$franceCountries->contains($enum)); + $countries = $franceCountries->merge($otherCountries); + } + return $countries; + } + // * RELATIONS /** diff --git a/app/Policies/ParticipationPolicy.php b/app/Policies/ParticipationPolicy.php index 47eab9a..edd950f 100644 --- a/app/Policies/ParticipationPolicy.php +++ b/app/Policies/ParticipationPolicy.php @@ -9,7 +9,7 @@ use App\Models\Participation; use App\Models\Settings; use App\Models\User; use App\Policies\Rules\UserStaticRules; -use Illuminate\Contracts\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Builder; class ParticipationPolicy { @@ -57,7 +57,7 @@ class ParticipationPolicy false; // * Deny banned IP, Check did not participate too much - return !(bool)BannedIp::getCached() + return !(bool) BannedIp::getCached() ->where('ip', app('clientIp'))->count() and $playerCanParticipate; } @@ -74,7 +74,7 @@ class ParticipationPolicy // * Recettage return true; } - return (bool)$authUser->participations()->where(function (Builder $q) { + return (bool) $authUser->participations()->where(function (Builder $q) { $q->whereHas('instantWin')->orWhereHas('discount'); })->count(); } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 6a7d59f..1ebb990 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -12,15 +12,6 @@ use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { - /** - * Register any application services. - * - * @return void - */ - public function register() - { - } - /** * Bootstrap any application services. * diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 05ad6b6..f061667 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -37,28 +37,28 @@ class AuthServiceProvider extends ServiceProvider // * POLICIES Gate::define( 'fo.showRe7', - // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass + // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found fn (User|Customer $user) => ($user instanceof User) and !app()->environment(['production']) and $user->role->value() <= RoleEnum::marketing->value() ); Gate::define( 'fo.showProducts', - // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass + // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found fn (User|Customer|null $user) => View::exists('theme::pages.products') and - (bool)Product::getCached()->count() + (bool) Product::getCached()->count() ); Gate::define( 'fo.showFaq', - // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass + // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found fn (User|Customer|null $user) => View::exists('theme::pages.faq') and - (bool)FaqQuestion::getCached()->count() + (bool) FaqQuestion::getCached()->count() ); Gate::define( 'fo.gameEnabled', - // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass + // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found function (User|Customer|null $user) { /** @var \App\Models\Settings */ $settings = app(Settings::class); diff --git a/app/Traits/Enums/BaseEnum.php b/app/Traits/Enums/BaseEnum.php index d6016cb..a8734d1 100644 --- a/app/Traits/Enums/BaseEnum.php +++ b/app/Traits/Enums/BaseEnum.php @@ -109,7 +109,7 @@ trait BaseEnum } $baseArray[$index] = $elements[$enum->name]; } - return [$enum->name => (object)$baseArray]; + return [$enum->name => (object) $baseArray]; })->all()); } return self::getCache('enums'); diff --git a/app/Traits/Requests/HasPicture.php b/app/Traits/Requests/HasPicture.php index 88806ad..e48750b 100644 --- a/app/Traits/Requests/HasPicture.php +++ b/app/Traits/Requests/HasPicture.php @@ -56,7 +56,7 @@ trait HasPicture ?int $maxHeight = null, bool $nullable = false ): array { - $sizes = (object)[ + $sizes = (object) [ 'minWith' => $minWith ?? $this->minWith, 'minHeight' => $minHeight ?? $this->minHeight, 'maxWith' => $maxWith ?? $this->maxWith, @@ -89,7 +89,7 @@ trait HasPicture ?int $maxWith = null, ?int $maxHeight = null ): array { - $sizes = (object)[ + $sizes = (object) [ 'minWith' => $minWith ?? $this->minWith, 'minHeight' => $minHeight ?? $this->minHeight, 'maxWith' => $maxWith ?? $this->maxWith, diff --git a/composer.json b/composer.json index c0abcc6..6d4d24d 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "uwil/gen-game-web", - "version": "2.5.11", + "version": "3.1.1", "type": "project", "description": "Game generator", "support": { diff --git a/config/app.php b/config/app.php index b002a6c..d216dfd 100644 --- a/config/app.php +++ b/config/app.php @@ -41,7 +41,7 @@ return [ | */ - 'debug' => (bool)env('APP_DEBUG', false), + 'debug' => (bool) env('APP_DEBUG', false), /* |-------------------------------------------------------------------------- diff --git a/config/sentry.php b/config/sentry.php index 29627fe..2e47564 100644 --- a/config/sentry.php +++ b/config/sentry.php @@ -55,7 +55,7 @@ return [ 'send_default_pii' => env('SENTRY_SEND_DEFAULT_PII', false), // @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#traces-sample-rate. - 'traces_sample_rate' => env('SENTRY_TRACES_SAMPLE_RATE') === null ? null : (float)env('SENTRY_TRACES_SAMPLE_RATE'), + 'traces_sample_rate' => env('SENTRY_TRACES_SAMPLE_RATE') === null ? null : (float) env('SENTRY_TRACES_SAMPLE_RATE'), 'before_send' => [\App\Lib\Helpers\ExceptionsHelper::class, 'sentryEventHandler'], ]; diff --git a/database/migrations/2025_07_23_100002_add_codes_types_settings_table.php b/database/migrations/2025_07_23_100002_add_codes_types_settings_table.php new file mode 100644 index 0000000..519781c --- /dev/null +++ b/database/migrations/2025_07_23_100002_add_codes_types_settings_table.php @@ -0,0 +1,36 @@ +dropColumn('use_ean_codes'); + $table->unsignedTinyInteger('code_type')->comment('Type of codes used (CodeType Enum)') + ->after('nb_unique_code_use'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down(): void + { + Schema::table('settings', function (Blueprint $table) { + $table->dropColumn('code_type'); + $table->boolean('use_ean_codes')->comment('Le joueur doivent entrer le code EAN du produit porteur') + ->after('nb_unique_code_use'); + }); + } +}; diff --git a/database/migrations/2025_07_25_120419_set_code_unique_id_nullable_participations_table.php b/database/migrations/2025_07_25_120419_set_code_unique_id_nullable_participations_table.php new file mode 100644 index 0000000..a362bb5 --- /dev/null +++ b/database/migrations/2025_07_25_120419_set_code_unique_id_nullable_participations_table.php @@ -0,0 +1,42 @@ +dropForeign('participations_code_unique_id_foreign'); + $table->dropColumn('code_unique_id'); + }); + Schema::table('participations', function (Blueprint $table) { + $table->foreignId('code_unique_id')->nullable()->comment('Code unique utilisé') + ->after('customer_id')->constrained('codes'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down(): void + { + Schema::table('participations', function (Blueprint $table) { + $table->dropForeign('participations_code_unique_id_foreign'); + $table->dropColumn('code_unique_id'); + }); + Schema::table('participations', function (Blueprint $table) { + $table->foreignId('code_unique_id')->nullable(false)->comment('Code unique utilisé') + ->after('customer_id')->constrained('codes'); + }); + } +}; diff --git a/database/migrations/2025_08_05_163038_add_authorized_countries_in_settings_table.php b/database/migrations/2025_08_05_163038_add_authorized_countries_in_settings_table.php new file mode 100644 index 0000000..069e67a --- /dev/null +++ b/database/migrations/2025_08_05_163038_add_authorized_countries_in_settings_table.php @@ -0,0 +1,33 @@ +json('authorized_countries')->after('discounts_campaign') + ->comment('All countries eligible to participate.'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down(): void + { + Schema::table('settings', function (Blueprint $table) { + $table->dropColumn('authorized_countries'); + }); + } +}; diff --git a/database/seeders/Mandatory/SettingsSeeder.php b/database/seeders/Mandatory/SettingsSeeder.php index 7f6af9f..2fae522 100644 --- a/database/seeders/Mandatory/SettingsSeeder.php +++ b/database/seeders/Mandatory/SettingsSeeder.php @@ -2,6 +2,7 @@ namespace Database\Seeders\Mandatory; +use App\Enums\CodeType; use App\Models\Settings; use Illuminate\Database\Seeder; use Illuminate\Support\Facades\DB; @@ -13,7 +14,7 @@ class SettingsSeeder extends Seeder * * @return void */ - public function run() + public function run(): void { DB::table('settings')->truncate(); $settings = new Settings(); @@ -24,15 +25,16 @@ class SettingsSeeder extends Seeder $settings->nb_participation_player = 30; $settings->nb_participation_ip = 30; $settings->nb_unique_code_use = 1; - $settings->use_ean_codes = false; + $settings->code_type = CodeType::unique; $settings->activated_teaser = false; $settings->activated_close_page = false; $settings->notify_prize_sent = false; $settings->redirect_close = "https://example.test"; $settings->discounts_activated = false; + $settings->authorized_countries = []; $settings->notification_emails = []; - $settings->winning_proof = 0; $settings->web_coupons = []; + $settings->winning_proof = 0; $settings->save(); } } diff --git a/package-lock.json b/package-lock.json index 879e7d4..06b53a0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2686,9 +2686,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001615", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001615.tgz", - "integrity": "sha512-1IpazM5G3r38meiae0bHRnPhz+CBQ3ZLqbQMtrg+AsTPKAXgW38JNsXkyZ+v8waCsDmPq87lmfun5Q2AGysNEQ==", + "version": "1.0.30001727", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", + "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==", "dev": true, "funding": [ { @@ -2703,7 +2703,8 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/chalk": { "version": "2.4.2", diff --git a/phpcs.xml b/phpcs.xml index 039330f..dea0338 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -29,8 +29,7 @@ - - + @@ -43,10 +42,8 @@ + - - - @@ -79,7 +76,6 @@ - diff --git a/resources/lang/en/validation.php b/resources/lang/en/validation.php index fd2fc21..56ab93d 100644 --- a/resources/lang/en/validation.php +++ b/resources/lang/en/validation.php @@ -220,17 +220,18 @@ return [ 'female' => 'of the ', 'vowel' => 'of the ' ], - 'event' => 'event', - 'model' => 'target element', - 'order' => 'order', - 'publication' => 'publication', - 'activated' => 'activated', - 'target' => 'target', - 'static_page_type' => 'static page type', - 'seo_title' => 'SEO title', - 'seo_description' => 'SEO description', + 'event' => 'event', + 'model' => 'target element', + 'order' => 'order', + 'publication' => 'publication', + 'activated' => 'activated', + 'target' => 'target', + 'static_page_type' => 'static page type', + 'seo_title' => 'SEO title', + 'seo_description' => 'SEO description', + 'authorized_countries' => 'eligible countries at game', - 'postal_code' => 'the :attribute field accepts postal codes from the following + 'postal_code' => 'the :attribute field accepts postal codes from the following :countries such as :examples', ], ]; diff --git a/resources/lang/fr/validation.php b/resources/lang/fr/validation.php index c36d4d3..37d9f7c 100644 --- a/resources/lang/fr/validation.php +++ b/resources/lang/fr/validation.php @@ -108,7 +108,8 @@ return [ 'mixed' => 'Le champ :attribute doit contenir au moins une majuscule et une minuscule.', 'numbers' => 'Le champ :attribute doit contenir au moins un chiffre.', 'symbols' => 'Le champ :attribute doit contenir au moins un symbole.', - 'uncompromised' => 'La valeur du champ :attribute est apparue dans une fuite de données. Veuillez choisir une valeur différente.', + 'uncompromised' => 'La valeur du champ :attribute est apparue dans une fuite de données. + Veuillez choisir une valeur différente.', ], 'present' => 'Le champ :attribute doit être présent.', 'prohibited' => 'Le champ :attribute est interdit.', @@ -219,7 +220,6 @@ return [ :countries comme par exemple :examples', 'phone' => 'Le champ :attribute n\'est pas un numéro de téléphone valide, il accepte les numéros des pays suivants :countries.', - 'custom' => [ 'of_the' => [ 'male' => 'du ', @@ -237,7 +237,6 @@ return [ 'static_page_type' => 'type de page', 'seo_title' => 'titre SEO', 'seo_description' => 'description SEO', - 'ip' => 'IP', 'code' => 'code', 'unique_code' => 'code unique', @@ -260,7 +259,7 @@ return [ 'nb_participation_ip' => 'nombre de participation par adresse IP', 'nb_unique_code_use' => 'nombre d\'utilisation du code unique', 'code_length' => 'longueur des code uniques', - 'use_ean_codes' => 'activer les codes EAN', + 'code_type' => 'type de code', 'activated_teaser' => 'activation du teaser', 'activated_close_page' => 'activation page de fermeture', 'close_page_active_date' => 'page active jusqu\'au', @@ -274,7 +273,7 @@ return [ 'game_rules' => 'règlement du jeu', 'notify_prize_sent' => 'notification de lot envoyé au participant', 'comment' => 'commentaire', - + 'authorized_countries' => 'pays éligibles au jeu', 'barcode' => 'code-barres du produit', 'ticket' => 'ticket de caisse avec la date', 'sticker' => 'sticker du produit', diff --git a/resources/ts/components/SelectMultipleEnumComponent.vue b/resources/ts/components/SelectMultipleEnumComponent.vue new file mode 100644 index 0000000..26c0535 --- /dev/null +++ b/resources/ts/components/SelectMultipleEnumComponent.vue @@ -0,0 +1,261 @@ + + + + + diff --git a/resources/ts/pages/settings.ts b/resources/ts/pages/settings.ts index 6056a9d..689a426 100644 --- a/resources/ts/pages/settings.ts +++ b/resources/ts/pages/settings.ts @@ -14,6 +14,7 @@ document.addEventListener("DOMContentLoaded", function () { const inputDiscountsActivated = document.querySelector("input[name=discounts_activated]") as HTMLInputElement | null; const inputActivatedClosePage = document.querySelector("input[name=activated_close_page]") as HTMLInputElement | null; + const selectCodeType = document.querySelector("select[name=code_type]") as HTMLSelectElement | null; const discountsSite = document.getElementById("discounts-site") as HTMLElement | null; const discountsCampaign = document.getElementById("discounts-campaign") as HTMLElement | null; @@ -24,6 +25,7 @@ document.addEventListener("DOMContentLoaded", function () { const settingsClosePageActiveDate = document.getElementById("settings-close-page-active-date") as HTMLInputElement | null; const settingsDiscountsSite = document.getElementById("settings-discounts-site") as HTMLInputElement | null; const settingsDiscountsCampaign = document.getElementById("settings-discounts-campaign") as HTMLInputElement | null; + const settingsNbUniqueCodeUse = document.getElementById("settings-nb-unique-code-use") as HTMLElement | null; if (!inputDiscountsActivated) { throw new Error("Missing input[name=discounts_activated]"); @@ -31,6 +33,9 @@ document.addEventListener("DOMContentLoaded", function () { if (!inputActivatedClosePage) { throw new Error("Missing input[name=activated_close_page]"); } + if (!selectCodeType) { + throw new Error("Missing select[name=code_type]"); + } if (!discountsSite) { throw new Error("Missing #discounts-site"); } @@ -52,6 +57,9 @@ document.addEventListener("DOMContentLoaded", function () { if (!settingsDiscountsCampaign) { throw new Error("Missing #settings-discounts-campaign"); } + if (!settingsNbUniqueCodeUse) { + throw new Error("Missing #settings-nb-unique-code-use"); + } const discountsWebCouponsInputs = discountsWebCoupons.getElementsByTagName("input"); @@ -87,6 +95,11 @@ document.addEventListener("DOMContentLoaded", function () { } }; + const toggleNbUniqueCodeUse = (): void => { + const shouldHide = selectCodeType.value === settingsNbUniqueCodeUse.dataset.enumHide; + settingsNbUniqueCodeUse.classList.toggle("d-none", shouldHide); + }; + // Gestion du tableau pour discount inputDiscountsActivated.addEventListener("change", toggleDiscount); toggleDiscount(); @@ -94,4 +107,8 @@ document.addEventListener("DOMContentLoaded", function () { // Gestion de l'activation de la page de fermeture (champ date à afficher ou non) inputActivatedClosePage.addEventListener("change", toggleClosePage); toggleClosePage(); + + // Gestion du nombre d'utilisation du code unique + selectCodeType.addEventListener("change", toggleNbUniqueCodeUse); + toggleNbUniqueCodeUse(); }); diff --git a/resources/views/pages/dashboard/index.blade.php b/resources/views/pages/dashboard/index.blade.php index e92c469..9f3789e 100644 --- a/resources/views/pages/dashboard/index.blade.php +++ b/resources/views/pages/dashboard/index.blade.php @@ -13,7 +13,8 @@

{{ __('Nombre de participations total') }} : {{ $participationsTotal }}
{{ __('Nombre de participations moyenne par joueur') }} : {{ $avgParticipUser }}
- @if ($settings->use_ean_codes) + @php $codeTypeEnum = \App\Enums\CodeType::class; @endphp + @if ($settings->code_type->equals($codeTypeEnum::ean) || $settings->code_type->equals($codeTypeEnum::both)) {{ __('Top 3 des codes EAN les plus utilisés') }} :

    @foreach ($codesEan as $codeEan) diff --git a/resources/views/pages/settings/form-inputs.blade.php b/resources/views/pages/settings/form-inputs.blade.php index 8fecf62..f836c9b 100644 --- a/resources/views/pages/settings/form-inputs.blade.php +++ b/resources/views/pages/settings/form-inputs.blade.php @@ -11,7 +11,9 @@
  • {{ __('Longueur des codes uniques') }}  - {{ config('codes.codeLength') }} + + {{ config('codes.codeLength') }} +
  • {{ __('Longueur des codes uniques') }}  @@ -134,6 +136,27 @@ {{-- Force next columns to break to new line --}}
    + {{-- AUTHORIZED_COUNTRIES --}} +
    +
    + {{ str(__('validation.custom.authorized_countries'))->ucfirst() }} +
    + @php $itemsList = collect(\LDC\LDCCRM\Enums\Country::getValidCountries())->values(); @endphp + @include('back::partials.inputs.select-multiple-enum', [ + 'name' => 'authorized_countries', + 'label' => str(__('validation.custom.authorized_countries'))->ucfirst(), + 'placeholder' => str(__('rechercher parmi les :field', ['field' => __('validation.custom.authorized_countries')]))->ucfirst(), + 'model' => $settingsModel ?? null, + 'itemsList' => $itemsList, + 'itemLimit' => [0, count($itemsList)], + 'helper' => sprintf('%s %s', __('crud.minicrud.enum_select', [ + 'enum' => str(__('validation.custom.authorized_countries'))->ucfirst(), + ]), __('Laisser vide afin de ne pas appliquer de restrictions.')), + 'required' => false, + ]) +
    +
    +
    {{-- NB_PARTICIPATION_PLAYER --}}
    @@ -180,8 +203,29 @@
    - {{-- NB_UNIQUE_CODE_USE --}} + {{-- CODES TYPES --}}
    +
    + {{ str(__('validation.custom.code_type'))->ucfirst() }} +
    + @include('back::partials.inputs.tinyint-enum', [ + 'id' => 'codeType', + 'label' => str(__('validation.custom.code_type'))->ucfirst(), + 'name' => 'code_type', + 'model' => $settingsModel ?? null, + 'required' => true, + 'disabled' => false, + 'value' => optional(optional($settingsModel ?? null)->code_type)->value(), + 'associatedModels' => \App\Enums\CodeType::toArray(), + 'helper' => __('Sélectionner un :field pour le jeu', [ + 'field' => __('validation.custom.code_type'), + ]), + ]) +
    +
    +
    + {{-- NB_UNIQUE_CODE_USE --}} +
    {{ Str::ucfirst(__('validation.custom.nb_unique_code_use')) }}
    @@ -294,25 +338,6 @@ {{-- Force next columns to break to new line --}}
    - {{-- USE_EAN_CODES --}} -
    -
    - {{ Str::ucfirst(__('validation.custom.use_ean_codes')) }} -
    - @include('back::partials.inputs.tinyint-boolean', [ - 'id' => 'settings-use-ean-codes', - 'label' => Str::ucfirst(__('validation.custom.use_ean_codes')), - 'name' => 'use_ean_codes', - 'required' => false, - 'model' => $settingsModel ?? null, - 'helper' => __('validation.boolean', [ - 'attribute' => Str::ucfirst(__('validation.custom.use_ean_codes')), - ]), - ]) -
    -
    -
    - {{-- WINNING_PROOF --}}
    diff --git a/resources/views/pages/winners-list/index-list.blade.php b/resources/views/pages/winners-list/index-list.blade.php index 51be7ac..1ce07e9 100644 --- a/resources/views/pages/winners-list/index-list.blade.php +++ b/resources/views/pages/winners-list/index-list.blade.php @@ -7,10 +7,13 @@ 'email' => __('Email'), 'prize' => __('Lot'), ]; - if ($settingsGlobal->use_ean_codes) { + $codeTypeEnum = \App\Enums\CodeType::class; + if ($settingsGlobal->code_type->equals($codeTypeEnum::ean) || $settingsGlobal->code_type->equals($codeTypeEnum::both)) { $cols['ean_code_id'] = __('Code EAN'); } - $cols['code_unique_id'] = __('Code unique'); + if ($settingsGlobal->code_type->equals($codeTypeEnum::unique) || $settingsGlobal->code_type->equals($codeTypeEnum::both)) { + $cols['code_unique_id'] = __('Code unique'); + } $cols['price_sent'] = __('Gain envoyé ?'); if ($settingsGlobal->winning_proof) { $cols['proof'] = __('Preuves'); @@ -38,14 +41,16 @@ {{ __('N° :number', ['number' => $participationModel->instantWin->prize->id]) }} - @if ($settingsGlobal->use_ean_codes) + @if ($settingsGlobal->code_type->equals($codeTypeEnum::ean) || $settingsGlobal->code_type->equals($codeTypeEnum::both)) - {{ $participationModel->codeEan }} + {{ $participationModel->product->ean_code }} + + @endif + @if ($settingsGlobal->code_type->equals($codeTypeEnum::unique) || $settingsGlobal->code_type->equals($codeTypeEnum::both)) + + {{ $participationModel->code_unique_id }} @endif - - {{ $participationModel->code->code_unique }} - @can('changePrizeSent', $participationModel)
    $name, - "label" => $label, - "value" => $value ?? [], - "placeholder" => $placeholder, - "itemsSelected" => $itemsSelected, - "itemsList" => $itemsList, - "itemLimit" => $itemLimit, - "showSpecificItem" => $showSpecificItem ?? null, - "errors" => $errors->getBag("default")->getMessages() ?? [], - 'helper' => $helper, - 'require' => $require -]; + $values = collect(old($name, $model->{$name} ?? []))->values(); + $itemsSelected = collect($itemsList) + ->filter(fn($item) => $values->filter(fn($value) => intval($value) === $item->value)->count()) + ->values(); + $data = [ + "name" => $name, + "label" => $label, + "placeholder" => $placeholder, + "itemsSelected" => $itemsSelected, + "itemsList" => $itemsList, + "itemLimit" => $itemLimit, + "showSpecificItem" => $showSpecificItem ?? null, + "errors" => $errors->getBag("default")->getMessages() ?? [], + 'helper' => $helper, + 'required' => $required + ]; @endphp