From 8eef52ab0b5b5ece8a4c000a229f3898ac1fbd4a Mon Sep 17 00:00:00 2001 From: msquare Date: Mon, 16 Dec 2024 19:30:14 +0100 Subject: [PATCH 001/157] 38c3 theme improvements --- resources/assets/themes/theme18.scss | 40 ++++++++++++---------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/resources/assets/themes/theme18.scss b/resources/assets/themes/theme18.scss index bb4c1a0a0..02bc73c34 100644 --- a/resources/assets/themes/theme18.scss +++ b/resources/assets/themes/theme18.scss @@ -5,17 +5,6 @@ @import 'dark'; -:root { - --color-primary: #ff5053; - --color-highlight: #fef2ff; - --color-accent-a: #b2aaff; - --color-accent-b: #6a5fdb; - --color-accent-c: #29114c; - --color-accent-d: #261a66; - --color-accent-e: #190b2f; - --color-background: #0f000a; -} - //== changed Colors $gray-dark: #29114c; $gray-darker: #190b2f; @@ -38,7 +27,7 @@ $text-muted: $gray-light; $btn-link-disabled-color: $gray-light; -$dropdown-bg: #212529; +$dropdown-bg: $gray-darker; $dropdown-link-hover-color: #000000; //== changed Forms @@ -89,6 +78,9 @@ $nav-tabs-link-active-border-color: $gray-dark; $nav-tabs-link-active-color: $gray-darker; $nav-pills-link-active-color: $gray-darker; +$popover-bg: $gray-dark; + + //== Pagination // //## @@ -148,19 +140,21 @@ h1, // Specials =================================================================== -.bg-success a, -.bg-primary a, -.bg-secondary a, -.bg-warning a, -.bg-info a, -.bg-light a { - color: $gray-darker !important; +.bg-success a, a.bg-success, +.bg-primary a, a.bg-primary, +.bg-secondary a, a.bg-secondary, +.bg-warning a, a.bg-warning, +.bg-info a, a.bg-info, +.bg-light a a.bg-light, { + color: $gray-darker; + & :hover { + color: $gray-darker; + } } -.bg-body a, .bg-danger a, -.bg-dark a { - color: $state-danger-text !important; +.bg-dark > a { + color: $state-danger-text; } .bg-primary, @@ -175,7 +169,7 @@ h1, .bg-body, .bg-danger, .bg-dark { - color: $state-danger-text; + color: $body-color; } .navbar { From c911b293c34fccfeaf9dfe598da83b33acb999ba Mon Sep 17 00:00:00 2001 From: msquare Date: Mon, 16 Dec 2024 20:00:42 +0100 Subject: [PATCH 002/157] 38c3 theme fix highlighted news --- resources/assets/themes/theme18.scss | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/resources/assets/themes/theme18.scss b/resources/assets/themes/theme18.scss index 02bc73c34..de5d2d65c 100644 --- a/resources/assets/themes/theme18.scss +++ b/resources/assets/themes/theme18.scss @@ -11,7 +11,7 @@ $gray-darker: #190b2f; $gray: #6a5fdb; $gray-light: #b2aaff; $gray-lighter: #fef2ff; -$dark: $gray-dark; +$dark: $gray-darker; $primary: #ff5053; $secondary: #b2aaff; @@ -80,7 +80,6 @@ $nav-pills-link-active-color: $gray-darker; $popover-bg: $gray-dark; - //== Pagination // //## @@ -140,12 +139,17 @@ h1, // Specials =================================================================== -.bg-success a, a.bg-success, -.bg-primary a, a.bg-primary, -.bg-secondary a, a.bg-secondary, -.bg-warning a, a.bg-warning, -.bg-info a, a.bg-info, -.bg-light a a.bg-light, { +.bg-success > a, +a.badge.bg-success, +.bg-primary > a, +a.badge.bg-primary, +.bg-secondary > a, +a.badge.bg-secondary, +.bg-warning > a, +a.badge.bg-warning, +.bg-info > a, +a.badge.bg-info, +.bg-light > a a.badge.bg-light { color: $gray-darker; & :hover { color: $gray-darker; @@ -153,7 +157,7 @@ h1, } .bg-danger a, -.bg-dark > a { +.bg-dark a { color: $state-danger-text; } @@ -167,7 +171,6 @@ h1, } .bg-body, -.bg-danger, .bg-dark { color: $body-color; } @@ -227,3 +230,7 @@ body { :root { scrollbar-color: $secondary $gray-dark; } + +.shift-calendar .lane { + background-color: $body-bg; +} From 570b5e074e6f61b731a8c1b6b42f77e5d76f2061 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sat, 21 Dec 2024 12:42:51 +0100 Subject: [PATCH 003/157] Fix arrive redirects --- includes/pages/admin_arrive.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/pages/admin_arrive.php b/includes/pages/admin_arrive.php index f73504d5e..cf785f282 100644 --- a/includes/pages/admin_arrive.php +++ b/includes/pages/admin_arrive.php @@ -43,7 +43,7 @@ function admin_arrive() engelsystem_log('User set to not arrived: ' . User_Nick_render($user_source, true)); success(__('Reset done. Angel has not arrived.')); - throw_redirect(back()->getHeaderLine('redirect')); + throw_redirect(back()->getHeaderLine('location')); } else { $msg = error(__('Angel not found.'), true); } @@ -62,7 +62,7 @@ function admin_arrive() engelsystem_log('User set has arrived: ' . User_Nick_render($user_source, true)); success(__('Angel has been marked as arrived.')); - throw_redirect(back()->getHeaderLine('redirect')); + throw_redirect(back()->getHeaderLine('location')); } else { $msg = error(__('Angel not found.'), true); } From 203c9acb7ac922b03f939b9cf1195280513eac21 Mon Sep 17 00:00:00 2001 From: weeman Date: Wed, 4 Dec 2024 20:06:15 +0100 Subject: [PATCH 004/157] Add a plus one voucher shortcut button --- includes/controller/users_controller.php | 14 +++- includes/sys_page.php | 12 +++ includes/view/User_view.php | 50 +++++++++++- resources/assets/js/utils.js | 24 ++++++ resources/assets/js/vendor.js | 1 + resources/assets/js/voucher.js | 98 ++++++++++++++++++++++++ resources/lang/de_DE/default.po | 3 - resources/views/layouts/app.twig | 8 ++ 8 files changed, 201 insertions(+), 9 deletions(-) create mode 100644 resources/assets/js/utils.js create mode 100644 resources/assets/js/voucher.js diff --git a/includes/controller/users_controller.php b/includes/controller/users_controller.php index 9780b7bcc..8d2febfdd 100644 --- a/includes/controller/users_controller.php +++ b/includes/controller/users_controller.php @@ -180,12 +180,21 @@ function user_edit_vouchers_controller() $user_source->state->got_voucher = $vouchers; $user_source->state->save(); - success(__('Saved the number of vouchers.')); engelsystem_log(User_Nick_render($user_source, true) . ': ' . sprintf( 'Got %s vouchers', $user_source->state->got_voucher )); + if (in_array('application/json', $request->getAcceptableContentTypes())) { + // This was an async request, send a JSON response. + json_output([ + 'issued' => $user_source->state->got_voucher, + 'eligible' => $user_source->state->got_voucher + User_get_eligable_voucher_count($user_source), + 'total' => (int) State::query()->sum('got_voucher'), + ]); + } + + success(__('Saved the number of vouchers.')); throw_redirect(user_link($user_source->id)); } } @@ -359,7 +368,8 @@ function users_list_controller() State::whereForceActive(true)->count(), ShiftEntry::whereNotNull('freeloaded_by')->count(), State::whereGotGoodie(true)->count(), - State::query()->sum('got_voucher') + State::query()->sum('got_voucher'), + auth()->can('admin_user'), ), ]; } diff --git a/includes/sys_page.php b/includes/sys_page.php index dc9d597de..9bcd841e4 100644 --- a/includes/sys_page.php +++ b/includes/sys_page.php @@ -49,6 +49,18 @@ function parse_date($pattern, $value) return $datetime->getTimestamp(); } +/** + * Send a JSON response. + * + * @param array $data + */ +function json_output(array $data): void +{ + header('Content-Type: application/json; charset=utf-8'); + echo json_encode($data); + die(); +} + /** * Leitet den Browser an die übergebene URL weiter und hält das Script an. * diff --git a/includes/view/User_view.php b/includes/view/User_view.php index 9ddafd9ad..240d0550c 100644 --- a/includes/view/User_view.php +++ b/includes/view/User_view.php @@ -73,6 +73,7 @@ function User_edit_vouchers_view($user) * @param int $freeloads_count * @param int $goodies_count * @param int $voucher_count + * @param bool $admin_user_privilege * @return string */ function Users_view( @@ -83,13 +84,54 @@ function Users_view( $force_active_count, $freeloads_count, $goodies_count, - $voucher_count + $voucher_count, + $admin_user_privilege ) { + $auth = auth(); $goodie = GoodieType::from(config('goodie_type')); $goodie_enabled = $goodie !== GoodieType::None; $goodie_tshirt = $goodie === GoodieType::Tshirt; $usersList = []; foreach ($users as $user) { + $voucher_field = ''; + + if (config('enable_voucher')) { + $voucher_template = << +
+ {issued} / {eligible} +
+ {plus_one} + +EOT; + + if ($admin_user_privilege || $auth->can('voucher.edit')) { + $plus_one = << + +1 + +EOT; + + $voucher_template = strtr($voucher_template, [ + '{plus_one}' => $plus_one, + ]); + } + + $voucher_field = strtr( + $voucher_template, + [ + '{issued}' => $user->state->got_voucher, + '{eligible}' => $user->state->got_voucher + User_get_eligable_voucher_count($user), + '{user}' => $user->id, + '{amount}' => $user->state->got_voucher + 1, + ] + ); + } + $u = []; $u['name'] = User_Nick_render($user) . User_Pronoun_render($user) @@ -99,7 +141,7 @@ function Users_view( $u['dect'] = sprintf('%1$s', htmlspecialchars((string) $user->contact->dect)); $u['arrived'] = icon_bool($user->state->arrived); if (config('enable_voucher')) { - $u['got_voucher'] = $user->state->got_voucher; + $u['got_voucher'] = $voucher_field; } $u['freeloads'] = $user->getAttribute('freeloads'); $u['active'] = icon_bool($user->state->active); @@ -132,7 +174,7 @@ function Users_view( $usersList[] = [ 'name' => '' . __('Sum') . '', 'arrived' => $arrived_count, - 'got_voucher' => $voucher_count, + 'got_voucher' => '
' . $voucher_count . '
', 'active' => $active_count, 'force_active' => $force_active_count, 'freeloads' => $freeloads_count, @@ -154,7 +196,7 @@ function Users_view( } $user_table_headers['arrived'] = Users_table_header_link('arrived', __('Arrived'), $order_by); if (config('enable_voucher')) { - $user_table_headers['got_voucher'] = Users_table_header_link('got_voucher', __('Voucher'), $order_by); + $user_table_headers['got_voucher'] = Users_table_header_link('got_voucher', __('Vouchers'), $order_by); } $user_table_headers['freeloads'] = Users_table_header_link('freeloads', __('Freeloads'), $order_by); $user_table_headers['active'] = Users_table_header_link('active', __('user.active'), $order_by); diff --git a/resources/assets/js/utils.js b/resources/assets/js/utils.js new file mode 100644 index 000000000..ac7c17644 --- /dev/null +++ b/resources/assets/js/utils.js @@ -0,0 +1,24 @@ +let csrfToken; + +/** + * Returns the CSRF token. + * + * @throws {Error} - Raises an error if the csrf meta tag cannot be found or is empty + * @returns {string} CSRF token + */ +export const getCSRFToken = () => { + if (!csrfToken) { + const csrfTokenElement = document.querySelector('meta[name="csrf-token"]'); + if (!csrfTokenElement) { + throw new Error('Unable to find csrf-token meta element'); + } + + if (!csrfTokenElement.content) { + throw new Error('Got empty csrf-token meta element'); + } + + csrfToken = csrfTokenElement.content; + } + + return csrfToken; +}; diff --git a/resources/assets/js/vendor.js b/resources/assets/js/vendor.js index ef30ac178..62d2ed1d4 100644 --- a/resources/assets/js/vendor.js +++ b/resources/assets/js/vendor.js @@ -4,3 +4,4 @@ import './forms'; import './countdown'; import './dashboard'; import './design'; +import './voucher'; diff --git a/resources/assets/js/voucher.js b/resources/assets/js/voucher.js new file mode 100644 index 000000000..7c6d0a490 --- /dev/null +++ b/resources/assets/js/voucher.js @@ -0,0 +1,98 @@ +import { getCSRFToken } from './utils'; +import { ready } from './ready'; + +ready(() => { + // Add plus 1 voucher click handler to all plus 1 voucher buttons + document.querySelectorAll('[data-voucher-amount][data-voucher-user-id]').forEach((element) => { + element.addEventListener('click', handlePlus1VoucherClick); + }); +}); + +/** + * @typedef {Object} EditVoucherResponse + * @property {number} eligible + * @property {number} issued + * @property {number} total - Total number of issued vouchers + */ + +/** + * Send an async request to increase the number of vouchers issued. + * + * @param {number} userId - ID of the user whose voucher amount is to be updated + * @param {number} amount - Voucher amount to set + * + * @returns {Promise} + */ +const sendEditVoucherRequest = async (userId, amount) => { + const csrfToken = getCSRFToken(); + + const data = new FormData(); + data.append('submit', 'true'); + data.append('vouchers', amount); + + const response = await fetch(`/users?action=edit_vouchers&user_id=${userId}`, { + method: 'POST', + headers: { + Accept: 'application/json', + 'X-CSRF-TOKEN': csrfToken, + }, + body: data, + }); + + if (!response.ok) { + throw new Error(`Voucher update response not okay`, response); + } + + return await response.json(); +}; + +/** + * @param {MouseEvent} event + */ +const handlePlus1VoucherClick = async (event) => { + event.preventDefault(); + event.stopPropagation(); + + /** + * @type {HTMLButtonElement} + */ + const element = event.target; + const dataset = element.dataset; + + const amount = Number(dataset.voucherAmount); + const userId = Number(dataset.voucherUserId); + + if (Number.isInteger(userId) === false) { + console.error('User ID is not an integer', userId); + return; + } + + if (Number.isInteger(amount) === false) { + console.error('Voucher amount is not an integer', amount); + return; + } + + // Block user from multiple inputs + element.disabled = true; + + try { + const editVoucherResponse = await sendEditVoucherRequest(userId, amount); + + // Update user voucher numbers + element.parentNode.querySelector( + '[data-field="voucher-status"]' + ).textContent = `${editVoucherResponse.issued} / ${editVoucherResponse.eligible}`; + element.dataset.voucherAmount = editVoucherResponse.issued + 1; + + // Update total voucher count + const totalElement = document.getElementById('voucher-count'); + + if (totalElement !== null) { + totalElement.innerText = editVoucherResponse.total; + } + } catch (error) { + console.error('Error during update voucher request', error); + } finally { + element.disabled = false; + } +}; diff --git a/resources/lang/de_DE/default.po b/resources/lang/de_DE/default.po index 8027f6ca1..692d20eec 100644 --- a/resources/lang/de_DE/default.po +++ b/resources/lang/de_DE/default.po @@ -1103,9 +1103,6 @@ msgstr "Engel kann noch %d Gutscheine bekommen und ist FA." msgid "Number of vouchers given out" msgstr "Anzahl Gutscheine bekommen" -msgid "Voucher" -msgstr "Gutschein" - msgid "Freeloads" msgstr "Schwänzereien" diff --git a/resources/views/layouts/app.twig b/resources/views/layouts/app.twig index 025d15361..da3851312 100644 --- a/resources/views/layouts/app.twig +++ b/resources/views/layouts/app.twig @@ -22,6 +22,14 @@ {%- endif %} {% endblock %} + + From 32265e824574b9c97d1fd55f16ab07facc11b345 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sun, 15 Dec 2024 00:46:48 +0100 Subject: [PATCH 005/157] Make angeltypes restricted by default on creation --- src/Models/AngelType.php | 2 +- tests/Feature/Controllers/RegistrationControllerTest.php | 2 ++ tests/Unit/Controllers/Metrics/StatsTest.php | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Models/AngelType.php b/src/Models/AngelType.php index bae063dad..50927760d 100644 --- a/src/Models/AngelType.php +++ b/src/Models/AngelType.php @@ -57,7 +57,7 @@ class AngelType extends BaseModel 'contact_name' => '', 'contact_dect' => '', 'contact_email' => '', - 'restricted' => false, + 'restricted' => true, 'requires_driver_license' => false, 'requires_ifsg_certificate' => false, 'shift_self_signup' => true, diff --git a/tests/Feature/Controllers/RegistrationControllerTest.php b/tests/Feature/Controllers/RegistrationControllerTest.php index 052970d13..067c87ed1 100644 --- a/tests/Feature/Controllers/RegistrationControllerTest.php +++ b/tests/Feature/Controllers/RegistrationControllerTest.php @@ -213,6 +213,7 @@ private function createAngelTypes(): array { $angelType1 = AngelType::create([ 'name' => 'Test angel type 1', + 'restricted' => false, ]); $angelType2 = AngelType::create([ @@ -228,6 +229,7 @@ private function createAngelTypes(): array $angelType4 = AngelType::create([ 'name' => 'Test angel type 4', 'hide_register' => true, + 'restricted' => false, ]); $this->modelsToBeDeleted[] = $angelType1; diff --git a/tests/Unit/Controllers/Metrics/StatsTest.php b/tests/Unit/Controllers/Metrics/StatsTest.php index 1c61e6715..e23b28ea2 100644 --- a/tests/Unit/Controllers/Metrics/StatsTest.php +++ b/tests/Unit/Controllers/Metrics/StatsTest.php @@ -216,9 +216,9 @@ public function testLocations(): void public function testAngelTypes(): void { (new AngelType(['id' => 1, 'name' => 'AngelType 1', 'restricted' => true]))->save(); - (new AngelType(['id' => 2, 'name' => 'Second AngelType']))->save(); + (new AngelType(['id' => 2, 'name' => 'Second AngelType', 'restricted' => false]))->save(); (new AngelType(['id' => 3, 'name' => 'Another AngelType', 'restricted' => true]))->save(); - (new AngelType(['id' => 4, 'name' => 'Old AngelType']))->save(); + (new AngelType(['id' => 4, 'name' => 'Old AngelType', 'restricted' => false]))->save(); UserAngelType::factory()->create(['angel_type_id' => 1, 'confirm_user_id' => 1, 'supporter' => true]); UserAngelType::factory()->create(['angel_type_id' => 1, 'confirm_user_id' => null, 'supporter' => false]); UserAngelType::factory()->create(['angel_type_id' => 1, 'confirm_user_id' => 1, 'supporter' => false]); From 9a6bdfaa40f58cd604ea061771e1a953a5cd0b45 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sun, 15 Dec 2024 00:57:31 +0100 Subject: [PATCH 006/157] Angeltype edit: Move state to left of the form --- includes/view/AngelTypes_view.php | 113 ++++++++++++++++-------------- 1 file changed, 61 insertions(+), 52 deletions(-) diff --git a/includes/view/AngelTypes_view.php b/includes/view/AngelTypes_view.php index ed3847518..d5c03ef13 100644 --- a/includes/view/AngelTypes_view.php +++ b/includes/view/AngelTypes_view.php @@ -130,60 +130,69 @@ function AngelType_edit_view(AngelType $angeltype, bool $supporter_mode) ]) : '', msg(), form([ - $supporter_mode - ? form_info(__('general.name'), htmlspecialchars($angeltype->name)) - : form_text('name', __('general.name'), $angeltype->name, false, 255), - $supporter_mode - ? form_info(__('angeltypes.restricted'), $angeltype->restricted ? __('Yes') : __('No')) - : form_checkbox( - 'restricted', - __('angeltypes.restricted') . - ' ', - $angeltype->restricted - ), - $supporter_mode - ? form_info(__('shift.self_signup'), $angeltype->shift_self_signup ? __('Yes') : __('No')) - : form_checkbox( - 'shift_self_signup', - __('shift.self_signup') . - ' ', - $angeltype->shift_self_signup - ), - $requires_driving_license, - $requires_ifsg, - $supporter_mode - ? form_info(__('Show on dashboard'), $angeltype->show_on_dashboard ? __('Yes') : __('No')) - : form_checkbox('show_on_dashboard', __('Show on dashboard'), $angeltype->show_on_dashboard), - $supporter_mode - ? form_info(__('Hide at Registration'), $angeltype->hide_register ? __('Yes') : __('No')) - : form_checkbox('hide_register', __('Hide at Registration'), $angeltype->hide_register), - $supporter_mode - ? form_info( - __('angeltypes.hide_on_shift_view'), - $angeltype->hide_on_shift_view ? __('Yes') : __('No') - ) - : form_checkbox( - 'hide_on_shift_view', - __('angeltypes.hide_on_shift_view') . - ' ', - $angeltype->hide_on_shift_view - ), - form_textarea('description', __('general.description'), $angeltype->description), - form_info('', __('Please use markdown for the description.')), - heading(__('Contact'), 3), - form_info( - '', - __('Primary contact person/desk for user questions.') - ), - form_text('contact_name', __('general.name'), $angeltype->contact_name), - config('enable_dect') ? form_text('contact_dect', __('general.dect'), $angeltype->contact_dect) : '', - form_text('contact_email', __('general.email'), $angeltype->contact_email), + div('row', [ + div('col-md-9', [ + $supporter_mode + ? form_info(__('general.name'), htmlspecialchars($angeltype->name)) + : form_text('name', __('general.name'), $angeltype->name, false, 255), + form_textarea('description', __('general.description'), $angeltype->description), + form_info('', __('Please use markdown for the description.')), + heading(__('Contact'), 3), + form_info( + '', + __('Primary contact person/desk for user questions.') + ), + form_text('contact_name', __('general.name'), $angeltype->contact_name), + config('enable_dect') ? form_text('contact_dect', __('general.dect'), $angeltype->contact_dect) : '', + form_text('contact_email', __('general.email'), $angeltype->contact_email), + ]), + + div('col-md-3', [ + heading(__('State'), 3), + $supporter_mode + ? form_info(__('angeltypes.restricted'), $angeltype->restricted ? __('Yes') : __('No')) + : form_checkbox( + 'restricted', + __('angeltypes.restricted') . + ' ', + $angeltype->restricted + ), + $supporter_mode + ? form_info(__('shift.self_signup'), $angeltype->shift_self_signup ? __('Yes') : __('No')) + : form_checkbox( + 'shift_self_signup', + __('shift.self_signup') . + ' ', + $angeltype->shift_self_signup + ), + $requires_driving_license, + $requires_ifsg, + $supporter_mode + ? form_info(__('Show on dashboard'), $angeltype->show_on_dashboard ? __('Yes') : __('No')) + : form_checkbox('show_on_dashboard', __('Show on dashboard'), $angeltype->show_on_dashboard), + $supporter_mode + ? form_info(__('Hide at Registration'), $angeltype->hide_register ? __('Yes') : __('No')) + : form_checkbox('hide_register', __('Hide at Registration'), $angeltype->hide_register), + $supporter_mode + ? form_info( + __('angeltypes.hide_on_shift_view'), + $angeltype->hide_on_shift_view ? __('Yes') : __('No') + ) + : form_checkbox( + 'hide_on_shift_view', + __('angeltypes.hide_on_shift_view') . + ' ', + $angeltype->hide_on_shift_view + ), + ]), + ]), form_submit('submit', icon('save') . __('form.save')), ]), - ] + ], + true ); } From c462208c4dc53de6755aaa5ba994d19450396109 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sun, 15 Dec 2024 01:26:37 +0100 Subject: [PATCH 007/157] Rearrange shift edit to match create --- includes/controller/shifts_controller.php | 65 +++++++++++++++-------- resources/lang/de_DE/default.po | 6 --- 2 files changed, 44 insertions(+), 27 deletions(-) diff --git a/includes/controller/shifts_controller.php b/includes/controller/shifts_controller.php index 040f9fbe3..586d17391 100644 --- a/includes/controller/shifts_controller.php +++ b/includes/controller/shifts_controller.php @@ -107,14 +107,14 @@ function shift_edit_controller() error(__('Please select a shift type.')); } - if ($request->has('start') && $tmp = DateTime::createFromFormat('Y-m-d H:i', $request->input('start'))) { + if ($request->has('start') && $tmp = DateTime::createFromFormat('Y-m-d\TH:i', $request->input('start'))) { $start = $tmp; } else { $valid = false; error(__('Please enter a valid starting time for the shifts.')); } - if ($request->has('end') && $tmp = DateTime::createFromFormat('Y-m-d H:i', $request->input('end'))) { + if ($request->has('end') && $tmp = DateTime::createFromFormat('Y-m-d\TH:i', $request->input('end'))) { $end = $tmp; } else { $valid = false; @@ -190,13 +190,16 @@ function shift_edit_controller() $angel_types_spinner = ''; foreach ($angeltypes as $angeltype_id => $angeltype_name) { - $angel_types_spinner .= form_spinner( - 'angeltype_count_' . $angeltype_id, - htmlspecialchars($angeltype_name), - $needed_angel_types[$angeltype_id], - [], - (bool) ScheduleShift::whereShiftId($shift->id)->first(), - ); + $angel_types_spinner .= + '
' + . form_spinner( + 'angeltype_count_' . $angeltype_id, + htmlspecialchars($angeltype_name), + $needed_angel_types[$angeltype_id], + [], + (bool) ScheduleShift::whereShiftId($shift->id)->first(), + ) + . '
'; } $link = button(url('/shifts', ['action' => 'view', 'shift_id' => $shift_id]), icon('chevron-left'), 'btn-sm', '', __('general.back')); @@ -208,18 +211,38 @@ function shift_edit_controller() . info(__('This page is much more comfortable with javascript.'), true) . '', form([ - form_select('shifttype_id', __('Shift type'), $shifttypes, $shifttype_id), - form_text('title', __('title.title'), $title), - form_select('rid', __('Location:'), $locations, $rid), - form_text('start', __('Start:'), $start->format('Y-m-d H:i')), - form_text('end', __('End:'), $end->format('Y-m-d H:i')), - form_textarea('description', __('Additional description'), $description), - form_info( - '', - __('This description is for single shifts, otherwise please use the description in shift type.') - ), - '

' . __('Needed angels') . '

', - $angel_types_spinner, + div('row', [ + div('col-md-6 col-xl-5', [ + form_select('shifttype_id', __('Shift type'), $shifttypes, $shifttype_id), + form_text('title', __('title.title'), $title), + form_select('rid', __('Location'), $locations, $rid), + ]), + div('col-md-6 col-xl-7', [ + form_textarea('description', __('Additional description'), $description), + form_info( + '', + __('This description is for single shifts, otherwise please use the description in shift type.') + ), + ]), + ]), + div('row', [ + div('col-md-6 col-xl-5', [ + div('row', [ + div('col-lg-6', [ + form_datetime('start', __('shifts.start'), $start), + ]), + div('col-lg-6', [ + form_datetime('end', __('shifts.end'), $end), + ]), + ]), + ]), + div('col-md-6 col-xl-7', [ + '

' . __('Needed angels') . '

', + div('row', [ + $angel_types_spinner, + ]), + ]), + ]), form_submit('submit', icon('save') . __('form.save')), ]), ] diff --git a/resources/lang/de_DE/default.po b/resources/lang/de_DE/default.po index 692d20eec..8584d23fa 100644 --- a/resources/lang/de_DE/default.po +++ b/resources/lang/de_DE/default.po @@ -282,12 +282,6 @@ msgstr "Titel" msgid "Location:" msgstr "Ort:" -msgid "Start:" -msgstr "Start:" - -msgid "End:" -msgstr "Ende:" - msgid "Needed angels" msgstr "Benötigte Engel" From 2e1a564d88e1bcbedf17da3cca92af246e0349a0 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sun, 15 Dec 2024 01:41:00 +0100 Subject: [PATCH 008/157] Add link to Teams-/Job description on registration --- resources/lang/de_DE/default.po | 5 +++++ resources/lang/en_US/default.po | 10 +++++++--- resources/views/pages/registration.twig | 3 +++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/resources/lang/de_DE/default.po b/resources/lang/de_DE/default.po index 8584d23fa..2d1a949ac 100644 --- a/resources/lang/de_DE/default.po +++ b/resources/lang/de_DE/default.po @@ -1984,6 +1984,11 @@ msgstr "Eventdaten" msgid "registration.what_todo" msgstr "Was möchtest Du machen?" +msgid "registration.jobs" +msgstr "" +"Weitere Informationen wie Du uns helfen kannst findest du in der " +"Teams-/Aufgabenbeschreibung." + msgid "registration.register" msgstr "Registrieren" diff --git a/resources/lang/en_US/default.po b/resources/lang/en_US/default.po index 81f00f255..0f065934d 100644 --- a/resources/lang/en_US/default.po +++ b/resources/lang/en_US/default.po @@ -738,9 +738,6 @@ msgid "angeltypes.hide_on_shift_view.info" msgstr "If checked only admins and members of the angel type " "can see the filter option for this angel type on the shifts page" -msgid "registration.register" -msgstr "Register" - msgid "location.location" msgstr "Location" @@ -804,6 +801,13 @@ msgstr "Event data" msgid "registration.what_todo" msgstr "What do you want to do?" +msgid "registration.jobs" +msgstr "You can find more information about how to help us in the " +"Teams-/Job description." + +msgid "registration.register" +msgstr "Register" + msgid "tshirt.required.hint" msgstr "Please specify a T-shirt size in your settings!" diff --git a/resources/views/pages/registration.twig b/resources/views/pages/registration.twig index 463ff76d5..7be788c6f 100644 --- a/resources/views/pages/registration.twig +++ b/resources/views/pages/registration.twig @@ -274,6 +274,9 @@

{{ __('registration.what_todo') }}

+
+

{{ __('registration.jobs', [url('/angeltypes/about')])|raw }}

+
{% for angelType in angelTypes %}
From 54785f82c419dee969674588e78d26c332fe8cf7 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sun, 15 Dec 2024 02:07:29 +0100 Subject: [PATCH 009/157] Allow setting as supporter when adding an angel --- .../controller/user_angeltypes_controller.php | 15 ++++++++++++++- includes/view/UserAngelTypes_view.php | 3 +++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/includes/controller/user_angeltypes_controller.php b/includes/controller/user_angeltypes_controller.php index 68196fd63..fde003086 100644 --- a/includes/controller/user_angeltypes_controller.php +++ b/includes/controller/user_angeltypes_controller.php @@ -367,7 +367,20 @@ function user_angeltype_add_controller(): array )); success(sprintf(__('User %s added to %s.'), $user_source->displayName, $angeltype->name)); - if ($request->hasPostData('auto_confirm_user')) { + $setSupporter = $request->hasPostData('set_supporter') + && (auth()->can('admin_angel_types') || config('supporters_can_promote')); + if ($setSupporter) { + $userAngelType->supporter = true; + $userAngelType->save(); + + engelsystem_log(sprintf( + 'User %s set as supporter for %s.', + User_Nick_render($user_source, true), + AngelType_name_render($angeltype, true) + )); + } + + if ($request->hasPostData('auto_confirm_user') || $setSupporter) { $userAngelType->confirmUser()->associate($user_source); $userAngelType->save(); diff --git a/includes/view/UserAngelTypes_view.php b/includes/view/UserAngelTypes_view.php index a1e093f23..82e6d9dcb 100644 --- a/includes/view/UserAngelTypes_view.php +++ b/includes/view/UserAngelTypes_view.php @@ -151,6 +151,9 @@ function UserAngelType_add_view(AngelType $angeltype, $users_select, $user_id) $angeltype->restricted ? form_checkbox('auto_confirm_user', __('Confirm user'), true) : '', + auth()->can('admin_angel_types') || config('supporters_can_promote') + ? form_checkbox('set_supporter', __('Supporter'), false) + : '', form_select('user_id', __('general.user'), $users_select, $user_id), form_submit('submit', icon('plus-lg') . __('general.add')), ]), From e3b32702a2440f953932ed9023e540f1045e7c6b Mon Sep 17 00:00:00 2001 From: Brent Edwards Date: Fri, 6 Oct 2023 10:46:41 -0700 Subject: [PATCH 010/157] Added translations for comments --- includes/pages/admin_shifts.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/includes/pages/admin_shifts.php b/includes/pages/admin_shifts.php index c4ba056e5..e5b5b873a 100644 --- a/includes/pages/admin_shifts.php +++ b/includes/pages/admin_shifts.php @@ -76,13 +76,13 @@ function admin_shifts() error(__('Please select a shift type.')); } - // Name/Bezeichnung der Schicht, darf leer sein + // Name/designation of the shift, may be empty $title = substr(strip_request_item('title'), 0, 255); - // Beschreibung der Schicht, darf leer sein + // Description of the shift, may be empty $description = strip_request_item_nl('description'); - // Auswahl der sichtbaren Locations für die Schichten + // Selection of the visible locations for the layers if ( $request->has('lid') && preg_match('/^\d+$/', $request->input('lid')) @@ -138,7 +138,7 @@ function admin_shifts() 'trim', explode(',', $request->input('change_hours')) ); - // Fehlende Minutenangaben ergänzen, 24 Uhr -> 00 Uhr + // Add missing minutes, 24:00 -> 00:00 array_walk($change_hours, function (&$value) use ($valid) { // Add minutes if (!preg_match('/^(\d{1,2}):\d{2}$/', $value)) { @@ -209,12 +209,12 @@ function admin_shifts() error(__('Please select needed angels.')); } - // Beim Zurück-Knopf das Formular zeigen + // Show the form when you press the back button if ($request->has('back')) { $valid = false; } - // Alle Eingaben in Ordnung + // All entries OK if ($valid) { if ($angelmode == 'shift_type') { $needed_angel_types = NeededAngelType::whereShiftTypeId($shifttype_id) From 137e5345e0c475e3570d9cd2c452f19abd1d1c26 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Mon, 16 Dec 2024 22:32:25 +0100 Subject: [PATCH 011/157] Set location url max length --- resources/lang/de_DE/default.po | 2 +- resources/lang/en_US/default.po | 2 +- resources/views/admin/locations/edit.twig | 1 + src/Controllers/Admin/LocationsController.php | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/resources/lang/de_DE/default.po b/resources/lang/de_DE/default.po index 2d1a949ac..d72f7eae2 100644 --- a/resources/lang/de_DE/default.po +++ b/resources/lang/de_DE/default.po @@ -54,7 +54,7 @@ msgid "page.419.title" msgstr "Autorisierung ist abgelaufen" msgid "page.419.text" -msgstr "Das angegebene CSRF Token ist ungültig oder abgelaufen" +msgstr "Das angegebene CSRF Token ist ungültig oder abgelaufen. Bitte versuche es erneut." msgid "general.date" msgstr "d.m.Y" diff --git a/resources/lang/en_US/default.po b/resources/lang/en_US/default.po index 0f065934d..3f13d3930 100644 --- a/resources/lang/en_US/default.po +++ b/resources/lang/en_US/default.po @@ -73,7 +73,7 @@ msgid "page.419.title" msgstr "Authentication expired" msgid "page.419.text" -msgstr "The provided CSRF token is invalid or has expired" +msgstr "The provided CSRF token is invalid or has expired. Please try again." msgid "credits.credit" msgstr "" diff --git a/resources/views/admin/locations/edit.twig b/resources/views/admin/locations/edit.twig index b68c21653..76157b4b0 100644 --- a/resources/views/admin/locations/edit.twig +++ b/resources/views/admin/locations/edit.twig @@ -25,6 +25,7 @@ 'type': 'url', 'value': f.formData('map_url', location ? location.map_url : ''), 'info': __('location.map_url.info'), + 'max_length': 300, }) }} {{ f.textarea('description', __('general.description'), { diff --git a/src/Controllers/Admin/LocationsController.php b/src/Controllers/Admin/LocationsController.php index c0c43cc7b..b4fc2b97a 100644 --- a/src/Controllers/Admin/LocationsController.php +++ b/src/Controllers/Admin/LocationsController.php @@ -85,7 +85,7 @@ public function save(Request $request): Response 'name' => 'required|max:35', 'description' => 'optional', 'dect' => 'optional', - 'map_url' => 'optional|url', + 'map_url' => 'optional|url|max:300', ] + $validation ); From c975d2cd2171f2cecae6b56c40005a192552d882 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sat, 21 Dec 2024 12:58:14 +0100 Subject: [PATCH 012/157] Update not arrived message --- resources/lang/de_DE/default.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/lang/de_DE/default.po b/resources/lang/de_DE/default.po index d72f7eae2..cc36ca4ea 100644 --- a/resources/lang/de_DE/default.po +++ b/resources/lang/de_DE/default.po @@ -974,7 +974,7 @@ msgid "ended" msgstr "vorbei" msgid "please arrive for signup" -msgstr "Ankommen zum Eintragen" +msgstr "Komme an um dich einzutragen" msgid "not yet possible" msgstr "noch nicht möglich" From fb60cb5241a01ac6e786af9cf24fbc0d70dc075e Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Mon, 23 Dec 2024 13:45:00 +0100 Subject: [PATCH 013/157] Fixed freeload metrics & flaky tests --- src/Controllers/Metrics/Stats.php | 4 ++-- tests/Unit/Controllers/Api/ShiftsControllerTest.php | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Controllers/Metrics/Stats.php b/src/Controllers/Metrics/Stats.php index 14dad0f3d..b1947ce9d 100644 --- a/src/Controllers/Metrics/Stats.php +++ b/src/Controllers/Metrics/Stats.php @@ -216,8 +216,8 @@ protected function workSecondsQuery(bool $done = null, bool $freeloaded = null): if (!is_null($freeloaded)) { $freeloaded - ? $query->whereNull('freeloaded_by') - : $query->whereNotNull('freeloaded_by'); + ? $query->whereNotNull('freeloaded_by') + : $query->whereNull('freeloaded_by'); } if (!is_null($done)) { diff --git a/tests/Unit/Controllers/Api/ShiftsControllerTest.php b/tests/Unit/Controllers/Api/ShiftsControllerTest.php index 1d2788f11..72fac83cd 100644 --- a/tests/Unit/Controllers/Api/ShiftsControllerTest.php +++ b/tests/Unit/Controllers/Api/ShiftsControllerTest.php @@ -279,16 +279,18 @@ public function setUp(): void ShiftEntry::factory(2)->create([ 'shift_id' => $this->shiftB->id, 'angel_type_id' => $byLocation->angel_type_id, + 'freeloaded_by' => null, ]); // By shift type via schedule ShiftEntry::factory(3)->create([ 'shift_id' => $this->shiftC->id, 'angel_type_id' => $byShiftType->angel_type_id, + 'freeloaded_by' => null, ]); // Additional (not required by shift nor location) - ShiftEntry::factory(5)->create(['shift_id' => $this->shiftA->id]); + ShiftEntry::factory(5)->create(['shift_id' => $this->shiftA->id, 'freeloaded_by' => null]); foreach (User::all() as $user) { // Generate user data From b8810c65cad0d1fdafa306ba4a4e26e92721084f Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Tue, 24 Dec 2024 00:33:20 +0100 Subject: [PATCH 014/157] Show oauth menu and entry when hidden provider is connected --- resources/views/pages/settings/oauth.twig | 2 +- src/Controllers/SettingsController.php | 5 +++-- .../Unit/Controllers/SettingsControllerTest.php | 17 +++++++++++++++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/resources/views/pages/settings/oauth.twig b/resources/views/pages/settings/oauth.twig index 9fd439098..8acc3645c 100644 --- a/resources/views/pages/settings/oauth.twig +++ b/resources/views/pages/settings/oauth.twig @@ -20,7 +20,7 @@ {% for name,config in providers %} - + {% if config.url|default %} diff --git a/src/Controllers/SettingsController.php b/src/Controllers/SettingsController.php index 4d6262db0..4e72f06bc 100644 --- a/src/Controllers/SettingsController.php +++ b/src/Controllers/SettingsController.php @@ -423,8 +423,9 @@ public function settingsMenu(): array protected function checkOauthHidden(): bool { - foreach (config('oauth') as $config) { - if (empty($config['hidden'])) { + $userServices = $this->auth->user()->oauth; + foreach (config('oauth') as $name => $config) { + if (empty($config['hidden']) || $userServices->contains('provider', $name)) { return false; } } diff --git a/tests/Unit/Controllers/SettingsControllerTest.php b/tests/Unit/Controllers/SettingsControllerTest.php index 17ab2dc60..17bff5bef 100644 --- a/tests/Unit/Controllers/SettingsControllerTest.php +++ b/tests/Unit/Controllers/SettingsControllerTest.php @@ -17,6 +17,7 @@ use Engelsystem\Http\UrlGenerator; use Engelsystem\Http\Validation\Validator; use Engelsystem\Models\AngelType; +use Engelsystem\Models\OAuth; use Engelsystem\Models\Session as SessionModel; use Engelsystem\Models\User\License; use Engelsystem\Models\User\Settings; @@ -1000,6 +1001,22 @@ public function testSettingsMenuWithOAuth(): void $this->assertEquals(['title' => 'settings.oauth', 'hidden' => true], $menu['http://localhost/settings/oauth']); } + /** + * @covers \Engelsystem\Controllers\SettingsController::checkOauthHidden + */ + public function testSettingsMenuWithOAuthShownWhenConnected(): void + { + // Provider configured as hidden + $providersHidden = ['foo' => ['lorem' => 'ipsum', 'hidden' => true]]; + config(['oauth' => $providersHidden]); + + OAuth::factory()->create(['provider' => 'foo', 'user_id' => $this->user->id]); + + $menu = $this->controller->settingsMenu(); + $this->assertArrayHasKey('http://localhost/settings/oauth', $menu); + $this->assertEquals(['title' => 'settings.oauth', 'hidden' => false], $menu['http://localhost/settings/oauth']); + } + /** * @covers \Engelsystem\Controllers\SettingsController::settingsMenu */ From 9019aa57839de03cbf5eed03c0efc508a2964f3a Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Tue, 24 Dec 2024 01:13:47 +0100 Subject: [PATCH 015/157] Schedule creation: Show form content on validation errors --- resources/views/admin/schedule/edit.twig | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/resources/views/admin/schedule/edit.twig b/resources/views/admin/schedule/edit.twig index b8c5749f4..34a555a66 100644 --- a/resources/views/admin/schedule/edit.twig +++ b/resources/views/admin/schedule/edit.twig @@ -23,32 +23,32 @@
{{ f.input('name', __('schedule.name'), { 'required': true, - 'value': schedule.name, + 'value': f.formData('name', schedule.name), 'max_length': 255, }) }} {{ f.input('url', __('schedule.url'), { 'type': 'url', 'required': true, - 'value': schedule.url + 'value': f.formData('url', schedule.url) }) }} {{ f.select('shift_type', __('schedule.shift-type'), shift_types|default([]), { - 'selected': schedule.shift_type, + 'selected': f.formData('shift_type', schedule.shift_type) ~ '', }) }} {{ f.checkbox('needed_from_shift_type', __('schedule.needed-from-shift-type'), { - 'checked': schedule.needed_from_shift_type, + 'checked': f.formData('needed_from_shift_type', schedule.needed_from_shift_type), }) }} {{ f.input('minutes_before', __('schedule.minutes-before'), { 'type': 'number', 'required': true, - 'value': schedule.id ? schedule.minutes_before : 15 + 'value': f.formData('minutes_before', schedule.id ? schedule.minutes_before : 15) }) }} {{ f.input('minutes_after', __('schedule.minutes-after'), { 'type': 'number', 'required': true, - 'value': schedule.id ? schedule.minutes_after : 15 + 'value': f.formData('minutes_after', schedule.id ? schedule.minutes_after : 15) }) }} {{ f.save(__('form.save')) }} @@ -68,7 +68,7 @@ {{ f.checkbox( 'location_' ~ id, name, - {'checked': schedule.id and id in schedule.activeLocations.pluck('id')} + {'checked': f.formData('location_' ~ id, id in schedule.activeLocations.pluck('id'))} ) }}
{% endfor %} From 4df29128322cc8f558b4f93b85ae275d1a631ecc Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Tue, 24 Dec 2024 01:40:44 +0100 Subject: [PATCH 016/157] Show total requested t-shirts/goodies --- includes/pages/admin_active.php | 20 ++++++++++++++------ resources/lang/de_DE/default.po | 3 +++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/includes/pages/admin_active.php b/includes/pages/admin_active.php index 585f0667e..c791eb345 100644 --- a/includes/pages/admin_active.php +++ b/includes/pages/admin_active.php @@ -400,24 +400,31 @@ function admin_active() } $goodie_statistics = []; + $total = 0; if ($goodie_tshirt) { foreach (array_keys($tshirt_sizes) as $size) { - $gc = State::query() + $query = State::query() ->leftJoin('users_settings', 'users_state.user_id', '=', 'users_settings.user_id') ->leftJoin('users_personal_data', 'users_state.user_id', '=', 'users_personal_data.user_id') - ->where('users_state.got_goodie', '=', true) ->where('users_personal_data.shirt_size', '=', $size) - ->count(); + ; + $given = $query->clone()->where('users_state.got_goodie', true)->count(); + $notGiven = $query->clone()->where('users_state.got_goodie', false)->count(); + + $totalSum = $given + $notGiven; + $total += $totalSum; $goodie_statistics[] = [ 'size' => $size, - 'given' => $gc, + 'given' => $given, + 'total' => $totalSum, ]; } } $goodie_statistics[] = array_merge( ($goodie_tshirt ? ['size' => '' . __('Sum') . ''] : []), - ['given' => '' . State::whereGotGoodie(true)->count() . ''] + ['given' => '' . State::whereGotGoodie(true)->count() . ''], + ['total' => '' . $total . ''], ); return page_with_title(admin_active_title(), [ @@ -460,7 +467,8 @@ function admin_active() $goodie_enabled ? '

' . ($goodie_tshirt ? __('T-shirt statistic') : __('Goodie statistic')) . '

' : '', $goodie_enabled ? table(array_merge( ($goodie_tshirt ? ['size' => __('Size')] : []), - ['given' => $goodie_tshirt ? __('Given T-shirts') : __('Given goodies')] + ['given' => $goodie_tshirt ? __('Given T-shirts') : __('Given goodies')], + $goodie_tshirt ? ['total' => __('Configured T-shirts')] : [], ), $goodie_statistics) : '', ]); } diff --git a/resources/lang/de_DE/default.po b/resources/lang/de_DE/default.po index cc36ca4ea..c23b9f5e8 100644 --- a/resources/lang/de_DE/default.po +++ b/resources/lang/de_DE/default.po @@ -527,6 +527,9 @@ msgstr "T-Shirt Statistik" msgid "Given T-shirts" msgstr "Ausgegebene T-Shirts" +msgid "Configured T-shirts" +msgstr "Konfigurierte T-Shirts" + msgid "Arrive angels" msgstr "Ankommende Engel" From 45f9e1134eb86073fb123a375be4f7a16b03c666 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Wed, 25 Dec 2024 13:19:07 +0100 Subject: [PATCH 017/157] Show info that user is already in a shift when adding --- includes/controller/shift_entries_controller.php | 5 ++++- resources/lang/de_DE/default.po | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/includes/controller/shift_entries_controller.php b/includes/controller/shift_entries_controller.php index 3eded31a7..5791fb7f5 100644 --- a/includes/controller/shift_entries_controller.php +++ b/includes/controller/shift_entries_controller.php @@ -113,13 +113,16 @@ function shift_entry_create_controller_admin(Shift $shift, ?AngelType $angeltype } /** @var User[]|Collection $users */ - $users = User::with('userAngelTypes')->orderBy('name')->get(); + $users = User::with(['userAngelTypes', 'shiftEntries'])->orderBy('name')->get(); $users_select = []; foreach ($users as $user) { $name = $user->displayName; if ($user->userAngelTypes->where('id', $angeltype->id)->isEmpty()) { $name = __('%s (not "%s")', [$name, $angeltype->name]); } + if ($user->shiftEntries->where('shift_id', $shift->id)->isNotEmpty()) { + $name = __('%s (already in shift)', [$name]); + } $users_select[$user->id] = $name; } diff --git a/resources/lang/de_DE/default.po b/resources/lang/de_DE/default.po index c23b9f5e8..e18557b73 100644 --- a/resources/lang/de_DE/default.po +++ b/resources/lang/de_DE/default.po @@ -192,6 +192,9 @@ msgstr "Team %s" msgid "%s (not \"%s\")" msgstr "%s (kein \"%s\")" +msgid "%s (already in shift)" +msgstr "%s (bereits eingetragen)" + msgid "form.view" msgstr "Ansehen" From 8f04f68a16113092480e17c702c380706998cbd4 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Wed, 25 Dec 2024 23:26:59 +0100 Subject: [PATCH 018/157] Link FAQ on angeltypes about page --- resources/lang/de_DE/default.po | 3 ++- resources/lang/en_US/default.po | 3 ++- resources/views/pages/angeltypes/about.twig | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/resources/lang/de_DE/default.po b/resources/lang/de_DE/default.po index e18557b73..a873baabf 100644 --- a/resources/lang/de_DE/default.po +++ b/resources/lang/de_DE/default.po @@ -1911,7 +1911,8 @@ msgid "angeltypes.about" msgstr "Teams-/Aufgabenbeschreibung" msgid "angeltypes.about.text" -msgstr "Hier findest Du die Liste der Teams und ihrer Aufgaben. Wenn Du weitere Fragen hast, schaue in den FAQ nach." +msgstr "Hier findest Du die Liste der Teams und ihrer Aufgaben. Wenn Du weitere Fragen hast, " +"schaue in den
FAQ nach." msgid "angeltypes.restricted.hint" msgstr "Dieser Engeltyp benötigt eine Einweisung bei einem Einführungstreffen. " diff --git a/resources/lang/en_US/default.po b/resources/lang/en_US/default.po index 3f13d3930..ac758f786 100644 --- a/resources/lang/en_US/default.po +++ b/resources/lang/en_US/default.po @@ -705,7 +705,8 @@ msgid "angeltypes.about" msgstr "Teams-/Job description" msgid "angeltypes.about.text" -msgstr "Here you can find the list of teams and their tasks. If you have further questions, have a look at the FAQ." +msgstr "Here you can find the list of teams and their tasks. If you have further questions, " +"have a look at the FAQ." msgid "angeltypes.restricted" msgstr "Requires introduction" diff --git a/resources/views/pages/angeltypes/about.twig b/resources/views/pages/angeltypes/about.twig index e3d4bd9f7..4e338e3b3 100644 --- a/resources/views/pages/angeltypes/about.twig +++ b/resources/views/pages/angeltypes/about.twig @@ -39,7 +39,7 @@
-

{{ __('angeltypes.about.text') }}

+

{{ __('angeltypes.about.text', [url('/faq')])|raw }}

From 2c65b962ebc8d196f2c702c1fc4a9730ed8c8541 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sun, 15 Dec 2024 00:16:28 +0100 Subject: [PATCH 019/157] Implemented tags page --- config/routes.php | 10 ++ .../2024_12_13_000000_add_tag_permission.php | 53 ++++++ includes/sys_menu.php | 1 + resources/lang/de_DE/additional.po | 9 + resources/lang/de_DE/default.po | 16 +- resources/lang/en_US/additional.po | 9 + resources/lang/en_US/default.po | 12 ++ resources/views/pages/tag/edit.twig | 39 ++++ resources/views/pages/tag/index.twig | 51 ++++++ src/Controllers/Admin/TagController.php | 104 +++++++++++ .../Controllers/Admin/TagControllerTest.php | 168 ++++++++++++++++++ 11 files changed, 470 insertions(+), 2 deletions(-) create mode 100644 db/migrations/2024_12_13_000000_add_tag_permission.php create mode 100644 resources/views/pages/tag/edit.twig create mode 100644 resources/views/pages/tag/index.twig create mode 100644 src/Controllers/Admin/TagController.php create mode 100644 tests/Unit/Controllers/Admin/TagControllerTest.php diff --git a/config/routes.php b/config/routes.php index 54da4af65..55d70b58a 100644 --- a/config/routes.php +++ b/config/routes.php @@ -240,6 +240,16 @@ function (RouteCollector $route): void { } ); + // Tag + $route->addGroup( + '/tags', + function (RouteCollector $route): void { + $route->get('', 'Admin\\TagController@list'); + $route->get('/edit[/{tag_id:\d+}]', 'Admin\\TagController@edit'); + $route->post('/edit[/{tag_id:\d+}]', 'Admin\\TagController@save'); + } + ); + // Questions $route->addGroup( '/questions', diff --git a/db/migrations/2024_12_13_000000_add_tag_permission.php b/db/migrations/2024_12_13_000000_add_tag_permission.php new file mode 100644 index 000000000..ef635a4e7 --- /dev/null +++ b/db/migrations/2024_12_13_000000_add_tag_permission.php @@ -0,0 +1,53 @@ +db = $this->schema->getConnection(); + } + + /** + * Run the migration + */ + public function up(): void + { + $this->db->table('privileges') + ->insert([ + 'name' => 'tag.edit', + 'description' => 'Edit tags', + ]); + $privilegeId = $this->db->table('privileges') + ->where('name', 'tag.edit') + ->get(['id']) + ->first() + ->id; + + $this->db->table('group_privileges') + ->insertOrIgnore([ + ['group_id' => $this->shiftCoordinatorId, 'privilege_id' => $privilegeId], + ]); + } + + /** + * Reverse the migration + */ + public function down(): void + { + $this->db->table('privileges') + ->where('name', 'tag.edit') + ->delete(); + } +} diff --git a/includes/sys_menu.php b/includes/sys_menu.php index 93deccc22..eb899eb59 100644 --- a/includes/sys_menu.php +++ b/includes/sys_menu.php @@ -75,6 +75,7 @@ function make_navigation(): array 'admin_shifts' => 'Create shifts', 'admin_groups' => 'Group rights', 'admin/schedule' => ['schedule.import', 'schedule.import'], + 'admin/tags' => ['tag.tags', 'tag.edit'], 'admin/logs' => ['log.log', 'admin_log'], 'admin/config' => ['config.config', 'config.edit'], ]; diff --git a/resources/lang/de_DE/additional.po b/resources/lang/de_DE/additional.po index 014999e4f..0484f3c39 100644 --- a/resources/lang/de_DE/additional.po +++ b/resources/lang/de_DE/additional.po @@ -201,6 +201,15 @@ msgstr "Frage erstellt." msgid "question.edit.success" msgstr "Frage erfolgreich bearbeitet." +msgid "tag.edit.duplicate" +msgstr "Ein Tag mit dem Namen existiert bereits!" + +msgid "tag.edit.success" +msgstr "Der Tag wurde erfolgreich aktualisiert." + +msgid "tag.delete.success" +msgstr "Tag erfolgreich gelöscht." + msgid "notification.news.new" msgstr "Neue News: %s" diff --git a/resources/lang/de_DE/default.po b/resources/lang/de_DE/default.po index a873baabf..f69633557 100644 --- a/resources/lang/de_DE/default.po +++ b/resources/lang/de_DE/default.po @@ -1347,10 +1347,10 @@ msgid "form.user_select" msgstr "Wähle einen User" msgid "form.tags" -msgstr "Schlagworte" +msgstr "Tags" msgid "form.tags.info" -msgstr "Komma separierte Liste von Schlagworten" +msgstr "Komma separierte Liste von Tags" msgid "schedule.import" msgstr "Programm importieren" @@ -1838,6 +1838,18 @@ msgstr "Frage \"%s\" löschen" msgid "question.contact_options" msgstr "Weitere Kontaktmöglichkeiten: " +msgid "tag.tags" +msgstr "Tags" + +msgid "tag.edit" +msgstr "Tag bearbeiten" + +msgid "tag.add" +msgstr "Tag erstellen" + +msgid "tag.delete.title" +msgstr "Tag \"%s\" löschen" + msgid "user.edit.shirt" msgstr "T-Shirt bearbeiten" diff --git a/resources/lang/en_US/additional.po b/resources/lang/en_US/additional.po index e57234667..0a22cf0c9 100644 --- a/resources/lang/en_US/additional.po +++ b/resources/lang/en_US/additional.po @@ -200,6 +200,15 @@ msgstr "Question added successfully." msgid "question.edit.success" msgstr "Question updated successfully." +msgid "tag.edit.duplicate" +msgstr "A tag with the name already exists!" + +msgid "tag.edit.success" +msgstr "Tag updated successfully." + +msgid "tag.delete.success" +msgstr "Tag deleted successfully." + msgid "notification.news.new" msgstr "New news: %s" diff --git a/resources/lang/en_US/default.po b/resources/lang/en_US/default.po index ac758f786..518c4ea82 100644 --- a/resources/lang/en_US/default.po +++ b/resources/lang/en_US/default.po @@ -635,6 +635,18 @@ msgstr "Delete question \"%s\"" msgid "question.contact_options" msgstr "Other contact options: " +msgid "tag.tags" +msgstr "Tags" + +msgid "tag.edit" +msgstr "Edit tag" + +msgid "tag.add" +msgstr "Create new tag" + +msgid "tag.delete.title" +msgstr "Delete tag \"%s\"" + msgid "user.edit.shirt" msgstr "Edit T-shirt" diff --git a/resources/views/pages/tag/edit.twig b/resources/views/pages/tag/edit.twig new file mode 100644 index 000000000..d65449b06 --- /dev/null +++ b/resources/views/pages/tag/edit.twig @@ -0,0 +1,39 @@ +{% extends 'layouts/app.twig' %} +{% import 'macros/base.twig' as m %} +{% import 'macros/form.twig' as f %} + +{% block title %}{{ tag and tag.id ? __('tag.edit') : __('tag.add') }}{% endblock %} + +{% block content %} +
+

+ {{ m.back(url('/admin/tags')) }} + {{ block('title') }} +

+ + {% include 'layouts/parts/messages.twig' %} + +
+ {{ csrf() }} + +
+
+ {{ f.input('name', __('general.name'), { + 'required': true, + 'required_icon': true, + 'value': tag ? tag.name : '', + 'max_length': 255, + }) }} +
+
+ {{ f.submit(__('form.save'), {'icon_left': 'save'}) }} + + {% if tag and tag.id %} + {{ f.delete(__('form.delete'), {'confirm_title': __('tag.delete.title', [tag.name[:40]|e])})}} + {% endif %} +
+
+ +
+
+{% endblock %} diff --git a/resources/views/pages/tag/index.twig b/resources/views/pages/tag/index.twig new file mode 100644 index 000000000..a8b560b74 --- /dev/null +++ b/resources/views/pages/tag/index.twig @@ -0,0 +1,51 @@ +{% extends 'layouts/app.twig' %} +{% import 'macros/base.twig' as m %} +{% import 'macros/form.twig' as f %} + +{% block title %} + {{ __('tag.tags') }} +{% endblock %} + +{% block content %} +
+

+ {{ block('title') }} + + {% if not tag|default(false) %} + {{ m.add(url('/admin/tags/edit')) }} + {% endif %} +

+ + {% include 'layouts/parts/messages.twig' %} + +
+ + + + + + + + {% block row %} + {% for item in items %} + + + + + {% endfor %} + {% endblock %} + +
{{ __('general.name') }}
{{ item.name }} +
+ {{ m.edit(url('admin/tags/edit/' ~ item.id)) }} + +
+ {{ csrf() }} + {{ f.delete(null, {'size': 'sm', 'confirm_title': __('tag.delete.title', [item.name[:40]|e])}) }} +
+
+
+ +
+
+{% endblock %} diff --git a/src/Controllers/Admin/TagController.php b/src/Controllers/Admin/TagController.php new file mode 100644 index 000000000..e69dff6c7 --- /dev/null +++ b/src/Controllers/Admin/TagController.php @@ -0,0 +1,104 @@ + */ + protected array $permissions = [ + 'tag.edit', + ]; + + public function __construct( + protected LoggerInterface $log, + protected Tag $tag, + protected Redirector $redirect, + protected Response $response + ) { + } + + public function list(): Response + { + $items = $this->tag->all(); + return $this->response->withView( + 'pages/tag/index.twig', + ['items' => $items] + ); + } + + public function edit(Request $request): Response + { + $tagId = $request->getAttribute('tag_id'); // optional + $tag = $this->tag->find($tagId); + + return $this->showEdit($tag); + } + + public function save(Request $request): Response + { + $tagId = $request->getAttribute('tag_id'); // optional + + /** @var Tag $tag */ + $tag = $this->tag->findOrNew($tagId); + + if ($request->request->has('delete')) { + return $this->delete($tag); + } + + $data = $this->validate($request, [ + 'name' => 'required|max:255', + 'delete' => 'optional|checked', + ]); + + $tag->name = $data['name']; + + if ( + $this->tag + ->where('name', $tag->name) + ->whereNot('id', $tag->id) + ->exists() + ) { + $this->addNotification('tag.edit.duplicate', NotificationType::ERROR); + + return $this->showEdit($tag); + } + + $tag->save(); + + $this->log->info('Updated tag "{name}"', ['name' => $tag->name]); + $this->addNotification('tag.edit.success'); + + return $this->redirect->to('/admin/tags'); + } + + protected function delete(Tag $tag): Response + { + $tag->delete(); + + $this->log->info('Deleted tag "{name}"', ['tag' => $tag->name]); + $this->addNotification('tag.delete.success'); + + return $this->redirect->to('/admin/tags'); + } + + protected function showEdit(?Tag $tag): Response + { + return $this->response->withView( + 'pages/tag/edit.twig', + ['tag' => $tag] + ); + } +} diff --git a/tests/Unit/Controllers/Admin/TagControllerTest.php b/tests/Unit/Controllers/Admin/TagControllerTest.php new file mode 100644 index 000000000..9ac020bca --- /dev/null +++ b/tests/Unit/Controllers/Admin/TagControllerTest.php @@ -0,0 +1,168 @@ +app->make(TagController::class); + + $this->response->expects($this->once()) + ->method('withView') + ->willReturnCallback(function ($view, $data) { + $this->assertEquals('pages/tag/index.twig', $view); + + $this->assertNotEmpty($data['items']); + + return $this->response; + }); + + $controller->list(); + } + + /** + * @covers \Engelsystem\Controllers\Admin\TagController::edit + * @covers \Engelsystem\Controllers\Admin\TagController::showEdit + */ + public function testEdit(): void + { + $this->request->attributes->set('tag_id', 1); + $this->response->expects($this->once()) + ->method('withView') + ->willReturnCallback(function ($view, $data) { + $this->assertEquals('pages/tag/edit.twig', $view); + + $this->assertNotEmpty($data['tag']); + + return $this->response; + }); + + /** @var TagController $controller */ + $controller = $this->app->make(TagController::class); + + $controller->edit($this->request); + } + + /** + * @covers \Engelsystem\Controllers\Admin\TagController::save + */ + public function testSaveCreateInvalid(): void + { + /** @var TagController $controller */ + $this->expectException(ValidationException::class); + + $controller = $this->app->make(TagController::class); + $controller->setValidator(new Validator()); + $controller->save($this->request); + } + + /** + * @covers \Engelsystem\Controllers\Admin\TagController::save + */ + public function testSaveDuplicate(): void + { + $body = ['name' => 'Lorem']; + + $this->request = $this->request->withParsedBody($body); + $this->response->expects($this->once()) + ->method('withView') + ->willReturnCallback(function ($view, $data) { + $this->assertEquals('pages/tag/edit.twig', $view); + + $this->assertNotEmpty($data['tag']); + + return $this->response; + }); + + /** @var TagController $controller */ + $controller = $this->app->make(TagController::class); + $controller->setValidator(new Validator()); + + $controller->save($this->request); + + $this->assertHasNotification('tag.edit.duplicate', NotificationType::ERROR); + $this->assertCount(1, Tag::all()); + } + + /** + * @covers \Engelsystem\Controllers\Admin\TagController::save + */ + public function testSaveCreateEdit(): void + { + $body = ['name' => 'Foo?']; + + $this->request = $this->request->withParsedBody($body); + $this->response->expects($this->once()) + ->method('redirectTo') + ->with('http://localhost/admin/tags') + ->willReturn($this->response); + + /** @var TagController $controller */ + $controller = $this->app->make(TagController::class); + $controller->setValidator(new Validator()); + + $controller->save($this->request); + + $this->assertTrue($this->log->hasInfoThatContains('Updated')); + + /** @var Tag $tag */ + $tag = (new Tag())->find(2); + $this->assertEquals('Foo?', $tag->name); + $this->assertHasNotification('tag.edit.success'); + $this->assertCount(2, Tag::all()); + $this->assertTrue(Tag::whereName('Lorem')->get()->isNotEmpty()); + } + + /** + * @covers \Engelsystem\Controllers\Admin\TagController::save + * @covers \Engelsystem\Controllers\Admin\TagController::delete + */ + public function testSaveDelete(): void + { + $this->request->attributes->set('tag_id', 1); + $this->request = $this->request->withParsedBody([ + 'delete' => '1', + ]); + $this->response->expects($this->once()) + ->method('redirectTo') + ->with('http://localhost/admin/tags') + ->willReturn($this->response); + + /** @var TagController $controller */ + $controller = $this->app->make(TagController::class); + $controller->setValidator(new Validator()); + + $controller->save($this->request); + + $this->assertTrue($this->log->hasInfoThatContains('Deleted')); + + $this->assertHasNotification('tag.delete.success'); + } + + /** + * Setup environment + */ + public function setUp(): void + { + parent::setUp(); + + (new Tag([ + 'name' => 'Lorem', + ]))->save(); + } +} From 6f899e6096e5879ff4bab226c961c8f34a93a640 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Mon, 23 Dec 2024 18:09:05 +0100 Subject: [PATCH 020/157] Show tags in FAQ preview --- resources/views/pages/faq/edit.twig | 9 +++++++++ src/Controllers/Admin/FaqController.php | 16 ++++++++++++---- .../Unit/Controllers/Admin/FaqControllerTest.php | 1 + 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/resources/views/pages/faq/edit.twig b/resources/views/pages/faq/edit.twig index 156efb874..7b8de7b2b 100644 --- a/resources/views/pages/faq/edit.twig +++ b/resources/views/pages/faq/edit.twig @@ -80,6 +80,15 @@
{{ faq.text|markdown }}
+
diff --git a/src/Controllers/Admin/FaqController.php b/src/Controllers/Admin/FaqController.php index 642f0885b..2e77e2be2 100644 --- a/src/Controllers/Admin/FaqController.php +++ b/src/Controllers/Admin/FaqController.php @@ -11,6 +11,7 @@ use Engelsystem\Http\Response; use Engelsystem\Models\Faq; use Engelsystem\Models\Tag; +use Illuminate\Support\Collection; use Psr\Log\LoggerInterface; class FaqController extends BaseController @@ -62,17 +63,24 @@ public function save(Request $request): Response $faq->question = $data['question']; $faq->text = $data['text']; + $tags = collect(explode(',', $data['tags'] ?? '')) + ->transform(fn($value) => trim($value)) + ->filter(fn($value) => $value != '') + ->unique(); + if (!is_null($data['preview'])) { + $faq['tags'] = new Collection(); + foreach ($tags as $tagName) { + $tag = new Tag(['name' => $tagName]); + $faq['tags'][] = $tag; + } + return $this->showEdit($faq, $data['tags']); } $faq->save(); $faq->tags()->detach(); - $tags = collect(explode(',', $data['tags'] ?? '')) - ->transform(fn($value) => trim($value)) - ->filter(fn($value) => $value != '') - ->unique(); foreach ($tags as $tagName) { $tag = Tag::whereName($tagName)->firstOrCreate(['name' => $tagName]); $faq->tags()->attach($tag); diff --git a/tests/Unit/Controllers/Admin/FaqControllerTest.php b/tests/Unit/Controllers/Admin/FaqControllerTest.php index 927d6fdf4..12f76a566 100644 --- a/tests/Unit/Controllers/Admin/FaqControllerTest.php +++ b/tests/Unit/Controllers/Admin/FaqControllerTest.php @@ -111,6 +111,7 @@ public function testSavePreview(): void $this->assertEquals('New question', $faq->question); $this->assertEquals('New text', $faq->text); $this->assertEquals('Foo, Bar', $data['tags']); + $this->assertEquals(collect([new Tag(['name' => 'Foo']), new Tag(['name' => 'Bar'])]), $faq->tags); return $this->response; }); From 480ad8a04d5c5becd1288458496586602224e920 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Thu, 26 Dec 2024 12:56:08 +0100 Subject: [PATCH 021/157] Use   between angel icon and name --- includes/view/User_view.php | 2 +- resources/views/macros/base.twig | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/includes/view/User_view.php b/includes/view/User_view.php index 240d0550c..0974f73ba 100644 --- a/includes/view/User_view.php +++ b/includes/view/User_view.php @@ -1015,7 +1015,7 @@ function User_Nick_render($user, $plain = false) } return render_profile_link( - ' ' . htmlspecialchars($user->displayName) . '', + ' ' . htmlspecialchars($user->displayName) . '', $user->id, ($user->state->arrived ? '' : 'text-muted') ); diff --git a/resources/views/macros/base.twig b/resources/views/macros/base.twig index 386c0b076..a89129eeb 100644 --- a/resources/views/macros/base.twig +++ b/resources/views/macros/base.twig @@ -1,6 +1,6 @@ -{% macro angel() %} +{% macro angel() -%} -{% endmacro %} +{%- endmacro %} {% macro icon(icon, color) %} @@ -26,7 +26,7 @@ - {{ _self.angel() }} {{ user.displayName }} + {{ _self.angel() }} {{ user.displayName }} {%- if opt.pronoun|default(false) and config('enable_pronoun') and user.personalData.pronoun %} ({{ user.personalData.pronoun }}) From e489ca5da48821c76315b9aa4e43c78a75aec783 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Thu, 26 Dec 2024 14:24:20 +0100 Subject: [PATCH 022/157] Show left border on blockquote --- resources/assets/themes/base.scss | 5 +++++ resources/assets/themes/theme15.scss | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/resources/assets/themes/base.scss b/resources/assets/themes/base.scss index 32e76b702..620c3b822 100644 --- a/resources/assets/themes/base.scss +++ b/resources/assets/themes/base.scss @@ -422,6 +422,11 @@ code { border-radius: inherit; } +blockquote { + border-left: .2em solid $primary; + padding-left: .5em; +} + /* Hide the arrow up/down buttons rendered by the browser in the input field */ /* Chrome, Safari, Edge, Opera */ input[type='number']::-webkit-outer-spin-button, diff --git a/resources/assets/themes/theme15.scss b/resources/assets/themes/theme15.scss index 3ff14ea7e..295ad3ca1 100644 --- a/resources/assets/themes/theme15.scss +++ b/resources/assets/themes/theme15.scss @@ -203,3 +203,7 @@ h5 { .card .btn { text-decoration: none; } + +blockquote { + border-left-color: $text-color; +} From d5c55dbef01f0f4f7f623120a6e3c31a00636a43 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Thu, 26 Dec 2024 15:05:26 +0100 Subject: [PATCH 023/157] Fix changing username case (already exist error) --- includes/pages/admin_user.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/includes/pages/admin_user.php b/includes/pages/admin_user.php index cf757a9fe..7fd9be53a 100644 --- a/includes/pages/admin_user.php +++ b/includes/pages/admin_user.php @@ -286,7 +286,8 @@ function admin_user() break; case 'save': - $user_source = User::find($user_id); + /** @var User $user_source */ + $user_source = User::findOrFail($user_id); $changed_email = false; $email = $request->postData('eemail'); @@ -302,7 +303,10 @@ function admin_user() $changed_nick = false; $nick = trim((string) $request->get('eNick')); $nickValid = (new Username())->validate($nick); - if (($user_source->name !== $nick) && User::whereName($nick)->exists()) { + if ( + $user_source->name !== $nick + && User::whereName($nick)->whereNot('id', $user_source->id)->exists() + ) { $html .= error(__('settings.profile.nick.already-taken') . "\n", true); break; } From 2a12e1b9868f48525ee8e1e00f8b25bac047ba72 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Thu, 26 Dec 2024 16:16:41 +0100 Subject: [PATCH 024/157] Prefetch state on user shifts page --- includes/controller/users_controller.php | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/includes/controller/users_controller.php b/includes/controller/users_controller.php index 8d2febfdd..4bad10a14 100644 --- a/includes/controller/users_controller.php +++ b/includes/controller/users_controller.php @@ -237,16 +237,14 @@ function user_controller() ); $neededAngeltypes = $shift->needed_angeltypes; foreach ($neededAngeltypes as &$needed_angeltype) { - $needed_angeltype['users'] = Db::select( - ' - SELECT `shift_entries`.`freeloaded_by`, `users`.* - FROM `shift_entries` - JOIN `users` ON `shift_entries`.`user_id`=`users`.`id` - WHERE `shift_entries`.`shift_id` = ? - AND `shift_entries`.`angel_type_id` = ? - ', - [$shift->id, $needed_angeltype['id']] - ); + $needed_angeltype['users'] = User::query() + ->select(['users.*', 'shift_entries.freeloaded_by']) + ->from('shift_entries') + ->join('users', 'shift_entries.user_id', 'users.id') + ->where('shift_entries.shift_id', $shift->id) + ->where('shift_entries.angel_type_id', $needed_angeltype['id']) + ->with('state') + ->get(); } $shift->needed_angeltypes = $neededAngeltypes; } From e8e9a6b4f5eef89faf9dfe45de23b6f3bcdb4c68 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sat, 28 Dec 2024 16:27:36 +0100 Subject: [PATCH 025/157] Fix empty value errors --- includes/pages/user_myshifts.php | 2 +- src/Controllers/SettingsController.php | 2 +- src/Http/SessionHandlers/DatabaseHandler.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/includes/pages/user_myshifts.php b/includes/pages/user_myshifts.php index 92e621343..f5fd8248d 100644 --- a/includes/pages/user_myshifts.php +++ b/includes/pages/user_myshifts.php @@ -24,7 +24,7 @@ function user_myshifts() if ($request->has('edit')) { $id = $request->input('edit'); $shiftEntry = ShiftEntry::where('id', $id) - ->where('user_id', User::find($request->input('id'))->id) + ->where('user_id', User::find($request->input('id'))?->id) ->first(); $is_angeltype_supporter = $shiftEntry && auth()->user()->isAngelTypeSupporter($shiftEntry->angelType); } diff --git a/src/Controllers/SettingsController.php b/src/Controllers/SettingsController.php index 4e72f06bc..8d09f12df 100644 --- a/src/Controllers/SettingsController.php +++ b/src/Controllers/SettingsController.php @@ -112,8 +112,8 @@ public function saveProfile(Request $request): Response if ( $goodie_tshirt - && (isset(config('tshirt_sizes')[$data['shirt_size'] ?? '']) || is_null($data['shirt_size'])) && !$user->state->got_goodie + && (isset(config('tshirt_sizes')[$data['shirt_size'] ?? '']) || is_null($data['shirt_size'])) ) { $user->personalData->shirt_size = $data['shirt_size']; } diff --git a/src/Http/SessionHandlers/DatabaseHandler.php b/src/Http/SessionHandlers/DatabaseHandler.php index 5851fa8be..9d885999d 100644 --- a/src/Http/SessionHandlers/DatabaseHandler.php +++ b/src/Http/SessionHandlers/DatabaseHandler.php @@ -33,7 +33,7 @@ public function write(string $id, string $data): bool $session->id = $id; $session->payload = $data; $session->last_activity = Carbon::now(); - $session->user_id = auth()->user()?->id; + $session->user_id = auth()->user()?->id ?? null; $session->save(); // The save return can't be used directly as it won't change if the second call is in the same second From ea0bab3c6315351130c7f54a53da0b71f34ad2c4 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Fri, 27 Dec 2024 14:50:37 +0100 Subject: [PATCH 026/157] Fix styling --- resources/assets/themes/base.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/assets/themes/base.scss b/resources/assets/themes/base.scss index 620c3b822..3f672352c 100644 --- a/resources/assets/themes/base.scss +++ b/resources/assets/themes/base.scss @@ -423,8 +423,8 @@ code { } blockquote { - border-left: .2em solid $primary; - padding-left: .5em; + border-left: 0.2em solid $primary; + padding-left: 0.5em; } /* Hide the arrow up/down buttons rendered by the browser in the input field */ From fd3c1f373149cdfbde169926f92fb7a7c0688c76 Mon Sep 17 00:00:00 2001 From: Xu Date: Sun, 29 Dec 2024 18:53:54 +0100 Subject: [PATCH 027/157] fix goodie logs wording and to be filtered by user --- includes/pages/admin_active.php | 4 ++-- src/Controllers/Admin/UserGoodieController.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/includes/pages/admin_active.php b/includes/pages/admin_active.php index c791eb345..8ebd3e725 100644 --- a/includes/pages/admin_active.php +++ b/includes/pages/admin_active.php @@ -150,7 +150,7 @@ function admin_active() } else { $user_source->state->got_goodie = true; $user_source->state->save(); - engelsystem_log('User ' . User_Nick_render($user_source, true) . ' has tshirt now.'); + engelsystem_log('User ' . User_Nick_render($user_source, true) . ($goodie_tshirt ? ' has T-shirt now.' : ' has goodie now.')); $msg = success( ($goodie_tshirt ? __('Angel has got a T-shirt.') @@ -167,7 +167,7 @@ function admin_active() if ($user_source) { $user_source->state->got_goodie = false; $user_source->state->save(); - engelsystem_log('User ' . User_Nick_render($user_source, true) . ' has NO tshirt.'); + engelsystem_log('User ' . User_Nick_render($user_source, true) . ($goodie_tshirt ? ' has NO T-shirt.' : ' has NO goodie.')); $msg = success( ($goodie_tshirt ? __('Angel has got no T-shirt.') diff --git a/src/Controllers/Admin/UserGoodieController.php b/src/Controllers/Admin/UserGoodieController.php index 38166fb33..b548bac29 100644 --- a/src/Controllers/Admin/UserGoodieController.php +++ b/src/Controllers/Admin/UserGoodieController.php @@ -88,7 +88,7 @@ public function saveGoodie(Request $request): Response $user->state->save(); $this->log->info( - 'Updated user goodie state "{user}" ({id}): ' + 'Updated user goodie state {user} ({id}): ' . '{size}, arrived: {arrived}, active: {active}, got goodie: {got_goodie}', [ 'id' => $user->id, From 094f264cdcb2fa71eb4e1e06e54106b222f1ca78 Mon Sep 17 00:00:00 2001 From: Xu Date: Sat, 4 Jan 2025 00:02:56 +0100 Subject: [PATCH 028/157] fix translation "mehr Genitiv" --- resources/lang/de_DE/default.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/lang/de_DE/default.po b/resources/lang/de_DE/default.po index f69633557..e4e096c9d 100644 --- a/resources/lang/de_DE/default.po +++ b/resources/lang/de_DE/default.po @@ -1574,7 +1574,7 @@ msgstr "Um gegebenenfalls Voucher für das nächste gleichartige Event zu erhalt msgid "settings.profile.privacy" msgstr "" -"Diese Zustimmung kann während dem Event in den Profil-Einstellungen, sowie, auch nach dem Event, " +"Diese Zustimmung kann während des Events in den Profil-Einstellungen, sowie, auch nach dem Event, " "per E-Mail an %1$s, widerrufen werden." msgid "settings.profile.shirt_size" From 68a4fa66498ad9dda1e805d9ba5211ff9fd0a40a Mon Sep 17 00:00:00 2001 From: Xu Date: Sat, 4 Jan 2025 15:29:58 +0100 Subject: [PATCH 029/157] logs "edit" worklog --- src/Controllers/Admin/UserWorklogController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Controllers/Admin/UserWorklogController.php b/src/Controllers/Admin/UserWorklogController.php index f66301542..6cb6e6e02 100644 --- a/src/Controllers/Admin/UserWorklogController.php +++ b/src/Controllers/Admin/UserWorklogController.php @@ -84,7 +84,7 @@ public function saveWorklog(Request $request): Response $worklog->save(); $this->log->info( - 'Added worklog for {name} ({id}) at {time} spanning {hours}h: {text}', + (isset($worklogId) ? 'Edited' : 'Added') . ' worklog for {name} ({id}) at {time} spanning {hours}h: {text}', [ 'name' => $user->name, 'id' => $user->id, From bf6fc534be2dcb9039a3c50047b81081c27b1c4c Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sun, 24 Nov 2024 22:34:38 +0100 Subject: [PATCH 030/157] Make implicit nullable explicit --- .phpcs.xml | 1 + .../public_dashboard_controller.php | 4 +-- includes/model/ShiftEntry_model.php | 2 +- includes/model/Shifts_model.php | 2 +- includes/model/Stats.php | 8 ++--- includes/sys_template.php | 2 +- src/Application.php | 5 ++- src/Controllers/Admin/FaqController.php | 2 +- src/Controllers/HasUserNotifications.php | 2 +- src/Controllers/Metrics/Stats.php | 33 +++++-------------- src/Exceptions/Handler.php | 2 +- src/Helpers/DayOfEvent.php | 2 +- src/Helpers/Translation/Translator.php | 2 +- src/Helpers/Uuid.php | 2 +- src/Http/Exceptions/HttpAuthExpired.php | 5 +-- src/Http/Exceptions/HttpException.php | 5 +-- src/Http/Exceptions/HttpForbidden.php | 5 +-- src/Http/Exceptions/HttpNotFound.php | 5 +-- src/Http/Exceptions/ValidationException.php | 5 +-- src/Mail/EngelsystemMailer.php | 15 ++------- src/Middleware/CallableHandler.php | 5 +-- src/Middleware/SendResponseHandler.php | 2 +- src/Renderer/Twig/Extensions/Notification.php | 2 +- src/helpers.php | 14 ++++---- .../Controllers/Admin/NewsControllerTest.php | 3 +- tests/Unit/Controllers/ControllerTest.php | 2 +- .../Unit/Http/RequestServiceProviderTest.php | 2 +- tests/Unit/Mail/EngelsystemMailerTest.php | 2 +- tests/Unit/Mail/MailerTest.php | 4 +-- tests/Unit/TestCase.php | 4 +-- 30 files changed, 51 insertions(+), 98 deletions(-) diff --git a/.phpcs.xml b/.phpcs.xml index c04ded719..be822f4cc 100644 --- a/.phpcs.xml +++ b/.phpcs.xml @@ -30,6 +30,7 @@ /includes + /includes diff --git a/includes/controller/public_dashboard_controller.php b/includes/controller/public_dashboard_controller.php index 223705776..ea80f6f89 100644 --- a/includes/controller/public_dashboard_controller.php +++ b/includes/controller/public_dashboard_controller.php @@ -76,7 +76,7 @@ function public_dashboard_controller() * * @return array */ -function public_dashboard_controller_free_shift(Shift $shift, ShiftsFilter $filter = null) +function public_dashboard_controller_free_shift(Shift $shift, ?ShiftsFilter $filter = null) { // ToDo move to model and return one $free_shift = [ @@ -109,7 +109,7 @@ function public_dashboard_controller_free_shift(Shift $shift, ShiftsFilter $filt * * @return array */ -function public_dashboard_needed_angels($needed_angels, ShiftsFilter $filter = null) +function public_dashboard_needed_angels($needed_angels, ?ShiftsFilter $filter = null) { $result = []; foreach ($needed_angels as $needed_angel) { diff --git a/includes/model/ShiftEntry_model.php b/includes/model/ShiftEntry_model.php index f5b6fbf08..bf5288eec 100644 --- a/includes/model/ShiftEntry_model.php +++ b/includes/model/ShiftEntry_model.php @@ -72,7 +72,7 @@ function ShiftEntries_upcoming_for_user(User $user): Collection * @param Carbon|null $sinceTime * @return ShiftEntry[]|Collection */ -function ShiftEntries_finished_by_user(User $user, Carbon $sinceTime = null): Collection +function ShiftEntries_finished_by_user(User $user, ?Carbon $sinceTime = null): Collection { $query = $user->shiftEntries() ->with(['shift', 'shift.shiftType']) diff --git a/includes/model/Shifts_model.php b/includes/model/Shifts_model.php index 5f71647d0..fa9e5db8d 100644 --- a/includes/model/Shifts_model.php +++ b/includes/model/Shifts_model.php @@ -59,7 +59,7 @@ function Shifts_by_angeltype(AngelType $angeltype) * * @return Collection|Shift[] */ -function Shifts_free($start, $end, ShiftsFilter $filter = null) +function Shifts_free($start, $end, ?ShiftsFilter $filter = null) { $start = Carbon::createFromTimestamp($start, Carbon::now()->timezone); $end = Carbon::createFromTimestamp($end, Carbon::now()->timezone); diff --git a/includes/model/Stats.php b/includes/model/Stats.php index 9157337cd..2b52df209 100644 --- a/includes/model/Stats.php +++ b/includes/model/Stats.php @@ -11,7 +11,7 @@ * * @return int|string */ -function stats_currently_working(ShiftsFilter $filter = null): int|string +function stats_currently_working(?ShiftsFilter $filter = null): int|string { $result = Db::selectOne( ' @@ -37,7 +37,7 @@ function stats_currently_working(ShiftsFilter $filter = null): int|string * * @return int|string */ -function stats_hours_to_work(ShiftsFilter $filter = null): int|string +function stats_hours_to_work(?ShiftsFilter $filter = null): int|string { $result = Db::selectOne( ' @@ -92,7 +92,7 @@ function stats_hours_to_work(ShiftsFilter $filter = null): int|string * * @return int|string */ -function stats_angels_needed_three_hours(ShiftsFilter $filter = null): int|string +function stats_angels_needed_three_hours(?ShiftsFilter $filter = null): int|string { $in3hours = Carbon::now()->addHours(3)->toDateTimeString(); $result = Db::selectOne(' @@ -200,7 +200,7 @@ function stats_angels_needed_three_hours(ShiftsFilter $filter = null): int|strin * * @return int|string */ -function stats_angels_needed_for_nightshifts(ShiftsFilter $filter = null): int|string +function stats_angels_needed_for_nightshifts(?ShiftsFilter $filter = null): int|string { $nightShiftsConfig = config('night_shifts'); $nightStartTime = $nightShiftsConfig['start']; diff --git a/includes/sys_template.php b/includes/sys_template.php index 2bc0573c6..a39987d91 100644 --- a/includes/sys_template.php +++ b/includes/sys_template.php @@ -183,7 +183,7 @@ function toolbar_item_link($href, $icon, $label, $active = false) . ''; } -function toolbar_dropdown_item(string $href, string $label, bool $active, string $icon = null): string +function toolbar_dropdown_item(string $href, string $label, bool $active, ?string $icon = null): string { return strtr( '
  • {icon} {label}
  • ', diff --git a/src/Application.php b/src/Application.php index ca549a108..b4c8bce5b 100644 --- a/src/Application.php +++ b/src/Application.php @@ -29,7 +29,7 @@ class Application extends Container /** * Application constructor. */ - public function __construct(string $appPath = null) + public function __construct(?string $appPath = null) { if (!is_null($appPath)) { $this->setAppPath($appPath); @@ -71,9 +71,8 @@ public function register(string|ServiceProvider $provider): ServiceProvider /** * Boot service providers * - * @param Config|null $config */ - public function bootstrap(Config $config = null): void + public function bootstrap(?Config $config = null): void { if ($this->isBootstrapped) { return; diff --git a/src/Controllers/Admin/FaqController.php b/src/Controllers/Admin/FaqController.php index 2e77e2be2..6d794b07e 100644 --- a/src/Controllers/Admin/FaqController.php +++ b/src/Controllers/Admin/FaqController.php @@ -104,7 +104,7 @@ protected function delete(Faq $faq): Response return $this->redirect->to('/faq'); } - protected function showEdit(?Faq $faq, string $tags = null): Response + protected function showEdit(?Faq $faq, ?string $tags = null): Response { return $this->response->withView( 'pages/faq/edit.twig', diff --git a/src/Controllers/HasUserNotifications.php b/src/Controllers/HasUserNotifications.php index 5789158fc..d3c60c8cc 100644 --- a/src/Controllers/HasUserNotifications.php +++ b/src/Controllers/HasUserNotifications.php @@ -21,7 +21,7 @@ protected function addNotification(string|array $value, NotificationType $type = * @param NotificationType[]|null $types * @return array> */ - protected function getNotifications(array $types = null): array + protected function getNotifications(?array $types = null): array { $return = []; $types = $types ?: [ diff --git a/src/Controllers/Metrics/Stats.php b/src/Controllers/Metrics/Stats.php index b1947ce9d..aef7d4612 100644 --- a/src/Controllers/Metrics/Stats.php +++ b/src/Controllers/Metrics/Stats.php @@ -41,9 +41,8 @@ public function __construct(protected Database $db) /** * The number of users that arrived/not arrived and/or did some work * - * @param bool|null $working */ - public function usersState(bool $working = null, bool $arrived = true): int + public function usersState(?bool $working = null, bool $arrived = true): int { $query = State::whereArrived($arrived); @@ -104,9 +103,8 @@ public function email(string $type): int /** * The number of currently working users * - * @param bool|null $freeloaded */ - public function currentlyWorkingUsers(bool $freeloaded = null): int + public function currentlyWorkingUsers(?bool $freeloaded = null): int { $query = User::query() ->join('shift_entries', 'shift_entries.user_id', '=', 'users.id') @@ -203,12 +201,10 @@ public function licenses(string $license, bool $confirmed = false): int } /** - * @param bool|null $done - * @param bool|null $freeloaded * * @codeCoverageIgnore because it is only used in functions that use TIMESTAMPDIFF */ - protected function workSecondsQuery(bool $done = null, bool $freeloaded = null): QueryBuilder + protected function workSecondsQuery(?bool $done = null, ?bool $freeloaded = null): QueryBuilder { $query = $this ->getQuery('shift_entries') @@ -230,12 +226,10 @@ protected function workSecondsQuery(bool $done = null, bool $freeloaded = null): /** * The amount of worked seconds * - * @param bool|null $done - * @param bool|null $freeloaded * * @codeCoverageIgnore as TIMESTAMPDIFF is not implemented in SQLite */ - public function workSeconds(bool $done = null, bool $freeloaded = null): int + public function workSeconds(?bool $done = null, ?bool $freeloaded = null): int { $query = $this->workSecondsQuery($done, $freeloaded); @@ -245,12 +239,10 @@ public function workSeconds(bool $done = null, bool $freeloaded = null): int /** * The number of worked shifts * - * @param bool|null $done - * @param bool|null $freeloaded * * @codeCoverageIgnore as TIMESTAMPDIFF is not implemented in SQLite */ - public function workBuckets(array $buckets, bool $done = null, bool $freeloaded = null): array + public function workBuckets(array $buckets, ?bool $done = null, ?bool $freeloaded = null): array { return $this->getBuckets( $buckets, @@ -352,10 +344,7 @@ public function shifts(): int return Shift::query()->count(); } - /** - * @param bool|null $meeting - */ - public function announcements(bool $meeting = null): int + public function announcements(?bool $meeting = null): int { $query = is_null($meeting) ? News::query() : News::whereIsMeeting($meeting); @@ -368,10 +357,7 @@ public function comments(): int ->count(); } - /** - * @param bool|null $answered - */ - public function questions(bool $answered = null): int + public function questions(?bool $answered = null): int { $query = Question::query(); if (!is_null($answered)) { @@ -433,10 +419,7 @@ public function databaseWrite(): float return microtime(true) - $start; } - /** - * @param string|null $level - */ - public function logEntries(string $level = null): int + public function logEntries(?string $level = null): int { $query = is_null($level) ? LogEntry::query() : LogEntry::whereLevel($level); diff --git a/src/Exceptions/Handler.php b/src/Exceptions/Handler.php index 4fc992fa1..d9ae75f5e 100644 --- a/src/Exceptions/Handler.php +++ b/src/Exceptions/Handler.php @@ -101,7 +101,7 @@ public function setEnvironment(Environment $environment): void /** * @return HandlerInterface|HandlerInterface[] */ - public function getHandler(Environment $environment = null): HandlerInterface|array + public function getHandler(?Environment $environment = null): HandlerInterface|array { if (!is_null($environment)) { return $this->handler[$environment->value]; diff --git a/src/Helpers/DayOfEvent.php b/src/Helpers/DayOfEvent.php index a997fd224..596ff256c 100644 --- a/src/Helpers/DayOfEvent.php +++ b/src/Helpers/DayOfEvent.php @@ -11,7 +11,7 @@ class DayOfEvent * If `event_has_day0` is set to true in config, the first day of the event will be 0, else 1. * Returns null if "event_start" is not set. */ - public static function get(Carbon $date = null): int | null + public static function get(?Carbon $date = null): int | null { if (!config('enable_day_of_event')) { return null; diff --git a/src/Helpers/Translation/Translator.php b/src/Helpers/Translation/Translator.php index cd27a1a2d..920bc1df2 100644 --- a/src/Helpers/Translation/Translator.php +++ b/src/Helpers/Translation/Translator.php @@ -24,7 +24,7 @@ public function __construct( protected string $fallbackLocale, callable $getTranslatorCallback, protected array $locales = [], - callable $localeChangeCallback = null + ?callable $localeChangeCallback = null ) { $this->localeChangeCallback = $localeChangeCallback; $this->getTranslatorCallback = $getTranslatorCallback; diff --git a/src/Helpers/Uuid.php b/src/Helpers/Uuid.php index 4fd94aafe..79507209f 100644 --- a/src/Helpers/Uuid.php +++ b/src/Helpers/Uuid.php @@ -31,7 +31,7 @@ public static function uuid(): string * Generate a dependent v4 UUID * @var string|int|float|Stringable $value any value that can be converted to string */ - public static function uuidBy(mixed $value, string $name = null): string + public static function uuidBy(mixed $value, ?string $name = null): string { if (!is_null($name)) { if (!preg_match('/^[\da-f]+$/i', $name)) { diff --git a/src/Http/Exceptions/HttpAuthExpired.php b/src/Http/Exceptions/HttpAuthExpired.php index a06a94841..f9005f222 100644 --- a/src/Http/Exceptions/HttpAuthExpired.php +++ b/src/Http/Exceptions/HttpAuthExpired.php @@ -8,14 +8,11 @@ class HttpAuthExpired extends HttpException { - /** - * @param Throwable|null $previous - */ public function __construct( string $message = 'Authentication Expired', array $headers = [], int $code = 0, - Throwable $previous = null + ?Throwable $previous = null ) { // The 419 code is used as "Page Expired" to differentiate from a 401 (not authorized) parent::__construct(419, $message, $headers, $code, $previous); diff --git a/src/Http/Exceptions/HttpException.php b/src/Http/Exceptions/HttpException.php index 74b93f089..488d37724 100644 --- a/src/Http/Exceptions/HttpException.php +++ b/src/Http/Exceptions/HttpException.php @@ -9,15 +9,12 @@ class HttpException extends RuntimeException { - /** - * @param Throwable|null $previous - */ public function __construct( protected int $statusCode, string $message = '', protected array $headers = [], int $code = 0, - Throwable $previous = null + ?Throwable $previous = null ) { parent::__construct($message, $code, $previous); diff --git a/src/Http/Exceptions/HttpForbidden.php b/src/Http/Exceptions/HttpForbidden.php index a76eb1478..d85320110 100644 --- a/src/Http/Exceptions/HttpForbidden.php +++ b/src/Http/Exceptions/HttpForbidden.php @@ -8,14 +8,11 @@ class HttpForbidden extends HttpException { - /** - * @param Throwable|null $previous - */ public function __construct( string $message = '', array $headers = [], int $code = 0, - Throwable $previous = null + ?Throwable $previous = null ) { parent::__construct(403, $message, $headers, $code, $previous); } diff --git a/src/Http/Exceptions/HttpNotFound.php b/src/Http/Exceptions/HttpNotFound.php index 0e7938f2c..a848aaca9 100644 --- a/src/Http/Exceptions/HttpNotFound.php +++ b/src/Http/Exceptions/HttpNotFound.php @@ -8,14 +8,11 @@ class HttpNotFound extends HttpException { - /** - * @param Throwable|null $previous - */ public function __construct( string $message = '', array $headers = [], int $code = 0, - Throwable $previous = null + ?Throwable $previous = null ) { parent::__construct(404, $message, $headers, $code, $previous); } diff --git a/src/Http/Exceptions/ValidationException.php b/src/Http/Exceptions/ValidationException.php index ea96bedb1..cd624a490 100644 --- a/src/Http/Exceptions/ValidationException.php +++ b/src/Http/Exceptions/ValidationException.php @@ -10,14 +10,11 @@ class ValidationException extends RuntimeException { - /** - * @param Throwable|null $previous - */ public function __construct( protected Validator $validator, string $message = '', int $code = 0, - Throwable $previous = null + ?Throwable $previous = null ) { parent::__construct($message, $code, $previous); } diff --git a/src/Mail/EngelsystemMailer.php b/src/Mail/EngelsystemMailer.php index bea73cc9c..8680a9329 100644 --- a/src/Mail/EngelsystemMailer.php +++ b/src/Mail/EngelsystemMailer.php @@ -12,26 +12,15 @@ class EngelsystemMailer extends Mailer { - protected ?Renderer $view = null; - - protected ?Translator $translation = null; - protected ?string $subjectPrefix = null; - /** - * @param Renderer|null $view - * @param Translator|null $translation - */ public function __construct( LoggerInterface $log, MailerInterface $mailer, - Renderer $view = null, - Translator $translation = null + protected ?Renderer $view = null, + protected ?Translator $translation = null ) { parent::__construct($log, $mailer); - - $this->translation = $translation; - $this->view = $view; } /** diff --git a/src/Middleware/CallableHandler.php b/src/Middleware/CallableHandler.php index 3923a66c5..f1c799fba 100644 --- a/src/Middleware/CallableHandler.php +++ b/src/Middleware/CallableHandler.php @@ -20,15 +20,12 @@ class CallableHandler implements MiddlewareInterface, RequestHandlerInterface /** @var callable */ protected $callable; - protected ?Container $container = null; - /** * @param callable $callable The callable that should be wrapped */ - public function __construct(callable $callable, Container $container = null) + public function __construct(callable $callable, protected ?Container $container = null) { $this->callable = $callable; - $this->container = $container; } /** diff --git a/src/Middleware/SendResponseHandler.php b/src/Middleware/SendResponseHandler.php index 9931afed7..2cfac4bb3 100644 --- a/src/Middleware/SendResponseHandler.php +++ b/src/Middleware/SendResponseHandler.php @@ -56,7 +56,7 @@ protected function headersSent(): bool * * @codeCoverageIgnore */ - protected function sendHeader(string $content, bool $replace = true, int $code = null): void + protected function sendHeader(string $content, bool $replace = true, ?int $code = null): void { if (is_null($code)) { header($content, $replace); diff --git a/src/Renderer/Twig/Extensions/Notification.php b/src/Renderer/Twig/Extensions/Notification.php index fefe8220c..476c103b1 100644 --- a/src/Renderer/Twig/Extensions/Notification.php +++ b/src/Renderer/Twig/Extensions/Notification.php @@ -32,7 +32,7 @@ public function getFunctions(): array /** * @return Collection|Collection[] */ - public function notifications(string $type = null): Collection + public function notifications(?string $type = null): Collection { $types = $type ? [NotificationType::from($type)] : null; diff --git a/src/helpers.php b/src/helpers.php index 1f166c6ea..6d428a7a4 100644 --- a/src/helpers.php +++ b/src/helpers.php @@ -19,7 +19,7 @@ * @return mixed|Application * @phpcsSuppress SlevomatCodingStandard.TypeHints.ReturnTypeHint.UselessAnnotation */ -function app(string $id = null): mixed +function app(?string $id = null): mixed { if (is_null($id)) { return Application::getInstance(); @@ -51,7 +51,7 @@ function back(int $status = 302, array $headers = []): Response * @return mixed|Config * @phpcsSuppress SlevomatCodingStandard.TypeHints.ReturnTypeHint.UselessAnnotation */ -function config(string|array $key = null, mixed $default = null): mixed +function config(string|array|null $key = null, mixed $default = null): mixed { /** @var Config $config */ $config = app('config'); @@ -112,7 +112,7 @@ function redirect(string $path, int $status = 302, array $headers = []): Respons * @return mixed|Request * @phpcsSuppress SlevomatCodingStandard.TypeHints.ReturnTypeHint.UselessAnnotation */ -function request(string $key = null, mixed $default = null): mixed +function request(?string $key = null, mixed $default = null): mixed { /** @var Request $request */ $request = app('request'); @@ -143,7 +143,7 @@ function response(mixed $content = '', int $status = 200, array $headers = []): * @return mixed|SessionInterface * @phpcsSuppress SlevomatCodingStandard.TypeHints.ReturnTypeHint.UselessAnnotation */ -function session(string $key = null, mixed $default = null): mixed +function session(?string $key = null, mixed $default = null): mixed { /** @var SessionInterface $session */ $session = app('session'); @@ -158,7 +158,7 @@ function session(string $key = null, mixed $default = null): mixed /** * Translate the given message */ -function trans(string $key = null, array $replace = []): string|Translator +function trans(?string $key = null, array $replace = []): string|Translator { /** @var Translator $translator */ $translator = app('translator'); @@ -192,7 +192,7 @@ function _e(string $key, string $keyPlural, int $number, array $replace = []): s return $translator->translatePlural($key, $keyPlural, $number, $replace); } -function url(string $path = null, array $parameters = []): UrlGeneratorInterface|string +function url(?string $path = null, array $parameters = []): UrlGeneratorInterface|string { /** @var UrlGeneratorInterface $urlGenerator */ $urlGenerator = app('http.urlGenerator'); @@ -204,7 +204,7 @@ function url(string $path = null, array $parameters = []): UrlGeneratorInterface return $urlGenerator->to($path, $parameters); } -function view(string $template = null, array $data = []): Renderer|string +function view(?string $template = null, array $data = []): Renderer|string { /** @var Renderer $renderer */ $renderer = app('renderer'); diff --git a/tests/Unit/Controllers/Admin/NewsControllerTest.php b/tests/Unit/Controllers/Admin/NewsControllerTest.php index 060965860..d7e8e269a 100644 --- a/tests/Unit/Controllers/Admin/NewsControllerTest.php +++ b/tests/Unit/Controllers/Admin/NewsControllerTest.php @@ -117,12 +117,11 @@ public function saveCreateEditProvider(): array * @covers \Engelsystem\Controllers\Admin\NewsController::save * @dataProvider saveCreateEditProvider * - * @param int|null $id */ public function testSaveCreateEdit( string $text, bool $isMeeting, - int $id = null, + ?int $id = null, bool $sendNotification = false ): void { $this->request->attributes->set('news_id', $id); diff --git a/tests/Unit/Controllers/ControllerTest.php b/tests/Unit/Controllers/ControllerTest.php index f6bf84472..0db71f27d 100644 --- a/tests/Unit/Controllers/ControllerTest.php +++ b/tests/Unit/Controllers/ControllerTest.php @@ -51,7 +51,7 @@ protected function assertHasNotification(string $value, NotificationType $type = $this->assertTrue(in_array($value, $messages), 'Has ' . $type->value . ' notification: ' . $value); } - protected function assertHasNoNotifications(NotificationType $type = null): void + protected function assertHasNoNotifications(?NotificationType $type = null): void { $messages = $this->session->get('messages' . ($type ? '.' . $type->value : ''), []); $this->assertEmpty($messages, 'Has no' . ($type ? ' ' . $type->value : '') . ' notification.'); diff --git a/tests/Unit/Http/RequestServiceProviderTest.php b/tests/Unit/Http/RequestServiceProviderTest.php index df87c91b9..ed61489da 100644 --- a/tests/Unit/Http/RequestServiceProviderTest.php +++ b/tests/Unit/Http/RequestServiceProviderTest.php @@ -106,7 +106,7 @@ public function provideRequestPathPrefix(): array * @covers \Engelsystem\Http\RequestServiceProvider::createRequestWithoutPrefix * @dataProvider provideRequestPathPrefix */ - public function testCreateRequestWithoutPrefix(string $requestUri, string $expected, string $url = null): void + public function testCreateRequestWithoutPrefix(string $requestUri, string $expected, ?string $url = null): void { $_SERVER['REQUEST_URI'] = $requestUri; $config = new Config([ diff --git a/tests/Unit/Mail/EngelsystemMailerTest.php b/tests/Unit/Mail/EngelsystemMailerTest.php index 6f558ce9e..860fdadd9 100644 --- a/tests/Unit/Mail/EngelsystemMailerTest.php +++ b/tests/Unit/Mail/EngelsystemMailerTest.php @@ -109,7 +109,7 @@ public function testSend(): void $symfonyMailer->expects($this->once()) ->method('send') - ->willReturnCallback(function (RawMessage $message, Envelope $envelope = null): void { + ->willReturnCallback(function (RawMessage $message, ?Envelope $envelope = null): void { $this->assertStringContainsString('foo@bar.baz', $message->toString()); $this->assertStringContainsString('Foo Bar', $message->toString()); $this->assertStringContainsString('Mail test', $message->toString()); diff --git a/tests/Unit/Mail/MailerTest.php b/tests/Unit/Mail/MailerTest.php index 8afd94f1c..7b0c1cb78 100644 --- a/tests/Unit/Mail/MailerTest.php +++ b/tests/Unit/Mail/MailerTest.php @@ -48,7 +48,7 @@ public function testSend(): void $symfonyMailer = $this->createMock(MailerInterface::class); $symfonyMailer->expects($this->once()) ->method('send') - ->willReturnCallback(function (RawMessage $message, Envelope $envelope = null): void { + ->willReturnCallback(function (RawMessage $message, ?Envelope $envelope = null): void { $this->assertStringContainsString('to@xam.pel', $message->toString()); $this->assertStringContainsString('foo@bar.baz', $message->toString()); $this->assertStringContainsString('Test Tester', $message->toString()); @@ -75,7 +75,7 @@ public function testSendException(): void $symfonyMailer = $this->createMock(MailerInterface::class); $symfonyMailer->expects($this->once()) ->method('send') - ->willReturnCallback(function (RawMessage $message, Envelope $envelope = null): void { + ->willReturnCallback(function (RawMessage $message, ?Envelope $envelope = null): void { throw new TransportException('Unable to connect to port 42'); }); diff --git a/tests/Unit/TestCase.php b/tests/Unit/TestCase.php index 4f849d7f4..5bfdf5a68 100644 --- a/tests/Unit/TestCase.php +++ b/tests/Unit/TestCase.php @@ -20,9 +20,9 @@ abstract class TestCase extends PHPUnitTestCase protected function setExpects( MockObject $object, string $method, - array $arguments = null, + ?array $arguments = null, mixed $return = null, - InvocationOrder|int $times = null + InvocationOrder|int|null $times = null ): void { if (is_null($times)) { $times = $this->once(); From 9e1726e1116e907e208006960054ccd6c4f411cf Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sun, 24 Nov 2024 22:14:34 +0100 Subject: [PATCH 031/157] Update packages --- composer.lock | 900 +++++++++++++++++++++++++++++--------------------- 1 file changed, 525 insertions(+), 375 deletions(-) diff --git a/composer.lock b/composer.lock index 0bba00aa4..5c700e097 100644 --- a/composer.lock +++ b/composer.lock @@ -137,21 +137,21 @@ }, { "name": "devizzent/cebe-php-openapi", - "version": "1.1.0", + "version": "1.1.4", "source": { "type": "git", "url": "https://github.com/DEVizzent/cebe-php-openapi.git", - "reference": "9ae960c072eda54d8d0eb9dc14c1e6d815167347" + "reference": "af42b77f339b6b2920b65bae5df748e47391e11d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/DEVizzent/cebe-php-openapi/zipball/9ae960c072eda54d8d0eb9dc14c1e6d815167347", - "reference": "9ae960c072eda54d8d0eb9dc14c1e6d815167347", + "url": "https://api.github.com/repos/DEVizzent/cebe-php-openapi/zipball/af42b77f339b6b2920b65bae5df748e47391e11d", + "reference": "af42b77f339b6b2920b65bae5df748e47391e11d", "shasum": "" }, "require": { "ext-json": "*", - "justinrainbow/json-schema": "^5.2", + "justinrainbow/json-schema": "^5.2 || ^6.0", "php": ">=7.1.0", "symfony/yaml": "^3.4 || ^4 || ^5 || ^6 || ^7" }, @@ -209,7 +209,7 @@ "issues": "https://github.com/DEVizzent/cebe-php-openapi/issues", "source": "https://github.com/DEVizzent/cebe-php-openapi" }, - "time": "2024-10-30T05:57:15+00:00" + "time": "2025-01-16T11:12:34+00:00" }, { "name": "doctrine/inflector", @@ -381,16 +381,16 @@ }, { "name": "egulias/email-validator", - "version": "4.0.2", + "version": "4.0.3", "source": { "type": "git", "url": "https://github.com/egulias/EmailValidator.git", - "reference": "ebaaf5be6c0286928352e054f2d5125608e5405e" + "reference": "b115554301161fa21467629f1e1391c1936de517" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/ebaaf5be6c0286928352e054f2d5125608e5405e", - "reference": "ebaaf5be6c0286928352e054f2d5125608e5405e", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/b115554301161fa21467629f1e1391c1936de517", + "reference": "b115554301161fa21467629f1e1391c1936de517", "shasum": "" }, "require": { @@ -436,7 +436,7 @@ ], "support": { "issues": "https://github.com/egulias/EmailValidator/issues", - "source": "https://github.com/egulias/EmailValidator/tree/4.0.2" + "source": "https://github.com/egulias/EmailValidator/tree/4.0.3" }, "funding": [ { @@ -444,7 +444,7 @@ "type": "github" } ], - "time": "2023-10-06T06:47:41+00:00" + "time": "2024-12-27T00:36:43+00:00" }, { "name": "erusev/parsedown", @@ -498,16 +498,16 @@ }, { "name": "gettext/gettext", - "version": "v5.7.1", + "version": "v5.7.3", "source": { "type": "git", "url": "https://github.com/php-gettext/Gettext.git", - "reference": "a9f89e0cc9d9a67b422632b594b5f1afb16eccfc" + "reference": "95820f020e4f2f05e0bbaa5603e4c6ec3edc50f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-gettext/Gettext/zipball/a9f89e0cc9d9a67b422632b594b5f1afb16eccfc", - "reference": "a9f89e0cc9d9a67b422632b594b5f1afb16eccfc", + "url": "https://api.github.com/repos/php-gettext/Gettext/zipball/95820f020e4f2f05e0bbaa5603e4c6ec3edc50f1", + "reference": "95820f020e4f2f05e0bbaa5603e4c6ec3edc50f1", "shasum": "" }, "require": { @@ -552,7 +552,7 @@ "support": { "email": "oom@oscarotero.com", "issues": "https://github.com/php-gettext/Gettext/issues", - "source": "https://github.com/php-gettext/Gettext/tree/v5.7.1" + "source": "https://github.com/php-gettext/Gettext/tree/v5.7.3" }, "funding": [ { @@ -568,7 +568,7 @@ "type": "patreon" } ], - "time": "2024-07-24T22:05:18+00:00" + "time": "2024-12-01T10:18:08+00:00" }, { "name": "gettext/languages", @@ -646,16 +646,16 @@ }, { "name": "gettext/translator", - "version": "v1.2.0", + "version": "v1.2.1", "source": { "type": "git", "url": "https://github.com/php-gettext/Translator.git", - "reference": "a4fa5ed740f304a0ed7b3e169b2b554a195c7570" + "reference": "8ae0ac79053bcb732a6c584cd86f7a82ef183161" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-gettext/Translator/zipball/a4fa5ed740f304a0ed7b3e169b2b554a195c7570", - "reference": "a4fa5ed740f304a0ed7b3e169b2b554a195c7570", + "url": "https://api.github.com/repos/php-gettext/Translator/zipball/8ae0ac79053bcb732a6c584cd86f7a82ef183161", + "reference": "8ae0ac79053bcb732a6c584cd86f7a82ef183161", "shasum": "" }, "require": { @@ -700,7 +700,7 @@ "support": { "email": "oom@oscarotero.com", "issues": "https://github.com/php-gettext/Translator/issues", - "source": "https://github.com/php-gettext/Translator/tree/v1.2.0" + "source": "https://github.com/php-gettext/Translator/tree/v1.2.1" }, "funding": [ { @@ -716,7 +716,7 @@ "type": "patreon" } ], - "time": "2023-11-06T15:42:03+00:00" + "time": "2025-01-09T09:20:22+00:00" }, { "name": "graham-campbell/result-type", @@ -1105,18 +1105,133 @@ ], "time": "2024-07-18T11:15:46+00:00" }, + { + "name": "icecave/parity", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/icecave/parity.git", + "reference": "0109fef58b3230d23b20b2ac52ecdf477218d300" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/icecave/parity/zipball/0109fef58b3230d23b20b2ac52ecdf477218d300", + "reference": "0109fef58b3230d23b20b2ac52ecdf477218d300", + "shasum": "" + }, + "require": { + "icecave/repr": "~1", + "php": ">=5.3" + }, + "require-dev": { + "eloquent/liberator": "~1", + "icecave/archer": "~1" + }, + "suggest": { + "eloquent/asplode": "Drop-in exception-based error handling." + }, + "type": "library", + "autoload": { + "psr-0": { + "Icecave\\Parity": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "James Harris", + "email": "james.harris@icecave.com.au", + "homepage": "https://github.com/jmalloc" + } + ], + "description": "A customizable deep comparison library.", + "homepage": "https://github.com/IcecaveStudios/parity", + "keywords": [ + "compare", + "comparison", + "equal", + "equality", + "greater", + "less", + "sort", + "sorting" + ], + "support": { + "issues": "https://github.com/icecave/parity/issues", + "source": "https://github.com/icecave/parity/tree/1.0.0" + }, + "time": "2014-01-17T05:56:27+00:00" + }, + { + "name": "icecave/repr", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/icecave/repr.git", + "reference": "8a3d2953adf5f464a06e3e2587aeacc97e2bed07" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/icecave/repr/zipball/8a3d2953adf5f464a06e3e2587aeacc97e2bed07", + "reference": "8a3d2953adf5f464a06e3e2587aeacc97e2bed07", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "require-dev": { + "icecave/archer": "~1" + }, + "suggest": { + "eloquent/asplode": "Drop-in exception-based error handling." + }, + "type": "library", + "autoload": { + "psr-4": { + "Icecave\\Repr\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "James Harris", + "email": "james.harris@icecave.com.au", + "homepage": "https://github.com/jmalloc" + } + ], + "description": "A library for generating string representations of any value, inspired by Python's reprlib library.", + "homepage": "https://github.com/IcecaveStudios/repr", + "keywords": [ + "human", + "readable", + "repr", + "representation", + "string" + ], + "support": { + "issues": "https://github.com/icecave/repr/issues", + "source": "https://github.com/icecave/repr/tree/1.0.1" + }, + "time": "2014-07-25T05:44:41+00:00" + }, { "name": "illuminate/bus", - "version": "v11.30.0", + "version": "v11.41.3", "source": { "type": "git", "url": "https://github.com/illuminate/bus.git", - "reference": "ed8d93dd49d57887ccf82dbd284b80934288cbba" + "reference": "ed6dd9b36ff18a57a951bd5946f1c3a534f900cb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/bus/zipball/ed8d93dd49d57887ccf82dbd284b80934288cbba", - "reference": "ed8d93dd49d57887ccf82dbd284b80934288cbba", + "url": "https://api.github.com/repos/illuminate/bus/zipball/ed6dd9b36ff18a57a951bd5946f1c3a534f900cb", + "reference": "ed6dd9b36ff18a57a951bd5946f1c3a534f900cb", "shasum": "" }, "require": { @@ -1156,20 +1271,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-10-11T15:12:02+00:00" + "time": "2025-01-22T21:19:28+00:00" }, { "name": "illuminate/collections", - "version": "v11.30.0", + "version": "v11.41.3", "source": { "type": "git", "url": "https://github.com/illuminate/collections.git", - "reference": "2d99ccbb19e34450508ff3ab2f62ba90aa2e9793" + "reference": "80c85f81573cc4c024da05312119f9149a6b64c1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/collections/zipball/2d99ccbb19e34450508ff3ab2f62ba90aa2e9793", - "reference": "2d99ccbb19e34450508ff3ab2f62ba90aa2e9793", + "url": "https://api.github.com/repos/illuminate/collections/zipball/80c85f81573cc4c024da05312119f9149a6b64c1", + "reference": "80c85f81573cc4c024da05312119f9149a6b64c1", "shasum": "" }, "require": { @@ -1189,6 +1304,7 @@ }, "autoload": { "files": [ + "functions.php", "helpers.php" ], "psr-4": { @@ -1211,20 +1327,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-10-10T19:23:07+00:00" + "time": "2025-01-24T15:40:32+00:00" }, { "name": "illuminate/conditionable", - "version": "v11.30.0", + "version": "v11.41.3", "source": { "type": "git", "url": "https://github.com/illuminate/conditionable.git", - "reference": "362dd761b9920367bca1427a902158225e9e3a23" + "reference": "911df1bda950a3b799cf80671764e34eede131c6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/conditionable/zipball/362dd761b9920367bca1427a902158225e9e3a23", - "reference": "362dd761b9920367bca1427a902158225e9e3a23", + "url": "https://api.github.com/repos/illuminate/conditionable/zipball/911df1bda950a3b799cf80671764e34eede131c6", + "reference": "911df1bda950a3b799cf80671764e34eede131c6", "shasum": "" }, "require": { @@ -1257,20 +1373,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-06-28T20:10:30+00:00" + "time": "2024-11-21T16:28:56+00:00" }, { "name": "illuminate/container", - "version": "v11.30.0", + "version": "v11.41.3", "source": { "type": "git", "url": "https://github.com/illuminate/container.git", - "reference": "06dfc614aff58384b28ba5ad191f6a02d6b192cb" + "reference": "1caf7d6cf42078fa8d30f26f1e43943ed6b01dae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/container/zipball/06dfc614aff58384b28ba5ad191f6a02d6b192cb", - "reference": "06dfc614aff58384b28ba5ad191f6a02d6b192cb", + "url": "https://api.github.com/repos/illuminate/container/zipball/1caf7d6cf42078fa8d30f26f1e43943ed6b01dae", + "reference": "1caf7d6cf42078fa8d30f26f1e43943ed6b01dae", "shasum": "" }, "require": { @@ -1308,20 +1424,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-10-11T15:30:11+00:00" + "time": "2025-01-28T20:44:34+00:00" }, { "name": "illuminate/contracts", - "version": "v11.30.0", + "version": "v11.41.3", "source": { "type": "git", "url": "https://github.com/illuminate/contracts.git", - "reference": "56312862af937bd6da8e6dc8bbd88188dfb478f8" + "reference": "534b697fc1dd9fbdd9fbf2f33fc9dcbb943dea75" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/contracts/zipball/56312862af937bd6da8e6dc8bbd88188dfb478f8", - "reference": "56312862af937bd6da8e6dc8bbd88188dfb478f8", + "url": "https://api.github.com/repos/illuminate/contracts/zipball/534b697fc1dd9fbdd9fbf2f33fc9dcbb943dea75", + "reference": "534b697fc1dd9fbdd9fbf2f33fc9dcbb943dea75", "shasum": "" }, "require": { @@ -1356,20 +1472,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-09-22T15:08:08+00:00" + "time": "2025-01-10T20:57:00+00:00" }, { "name": "illuminate/database", - "version": "v11.30.0", + "version": "v11.41.3", "source": { "type": "git", "url": "https://github.com/illuminate/database.git", - "reference": "29500e97a251419a6e42aeebe4c07ccb174af5b3" + "reference": "ca7441b61fa56e45286d98f130cb1c71eac3ac7f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/database/zipball/29500e97a251419a6e42aeebe4c07ccb174af5b3", - "reference": "29500e97a251419a6e42aeebe4c07ccb174af5b3", + "url": "https://api.github.com/repos/illuminate/database/zipball/ca7441b61fa56e45286d98f130cb1c71eac3ac7f", + "reference": "ca7441b61fa56e45286d98f130cb1c71eac3ac7f", "shasum": "" }, "require": { @@ -1380,16 +1496,16 @@ "illuminate/contracts": "^11.0", "illuminate/macroable": "^11.0", "illuminate/support": "^11.0", + "laravel/serializable-closure": "^1.3|^2.0", "php": "^8.2" }, "suggest": { "ext-filter": "Required to use the Postgres database driver.", - "fakerphp/faker": "Required to use the eloquent factory builder (^1.21).", + "fakerphp/faker": "Required to use the eloquent factory builder (^1.24).", "illuminate/console": "Required to use the database commands (^11.0).", "illuminate/events": "Required to use the observers with Eloquent (^11.0).", "illuminate/filesystem": "Required to use the migrations (^11.0).", "illuminate/pagination": "Required to paginate the result set (^11.0).", - "laravel/serializable-closure": "Required to handle circular references in model serialization (^1.3).", "symfony/finder": "Required to use Eloquent model factories (^7.0)." }, "type": "library", @@ -1425,20 +1541,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-10-29T20:30:27+00:00" + "time": "2025-01-30T09:49:46+00:00" }, { "name": "illuminate/events", - "version": "v11.30.0", + "version": "v11.41.3", "source": { "type": "git", "url": "https://github.com/illuminate/events.git", - "reference": "cfd8a636234cc5b5f736f2987f33b0d471d974b3" + "reference": "2fcff2a924d1e2d58561f7b5d5078d6847a9eee8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/events/zipball/cfd8a636234cc5b5f736f2987f33b0d471d974b3", - "reference": "cfd8a636234cc5b5f736f2987f33b0d471d974b3", + "url": "https://api.github.com/repos/illuminate/events/zipball/2fcff2a924d1e2d58561f7b5d5078d6847a9eee8", + "reference": "2fcff2a924d1e2d58561f7b5d5078d6847a9eee8", "shasum": "" }, "require": { @@ -1480,20 +1596,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-08-07T14:43:54+00:00" + "time": "2024-12-06T19:16:00+00:00" }, { "name": "illuminate/filesystem", - "version": "v11.30.0", + "version": "v11.41.3", "source": { "type": "git", "url": "https://github.com/illuminate/filesystem.git", - "reference": "ce7013a350fb06bc65e8a2cf15fd2015f49e476d" + "reference": "a8768cca697ddf6f0cecc6f5bd4763808d84c0b7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/filesystem/zipball/ce7013a350fb06bc65e8a2cf15fd2015f49e476d", - "reference": "ce7013a350fb06bc65e8a2cf15fd2015f49e476d", + "url": "https://api.github.com/repos/illuminate/filesystem/zipball/a8768cca697ddf6f0cecc6f5bd4763808d84c0b7", + "reference": "a8768cca697ddf6f0cecc6f5bd4763808d84c0b7", "shasum": "" }, "require": { @@ -1502,17 +1618,17 @@ "illuminate/macroable": "^11.0", "illuminate/support": "^11.0", "php": "^8.2", - "symfony/finder": "^7.0" + "symfony/finder": "^7.0.3" }, "suggest": { "ext-fileinfo": "Required to use the Filesystem class.", "ext-ftp": "Required to use the Flysystem FTP driver.", "ext-hash": "Required to use the Filesystem class.", "illuminate/http": "Required for handling uploaded files (^7.0).", - "league/flysystem": "Required to use the Flysystem local driver (^3.0.16).", - "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.0).", - "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.0).", - "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.0).", + "league/flysystem": "Required to use the Flysystem local driver (^3.25.1).", + "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.25.1).", + "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.25.1).", + "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.25.1).", "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", "symfony/filesystem": "Required to enable support for relative symbolic links (^7.0).", "symfony/mime": "Required to enable support for guessing extensions (^7.0)." @@ -1547,11 +1663,11 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-09-22T15:10:50+00:00" + "time": "2025-01-24T16:08:55+00:00" }, { "name": "illuminate/macroable", - "version": "v11.30.0", + "version": "v11.41.3", "source": { "type": "git", "url": "https://github.com/illuminate/macroable.git", @@ -1597,16 +1713,16 @@ }, { "name": "illuminate/pipeline", - "version": "v11.30.0", + "version": "v11.41.3", "source": { "type": "git", "url": "https://github.com/illuminate/pipeline.git", - "reference": "b359be74adc3ba4a637ca01c3645a26724a4c8a0" + "reference": "a784f85ca9d6c37435c542ea487d78206a7df3ad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/pipeline/zipball/b359be74adc3ba4a637ca01c3645a26724a4c8a0", - "reference": "b359be74adc3ba4a637ca01c3645a26724a4c8a0", + "url": "https://api.github.com/repos/illuminate/pipeline/zipball/a784f85ca9d6c37435c542ea487d78206a7df3ad", + "reference": "a784f85ca9d6c37435c542ea487d78206a7df3ad", "shasum": "" }, "require": { @@ -1641,20 +1757,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-10-18T13:11:08+00:00" + "time": "2025-01-07T23:29:34+00:00" }, { "name": "illuminate/support", - "version": "v11.30.0", + "version": "v11.41.3", "source": { "type": "git", "url": "https://github.com/illuminate/support.git", - "reference": "69453485fa4c76589b5a1a98ebef0e8fee749220" + "reference": "5dc4a31f34d8a0529cf1fd6b7fe167f6cae91e0c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/support/zipball/69453485fa4c76589b5a1a98ebef0e8fee749220", - "reference": "69453485fa4c76589b5a1a98ebef0e8fee749220", + "url": "https://api.github.com/repos/illuminate/support/zipball/5dc4a31f34d8a0529cf1fd6b7fe167f6cae91e0c", + "reference": "5dc4a31f34d8a0529cf1fd6b7fe167f6cae91e0c", "shasum": "" }, "require": { @@ -1666,9 +1782,9 @@ "illuminate/conditionable": "^11.0", "illuminate/contracts": "^11.0", "illuminate/macroable": "^11.0", - "nesbot/carbon": "^2.72.2|^3.0", + "nesbot/carbon": "^2.72.6|^3.8.4", "php": "^8.2", - "voku/portable-ascii": "^2.0" + "voku/portable-ascii": "^2.0.2" }, "conflict": { "tightenco/collect": "<5.5.33" @@ -1677,14 +1793,15 @@ "spatie/once": "*" }, "suggest": { - "illuminate/filesystem": "Required to use the composer class (^11.0).", - "laravel/serializable-closure": "Required to use the once function (^1.3).", - "league/commonmark": "Required to use Str::markdown() and Stringable::markdown() (^2.0.2).", + "illuminate/filesystem": "Required to use the Composer class (^11.0).", + "laravel/serializable-closure": "Required to use the once function (^1.3|^2.0).", + "league/commonmark": "Required to use Str::markdown() and Stringable::markdown() (^2.6).", + "league/uri": "Required to use the Uri class (^7.5.1).", "ramsey/uuid": "Required to use Str::uuid() (^4.7).", - "symfony/process": "Required to use the composer class (^7.0).", + "symfony/process": "Required to use the Composer class (^7.0).", "symfony/uid": "Required to use Str::ulid() (^7.0).", "symfony/var-dumper": "Required to use the dd function (^7.0).", - "vlucas/phpdotenv": "Required to use the Env class and env helper (^5.4.1)." + "vlucas/phpdotenv": "Required to use the Env class and env helper (^5.6.1)." }, "type": "library", "extra": { @@ -1717,20 +1834,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-10-29T20:21:52+00:00" + "time": "2025-01-30T09:11:36+00:00" }, { "name": "illuminate/view", - "version": "v11.30.0", + "version": "v11.41.3", "source": { "type": "git", "url": "https://github.com/illuminate/view.git", - "reference": "b0ca05925b882b7887f9c2591497a1b2835f9144" + "reference": "9f9bed5b55b1b888a6149860b4b26b20ca4435bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/view/zipball/b0ca05925b882b7887f9c2591497a1b2835f9144", - "reference": "b0ca05925b882b7887f9c2591497a1b2835f9144", + "url": "https://api.github.com/repos/illuminate/view/zipball/9f9bed5b55b1b888a6149860b4b26b20ca4435bf", + "reference": "9f9bed5b55b1b888a6149860b4b26b20ca4435bf", "shasum": "" }, "require": { @@ -1771,27 +1888,29 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-10-24T14:26:35+00:00" + "time": "2025-01-22T21:19:28+00:00" }, { "name": "justinrainbow/json-schema", - "version": "5.3.0", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/jsonrainbow/json-schema.git", - "reference": "feb2ca6dd1cebdaf1ed60a4c8de2e53ce11c4fd8" + "reference": "a38c6198d53b09c0702f440585a4f4a5d9137bd9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/feb2ca6dd1cebdaf1ed60a4c8de2e53ce11c4fd8", - "reference": "feb2ca6dd1cebdaf1ed60a4c8de2e53ce11c4fd8", + "url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/a38c6198d53b09c0702f440585a4f4a5d9137bd9", + "reference": "a38c6198d53b09c0702f440585a4f4a5d9137bd9", "shasum": "" }, "require": { - "php": ">=7.1" + "icecave/parity": "1.0.0", + "marc-mabe/php-enum": "^2.0 || ^3.0 || ^4.0", + "php": ">=5.3.3" }, "require-dev": { - "friendsofphp/php-cs-fixer": "~2.2.20||~2.15.1", + "friendsofphp/php-cs-fixer": "~2.2.20 || ~2.19.0", "json-schema/json-schema-test-suite": "1.2.0", "phpunit/phpunit": "^4.8.35" }, @@ -1799,6 +1918,11 @@ "bin/validate-json" ], "type": "library", + "extra": { + "branch-alias": { + "dev-master": "6.x-dev" + } + }, "autoload": { "psr-4": { "JsonSchema\\": "src/JsonSchema/" @@ -1827,29 +1951,29 @@ } ], "description": "A library to validate a json schema.", - "homepage": "https://github.com/justinrainbow/json-schema", + "homepage": "https://github.com/jsonrainbow/json-schema", "keywords": [ "json", "schema" ], "support": { "issues": "https://github.com/jsonrainbow/json-schema/issues", - "source": "https://github.com/jsonrainbow/json-schema/tree/5.3.0" + "source": "https://github.com/jsonrainbow/json-schema/tree/6.0.0" }, - "time": "2024-07-06T21:00:26+00:00" + "time": "2024-07-30T17:49:21+00:00" }, { "name": "laravel/serializable-closure", - "version": "v1.3.5", + "version": "v1.3.7", "source": { "type": "git", "url": "https://github.com/laravel/serializable-closure.git", - "reference": "1dc4a3dbfa2b7628a3114e43e32120cce7cdda9c" + "reference": "4f48ade902b94323ca3be7646db16209ec76be3d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/1dc4a3dbfa2b7628a3114e43e32120cce7cdda9c", - "reference": "1dc4a3dbfa2b7628a3114e43e32120cce7cdda9c", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/4f48ade902b94323ca3be7646db16209ec76be3d", + "reference": "4f48ade902b94323ca3be7646db16209ec76be3d", "shasum": "" }, "require": { @@ -1897,39 +2021,34 @@ "issues": "https://github.com/laravel/serializable-closure/issues", "source": "https://github.com/laravel/serializable-closure" }, - "time": "2024-09-23T13:33:08+00:00" + "time": "2024-11-14T18:34:49+00:00" }, { "name": "league/oauth2-client", - "version": "2.7.0", + "version": "2.8.0", "source": { "type": "git", "url": "https://github.com/thephpleague/oauth2-client.git", - "reference": "160d6274b03562ebeb55ed18399281d8118b76c8" + "reference": "3d5cf8d0543731dfb725ab30e4d7289891991e13" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/oauth2-client/zipball/160d6274b03562ebeb55ed18399281d8118b76c8", - "reference": "160d6274b03562ebeb55ed18399281d8118b76c8", + "url": "https://api.github.com/repos/thephpleague/oauth2-client/zipball/3d5cf8d0543731dfb725ab30e4d7289891991e13", + "reference": "3d5cf8d0543731dfb725ab30e4d7289891991e13", "shasum": "" }, "require": { - "guzzlehttp/guzzle": "^6.0 || ^7.0", - "paragonie/random_compat": "^1 || ^2 || ^9.99", - "php": "^5.6 || ^7.0 || ^8.0" + "ext-json": "*", + "guzzlehttp/guzzle": "^6.5.8 || ^7.4.5", + "php": "^7.1 || >=8.0.0 <8.5.0" }, "require-dev": { "mockery/mockery": "^1.3.5", - "php-parallel-lint/php-parallel-lint": "^1.3.1", - "phpunit/phpunit": "^5.7 || ^6.0 || ^9.5", - "squizlabs/php_codesniffer": "^2.3 || ^3.0" + "php-parallel-lint/php-parallel-lint": "^1.4", + "phpunit/phpunit": "^7 || ^8 || ^9 || ^10 || ^11", + "squizlabs/php_codesniffer": "^3.11" }, "type": "library", - "extra": { - "branch-alias": { - "dev-2.x": "2.0.x-dev" - } - }, "autoload": { "psr-4": { "League\\OAuth2\\Client\\": "src/" @@ -1965,9 +2084,9 @@ ], "support": { "issues": "https://github.com/thephpleague/oauth2-client/issues", - "source": "https://github.com/thephpleague/oauth2-client/tree/2.7.0" + "source": "https://github.com/thephpleague/oauth2-client/tree/2.8.0" }, - "time": "2023-04-16T18:19:15+00:00" + "time": "2024-12-11T05:05:52+00:00" }, { "name": "league/openapi-psr7-validator", @@ -2033,20 +2152,20 @@ }, { "name": "league/uri", - "version": "7.4.1", + "version": "7.5.1", "source": { "type": "git", "url": "https://github.com/thephpleague/uri.git", - "reference": "bedb6e55eff0c933668addaa7efa1e1f2c417cc4" + "reference": "81fb5145d2644324614cc532b28efd0215bda430" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri/zipball/bedb6e55eff0c933668addaa7efa1e1f2c417cc4", - "reference": "bedb6e55eff0c933668addaa7efa1e1f2c417cc4", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/81fb5145d2644324614cc532b28efd0215bda430", + "reference": "81fb5145d2644324614cc532b28efd0215bda430", "shasum": "" }, "require": { - "league/uri-interfaces": "^7.3", + "league/uri-interfaces": "^7.5", "php": "^8.1" }, "conflict": { @@ -2111,7 +2230,7 @@ "docs": "https://uri.thephpleague.com", "forum": "https://thephpleague.slack.com", "issues": "https://github.com/thephpleague/uri-src/issues", - "source": "https://github.com/thephpleague/uri/tree/7.4.1" + "source": "https://github.com/thephpleague/uri/tree/7.5.1" }, "funding": [ { @@ -2119,20 +2238,20 @@ "type": "github" } ], - "time": "2024-03-23T07:42:40+00:00" + "time": "2024-12-08T08:40:02+00:00" }, { "name": "league/uri-interfaces", - "version": "7.4.1", + "version": "7.5.0", "source": { "type": "git", "url": "https://github.com/thephpleague/uri-interfaces.git", - "reference": "8d43ef5c841032c87e2de015972c06f3865ef718" + "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/8d43ef5c841032c87e2de015972c06f3865ef718", - "reference": "8d43ef5c841032c87e2de015972c06f3865ef718", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/08cfc6c4f3d811584fb09c37e2849e6a7f9b0742", + "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742", "shasum": "" }, "require": { @@ -2195,7 +2314,7 @@ "docs": "https://uri.thephpleague.com", "forum": "https://thephpleague.slack.com", "issues": "https://github.com/thephpleague/uri-src/issues", - "source": "https://github.com/thephpleague/uri-interfaces/tree/7.4.1" + "source": "https://github.com/thephpleague/uri-interfaces/tree/7.5.0" }, "funding": [ { @@ -2203,20 +2322,93 @@ "type": "github" } ], - "time": "2024-03-23T07:42:40+00:00" + "time": "2024-12-08T08:18:47+00:00" + }, + { + "name": "marc-mabe/php-enum", + "version": "v4.7.1", + "source": { + "type": "git", + "url": "https://github.com/marc-mabe/php-enum.git", + "reference": "7159809e5cfa041dca28e61f7f7ae58063aae8ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/marc-mabe/php-enum/zipball/7159809e5cfa041dca28e61f7f7ae58063aae8ed", + "reference": "7159809e5cfa041dca28e61f7f7ae58063aae8ed", + "shasum": "" + }, + "require": { + "ext-reflection": "*", + "php": "^7.1 | ^8.0" + }, + "require-dev": { + "phpbench/phpbench": "^0.16.10 || ^1.0.4", + "phpstan/phpstan": "^1.3.1", + "phpunit/phpunit": "^7.5.20 | ^8.5.22 | ^9.5.11", + "vimeo/psalm": "^4.17.0 | ^5.26.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-3.x": "3.2-dev", + "dev-master": "4.7-dev" + } + }, + "autoload": { + "psr-4": { + "MabeEnum\\": "src/" + }, + "classmap": [ + "stubs/Stringable.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Marc Bennewitz", + "email": "dev@mabe.berlin", + "homepage": "https://mabe.berlin/", + "role": "Lead" + } + ], + "description": "Simple and fast implementation of enumerations with native PHP", + "homepage": "https://github.com/marc-mabe/php-enum", + "keywords": [ + "enum", + "enum-map", + "enum-set", + "enumeration", + "enumerator", + "enummap", + "enumset", + "map", + "set", + "type", + "type-hint", + "typehint" + ], + "support": { + "issues": "https://github.com/marc-mabe/php-enum/issues", + "source": "https://github.com/marc-mabe/php-enum/tree/v4.7.1" + }, + "time": "2024-11-28T04:54:44+00:00" }, { "name": "nesbot/carbon", - "version": "3.8.2", + "version": "3.8.4", "source": { "type": "git", - "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "e1268cdbc486d97ce23fef2c666dc3c6b6de9947" + "url": "https://github.com/CarbonPHP/carbon.git", + "reference": "129700ed449b1f02d70272d2ac802357c8c30c58" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/e1268cdbc486d97ce23fef2c666dc3c6b6de9947", - "reference": "e1268cdbc486d97ce23fef2c666dc3c6b6de9947", + "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/129700ed449b1f02d70272d2ac802357c8c30c58", + "reference": "129700ed449b1f02d70272d2ac802357c8c30c58", "shasum": "" }, "require": { @@ -2248,10 +2440,6 @@ ], "type": "library", "extra": { - "branch-alias": { - "dev-master": "3.x-dev", - "dev-2.x": "2.x-dev" - }, "laravel": { "providers": [ "Carbon\\Laravel\\ServiceProvider" @@ -2261,6 +2449,10 @@ "includes": [ "extension.neon" ] + }, + "branch-alias": { + "dev-2.x": "2.x-dev", + "dev-master": "3.x-dev" } }, "autoload": { @@ -2309,7 +2501,7 @@ "type": "tidelift" } ], - "time": "2024-11-07T17:46:48+00:00" + "time": "2024-12-27T09:25:35+00:00" }, { "name": "nikic/fast-route", @@ -2439,56 +2631,6 @@ ], "time": "2024-09-09T07:06:30+00:00" }, - { - "name": "paragonie/random_compat", - "version": "v9.99.100", - "source": { - "type": "git", - "url": "https://github.com/paragonie/random_compat.git", - "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a", - "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a", - "shasum": "" - }, - "require": { - "php": ">= 7" - }, - "require-dev": { - "phpunit/phpunit": "4.*|5.*", - "vimeo/psalm": "^1" - }, - "suggest": { - "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." - }, - "type": "library", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Paragon Initiative Enterprises", - "email": "security@paragonie.com", - "homepage": "https://paragonie.com" - } - ], - "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", - "keywords": [ - "csprng", - "polyfill", - "pseudorandom", - "random" - ], - "support": { - "email": "info@paragonie.com", - "issues": "https://github.com/paragonie/random_compat/issues", - "source": "https://github.com/paragonie/random_compat" - }, - "time": "2020-10-15T08:29:30+00:00" - }, { "name": "phpoption/phpoption", "version": "1.9.3", @@ -3188,12 +3330,12 @@ "source": { "type": "git", "url": "https://github.com/rcrowe/TwigBridge.git", - "reference": "418de7c4fbfa67678802093a8c3e1c319d6ad3fc" + "reference": "f6cbdb9c152811fdf602736b4451ba62a34aa4c1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/rcrowe/TwigBridge/zipball/418de7c4fbfa67678802093a8c3e1c319d6ad3fc", - "reference": "418de7c4fbfa67678802093a8c3e1c319d6ad3fc", + "url": "https://api.github.com/repos/rcrowe/TwigBridge/zipball/f6cbdb9c152811fdf602736b4451ba62a34aa4c1", + "reference": "f6cbdb9c152811fdf602736b4451ba62a34aa4c1", "shasum": "" }, "require": { @@ -3212,16 +3354,16 @@ "default-branch": true, "type": "library", "extra": { - "branch-alias": { - "dev-master": "0.14-dev" - }, "laravel": { - "providers": [ - "TwigBridge\\ServiceProvider" - ], "aliases": { "Twig": "TwigBridge\\Facade\\Twig" - } + }, + "providers": [ + "TwigBridge\\ServiceProvider" + ] + }, + "branch-alias": { + "dev-master": "0.14-dev" } }, "autoload": { @@ -3253,7 +3395,7 @@ "issues": "https://github.com/rcrowe/TwigBridge/issues", "source": "https://github.com/rcrowe/TwigBridge/tree/master" }, - "time": "2024-09-15T13:48:49+00:00" + "time": "2025-02-01T06:09:05+00:00" }, { "name": "respect/validation", @@ -3380,16 +3522,16 @@ }, { "name": "symfony/clock", - "version": "v7.1.6", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/symfony/clock.git", - "reference": "97bebc53548684c17ed696bc8af016880f0f098d" + "reference": "b81435fbd6648ea425d1ee96a2d8e68f4ceacd24" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/clock/zipball/97bebc53548684c17ed696bc8af016880f0f098d", - "reference": "97bebc53548684c17ed696bc8af016880f0f098d", + "url": "https://api.github.com/repos/symfony/clock/zipball/b81435fbd6648ea425d1ee96a2d8e68f4ceacd24", + "reference": "b81435fbd6648ea425d1ee96a2d8e68f4ceacd24", "shasum": "" }, "require": { @@ -3434,7 +3576,7 @@ "time" ], "support": { - "source": "https://github.com/symfony/clock/tree/v7.1.6" + "source": "https://github.com/symfony/clock/tree/v7.2.0" }, "funding": [ { @@ -3450,20 +3592,20 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2024-09-25T14:21:43+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.5.0", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", - "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", "shasum": "" }, "require": { @@ -3471,12 +3613,12 @@ }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { "dev-main": "3.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -3501,7 +3643,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1" }, "funding": [ { @@ -3517,20 +3659,20 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:32:20+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v7.1.6", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "87254c78dd50721cfd015b62277a8281c5589702" + "reference": "910c5db85a5356d0fea57680defec4e99eb9c8c1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/87254c78dd50721cfd015b62277a8281c5589702", - "reference": "87254c78dd50721cfd015b62277a8281c5589702", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/910c5db85a5356d0fea57680defec4e99eb9c8c1", + "reference": "910c5db85a5356d0fea57680defec4e99eb9c8c1", "shasum": "" }, "require": { @@ -3581,7 +3723,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v7.1.6" + "source": "https://github.com/symfony/event-dispatcher/tree/v7.2.0" }, "funding": [ { @@ -3597,20 +3739,20 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2024-09-25T14:21:43+00:00" }, { "name": "symfony/event-dispatcher-contracts", - "version": "v3.5.0", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "8f93aec25d41b72493c6ddff14e916177c9efc50" + "reference": "7642f5e970b672283b7823222ae8ef8bbc160b9f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/8f93aec25d41b72493c6ddff14e916177c9efc50", - "reference": "8f93aec25d41b72493c6ddff14e916177c9efc50", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/7642f5e970b672283b7823222ae8ef8bbc160b9f", + "reference": "7642f5e970b672283b7823222ae8ef8bbc160b9f", "shasum": "" }, "require": { @@ -3619,12 +3761,12 @@ }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { "dev-main": "3.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -3657,7 +3799,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.5.0" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.5.1" }, "funding": [ { @@ -3673,20 +3815,20 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:32:20+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/finder", - "version": "v7.1.6", + "version": "v7.2.2", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "2cb89664897be33f78c65d3d2845954c8d7a43b8" + "reference": "87a71856f2f56e4100373e92529eed3171695cfb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/2cb89664897be33f78c65d3d2845954c8d7a43b8", - "reference": "2cb89664897be33f78c65d3d2845954c8d7a43b8", + "url": "https://api.github.com/repos/symfony/finder/zipball/87a71856f2f56e4100373e92529eed3171695cfb", + "reference": "87a71856f2f56e4100373e92529eed3171695cfb", "shasum": "" }, "require": { @@ -3721,7 +3863,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.1.6" + "source": "https://github.com/symfony/finder/tree/v7.2.2" }, "funding": [ { @@ -3737,35 +3879,36 @@ "type": "tidelift" } ], - "time": "2024-10-01T08:31:23+00:00" + "time": "2024-12-30T19:00:17+00:00" }, { "name": "symfony/http-foundation", - "version": "v7.1.7", + "version": "v7.2.3", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "5183b61657807099d98f3367bcccb850238b17a9" + "reference": "ee1b504b8926198be89d05e5b6fc4c3810c090f0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/5183b61657807099d98f3367bcccb850238b17a9", - "reference": "5183b61657807099d98f3367bcccb850238b17a9", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/ee1b504b8926198be89d05e5b6fc4c3810c090f0", + "reference": "ee1b504b8926198be89d05e5b6fc4c3810c090f0", "shasum": "" }, "require": { "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3.0", "symfony/polyfill-mbstring": "~1.1", "symfony/polyfill-php83": "^1.27" }, "conflict": { "doctrine/dbal": "<3.6", - "symfony/cache": "<6.4" + "symfony/cache": "<6.4.12|>=7.0,<7.1.5" }, "require-dev": { "doctrine/dbal": "^3.6|^4", "predis/predis": "^1.1|^2.0", - "symfony/cache": "^6.4|^7.0", + "symfony/cache": "^6.4.12|^7.1.5", "symfony/dependency-injection": "^6.4|^7.0", "symfony/expression-language": "^6.4|^7.0", "symfony/http-kernel": "^6.4|^7.0", @@ -3798,7 +3941,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.1.7" + "source": "https://github.com/symfony/http-foundation/tree/v7.2.3" }, "funding": [ { @@ -3814,20 +3957,20 @@ "type": "tidelift" } ], - "time": "2024-11-06T09:02:46+00:00" + "time": "2025-01-17T10:56:55+00:00" }, { "name": "symfony/mailer", - "version": "v7.1.6", + "version": "v7.2.3", "source": { "type": "git", "url": "https://github.com/symfony/mailer.git", - "reference": "69c9948451fb3a6a4d47dc8261d1794734e76cdd" + "reference": "f3871b182c44997cf039f3b462af4a48fb85f9d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/69c9948451fb3a6a4d47dc8261d1794734e76cdd", - "reference": "69c9948451fb3a6a4d47dc8261d1794734e76cdd", + "url": "https://api.github.com/repos/symfony/mailer/zipball/f3871b182c44997cf039f3b462af4a48fb85f9d3", + "reference": "f3871b182c44997cf039f3b462af4a48fb85f9d3", "shasum": "" }, "require": { @@ -3836,7 +3979,7 @@ "psr/event-dispatcher": "^1", "psr/log": "^1|^2|^3", "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/mime": "^6.4|^7.0", + "symfony/mime": "^7.2", "symfony/service-contracts": "^2.5|^3" }, "conflict": { @@ -3878,7 +4021,7 @@ "description": "Helps sending emails", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailer/tree/v7.1.6" + "source": "https://github.com/symfony/mailer/tree/v7.2.3" }, "funding": [ { @@ -3894,20 +4037,20 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2025-01-27T11:08:17+00:00" }, { "name": "symfony/mime", - "version": "v7.1.6", + "version": "v7.2.3", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "caa1e521edb2650b8470918dfe51708c237f0598" + "reference": "2fc3b4bd67e4747e45195bc4c98bea4628476204" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/caa1e521edb2650b8470918dfe51708c237f0598", - "reference": "caa1e521edb2650b8470918dfe51708c237f0598", + "url": "https://api.github.com/repos/symfony/mime/zipball/2fc3b4bd67e4747e45195bc4c98bea4628476204", + "reference": "2fc3b4bd67e4747e45195bc4c98bea4628476204", "shasum": "" }, "require": { @@ -3962,7 +4105,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v7.1.6" + "source": "https://github.com/symfony/mime/tree/v7.2.3" }, "funding": [ { @@ -3978,7 +4121,7 @@ "type": "tidelift" } ], - "time": "2024-10-25T15:11:02+00:00" + "time": "2025-01-27T11:08:17+00:00" }, { "name": "symfony/polyfill-ctype", @@ -4006,8 +4149,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -4083,8 +4226,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -4165,8 +4308,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -4249,8 +4392,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -4323,8 +4466,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -4403,8 +4546,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -4479,8 +4622,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -4537,16 +4680,16 @@ }, { "name": "symfony/psr-http-message-bridge", - "version": "v7.1.6", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/symfony/psr-http-message-bridge.git", - "reference": "f16471bb19f6685b9ccf0a2c03c213840ae68cd6" + "reference": "03f2f72319e7acaf2a9f6fcbe30ef17eec51594f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/f16471bb19f6685b9ccf0a2c03c213840ae68cd6", - "reference": "f16471bb19f6685b9ccf0a2c03c213840ae68cd6", + "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/03f2f72319e7acaf2a9f6fcbe30ef17eec51594f", + "reference": "03f2f72319e7acaf2a9f6fcbe30ef17eec51594f", "shasum": "" }, "require": { @@ -4600,7 +4743,7 @@ "psr-7" ], "support": { - "source": "https://github.com/symfony/psr-http-message-bridge/tree/v7.1.6" + "source": "https://github.com/symfony/psr-http-message-bridge/tree/v7.2.0" }, "funding": [ { @@ -4616,20 +4759,20 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2024-09-26T08:57:56+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.5.0", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f" + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", - "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e53260aabf78fb3d63f8d79d69ece59f80d5eda0", + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0", "shasum": "" }, "require": { @@ -4642,12 +4785,12 @@ }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { "dev-main": "3.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -4683,7 +4826,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.5.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.5.1" }, "funding": [ { @@ -4699,24 +4842,25 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:32:20+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/translation", - "version": "v7.1.6", + "version": "v7.2.2", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "b9f72ab14efdb6b772f85041fa12f820dee8d55f" + "reference": "e2674a30132b7cc4d74540d6c2573aa363f05923" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/b9f72ab14efdb6b772f85041fa12f820dee8d55f", - "reference": "b9f72ab14efdb6b772f85041fa12f820dee8d55f", + "url": "https://api.github.com/repos/symfony/translation/zipball/e2674a30132b7cc4d74540d6c2573aa363f05923", + "reference": "e2674a30132b7cc4d74540d6c2573aa363f05923", "shasum": "" }, "require": { "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", "symfony/translation-contracts": "^2.5|^3.0" }, @@ -4777,7 +4921,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v7.1.6" + "source": "https://github.com/symfony/translation/tree/v7.2.2" }, "funding": [ { @@ -4793,20 +4937,20 @@ "type": "tidelift" } ], - "time": "2024-09-28T12:35:13+00:00" + "time": "2024-12-07T08:18:10+00:00" }, { "name": "symfony/translation-contracts", - "version": "v3.5.0", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "b9d2189887bb6b2e0367a9fc7136c5239ab9b05a" + "reference": "4667ff3bd513750603a09c8dedbea942487fb07c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/b9d2189887bb6b2e0367a9fc7136c5239ab9b05a", - "reference": "b9d2189887bb6b2e0367a9fc7136c5239ab9b05a", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/4667ff3bd513750603a09c8dedbea942487fb07c", + "reference": "4667ff3bd513750603a09c8dedbea942487fb07c", "shasum": "" }, "require": { @@ -4814,12 +4958,12 @@ }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { "dev-main": "3.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -4855,7 +4999,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v3.5.0" + "source": "https://github.com/symfony/translation-contracts/tree/v3.5.1" }, "funding": [ { @@ -4871,24 +5015,25 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:32:20+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/yaml", - "version": "v7.1.6", + "version": "v7.2.3", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "3ced3f29e4f0d6bce2170ff26719f1fe9aacc671" + "reference": "ac238f173df0c9c1120f862d0f599e17535a87ec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/3ced3f29e4f0d6bce2170ff26719f1fe9aacc671", - "reference": "3ced3f29e4f0d6bce2170ff26719f1fe9aacc671", + "url": "https://api.github.com/repos/symfony/yaml/zipball/ac238f173df0c9c1120f862d0f599e17535a87ec", + "reference": "ac238f173df0c9c1120f862d0f599e17535a87ec", "shasum": "" }, "require": { "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3.0", "symfony/polyfill-ctype": "^1.8" }, "conflict": { @@ -4926,7 +5071,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v7.1.6" + "source": "https://github.com/symfony/yaml/tree/v7.2.3" }, "funding": [ { @@ -4942,20 +5087,20 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2025-01-07T12:55:42+00:00" }, { "name": "twig/twig", - "version": "v3.14.2", + "version": "v3.19.0", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "0b6f9d8370bb3b7f1ce5313ed8feb0fafd6e399a" + "reference": "d4f8c2b86374f08efc859323dbcd95c590f7124e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/0b6f9d8370bb3b7f1ce5313ed8feb0fafd6e399a", - "reference": "0b6f9d8370bb3b7f1ce5313ed8feb0fafd6e399a", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/d4f8c2b86374f08efc859323dbcd95c590f7124e", + "reference": "d4f8c2b86374f08efc859323dbcd95c590f7124e", "shasum": "" }, "require": { @@ -4966,6 +5111,7 @@ "symfony/polyfill-php81": "^1.29" }, "require-dev": { + "phpstan/phpstan": "^2.0", "psr/container": "^1.0|^2.0", "symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0" }, @@ -5009,7 +5155,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.14.2" + "source": "https://github.com/twigphp/Twig/tree/v3.19.0" }, "funding": [ { @@ -5021,7 +5167,7 @@ "type": "tidelift" } ], - "time": "2024-11-07T12:36:22+00:00" + "time": "2025-01-29T07:06:14+00:00" }, { "name": "vlucas/phpdotenv", @@ -5109,16 +5255,16 @@ }, { "name": "voku/portable-ascii", - "version": "2.0.1", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/voku/portable-ascii.git", - "reference": "b56450eed252f6801410d810c8e1727224ae0743" + "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b56450eed252f6801410d810c8e1727224ae0743", - "reference": "b56450eed252f6801410d810c8e1727224ae0743", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", + "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", "shasum": "" }, "require": { @@ -5143,7 +5289,7 @@ "authors": [ { "name": "Lars Moelleken", - "homepage": "http://www.moelleken.org/" + "homepage": "https://www.moelleken.org/" } ], "description": "Portable ASCII library - performance optimized (ascii) string functions for php.", @@ -5155,7 +5301,7 @@ ], "support": { "issues": "https://github.com/voku/portable-ascii/issues", - "source": "https://github.com/voku/portable-ascii/tree/2.0.1" + "source": "https://github.com/voku/portable-ascii/tree/2.0.3" }, "funding": [ { @@ -5179,7 +5325,7 @@ "type": "tidelift" } ], - "time": "2022-03-08T17:03:00+00:00" + "time": "2024-11-21T01:49:47+00:00" }, { "name": "webmozart/assert", @@ -5435,16 +5581,16 @@ }, { "name": "fakerphp/faker", - "version": "v1.24.0", + "version": "v1.24.1", "source": { "type": "git", "url": "https://github.com/FakerPHP/Faker.git", - "reference": "a136842a532bac9ecd8a1c723852b09915d7db50" + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/a136842a532bac9ecd8a1c723852b09915d7db50", - "reference": "a136842a532bac9ecd8a1c723852b09915d7db50", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", "shasum": "" }, "require": { @@ -5492,9 +5638,9 @@ ], "support": { "issues": "https://github.com/FakerPHP/Faker/issues", - "source": "https://github.com/FakerPHP/Faker/tree/v1.24.0" + "source": "https://github.com/FakerPHP/Faker/tree/v1.24.1" }, - "time": "2024-11-07T15:11:20+00:00" + "time": "2024-11-21T13:46:39+00:00" }, { "name": "fig/log-test", @@ -5544,16 +5690,16 @@ }, { "name": "filp/whoops", - "version": "2.16.0", + "version": "2.17.0", "source": { "type": "git", "url": "https://github.com/filp/whoops.git", - "reference": "befcdc0e5dce67252aa6322d82424be928214fa2" + "reference": "075bc0c26631110584175de6523ab3f1652eb28e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/befcdc0e5dce67252aa6322d82424be928214fa2", - "reference": "befcdc0e5dce67252aa6322d82424be928214fa2", + "url": "https://api.github.com/repos/filp/whoops/zipball/075bc0c26631110584175de6523ab3f1652eb28e", + "reference": "075bc0c26631110584175de6523ab3f1652eb28e", "shasum": "" }, "require": { @@ -5603,7 +5749,7 @@ ], "support": { "issues": "https://github.com/filp/whoops/issues", - "source": "https://github.com/filp/whoops/tree/2.16.0" + "source": "https://github.com/filp/whoops/tree/2.17.0" }, "funding": [ { @@ -5611,7 +5757,7 @@ "type": "github" } ], - "time": "2024-09-25T12:00:00+00:00" + "time": "2025-01-25T12:00:00+00:00" }, { "name": "myclabs/deep-copy", @@ -5675,16 +5821,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.3.1", + "version": "v5.4.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b" + "reference": "447a020a1f875a434d62f2a401f53b82a396e494" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/8eea230464783aa9671db8eea6f8c6ac5285794b", - "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494", + "reference": "447a020a1f875a434d62f2a401f53b82a396e494", "shasum": "" }, "require": { @@ -5727,9 +5873,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.3.1" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0" }, - "time": "2024-10-08T18:51:32+00:00" + "time": "2024-12-30T11:07:19+00:00" }, { "name": "phar-io/manifest", @@ -5898,16 +6044,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.12.8", + "version": "1.12.16", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "f6a60a4d66142b8156c9da923f1972657bc4748c" + "reference": "e0bb5cb78545aae631220735aa706eac633a6be9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/f6a60a4d66142b8156c9da923f1972657bc4748c", - "reference": "f6a60a4d66142b8156c9da923f1972657bc4748c", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e0bb5cb78545aae631220735aa706eac633a6be9", + "reference": "e0bb5cb78545aae631220735aa706eac633a6be9", "shasum": "" }, "require": { @@ -5952,7 +6098,7 @@ "type": "github" } ], - "time": "2024-11-06T19:06:49+00:00" + "time": "2025-01-21T14:50:05+00:00" }, { "name": "phpunit/php-code-coverage", @@ -6275,16 +6421,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.21", + "version": "9.6.22", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "de6abf3b6f8dd955fac3caad3af7a9504e8c2ffa" + "reference": "f80235cb4d3caa59ae09be3adf1ded27521d1a9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/de6abf3b6f8dd955fac3caad3af7a9504e8c2ffa", - "reference": "de6abf3b6f8dd955fac3caad3af7a9504e8c2ffa", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f80235cb4d3caa59ae09be3adf1ded27521d1a9c", + "reference": "f80235cb4d3caa59ae09be3adf1ded27521d1a9c", "shasum": "" }, "require": { @@ -6295,7 +6441,7 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.12.0", + "myclabs/deep-copy": "^1.12.1", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=7.3", @@ -6358,7 +6504,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.21" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.22" }, "funding": [ { @@ -6374,7 +6520,7 @@ "type": "tidelift" } ], - "time": "2024-09-19T10:50:18+00:00" + "time": "2024-12-05T13:48:26+00:00" }, { "name": "sebastian/cli-parser", @@ -7406,16 +7552,16 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "3.10.3", + "version": "3.11.3", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "62d32998e820bddc40f99f8251958aed187a5c9c" + "reference": "ba05f990e79cbe69b9f35c8c1ac8dca7eecc3a10" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/62d32998e820bddc40f99f8251958aed187a5c9c", - "reference": "62d32998e820bddc40f99f8251958aed187a5c9c", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/ba05f990e79cbe69b9f35c8c1ac8dca7eecc3a10", + "reference": "ba05f990e79cbe69b9f35c8c1ac8dca7eecc3a10", "shasum": "" }, "require": { @@ -7480,22 +7626,26 @@ { "url": "https://opencollective.com/php_codesniffer", "type": "open_collective" + }, + { + "url": "https://thanks.dev/phpcsstandards", + "type": "thanks_dev" } ], - "time": "2024-09-18T10:38:58+00:00" + "time": "2025-01-23T17:04:15+00:00" }, { "name": "symfony/var-dumper", - "version": "v7.1.7", + "version": "v7.2.3", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "f6ea51f669760cacd7464bf7eaa0be87b8072db1" + "reference": "82b478c69745d8878eb60f9a049a4d584996f73a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/f6ea51f669760cacd7464bf7eaa0be87b8072db1", - "reference": "f6ea51f669760cacd7464bf7eaa0be87b8072db1", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/82b478c69745d8878eb60f9a049a4d584996f73a", + "reference": "82b478c69745d8878eb60f9a049a4d584996f73a", "shasum": "" }, "require": { @@ -7511,7 +7661,7 @@ "symfony/http-kernel": "^6.4|^7.0", "symfony/process": "^6.4|^7.0", "symfony/uid": "^6.4|^7.0", - "twig/twig": "^3.0.4" + "twig/twig": "^3.12" }, "bin": [ "Resources/bin/var-dump-server" @@ -7549,7 +7699,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.1.7" + "source": "https://github.com/symfony/var-dumper/tree/v7.2.3" }, "funding": [ { @@ -7565,7 +7715,7 @@ "type": "tidelift" } ], - "time": "2024-11-05T15:34:55+00:00" + "time": "2025-01-17T11:39:41+00:00" }, { "name": "theseer/tokenizer", From b4bad02362cf7853301853dce3659b18fcefd1ff Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Tue, 28 Jan 2025 18:02:02 +0100 Subject: [PATCH 032/157] Fix locations deletion from list --- resources/views/pages/locations/index.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/pages/locations/index.twig b/resources/views/pages/locations/index.twig index 54b6a3985..033c86211 100644 --- a/resources/views/pages/locations/index.twig +++ b/resources/views/pages/locations/index.twig @@ -69,7 +69,7 @@ {{ m.edit(url('/admin/locations/edit/' ~ location.id), {'class': 'm-1'}) }} -
    + {{ csrf() }} {{ f.hidden('id', location.id) }} {{ f.delete(null, { From 10dc35be1d434ed7dddffd80d0fc47fdddec79b0 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Tue, 28 Jan 2025 18:08:00 +0100 Subject: [PATCH 033/157] Fixed log messages --- includes/pages/admin_arrive.php | 2 +- includes/pages/admin_user.php | 2 +- src/Controllers/Admin/FaqController.php | 7 +++++-- src/Controllers/Admin/NewsController.php | 6 ++++-- src/Controllers/Admin/QuestionsController.php | 11 +++++++---- src/Controllers/Admin/ShiftTypesController.php | 5 +++-- src/Controllers/Admin/TagController.php | 4 ++-- src/Controllers/Admin/UserWorklogController.php | 6 ++++-- tests/Unit/Controllers/Admin/FaqControllerTest.php | 2 +- tests/Unit/Controllers/Admin/NewsControllerTest.php | 2 +- .../Controllers/Admin/QuestionsControllerTest.php | 2 +- .../Controllers/Admin/ShiftTypesControllerTest.php | 2 +- tests/Unit/Controllers/Admin/TagControllerTest.php | 2 +- .../Controllers/Admin/UserWorklogControllerTest.php | 2 +- 14 files changed, 33 insertions(+), 22 deletions(-) diff --git a/includes/pages/admin_arrive.php b/includes/pages/admin_arrive.php index cf785f282..92ecb2f6f 100644 --- a/includes/pages/admin_arrive.php +++ b/includes/pages/admin_arrive.php @@ -59,7 +59,7 @@ function admin_arrive() $user_source->state->arrival_date = new Carbon\Carbon(); $user_source->state->save(); - engelsystem_log('User set has arrived: ' . User_Nick_render($user_source, true)); + engelsystem_log('User set as arrived: ' . User_Nick_render($user_source, true)); success(__('Angel has been marked as arrived.')); throw_redirect(back()->getHeaderLine('location')); diff --git a/includes/pages/admin_user.php b/includes/pages/admin_user.php index 7fd9be53a..205aaad0c 100644 --- a/includes/pages/admin_user.php +++ b/includes/pages/admin_user.php @@ -312,7 +312,7 @@ function admin_user() } $old_nick = $user_source->name; if ($nickValid && $user_nick_edit) { - $changed_nick = ($user_source->name !== $nick) || User::whereName($nick)->exists(); + $changed_nick = $user_source->name !== $nick; $user_source->name = $nick; } $user_source->save(); diff --git a/src/Controllers/Admin/FaqController.php b/src/Controllers/Admin/FaqController.php index 6d794b07e..f1eec58ba 100644 --- a/src/Controllers/Admin/FaqController.php +++ b/src/Controllers/Admin/FaqController.php @@ -86,7 +86,10 @@ public function save(Request $request): Response $faq->tags()->attach($tag); } - $this->log->info('Updated faq "{question}": {text}', ['question' => $faq->question, 'text' => $faq->text]); + $this->log->info( + 'Saved faq "{question}" ({id}): {text}', + ['question' => $faq->question, 'text' => $faq->text, 'id' => $faq->id] + ); $this->addNotification('faq.edit.success'); @@ -97,7 +100,7 @@ protected function delete(Faq $faq): Response { $faq->delete(); - $this->log->info('Deleted faq "{question}"', ['question' => $faq->question]); + $this->log->info('Deleted faq "{question}" ({id})', ['question' => $faq->question, 'id' => $faq->id]); $this->addNotification('faq.delete.success'); diff --git a/src/Controllers/Admin/NewsController.php b/src/Controllers/Admin/NewsController.php index 10a18b3b8..25b5894f2 100644 --- a/src/Controllers/Admin/NewsController.php +++ b/src/Controllers/Admin/NewsController.php @@ -112,8 +112,9 @@ public function save(Request $request): Response } $this->log->info( - 'Updated {pinned}{type} "{news}": {text}', + 'Saved {pinned}{highlighted}{type} "{news}" ({id}): {text}', [ + 'id' => $news->id, 'pinned' => $news->is_pinned ? 'pinned ' : '', 'highlighted' => $news->is_highlighted ? 'highlighted ' : '', 'type' => $news->is_meeting ? 'meeting' : 'news', @@ -132,8 +133,9 @@ protected function delete(News $news): Response $news->delete(); $this->log->info( - 'Deleted {type} "{news}"', + 'Deleted {type} "{news}" ({id})', [ + 'id' => $news->id, 'type' => $news->is_meeting ? 'meeting' : 'news', 'news' => $news->title, ] diff --git a/src/Controllers/Admin/QuestionsController.php b/src/Controllers/Admin/QuestionsController.php index 890c98724..61c540dca 100644 --- a/src/Controllers/Admin/QuestionsController.php +++ b/src/Controllers/Admin/QuestionsController.php @@ -57,7 +57,7 @@ public function delete(Request $request): Response $question = $this->question->findOrFail($data['id']); $question->delete(); - $this->log->info('Deleted question {question}', ['question' => $question->text]); + $this->log->info('Deleted question {question} ({id})', ['question' => $question->text, 'id' => $question->id]); $this->addNotification('question.delete.success'); return $this->redirect->to('/admin/questions'); @@ -89,7 +89,10 @@ public function save(Request $request): Response if (!is_null($data['delete'])) { $question->delete(); - $this->log->info('Deleted question "{question}"', ['question' => $question->text]); + $this->log->info( + 'Deleted question {question} ({id})', + ['question' => $question->text, 'id' => $question->id] + ); $this->addNotification('question.delete.success'); @@ -108,8 +111,8 @@ public function save(Request $request): Response $question->save(); $this->log->info( - 'Updated questions "{text}": {answer}', - ['text' => $question->text, 'answer' => $question->answer] + 'Saved questions "{text}" ({id}): {answer}', + ['text' => $question->text, 'answer' => $question->answer, 'id' => $question->id] ); $this->addNotification('question.edit.success'); diff --git a/src/Controllers/Admin/ShiftTypesController.php b/src/Controllers/Admin/ShiftTypesController.php index 4bc7f46ab..1f37e7290 100644 --- a/src/Controllers/Admin/ShiftTypesController.php +++ b/src/Controllers/Admin/ShiftTypesController.php @@ -136,8 +136,9 @@ public function save(Request $request): Response } $this->log->info( - 'Updated shift type "{name}": {description}, {signup_advance_hours}, {angels}', + 'Saved shift type "{name}" ({id}): {description}, {signup_advance_hours}, {angels}', [ + 'id' => $shiftType->id, 'name' => $shiftType->name, 'description' => $shiftType->description, 'signup_advance_hours' => $shiftType->signup_advance_hours, @@ -165,7 +166,7 @@ public function delete(Request $request): Response } $shiftType->delete(); - $this->log->info('Deleted shift type {name}', ['name' => $shiftType->name]); + $this->log->info('Deleted shift type {name} ({id})', ['name' => $shiftType->name, 'id' => $shiftType->id]); $this->addNotification('shifttype.delete.success'); return $this->redirect->to('/admin/shifttypes'); diff --git a/src/Controllers/Admin/TagController.php b/src/Controllers/Admin/TagController.php index e69dff6c7..e1fd690c7 100644 --- a/src/Controllers/Admin/TagController.php +++ b/src/Controllers/Admin/TagController.php @@ -78,7 +78,7 @@ public function save(Request $request): Response $tag->save(); - $this->log->info('Updated tag "{name}"', ['name' => $tag->name]); + $this->log->info('Saved tag "{name}" ({id})', ['name' => $tag->name, 'id' => $tag->id]); $this->addNotification('tag.edit.success'); return $this->redirect->to('/admin/tags'); @@ -88,7 +88,7 @@ protected function delete(Tag $tag): Response { $tag->delete(); - $this->log->info('Deleted tag "{name}"', ['tag' => $tag->name]); + $this->log->info('Deleted tag "{name}" ({id})', ['name' => $tag->name, 'id' => $tag->id]); $this->addNotification('tag.delete.success'); return $this->redirect->to('/admin/tags'); diff --git a/src/Controllers/Admin/UserWorklogController.php b/src/Controllers/Admin/UserWorklogController.php index 6cb6e6e02..fd4316d46 100644 --- a/src/Controllers/Admin/UserWorklogController.php +++ b/src/Controllers/Admin/UserWorklogController.php @@ -84,8 +84,9 @@ public function saveWorklog(Request $request): Response $worklog->save(); $this->log->info( - (isset($worklogId) ? 'Edited' : 'Added') . ' worklog for {name} ({id}) at {time} spanning {hours}h: {text}', + 'Saved worklog ({wl_id}) for {name} ({id}) at {time} spanning {hours}h: {text}', [ + 'wl_id' => $worklog->id, 'name' => $user->name, 'id' => $user->id, 'time' => $worklog->worked_at, @@ -127,8 +128,9 @@ public function deleteWorklog(Request $request): Response $worklog->delete(); $this->log->info( - 'Deleted worklog for {name} ({id}) at {time} spanning {hours}h: {text}', + 'Deleted worklog ({wl_id}) for {name} ({id}) at {time} spanning {hours}h: {text}', [ + 'wl_id' => $worklog->id, 'name' => $worklog->user->name, 'id' => $worklog->user->id, 'time' => $worklog->worked_at, diff --git a/tests/Unit/Controllers/Admin/FaqControllerTest.php b/tests/Unit/Controllers/Admin/FaqControllerTest.php index 12f76a566..6eb8dec72 100644 --- a/tests/Unit/Controllers/Admin/FaqControllerTest.php +++ b/tests/Unit/Controllers/Admin/FaqControllerTest.php @@ -78,7 +78,7 @@ public function testSaveCreateEdit(): void $controller->save($this->request); - $this->assertTrue($this->log->hasInfoThatContains('Updated')); + $this->assertTrue($this->log->hasInfoThatContains('Saved')); $faq = (new Faq())->find(2); $this->assertEquals('Foo?', $faq->question); diff --git a/tests/Unit/Controllers/Admin/NewsControllerTest.php b/tests/Unit/Controllers/Admin/NewsControllerTest.php index d7e8e269a..34061e442 100644 --- a/tests/Unit/Controllers/Admin/NewsControllerTest.php +++ b/tests/Unit/Controllers/Admin/NewsControllerTest.php @@ -159,7 +159,7 @@ public function testSaveCreateEdit( $controller->save($this->request); - $this->assertTrue($this->log->hasInfoThatContains('Updated')); + $this->assertTrue($this->log->hasInfoThatContains('Saved')); $this->assertHasNotification('news.edit.success'); diff --git a/tests/Unit/Controllers/Admin/QuestionsControllerTest.php b/tests/Unit/Controllers/Admin/QuestionsControllerTest.php index 480c47126..eabe560a7 100644 --- a/tests/Unit/Controllers/Admin/QuestionsControllerTest.php +++ b/tests/Unit/Controllers/Admin/QuestionsControllerTest.php @@ -157,7 +157,7 @@ public function testSaveCreateEdit(): void $controller->save($this->request); - $this->assertTrue($this->log->hasInfoThatContains('Updated')); + $this->assertTrue($this->log->hasInfoThatContains('Saved')); $this->assertHasNotification('question.edit.success'); $question = Question::find(2); diff --git a/tests/Unit/Controllers/Admin/ShiftTypesControllerTest.php b/tests/Unit/Controllers/Admin/ShiftTypesControllerTest.php index f733e9524..6cffdaf03 100644 --- a/tests/Unit/Controllers/Admin/ShiftTypesControllerTest.php +++ b/tests/Unit/Controllers/Admin/ShiftTypesControllerTest.php @@ -136,7 +136,7 @@ public function testSave(): void $controller->save($this->request); - $this->assertTrue($this->log->hasInfoThatContains('Updated shift type')); + $this->assertTrue($this->log->hasInfoThatContains('Saved shift type')); $this->assertHasNotification('shifttype.edit.success'); $this->assertCount(1, ShiftType::whereName('Test shift type')->get()); $this->assertCount(1, ShiftType::whereDescription('Something')->get()); diff --git a/tests/Unit/Controllers/Admin/TagControllerTest.php b/tests/Unit/Controllers/Admin/TagControllerTest.php index 9ac020bca..6a6148771 100644 --- a/tests/Unit/Controllers/Admin/TagControllerTest.php +++ b/tests/Unit/Controllers/Admin/TagControllerTest.php @@ -118,7 +118,7 @@ public function testSaveCreateEdit(): void $controller->save($this->request); - $this->assertTrue($this->log->hasInfoThatContains('Updated')); + $this->assertTrue($this->log->hasInfoThatContains('Saved')); /** @var Tag $tag */ $tag = (new Tag())->find(2); diff --git a/tests/Unit/Controllers/Admin/UserWorklogControllerTest.php b/tests/Unit/Controllers/Admin/UserWorklogControllerTest.php index ed7467d6a..3035d763e 100644 --- a/tests/Unit/Controllers/Admin/UserWorklogControllerTest.php +++ b/tests/Unit/Controllers/Admin/UserWorklogControllerTest.php @@ -162,7 +162,7 @@ public function testSaveNewWorklog(): void $this->controller->saveWorklog($request); $this->assertHasNotification('worklog.add.success'); - $this->assertTrue($this->log->hasInfoThatContains('Added worklog for')); + $this->assertTrue($this->log->hasInfoThatContains('Saved worklog')); $this->assertEquals(1, $this->user->worklogs->count()); $new_worklog = $this->user->worklogs[0]; From 3800cd7fe5f8f442fc66d3f9f5034334b7a61016 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sat, 1 Feb 2025 21:29:25 +0100 Subject: [PATCH 034/157] Update erusev/parsedown to dev for PHP 8.4 fix --- .gitlab-ci.yml | 2 +- composer.json | 2 +- composer.lock | 15 ++++++++------- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2569f189a..0bb656d8c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -52,7 +52,7 @@ composer validate: image: composer:latest stage: prepare script: - - composer --no-ansi validate --strict + - composer --no-ansi validate composer install: <<: *use_cache diff --git a/composer.json b/composer.json index 9962c11fd..ddc8ddf78 100644 --- a/composer.json +++ b/composer.json @@ -35,7 +35,7 @@ "ext-pdo": "*", "ext-simplexml": "*", "ext-xml": "*", - "erusev/parsedown": "^1.7", + "erusev/parsedown": "1.7.x-dev#f7285e7b2c55039401e9d380741c2dc805edf980", "gettext/gettext": "^5.7", "gettext/translator": "^1.2", "guzzlehttp/guzzle": "^7.9", diff --git a/composer.lock b/composer.lock index 5c700e097..db13d17c3 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "29d2127c46a82264d42942c32817dc43", + "content-hash": "b908a01f747299e59005bbad709cfef2", "packages": [ { "name": "brick/math", @@ -448,16 +448,16 @@ }, { "name": "erusev/parsedown", - "version": "1.7.4", + "version": "1.7.x-dev", "source": { "type": "git", "url": "https://github.com/erusev/parsedown.git", - "reference": "cb17b6477dfff935958ba01325f2e8a2bfa6dab3" + "reference": "f7285e7b2c55039401e9d380741c2dc805edf980" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/erusev/parsedown/zipball/cb17b6477dfff935958ba01325f2e8a2bfa6dab3", - "reference": "cb17b6477dfff935958ba01325f2e8a2bfa6dab3", + "url": "https://api.github.com/repos/erusev/parsedown/zipball/f7285e7b2c55039401e9d380741c2dc805edf980", + "reference": "f7285e7b2c55039401e9d380741c2dc805edf980", "shasum": "" }, "require": { @@ -465,7 +465,7 @@ "php": ">=5.3.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35" + "phpunit/phpunit": "^4.8|^5.7|^6.5|^7.5|^8.5|^9.6" }, "type": "library", "autoload": { @@ -494,7 +494,7 @@ "issues": "https://github.com/erusev/parsedown/issues", "source": "https://github.com/erusev/parsedown/tree/1.7.x" }, - "time": "2019-12-30T22:54:17+00:00" + "time": "2024-07-12T14:59:16+00:00" }, { "name": "gettext/gettext", @@ -7771,6 +7771,7 @@ "aliases": [], "minimum-stability": "stable", "stability-flags": { + "erusev/parsedown": 20, "rcrowe/twigbridge": 20 }, "prefer-stable": false, From 53f216b9228e93e0dd52ae274612efc102a12da1 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Thu, 26 Dec 2024 20:15:41 +0100 Subject: [PATCH 035/157] Move angeltype supporter checks higher up the stack --- includes/controller/shifts_controller.php | 4 +- includes/model/Shifts_model.php | 11 ++++-- includes/view/Shifts_view.php | 45 ++++++++++++++++++----- includes/view/User_view.php | 27 ++++++++------ 4 files changed, 61 insertions(+), 26 deletions(-) diff --git a/includes/controller/shifts_controller.php b/includes/controller/shifts_controller.php index 586d17391..4d187b355 100644 --- a/includes/controller/shifts_controller.php +++ b/includes/controller/shifts_controller.php @@ -303,7 +303,9 @@ function shift_controller() throw_redirect(url('/user-shifts')); } - $shift = Shift($request->input('shift_id')); + $shift = Shift::with(['shiftEntries.user.state', 'shiftEntries.angelType']) + ->findOrFail($request->input('shift_id')); + $shift = Shift($shift); if (empty($shift)) { error(__('Shift could not be found.')); throw_redirect(url('/user-shifts')); diff --git a/includes/model/Shifts_model.php b/includes/model/Shifts_model.php index fa9e5db8d..2ca09250a 100644 --- a/includes/model/Shifts_model.php +++ b/includes/model/Shifts_model.php @@ -536,9 +536,10 @@ function Shift_signup_allowed_admin(AngelType $needed_angeltype, $shift_entries) * @param Shift $shift The shift * @param AngelType $angeltype The angeltype * @param int $signout_user_id The user that was signed up for the shift + * @param ?bool $isAngeltypeSupporter User is supporter for angeltype * @return bool */ -function Shift_signout_allowed(Shift $shift, AngelType $angeltype, $signout_user_id) +function Shift_signout_allowed(Shift $shift, AngelType $angeltype, $signout_user_id, ?bool $isAngeltypeSupporter = null) { $user = auth()->user(); @@ -548,9 +549,10 @@ function Shift_signout_allowed(Shift $shift, AngelType $angeltype, $signout_user } // angeltype supporter can sign out any user at any time from their supported angeltype - if ( - $user->isAngelTypeSupporter($angeltype) || auth()->can('admin_user_angeltypes') - ) { + $isAngeltypeSupporter = !is_null($isAngeltypeSupporter) + ? $isAngeltypeSupporter + : $user->isAngelTypeSupporter($angeltype); + if ($isAngeltypeSupporter || auth()->can('admin_user_angeltypes')) { return true; } @@ -632,6 +634,7 @@ function Shifts_by_user($userId, $include_freeloaded_comments = false) JOIN `shift_types` ON (`shift_types`.`id` = `shifts`.`shift_type_id`) JOIN `locations` ON (`shifts`.`location_id` = `locations`.`id`) WHERE shift_entries.`user_id` = ? + GROUP BY shifts.id ORDER BY `start` ', [ diff --git a/includes/view/Shifts_view.php b/includes/view/Shifts_view.php index be139cada..e4c945cde 100644 --- a/includes/view/Shifts_view.php +++ b/includes/view/Shifts_view.php @@ -161,6 +161,11 @@ function Shift_view( $goodie_enabled = $goodie !== GoodieType::None; $goodie_tshirt = $goodie === GoodieType::Tshirt; + $supportsAngelTypes = auth()->user() + ->userAngelTypes() + ->where('supporter', true) + ->pluck('angel_types.id'); + $parsedown = new Parsedown(); $angeltypes = []; @@ -171,7 +176,7 @@ function Shift_view( $needed_angels = ''; $neededAngels = new Collection($shift->neededAngels); foreach ($neededAngels as $needed_angeltype) { - $needed_angels .= Shift_view_render_needed_angeltype($needed_angeltype, $angeltypes, $shift, $user_shift_admin); + $needed_angels .= Shift_view_render_needed_angeltype($needed_angeltype, $angeltypes, $shift, $user_shift_admin, $supportsAngelTypes); } $shiftEntry = $shift->shiftEntries; @@ -189,7 +194,7 @@ function Shift_view( ->where('angel_type_id', $type) ->whereNull('freeloaded_by') ->count(), - ], $angeltypes, $shift, $user_shift_admin); + ], $angeltypes, $shift, $user_shift_admin, $supportsAngelTypes); } } @@ -298,12 +303,18 @@ function Shift_view( * @param AngelType[]|Collection $angeltypes * @param Shift $shift * @param bool $user_shift_admin + * @param Collection|int[] $supportsAngelTypes * @return string */ -function Shift_view_render_needed_angeltype($needed_angeltype, $angeltypes, Shift $shift, $user_shift_admin) -{ +function Shift_view_render_needed_angeltype( + $needed_angeltype, + $angeltypes, + Shift $shift, + $user_shift_admin, + $supportsAngelTypes +) { $angeltype = $angeltypes[$needed_angeltype['angel_type_id']]; - $angeltype_supporter = auth()->user()->isAngelTypeSupporter($angeltype) + $angeltype_supporter = $supportsAngelTypes->contains($needed_angeltype['angel_type_id']) || auth()->can('admin_user_angeltypes'); $needed_angels = ''; @@ -333,7 +344,13 @@ function Shift_view_render_needed_angeltype($needed_angeltype, $angeltypes, Shif $angels = []; foreach ($shift->shiftEntries as $shift_entry) { if ($shift_entry->angel_type_id == $needed_angeltype['angel_type_id']) { - $angels[] = Shift_view_render_shift_entry($shift_entry, $user_shift_admin, $angeltype_supporter, $shift); + $angels[] = Shift_view_render_shift_entry( + $shift_entry, + $user_shift_admin, + $angeltype_supporter, + $shift, + $supportsAngelTypes + ); } } @@ -348,10 +365,16 @@ function Shift_view_render_needed_angeltype($needed_angeltype, $angeltypes, Shif * @param bool $user_shift_admin * @param bool $angeltype_supporter * @param Shift $shift + * @param Collection|int[] $supportsAngelTypes * @return string */ -function Shift_view_render_shift_entry(ShiftEntry $shift_entry, $user_shift_admin, $angeltype_supporter, Shift $shift) -{ +function Shift_view_render_shift_entry( + ShiftEntry $shift_entry, + $user_shift_admin, + $angeltype_supporter, + Shift $shift, + $supportsAngelTypes +) { $entry = User_Nick_render($shift_entry->user); if ($shift_entry->freeloaded_by) { $entry = '' . $entry . ''; @@ -366,13 +389,15 @@ function Shift_view_render_shift_entry(ShiftEntry $shift_entry, $user_shift_admi __('form.edit') ); $angeltype = $shift_entry->angelType; - $disabled = Shift_signout_allowed($shift, $angeltype, $shift_entry->user_id) ? '' : ' btn-disabled'; + $isAngeltypeSupporter = $supportsAngelTypes->contains($angeltype->id); + $signOutAllowed = Shift_signout_allowed($shift, $angeltype, $shift_entry->user_id, $isAngeltypeSupporter); + $disabled = $signOutAllowed ? '' : ' btn-disabled'; $entry .= button_icon( shift_entry_delete_link($shift_entry), 'trash', 'btn-sm btn-danger' . $disabled, __('form.delete'), - !Shift_signout_allowed($shift, $angeltype, $shift_entry->user_id) + !$signOutAllowed ); $entry .= ''; } diff --git a/includes/view/User_view.php b/includes/view/User_view.php index 0974f73ba..925641dfb 100644 --- a/includes/view/User_view.php +++ b/includes/view/User_view.php @@ -348,15 +348,15 @@ function User_view_shiftentries($needed_angel_type) * @param Shift $shift * @param User $user_source * @param bool $its_me + * @param bool $supporter * @return array */ -function User_view_myshift(Shift $shift, $user_source, $its_me) +function User_view_myshift(Shift $shift, $user_source, $its_me, $supporter) { $nightShiftsConfig = config('night_shifts'); $goodie = GoodieType::from(config('goodie_type')); $goodie_enabled = $goodie !== GoodieType::None; $goodie_tshirt = $goodie === GoodieType::Tshirt; - $supporter = auth()->user()->isAngelTypeSupporter(AngelType::findOrFail($shift->angel_type_id)); $shift_info = '' . htmlspecialchars($shift->shiftType->name) . ''; if ($shift->title) { @@ -461,6 +461,7 @@ function User_view_myshift(Shift $shift, $user_source, $its_me) * @param bool $goodie_admin * @param Worklog[]|Collection $user_worklogs * @param bool $admin_user_worklog_privilege + * @param Collection|int[] $supported_angeltypes * * @return array */ @@ -471,15 +472,12 @@ function User_view_myshifts( $goodie_score, $goodie_admin, $user_worklogs, - $admin_user_worklog_privilege + $admin_user_worklog_privilege, + $supported_angeltypes, ) { $goodie = GoodieType::from(config('goodie_type')); $goodie_enabled = $goodie !== GoodieType::None; $goodie_tshirt = $goodie === GoodieType::Tshirt; - $supported_angeltypes = auth()->user() - ->userAngelTypes() - ->where('supporter', true) - ->pluck('angel_types.id'); $show_sum = true; $myshifts_table = []; @@ -491,7 +489,7 @@ function User_view_myshifts( $show_sum = false; continue; } - $myshifts_table[$key] = User_view_myshift($shift, $user_source, $its_me); + $myshifts_table[$key] = User_view_myshift($shift, $user_source, $its_me, $supporter); if (!$shift->freeloaded_by) { $timeSum += ($shift->end->timestamp - $shift->start->timestamp); } @@ -637,10 +635,16 @@ function User_view( $user_name = htmlspecialchars((string) $user_source->personalData->first_name) . ' ' . htmlspecialchars((string) $user_source->personalData->last_name); $myshifts_table = ''; + $supported_angeltypes = auth()->user() + ->userAngelTypes() + ->where('supporter', true) + ->pluck('angel_types.id'); $user_angeltypes_supporter = false; foreach ($user_source->userAngelTypes as $user_angeltype) { - $user_angeltypes_supporter = $user_angeltypes_supporter - || $auth->user()->isAngelTypeSupporter($user_angeltype); + if ($supported_angeltypes->contains($user_angeltype->id)) { + $user_angeltypes_supporter = true; + break; + } } if ($its_me || $admin_user_privilege || $goodie_admin || $user_angeltypes_supporter) { @@ -651,7 +655,8 @@ function User_view( $goodie_score, $goodie_admin, $user_worklogs, - $admin_user_worklog_privilege + $admin_user_worklog_privilege, + $supported_angeltypes, ); if (count($my_shifts) > 0) { $myshifts_table = div('table-responsive', table([ From fce4f0da08fd34211e94fd5457fe65451f093b42 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sat, 28 Dec 2024 16:27:49 +0100 Subject: [PATCH 036/157] Fix answering deleted questions --- src/Controllers/Admin/QuestionsController.php | 4 +-- .../Admin/QuestionsControllerTest.php | 28 +++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/Controllers/Admin/QuestionsController.php b/src/Controllers/Admin/QuestionsController.php index 61c540dca..d07b905f0 100644 --- a/src/Controllers/Admin/QuestionsController.php +++ b/src/Controllers/Admin/QuestionsController.php @@ -67,7 +67,7 @@ public function edit(Request $request): Response { $questionId = (int) $request->getAttribute('question_id'); - $questions = $this->question->find($questionId); + $questions = $this->question->findOrFail($questionId); return $this->showEdit($questions); } @@ -77,7 +77,7 @@ public function save(Request $request): Response $questionId = (int) $request->getAttribute('question_id'); /** @var Question $question */ - $question = $this->question->findOrNew($questionId); + $question = $this->question->findOrFail($questionId); $data = $this->validate($request, [ 'text' => 'required', diff --git a/tests/Unit/Controllers/Admin/QuestionsControllerTest.php b/tests/Unit/Controllers/Admin/QuestionsControllerTest.php index eabe560a7..e0e64bbfc 100644 --- a/tests/Unit/Controllers/Admin/QuestionsControllerTest.php +++ b/tests/Unit/Controllers/Admin/QuestionsControllerTest.php @@ -121,11 +121,39 @@ public function testEdit(): void $controller->edit($this->request); } + /** + * @covers \Engelsystem\Controllers\Admin\QuestionsController::edit + */ + public function testEditNotFound(): void + { + $this->request->attributes->set('question_id', 42); + $this->expectException(ModelNotFoundException::class); + + /** @var QuestionsController $controller */ + $controller = $this->app->get(QuestionsController::class); + + $controller->edit($this->request); + } + + /** + * @covers \Engelsystem\Controllers\Admin\QuestionsController::save + */ + public function testSaveNotFound(): void + { + $this->expectException(ModelNotFoundException::class); + + /** @var QuestionsController $controller */ + $controller = $this->app->make(QuestionsController::class); + $controller->setValidator(new Validator()); + $controller->save($this->request); + } + /** * @covers \Engelsystem\Controllers\Admin\QuestionsController::save */ public function testSaveCreateInvalid(): void { + $this->request->attributes->set('question_id', 1); $this->expectException(ValidationException::class); /** @var QuestionsController $controller */ From bb7375ddb455e26293a8843a9a475a2808c58248 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sat, 28 Dec 2024 16:59:23 +0100 Subject: [PATCH 037/157] Fixed email modified logging --- includes/pages/admin_user.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/includes/pages/admin_user.php b/includes/pages/admin_user.php index 205aaad0c..7e6f1c19b 100644 --- a/includes/pages/admin_user.php +++ b/includes/pages/admin_user.php @@ -291,7 +291,10 @@ function admin_user() $changed_email = false; $email = $request->postData('eemail'); - if (($user_source->email !== $email) && User::whereEmail($email)->exists()) { + if ( + $user_source->email !== $email + && User::whereEmail($email)->whereNot('id', $user_source->id)->exists() + ) { $html .= error(__('settings.profile.email.already-taken') . "\n", true); break; } @@ -312,7 +315,8 @@ function admin_user() } $old_nick = $user_source->name; if ($nickValid && $user_nick_edit) { - $changed_nick = $user_source->name !== $nick; + $changed_nick = $user_source->name !== $nick + && !User::whereName($nick)->whereNot('id', $user_source->id)->exists(); $user_source->name = $nick; } $user_source->save(); From cbbdeb88f1e7038d58e4737fb4134e00ec710059 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Mon, 10 Feb 2025 18:29:10 +0100 Subject: [PATCH 038/157] Error handler: Only trigger when not recoverable on prod --- src/Exceptions/Handler.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Exceptions/Handler.php b/src/Exceptions/Handler.php index d9ae75f5e..215f72e4f 100644 --- a/src/Exceptions/Handler.php +++ b/src/Exceptions/Handler.php @@ -37,7 +37,10 @@ public function register(): void return; } - set_error_handler([$this, 'errorHandler']); + $level = $this->environment == Environment::DEVELOPMENT + ? E_ALL + : E_RECOVERABLE_ERROR | E_COMPILE_ERROR; + set_error_handler([$this, 'errorHandler'], $level); set_exception_handler([$this, 'exceptionHandler']); } From f56b873b95be1750aac0a3db35ff25b2bc66e957 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Wed, 12 Feb 2025 17:23:30 +0100 Subject: [PATCH 039/157] Log names exceptions --- src/Exceptions/Handlers/Legacy.php | 3 ++- src/Logger/Logger.php | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Exceptions/Handlers/Legacy.php b/src/Exceptions/Handlers/Legacy.php index c121872da..fd35902bc 100644 --- a/src/Exceptions/Handlers/Legacy.php +++ b/src/Exceptions/Handlers/Legacy.php @@ -25,7 +25,8 @@ public function report(Throwable $e): void { $previous = $e->getPrevious(); error_log(sprintf( - 'Exception: Code: %s, Message: %s, File: %s:%u, Previous: %s, Trace: %s', + '%s: Code: %s, Message: %s, File: %s:%u, Previous: %s, Trace: %s', + get_class($e), $e->getCode(), $e->getMessage(), $this->stripBasePath($e->getFile()), diff --git a/src/Logger/Logger.php b/src/Logger/Logger.php index f697a2a89..c33350704 100644 --- a/src/Logger/Logger.php +++ b/src/Logger/Logger.php @@ -68,7 +68,8 @@ protected function interpolate(string $message, array $context = []): string protected function formatException(Throwable $e): string { return sprintf( - implode(PHP_EOL, ['', 'Exception: %s', 'File: %s:%u', 'Code: %s', 'Trace:', '%s']), + implode(PHP_EOL, ['', '%s: %s', 'File: %s:%u', 'Code: %s', 'Trace:', '%s']), + get_class($e), $e->getMessage(), $e->getFile(), $e->getLine(), From 39c5ed85e325313623c78ea9f353e8b4745f8734 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Mon, 17 Feb 2025 22:28:00 +0100 Subject: [PATCH 040/157] Update dependencies --- yarn.lock | 303 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 170 insertions(+), 133 deletions(-) diff --git a/yarn.lock b/yarn.lock index 614ebc613..aa56a7b1f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1100,19 +1100,19 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz#d9fae00a2d5cb40f92cfe64b47ad749fbc38f917" integrity sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw== -"@jest/schemas@^29.4.0": - version "29.4.0" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.4.0.tgz#0d6ad358f295cc1deca0b643e6b4c86ebd539f17" - integrity sha512-0E01f/gOZeNTG76i5eWWSupvSHaIINrTie7vCyjiYFKgzNdyEGd12BUv4oNBFHOqlHDbtoJi3HrQ38KCC90NsQ== +"@jest/schemas@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" + integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== dependencies: - "@sinclair/typebox" "^0.25.16" + "@sinclair/typebox" "^0.27.8" -"@jest/types@^29.4.1": - version "29.4.1" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.4.1.tgz#f9f83d0916f50696661da72766132729dcb82ecb" - integrity sha512-zbrAXDUOnpJ+FMST2rV7QZOgec8rskg2zv8g2ajeqitp4tvZiyqTCYXANrKsM+ryj5o+LI+ZN2EgU9drrkiwSA== +"@jest/types@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" + integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== dependencies: - "@jest/schemas" "^29.4.0" + "@jest/schemas" "^29.6.3" "@types/istanbul-lib-coverage" "^2.0.0" "@types/istanbul-reports" "^3.0.0" "@types/node" "*" @@ -1245,10 +1245,10 @@ resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.6.tgz#cee20bd55e68a1720bdab363ecf0c821ded4cd45" integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw== -"@sinclair/typebox@^0.25.16": - version "0.25.21" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.21.tgz#763b05a4b472c93a8db29b2c3e359d55b29ce272" - integrity sha512-gFukHN4t8K4+wVC+ECqeqwzBDeFeTzBXroBTqE6vcWrQGbEUpHO7LYdG0f4xnvYq4VOEwITSlHlp0JBAIFMS/g== +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== "@trysound/sax@0.2.0": version "0.2.0" @@ -1261,33 +1261,40 @@ integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" - integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== + version "2.0.6" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" + integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== "@types/istanbul-lib-report@*": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" - integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf" + integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA== dependencies: "@types/istanbul-lib-coverage" "*" "@types/istanbul-reports@^3.0.0": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff" - integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54" + integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== dependencies: "@types/istanbul-lib-report" "*" -"@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": +"@types/json-schema@^7.0.8": version "7.0.11" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== +"@types/json-schema@^7.0.9": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + "@types/node@*": - version "18.11.19" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.19.tgz#35e26df9ec441ab99d73e99e9aca82935eea216d" - integrity sha512-YUgMWAQBWLObABqrvx8qKO1enAvBUdjZOAWQ5grBAkp5LQv45jBvYKZ3oFS9iKRCQyFjqw6iuEa1vmFqtxYLZw== + version "22.13.4" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.13.4.tgz#3fe454d77cd4a2d73c214008b3e331bfaaf5038a" + integrity sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg== + dependencies: + undici-types "~6.20.0" "@types/parse-json@^4.0.0": version "4.0.0" @@ -1295,14 +1302,14 @@ integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== "@types/yargs-parser@*": - version "21.0.0" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b" - integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA== + version "21.0.3" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" + integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== "@types/yargs@^17.0.8": - version "17.0.22" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.22.tgz#7dd37697691b5f17d020f3c63e7a45971ff71e9a" - integrity sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g== + version "17.0.33" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.33.tgz#8c32303da83eec050a84b3c7ae7b9f922d13e32d" + integrity sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA== dependencies: "@types/yargs-parser" "*" @@ -1492,7 +1499,7 @@ ajv-keywords@^3.5.2: resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv-keywords@^5.0.0: +ajv-keywords@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== @@ -1509,15 +1516,15 @@ ajv@^6.10.0, ajv@^6.12.4, ajv@^6.12.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.0, ajv@^8.8.0: - version "8.12.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" - integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== +ajv@^8.0.0, ajv@^8.9.0: + version "8.17.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" + integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== dependencies: - fast-deep-equal "^3.1.1" + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" json-schema-traverse "^1.0.0" require-from-string "^2.0.2" - uri-js "^4.2.2" ansi-regex@^5.0.1: version "5.0.1" @@ -1647,7 +1654,17 @@ braces@~3.0.2: dependencies: fill-range "^7.1.1" -browserslist@^4.0.0, browserslist@^4.16.6, browserslist@^4.21.10, browserslist@^4.21.3, browserslist@^4.21.4: +browserslist@^4.0.0, browserslist@^4.21.4: + version "4.24.4" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.4.tgz#c6b2865a3f08bcb860a0e827389003b9fe686e4b" + integrity sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A== + dependencies: + caniuse-lite "^1.0.30001688" + electron-to-chromium "^1.5.73" + node-releases "^2.0.19" + update-browserslist-db "^1.1.1" + +browserslist@^4.21.10, browserslist@^4.21.3: version "4.23.3" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.3.tgz#debb029d3c93ebc97ffbc8d9cbb03403e227c800" integrity sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA== @@ -1677,16 +1694,16 @@ caniuse-api@^3.0.0: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001426: +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001646, caniuse-lite@^1.0.30001688: + version "1.0.30001700" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001700.tgz#26cd429cf09b4fd4e745daf4916039c794d720f6" + integrity sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ== + +caniuse-lite@^1.0.30001426: version "1.0.30001450" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001450.tgz#022225b91200589196b814b51b1bbe45144cf74f" integrity sha512-qMBmvmQmFXaSxexkjjfMvD5rnDL0+m+dUMZKoDYsGG8iZN29RuYh9eRoMvKsT6uMAWlyUUGDEQGJJYjzCIO9ew== -caniuse-lite@^1.0.30001646: - version "1.0.30001655" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001655.tgz#0ce881f5a19a2dcfda2ecd927df4d5c1684b982f" - integrity sha512-jRGVy3iSGO5Uutn2owlb5gR6qsGngTw9ZTb4ali9f3glshcNmJ2noam4Mo9zia5P9Dk3jNNydy7vQjuE5dQmfg== - chalk@^2.0.0, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -1734,9 +1751,9 @@ chrome-trace-event@^1.0.2: integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== ci-info@^3.2.0: - version "3.7.1" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.7.1.tgz#708a6cdae38915d597afdf3b145f2f8e1ff55f3f" - integrity sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w== + version "3.9.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" + integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== clone-deep@^4.0.1: version "4.0.1" @@ -1849,9 +1866,9 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3: which "^2.0.1" css-declaration-sorter@^6.3.1: - version "6.3.1" - resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz#be5e1d71b7a992433fb1c542c7a1b835e45682ec" - integrity sha512-fBffmak0bPAnyqc/HO8C3n2sHrp9wcqQz6ES9koRF2/mLOVAx9zIQ3Y7R29sYCteTPqMCwns4WYQoCX91Xl3+w== + version "6.4.1" + resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz#28beac7c20bad7f1775be3a7129d7eae409a3a71" + integrity sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g== css-loader@^6.7.3: version "6.7.3" @@ -1908,22 +1925,22 @@ cssesc@^3.0.0: resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== -cssnano-preset-default@^5.2.13: - version "5.2.13" - resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-5.2.13.tgz#e7353b0c57975d1bdd97ac96e68e5c1b8c68e990" - integrity sha512-PX7sQ4Pb+UtOWuz8A1d+Rbi+WimBIxJTRyBdgGp1J75VU0r/HFQeLnMYgHiCAp6AR4rqrc7Y4R+1Rjk3KJz6DQ== +cssnano-preset-default@^5.2.14: + version "5.2.14" + resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-5.2.14.tgz#309def4f7b7e16d71ab2438052093330d9ab45d8" + integrity sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A== dependencies: css-declaration-sorter "^6.3.1" cssnano-utils "^3.1.0" postcss-calc "^8.2.3" - postcss-colormin "^5.3.0" + postcss-colormin "^5.3.1" postcss-convert-values "^5.1.3" postcss-discard-comments "^5.1.2" postcss-discard-duplicates "^5.1.0" postcss-discard-empty "^5.1.1" postcss-discard-overridden "^5.1.0" postcss-merge-longhand "^5.1.7" - postcss-merge-rules "^5.1.3" + postcss-merge-rules "^5.1.4" postcss-minify-font-values "^5.1.0" postcss-minify-gradients "^5.1.1" postcss-minify-params "^5.1.4" @@ -1938,7 +1955,7 @@ cssnano-preset-default@^5.2.13: postcss-normalize-url "^5.1.0" postcss-normalize-whitespace "^5.1.1" postcss-ordered-values "^5.1.3" - postcss-reduce-initial "^5.1.1" + postcss-reduce-initial "^5.1.2" postcss-reduce-transforms "^5.1.0" postcss-svgo "^5.1.0" postcss-unique-selectors "^5.1.1" @@ -1949,11 +1966,11 @@ cssnano-utils@^3.1.0: integrity sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA== cssnano@^5.1.8: - version "5.1.14" - resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-5.1.14.tgz#07b0af6da73641276fe5a6d45757702ebae2eb05" - integrity sha512-Oou7ihiTocbKqi0J1bB+TRJIQX5RMR3JghA8hcWSw9mjBLQ5Y3RWqEDoYG3sRNlAbCIXpqMoZGbq5KDR3vdzgw== + version "5.1.15" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-5.1.15.tgz#ded66b5480d5127fcb44dac12ea5a983755136bf" + integrity sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw== dependencies: - cssnano-preset-default "^5.2.13" + cssnano-preset-default "^5.2.14" lilconfig "^2.0.3" yaml "^1.10.2" @@ -2033,10 +2050,10 @@ editorconfig@^1.0.2: minimatch "9.0.1" semver "^7.5.3" -electron-to-chromium@^1.5.4: - version "1.5.13" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz#1abf0410c5344b2b829b7247e031f02810d442e6" - integrity sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q== +electron-to-chromium@^1.5.4, electron-to-chromium@^1.5.73: + version "1.5.102" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.102.tgz#81a452ace8e2c3fa7fba904ea4fed25052c53d3f" + integrity sha512-eHhqaja8tE/FNpIiBrvBjFV/SSKpyWHLvxuR9dPTdo+3V9ppdLmFB7ZZQ98qNovcngPLYIz0oOBF9P0FfZef5Q== emojis-list@^3.0.0: version "3.0.0" @@ -2073,7 +2090,7 @@ es-module-lexer@^1.2.1: resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.5.4.tgz#a8efec3a3da991e60efa6b633a7cad6ab8d26b78" integrity sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw== -escalade@^3.1.2: +escalade@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== @@ -2289,6 +2306,11 @@ fast-levenshtein@^2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== +fast-uri@^3.0.1: + version "3.0.6" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.6.tgz#88f130b77cfaea2378d56bf970dea21257a68748" + integrity sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw== + fastest-levenshtein@^1.0.12: version "1.0.16" resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" @@ -2565,12 +2587,12 @@ isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== -jest-util@^29.4.1: - version "29.4.1" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.4.1.tgz#2eeed98ff4563b441b5a656ed1a786e3abc3e4c4" - integrity sha512-bQy9FPGxVutgpN4VRc0hk6w7Hx/m6L53QxpDreTZgJd9gfx/AV2MjyPde9tGyZRINAUrSv57p2inGBu2dRLmkQ== +jest-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" + integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== dependencies: - "@jest/types" "^29.4.1" + "@jest/types" "^29.6.3" "@types/node" "*" chalk "^4.0.0" ci-info "^3.2.0" @@ -2587,12 +2609,12 @@ jest-worker@^27.4.5: supports-color "^8.0.0" jest-worker@^29.1.2: - version "29.4.1" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.4.1.tgz#7cb4a99a38975679600305650f86f4807460aab1" - integrity sha512-O9doU/S1EBe+yp/mstQ0VpPwpv0Clgn68TkNwGxL6/usX/KUW9Arnn4ag8C3jc6qHcXznhsT5Na1liYzAsuAbQ== + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" + integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== dependencies: "@types/node" "*" - jest-util "^29.4.1" + jest-util "^29.7.0" merge-stream "^2.0.0" supports-color "^8.0.0" @@ -2662,9 +2684,9 @@ levn@^0.4.1: type-check "~0.4.0" lilconfig@^2.0.3: - version "2.0.6" - resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.6.tgz#32a384558bd58af3d4c6e077dd1ad1d397bc69d4" - integrity sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg== + version "2.1.0" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" + integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== lines-and-columns@^1.1.6: version "1.2.4" @@ -2788,7 +2810,7 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -nanoid@^3.3.6: +nanoid@^3.3.6, nanoid@^3.3.8: version "3.3.8" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf" integrity sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w== @@ -2803,10 +2825,10 @@ neo-async@^2.6.2: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== -node-releases@^2.0.18: - version "2.0.18" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f" - integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== +node-releases@^2.0.18, node-releases@^2.0.19: + version "2.0.19" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314" + integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" @@ -2924,15 +2946,10 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -picocolors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" - integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== - -picocolors@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1" - integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== +picocolors@^1.0.0, picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3: version "2.3.1" @@ -2954,12 +2971,12 @@ postcss-calc@^8.2.3: postcss-selector-parser "^6.0.9" postcss-value-parser "^4.2.0" -postcss-colormin@^5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-5.3.0.tgz#3cee9e5ca62b2c27e84fce63affc0cfb5901956a" - integrity sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg== +postcss-colormin@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-5.3.1.tgz#86c27c26ed6ba00d96c79e08f3ffb418d1d1988f" + integrity sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ== dependencies: - browserslist "^4.16.6" + browserslist "^4.21.4" caniuse-api "^3.0.0" colord "^2.9.1" postcss-value-parser "^4.2.0" @@ -3009,10 +3026,10 @@ postcss-merge-longhand@^5.1.7: postcss-value-parser "^4.2.0" stylehacks "^5.1.1" -postcss-merge-rules@^5.1.3: - version "5.1.3" - resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-5.1.3.tgz#8f97679e67cc8d08677a6519afca41edf2220894" - integrity sha512-LbLd7uFC00vpOuMvyZop8+vvhnfRGpp2S+IMQKeuOZZapPRY4SMq5ErjQeHbHsjCUgJkRNrlU+LmxsKIqPKQlA== +postcss-merge-rules@^5.1.4: + version "5.1.4" + resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-5.1.4.tgz#2f26fa5cacb75b1402e213789f6766ae5e40313c" + integrity sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g== dependencies: browserslist "^4.21.4" caniuse-api "^3.0.0" @@ -3150,10 +3167,10 @@ postcss-ordered-values@^5.1.3: cssnano-utils "^3.1.0" postcss-value-parser "^4.2.0" -postcss-reduce-initial@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-5.1.1.tgz#c18b7dfb88aee24b1f8e4936541c29adbd35224e" - integrity sha512-//jeDqWcHPuXGZLoolFrUXBDyuEGbr9S2rMo19bkTIjBQ4PqkaO+oI8wua5BOUxpfi97i3PCoInsiFIEBfkm9w== +postcss-reduce-initial@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-5.1.2.tgz#798cd77b3e033eae7105c18c9d371d989e1382d6" + integrity sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg== dependencies: browserslist "^4.21.4" caniuse-api "^3.0.0" @@ -3165,7 +3182,7 @@ postcss-reduce-transforms@^5.1.0: dependencies: postcss-value-parser "^4.2.0" -postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.0.5, postcss-selector-parser@^6.0.9: +postcss-selector-parser@^6.0.2: version "6.0.11" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz#2e41dc39b7ad74046e1615185185cd0b17d0c8dc" integrity sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g== @@ -3173,6 +3190,14 @@ postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4, postcss-selector cssesc "^3.0.0" util-deprecate "^1.0.2" +postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.0.5, postcss-selector-parser@^6.0.9: + version "6.1.2" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz#27ecb41fb0e3b6ba7a1ec84fff347f734c7929de" + integrity sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + postcss-svgo@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-5.1.0.tgz#0a317400ced789f233a28826e77523f15857d80d" @@ -3193,7 +3218,7 @@ postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -postcss@^8.2.14, postcss@^8.4.17, postcss@^8.4.19, postcss@^8.4.31: +postcss@^8.2.14, postcss@^8.4.19, postcss@^8.4.31: version "8.4.31" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d" integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ== @@ -3202,6 +3227,15 @@ postcss@^8.2.14, postcss@^8.4.17, postcss@^8.4.19, postcss@^8.4.31: picocolors "^1.0.0" source-map-js "^1.0.2" +postcss@^8.4.17: + version "8.5.2" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.2.tgz#e7b99cb9d2ec3e8dd424002e7c16517cb2b846bd" + integrity sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA== + dependencies: + nanoid "^3.3.8" + picocolors "^1.1.1" + source-map-js "^1.2.1" + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -3213,9 +3247,9 @@ prettier@^2.8.3: integrity sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw== punycode@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" - integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== queue-microtask@^1.2.2: version "1.2.3" @@ -3396,14 +3430,14 @@ schema-utils@^3.1.1, schema-utils@^3.2.0: ajv-keywords "^3.5.2" schema-utils@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.0.0.tgz#60331e9e3ae78ec5d16353c467c34b3a0a1d3df7" - integrity sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg== + version "4.3.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.3.0.tgz#3b669f04f71ff2dfb5aba7ce2d5a9d79b35622c0" + integrity sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g== dependencies: "@types/json-schema" "^7.0.9" - ajv "^8.8.0" + ajv "^8.9.0" ajv-formats "^2.1.1" - ajv-keywords "^5.0.0" + ajv-keywords "^5.1.0" semver@7.5.3, semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0, semver@^7.3.8, semver@^7.5.3: version "7.5.3" @@ -3412,14 +3446,7 @@ semver@7.5.3, semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0, semver dependencies: lru-cache "^6.0.0" -serialize-javascript@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.1.tgz#b206efb27c3da0b0ab6b52f48d170b7996458e5c" - integrity sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w== - dependencies: - randombytes "^2.1.0" - -serialize-javascript@^6.0.1: +serialize-javascript@^6.0.0, serialize-javascript@^6.0.1: version "6.0.2" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== @@ -3450,11 +3477,16 @@ source-list-map@^2.0.1: resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== -"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.2: +"source-map-js@>=0.6.2 <2.0.0": version "1.0.2" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== +source-map-js@^1.0.2, source-map-js@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== + source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" @@ -3592,6 +3624,11 @@ type-fest@^0.20.2: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== +undici-types@~6.20.0: + version "6.20.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433" + integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg== + unicode-canonical-property-names-ecmascript@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" @@ -3615,13 +3652,13 @@ unicode-property-aliases-ecmascript@^2.0.0: resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== -update-browserslist-db@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz#7ca61c0d8650766090728046e416a8cde682859e" - integrity sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ== +update-browserslist-db@^1.1.0, update-browserslist-db@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz#97e9c96ab0ae7bcac08e9ae5151d26e6bc6b5580" + integrity sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg== dependencies: - escalade "^3.1.2" - picocolors "^1.0.1" + escalade "^3.2.0" + picocolors "^1.1.1" uri-js@^4.2.2: version "4.4.1" From 4300c56466e307316acbe0b1e8425bef3dac5dfc Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Tue, 26 Nov 2024 23:19:42 +0100 Subject: [PATCH 041/157] Upgrade validation --- composer.json | 2 +- composer.lock | 101 ++++++++++++++---- src/Controllers/PasswordResetController.php | 2 +- src/Controllers/SettingsController.php | 2 +- src/Http/Validation/Rules/Between.php | 8 +- src/Http/Validation/Rules/DateTime.php | 7 +- src/Http/Validation/Rules/Email.php | 14 --- src/Http/Validation/Rules/In.php | 5 +- src/Http/Validation/Rules/Max.php | 8 +- src/Http/Validation/Rules/Min.php | 8 +- src/Http/Validation/Rules/ShirtSize.php | 2 - src/Http/Validation/Validator.php | 23 +++- .../Admin/UserWorklogControllerTest.php | 14 +-- tests/Unit/Factories/UserTest.php | 4 +- tests/Unit/Http/Validation/Rules/InTest.php | 6 +- tests/Unit/Http/Validation/Rules/MinTest.php | 6 ++ tests/Unit/Http/Validation/ValidatorTest.php | 1 + 17 files changed, 148 insertions(+), 65 deletions(-) delete mode 100644 src/Http/Validation/Rules/Email.php diff --git a/composer.json b/composer.json index ddc8ddf78..f5713025c 100644 --- a/composer.json +++ b/composer.json @@ -52,7 +52,7 @@ "psr/http-server-middleware": "^1.0", "psr/log": "^3.0", "rcrowe/twigbridge": "^0.14.x-dev", - "respect/validation": "^1.1", + "respect/validation": "^2.3", "symfony/http-foundation": "^7.1", "symfony/mailer": "^7.1", "symfony/psr-http-message-bridge": "^7.1", diff --git a/composer.lock b/composer.lock index db13d17c3..69816b53b 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b908a01f747299e59005bbad709cfef2", + "content-hash": "228d02f27e034e40dffa1b18ae9947f5", "packages": [ { "name": "brick/math", @@ -3397,45 +3397,100 @@ }, "time": "2025-02-01T06:09:05+00:00" }, + { + "name": "respect/stringifier", + "version": "0.2.0", + "source": { + "type": "git", + "url": "https://github.com/Respect/Stringifier.git", + "reference": "e55af3c8aeaeaa2abb5fa47a58a8e9688cc23b59" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Respect/Stringifier/zipball/e55af3c8aeaeaa2abb5fa47a58a8e9688cc23b59", + "reference": "e55af3c8aeaeaa2abb5fa47a58a8e9688cc23b59", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.8", + "malukenho/docheader": "^0.1.7", + "phpunit/phpunit": "^6.4" + }, + "type": "library", + "autoload": { + "files": [ + "src/stringify.php" + ], + "psr-4": { + "Respect\\Stringifier\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Respect/Stringifier Contributors", + "homepage": "https://github.com/Respect/Stringifier/graphs/contributors" + } + ], + "description": "Converts any value to a string", + "homepage": "http://respect.github.io/Stringifier/", + "keywords": [ + "respect", + "stringifier", + "stringify" + ], + "support": { + "issues": "https://github.com/Respect/Stringifier/issues", + "source": "https://github.com/Respect/Stringifier/tree/0.2.0" + }, + "time": "2017-12-29T19:39:25+00:00" + }, { "name": "respect/validation", - "version": "1.1.31", + "version": "2.4.0", "source": { "type": "git", "url": "https://github.com/Respect/Validation.git", - "reference": "45d109fc830644fecc1145200d6351ce4f2769d0" + "reference": "48b38bd91e0badbc2c4381dce726b09fd68850d9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Respect/Validation/zipball/45d109fc830644fecc1145200d6351ce4f2769d0", - "reference": "45d109fc830644fecc1145200d6351ce4f2769d0", + "url": "https://api.github.com/repos/Respect/Validation/zipball/48b38bd91e0badbc2c4381dce726b09fd68850d9", + "reference": "48b38bd91e0badbc2c4381dce726b09fd68850d9", "shasum": "" }, "require": { - "php": ">=5.4", + "php": ">=8.1", + "respect/stringifier": "^0.2.0", "symfony/polyfill-mbstring": "^1.2" }, "require-dev": { - "egulias/email-validator": "~1.2 || ~2.1", - "mikey179/vfsstream": "^1.5", - "phpunit/phpunit": "~4.0 || ~5.0", - "symfony/validator": "~2.6.9", - "zendframework/zend-validator": "~2.3" + "egulias/email-validator": "^3.0", + "giggsey/libphonenumber-for-php-lite": "^8.13", + "malukenho/docheader": "^1.0", + "mikey179/vfsstream": "^1.6", + "phpstan/phpstan": "^1.9", + "phpstan/phpstan-deprecation-rules": "^1.1", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.6", + "psr/http-message": "^1.0", + "respect/coding-standard": "^4.0", + "squizlabs/php_codesniffer": "^3.7" }, "suggest": { - "egulias/email-validator": "Strict (RFC compliant) email validation", + "egulias/email-validator": "Improves the Email rule if available", "ext-bcmath": "Arbitrary Precision Mathematics", + "ext-fileinfo": "File Information", "ext-mbstring": "Multibyte String Functions", - "friendsofphp/php-cs-fixer": "Fix PSR2 and other coding style issues", - "symfony/validator": "Use Symfony validator through Respect\\Validation", - "zendframework/zend-validator": "Use Zend Framework validator through Respect\\Validation" + "giggsey/libphonenumber-for-php-lite": "Enables the phone rule if available" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, "autoload": { "psr-4": { "Respect\\Validation\\": "library/" @@ -3443,7 +3498,7 @@ }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { @@ -3460,9 +3515,9 @@ ], "support": { "issues": "https://github.com/Respect/Validation/issues", - "source": "https://github.com/Respect/Validation/tree/1.1.31" + "source": "https://github.com/Respect/Validation/tree/2.4.0" }, - "time": "2019-05-28T06:10:06+00:00" + "time": "2025-01-07T00:34:58+00:00" }, { "name": "riverline/multipart-parser", diff --git a/src/Controllers/PasswordResetController.php b/src/Controllers/PasswordResetController.php index ec601acce..7d081ec91 100644 --- a/src/Controllers/PasswordResetController.php +++ b/src/Controllers/PasswordResetController.php @@ -83,7 +83,7 @@ public function postResetPassword(Request $request): Response $reset = $this->requireToken($request); $data = $this->validate($request, [ - 'password' => 'required|min:' . config('password_min_length'), + 'password' => 'required|length:' . config('password_min_length'), 'password_confirmation' => 'required', ]); diff --git a/src/Controllers/SettingsController.php b/src/Controllers/SettingsController.php index 8d09f12df..750ad312b 100644 --- a/src/Controllers/SettingsController.php +++ b/src/Controllers/SettingsController.php @@ -146,7 +146,7 @@ public function savePassword(Request $request): Response $minLength = config('password_min_length'); $data = $this->validate($request, [ 'password' => empty($user->password) ? 'optional' : 'required', - 'new_password' => 'required|min:' . $minLength, + 'new_password' => 'required|length:' . $minLength, 'new_password2' => 'required', ]); diff --git a/src/Http/Validation/Rules/Between.php b/src/Http/Validation/Rules/Between.php index 8f502f03c..e85af5076 100644 --- a/src/Http/Validation/Rules/Between.php +++ b/src/Http/Validation/Rules/Between.php @@ -4,9 +4,15 @@ namespace Engelsystem\Http\Validation\Rules; +use Respect\Validation\Rules\AbstractEnvelope; use Respect\Validation\Rules\Between as RespectBetween; -class Between extends RespectBetween +class Between extends AbstractEnvelope { use StringInputLength; + + public function __construct(mixed $minValue, mixed $maxValue) + { + parent::__construct(new RespectBetween($minValue, $maxValue)); + } } diff --git a/src/Http/Validation/Rules/DateTime.php b/src/Http/Validation/Rules/DateTime.php index fba556845..4e07767f7 100644 --- a/src/Http/Validation/Rules/DateTime.php +++ b/src/Http/Validation/Rules/DateTime.php @@ -4,9 +4,10 @@ namespace Engelsystem\Http\Validation\Rules; -use Respect\Validation\Rules\Date; +use Respect\Validation\Rules\AbstractEnvelope; +use Respect\Validation\Rules\DateTime as RespectDateTime; -class DateTime extends Date +class DateTime extends AbstractEnvelope { public function __construct(?string $format = null) { @@ -14,6 +15,6 @@ public function __construct(?string $format = null) $format = 'Y-m-d\TH:i'; } - parent::__construct($format); + parent::__construct(new RespectDateTime($format)); } } diff --git a/src/Http/Validation/Rules/Email.php b/src/Http/Validation/Rules/Email.php deleted file mode 100644 index a14d0e92f..000000000 --- a/src/Http/Validation/Rules/Email.php +++ /dev/null @@ -1,14 +0,0 @@ -errors = []; $this->data = []; + $this->configureValidationFactory(); + $validData = []; foreach ($rules as $fieldName => $rulesList) { - $v = new RespectValidator(); - $v->with('\\Engelsystem\\Http\\Validation\\Rules', true); - $value = $data[$fieldName] ?? null; $rulesList = is_array($rulesList) ? $rulesList : explode('|', $rulesList); // Configure the check to be run for every rule foreach ($rulesList as $parameters) { + $v = new RespectValidator(); + $parameters = is_array($parameters) ? $parameters : explode(':', $parameters); $rule = array_shift($parameters); $rule = Str::camel($rule); @@ -68,8 +71,6 @@ public function validate(array $data, array $rules): bool } else { $this->errors[$fieldName][] = implode('.', ['validation', $fieldName, $this->mapBack($rule)]); } - - $v->removeRules(); } } @@ -112,4 +113,16 @@ public function addErrors(array $errors): self return $this; } + + protected function configureValidationFactory(): void + { + $f = (new Factory()) + ->withRuleNamespace('\\Engelsystem\\Http\\Validation\\Rules'); + + // Hacking around, alternative is to reimplement it... + $property = new ReflectionProperty($f, 'rulesNamespaces'); + $property->setValue($f, array_reverse($property->getValue($f))); + + Factory::setDefaultInstance($f); + } } diff --git a/tests/Unit/Controllers/Admin/UserWorklogControllerTest.php b/tests/Unit/Controllers/Admin/UserWorklogControllerTest.php index 3035d763e..cd44b95cd 100644 --- a/tests/Unit/Controllers/Admin/UserWorklogControllerTest.php +++ b/tests/Unit/Controllers/Admin/UserWorklogControllerTest.php @@ -148,7 +148,7 @@ public function testSaveWorklogWithInvalidParamsThrows(array $body): void */ public function testSaveNewWorklog(): void { - $work_date = Carbon::today(); + $work_date = Carbon::today()->format('Y-m-d'); $work_hours = 3.14; $comment = str_repeat('X', 200); $body = ['work_date' => $work_date, 'work_hours' => $work_hours, 'comment' => $comment]; @@ -167,7 +167,7 @@ public function testSaveNewWorklog(): void $this->assertEquals(1, $this->user->worklogs->count()); $new_worklog = $this->user->worklogs[0]; $this->assertEquals($this->user->id, $new_worklog->user->id); - $this->assertEquals($work_date, $new_worklog->worked_at); + $this->assertEquals($work_date, $new_worklog->worked_at->format('Y-m-d')); $this->assertEquals($work_hours, $new_worklog->hours); $this->assertEquals($comment, $new_worklog->comment); } @@ -178,7 +178,7 @@ public function testSaveNewWorklog(): void */ public function testOverwriteWorklogWithUnknownWorklogIdThrows(): void { - $body = ['work_date' => Carbon::today(), 'work_hours' => 3.14, 'comment' => 'a comment']; + $body = ['work_date' => Carbon::today()->format('Y-m-d'), 'work_hours' => 3.14, 'comment' => 'a comment']; $request = $this->request ->withAttribute('user_id', $this->user->id) ->withAttribute('worklog_id', 1234) @@ -197,7 +197,7 @@ public function testOverwriteWorklogWithWorklogNotAssociatedToUserThrows(): void /** @var Worklog $worklog */ $worklog = Worklog::factory(['user_id' => $user2->id])->create(); - $body = ['work_date' => Carbon::today(), 'work_hours' => 3.14, 'comment' => 'a comment']; + $body = ['work_date' => Carbon::today()->format('Y-m-d'), 'work_hours' => 3.14, 'comment' => 'a comment']; $request = $this->request ->withAttribute('user_id', $this->user->id) ->withAttribute('worklog_id', $worklog->id) @@ -213,7 +213,7 @@ public function testOverwriteWorklog(): void { /** @var Worklog $worklog */ $worklog = Worklog::factory(['user_id' => $this->user->id])->create(); - $work_date = Carbon::today(); + $work_date = Carbon::today()->format('Y-m-d'); $work_hours = 3.14; $comment = str_repeat('X', 200); $body = ['work_date' => $work_date, 'work_hours' => $work_hours, 'comment' => $comment]; @@ -232,7 +232,7 @@ public function testOverwriteWorklog(): void $this->assertHasNotification('worklog.edit.success'); $worklog = Worklog::find($worklog->id); - $this->assertEquals($work_date, $worklog->worked_at); + $this->assertEquals($work_date, $worklog->worked_at->format('Y-m-d')); $this->assertEquals($work_hours, $worklog->hours); $this->assertEquals($comment, $worklog->comment); } @@ -334,7 +334,7 @@ public function testDeleteWorklog(): void */ public function invalidSaveWorklogParams(): array { - $today = Carbon::today(); + $today = Carbon::today()->format('Y-m-d'); return [ // missing work_date [['work_hours' => 3.14, 'comment' => 'com']], diff --git a/tests/Unit/Factories/UserTest.php b/tests/Unit/Factories/UserTest.php index 07a55f994..2f1895aa1 100644 --- a/tests/Unit/Factories/UserTest.php +++ b/tests/Unit/Factories/UserTest.php @@ -174,7 +174,7 @@ public function testMaximumConfigInvalid(): void 'email' => 'notanemail', 'password' => 'a', 'tshirt_size' => 'A', - 'planned_arrival_date' => $this->now->subDays(7), + 'planned_arrival_date' => $this->now->subDays(7)->format('Y-m-d'), 'dect' => str_repeat('a', 50), 'mobile' => str_repeat('a', 50), ], @@ -413,7 +413,7 @@ public function testBuildUpAndTearDownDates(): void 'email' => 'fritz@example.com', 'password' => 's3cret', 'password_confirmation' => 's3cret', - 'planned_arrival_date' => $this->now->subDays(7), + 'planned_arrival_date' => $this->now->subDays(7)->format('Y-m-d'), ], [ 'planned_arrival_date' => [ diff --git a/tests/Unit/Http/Validation/Rules/InTest.php b/tests/Unit/Http/Validation/Rules/InTest.php index 03f8c248e..758934b09 100644 --- a/tests/Unit/Http/Validation/Rules/InTest.php +++ b/tests/Unit/Http/Validation/Rules/InTest.php @@ -16,6 +16,10 @@ public function testConstruct(): void { $rule = new In('foo,bar'); - $this->assertEquals(['foo', 'bar'], $rule->haystack); + $this->assertTrue($rule->validate('foo')); + $this->assertTrue($rule->validate('bar')); + + $this->assertFalse($rule->validate('baz')); + $this->assertFalse($rule->validate('foo,bar')); } } diff --git a/tests/Unit/Http/Validation/Rules/MinTest.php b/tests/Unit/Http/Validation/Rules/MinTest.php index 60e95d546..7a395a42f 100644 --- a/tests/Unit/Http/Validation/Rules/MinTest.php +++ b/tests/Unit/Http/Validation/Rules/MinTest.php @@ -29,5 +29,11 @@ public function testValidate(): void $this->assertTrue($rule->validate('2042-01-01')); $this->assertTrue($rule->validate('2042-01-02')); $this->assertTrue($rule->validate('2345-01-01')); + + $rule = new Min(3); + $this->assertFalse($rule->validate('')); + $this->assertFalse($rule->validate('TE')); + $this->assertTrue($rule->validate('TES')); + $this->assertTrue($rule->validate('FOO BAR')); } } diff --git a/tests/Unit/Http/Validation/ValidatorTest.php b/tests/Unit/Http/Validation/ValidatorTest.php index b301557d3..52ee7b0e5 100644 --- a/tests/Unit/Http/Validation/ValidatorTest.php +++ b/tests/Unit/Http/Validation/ValidatorTest.php @@ -14,6 +14,7 @@ class ValidatorTest extends TestCase * @covers \Engelsystem\Http\Validation\Validator::validate * @covers \Engelsystem\Http\Validation\Validator::getData * @covers \Engelsystem\Http\Validation\Validator::getErrors + * @covers \Engelsystem\Http\Validation\Validator::configureValidationFactory */ public function testValidate(): void { From 11c26509f919e2bc4bad017f4dbd1dcbcc6a1369 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Mon, 30 Dec 2024 18:10:21 +0100 Subject: [PATCH 042/157] Added schedule guid to api --- resources/api/openapi.yml | 6 ++++++ .../Api/Resources/ShiftResource.php | 1 + src/Controllers/Api/ShiftsController.php | 1 + src/Models/Shifts/Shift.php | 7 +++++++ .../Controllers/Api/ShiftsControllerTest.php | 13 ++++++++++-- tests/Unit/Models/Shifts/ShiftTest.php | 20 +++++++++++++++++++ 6 files changed, 46 insertions(+), 2 deletions(-) diff --git a/resources/api/openapi.yml b/resources/api/openapi.yml index c28421f62..118a92f0a 100644 --- a/resources/api/openapi.yml +++ b/resources/api/openapi.yml @@ -240,6 +240,11 @@ components: $ref: '#/components/schemas/Reference' shift_type: $ref: '#/components/schemas/Reference' + schedule_guid: + type: string + format: uuid + nullable: true + description: schedule.xml event guid created_at: $ref: '#/components/schemas/DateTimeOptional' updated_at: @@ -261,6 +266,7 @@ components: - ends_at - location - shift_type + - schedule_guid - created_at - updated_at - needed_angel_types diff --git a/src/Controllers/Api/Resources/ShiftResource.php b/src/Controllers/Api/Resources/ShiftResource.php index 4d8aafb97..204595934 100644 --- a/src/Controllers/Api/Resources/ShiftResource.php +++ b/src/Controllers/Api/Resources/ShiftResource.php @@ -23,6 +23,7 @@ public function toArray(array | Arrayable $location = []): array 'ends_at' => $this->model->end, 'location' => LocationResource::toIdentifierArray($location), 'shift_type' => ShiftTypeResource::toIdentifierArray($this->model->shiftType), + 'schedule_guid' => $this->model->scheduleShift?->guid, 'created_at' => $this->model->created_at, 'updated_at' => $this->model->updated_at, 'url' => url('/shifts', ['action' => 'view', 'shift_id' => $this->model->id]), diff --git a/src/Controllers/Api/ShiftsController.php b/src/Controllers/Api/ShiftsController.php index dbb69697b..5e294716b 100644 --- a/src/Controllers/Api/ShiftsController.php +++ b/src/Controllers/Api/ShiftsController.php @@ -158,6 +158,7 @@ protected function shiftEntriesResponse(BuilderContract $shifts): Response 'shiftEntries.user.contact', 'shiftEntries.user.personalData', 'shiftType', + 'scheduleShift', 'schedule.shiftType.neededAngelTypes.angelType', ]) ->orderBy('start') diff --git a/src/Models/Shifts/Shift.php b/src/Models/Shifts/Shift.php index cbe96b02b..8da36223f 100644 --- a/src/Models/Shifts/Shift.php +++ b/src/Models/Shifts/Shift.php @@ -12,6 +12,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Eloquent\Relations\HasOne; use Illuminate\Database\Eloquent\Relations\HasOneThrough; use Illuminate\Database\Query\Builder as QueryBuilder; @@ -32,6 +33,7 @@ * * @property-read Collection|NeededAngelType[] $neededAngelTypes * @property-read Schedule $schedule + * @property-read ScheduleShift $scheduleShift * @property-read Collection|ShiftEntry[] $shiftEntries * @property-read ShiftType $shiftType * @property-read Location $location @@ -101,6 +103,11 @@ public function schedule(): HasOneThrough return $this->hasOneThrough(Schedule::class, ScheduleShift::class, null, 'id', null, 'schedule_id'); } + public function scheduleShift(): HasOne + { + return $this->hasOne(ScheduleShift::class); + } + public function shiftEntries(): HasMany { return $this->hasMany(ShiftEntry::class); diff --git a/tests/Unit/Controllers/Api/ShiftsControllerTest.php b/tests/Unit/Controllers/Api/ShiftsControllerTest.php index 72fac83cd..d0931d98d 100644 --- a/tests/Unit/Controllers/Api/ShiftsControllerTest.php +++ b/tests/Unit/Controllers/Api/ShiftsControllerTest.php @@ -20,6 +20,7 @@ use Engelsystem\Models\User\PersonalData; use Engelsystem\Models\User\User; use Illuminate\Database\Eloquent\ModelNotFoundException; +use Illuminate\Support\Str; class ShiftsControllerTest extends ApiBaseControllerTest { @@ -241,9 +242,17 @@ public function setUp(): void 'location_id' => $this->location->id, ]); - (new ScheduleShift(['shift_id' => $this->shiftB->id, 'schedule_id' => $this->schedule1->id, 'guid' => 'a'])) + (new ScheduleShift([ + 'shift_id' => $this->shiftB->id, + 'schedule_id' => $this->schedule1->id, + 'guid' => Str::uuid(), + ])) ->save(); - (new ScheduleShift(['shift_id' => $this->shiftC->id, 'schedule_id' => $this->schedule2->id, 'guid' => 'b'])) + (new ScheduleShift([ + 'shift_id' => $this->shiftC->id, + 'schedule_id' => $this->schedule2->id, + 'guid' => Str::uuid(), + ])) ->save(); // "Empty" entry to be skipped diff --git a/tests/Unit/Models/Shifts/ShiftTest.php b/tests/Unit/Models/Shifts/ShiftTest.php index f138d34d7..381c39586 100644 --- a/tests/Unit/Models/Shifts/ShiftTest.php +++ b/tests/Unit/Models/Shifts/ShiftTest.php @@ -99,6 +99,26 @@ public function testSchedule(): void $this->assertEquals(1, Shift::find(3)->schedule->id); } + /** + * @covers \Engelsystem\Models\Shifts\Shift::scheduleShift + */ + public function testScheduleShift(): void + { + /** @var Schedule $schedule */ + $schedule = Schedule::factory()->create(); + /** @var Collection|Shift[] $shifts */ + $shifts = Shift::factory(4)->create(); + + (new ScheduleShift(['shift_id' => $shifts[0]->id, 'schedule_id' => $schedule->id, 'guid' => 'd']))->save(); + (new ScheduleShift(['shift_id' => $shifts[1]->id, 'schedule_id' => $schedule->id, 'guid' => 'e']))->save(); + (new ScheduleShift(['shift_id' => $shifts[2]->id, 'schedule_id' => $schedule->id, 'guid' => 'f']))->save(); + + $this->assertEquals('d', Shift::find(1)->scheduleShift->guid); + $this->assertEquals('e', Shift::find(2)->scheduleShift->guid); + $this->assertEquals('f', Shift::find(3)->scheduleShift->guid); + $this->assertNull(Shift::find(4)->scheduleShift?->guid); + } + /** * @covers \Engelsystem\Models\Shifts\Shift::shiftEntries */ From 5d61991b5e769cfaee4ba8e6ed68772545db22dc Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sun, 2 Feb 2025 18:17:51 +0100 Subject: [PATCH 043/157] Schedule: Add origin_url to parser --- src/Helpers/Schedule/Event.php | 6 ++++++ src/Helpers/Schedule/XmlParser.php | 1 + tests/Unit/Helpers/Schedule/Assets/schedule-extended.xml | 1 + tests/Unit/Helpers/Schedule/EventTest.php | 7 ++++++- tests/Unit/Helpers/Schedule/XmlParserTest.php | 1 + 5 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/Helpers/Schedule/Event.php b/src/Helpers/Schedule/Event.php index 9ea738827..ee7b50a9b 100644 --- a/src/Helpers/Schedule/Event.php +++ b/src/Helpers/Schedule/Event.php @@ -48,6 +48,7 @@ public function __construct( protected ?string $url = null, protected ?string $videoDownloadUrl = null, protected ?string $feedbackUrl = null, + protected ?string $originUrl = null, ) { $this->endDate = $this->date ->copy() @@ -172,6 +173,11 @@ public function getFeedbackUrl(): ?string return $this->feedbackUrl; } + public function getOriginUrl(): ?string + { + return $this->originUrl; + } + public function getVideoDownloadUrl(): ?string { return $this->videoDownloadUrl; diff --git a/src/Helpers/Schedule/XmlParser.php b/src/Helpers/Schedule/XmlParser.php index 5fedeb4f0..ac8d9e23c 100644 --- a/src/Helpers/Schedule/XmlParser.php +++ b/src/Helpers/Schedule/XmlParser.php @@ -180,6 +180,7 @@ protected function parseEvents(array $eventElements, Room $room, array $tracks): $this->getFirstXpathContent('url', $event) ?: null, $this->getFirstXpathContent('video_download_url', $event) ?: null, $this->getFirstXpathContent('feedback_url', $event) ?: null, + $this->getFirstXpathContent('origin_url', $event) ?: null, ); } diff --git a/tests/Unit/Helpers/Schedule/Assets/schedule-extended.xml b/tests/Unit/Helpers/Schedule/Assets/schedule-extended.xml index 18e774253..6963a5cfb 100644 --- a/tests/Unit/Helpers/Schedule/Assets/schedule-extended.xml +++ b/tests/Unit/Helpers/Schedule/Assets/schedule-extended.xml @@ -43,6 +43,7 @@ Any describing stuff? https://foo.bar/baz/schedule/ipsum https://foo.bar/baz/schedule/ipsum#feedback + https://some.example/event/ipsum https://lorem.ipsum/foo/bar.png Some Person diff --git a/tests/Unit/Helpers/Schedule/EventTest.php b/tests/Unit/Helpers/Schedule/EventTest.php index 1489c404e..44aff7140 100644 --- a/tests/Unit/Helpers/Schedule/EventTest.php +++ b/tests/Unit/Helpers/Schedule/EventTest.php @@ -38,6 +38,7 @@ class EventTest extends TestCase * @covers \Engelsystem\Helpers\Schedule\Event::getAttachments * @covers \Engelsystem\Helpers\Schedule\Event::getUrl * @covers \Engelsystem\Helpers\Schedule\Event::getFeedbackUrl + * @covers \Engelsystem\Helpers\Schedule\Event::getOriginUrl * @covers \Engelsystem\Helpers\Schedule\Event::getVideoDownloadUrl * @covers \Engelsystem\Helpers\Schedule\Event::getEndDate */ @@ -83,6 +84,7 @@ public function testCreateDefault(): void $this->assertNull($event->getUrl()); $this->assertNull($event->getVideoDownloadUrl()); $this->assertNull($event->getFeedbackUrl()); + $this->assertNull($event->getOriginUrl()); $this->assertEquals('2020-12-28T20:20:00+00:00', $event->getEndDate()->format(Carbon::RFC3339)); } @@ -111,6 +113,7 @@ public function testCreateDefault(): void * @covers \Engelsystem\Helpers\Schedule\Event::getAttachments * @covers \Engelsystem\Helpers\Schedule\Event::getUrl * @covers \Engelsystem\Helpers\Schedule\Event::getFeedbackUrl + * @covers \Engelsystem\Helpers\Schedule\Event::getOriginUrl * @covers \Engelsystem\Helpers\Schedule\Event::getVideoDownloadUrl */ public function testCreate(): void @@ -140,7 +143,8 @@ public function testCreate(): void $attachments, 'https://foo.bar/2-lorem', 'https://videos.orem.ipsum/2-lorem.mp4', - 'https://videos.orem.ipsum/2-lorem/feedback' + 'https://videos.orem.ipsum/2-lorem/feedback', + 'https://some.example/2-lorem/', ); $this->assertEquals('/foo/bar.png', $event->getLogo()); @@ -154,6 +158,7 @@ public function testCreate(): void $this->assertEquals('https://foo.bar/2-lorem', $event->getUrl()); $this->assertEquals('https://videos.orem.ipsum/2-lorem.mp4', $event->getVideoDownloadUrl()); $this->assertEquals('https://videos.orem.ipsum/2-lorem/feedback', $event->getFeedbackUrl()); + $this->assertEquals('https://some.example/2-lorem/', $event->getOriginUrl()); $event->setTitle('Event title'); $this->assertEquals('Event title', $event->getTitle()); diff --git a/tests/Unit/Helpers/Schedule/XmlParserTest.php b/tests/Unit/Helpers/Schedule/XmlParserTest.php index 7b6e29150..47eb5c509 100644 --- a/tests/Unit/Helpers/Schedule/XmlParserTest.php +++ b/tests/Unit/Helpers/Schedule/XmlParserTest.php @@ -113,6 +113,7 @@ public function testLoad(): void $this->assertEquals('Any describing stuff?', $event->getDescription()); $this->assertEquals('https://foo.bar/baz/schedule/ipsum', $event->getUrl()); $this->assertEquals('https://foo.bar/baz/schedule/ipsum#feedback', $event->getFeedbackUrl()); + $this->assertEquals('https://some.example/event/ipsum', $event->getOriginUrl()); $this->assertEquals('https://lorem.ipsum/foo/bar.png', $event->getLogo()); $this->assertEquals([1234 => 'Some Person', 1337 => 'Another Person'], $event->getPersons()); $this->assertEquals([ From cbe60d543f9f56af8ca6c8d4081e95d841567e22 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sun, 2 Feb 2025 19:02:19 +0100 Subject: [PATCH 044/157] API: Add /users endpoint --- config/routes.php | 1 + resources/api/openapi.yml | 24 ++++++++++++++++ src/Controllers/Api/UsersController.php | 17 +++++++++++ .../Controllers/Api/UsersControllerTest.php | 28 +++++++++++++++++++ 4 files changed, 70 insertions(+) diff --git a/config/routes.php b/config/routes.php index 55d70b58a..2773d04bc 100644 --- a/config/routes.php +++ b/config/routes.php @@ -150,6 +150,7 @@ function (RouteCollector $route): void { $route->get('/shifttypes', 'Api\ShiftTypeController@index'); $route->get('/shifttypes/{shifttype_id:\d+}/shifts', 'Api\ShiftsController@entriesByShiftType'); + $route->get('/users', 'Api\UsersController@index'); $route->get('/users/{user_id:(?:\d+|self)}', 'Api\UsersController@user'); $route->get('/users/{user_id:(?:\d+|self)}/angeltypes', 'Api\AngelTypeController@ofUser'); $route->get('/users/{user_id:(?:\d+|self)}/shifts', 'Api\ShiftsController@entriesByUser'); diff --git a/resources/api/openapi.yml b/resources/api/openapi.yml index 118a92f0a..1556f6197 100644 --- a/resources/api/openapi.yml +++ b/resources/api/openapi.yml @@ -663,6 +663,30 @@ paths: '404': $ref: '#/components/responses/NotFoundError' + /users: + get: + tags: + - user + summary: Get a list of all users + responses: + '200': + description: Ok + content: + application/json: + schema: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/Reference' + '401': + $ref: '#/components/responses/UnauthorizedError' + '403': + $ref: '#/components/responses/ForbiddenError' + '404': + $ref: '#/components/responses/NotFoundError' + /users/{id}: parameters: - name: id diff --git a/src/Controllers/Api/UsersController.php b/src/Controllers/Api/UsersController.php index 5173a1280..9141d248f 100644 --- a/src/Controllers/Api/UsersController.php +++ b/src/Controllers/Api/UsersController.php @@ -8,11 +8,28 @@ use Engelsystem\Controllers\Api\Resources\UserResource; use Engelsystem\Http\Request; use Engelsystem\Http\Response; +use Engelsystem\Models\BaseModel; +use Engelsystem\Models\User\User; class UsersController extends ApiController { use UsesAuth; + public function index(): Response + { + $models = User::query() + ->orderBy('name') + ->get(); + + $models = $models->map(function (BaseModel $model) { + return UserResource::toIdentifierArray($model); + }); + + $data = ['data' => $models]; + return $this->response + ->withContent(json_encode($data)); + } + public function user(Request $request): Response { $id = $request->getAttribute('user_id'); diff --git a/tests/Unit/Controllers/Api/UsersControllerTest.php b/tests/Unit/Controllers/Api/UsersControllerTest.php index b5a6554f7..3539ecfa1 100644 --- a/tests/Unit/Controllers/Api/UsersControllerTest.php +++ b/tests/Unit/Controllers/Api/UsersControllerTest.php @@ -17,6 +17,34 @@ class UsersControllerTest extends ApiBaseControllerTest { + /** + * @covers \Engelsystem\Controllers\Api\UsersController::index + */ + public function testIndex(): void + { + /** @var User $user */ + $user = User::factory()->create(); + $controller = new UsersController(new Response()); + + $response = $controller->index(); + $this->validateApiResponse('/users', 'get', $response); + + $this->assertEquals(['application/json'], $response->getHeader('content-type')); + $this->assertJson($response->getContent()); + + $data = json_decode($response->getContent(), true); + + $this->assertArrayHasKey('data', $data); + $this->assertIsArray($data['data']); + $this->assertNotEmpty($data['data']); + + $firstUser = $data['data'][0]; + $this->assertArrayHasKey('id', $firstUser); + $this->assertEquals($user->id, $firstUser['id']); + $this->assertArrayHasKey('name', $firstUser); + $this->assertEquals($user->name, $firstUser['name']); + } + /** * @covers \Engelsystem\Controllers\Api\UsersController::user * @covers \Engelsystem\Controllers\Api\Resources\UserDetailResource::toArray From 8e639e2473f67a373cbcf8a21193a95e77fbb898 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Tue, 25 Mar 2025 00:04:17 +0100 Subject: [PATCH 045/157] Upgraded js packages --- yarn.lock | 1757 +++++++++++++++++++++++------------------------------ 1 file changed, 772 insertions(+), 985 deletions(-) diff --git a/yarn.lock b/yarn.lock index aa56a7b1f..482988639 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,54 +7,55 @@ resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== -"@ampproject/remapping@^2.1.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" - integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== +"@ampproject/remapping@^2.2.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" + integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== dependencies: - "@jridgewell/gen-mapping" "^0.1.0" - "@jridgewell/trace-mapping" "^0.3.9" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.24" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.18.6": +"@babel/code-frame@^7.0.0": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== dependencies: "@babel/highlight" "^7.18.6" -"@babel/code-frame@^7.22.13": - version "7.22.13" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" - integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== +"@babel/code-frame@^7.26.2": + version "7.26.2" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85" + integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== dependencies: - "@babel/highlight" "^7.22.13" - chalk "^2.4.2" + "@babel/helper-validator-identifier" "^7.25.9" + js-tokens "^4.0.0" + picocolors "^1.0.0" -"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.1", "@babel/compat-data@^7.20.5": - version "7.20.14" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.14.tgz#4106fc8b755f3e3ee0a0a7c27dde5de1d2b2baf8" - integrity sha512-0YpKHD6ImkWMEINCyDAD0HLLUH/lPCefG8ld9it8DJB2wnApraKuhgYTvTY1z7UFIfBTGy5LwncZ+5HWWGbhFw== +"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.26.8": + version "7.26.8" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.8.tgz#821c1d35641c355284d4a870b8a4a7b0c141e367" + integrity sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ== "@babel/core@^7.20.12": - version "7.20.12" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.12.tgz#7930db57443c6714ad216953d1356dac0eb8496d" - integrity sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg== - dependencies: - "@ampproject/remapping" "^2.1.0" - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.7" - "@babel/helper-compilation-targets" "^7.20.7" - "@babel/helper-module-transforms" "^7.20.11" - "@babel/helpers" "^7.20.7" - "@babel/parser" "^7.20.7" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.12" - "@babel/types" "^7.20.7" - convert-source-map "^1.7.0" + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.10.tgz#5c876f83c8c4dcb233ee4b670c0606f2ac3000f9" + integrity sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.26.2" + "@babel/generator" "^7.26.10" + "@babel/helper-compilation-targets" "^7.26.5" + "@babel/helper-module-transforms" "^7.26.0" + "@babel/helpers" "^7.26.10" + "@babel/parser" "^7.26.10" + "@babel/template" "^7.26.9" + "@babel/traverse" "^7.26.10" + "@babel/types" "^7.26.10" + convert-source-map "^2.0.0" debug "^4.1.0" gensync "^1.0.0-beta.2" - json5 "^2.2.2" - semver "^6.3.0" + json5 "^2.2.3" + semver "^6.3.1" "@babel/eslint-parser@^7.19.1": version "7.19.1" @@ -65,945 +66,753 @@ eslint-visitor-keys "^2.1.0" semver "^6.3.0" -"@babel/generator@^7.20.7": - version "7.20.14" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.14.tgz#9fa772c9f86a46c6ac9b321039400712b96f64ce" - integrity sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg== +"@babel/generator@^7.26.10", "@babel/generator@^7.27.0": + version "7.27.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.27.0.tgz#764382b5392e5b9aff93cadb190d0745866cbc2c" + integrity sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw== dependencies: - "@babel/types" "^7.20.7" - "@jridgewell/gen-mapping" "^0.3.2" - jsesc "^2.5.1" - -"@babel/generator@^7.23.0": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.0.tgz#df5c386e2218be505b34837acbcb874d7a983420" - integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g== - dependencies: - "@babel/types" "^7.23.0" - "@jridgewell/gen-mapping" "^0.3.2" - "@jridgewell/trace-mapping" "^0.3.17" - jsesc "^2.5.1" - -"@babel/helper-annotate-as-pure@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" - integrity sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA== - dependencies: - "@babel/types" "^7.18.6" + "@babel/parser" "^7.27.0" + "@babel/types" "^7.27.0" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^3.0.2" -"@babel/helper-builder-binary-assignment-operator-visitor@^7.18.6": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz#acd4edfd7a566d1d51ea975dff38fd52906981bb" - integrity sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw== +"@babel/helper-annotate-as-pure@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz#d8eac4d2dc0d7b6e11fa6e535332e0d3184f06b4" + integrity sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g== dependencies: - "@babel/helper-explode-assignable-expression" "^7.18.6" - "@babel/types" "^7.18.9" + "@babel/types" "^7.25.9" -"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.0", "@babel/helper-compilation-targets@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz#a6cd33e93629f5eb473b021aac05df62c4cd09bb" - integrity sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ== +"@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.25.9", "@babel/helper-compilation-targets@^7.26.5": + version "7.27.0" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.0.tgz#de0c753b1cd1d9ab55d473c5a5cf7170f0a81880" + integrity sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA== dependencies: - "@babel/compat-data" "^7.20.5" - "@babel/helper-validator-option" "^7.18.6" - browserslist "^4.21.3" + "@babel/compat-data" "^7.26.8" + "@babel/helper-validator-option" "^7.25.9" + browserslist "^4.24.0" lru-cache "^5.1.1" - semver "^6.3.0" - -"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.20.5", "@babel/helper-create-class-features-plugin@^7.20.7": - version "7.20.12" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.12.tgz#4349b928e79be05ed2d1643b20b99bb87c503819" - integrity sha512-9OunRkbT0JQcednL0UFvbfXpAsUXiGjUk0a7sN8fUXX7Mue79cUSMjHGDRRi/Vz9vYlpIhLV5fMD5dKoMhhsNQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" - "@babel/helper-member-expression-to-functions" "^7.20.7" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-replace-supers" "^7.20.7" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" - "@babel/helper-split-export-declaration" "^7.18.6" - -"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.20.5": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.20.5.tgz#5ea79b59962a09ec2acf20a963a01ab4d076ccca" - integrity sha512-m68B1lkg3XDGX5yCvGO0kPx3v9WIYLnzjKfPcQiwntEQa5ZeRkPmo2X/ISJc8qxWGfwUr+kvZAeEzAwLec2r2w== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - regexpu-core "^5.2.1" - -"@babel/helper-define-polyfill-provider@^0.3.3": - version "0.3.3" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz#8612e55be5d51f0cd1f36b4a5a83924e89884b7a" - integrity sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww== - dependencies: - "@babel/helper-compilation-targets" "^7.17.7" - "@babel/helper-plugin-utils" "^7.16.7" + semver "^6.3.1" + +"@babel/helper-create-class-features-plugin@^7.25.9": + version "7.27.0" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.0.tgz#518fad6a307c6a96f44af14912b2c20abe9bfc30" + integrity sha512-vSGCvMecvFCd/BdpGlhpXYNhhC4ccxyvQWpbGL4CWbvfEoLFWUZuSuf7s9Aw70flgQF+6vptvgK2IfOnKlRmBg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.25.9" + "@babel/helper-member-expression-to-functions" "^7.25.9" + "@babel/helper-optimise-call-expression" "^7.25.9" + "@babel/helper-replace-supers" "^7.26.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" + "@babel/traverse" "^7.27.0" + semver "^6.3.1" + +"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.25.9": + version "7.27.0" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.0.tgz#0e41f7d38c2ebe06ebd9cf0e02fb26019c77cd95" + integrity sha512-fO8l08T76v48BhpNRW/nQ0MxfnSdoSKUJBMjubOAYffsVuGG5qOfMq7N6Es7UJvi7Y8goXXo07EfcHZXDPuELQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.25.9" + regexpu-core "^6.2.0" + semver "^6.3.1" + +"@babel/helper-define-polyfill-provider@^0.6.3", "@babel/helper-define-polyfill-provider@^0.6.4": + version "0.6.4" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.4.tgz#15e8746368bfa671785f5926ff74b3064c291fab" + integrity sha512-jljfR1rGnXXNWnmQg2K3+bvhkxB51Rl32QRaOTuwwjviGrHzIbSc8+x9CpraDtbT7mfyjXObULP4w/adunNwAw== + dependencies: + "@babel/helper-compilation-targets" "^7.22.6" + "@babel/helper-plugin-utils" "^7.22.5" debug "^4.1.1" lodash.debounce "^4.0.8" resolve "^1.14.2" - semver "^6.1.2" - -"@babel/helper-environment-visitor@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" - integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== - -"@babel/helper-environment-visitor@^7.22.20": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" - integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== - -"@babel/helper-explode-assignable-expression@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz#41f8228ef0a6f1a036b8dfdfec7ce94f9a6bc096" - integrity sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg== - dependencies: - "@babel/types" "^7.18.6" -"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c" - integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== - dependencies: - "@babel/template" "^7.18.10" - "@babel/types" "^7.19.0" - -"@babel/helper-function-name@^7.23.0": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" - integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== - dependencies: - "@babel/template" "^7.22.15" - "@babel/types" "^7.23.0" - -"@babel/helper-hoist-variables@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" - integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-hoist-variables@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" - integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== - dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-member-expression-to-functions@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.20.7.tgz#a6f26e919582275a93c3aa6594756d71b0bb7f05" - integrity sha512-9J0CxJLq315fEdi4s7xK5TQaNYjZw+nDVpVqr1axNGKzdrdwYBD5b4uKv3n75aABG0rCCTK8Im8Ww7eYfMrZgw== - dependencies: - "@babel/types" "^7.20.7" - -"@babel/helper-module-imports@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" - integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11": - version "7.20.11" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz#df4c7af713c557938c50ea3ad0117a7944b2f1b0" - integrity sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg== - dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-simple-access" "^7.20.2" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/helper-validator-identifier" "^7.19.1" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.10" - "@babel/types" "^7.20.7" - -"@babel/helper-optimise-call-expression@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe" - integrity sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629" - integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== - -"@babel/helper-remap-async-to-generator@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz#997458a0e3357080e54e1d79ec347f8a8cd28519" - integrity sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-wrap-function" "^7.18.9" - "@babel/types" "^7.18.9" - -"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz#243ecd2724d2071532b2c8ad2f0f9f083bcae331" - integrity sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A== - dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-member-expression-to-functions" "^7.20.7" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.7" - "@babel/types" "^7.20.7" - -"@babel/helper-simple-access@^7.20.2": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz#0ab452687fe0c2cfb1e2b9e0015de07fc2d62dd9" - integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== - dependencies: - "@babel/types" "^7.20.2" - -"@babel/helper-skip-transparent-expression-wrappers@^7.20.0": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz#fbe4c52f60518cab8140d77101f0e63a8a230684" - integrity sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg== - dependencies: - "@babel/types" "^7.20.0" - -"@babel/helper-split-export-declaration@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" - integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-split-export-declaration@^7.22.6": - version "7.22.6" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" - integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== - dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-string-parser@^7.19.4": - version "7.19.4" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" - integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== - -"@babel/helper-string-parser@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" - integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== - -"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" - integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== - -"@babel/helper-validator-identifier@^7.22.20": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" - integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== - -"@babel/helper-validator-option@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" - integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== - -"@babel/helper-wrap-function@^7.18.9": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz#75e2d84d499a0ab3b31c33bcfe59d6b8a45f62e3" - integrity sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q== - dependencies: - "@babel/helper-function-name" "^7.19.0" - "@babel/template" "^7.18.10" - "@babel/traverse" "^7.20.5" - "@babel/types" "^7.20.5" - -"@babel/helpers@^7.20.7": - version "7.20.13" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.13.tgz#e3cb731fb70dc5337134cadc24cbbad31cc87ad2" - integrity sha512-nzJ0DWCL3gB5RCXbUO3KIMMsBY2Eqbx8mBpKGE/02PgyRQFcPQLbkQ1vyy596mZLaP+dAfD+R4ckASzNVmW3jg== - dependencies: - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.13" - "@babel/types" "^7.20.7" +"@babel/helper-member-expression-to-functions@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz#9dfffe46f727005a5ea29051ac835fb735e4c1a3" + integrity sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ== + dependencies: + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" + +"@babel/helper-module-imports@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz#e7f8d20602ebdbf9ebbea0a0751fb0f2a4141715" + integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw== + dependencies: + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" + +"@babel/helper-module-transforms@^7.25.9", "@babel/helper-module-transforms@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz#8ce54ec9d592695e58d84cd884b7b5c6a2fdeeae" + integrity sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw== + dependencies: + "@babel/helper-module-imports" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + "@babel/traverse" "^7.25.9" + +"@babel/helper-optimise-call-expression@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz#3324ae50bae7e2ab3c33f60c9a877b6a0146b54e" + integrity sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ== + dependencies: + "@babel/types" "^7.25.9" + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.25.9", "@babel/helper-plugin-utils@^7.26.5": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz#18580d00c9934117ad719392c4f6585c9333cc35" + integrity sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg== + +"@babel/helper-remap-async-to-generator@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz#e53956ab3d5b9fb88be04b3e2f31b523afd34b92" + integrity sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.25.9" + "@babel/helper-wrap-function" "^7.25.9" + "@babel/traverse" "^7.25.9" + +"@babel/helper-replace-supers@^7.25.9", "@babel/helper-replace-supers@^7.26.5": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.26.5.tgz#6cb04e82ae291dae8e72335dfe438b0725f14c8d" + integrity sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.25.9" + "@babel/helper-optimise-call-expression" "^7.25.9" + "@babel/traverse" "^7.26.5" + +"@babel/helper-skip-transparent-expression-wrappers@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz#0b2e1b62d560d6b1954893fd2b705dc17c91f0c9" + integrity sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA== + dependencies: + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" + +"@babel/helper-string-parser@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c" + integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA== + +"@babel/helper-validator-identifier@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" + integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== + +"@babel/helper-validator-option@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz#86e45bd8a49ab7e03f276577f96179653d41da72" + integrity sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw== + +"@babel/helper-wrap-function@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz#d99dfd595312e6c894bd7d237470025c85eea9d0" + integrity sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g== + dependencies: + "@babel/template" "^7.25.9" + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" + +"@babel/helpers@^7.26.10": + version "7.27.0" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.27.0.tgz#53d156098defa8243eab0f32fa17589075a1b808" + integrity sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg== + dependencies: + "@babel/template" "^7.27.0" + "@babel/types" "^7.27.0" "@babel/highlight@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" - integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.25.9.tgz#8141ce68fc73757946f983b343f1231f4691acc6" + integrity sha512-llL88JShoCsth8fF8R4SJnIn+WLvR6ccFxu1H3FlMhDontdcmZWf2HgIZ7AIqV3Xcck1idlohrN4EUBQz6klbw== dependencies: - "@babel/helper-validator-identifier" "^7.18.6" - chalk "^2.0.0" - js-tokens "^4.0.0" - -"@babel/highlight@^7.22.13": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54" - integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg== - dependencies: - "@babel/helper-validator-identifier" "^7.22.20" + "@babel/helper-validator-identifier" "^7.25.9" chalk "^2.4.2" js-tokens "^4.0.0" + picocolors "^1.0.0" -"@babel/parser@^7.20.7": - version "7.20.15" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.15.tgz#eec9f36d8eaf0948bb88c87a46784b5ee9fd0c89" - integrity sha512-DI4a1oZuf8wC+oAJA9RW6ga3Zbe8RZFt7kD9i4qAspz3I/yHet1VvC3DiSy/fsUvv5pvJuNPh0LPOdCcqinDPg== - -"@babel/parser@^7.22.15", "@babel/parser@^7.23.0": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719" - integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw== - -"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2" - integrity sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.18.9": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz#d9c85589258539a22a901033853101a6198d4ef1" - integrity sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ== +"@babel/parser@^7.26.10", "@babel/parser@^7.27.0": + version "7.27.0" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.27.0.tgz#3d7d6ee268e41d2600091cbd4e145ffee85a44ec" + integrity sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" - "@babel/plugin-proposal-optional-chaining" "^7.20.7" + "@babel/types" "^7.27.0" -"@babel/plugin-proposal-async-generator-functions@^7.20.1": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz#bfb7276d2d573cb67ba379984a2334e262ba5326" - integrity sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA== +"@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz#cc2e53ebf0a0340777fff5ed521943e253b4d8fe" + integrity sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g== dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-remap-async-to-generator" "^7.18.9" - "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/traverse" "^7.25.9" -"@babel/plugin-proposal-class-properties@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3" - integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== +"@babel/plugin-bugfix-safari-class-field-initializer-scope@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz#af9e4fb63ccb8abcb92375b2fcfe36b60c774d30" + integrity sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw== dependencies: - "@babel/helper-create-class-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-proposal-class-static-block@^7.18.6": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.20.7.tgz#92592e9029b13b15be0f7ce6a7aedc2879ca45a7" - integrity sha512-AveGOoi9DAjUYYuUAG//Ig69GlazLnoyzMw68VCDux+c1tsnnH/OkYcpz/5xzMkEFC6UxjR5Gw1c+iY2wOGVeQ== +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz#e8dc26fcd616e6c5bf2bd0d5a2c151d4f92a9137" + integrity sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug== dependencies: - "@babel/helper-create-class-features-plugin" "^7.20.7" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-proposal-dynamic-import@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz#72bcf8d408799f547d759298c3c27c7e7faa4d94" - integrity sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw== +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz#807a667f9158acac6f6164b4beb85ad9ebc9e1d1" + integrity sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" + "@babel/plugin-transform-optional-chaining" "^7.25.9" -"@babel/plugin-proposal-export-namespace-from@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz#5f7313ab348cdb19d590145f9247540e94761203" - integrity sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA== +"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz#de7093f1e7deaf68eadd7cc6b07f2ab82543269e" + integrity sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/traverse" "^7.25.9" -"@babel/plugin-proposal-json-strings@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz#7e8788c1811c393aff762817e7dbf1ebd0c05f0b" - integrity sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-json-strings" "^7.8.3" +"@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2": + version "7.21.0-placeholder-for-preset-env.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703" + integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w== -"@babel/plugin-proposal-logical-assignment-operators@^7.18.9": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz#dfbcaa8f7b4d37b51e8bfb46d94a5aea2bb89d83" - integrity sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug== +"@babel/plugin-syntax-import-assertions@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz#620412405058efa56e4a564903b79355020f445f" + integrity sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-proposal-nullish-coalescing-operator@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz#fdd940a99a740e577d6c753ab6fbb43fdb9467e1" - integrity sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA== +"@babel/plugin-syntax-import-attributes@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz#3b1412847699eea739b4f2602c74ce36f6b0b0f7" + integrity sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-proposal-numeric-separator@^7.18.6": +"@babel/plugin-syntax-unicode-sets-regex@^7.18.6": version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz#899b14fbafe87f053d2c5ff05b36029c62e13c75" - integrity sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q== + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz#d49a3b3e6b52e5be6740022317580234a6a47357" + integrity sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg== dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" -"@babel/plugin-proposal-object-rest-spread@^7.20.2": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz#aa662940ef425779c75534a5c41e9d936edc390a" - integrity sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg== +"@babel/plugin-transform-arrow-functions@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz#7821d4410bee5daaadbb4cdd9a6649704e176845" + integrity sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg== dependencies: - "@babel/compat-data" "^7.20.5" - "@babel/helper-compilation-targets" "^7.20.7" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.20.7" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-proposal-optional-catch-binding@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz#f9400d0e6a3ea93ba9ef70b09e72dd6da638a2cb" - integrity sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw== +"@babel/plugin-transform-async-generator-functions@^7.26.8": + version "7.26.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.26.8.tgz#5e3991135e3b9c6eaaf5eff56d1ae5a11df45ff8" + integrity sha512-He9Ej2X7tNf2zdKMAGOsmg2MrFc+hfoAhd3po4cWfo/NWjzEAKa0oQruj1ROVUdl0e6fb6/kE/G3SSxE0lRJOg== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/helper-plugin-utils" "^7.26.5" + "@babel/helper-remap-async-to-generator" "^7.25.9" + "@babel/traverse" "^7.26.8" -"@babel/plugin-proposal-optional-chaining@^7.18.9", "@babel/plugin-proposal-optional-chaining@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.20.7.tgz#49f2b372519ab31728cc14115bb0998b15bfda55" - integrity sha512-T+A7b1kfjtRM51ssoOfS1+wbyCVqorfyZhT99TvxxLMirPShD8CzKMRepMlCBGM5RpHMbn8s+5MMHnPstJH6mQ== +"@babel/plugin-transform-async-to-generator@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz#c80008dacae51482793e5a9c08b39a5be7e12d71" + integrity sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/helper-module-imports" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-remap-async-to-generator" "^7.25.9" -"@babel/plugin-proposal-private-methods@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz#5209de7d213457548a98436fa2882f52f4be6bea" - integrity sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA== +"@babel/plugin-transform-block-scoped-functions@^7.26.5": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.26.5.tgz#3dc4405d31ad1cbe45293aa57205a6e3b009d53e" + integrity sha512-chuTSY+hq09+/f5lMj8ZSYgCFpppV2CbYrhNFJ1BFoXpiWPnnAb7R0MqrafCpN8E1+YRrtM1MXZHJdIx8B6rMQ== dependencies: - "@babel/helper-create-class-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.26.5" -"@babel/plugin-proposal-private-property-in-object@^7.18.6": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.20.5.tgz#309c7668f2263f1c711aa399b5a9a6291eef6135" - integrity sha512-Vq7b9dUA12ByzB4EjQTPo25sFhY+08pQDBSZRtUAkj7lb7jahaHR5igera16QZ+3my1nYR4dKsNdYj5IjPHilQ== +"@babel/plugin-transform-block-scoping@^7.25.9": + version "7.27.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.27.0.tgz#acc2c0d98a7439bbde4244588ddbd4904701d47f" + integrity sha512-u1jGphZ8uDI2Pj/HJj6YQ6XQLZCNjOlprjxB5SVz6rq2T6SwAR+CdrWK0CP7F+9rDVMXdB0+r6Am5G5aobOjAQ== dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-create-class-features-plugin" "^7.20.5" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-private-property-in-object" "^7.14.5" - -"@babel/plugin-proposal-unicode-property-regex@^7.18.6", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz#af613d2cd5e643643b65cded64207b15c85cb78e" - integrity sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.26.5" -"@babel/plugin-syntax-async-generators@^7.8.4": - version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" - integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== +"@babel/plugin-transform-class-properties@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz#a8ce84fedb9ad512549984101fa84080a9f5f51f" + integrity sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q== dependencies: - "@babel/helper-plugin-utils" "^7.8.0" + "@babel/helper-create-class-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-syntax-class-properties@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" - integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== +"@babel/plugin-transform-class-static-block@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz#6c8da219f4eb15cae9834ec4348ff8e9e09664a0" + integrity sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ== dependencies: - "@babel/helper-plugin-utils" "^7.12.13" + "@babel/helper-create-class-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-syntax-class-static-block@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" - integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== +"@babel/plugin-transform-classes@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz#7152457f7880b593a63ade8a861e6e26a4469f52" + integrity sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg== dependencies: - "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-annotate-as-pure" "^7.25.9" + "@babel/helper-compilation-targets" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-replace-supers" "^7.25.9" + "@babel/traverse" "^7.25.9" + globals "^11.1.0" -"@babel/plugin-syntax-dynamic-import@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" - integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== +"@babel/plugin-transform-computed-properties@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz#db36492c78460e534b8852b1d5befe3c923ef10b" + integrity sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA== dependencies: - "@babel/helper-plugin-utils" "^7.8.0" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/template" "^7.25.9" -"@babel/plugin-syntax-export-namespace-from@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" - integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== +"@babel/plugin-transform-destructuring@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz#966ea2595c498224340883602d3cfd7a0c79cea1" + integrity sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-syntax-import-assertions@^7.20.0": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz#bb50e0d4bea0957235390641209394e87bdb9cc4" - integrity sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ== +"@babel/plugin-transform-dotall-regex@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz#bad7945dd07734ca52fe3ad4e872b40ed09bb09a" + integrity sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA== dependencies: - "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-create-regexp-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-syntax-json-strings@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" - integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== +"@babel/plugin-transform-duplicate-keys@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz#8850ddf57dce2aebb4394bb434a7598031059e6d" + integrity sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw== dependencies: - "@babel/helper-plugin-utils" "^7.8.0" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" - integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== +"@babel/plugin-transform-duplicate-named-capturing-groups-regex@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz#6f7259b4de127721a08f1e5165b852fcaa696d31" + integrity sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog== dependencies: - "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-create-regexp-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" - integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== +"@babel/plugin-transform-dynamic-import@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz#23e917de63ed23c6600c5dd06d94669dce79f7b8" + integrity sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg== dependencies: - "@babel/helper-plugin-utils" "^7.8.0" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-syntax-numeric-separator@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" - integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== +"@babel/plugin-transform-exponentiation-operator@^7.26.3": + version "7.26.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz#e29f01b6de302c7c2c794277a48f04a9ca7f03bc" + integrity sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ== dependencies: - "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-syntax-object-rest-spread@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" - integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== +"@babel/plugin-transform-export-namespace-from@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz#90745fe55053394f554e40584cda81f2c8a402a2" + integrity sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww== dependencies: - "@babel/helper-plugin-utils" "^7.8.0" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-syntax-optional-catch-binding@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" - integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== +"@babel/plugin-transform-for-of@^7.26.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.26.9.tgz#27231f79d5170ef33b5111f07fe5cafeb2c96a56" + integrity sha512-Hry8AusVm8LW5BVFgiyUReuoGzPUpdHQQqJY5bZnbbf+ngOHWuCuYFKw/BqaaWlvEUrF91HMhDtEaI1hZzNbLg== dependencies: - "@babel/helper-plugin-utils" "^7.8.0" + "@babel/helper-plugin-utils" "^7.26.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" -"@babel/plugin-syntax-optional-chaining@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" - integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== +"@babel/plugin-transform-function-name@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz#939d956e68a606661005bfd550c4fc2ef95f7b97" + integrity sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA== dependencies: - "@babel/helper-plugin-utils" "^7.8.0" + "@babel/helper-compilation-targets" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/traverse" "^7.25.9" -"@babel/plugin-syntax-private-property-in-object@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" - integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== +"@babel/plugin-transform-json-strings@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz#c86db407cb827cded902a90c707d2781aaa89660" + integrity sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw== dependencies: - "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-syntax-top-level-await@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" - integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== +"@babel/plugin-transform-literals@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz#1a1c6b4d4aa59bc4cad5b6b3a223a0abd685c9de" + integrity sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ== dependencies: - "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-arrow-functions@^7.18.6": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz#bea332b0e8b2dab3dafe55a163d8227531ab0551" - integrity sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ== +"@babel/plugin-transform-logical-assignment-operators@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz#b19441a8c39a2fda0902900b306ea05ae1055db7" + integrity sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-async-to-generator@^7.18.6": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz#dfee18623c8cb31deb796aa3ca84dda9cea94354" - integrity sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q== +"@babel/plugin-transform-member-expression-literals@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz#63dff19763ea64a31f5e6c20957e6a25e41ed5de" + integrity sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA== dependencies: - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-remap-async-to-generator" "^7.18.9" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-block-scoped-functions@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz#9187bf4ba302635b9d70d986ad70f038726216a8" - integrity sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ== +"@babel/plugin-transform-modules-amd@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz#49ba478f2295101544abd794486cd3088dddb6c5" + integrity sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-block-scoping@^7.20.2": - version "7.20.15" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.15.tgz#3e1b2aa9cbbe1eb8d644c823141a9c5c2a22392d" - integrity sha512-Vv4DMZ6MiNOhu/LdaZsT/bsLRxgL94d269Mv4R/9sp6+Mp++X/JqypZYypJXLlM4mlL352/Egzbzr98iABH1CA== - dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - -"@babel/plugin-transform-classes@^7.20.2": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.7.tgz#f438216f094f6bb31dc266ebfab8ff05aecad073" - integrity sha512-LWYbsiXTPKl+oBlXUGlwNlJZetXD5Am+CyBdqhPsDVjM9Jc8jwBJFrKhHf900Kfk2eZG1y9MAG3UNajol7A4VQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-compilation-targets" "^7.20.7" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-replace-supers" "^7.20.7" - "@babel/helper-split-export-declaration" "^7.18.6" - globals "^11.1.0" + "@babel/helper-module-transforms" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-computed-properties@^7.18.9": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.20.7.tgz#704cc2fd155d1c996551db8276d55b9d46e4d0aa" - integrity sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ== +"@babel/plugin-transform-modules-commonjs@^7.26.3": + version "7.26.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz#8f011d44b20d02c3de44d8850d971d8497f981fb" + integrity sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/template" "^7.20.7" + "@babel/helper-module-transforms" "^7.26.0" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-destructuring@^7.20.2": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.7.tgz#8bda578f71620c7de7c93af590154ba331415454" - integrity sha512-Xwg403sRrZb81IVB79ZPqNQME23yhugYVqgTxAhT99h485F4f+GMELFhhOsscDUB7HCswepKeCKLn/GZvUKoBA== +"@babel/plugin-transform-modules-systemjs@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz#8bd1b43836269e3d33307151a114bcf3ba6793f8" + integrity sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-module-transforms" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + "@babel/traverse" "^7.25.9" -"@babel/plugin-transform-dotall-regex@^7.18.6", "@babel/plugin-transform-dotall-regex@^7.4.4": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz#b286b3e7aae6c7b861e45bed0a2fafd6b1a4fef8" - integrity sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg== +"@babel/plugin-transform-modules-umd@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz#6710079cdd7c694db36529a1e8411e49fcbf14c9" + integrity sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-module-transforms" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-duplicate-keys@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz#687f15ee3cdad6d85191eb2a372c4528eaa0ae0e" - integrity sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw== +"@babel/plugin-transform-named-capturing-groups-regex@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz#454990ae6cc22fd2a0fa60b3a2c6f63a38064e6a" + integrity sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-create-regexp-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-exponentiation-operator@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz#421c705f4521888c65e91fdd1af951bfefd4dacd" - integrity sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw== +"@babel/plugin-transform-new-target@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz#42e61711294b105c248336dcb04b77054ea8becd" + integrity sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ== dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-for-of@^7.18.8": - version "7.18.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz#6ef8a50b244eb6a0bdbad0c7c61877e4e30097c1" - integrity sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ== +"@babel/plugin-transform-nullish-coalescing-operator@^7.26.6": + version "7.26.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.26.6.tgz#fbf6b3c92cb509e7b319ee46e3da89c5bedd31fe" + integrity sha512-CKW8Vu+uUZneQCPtXmSBUC6NCAUdya26hWCElAWh5mVSlSRsmiCPUUDKb3Z0szng1hiAJa098Hkhg9o4SE35Qw== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.26.5" -"@babel/plugin-transform-function-name@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz#cc354f8234e62968946c61a46d6365440fc764e0" - integrity sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ== +"@babel/plugin-transform-numeric-separator@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz#bfed75866261a8b643468b0ccfd275f2033214a1" + integrity sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q== dependencies: - "@babel/helper-compilation-targets" "^7.18.9" - "@babel/helper-function-name" "^7.18.9" - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-literals@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz#72796fdbef80e56fba3c6a699d54f0de557444bc" - integrity sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg== +"@babel/plugin-transform-object-rest-spread@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz#0203725025074164808bcf1a2cfa90c652c99f18" + integrity sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-compilation-targets" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-transform-parameters" "^7.25.9" -"@babel/plugin-transform-member-expression-literals@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz#ac9fdc1a118620ac49b7e7a5d2dc177a1bfee88e" - integrity sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA== +"@babel/plugin-transform-object-super@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz#385d5de135162933beb4a3d227a2b7e52bb4cf03" + integrity sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-replace-supers" "^7.25.9" -"@babel/plugin-transform-modules-amd@^7.19.6": - version "7.20.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz#3daccca8e4cc309f03c3a0c4b41dc4b26f55214a" - integrity sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g== +"@babel/plugin-transform-optional-catch-binding@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz#10e70d96d52bb1f10c5caaac59ac545ea2ba7ff3" + integrity sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g== dependencies: - "@babel/helper-module-transforms" "^7.20.11" - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-modules-commonjs@^7.19.6": - version "7.20.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.20.11.tgz#8cb23010869bf7669fd4b3098598b6b2be6dc607" - integrity sha512-S8e1f7WQ7cimJQ51JkAaDrEtohVEitXjgCGAS2N8S31Y42E+kWwfSz83LYz57QdBm7q9diARVqanIaH2oVgQnw== +"@babel/plugin-transform-optional-chaining@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz#e142eb899d26ef715435f201ab6e139541eee7dd" + integrity sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A== dependencies: - "@babel/helper-module-transforms" "^7.20.11" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-simple-access" "^7.20.2" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" -"@babel/plugin-transform-modules-systemjs@^7.19.6": - version "7.20.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz#467ec6bba6b6a50634eea61c9c232654d8a4696e" - integrity sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw== +"@babel/plugin-transform-parameters@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz#b856842205b3e77e18b7a7a1b94958069c7ba257" + integrity sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g== dependencies: - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-module-transforms" "^7.20.11" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-validator-identifier" "^7.19.1" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-modules-umd@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz#81d3832d6034b75b54e62821ba58f28ed0aab4b9" - integrity sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ== +"@babel/plugin-transform-private-methods@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz#847f4139263577526455d7d3223cd8bda51e3b57" + integrity sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw== dependencies: - "@babel/helper-module-transforms" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-create-class-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-named-capturing-groups-regex@^7.19.1": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz#626298dd62ea51d452c3be58b285d23195ba69a8" - integrity sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA== +"@babel/plugin-transform-private-property-in-object@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz#9c8b73e64e6cc3cbb2743633885a7dd2c385fe33" + integrity sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.20.5" - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-annotate-as-pure" "^7.25.9" + "@babel/helper-create-class-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-new-target@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz#d128f376ae200477f37c4ddfcc722a8a1b3246a8" - integrity sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw== +"@babel/plugin-transform-property-literals@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz#d72d588bd88b0dec8b62e36f6fda91cedfe28e3f" + integrity sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-object-super@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz#fb3c6ccdd15939b6ff7939944b51971ddc35912c" - integrity sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA== +"@babel/plugin-transform-regenerator@^7.25.9": + version "7.27.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.27.0.tgz#822feebef43d6a59a81f696b2512df5b1682db31" + integrity sha512-LX/vCajUJQDqE7Aum/ELUMZAY19+cDpghxrnyt5I1tV6X5PyC86AOoWXWFYFeIvauyeSA6/ktn4tQVn/3ZifsA== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/helper-replace-supers" "^7.18.6" + "@babel/helper-plugin-utils" "^7.26.5" + regenerator-transform "^0.15.2" -"@babel/plugin-transform-parameters@^7.20.1", "@babel/plugin-transform-parameters@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.7.tgz#0ee349e9d1bc96e78e3b37a7af423a4078a7083f" - integrity sha512-WiWBIkeHKVOSYPO0pWkxGPfKeWrCJyD3NJ53+Lrp/QMSZbsVPovrVl2aWZ19D/LTVnaDv5Ap7GJ/B2CTOZdrfA== +"@babel/plugin-transform-regexp-modifiers@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz#2f5837a5b5cd3842a919d8147e9903cc7455b850" + integrity sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-create-regexp-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-property-literals@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz#e22498903a483448e94e032e9bbb9c5ccbfc93a3" - integrity sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg== +"@babel/plugin-transform-reserved-words@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz#0398aed2f1f10ba3f78a93db219b27ef417fb9ce" + integrity sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-regenerator@^7.18.6": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz#57cda588c7ffb7f4f8483cc83bdcea02a907f04d" - integrity sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ== +"@babel/plugin-transform-shorthand-properties@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz#bb785e6091f99f826a95f9894fc16fde61c163f2" + integrity sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - regenerator-transform "^0.15.1" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-reserved-words@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz#b1abd8ebf8edaa5f7fe6bbb8d2133d23b6a6f76a" - integrity sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA== +"@babel/plugin-transform-spread@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz#24a35153931b4ba3d13cec4a7748c21ab5514ef9" + integrity sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" -"@babel/plugin-transform-shorthand-properties@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz#6d6df7983d67b195289be24909e3f12a8f664dc9" - integrity sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw== +"@babel/plugin-transform-sticky-regex@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz#c7f02b944e986a417817b20ba2c504dfc1453d32" + integrity sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-spread@^7.19.0": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz#c2d83e0b99d3bf83e07b11995ee24bf7ca09401e" - integrity sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw== +"@babel/plugin-transform-template-literals@^7.26.8": + version "7.26.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.26.8.tgz#966b15d153a991172a540a69ad5e1845ced990b5" + integrity sha512-OmGDL5/J0CJPJZTHZbi2XpO0tyT2Ia7fzpW5GURwdtp2X3fMmN8au/ej6peC/T33/+CRiIpA8Krse8hFGVmT5Q== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/helper-plugin-utils" "^7.26.5" -"@babel/plugin-transform-sticky-regex@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz#c6706eb2b1524028e317720339583ad0f444adcc" - integrity sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q== +"@babel/plugin-transform-typeof-symbol@^7.26.7": + version "7.27.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.0.tgz#044a0890f3ca694207c7826d0c7a65e5ac008aae" + integrity sha512-+LLkxA9rKJpNoGsbLnAgOCdESl73vwYn+V6b+5wHbrE7OGKVDPHIQvbFSzqE6rwqaCw2RE+zdJrlLkcf8YOA0w== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.26.5" -"@babel/plugin-transform-template-literals@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz#04ec6f10acdaa81846689d63fae117dd9c243a5e" - integrity sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA== +"@babel/plugin-transform-unicode-escapes@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz#a75ef3947ce15363fccaa38e2dd9bc70b2788b82" + integrity sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-typeof-symbol@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz#c8cea68263e45addcd6afc9091429f80925762c0" - integrity sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw== +"@babel/plugin-transform-unicode-property-regex@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz#a901e96f2c1d071b0d1bb5dc0d3c880ce8f53dd3" + integrity sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-create-regexp-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-unicode-escapes@^7.18.10": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz#1ecfb0eda83d09bbcb77c09970c2dd55832aa246" - integrity sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ== +"@babel/plugin-transform-unicode-regex@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz#5eae747fe39eacf13a8bd006a4fb0b5d1fa5e9b1" + integrity sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-create-regexp-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-unicode-regex@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz#194317225d8c201bbae103364ffe9e2cea36cdca" - integrity sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA== +"@babel/plugin-transform-unicode-sets-regex@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz#65114c17b4ffc20fa5b163c63c70c0d25621fabe" + integrity sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-create-regexp-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" "@babel/preset-env@^7.20.2": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.20.2.tgz#9b1642aa47bb9f43a86f9630011780dab7f86506" - integrity sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg== - dependencies: - "@babel/compat-data" "^7.20.1" - "@babel/helper-compilation-targets" "^7.20.0" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-validator-option" "^7.18.6" - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.18.6" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.18.9" - "@babel/plugin-proposal-async-generator-functions" "^7.20.1" - "@babel/plugin-proposal-class-properties" "^7.18.6" - "@babel/plugin-proposal-class-static-block" "^7.18.6" - "@babel/plugin-proposal-dynamic-import" "^7.18.6" - "@babel/plugin-proposal-export-namespace-from" "^7.18.9" - "@babel/plugin-proposal-json-strings" "^7.18.6" - "@babel/plugin-proposal-logical-assignment-operators" "^7.18.9" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.18.6" - "@babel/plugin-proposal-numeric-separator" "^7.18.6" - "@babel/plugin-proposal-object-rest-spread" "^7.20.2" - "@babel/plugin-proposal-optional-catch-binding" "^7.18.6" - "@babel/plugin-proposal-optional-chaining" "^7.18.9" - "@babel/plugin-proposal-private-methods" "^7.18.6" - "@babel/plugin-proposal-private-property-in-object" "^7.18.6" - "@babel/plugin-proposal-unicode-property-regex" "^7.18.6" - "@babel/plugin-syntax-async-generators" "^7.8.4" - "@babel/plugin-syntax-class-properties" "^7.12.13" - "@babel/plugin-syntax-class-static-block" "^7.14.5" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - "@babel/plugin-syntax-import-assertions" "^7.20.0" - "@babel/plugin-syntax-json-strings" "^7.8.3" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - "@babel/plugin-syntax-private-property-in-object" "^7.14.5" - "@babel/plugin-syntax-top-level-await" "^7.14.5" - "@babel/plugin-transform-arrow-functions" "^7.18.6" - "@babel/plugin-transform-async-to-generator" "^7.18.6" - "@babel/plugin-transform-block-scoped-functions" "^7.18.6" - "@babel/plugin-transform-block-scoping" "^7.20.2" - "@babel/plugin-transform-classes" "^7.20.2" - "@babel/plugin-transform-computed-properties" "^7.18.9" - "@babel/plugin-transform-destructuring" "^7.20.2" - "@babel/plugin-transform-dotall-regex" "^7.18.6" - "@babel/plugin-transform-duplicate-keys" "^7.18.9" - "@babel/plugin-transform-exponentiation-operator" "^7.18.6" - "@babel/plugin-transform-for-of" "^7.18.8" - "@babel/plugin-transform-function-name" "^7.18.9" - "@babel/plugin-transform-literals" "^7.18.9" - "@babel/plugin-transform-member-expression-literals" "^7.18.6" - "@babel/plugin-transform-modules-amd" "^7.19.6" - "@babel/plugin-transform-modules-commonjs" "^7.19.6" - "@babel/plugin-transform-modules-systemjs" "^7.19.6" - "@babel/plugin-transform-modules-umd" "^7.18.6" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.19.1" - "@babel/plugin-transform-new-target" "^7.18.6" - "@babel/plugin-transform-object-super" "^7.18.6" - "@babel/plugin-transform-parameters" "^7.20.1" - "@babel/plugin-transform-property-literals" "^7.18.6" - "@babel/plugin-transform-regenerator" "^7.18.6" - "@babel/plugin-transform-reserved-words" "^7.18.6" - "@babel/plugin-transform-shorthand-properties" "^7.18.6" - "@babel/plugin-transform-spread" "^7.19.0" - "@babel/plugin-transform-sticky-regex" "^7.18.6" - "@babel/plugin-transform-template-literals" "^7.18.9" - "@babel/plugin-transform-typeof-symbol" "^7.18.9" - "@babel/plugin-transform-unicode-escapes" "^7.18.10" - "@babel/plugin-transform-unicode-regex" "^7.18.6" - "@babel/preset-modules" "^0.1.5" - "@babel/types" "^7.20.2" - babel-plugin-polyfill-corejs2 "^0.3.3" - babel-plugin-polyfill-corejs3 "^0.6.0" - babel-plugin-polyfill-regenerator "^0.4.1" - core-js-compat "^3.25.1" - semver "^6.3.0" - -"@babel/preset-modules@^0.1.5": - version "0.1.5" - resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.5.tgz#ef939d6e7f268827e1841638dc6ff95515e115d9" - integrity sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA== + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.26.9.tgz#2ec64e903d0efe743699f77a10bdf7955c2123c3" + integrity sha512-vX3qPGE8sEKEAZCWk05k3cpTAE3/nOYca++JA+Rd0z2NCNzabmYvEiSShKzm10zdquOIAVXsy2Ei/DTW34KlKQ== + dependencies: + "@babel/compat-data" "^7.26.8" + "@babel/helper-compilation-targets" "^7.26.5" + "@babel/helper-plugin-utils" "^7.26.5" + "@babel/helper-validator-option" "^7.25.9" + "@babel/plugin-bugfix-firefox-class-in-computed-class-key" "^7.25.9" + "@babel/plugin-bugfix-safari-class-field-initializer-scope" "^7.25.9" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.25.9" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.25.9" + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly" "^7.25.9" + "@babel/plugin-proposal-private-property-in-object" "7.21.0-placeholder-for-preset-env.2" + "@babel/plugin-syntax-import-assertions" "^7.26.0" + "@babel/plugin-syntax-import-attributes" "^7.26.0" + "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" + "@babel/plugin-transform-arrow-functions" "^7.25.9" + "@babel/plugin-transform-async-generator-functions" "^7.26.8" + "@babel/plugin-transform-async-to-generator" "^7.25.9" + "@babel/plugin-transform-block-scoped-functions" "^7.26.5" + "@babel/plugin-transform-block-scoping" "^7.25.9" + "@babel/plugin-transform-class-properties" "^7.25.9" + "@babel/plugin-transform-class-static-block" "^7.26.0" + "@babel/plugin-transform-classes" "^7.25.9" + "@babel/plugin-transform-computed-properties" "^7.25.9" + "@babel/plugin-transform-destructuring" "^7.25.9" + "@babel/plugin-transform-dotall-regex" "^7.25.9" + "@babel/plugin-transform-duplicate-keys" "^7.25.9" + "@babel/plugin-transform-duplicate-named-capturing-groups-regex" "^7.25.9" + "@babel/plugin-transform-dynamic-import" "^7.25.9" + "@babel/plugin-transform-exponentiation-operator" "^7.26.3" + "@babel/plugin-transform-export-namespace-from" "^7.25.9" + "@babel/plugin-transform-for-of" "^7.26.9" + "@babel/plugin-transform-function-name" "^7.25.9" + "@babel/plugin-transform-json-strings" "^7.25.9" + "@babel/plugin-transform-literals" "^7.25.9" + "@babel/plugin-transform-logical-assignment-operators" "^7.25.9" + "@babel/plugin-transform-member-expression-literals" "^7.25.9" + "@babel/plugin-transform-modules-amd" "^7.25.9" + "@babel/plugin-transform-modules-commonjs" "^7.26.3" + "@babel/plugin-transform-modules-systemjs" "^7.25.9" + "@babel/plugin-transform-modules-umd" "^7.25.9" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.25.9" + "@babel/plugin-transform-new-target" "^7.25.9" + "@babel/plugin-transform-nullish-coalescing-operator" "^7.26.6" + "@babel/plugin-transform-numeric-separator" "^7.25.9" + "@babel/plugin-transform-object-rest-spread" "^7.25.9" + "@babel/plugin-transform-object-super" "^7.25.9" + "@babel/plugin-transform-optional-catch-binding" "^7.25.9" + "@babel/plugin-transform-optional-chaining" "^7.25.9" + "@babel/plugin-transform-parameters" "^7.25.9" + "@babel/plugin-transform-private-methods" "^7.25.9" + "@babel/plugin-transform-private-property-in-object" "^7.25.9" + "@babel/plugin-transform-property-literals" "^7.25.9" + "@babel/plugin-transform-regenerator" "^7.25.9" + "@babel/plugin-transform-regexp-modifiers" "^7.26.0" + "@babel/plugin-transform-reserved-words" "^7.25.9" + "@babel/plugin-transform-shorthand-properties" "^7.25.9" + "@babel/plugin-transform-spread" "^7.25.9" + "@babel/plugin-transform-sticky-regex" "^7.25.9" + "@babel/plugin-transform-template-literals" "^7.26.8" + "@babel/plugin-transform-typeof-symbol" "^7.26.7" + "@babel/plugin-transform-unicode-escapes" "^7.25.9" + "@babel/plugin-transform-unicode-property-regex" "^7.25.9" + "@babel/plugin-transform-unicode-regex" "^7.25.9" + "@babel/plugin-transform-unicode-sets-regex" "^7.25.9" + "@babel/preset-modules" "0.1.6-no-external-plugins" + babel-plugin-polyfill-corejs2 "^0.4.10" + babel-plugin-polyfill-corejs3 "^0.11.0" + babel-plugin-polyfill-regenerator "^0.6.1" + core-js-compat "^3.40.0" + semver "^6.3.1" + +"@babel/preset-modules@0.1.6-no-external-plugins": + version "0.1.6-no-external-plugins" + resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz#ccb88a2c49c817236861fee7826080573b8a923a" + integrity sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA== dependencies: "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" - "@babel/plugin-transform-dotall-regex" "^7.4.4" "@babel/types" "^7.4.4" esutils "^2.0.2" "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": - version "7.20.13" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.13.tgz#7055ab8a7cff2b8f6058bf6ae45ff84ad2aded4b" - integrity sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA== - dependencies: - regenerator-runtime "^0.13.11" - -"@babel/template@^7.18.10", "@babel/template@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8" - integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== - dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/parser" "^7.20.7" - "@babel/types" "^7.20.7" - -"@babel/template@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" - integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== - dependencies: - "@babel/code-frame" "^7.22.13" - "@babel/parser" "^7.22.15" - "@babel/types" "^7.22.15" - -"@babel/traverse@^7.20.10", "@babel/traverse@^7.20.12", "@babel/traverse@^7.20.13", "@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7": - version "7.23.2" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8" - integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw== - dependencies: - "@babel/code-frame" "^7.22.13" - "@babel/generator" "^7.23.0" - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-function-name" "^7.23.0" - "@babel/helper-hoist-variables" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/parser" "^7.23.0" - "@babel/types" "^7.23.0" - debug "^4.1.0" + version "7.27.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.27.0.tgz#fbee7cf97c709518ecc1f590984481d5460d4762" + integrity sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw== + dependencies: + regenerator-runtime "^0.14.0" + +"@babel/template@^7.25.9", "@babel/template@^7.26.9", "@babel/template@^7.27.0": + version "7.27.0" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.0.tgz#b253e5406cc1df1c57dcd18f11760c2dbf40c0b4" + integrity sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA== + dependencies: + "@babel/code-frame" "^7.26.2" + "@babel/parser" "^7.27.0" + "@babel/types" "^7.27.0" + +"@babel/traverse@^7.25.9", "@babel/traverse@^7.26.10", "@babel/traverse@^7.26.5", "@babel/traverse@^7.26.8", "@babel/traverse@^7.27.0": + version "7.27.0" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.27.0.tgz#11d7e644779e166c0442f9a07274d02cd91d4a70" + integrity sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA== + dependencies: + "@babel/code-frame" "^7.26.2" + "@babel/generator" "^7.27.0" + "@babel/parser" "^7.27.0" + "@babel/template" "^7.27.0" + "@babel/types" "^7.27.0" + debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.4.4": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.7.tgz#54ec75e252318423fc07fb644dc6a58a64c09b7f" - integrity sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg== +"@babel/types@^7.25.9", "@babel/types@^7.26.10", "@babel/types@^7.27.0", "@babel/types@^7.4.4": + version "7.27.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.27.0.tgz#ef9acb6b06c3173f6632d993ecb6d4ae470b4559" + integrity sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg== dependencies: - "@babel/helper-string-parser" "^7.19.4" - "@babel/helper-validator-identifier" "^7.19.1" - to-fast-properties "^2.0.0" - -"@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb" - integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg== - dependencies: - "@babel/helper-string-parser" "^7.22.5" - "@babel/helper-validator-identifier" "^7.22.20" - to-fast-properties "^2.0.0" + "@babel/helper-string-parser" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" "@discoveryjs/json-ext@^0.5.0": version "0.5.7" @@ -1119,46 +928,19 @@ "@types/yargs" "^17.0.8" chalk "^4.0.0" -"@jridgewell/gen-mapping@^0.1.0": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" - integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== - dependencies: - "@jridgewell/set-array" "^1.0.0" - "@jridgewell/sourcemap-codec" "^1.4.10" - -"@jridgewell/gen-mapping@^0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" - integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== - dependencies: - "@jridgewell/set-array" "^1.0.1" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/trace-mapping" "^0.3.9" - "@jridgewell/gen-mapping@^0.3.5": - version "0.3.5" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" - integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== + version "0.3.8" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz#4f0e06362e01362f823d348f1872b08f666d8142" + integrity sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA== dependencies: "@jridgewell/set-array" "^1.2.1" "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.24" -"@jridgewell/resolve-uri@3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" - integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== - "@jridgewell/resolve-uri@^3.1.0": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" - integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== - -"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" - integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== "@jridgewell/set-array@^1.2.1": version "1.2.1" @@ -1173,23 +955,10 @@ "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.25" -"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10": - version "1.4.14" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" - integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== - -"@jridgewell/sourcemap-codec@^1.4.14": - version "1.4.15" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" - integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== - -"@jridgewell/trace-mapping@^0.3.17": - version "0.3.20" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz#72e45707cf240fa6b081d0366f8265b0cd10197f" - integrity sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q== - dependencies: - "@jridgewell/resolve-uri" "^3.1.0" - "@jridgewell/sourcemap-codec" "^1.4.14" +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== "@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": version "0.3.25" @@ -1199,14 +968,6 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@jridgewell/trace-mapping@^0.3.9": - version "0.3.17" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" - integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== - dependencies: - "@jridgewell/resolve-uri" "3.1.0" - "@jridgewell/sourcemap-codec" "1.4.14" - "@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1": version "5.1.1-v1" resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz#dbf733a965ca47b1973177dc0bb6c889edcfb129" @@ -1578,29 +1339,29 @@ babel-loader@^9.1.2: find-cache-dir "^3.3.2" schema-utils "^4.0.0" -babel-plugin-polyfill-corejs2@^0.3.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz#5d1bd3836d0a19e1b84bbf2d9640ccb6f951c122" - integrity sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q== +babel-plugin-polyfill-corejs2@^0.4.10: + version "0.4.13" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.13.tgz#7d445f0e0607ebc8fb6b01d7e8fb02069b91dd8b" + integrity sha512-3sX/eOms8kd3q2KZ6DAhKPc0dgm525Gqq5NtWKZ7QYYZEv57OQ54KtblzJzH1lQF/eQxO8KjWGIK9IPUJNus5g== dependencies: - "@babel/compat-data" "^7.17.7" - "@babel/helper-define-polyfill-provider" "^0.3.3" - semver "^6.1.1" + "@babel/compat-data" "^7.22.6" + "@babel/helper-define-polyfill-provider" "^0.6.4" + semver "^6.3.1" -babel-plugin-polyfill-corejs3@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz#56ad88237137eade485a71b52f72dbed57c6230a" - integrity sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA== +babel-plugin-polyfill-corejs3@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz#4e4e182f1bb37c7ba62e2af81d8dd09df31344f6" + integrity sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ== dependencies: - "@babel/helper-define-polyfill-provider" "^0.3.3" - core-js-compat "^3.25.1" + "@babel/helper-define-polyfill-provider" "^0.6.3" + core-js-compat "^3.40.0" -babel-plugin-polyfill-regenerator@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz#390f91c38d90473592ed43351e801a9d3e0fd747" - integrity sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw== +babel-plugin-polyfill-regenerator@^0.6.1: + version "0.6.4" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.4.tgz#428c615d3c177292a22b4f93ed99e358d7906a9b" + integrity sha512-7gD3pRadPrbjhjLyxebmx/WrFYcuSjZ0XbdUujQMZ/fcE9oeewk2U/7PCvez84UeuK3oSjmPZ0Ch0dlupQvGzw== dependencies: - "@babel/helper-define-polyfill-provider" "^0.3.3" + "@babel/helper-define-polyfill-provider" "^0.6.4" balanced-match@^1.0.0: version "1.0.2" @@ -1654,7 +1415,7 @@ braces@~3.0.2: dependencies: fill-range "^7.1.1" -browserslist@^4.0.0, browserslist@^4.21.4: +browserslist@^4.0.0, browserslist@^4.21.4, browserslist@^4.24.0, browserslist@^4.24.4: version "4.24.4" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.4.tgz#c6b2865a3f08bcb860a0e827389003b9fe686e4b" integrity sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A== @@ -1664,7 +1425,7 @@ browserslist@^4.0.0, browserslist@^4.21.4: node-releases "^2.0.19" update-browserslist-db "^1.1.1" -browserslist@^4.21.10, browserslist@^4.21.3: +browserslist@^4.21.10: version "4.23.3" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.3.tgz#debb029d3c93ebc97ffbc8d9cbb03403e227c800" integrity sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA== @@ -1694,7 +1455,7 @@ caniuse-api@^3.0.0: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001646, caniuse-lite@^1.0.30001688: +caniuse-lite@^1.0.0: version "1.0.30001700" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001700.tgz#26cd429cf09b4fd4e745daf4916039c794d720f6" integrity sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ== @@ -1704,7 +1465,12 @@ caniuse-lite@^1.0.30001426: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001450.tgz#022225b91200589196b814b51b1bbe45144cf74f" integrity sha512-qMBmvmQmFXaSxexkjjfMvD5rnDL0+m+dUMZKoDYsGG8iZN29RuYh9eRoMvKsT6uMAWlyUUGDEQGJJYjzCIO9ew== -chalk@^2.0.0, chalk@^2.4.2: +caniuse-lite@^1.0.30001646, caniuse-lite@^1.0.30001688: + version "1.0.30001707" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001707.tgz#c5e104d199e6f4355a898fcd995a066c7eb9bf41" + integrity sha512-3qtRjw/HQSMlDWf+X79N206fepf4SOOU6SQLMaq/0KkZLmSjPxAkBOQQ+FxbHKfHmYLZFfdWsO3KA90ceHPSnw== + +chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -1833,12 +1599,17 @@ convert-source-map@^1.7.0: resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== -core-js-compat@^3.25.1: - version "3.27.2" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.27.2.tgz#607c50ad6db8fd8326af0b2883ebb987be3786da" - integrity sha512-welaYuF7ZtbYKGrIy7y3eb40d37rG1FvzEOfe7hSLd2iD6duMDqUhRfSvCGyC46HhR6Y8JXXdZ2lnRUMkPBpvg== +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + +core-js-compat@^3.40.0: + version "3.41.0" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.41.0.tgz#4cdfce95f39a8f27759b667cf693d96e5dda3d17" + integrity sha512-RFsU9LySVue9RTwdDVX/T0e2Y6jRYWXERKElIjpuEOEnxaXffI0X7RUwVzfYLfzuLXSNJDYoRYUAmRUcyln20A== dependencies: - browserslist "^4.21.4" + browserslist "^4.24.4" core-js@^3.27.2: version "3.27.2" @@ -1981,7 +1752,14 @@ csso@^4.2.0: dependencies: css-tree "^1.1.2" -debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2: +debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: + version "4.4.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" + integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== + dependencies: + ms "^2.1.3" + +debug@^4.3.2: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -1994,9 +1772,9 @@ deep-is@^0.1.3: integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== deepmerge@^4.2.2: - version "4.3.0" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.0.tgz#65491893ec47756d44719ae520e0e2609233b59b" - integrity sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og== + version "4.3.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== doctrine@^3.0.0: version "3.0.0" @@ -2051,9 +1829,9 @@ editorconfig@^1.0.2: semver "^7.5.3" electron-to-chromium@^1.5.4, electron-to-chromium@^1.5.73: - version "1.5.102" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.102.tgz#81a452ace8e2c3fa7fba904ea4fed25052c53d3f" - integrity sha512-eHhqaja8tE/FNpIiBrvBjFV/SSKpyWHLvxuR9dPTdo+3V9ppdLmFB7ZZQ98qNovcngPLYIz0oOBF9P0FfZef5Q== + version "1.5.123" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.123.tgz#fae5bdba0ba27045895176327aa79831aba0790c" + integrity sha512-refir3NlutEZqlKaBLK0tzlVLe5P2wDKS7UQt/3SpibizgsRAPOsqQC3ffw1nlv3ze5gjRQZYHoPymgVZkplFA== emojis-list@^3.0.0: version "3.0.0" @@ -2390,10 +2168,10 @@ fsevents@~2.3.2: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== fuse.js@^6.6.2: version "6.6.2" @@ -2468,12 +2246,12 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== dependencies: - function-bind "^1.1.1" + function-bind "^1.1.2" icss-utils@^5.0.0, icss-utils@^5.1.0: version "5.1.0" @@ -2541,12 +2319,12 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" -is-core-module@^2.9.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" - integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== +is-core-module@^2.16.0, is-core-module@^2.9.0: + version "2.16.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" + integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== dependencies: - has "^1.0.3" + hasown "^2.0.2" is-extglob@^2.1.1: version "2.1.1" @@ -2630,15 +2408,15 @@ js-yaml@^4.1.0: dependencies: argparse "^2.0.1" -jsesc@^2.5.1: - version "2.5.2" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" - integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== +jsesc@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" + integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== -jsesc@~0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" - integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== +jsesc@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e" + integrity sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g== json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: version "2.3.1" @@ -2660,7 +2438,7 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== -json5@^2.1.2, json5@^2.2.2: +json5@^2.1.2, json5@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== @@ -2810,6 +2588,11 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +ms@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + nanoid@^3.3.6, nanoid@^3.3.8: version "3.3.8" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf" @@ -3284,10 +3067,10 @@ redux@^4.2.0: dependencies: "@babel/runtime" "^7.9.2" -regenerate-unicode-properties@^10.1.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz#7c3192cab6dd24e21cb4461e5ddd7dd24fa8374c" - integrity sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ== +regenerate-unicode-properties@^10.2.0: + version "10.2.0" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz#626e39df8c372338ea9b8028d1f99dc3fd9c3db0" + integrity sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA== dependencies: regenerate "^1.4.2" @@ -3296,15 +3079,15 @@ regenerate@^1.4.2: resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== -regenerator-runtime@^0.13.11: - version "0.13.11" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" - integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== +regenerator-runtime@^0.14.0: + version "0.14.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" + integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== -regenerator-transform@^0.15.1: - version "0.15.1" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.1.tgz#f6c4e99fc1b4591f780db2586328e4d9a9d8dc56" - integrity sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg== +regenerator-transform@^0.15.2: + version "0.15.2" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.2.tgz#5bbae58b522098ebdf09bca2f83838929001c7a4" + integrity sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg== dependencies: "@babel/runtime" "^7.8.4" @@ -3313,29 +3096,29 @@ regex-parser@^2.2.11: resolved "https://registry.yarnpkg.com/regex-parser/-/regex-parser-2.2.11.tgz#3b37ec9049e19479806e878cabe7c1ca83ccfe58" integrity sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q== -regexpu-core@^5.2.1: - version "5.2.2" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.2.2.tgz#3e4e5d12103b64748711c3aad69934d7718e75fc" - integrity sha512-T0+1Zp2wjF/juXMrMxHxidqGYn8U4R+zleSJhX9tQ1PUsS8a9UtYfbsF9LdiVgNX3kiX8RNaKM42nfSgvFJjmw== +regexpu-core@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-6.2.0.tgz#0e5190d79e542bf294955dccabae04d3c7d53826" + integrity sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA== dependencies: regenerate "^1.4.2" - regenerate-unicode-properties "^10.1.0" - regjsgen "^0.7.1" - regjsparser "^0.9.1" + regenerate-unicode-properties "^10.2.0" + regjsgen "^0.8.0" + regjsparser "^0.12.0" unicode-match-property-ecmascript "^2.0.0" unicode-match-property-value-ecmascript "^2.1.0" -regjsgen@^0.7.1: - version "0.7.1" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.7.1.tgz#ee5ef30e18d3f09b7c369b76e7c2373ed25546f6" - integrity sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA== +regjsgen@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.8.0.tgz#df23ff26e0c5b300a6470cad160a9d090c3a37ab" + integrity sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q== -regjsparser@^0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.9.1.tgz#272d05aa10c7c1f67095b1ff0addae8442fc5709" - integrity sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ== +regjsparser@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.12.0.tgz#0e846df6c6530586429377de56e0475583b088dc" + integrity sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ== dependencies: - jsesc "~0.5.0" + jsesc "~3.0.2" require-from-string@^2.0.2: version "2.0.2" @@ -3370,7 +3153,16 @@ resolve-url-loader@^5.0.0: postcss "^8.2.14" source-map "0.6.1" -resolve@^1.14.2, resolve@^1.20.0: +resolve@^1.14.2: + version "1.22.10" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.10.tgz#b663e83ffb09bbf2386944736baae803029b8b39" + integrity sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w== + dependencies: + is-core-module "^2.16.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +resolve@^1.20.0: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== @@ -3439,7 +3231,7 @@ schema-utils@^4.0.0: ajv-formats "^2.1.1" ajv-keywords "^5.1.0" -semver@7.5.3, semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0, semver@^7.3.8, semver@^7.5.3: +semver@7.5.3, semver@^6.0.0, semver@^6.3.0, semver@^6.3.1, semver@^7.3.8, semver@^7.5.3: version "7.5.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e" integrity sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ== @@ -3600,11 +3392,6 @@ text-table@^0.2.0: resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== -to-fast-properties@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" - integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== - to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -3630,9 +3417,9 @@ undici-types@~6.20.0: integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg== unicode-canonical-property-names-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" - integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== + version "2.0.1" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz#cb3173fe47ca743e228216e4a3ddc4c84d628cc2" + integrity sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg== unicode-match-property-ecmascript@^2.0.0: version "2.0.0" @@ -3643,9 +3430,9 @@ unicode-match-property-ecmascript@^2.0.0: unicode-property-aliases-ecmascript "^2.0.0" unicode-match-property-value-ecmascript@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz#cb5fffdcd16a05124f5a4b0bf7c3770208acbbe0" - integrity sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA== + version "2.2.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz#a0401aee72714598f739b68b104e4fe3a0cb3c71" + integrity sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg== unicode-property-aliases-ecmascript@^2.0.0: version "2.1.0" @@ -3653,9 +3440,9 @@ unicode-property-aliases-ecmascript@^2.0.0: integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== update-browserslist-db@^1.1.0, update-browserslist-db@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz#97e9c96ab0ae7bcac08e9ae5151d26e6bc6b5580" - integrity sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg== + version "1.1.3" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz#348377dd245216f9e7060ff50b15a1b740b75420" + integrity sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw== dependencies: escalade "^3.2.0" picocolors "^1.1.1" From e8584526dfe1805d42e2b082168a687c4df93d66 Mon Sep 17 00:00:00 2001 From: Lotte Steenbrink Date: Mon, 31 Mar 2025 19:08:07 +0200 Subject: [PATCH 046/157] DEVELOPMENT.md: add composer install hint to troubleshooting --- DEVELOPMENT.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 69382b730..2292e90a6 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -229,3 +229,11 @@ If unspecific issues appear try using Docker version >= 20.10.14. ### `service "es_workspace" is not running` Make sure you're running your docker commands from the `docker/dev` directory, not from `docker` + +### `main` is broken after pulling the latest commits from upstream +Try running +```bash +composer install +``` +from this repository's root directory. +If dependencies have been updated in `composer.json` since you last synced `main`, this should fix it. \ No newline at end of file From 84fed46d894df97895ffd5b97bbcee74cd08916a Mon Sep 17 00:00:00 2001 From: Jens Brandt <39161595+flyingapfopenguin@users.noreply.github.com> Date: Sun, 30 Mar 2025 10:31:23 +0200 Subject: [PATCH 047/157] change MYSQL_RANDOM_ROOT_PASSWORD change MYSQL_RANDOM_ROOT_PASSWORD to a non-null string, such that it can be nulled in a docker-compose overwrite. Podman does typechecking and therefore an integer cannot be nulled in an overwrite file. --- docker/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 580b743e7..c94051ea6 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -24,7 +24,7 @@ services: MYSQL_DATABASE: engelsystem MYSQL_USER: engelsystem MYSQL_PASSWORD: engelsystem - MYSQL_RANDOM_ROOT_PASSWORD: 1 + MYSQL_RANDOM_ROOT_PASSWORD: "1" MYSQL_INITDB_SKIP_TZINFO: "yes" volumes: - db:/var/lib/mysql From 86eb0adb2eb6ae348b1b4054f5fcf134e3d3ffe1 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Mon, 31 Mar 2025 21:29:16 +0200 Subject: [PATCH 048/157] Fix formatting --- DEVELOPMENT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 2292e90a6..b1ed01260 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -236,4 +236,4 @@ Try running composer install ``` from this repository's root directory. -If dependencies have been updated in `composer.json` since you last synced `main`, this should fix it. \ No newline at end of file +If dependencies have been updated in `composer.json` since you last synced `main`, this should fix it. From 4ef8cdbae2fbd87e60e0fb79f3480d8b5be1d058 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Wed, 4 Dec 2024 21:10:34 +0100 Subject: [PATCH 049/157] Move night shift calculation to goodie helper --- includes/controller/users_controller.php | 3 +- includes/model/User_model.php | 75 -------------- includes/pages/admin_active.php | 4 +- src/Helpers/Goodie.php | 120 +++++++++++++++++++++++ src/Models/Shifts/Shift.php | 2 +- tests/Unit/Helpers/GoodieTest.php | 50 ++++++++++ 6 files changed, 176 insertions(+), 78 deletions(-) create mode 100644 src/Helpers/Goodie.php create mode 100644 tests/Unit/Helpers/GoodieTest.php diff --git a/includes/controller/users_controller.php b/includes/controller/users_controller.php index 4bad10a14..4f2d35156 100644 --- a/includes/controller/users_controller.php +++ b/includes/controller/users_controller.php @@ -1,6 +1,7 @@ resetApiKey($user_source); } - $goodie_score = sprintf('%.2f', User_goodie_score($user_source->id)) . ' h'; + $goodie_score = sprintf('%.2f', Goodie::userScore($user_source)) . ' h'; if ($user_source->state->force_active && config('enable_force_active')) { $goodie_score = '' . __('Enough') . ''; } diff --git a/includes/model/User_model.php b/includes/model/User_model.php index 3bbaec583..52ebc1b0d 100644 --- a/includes/model/User_model.php +++ b/includes/model/User_model.php @@ -1,46 +1,12 @@ 0]; - } - - $worklogHours = Worklog::query() - ->where('user_id', $userId) - ->where('worked_at', '<=', Carbon::Now()) - ->sum('hours'); - - return $result_shifts['goodie_score'] + $worklogHours; -} - /** * @param User $user * @return float @@ -85,44 +51,3 @@ function User_get_eligable_voucher_count($user) return $vouchers; } - -/** - * Generates the query to sum night shifts - * - * @return string - */ -function User_get_shifts_sum_query() -{ - $nightShifts = config('night_shifts'); - if (!$nightShifts['enabled']) { - return 'COALESCE(SUM(UNIX_TIMESTAMP(shifts.end) - UNIX_TIMESTAMP(shifts.start)), 0)'; - } - - /* @see \Engelsystem\Models\Shifts\Shift::isNightShift to keep it in sync */ - return sprintf( - ' - COALESCE(SUM( - (1 + ( - /* Starts during night */ - HOUR(shifts.start) >= %1$d AND HOUR(shifts.start) < %2$d - /* Ends during night */ - OR ( - HOUR(shifts.end) > %1$d - || HOUR(shifts.end) = %1$d AND MINUTE(shifts.end) > 0 - ) AND HOUR(shifts.end) <= %2$d - /* Starts before and ends after night */ - OR HOUR(shifts.start) <= %1$d AND HOUR(shifts.end) >= %2$d - )) - * (UNIX_TIMESTAMP(shifts.end) - UNIX_TIMESTAMP(shifts.start)) - * (1 - (%3$d + 1) - * (CASE - WHEN `shift_entries`.`freeloaded_by` IS NULL THEN 0 - ELSE 1 - END)) - ), 0) - ', - $nightShifts['start'], - $nightShifts['end'], - $nightShifts['multiplier'] - ); -} diff --git a/includes/pages/admin_active.php b/includes/pages/admin_active.php index 8ebd3e725..c40fd6961 100644 --- a/includes/pages/admin_active.php +++ b/includes/pages/admin_active.php @@ -1,7 +1,9 @@ getValue(Db::connection()->getQueryGrammar()); $request = request(); $goodie = GoodieType::from(config('goodie_type')); $goodie_enabled = $goodie !== GoodieType::None; diff --git a/src/Helpers/Goodie.php b/src/Helpers/Goodie.php new file mode 100644 index 000000000..c52ac3249 --- /dev/null +++ b/src/Helpers/Goodie.php @@ -0,0 +1,120 @@ +getConnection(); + + if (!$connection->getQueryGrammar() instanceof MySqlGrammar) { + return $connection->raw('0'); + } + + // @codeCoverageIgnoreStart + // as sqlite does not support TIMESTAMPDIFF + + if (!$nightShifts['enabled']) { + return $connection->raw( + /** @lang MySQL */ + 'COALESCE(SUM(TIMESTAMPDIFF(MINUTE, shifts.start, shifts.end) * 60), 0)' + ); + } + + /* @see \Engelsystem\Models\Shifts\Shift::isNightShift to keep them in sync */ + $query = + /** @lang MySQL */ + ' + COALESCE(SUM( + /* Shift length */ + TIMESTAMPDIFF(MINUTE, shifts.start, shifts.end) * 60 + + /* Handle freeloading */ + * ( + 1 + - (%3$d + 1) + * ( + CASE WHEN `shift_entries`.`freeloaded_by` IS NULL + THEN 0 + ELSE 1 + END + ) + ) + + /** Is night shift */ + * (1 + ( + /* Starts during night */ + HOUR(shifts.start) >= %1$d AND HOUR(shifts.start) < %2$d + /* Ends during night */ + OR ( + HOUR(shifts.end) > %1$d + || HOUR(shifts.end) = %1$d AND MINUTE(shifts.end) > 0 + ) AND HOUR(shifts.end) <= %2$d + /* Starts before and ends after night */ + OR HOUR(shifts.start) <= %1$d AND HOUR(shifts.end) >= %2$d + )) + ), 0) + '; + + $query = sprintf($query, $nightShifts['start'], $nightShifts['end'], $nightShifts['multiplier']); + + return $connection->raw($query); + // @codeCoverageIgnoreEnd + } + + /** + * Returns the goodie score (number of hours counted for goodie score) + * Includes only ended shifts + */ + public static function userScore(User $user): float + { + /** @var Database $db */ + $db = app(Database::class); + $con = $db->getConnection(); + + $state = $con + ->query() + ->from('users') + ->selectRaw(sprintf( + /** @lang MySQL */ + 'ROUND((%s) / 3600, 2) AS `goodie_score`', + self::shiftScoreQuery()->getValue($con->getQueryGrammar()) + )) + ->where('users.id', $user->id) + ->join('shift_entries', 'users.id', 'shift_entries.user_id') + ->join('shifts', 'shift_entries.shift_id', 'shifts.id') + ->where('shifts.end', '<', Carbon::now()) + ->groupBy('users.id') + ->first(); + + $shiftHours = 0; + if ($state) { + // @codeCoverageIgnoreStart + $shiftHours = (float) $state->goodie_score; + // @codeCoverageIgnoreEnd + } + + $worklogHours = $user->worklogs() + ->where('worked_at', '<=', Carbon::Now()) + ->sum('hours'); + + return $shiftHours + $worklogHours; + } +} diff --git a/src/Models/Shifts/Shift.php b/src/Models/Shifts/Shift.php index 8da36223f..8ea6bade8 100644 --- a/src/Models/Shifts/Shift.php +++ b/src/Models/Shifts/Shift.php @@ -140,7 +140,7 @@ public function isNightShift(): bool { $config = config('night_shifts'); - /** @see User_get_shifts_sum_query to keep it in sync */ + /** @see \Engelsystem\Helpers\Goodie::shiftScoreQuery to keep them in sync */ return $config['enabled'] && ( // Starts during night $this->start->hour >= $config['start'] && $this->start->hour < $config['end'] diff --git a/tests/Unit/Helpers/GoodieTest.php b/tests/Unit/Helpers/GoodieTest.php new file mode 100644 index 000000000..c7b855b59 --- /dev/null +++ b/tests/Unit/Helpers/GoodieTest.php @@ -0,0 +1,50 @@ +assertEquals('0', $result->getValue(new SQLiteGrammar())); + } + + /** + * @covers \Engelsystem\Helpers\Goodie::userScore + */ + public function testUserScore(): void + { + /** @var User $user */ + $user = User::factory()->create(); + Worklog::factory()->create(['user_id' => $user->id, 'hours' => 42.23]); + + $result = Goodie::userScore($user); + + $this->assertEquals(42.23, $result); + } + + protected function setUp(): void + { + parent::setUp(); + + $this->initDatabase(); + $this->app->instance('config', new Config(['night_shifts' => []])); + } +} From 238709e49585e503b9257c1deceb75d92d2572f3 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Wed, 25 Dec 2024 15:03:31 +0100 Subject: [PATCH 050/157] Show goodie score on goodie edit page --- includes/controller/users_controller.php | 2 +- resources/lang/de_DE/default.po | 15 ++++++++++++--- resources/lang/en_US/default.po | 12 ++++++++++++ resources/views/admin/user/edit-goodie.twig | 5 +++++ src/Controllers/Admin/UserGoodieController.php | 4 ++++ 5 files changed, 34 insertions(+), 4 deletions(-) diff --git a/includes/controller/users_controller.php b/includes/controller/users_controller.php index 4f2d35156..a9f4e72d2 100644 --- a/includes/controller/users_controller.php +++ b/includes/controller/users_controller.php @@ -256,7 +256,7 @@ function user_controller() $goodie_score = sprintf('%.2f', Goodie::userScore($user_source)) . ' h'; if ($user_source->state->force_active && config('enable_force_active')) { - $goodie_score = '' . __('Enough') . ''; + $goodie_score = '' . __('user.goodie_score.enough') . ''; } $worklogs = $user_source->worklogs() diff --git a/resources/lang/de_DE/default.po b/resources/lang/de_DE/default.po index e4e096c9d..709d04c82 100644 --- a/resources/lang/de_DE/default.po +++ b/resources/lang/de_DE/default.po @@ -395,9 +395,6 @@ msgstr "Anzahl der Gutscheine gespeichert." msgid "User not found." msgstr "Benutzer nicht gefunden." -msgid "Enough" -msgstr "Genug" - msgid "All users" msgstr "Alle Benutzer" @@ -1856,6 +1853,18 @@ msgstr "T-Shirt bearbeiten" msgid "user.edit.goodie" msgstr "Goodie bearbeiten" +msgid "user.goodie_score.enough" +msgstr "Genug" + +msgid "user.goodie_score.value" +msgstr "%s h" + +msgid "user.tshirt_score" +msgstr "T-Shirt Score: %s" + +msgid "user.goodie_score" +msgstr "Goodie Score: %s" + msgid "form.shirt" msgstr "T-Shirt" diff --git a/resources/lang/en_US/default.po b/resources/lang/en_US/default.po index 518c4ea82..6051d735f 100644 --- a/resources/lang/en_US/default.po +++ b/resources/lang/en_US/default.po @@ -653,6 +653,18 @@ msgstr "Edit T-shirt" msgid "user.edit.goodie" msgstr "Edit goodie" +msgid "user.goodie_score.enough" +msgstr "Enough" + +msgid "user.goodie_score.value" +msgstr "%s h" + +msgid "user.tshirt_score" +msgstr "T-Shirt score: %s" + +msgid "user.goodie_score" +msgstr "Goodie score: %s" + msgid "user.shirt_size" msgstr "T-shirt size" diff --git a/resources/views/admin/user/edit-goodie.twig b/resources/views/admin/user/edit-goodie.twig index ec6891331..3a4a0d60a 100644 --- a/resources/views/admin/user/edit-goodie.twig +++ b/resources/views/admin/user/edit-goodie.twig @@ -15,6 +15,11 @@ {% include 'layouts/parts/messages.twig' %} +
    + {% set score = goodie_score == '~' ? __('user.goodie_score.enough') : __('user.goodie_score.value', [goodie_score])%} +

    {{ is_tshirt ? __('user.tshirt_score', [score]) : __('user.goodie_score', [score]) }}

    +
    + {{ csrf() }} diff --git a/src/Controllers/Admin/UserGoodieController.php b/src/Controllers/Admin/UserGoodieController.php index b548bac29..589ba57a3 100644 --- a/src/Controllers/Admin/UserGoodieController.php +++ b/src/Controllers/Admin/UserGoodieController.php @@ -9,6 +9,7 @@ use Engelsystem\Controllers\BaseController; use Engelsystem\Controllers\HasUserNotifications; use Engelsystem\Helpers\Authenticator; +use Engelsystem\Helpers\Goodie; use Engelsystem\Http\Exceptions\HttpNotFound; use Engelsystem\Http\Redirector; use Engelsystem\Http\Request; @@ -48,13 +49,16 @@ public function editGoodie(Request $request): Response $this->checkActive(); $userId = (int) $request->getAttribute('user_id'); + /** @var User $user */ $user = $this->user->findOrFail($userId); + $goodieScore = $user->state->force_active ? '~' : Goodie::userScore($user); return $this->response->withView( 'admin/user/edit-goodie.twig', [ 'userdata' => $user, 'is_tshirt' => $this->config->get('goodie_type') === GoodieType::Tshirt->value, + 'goodie_score' => $goodieScore, ] ); } From d12347b70d072e2516eb15b3739aab3063c2b95b Mon Sep 17 00:00:00 2001 From: Xu Date: Thu, 2 Jan 2025 14:33:10 +0100 Subject: [PATCH 051/157] fix nightShiftsSumQuery --- src/Helpers/Goodie.php | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/src/Helpers/Goodie.php b/src/Helpers/Goodie.php index c52ac3249..4ff6d1088 100644 --- a/src/Helpers/Goodie.php +++ b/src/Helpers/Goodie.php @@ -48,28 +48,29 @@ public static function shiftScoreQuery(): Expression /* Handle freeloading */ * ( - 1 - - (%3$d + 1) - * ( - CASE WHEN `shift_entries`.`freeloaded_by` IS NULL - THEN 0 - ELSE 1 - END - ) + CASE WHEN `shift_entries`.`freeloaded_by` IS NULL + THEN 1 + ELSE -2 + END ) - /** Is night shift */ - * (1 + ( - /* Starts during night */ - HOUR(shifts.start) >= %1$d AND HOUR(shifts.start) < %2$d - /* Ends during night */ - OR ( - HOUR(shifts.end) > %1$d - || HOUR(shifts.end) = %1$d AND MINUTE(shifts.end) > 0 - ) AND HOUR(shifts.end) <= %2$d - /* Starts before and ends after night */ - OR HOUR(shifts.start) <= %1$d AND HOUR(shifts.end) >= %2$d - )) + /* Is night shift */ + * ( + CASE WHEN + /* Starts during night */ + HOUR(shifts.start) >= %1$d AND HOUR(shifts.start) < %2$d + /* Ends during night */ + OR ( + HOUR(shifts.end) > %1$d + || HOUR(shifts.end) = %1$d AND MINUTE(shifts.end) > 0 + ) AND HOUR(shifts.end) <= %2$d + /* Starts before and ends after night */ + OR HOUR(shifts.start) <= %1$d AND HOUR(shifts.end) >= %2$d + /* Use multiplier */ + THEN %3$d + ELSE 1 + END + ) ), 0) '; From 3e36578298fd40f16cee9104de6ebfc7cea2a876 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sun, 2 Mar 2025 13:08:37 +0100 Subject: [PATCH 052/157] Translation EN: Use T-shirt --- config/config.default.php | 2 +- includes/view/User_view.php | 2 +- resources/lang/en_US/default.po | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/config.default.php b/config/config.default.php index 1d96ec021..dc4061f04 100644 --- a/config/config.default.php +++ b/config/config.default.php @@ -398,7 +398,7 @@ // The default locale to use 'default_locale' => env('DEFAULT_LOCALE', 'en_US'), - // Available T-Shirt sizes + // Available T-shirt sizes // To disable a t-shirt size in config.php, you can set its value to null 'tshirt_sizes' => [ 'S' => 'Small Straight-Cut', diff --git a/includes/view/User_view.php b/includes/view/User_view.php index 925641dfb..e962c0aa3 100644 --- a/includes/view/User_view.php +++ b/includes/view/User_view.php @@ -205,7 +205,7 @@ class="btn btn-sm btn-secondary ms-2 js-only" } if ($goodie_enabled) { if ($goodie_tshirt) { - $user_table_headers['got_goodie'] = Users_table_header_link('got_goodie', __('T-Shirt'), $order_by); + $user_table_headers['got_goodie'] = Users_table_header_link('got_goodie', __('T-shirt'), $order_by); $user_table_headers['shirt_size'] = Users_table_header_link('shirt_size', __('Size'), $order_by); } else { $user_table_headers['got_goodie'] = Users_table_header_link('got_goodie', __('Goodie'), $order_by); diff --git a/resources/lang/en_US/default.po b/resources/lang/en_US/default.po index 6051d735f..54320e8ed 100644 --- a/resources/lang/en_US/default.po +++ b/resources/lang/en_US/default.po @@ -660,7 +660,7 @@ msgid "user.goodie_score.value" msgstr "%s h" msgid "user.tshirt_score" -msgstr "T-Shirt score: %s" +msgstr "T-shirt score: %s" msgid "user.goodie_score" msgstr "Goodie score: %s" From 0f9f52cf2104e792c6e54e77da452ecf4129c2bf Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Mon, 24 Mar 2025 22:17:44 +0100 Subject: [PATCH 053/157] Add goodie feature test --- tests/Feature/ApplicationFeatureTest.php | 10 +++ tests/Feature/Helpers/GoodieTest.php | 109 +++++++++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 tests/Feature/Helpers/GoodieTest.php diff --git a/tests/Feature/ApplicationFeatureTest.php b/tests/Feature/ApplicationFeatureTest.php index 3560e6ec2..06cd2af36 100644 --- a/tests/Feature/ApplicationFeatureTest.php +++ b/tests/Feature/ApplicationFeatureTest.php @@ -4,10 +4,13 @@ namespace Engelsystem\Test\Feature; +use Engelsystem\Application; use PHPUnit\Framework\TestCase; abstract class ApplicationFeatureTest extends TestCase { + protected Application $app; + public static function setUpBeforeClass(): void { $_SERVER['HTTP_HOST'] = 'foo.bar'; @@ -27,4 +30,11 @@ protected function tearDown(): void ini_set('date.timezone', 'UTC'); date_default_timezone_set('UTC'); } + + protected function setUp(): void + { + parent::setUp(); + + $this->app = app(); + } } diff --git a/tests/Feature/Helpers/GoodieTest.php b/tests/Feature/Helpers/GoodieTest.php new file mode 100644 index 000000000..dfe2b47d6 --- /dev/null +++ b/tests/Feature/Helpers/GoodieTest.php @@ -0,0 +1,109 @@ + '', 'email' => '', 'password' => '', 'api_key' => '']); + $user->save(); + $this->createdModels[] = $user; + $workLog = new Worklog([ + 'user_id' => $user->id, + 'hours' => 3.87, + 'creator_id' => $user->id, + 'comment' => '', + 'worked_at' => Carbon::now()->subHour(), + ]); + $workLog->save(); + $this->createdModels[] = $workLog; + $shiftType = new ShiftType([ + 'name' => 'Type', + 'description' => '', + ]); + $shiftType->save(); + $this->createdModels[] = $shiftType; + $location = new Location([ + 'name' => 'Local', + ]); + $location->save(); + $this->createdModels[] = $location; + $shift = new Shift([ + 'title' => 'Shift', + 'start' => Carbon::create('2020-03-02 1:00'), + 'end' => Carbon::create('2020-03-02 4:00'), + 'shift_type_id' => $shiftType->id, + 'location_id' => $location->id, + 'created_by' => $user->id, + ]); + $shift->save(); + $this->createdModels[] = $shift; + $angelType = new AngelType([ + 'name' => 'AngelType', + ]); + $angelType->save(); + $this->createdModels[] = $angelType; + $shiftEntry = new ShiftEntry([ + 'user_id' => $user->id, + 'shift_id' => $shift->id, + 'angel_type_id' => $angelType->id, + ]); + $shiftEntry->save(); + $this->createdModels[] = $shiftEntry; + + $result = Goodie::userScore($user); + + $this->assertEquals(9.87, round($result, 2)); + } + + private function deleteModels(): void + { + foreach ($this->createdModels as $model) { + $model->delete(); + } + } + + protected function setUp(): void + { + parent::setUp(); + + $this->createdModels = []; + config( + 'night_shifts', + [ + 'enabled' => true, + 'start' => 2, + 'end' => 6, + 'multiplier' => 2, + ] + ); + } + + public function tearDown(): void + { + parent::tearDown(); + $this->deleteModels(); + } +} From a64d60cc895f41e6c0a22c3175dd3890fa063fb7 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Tue, 18 Feb 2025 23:47:00 +0100 Subject: [PATCH 054/157] Explain [more] tag --- resources/lang/de_DE/default.po | 2 +- resources/lang/en_US/default.po | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/lang/de_DE/default.po b/resources/lang/de_DE/default.po index 709d04c82..51ee6f362 100644 --- a/resources/lang/de_DE/default.po +++ b/resources/lang/de_DE/default.po @@ -1465,7 +1465,7 @@ msgid "news.edit.message" msgstr "Nachricht" msgid "news.edit.hint" -msgstr "Du kannst Markdown und den [more] Tag benutzen" +msgstr "Du kannst Markdown verwenden sowie den [more] Tag um die Vorschau vom Text zu trennen." msgid "news.delete.title" msgstr "News \"%s\" löschen" diff --git a/resources/lang/en_US/default.po b/resources/lang/en_US/default.po index 54320e8ed..aac3f4e75 100644 --- a/resources/lang/en_US/default.po +++ b/resources/lang/en_US/default.po @@ -260,7 +260,7 @@ msgid "news.edit.message" msgstr "Message" msgid "news.edit.hint" -msgstr "You can use Markdown and the [more] tag" +msgstr "You can use Markdown and the [more] tag to separate the preview from the main text." msgid "news.delete.title" msgstr "Delete news \"%s\"" From 57b414c8289f2c6322efe01516a3dd9fa3bb6d82 Mon Sep 17 00:00:00 2001 From: Lotte Steenbrink Date: Wed, 26 Feb 2025 13:44:33 +0100 Subject: [PATCH 055/157] make shift collision message a warning --- includes/view/Shifts_view.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/view/Shifts_view.php b/includes/view/Shifts_view.php index e4c945cde..9e0bd48db 100644 --- a/includes/view/Shifts_view.php +++ b/includes/view/Shifts_view.php @@ -201,7 +201,7 @@ function Shift_view( $content = [msg()]; if ($shift_signup_state->getState() === ShiftSignupStatus::COLLIDES) { - $content[] = info(__('This shift collides with one of your shifts.'), true); + $content[] = warning(__('This shift collides with one of your shifts.'), true); } if ($shift_signup_state->getState() === ShiftSignupStatus::SIGNED_UP) { From aebc966480d545728b4253d23a0b3dd2b8175601 Mon Sep 17 00:00:00 2001 From: Lotte Steenbrink Date: Wed, 26 Feb 2025 14:22:10 +0100 Subject: [PATCH 056/157] Shifts_view: make alert harder to miss, refactor into Shift_view_alert_render() --- includes/view/Shifts_view.php | 59 +++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/includes/view/Shifts_view.php b/includes/view/Shifts_view.php index 9e0bd48db..98d5cc074 100644 --- a/includes/view/Shifts_view.php +++ b/includes/view/Shifts_view.php @@ -200,25 +200,6 @@ function Shift_view( $content = [msg()]; - if ($shift_signup_state->getState() === ShiftSignupStatus::COLLIDES) { - $content[] = warning(__('This shift collides with one of your shifts.'), true); - } - - if ($shift_signup_state->getState() === ShiftSignupStatus::SIGNED_UP) { - $content[] = info(__('You are signed up for this shift.') - . (($shift->start->subHours(config('last_unsubscribe')) < Carbon::now() && $shift->end > Carbon::now()) - ? ' ' . __('shift.sign_out.hint', [config('last_unsubscribe')]) - : ''), true); - } - - $signupAdvanceSeconds = ($shift->shiftType->signup_advance_hours ?: config('signup_advance_hours')) * 3600; - if ($signupAdvanceSeconds && $shift->start->timestamp > time() + $signupAdvanceSeconds) { - $content[] = info(sprintf( - __('This shift is in the far future. It becomes available for signup at %s.'), - date(__('general.datetime'), $shift->start->timestamp - $signupAdvanceSeconds) - ), true); - } - $buttons = []; if ($shift_admin || $shiftTypesEdit || $locationsEdit) { $buttons = [ @@ -257,11 +238,13 @@ function Shift_view( user_link(auth()->user()->id), ' ' . __('profile.my_shifts') ); - $content[] = buttons($buttons); + $content[] = buttons($buttons); $content[] = Shift_view_header($shift, $location); + $content[] = div('row', [ div('col-sm-6', [ + Shift_view_alert_render($shift, $shift_signup_state), '

    ' . __('Needed angels') . '

    ', '
    ' . $needed_angels . '
    ', ]), @@ -298,6 +281,42 @@ function Shift_view( ); } +/** + * Checks whether the user is elligible and able to sign up for this shift and + * generates an appropriate alert if not. + * + * @param Shift $shift + * @param Engelsystem\ShiftSignupState $shift_signup_state + * @return string '' if no alert is necessary, alert div otherwise + */ +function Shift_view_alert_render( + Shift $shift, + ShiftSignupState $shift_signup_state +) { + $alert = ''; + + if ($shift_signup_state->getState() === ShiftSignupStatus::COLLIDES) { + $alert = warning(__('This shift collides with one of your shifts.'), true); + } + + if ($shift_signup_state->getState() === ShiftSignupStatus::SIGNED_UP) { + $alert = info(__('You are signed up for this shift.') + . (($shift->start->subHours(config('last_unsubscribe')) < Carbon::now() && $shift->end > Carbon::now()) + ? ' ' . __('shift.sign_out.hint', [config('last_unsubscribe')]) + : ''), true); + } + + $signupAdvanceSeconds = ($shift->shiftType->signup_advance_hours ?: config('signup_advance_hours')) * 3600; + if ($signupAdvanceSeconds && $shift->start->timestamp > time() + $signupAdvanceSeconds) { + $alert = info(sprintf( + __('This shift is in the far future. It becomes available for signup at %s.'), + date(__('general.datetime'), $shift->start->timestamp - $signupAdvanceSeconds) + ), true); + } + + return $alert; +} + /** * @param array $needed_angeltype * @param AngelType[]|Collection $angeltypes From 3e5a3a6ddd87620f73290d2f4b7d2e03bf25f052 Mon Sep 17 00:00:00 2001 From: Xu Date: Sun, 5 Jan 2025 18:25:03 +0100 Subject: [PATCH 057/157] Refactor voucher edit --- config/routes.php | 9 + includes/controller/users_controller.php | 66 ------- includes/includes.php | 1 - includes/model/User_model.php | 53 ----- includes/view/User_view.php | 48 +---- resources/assets/js/voucher.js | 5 +- resources/lang/de_DE/additional.po | 3 + resources/lang/de_DE/default.po | 33 ++-- resources/lang/en_US/additional.po | 3 + resources/lang/en_US/default.po | 21 ++ resources/views/admin/user/edit-voucher.twig | 35 ++++ .../Admin/UserVoucherController.php | 106 ++++++++++ src/Helpers/UserVouchers.php | 60 ++++++ .../Admin/UserVoucherControllerTest.php | 187 ++++++++++++++++++ tests/Unit/Helpers/UserVouchersTest.php | 155 +++++++++++++++ 15 files changed, 604 insertions(+), 181 deletions(-) delete mode 100644 includes/model/User_model.php create mode 100644 resources/views/admin/user/edit-voucher.twig create mode 100644 src/Controllers/Admin/UserVoucherController.php create mode 100644 src/Helpers/UserVouchers.php create mode 100644 tests/Unit/Controllers/Admin/UserVoucherControllerTest.php create mode 100644 tests/Unit/Helpers/UserVouchersTest.php diff --git a/config/routes.php b/config/routes.php index 2773d04bc..81f76e04d 100644 --- a/config/routes.php +++ b/config/routes.php @@ -301,6 +301,15 @@ function (RouteCollector $route): void { ); } ); + + // Vouchers + $route->addGroup( + '/voucher', + function (RouteCollector $route): void { + $route->get('', 'Admin\\UserVoucherController@editVoucher'); + $route->post('', 'Admin\\UserVoucherController@saveVoucher'); + } + ); } ); diff --git a/includes/controller/users_controller.php b/includes/controller/users_controller.php index a9f4e72d2..bedc9a5e2 100644 --- a/includes/controller/users_controller.php +++ b/includes/controller/users_controller.php @@ -34,7 +34,6 @@ function users_controller() return match ($action) { 'view' => user_controller(), 'delete' => user_delete_controller(), - 'edit_vouchers' => user_edit_vouchers_controller(), 'list' => users_list_controller(), default => users_list_controller(), }; @@ -141,71 +140,6 @@ function user_link($userId) return url('/users', ['action' => 'view', 'user_id' => $userId]); } -/** - * @return array - */ -function user_edit_vouchers_controller() -{ - $user = auth()->user(); - $request = request(); - - if ($request->has('user_id')) { - $user_source = User::find($request->input('user_id')); - } else { - $user_source = $user; - } - - if ( - (!auth()->can('admin_user') && !auth()->can('voucher.edit')) - || !config('enable_voucher') - ) { - throw_redirect(url('/')); - } - - if ($request->hasPostData('submit')) { - $valid = true; - - $vouchers = ''; - if ( - $request->has('vouchers') - && test_request_int('vouchers') - && trim($request->input('vouchers')) >= 0 - ) { - $vouchers = trim($request->input('vouchers')); - } else { - $valid = false; - error(__('Please enter a valid number of vouchers.')); - } - - if ($valid) { - $user_source->state->got_voucher = $vouchers; - $user_source->state->save(); - - engelsystem_log(User_Nick_render($user_source, true) . ': ' . sprintf( - 'Got %s vouchers', - $user_source->state->got_voucher - )); - - if (in_array('application/json', $request->getAcceptableContentTypes())) { - // This was an async request, send a JSON response. - json_output([ - 'issued' => $user_source->state->got_voucher, - 'eligible' => $user_source->state->got_voucher + User_get_eligable_voucher_count($user_source), - 'total' => (int) State::query()->sum('got_voucher'), - ]); - } - - success(__('Saved the number of vouchers.')); - throw_redirect(user_link($user_source->id)); - } - } - - return [ - sprintf(__('%s\'s vouchers'), htmlspecialchars($user_source->displayName)), - User_edit_vouchers_view($user_source), - ]; -} - /** * @return array */ diff --git a/includes/includes.php b/includes/includes.php index d68579cf6..da25d0307 100644 --- a/includes/includes.php +++ b/includes/includes.php @@ -17,7 +17,6 @@ __DIR__ . '/../includes/model/ShiftsFilter.php', __DIR__ . '/../includes/model/ShiftSignupState.php', __DIR__ . '/../includes/model/Stats.php', - __DIR__ . '/../includes/model/User_model.php', __DIR__ . '/../includes/view/AngelTypes_view.php', __DIR__ . '/../includes/view/PublicDashboard_view.php', diff --git a/includes/model/User_model.php b/includes/model/User_model.php deleted file mode 100644 index 52ebc1b0d..000000000 --- a/includes/model/User_model.php +++ /dev/null @@ -1,53 +0,0 @@ -setTime(0, 0) - : null; - - $shiftEntries = ShiftEntries_finished_by_user($user, $start); - $worklog = $user->worklogs() - ->whereDate('worked_at', '>=', $start ?: 0) - ->with(['user', 'creator']) - ->get(); - $shifts_done = - count($shiftEntries) - + $worklog->count(); - - $shiftsTime = 0; - foreach ($shiftEntries as $shiftEntry) { - $shiftsTime += ($shiftEntry->shift->end->timestamp - $shiftEntry->shift->start->timestamp) / 60 / 60; - } - foreach ($worklog as $entry) { - $shiftsTime += $entry->hours; - } - - $vouchers = $voucher_settings['initial_vouchers']; - if ($voucher_settings['shifts_per_voucher']) { - $vouchers += $shifts_done / $voucher_settings['shifts_per_voucher']; - } - if ($voucher_settings['hours_per_voucher']) { - $vouchers += $shiftsTime / $voucher_settings['hours_per_voucher']; - } - - $vouchers -= $user->state->got_voucher; - $vouchers = floor($vouchers); - if ($vouchers < 0) { - return 0; - } - - return $vouchers; -} diff --git a/includes/view/User_view.php b/includes/view/User_view.php index e962c0aa3..3b9da9dfd 100644 --- a/includes/view/User_view.php +++ b/includes/view/User_view.php @@ -2,6 +2,7 @@ use Carbon\Carbon; use Engelsystem\Config\GoodieType; +use Engelsystem\Helpers\UserVouchers; use Engelsystem\Models\AngelType; use Engelsystem\Models\Group; use Engelsystem\Models\Shifts\Shift; @@ -34,36 +35,6 @@ function User_delete_view($user) ]); } -/** - * View for editing the number of given vouchers - * - * @param User $user - * @return string - */ -function User_edit_vouchers_view($user) -{ - $link = button(user_link($user->id), icon('chevron-left'), 'btn-sm', '', __('general.back')); - return page_with_title( - $link . ' ' . sprintf(__('%s\'s vouchers'), User_Nick_render($user)), - [ - msg(), - info(sprintf( - $user->state->force_active && config('enable_force_active') - ? __('Angel can receive another %d vouchers and is FA.') - : __('Angel can receive another %d vouchers.'), - User_get_eligable_voucher_count($user) - ), true), - form( - [ - form_spinner('vouchers', __('Number of vouchers given out'), $user->state->got_voucher), - form_submit('submit', icon('save') . __('form.save')), - ], - url('/users', ['action' => 'edit_vouchers', 'user_id' => $user->id]) - ), - ] - ); -} - /** * @param User[] $users * @param string $order_by @@ -125,7 +96,7 @@ class="btn btn-sm btn-secondary ms-2 js-only" $voucher_template, [ '{issued}' => $user->state->got_voucher, - '{eligible}' => $user->state->got_voucher + User_get_eligable_voucher_count($user), + '{eligible}' => $user->state->got_voucher + UserVouchers::eligibleVoucherCount($user), '{user}' => $user->id, '{amount}' => $user->state->got_voucher + 1, ] @@ -196,7 +167,7 @@ class="btn btn-sm btn-secondary ms-2 js-only" } $user_table_headers['arrived'] = Users_table_header_link('arrived', __('Arrived'), $order_by); if (config('enable_voucher')) { - $user_table_headers['got_voucher'] = Users_table_header_link('got_voucher', __('Vouchers'), $order_by); + $user_table_headers['got_voucher'] = Users_table_header_link('got_voucher', __('voucher.vouchers'), $order_by); } $user_table_headers['freeloads'] = Users_table_header_link('freeloads', __('Freeloads'), $order_by); $user_table_headers['active'] = Users_table_header_link('active', __('user.active'), $order_by); @@ -718,11 +689,8 @@ function User_view( ], url('/admin-arrive'), 'float:left') : '', ($admin_user_privilege || $auth->can('voucher.edit')) && config('enable_voucher') ? button( - url( - '/users', - ['action' => 'edit_vouchers', 'user_id' => $user_source->id] - ), - icon('valentine') . __('Vouchers') + url('/admin/user/' . $user_source->id . '/voucher'), + icon('valentine') . __('voucher.vouchers') ) : '', ( @@ -912,16 +880,16 @@ function User_view_state_admin($freeloader, $user_source) if (config('enable_voucher')) { $voucherCount = $user_source->state->got_voucher; - $availableCount = $voucherCount + User_get_eligable_voucher_count($user_source); + $availableCount = $voucherCount + UserVouchers::eligibleVoucherCount($user_source); $availableCount = max($voucherCount, $availableCount); if ($user_source->state->got_voucher > 0) { $state[] = '' . icon('valentine') - . __('Got %s of %s vouchers', [$voucherCount, $availableCount]) + . __('user.state.vouchers', [$voucherCount, $availableCount]) . ''; } else { $state[] = '' - . __('Got no vouchers') + . __('user.state.vouchers.none') . ($availableCount ? ' (' . __('out of %s', [$availableCount]) . ')' : '') . ''; } diff --git a/resources/assets/js/voucher.js b/resources/assets/js/voucher.js index 7c6d0a490..5f157c15e 100644 --- a/resources/assets/js/voucher.js +++ b/resources/assets/js/voucher.js @@ -27,10 +27,9 @@ const sendEditVoucherRequest = async (userId, amount) => { const csrfToken = getCSRFToken(); const data = new FormData(); - data.append('submit', 'true'); - data.append('vouchers', amount); + data.append('got_voucher', amount); - const response = await fetch(`/users?action=edit_vouchers&user_id=${userId}`, { + const response = await fetch(`/admin/user/${userId}/voucher`, { method: 'POST', headers: { Accept: 'application/json', diff --git a/resources/lang/de_DE/additional.po b/resources/lang/de_DE/additional.po index 0484f3c39..a09b5f296 100644 --- a/resources/lang/de_DE/additional.po +++ b/resources/lang/de_DE/additional.po @@ -278,6 +278,9 @@ msgstr "Arbeitseinsatz erfolgreich bearbeitet." msgid "worklog.delete.success" msgstr "Arbeitseinsatz erfolgreich gelöscht." +msgid "voucher.save.success" +msgstr "Anzahl der Gutscheine gespeichert." + msgid "location.edit.success" msgstr "Ort erfolgreich bearbeitet." diff --git a/resources/lang/de_DE/default.po b/resources/lang/de_DE/default.po index 51ee6f362..af2c8aed3 100644 --- a/resources/lang/de_DE/default.po +++ b/resources/lang/de_DE/default.po @@ -386,12 +386,6 @@ msgstr "Engel gelöscht." msgid "Delete %s" msgstr "%s löschen" -msgid "Please enter a valid number of vouchers." -msgstr "Bitte gib eine korrekte Anzahl von Gutscheinen ein." - -msgid "Saved the number of vouchers." -msgstr "Anzahl der Gutscheine gespeichert." - msgid "User not found." msgstr "Benutzer nicht gefunden." @@ -1091,15 +1085,27 @@ msgstr "" msgid "Your password" msgstr "Dein Passwort" -msgid "Angel can receive another %d vouchers." +msgid "voucher.vouchers" +msgstr "Gutscheine" + +msgid "voucher.edit" +msgstr "Gutscheine bearbeiten" + +msgid "voucher.eligible" msgstr "Engel kann noch %d Gutscheine bekommen." -msgid "Angel can receive another %d vouchers and is FA." +msgid "voucher.eligible.fa" msgstr "Engel kann noch %d Gutscheine bekommen und ist FA." -msgid "Number of vouchers given out" +msgid "voucher.count" msgstr "Anzahl Gutscheine bekommen" +msgid "user.state.vouchers" +msgstr "%s von %s Gutscheinen bekommen" + +msgid "user.state.vouchers.none" +msgstr "Keine Gutscheine bekommen" + msgid "Freeloads" msgstr "Schwänzereien" @@ -1148,9 +1154,6 @@ msgstr "Du hast genug gemacht." msgid "%s has done enough." msgstr "%s hat genug gemacht." -msgid "Vouchers" -msgstr "Gutscheine" - msgid "iCal Export" msgstr "iCal Export" @@ -1182,12 +1185,6 @@ msgstr "Angekommen am %s" msgid "Not arrived (Planned: %s)" msgstr "Nicht angekommen (Geplant: %s)" -msgid "Got %s of %s vouchers" -msgstr "%s von %s Gutscheinen bekommen" - -msgid "Got no vouchers" -msgstr "Keine Gutscheine bekommen" - msgid "out of %s" msgstr "von %s" diff --git a/resources/lang/en_US/additional.po b/resources/lang/en_US/additional.po index 0a22cf0c9..c19597a23 100644 --- a/resources/lang/en_US/additional.po +++ b/resources/lang/en_US/additional.po @@ -277,6 +277,9 @@ msgstr "Work log successfully updated." msgid "worklog.delete.success" msgstr "Work log successfully deleted." +msgid "voucher.save.success" +msgstr "Saved the number of vouchers." + msgid "location.edit.success" msgstr "Location edited successfully." diff --git a/resources/lang/en_US/default.po b/resources/lang/en_US/default.po index aac3f4e75..5ea0139d4 100644 --- a/resources/lang/en_US/default.po +++ b/resources/lang/en_US/default.po @@ -722,6 +722,27 @@ msgstr "Delete work log" msgid "worklog.delete.info" msgstr "Do you really want to delete the work log for %s?" +msgid "voucher.vouchers" +msgstr "Vouchers" + +msgid "voucher.edit" +msgstr "Edit vouchers" + +msgid "voucher.eligible" +msgstr "Angel can receive another %d vouchers." + +msgid "voucher.eligible.fa" +msgstr "Angel can receive another %d vouchers and is FA." + +msgid "voucher.count" +msgstr "Number of vouchers handed out" + +msgid "user.state.vouchers" +msgstr "Got %s of %s vouchers" + +msgid "user.state.vouchers.none" +msgstr "Got no vouchers" + msgid "angeltypes.angeltypes" msgstr "Angel types" diff --git a/resources/views/admin/user/edit-voucher.twig b/resources/views/admin/user/edit-voucher.twig new file mode 100644 index 000000000..9e437738e --- /dev/null +++ b/resources/views/admin/user/edit-voucher.twig @@ -0,0 +1,35 @@ +{% extends "layouts/app.twig" %} +{% import 'macros/base.twig' as m %} +{% import 'macros/form.twig' as f %} + +{% block title %}{{ __('voucher.edit') }}{% endblock %} + +{% block content %} +
    +

    + {{ m.back(url('/users', {action: 'view', user_id: userdata.id})) }} + {{ block('title') }}: {{ m.user(userdata) }} +

    + + {% include 'layouts/parts/messages.twig' %} + {{ m.alert( + forceActive + ? __('voucher.eligible.fa', [eligibleVoucherCount]) + : __('voucher.eligible', [eligibleVoucherCount]) + ) }} + + + {{ csrf() }} +
    + {{ f.input('got_voucher', __('voucher.count'), { + 'type': 'number', + 'value': gotVoucher, + 'required': true, + 'step': '1', + 'min': 0, + }) }} +
    + {{ f.submit(__('form.save'), {'icon_left': 'save'}) }} + +
    +{% endblock %} diff --git a/src/Controllers/Admin/UserVoucherController.php b/src/Controllers/Admin/UserVoucherController.php new file mode 100644 index 000000000..1e25ebabb --- /dev/null +++ b/src/Controllers/Admin/UserVoucherController.php @@ -0,0 +1,106 @@ + */ + protected array $permissions = [ + 'voucher.edit', + ]; + + public function __construct( + protected Authenticator $auth, + protected Config $config, + protected LoggerInterface $log, + protected Worklog $worklog, + protected Redirector $redirect, + protected Response $response, + protected User $user + ) { + } + + private function checkActive(): void + { + if (!config('enable_voucher')) { + throw new HttpNotFound(); + } + } + + public function editVoucher(Request $request): Response + { + $this->checkActive(); + $userId = (int) $request->getAttribute('user_id'); + + /** @var User $user */ + $user = $this->user->findOrFail($userId); + + return $this->response->withView( + 'admin/user/edit-voucher.twig', + [ + 'userdata' => $user, + 'gotVoucher' => $user->state->got_voucher ?? 0, + 'forceActive' => $user->state->force_active && config('enable_force_active'), + 'eligibleVoucherCount' => UserVouchers::eligibleVoucherCount($user), + ] + ); + } + + public function saveVoucher(Request $request): Response + { + $this->checkActive(); + $userId = (int) $request->getAttribute('user_id'); + /** @var User $user */ + $user = $this->user->findOrFail($userId); + + $data = $this->validate($request, [ + 'got_voucher' => 'int|min:0', + ]); + + $user->state->got_voucher = (int) $data['got_voucher']; + $user->state->save(); + + $this->log->info( + '{name} ({id}) got {got_voucher} vouchers.', + [ + 'name' => $user->name, + 'id' => $user->id, + 'got_voucher' => $user->state->got_voucher, + ] + ); + + if (in_array('application/json', $request->getAcceptableContentTypes())) { + // This was an async request, send a JSON response. + return $this->response + ->withHeader('content-type', 'application/json') + ->withContent(json_encode([ + 'issued' => $user->state->got_voucher, + 'eligible' => $user->state->got_voucher + UserVouchers::eligibleVoucherCount($user), + 'total' => (int) State::query()->sum('got_voucher'), + ])); + } + + $this->addNotification('voucher.save.success'); + + return $this->redirect->to('/users?action=view&user_id=' . $user->id); + // TODO Once User_view.php gets removed, change this to withView + getNotifications + } +} diff --git a/src/Helpers/UserVouchers.php b/src/Helpers/UserVouchers.php new file mode 100644 index 000000000..c9fe7b6b5 --- /dev/null +++ b/src/Helpers/UserVouchers.php @@ -0,0 +1,60 @@ +setTime(0, 0) + : null; + + $shiftEntries = $user->shiftEntries() + ->join('shifts', 'shift_entries.shift_id', '=', 'shifts.id') + ->where('shifts.end', '<', Carbon::now()) + ->where('shifts.start', '>=', $start ?: 0) + ->whereNull('freeloaded_by') + ->get(); + $worklogs = $user->worklogs() + ->where('worked_at', '>=', $start ?: 0) + ->where('worked_at', '<=', Carbon::now()) + ->get(); + $shiftsCount = + $shiftEntries->count() + + $worklogs->count(); + + $shiftsTime = 0; + foreach ($shiftEntries as $shiftEntry) { + $shiftsTime += $shiftEntry->shift->start->diffInHours($shiftEntry->shift->end); + } + foreach ($worklogs as $worklog) { + $shiftsTime += $worklog->hours; + } + + $vouchers = $voucherSettings['initial_vouchers']; + if ($voucherSettings['shifts_per_voucher']) { + $vouchers += $shiftsCount / $voucherSettings['shifts_per_voucher']; + } + if ($voucherSettings['hours_per_voucher']) { + $vouchers += $shiftsTime / $voucherSettings['hours_per_voucher']; + } + + $vouchers -= $user->state->got_voucher; + $vouchers = floor($vouchers); + if ($vouchers <= 0) { + return 0; + } + + return (int) $vouchers; + } +} diff --git a/tests/Unit/Controllers/Admin/UserVoucherControllerTest.php b/tests/Unit/Controllers/Admin/UserVoucherControllerTest.php new file mode 100644 index 000000000..2ceae6ed9 --- /dev/null +++ b/tests/Unit/Controllers/Admin/UserVoucherControllerTest.php @@ -0,0 +1,187 @@ +config->set('enable_voucher', false); + $request = $this->request; + $this->expectException(HttpNotFound::class); + $this->controller->editVoucher($request); + } + + /** + * @covers \Engelsystem\Controllers\Admin\UserVoucherController::editVoucher + */ + public function testShowEditVoucherWithUnknownUserIdThrows(): void + { + $request = $this->request->withAttribute('user_id', 1234); + $this->expectException(ModelNotFoundException::class); + $this->controller->editVoucher($request); + } + + /** + * @covers \Engelsystem\Controllers\Admin\UserVoucherController::editVoucher + * @covers \Engelsystem\Controllers\Admin\UserVoucherController::__construct + * + * @uses \Engelsystem\Helpers\UserVouchers::eligibleVoucherCount + */ + public function testShowEditVoucher(): void + { + $request = $this->request->withAttribute('user_id', $this->user->id); + $this->response->expects($this->once()) + ->method('withView') + ->willReturnCallback(function (string $view, array $data) { + $this->assertEquals('admin/user/edit-voucher.twig', $view); + $this->assertEquals($this->user->id, $data['userdata']->id); + $this->assertEquals($this->user->state->got_voucher, $data['gotVoucher']); + $this->assertEquals( + $this->user->state->force_active && config('enable_force_active'), + $data['forceActive'] + ); + $this->assertEquals(UserVouchers::eligibleVoucherCount($this->user), $data['eligibleVoucherCount']); + return $this->response; + }); + $this->controller->editVoucher($request); + } + + /** + * @covers \Engelsystem\Controllers\Admin\UserVoucherController::saveVoucher + */ + public function testSaveVoucherWithUnknownUserIdThrows(): void + { + $request = $this->request->withAttribute('user_id', 1234)->withParsedBody([]); + $this->expectException(ModelNotFoundException::class); + $this->controller->saveVoucher($request); + } + + /** + * @covers \Engelsystem\Controllers\Admin\UserVoucherController::saveVoucher + * + * @dataProvider invalidSaveVoucherParams + */ + public function testSaveVoucherWithInvalidParamsThrows(array $body): void + { + $request = $this->request->withAttribute('user_id', $this->user->id)->withParsedBody($body); + $this->expectException(ValidationException::class); + $this->controller->saveVoucher($request); + } + + /** + * @covers \Engelsystem\Controllers\Admin\UserVoucherController::saveVoucher + */ + public function testSaveVoucher(): void + { + $got_voucher = 4; + $body = ['got_voucher' => $got_voucher]; + $request = $this->request->withAttribute('user_id', $this->user->id)->withParsedBody($body); + $this->setExpects($this->auth, 'user', null, $this->user, $this->any()); + $this->redirect->expects($this->once()) + ->method('to') + ->with('/users?action=view&user_id=' . $this->user->id) + ->willReturn($this->response); + + $this->controller->saveVoucher($request); + + $this->assertHasNotification('voucher.save.success'); + $this->assertTrue($this->log->hasInfoThatContains('vouchers.')); + + $this->assertEquals(4, $this->user->state->got_voucher); + } + + /** + * @covers \Engelsystem\Controllers\Admin\UserVoucherController::saveVoucher + */ + public function testSaveVoucherJsonResponce(): void + { + $got_voucher = 4; + $body = ['got_voucher' => $got_voucher]; + $response_body = [ + 'issued' => $got_voucher, + 'eligible' => $got_voucher + UserVouchers::eligibleVoucherCount($this->user), + 'total' => 4, + ]; + $request = $this->request + ->withAttribute('user_id', $this->user->id) + ->withParsedBody($body) + ->withHeader('accept', 'application/json'); + + $this->setExpects($this->auth, 'user', null, $this->user, $this->any()); + $this->setExpects($this->response, 'withHeader', ['content-type', 'application/json'], $this->response); + $this->setExpects($this->response, 'withContent', [json_encode($response_body)], $this->response); + + $this->controller->saveVoucher($request); + } + + /** + * @return array[] + */ + public function invalidSaveVoucherParams(): array + { + return [ + // missing got_voucher + [[]], + // got_voucher not int + [['got_voucher' => 3.14]], + ]; + } + + /** + * Setup environment + */ + public function setUp(): void + { + parent::setUp(); + $this->config->set('enable_voucher', true); + $this->config->set('voucher_settings', [ + 'initial_vouchers' => 0, + 'shifts_per_voucher' => 0, + 'hours_per_voucher' => 2, + // 'Y-m-d' formatted + 'voucher_start' => null, + ]); + + $this->app->bind('http.urlGenerator', UrlGenerator::class); + + $this->auth = $this->createMock(Authenticator::class); + $this->app->instance(Authenticator::class, $this->auth); + + $this->redirect = $this->createMock(Redirector::class); + $this->app->instance(Redirector::class, $this->redirect); + + $this->user = User::factory()->create(); + $this->setExpects($this->auth, 'user', null, $this->user, $this->any()); + + $this->controller = $this->app->make(UserVoucherController::class); + $this->controller->setValidator(new Validator()); + } +} diff --git a/tests/Unit/Helpers/UserVouchersTest.php b/tests/Unit/Helpers/UserVouchersTest.php new file mode 100644 index 000000000..91820b0da --- /dev/null +++ b/tests/Unit/Helpers/UserVouchersTest.php @@ -0,0 +1,155 @@ +config->set('enable_voucher', true); + $this->config->set('voucher_settings', [ + 'initial_vouchers' => 0, + 'shifts_per_voucher' => 0, + 'hours_per_voucher' => 2, + 'voucher_start' => null, + ]); + + $this->user = User::factory()->create(); + $user2 = User::factory()->create(); + + // user + // start more than 3 days ago and ended, 2 hours long + $shift1 = Shift::factory()->create([ + 'start' => Carbon::now()->subDays(3)->subHour(), + 'end' => Carbon::now()->subDays(3)->addHour(), + ]); + ShiftEntry::factory()->create([ + 'shift_id' => $shift1->id, + 'user_id' => $this->user->id, + 'freeloaded_by' => null, + ]); + + // started less than 1 day ago and ended, 2 hours long + $shift2 = Shift::factory()->create([ + 'start' => Carbon::now()->subHours(3), + 'end' => Carbon::now()->subHour(), + ]); + ShiftEntry::factory()->create([ + 'shift_id' => $shift2->id, + 'user_id' => $this->user->id, + 'freeloaded_by' => null, + ]); + // entry freeloaded + ShiftEntry::factory()->create([ + 'shift_id' => $shift1->id, + 'user_id' => $this->user->id, + 'freeloaded_by' => $user2->id, + ]); + + // started less than 1 day ago and ended, 4 hours long + $shift3 = Shift::factory()->create([ + 'start' => Carbon::now()->subHours(5), + 'end' => Carbon::now()->subHour(), + ]); + ShiftEntry::factory()->create([ + 'shift_id' => $shift3->id, + 'user_id' => $this->user->id, + 'freeloaded_by' => null, + ]); + + // shifts still running, 2 hours long + $shift4 = Shift::factory()->create([ + 'start' => Carbon::now()->subHour(), + 'end' => Carbon::now()->addHour(), + ]); + ShiftEntry::factory()->create([ + 'shift_id' => $shift4->id, + 'user_id' => $this->user->id, + 'freeloaded_by' => null, + ]); + + // worklog 3 days ago, 2 hours long + Worklog::factory()->create([ + 'user_id' => $this->user->id, + 'creator_id' => $user2->id, + 'hours' => 4, + 'worked_at' => Carbon::today()->subDays(3), + ]); + // worklog today, 4 hours long + Worklog::factory()->create([ + 'user_id' => $this->user->id, + 'creator_id' => $user2->id, + 'hours' => 4, 'worked_at' => Carbon::today(), + ]); + // worklog tomorrow, 2 hours long + Worklog::factory()->create([ + 'user_id' => $this->user->id, + 'creator_id' => $user2->id, + 'hours' => 4, + 'worked_at' => Carbon::tomorrow(), + ]); + } + + /** + * @return Array + */ + public function provideTestData(): array + { + return [ + // settings, userId, got_voucher, expected vouchers + 'initial_vouchers 2, user, got voucher 2' => [['initial_vouchers' => 2], 1, 2, 8], + 'shifts_per_voucher 1, hours_per_voucher 0, voucher_start 2 days ago, user, got voucher 0' => [[ + 'shifts_per_voucher' => 1, + 'hours_per_voucher' => 0, + 'voucher_start' => Carbon::now()->subDays(2)->format('Y-m-d'), + ], 1, 0, 3], + 'default settings, user2, got voucher 2' => [null, 2, 2, 0], + ]; + } + + /** + * @dataProvider provideTestData + * @covers \Engelsystem\Helpers\UserVouchers::eligibleVoucherCount + */ + public function testEligibleVoucherCount( + array | null $voucherSettings, + int $userId, + int $gotVoucher, + int $expected + ): void { + if ($voucherSettings) { + $this->config->set( + 'voucher_settings', + array_merge($this->config->get('voucher_settings'), $voucherSettings) + ); + } + + $user = User::find($userId); + $user->state->got_voucher = $gotVoucher; + $user->state->save(); + + $this->assertEquals($expected, UserVouchers::eligibleVoucherCount($user)); + } + + /** + * @covers \Engelsystem\Helpers\UserVouchers::eligibleVoucherCount + */ + public function testUserVouchersWithVouchersDisabled(): void + { + $this->config->set('enable_voucher', false); + $this->assertEquals(0, UserVouchers::eligibleVoucherCount($this->user)); + } +} From addfdca52619346dc3aa7409433108213036bccb Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sun, 2 Feb 2025 12:24:14 +0100 Subject: [PATCH 058/157] Show user info on goodie page --- ...9_000000_goodie_manager_show_user_info.php | 49 +++++++++++++++++++ resources/views/admin/user/edit-goodie.twig | 2 +- resources/views/macros/base.twig | 13 +++++ 3 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 db/migrations/2024_12_29_000000_goodie_manager_show_user_info.php diff --git a/db/migrations/2024_12_29_000000_goodie_manager_show_user_info.php b/db/migrations/2024_12_29_000000_goodie_manager_show_user_info.php new file mode 100644 index 000000000..89df682fc --- /dev/null +++ b/db/migrations/2024_12_29_000000_goodie_manager_show_user_info.php @@ -0,0 +1,49 @@ +schema->getConnection(); + $privilege = $this->getPrivilege($this->privilegeName); + + $db->table('group_privileges') + ->insertOrIgnore([ + ['group_id' => $this->groupId, 'privilege_id' => $privilege->id], + ]); + } + + /** + * Reverse the migration + */ + public function down(): void + { + $db = $this->schema->getConnection(); + $privilege = $this->getPrivilege($this->privilegeName); + + $db->table('group_privileges') + ->where('group_id', $this->groupId) + ->where('privilege_id', $privilege->id) + ->delete(); + } + + protected function getPrivilege(string $name): mixed + { + return $this->schema->getConnection() + ->table('privileges') + ->where('name', $name) + ->first(); + } +} diff --git a/resources/views/admin/user/edit-goodie.twig b/resources/views/admin/user/edit-goodie.twig index 3a4a0d60a..e3801f53e 100644 --- a/resources/views/admin/user/edit-goodie.twig +++ b/resources/views/admin/user/edit-goodie.twig @@ -10,7 +10,7 @@

    {{ m.back(url('/admin-active')) }} - {{ block('title') }}: {{ m.user(userdata) }} + {{ block('title') }}: {{ m.user(userdata, {'pronoun': true}) }} {{ m.user_info(userdata) }}

    {% include 'layouts/parts/messages.twig' %} diff --git a/resources/views/macros/base.twig b/resources/views/macros/base.twig index a89129eeb..51c178b50 100644 --- a/resources/views/macros/base.twig +++ b/resources/views/macros/base.twig @@ -34,6 +34,19 @@ {% endmacro %} +{% macro user_info(user, opt) %} + {% if user.state.user_info and can('admin_arrive') %} + + {{ _self.icon('info-circle-fill', 'info') }} + + {% endif %} +{% endmacro %} + {% macro button(label, url, opt) %} ' . "\n"; $html .= $user_goodie_edit ? html_options('eTshirt', $options, $user_source->state->got_goodie) @@ -364,7 +360,7 @@ function admin_user() . ', arrived: ' . $user_source->state->arrived . ', active: ' . $user_source->state->active . ', force-active: ' . $user_source->state->force_active - . ($goodie_tshirt ? ', t-shirt: ' : ', goodie: ' . $user_source->state->got_goodie) + . ', goodie: ' . $user_source->state->got_goodie . ($user_info_edit ? ', user-info: ' . $user_source->state->user_info : '') ); $html .= success(__('Changes were saved.') . "\n", true); diff --git a/includes/view/AngelTypes_view.php b/includes/view/AngelTypes_view.php index d5c03ef13..6af5e665b 100644 --- a/includes/view/AngelTypes_view.php +++ b/includes/view/AngelTypes_view.php @@ -358,12 +358,8 @@ function AngelType_view_members(AngelType $angeltype, $members, $admin_user_ange ? ($member->personalData->shirt_size ?: '-') : ''; $got_goodie_button_title = $member->state->got_goodie - ? ($goodie_tshirt - ? __('Remove T-shirt') - : __('Remove goodie')) - : ($goodie_tshirt - ? __('user.got_shirt') - : __('user.got_goodie')); + ? __('Remove goodie') + : __('user.got_goodie'); $goodie_actions[] = ($shirtSize !== '-') ? form( [ form_submit( @@ -390,9 +386,7 @@ function AngelType_view_members(AngelType $angeltype, $members, $admin_user_ange icon('pencil'), 'btn-secondary btn-sm', false, - $goodie_tshirt - ? __('user.edit.shirt') - : __('user.edit.goodie'), + __('user.edit.goodie'), ); if ($goodie_tshirt) { $member['shirt_size'] = isset($tshirt_sizes[$shirtSize]) ? $tshirt_sizes[$shirtSize] : '-'; @@ -560,7 +554,6 @@ function AngelType_view_table_headers(AngelType $angeltype, $supporter, $admin_a ) { $headers['goodie_actions'] = __('Goodie actions'); if ($goodie_tshirt) { - $headers['goodie_actions'] = __('T-shirt actions'); $headers['shirt_size'] = __('user.shirt_size'); } } diff --git a/includes/view/ShiftEntry_view.php b/includes/view/ShiftEntry_view.php index acaad4bff..40c0946ba 100644 --- a/includes/view/ShiftEntry_view.php +++ b/includes/view/ShiftEntry_view.php @@ -212,15 +212,12 @@ function ShiftEntry_edit_view( $freeload_form = []; $goodie = GoodieType::from(config('goodie_type')); $goodie_enabled = $goodie !== GoodieType::None; - $goodie_tshirt = $goodie === GoodieType::Tshirt; if ($user_admin_shifts || $angeltype_supporter) { if (!$goodie_enabled) { $freeload_info = __('freeload.freeloaded.info', [config('max_freeloadable_shifts')]); } else { - $freeload_info = __('freeload.freeloaded.info.goodie', [($goodie_tshirt - ? __('T-shirt score') - : __('Goodie score')), + $freeload_info = __('freeload.freeloaded.info.goodie', [__('Goodie score'), config('max_freeloadable_shifts')]); } $freeload_form = [ diff --git a/includes/view/Shifts_view.php b/includes/view/Shifts_view.php index 98d5cc074..704bef14d 100644 --- a/includes/view/Shifts_view.php +++ b/includes/view/Shifts_view.php @@ -159,7 +159,6 @@ function Shift_view( $nightShiftsConfig = config('night_shifts'); $goodie = GoodieType::from(config('goodie_type')); $goodie_enabled = $goodie !== GoodieType::None; - $goodie_tshirt = $goodie === GoodieType::Tshirt; $supportsAngelTypes = auth()->user() ->userAngelTypes() @@ -264,11 +263,11 @@ function Shift_view( $night_shift_hint = ''; if ($shift->isNightShift() && $goodie_enabled) { $night_shift_hint = ' '; } $link = button(url('/user-shifts'), icon('chevron-left'), 'btn-sm', '', __('general.back')); diff --git a/includes/view/User_view.php b/includes/view/User_view.php index 3b9da9dfd..fac23c2f6 100644 --- a/includes/view/User_view.php +++ b/includes/view/User_view.php @@ -175,11 +175,9 @@ class="btn btn-sm btn-secondary ms-2 js-only" $user_table_headers['force_active'] = Users_table_header_link('force_active', __('Forced'), $order_by); } if ($goodie_enabled) { + $user_table_headers['got_goodie'] = Users_table_header_link('got_goodie', __('Goodie'), $order_by); if ($goodie_tshirt) { - $user_table_headers['got_goodie'] = Users_table_header_link('got_goodie', __('T-shirt'), $order_by); $user_table_headers['shirt_size'] = Users_table_header_link('shirt_size', __('Size'), $order_by); - } else { - $user_table_headers['got_goodie'] = Users_table_header_link('got_goodie', __('Goodie'), $order_by); } } $user_table_headers['arrival_date'] = Users_table_header_link( @@ -327,7 +325,7 @@ function User_view_myshift(Shift $shift, $user_source, $its_me, $supporter) $nightShiftsConfig = config('night_shifts'); $goodie = GoodieType::from(config('goodie_type')); $goodie_enabled = $goodie !== GoodieType::None; - $goodie_tshirt = $goodie === GoodieType::Tshirt; + $supporter = auth()->user()->isAngelTypeSupporter(AngelType::findOrFail($shift->angel_type_id)); $shift_info = '' . htmlspecialchars($shift->shiftType->name) . ''; if ($shift->title) { @@ -340,11 +338,10 @@ function User_view_myshift(Shift $shift, $user_source, $its_me, $supporter) $night_shift = ''; if ($shift->isNightShift() && $goodie_enabled) { $night_shift = ' '; } @@ -386,9 +383,7 @@ function User_view_myshift(Shift $shift, $user_source, $its_me, $supporter) if (!$goodie_enabled) { $freeload_info = __('freeload.info'); } else { - $freeload_info = __('freeload.info.goodie', [($goodie_tshirt - ? __('T-shirt score') - : __('Goodie score'))]); + $freeload_info = __('freeload.info.goodie', [__('Goodie score')]); } $myshift['hints'] .= ' ' . __('user.active') . ''; } if ($user_source->state->got_goodie && $goodie_enabled) { - $state[] = '' . ($goodie_tshirt ? __('T-shirt') : __('Goodie')) . ''; + $state[] = '' . __('Goodie') . ''; } } else { $arrivalDate = $user_source->personalData->planned_arrival_date; diff --git a/resources/lang/de_DE/default.po b/resources/lang/de_DE/default.po index af2c8aed3..7b5078e99 100644 --- a/resources/lang/de_DE/default.po +++ b/resources/lang/de_DE/default.po @@ -452,12 +452,6 @@ msgstr "Engel nicht gefunden." msgid "Angel has been marked as not active." msgstr "Engel wurde als nicht aktiv markiert." -msgid "Angel has got a T-shirt." -msgstr "Engel hat ein T-Shirt bekommen." - -msgid "Angel has got no T-shirt." -msgstr "Engel hat kein T-Shirt bekommen." - msgid "Angel has no valid T-shirt size. T-shirt was not set." msgstr "Engel hat keine valide T-Shirt Größe. T-Shirt wurde nicht gespeichert." @@ -470,15 +464,9 @@ msgstr "Setze aktiv" msgid "Remove active" msgstr "Entferne aktiv" -msgid "Remove T-shirt" -msgstr "Entferne T-Shirt" - msgid "Goodie actions" msgstr "Goodie Aktionen" -msgid "T-shirt actions" -msgstr "T-Shirt Aktionen" - msgid "Sum" msgstr "Summe" @@ -512,12 +500,6 @@ msgstr "Aktiv" msgid "Forced" msgstr "Erzwungen" -msgid "T-shirt" -msgstr "T-Shirt" - -msgid "T-shirt statistic" -msgstr "T-Shirt Statistik" - msgid "Given T-shirts" msgstr "Ausgegebene T-Shirts" @@ -1130,9 +1112,6 @@ msgstr "Austragen" msgid "Sum:" msgstr "Summe:" -msgid "T-shirt score" -msgstr "T-Shirt Score" - msgid "Work log entry" msgstr "Arbeitseinsatz" @@ -1160,8 +1139,8 @@ msgstr "iCal Export" msgid "JSON Export" msgstr "JSON Export" -msgid "Night shifts between %d and %d am are multiplied by %d for the %s score." -msgstr "Nachtschichten zwischen %d und %d Uhr werden für den %4$s Score mit %3$d multipliziert." +msgid "Night shifts between %d and %d am are multiplied by %d for the goodie score." +msgstr "Nachtschichten zwischen %d und %d Uhr werden für den Goodie Score mit %3$d multipliziert." msgid "" "Go to the shifts table to sign yourself up for some " @@ -1232,12 +1211,6 @@ msgstr "" "Hier kannst du den Benutzer Eintrag ändern. Unter dem Punkt 'Angekommen' wird der Engel als anwesend markiert, " "ein Ja bei Aktiv bedeutet, dass der Engel aktiv war." -msgid "" -"If the angel is active, it can claim a T-shirt. If T-shirt is set to 'Yes', the angel already got their T-shirt." -msgstr "" -"Ist der Engel Aktiv, hat er damit Anspruch auf ein T-Shirt. Wenn T-Shirt ein 'Ja' enthält, bedeutet dies, " -"dass der Engel bereits sein T-Shirt erhalten hat." - msgid "Here you can reset the password of this angel:" msgstr "Hier kannst du das Passwort für diesen Engel zurücksetzen:" @@ -1290,10 +1263,10 @@ msgstr "Goodie Statistik" msgid "Remove goodie" msgstr "Goodie entfernen" -msgid "Angel has got a goodie." +msgid "Angel got a goodie." msgstr "Engel hat ein Goodie bekommen." -msgid "Angel has got no goodie." +msgid "Angel got no goodie." msgstr "Engel hat kein Goodie bekommen." msgid "page.404.text" @@ -1571,9 +1544,6 @@ msgstr "" "Diese Zustimmung kann während des Events in den Profil-Einstellungen, sowie, auch nach dem Event, " "per E-Mail an %1$s, widerrufen werden." -msgid "settings.profile.shirt_size" -msgstr "T-Shirt-Größe" - msgid "settings.profile.shirt_size.hint" msgstr "Ein straight-cut T-Shirt hat breite Schultern und einen fast quadratischen Körper. " "Ein fitted-cut (tailliertes) T-Shirt hat eine geschwungene Seitennaht, die an der Taille schmaler " @@ -1844,9 +1814,6 @@ msgstr "Tag erstellen" msgid "tag.delete.title" msgstr "Tag \"%s\" löschen" -msgid "user.edit.shirt" -msgstr "T-Shirt bearbeiten" - msgid "user.edit.goodie" msgstr "Goodie bearbeiten" @@ -1856,17 +1823,11 @@ msgstr "Genug" msgid "user.goodie_score.value" msgstr "%s h" -msgid "user.tshirt_score" -msgstr "T-Shirt Score: %s" - msgid "user.goodie_score" msgstr "Goodie Score: %s" -msgid "form.shirt" -msgstr "T-Shirt" - msgid "user.shirt_size" -msgstr "T-Shirt größe" +msgstr "T-Shirt-Größe" msgid "user.active" msgstr "Aktiv" @@ -1880,9 +1841,6 @@ msgstr "Angekommen" msgid "user.arrive" msgstr "Ankommen" -msgid "user.got_shirt" -msgstr "T-Shirt bekommen" - msgid "user.got_goodie" msgstr "Goodie bekommen" diff --git a/resources/lang/en_US/default.po b/resources/lang/en_US/default.po index 5ea0139d4..3329df6c8 100644 --- a/resources/lang/en_US/default.po +++ b/resources/lang/en_US/default.po @@ -368,9 +368,6 @@ msgid "settings.profile.privacy" msgstr "You can withdraw your approval during the event in your profile settings as well " "as after the event via e-mail to %1$s." -msgid "settings.profile.shirt_size" -msgstr "T-shirt size" - msgid "settings.profile.shirt_size.hint" msgstr "A straight-cut shirt has wide shoulders and a body which is almost square. " "A fitted-cut t-shirt has a curved side seam which comes in at the waist " @@ -647,9 +644,6 @@ msgstr "Create new tag" msgid "tag.delete.title" msgstr "Delete tag \"%s\"" -msgid "user.edit.shirt" -msgstr "Edit T-shirt" - msgid "user.edit.goodie" msgstr "Edit goodie" @@ -659,9 +653,6 @@ msgstr "Enough" msgid "user.goodie_score.value" msgstr "%s h" -msgid "user.tshirt_score" -msgstr "T-shirt score: %s" - msgid "user.goodie_score" msgstr "Goodie score: %s" @@ -680,9 +671,6 @@ msgstr "Arrived" msgid "user.arrive" msgstr "Arrive" -msgid "user.got_shirt" -msgstr "Got T-shirt" - msgid "user.got_goodie" msgstr "Got goodie" diff --git a/resources/views/admin/user/edit-goodie.twig b/resources/views/admin/user/edit-goodie.twig index e3801f53e..69f08b518 100644 --- a/resources/views/admin/user/edit-goodie.twig +++ b/resources/views/admin/user/edit-goodie.twig @@ -3,7 +3,7 @@ {% import 'macros/form.twig' as f %} {% block title %} - {{ is_tshirt ? __('user.edit.shirt') : __('user.edit.goodie') }} + {{ __('user.edit.goodie') }} {%- endblock %} {% block content %} @@ -17,7 +17,7 @@
    {% set score = goodie_score == '~' ? __('user.goodie_score.enough') : __('user.goodie_score.value', [goodie_score])%} -

    {{ is_tshirt ? __('user.tshirt_score', [score]) : __('user.goodie_score', [score]) }}

    +

    {{ __('user.goodie_score', [score]) }}

    @@ -51,7 +51,7 @@ 'checked': userdata.state.active, }) }} - {{ f.switch('got_goodie', is_tshirt ? __('user.got_shirt') : __('user.got_goodie'), { + {{ f.switch('got_goodie', __('user.got_goodie'), { 'checked': userdata.state.got_goodie, }) }}
    diff --git a/resources/views/pages/design.twig b/resources/views/pages/design.twig index 054cd0045..8e948aee2 100644 --- a/resources/views/pages/design.twig +++ b/resources/views/pages/design.twig @@ -214,7 +214,7 @@ occupancy: {{ m.icon('person-fill-slash') }}
    password: {{ m.icon('key-fill') }}
    phone: {{ m.icon('phone') }}
    - T-shirt / goodie: {{ m.icon('gift') }}
    + Goodie: {{ m.icon('gift') }}
    supporter: {{ m.icon('patch-check') }}
    user settings: {{ m.icon('person-fill-gear') }}
    voucher: {{ m.icon('valentine') }}
    diff --git a/resources/views/pages/registration.twig b/resources/views/pages/registration.twig index 7be788c6f..72c64761f 100644 --- a/resources/views/pages/registration.twig +++ b/resources/views/pages/registration.twig @@ -210,7 +210,7 @@
    {{ f.select( 'tshirt_size', - __('settings.profile.shirt_size'), + __('user.shirt_size'), tShirtSizes, { 'default_option': __('form.select_placeholder'), diff --git a/resources/views/pages/settings/profile.twig b/resources/views/pages/settings/profile.twig index 4926cbd67..cbac45c1d 100644 --- a/resources/views/pages/settings/profile.twig +++ b/resources/views/pages/settings/profile.twig @@ -150,7 +150,7 @@
    {% if goodie_tshirt %}
    - {{ f.select('shirt_size', __('settings.profile.shirt_size'), config('tshirt_sizes'), { + {{ f.select('shirt_size', __('user.shirt_size'), config('tshirt_sizes'), { 'selected': userdata.personalData.shirt_size, 'required': isTShirtSizeRequired, 'required_icon': isTShirtSizeRequired, From b98b88b3519c08dea63c03a17732835ca5e82d14 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sun, 2 Feb 2025 14:54:08 +0100 Subject: [PATCH 060/157] Log: Allow filtering by level --- resources/lang/de_DE/default.po | 3 ++ resources/lang/en_US/default.po | 3 ++ resources/views/admin/log.twig | 8 ++++- src/Controllers/Admin/LogsController.php | 34 +++++++++++++++++-- src/Models/LogEntry.php | 17 ++++++---- .../Controllers/Admin/LogsControllerTest.php | 14 ++++++++ tests/Unit/Models/LogEntryTest.php | 4 +-- 7 files changed, 71 insertions(+), 12 deletions(-) diff --git a/resources/lang/de_DE/default.po b/resources/lang/de_DE/default.po index 7b5078e99..575f92217 100644 --- a/resources/lang/de_DE/default.po +++ b/resources/lang/de_DE/default.po @@ -1757,6 +1757,9 @@ msgstr "Login mit %s" msgid "form.connect" msgstr "Verbinden" +msgid "form.all" +msgstr "Alle" + msgid "form.disconnect" msgstr "Trennen" diff --git a/resources/lang/en_US/default.po b/resources/lang/en_US/default.po index 3329df6c8..e1882c0cd 100644 --- a/resources/lang/en_US/default.po +++ b/resources/lang/en_US/default.po @@ -587,6 +587,9 @@ msgstr "Login using %s" msgid "form.connect" msgstr "Connect" +msgid "form.all" +msgstr "All" + msgid "form.disconnect" msgstr "Disconnect" diff --git a/resources/views/admin/log.twig b/resources/views/admin/log.twig index ad0e8d850..d13d812bb 100644 --- a/resources/views/admin/log.twig +++ b/resources/views/admin/log.twig @@ -15,7 +15,7 @@ {{ csrf() }}
    -
    +
    {{ f.input('search', __('form.search'), { 'value': search, }) }} @@ -28,6 +28,12 @@ 'selected': search_user_id, }) }}
    +
    + {{ f.select('level', __('log.level'), levels, { + 'default_option': __('form.all'), + 'selected': level, + }) }} +
    {% endif %}
    diff --git a/src/Controllers/Admin/LogsController.php b/src/Controllers/Admin/LogsController.php index 10f25240c..90e874afe 100644 --- a/src/Controllers/Admin/LogsController.php +++ b/src/Controllers/Admin/LogsController.php @@ -11,6 +11,8 @@ use Engelsystem\Models\LogEntry; use Engelsystem\Models\User\User; use Illuminate\Support\Collection; +use Illuminate\Support\Str; +use Psr\Log\LogLevel; class LogsController extends BaseController { @@ -19,6 +21,17 @@ class LogsController extends BaseController 'admin_log', ]; + protected array $levels = [ + LogLevel::ALERT, + LogLevel::CRITICAL, + LogLevel::DEBUG, + LogLevel::EMERGENCY, + LogLevel::ERROR, + LogLevel::INFO, + LogLevel::NOTICE, + LogLevel::WARNING, + ]; + public function __construct(protected LogEntry $log, protected Response $response, protected Authenticator $auth) { } @@ -27,13 +40,18 @@ public function index(Request $request): Response { $searchUserId = (int) $request->input('search_user_id') ?: null; $search = $request->input('search'); + $level = $request->input('level'); $userId = $this->auth->user()?->id; if ($this->auth->can('logs.all')) { $userId = $searchUserId; } - $entries = $this->log->filter($search, $userId); + if (!in_array($level, $this->levels)) { + $level = null; + } + + $entries = $this->log->filter($search, $userId, $level); /** @var Collection $users */ $users = User::with('personalData') @@ -43,9 +61,21 @@ public function index(Request $request): Response return [$u->id => $u->displayName]; }); + $levels = array_combine($this->levels, $this->levels); + foreach ($levels as $k => $v) { + $levels[$k] = Str::ucfirst($v); + } + return $this->response->withView( 'admin/log.twig', - ['entries' => $entries, 'search' => $search, 'users' => $users, 'search_user_id' => $searchUserId] + [ + 'entries' => $entries, + 'search' => $search, + 'users' => $users, + 'search_user_id' => $searchUserId, + 'level' => $level, + 'levels' => $levels, + ] ); } } diff --git a/src/Models/LogEntry.php b/src/Models/LogEntry.php index 7ee78730b..4a8d1bbe5 100644 --- a/src/Models/LogEntry.php +++ b/src/Models/LogEntry.php @@ -60,8 +60,11 @@ class LogEntry extends BaseModel /** * @return Builder[]|Collection|SupportCollection|LogEntry[] */ - public static function filter(?string $keyword = null, ?int $userId = null): array|Collection|SupportCollection - { + public static function filter( + ?string $keyword = null, + ?int $userId = null, + ?string $level = null + ): array | Collection | SupportCollection { $query = self::with(['user', 'user.personalData', 'user.state']) ->orderByDesc('created_at') ->orderByDesc('id') @@ -75,12 +78,12 @@ public static function filter(?string $keyword = null, ?int $userId = null): arr }); } + if (!empty($level)) { + $query->where('level', '=', $level); + } + if (!empty($keyword)) { - $query - ->where(function (Builder $query) use ($keyword): void { - $query->where('level', '=', $keyword) - ->orWhere('message', 'LIKE', '%' . $keyword . '%'); - }); + $query->where('message', 'LIKE', '%' . $keyword . '%'); } return $query->get(); diff --git a/tests/Unit/Controllers/Admin/LogsControllerTest.php b/tests/Unit/Controllers/Admin/LogsControllerTest.php index 9cc7f9ade..7be59f34e 100644 --- a/tests/Unit/Controllers/Admin/LogsControllerTest.php +++ b/tests/Unit/Controllers/Admin/LogsControllerTest.php @@ -37,6 +37,16 @@ public function testIndex(): void $this->setExpects($auth, 'can', ['logs.all'], true, 2); $response = $this->createMock(Response::class); + $levels = [ + LogLevel::ALERT => 'Alert', + LogLevel::CRITICAL => 'Critical', + LogLevel::DEBUG => 'Debug', + LogLevel::EMERGENCY => 'Emergency', + LogLevel::ERROR => 'Error', + LogLevel::INFO => 'Info', + LogLevel::NOTICE => 'Notice', + LogLevel::WARNING => 'Warning', + ]; $response->expects($this->exactly(2)) ->method('withView') ->withConsecutive( @@ -45,12 +55,16 @@ public function testIndex(): void 'search' => null, 'users' => new Collection(), 'search_user_id' => null, + 'level' => null, + 'levels' => $levels, ]], ['admin/log.twig', [ 'entries' => new Collection([$error]), 'search' => 'error', 'users' => new Collection(), 'search_user_id' => null, + 'level' => null, + 'levels' => $levels, ]] ) ->willReturn($response); diff --git a/tests/Unit/Models/LogEntryTest.php b/tests/Unit/Models/LogEntryTest.php index 944d6ec3a..b20e83e97 100644 --- a/tests/Unit/Models/LogEntryTest.php +++ b/tests/Unit/Models/LogEntryTest.php @@ -22,7 +22,7 @@ public function testFilter(): void 'I\'m an info' => LogLevel::INFO, '*Insert explosion here*' => LogLevel::EMERGENCY, 'Tracing along' => LogLevel::DEBUG, - 'Oops,no notice given' => LogLevel::NOTICE, + 'Oops, no notice given' => LogLevel::NOTICE, 'It\'s happening' => LogLevel::INFO, 'Something is wrong' => LogLevel::ERROR, 'Ohi' => LogLevel::INFO, @@ -35,7 +35,7 @@ public function testFilter(): void } $this->assertCount(11, LogEntry::filter()); - $this->assertCount(3, LogEntry::filter(LogLevel::INFO)); + $this->assertCount(3, LogEntry::filter(null, null, LogLevel::INFO)); $this->assertCount(1, LogEntry::filter('Oops')); $this->assertCount(1, LogEntry::filter(null, $user->id)); From 93529be3081dc3a77bb45037a9126992958c35ac Mon Sep 17 00:00:00 2001 From: Enterprize1 Date: Wed, 19 Mar 2025 20:23:28 +0100 Subject: [PATCH 061/157] Navbar: Allow scrolling on mobile devices --- resources/assets/themes/base.scss | 2 +- resources/views/layouts/parts/navbar.twig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/assets/themes/base.scss b/resources/assets/themes/base.scss index 3f672352c..70d7df4d6 100644 --- a/resources/assets/themes/base.scss +++ b/resources/assets/themes/base.scss @@ -195,7 +195,7 @@ table .border-bottom { // prevent dropdown-menu from overflowing the view .dropdown-menu { - max-height: calc(100vh - 300px); // 300px: menu offset + max-height: 100dvh; overflow: auto; // Show above shifts headers diff --git a/resources/views/layouts/parts/navbar.twig b/resources/views/layouts/parts/navbar.twig index f05ee4d8e..54f25abaf 100644 --- a/resources/views/layouts/parts/navbar.twig +++ b/resources/views/layouts/parts/navbar.twig @@ -41,7 +41,7 @@ > - diff --git a/resources/views/admin/schedule/index.twig b/resources/views/admin/schedule/index.twig index 85fd35c51..f7f1bae4f 100644 --- a/resources/views/admin/schedule/index.twig +++ b/resources/views/admin/schedule/index.twig @@ -21,7 +21,7 @@

    {{ __('schedule.import.text') }}

    -
    +
    diff --git a/resources/views/admin/schedule/load.twig b/resources/views/admin/schedule/load.twig index db0a43a5a..57a673506 100644 --- a/resources/views/admin/schedule/load.twig +++ b/resources/views/admin/schedule/load.twig @@ -35,7 +35,7 @@ {% macro locationsTable(locations) %} -
    +
    @@ -55,7 +55,7 @@ {% endmacro %} {% macro shiftsTable(shifts) %} -
    +
    diff --git a/resources/views/admin/shifts/history.twig b/resources/views/admin/shifts/history.twig index ace45b592..9757efa80 100644 --- a/resources/views/admin/shifts/history.twig +++ b/resources/views/admin/shifts/history.twig @@ -16,7 +16,7 @@
    {% block row_content %}
    -
    +
    diff --git a/resources/views/admin/shifttypes/index.twig b/resources/views/admin/shifttypes/index.twig index fe24b82f3..176374a56 100644 --- a/resources/views/admin/shifttypes/index.twig +++ b/resources/views/admin/shifttypes/index.twig @@ -26,7 +26,7 @@ {% block row_content %}
    -
    +
    diff --git a/resources/views/pages/design.twig b/resources/views/pages/design.twig index 945d0896f..1f5ce1849 100644 --- a/resources/views/pages/design.twig +++ b/resources/views/pages/design.twig @@ -72,22 +72,26 @@
    - - - - - - - - - - - - - - - +
    Header 1Header 2Header 3
    Table content{{ lipsum }}{{ m.icon('check-lg') }}
    Another contentLorem ipsum{{ m.icon('x-lg') }}
    + + + + + + + + + + + + + + + + + + +
    Header 1Header 2Header 3
    Table content{{ lipsum }}{{ m.icon('check-lg') }}
    Another contentLorem ipsum{{ m.icon('x-lg') }}
    diff --git a/resources/views/pages/locations/index.twig b/resources/views/pages/locations/index.twig index 033c86211..dcdf1bf17 100644 --- a/resources/views/pages/locations/index.twig +++ b/resources/views/pages/locations/index.twig @@ -26,7 +26,7 @@ {% block row_content %}
    -
    +
    diff --git a/resources/views/pages/messages/index.twig b/resources/views/pages/messages/index.twig index 9a1392a26..354d7126b 100644 --- a/resources/views/pages/messages/index.twig +++ b/resources/views/pages/messages/index.twig @@ -27,7 +27,7 @@ -
    +
    diff --git a/resources/views/pages/settings/oauth.twig b/resources/views/pages/settings/oauth.twig index 8acc3645c..6606e74aa 100644 --- a/resources/views/pages/settings/oauth.twig +++ b/resources/views/pages/settings/oauth.twig @@ -11,7 +11,7 @@ {% endblock %} {% block row_content %} -
    {{ __('general.angel') }}
    +
    diff --git a/resources/views/pages/settings/sessions.twig b/resources/views/pages/settings/sessions.twig index 934010261..1b711ca03 100644 --- a/resources/views/pages/settings/sessions.twig +++ b/resources/views/pages/settings/sessions.twig @@ -9,7 +9,7 @@
    {{ m.info(__('settings.sessions.info')) }} -
    +
    {{ __('settings.oauth.identity-provider') }}
    diff --git a/resources/views/pages/tag/index.twig b/resources/views/pages/tag/index.twig index a8b560b74..492fb6f9c 100644 --- a/resources/views/pages/tag/index.twig +++ b/resources/views/pages/tag/index.twig @@ -19,33 +19,36 @@ {% include 'layouts/parts/messages.twig' %}
    - -
    - - - - - - {% block row %} - {% for item in items %} +
    +
    {{ __('general.name') }}
    + - - + + - {% endfor %} - {% endblock %} - -
    {{ item.name }} -
    - {{ m.edit(url('admin/tags/edit/' ~ item.id)) }} - -
    - {{ csrf() }} - {{ f.delete(null, {'size': 'sm', 'confirm_title': __('tag.delete.title', [item.name[:40]|e])}) }} -
    -
    -
    {{ __('general.name') }}
    - + + + + {% block row %} + {% for item in items %} + + {{ item.name }} + +
    + {{ m.edit(url('admin/tags/edit/' ~ item.id)) }} + +
    + {{ csrf() }} + {{ f.delete(null, {'size': 'sm', 'confirm_title': __('tag.delete.title', [item.name[:40]|e])}) }} +
    +
    + + + {% endfor %} + {% endblock %} + + +
    {% endblock %} From 505560cf7475433a73066c58998eb0647b25fabe Mon Sep 17 00:00:00 2001 From: Xu Date: Sat, 17 May 2025 12:31:16 +0200 Subject: [PATCH 092/157] make shift filter selections scrollable --- includes/pages/user_shifts.php | 27 ++++++++++++++++++--------- includes/view/User_view.php | 1 - resources/assets/themes/base.scss | 11 +++++++++++ 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/includes/pages/user_shifts.php b/includes/pages/user_shifts.php index 1badce1ee..4b49cc5f3 100644 --- a/includes/pages/user_shifts.php +++ b/includes/pages/user_shifts.php @@ -194,7 +194,14 @@ function load_types() NOT `user_angel_type`.`confirm_user_id` IS NULL OR `user_angel_type`.`id` IS NULL ) - ) AS `enabled` + ) AS `enabled`, + ( + `user_angel_type`.`id` IS NOT NULL + AND ( + `angel_types`.`restricted`=0 + OR `user_angel_type`.`confirm_user_id` IS NOT NULL + ) + ) AS `own` FROM `angel_types` LEFT JOIN `user_angel_type` ON ( @@ -204,7 +211,7 @@ function load_types() . ($isShico ? '' : 'WHERE angel_types.hide_on_shift_view = 0 OR user_angel_type.user_id IS NOT NULL ') . - 'ORDER BY `angel_types`.`name` + 'ORDER BY `own` DESC, `angel_types`.`name` ', [ $user->id, @@ -411,14 +418,16 @@ function make_select($items, $selected, $name, $title = null, $ownSelect = []) $html .= '
    ' . "\n"; $htmlItems = []; - foreach ($items as $i) { - $id = $name . '_' . $i['id']; + foreach ($items as $i => $item) { + $break = isset($item['own'], $items[$i + 1]) && $item['own'] && !$items[$i + 1]['own']; + $id = $name . '_' . $item['id']; $htmlItems[] = '
    ' - . '' - . (!isset($i['enabled']) || $i['enabled'] ? '' : icon('mortarboard-fill')) - . '
    '; + . '' + . (!isset($item['enabled']) || $item['enabled'] ? '' : icon('mortarboard-fill')) + . '
    ' + . ($break ? '
    ' : ''); } $html .= implode("\n", $htmlItems); diff --git a/includes/view/User_view.php b/includes/view/User_view.php index fac23c2f6..fd5a0326e 100644 --- a/includes/view/User_view.php +++ b/includes/view/User_view.php @@ -623,7 +623,6 @@ function User_view( $goodie_admin, $user_worklogs, $admin_user_worklog_privilege, - $supported_angeltypes, ); if (count($my_shifts) > 0) { $myshifts_table = div('table-responsive', table([ diff --git a/resources/assets/themes/base.scss b/resources/assets/themes/base.scss index f7ad787ff..9166b7f62 100644 --- a/resources/assets/themes/base.scss +++ b/resources/assets/themes/base.scss @@ -215,6 +215,17 @@ table.table-sticky-header thead { } } +/* make selections scrollable and add a max height of 4.5 entries */ +#collapseShiftsFilterSelect .selection { + overflow-y: auto; + max-height: ($form-check-min-height + $form-check-margin-bottom) * 4.5; +} + +/* add divider between sorting groups of angel types selection */ +#angel_types_selection_hr { + margin: 0; +} + .selection .checkbox { display: block; } From ef9f7bb537257877301a834f1638e60f3a4ca6f1 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sun, 23 Feb 2025 11:48:42 +0100 Subject: [PATCH 093/157] Validation: Fix most deprecations --- src/Http/Validation/Rules/After.php | 8 ++++---- src/Http/Validation/Rules/Before.php | 8 ++++---- src/Http/Validation/Rules/Between.php | 4 ++-- src/Http/Validation/Rules/Checked.php | 6 +++--- src/Http/Validation/Rules/ComparesDateTime.php | 2 +- src/Http/Validation/Rules/DateTime.php | 4 ++-- src/Http/Validation/Rules/In.php | 4 ++-- src/Http/Validation/Rules/Max.php | 4 ++-- src/Http/Validation/Rules/Min.php | 4 ++-- src/Http/Validation/Rules/Username.php | 6 +++--- tests/Unit/Http/Validation/Rules/CheckedTest.php | 8 ++++---- .../Validation/Rules/ComparesDateTimeTest.php | 8 ++++++-- .../Rules/Stub/UsesComparesDateTime.php | 4 ++-- .../Unit/Http/Validation/Rules/UsernameTest.php | 16 ++++++++-------- 14 files changed, 45 insertions(+), 41 deletions(-) diff --git a/src/Http/Validation/Rules/After.php b/src/Http/Validation/Rules/After.php index afd329763..f378e1f10 100644 --- a/src/Http/Validation/Rules/After.php +++ b/src/Http/Validation/Rules/After.php @@ -4,14 +4,14 @@ namespace Engelsystem\Http\Validation\Rules; -use Respect\Validation\Rules\AbstractRule; +use Respect\Validation\Rules\AbstractComparison; -class After extends AbstractRule +class After extends AbstractComparison { use ComparesDateTime; - protected function compare(mixed $input): bool + protected function compare(mixed $left, mixed $right): bool { - return $this->orEqual ? $input >= $this->compareTo : $input > $this->compareTo; + return $this->orEqual ? $left >= $right : $left > $right; } } diff --git a/src/Http/Validation/Rules/Before.php b/src/Http/Validation/Rules/Before.php index 926bdcc3a..77ee3b09b 100644 --- a/src/Http/Validation/Rules/Before.php +++ b/src/Http/Validation/Rules/Before.php @@ -4,14 +4,14 @@ namespace Engelsystem\Http\Validation\Rules; -use Respect\Validation\Rules\AbstractRule; +use Respect\Validation\Rules\AbstractComparison; -class Before extends AbstractRule +class Before extends AbstractComparison { use ComparesDateTime; - protected function compare(mixed $input): bool + protected function compare(mixed $left, mixed $right): bool { - return $this->orEqual ? $input <= $this->compareTo : $input < $this->compareTo; + return $this->orEqual ? $left <= $right : $left < $right; } } diff --git a/src/Http/Validation/Rules/Between.php b/src/Http/Validation/Rules/Between.php index e85af5076..731d37be1 100644 --- a/src/Http/Validation/Rules/Between.php +++ b/src/Http/Validation/Rules/Between.php @@ -4,10 +4,10 @@ namespace Engelsystem\Http\Validation\Rules; -use Respect\Validation\Rules\AbstractEnvelope; use Respect\Validation\Rules\Between as RespectBetween; +use Respect\Validation\Rules\Core\Envelope; -class Between extends AbstractEnvelope +class Between extends Envelope { use StringInputLength; diff --git a/src/Http/Validation/Rules/Checked.php b/src/Http/Validation/Rules/Checked.php index f4da9352b..0b220753a 100644 --- a/src/Http/Validation/Rules/Checked.php +++ b/src/Http/Validation/Rules/Checked.php @@ -4,13 +4,13 @@ namespace Engelsystem\Http\Validation\Rules; -use Respect\Validation\Rules\AbstractRule; +use Respect\Validation\Rules\Core\Simple; -class Checked extends AbstractRule +class Checked extends Simple { use Truthy; - public function validate(mixed $input): bool + public function isValid(mixed $input): bool { return $this->truthy($input); } diff --git a/src/Http/Validation/Rules/ComparesDateTime.php b/src/Http/Validation/Rules/ComparesDateTime.php index 13f678d1e..9f8624a8e 100644 --- a/src/Http/Validation/Rules/ComparesDateTime.php +++ b/src/Http/Validation/Rules/ComparesDateTime.php @@ -23,7 +23,7 @@ public function validate(mixed $input): bool { $input = $this->toDateTime($input); - return $this->compare($input); + return $this->compare($input, $this->compareTo); } protected function toDateTime(mixed $value): mixed diff --git a/src/Http/Validation/Rules/DateTime.php b/src/Http/Validation/Rules/DateTime.php index 4e07767f7..87f21bc9a 100644 --- a/src/Http/Validation/Rules/DateTime.php +++ b/src/Http/Validation/Rules/DateTime.php @@ -4,10 +4,10 @@ namespace Engelsystem\Http\Validation\Rules; -use Respect\Validation\Rules\AbstractEnvelope; +use Respect\Validation\Rules\Core\Envelope; use Respect\Validation\Rules\DateTime as RespectDateTime; -class DateTime extends AbstractEnvelope +class DateTime extends Envelope { public function __construct(?string $format = null) { diff --git a/src/Http/Validation/Rules/In.php b/src/Http/Validation/Rules/In.php index 9b88643f7..0192983c2 100644 --- a/src/Http/Validation/Rules/In.php +++ b/src/Http/Validation/Rules/In.php @@ -4,10 +4,10 @@ namespace Engelsystem\Http\Validation\Rules; -use Respect\Validation\Rules\AbstractEnvelope; +use Respect\Validation\Rules\Core\Envelope; use Respect\Validation\Rules\In as RespectIn; -class In extends AbstractEnvelope +class In extends Envelope { public function __construct(mixed $haystack, bool $compareIdentical = false) { diff --git a/src/Http/Validation/Rules/Max.php b/src/Http/Validation/Rules/Max.php index 9beaf6ec9..7d9aff1a2 100644 --- a/src/Http/Validation/Rules/Max.php +++ b/src/Http/Validation/Rules/Max.php @@ -4,10 +4,10 @@ namespace Engelsystem\Http\Validation\Rules; -use Respect\Validation\Rules\AbstractEnvelope; +use Respect\Validation\Rules\Core\Envelope; use Respect\Validation\Rules\Max as RespectMax; -class Max extends AbstractEnvelope +class Max extends Envelope { use StringInputLength; diff --git a/src/Http/Validation/Rules/Min.php b/src/Http/Validation/Rules/Min.php index 2dcd4c462..6df3e21e3 100644 --- a/src/Http/Validation/Rules/Min.php +++ b/src/Http/Validation/Rules/Min.php @@ -4,10 +4,10 @@ namespace Engelsystem\Http\Validation\Rules; -use Respect\Validation\Rules\AbstractEnvelope; +use Respect\Validation\Rules\Core\Envelope; use Respect\Validation\Rules\Min as RespectMin; -class Min extends AbstractEnvelope +class Min extends Envelope { use StringInputLength; diff --git a/src/Http/Validation/Rules/Username.php b/src/Http/Validation/Rules/Username.php index ff9b7cea8..2d861eb9d 100644 --- a/src/Http/Validation/Rules/Username.php +++ b/src/Http/Validation/Rules/Username.php @@ -4,7 +4,7 @@ namespace Engelsystem\Http\Validation\Rules; -use Respect\Validation\Rules\AbstractRule; +use Respect\Validation\Rules\Core\Simple; use Respect\Validation\Validator; use RuntimeException; @@ -12,9 +12,9 @@ * Username validation. * Usernames must have 1-24 chars and NOT match the regular expression defined under the config key "username_regex". */ -class Username extends AbstractRule +class Username extends Simple { - public function validate(mixed $input): bool + public function isValid(mixed $input): bool { $regex = config('username_regex'); diff --git a/tests/Unit/Http/Validation/Rules/CheckedTest.php b/tests/Unit/Http/Validation/Rules/CheckedTest.php index f7c14ce30..02ad17c29 100644 --- a/tests/Unit/Http/Validation/Rules/CheckedTest.php +++ b/tests/Unit/Http/Validation/Rules/CheckedTest.php @@ -10,14 +10,14 @@ class CheckedTest extends TestCase { /** - * @covers \Engelsystem\Http\Validation\Rules\Checked::validate + * @covers \Engelsystem\Http\Validation\Rules\Checked::isValid * @see TruthyTest */ - public function testValidate(): void + public function testIsValid(): void { $rule = new Checked(); - $this->assertTrue($rule->validate('on')); - $this->assertFalse($rule->validate(null)); + $this->assertTrue($rule->isValid('on')); + $this->assertFalse($rule->isValid(null)); } } diff --git a/tests/Unit/Http/Validation/Rules/ComparesDateTimeTest.php b/tests/Unit/Http/Validation/Rules/ComparesDateTimeTest.php index 181657328..7cbd8bb8b 100644 --- a/tests/Unit/Http/Validation/Rules/ComparesDateTimeTest.php +++ b/tests/Unit/Http/Validation/Rules/ComparesDateTimeTest.php @@ -18,11 +18,14 @@ class ComparesDateTimeTest extends TestCase public function testValidate(): void { $rule = new UsesComparesDateTime('2024-01-02 13:37'); - $rule->setCallback(function ($input) { + $rule->setCallback(function ($input, $comparison) { /** @var Carbon $input */ $this->assertInstanceOf(Carbon::class, $input); $this->assertEquals('2042-10-11 00:00:00', $input->toDateTimeString()); + $this->assertInstanceOf(Carbon::class, $comparison); + $this->assertEquals('2024-01-02 13:37:00', $comparison->toDateTimeString()); + return true; }); @@ -63,8 +66,9 @@ public function testValidateDateTimeStaysSame(): void $b = Carbon::now(); $rule = new UsesComparesDateTime($a); - $rule->setCallback(function ($input) use ($b) { + $rule->setCallback(function ($input, $comparison) use ($a, $b) { $this->assertEquals($b, $input); + $this->assertEquals($a, $comparison); return false; }); diff --git a/tests/Unit/Http/Validation/Rules/Stub/UsesComparesDateTime.php b/tests/Unit/Http/Validation/Rules/Stub/UsesComparesDateTime.php index 24d5ef7ec..f439672b0 100644 --- a/tests/Unit/Http/Validation/Rules/Stub/UsesComparesDateTime.php +++ b/tests/Unit/Http/Validation/Rules/Stub/UsesComparesDateTime.php @@ -12,11 +12,11 @@ class UsesComparesDateTime protected mixed $callback = null; - public function compare(mixed $input): bool + public function compare(mixed $left, mixed $right): bool { /** @var callable $callback */ $callback = $this->callback; - return $callback($input); + return $callback($left, $right); } public function setCallback(callable $callback): void diff --git a/tests/Unit/Http/Validation/Rules/UsernameTest.php b/tests/Unit/Http/Validation/Rules/UsernameTest.php index 705d97457..66a119331 100644 --- a/tests/Unit/Http/Validation/Rules/UsernameTest.php +++ b/tests/Unit/Http/Validation/Rules/UsernameTest.php @@ -30,7 +30,7 @@ public function setUp(): void /** * @return array */ - public function provideValidateWithDefaultConfigTestData(): array + public function provideIsValidWithDefaultConfigTestData(): array { return [ 'empty string' => ['', false], @@ -46,22 +46,22 @@ public function provideValidateWithDefaultConfigTestData(): array } /** - * @covers \Engelsystem\Http\Validation\Rules\Username::validate - * @dataProvider provideValidateWithDefaultConfigTestData + * @covers \Engelsystem\Http\Validation\Rules\Username::isValid + * @dataProvider provideIsValidWithDefaultConfigTestData */ - public function testValidateWithDefaultConfig(mixed $value, bool $expectedValid): void + public function testIsValidWithDefaultConfig(mixed $value, bool $expectedValid): void { - self::assertSame($expectedValid, $this->subject->validate($value)); + self::assertSame($expectedValid, $this->subject->isValid($value)); } /** - * @covers \Engelsystem\Http\Validation\Rules\Username::validate + * @covers \Engelsystem\Http\Validation\Rules\Username::isValid */ - public function testMissingConfigRaisesException(): void + public function testIsValidMissingConfigRaisesException(): void { $this->config->set('username_regex', null); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('username_regex not set in config'); - $this->subject->validate('test'); + $this->subject->isValid('test'); } } From af92582dddeef639b028c52d6c5a7154c9c7b113 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Mon, 10 Feb 2025 19:53:07 +0100 Subject: [PATCH 094/157] Error handling: Log all errors, only show not recoverable on prod --- src/Exceptions/Handler.php | 23 +++++++----- src/Exceptions/Handlers/Legacy.php | 10 +++++- src/Middleware/ApiRouteHandler.php | 2 +- src/Middleware/ExceptionHandler.php | 2 +- tests/Unit/Exceptions/HandlerTest.php | 36 ++++++++++++++++--- tests/Unit/Exceptions/Handlers/LegacyTest.php | 2 +- tests/Unit/Middleware/ApiRouteHandlerTest.php | 2 +- 7 files changed, 59 insertions(+), 18 deletions(-) diff --git a/src/Exceptions/Handler.php b/src/Exceptions/Handler.php index 215f72e4f..5a4dea963 100644 --- a/src/Exceptions/Handler.php +++ b/src/Exceptions/Handler.php @@ -37,20 +37,25 @@ public function register(): void return; } - $level = $this->environment == Environment::DEVELOPMENT - ? E_ALL - : E_RECOVERABLE_ERROR | E_COMPILE_ERROR; - set_error_handler([$this, 'errorHandler'], $level); + set_error_handler([$this, 'errorHandler']); set_exception_handler([$this, 'exceptionHandler']); } - public function errorHandler(int $number, string $message, string $file, int $line): void + public function errorHandler(int $errorNumber, string $message, string $file, int $line): bool { - $exception = new ErrorException($message, 0, $number, $file, $line); - $this->exceptionHandler($exception); + $handleLevel = $this->environment == Environment::DEVELOPMENT + ? E_ALL + : E_RECOVERABLE_ERROR | E_COMPILE_ERROR; + // If error level should be intercepted or ignored (like warnings in prod) + $shouldHandle = (bool) ($errorNumber & $handleLevel); + + $exception = new ErrorException($message, 0, $errorNumber, $file, $line); + $this->exceptionHandler($exception, $shouldHandle); + + return $shouldHandle; } - public function exceptionHandler(Throwable $e, bool $return = false): string + public function exceptionHandler(Throwable $e, bool $showError = true): string { if (!$this->request instanceof Request) { $this->request = new Request(); @@ -61,7 +66,7 @@ public function exceptionHandler(Throwable $e, bool $return = false): string ob_start(); $handler->render($this->request, $e); - if ($return) { + if (!$showError) { $output = ob_get_contents(); ob_end_clean(); return $output; diff --git a/src/Exceptions/Handlers/Legacy.php b/src/Exceptions/Handlers/Legacy.php index fd35902bc..5788c36b7 100644 --- a/src/Exceptions/Handlers/Legacy.php +++ b/src/Exceptions/Handlers/Legacy.php @@ -5,7 +5,9 @@ namespace Engelsystem\Exceptions\Handlers; use Engelsystem\Http\Request; +use ErrorException; use Psr\Log\LoggerInterface; +use Psr\Log\LogLevel; use Throwable; class Legacy implements HandlerInterface @@ -39,8 +41,14 @@ public function report(Throwable $e): void return; } + $errorLevels = E_ERROR | E_RECOVERABLE_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR; + $logAsError = !$e instanceof ErrorException || $e->getSeverity() & $errorLevels; try { - $this->log->critical('', ['exception' => $e]); + $this->log->log( + $logAsError ? LogLevel::CRITICAL : LogLevel::WARNING, + '', + ['exception' => $e], + ); } catch (Throwable) { } } diff --git a/src/Middleware/ApiRouteHandler.php b/src/Middleware/ApiRouteHandler.php index f2a42a16b..54c50fa48 100644 --- a/src/Middleware/ApiRouteHandler.php +++ b/src/Middleware/ApiRouteHandler.php @@ -70,7 +70,7 @@ protected function processApi(ServerRequestInterface $request, RequestHandlerInt } catch (Throwable $e) { /** @var Handler $handler */ $handler = app('error.handler'); - $handler->exceptionHandler($e, true); + $handler->exceptionHandler($e, false); $response = new Response('', 500); $response->setContent($response->getReasonPhrase()); } diff --git a/src/Middleware/ExceptionHandler.php b/src/Middleware/ExceptionHandler.php index 967b0ff65..440624960 100644 --- a/src/Middleware/ExceptionHandler.php +++ b/src/Middleware/ExceptionHandler.php @@ -32,7 +32,7 @@ public function process( } catch (Throwable $e) { /** @var ExceptionsHandler $handler */ $handler = $this->container->get('error.handler'); - $content = $handler->exceptionHandler($e, true); + $content = $handler->exceptionHandler($e, false); return response($content, 500); } diff --git a/tests/Unit/Exceptions/HandlerTest.php b/tests/Unit/Exceptions/HandlerTest.php index 767fd2274..d4e91ab5a 100644 --- a/tests/Unit/Exceptions/HandlerTest.php +++ b/tests/Unit/Exceptions/HandlerTest.php @@ -29,21 +29,49 @@ public function testCreate(): void $this->assertEquals(Environment::DEVELOPMENT, $anotherHandler->getEnvironment()); } + public function errorHandlerProvider(): array + { + return [ + // Environment, Error level, should display message or (if false) ignore the output by returning + + [Environment::PRODUCTION, E_RECOVERABLE_ERROR, true], + [Environment::PRODUCTION, E_WARNING, false], + [Environment::PRODUCTION, E_NOTICE, false], + [Environment::PRODUCTION, E_DEPRECATED, false], + [Environment::PRODUCTION, E_COMPILE_ERROR, true], + [Environment::PRODUCTION, E_USER_WARNING, false], + [Environment::PRODUCTION, E_USER_NOTICE, false], + [Environment::PRODUCTION, E_USER_DEPRECATED, false], + + [Environment::DEVELOPMENT, E_RECOVERABLE_ERROR, true], + [Environment::DEVELOPMENT, E_WARNING, true], + [Environment::DEVELOPMENT, E_NOTICE, true], + [Environment::DEVELOPMENT, E_DEPRECATED, true], + [Environment::DEVELOPMENT, E_COMPILE_ERROR, true], + [Environment::DEVELOPMENT, E_USER_WARNING, true], + [Environment::DEVELOPMENT, E_USER_NOTICE, true], + [Environment::DEVELOPMENT, E_USER_DEPRECATED, true], + ]; + } + /** * @covers \Engelsystem\Exceptions\Handler::errorHandler() + * @dataProvider errorHandlerProvider */ - public function testErrorHandler(): void + public function testErrorHandler(Environment $env, int $level, bool $showError): void { /** @var Handler|MockObject $handler */ $handler = $this->getMockBuilder(Handler::class) + ->setConstructorArgs([$env]) ->onlyMethods(['exceptionHandler']) ->getMock(); $handler->expects($this->once()) ->method('exceptionHandler') - ->with($this->isInstanceOf(ErrorException::class)); + ->with($this->isInstanceOf(ErrorException::class), $showError); - $handler->errorHandler(1, 'Foo and bar!', '/lo/rem.php', 123); + $return = $handler->errorHandler($level, 'Foo and bar!', '/lo/rem.php', 123); + $this->assertEquals($showError, $return); } /** @@ -78,7 +106,7 @@ public function testExceptionHandler(): void $this->expectOutputString($errorMessage); $handler->exceptionHandler($exception); - $return = $handler->exceptionHandler($exception, true); + $return = $handler->exceptionHandler($exception, false); $this->assertEquals($errorMessage, $return); } diff --git a/tests/Unit/Exceptions/Handlers/LegacyTest.php b/tests/Unit/Exceptions/Handlers/LegacyTest.php index 46e05968b..7217237a7 100644 --- a/tests/Unit/Exceptions/Handlers/LegacyTest.php +++ b/tests/Unit/Exceptions/Handlers/LegacyTest.php @@ -54,7 +54,7 @@ public function testReport(): void $logger = new TestLogger(); $logger2 = $this->createMock(TestLogger::class); $logger2->expects($this->once()) - ->method('critical') + ->method('log') ->willReturnCallback(function (): void { throw new ErrorException(); }); diff --git a/tests/Unit/Middleware/ApiRouteHandlerTest.php b/tests/Unit/Middleware/ApiRouteHandlerTest.php index 61b2eab3c..b2f88c359 100644 --- a/tests/Unit/Middleware/ApiRouteHandlerTest.php +++ b/tests/Unit/Middleware/ApiRouteHandlerTest.php @@ -155,7 +155,7 @@ public function testProcessGenericException(): void /** @var RequestHandlerInterface|MockObject $handler */ $handler = $this->getMockForAbstractClass(RequestHandlerInterface::class); $errorHandler = $this->createMock(Handler::class); - $this->setExpects($errorHandler, 'exceptionHandler', [$e, true], '', $this->once()); + $this->setExpects($errorHandler, 'exceptionHandler', [$e, false], '', $this->once()); $this->app->instance('error.handler', $errorHandler); $handler->expects($this->once()) From d0d8641e27b0d569968d2f8258d0319d80cea909 Mon Sep 17 00:00:00 2001 From: Ole Bittner Date: Thu, 16 Jan 2025 22:49:54 +0100 Subject: [PATCH 095/157] add ability to mark work log as nightshift --- db/factories/WorklogFactory.php | 11 ++++--- ...25_01_16_000000_add_worklog_nightshift.php | 31 +++++++++++++++++++ includes/pages/admin_active.php | 11 ++++--- includes/view/User_view.php | 29 ++++++++++++----- resources/lang/de_DE/default.po | 3 ++ resources/lang/en_US/default.po | 3 ++ resources/views/admin/user/edit-worklog.twig | 5 +++ .../Admin/UserWorklogController.php | 19 ++++++++++-- src/Helpers/Goodie.php | 29 ++++++++++++++++- src/Models/Worklog.php | 11 ++++--- .../Admin/UserGoodieControllerTest.php | 1 + .../Admin/UserWorklogControllerTest.php | 13 ++++++-- tests/Unit/Helpers/GoodieTest.php | 2 +- tests/Unit/Models/WorklogTest.php | 1 + 14 files changed, 141 insertions(+), 28 deletions(-) create mode 100644 db/migrations/2025_01_16_000000_add_worklog_nightshift.php diff --git a/db/factories/WorklogFactory.php b/db/factories/WorklogFactory.php index c88833b2b..1ad7ef48b 100644 --- a/db/factories/WorklogFactory.php +++ b/db/factories/WorklogFactory.php @@ -16,11 +16,12 @@ class WorklogFactory extends Factory public function definition(): array { return [ - 'user_id' => User::factory(), - 'creator_id' => User::factory(), - 'hours' => $this->faker->randomFloat(2, 0.01, 10), - 'comment' => $this->faker->text(30), - 'worked_at' => $this->faker->dateTimeThisMonth(), + 'user_id' => User::factory(), + 'creator_id' => User::factory(), + 'hours' => $this->faker->randomFloat(2, 0.01, 10), + 'comment' => $this->faker->text(30), + 'worked_at' => $this->faker->dateTimeThisMonth(), + 'night_shift' => $this->faker->boolean(), ]; } } diff --git a/db/migrations/2025_01_16_000000_add_worklog_nightshift.php b/db/migrations/2025_01_16_000000_add_worklog_nightshift.php new file mode 100644 index 000000000..ca293d0e5 --- /dev/null +++ b/db/migrations/2025_01_16_000000_add_worklog_nightshift.php @@ -0,0 +1,31 @@ +schema->table('worklogs', function (Blueprint $table): void { + $table->boolean('night_shift')->default(false)->after('worked_at'); + }); + } + + /** + * Reverse the migration + */ + public function down(): void + { + $this->schema->table('worklogs', function (Blueprint $table): void { + $table->dropColumn('night_shift'); + }); + } +} diff --git a/includes/pages/admin_active.php b/includes/pages/admin_active.php index 6c52b4935..4ed0876b9 100644 --- a/includes/pages/admin_active.php +++ b/includes/pages/admin_active.php @@ -26,6 +26,7 @@ function admin_active() { $tshirt_sizes = config('tshirt_sizes'); $shift_sum_formula = Goodie::shiftScoreQuery()->getValue(Db::connection()->getQueryGrammar()); + $worklog_sum_formula = Goodie::worklogScoreQuery()->getValue(Db::connection()->getQueryGrammar()); $request = request(); $goodie = GoodieType::from(config('goodie_type')); $goodie_enabled = $goodie !== GoodieType::None; @@ -70,12 +71,13 @@ function admin_active() users.*, COUNT(shift_entries.id) AS shift_count, (%s + ( - SELECT COALESCE(SUM(`hours`) * 3600, 0) + SELECT %s * 3600 FROM `worklogs` WHERE `user_id`=`users`.`id` AND `worked_at` <= NOW() )) AS `shift_length` ', - $shift_sum_formula + $shift_sum_formula, + $worklog_sum_formula ) ) ->leftJoin('shift_entries', 'users.id', '=', 'shift_entries.user_id') @@ -185,12 +187,13 @@ function admin_active() users.*, COUNT(shift_entries.id) AS shift_count, (%s + ( - SELECT COALESCE(SUM(`hours`) * 3600, 0) + SELECT %s * 3600 FROM `worklogs` WHERE `user_id`=`users`.`id` AND `worked_at` <= NOW() )) AS `shift_length` ', - $shift_sum_formula + $shift_sum_formula, + $worklog_sum_formula ) ) ->leftJoin('shift_entries', 'users.id', '=', 'shift_entries.user_id') diff --git a/includes/view/User_view.php b/includes/view/User_view.php index fd5a0326e..8c21656a0 100644 --- a/includes/view/User_view.php +++ b/includes/view/User_view.php @@ -337,13 +337,7 @@ function User_view_myshift(Shift $shift, $user_source, $its_me, $supporter) $night_shift = ''; if ($shift->isNightShift() && $goodie_enabled) { - $night_shift = ' '; + $night_shift = render_night_shift_hint($nightShiftsConfig); } $myshift = [ 'date' => icon('calendar-event') @@ -524,6 +518,9 @@ function User_view_worklog(Worklog $worklog, $admin_user_worklog_privilege, $its { $actions = ''; $self_worklog = config('enable_self_worklog') || !$its_me; + $nightShiftsConfig = config('night_shifts'); + $goodie = GoodieType::from(config('goodie_type')); + $goodie_enabled = $goodie !== GoodieType::None; if ($admin_user_worklog_privilege && $self_worklog) { $actions = '
    ' . table_buttons([ @@ -544,10 +541,15 @@ function User_view_worklog(Worklog $worklog, $admin_user_worklog_privilege, $its ]) . '
    '; } + $night_shift = ''; + if ($worklog->night_shift && $goodie_enabled && $nightShiftsConfig['enabled']) { + $night_shift = render_night_shift_hint($nightShiftsConfig); + } + return [ 'date' => icon('calendar-event') . date(__('general.date'), $worklog->worked_at->timestamp), 'duration' => sprintf('%.2f', $worklog->hours) . ' h', - 'hints' => '', + 'hints' => $night_shift, 'location' => '', 'shift_info' => __('Work log entry'), 'comment' => htmlspecialchars($worklog->comment) . '
    ' @@ -1165,3 +1167,14 @@ function render_user_mobile_hint() return null; } + +function render_night_shift_hint(mixed $nightShiftsConfig): string +{ + return ' '; +} diff --git a/resources/lang/de_DE/default.po b/resources/lang/de_DE/default.po index 641243b12..fa897f0d5 100644 --- a/resources/lang/de_DE/default.po +++ b/resources/lang/de_DE/default.po @@ -1880,6 +1880,9 @@ msgstr "Arbeitsstunden" msgid "worklog.comment" msgstr "Kommentar" +msgid "worklog.night_shift" +msgstr "Arbeitseinsatz ist eine Nachtschicht" + msgid "worklog.delete" msgstr "Arbeitseinsatz löschen" diff --git a/resources/lang/en_US/default.po b/resources/lang/en_US/default.po index 4f9c2193b..a720d1441 100644 --- a/resources/lang/en_US/default.po +++ b/resources/lang/en_US/default.po @@ -707,6 +707,9 @@ msgstr "Work hours" msgid "worklog.comment" msgstr "Comment" +msgid "worklog.night_shift" +msgstr "Work log is a night shift" + msgid "worklog.delete" msgstr "Delete work log" diff --git a/resources/views/admin/user/edit-worklog.twig b/resources/views/admin/user/edit-worklog.twig index 14a9d2275..bd6282701 100644 --- a/resources/views/admin/user/edit-worklog.twig +++ b/resources/views/admin/user/edit-worklog.twig @@ -40,6 +40,11 @@ 'required': true, 'max_length': 200, }) }} + {% if attribute(config('night_shifts'), 'enabled') %} + {{ f.checkbox('night_shift', __('worklog.night_shift'), { + 'checked': night_shift, + }) }} + {% endif %} {{ f.submit(__('form.save'), {'icon_left': 'save'}) }}
    diff --git a/src/Controllers/Admin/UserWorklogController.php b/src/Controllers/Admin/UserWorklogController.php index fd4316d46..63169ae94 100644 --- a/src/Controllers/Admin/UserWorklogController.php +++ b/src/Controllers/Admin/UserWorklogController.php @@ -49,7 +49,14 @@ public function editWorklog(Request $request): Response if ($worklog->user->id != $user->id) { throw new HttpNotFound(); } - return $this->showEditWorklog($user, $worklog->worked_at, $worklog->hours, $worklog->comment, true); + return $this->showEditWorklog( + $user, + $worklog->worked_at, + $worklog->hours, + $worklog->comment, + $worklog->night_shift, + true + ); } else { return $this->showEditWorklog($user, Carbon::today()); } @@ -64,6 +71,7 @@ public function saveWorklog(Request $request): Response 'work_date' => 'required|date:Y-m-d', 'work_hours' => 'float|min:0', 'comment' => 'required|max:200', + 'night_shift' => 'optional|checked', ]); // Search / create worklog @@ -81,10 +89,11 @@ public function saveWorklog(Request $request): Response $worklog->worked_at = $data['work_date']; $worklog->hours = $data['work_hours']; $worklog->comment = $data['comment']; + $worklog->night_shift = $data['night_shift'] ?: false; $worklog->save(); $this->log->info( - 'Saved worklog ({wl_id}) for {name} ({id}) at {time} spanning {hours}h: {text}', + 'Saved worklog ({wl_id}) for {name} ({id}) at {time} spanning {hours}h{night_shift}: {text}', [ 'wl_id' => $worklog->id, 'name' => $user->name, @@ -92,6 +101,7 @@ public function saveWorklog(Request $request): Response 'time' => $worklog->worked_at, 'hours' => $worklog->hours, 'text' => $worklog->comment, + 'night_shift' => $worklog->night_shift ? ' at night' : '', ] ); $this->addNotification(isset($worklogId) ? 'worklog.edit.success' : 'worklog.add.success'); @@ -128,7 +138,7 @@ public function deleteWorklog(Request $request): Response $worklog->delete(); $this->log->info( - 'Deleted worklog ({wl_id}) for {name} ({id}) at {time} spanning {hours}h: {text}', + 'Deleted worklog ({wl_id}) for {name} ({id}) at {time} spanning {hours}h{night_shift}: {text}', [ 'wl_id' => $worklog->id, 'name' => $worklog->user->name, @@ -136,6 +146,7 @@ public function deleteWorklog(Request $request): Response 'time' => $worklog->worked_at, 'hours' => $worklog->hours, 'text' => $worklog->comment, + 'night_shift' => $worklog->night_shift ? ' at night' : '', ] ); $this->addNotification('worklog.delete.success'); @@ -149,6 +160,7 @@ private function showEditWorklog( Carbon $work_date, float $work_hours = 0, string $comment = '', + bool $night_shift = false, bool $is_edit = false ): Response { return $this->response->withView( @@ -158,6 +170,7 @@ private function showEditWorklog( 'work_date' => $work_date, 'work_hours' => $work_hours, 'comment' => $comment, + 'night_shift' => $night_shift, 'is_edit' => $is_edit, ] ); diff --git a/src/Helpers/Goodie.php b/src/Helpers/Goodie.php index 09927eb94..c776a3364 100644 --- a/src/Helpers/Goodie.php +++ b/src/Helpers/Goodie.php @@ -81,6 +81,28 @@ public static function shiftScoreQuery(): Expression // @codeCoverageIgnoreEnd } + public static function worklogScoreQuery(): Expression + { + $nightShifts = config('night_shifts'); + + /** @var Database $db */ + $db = app(Database::class); + $connection = $db->getConnection(); + + if (!$nightShifts['enabled']) { + return $connection->raw( + /** @lang MySQL */ + 'COALESCE(SUM(`hours`), 0)' + ); + } + + return $connection->raw(sprintf( + /** @lang MySQL */ + 'COALESCE(SUM(IF(`night_shift`, `hours` * %d, `hours`)), 0)', + $nightShifts['multiplier'] + )); + } + /** * Returns the goodie score (number of hours counted for goodie score) * Includes only ended shifts @@ -115,7 +137,12 @@ public static function userScore(User $user): float $worklogHours = $user->worklogs() ->where('worked_at', '<=', Carbon::Now()) - ->sum('hours'); + ->selectRaw(sprintf( + /** @lang MySQL */ + '%s as `total_hours`', + self::worklogScoreQuery()->getValue($con->getQueryGrammar()) + )) + ->value('total_hours'); return $shiftHours + $worklogHours; } diff --git a/src/Models/Worklog.php b/src/Models/Worklog.php index f17343d74..9705bcf0f 100644 --- a/src/Models/Worklog.php +++ b/src/Models/Worklog.php @@ -17,6 +17,7 @@ * @property float $hours * @property string $comment * @property Carbon $worked_at + * @property bool $night_shift * @property Carbon|null $created_at * @property Carbon|null $updated_at * @@ -38,10 +39,11 @@ class Worklog extends BaseModel /** @var array */ protected $casts = [ // phpcs:ignore - 'user_id' => 'integer', - 'creator_id' => 'integer', - 'hours' => 'float', - 'worked_at' => 'datetime', + 'user_id' => 'integer', + 'creator_id' => 'integer', + 'hours' => 'float', + 'worked_at' => 'datetime', + 'night_shift' => 'boolean', ]; /** @@ -55,6 +57,7 @@ class Worklog extends BaseModel 'hours', 'comment', 'worked_at', + 'night_shift', ]; public function creator(): BelongsTo diff --git a/tests/Unit/Controllers/Admin/UserGoodieControllerTest.php b/tests/Unit/Controllers/Admin/UserGoodieControllerTest.php index 32b9b6221..922d27bb3 100644 --- a/tests/Unit/Controllers/Admin/UserGoodieControllerTest.php +++ b/tests/Unit/Controllers/Admin/UserGoodieControllerTest.php @@ -30,6 +30,7 @@ class UserGoodieControllerTest extends ControllerTest public function testIndex(): void { $this->config->set('goodie_type', GoodieType::Tshirt->value); + $this->config->set('night_shifts', ['enabled' => false]); $request = $this->request->withAttribute('user_id', 1); /** @var Authenticator|MockObject $auth */ $auth = $this->createMock(Authenticator::class); diff --git a/tests/Unit/Controllers/Admin/UserWorklogControllerTest.php b/tests/Unit/Controllers/Admin/UserWorklogControllerTest.php index cd44b95cd..cb28260e7 100644 --- a/tests/Unit/Controllers/Admin/UserWorklogControllerTest.php +++ b/tests/Unit/Controllers/Admin/UserWorklogControllerTest.php @@ -68,6 +68,7 @@ public function testShowAddWorklog(): void $this->assertEquals(Carbon::today(), $data['work_date']); $this->assertEquals(0, $data['work_hours']); $this->assertEquals('', $data['comment']); + $this->assertFalse($data['night_shift']); $this->assertFalse($data['is_edit']); return $this->response; }); @@ -102,6 +103,7 @@ public function testShowEditWorklog(): void 'worked_at' => new Carbon('2022-01-01'), 'hours' => 3.14, 'comment' => 'a comment', + 'night_shift' => true, ])->create(); $request = $this->request @@ -114,6 +116,7 @@ public function testShowEditWorklog(): void $this->assertEquals(new Carbon('2022-01-01'), $data['work_date']); $this->assertEquals(3.14, $data['work_hours']); $this->assertEquals('a comment', $data['comment']); + $this->assertTrue($data['night_shift']); $this->assertTrue($data['is_edit']); return $this->response; }); @@ -151,7 +154,9 @@ public function testSaveNewWorklog(): void $work_date = Carbon::today()->format('Y-m-d'); $work_hours = 3.14; $comment = str_repeat('X', 200); - $body = ['work_date' => $work_date, 'work_hours' => $work_hours, 'comment' => $comment]; + $night_shift = true; + $body = ['work_date' => $work_date, 'work_hours' => $work_hours, 'comment' => $comment, + 'night_shift' => $night_shift]; $request = $this->request->withAttribute('user_id', $this->user->id)->withParsedBody($body); $this->setExpects($this->auth, 'user', null, $this->user, $this->any()); $this->redirect->expects($this->once()) @@ -170,6 +175,7 @@ public function testSaveNewWorklog(): void $this->assertEquals($work_date, $new_worklog->worked_at->format('Y-m-d')); $this->assertEquals($work_hours, $new_worklog->hours); $this->assertEquals($comment, $new_worklog->comment); + $this->assertEquals($night_shift, $new_worklog->night_shift); } /** @@ -216,7 +222,9 @@ public function testOverwriteWorklog(): void $work_date = Carbon::today()->format('Y-m-d'); $work_hours = 3.14; $comment = str_repeat('X', 200); - $body = ['work_date' => $work_date, 'work_hours' => $work_hours, 'comment' => $comment]; + $night_shift = true; + $body = ['work_date' => $work_date, 'work_hours' => $work_hours, 'comment' => $comment, + 'night_shift' => $night_shift]; $request = $this->request ->withAttribute('user_id', $this->user->id) @@ -235,6 +243,7 @@ public function testOverwriteWorklog(): void $this->assertEquals($work_date, $worklog->worked_at->format('Y-m-d')); $this->assertEquals($work_hours, $worklog->hours); $this->assertEquals($comment, $worklog->comment); + $this->assertEquals($night_shift, $worklog->night_shift); } /** diff --git a/tests/Unit/Helpers/GoodieTest.php b/tests/Unit/Helpers/GoodieTest.php index c7b855b59..ad9e37714 100644 --- a/tests/Unit/Helpers/GoodieTest.php +++ b/tests/Unit/Helpers/GoodieTest.php @@ -45,6 +45,6 @@ protected function setUp(): void parent::setUp(); $this->initDatabase(); - $this->app->instance('config', new Config(['night_shifts' => []])); + $this->app->instance('config', new Config(['night_shifts' => ['enabled' => false]])); } } diff --git a/tests/Unit/Models/WorklogTest.php b/tests/Unit/Models/WorklogTest.php index dcf96380f..c7609a367 100644 --- a/tests/Unit/Models/WorklogTest.php +++ b/tests/Unit/Models/WorklogTest.php @@ -24,6 +24,7 @@ public function testCreator(): void $worklog->hours = 4.2; $worklog->comment = 'Lorem ipsum'; $worklog->worked_at = new Carbon(); + $worklog->night_shift = false; $worklog->save(); $savedWorklog = Worklog::first(); From fd4585610687df76f4bce8c16aa066f79000f12c Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Wed, 1 Oct 2025 18:38:13 +0200 Subject: [PATCH 096/157] CI: Update kubectl image --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0bb656d8c..35a5c1c4f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -386,7 +386,7 @@ deploy: .kubectl_deployment: &kubectl_deployment stage: deploy image: - name: bitnami/kubectl:latest + name: bitnamisecure/kubectl:latest entrypoint: [ '' ] needs: - test From a3b98fa584791c5fe7fe289b06863b99ebcca1b4 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sat, 3 May 2025 00:10:29 +0200 Subject: [PATCH 097/157] Api: Fix error codes specification --- resources/api/openapi.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/resources/api/openapi.yml b/resources/api/openapi.yml index 1556f6197..1d9608bac 100644 --- a/resources/api/openapi.yml +++ b/resources/api/openapi.yml @@ -530,6 +530,12 @@ paths: type: array items: $ref: '#/components/schemas/Shift' + '401': + $ref: '#/components/responses/UnauthorizedError' + '403': + $ref: '#/components/responses/ForbiddenError' + '404': + $ref: '#/components/responses/NotFoundError' /news: get: @@ -684,8 +690,6 @@ paths: $ref: '#/components/responses/UnauthorizedError' '403': $ref: '#/components/responses/ForbiddenError' - '404': - $ref: '#/components/responses/NotFoundError' /users/{id}: parameters: @@ -810,8 +814,6 @@ paths: $ref: '#/components/responses/UnauthorizedError' '403': $ref: '#/components/responses/ForbiddenError' - '404': - $ref: '#/components/responses/NotFoundError' /openapi: get: @@ -827,5 +829,3 @@ paths: $ref: '#/components/responses/UnauthorizedError' '403': $ref: '#/components/responses/ForbiddenError' - '404': - $ref: '#/components/responses/NotFoundError' From 69f42989f7622d3721f49a524ec79ecf2e18db35 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Fri, 2 May 2025 23:21:32 +0200 Subject: [PATCH 098/157] Api: Add angeltypes/[id]/users --- config/routes.php | 1 + resources/api/openapi.yml | 67 ++++++++++++++++--- .../Api/Resources/AngelTypeResource.php | 3 +- .../Api/Resources/BasicResource.php | 3 +- .../Api/Resources/LocationResource.php | 3 +- .../Api/Resources/NewsResource.php | 3 +- .../Api/Resources/ShiftResource.php | 3 +- .../Api/Resources/ShiftTypeResource.php | 3 +- .../UserAngelTypeReferenceResource.php | 30 +++++++++ .../Api/Resources/UserResource.php | 3 +- src/Controllers/Api/UsersController.php | 30 +++++++++ .../Controllers/Api/UsersControllerTest.php | 37 ++++++++++ 12 files changed, 168 insertions(+), 18 deletions(-) create mode 100644 src/Controllers/Api/Resources/UserAngelTypeReferenceResource.php diff --git a/config/routes.php b/config/routes.php index 81f76e04d..148ca0ccb 100644 --- a/config/routes.php +++ b/config/routes.php @@ -141,6 +141,7 @@ function (RouteCollector $route): void { $route->get('/angeltypes', 'Api\AngelTypeController@index'); $route->get('/angeltypes/{angeltype_id:\d+}/shifts', 'Api\ShiftsController@entriesByAngeltype'); + $route->get('/angeltypes/{angeltype_id:\d+}/users', 'Api\UsersController@entriesByAngeltype'); $route->get('/locations', 'Api\LocationsController@index'); $route->get('/locations/{location_id:\d+}/shifts', 'Api\ShiftsController@entriesByLocation'); diff --git a/resources/api/openapi.yml b/resources/api/openapi.yml index 1d9608bac..2d7e7862f 100644 --- a/resources/api/openapi.yml +++ b/resources/api/openapi.yml @@ -1,7 +1,7 @@ openapi: 3.0.3 info: - version: 0.1.0-beta + version: 0.1.2-beta title: Engelsystem description: > This API is as stable as a **beta** version might be. @@ -111,21 +111,33 @@ components: UserAngelType: allOf: - $ref: '#/components/schemas/AngelType' + - $ref: '#/components/schemas/UserAngelTypeRelation' + AngelTypeUser: + allOf: - type: object properties: - confirmed: - type: boolean - example: true - description: > - If the user is confirmed - (either by the angel type not requiring confirmation, being a supporter or being confirmed by one). - supporter: - type: boolean - example: false - description: If the user is a supporter of the angel type. + user: + $ref: '#/components/schemas/Reference' required: + - user - confirmed - supporter + - $ref: '#/components/schemas/UserAngelTypeRelation' + UserAngelTypeRelation: + properties: + confirmed: + type: boolean + example: true + description: > + If the user is confirmed + (either by the angel type not requiring confirmation, being a supporter or being confirmed by one). + supporter: + type: boolean + example: false + description: If the user is a supporter of the angel type. + required: + - confirmed + - supporter News: type: object properties: @@ -537,6 +549,39 @@ paths: '404': $ref: '#/components/responses/NotFoundError' + /angeltypes/{id}/users: + parameters: + - name: id + in: path + required: true + description: The angel type identifier + example: 42 + schema: + type: integer + get: + tags: + - angel type + - user + summary: Get all users of the requested angel type + responses: + '200': + description: Ok + content: + application/json: + schema: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/AngelTypeUser' + '401': + $ref: '#/components/responses/UnauthorizedError' + '403': + $ref: '#/components/responses/ForbiddenError' + '404': + $ref: '#/components/responses/NotFoundError' + /news: get: tags: diff --git a/src/Controllers/Api/Resources/AngelTypeResource.php b/src/Controllers/Api/Resources/AngelTypeResource.php index bcfd81f90..1027d1fea 100644 --- a/src/Controllers/Api/Resources/AngelTypeResource.php +++ b/src/Controllers/Api/Resources/AngelTypeResource.php @@ -6,11 +6,12 @@ use Engelsystem\Models\AngelType; use Engelsystem\Models\BaseModel; +use Illuminate\Database\Eloquent\Relations\Pivot; use Illuminate\Support\Collection; class AngelTypeResource extends BasicResource { - protected Collection | BaseModel | AngelType $model; + protected Collection | BaseModel | Pivot | AngelType $model; public function toArray(): array { diff --git a/src/Controllers/Api/Resources/BasicResource.php b/src/Controllers/Api/Resources/BasicResource.php index de0a43d80..0e48e1ac6 100644 --- a/src/Controllers/Api/Resources/BasicResource.php +++ b/src/Controllers/Api/Resources/BasicResource.php @@ -7,13 +7,14 @@ use Engelsystem\Models\BaseModel; use Illuminate\Contracts\Support\Arrayable; use Illuminate\Contracts\Support\Jsonable; +use Illuminate\Database\Eloquent\Relations\Pivot; use Illuminate\Support\Collection; use Stringable; /** @phpstan-consistent-constructor */ abstract class BasicResource implements Arrayable, Jsonable, Stringable { - public function __construct(protected BaseModel | Collection $model) + public function __construct(protected Collection | BaseModel | Pivot $model) { } diff --git a/src/Controllers/Api/Resources/LocationResource.php b/src/Controllers/Api/Resources/LocationResource.php index cf1028c85..74585cb58 100644 --- a/src/Controllers/Api/Resources/LocationResource.php +++ b/src/Controllers/Api/Resources/LocationResource.php @@ -6,11 +6,12 @@ use Engelsystem\Models\BaseModel; use Engelsystem\Models\Location; +use Illuminate\Database\Eloquent\Relations\Pivot; use Illuminate\Support\Collection; class LocationResource extends BasicResource { - protected Collection | BaseModel | Location $model; + protected Collection | BaseModel | Pivot | Location $model; public function toArray(): array { diff --git a/src/Controllers/Api/Resources/NewsResource.php b/src/Controllers/Api/Resources/NewsResource.php index 63a31f170..dbd042102 100644 --- a/src/Controllers/Api/Resources/NewsResource.php +++ b/src/Controllers/Api/Resources/NewsResource.php @@ -6,11 +6,12 @@ use Engelsystem\Models\BaseModel; use Engelsystem\Models\News; +use Illuminate\Database\Eloquent\Relations\Pivot; use Illuminate\Support\Collection; class NewsResource extends BasicResource { - protected Collection | BaseModel | News $model; + protected Collection | BaseModel | Pivot | News $model; public function toArray(): array { diff --git a/src/Controllers/Api/Resources/ShiftResource.php b/src/Controllers/Api/Resources/ShiftResource.php index 204595934..be1f752c5 100644 --- a/src/Controllers/Api/Resources/ShiftResource.php +++ b/src/Controllers/Api/Resources/ShiftResource.php @@ -7,11 +7,12 @@ use Engelsystem\Models\BaseModel; use Engelsystem\Models\Shifts\Shift; use Illuminate\Contracts\Support\Arrayable; +use Illuminate\Database\Eloquent\Relations\Pivot; use Illuminate\Support\Collection; class ShiftResource extends BasicResource { - protected Collection | BaseModel | Shift $model; + protected Collection | BaseModel | Pivot | Shift $model; public function toArray(array | Arrayable $location = []): array { diff --git a/src/Controllers/Api/Resources/ShiftTypeResource.php b/src/Controllers/Api/Resources/ShiftTypeResource.php index cd1b26ed0..a23406067 100644 --- a/src/Controllers/Api/Resources/ShiftTypeResource.php +++ b/src/Controllers/Api/Resources/ShiftTypeResource.php @@ -6,11 +6,12 @@ use Engelsystem\Models\BaseModel; use Engelsystem\Models\Shifts\ShiftType; +use Illuminate\Database\Eloquent\Relations\Pivot; use Illuminate\Support\Collection; class ShiftTypeResource extends BasicResource { - protected Collection | BaseModel | ShiftType $model; + protected Collection | BaseModel | Pivot | ShiftType $model; public function toArray(): array { diff --git a/src/Controllers/Api/Resources/UserAngelTypeReferenceResource.php b/src/Controllers/Api/Resources/UserAngelTypeReferenceResource.php new file mode 100644 index 000000000..54ece78cb --- /dev/null +++ b/src/Controllers/Api/Resources/UserAngelTypeReferenceResource.php @@ -0,0 +1,30 @@ +model->pivotRelated; + /** @var AngelType $angelType */ + $angelType = $this->model->pivotParent; + /** @var UserAngelType $userAngelType */ + $userAngelType = $this->model; + + return [ + 'user' => UserResource::toIdentifierArray($user), + 'confirmed' => !$angelType->restricted + || $userAngelType->supporter + || $userAngelType->confirm_user_id, + 'supporter' => $userAngelType->supporter, + ]; + } +} diff --git a/src/Controllers/Api/Resources/UserResource.php b/src/Controllers/Api/Resources/UserResource.php index 2793cc8c9..02e06ccc4 100644 --- a/src/Controllers/Api/Resources/UserResource.php +++ b/src/Controllers/Api/Resources/UserResource.php @@ -6,11 +6,12 @@ use Engelsystem\Models\BaseModel; use Engelsystem\Models\User\User; +use Illuminate\Database\Eloquent\Relations\Pivot; use Illuminate\Support\Collection; class UserResource extends BasicResource { - protected Collection | BaseModel | User $model; + protected Collection | BaseModel | Pivot | User $model; public function toArray(): array { diff --git a/src/Controllers/Api/UsersController.php b/src/Controllers/Api/UsersController.php index 9141d248f..e487b1b2f 100644 --- a/src/Controllers/Api/UsersController.php +++ b/src/Controllers/Api/UsersController.php @@ -4,12 +4,16 @@ namespace Engelsystem\Controllers\Api; +use Engelsystem\Controllers\Api\Resources\UserAngelTypeReferenceResource; use Engelsystem\Controllers\Api\Resources\UserDetailResource; use Engelsystem\Controllers\Api\Resources\UserResource; use Engelsystem\Http\Request; use Engelsystem\Http\Response; +use Engelsystem\Models\AngelType; use Engelsystem\Models\BaseModel; use Engelsystem\Models\User\User; +use Engelsystem\Models\UserAngelType; +use Illuminate\Database\Eloquent\Collection; class UsersController extends ApiController { @@ -40,4 +44,30 @@ public function user(Request $request): Response return $this->response ->withContent(json_encode($data)); } + + public function entriesByAngeltype(Request $request): Response + { + $id = (int) $request->getAttribute('angeltype_id'); + /** @var AngelType $angelType */ + $angelType = AngelType::findOrFail($id); + + /** @var User[]|Collection $models */ + $models = $angelType->userAngelTypes() + ->orderBy('name') + ->get(); + + /** @var UserAngelType[]|Collection $models */ + $models = $models->map(function (User $model) { + // Patch to use the existing user model instead of a partially populated one + $model->pivot->setRelatedModel($model); + return $model->pivot; + }); + + /** @var UserAngelTypeReferenceResource[]|Collection $models */ + $models = UserAngelTypeReferenceResource::collection($models); + + $data = ['data' => $models]; + return $this->response + ->withContent(json_encode($data)); + } } diff --git a/tests/Unit/Controllers/Api/UsersControllerTest.php b/tests/Unit/Controllers/Api/UsersControllerTest.php index 3539ecfa1..ebaf6607b 100644 --- a/tests/Unit/Controllers/Api/UsersControllerTest.php +++ b/tests/Unit/Controllers/Api/UsersControllerTest.php @@ -8,6 +8,7 @@ use Engelsystem\Helpers\Authenticator; use Engelsystem\Http\Request; use Engelsystem\Http\Response; +use Engelsystem\Models\AngelType; use Engelsystem\Models\User\Contact; use Engelsystem\Models\User\PersonalData; use Engelsystem\Models\User\Settings; @@ -117,4 +118,40 @@ public function testUserById(): void $this->assertEquals($otherUser->id, $data['data']['id']); $this->assertArrayNotHasKey('dates', $data['data']); } + + /** + * @covers \Engelsystem\Controllers\Api\UsersController::entriesByAngeltype + * @covers \Engelsystem\Controllers\Api\Resources\UserAngelTypeReferenceResource::toArray + */ + public function testEntriesByAngeltype(): void + { + /** @var User $user */ + $user = User::factory()->create(); + $controller = new UsersController(new Response()); + /** @var AngelType $angelType */ + $angelType = AngelType::factory()->create(); + $user->userAngelTypes()->attach($angelType); + $request = new Request([], [], ['angeltype_id' => $angelType->id]); + + $response = $controller->entriesByAngeltype($request); + $this->validateApiResponse('/angeltypes/{id}/users', 'get', $response); + + $this->assertEquals(['application/json'], $response->getHeader('content-type')); + $this->assertJson($response->getContent()); + + $data = json_decode($response->getContent(), true); + + $this->assertArrayHasKey('data', $data); + $this->assertIsArray($data['data']); + $this->assertNotEmpty($data['data']); + + $firstEntry = $data['data'][0]; + $this->assertArrayHasKey('user', $firstEntry); + $this->assertArrayHasKey('confirmed', $firstEntry); + $this->assertArrayHasKey('supporter', $firstEntry); + + $firstUser = $firstEntry['user']; + $this->assertArrayHasKey('id', $firstUser); + $this->assertEquals($user->id, $firstUser['id']); + } } From 270f3decdcacfed0414e83a41b921f80ebe17518 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Fri, 2 May 2025 23:52:36 +0200 Subject: [PATCH 099/157] Api: Simplify users/[id]/angeltypes --- resources/api/openapi.yml | 11 +++++++---- .../Api/Resources/UserAngelTypeResource.php | 17 ++++++++++++----- .../Controllers/Api/AngelTypeControllerTest.php | 2 +- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/resources/api/openapi.yml b/resources/api/openapi.yml index 2d7e7862f..5d567f0a6 100644 --- a/resources/api/openapi.yml +++ b/resources/api/openapi.yml @@ -1,7 +1,7 @@ openapi: 3.0.3 info: - version: 0.1.2-beta + version: 0.2.0-beta title: Engelsystem description: > This API is as stable as a **beta** version might be. @@ -110,7 +110,12 @@ components: - url UserAngelType: allOf: - - $ref: '#/components/schemas/AngelType' + - type: object + properties: + angeltype: + $ref: '#/components/schemas/Reference' + required: + - angeltype - $ref: '#/components/schemas/UserAngelTypeRelation' AngelTypeUser: allOf: @@ -120,8 +125,6 @@ components: $ref: '#/components/schemas/Reference' required: - user - - confirmed - - supporter - $ref: '#/components/schemas/UserAngelTypeRelation' UserAngelTypeRelation: properties: diff --git a/src/Controllers/Api/Resources/UserAngelTypeResource.php b/src/Controllers/Api/Resources/UserAngelTypeResource.php index 70da33322..d050a80b6 100644 --- a/src/Controllers/Api/Resources/UserAngelTypeResource.php +++ b/src/Controllers/Api/Resources/UserAngelTypeResource.php @@ -4,16 +4,23 @@ namespace Engelsystem\Controllers\Api\Resources; +use Engelsystem\Models\AngelType; + class UserAngelTypeResource extends AngelTypeResource { public function toArray(): array { + /** @var AngelType $angelType */ + $angelType = $this->model; + /** @var AngelType $angelType */ + $userAngelType = $this->model->pivot; + return [ - ...parent::toArray(), - 'confirmed' => !$this->model->restricted - || $this->model->pivot->supporter - || $this->model->pivot->confirm_user_id, - 'supporter' => $this->model->pivot->supporter, + 'angeltype' => AngelTypeResource::toIdentifierArray($angelType), + 'confirmed' => !$angelType->restricted + || $userAngelType->supporter + || $userAngelType->confirm_user_id, + 'supporter' => $userAngelType->supporter, ]; } } diff --git a/tests/Unit/Controllers/Api/AngelTypeControllerTest.php b/tests/Unit/Controllers/Api/AngelTypeControllerTest.php index 12a1f4865..2d79451d4 100644 --- a/tests/Unit/Controllers/Api/AngelTypeControllerTest.php +++ b/tests/Unit/Controllers/Api/AngelTypeControllerTest.php @@ -63,7 +63,7 @@ public function testOfUser(): void $this->assertArrayHasKey('data', $data); $this->assertCount(3, $data['data']); $this->assertCount(1, collect($data['data'])->filter(function ($item) use ($items) { - return $item['name'] == $items->first()->angelType->name; + return $item['angeltype']['id'] == $items->first()->angelType->id; })); } From 88fa3fe37a1718a9bae6d846967272a026c855c7 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sat, 3 May 2025 00:30:34 +0200 Subject: [PATCH 100/157] Api: Added /users/[id]/worklogs --- config/routes.php | 1 + resources/api/openapi.yml | 67 +++++++++++++++++++ .../Api/Resources/WorklogResource.php | 28 ++++++++ src/Controllers/Api/UsersController.php | 20 ++++++ .../Controllers/Api/UsersControllerTest.php | 34 ++++++++++ 5 files changed, 150 insertions(+) create mode 100644 src/Controllers/Api/Resources/WorklogResource.php diff --git a/config/routes.php b/config/routes.php index 148ca0ccb..e2566038a 100644 --- a/config/routes.php +++ b/config/routes.php @@ -155,6 +155,7 @@ function (RouteCollector $route): void { $route->get('/users/{user_id:(?:\d+|self)}', 'Api\UsersController@user'); $route->get('/users/{user_id:(?:\d+|self)}/angeltypes', 'Api\AngelTypeController@ofUser'); $route->get('/users/{user_id:(?:\d+|self)}/shifts', 'Api\ShiftsController@entriesByUser'); + $route->get('/users/{user_id:(?:\d+|self)}/worklogs', 'Api\UsersController@worklogs'); $route->addRoute( ['POST', 'PUT', 'DELETE', 'PATCH'], diff --git a/resources/api/openapi.yml b/resources/api/openapi.yml index 5d567f0a6..da71f1bcf 100644 --- a/resources/api/openapi.yml +++ b/resources/api/openapi.yml @@ -40,6 +40,8 @@ tags: description: Shift types - name: user description: Users + - name: worklog + description: Worklogs security: - bearer-auth: [ ] @@ -404,6 +406,36 @@ components: - dates - language - arrived + Worklog: + type: object + properties: + id: + type: integer + example: 42 + description: + type: string + example: Main stage build-up + hours: + type: number + format: float + example: 4.23 + description: Hours worked with up to two decimals precision + created_by: + $ref: '#/components/schemas/Reference' + worked_at: + $ref: '#/components/schemas/DateTime' + created_at: + $ref: '#/components/schemas/DateTimeOptional' + updated_at: + $ref: '#/components/schemas/DateTimeOptional' + required: + - id + - description + - hours + - created_by + - worked_at + - created_at + - updated_at DateTime: type: string @@ -843,6 +875,41 @@ paths: '404': $ref: '#/components/responses/NotFoundError' + /users/{id}/worklogs: + parameters: + - name: id + in: path + required: true + description: The user identifier or `self` + example: 42 + schema: + oneOf: + - type: string + - type: integer + get: + tags: + - worklog + - user + summary: Get all worklogs of the requested user + responses: + '200': + description: Ok + content: + application/json: + schema: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/Worklog' + '401': + $ref: '#/components/responses/UnauthorizedError' + '403': + $ref: '#/components/responses/ForbiddenError' + '404': + $ref: '#/components/responses/NotFoundError' + /info: get: tags: diff --git a/src/Controllers/Api/Resources/WorklogResource.php b/src/Controllers/Api/Resources/WorklogResource.php new file mode 100644 index 000000000..d6555d45e --- /dev/null +++ b/src/Controllers/Api/Resources/WorklogResource.php @@ -0,0 +1,28 @@ + $this->model->id, + 'description' => $this->model->comment, + 'hours' => $this->model->hours, + 'created_by' => UserResource::toIdentifierArray($this->model->creator), + 'worked_at' => $this->model->worked_at, + 'created_at' => $this->model->created_at, + 'updated_at' => $this->model->updated_at, + ]; + } +} diff --git a/src/Controllers/Api/UsersController.php b/src/Controllers/Api/UsersController.php index e487b1b2f..0508efd8e 100644 --- a/src/Controllers/Api/UsersController.php +++ b/src/Controllers/Api/UsersController.php @@ -7,6 +7,7 @@ use Engelsystem\Controllers\Api\Resources\UserAngelTypeReferenceResource; use Engelsystem\Controllers\Api\Resources\UserDetailResource; use Engelsystem\Controllers\Api\Resources\UserResource; +use Engelsystem\Controllers\Api\Resources\WorklogResource; use Engelsystem\Http\Request; use Engelsystem\Http\Response; use Engelsystem\Models\AngelType; @@ -70,4 +71,23 @@ public function entriesByAngeltype(Request $request): Response return $this->response ->withContent(json_encode($data)); } + + public function worklogs(Request $request): Response + { + $id = (int) $request->getAttribute('user_id'); + /** @var User $user */ + $user = User::findOrFail($id); + + $models = $user->worklogs(); + + $models = $models + ->orderBy('worked_at') + ->get(); + + $models = WorklogResource::collection($models); + + $data = ['data' => $models]; + return $this->response + ->withContent(json_encode($data)); + } } diff --git a/tests/Unit/Controllers/Api/UsersControllerTest.php b/tests/Unit/Controllers/Api/UsersControllerTest.php index ebaf6607b..4e8728eb9 100644 --- a/tests/Unit/Controllers/Api/UsersControllerTest.php +++ b/tests/Unit/Controllers/Api/UsersControllerTest.php @@ -14,6 +14,7 @@ use Engelsystem\Models\User\Settings; use Engelsystem\Models\User\State; use Engelsystem\Models\User\User; +use Engelsystem\Models\Worklog; use PHPUnit\Framework\MockObject\MockObject; class UsersControllerTest extends ApiBaseControllerTest @@ -154,4 +155,37 @@ public function testEntriesByAngeltype(): void $this->assertArrayHasKey('id', $firstUser); $this->assertEquals($user->id, $firstUser['id']); } + + /** + * @covers \Engelsystem\Controllers\Api\UsersController::worklogs + * @covers \Engelsystem\Controllers\Api\Resources\WorklogResource::toArray + */ + public function testWorklogs(): void + { + /** @var User $user */ + $user = User::factory()->create(); + $controller = new UsersController(new Response()); + /** @var Worklog $worklog */ + $worklog = Worklog::factory()->create(['user_id' => $user->id, 'hours' => 1.23]); + $request = new Request([], [], ['user_id' => $user->id]); + + $response = $controller->worklogs($request); + $this->validateApiResponse('/users/{id}/worklogs', 'get', $response); + + $this->assertEquals(['application/json'], $response->getHeader('content-type')); + $this->assertJson($response->getContent()); + + $data = json_decode($response->getContent(), true); + + $this->assertArrayHasKey('data', $data); + $this->assertIsArray($data['data']); + $this->assertNotEmpty($data['data']); + + $firstEntry = $data['data'][0]; + $this->assertArrayHasKey('id', $firstEntry); + $this->assertArrayHasKey('description', $firstEntry); + $this->assertArrayHasKey('hours', $firstEntry); + + $this->assertEquals($worklog->hours, $firstEntry['hours']); + } } From b2d02ea50c7d7e046f51ac82742cccd200e323fb Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sun, 21 Sep 2025 23:38:05 +0200 Subject: [PATCH 101/157] Api: Fix naming --- resources/api/openapi.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/resources/api/openapi.yml b/resources/api/openapi.yml index da71f1bcf..82f76255e 100644 --- a/resources/api/openapi.yml +++ b/resources/api/openapi.yml @@ -556,7 +556,7 @@ paths: - name: id in: path required: true - description: The angel type identifier + description: Angel type identifier example: 42 schema: type: integer @@ -589,7 +589,7 @@ paths: - name: id in: path required: true - description: The angel type identifier + description: Angel type identifier example: 42 schema: type: integer @@ -666,7 +666,7 @@ paths: - name: id in: path required: true - description: The locations identifier + description: Location identifier example: 42 schema: type: integer @@ -721,7 +721,7 @@ paths: - name: id in: path required: true - description: The shift types identifier + description: Shift type identifier example: 42 schema: type: integer @@ -776,7 +776,7 @@ paths: - name: id in: path required: true - description: The user identifier or `self` + description: User identifier or `self` example: 42 schema: oneOf: @@ -810,7 +810,7 @@ paths: - name: id in: path required: true - description: The user identifier or `self` + description: User identifier or `self` example: 42 schema: oneOf: @@ -845,7 +845,7 @@ paths: - name: id in: path required: true - description: The user identifier or `self` + description: User identifier or `self` example: 42 schema: oneOf: @@ -880,7 +880,7 @@ paths: - name: id in: path required: true - description: The user identifier or `self` + description: User identifier or `self` example: 42 schema: oneOf: From f2d44b6ed3bdc822231f329bf97f381b20496cbc Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Tue, 23 Sep 2025 17:11:02 +0200 Subject: [PATCH 102/157] Renamed Worklog comment to description --- db/factories/WorklogFactory.php | 2 +- ..._worklog_rename_comment_to_description.php | 31 +++++++++++ includes/view/User_view.php | 4 +- resources/lang/de_DE/default.po | 4 +- resources/lang/en_US/default.po | 4 +- resources/views/admin/user/edit-worklog.twig | 4 +- .../Admin/UserWorklogController.php | 14 ++--- .../Api/Resources/WorklogResource.php | 2 +- src/Events/Listener/Shifts.php | 4 +- src/Models/Worklog.php | 6 +-- tests/Feature/Helpers/GoodieTest.php | 2 +- .../Admin/UserWorklogControllerTest.php | 52 ++++++++++++------- tests/Unit/Controllers/Metrics/StatsTest.php | 10 ++-- tests/Unit/Events/Listener/ShiftsTest.php | 2 +- tests/Unit/Helpers/UserVouchersTest.php | 3 +- tests/Unit/Models/User/UserTest.php | 20 +++---- tests/Unit/Models/WorklogTest.php | 2 +- 17 files changed, 107 insertions(+), 59 deletions(-) create mode 100644 db/migrations/2025_09_23_worklog_rename_comment_to_description.php diff --git a/db/factories/WorklogFactory.php b/db/factories/WorklogFactory.php index 1ad7ef48b..990d8ad00 100644 --- a/db/factories/WorklogFactory.php +++ b/db/factories/WorklogFactory.php @@ -19,7 +19,7 @@ public function definition(): array 'user_id' => User::factory(), 'creator_id' => User::factory(), 'hours' => $this->faker->randomFloat(2, 0.01, 10), - 'comment' => $this->faker->text(30), + 'description' => $this->faker->text(30), 'worked_at' => $this->faker->dateTimeThisMonth(), 'night_shift' => $this->faker->boolean(), ]; diff --git a/db/migrations/2025_09_23_worklog_rename_comment_to_description.php b/db/migrations/2025_09_23_worklog_rename_comment_to_description.php new file mode 100644 index 000000000..c6ede0dbe --- /dev/null +++ b/db/migrations/2025_09_23_worklog_rename_comment_to_description.php @@ -0,0 +1,31 @@ +schema->table('worklogs', function (Blueprint $table): void { + $table->renameColumn('comment', 'description'); + }); + } + + /** + * Reverse the migration + */ + public function down(): void + { + $this->schema->table('worklogs', function (Blueprint $table): void { + $table->renameColumn('description', 'comment'); + }); + } +} diff --git a/includes/view/User_view.php b/includes/view/User_view.php index 8c21656a0..930401e4d 100644 --- a/includes/view/User_view.php +++ b/includes/view/User_view.php @@ -552,7 +552,7 @@ function User_view_worklog(Worklog $worklog, $admin_user_worklog_privilege, $its 'hints' => $night_shift, 'location' => '', 'shift_info' => __('Work log entry'), - 'comment' => htmlspecialchars($worklog->comment) . '
    ' + 'comment' => htmlspecialchars($worklog->description) . '
    ' . sprintf( __('Added by %s at %s'), User_Nick_render($worklog->creator), @@ -633,7 +633,7 @@ function User_view( 'hints' => '', 'location' => __('Location'), 'shift_info' => __('Name & Workmates'), - 'comment' => __('worklog.comment'), + 'comment' => __('worklog.description'), 'actions' => __('general.actions'), ], $my_shifts)); } elseif ($user_source->state->force_active && config('enable_force_active')) { diff --git a/resources/lang/de_DE/default.po b/resources/lang/de_DE/default.po index fa897f0d5..d38bbcf31 100644 --- a/resources/lang/de_DE/default.po +++ b/resources/lang/de_DE/default.po @@ -1877,8 +1877,8 @@ msgstr "Einsatzdatum" msgid "worklog.hours" msgstr "Arbeitsstunden" -msgid "worklog.comment" -msgstr "Kommentar" +msgid "worklog.description" +msgstr "Beschreibung" msgid "worklog.night_shift" msgstr "Arbeitseinsatz ist eine Nachtschicht" diff --git a/resources/lang/en_US/default.po b/resources/lang/en_US/default.po index a720d1441..502c69da3 100644 --- a/resources/lang/en_US/default.po +++ b/resources/lang/en_US/default.po @@ -704,8 +704,8 @@ msgstr "Work date" msgid "worklog.hours" msgstr "Work hours" -msgid "worklog.comment" -msgstr "Comment" +msgid "worklog.description" +msgstr "Description" msgid "worklog.night_shift" msgstr "Work log is a night shift" diff --git a/resources/views/admin/user/edit-worklog.twig b/resources/views/admin/user/edit-worklog.twig index bd6282701..479a1a2eb 100644 --- a/resources/views/admin/user/edit-worklog.twig +++ b/resources/views/admin/user/edit-worklog.twig @@ -35,8 +35,8 @@ 'step': '0.01', 'min': 0, }) }} - {{ f.input('comment', __('worklog.comment'), { - 'value': comment, + {{ f.input('description', __('worklog.description'), { + 'value': description, 'required': true, 'max_length': 200, }) }} diff --git a/src/Controllers/Admin/UserWorklogController.php b/src/Controllers/Admin/UserWorklogController.php index 63169ae94..9ae3907b9 100644 --- a/src/Controllers/Admin/UserWorklogController.php +++ b/src/Controllers/Admin/UserWorklogController.php @@ -53,7 +53,7 @@ public function editWorklog(Request $request): Response $user, $worklog->worked_at, $worklog->hours, - $worklog->comment, + $worklog->description, $worklog->night_shift, true ); @@ -70,7 +70,7 @@ public function saveWorklog(Request $request): Response $data = $this->validate($request, [ 'work_date' => 'required|date:Y-m-d', 'work_hours' => 'float|min:0', - 'comment' => 'required|max:200', + 'description' => 'required|max:200', 'night_shift' => 'optional|checked', ]); @@ -88,7 +88,7 @@ public function saveWorklog(Request $request): Response } $worklog->worked_at = $data['work_date']; $worklog->hours = $data['work_hours']; - $worklog->comment = $data['comment']; + $worklog->description = $data['description']; $worklog->night_shift = $data['night_shift'] ?: false; $worklog->save(); @@ -100,7 +100,7 @@ public function saveWorklog(Request $request): Response 'id' => $user->id, 'time' => $worklog->worked_at, 'hours' => $worklog->hours, - 'text' => $worklog->comment, + 'text' => $worklog->description, 'night_shift' => $worklog->night_shift ? ' at night' : '', ] ); @@ -145,7 +145,7 @@ public function deleteWorklog(Request $request): Response 'id' => $worklog->user->id, 'time' => $worklog->worked_at, 'hours' => $worklog->hours, - 'text' => $worklog->comment, + 'text' => $worklog->description, 'night_shift' => $worklog->night_shift ? ' at night' : '', ] ); @@ -159,7 +159,7 @@ private function showEditWorklog( User $user, Carbon $work_date, float $work_hours = 0, - string $comment = '', + string $description = '', bool $night_shift = false, bool $is_edit = false ): Response { @@ -169,7 +169,7 @@ private function showEditWorklog( 'userdata' => $user, 'work_date' => $work_date, 'work_hours' => $work_hours, - 'comment' => $comment, + 'description' => $description, 'night_shift' => $night_shift, 'is_edit' => $is_edit, ] diff --git a/src/Controllers/Api/Resources/WorklogResource.php b/src/Controllers/Api/Resources/WorklogResource.php index d6555d45e..ce02eaec0 100644 --- a/src/Controllers/Api/Resources/WorklogResource.php +++ b/src/Controllers/Api/Resources/WorklogResource.php @@ -17,7 +17,7 @@ public function toArray(): array { return [ 'id' => $this->model->id, - 'description' => $this->model->comment, + 'description' => $this->model->description, 'hours' => $this->model->hours, 'created_by' => UserResource::toIdentifierArray($this->model->creator), 'worked_at' => $this->model->worked_at, diff --git a/src/Events/Listener/Shifts.php b/src/Events/Listener/Shifts.php index c9a8a40f3..bc7e628fd 100644 --- a/src/Events/Listener/Shifts.php +++ b/src/Events/Listener/Shifts.php @@ -35,7 +35,7 @@ public function deletingCreateWorklogs(Shift $shift): void $worklog->hours = (($shift->end->timestamp - $shift->start->timestamp) / 60 / 60) * $shift->getNightShiftMultiplier(); - $worklog->comment = Str::substr(sprintf( + $worklog->description = Str::substr(sprintf( __('%s (%s as %s) in %s, %s - %s'), $shift->shiftType->name, $shift->title, @@ -48,7 +48,7 @@ public function deletingCreateWorklogs(Shift $shift): void $this->log->info( 'Created worklog entry from shift for {user} ({uid}): {worklog})', - ['user' => $worklog->user->name, 'uid' => $worklog->user->id, 'worklog' => $worklog->comment] + ['user' => $worklog->user->name, 'uid' => $worklog->user->id, 'worklog' => $worklog->description] ); } } diff --git a/src/Models/Worklog.php b/src/Models/Worklog.php index 9705bcf0f..174206775 100644 --- a/src/Models/Worklog.php +++ b/src/Models/Worklog.php @@ -15,7 +15,7 @@ * @property int $id * @property int $creator_id * @property float $hours - * @property string $comment + * @property string $description * @property Carbon $worked_at * @property bool $night_shift * @property Carbon|null $created_at @@ -27,7 +27,7 @@ * @method static QueryBuilder|Worklog[] whereCreatorId($value) * @method static QueryBuilder|Worklog[] whereWorkedAt($value) * @method static QueryBuilder|Worklog[] whereHours($value) - * @method static QueryBuilder|Worklog[] whereComment($value) + * @method static QueryBuilder|Worklog[] whereDescription($value) */ class Worklog extends BaseModel { @@ -55,7 +55,7 @@ class Worklog extends BaseModel 'user_id', 'creator_id', 'hours', - 'comment', + 'description', 'worked_at', 'night_shift', ]; diff --git a/tests/Feature/Helpers/GoodieTest.php b/tests/Feature/Helpers/GoodieTest.php index dfe2b47d6..89d8d98ea 100644 --- a/tests/Feature/Helpers/GoodieTest.php +++ b/tests/Feature/Helpers/GoodieTest.php @@ -34,7 +34,7 @@ public function testUserScoreNightShift(): void 'user_id' => $user->id, 'hours' => 3.87, 'creator_id' => $user->id, - 'comment' => '', + 'description' => '', 'worked_at' => Carbon::now()->subHour(), ]); $workLog->save(); diff --git a/tests/Unit/Controllers/Admin/UserWorklogControllerTest.php b/tests/Unit/Controllers/Admin/UserWorklogControllerTest.php index cb28260e7..f8cf7fd05 100644 --- a/tests/Unit/Controllers/Admin/UserWorklogControllerTest.php +++ b/tests/Unit/Controllers/Admin/UserWorklogControllerTest.php @@ -67,7 +67,7 @@ public function testShowAddWorklog(): void $this->assertEquals($this->user->id, $data['userdata']->id); $this->assertEquals(Carbon::today(), $data['work_date']); $this->assertEquals(0, $data['work_hours']); - $this->assertEquals('', $data['comment']); + $this->assertEquals('', $data['description']); $this->assertFalse($data['night_shift']); $this->assertFalse($data['is_edit']); return $this->response; @@ -102,7 +102,7 @@ public function testShowEditWorklog(): void 'user_id' => $this->user->id, 'worked_at' => new Carbon('2022-01-01'), 'hours' => 3.14, - 'comment' => 'a comment', + 'description' => 'a description', 'night_shift' => true, ])->create(); @@ -115,7 +115,7 @@ public function testShowEditWorklog(): void $this->assertEquals($this->user->id, $data['userdata']->id); $this->assertEquals(new Carbon('2022-01-01'), $data['work_date']); $this->assertEquals(3.14, $data['work_hours']); - $this->assertEquals('a comment', $data['comment']); + $this->assertEquals('a description', $data['description']); $this->assertTrue($data['night_shift']); $this->assertTrue($data['is_edit']); return $this->response; @@ -153,10 +153,14 @@ public function testSaveNewWorklog(): void { $work_date = Carbon::today()->format('Y-m-d'); $work_hours = 3.14; - $comment = str_repeat('X', 200); + $description = str_repeat('X', 200); $night_shift = true; - $body = ['work_date' => $work_date, 'work_hours' => $work_hours, 'comment' => $comment, - 'night_shift' => $night_shift]; + $body = [ + 'work_date' => $work_date, + 'work_hours' => $work_hours, + 'description' => $description, + 'night_shift' => $night_shift, + ]; $request = $this->request->withAttribute('user_id', $this->user->id)->withParsedBody($body); $this->setExpects($this->auth, 'user', null, $this->user, $this->any()); $this->redirect->expects($this->once()) @@ -174,7 +178,7 @@ public function testSaveNewWorklog(): void $this->assertEquals($this->user->id, $new_worklog->user->id); $this->assertEquals($work_date, $new_worklog->worked_at->format('Y-m-d')); $this->assertEquals($work_hours, $new_worklog->hours); - $this->assertEquals($comment, $new_worklog->comment); + $this->assertEquals($description, $new_worklog->description); $this->assertEquals($night_shift, $new_worklog->night_shift); } @@ -184,7 +188,11 @@ public function testSaveNewWorklog(): void */ public function testOverwriteWorklogWithUnknownWorklogIdThrows(): void { - $body = ['work_date' => Carbon::today()->format('Y-m-d'), 'work_hours' => 3.14, 'comment' => 'a comment']; + $body = [ + 'work_date' => Carbon::today()->format('Y-m-d'), + 'work_hours' => 3.14, + 'description' => 'a description', + ]; $request = $this->request ->withAttribute('user_id', $this->user->id) ->withAttribute('worklog_id', 1234) @@ -203,7 +211,11 @@ public function testOverwriteWorklogWithWorklogNotAssociatedToUserThrows(): void /** @var Worklog $worklog */ $worklog = Worklog::factory(['user_id' => $user2->id])->create(); - $body = ['work_date' => Carbon::today()->format('Y-m-d'), 'work_hours' => 3.14, 'comment' => 'a comment']; + $body = [ + 'work_date' => Carbon::today()->format('Y-m-d'), + 'work_hours' => 3.14, + 'description' => 'a description', + ]; $request = $this->request ->withAttribute('user_id', $this->user->id) ->withAttribute('worklog_id', $worklog->id) @@ -221,10 +233,14 @@ public function testOverwriteWorklog(): void $worklog = Worklog::factory(['user_id' => $this->user->id])->create(); $work_date = Carbon::today()->format('Y-m-d'); $work_hours = 3.14; - $comment = str_repeat('X', 200); + $description = str_repeat('X', 200); $night_shift = true; - $body = ['work_date' => $work_date, 'work_hours' => $work_hours, 'comment' => $comment, - 'night_shift' => $night_shift]; + $body = [ + 'work_date' => $work_date, + 'work_hours' => $work_hours, + 'description' => $description, + 'night_shift' => $night_shift, + ]; $request = $this->request ->withAttribute('user_id', $this->user->id) @@ -242,7 +258,7 @@ public function testOverwriteWorklog(): void $worklog = Worklog::find($worklog->id); $this->assertEquals($work_date, $worklog->worked_at->format('Y-m-d')); $this->assertEquals($work_hours, $worklog->hours); - $this->assertEquals($comment, $worklog->comment); + $this->assertEquals($description, $worklog->description); $this->assertEquals($night_shift, $worklog->night_shift); } @@ -346,15 +362,15 @@ public function invalidSaveWorklogParams(): array $today = Carbon::today()->format('Y-m-d'); return [ // missing work_date - [['work_hours' => 3.14, 'comment' => 'com']], + [['work_hours' => 3.14, 'description' => 'com']], // missing work_hours - [['work_date' => $today, 'comment' => 'com']], - // missing comment + [['work_date' => $today, 'description' => 'com']], + // missing description [['work_date' => $today, 'work_hours' => 3.14]], // too low work_hours - [['work_date' => $today, 'work_hours' => -.1, 'comment' => 'com']], + [['work_date' => $today, 'work_hours' => -.1, 'description' => 'com']], // too low work_hours - [['work_date' => $today, 'work_hours' => 3.14, 'comment' => str_repeat('X', 201)]], + [['work_date' => $today, 'work_hours' => 3.14, 'description' => str_repeat('X', 201)]], ]; } diff --git a/tests/Unit/Controllers/Metrics/StatsTest.php b/tests/Unit/Controllers/Metrics/StatsTest.php index e23b28ea2..735323e0d 100644 --- a/tests/Unit/Controllers/Metrics/StatsTest.php +++ b/tests/Unit/Controllers/Metrics/StatsTest.php @@ -152,11 +152,11 @@ public function testWorklogSeconds(): void { $this->addUsers(); $worklogData = [ - 'user_id' => 1, - 'creator_id' => 1, - 'hours' => 2.4, - 'comment' => '', - 'worked_at' => new Carbon(), + 'user_id' => 1, + 'creator_id' => 1, + 'hours' => 2.4, + 'description' => '', + 'worked_at' => new Carbon(), ]; (new Worklog($worklogData))->save(); (new Worklog(['hours' => 1.2, 'user_id' => 3] + $worklogData))->save(); diff --git a/tests/Unit/Events/Listener/ShiftsTest.php b/tests/Unit/Events/Listener/ShiftsTest.php index 669d9addd..55c13fa3c 100644 --- a/tests/Unit/Events/Listener/ShiftsTest.php +++ b/tests/Unit/Events/Listener/ShiftsTest.php @@ -53,7 +53,7 @@ public function testDeletingCreateWorklogs(): void $this->assertCount(1, $this->user->worklogs); $this->assertEquals($this->shift->isNightShift() ? 4 : 2, $this->user->worklogs[0]->hours); - $this->assertEquals('Text', $this->user->worklogs[0]->comment); + $this->assertEquals('Text', $this->user->worklogs[0]->description); $this->assertTrue($this->log->hasInfoThatContains('Created worklog entry')); } diff --git a/tests/Unit/Helpers/UserVouchersTest.php b/tests/Unit/Helpers/UserVouchersTest.php index 91820b0da..f87cac3e1 100644 --- a/tests/Unit/Helpers/UserVouchersTest.php +++ b/tests/Unit/Helpers/UserVouchersTest.php @@ -92,7 +92,8 @@ public function setUp(): void Worklog::factory()->create([ 'user_id' => $this->user->id, 'creator_id' => $user2->id, - 'hours' => 4, 'worked_at' => Carbon::today(), + 'hours' => 4, + 'worked_at' => Carbon::today(), ]); // worklog tomorrow, 2 hours long Worklog::factory()->create([ diff --git a/tests/Unit/Models/User/UserTest.php b/tests/Unit/Models/User/UserTest.php index e381ea11e..a0ba01213 100644 --- a/tests/Unit/Models/User/UserTest.php +++ b/tests/Unit/Models/User/UserTest.php @@ -437,11 +437,11 @@ public function testWorklogs(): void { ($user = new User($this->data))->save(); $worklogEntry = Worklog::create([ - 'user_id' => $user->id, - 'creator_id' => $user->id, - 'hours' => 1, - 'comment' => '', - 'worked_at' => Carbon::now(), + 'user_id' => $user->id, + 'creator_id' => $user->id, + 'hours' => 1, + 'description' => '', + 'worked_at' => Carbon::now(), ]); $worklogs = $user->worklogs; @@ -457,11 +457,11 @@ public function testWorklogsCreated(): void { ($user = new User($this->data))->save(); $worklogEntry = Worklog::create([ - 'user_id' => $user->id, - 'creator_id' => $user->id, - 'hours' => 1, - 'comment' => '', - 'worked_at' => Carbon::now(), + 'user_id' => $user->id, + 'creator_id' => $user->id, + 'hours' => 1, + 'description' => '', + 'worked_at' => Carbon::now(), ]); $worklogs = $user->worklogsCreated; diff --git a/tests/Unit/Models/WorklogTest.php b/tests/Unit/Models/WorklogTest.php index c7609a367..f2c70abf6 100644 --- a/tests/Unit/Models/WorklogTest.php +++ b/tests/Unit/Models/WorklogTest.php @@ -22,7 +22,7 @@ public function testCreator(): void $worklog->user()->associate($user1); $worklog->creator()->associate($user2); $worklog->hours = 4.2; - $worklog->comment = 'Lorem ipsum'; + $worklog->description = 'Lorem ipsum'; $worklog->worked_at = new Carbon(); $worklog->night_shift = false; $worklog->save(); From ac0e38771bbdf5d6c016442a37aa7879b38b6250 Mon Sep 17 00:00:00 2001 From: Xu Date: Mon, 6 Oct 2025 17:49:18 +0200 Subject: [PATCH 103/157] no double responsivness --- includes/view/User_view.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/view/User_view.php b/includes/view/User_view.php index 930401e4d..b2529cc7f 100644 --- a/includes/view/User_view.php +++ b/includes/view/User_view.php @@ -627,7 +627,7 @@ function User_view( $admin_user_worklog_privilege, ); if (count($my_shifts) > 0) { - $myshifts_table = div('table-responsive', table([ + $myshifts_table = div('', table([ 'date' => __('Day & Time'), 'duration' => __('Duration'), 'hints' => '', From 4fcac53b6e57683026b2c474cdd4f578f3de760e Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sat, 11 Oct 2025 14:37:17 +0200 Subject: [PATCH 104/157] Fix goodie test coverage --- tests/Feature/Helpers/GoodieTest.php | 3 ++- tests/Unit/Helpers/GoodieTest.php | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/Feature/Helpers/GoodieTest.php b/tests/Feature/Helpers/GoodieTest.php index 89d8d98ea..a3b2accf7 100644 --- a/tests/Feature/Helpers/GoodieTest.php +++ b/tests/Feature/Helpers/GoodieTest.php @@ -24,10 +24,11 @@ class GoodieTest extends ApplicationFeatureTest /** * @covers \Engelsystem\Helpers\Goodie::userScore * @covers \Engelsystem\Helpers\Goodie::shiftScoreQuery + * @covers \Engelsystem\Helpers\Goodie::worklogScoreQuery */ public function testUserScoreNightShift(): void { - $user = new User(['name' => '', 'email' => '', 'password' => '', 'api_key' => '']); + $user = new User(['name' => 'gn8', 'email' => 'g@n.8', 'password' => '', 'api_key' => '']); $user->save(); $this->createdModels[] = $user; $workLog = new Worklog([ diff --git a/tests/Unit/Helpers/GoodieTest.php b/tests/Unit/Helpers/GoodieTest.php index ad9e37714..06ab809ee 100644 --- a/tests/Unit/Helpers/GoodieTest.php +++ b/tests/Unit/Helpers/GoodieTest.php @@ -28,6 +28,7 @@ public function testShiftScoreQuery(): void /** * @covers \Engelsystem\Helpers\Goodie::userScore + * @covers \Engelsystem\Helpers\Goodie::worklogScoreQuery */ public function testUserScore(): void { From 1c8466eb947d083db885cc39914507e4432dc1eb Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sun, 12 Oct 2025 16:53:39 +0200 Subject: [PATCH 105/157] Fix migration name --- ...> 2025_09_23_000000_worklog_rename_comment_to_description.php} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename db/migrations/{2025_09_23_worklog_rename_comment_to_description.php => 2025_09_23_000000_worklog_rename_comment_to_description.php} (100%) diff --git a/db/migrations/2025_09_23_worklog_rename_comment_to_description.php b/db/migrations/2025_09_23_000000_worklog_rename_comment_to_description.php similarity index 100% rename from db/migrations/2025_09_23_worklog_rename_comment_to_description.php rename to db/migrations/2025_09_23_000000_worklog_rename_comment_to_description.php From e470f057897ff0a2321950940cbf686f389d17ed Mon Sep 17 00:00:00 2001 From: Ole Bittner Date: Sun, 19 Oct 2025 22:56:20 +0200 Subject: [PATCH 106/157] translate "Ask the Heaven" menu item in additional.po --- includes/sys_menu.php | 2 +- resources/lang/de_DE/additional.po | 3 +++ resources/lang/de_DE/default.po | 3 --- resources/lang/en_US/additional.po | 3 +++ 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/includes/sys_menu.php b/includes/sys_menu.php index eb899eb59..34e187152 100644 --- a/includes/sys_menu.php +++ b/includes/sys_menu.php @@ -48,7 +48,7 @@ function make_navigation(): array 'user_shifts' => 'general.shifts', 'angeltypes' => 'angeltypes.angeltypes', 'locations' => ['location.locations', 'locations.view'], - 'questions' => ['Ask the Heaven', 'question.add'], + 'questions' => ['question.menu', 'question.add'], ]; foreach ($pages as $menu_page => $options) { diff --git a/resources/lang/de_DE/additional.po b/resources/lang/de_DE/additional.po index a09b5f296..3e819cb06 100644 --- a/resources/lang/de_DE/additional.po +++ b/resources/lang/de_DE/additional.po @@ -192,6 +192,9 @@ msgstr "FAQ Eintrag erfolgreich gelöscht." msgid "faq.edit.success" msgstr "FAQ Eintrag erfolgreich aktualisiert." +msgid "question.menu" +msgstr "Frag den Himmel" + msgid "question.delete.success" msgstr "Frage erfolgreich gelöscht." diff --git a/resources/lang/de_DE/default.po b/resources/lang/de_DE/default.po index d38bbcf31..f012a494b 100644 --- a/resources/lang/de_DE/default.po +++ b/resources/lang/de_DE/default.po @@ -716,9 +716,6 @@ msgstr "Gib bitte einen Schwänz-Kommentar ein!" msgid "Shift saved." msgstr "Schicht gespeichert." -msgid "Ask the Heaven" -msgstr "Frag den Himmel" - msgid "The administration has not configured any locations yet." msgstr "Die Administratoren habe noch keine Orte eingerichtet." diff --git a/resources/lang/en_US/additional.po b/resources/lang/en_US/additional.po index c19597a23..d17701c39 100644 --- a/resources/lang/en_US/additional.po +++ b/resources/lang/en_US/additional.po @@ -191,6 +191,9 @@ msgstr "FAQ entry successfully deleted." msgid "faq.edit.success" msgstr "FAQ entry successfully updated." +msgid "question.menu" +msgstr "Ask the Heaven" + msgid "question.delete.success" msgstr "Question deleted successfully." From 1b45ad24cda323e8672caffa7fc0bbe5d8f527f9 Mon Sep 17 00:00:00 2001 From: Xu Date: Fri, 8 Aug 2025 13:25:20 +0200 Subject: [PATCH 107/157] Set fastcgi_read_timeout and gateway timeout to 10m (cherry picked from commit 3ae50ffe2df9c446cce17a14743eccd8f0a01901) (cherry picked from commit d6789772bebf5c4f925e854e87175014735054cb) (cherry picked from commit f0a6af4a7f9da2808fad0ef2f76780e442b8e209) --- docker/nginx.conf | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docker/nginx.conf b/docker/nginx.conf index 0f5ca4af0..189152dd5 100644 --- a/docker/nginx.conf +++ b/docker/nginx.conf @@ -28,6 +28,11 @@ http { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $forwarded_proto; + proxy_connect_timeout 600; + proxy_send_timeout 600; + proxy_read_timeout 600; + send_timeout 600; + index index.php; root /var/www/public; @@ -38,6 +43,7 @@ http { location ~ \.php$ { fastcgi_pass localhost:9000; fastcgi_index index.php; + fastcgi_read_timeout 600s; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } From 964160d68d30f66c572184afd7d88a41dc0849cb Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Wed, 6 Aug 2025 15:09:01 +0200 Subject: [PATCH 108/157] Add "exact match" search for arriving angels (cherry picked from commit 29eae6c86ff6abbbc889996deab061022eaa4ed2) (cherry picked from commit a94c5d150cb69b5c067eafc39edc9f739592e5ba) --- includes/pages/admin_arrive.php | 45 ++++++++++++++++++++------------- resources/lang/de_DE/default.po | 3 +++ resources/lang/en_US/default.po | 3 +++ 3 files changed, 34 insertions(+), 17 deletions(-) diff --git a/includes/pages/admin_arrive.php b/includes/pages/admin_arrive.php index 92ecb2f6f..6b42ebdd6 100644 --- a/includes/pages/admin_arrive.php +++ b/includes/pages/admin_arrive.php @@ -2,6 +2,7 @@ use Engelsystem\Helpers\BarChart; use Engelsystem\Models\User\User; +use Illuminate\Support\Str; /** * @return string @@ -21,6 +22,7 @@ function admin_arrive() $request = request(); $admin_arrive = auth()->can('admin_arrive'); + $exactSearch = $request->has('exact'); if ($request->has('search')) { $search = strip_request_item('search'); $search = trim($search); @@ -82,24 +84,30 @@ function admin_arrive() } foreach ($users as $usr) { if (count($tokens) > 0) { - $match = false; - $data = collect($usr->toArray())->flatten()->filter(function ($value) { - // Remove empty values - return !empty($value) && - // Skip datetime - !preg_match('/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{6}Z$/', (string) $value); - }); - $index = join(' ', $data->toArray()); - foreach ($tokens as $token) { - $token = trim($token); - if (!empty($token) && stristr($index, $token)) { - $match = true; - break; - } + if ($exactSearch && Str::lower($usr->name) != Str::lower(implode(' ', $tokens))) { + continue; } - if (!$match) { - continue; + if (!$exactSearch) { + $match = false; + $data = collect($usr->toArray())->flatten()->filter(function ($value) { + // Remove empty values + return !empty($value) && + // Skip datetime + !preg_match('/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{6}Z$/', (string) $value); + }); + $index = join(' ', $data->toArray()); + foreach ($tokens as $token) { + $token = trim($token); + if (!empty($token) && stristr($index, $token)) { + $match = true; + break; + } + } + + if (!$match) { + continue; + } } } @@ -213,7 +221,10 @@ function admin_arrive() $msg . msg(), form([ form_text('search', __('form.search'), $search), - form_submit('submit', icon('search') . __('form.search')), + div('row mb-3 align-items-center', [ + div('col-sm-auto', [form_submit('submit', icon('search') . __('form.search'), '', false)]), + div('col', [form_checkbox('exact', __('form.exact_match'), $exactSearch)]), + ]), ], url('/admin-arrive')), table(array_merge( ['name' => __('general.name'),], diff --git a/resources/lang/de_DE/default.po b/resources/lang/de_DE/default.po index f012a494b..8f0983c8b 100644 --- a/resources/lang/de_DE/default.po +++ b/resources/lang/de_DE/default.po @@ -1452,6 +1452,9 @@ msgstr "Du kannst sie dir unter %3$s anschauen." msgid "form.search" msgstr "Suchen" +msgid "form.exact_match" +msgstr "Genaue Übereinstimmung" + msgid "log.log" msgstr "Logs" diff --git a/resources/lang/en_US/default.po b/resources/lang/en_US/default.po index 502c69da3..607ff6f64 100644 --- a/resources/lang/en_US/default.po +++ b/resources/lang/en_US/default.po @@ -277,6 +277,9 @@ msgstr "You can view it at %3$s" msgid "form.search" msgstr "Search" +msgid "form.exact_match" +msgstr "Exact match" + msgid "log.log" msgstr "Logs" From c1037a1a37a29d4197cebd849418888316a3be73 Mon Sep 17 00:00:00 2001 From: Xu Date: Wed, 6 Aug 2025 12:01:59 +0200 Subject: [PATCH 109/157] fix classes of Shift calendar card delete button (cherry picked from commit b4bf779cc3864206394df63ba7a9749daa7b3564) --- includes/view/ShiftCalendarShiftRenderer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/view/ShiftCalendarShiftRenderer.php b/includes/view/ShiftCalendarShiftRenderer.php index cc1181c97..7e19ddf5f 100644 --- a/includes/view/ShiftCalendarShiftRenderer.php +++ b/includes/view/ShiftCalendarShiftRenderer.php @@ -271,7 +271,7 @@ private function renderShiftHead(Shift $shift, $class, $needed_angeltypes_count) form_submit( 'delete', icon('trash'), - 'btn-' . $class . ' btn-sm border-light text-white ms-1', + 'btn-sm border-light text-white ms-1', false, 'danger', __('form.delete'), From 6dd77fda655e0c022cbec861e7366f22df539b2e Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Wed, 6 Aug 2025 16:06:59 +0200 Subject: [PATCH 110/157] News: Don't mark as updated when only checkboxes change (cherry picked from commit 0370771e85d2d2b92c1a8e2e16b373e8570d4172) --- src/Controllers/Admin/NewsController.php | 4 +++ .../Controllers/Admin/NewsControllerTest.php | 36 ++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/Controllers/Admin/NewsController.php b/src/Controllers/Admin/NewsController.php index 25b5894f2..39d959680 100644 --- a/src/Controllers/Admin/NewsController.php +++ b/src/Controllers/Admin/NewsController.php @@ -84,6 +84,7 @@ public function save(Request $request): Response if (!$news->user) { $news->user()->associate($this->auth->user()); } + $contentChanged = $news->title != $data['title'] || $news->text != $data['text']; $news->title = $data['title']; $news->text = $data['text']; $news->is_meeting = !is_null($data['is_meeting']); @@ -103,6 +104,9 @@ public function save(Request $request): Response $this->addNotification('news.edit.duplicate', NotificationType::ERROR); return $this->showEdit($news, $notify); } + if (!$isNewNews & !$contentChanged) { + $news->timestamps = false; + } $news->save(); if ($isNewNews) { diff --git a/tests/Unit/Controllers/Admin/NewsControllerTest.php b/tests/Unit/Controllers/Admin/NewsControllerTest.php index 34061e442..ce04ffc1b 100644 --- a/tests/Unit/Controllers/Admin/NewsControllerTest.php +++ b/tests/Unit/Controllers/Admin/NewsControllerTest.php @@ -163,11 +163,45 @@ public function testSaveCreateEdit( $this->assertHasNotification('news.edit.success'); - $news = (new News())->find($id ?: 2); + /** @var News $news */ + $news = News::find($id ?: 2); $this->assertEquals($text, $news->text); $this->assertEquals($isMeeting, (bool) $news->is_meeting); } + /** + * @covers \Engelsystem\Controllers\Admin\NewsController::save + */ + public function testSaveNoContentChange(): void + { + /** @var News $news */ + $news = News::factory()->create([ + 'is_pinned' => false, + ]); + $this->request->attributes->set('news_id', $news->id); + $body = [ + 'title' => $news->title, + 'text' => $news->text, + 'is_pinned' => '1', + ]; + $this->addUser(); + $this->request = $this->request->withParsedBody($body); + + /** @var NewsController $controller */ + $controller = $this->app->make(NewsController::class); + $controller->setValidator(new Validator()); + + $controller->save($this->request); + + /** @var News $updatedNews */ + $updatedNews = News::find($news->id); + $this->assertEquals($news->title, $updatedNews->title); + $this->assertEquals($news->text, $updatedNews->text); + $this->assertEquals($news->created_at, $updatedNews->created_at); + $this->assertEquals($news->updated_at, $updatedNews->updated_at); + $this->assertTrue($updatedNews->is_pinned); + } + /** * @covers \Engelsystem\Controllers\Admin\NewsController::save */ From d443ac914321de80f12dbf05735acbd124e802db Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sat, 9 Aug 2025 14:07:09 +0200 Subject: [PATCH 111/157] allow DateTime in from_datetime (cherry picked from commit 36777222c67fdcefee2ca7c778c7741bbe891e95) --- includes/sys_form.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/includes/sys_form.php b/includes/sys_form.php index 4a98d3665..2c41096a8 100644 --- a/includes/sys_form.php +++ b/includes/sys_form.php @@ -60,6 +60,9 @@ function form_datetime(string $name, string $label, $value) { $dom_id = $name . '-datetime'; if ($value) { + if ($value instanceof DateTime) { + $value = Carbon::instance($value); + } $value = ($value instanceof Carbon) ? $value : Carbon::createFromTimestamp($value, Carbon::now()->timezone); } From d92c959b1823571bc00ce047604233dea8184bd3 Mon Sep 17 00:00:00 2001 From: Xu Date: Thu, 25 Sep 2025 17:50:51 +0200 Subject: [PATCH 112/157] add user state force food --- config/config.default.php | 3 + db/factories/User/StateFactory.php | 1 + .../2025_09_24_000000_add_force_food.php | 57 +++++++++++++++++++ includes/controller/users_controller.php | 2 + includes/pages/admin_active.php | 1 + includes/pages/admin_user.php | 17 +++++- includes/view/User_view.php | 39 +++++++++---- resources/lang/de_DE/default.po | 23 +++++--- resources/lang/en_US/default.po | 13 +++-- resources/views/admin/user/edit-voucher.twig | 6 +- .../Admin/UserVoucherController.php | 1 + src/Controllers/Metrics/Controller.php | 1 + src/Controllers/Metrics/Stats.php | 5 ++ src/Models/User/State.php | 5 ++ .../Admin/UserVoucherControllerTest.php | 4 ++ tests/Unit/Controllers/Metrics/StatsTest.php | 19 ++++++- tests/Unit/Models/User/UserTest.php | 1 + 17 files changed, 167 insertions(+), 31 deletions(-) create mode 100644 db/migrations/2025_09_24_000000_add_force_food.php diff --git a/config/config.default.php b/config/config.default.php index 09b88d218..6890b63ec 100644 --- a/config/config.default.php +++ b/config/config.default.php @@ -350,6 +350,9 @@ // Whether force active should be enabled 'enable_force_active' => (bool) env('ENABLE_FORCE_ACTIVE', true), + // Whether force food should be enabled + 'enable_force_food' => (bool) env('ENABLE_FORCE_FOOD', false), + // Allow users with sufficient permission to add worklogs for themselves 'enable_self_worklog' => (bool) env('ENABLE_SELF_WORKLOG', true), diff --git a/db/factories/User/StateFactory.php b/db/factories/User/StateFactory.php index b87c3b2eb..ee3dd15a2 100644 --- a/db/factories/User/StateFactory.php +++ b/db/factories/User/StateFactory.php @@ -25,6 +25,7 @@ public function definition(): array 'user_info' => $this->faker->optional(.1)->text(), 'active' => $this->faker->boolean(.3), 'force_active' => $this->faker->boolean(.1), + 'force_food' => $this->faker->boolean(.1), 'got_goodie' => $this->faker->boolean(), 'got_voucher' => $this->faker->numberBetween(0, 10), ]; diff --git a/db/migrations/2025_09_24_000000_add_force_food.php b/db/migrations/2025_09_24_000000_add_force_food.php new file mode 100644 index 000000000..c38172a45 --- /dev/null +++ b/db/migrations/2025_09_24_000000_add_force_food.php @@ -0,0 +1,57 @@ +db = $this->schema->getConnection(); + } + + /** + * Run the migration + */ + public function up(): void + { + $this->schema->table('users_state', function (Blueprint $table): void { + $table->boolean('force_food')->after('force_active'); + }); + $this->db->table('privileges')->insertOrIgnore([ + 'name' => 'user.ff.edit', + 'description' => 'Edit user force food state', + ]); + $permissionId = $this->db->table('privileges') + ->where('name', 'user.ff.edit') + ->get(['id']) + ->first()->id; + + // add permission to group Shift Coordinator + $this->db->table('group_privileges') + ->insertOrIgnore([ + ['group_id' => $this->shiftCoordinatorId, 'privilege_id' => $permissionId], + ]); + } + + /** + * Reverse the migration + */ + public function down(): void + { + $this->schema->table('users_state', function (Blueprint $table): void { + $table->dropColumn('force_food'); + }); + $this->db->table('privileges')->where('name', 'user.ff.edit')->delete(); + } +} diff --git a/includes/controller/users_controller.php b/includes/controller/users_controller.php index c57db9280..c5c482309 100644 --- a/includes/controller/users_controller.php +++ b/includes/controller/users_controller.php @@ -256,6 +256,7 @@ function users_list_controller() 'freeloads', 'active', 'force_active', + 'force_food', 'got_goodie', 'shirt_size', 'planned_arrival_date', @@ -299,6 +300,7 @@ function users_list_controller() State::whereArrived(true)->count(), State::whereActive(true)->count(), State::whereForceActive(true)->count(), + State::whereForceFood(true)->count(), ShiftEntry::whereNotNull('freeloaded_by')->count(), State::whereGotGoodie(true)->count(), State::query()->sum('got_voucher'), diff --git a/includes/pages/admin_active.php b/includes/pages/admin_active.php index 4ed0876b9..818d5b88b 100644 --- a/includes/pages/admin_active.php +++ b/includes/pages/admin_active.php @@ -280,6 +280,7 @@ function admin_active() . ' min (' . sprintf('%.2f', $user['shift_length'] / 3600) . ' h)'; $userData['active'] = icon_bool($user->state->active); $userData['force_active'] = icon_bool($user->state->force_active); + $userData['force_food'] = icon_bool($user->state->force_food); $userData['tshirt'] = icon_bool($user->state->got_goodie); $userData['shift_count'] = $user['shift_count']; diff --git a/includes/pages/admin_user.php b/includes/pages/admin_user.php index e17c09a10..8ee37a8a7 100644 --- a/includes/pages/admin_user.php +++ b/includes/pages/admin_user.php @@ -139,6 +139,15 @@ function admin_user() $html .= '' . "\n"; } + // Forced food? + if (config('enable_force_food')) { + $html .= ' ' . __('Force food') . '' . "\n"; + $html .= auth()->can('user.ff.edit') + ? html_options('force_food', $options, $user_source->state->force_food) + : icon_bool($user_source->state->force_food); + $html .= '' . "\n"; + } + if ($goodie_enabled) { // got goodie? $html .= ' ' @@ -348,6 +357,9 @@ function admin_user() if (auth()->can('user.fa.edit') && config('enable_force_active')) { $user_source->state->force_active = $request->input('force_active'); } + if (auth()->can('user.ff.edit') && config('enable_force_food')) { + $user_source->state->force_food = $request->input('force_food'); + } $user_source->state->save(); engelsystem_log( @@ -355,11 +367,12 @@ function admin_user() ? ('nick modified from ' . $old_nick . ' to ' . $user_source->name) : $user_source->name) . ' (' . $user_source->id . ')' - . ($changed_email ? ', email modified' : '') + . ($changed_email ? ', e-mail modified' : '') . ($goodie_tshirt ? ', T-shirt size: ' . $user_source->personalData->shirt_size : '') . ', arrived: ' . $user_source->state->arrived . ', active: ' . $user_source->state->active - . ', force-active: ' . $user_source->state->force_active + . (config('enable_force_active') ? (', force-active: ' . $user_source->state->force_active) : '') + . (config('enable_force_food') ? (', force-food: ' . $user_source->state->force_food) : '') . ($goodie_enabled ? ', goodie: ' . $user_source->state->got_goodie : '') . ($user_info_edit ? ', user-info: ' . $user_source->state->user_info : '') ); diff --git a/includes/view/User_view.php b/includes/view/User_view.php index b2529cc7f..6a3990231 100644 --- a/includes/view/User_view.php +++ b/includes/view/User_view.php @@ -41,6 +41,7 @@ function User_delete_view($user) * @param int $arrived_count * @param int $active_count * @param int $force_active_count + * @param int $force_food_count * @param int $freeloads_count * @param int $goodies_count * @param int $voucher_count @@ -53,6 +54,7 @@ function Users_view( $arrived_count, $active_count, $force_active_count, + $force_food_count, $freeloads_count, $goodies_count, $voucher_count, @@ -116,7 +118,12 @@ class="btn btn-sm btn-secondary ms-2 js-only" } $u['freeloads'] = $user->getAttribute('freeloads'); $u['active'] = icon_bool($user->state->active); - $u['force_active'] = icon_bool($user->state->force_active); + if (config('enable_force_active')) { + $u['force_active'] = icon_bool($user->state->force_active); + } + if (config('enable_force_food')) { + $u['force_food'] = icon_bool($user->state->force_food); + } if ($goodie_enabled) { $u['got_goodie'] = icon_bool($user->state->got_goodie); if ($goodie_tshirt) { @@ -148,6 +155,7 @@ class="btn btn-sm btn-secondary ms-2 js-only" 'got_voucher' => '
    ' . $voucher_count . '
    ', 'active' => $active_count, 'force_active' => $force_active_count, + 'force_food' => $force_food_count, 'freeloads' => $freeloads_count, 'got_goodie' => $goodies_count, 'actions' => '' . count($usersList) . '', @@ -174,6 +182,9 @@ class="btn btn-sm btn-secondary ms-2 js-only" if (config('enable_force_active')) { $user_table_headers['force_active'] = Users_table_header_link('force_active', __('Forced'), $order_by); } + if (config('enable_force_food')) { + $user_table_headers['force_food'] = Users_table_header_link('force_food', __('Food'), $order_by); + } if ($goodie_enabled) { $user_table_headers['got_goodie'] = Users_table_header_link('got_goodie', __('Goodie'), $order_by); if ($goodie_tshirt) { @@ -860,6 +871,9 @@ function User_view_state_admin($freeloader, $user_source) } elseif ($user_source->state->active) { $state[] = '' . __('user.active') . ''; } + if ($user_source->state->force_food && config('enable_force_food')) { + $state[] = '' . __('user.force_food') . ''; + } if ($user_source->state->got_goodie && $goodie_enabled) { $state[] = '' . __('Goodie') . ''; } @@ -876,18 +890,19 @@ function User_view_state_admin($freeloader, $user_source) if (config('enable_voucher')) { $voucherCount = $user_source->state->got_voucher; $availableCount = $voucherCount + UserVouchers::eligibleVoucherCount($user_source); - $availableCount = max($voucherCount, $availableCount); - if ($user_source->state->got_voucher > 0) { - $state[] = '' - . icon('valentine') - . __('user.state.vouchers', [$voucherCount, $availableCount]) - . ''; - } else { - $state[] = '' - . __('user.state.vouchers.none') - . ($availableCount ? ' (' . __('out of %s', [$availableCount]) . ')' : '') - . ''; + $availableVoucher = $availableCount; + if ( + (config('enable_force_active') && $user_source->state->force_active) + || config('enable_force_food') && $user_source->state->force_food + ) { + $availableVoucher = __('user.state.vouchers.force', [$availableCount]); } + $state[] = '' + . icon('valentine') + . __('user.state.vouchers', [$voucherCount, $availableVoucher]) + . ''; } if ($password_reset) { diff --git a/resources/lang/de_DE/default.po b/resources/lang/de_DE/default.po index 8f0983c8b..dfcc8ff08 100644 --- a/resources/lang/de_DE/default.po +++ b/resources/lang/de_DE/default.po @@ -498,7 +498,10 @@ msgid "Active" msgstr "Aktiv" msgid "Forced" -msgstr "Erzwungen" +msgstr "Dauerhaft aktiv" + +msgid "Food" +msgstr "Essen" msgid "Given T-shirts" msgstr "Ausgegebene T-Shirts" @@ -699,7 +702,10 @@ msgid "No" msgstr "Nein" msgid "Force active" -msgstr "Aktiv erzwingen" +msgstr "Dauerhaft aktiv" + +msgid "Force food" +msgstr "Unbegrenzt Essen" msgid "Edit user" msgstr "User bearbeiten" @@ -1076,8 +1082,8 @@ msgstr "Gutscheine bearbeiten" msgid "voucher.eligible" msgstr "Engel kann noch %d Gutscheine bekommen." -msgid "voucher.eligible.fa" -msgstr "Engel kann noch %d Gutscheine bekommen und ist FA." +msgid "voucher.eligible.force" +msgstr "Engel kann %d unbegrenzt Gutscheine bekommen." msgid "voucher.count" msgstr "Anzahl Gutscheine bekommen" @@ -1085,8 +1091,8 @@ msgstr "Anzahl Gutscheine bekommen" msgid "user.state.vouchers" msgstr "%s von %s Gutscheinen bekommen" -msgid "user.state.vouchers.none" -msgstr "Keine Gutscheine bekommen" +msgid "user.state.vouchers.force" +msgstr "%d unendlich" msgid "Freeloads" msgstr "Schwänzereien" @@ -1839,7 +1845,10 @@ msgid "user.active" msgstr "Aktiv" msgid "user.force_active" -msgstr "Aktiv (erzwungen)" +msgstr "Dauerhaft aktiv" + +msgid "user.force_food" +msgstr "Unbegrenzt Essen" msgid "user.arrived" msgstr "Angekommen" diff --git a/resources/lang/en_US/default.po b/resources/lang/en_US/default.po index 607ff6f64..44606e43b 100644 --- a/resources/lang/en_US/default.po +++ b/resources/lang/en_US/default.po @@ -669,7 +669,10 @@ msgid "user.active" msgstr "Active" msgid "user.force_active" -msgstr "Active (forced)" +msgstr "Always active" + +msgid "user.force_food" +msgstr "Unlimited food" msgid "user.arrived" msgstr "Arrived" @@ -728,8 +731,8 @@ msgstr "Edit vouchers" msgid "voucher.eligible" msgstr "Angel can receive another %d vouchers." -msgid "voucher.eligible.fa" -msgstr "Angel can receive another %d vouchers and is FA." +msgid "voucher.eligible.force" +msgstr "Angel can receive %d unlimited vouchers." msgid "voucher.count" msgstr "Number of vouchers handed out" @@ -737,8 +740,8 @@ msgstr "Number of vouchers handed out" msgid "user.state.vouchers" msgstr "Got %s of %s vouchers" -msgid "user.state.vouchers.none" -msgstr "Got no vouchers" +msgid "user.state.vouchers.force" +msgstr "%d unlimited" msgid "angeltypes.angeltypes" msgstr "Angel types" diff --git a/resources/views/admin/user/edit-voucher.twig b/resources/views/admin/user/edit-voucher.twig index 9e437738e..7b4be43e4 100644 --- a/resources/views/admin/user/edit-voucher.twig +++ b/resources/views/admin/user/edit-voucher.twig @@ -13,10 +13,10 @@ {% include 'layouts/parts/messages.twig' %} {{ m.alert( - forceActive - ? __('voucher.eligible.fa', [eligibleVoucherCount]) + forceActive or forceFood + ? __('voucher.eligible.force', [eligibleVoucherCount]) : __('voucher.eligible', [eligibleVoucherCount]) - ) }} + , 'info', true) }}
    {{ csrf() }} diff --git a/src/Controllers/Admin/UserVoucherController.php b/src/Controllers/Admin/UserVoucherController.php index 1e25ebabb..9cabb6b27 100644 --- a/src/Controllers/Admin/UserVoucherController.php +++ b/src/Controllers/Admin/UserVoucherController.php @@ -59,6 +59,7 @@ public function editVoucher(Request $request): Response 'userdata' => $user, 'gotVoucher' => $user->state->got_voucher ?? 0, 'forceActive' => $user->state->force_active && config('enable_force_active'), + 'forceFood' => $user->state->force_food && config('enable_force_food'), 'eligibleVoucherCount' => UserVouchers::eligibleVoucherCount($user), ] ); diff --git a/src/Controllers/Metrics/Controller.php b/src/Controllers/Metrics/Controller.php index d8883129f..d37bb559e 100644 --- a/src/Controllers/Metrics/Controller.php +++ b/src/Controllers/Metrics/Controller.php @@ -104,6 +104,7 @@ public function metrics(): Response ], 'users_info' => ['type' => 'gauge', $this->stats->usersInfo()], 'users_force_active' => ['type' => 'gauge', $this->stats->forceActiveUsers()], + 'users_force_food' => ['type' => 'gauge', $this->stats->forceFoodUsers()], 'users_pronouns' => ['type' => 'gauge', $this->stats->usersPronouns()], 'licenses' => [ 'type' => 'gauge', diff --git a/src/Controllers/Metrics/Stats.php b/src/Controllers/Metrics/Stats.php index aef7d4612..f49e5d814 100644 --- a/src/Controllers/Metrics/Stats.php +++ b/src/Controllers/Metrics/Stats.php @@ -84,6 +84,11 @@ public function forceActiveUsers(): int return State::whereForceActive(true)->count(); } + public function forceFoodUsers(): int + { + return State::whereForceFood(true)->count(); + } + public function usersPronouns(): int { return PersonalData::query()->where('pronoun', '!=', '')->count(); diff --git a/src/Models/User/State.php b/src/Models/User/State.php index c43e9fc63..6b2bdab26 100644 --- a/src/Models/User/State.php +++ b/src/Models/User/State.php @@ -14,6 +14,7 @@ * @property string|null $user_info * @property bool $active * @property bool $force_active + * @property bool $force_food * @property bool $got_goodie * @property int $got_voucher * @@ -22,6 +23,7 @@ * @method static QueryBuilder|State[] whereUserInfo($value) * @method static QueryBuilder|State[] whereActive($value) * @method static QueryBuilder|State[] whereForceActive($value) + * @method static QueryBuilder|State[] whereForceFood($value) * @method static QueryBuilder|State[] whereGotGoodie($value) * @method static QueryBuilder|State[] whereGotVoucher($value) */ @@ -39,6 +41,7 @@ class State extends HasUserModel 'user_info' => null, 'active' => false, 'force_active' => false, + 'force_food' => false, 'got_goodie' => false, 'got_voucher' => 0, ]; @@ -50,6 +53,7 @@ class State extends HasUserModel 'arrival_date' => 'datetime', 'active' => 'boolean', 'force_active' => 'boolean', + 'force_food' => 'boolean', 'got_goodie' => 'boolean', 'got_voucher' => 'integer', ]; @@ -66,6 +70,7 @@ class State extends HasUserModel 'user_info', 'active', 'force_active', + 'force_food', 'got_goodie', 'got_voucher', ]; diff --git a/tests/Unit/Controllers/Admin/UserVoucherControllerTest.php b/tests/Unit/Controllers/Admin/UserVoucherControllerTest.php index 2ceae6ed9..1fb31d0d0 100644 --- a/tests/Unit/Controllers/Admin/UserVoucherControllerTest.php +++ b/tests/Unit/Controllers/Admin/UserVoucherControllerTest.php @@ -68,6 +68,10 @@ public function testShowEditVoucher(): void $this->user->state->force_active && config('enable_force_active'), $data['forceActive'] ); + $this->assertEquals( + $this->user->state->force_food && config('enable_force_food'), + $data['forceFood'] + ); $this->assertEquals(UserVouchers::eligibleVoucherCount($this->user), $data['eligibleVoucherCount']); return $this->response; }); diff --git a/tests/Unit/Controllers/Metrics/StatsTest.php b/tests/Unit/Controllers/Metrics/StatsTest.php index 735323e0d..7b8e68ad7 100644 --- a/tests/Unit/Controllers/Metrics/StatsTest.php +++ b/tests/Unit/Controllers/Metrics/StatsTest.php @@ -396,6 +396,17 @@ public function testForceActiveUsers(): void $this->assertEquals(2, $stats->forceActiveUsers()); } + /** + * @covers \Engelsystem\Controllers\Metrics\Stats::forceFoodUsers + */ + public function testForceFoodUsers(): void + { + $this->addUsers(); + + $stats = new Stats($this->database); + $this->assertEquals(2, $stats->forceFoodUsers()); + } + /** * @covers \Engelsystem\Controllers\Metrics\Stats::usersPronouns */ @@ -580,7 +591,7 @@ protected function addUsers(): void ['drive_car' => true, 'drive_12t' => true, 'drive_confirmed' => true, 'ifsg_certificate_light' => true] ); $this->addUser( - ['arrived' => 1, 'got_voucher' => 3], + ['arrived' => 1, 'got_voucher' => 3, 'force_food' => true], ['pronoun' => 'per'], ['theme' => 1, 'email_human' => true], [ @@ -592,7 +603,11 @@ protected function addUsers(): void ] ); $this->addUser(['arrived' => 1, 'active' => 1, 'got_goodie' => true, 'force_active' => true]); - $this->addUser(['arrived' => 1, 'active' => 1, 'got_goodie' => true], ['shirt_size' => 'L'], ['theme' => 4]); + $this->addUser( + ['arrived' => 1, 'active' => 1, 'got_goodie' => true, 'force_food' => true], + ['shirt_size' => 'L'], + ['theme' => 4] + ); } protected function addUser( diff --git a/tests/Unit/Models/User/UserTest.php b/tests/Unit/Models/User/UserTest.php index a0ba01213..0b522271a 100644 --- a/tests/Unit/Models/User/UserTest.php +++ b/tests/Unit/Models/User/UserTest.php @@ -77,6 +77,7 @@ public function hasOneRelationsProvider(): array 'state', [ 'force_active' => true, + 'force_food' => true, ], ], [ From 06b6ba40582791b0bb86096320cfec52be4bc13c Mon Sep 17 00:00:00 2001 From: Xu Date: Fri, 10 Oct 2025 16:37:56 +0200 Subject: [PATCH 113/157] refactor user view user state --- includes/view/User_view.php | 109 +++++++++++++----------------------- 1 file changed, 39 insertions(+), 70 deletions(-) diff --git a/includes/view/User_view.php b/includes/view/User_view.php index 6a3990231..cac82ff72 100644 --- a/includes/view/User_view.php +++ b/includes/view/User_view.php @@ -802,89 +802,55 @@ function User_view( */ function User_view_state($admin_user_privilege, $freeloader, $user_source) { - if ($admin_user_privilege) { - $state = User_view_state_admin($freeloader, $user_source); - } else { - $state = User_view_state_user($user_source); - } - - return div('col-md-2', [ - heading(__('State'), 4), - join('
    ', $state), - ]); -} - -/** - * Render the state section of user view for users. - * - * @param User $user_source - * @return array - */ -function User_view_state_user($user_source) -{ - $state = [ - User_shift_state_render($user_source), - ]; - - if ($user_source->state->arrived) { - $state[] = '' . icon('house') . __('user.arrived') . ''; - } else { - $state[] = '' . __('Not arrived') . ''; - } - - return $state; -} - - -/** - * Render the state section of user view for admins. - * - * @param bool $freeloader - * @param User $user_source - * @return array - */ -function User_view_state_admin($freeloader, $user_source) -{ - $state = []; $goodie = GoodieType::from(config('goodie_type')); $goodie_enabled = $goodie !== GoodieType::None; $password_reset = PasswordReset::whereUserId($user_source->id) ->where('created_at', '>', $user_source->last_login_at ?: '') ->count(); + $state = []; - if ($freeloader) { + if ($freeloader && $admin_user_privilege) { $state[] = '' . icon('exclamation-circle') . __('Freeloader') . ''; } $state[] = User_shift_state_render($user_source); if ($user_source->state->arrived) { - $state[] = '' . icon('house') - . sprintf( - __('Arrived at %s'), - $user_source->state->arrival_date ? $user_source->state->arrival_date->format(__('general.date')) : '' - ) - . ''; + if ($admin_user_privilege) { + $state[] = '' . icon('house') + . sprintf( + __('Arrived at %s'), + $user_source->state->arrival_date + ? $user_source->state->arrival_date->format(__('general.date')) : '' + ) + . ''; - if ($user_source->state->force_active && config('enable_force_active')) { - $state[] = '' . __('user.force_active') . ''; - } elseif ($user_source->state->active) { - $state[] = '' . __('user.active') . ''; - } - if ($user_source->state->force_food && config('enable_force_food')) { - $state[] = '' . __('user.force_food') . ''; - } - if ($user_source->state->got_goodie && $goodie_enabled) { - $state[] = '' . __('Goodie') . ''; + if ($user_source->state->force_active && config('enable_force_active')) { + $state[] = '' . __('user.force_active') . ''; + } elseif ($user_source->state->active) { + $state[] = '' . __('user.active') . ''; + } + if ($user_source->state->force_food && config('enable_force_food')) { + $state[] = '' . __('user.force_food') . ''; + } + if ($user_source->state->got_goodie && $goodie_enabled) { + $state[] = '' . __('Goodie') . ''; + } + } else { + $state[] = '' . icon('house') . __('user.arrived') . ''; } } else { - $arrivalDate = $user_source->personalData->planned_arrival_date; - $state[] = '' - . ($arrivalDate ? sprintf( - __('Not arrived (Planned: %s)'), - $arrivalDate->format(__('general.date')) - ) : __('Not arrived')) - . ''; + if ($admin_user_privilege) { + $arrivalDate = $user_source->personalData->planned_arrival_date; + $state[] = '' + . ($arrivalDate ? sprintf( + __('Not arrived (Planned: %s)'), + $arrivalDate->format(__('general.date')) + ) : __('Not arrived')) + . ''; + } else { + $state[] = '' . __('Not arrived') . ''; + } } if (config('enable_voucher')) { @@ -905,11 +871,14 @@ function User_view_state_admin($freeloader, $user_source) . ''; } - if ($password_reset) { + if ($password_reset && $admin_user_privilege) { $state[] = __('Password reset in progress'); } - return $state; + return div('col-md-2', [ + heading(__('State'), 4), + join('
    ', $state), + ]); } /** From 9035954aa1a918e37b029bc3a8b7e34bcbe8a542 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Thu, 24 Apr 2025 00:43:41 +0200 Subject: [PATCH 114/157] Updated some translations --- includes/pages/admin_user.php | 2 +- resources/lang/de_DE/default.po | 14 +++++++------- resources/lang/en_US/default.po | 10 +++++----- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/includes/pages/admin_user.php b/includes/pages/admin_user.php index 8ee37a8a7..36ab9da0d 100644 --- a/includes/pages/admin_user.php +++ b/includes/pages/admin_user.php @@ -364,7 +364,7 @@ function admin_user() engelsystem_log( 'Updated user: ' . ($changed_nick - ? ('nick modified from ' . $old_nick . ' to ' . $user_source->name) + ? ('nick modified from ' . $old_nick . ' (' . $user_source->id . ') to ' . $user_source->name) : $user_source->name) . ' (' . $user_source->id . ')' . ($changed_email ? ', e-mail modified' : '') diff --git a/resources/lang/de_DE/default.po b/resources/lang/de_DE/default.po index dfcc8ff08..eee0b429c 100644 --- a/resources/lang/de_DE/default.po +++ b/resources/lang/de_DE/default.po @@ -69,7 +69,7 @@ msgid "footer.eventinfo.start_end" msgstr "Event vom %1$s bis %2$s" msgid "footer.issues" -msgstr "Bugs / Features" +msgstr "Bugs/Features" msgid "footer.github" msgstr "Entwicklerplattform" @@ -252,7 +252,7 @@ msgstr "Schichteintrag gelöscht." msgid "This shift was imported from a schedule so some changes will be overwritten with the next import." msgstr "" -"Diese Schicht wurde aus einem Fahrplan importiert. " +"Diese Schicht wurde aus einem Programm importiert. " "Dadurch werden einige Änderungen beim nächsten Import überschrieben." msgid "Please select a location." @@ -935,8 +935,8 @@ msgstr "Meine Schicht" msgid "Help needed" msgstr "Hilfe benötigt" -msgid "Other angel type needed / collides with my shifts" -msgstr "Andere Engeltypen benötigt / kollidiert mit meinen Schichten" +msgid "Other angel type needed/collides with my shifts" +msgstr "Andere Engeltypen benötigt/kollidiert mit meinen Schichten" msgid "Shift is full" msgstr "Schicht ist voll" @@ -1675,7 +1675,7 @@ msgid "settings.certificates.drive_confirmed.hint" msgstr "Dein Führerschein wurde bestätigt, du kannst deine Angaben nicht mehr selber ändern." msgid "settings.certificates.confirmation.info" -msgstr "Du hast persönlich überprüft, dass die Zertifizierung / Bescheinigung den Anforderungen genügt." +msgstr "Du hast persönlich überprüft, dass die Zertifizierung/Bescheinigung den Anforderungen genügt." msgid "settings.certificates.success" msgstr "Zertifikate wurden erfolgreich aktualisiert." @@ -1932,7 +1932,7 @@ msgid "location.map_url" msgstr "Karte" msgid "location.required_angels" -msgstr "Benötigte Engel (bei Fahrplan import)" +msgstr "Benötigte Engel (bei Programmimport/-kopie)" msgid "location.map_url.info" msgstr "Die Karte wird auf der Ort-Seite als iframe eingebettet." @@ -1959,7 +1959,7 @@ msgid "shifttype.delete.title" msgstr "Schichttyp \"%s\" löschen" msgid "shifttype.required_angels" -msgstr "Benötigte Engel (bei Fahrplan import)" +msgstr "Benötigte Engel (bei Programmimport/-kopie)" msgid "shifttype.edit.signup_advance_hours" msgstr "Selbsteintragen im voraus in Stunden" diff --git a/resources/lang/en_US/default.po b/resources/lang/en_US/default.po index 44606e43b..b952cef6a 100644 --- a/resources/lang/en_US/default.po +++ b/resources/lang/en_US/default.po @@ -164,7 +164,7 @@ msgid "schedule.import.load.info" msgstr "Import \"%s\" (version \"%s\")" msgid "schedule.name" -msgstr "Programm name" +msgstr "Schedule name" msgid "schedule.url" msgstr "Schedule URL (schedule.xml)" @@ -493,7 +493,7 @@ msgid "settings.certificates.drive_confirmed.hint" msgstr "Your driving license has been confirmed, you can no longer change it by yourself." msgid "settings.certificates.confirmation.info" -msgstr "You personally checked that the certificate / license meets the requirements." +msgstr "You personally checked that the certificate/license meets the requirements." msgid "settings.certificates.success" msgstr "Certificates were updated successfully." @@ -794,7 +794,7 @@ msgid "location.map_url" msgstr "Map" msgid "location.required_angels" -msgstr "Required angels (on schedule import)" +msgstr "Required angels (on schedule import or copy)" msgid "location.map_url.info" msgstr "The map will be embedded on the location page as an iframe." @@ -821,7 +821,7 @@ msgid "shifttype.delete.title" msgstr "Delete shift type \"%s\"" msgid "shifttype.required_angels" -msgstr "Required angels (on schedule import)" +msgstr "Required angels (on schedule import or copy)" msgid "shifttype.edit.signup_advance_hours" msgstr "Self signup advance hours" @@ -951,7 +951,7 @@ msgid "footer.eventinfo.start_end" msgstr "Event from %1$s to %2$s" msgid "footer.issues" -msgstr "Bugs / Features" +msgstr "Bugs/Features" msgid "footer.github" msgstr "Development Platform" From 58aaacb7b39bba59a8cefdb4fefce8f4ce72c9e1 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Thu, 24 Apr 2025 01:01:39 +0200 Subject: [PATCH 115/157] Add logs button on user page --- includes/view/User_view.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/includes/view/User_view.php b/includes/view/User_view.php index cac82ff72..e4b70a696 100644 --- a/includes/view/User_view.php +++ b/includes/view/User_view.php @@ -708,6 +708,11 @@ function User_view( url('/users/' . $user_source->id . '/certificates'), icon('card-checklist') . __('settings.certificates') ) : '', + $auth->can(['admin_log', 'logs.all']) ? + form([ + form_hidden('search_user_id', $user_source->id), + form_submit('submit', icon('journal-text') . __('log.log'), '', false, 'secondary'), + ], url('/admin/logs')) : '', ($admin_user_worklog_privilege && $self_worklog) ? button( url('/admin/user/' . $user_source->id . '/worklog'), icon('clock-history') . __('worklog.add') From 45775bab8978ba1fcfdab003ec70123731a3ce39 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Mon, 26 May 2025 00:06:14 +0200 Subject: [PATCH 116/157] CI: Better version generation fallback --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 35a5c1c4f..c51a55059 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -120,6 +120,7 @@ generate-version: (git describe --abbrev=0 --tags | tr -d '\n')\ && echo "-${CI_COMMIT_REF_NAME}+${CI_PIPELINE_ID}.${CI_COMMIT_SHORT_SHA}"\ )\ + || echo "0-${CI_COMMIT_REF_NAME}+${CI_PIPELINE_ID}.${CI_COMMIT_SHORT_SHA}"\ )" - echo "${VERSION}" - echo -n "${VERSION}" > storage/app/VERSION From 07f4227f41c5e79dcf412604d1869fa9b86d92b8 Mon Sep 17 00:00:00 2001 From: Ole Bittner Date: Sat, 18 Oct 2025 01:59:17 +0200 Subject: [PATCH 117/157] show correct 'Shift ended' text after countdown expires --- includes/view/User_view.php | 2 +- resources/assets/js/countdown.js | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/includes/view/User_view.php b/includes/view/User_view.php index e4b70a696..22054f1d9 100644 --- a/includes/view/User_view.php +++ b/includes/view/User_view.php @@ -273,7 +273,7 @@ function User_shift_state_render($user) . ''; } - return '' + return '' . __('Shift ends %c') . ''; } diff --git a/resources/assets/js/countdown.js b/resources/assets/js/countdown.js index 31fb14443..e45a864a2 100644 --- a/resources/assets/js/countdown.js +++ b/resources/assets/js/countdown.js @@ -39,9 +39,14 @@ ready(() => { document.querySelectorAll('[data-countdown-ts]').forEach((element) => { const timestamp = Number(element.dataset.countdownTs); const template = element.textContent; + const templateExpired = String(element.dataset.countdownExpiredTemplate); element.textContent = template.replace('%c', formatFromNow(timestamp)); setInterval(() => { - element.textContent = template.replace('%c', formatFromNow(timestamp)); + if (templateExpired !== 'undefined' && Date.now() / 1000 >= timestamp) { + element.textContent = templateExpired.replace('%c', formatFromNow(timestamp)); + } else { + element.textContent = template.replace('%c', formatFromNow(timestamp)); + } }, 1000); }); }); From eac0b985dc824088a688a85ca56f96653b5eda49 Mon Sep 17 00:00:00 2001 From: Ole Bittner Date: Sat, 18 Oct 2025 21:23:53 +0200 Subject: [PATCH 118/157] resize add button to btn-sm --- resources/views/macros/base.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/macros/base.twig b/resources/views/macros/base.twig index 8e384b923..15edf3b93 100644 --- a/resources/views/macros/base.twig +++ b/resources/views/macros/base.twig @@ -74,7 +74,7 @@ {% endmacro %} {% macro add(url, opt) %} - {{ _self.button(_self.icon('plus-lg'), url, {'title': __('general.add')}|merge(opt|default({}))) }} + {{ _self.button(_self.icon('plus-lg'), url, {'size': 'sm', 'title': __('general.add')}|merge(opt|default({}))) }} {% endmacro %} {% macro info(text, raw) %} From 2512baad7bf3dcf48316ce4264812f897351efbd Mon Sep 17 00:00:00 2001 From: Ole Bittner Date: Thu, 23 Oct 2025 00:50:41 +0200 Subject: [PATCH 119/157] use btn-sm for "add" buttons in legacy titles --- includes/pages/user_shifts.php | 2 +- includes/view/AngelTypes_view.php | 4 ++-- includes/view/User_view.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/includes/pages/user_shifts.php b/includes/pages/user_shifts.php index 4b49cc5f3..a826e329b 100644 --- a/includes/pages/user_shifts.php +++ b/includes/pages/user_shifts.php @@ -303,7 +303,7 @@ function view_user_shifts() return dateWithEventDay(Carbon::make($value)->format('Y-m-d')); })->toArray(); - $link = button(url('/admin-shifts'), icon('plus-lg'), 'add'); + $link = button(url('/admin-shifts'), icon('plus-lg'), 'btn-sm add'); return page([ div('col-md-12', [ diff --git a/includes/view/AngelTypes_view.php b/includes/view/AngelTypes_view.php index f28be0f4a..c9ba6892e 100644 --- a/includes/view/AngelTypes_view.php +++ b/includes/view/AngelTypes_view.php @@ -595,7 +595,7 @@ function AngelType_view( $add = (($admin_angeltypes || $admin_user_angeltypes) ? button( url('/user-angeltypes', ['action' => 'add', 'angeltype_id' => $angeltype->id]), icon('plus-lg'), - '', + 'btn-sm', '', __('general.add') ) : ''); @@ -782,7 +782,7 @@ function AngelTypes_list_view($angeltypes, bool $admin_angeltypes) $add = button( url('/angeltypes', ['action' => 'edit']), icon('plus-lg'), - '', + 'btn-sm', '', __('general.add') ); diff --git a/includes/view/User_view.php b/includes/view/User_view.php index 22054f1d9..cb67222c4 100644 --- a/includes/view/User_view.php +++ b/includes/view/User_view.php @@ -208,7 +208,7 @@ class="btn btn-sm btn-secondary ms-2 js-only" unset($user_table_headers[$key]); } - $link = button(url('/register'), icon('plus-lg'), 'add'); + $link = button(url('/register'), icon('plus-lg'), 'btn-sm add'); return page_with_title(__('All users') . ' ' . $link, [ msg(), table($user_table_headers, $usersList), From 24c3a8e4c27f8c559151d46479dde7608436c368 Mon Sep 17 00:00:00 2001 From: Ole Bittner Date: Thu, 23 Oct 2025 00:52:29 +0200 Subject: [PATCH 120/157] center buttons in titles --- resources/assets/themes/base.scss | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/resources/assets/themes/base.scss b/resources/assets/themes/base.scss index 9166b7f62..1b30af17d 100644 --- a/resources/assets/themes/base.scss +++ b/resources/assets/themes/base.scss @@ -445,6 +445,17 @@ blockquote { padding-left: 0.5em; } +/* center buttons in titles */ +h1 { + display: flex; + gap: 0.5rem; + align-items: flex-end; + + & .btn { + align-self: center; + } +} + /* Hide the arrow up/down buttons rendered by the browser in the input field */ /* Chrome, Safari, Edge, Opera */ input[type='number']::-webkit-outer-spin-button, From c1eded6358fc989a0436d92ad973b7089d6f672a Mon Sep 17 00:00:00 2001 From: Ole Bittner Date: Sat, 25 Oct 2025 13:16:35 +0200 Subject: [PATCH 121/157] replace user state 'arrived' with 'arrival_date NOT NULL' --- .editorconfig | 2 +- composer.json | 2 +- db/factories/User/StateFactory.php | 2 - ...10_19_000000_remove_user_arrived_state.php | 44 +++++++++++++++++++ includes/pages/admin_active.php | 8 ++-- includes/pages/admin_arrive.php | 2 - includes/pages/admin_free.php | 2 +- includes/pages/admin_user.php | 9 +++- .../Admin/UserGoodieController.php | 9 +++- src/Controllers/OAuthController.php | 1 - src/Factories/User.php | 1 - src/Models/User/State.php | 25 +++++++++-- .../Admin/UserGoodieControllerTest.php | 22 ++++++++-- tests/Unit/Controllers/Metrics/StatsTest.php | 26 ++++++++--- tests/Unit/Models/User/UserStateTest.php | 41 +++++++++++++++++ 15 files changed, 166 insertions(+), 30 deletions(-) create mode 100644 db/migrations/2025_10_19_000000_remove_user_arrived_state.php create mode 100644 tests/Unit/Models/User/UserStateTest.php diff --git a/.editorconfig b/.editorconfig index 5761cb154..e80b01571 100644 --- a/.editorconfig +++ b/.editorconfig @@ -45,7 +45,7 @@ indent_size = 4 [*.sh] indent_size = 2 -[{db/*.sql,includes/**}] +[{db/*.sql,includes/**,*.json}] max_line_length = unset [*.{yml,yaml}] diff --git a/composer.json b/composer.json index f5713025c..ced86caee 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,7 @@ ], "phpstan": "phpstan", "phpunit": "phpunit", - "phpunit:coverage": "phpunit --coverage-text --coverage-html ./public/coverage/" + "phpunit:coverage": "php -d memory_limit=-1 vendor/bin/phpunit --coverage-text --coverage-html ./public/coverage/" }, "require": { "php": ">=8.2.0", diff --git a/db/factories/User/StateFactory.php b/db/factories/User/StateFactory.php index ee3dd15a2..34911b65b 100644 --- a/db/factories/User/StateFactory.php +++ b/db/factories/User/StateFactory.php @@ -20,7 +20,6 @@ public function definition(): array return [ 'user_id' => User::factory(), - 'arrived' => (bool) $arrival, 'arrival_date' => $arrival ? Carbon::instance($arrival) : null, 'user_info' => $this->faker->optional(.1)->text(), 'active' => $this->faker->boolean(.3), @@ -39,7 +38,6 @@ public function arrived(): self return $this->state( function (array $attributes) { return [ - 'arrived' => true, 'arrival_date' => Carbon::instance($this->faker->dateTimeThisMonth()), ]; } diff --git a/db/migrations/2025_10_19_000000_remove_user_arrived_state.php b/db/migrations/2025_10_19_000000_remove_user_arrived_state.php new file mode 100644 index 000000000..1e8fd96e1 --- /dev/null +++ b/db/migrations/2025_10_19_000000_remove_user_arrived_state.php @@ -0,0 +1,44 @@ +db = $this->schema->getConnection(); + } + + /** + * Run the migration + */ + public function up(): void + { + $this->schema->table('users_state', function (Blueprint $table): void { + $table->dropColumn('arrived'); + }); + } + + /** + * Reverse the migration + */ + public function down(): void + { + $this->schema->table('users_state', function (Blueprint $table): void { + $table->boolean('arrived')->default(false)->after('user_id'); + }); + $this->db->table('users_state') + ->whereNotNull('arrival_date') + ->update(['arrived' => true]); + } +} diff --git a/includes/pages/admin_active.php b/includes/pages/admin_active.php index 818d5b88b..f95d1722a 100644 --- a/includes/pages/admin_active.php +++ b/includes/pages/admin_active.php @@ -83,9 +83,9 @@ function admin_active() ->leftJoin('shift_entries', 'users.id', '=', 'shift_entries.user_id') ->leftJoin('shifts', 'shift_entries.shift_id', '=', 'shifts.id') ->leftJoin('users_state', 'users.id', '=', 'users_state.user_id') - ->where('users_state.arrived', '=', true) + ->whereNotNull('users_state.arrival_date') ->orWhere(function (EloquentBuilder $userinfo) { - $userinfo->where('users_state.arrived', '=', false) + $userinfo->whereNull('users_state.arrival_date') ->whereNotNull('users_state.user_info') ->whereNot('users_state.user_info', ''); }) @@ -209,9 +209,9 @@ function admin_active() } }) ->leftJoin('users_state', 'users.id', '=', 'users_state.user_id') - ->where('users_state.arrived', '=', true) + ->whereNotNull('users_state.arrival_date') ->orWhere(function (EloquentBuilder $userinfo) { - $userinfo->where('users_state.arrived', '=', false) + $userinfo->whereNull('users_state.arrival_date') ->whereNotNull('users_state.user_info') ->whereNot('users_state.user_info', ''); }) diff --git a/includes/pages/admin_arrive.php b/includes/pages/admin_arrive.php index 6b42ebdd6..2c971c4f2 100644 --- a/includes/pages/admin_arrive.php +++ b/includes/pages/admin_arrive.php @@ -38,7 +38,6 @@ function admin_arrive() $user_id = $request->input('user'); $user_source = User::find($user_id); if ($user_source) { - $user_source->state->arrived = false; $user_source->state->arrival_date = null; $user_source->state->save(); @@ -57,7 +56,6 @@ function admin_arrive() $user_id = $request->input('user'); $user_source = User::find($user_id); if ($user_source) { - $user_source->state->arrived = true; $user_source->state->arrival_date = new Carbon\Carbon(); $user_source->state->save(); diff --git a/includes/pages/admin_free.php b/includes/pages/admin_free.php index 2fc2ce291..8fcd61c4c 100644 --- a/includes/pages/admin_free.php +++ b/includes/pages/admin_free.php @@ -50,7 +50,7 @@ function admin_free() ->where('shifts.start', '<', Carbon::now()) ->where('shifts.end', '>', Carbon::now()); }) - ->where('users_state.arrived', '=', 1) + ->whereNotNull('users_state.arrival_date') ->whereNull('shifts.id') ->orderBy('users.name') ->groupBy('users.id'); diff --git a/includes/pages/admin_user.php b/includes/pages/admin_user.php index 36ab9da0d..b3f85a691 100644 --- a/includes/pages/admin_user.php +++ b/includes/pages/admin_user.php @@ -1,5 +1,6 @@ state->user_info = $request->postData('userInfo'); } if ($admin_arrive) { - $user_source->state->arrived = $request->postData('arrive'); + if ($user_source->state->arrived != $request->postData('arrive')) { + if ($request->postData('arrive')) { + $user_source->state->arrival_date = new Carbon(); + } else { + $user_source->state->arrival_date = null; + } + } } if ($user_goodie_edit) { diff --git a/src/Controllers/Admin/UserGoodieController.php b/src/Controllers/Admin/UserGoodieController.php index 589ba57a3..67b90bc65 100644 --- a/src/Controllers/Admin/UserGoodieController.php +++ b/src/Controllers/Admin/UserGoodieController.php @@ -4,6 +4,7 @@ namespace Engelsystem\Controllers\Admin; +use Carbon\Carbon; use Engelsystem\Config\Config; use Engelsystem\Config\GoodieType; use Engelsystem\Controllers\BaseController; @@ -84,7 +85,13 @@ public function saveGoodie(Request $request): Response } if ($this->auth->can('admin_arrive')) { - $user->state->arrived = (bool) $data['arrived']; + if ($user->state->arrived != (bool) $data['arrived']) { + if ((bool) $data['arrived']) { + $user->state->arrival_date = new Carbon(); + } else { + $user->state->arrival_date = null; + } + } } $user->state->active = (bool) $data['active']; diff --git a/src/Controllers/OAuthController.php b/src/Controllers/OAuthController.php index 3eee8277a..0cb0af78d 100644 --- a/src/Controllers/OAuthController.php +++ b/src/Controllers/OAuthController.php @@ -260,7 +260,6 @@ protected function handleArrive( return; } - $userState->arrived = true; $userState->arrival_date = new Carbon(); $userState->save(); diff --git a/src/Factories/User.php b/src/Factories/User.php index f262ed19c..1b0b5075a 100644 --- a/src/Factories/User.php +++ b/src/Factories/User.php @@ -275,7 +275,6 @@ private function createUser(array $data, array $rawData): EngelsystemUser $state = new State([]); if ($this->config->get('autoarrive')) { - $state->arrived = true; $state->arrival_date = CarbonImmutable::now(); } diff --git a/src/Models/User/State.php b/src/Models/User/State.php index 6b2bdab26..067c70ea9 100644 --- a/src/Models/User/State.php +++ b/src/Models/User/State.php @@ -5,11 +5,12 @@ namespace Engelsystem\Models\User; use Carbon\Carbon; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Query\Builder as QueryBuilder; /** - * @property bool $arrived + * @property-read bool $arrived * @property Carbon|null $arrival_date * @property string|null $user_info * @property bool $active @@ -36,7 +37,6 @@ class State extends HasUserModel /** @var array Default attributes */ protected $attributes = [ // phpcs:ignore - 'arrived' => false, 'arrival_date' => null, 'user_info' => null, 'active' => false, @@ -49,7 +49,6 @@ class State extends HasUserModel /** @var array */ protected $casts = [ // phpcs:ignore 'user_id' => 'integer', - 'arrived' => 'boolean', 'arrival_date' => 'datetime', 'active' => 'boolean', 'force_active' => 'boolean', @@ -65,7 +64,6 @@ class State extends HasUserModel */ protected $fillable = [ // phpcs:ignore 'user_id', - 'arrived', 'arrival_date', 'user_info', 'active', @@ -74,4 +72,23 @@ class State extends HasUserModel 'got_goodie', 'got_voucher', ]; + + /** + * Accessor: for arrived property + * Derived from arrival_date being not null + */ + public function getArrivedAttribute(): bool + { + return $this->arrival_date !== null; + } + + /** + * provide WhereArrived query scope + */ + public static function scopeWhereArrived(Builder $query, bool $value): Builder + { + return $value + ? $query->whereNotNull('arrival_date') + : $query->whereNull('arrival_date'); + } } diff --git a/tests/Unit/Controllers/Admin/UserGoodieControllerTest.php b/tests/Unit/Controllers/Admin/UserGoodieControllerTest.php index 922d27bb3..fe650bba1 100644 --- a/tests/Unit/Controllers/Admin/UserGoodieControllerTest.php +++ b/tests/Unit/Controllers/Admin/UserGoodieControllerTest.php @@ -4,6 +4,7 @@ namespace Engelsystem\Test\Unit\Controllers\Admin; +use Carbon\Carbon; use Engelsystem\Config\GoodieType; use Engelsystem\Controllers\Admin\UserGoodieController; use Engelsystem\Helpers\Authenticator; @@ -125,11 +126,11 @@ public function testSaveGoodie(): void ->create(); $auth - ->expects($this->exactly(5)) + ->expects($this->exactly(6)) ->method('can') ->with('admin_arrive') - ->willReturnOnConsecutiveCalls(true, true, false, false, true); - $this->setExpects($redirector, 'back', null, $this->response, $this->exactly(5)); + ->willReturnOnConsecutiveCalls(true, true, false, false, true, true); + $this->setExpects($redirector, 'back', null, $this->response, $this->exactly(6)); $controller = new UserGoodieController( $auth, @@ -191,7 +192,7 @@ public function testSaveGoodie(): void 'arrived' => '1', ]); - $user->state->arrived = false; + $user->state->arrival_date = null; $user->state->save(); $this->assertFalse($user->state->arrived); $controller->saveGoodie($request); @@ -219,6 +220,19 @@ public function testSaveGoodie(): void $controller->saveGoodie($request); $user = User::find(1); $this->assertEquals('XS', $user->personalData->shirt_size); + + // remove arrived + $user->state->arrival_date = Carbon::now(); + $user->state->save(); + $request = $request + ->withParsedBody([ + 'shirt_size' => 'XS', + 'arrived' => '', + ]); + + $controller->saveGoodie($request); + $user = User::find(1); + $this->assertFalse($user->state->arrived); } /** diff --git a/tests/Unit/Controllers/Metrics/StatsTest.php b/tests/Unit/Controllers/Metrics/StatsTest.php index 7b8e68ad7..b27a6306f 100644 --- a/tests/Unit/Controllers/Metrics/StatsTest.php +++ b/tests/Unit/Controllers/Metrics/StatsTest.php @@ -581,17 +581,29 @@ protected function addUsers(): void { $this->addUser(); $this->addUser([], ['shirt_size' => 'L'], ['email_human' => true, 'email_shiftinfo' => true]); - $this->addUser(['arrived' => 1], [], ['email_human' => true, 'email_goodie' => true, 'email_news' => true]); - $this->addUser(['arrived' => 1], ['pronoun' => 'unicorn'], ['language' => 'lo_RM', 'email_shiftinfo' => true]); - $this->addUser(['arrived' => 1, 'got_voucher' => 2], ['shirt_size' => 'XXL'], ['language' => 'lo_RM']); $this->addUser( - ['arrived' => 1, 'got_voucher' => 9, 'force_active' => true, 'user_info' => 'Info'], + ['arrival_date' => Carbon::now()], + [], + ['email_human' => true, 'email_goodie' => true, 'email_news' => true] + ); + $this->addUser( + ['arrival_date' => Carbon::now()], + ['pronoun' => 'unicorn'], + ['language' => 'lo_RM', 'email_shiftinfo' => true] + ); + $this->addUser( + ['arrival_date' => Carbon::now(), 'got_voucher' => 2], + ['shirt_size' => 'XXL'], + ['language' => 'lo_RM'] + ); + $this->addUser( + ['arrival_date' => Carbon::now(), 'got_voucher' => 9, 'force_active' => true, 'user_info' => 'Info'], [], ['theme' => 1], ['drive_car' => true, 'drive_12t' => true, 'drive_confirmed' => true, 'ifsg_certificate_light' => true] ); $this->addUser( - ['arrived' => 1, 'got_voucher' => 3, 'force_food' => true], + ['arrival_date' => Carbon::now(), 'got_voucher' => 3, 'force_food' => true], ['pronoun' => 'per'], ['theme' => 1, 'email_human' => true], [ @@ -602,9 +614,9 @@ protected function addUsers(): void 'ifsg_confirmed' => true, ] ); - $this->addUser(['arrived' => 1, 'active' => 1, 'got_goodie' => true, 'force_active' => true]); + $this->addUser(['arrival_date' => Carbon::now(), 'active' => 1, 'got_goodie' => true, 'force_active' => true]); $this->addUser( - ['arrived' => 1, 'active' => 1, 'got_goodie' => true, 'force_food' => true], + ['arrival_date' => Carbon::now(), 'active' => 1, 'got_goodie' => true, 'force_food' => true], ['shirt_size' => 'L'], ['theme' => 4] ); diff --git a/tests/Unit/Models/User/UserStateTest.php b/tests/Unit/Models/User/UserStateTest.php new file mode 100644 index 000000000..0ce8afe9e --- /dev/null +++ b/tests/Unit/Models/User/UserStateTest.php @@ -0,0 +1,41 @@ +assertFalse($state->arrived); + + $state->arrival_date = Carbon::now(); + $this->assertTrue($state->arrived); + } + + /** + * @covers \Engelsystem\Models\User\State::scopeWhereArrived + */ + public function testScopeWhereArrived(): void + { + $state = State::factory()->create([ + 'arrival_date' => null, + ]); + $this->assertCount(0, State::whereArrived(true)->get()); + $this->assertCount(1, State::whereArrived(false)->get()); + + $state->arrival_date = Carbon::now(); + $state->save(); + $this->assertCount(1, State::whereArrived(true)->get()); + $this->assertCount(0, State::whereArrived(false)->get()); + } +} From 3bd43f4f73ced386f28f24f6fdc1c41a6792b272 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sat, 11 Oct 2025 14:39:05 +0200 Subject: [PATCH 122/157] Added cache abstraction --- config/app.php | 1 + src/Helpers/Cache.php | 57 +++++++++ src/Helpers/CacheServiceProvider.php | 18 +++ src/helpers.php | 13 ++ .../Unit/Helpers/CacheServiceProviderTest.php | 31 +++++ tests/Unit/Helpers/CacheTest.php | 119 ++++++++++++++++++ tests/Unit/Helpers/Stub/cache/.gitignore | 2 + tests/Unit/HelpersTest.php | 18 +++ 8 files changed, 259 insertions(+) create mode 100644 src/Helpers/Cache.php create mode 100644 src/Helpers/CacheServiceProvider.php create mode 100644 tests/Unit/Helpers/CacheServiceProviderTest.php create mode 100644 tests/Unit/Helpers/CacheTest.php create mode 100644 tests/Unit/Helpers/Stub/cache/.gitignore diff --git a/config/app.php b/config/app.php index 500597655..52f3b0053 100644 --- a/config/app.php +++ b/config/app.php @@ -23,6 +23,7 @@ \Engelsystem\Helpers\Translation\TranslationServiceProvider::class, \Engelsystem\Http\ResponseServiceProvider::class, \Engelsystem\Http\Psr7ServiceProvider::class, + \Engelsystem\Helpers\CacheServiceProvider::class, \Engelsystem\Helpers\AuthenticatorServiceProvider::class, \Engelsystem\Helpers\AssetsServiceProvider::class, \Engelsystem\Renderer\TwigServiceProvider::class, diff --git a/src/Helpers/Cache.php b/src/Helpers/Cache.php new file mode 100644 index 000000000..f3052d42e --- /dev/null +++ b/src/Helpers/Cache.php @@ -0,0 +1,57 @@ +cacheFilePath($key); + + // Check for file existence, forget old ones + $exists = file_exists($cacheFile); + if ($exists && filemtime($cacheFile) < time() - $seconds) { + $this->forget($key); + $exists = false; + } + + // Handle callback to get default value + if (!$exists) { + if (!is_callable($default)) { + return $default; + } + + file_put_contents($cacheFile, serialize($default())); + } + + // Get data from cache + return unserialize(file_get_contents($cacheFile)); + } + + public function forget(string $key): void + { + $cacheFile = $this->cacheFilePath($key); + if (!file_exists($cacheFile)) { + return; + } + + unlink($cacheFile); + } + + protected function cacheFilePath(string $key): string + { + return $this->path . '/' . $key . '.cache'; + } +} diff --git a/src/Helpers/CacheServiceProvider.php b/src/Helpers/CacheServiceProvider.php new file mode 100644 index 000000000..e03eaba8c --- /dev/null +++ b/src/Helpers/CacheServiceProvider.php @@ -0,0 +1,18 @@ +app->bind('cache', Cache::class); + $this->app->when(Cache::class) + ->needs('$path') + ->give(fn() => $this->app->get('path.cache')); + } +} diff --git a/src/helpers.php b/src/helpers.php index 6d428a7a4..65decd755 100644 --- a/src/helpers.php +++ b/src/helpers.php @@ -6,6 +6,7 @@ use Engelsystem\Config\Config; use Engelsystem\Events\EventDispatcher; use Engelsystem\Helpers\Authenticator; +use Engelsystem\Helpers\Cache; use Engelsystem\Helpers\Translation\Translator; use Engelsystem\Http\Redirector; use Engelsystem\Http\Request; @@ -46,6 +47,18 @@ function back(int $status = 302, array $headers = []): Response return $redirect->back($status, $headers); } +function cache(string|null $key = null, mixed $default = null, int $seconds = 60 * 60): mixed +{ + /** @var Cache $cache */ + $cache = app('cache'); + + if (empty($key)) { + return $cache; + } + + return $cache->get($key, $default, $seconds); +} + /** * Get or set config values * @return mixed|Config diff --git a/tests/Unit/Helpers/CacheServiceProviderTest.php b/tests/Unit/Helpers/CacheServiceProviderTest.php new file mode 100644 index 000000000..0077eae4d --- /dev/null +++ b/tests/Unit/Helpers/CacheServiceProviderTest.php @@ -0,0 +1,31 @@ +instance('path.cache', '/tmp'); + + $serviceProvider = new CacheServiceProvider($app); + $serviceProvider->register(); + + $this->assertTrue($app->bound('cache')); + $this->assertArrayHasKey(Cache::class, $app->contextual); + + $cache = $app->get(Cache::class); + $this->assertInstanceOf(Cache::class, $cache); + } +} diff --git a/tests/Unit/Helpers/CacheTest.php b/tests/Unit/Helpers/CacheTest.php new file mode 100644 index 000000000..d82af5e92 --- /dev/null +++ b/tests/Unit/Helpers/CacheTest.php @@ -0,0 +1,119 @@ +cacheDir); + $value = new stdClass(); + + $this->assertNull($cache->get('this-is-not-there')); + $this->assertEquals('', $cache->get('give-me-my-string', '')); + $this->assertEquals(42, $cache->get('meaning-of-life', 42)); + $this->assertEquals(['key' => 'value'], $cache->get('array-data', ['key' => 'value'])); + $this->assertEquals($value, $cache->get('some-object', $value)); + + $this->assertCount(0, $this->listCacheFiles()); + } + + /** + * @covers \Engelsystem\Helpers\Cache::get + */ + public function testGetDefaultCallback(): void + { + $cache = new Cache($this->cacheDir); + + $this->assertEquals('some test', $cache->get('this-has-a-callback', fn() => 'some test')); + $this->assertEquals('some test', $cache->get('this-has-a-callback', 'unused-default-value')); + + $cacheFile = $this->cacheDir . '/this-has-a-callback.cache'; + $this->assertFileExists($cacheFile); + $this->assertEquals(serialize('some test'), file_get_contents($cacheFile)); + + $this->assertCount(1, $this->listCacheFiles()); + } + + /** + * @covers \Engelsystem\Helpers\Cache::get + * @covers \Engelsystem\Helpers\Cache::cacheFilePath + */ + public function testGetReadFile(): void + { + $cache = new Cache($this->cacheDir); + + $cacheFile = $this->cacheDir . '/cached.cache'; + file_put_contents($cacheFile, serialize('cached-value')); + + $this->assertEquals('cached-value', $cache->get('cached')); + } + + /** + * @covers \Engelsystem\Helpers\Cache::forget + */ + public function testForgetNotExisting(): void + { + $cache = new Cache($this->cacheDir); + $cache->forget('not-cached-value'); + + $this->assertCount(0, $this->listCacheFiles()); + } + + /** + * @covers \Engelsystem\Helpers\Cache::forget + * @covers \Engelsystem\Helpers\Cache::cacheFilePath + */ + public function testForget(): void + { + $cache = new Cache($this->cacheDir); + + $cacheFile = $this->cacheDir . '/rm-cache.cache'; + file_put_contents($cacheFile, serialize('removed-cache-value')); + + $cache->forget('rm-cache'); + $this->assertNull($cache->get('rm-cache')); + $this->assertFileDoesNotExist($cacheFile); + } + + /** + * @covers \Engelsystem\Helpers\Cache::get + * @covers \Engelsystem\Helpers\Cache::forget + * @covers \Engelsystem\Helpers\Cache::cacheFilePath + */ + public function testGetForgetOld(): void + { + $cache = new Cache($this->cacheDir); + + $cacheFile = $this->cacheDir . '/cached.cache'; + file_put_contents($cacheFile, serialize('cached-value')); + touch($cacheFile, time() - 60 * 60 * 2); + + $this->assertEquals('default', $cache->get('cached', 'default')); + $this->assertFileDoesNotExist($cacheFile); + } + + public function tearDown(): void + { + foreach ($this->listCacheFiles() as $file) { + unlink($this->cacheDir . '/' . $file); + } + } + + protected function listCacheFiles(): array + { + return array_diff(scandir($this->cacheDir), ['..', '.', '.gitignore']); + } +} diff --git a/tests/Unit/Helpers/Stub/cache/.gitignore b/tests/Unit/Helpers/Stub/cache/.gitignore new file mode 100644 index 000000000..d6b7ef32c --- /dev/null +++ b/tests/Unit/Helpers/Stub/cache/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/tests/Unit/HelpersTest.php b/tests/Unit/HelpersTest.php index b39a6e467..16f5879af 100644 --- a/tests/Unit/HelpersTest.php +++ b/tests/Unit/HelpersTest.php @@ -9,6 +9,7 @@ use Engelsystem\Container\Container; use Engelsystem\Events\EventDispatcher; use Engelsystem\Helpers\Authenticator; +use Engelsystem\Helpers\Cache; use Engelsystem\Helpers\Translation\Translator; use Engelsystem\Http\Redirector; use Engelsystem\Http\Request; @@ -158,6 +159,23 @@ public function testBack(): void $this->assertEquals($response, $return); } + /** + * @covers \cache + */ + public function testCache(): void + { + $cache = $this->createMock(Cache::class); + $this->setExpects($cache, 'get', ['test', 'default', 42], 'default'); + + $app = new Application(); + $app->instance('cache', $cache); + + $this->assertEquals($cache, cache()); + + $return = cache('test', 'default', 42); + $this->assertEquals('default', $return); + } + /** * @covers \config_path */ From 3ffca4a8aff1ee1402f3cd6ba9385f2ea0538922 Mon Sep 17 00:00:00 2001 From: Ole Bittner Date: Thu, 30 Oct 2025 00:06:10 +0100 Subject: [PATCH 123/157] set memory_limit=-1 for phpunit --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index ced86caee..0ae9c58e3 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,7 @@ "phpcbf -p" ], "phpstan": "phpstan", - "phpunit": "phpunit", + "phpunit": "php -d memory_limit=-1 vendor/bin/phpunit", "phpunit:coverage": "php -d memory_limit=-1 vendor/bin/phpunit --coverage-text --coverage-html ./public/coverage/" }, "require": { From 4a27896afec349fa21dd12acd4584dcc2b9e4585 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Wed, 29 Oct 2025 23:31:59 +0100 Subject: [PATCH 124/157] Added legacy map for shifts renderer and dateWithEventDay filter --- src/Helpers/ShiftsRenderer.php | 83 ++++++++++++++++ src/Renderer/Twig/Extensions/Legacy.php | 21 ++++ tests/Unit/Helpers/ShiftsRendererTest.php | 95 +++++++++++++++++++ .../Renderer/Twig/Extensions/LegacyTest.php | 43 ++++++++- 4 files changed, 241 insertions(+), 1 deletion(-) create mode 100644 src/Helpers/ShiftsRenderer.php create mode 100644 tests/Unit/Helpers/ShiftsRendererTest.php diff --git a/src/Helpers/ShiftsRenderer.php b/src/Helpers/ShiftsRenderer.php new file mode 100644 index 000000000..41ba4f189 --- /dev/null +++ b/src/Helpers/ShiftsRenderer.php @@ -0,0 +1,83 @@ +id] = $shift->shiftEntries; + + if (!$shift->schedule) { + $angelTypes = $shift->neededAngelTypes; + } else { + if ($shift->schedule->needed_from_shift_type) { + $angelTypes = $shift->shiftType->neededAngelTypes; + } else { + $angelTypes = $shift->location->neededAngelTypes; + } + } + + $neededAngelTypes[$shift->id] = []; + foreach ($angelTypes as $nAngelType) { + $data = $nAngelType->toArray(); + $data['id'] = $nAngelType->angelType->id; + $data['name'] = $nAngelType->angelType->name; + $data['restricted'] = $nAngelType->angelType->restricted; + $data['shift_self_signup'] = $nAngelType->angelType->shift_self_signup; + $neededAngelTypes[$shift->id][] = $data; + } + } + + return $this->renderShiftCalendar($shifts, $neededAngelTypes, $shiftEntries); + } + + /** + * @param Collection|Shift[] $shifts + * @param array[] $neededAngelTypes + * @param ShiftEntry[][] $shiftEntries + * @codeCoverageIgnore + */ + protected function renderShiftCalendar( + array | Collection $shifts, + array $neededAngelTypes, + array $shiftEntries + ): string { + if (!$shifts instanceof Collection) { + $shifts = collect($shifts); + } + + /** @var Carbon $start */ + $start = $shifts->min('start'); + /** @var Carbon $end */ + $end = $shifts->max('end'); + + $shiftsFilter = new ShiftsFilter(); + $shiftsFilter->setStartTime($start ? $start->timestamp : 0); + $shiftsFilter->setEndTime($end ? $end->timestamp : 0); + + $renderer = new ShiftCalendarRenderer($shifts, $neededAngelTypes, $shiftEntries, $shiftsFilter); + + return $renderer->render(); + } +} diff --git a/src/Renderer/Twig/Extensions/Legacy.php b/src/Renderer/Twig/Extensions/Legacy.php index ea23fdd01..ae628cb20 100644 --- a/src/Renderer/Twig/Extensions/Legacy.php +++ b/src/Renderer/Twig/Extensions/Legacy.php @@ -4,8 +4,11 @@ namespace Engelsystem\Renderer\Twig\Extensions; +use Engelsystem\Helpers\ShiftsRenderer; use Engelsystem\Http\Request; +use Illuminate\Database\Eloquent\Collection; use Twig\Extension\AbstractExtension as TwigExtension; +use Twig\TwigFilter; use Twig\TwigFunction; class Legacy extends TwigExtension @@ -25,10 +28,21 @@ public function getFunctions(): array new TwigFunction('menuUserShiftState', 'User_shift_state_render', $isSafeHtml), new TwigFunction('menuUserHints', 'header_render_hints', $isSafeHtml), new TwigFunction('menuLanguages', 'make_language_select', $isSafeHtml), + new TwigFunction('renderShifts', [$this, 'renderShifts'], $isSafeHtml), new TwigFunction('page', [$this, 'getPage']), ]; } + /** + * @return TwigFilter[] + */ + public function getFilters(): array + { + return [ + new TwigFilter('dateWithEventDay', 'dateWithEventDay'), + ]; + } + public function getPage(): string { if ($this->request->has('p')) { @@ -37,4 +51,11 @@ public function getPage(): string return $this->request->path(); } + + public function renderShifts(array | Collection $shifts): string + { + /** @var ShiftsRenderer $renderer */ + $renderer = app()->make(ShiftsRenderer::class); + return $renderer->render($shifts); + } } diff --git a/tests/Unit/Helpers/ShiftsRendererTest.php b/tests/Unit/Helpers/ShiftsRendererTest.php new file mode 100644 index 000000000..b82b9e5d0 --- /dev/null +++ b/tests/Unit/Helpers/ShiftsRendererTest.php @@ -0,0 +1,95 @@ +create(); + /** @var AngelType $angelType */ + $angelType = AngelType::factory()->create(); + /** @var Schedule $scheduleLocation */ + $scheduleLocation = Schedule::factory()->create(['needed_from_shift_type' => false]); + /** @var Schedule $scheduleType */ + $scheduleType = Schedule::factory()->create(['needed_from_shift_type' => true]); + + /** @var Shift $shiftNormal */ + $shiftNormal = Shift::factory()->create(); + $shiftNormal->scheduleShift()->delete(); + /** @var Shift $shiftScheduleLocation */ + $shiftScheduleLocation = Shift::factory()->create(); + /** @var Shift $shiftScheduleType */ + $shiftScheduleType = Shift::factory()->create(); + + $scheduleShiftLocation = new ScheduleShift(['guid' => Str::uuid()]); + $scheduleShiftLocation->schedule()->associate($scheduleLocation); + $scheduleShiftLocation->shift()->associate($shiftScheduleLocation); + $scheduleShiftLocation->save(); + $scheduleShiftType = new ScheduleShift(['guid' => Str::uuid()]); + $scheduleShiftType->schedule()->associate($scheduleType); + $scheduleShiftType->shift()->associate($shiftScheduleType); + $scheduleShiftType->save(); + + $shiftNormal->neededAngelTypes()->create(['angel_type_id' => $angelType->id, 'count' => 3]); + + ShiftEntry::factory()->create([ + 'shift_id' => $shiftNormal, + 'angel_type_id' => $angelType, + 'user_id' => $user, + ]); + + /** @var ShiftsRenderer|MockObject $renderer */ + $renderer = $this->getMockBuilder(ShiftsRenderer::class) + ->onlyMethods(['renderShiftCalendar']) + ->getMock(); + $renderer->expects($this->once()) + ->method('renderShiftCalendar') + ->willReturnCallback(function (array | Collection $shifts, array $neededAngelTypes, array $shiftEntries) { + $this->assertCount(3, $shifts); + + $angelType = $neededAngelTypes[1][0]; + $this->assertNotEmpty($angelType); + $this->assertArrayHasKey('name', $angelType); + $this->assertArrayHasKey('restricted', $angelType); + $this->assertArrayHasKey('shift_self_signup', $angelType); + + $entry = $shiftEntries[1][0]; + $this->assertNotEmpty($entry); + + return 'rendered table'; + }); + + $output = $renderer->render(Shift::all()); + $this->assertEquals('rendered table', $output); + } + + protected function setUp(): void + { + parent::setUp(); + $this->initDatabase(); + Str::createUuidsUsing(Uuid::class . '::uuid'); + } +} diff --git a/tests/Unit/Renderer/Twig/Extensions/LegacyTest.php b/tests/Unit/Renderer/Twig/Extensions/LegacyTest.php index 67da45678..90d46950c 100644 --- a/tests/Unit/Renderer/Twig/Extensions/LegacyTest.php +++ b/tests/Unit/Renderer/Twig/Extensions/LegacyTest.php @@ -4,8 +4,11 @@ namespace Engelsystem\Test\Unit\Renderer\Twig\Extensions; +use Engelsystem\Helpers\ShiftsRenderer; use Engelsystem\Http\Request; +use Engelsystem\Models\Shifts\Shift; use Engelsystem\Renderer\Twig\Extensions\Legacy; +use Illuminate\Database\Eloquent\Collection; use PHPUnit\Framework\MockObject\MockObject; class LegacyTest extends ExtensionTest @@ -26,14 +29,52 @@ public function testGetFunctions(): void $this->assertExtensionExists('menuUserShiftState', 'User_shift_state_render', $functions, $isSafeHtml); $this->assertExtensionExists('menuUserHints', 'header_render_hints', $functions, $isSafeHtml); $this->assertExtensionExists('menuLanguages', 'make_language_select', $functions, $isSafeHtml); + $this->assertExtensionExists('renderShifts', [$extension, 'renderShifts'], $functions, $isSafeHtml); $this->assertExtensionExists('page', [$extension, 'getPage'], $functions); } + /** + * @covers \Engelsystem\Renderer\Twig\Extensions\Legacy::getFilters + */ + public function testGetFilters(): void + { + /** @var Request|MockObject $request */ + $request = $this->createMock(Request::class); + + $extension = new Legacy($request); + $filters = $extension->getFilters(); + + $this->assertFilterExists('dateWithEventDay', 'dateWithEventDay', $filters); + } + + /** + * @covers \Engelsystem\Renderer\Twig\Extensions\Legacy::renderShifts + */ + public function testRenderShifts(): void + { + /** @var Request|MockObject $request */ + $request = $this->createMock(Request::class); + $renderingShifts = [new Shift()]; + /** @var ShiftsRenderer|MockObject $shiftsRenderer */ + $shiftsRenderer = $this->createMock(ShiftsRenderer::class); + $shiftsRenderer->expects($this->once()) + ->method('render') + ->willReturnCallback(function (array | Collection $shifts) use ($renderingShifts) { + $this->assertEquals($renderingShifts, $shifts); + return 'rendered shifts'; + }); + $this->app->instance(ShiftsRenderer::class, $shiftsRenderer); + + $extension = new Legacy($request); + $output = $extension->renderShifts($renderingShifts); + $this->assertEquals('rendered shifts', $output); + } + /** * @covers \Engelsystem\Renderer\Twig\Extensions\Legacy::__construct * @covers \Engelsystem\Renderer\Twig\Extensions\Legacy::getPage */ - public function testIsAuthenticated(): void + public function testGetPage(): void { /** @var Request|MockObject $request */ $request = $this->createMock(Request::class); From e42f8951435fa9758c3172bd16a3017d2af93e8f Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sun, 2 Nov 2025 01:19:28 +0100 Subject: [PATCH 125/157] Added needsUsers scope to shift --- db/factories/Shifts/ScheduleShiftFactory.php | 25 ++++++++ src/Models/Shifts/ScheduleShift.php | 3 + src/Models/Shifts/Shift.php | 54 +++++++++++++++++ tests/Unit/Models/Shifts/ShiftTest.php | 62 ++++++++++++++++++++ 4 files changed, 144 insertions(+) create mode 100644 db/factories/Shifts/ScheduleShiftFactory.php diff --git a/db/factories/Shifts/ScheduleShiftFactory.php b/db/factories/Shifts/ScheduleShiftFactory.php new file mode 100644 index 000000000..9b5d5bb9d --- /dev/null +++ b/db/factories/Shifts/ScheduleShiftFactory.php @@ -0,0 +1,25 @@ + Shift::factory(), + 'schedule_id' => Schedule::factory(), + 'guid' => $this->faker->uuid(), + ]; + } +} diff --git a/src/Models/Shifts/ScheduleShift.php b/src/Models/Shifts/ScheduleShift.php index 39a7759ee..72c42093c 100644 --- a/src/Models/Shifts/ScheduleShift.php +++ b/src/Models/Shifts/ScheduleShift.php @@ -5,6 +5,7 @@ namespace Engelsystem\Models\Shifts; use Engelsystem\Models\BaseModel; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Query\Builder as QueryBuilder; @@ -22,6 +23,8 @@ */ class ScheduleShift extends BaseModel { + use HasFactory; + /** @var string The primary key for the model */ protected $primaryKey = 'shift_id'; // phpcs:ignore diff --git a/src/Models/Shifts/Shift.php b/src/Models/Shifts/Shift.php index f14a18742..c47237fe5 100644 --- a/src/Models/Shifts/Shift.php +++ b/src/Models/Shifts/Shift.php @@ -8,6 +8,7 @@ use Engelsystem\Models\BaseModel; use Engelsystem\Models\Location; use Engelsystem\Models\User\User; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -15,6 +16,8 @@ use Illuminate\Database\Eloquent\Relations\HasOne; use Illuminate\Database\Eloquent\Relations\HasOneThrough; use Illuminate\Database\Query\Builder as QueryBuilder; +use Illuminate\Database\Query\Grammars\SQLiteGrammar; +use Illuminate\Database\Query\JoinClause; /** * @property int $id @@ -133,6 +136,57 @@ public function updatedBy(): BelongsTo return $this->belongsTo(User::class, 'updated_by'); } + public function scopeNeedsUsers(Builder $query): void + { + $query + ->addSelect([ + // This is "hidden" behind an attribute to not "poison" the SELECT default with fields from added joins + 'needs_users' => Shift::from('shifts as s2') + ->leftJoin('schedule_shift', 'schedule_shift.shift_id', 's2.id') + ->leftJoin('schedules', 'schedules.id', 'schedule_shift.schedule_id') + ->leftJoin('needed_angel_types', function (JoinClause $join): void { + // Directly + $join->on('needed_angel_types.shift_id', 's2.id') + // Via schedule location + ->orOn('needed_angel_types.location_id', 's2.location_id') + // Via schedule shift type + ->orOn('needed_angel_types.shift_type_id', 'schedules.shift_type'); + }) + ->whereColumn('s2.id', 'shifts.id') + ->where(function (Builder $query): void { + $query + ->where(function (Builder $query): void { + $query + // Direct requirement + ->whereColumn('needed_angel_types.shift_id', 's2.id') + // Or has schedule & via location + ->orWhere(function (Builder $query): void { + $query + ->where('schedules.needed_from_shift_type', false) + ->whereColumn('needed_angel_types.location_id', 's2.location_id'); + }) + // Or has schedule & via type + ->orWhere(function (Builder $query): void { + $query + ->where('schedules.needed_from_shift_type', true) + ->whereColumn('needed_angel_types.shift_type_id', 's2.shift_type_id'); + }); + }); + }) + ->selectRaw('COUNT(*) > 0'), + ]); + + if ($query->getConnection()->getQueryGrammar() instanceof SQLiteGrammar) { + // SQLite does not support HAVING for non-aggregate queries + $query->where('needs_users', '>', 0); + } else { + // @codeCoverageIgnoreStart + // needs_users is defined on select and thus only available after select + $query->having('needs_users', '>', 0); + // @codeCoverageIgnoreEnd + } + } + /** * get next shift with same shift type and location */ diff --git a/tests/Unit/Models/Shifts/ShiftTest.php b/tests/Unit/Models/Shifts/ShiftTest.php index 11bbb0d6a..9e3de7a0f 100644 --- a/tests/Unit/Models/Shifts/ShiftTest.php +++ b/tests/Unit/Models/Shifts/ShiftTest.php @@ -6,6 +6,7 @@ use Engelsystem\Config\Config; use Engelsystem\Helpers\Carbon; +use Engelsystem\Models\AngelType; use Engelsystem\Models\Location; use Engelsystem\Models\Shifts\NeededAngelType; use Engelsystem\Models\Shifts\Schedule; @@ -133,6 +134,67 @@ public function testShiftEntries(): void $this->assertCount(5, $shift->shiftEntries); } + /** + * @covers \Engelsystem\Models\Shifts\Shift::scopeNeedsUsers + */ + public function testScopeNeedsUsers(): void + { + /** @var AngelType $angelType */ + $angelType = AngelType::factory()->create(); + /** @var Shift $shift */ + $shift = Shift::factory()->create(); + Shift::factory()->create(); + + $this->assertCount(2, Shift::all()); + $this->assertCount(0, Shift::scopes('needsUsers')->get()); + + NeededAngelType::factory()->create(['angel_type_id' => $angelType->id, 'shift_id' => $shift->id]); + + $this->assertTrue(Shift::count() >= 2); + $this->assertCount(1, Shift::scopes('needsUsers')->get()); + } + + /** + * @covers \Engelsystem\Models\Shifts\Shift::scopeNeedsUsers + */ + public function testScopeNeedsUsersFromSchedule(): void + { + /** @var Schedule $schedule1 */ + $schedule1 = Schedule::factory()->create(['needed_from_shift_type' => true]); + /** @var Schedule $schedule2 */ + $schedule2 = Schedule::factory()->create(['needed_from_shift_type' => false]); + $shiftType = $schedule1->shiftType; + /** @var AngelType $angelType */ + $angelType = AngelType::factory()->create(); + /** @var Shift $shift1 Via schedule shift type */ + $shift1 = Shift::factory()->create(['shift_type_id' => $shiftType->id]); + /** @var Shift $shift2 Via schedule location */ + $shift2 = Shift::factory()->create(); + /** @var Shift $shift3 Direct */ + $shift3 = Shift::factory()->create(); + /** @var Shift $shift4 Via schedule location, no needed angel types */ + $shift4 = Shift::factory()->create(); + /** @var Shift $shift5 Empty shift */ + $shift5 = Shift::factory()->create(); + $location = $shift2->location; + + ScheduleShift::factory()->create(['shift_id' => $shift1->id, 'schedule_id' => $schedule1->id]); + ScheduleShift::factory()->create(['shift_id' => $shift2->id, 'schedule_id' => $schedule2->id]); + ScheduleShift::factory()->create(['shift_id' => $shift4->id, 'schedule_id' => $schedule2->id]); + + NeededAngelType::factory()->create(['angel_type_id' => $angelType->id, 'shift_type_id' => $shiftType->id]); + NeededAngelType::factory()->create(['angel_type_id' => $angelType->id, 'location_id' => $location->id]); + NeededAngelType::factory()->create(['angel_type_id' => $angelType->id, 'shift_id' => $shift3->id]); + + $this->assertTrue(Shift::count() >= 5); + + $shifts = Shift::scopes('needsUsers')->get()->pluck('id'); + $this->assertContains($shift1->id, $shifts, 'Shift should be selected via schedule shift type'); + $this->assertContains($shift2->id, $shifts, 'Shift should be selected via schedule location selected'); + $this->assertContains($shift3->id, $shifts, 'Shift should be selected via direct requirement selected'); + $this->assertNotContains($shift4->id, $shifts, 'Empty schedule location shift selected'); + $this->assertNotContains($shift5->id, $shifts, 'Empty shift selected'); + } /** * @covers \Engelsystem\Models\Shifts\Shift::nextShift From 25b79381aee0a3189a26ff66a3e8188c9a80c545 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Thu, 30 Oct 2025 01:09:11 +0100 Subject: [PATCH 126/157] Added shifts view for shift type, performance improvements --- includes/model/Shifts_model.php | 7 ++ includes/view/AngelTypes_view.php | 2 +- resources/lang/de_DE/default.po | 6 +- resources/lang/en_US/default.po | 3 + resources/views/admin/shifttypes/view.twig | 107 ++++++++++++++---- .../Admin/ShiftTypesController.php | 34 +++++- src/Database/DatabaseServiceProvider.php | 3 + .../Admin/ShiftTypesControllerTest.php | 25 +++- 8 files changed, 158 insertions(+), 29 deletions(-) diff --git a/includes/model/Shifts_model.php b/includes/model/Shifts_model.php index 2ca09250a..ef8677930 100644 --- a/includes/model/Shifts_model.php +++ b/includes/model/Shifts_model.php @@ -614,6 +614,12 @@ function Shift_signup_allowed( */ function Shifts_by_user($userId, $include_freeloaded_comments = false) { + # Cache static content per request + static $cached; + if (!empty($cached[$userId][$include_freeloaded_comments])) { + return $cached[$userId][$include_freeloaded_comments]; + } + $shiftsData = Db::select( ' SELECT @@ -647,6 +653,7 @@ function Shifts_by_user($userId, $include_freeloaded_comments = false) $shifts[] = (new Shift())->forceFill($data); } $shifts->load(['shiftType', 'location']); + $cached[$userId][$include_freeloaded_comments] = $shifts; return $shifts; } diff --git a/includes/view/AngelTypes_view.php b/includes/view/AngelTypes_view.php index c9ba6892e..29f0a9304 100644 --- a/includes/view/AngelTypes_view.php +++ b/includes/view/AngelTypes_view.php @@ -605,7 +605,7 @@ function AngelType_view( AngelType_view_buttons($angeltype, $user_angeltype, $admin_angeltypes, $supporter, $user_license, $user), msg(), tabs([ - __('Info') => AngelType_view_info( + __('general.info') => AngelType_view_info( $angeltype, $members, $admin_user_angeltypes, diff --git a/resources/lang/de_DE/default.po b/resources/lang/de_DE/default.po index eee0b429c..ce6260f40 100644 --- a/resources/lang/de_DE/default.po +++ b/resources/lang/de_DE/default.po @@ -488,6 +488,9 @@ msgstr "Nr." msgid "general.shifts" msgstr "Schichten" +msgid "general.info" +msgstr "Info" + msgid "Length" msgstr "Länge" @@ -887,9 +890,6 @@ msgstr "Fahrer" msgid "Has car" msgstr "Hat Auto" -msgid "Info" -msgstr "Info" - msgid "Supporters" msgstr "Supporter" diff --git a/resources/lang/en_US/default.po b/resources/lang/en_US/default.po index b952cef6a..beaffc5d3 100644 --- a/resources/lang/en_US/default.po +++ b/resources/lang/en_US/default.po @@ -40,6 +40,9 @@ msgstr "Name" msgid "general.shifts" msgstr "Shifts" +msgid "general.info" +msgstr "Info" + msgid "general.description" msgstr "Description" diff --git a/resources/views/admin/shifttypes/view.twig b/resources/views/admin/shifttypes/view.twig index 42d6540c3..f885f8b25 100644 --- a/resources/views/admin/shifttypes/view.twig +++ b/resources/views/admin/shifttypes/view.twig @@ -5,30 +5,93 @@ {% block title %}{{ shifttype.name }}{% endblock %} {% block row_content %} - {% if shifttype.signup_advance_hours %} -
    - {{ __('shifttype.edit.signup_advance_hours') }} {{ f.info(__('shifttype.edit.signup_advance_hours.info')) }}: {{ shifttype.signup_advance_hours }} -
    - {% endif %} -
    -

    {{ __('general.description') }}

    {{ shifttype.description|md }} -
    + + +
    +
    + + {% if shifttype.signup_advance_hours %} +
    + {{ __('shifttype.edit.signup_advance_hours') }} + {{ f.info(__('shifttype.edit.signup_advance_hours.info')) }}: + {{ shifttype.signup_advance_hours }} +
    + {% endif %} + +
    +

    {{ __('general.description') }}

    {{ shifttype.description|md }} +
    - {% if shifttype.neededAngelTypes.isNotEmpty() %} -
    -

    {{ __('location.required_angels') }}

    -
      - {% for neededAngelType in shifttype.neededAngelTypes %} - {% if neededAngelType.count %} -
    • - - {{ neededAngelType.angelType.name -}} - : {{ neededAngelType.count }} -
    • + {% if shifttype.neededAngelTypes.isNotEmpty() %} +
      +

      {{ __('location.required_angels') }}

      + +
      + {% endif %} + +
    +
    + + + + {{ renderShifts(shifts) }} + +
    - {% endif %} +
    {% endblock %} diff --git a/src/Controllers/Admin/ShiftTypesController.php b/src/Controllers/Admin/ShiftTypesController.php index 1f37e7290..4137d50b9 100644 --- a/src/Controllers/Admin/ShiftTypesController.php +++ b/src/Controllers/Admin/ShiftTypesController.php @@ -69,11 +69,43 @@ public function edit(Request $request): Response public function view(Request $request): Response { $shiftTypeId = (int) $request->getAttribute('shift_type_id'); + /** @var ShiftType $shiftType */ $shiftType = $this->shiftType->findOrFail($shiftTypeId); + $days = $shiftType->shifts() + ->scopes('needsUsers') + ->selectRaw('DATE(start) AS date') + ->orderBy('date') + ->groupBy('date') + ->pluck('date'); + + $day = $request->get('day'); + $day = $days->contains($day) ? $day : $days->first(); + + $shifts = $shiftType->shifts() + ->with([ + 'neededAngelTypes.angelType', + 'schedule', + 'shiftEntries.user.personalData', + 'shiftEntries.user.state', + 'shiftEntries.angelType', + 'shiftType.neededAngelTypes.angelType', + 'location.neededAngelTypes.angelType', + ]) + ->whereDate('start', $day) + ->orderBy('start') + ->get(); + return $this->response->withView( 'admin/shifttypes/view', - ['shifttype' => $shiftType, 'is_view' => true] + [ + 'shifttype' => $shiftType, + 'is_view' => true, + 'shifts_active' => $request->has('shifts') || $request->get('day'), + 'days' => $days, + 'selected_day' => $day, + 'shifts' => $shifts, + ] ); } diff --git a/src/Database/DatabaseServiceProvider.php b/src/Database/DatabaseServiceProvider.php index b1b6713bb..d6b68d9e9 100644 --- a/src/Database/DatabaseServiceProvider.php +++ b/src/Database/DatabaseServiceProvider.php @@ -5,6 +5,7 @@ namespace Engelsystem\Database; use Carbon\Carbon; +use Engelsystem\Config\Config; use Engelsystem\Container\ServiceProvider; use Exception; use Illuminate\Database\Capsule\Manager as CapsuleManager; @@ -16,7 +17,9 @@ class DatabaseServiceProvider extends ServiceProvider { public function register(): void { + /** @var Config $config */ $config = $this->app->get('config'); + /** @var CapsuleManager $capsule */ $capsule = $this->app->make(CapsuleManager::class); $now = Carbon::now($config->get('timezone')); diff --git a/tests/Unit/Controllers/Admin/ShiftTypesControllerTest.php b/tests/Unit/Controllers/Admin/ShiftTypesControllerTest.php index 6cffdaf03..0b54c8bda 100644 --- a/tests/Unit/Controllers/Admin/ShiftTypesControllerTest.php +++ b/tests/Unit/Controllers/Admin/ShiftTypesControllerTest.php @@ -12,6 +12,7 @@ use Engelsystem\Http\Request; use Engelsystem\Http\Validation\Validator; use Engelsystem\Models\AngelType; +use Engelsystem\Models\Shifts\NeededAngelType; use Engelsystem\Models\Shifts\Shift; use Engelsystem\Models\Shifts\ShiftEntry; use Engelsystem\Models\Shifts\ShiftType; @@ -52,14 +53,34 @@ public function testView(): void { /** @var ShiftTypesController $controller */ $controller = $this->app->make(ShiftTypesController::class); + /** @var ShiftType $shiftType */ $shiftType = ShiftType::factory()->create(); + /** @var AngelType $angelType */ + $angelType = AngelType::factory()->create(); + /** @var Shift $shift */ + $shift = Shift::factory()->create(['shift_type_id' => $shiftType->id]); + NeededAngelType::factory()->create(['angel_type_id' => $angelType->id, 'shift_id' => $shift->id]); $this->response->expects($this->once()) ->method('withView') - ->willReturnCallback(function (string $view, array $data) use ($shiftType) { + ->willReturnCallback(function (string $view, array $data) use ($shift) { $this->assertEquals('admin/shifttypes/view', $view); + $this->assertArrayHasKey('shifttype', $data); - $this->assertEquals($shiftType->id, $data['shifttype']['id']); + $this->assertEquals($shift->shiftType->id, $data['shifttype']['id']); + + $this->assertArrayHasKey('days', $data); + $this->assertArrayHasKey('selected_day', $data); + $day = $shift->start->format('Y-m-d'); + $this->assertEquals([$day], $data['days']->toArray()); + $this->assertEquals($shift->start->format('Y-m-d'), $data['selected_day']); + + $this->assertArrayHasKey('shifts_active', $data); + $this->assertFalse($data['shifts_active']); + + $this->assertArrayHasKey('shifts', $data); + $this->assertEquals($shift->id, $data['shifts']->first()->id); + return $this->response; }); From c43eec1f9d7af712ca1c876ca1ac5fbc21b2f6c2 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Tue, 11 Nov 2025 00:05:14 +0100 Subject: [PATCH 127/157] Fix package.json format --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7d30225f3..e55e049fc 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "engelsystem", "version": "1.1.0", "main": "index.js", - "repository": "https://github.com/engelsystem/engelsystem.git", + "repository": "github:engelsystem/engelsystem", "author": "https://github.com/engelsystem/engelsystem/contributors", "license": "GPL-2.0-or-later", "scripts": { From 37f041e369806bd7102b69b3eec0838bec27143c Mon Sep 17 00:00:00 2001 From: Christian Eichler Date: Sat, 8 Nov 2025 09:07:04 +0100 Subject: [PATCH 128/157] tests: Make GoodieTests run successful with disabled night-shifts in config.php --- tests/Feature/Helpers/GoodieTest.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tests/Feature/Helpers/GoodieTest.php b/tests/Feature/Helpers/GoodieTest.php index a3b2accf7..09ebb4c62 100644 --- a/tests/Feature/Helpers/GoodieTest.php +++ b/tests/Feature/Helpers/GoodieTest.php @@ -91,15 +91,14 @@ protected function setUp(): void parent::setUp(); $this->createdModels = []; - config( - 'night_shifts', - [ + config([ + 'night_shifts' => [ 'enabled' => true, 'start' => 2, 'end' => 6, 'multiplier' => 2, - ] - ); + ], + ]); } public function tearDown(): void From 447237aead3c843b5d94959aa370c31927672b13 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sun, 16 Nov 2025 13:45:39 +0100 Subject: [PATCH 129/157] Fix package audits --- composer.lock | 17 +++++++++++------ yarn.lock | 27 +++++++++++++++++---------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/composer.lock b/composer.lock index 69816b53b..2edf2a739 100644 --- a/composer.lock +++ b/composer.lock @@ -3938,16 +3938,16 @@ }, { "name": "symfony/http-foundation", - "version": "v7.2.3", + "version": "v7.3.7", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "ee1b504b8926198be89d05e5b6fc4c3810c090f0" + "reference": "db488a62f98f7a81d5746f05eea63a74e55bb7c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/ee1b504b8926198be89d05e5b6fc4c3810c090f0", - "reference": "ee1b504b8926198be89d05e5b6fc4c3810c090f0", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/db488a62f98f7a81d5746f05eea63a74e55bb7c4", + "reference": "db488a62f98f7a81d5746f05eea63a74e55bb7c4", "shasum": "" }, "require": { @@ -3964,6 +3964,7 @@ "doctrine/dbal": "^3.6|^4", "predis/predis": "^1.1|^2.0", "symfony/cache": "^6.4.12|^7.1.5", + "symfony/clock": "^6.4|^7.0", "symfony/dependency-injection": "^6.4|^7.0", "symfony/expression-language": "^6.4|^7.0", "symfony/http-kernel": "^6.4|^7.0", @@ -3996,7 +3997,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.2.3" + "source": "https://github.com/symfony/http-foundation/tree/v7.3.7" }, "funding": [ { @@ -4007,12 +4008,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-01-17T10:56:55+00:00" + "time": "2025-11-08T16:41:12+00:00" }, { "name": "symfony/mailer", diff --git a/yarn.lock b/yarn.lock index 2ef644aec..c158430c8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -815,16 +815,16 @@ integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== "@eslint-community/eslint-utils@^4.2.0": - version "4.7.0" - resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz#607084630c6c033992a082de6e6fbc1a8b52175a" - integrity sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw== + version "4.9.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz#7308df158e064f0dd8b8fdb58aa14fa2a7f913b3" + integrity sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g== dependencies: eslint-visitor-keys "^3.4.3" "@eslint-community/regexpp@^4.6.1": - version "4.12.1" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" - integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== + version "4.12.2" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.2.tgz#bccdf615bcf7b6e8db830ec0b8d21c9a25de597b" + integrity sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew== "@eslint/eslintrc@^2.1.4": version "2.1.4" @@ -1720,13 +1720,20 @@ debug@^4.1.0: dependencies: ms "^2.1.3" -debug@^4.1.1, debug@^4.3.1, debug@^4.3.2: +debug@^4.1.1: version "4.4.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b" integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ== dependencies: ms "^2.1.3" +debug@^4.3.1, debug@^4.3.2: + version "4.4.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== + dependencies: + ms "^2.1.3" + deep-is@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" @@ -2305,9 +2312,9 @@ js-tokens@^4.0.0: integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== js-yaml@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + version "4.1.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.1.tgz#854c292467705b699476e1a2decc0c8a3458806b" + integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA== dependencies: argparse "^2.0.1" From 533b07393277428717f98c488c033aec0c81b515 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sun, 16 Nov 2025 14:30:10 +0100 Subject: [PATCH 130/157] Upgrade @babel --- package.json | 3 - yarn.lock | 1455 ++++++++++++++++++++++++++------------------------ 2 files changed, 743 insertions(+), 715 deletions(-) diff --git a/package.json b/package.json index e55e049fc..1e4fbe142 100644 --- a/package.json +++ b/package.json @@ -48,8 +48,5 @@ "webpack": "^5.94.0", "webpack-cli": "^5.0.1", "webpack-manifest-plugin": "^5.0.0" - }, - "resolutions": { - "semver": "7.5.3" } } diff --git a/yarn.lock b/yarn.lock index c158430c8..aa24d5d88 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,14 +2,6 @@ # yarn lockfile v1 -"@ampproject/remapping@^2.2.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" - integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== - dependencies: - "@jridgewell/gen-mapping" "^0.3.5" - "@jridgewell/trace-mapping" "^0.3.24" - "@babel/code-frame@^7.0.0": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" @@ -17,35 +9,35 @@ dependencies: "@babel/highlight" "^7.18.6" -"@babel/code-frame@^7.26.2": - version "7.26.2" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85" - integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== +"@babel/code-frame@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be" + integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg== dependencies: - "@babel/helper-validator-identifier" "^7.25.9" + "@babel/helper-validator-identifier" "^7.27.1" js-tokens "^4.0.0" - picocolors "^1.0.0" + picocolors "^1.1.1" -"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.26.8": - version "7.26.8" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.8.tgz#821c1d35641c355284d4a870b8a4a7b0c141e367" - integrity sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ== +"@babel/compat-data@^7.27.2", "@babel/compat-data@^7.27.7", "@babel/compat-data@^7.28.5": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.28.5.tgz#a8a4962e1567121ac0b3b487f52107443b455c7f" + integrity sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA== "@babel/core@^7.20.12": - version "7.26.10" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.10.tgz#5c876f83c8c4dcb233ee4b670c0606f2ac3000f9" - integrity sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ== - dependencies: - "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.26.2" - "@babel/generator" "^7.26.10" - "@babel/helper-compilation-targets" "^7.26.5" - "@babel/helper-module-transforms" "^7.26.0" - "@babel/helpers" "^7.26.10" - "@babel/parser" "^7.26.10" - "@babel/template" "^7.26.9" - "@babel/traverse" "^7.26.10" - "@babel/types" "^7.26.10" + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.28.5.tgz#4c81b35e51e1b734f510c99b07dfbc7bbbb48f7e" + integrity sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw== + dependencies: + "@babel/code-frame" "^7.27.1" + "@babel/generator" "^7.28.5" + "@babel/helper-compilation-targets" "^7.27.2" + "@babel/helper-module-transforms" "^7.28.3" + "@babel/helpers" "^7.28.4" + "@babel/parser" "^7.28.5" + "@babel/template" "^7.27.2" + "@babel/traverse" "^7.28.5" + "@babel/types" "^7.28.5" + "@jridgewell/remapping" "^2.3.5" convert-source-map "^2.0.0" debug "^4.1.0" gensync "^1.0.0-beta.2" @@ -53,170 +45,175 @@ semver "^6.3.1" "@babel/eslint-parser@^7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.19.1.tgz#4f68f6b0825489e00a24b41b6a1ae35414ecd2f4" - integrity sha512-AqNf2QWt1rtu2/1rLswy6CDP7H9Oh3mMhk177Y67Rg8d7RD9WfOLLv8CGn6tisFvS2htm86yIe1yLF6I1UDaGQ== + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.28.5.tgz#0b8883a4a1c2cbed7b3cd9d7765d80e8f480b9ae" + integrity sha512-fcdRcWahONYo+JRnJg1/AekOacGvKx12Gu0qXJXFi2WBqQA1i7+O5PaxRB7kxE/Op94dExnCiiar6T09pvdHpA== dependencies: "@nicolo-ribaudo/eslint-scope-5-internals" "5.1.1-v1" eslint-visitor-keys "^2.1.0" - semver "^6.3.0" + semver "^6.3.1" -"@babel/generator@^7.26.10", "@babel/generator@^7.27.0": - version "7.27.0" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.27.0.tgz#764382b5392e5b9aff93cadb190d0745866cbc2c" - integrity sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw== +"@babel/generator@^7.28.5": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.28.5.tgz#712722d5e50f44d07bc7ac9fe84438742dd61298" + integrity sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ== dependencies: - "@babel/parser" "^7.27.0" - "@babel/types" "^7.27.0" - "@jridgewell/gen-mapping" "^0.3.5" - "@jridgewell/trace-mapping" "^0.3.25" + "@babel/parser" "^7.28.5" + "@babel/types" "^7.28.5" + "@jridgewell/gen-mapping" "^0.3.12" + "@jridgewell/trace-mapping" "^0.3.28" jsesc "^3.0.2" -"@babel/helper-annotate-as-pure@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz#d8eac4d2dc0d7b6e11fa6e535332e0d3184f06b4" - integrity sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g== +"@babel/helper-annotate-as-pure@^7.27.1", "@babel/helper-annotate-as-pure@^7.27.3": + version "7.27.3" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz#f31fd86b915fc4daf1f3ac6976c59be7084ed9c5" + integrity sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg== dependencies: - "@babel/types" "^7.25.9" + "@babel/types" "^7.27.3" -"@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.25.9", "@babel/helper-compilation-targets@^7.26.5": - version "7.27.0" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.0.tgz#de0c753b1cd1d9ab55d473c5a5cf7170f0a81880" - integrity sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA== +"@babel/helper-compilation-targets@^7.27.1", "@babel/helper-compilation-targets@^7.27.2": + version "7.27.2" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz#46a0f6efab808d51d29ce96858dd10ce8732733d" + integrity sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ== dependencies: - "@babel/compat-data" "^7.26.8" - "@babel/helper-validator-option" "^7.25.9" + "@babel/compat-data" "^7.27.2" + "@babel/helper-validator-option" "^7.27.1" browserslist "^4.24.0" lru-cache "^5.1.1" semver "^6.3.1" -"@babel/helper-create-class-features-plugin@^7.25.9": - version "7.27.0" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.0.tgz#518fad6a307c6a96f44af14912b2c20abe9bfc30" - integrity sha512-vSGCvMecvFCd/BdpGlhpXYNhhC4ccxyvQWpbGL4CWbvfEoLFWUZuSuf7s9Aw70flgQF+6vptvgK2IfOnKlRmBg== - dependencies: - "@babel/helper-annotate-as-pure" "^7.25.9" - "@babel/helper-member-expression-to-functions" "^7.25.9" - "@babel/helper-optimise-call-expression" "^7.25.9" - "@babel/helper-replace-supers" "^7.26.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" - "@babel/traverse" "^7.27.0" +"@babel/helper-create-class-features-plugin@^7.27.1", "@babel/helper-create-class-features-plugin@^7.28.3": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.5.tgz#472d0c28028850968979ad89f173594a6995da46" + integrity sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.27.3" + "@babel/helper-member-expression-to-functions" "^7.28.5" + "@babel/helper-optimise-call-expression" "^7.27.1" + "@babel/helper-replace-supers" "^7.27.1" + "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1" + "@babel/traverse" "^7.28.5" semver "^6.3.1" -"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.25.9": - version "7.27.0" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.0.tgz#0e41f7d38c2ebe06ebd9cf0e02fb26019c77cd95" - integrity sha512-fO8l08T76v48BhpNRW/nQ0MxfnSdoSKUJBMjubOAYffsVuGG5qOfMq7N6Es7UJvi7Y8goXXo07EfcHZXDPuELQ== +"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.27.1": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.28.5.tgz#7c1ddd64b2065c7f78034b25b43346a7e19ed997" + integrity sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw== dependencies: - "@babel/helper-annotate-as-pure" "^7.25.9" - regexpu-core "^6.2.0" + "@babel/helper-annotate-as-pure" "^7.27.3" + regexpu-core "^6.3.1" semver "^6.3.1" -"@babel/helper-define-polyfill-provider@^0.6.3", "@babel/helper-define-polyfill-provider@^0.6.4": - version "0.6.4" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.4.tgz#15e8746368bfa671785f5926ff74b3064c291fab" - integrity sha512-jljfR1rGnXXNWnmQg2K3+bvhkxB51Rl32QRaOTuwwjviGrHzIbSc8+x9CpraDtbT7mfyjXObULP4w/adunNwAw== +"@babel/helper-define-polyfill-provider@^0.6.5": + version "0.6.5" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.5.tgz#742ccf1cb003c07b48859fc9fa2c1bbe40e5f753" + integrity sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg== dependencies: - "@babel/helper-compilation-targets" "^7.22.6" - "@babel/helper-plugin-utils" "^7.22.5" - debug "^4.1.1" + "@babel/helper-compilation-targets" "^7.27.2" + "@babel/helper-plugin-utils" "^7.27.1" + debug "^4.4.1" lodash.debounce "^4.0.8" - resolve "^1.14.2" - -"@babel/helper-member-expression-to-functions@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz#9dfffe46f727005a5ea29051ac835fb735e4c1a3" - integrity sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ== - dependencies: - "@babel/traverse" "^7.25.9" - "@babel/types" "^7.25.9" - -"@babel/helper-module-imports@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz#e7f8d20602ebdbf9ebbea0a0751fb0f2a4141715" - integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw== - dependencies: - "@babel/traverse" "^7.25.9" - "@babel/types" "^7.25.9" - -"@babel/helper-module-transforms@^7.25.9", "@babel/helper-module-transforms@^7.26.0": - version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz#8ce54ec9d592695e58d84cd884b7b5c6a2fdeeae" - integrity sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw== - dependencies: - "@babel/helper-module-imports" "^7.25.9" - "@babel/helper-validator-identifier" "^7.25.9" - "@babel/traverse" "^7.25.9" - -"@babel/helper-optimise-call-expression@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz#3324ae50bae7e2ab3c33f60c9a877b6a0146b54e" - integrity sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ== - dependencies: - "@babel/types" "^7.25.9" - -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.25.9", "@babel/helper-plugin-utils@^7.26.5": - version "7.26.5" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz#18580d00c9934117ad719392c4f6585c9333cc35" - integrity sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg== - -"@babel/helper-remap-async-to-generator@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz#e53956ab3d5b9fb88be04b3e2f31b523afd34b92" - integrity sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.25.9" - "@babel/helper-wrap-function" "^7.25.9" - "@babel/traverse" "^7.25.9" - -"@babel/helper-replace-supers@^7.25.9", "@babel/helper-replace-supers@^7.26.5": - version "7.26.5" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.26.5.tgz#6cb04e82ae291dae8e72335dfe438b0725f14c8d" - integrity sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg== - dependencies: - "@babel/helper-member-expression-to-functions" "^7.25.9" - "@babel/helper-optimise-call-expression" "^7.25.9" - "@babel/traverse" "^7.26.5" - -"@babel/helper-skip-transparent-expression-wrappers@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz#0b2e1b62d560d6b1954893fd2b705dc17c91f0c9" - integrity sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA== - dependencies: - "@babel/traverse" "^7.25.9" - "@babel/types" "^7.25.9" - -"@babel/helper-string-parser@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c" - integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA== - -"@babel/helper-validator-identifier@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" - integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== - -"@babel/helper-validator-option@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz#86e45bd8a49ab7e03f276577f96179653d41da72" - integrity sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw== - -"@babel/helper-wrap-function@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz#d99dfd595312e6c894bd7d237470025c85eea9d0" - integrity sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g== - dependencies: - "@babel/template" "^7.25.9" - "@babel/traverse" "^7.25.9" - "@babel/types" "^7.25.9" - -"@babel/helpers@^7.26.10": - version "7.27.0" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.27.0.tgz#53d156098defa8243eab0f32fa17589075a1b808" - integrity sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg== - dependencies: - "@babel/template" "^7.27.0" - "@babel/types" "^7.27.0" + resolve "^1.22.10" + +"@babel/helper-globals@^7.28.0": + version "7.28.0" + resolved "https://registry.yarnpkg.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz#b9430df2aa4e17bc28665eadeae8aa1d985e6674" + integrity sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw== + +"@babel/helper-member-expression-to-functions@^7.27.1", "@babel/helper-member-expression-to-functions@^7.28.5": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz#f3e07a10be37ed7a63461c63e6929575945a6150" + integrity sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg== + dependencies: + "@babel/traverse" "^7.28.5" + "@babel/types" "^7.28.5" + +"@babel/helper-module-imports@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz#7ef769a323e2655e126673bb6d2d6913bbead204" + integrity sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w== + dependencies: + "@babel/traverse" "^7.27.1" + "@babel/types" "^7.27.1" + +"@babel/helper-module-transforms@^7.27.1", "@babel/helper-module-transforms@^7.28.3": + version "7.28.3" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz#a2b37d3da3b2344fe085dab234426f2b9a2fa5f6" + integrity sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw== + dependencies: + "@babel/helper-module-imports" "^7.27.1" + "@babel/helper-validator-identifier" "^7.27.1" + "@babel/traverse" "^7.28.3" + +"@babel/helper-optimise-call-expression@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz#c65221b61a643f3e62705e5dd2b5f115e35f9200" + integrity sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw== + dependencies: + "@babel/types" "^7.27.1" + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz#ddb2f876534ff8013e6c2b299bf4d39b3c51d44c" + integrity sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw== + +"@babel/helper-remap-async-to-generator@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz#4601d5c7ce2eb2aea58328d43725523fcd362ce6" + integrity sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.27.1" + "@babel/helper-wrap-function" "^7.27.1" + "@babel/traverse" "^7.27.1" + +"@babel/helper-replace-supers@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz#b1ed2d634ce3bdb730e4b52de30f8cccfd692bc0" + integrity sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.27.1" + "@babel/helper-optimise-call-expression" "^7.27.1" + "@babel/traverse" "^7.27.1" + +"@babel/helper-skip-transparent-expression-wrappers@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz#62bb91b3abba8c7f1fec0252d9dbea11b3ee7a56" + integrity sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg== + dependencies: + "@babel/traverse" "^7.27.1" + "@babel/types" "^7.27.1" + +"@babel/helper-string-parser@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687" + integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== + +"@babel/helper-validator-identifier@^7.25.9", "@babel/helper-validator-identifier@^7.27.1", "@babel/helper-validator-identifier@^7.28.5": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4" + integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q== + +"@babel/helper-validator-option@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz#fa52f5b1e7db1ab049445b421c4471303897702f" + integrity sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg== + +"@babel/helper-wrap-function@^7.27.1": + version "7.28.3" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.28.3.tgz#fe4872092bc1438ffd0ce579e6f699609f9d0a7a" + integrity sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g== + dependencies: + "@babel/template" "^7.27.2" + "@babel/traverse" "^7.28.3" + "@babel/types" "^7.28.2" + +"@babel/helpers@^7.28.4": + version "7.28.4" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.28.4.tgz#fe07274742e95bdf7cf1443593eeb8926ab63827" + integrity sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w== + dependencies: + "@babel/template" "^7.27.2" + "@babel/types" "^7.28.4" "@babel/highlight@^7.18.6": version "7.25.9" @@ -228,70 +225,70 @@ js-tokens "^4.0.0" picocolors "^1.0.0" -"@babel/parser@^7.26.10", "@babel/parser@^7.27.0": - version "7.27.0" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.27.0.tgz#3d7d6ee268e41d2600091cbd4e145ffee85a44ec" - integrity sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg== +"@babel/parser@^7.27.2", "@babel/parser@^7.28.5": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.5.tgz#0b0225ee90362f030efd644e8034c99468893b08" + integrity sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ== dependencies: - "@babel/types" "^7.27.0" + "@babel/types" "^7.28.5" -"@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz#cc2e53ebf0a0340777fff5ed521943e253b4d8fe" - integrity sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g== +"@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.28.5": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz#fbde57974707bbfa0376d34d425ff4fa6c732421" + integrity sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/traverse" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/traverse" "^7.28.5" -"@babel/plugin-bugfix-safari-class-field-initializer-scope@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz#af9e4fb63ccb8abcb92375b2fcfe36b60c774d30" - integrity sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw== +"@babel/plugin-bugfix-safari-class-field-initializer-scope@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz#43f70a6d7efd52370eefbdf55ae03d91b293856d" + integrity sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz#e8dc26fcd616e6c5bf2bd0d5a2c151d4f92a9137" - integrity sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug== +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz#beb623bd573b8b6f3047bd04c32506adc3e58a72" + integrity sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz#807a667f9158acac6f6164b4beb85ad9ebc9e1d1" - integrity sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g== +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz#e134a5479eb2ba9c02714e8c1ebf1ec9076124fd" + integrity sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" - "@babel/plugin-transform-optional-chaining" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1" + "@babel/plugin-transform-optional-chaining" "^7.27.1" -"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz#de7093f1e7deaf68eadd7cc6b07f2ab82543269e" - integrity sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg== +"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@^7.28.3": + version "7.28.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.3.tgz#373f6e2de0016f73caf8f27004f61d167743742a" + integrity sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/traverse" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/traverse" "^7.28.3" "@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2": version "7.21.0-placeholder-for-preset-env.2" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703" integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w== -"@babel/plugin-syntax-import-assertions@^7.26.0": - version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz#620412405058efa56e4a564903b79355020f445f" - integrity sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg== +"@babel/plugin-syntax-import-assertions@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz#88894aefd2b03b5ee6ad1562a7c8e1587496aecd" + integrity sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-syntax-import-attributes@^7.26.0": - version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz#3b1412847699eea739b4f2602c74ce36f6b0b0f7" - integrity sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A== +"@babel/plugin-syntax-import-attributes@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz#34c017d54496f9b11b61474e7ea3dfd5563ffe07" + integrity sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-syntax-unicode-sets-regex@^7.18.6": version "7.18.6" @@ -301,466 +298,477 @@ "@babel/helper-create-regexp-features-plugin" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-arrow-functions@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz#7821d4410bee5daaadbb4cdd9a6649704e176845" - integrity sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg== +"@babel/plugin-transform-arrow-functions@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz#6e2061067ba3ab0266d834a9f94811196f2aba9a" + integrity sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-async-generator-functions@^7.26.8": - version "7.26.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.26.8.tgz#5e3991135e3b9c6eaaf5eff56d1ae5a11df45ff8" - integrity sha512-He9Ej2X7tNf2zdKMAGOsmg2MrFc+hfoAhd3po4cWfo/NWjzEAKa0oQruj1ROVUdl0e6fb6/kE/G3SSxE0lRJOg== +"@babel/plugin-transform-async-generator-functions@^7.28.0": + version "7.28.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.28.0.tgz#1276e6c7285ab2cd1eccb0bc7356b7a69ff842c2" + integrity sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q== dependencies: - "@babel/helper-plugin-utils" "^7.26.5" - "@babel/helper-remap-async-to-generator" "^7.25.9" - "@babel/traverse" "^7.26.8" + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-remap-async-to-generator" "^7.27.1" + "@babel/traverse" "^7.28.0" -"@babel/plugin-transform-async-to-generator@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz#c80008dacae51482793e5a9c08b39a5be7e12d71" - integrity sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ== +"@babel/plugin-transform-async-to-generator@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz#9a93893b9379b39466c74474f55af03de78c66e7" + integrity sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA== dependencies: - "@babel/helper-module-imports" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/helper-remap-async-to-generator" "^7.25.9" + "@babel/helper-module-imports" "^7.27.1" + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-remap-async-to-generator" "^7.27.1" -"@babel/plugin-transform-block-scoped-functions@^7.26.5": - version "7.26.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.26.5.tgz#3dc4405d31ad1cbe45293aa57205a6e3b009d53e" - integrity sha512-chuTSY+hq09+/f5lMj8ZSYgCFpppV2CbYrhNFJ1BFoXpiWPnnAb7R0MqrafCpN8E1+YRrtM1MXZHJdIx8B6rMQ== +"@babel/plugin-transform-block-scoped-functions@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz#558a9d6e24cf72802dd3b62a4b51e0d62c0f57f9" + integrity sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg== dependencies: - "@babel/helper-plugin-utils" "^7.26.5" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-block-scoping@^7.25.9": - version "7.27.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.27.0.tgz#acc2c0d98a7439bbde4244588ddbd4904701d47f" - integrity sha512-u1jGphZ8uDI2Pj/HJj6YQ6XQLZCNjOlprjxB5SVz6rq2T6SwAR+CdrWK0CP7F+9rDVMXdB0+r6Am5G5aobOjAQ== +"@babel/plugin-transform-block-scoping@^7.28.5": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.5.tgz#e0d3af63bd8c80de2e567e690a54e84d85eb16f6" + integrity sha512-45DmULpySVvmq9Pj3X9B+62Xe+DJGov27QravQJU1LLcapR6/10i+gYVAucGGJpHBp5mYxIMK4nDAT/QDLr47g== dependencies: - "@babel/helper-plugin-utils" "^7.26.5" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-class-properties@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz#a8ce84fedb9ad512549984101fa84080a9f5f51f" - integrity sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q== +"@babel/plugin-transform-class-properties@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz#dd40a6a370dfd49d32362ae206ddaf2bb082a925" + integrity sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA== dependencies: - "@babel/helper-create-class-features-plugin" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-create-class-features-plugin" "^7.27.1" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-class-static-block@^7.26.0": - version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz#6c8da219f4eb15cae9834ec4348ff8e9e09664a0" - integrity sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ== +"@babel/plugin-transform-class-static-block@^7.28.3": + version "7.28.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.3.tgz#d1b8e69b54c9993bc558203e1f49bfc979bfd852" + integrity sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg== dependencies: - "@babel/helper-create-class-features-plugin" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-create-class-features-plugin" "^7.28.3" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-classes@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz#7152457f7880b593a63ade8a861e6e26a4469f52" - integrity sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg== +"@babel/plugin-transform-classes@^7.28.4": + version "7.28.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.4.tgz#75d66175486788c56728a73424d67cbc7473495c" + integrity sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA== dependencies: - "@babel/helper-annotate-as-pure" "^7.25.9" - "@babel/helper-compilation-targets" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/helper-replace-supers" "^7.25.9" - "@babel/traverse" "^7.25.9" - globals "^11.1.0" + "@babel/helper-annotate-as-pure" "^7.27.3" + "@babel/helper-compilation-targets" "^7.27.2" + "@babel/helper-globals" "^7.28.0" + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-replace-supers" "^7.27.1" + "@babel/traverse" "^7.28.4" -"@babel/plugin-transform-computed-properties@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz#db36492c78460e534b8852b1d5befe3c923ef10b" - integrity sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA== +"@babel/plugin-transform-computed-properties@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz#81662e78bf5e734a97982c2b7f0a793288ef3caa" + integrity sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/template" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/template" "^7.27.1" -"@babel/plugin-transform-destructuring@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz#966ea2595c498224340883602d3cfd7a0c79cea1" - integrity sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ== +"@babel/plugin-transform-destructuring@^7.28.0", "@babel/plugin-transform-destructuring@^7.28.5": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz#b8402764df96179a2070bb7b501a1586cf8ad7a7" + integrity sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/traverse" "^7.28.5" -"@babel/plugin-transform-dotall-regex@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz#bad7945dd07734ca52fe3ad4e872b40ed09bb09a" - integrity sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA== +"@babel/plugin-transform-dotall-regex@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz#aa6821de864c528b1fecf286f0a174e38e826f4d" + integrity sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-create-regexp-features-plugin" "^7.27.1" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-duplicate-keys@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz#8850ddf57dce2aebb4394bb434a7598031059e6d" - integrity sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw== +"@babel/plugin-transform-duplicate-keys@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz#f1fbf628ece18e12e7b32b175940e68358f546d1" + integrity sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-duplicate-named-capturing-groups-regex@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz#6f7259b4de127721a08f1e5165b852fcaa696d31" - integrity sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog== +"@babel/plugin-transform-duplicate-named-capturing-groups-regex@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.27.1.tgz#5043854ca620a94149372e69030ff8cb6a9eb0ec" + integrity sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-create-regexp-features-plugin" "^7.27.1" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-dynamic-import@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz#23e917de63ed23c6600c5dd06d94669dce79f7b8" - integrity sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg== +"@babel/plugin-transform-dynamic-import@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz#4c78f35552ac0e06aa1f6e3c573d67695e8af5a4" + integrity sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-exponentiation-operator@^7.26.3": - version "7.26.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz#e29f01b6de302c7c2c794277a48f04a9ca7f03bc" - integrity sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ== +"@babel/plugin-transform-explicit-resource-management@^7.28.0": + version "7.28.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.0.tgz#45be6211b778dbf4b9d54c4e8a2b42fa72e09a1a" + integrity sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/plugin-transform-destructuring" "^7.28.0" -"@babel/plugin-transform-export-namespace-from@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz#90745fe55053394f554e40584cda81f2c8a402a2" - integrity sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww== +"@babel/plugin-transform-exponentiation-operator@^7.28.5": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.5.tgz#7cc90a8170e83532676cfa505278e147056e94fe" + integrity sha512-D4WIMaFtwa2NizOp+dnoFjRez/ClKiC2BqqImwKd1X28nqBtZEyCYJ2ozQrrzlxAFrcrjxo39S6khe9RNDlGzw== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-for-of@^7.26.9": - version "7.26.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.26.9.tgz#27231f79d5170ef33b5111f07fe5cafeb2c96a56" - integrity sha512-Hry8AusVm8LW5BVFgiyUReuoGzPUpdHQQqJY5bZnbbf+ngOHWuCuYFKw/BqaaWlvEUrF91HMhDtEaI1hZzNbLg== +"@babel/plugin-transform-export-namespace-from@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz#71ca69d3471edd6daa711cf4dfc3400415df9c23" + integrity sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ== dependencies: - "@babel/helper-plugin-utils" "^7.26.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-function-name@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz#939d956e68a606661005bfd550c4fc2ef95f7b97" - integrity sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA== +"@babel/plugin-transform-for-of@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz#bc24f7080e9ff721b63a70ac7b2564ca15b6c40a" + integrity sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw== dependencies: - "@babel/helper-compilation-targets" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/traverse" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1" -"@babel/plugin-transform-json-strings@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz#c86db407cb827cded902a90c707d2781aaa89660" - integrity sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw== +"@babel/plugin-transform-function-name@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz#4d0bf307720e4dce6d7c30fcb1fd6ca77bdeb3a7" + integrity sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-compilation-targets" "^7.27.1" + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/traverse" "^7.27.1" -"@babel/plugin-transform-literals@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz#1a1c6b4d4aa59bc4cad5b6b3a223a0abd685c9de" - integrity sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ== +"@babel/plugin-transform-json-strings@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.27.1.tgz#a2e0ce6ef256376bd527f290da023983527a4f4c" + integrity sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-logical-assignment-operators@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz#b19441a8c39a2fda0902900b306ea05ae1055db7" - integrity sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q== +"@babel/plugin-transform-literals@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz#baaefa4d10a1d4206f9dcdda50d7d5827bb70b24" + integrity sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-member-expression-literals@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz#63dff19763ea64a31f5e6c20957e6a25e41ed5de" - integrity sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA== +"@babel/plugin-transform-logical-assignment-operators@^7.28.5": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.5.tgz#d028fd6db8c081dee4abebc812c2325e24a85b0e" + integrity sha512-axUuqnUTBuXyHGcJEVVh9pORaN6wC5bYfE7FGzPiaWa3syib9m7g+/IT/4VgCOe2Upef43PHzeAvcrVek6QuuA== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-modules-amd@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz#49ba478f2295101544abd794486cd3088dddb6c5" - integrity sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw== +"@babel/plugin-transform-member-expression-literals@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz#37b88ba594d852418e99536f5612f795f23aeaf9" + integrity sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ== dependencies: - "@babel/helper-module-transforms" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-modules-commonjs@^7.26.3": - version "7.26.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz#8f011d44b20d02c3de44d8850d971d8497f981fb" - integrity sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ== +"@babel/plugin-transform-modules-amd@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz#a4145f9d87c2291fe2d05f994b65dba4e3e7196f" + integrity sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA== dependencies: - "@babel/helper-module-transforms" "^7.26.0" - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-module-transforms" "^7.27.1" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-modules-systemjs@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz#8bd1b43836269e3d33307151a114bcf3ba6793f8" - integrity sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA== +"@babel/plugin-transform-modules-commonjs@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz#8e44ed37c2787ecc23bdc367f49977476614e832" + integrity sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw== dependencies: - "@babel/helper-module-transforms" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/helper-validator-identifier" "^7.25.9" - "@babel/traverse" "^7.25.9" + "@babel/helper-module-transforms" "^7.27.1" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-modules-umd@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz#6710079cdd7c694db36529a1e8411e49fcbf14c9" - integrity sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw== +"@babel/plugin-transform-modules-systemjs@^7.28.5": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.28.5.tgz#7439e592a92d7670dfcb95d0cbc04bd3e64801d2" + integrity sha512-vn5Jma98LCOeBy/KpeQhXcV2WZgaRUtjwQmjoBuLNlOmkg0fB5pdvYVeWRYI69wWKwK2cD1QbMiUQnoujWvrew== dependencies: - "@babel/helper-module-transforms" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-module-transforms" "^7.28.3" + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-validator-identifier" "^7.28.5" + "@babel/traverse" "^7.28.5" -"@babel/plugin-transform-named-capturing-groups-regex@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz#454990ae6cc22fd2a0fa60b3a2c6f63a38064e6a" - integrity sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA== +"@babel/plugin-transform-modules-umd@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz#63f2cf4f6dc15debc12f694e44714863d34cd334" + integrity sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-module-transforms" "^7.27.1" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-new-target@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz#42e61711294b105c248336dcb04b77054ea8becd" - integrity sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ== +"@babel/plugin-transform-named-capturing-groups-regex@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz#f32b8f7818d8fc0cc46ee20a8ef75f071af976e1" + integrity sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-create-regexp-features-plugin" "^7.27.1" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-nullish-coalescing-operator@^7.26.6": - version "7.26.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.26.6.tgz#fbf6b3c92cb509e7b319ee46e3da89c5bedd31fe" - integrity sha512-CKW8Vu+uUZneQCPtXmSBUC6NCAUdya26hWCElAWh5mVSlSRsmiCPUUDKb3Z0szng1hiAJa098Hkhg9o4SE35Qw== +"@babel/plugin-transform-new-target@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz#259c43939728cad1706ac17351b7e6a7bea1abeb" + integrity sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ== dependencies: - "@babel/helper-plugin-utils" "^7.26.5" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-numeric-separator@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz#bfed75866261a8b643468b0ccfd275f2033214a1" - integrity sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q== +"@babel/plugin-transform-nullish-coalescing-operator@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz#4f9d3153bf6782d73dd42785a9d22d03197bc91d" + integrity sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-object-rest-spread@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz#0203725025074164808bcf1a2cfa90c652c99f18" - integrity sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg== +"@babel/plugin-transform-numeric-separator@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.27.1.tgz#614e0b15cc800e5997dadd9bd6ea524ed6c819c6" + integrity sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw== dependencies: - "@babel/helper-compilation-targets" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/plugin-transform-parameters" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-object-super@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz#385d5de135162933beb4a3d227a2b7e52bb4cf03" - integrity sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A== +"@babel/plugin-transform-object-rest-spread@^7.28.4": + version "7.28.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.4.tgz#9ee1ceca80b3e6c4bac9247b2149e36958f7f98d" + integrity sha512-373KA2HQzKhQCYiRVIRr+3MjpCObqzDlyrM6u4I201wL8Mp2wHf7uB8GhDwis03k2ti8Zr65Zyyqs1xOxUF/Ew== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/helper-replace-supers" "^7.25.9" + "@babel/helper-compilation-targets" "^7.27.2" + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/plugin-transform-destructuring" "^7.28.0" + "@babel/plugin-transform-parameters" "^7.27.7" + "@babel/traverse" "^7.28.4" -"@babel/plugin-transform-optional-catch-binding@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz#10e70d96d52bb1f10c5caaac59ac545ea2ba7ff3" - integrity sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g== +"@babel/plugin-transform-object-super@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz#1c932cd27bf3874c43a5cac4f43ebf970c9871b5" + integrity sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-replace-supers" "^7.27.1" -"@babel/plugin-transform-optional-chaining@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz#e142eb899d26ef715435f201ab6e139541eee7dd" - integrity sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A== +"@babel/plugin-transform-optional-catch-binding@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.27.1.tgz#84c7341ebde35ccd36b137e9e45866825072a30c" + integrity sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-parameters@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz#b856842205b3e77e18b7a7a1b94958069c7ba257" - integrity sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g== +"@babel/plugin-transform-optional-chaining@^7.27.1", "@babel/plugin-transform-optional-chaining@^7.28.5": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.5.tgz#8238c785f9d5c1c515a90bf196efb50d075a4b26" + integrity sha512-N6fut9IZlPnjPwgiQkXNhb+cT8wQKFlJNqcZkWlcTqkcqx6/kU4ynGmLFoa4LViBSirn05YAwk+sQBbPfxtYzQ== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1" -"@babel/plugin-transform-private-methods@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz#847f4139263577526455d7d3223cd8bda51e3b57" - integrity sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw== +"@babel/plugin-transform-parameters@^7.27.7": + version "7.27.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz#1fd2febb7c74e7d21cf3b05f7aebc907940af53a" + integrity sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg== dependencies: - "@babel/helper-create-class-features-plugin" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-private-property-in-object@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz#9c8b73e64e6cc3cbb2743633885a7dd2c385fe33" - integrity sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw== +"@babel/plugin-transform-private-methods@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.27.1.tgz#fdacbab1c5ed81ec70dfdbb8b213d65da148b6af" + integrity sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA== dependencies: - "@babel/helper-annotate-as-pure" "^7.25.9" - "@babel/helper-create-class-features-plugin" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-create-class-features-plugin" "^7.27.1" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-property-literals@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz#d72d588bd88b0dec8b62e36f6fda91cedfe28e3f" - integrity sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA== +"@babel/plugin-transform-private-property-in-object@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.27.1.tgz#4dbbef283b5b2f01a21e81e299f76e35f900fb11" + integrity sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-annotate-as-pure" "^7.27.1" + "@babel/helper-create-class-features-plugin" "^7.27.1" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-regenerator@^7.25.9": - version "7.27.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.27.0.tgz#822feebef43d6a59a81f696b2512df5b1682db31" - integrity sha512-LX/vCajUJQDqE7Aum/ELUMZAY19+cDpghxrnyt5I1tV6X5PyC86AOoWXWFYFeIvauyeSA6/ktn4tQVn/3ZifsA== +"@babel/plugin-transform-property-literals@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz#07eafd618800591e88073a0af1b940d9a42c6424" + integrity sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ== dependencies: - "@babel/helper-plugin-utils" "^7.26.5" - regenerator-transform "^0.15.2" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-regexp-modifiers@^7.26.0": - version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz#2f5837a5b5cd3842a919d8147e9903cc7455b850" - integrity sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw== +"@babel/plugin-transform-regenerator@^7.28.4": + version "7.28.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.4.tgz#9d3fa3bebb48ddd0091ce5729139cd99c67cea51" + integrity sha512-+ZEdQlBoRg9m2NnzvEeLgtvBMO4tkFBw5SQIUgLICgTrumLoU7lr+Oghi6km2PFj+dbUt2u1oby2w3BDO9YQnA== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-reserved-words@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz#0398aed2f1f10ba3f78a93db219b27ef417fb9ce" - integrity sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg== +"@babel/plugin-transform-regexp-modifiers@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.27.1.tgz#df9ba5577c974e3f1449888b70b76169998a6d09" + integrity sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-create-regexp-features-plugin" "^7.27.1" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-shorthand-properties@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz#bb785e6091f99f826a95f9894fc16fde61c163f2" - integrity sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng== +"@babel/plugin-transform-reserved-words@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz#40fba4878ccbd1c56605a4479a3a891ac0274bb4" + integrity sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-spread@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz#24a35153931b4ba3d13cec4a7748c21ab5514ef9" - integrity sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A== +"@babel/plugin-transform-shorthand-properties@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz#532abdacdec87bfee1e0ef8e2fcdee543fe32b90" + integrity sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-sticky-regex@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz#c7f02b944e986a417817b20ba2c504dfc1453d32" - integrity sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA== +"@babel/plugin-transform-spread@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz#1a264d5fc12750918f50e3fe3e24e437178abb08" + integrity sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1" -"@babel/plugin-transform-template-literals@^7.26.8": - version "7.26.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.26.8.tgz#966b15d153a991172a540a69ad5e1845ced990b5" - integrity sha512-OmGDL5/J0CJPJZTHZbi2XpO0tyT2Ia7fzpW5GURwdtp2X3fMmN8au/ej6peC/T33/+CRiIpA8Krse8hFGVmT5Q== +"@babel/plugin-transform-sticky-regex@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz#18984935d9d2296843a491d78a014939f7dcd280" + integrity sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g== dependencies: - "@babel/helper-plugin-utils" "^7.26.5" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-typeof-symbol@^7.26.7": - version "7.27.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.0.tgz#044a0890f3ca694207c7826d0c7a65e5ac008aae" - integrity sha512-+LLkxA9rKJpNoGsbLnAgOCdESl73vwYn+V6b+5wHbrE7OGKVDPHIQvbFSzqE6rwqaCw2RE+zdJrlLkcf8YOA0w== +"@babel/plugin-transform-template-literals@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz#1a0eb35d8bb3e6efc06c9fd40eb0bcef548328b8" + integrity sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg== dependencies: - "@babel/helper-plugin-utils" "^7.26.5" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-unicode-escapes@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz#a75ef3947ce15363fccaa38e2dd9bc70b2788b82" - integrity sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q== +"@babel/plugin-transform-typeof-symbol@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz#70e966bb492e03509cf37eafa6dcc3051f844369" + integrity sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-unicode-property-regex@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz#a901e96f2c1d071b0d1bb5dc0d3c880ce8f53dd3" - integrity sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg== +"@babel/plugin-transform-unicode-escapes@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz#3e3143f8438aef842de28816ece58780190cf806" + integrity sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-unicode-regex@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz#5eae747fe39eacf13a8bd006a4fb0b5d1fa5e9b1" - integrity sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA== +"@babel/plugin-transform-unicode-property-regex@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.27.1.tgz#bdfe2d3170c78c5691a3c3be934c8c0087525956" + integrity sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-create-regexp-features-plugin" "^7.27.1" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-unicode-sets-regex@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz#65114c17b4ffc20fa5b163c63c70c0d25621fabe" - integrity sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ== +"@babel/plugin-transform-unicode-regex@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz#25948f5c395db15f609028e370667ed8bae9af97" + integrity sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-create-regexp-features-plugin" "^7.27.1" + "@babel/helper-plugin-utils" "^7.27.1" + +"@babel/plugin-transform-unicode-sets-regex@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.27.1.tgz#6ab706d10f801b5c72da8bb2548561fa04193cd1" + integrity sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.27.1" + "@babel/helper-plugin-utils" "^7.27.1" "@babel/preset-env@^7.20.2": - version "7.26.9" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.26.9.tgz#2ec64e903d0efe743699f77a10bdf7955c2123c3" - integrity sha512-vX3qPGE8sEKEAZCWk05k3cpTAE3/nOYca++JA+Rd0z2NCNzabmYvEiSShKzm10zdquOIAVXsy2Ei/DTW34KlKQ== - dependencies: - "@babel/compat-data" "^7.26.8" - "@babel/helper-compilation-targets" "^7.26.5" - "@babel/helper-plugin-utils" "^7.26.5" - "@babel/helper-validator-option" "^7.25.9" - "@babel/plugin-bugfix-firefox-class-in-computed-class-key" "^7.25.9" - "@babel/plugin-bugfix-safari-class-field-initializer-scope" "^7.25.9" - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.25.9" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.25.9" - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly" "^7.25.9" + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.28.5.tgz#82dd159d1563f219a1ce94324b3071eb89e280b0" + integrity sha512-S36mOoi1Sb6Fz98fBfE+UZSpYw5mJm0NUHtIKrOuNcqeFauy1J6dIvXm2KRVKobOSaGq4t/hBXdN4HGU3wL9Wg== + dependencies: + "@babel/compat-data" "^7.28.5" + "@babel/helper-compilation-targets" "^7.27.2" + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-validator-option" "^7.27.1" + "@babel/plugin-bugfix-firefox-class-in-computed-class-key" "^7.28.5" + "@babel/plugin-bugfix-safari-class-field-initializer-scope" "^7.27.1" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.27.1" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.27.1" + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly" "^7.28.3" "@babel/plugin-proposal-private-property-in-object" "7.21.0-placeholder-for-preset-env.2" - "@babel/plugin-syntax-import-assertions" "^7.26.0" - "@babel/plugin-syntax-import-attributes" "^7.26.0" + "@babel/plugin-syntax-import-assertions" "^7.27.1" + "@babel/plugin-syntax-import-attributes" "^7.27.1" "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" - "@babel/plugin-transform-arrow-functions" "^7.25.9" - "@babel/plugin-transform-async-generator-functions" "^7.26.8" - "@babel/plugin-transform-async-to-generator" "^7.25.9" - "@babel/plugin-transform-block-scoped-functions" "^7.26.5" - "@babel/plugin-transform-block-scoping" "^7.25.9" - "@babel/plugin-transform-class-properties" "^7.25.9" - "@babel/plugin-transform-class-static-block" "^7.26.0" - "@babel/plugin-transform-classes" "^7.25.9" - "@babel/plugin-transform-computed-properties" "^7.25.9" - "@babel/plugin-transform-destructuring" "^7.25.9" - "@babel/plugin-transform-dotall-regex" "^7.25.9" - "@babel/plugin-transform-duplicate-keys" "^7.25.9" - "@babel/plugin-transform-duplicate-named-capturing-groups-regex" "^7.25.9" - "@babel/plugin-transform-dynamic-import" "^7.25.9" - "@babel/plugin-transform-exponentiation-operator" "^7.26.3" - "@babel/plugin-transform-export-namespace-from" "^7.25.9" - "@babel/plugin-transform-for-of" "^7.26.9" - "@babel/plugin-transform-function-name" "^7.25.9" - "@babel/plugin-transform-json-strings" "^7.25.9" - "@babel/plugin-transform-literals" "^7.25.9" - "@babel/plugin-transform-logical-assignment-operators" "^7.25.9" - "@babel/plugin-transform-member-expression-literals" "^7.25.9" - "@babel/plugin-transform-modules-amd" "^7.25.9" - "@babel/plugin-transform-modules-commonjs" "^7.26.3" - "@babel/plugin-transform-modules-systemjs" "^7.25.9" - "@babel/plugin-transform-modules-umd" "^7.25.9" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.25.9" - "@babel/plugin-transform-new-target" "^7.25.9" - "@babel/plugin-transform-nullish-coalescing-operator" "^7.26.6" - "@babel/plugin-transform-numeric-separator" "^7.25.9" - "@babel/plugin-transform-object-rest-spread" "^7.25.9" - "@babel/plugin-transform-object-super" "^7.25.9" - "@babel/plugin-transform-optional-catch-binding" "^7.25.9" - "@babel/plugin-transform-optional-chaining" "^7.25.9" - "@babel/plugin-transform-parameters" "^7.25.9" - "@babel/plugin-transform-private-methods" "^7.25.9" - "@babel/plugin-transform-private-property-in-object" "^7.25.9" - "@babel/plugin-transform-property-literals" "^7.25.9" - "@babel/plugin-transform-regenerator" "^7.25.9" - "@babel/plugin-transform-regexp-modifiers" "^7.26.0" - "@babel/plugin-transform-reserved-words" "^7.25.9" - "@babel/plugin-transform-shorthand-properties" "^7.25.9" - "@babel/plugin-transform-spread" "^7.25.9" - "@babel/plugin-transform-sticky-regex" "^7.25.9" - "@babel/plugin-transform-template-literals" "^7.26.8" - "@babel/plugin-transform-typeof-symbol" "^7.26.7" - "@babel/plugin-transform-unicode-escapes" "^7.25.9" - "@babel/plugin-transform-unicode-property-regex" "^7.25.9" - "@babel/plugin-transform-unicode-regex" "^7.25.9" - "@babel/plugin-transform-unicode-sets-regex" "^7.25.9" + "@babel/plugin-transform-arrow-functions" "^7.27.1" + "@babel/plugin-transform-async-generator-functions" "^7.28.0" + "@babel/plugin-transform-async-to-generator" "^7.27.1" + "@babel/plugin-transform-block-scoped-functions" "^7.27.1" + "@babel/plugin-transform-block-scoping" "^7.28.5" + "@babel/plugin-transform-class-properties" "^7.27.1" + "@babel/plugin-transform-class-static-block" "^7.28.3" + "@babel/plugin-transform-classes" "^7.28.4" + "@babel/plugin-transform-computed-properties" "^7.27.1" + "@babel/plugin-transform-destructuring" "^7.28.5" + "@babel/plugin-transform-dotall-regex" "^7.27.1" + "@babel/plugin-transform-duplicate-keys" "^7.27.1" + "@babel/plugin-transform-duplicate-named-capturing-groups-regex" "^7.27.1" + "@babel/plugin-transform-dynamic-import" "^7.27.1" + "@babel/plugin-transform-explicit-resource-management" "^7.28.0" + "@babel/plugin-transform-exponentiation-operator" "^7.28.5" + "@babel/plugin-transform-export-namespace-from" "^7.27.1" + "@babel/plugin-transform-for-of" "^7.27.1" + "@babel/plugin-transform-function-name" "^7.27.1" + "@babel/plugin-transform-json-strings" "^7.27.1" + "@babel/plugin-transform-literals" "^7.27.1" + "@babel/plugin-transform-logical-assignment-operators" "^7.28.5" + "@babel/plugin-transform-member-expression-literals" "^7.27.1" + "@babel/plugin-transform-modules-amd" "^7.27.1" + "@babel/plugin-transform-modules-commonjs" "^7.27.1" + "@babel/plugin-transform-modules-systemjs" "^7.28.5" + "@babel/plugin-transform-modules-umd" "^7.27.1" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.27.1" + "@babel/plugin-transform-new-target" "^7.27.1" + "@babel/plugin-transform-nullish-coalescing-operator" "^7.27.1" + "@babel/plugin-transform-numeric-separator" "^7.27.1" + "@babel/plugin-transform-object-rest-spread" "^7.28.4" + "@babel/plugin-transform-object-super" "^7.27.1" + "@babel/plugin-transform-optional-catch-binding" "^7.27.1" + "@babel/plugin-transform-optional-chaining" "^7.28.5" + "@babel/plugin-transform-parameters" "^7.27.7" + "@babel/plugin-transform-private-methods" "^7.27.1" + "@babel/plugin-transform-private-property-in-object" "^7.27.1" + "@babel/plugin-transform-property-literals" "^7.27.1" + "@babel/plugin-transform-regenerator" "^7.28.4" + "@babel/plugin-transform-regexp-modifiers" "^7.27.1" + "@babel/plugin-transform-reserved-words" "^7.27.1" + "@babel/plugin-transform-shorthand-properties" "^7.27.1" + "@babel/plugin-transform-spread" "^7.27.1" + "@babel/plugin-transform-sticky-regex" "^7.27.1" + "@babel/plugin-transform-template-literals" "^7.27.1" + "@babel/plugin-transform-typeof-symbol" "^7.27.1" + "@babel/plugin-transform-unicode-escapes" "^7.27.1" + "@babel/plugin-transform-unicode-property-regex" "^7.27.1" + "@babel/plugin-transform-unicode-regex" "^7.27.1" + "@babel/plugin-transform-unicode-sets-regex" "^7.27.1" "@babel/preset-modules" "0.1.6-no-external-plugins" - babel-plugin-polyfill-corejs2 "^0.4.10" - babel-plugin-polyfill-corejs3 "^0.11.0" - babel-plugin-polyfill-regenerator "^0.6.1" - core-js-compat "^3.40.0" + babel-plugin-polyfill-corejs2 "^0.4.14" + babel-plugin-polyfill-corejs3 "^0.13.0" + babel-plugin-polyfill-regenerator "^0.6.5" + core-js-compat "^3.43.0" semver "^6.3.1" "@babel/preset-modules@0.1.6-no-external-plugins": @@ -772,42 +780,42 @@ "@babel/types" "^7.4.4" esutils "^2.0.2" -"@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.9.2": version "7.27.0" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.27.0.tgz#fbee7cf97c709518ecc1f590984481d5460d4762" integrity sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw== dependencies: regenerator-runtime "^0.14.0" -"@babel/template@^7.25.9", "@babel/template@^7.26.9", "@babel/template@^7.27.0": - version "7.27.0" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.0.tgz#b253e5406cc1df1c57dcd18f11760c2dbf40c0b4" - integrity sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA== - dependencies: - "@babel/code-frame" "^7.26.2" - "@babel/parser" "^7.27.0" - "@babel/types" "^7.27.0" - -"@babel/traverse@^7.25.9", "@babel/traverse@^7.26.10", "@babel/traverse@^7.26.5", "@babel/traverse@^7.26.8", "@babel/traverse@^7.27.0": - version "7.27.0" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.27.0.tgz#11d7e644779e166c0442f9a07274d02cd91d4a70" - integrity sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA== - dependencies: - "@babel/code-frame" "^7.26.2" - "@babel/generator" "^7.27.0" - "@babel/parser" "^7.27.0" - "@babel/template" "^7.27.0" - "@babel/types" "^7.27.0" +"@babel/template@^7.27.1", "@babel/template@^7.27.2": + version "7.27.2" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.2.tgz#fa78ceed3c4e7b63ebf6cb39e5852fca45f6809d" + integrity sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw== + dependencies: + "@babel/code-frame" "^7.27.1" + "@babel/parser" "^7.27.2" + "@babel/types" "^7.27.1" + +"@babel/traverse@^7.27.1", "@babel/traverse@^7.28.0", "@babel/traverse@^7.28.3", "@babel/traverse@^7.28.4", "@babel/traverse@^7.28.5": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.5.tgz#450cab9135d21a7a2ca9d2d35aa05c20e68c360b" + integrity sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ== + dependencies: + "@babel/code-frame" "^7.27.1" + "@babel/generator" "^7.28.5" + "@babel/helper-globals" "^7.28.0" + "@babel/parser" "^7.28.5" + "@babel/template" "^7.27.2" + "@babel/types" "^7.28.5" debug "^4.3.1" - globals "^11.1.0" -"@babel/types@^7.25.9", "@babel/types@^7.26.10", "@babel/types@^7.27.0", "@babel/types@^7.4.4": - version "7.27.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.27.0.tgz#ef9acb6b06c3173f6632d993ecb6d4ae470b4559" - integrity sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg== +"@babel/types@^7.27.1", "@babel/types@^7.27.3", "@babel/types@^7.28.2", "@babel/types@^7.28.4", "@babel/types@^7.28.5", "@babel/types@^7.4.4": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.5.tgz#10fc405f60897c35f07e85493c932c7b5ca0592b" + integrity sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA== dependencies: - "@babel/helper-string-parser" "^7.25.9" - "@babel/helper-validator-identifier" "^7.25.9" + "@babel/helper-string-parser" "^7.27.1" + "@babel/helper-validator-identifier" "^7.28.5" "@discoveryjs/json-ext@^0.5.0": version "0.5.7" @@ -884,13 +892,20 @@ "@types/yargs" "^17.0.8" chalk "^4.0.0" -"@jridgewell/gen-mapping@^0.3.5": - version "0.3.8" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz#4f0e06362e01362f823d348f1872b08f666d8142" - integrity sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA== +"@jridgewell/gen-mapping@^0.3.12", "@jridgewell/gen-mapping@^0.3.5": + version "0.3.13" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f" + integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA== dependencies: - "@jridgewell/set-array" "^1.2.1" - "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/sourcemap-codec" "^1.5.0" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/remapping@^2.3.5": + version "2.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/remapping/-/remapping-2.3.5.tgz#375c476d1972947851ba1e15ae8f123047445aa1" + integrity sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.24" "@jridgewell/resolve-uri@^3.1.0": @@ -898,11 +913,6 @@ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== -"@jridgewell/set-array@^1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" - integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== - "@jridgewell/source-map@^0.3.3": version "0.3.6" resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.6.tgz#9d71ca886e32502eb9362c9a74a46787c36df81a" @@ -911,12 +921,12 @@ "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.25" -"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" - integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== +"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0": + version "1.5.5" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" + integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== -"@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": +"@jridgewell/trace-mapping@^0.3.20": version "0.3.25" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== @@ -924,6 +934,14 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25", "@jridgewell/trace-mapping@^0.3.28": + version "0.3.31" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0" + integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + "@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1": version "5.1.1-v1" resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz#dbf733a965ca47b1973177dc0bb6c889edcfb129" @@ -1300,35 +1318,40 @@ babel-loader@^9.1.2: find-cache-dir "^3.3.2" schema-utils "^4.0.0" -babel-plugin-polyfill-corejs2@^0.4.10: - version "0.4.13" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.13.tgz#7d445f0e0607ebc8fb6b01d7e8fb02069b91dd8b" - integrity sha512-3sX/eOms8kd3q2KZ6DAhKPc0dgm525Gqq5NtWKZ7QYYZEv57OQ54KtblzJzH1lQF/eQxO8KjWGIK9IPUJNus5g== +babel-plugin-polyfill-corejs2@^0.4.14: + version "0.4.14" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.14.tgz#8101b82b769c568835611542488d463395c2ef8f" + integrity sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg== dependencies: - "@babel/compat-data" "^7.22.6" - "@babel/helper-define-polyfill-provider" "^0.6.4" + "@babel/compat-data" "^7.27.7" + "@babel/helper-define-polyfill-provider" "^0.6.5" semver "^6.3.1" -babel-plugin-polyfill-corejs3@^0.11.0: - version "0.11.1" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz#4e4e182f1bb37c7ba62e2af81d8dd09df31344f6" - integrity sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ== +babel-plugin-polyfill-corejs3@^0.13.0: + version "0.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz#bb7f6aeef7addff17f7602a08a6d19a128c30164" + integrity sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A== dependencies: - "@babel/helper-define-polyfill-provider" "^0.6.3" - core-js-compat "^3.40.0" + "@babel/helper-define-polyfill-provider" "^0.6.5" + core-js-compat "^3.43.0" -babel-plugin-polyfill-regenerator@^0.6.1: - version "0.6.4" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.4.tgz#428c615d3c177292a22b4f93ed99e358d7906a9b" - integrity sha512-7gD3pRadPrbjhjLyxebmx/WrFYcuSjZ0XbdUujQMZ/fcE9oeewk2U/7PCvez84UeuK3oSjmPZ0Ch0dlupQvGzw== +babel-plugin-polyfill-regenerator@^0.6.5: + version "0.6.5" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.5.tgz#32752e38ab6f6767b92650347bf26a31b16ae8c5" + integrity sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg== dependencies: - "@babel/helper-define-polyfill-provider" "^0.6.4" + "@babel/helper-define-polyfill-provider" "^0.6.5" balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +baseline-browser-mapping@^2.8.25: + version "2.8.28" + resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.8.28.tgz#9ef511f5a7c19d74a94cafcbf951608398e9bdb3" + integrity sha512-gYjt7OIqdM0PcttNYP2aVrr2G0bMALkBaoehD4BuRGjAOtipg0b6wHg1yNL+s5zSnLZZrGHOw4IrND8CD+3oIQ== + big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" @@ -1376,7 +1399,7 @@ braces@~3.0.2: dependencies: fill-range "^7.1.1" -browserslist@^4.0.0, browserslist@^4.21.4, browserslist@^4.24.0, browserslist@^4.24.4: +browserslist@^4.0.0, browserslist@^4.21.4: version "4.24.4" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.4.tgz#c6b2865a3f08bcb860a0e827389003b9fe686e4b" integrity sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A== @@ -1396,6 +1419,17 @@ browserslist@^4.21.10: node-releases "^2.0.18" update-browserslist-db "^1.1.0" +browserslist@^4.24.0, browserslist@^4.26.3: + version "4.28.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.0.tgz#9cefece0a386a17a3cd3d22ebf67b9deca1b5929" + integrity sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ== + dependencies: + baseline-browser-mapping "^2.8.25" + caniuse-lite "^1.0.30001754" + electron-to-chromium "^1.5.249" + node-releases "^2.0.27" + update-browserslist-db "^1.1.4" + buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" @@ -1426,11 +1460,16 @@ caniuse-lite@^1.0.30001426: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001450.tgz#022225b91200589196b814b51b1bbe45144cf74f" integrity sha512-qMBmvmQmFXaSxexkjjfMvD5rnDL0+m+dUMZKoDYsGG8iZN29RuYh9eRoMvKsT6uMAWlyUUGDEQGJJYjzCIO9ew== -caniuse-lite@^1.0.30001646, caniuse-lite@^1.0.30001688: +caniuse-lite@^1.0.30001646: version "1.0.30001707" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001707.tgz#c5e104d199e6f4355a898fcd995a066c7eb9bf41" integrity sha512-3qtRjw/HQSMlDWf+X79N206fepf4SOOU6SQLMaq/0KkZLmSjPxAkBOQQ+FxbHKfHmYLZFfdWsO3KA90ceHPSnw== +caniuse-lite@^1.0.30001688, caniuse-lite@^1.0.30001754: + version "1.0.30001755" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001755.tgz#c01cfb1c30f5acf1229391666ec03492f4c332ff" + integrity sha512-44V+Jm6ctPj7R52Na4TLi3Zri4dWUljJd+RDm+j8LtNCc/ihLCT+X1TzoOAkRETEWqjuLnh9581Tl80FvK7jVA== + chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -1565,12 +1604,12 @@ convert-source-map@^2.0.0: resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== -core-js-compat@^3.40.0: - version "3.41.0" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.41.0.tgz#4cdfce95f39a8f27759b667cf693d96e5dda3d17" - integrity sha512-RFsU9LySVue9RTwdDVX/T0e2Y6jRYWXERKElIjpuEOEnxaXffI0X7RUwVzfYLfzuLXSNJDYoRYUAmRUcyln20A== +core-js-compat@^3.43.0: + version "3.46.0" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.46.0.tgz#0c87126a19a1af00371e12b02a2b088a40f3c6f7" + integrity sha512-p9hObIIEENxSV8xIu+V68JjSeARg6UVMG5mR+JEUguG3sI6MsiS1njz2jHmyJDvA+8jX/sytkBHup6kxhM9law== dependencies: - browserslist "^4.24.4" + browserslist "^4.26.3" core-js@^3.27.2: version "3.27.2" @@ -1713,21 +1752,7 @@ csso@^4.2.0: dependencies: css-tree "^1.1.2" -debug@^4.1.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" - integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== - dependencies: - ms "^2.1.3" - -debug@^4.1.1: - version "4.4.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b" - integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ== - dependencies: - ms "^2.1.3" - -debug@^4.3.1, debug@^4.3.2: +debug@^4.1.0, debug@^4.3.1, debug@^4.3.2, debug@^4.4.1: version "4.4.3" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== @@ -1796,7 +1821,12 @@ editorconfig@^1.0.2: minimatch "9.0.1" semver "^7.5.3" -electron-to-chromium@^1.5.4, electron-to-chromium@^1.5.73: +electron-to-chromium@^1.5.249, electron-to-chromium@^1.5.73: + version "1.5.254" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.254.tgz#94b84c0a5faff94b334536090a9dec1c74b10130" + integrity sha512-DcUsWpVhv9svsKRxnSCZ86SjD+sp32SGidNB37KpqXJncp1mfUgKbHvBomE89WJDbfVKw1mdv5+ikrvd43r+Bg== + +electron-to-chromium@^1.5.4: version "1.5.123" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.123.tgz#fae5bdba0ba27045895176327aa79831aba0790c" integrity sha512-refir3NlutEZqlKaBLK0tzlVLe5P2wDKS7UQt/3SpibizgsRAPOsqQC3ffw1nlv3ze5gjRQZYHoPymgVZkplFA== @@ -2124,11 +2154,6 @@ glob@^7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" -globals@^11.1.0: - version "11.12.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" - integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== - globals@^13.19.0: version "13.24.0" resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" @@ -2229,7 +2254,7 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" -is-core-module@^2.16.0, is-core-module@^2.9.0: +is-core-module@^2.16.1, is-core-module@^2.9.0: version "2.16.1" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== @@ -2318,16 +2343,11 @@ js-yaml@^4.1.0: dependencies: argparse "^2.0.1" -jsesc@^3.0.2: +jsesc@^3.0.2, jsesc@~3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== -jsesc@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e" - integrity sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g== - json-buffer@3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" @@ -2525,11 +2545,16 @@ neo-async@^2.6.2: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== -node-releases@^2.0.18, node-releases@^2.0.19: +node-releases@^2.0.18: version "2.0.19" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314" integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== +node-releases@^2.0.19, node-releases@^2.0.27: + version "2.0.27" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.27.tgz#eedca519205cf20f650f61d56b070db111231e4e" + integrity sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA== + normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" @@ -2984,10 +3009,10 @@ redux@^4.2.0: dependencies: "@babel/runtime" "^7.9.2" -regenerate-unicode-properties@^10.2.0: - version "10.2.0" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz#626e39df8c372338ea9b8028d1f99dc3fd9c3db0" - integrity sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA== +regenerate-unicode-properties@^10.2.2: + version "10.2.2" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz#aa113812ba899b630658c7623466be71e1f86f66" + integrity sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g== dependencies: regenerate "^1.4.2" @@ -3001,41 +3026,34 @@ regenerator-runtime@^0.14.0: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== -regenerator-transform@^0.15.2: - version "0.15.2" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.2.tgz#5bbae58b522098ebdf09bca2f83838929001c7a4" - integrity sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg== - dependencies: - "@babel/runtime" "^7.8.4" - regex-parser@^2.2.11: version "2.2.11" resolved "https://registry.yarnpkg.com/regex-parser/-/regex-parser-2.2.11.tgz#3b37ec9049e19479806e878cabe7c1ca83ccfe58" integrity sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q== -regexpu-core@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-6.2.0.tgz#0e5190d79e542bf294955dccabae04d3c7d53826" - integrity sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA== +regexpu-core@^6.3.1: + version "6.4.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-6.4.0.tgz#3580ce0c4faedef599eccb146612436b62a176e5" + integrity sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA== dependencies: regenerate "^1.4.2" - regenerate-unicode-properties "^10.2.0" + regenerate-unicode-properties "^10.2.2" regjsgen "^0.8.0" - regjsparser "^0.12.0" + regjsparser "^0.13.0" unicode-match-property-ecmascript "^2.0.0" - unicode-match-property-value-ecmascript "^2.1.0" + unicode-match-property-value-ecmascript "^2.2.1" regjsgen@^0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.8.0.tgz#df23ff26e0c5b300a6470cad160a9d090c3a37ab" integrity sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q== -regjsparser@^0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.12.0.tgz#0e846df6c6530586429377de56e0475583b088dc" - integrity sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ== +regjsparser@^0.13.0: + version "0.13.0" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.13.0.tgz#01f8351335cf7898d43686bc74d2dd71c847ecc0" + integrity sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q== dependencies: - jsesc "~3.0.2" + jsesc "~3.1.0" require-from-string@^2.0.2: version "2.0.2" @@ -3070,15 +3088,6 @@ resolve-url-loader@^5.0.0: postcss "^8.2.14" source-map "0.6.1" -resolve@^1.14.2: - version "1.22.10" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.10.tgz#b663e83ffb09bbf2386944736baae803029b8b39" - integrity sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w== - dependencies: - is-core-module "^2.16.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - resolve@^1.20.0: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" @@ -3088,6 +3097,15 @@ resolve@^1.20.0: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +resolve@^1.22.10: + version "1.22.11" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.11.tgz#aad857ce1ffb8bfa9b0b1ac29f1156383f68c262" + integrity sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ== + dependencies: + is-core-module "^2.16.1" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + reusify@^1.0.4: version "1.1.0" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.1.0.tgz#0fe13b9522e1473f51b558ee796e08f11f9b489f" @@ -3148,7 +3166,12 @@ schema-utils@^4.0.0: ajv-formats "^2.1.1" ajv-keywords "^5.1.0" -semver@7.5.3, semver@^6.0.0, semver@^6.3.0, semver@^6.3.1, semver@^7.3.8, semver@^7.5.3: +semver@^6.0.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.3.8, semver@^7.5.3: version "7.5.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e" integrity sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ== @@ -3346,17 +3369,17 @@ unicode-match-property-ecmascript@^2.0.0: unicode-canonical-property-names-ecmascript "^2.0.0" unicode-property-aliases-ecmascript "^2.0.0" -unicode-match-property-value-ecmascript@^2.1.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz#a0401aee72714598f739b68b104e4fe3a0cb3c71" - integrity sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg== +unicode-match-property-value-ecmascript@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz#65a7adfad8574c219890e219285ce4c64ed67eaa" + integrity sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg== unicode-property-aliases-ecmascript@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" - integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== + version "2.2.0" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz#301d4f8a43d2b75c97adfad87c9dd5350c9475d1" + integrity sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ== -update-browserslist-db@^1.1.0, update-browserslist-db@^1.1.1: +update-browserslist-db@^1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz#348377dd245216f9e7060ff50b15a1b740b75420" integrity sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw== @@ -3364,6 +3387,14 @@ update-browserslist-db@^1.1.0, update-browserslist-db@^1.1.1: escalade "^3.2.0" picocolors "^1.1.1" +update-browserslist-db@^1.1.1, update-browserslist-db@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz#7802aa2ae91477f255b86e0e46dbc787a206ad4a" + integrity sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A== + dependencies: + escalade "^3.2.0" + picocolors "^1.1.1" + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" From decb7ceea8e3b848a964214b3f2d9faed55df202 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sun, 2 Nov 2025 20:34:20 +0100 Subject: [PATCH 131/157] Removed /stats --- config/config.default.php | 2 +- config/routes.php | 3 +- src/Controllers/Metrics/Controller.php | 28 +--------- src/Middleware/ApiRouteHandler.php | 1 - .../Controllers/Metrics/ControllerTest.php | 51 ++----------------- 5 files changed, 7 insertions(+), 78 deletions(-) diff --git a/config/config.default.php b/config/config.default.php index 6890b63ec..8280375a5 100644 --- a/config/config.default.php +++ b/config/config.default.php @@ -14,7 +14,7 @@ 'password' => env_secret('MYSQL_PASSWORD', ''), ], - // For accessing /metrics (and /stats) + // For accessing /metrics 'api_key' => env('API_KEY', ''), // Enable maintenance mode (show a static page to all users) diff --git a/config/routes.php b/config/routes.php index e2566038a..9741ae7cf 100644 --- a/config/routes.php +++ b/config/routes.php @@ -72,9 +72,8 @@ function (RouteCollector $route): void { } ); -// Stats +// Metrics $route->get('/metrics', 'Metrics\\Controller@metrics'); -$route->get('/stats', 'Metrics\\Controller@stats'); // Angeltypes $route->addGroup('/angeltypes', function (RouteCollector $route): void { diff --git a/src/Controllers/Metrics/Controller.php b/src/Controllers/Metrics/Controller.php index d37bb559e..d0d015b40 100644 --- a/src/Controllers/Metrics/Controller.php +++ b/src/Controllers/Metrics/Controller.php @@ -235,41 +235,17 @@ public function metrics(): Response ->withContent($this->engine->get('/metrics', $data)); } - public function stats(): Response - { - $this->checkAuth(true); - - $data = [ - 'user_count' => $this->stats->usersState() + $this->stats->usersState(null, false), - 'arrived_user_count' => $this->stats->usersState(), - 'done_work_hours' => round($this->stats->workSeconds(true) / 60 / 60), - 'users_in_action' => $this->stats->currentlyWorkingUsers(), - ]; - - return $this->response - ->withHeader('Content-Type', 'application/json') - ->withContent(json_encode($data)); - } - /** * Ensure that the request is authorized */ - protected function checkAuth(bool $isJson = false): void + protected function checkAuth(): void { $apiKey = $this->config->get('api_key'); if (empty($apiKey) || $this->request->get('api_key') == $apiKey) { return; } - $message = 'The api_key is invalid'; - $headers = []; - - if ($isJson) { - $message = json_encode(['error' => $message]); - $headers['Content-Type'] = 'application/json'; - } - - throw new HttpForbidden($message, $headers); + throw new HttpForbidden('The api_key is invalid'); } /** diff --git a/src/Middleware/ApiRouteHandler.php b/src/Middleware/ApiRouteHandler.php index 54c50fa48..4df9b36b2 100644 --- a/src/Middleware/ApiRouteHandler.php +++ b/src/Middleware/ApiRouteHandler.php @@ -29,7 +29,6 @@ public function __construct( '/ical', '/metrics', '/shifts-json-export', - '/stats', ] ) { } diff --git a/tests/Unit/Controllers/Metrics/ControllerTest.php b/tests/Unit/Controllers/Metrics/ControllerTest.php index 3b44e9e6c..a2537e558 100644 --- a/tests/Unit/Controllers/Metrics/ControllerTest.php +++ b/tests/Unit/Controllers/Metrics/ControllerTest.php @@ -27,6 +27,7 @@ class ControllerTest extends TestCase * @covers \Engelsystem\Controllers\Metrics\Controller::__construct * @covers \Engelsystem\Controllers\Metrics\Controller::metrics * @covers \Engelsystem\Controllers\Metrics\Controller::formatStats + * @covers \Engelsystem\Controllers\Metrics\Controller::checkAuth */ public function testMetrics(): void { @@ -193,52 +194,6 @@ public function testMetrics(): void $controller->metrics(); } - /** - * @covers \Engelsystem\Controllers\Metrics\Controller::checkAuth - * @covers \Engelsystem\Controllers\Metrics\Controller::stats - */ - public function testStats(): void - { - /** @var Response|MockObject $response */ - /** @var Request|MockObject $request */ - /** @var MetricsEngine|MockObject $engine */ - /** @var Stats|MockObject $stats */ - /** @var Config $config */ - /** @var Version|MockObject $version */ - list($response, $request, $engine, $stats, $config, $version) = $this->getMocks(); - - $response->expects($this->once()) - ->method('withHeader') - ->with('Content-Type', 'application/json') - ->willReturn($response); - $response->expects($this->once()) - ->method('withContent') - ->with(json_encode([ - 'user_count' => 20, - 'arrived_user_count' => 10, - 'done_work_hours' => 99, - 'users_in_action' => 5, - ])) - ->willReturn($response); - - $request->expects($this->once()) - ->method('get') - ->with('api_key') - ->willReturn('ApiKey987'); - - $config->set('api_key', 'ApiKey987'); - - $stats->expects($this->once()) - ->method('workSeconds') - ->with(true) - ->willReturn((int) (60 * 60 * 99.47)); - $this->setExpects($stats, 'usersState', null, 10, $this->exactly(3)); - $this->setExpects($stats, 'currentlyWorkingUsers', null, 5); - - $controller = new Controller($response, $engine, $config, $request, $stats, $version); - $controller->stats(); - } - /** * @covers \Engelsystem\Controllers\Metrics\Controller::checkAuth */ @@ -262,8 +217,8 @@ public function testCheckAuth(): void $controller = new Controller($response, $engine, $config, $request, $stats, $version); $this->expectException(HttpForbidden::class); - $this->expectExceptionMessage(json_encode(['error' => 'The api_key is invalid'])); - $controller->stats(); + $this->expectExceptionMessage('The api_key is invalid'); + $controller->metrics(); } protected function getMocks(): array From c2573c174f914a21e13faf21260a22ceab9be683 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Fri, 21 Nov 2025 23:54:43 +0100 Subject: [PATCH 132/157] CI: Ignore PHP version on install --- .gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c51a55059..e97fd3d4e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -34,7 +34,7 @@ stages: needs: - composer install before_script: - - composer install --no-ansi --no-progress + - composer install --no-ansi --no-progress --ignore-platform-req=php # for jobs that depend on yarn .use_yarn: &use_yarn @@ -62,7 +62,7 @@ composer install: - composer audit - composer validate script: - - composer install --no-ansi --no-progress + - composer install --no-ansi --no-progress --ignore-platform-req=php composer audit: image: php:latest From a26095c87513631dff0e555bb93c5fc5c4e37594 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sat, 22 Nov 2025 21:25:21 +0100 Subject: [PATCH 133/157] Update packages --- composer.json | 2 +- composer.lock | 123 ++++++++++++++++++++++++++++---------------------- 2 files changed, 69 insertions(+), 56 deletions(-) diff --git a/composer.json b/composer.json index 0ae9c58e3..e18e1a98d 100644 --- a/composer.json +++ b/composer.json @@ -66,7 +66,7 @@ "filp/whoops": "^2.16", "phpstan/phpstan": "^1.12", "phpunit/phpunit": "^9.6", - "slevomat/coding-standard": "^8.15", + "slevomat/coding-standard": "^8.22", "squizlabs/php_codesniffer": "^3.10", "symfony/var-dumper": "^7.1" }, diff --git a/composer.lock b/composer.lock index 2edf2a739..986709e78 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "228d02f27e034e40dffa1b18ae9947f5", + "content-hash": "99456b29bb3cb52bbc450c70787b2294", "packages": [ { "name": "brick/math", @@ -5449,29 +5449,29 @@ "packages-dev": [ { "name": "dealerdirect/phpcodesniffer-composer-installer", - "version": "v1.0.0", + "version": "v1.2.0", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/composer-installer.git", - "reference": "4be43904336affa5c2f70744a348312336afd0da" + "reference": "845eb62303d2ca9b289ef216356568ccc075ffd1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/4be43904336affa5c2f70744a348312336afd0da", - "reference": "4be43904336affa5c2f70744a348312336afd0da", + "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/845eb62303d2ca9b289ef216356568ccc075ffd1", + "reference": "845eb62303d2ca9b289ef216356568ccc075ffd1", "shasum": "" }, "require": { - "composer-plugin-api": "^1.0 || ^2.0", + "composer-plugin-api": "^2.2", "php": ">=5.4", - "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0" + "squizlabs/php_codesniffer": "^3.1.0 || ^4.0" }, "require-dev": { - "composer/composer": "*", + "composer/composer": "^2.2", "ext-json": "*", "ext-zip": "*", - "php-parallel-lint/php-parallel-lint": "^1.3.1", - "phpcompatibility/php-compatibility": "^9.0", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpcompatibility/php-compatibility": "^9.0 || ^10.0.0@dev", "yoast/phpunit-polyfills": "^1.0" }, "type": "composer-plugin", @@ -5490,9 +5490,9 @@ "authors": [ { "name": "Franck Nijhof", - "email": "franck.nijhof@dealerdirect.com", - "homepage": "http://www.frenck.nl", - "role": "Developer / IT Manager" + "email": "opensource@frenck.dev", + "homepage": "https://frenck.dev", + "role": "Open source developer" }, { "name": "Contributors", @@ -5500,7 +5500,6 @@ } ], "description": "PHP_CodeSniffer Standards Composer Installer Plugin", - "homepage": "http://www.dealerdirect.com", "keywords": [ "PHPCodeSniffer", "PHP_CodeSniffer", @@ -5521,9 +5520,28 @@ ], "support": { "issues": "https://github.com/PHPCSStandards/composer-installer/issues", + "security": "https://github.com/PHPCSStandards/composer-installer/security/policy", "source": "https://github.com/PHPCSStandards/composer-installer" }, - "time": "2023-01-05T11:28:13+00:00" + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcsstandards", + "type": "thanks_dev" + } + ], + "time": "2025-11-11T04:32:07+00:00" }, { "name": "dms/phpunit-arraysubset-asserts", @@ -6057,30 +6075,30 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "1.33.0", + "version": "2.3.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "82a311fd3690fb2bf7b64d5c98f912b3dd746140" + "reference": "1e0cd5370df5dd2e556a36b9c62f62e555870495" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/82a311fd3690fb2bf7b64d5c98f912b3dd746140", - "reference": "82a311fd3690fb2bf7b64d5c98f912b3dd746140", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/1e0cd5370df5dd2e556a36b9c62f62e555870495", + "reference": "1e0cd5370df5dd2e556a36b9c62f62e555870495", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "php": "^7.4 || ^8.0" }, "require-dev": { "doctrine/annotations": "^2.0", - "nikic/php-parser": "^4.15", + "nikic/php-parser": "^5.3.0", "php-parallel-lint/php-parallel-lint": "^1.2", "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.5", - "phpstan/phpstan-phpunit": "^1.1", - "phpstan/phpstan-strict-rules": "^1.0", - "phpunit/phpunit": "^9.5", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6", "symfony/process": "^5.2" }, "type": "library", @@ -6098,9 +6116,9 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.33.0" + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.0" }, - "time": "2024-10-13T11:25:22+00:00" + "time": "2025-08-30T15:50:23+00:00" }, { "name": "phpstan/phpstan", @@ -7547,32 +7565,32 @@ }, { "name": "slevomat/coding-standard", - "version": "8.15.0", + "version": "8.22.1", "source": { "type": "git", "url": "https://github.com/slevomat/coding-standard.git", - "reference": "7d1d957421618a3803b593ec31ace470177d7817" + "reference": "1dd80bf3b93692bedb21a6623c496887fad05fec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/7d1d957421618a3803b593ec31ace470177d7817", - "reference": "7d1d957421618a3803b593ec31ace470177d7817", + "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/1dd80bf3b93692bedb21a6623c496887fad05fec", + "reference": "1dd80bf3b93692bedb21a6623c496887fad05fec", "shasum": "" }, "require": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.0", - "php": "^7.2 || ^8.0", - "phpstan/phpdoc-parser": "^1.23.1", - "squizlabs/php_codesniffer": "^3.9.0" + "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.1.2", + "php": "^7.4 || ^8.0", + "phpstan/phpdoc-parser": "^2.3.0", + "squizlabs/php_codesniffer": "^3.13.4" }, "require-dev": { - "phing/phing": "2.17.4", - "php-parallel-lint/php-parallel-lint": "1.3.2", - "phpstan/phpstan": "1.10.60", - "phpstan/phpstan-deprecation-rules": "1.1.4", - "phpstan/phpstan-phpunit": "1.3.16", - "phpstan/phpstan-strict-rules": "1.5.2", - "phpunit/phpunit": "8.5.21|9.6.8|10.5.11" + "phing/phing": "3.0.1|3.1.0", + "php-parallel-lint/php-parallel-lint": "1.4.0", + "phpstan/phpstan": "2.1.24", + "phpstan/phpstan-deprecation-rules": "2.0.3", + "phpstan/phpstan-phpunit": "2.0.7", + "phpstan/phpstan-strict-rules": "2.0.6", + "phpunit/phpunit": "9.6.8|10.5.48|11.4.4|11.5.36|12.3.10" }, "type": "phpcodesniffer-standard", "extra": { @@ -7596,7 +7614,7 @@ ], "support": { "issues": "https://github.com/slevomat/coding-standard/issues", - "source": "https://github.com/slevomat/coding-standard/tree/8.15.0" + "source": "https://github.com/slevomat/coding-standard/tree/8.22.1" }, "funding": [ { @@ -7608,20 +7626,20 @@ "type": "tidelift" } ], - "time": "2024-03-09T15:20:58+00:00" + "time": "2025-09-13T08:53:30+00:00" }, { "name": "squizlabs/php_codesniffer", - "version": "3.11.3", + "version": "3.13.5", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "ba05f990e79cbe69b9f35c8c1ac8dca7eecc3a10" + "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/ba05f990e79cbe69b9f35c8c1ac8dca7eecc3a10", - "reference": "ba05f990e79cbe69b9f35c8c1ac8dca7eecc3a10", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/0ca86845ce43291e8f5692c7356fccf3bcf02bf4", + "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4", "shasum": "" }, "require": { @@ -7638,11 +7656,6 @@ "bin/phpcs" ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" @@ -7688,11 +7701,11 @@ "type": "open_collective" }, { - "url": "https://thanks.dev/phpcsstandards", + "url": "https://thanks.dev/u/gh/phpcsstandards", "type": "thanks_dev" } ], - "time": "2025-01-23T17:04:15+00:00" + "time": "2025-11-04T16:30:35+00:00" }, { "name": "symfony/var-dumper", From 72bd116a802e5d9ff2485ca1c493ade76dad8a9c Mon Sep 17 00:00:00 2001 From: Christian Eichler Date: Sat, 8 Nov 2025 10:48:31 +0100 Subject: [PATCH 134/157] Implement an oauth config flag to disable disconnecting from provider --- config/config.default.php | 2 + resources/views/pages/settings/oauth.twig | 2 +- src/Controllers/OAuthController.php | 6 ++ .../Unit/Controllers/OAuthControllerTest.php | 59 +++++++++++++++++-- 4 files changed, 63 insertions(+), 6 deletions(-) diff --git a/config/config.default.php b/config/config.default.php index 8280375a5..11aa50a1a 100644 --- a/config/config.default.php +++ b/config/config.default.php @@ -138,6 +138,8 @@ 'enable_password' => false, // Allow registration even if disabled in config (optional) 'allow_registration' => null, + // Allow disconnecting user accounts from the oauth provider (optional) + 'allow_user_disconnect' => true, // Auto join teams // Info groups field (optional) 'groups' => 'groups', diff --git a/resources/views/pages/settings/oauth.twig b/resources/views/pages/settings/oauth.twig index 6606e74aa..e33dc1898 100644 --- a/resources/views/pages/settings/oauth.twig +++ b/resources/views/pages/settings/oauth.twig @@ -37,7 +37,7 @@ {{ f.submit(__('form.connect'), {'size' : 'sm', 'icon_left': 'box-arrow-in-right'}) }} - {% else %} + {% elseif config.allow_user_disconnect is not defined or config.allow_user_disconnect %}
    {{ csrf() }} diff --git a/src/Controllers/OAuthController.php b/src/Controllers/OAuthController.php index 0cb0af78d..32f4b4f30 100644 --- a/src/Controllers/OAuthController.php +++ b/src/Controllers/OAuthController.php @@ -194,6 +194,12 @@ public function disconnect(Request $request): Response { $providerName = $request->getAttribute('provider'); + $this->requireProvider($providerName); + + if (!($this->config->get('oauth')[$providerName]['allow_user_disconnect'] ?? true)) { + throw new HttpNotFound(); + } + $this->oauth ->whereUserId($this->auth->user()->id) ->where('provider', $providerName) diff --git a/tests/Unit/Controllers/OAuthControllerTest.php b/tests/Unit/Controllers/OAuthControllerTest.php index 413321ff7..7752a9bbf 100644 --- a/tests/Unit/Controllers/OAuthControllerTest.php +++ b/tests/Unit/Controllers/OAuthControllerTest.php @@ -574,20 +574,69 @@ public function testConnect(): void /** * @covers \Engelsystem\Controllers\OAuthController::disconnect */ - public function testDisconnect(): void + public function testDisconnectIfAllowIsTrue(): void { + $oauthConfig = $this->config->get('oauth'); + $oauthConfig['testprovider']['allow_user_disconnect'] = true; + + $this->runDisconnectTest($oauthConfig, true); + } + + /** + * @covers \Engelsystem\Controllers\OAuthController::disconnect + */ + public function testDisconnectIfAllowIsNull(): void + { + $oauthConfig = $this->config->get('oauth'); + $oauthConfig['testprovider']['allow_user_disconnect'] = null; + + $this->runDisconnectTest($oauthConfig, true); + } + + /** + * @covers \Engelsystem\Controllers\OAuthController::disconnect + */ + public function testDisconnectIfAllowIsUnset(): void + { + $oauthConfig = $this->config->get('oauth'); + unset($oauthConfig['testprovider']['allow_user_disconnect']); + + $this->runDisconnectTest($oauthConfig, true); + } + + /** + * @covers \Engelsystem\Controllers\OAuthController::disconnect + */ + public function testDisconnectIfAllowIsFalse(): void + { + $oauthConfig = $this->config->get('oauth'); + $oauthConfig['testprovider']['allow_user_disconnect'] = false; + + $this->runDisconnectTest($oauthConfig, false); + } + + private function runDisconnectTest(mixed $oauthConfig, bool $shouldDisconnect): void + { + $this->config->set('oauth', $oauthConfig); + $controller = $this->getMock(['addNotification']); - $this->setExpects($controller, 'addNotification', ['oauth.disconnected']); + $request = (new Request())->withAttribute('provider', 'testprovider'); - $request = (new Request()) - ->withAttribute('provider', 'testprovider'); + if (!$shouldDisconnect) { + $this->expectException(HttpNotFound::class); + $controller->disconnect($request); + return; // Should never happen, creates cleaner errors + } + + $this->setExpects($controller, 'addNotification', ['oauth.disconnected']); $this->setExpects($this->auth, 'user', null, $this->authenticatedUser); $this->setExpects($this->redirect, 'back', null, new Response()); $controller->disconnect($request); + $this->assertCount(1, OAuth::all()); - $this->log->hasInfoThatContains('Disconnected'); + $this->assertTrue($this->log->hasInfoThatContains('Disconnected')); } protected function getMock(array $mockMethods = []): OAuthController | MockObject From 831a781f754f6c97354ce6e77edd93c7bc1cc8ad Mon Sep 17 00:00:00 2001 From: Enterprize1 Date: Sat, 1 Nov 2025 00:14:51 +0100 Subject: [PATCH 135/157] Limit the height of the angel list in the myshifts view --- includes/view/User_view.php | 2 ++ resources/assets/themes/base.scss | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/includes/view/User_view.php b/includes/view/User_view.php index cb67222c4..de6f7f37b 100644 --- a/includes/view/User_view.php +++ b/includes/view/User_view.php @@ -346,6 +346,8 @@ function User_view_myshift(Shift $shift, $user_source, $its_me, $supporter) $shift_info .= User_view_shiftentries($needed_angel_type); } + $shift_info = div('table-myshifts-shift-info-limit-height', $shift_info); + $night_shift = ''; if ($shift->isNightShift() && $goodie_enabled) { $night_shift = render_night_shift_hint($nightShiftsConfig); diff --git a/resources/assets/themes/base.scss b/resources/assets/themes/base.scss index 1b30af17d..86f94a8dd 100644 --- a/resources/assets/themes/base.scss +++ b/resources/assets/themes/base.scss @@ -307,6 +307,17 @@ table.table-sticky-header thead { text-align: right; } +.column_shift_info:not(:hover) .table-myshifts-shift-info-limit-height { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 6; + overflow: hidden; + + @media (min-width: map-get($grid-breakpoints, 'lg')) { + -webkit-line-clamp: 4; + } +} + h1, h2, h3, From 2f67a6de78751dfbdbcc3de60f830010072f1e31 Mon Sep 17 00:00:00 2001 From: Ole Bittner Date: Thu, 23 Oct 2025 22:24:08 +0200 Subject: [PATCH 136/157] build days list for location view only from shifts which require angels --- includes/controller/locations_controller.php | 5 +- includes/model/Shifts_model.php | 54 ++++++++++++++++++++ 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/includes/controller/locations_controller.php b/includes/controller/locations_controller.php index 3d2d20be0..84cf8c25c 100644 --- a/includes/controller/locations_controller.php +++ b/includes/controller/locations_controller.php @@ -24,10 +24,9 @@ function location_controller(): array $request = request(); $location = load_location(); - $all_shifts = $location->shifts->sortBy('start'); + $days_list = Days_by_Location_id($location->id); $days = []; - foreach ($all_shifts as $shift) { - $day = $shift->start->format('Y-m-d'); + foreach ($days_list as $day) { if (!isset($days[$day])) { $days[$day] = dateWithEventDay($day); } diff --git a/includes/model/Shifts_model.php b/includes/model/Shifts_model.php index ef8677930..83fe0217f 100644 --- a/includes/model/Shifts_model.php +++ b/includes/model/Shifts_model.php @@ -350,6 +350,60 @@ function NeededAngeltype_by_Shift_and_Angeltype(Shift $shift, AngelType $angelty ); } +/** + * returns all days with shifts needing angels for a location + * + * @param int $location_id + * @return list + */ +function Days_by_Location_id(int $location_id): array +{ + $sql = ' + SELECT + DATE(`shifts`.`start`) AS `day` + FROM `shifts` + JOIN `needed_angel_types` ON `needed_angel_types`.`shift_id`=`shifts`.`id` + LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id + WHERE `shifts`.`location_id` = ? + AND s.shift_id IS NULL + + UNION + + /* By shift type */ + SELECT + DATE(`shifts`.`start`) AS `day` + FROM `shifts` + JOIN `needed_angel_types` ON `needed_angel_types`.`shift_type_id`=`shifts`.`shift_type_id` + LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id + LEFT JOIN schedules AS se on s.schedule_id = se.id + WHERE `shifts`.`location_id` = ? + AND NOT s.shift_id IS NULL + AND se.needed_from_shift_type = TRUE + + UNION + + /* By location */ + SELECT + DATE(`shifts`.`start`) AS `day` + FROM `shifts` + JOIN `needed_angel_types` ON `needed_angel_types`.`location_id`=`shifts`.`location_id` + LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id + LEFT JOIN schedules AS se on s.schedule_id = se.id + WHERE `shifts`.`location_id` = ? + AND NOT s.shift_id IS NULL + AND se.needed_from_shift_type = FALSE + '; + + return array_column(Db::select( + $sql, + [ + $location_id, + $location_id, + $location_id, + ] + ), 'day'); +} + /** * @param ShiftsFilter $shiftsFilter * @return ShiftEntry[]|Collection From 3c155fa208bd21cfea803a98cd9157d89fe8f900 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Wed, 15 Oct 2025 22:44:19 +0200 Subject: [PATCH 137/157] Renderer: Added qr function --- composer.json | 1 + composer.lock | 107 +++++++++++++++++- src/Renderer/Twig/Extensions/Qr.php | 36 ++++++ src/Renderer/TwigServiceProvider.php | 2 + .../Unit/Renderer/Twig/Extensions/QrTest.php | 36 ++++++ 5 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 src/Renderer/Twig/Extensions/Qr.php create mode 100644 tests/Unit/Renderer/Twig/Extensions/QrTest.php diff --git a/composer.json b/composer.json index e18e1a98d..7366658c5 100644 --- a/composer.json +++ b/composer.json @@ -35,6 +35,7 @@ "ext-pdo": "*", "ext-simplexml": "*", "ext-xml": "*", + "bacon/bacon-qr-code": "^3.0", "erusev/parsedown": "1.7.x-dev#f7285e7b2c55039401e9d380741c2dc805edf980", "gettext/gettext": "^5.7", "gettext/translator": "^1.2", diff --git a/composer.lock b/composer.lock index 986709e78..d4e0692fc 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,63 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "99456b29bb3cb52bbc450c70787b2294", + "content-hash": "87d94e8e5cfbf61ca20f1d346cc9d458", "packages": [ + { + "name": "bacon/bacon-qr-code", + "version": "v3.0.3", + "source": { + "type": "git", + "url": "https://github.com/Bacon/BaconQrCode.git", + "reference": "36a1cb2b81493fa5b82e50bf8068bf84d1542563" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/36a1cb2b81493fa5b82e50bf8068bf84d1542563", + "reference": "36a1cb2b81493fa5b82e50bf8068bf84d1542563", + "shasum": "" + }, + "require": { + "dasprid/enum": "^1.0.3", + "ext-iconv": "*", + "php": "^8.1" + }, + "require-dev": { + "phly/keep-a-changelog": "^2.12", + "phpunit/phpunit": "^10.5.11 || ^11.0.4", + "spatie/phpunit-snapshot-assertions": "^5.1.5", + "spatie/pixelmatch-php": "^1.2.0", + "squizlabs/php_codesniffer": "^3.9" + }, + "suggest": { + "ext-imagick": "to generate QR code images" + }, + "type": "library", + "autoload": { + "psr-4": { + "BaconQrCode\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Ben Scholzen 'DASPRiD'", + "email": "mail@dasprids.de", + "homepage": "https://dasprids.de/", + "role": "Developer" + } + ], + "description": "BaconQrCode is a QR code generator for PHP.", + "homepage": "https://github.com/Bacon/BaconQrCode", + "support": { + "issues": "https://github.com/Bacon/BaconQrCode/issues", + "source": "https://github.com/Bacon/BaconQrCode/tree/v3.0.3" + }, + "time": "2025-11-19T17:15:36+00:00" + }, { "name": "brick/math", "version": "0.12.1", @@ -135,6 +190,56 @@ ], "time": "2024-02-09T16:56:22+00:00" }, + { + "name": "dasprid/enum", + "version": "1.0.7", + "source": { + "type": "git", + "url": "https://github.com/DASPRiD/Enum.git", + "reference": "b5874fa9ed0043116c72162ec7f4fb50e02e7cce" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/b5874fa9ed0043116c72162ec7f4fb50e02e7cce", + "reference": "b5874fa9ed0043116c72162ec7f4fb50e02e7cce", + "shasum": "" + }, + "require": { + "php": ">=7.1 <9.0" + }, + "require-dev": { + "phpunit/phpunit": "^7 || ^8 || ^9 || ^10 || ^11", + "squizlabs/php_codesniffer": "*" + }, + "type": "library", + "autoload": { + "psr-4": { + "DASPRiD\\Enum\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Ben Scholzen 'DASPRiD'", + "email": "mail@dasprids.de", + "homepage": "https://dasprids.de/", + "role": "Developer" + } + ], + "description": "PHP 7.1 enum implementation", + "keywords": [ + "enum", + "map" + ], + "support": { + "issues": "https://github.com/DASPRiD/Enum/issues", + "source": "https://github.com/DASPRiD/Enum/tree/1.0.7" + }, + "time": "2025-09-16T12:23:56+00:00" + }, { "name": "devizzent/cebe-php-openapi", "version": "1.1.4", diff --git a/src/Renderer/Twig/Extensions/Qr.php b/src/Renderer/Twig/Extensions/Qr.php new file mode 100644 index 000000000..9af87fa99 --- /dev/null +++ b/src/Renderer/Twig/Extensions/Qr.php @@ -0,0 +1,36 @@ + ['html']]), + ]; + } + + public function getQr(string $content, int $size = 200): string + { + $renderer = new ImageRenderer( + new RendererStyle($size), + new SvgImageBackEnd(), + ); + $writer = new Writer($renderer); + + return $writer->writeString($content); + } +} diff --git a/src/Renderer/TwigServiceProvider.php b/src/Renderer/TwigServiceProvider.php index 22aae06a7..ddbef9a59 100644 --- a/src/Renderer/TwigServiceProvider.php +++ b/src/Renderer/TwigServiceProvider.php @@ -15,6 +15,7 @@ use Engelsystem\Renderer\Twig\Extensions\Legacy; use Engelsystem\Renderer\Twig\Extensions\Markdown; use Engelsystem\Renderer\Twig\Extensions\Notification; +use Engelsystem\Renderer\Twig\Extensions\Qr; use Engelsystem\Renderer\Twig\Extensions\Session; use Engelsystem\Renderer\Twig\Extensions\StringExtension; use Engelsystem\Renderer\Twig\Extensions\Translation; @@ -42,6 +43,7 @@ class TwigServiceProvider extends ServiceProvider 'string' => StringExtension::class, 'legacy' => Legacy::class, 'markdown' => Markdown::class, + 'qr' => Qr::class, 'translation' => Translation::class, 'url' => Url::class, 'uuid' => Uuid::class, diff --git a/tests/Unit/Renderer/Twig/Extensions/QrTest.php b/tests/Unit/Renderer/Twig/Extensions/QrTest.php new file mode 100644 index 000000000..d04f3f5cd --- /dev/null +++ b/tests/Unit/Renderer/Twig/Extensions/QrTest.php @@ -0,0 +1,36 @@ +getFunctions(); + + $this->assertExtensionExists('qr', [$extension, 'getQr'], $functions, ['is_safe' => ['html']]); + } + + /** + * @covers \Engelsystem\Renderer\Twig\Extensions\Qr::getQr + */ + public function testGetQr(): void + { + $extension = new Qr(); + + $generatedCode = $extension->getQr('Test'); + $this->assertStringContainsString('assertStringContainsString('width="200" height="200"', $generatedCode); + + $generatedCode = $extension->getQr('Test', 1337); + $this->assertStringContainsString('width="1337" height="1337"', $generatedCode); + } +} From 1401ca70ce49c08198c1d28d68d9185fc6eff31f Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sun, 19 Oct 2025 12:56:39 +0200 Subject: [PATCH 138/157] BaseController: Add hasPermission --- src/Controllers/BaseController.php | 13 ++++++++- src/Middleware/RequestHandler.php | 22 +++++++++++---- tests/Unit/Controllers/BaseControllerTest.php | 14 ++++++++++ .../Stub/ControllerImplementation.php | 10 +++++++ tests/Unit/Middleware/RequestHandlerTest.php | 28 +++++++++++++++++++ .../Stub/ControllerImplementation.php | 20 +++++++++++++ 6 files changed, 101 insertions(+), 6 deletions(-) diff --git a/src/Controllers/BaseController.php b/src/Controllers/BaseController.php index 7575bf805..03e994036 100644 --- a/src/Controllers/BaseController.php +++ b/src/Controllers/BaseController.php @@ -5,6 +5,7 @@ namespace Engelsystem\Controllers; use Engelsystem\Http\Validation\ValidatesRequest; +use Psr\Http\Message\ServerRequestInterface; abstract class BaseController { @@ -14,7 +15,7 @@ abstract class BaseController protected array $permissions = []; /** - * Returns the list of permissions + * Returns the list of permissions for instance / methods * * @return string[]|string[][] */ @@ -22,4 +23,14 @@ public function getPermissions(): array { return $this->permissions; } + + /** + * Check if the request should be permitted + * + * $this->getPermissions will be interpreted on null return + */ + public function hasPermission(ServerRequestInterface $request, string $method): ?bool + { + return null; + } } diff --git a/src/Middleware/RequestHandler.php b/src/Middleware/RequestHandler.php index 91592aa2b..212bcc764 100644 --- a/src/Middleware/RequestHandler.php +++ b/src/Middleware/RequestHandler.php @@ -39,8 +39,12 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface if ($requestHandler instanceof CallableHandler) { $callable = $requestHandler->getCallable(); - if (is_array($callable) && $callable[0] instanceof BaseController) { - $this->checkPermissions($callable[0], $callable[1]); + if ( + is_array($callable) + && $callable[0] instanceof BaseController + && !$this->checkPermissions($request, $callable[0], $callable[1]) + ) { + throw new HttpForbidden(); } } @@ -87,8 +91,16 @@ class_exists($handler[0]) /** * Check required page permissions */ - protected function checkPermissions(BaseController $controller, string $method): bool - { + protected function checkPermissions( + ServerRequestInterface $request, + BaseController $controller, + string $method + ): bool { + $hasPermission = $controller->hasPermission($request, $method); + if (!is_null($hasPermission)) { + return $hasPermission; + } + /** @var Authenticator $auth */ $auth = $this->container->get('auth'); $permissions = $controller->getPermissions(); @@ -110,7 +122,7 @@ protected function checkPermissions(BaseController $controller, string $method): ? !$auth->canAny(explode('||', $value)) : !$auth->can($permission) ) { - throw new HttpForbidden(); + return false; } } } diff --git a/tests/Unit/Controllers/BaseControllerTest.php b/tests/Unit/Controllers/BaseControllerTest.php index 587059e99..db2c21fbc 100644 --- a/tests/Unit/Controllers/BaseControllerTest.php +++ b/tests/Unit/Controllers/BaseControllerTest.php @@ -4,6 +4,7 @@ namespace Engelsystem\Test\Unit\Controllers; +use Engelsystem\Http\Request; use Engelsystem\Test\Unit\Controllers\Stub\ControllerImplementation; use PHPUnit\Framework\TestCase; @@ -26,4 +27,17 @@ public function testGetPermissions(): void $this->assertTrue(method_exists($controller, 'setValidator')); } + + /** + * @covers \Engelsystem\Controllers\BaseController::hasPermission + */ + public function testHasPermission(): void + { + $request = new Request(); + $controller = new ControllerImplementation(); + + $this->assertTrue($controller->hasPermission($request, 'yay')); + $this->assertNull($controller->hasPermission($request, 'test')); + $this->assertFalse($controller->hasPermission($request, 'nope')); + } } diff --git a/tests/Unit/Controllers/Stub/ControllerImplementation.php b/tests/Unit/Controllers/Stub/ControllerImplementation.php index c3a6af55c..be95f9e53 100644 --- a/tests/Unit/Controllers/Stub/ControllerImplementation.php +++ b/tests/Unit/Controllers/Stub/ControllerImplementation.php @@ -5,6 +5,7 @@ namespace Engelsystem\Test\Unit\Controllers\Stub; use Engelsystem\Controllers\BaseController; +use Psr\Http\Message\ServerRequestInterface; class ControllerImplementation extends BaseController { @@ -16,4 +17,13 @@ class ControllerImplementation extends BaseController 'dolor', ], ]; + + public function hasPermission(ServerRequestInterface $request, string $method): ?bool + { + return match ($method) { + 'yay' => true, + 'nope' => false, + default => parent::hasPermission($request, $method), + }; + } } diff --git a/tests/Unit/Middleware/RequestHandlerTest.php b/tests/Unit/Middleware/RequestHandlerTest.php index fcf8327e7..da93dd852 100644 --- a/tests/Unit/Middleware/RequestHandlerTest.php +++ b/tests/Unit/Middleware/RequestHandlerTest.php @@ -262,6 +262,34 @@ public function testCheckPermissionsAny(): void $this->assertEquals(200, $response->getStatusCode()); } + /** + * @covers \Engelsystem\Middleware\RequestHandler::checkPermissions + */ + public function testCheckPermissionsHasPermission(): void + { + /** @var RequestHandlerInterface|MockObject $handler */ + list(, , $handler) = $this->getMocks(); + $this->app->instance('response', new Response()); + /** @var Authenticator|MockObject $auth */ + $auth = $this->createMock(Authenticator::class); + $this->app->instance('auth', $auth); + + $controller = new ControllerImplementation(); + $request = (new Request()) + ->withAttribute('route-request-handler', [$controller, 'allow']); + + $middleware = new RequestHandler($this->app); + + $response = $middleware->process($request, $handler); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('yay', $response->getBody()->getContents()); + + $request = (new Request()) + ->withAttribute('route-request-handler', [$controller, 'deny']); + $this->expectException(HttpForbidden::class); + $middleware->process($request, $handler); + } + protected function getMocks(): array { /** @var Application|MockObject $container */ diff --git a/tests/Unit/Middleware/Stub/ControllerImplementation.php b/tests/Unit/Middleware/Stub/ControllerImplementation.php index 2d6227742..766320178 100644 --- a/tests/Unit/Middleware/Stub/ControllerImplementation.php +++ b/tests/Unit/Middleware/Stub/ControllerImplementation.php @@ -5,6 +5,7 @@ namespace Engelsystem\Test\Unit\Middleware\Stub; use Engelsystem\Controllers\BaseController; +use Psr\Http\Message\ServerRequestInterface; class ControllerImplementation extends BaseController { @@ -17,4 +18,23 @@ public function actionStub(): string { return ''; } + + public function hasPermission(ServerRequestInterface $request, string $method): ?bool + { + return match ($method) { + 'allow' => true, + 'deny' => false, + default => parent::hasPermission($request, $method), + }; + } + + public function allow(): string + { + return 'yay'; + } + + public function deny(): string + { + return 'nope'; + } } From fdee4504ab677ddc355800bdf5fec2ee678ed9ee Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Mon, 20 Oct 2025 18:44:04 +0200 Subject: [PATCH 139/157] Allow joining angel type via QR codes --- composer.json | 1 + composer.lock | 65 +++- config/config.default.php | 12 + config/routes.php | 3 + includes/view/AngelTypes_view.php | 9 + resources/lang/de_DE/additional.po | 3 + resources/lang/de_DE/default.po | 21 ++ resources/lang/en_US/additional.po | 3 + resources/lang/en_US/default.po | 21 ++ resources/views/pages/angeltypes/qr.twig | 59 ++++ src/Controllers/AngelTypesController.php | 149 ++++++++- .../Controllers/AngelTypesControllerTest.php | 284 +++++++++++++++++- 12 files changed, 625 insertions(+), 5 deletions(-) create mode 100644 resources/views/pages/angeltypes/qr.twig diff --git a/composer.json b/composer.json index 7366658c5..9313c1ede 100644 --- a/composer.json +++ b/composer.json @@ -37,6 +37,7 @@ "ext-xml": "*", "bacon/bacon-qr-code": "^3.0", "erusev/parsedown": "1.7.x-dev#f7285e7b2c55039401e9d380741c2dc805edf980", + "firebase/php-jwt": "^6.11", "gettext/gettext": "^5.7", "gettext/translator": "^1.2", "guzzlehttp/guzzle": "^7.9", diff --git a/composer.lock b/composer.lock index d4e0692fc..db1b8bdc0 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "87d94e8e5cfbf61ca20f1d346cc9d458", + "content-hash": "08cd395ef3c606dca6150635943267e6", "packages": [ { "name": "bacon/bacon-qr-code", @@ -601,6 +601,69 @@ }, "time": "2024-07-12T14:59:16+00:00" }, + { + "name": "firebase/php-jwt", + "version": "v6.11.1", + "source": { + "type": "git", + "url": "https://github.com/firebase/php-jwt.git", + "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/firebase/php-jwt/zipball/d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", + "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", + "shasum": "" + }, + "require": { + "php": "^8.0" + }, + "require-dev": { + "guzzlehttp/guzzle": "^7.4", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "psr/cache": "^2.0||^3.0", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0" + }, + "suggest": { + "ext-sodium": "Support EdDSA (Ed25519) signatures", + "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present" + }, + "type": "library", + "autoload": { + "psr-4": { + "Firebase\\JWT\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Neuman Vong", + "email": "neuman+pear@twilio.com", + "role": "Developer" + }, + { + "name": "Anant Narayanan", + "email": "anant@php.net", + "role": "Developer" + } + ], + "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", + "homepage": "https://github.com/firebase/php-jwt", + "keywords": [ + "jwt", + "php" + ], + "support": { + "issues": "https://github.com/firebase/php-jwt/issues", + "source": "https://github.com/firebase/php-jwt/tree/v6.11.1" + }, + "time": "2025-04-09T20:32:01+00:00" + }, { "name": "gettext/gettext", "version": "v5.7.3", diff --git a/config/config.default.php b/config/config.default.php index 11aa50a1a..af2a1676c 100644 --- a/config/config.default.php +++ b/config/config.default.php @@ -152,6 +152,15 @@ */ ], + // Random, long (at least 32 characters) alphanumeric or base64 encoded key, used for signing + 'app_key' => env_secret('APP_KEY'), + + // see https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40 + 'jwt_algorithm' => env('JWT_ALGORITHM', 'HS256'), + + // Number of minutes after a JWT must expire for example max angel type join time + 'jwt_expiration_time' => env('JWT_EXPIRATION_TIME', 60 * 24 * 7), + // Default theme, 1 = theme1.scss etc. 'theme' => env('THEME', 1), @@ -288,6 +297,9 @@ 'dect' => (bool) env('DECT_REQUIRED', false), ], + // Allow joining angel type via generated QR code + 'join_qr_code' => (bool) env('JOIN_QR_CODE', true), + // Only arrived users can sign up for shifts 'signup_requires_arrival' => (bool) env('SIGNUP_REQUIRES_ARRIVAL', false), diff --git a/config/routes.php b/config/routes.php index 9741ae7cf..8af928356 100644 --- a/config/routes.php +++ b/config/routes.php @@ -78,6 +78,9 @@ function (RouteCollector $route): void { // Angeltypes $route->addGroup('/angeltypes', function (RouteCollector $route): void { $route->get('/about', 'AngelTypesController@about'); + $route->get('/{angel_type_id:\d+}/qr', 'AngelTypesController@qrCode'); + $route->post('/{angel_type_id:\d+}/qr', 'AngelTypesController@qrCode'); + $route->get('/{angel_type_id:\d+}/join', 'AngelTypesController@join'); }); // Shifts diff --git a/includes/view/AngelTypes_view.php b/includes/view/AngelTypes_view.php index 29f0a9304..6823c3168 100644 --- a/includes/view/AngelTypes_view.php +++ b/includes/view/AngelTypes_view.php @@ -284,6 +284,15 @@ function AngelType_view_buttons( '', __('form.edit') ); + if (config('app_key') && config('join_qr_code', true)) { + $buttons[] = button( + url('/angeltypes/' . $angeltype->id . '/qr'), + icon('qr-code'), + '', + '', + __('general.qr') + ); + } } if ($admin_angeltypes) { $buttons[] = button( diff --git a/resources/lang/de_DE/additional.po b/resources/lang/de_DE/additional.po index 3e819cb06..2ee0e1238 100644 --- a/resources/lang/de_DE/additional.po +++ b/resources/lang/de_DE/additional.po @@ -249,6 +249,9 @@ msgstr "Du wurdest von einem Supporter als %1$s hinzugefügt." msgid "notification.angeltype.added.text" msgstr "Eine Beschreibung findest du unter %2$s" +msgid "angeltype.add.success" +msgstr "Erfolgreich dem Engeltyp beigetreten" + msgid "notification.shift.deleted" msgstr "Deine Schicht wurde gelöscht" diff --git a/resources/lang/de_DE/default.po b/resources/lang/de_DE/default.po index ce6260f40..c1317e7ae 100644 --- a/resources/lang/de_DE/default.po +++ b/resources/lang/de_DE/default.po @@ -1307,6 +1307,9 @@ msgstr "Alle löschen" msgid "form.updated" msgstr "Aktualisiert" +msgid "form.generate" +msgstr "Generieren" + msgid "form.cancel" msgstr "Abbrechen" @@ -1922,6 +1925,21 @@ msgid "angeltypes.hide_on_shift_view.info" msgstr "Wenn ausgewählt, können nur Admins und Mitglieder des Engeltyps auf der " "Schicht Seite die Filteroption für diesen Engeltyp sehen." +msgid "angeltypes.qr" +msgstr "Engeltyp \"%s\" beitreten QR Code" + +msgid "angeltypes.qr.expires" +msgstr "Läuft ab %c" + +msgid "angeltypes.qr.expired" +msgstr "Abgelaufen" + +msgid "angeltypes.qr.time" +msgstr "Lebensdauer (Minuten)" + +msgid "angeltypes.qr.time.info" +msgstr "Der Link ist für die gesamte angegebene Zeitspanne gültig und kann nicht zurückgezogen werden!" + msgid "location.location" msgstr "Ort" @@ -2008,6 +2026,9 @@ msgstr "Anzahl" msgid "general.created_at" msgstr "Erstellt am" +msgid "general.url" +msgstr "URL" + msgid "shifts.history" msgstr "Schichten Historie" diff --git a/resources/lang/en_US/additional.po b/resources/lang/en_US/additional.po index d17701c39..654845f95 100644 --- a/resources/lang/en_US/additional.po +++ b/resources/lang/en_US/additional.po @@ -248,6 +248,9 @@ msgstr "You have been added as an %1$s by a supporter." msgid "notification.angeltype.added.text" msgstr "You can find a description at %2$s" +msgid "angeltype.add.success" +msgstr "Successfully joined angel type" + msgid "notification.shift.deleted" msgstr "Your Shift was deleted" diff --git a/resources/lang/en_US/default.po b/resources/lang/en_US/default.po index beaffc5d3..4a41ff519 100644 --- a/resources/lang/en_US/default.po +++ b/resources/lang/en_US/default.po @@ -121,6 +121,9 @@ msgstr "Delete" msgid "form.delete_all" msgstr "Delete all" +msgid "form.generate" +msgstr "Generate" + msgid "form.updated" msgstr "Updated" @@ -787,6 +790,21 @@ msgid "angeltypes.hide_on_shift_view.info" msgstr "If checked only admins and members of the angel type " "can see the filter option for this angel type on the shifts page" +msgid "angeltypes.qr" +msgstr "Join angel type \"%s\" QR code" + +msgid "angeltypes.qr.expires" +msgstr "Expires %c" + +msgid "angeltypes.qr.expired" +msgstr "Expired" + +msgid "angeltypes.qr.time" +msgstr "Lifetime (minutes)" + +msgid "angeltypes.qr.time.info" +msgstr "The link will be valid for the whole time span and can't be revoked!" + msgid "location.location" msgstr "Location" @@ -898,6 +916,9 @@ msgstr "Count" msgid "general.created_at" msgstr "Created at" +msgid "general.url" +msgstr "URL" + msgid "shifts.random" msgstr "Random shift" diff --git a/resources/views/pages/angeltypes/qr.twig b/resources/views/pages/angeltypes/qr.twig new file mode 100644 index 000000000..e9c7fc6d1 --- /dev/null +++ b/resources/views/pages/angeltypes/qr.twig @@ -0,0 +1,59 @@ +{% extends "layouts/app.twig" %} +{% import 'macros/base.twig' as m %} +{% import 'macros/form.twig' as f %} + +{% block title %}{{ __('angeltypes.qr', [angel_type.name]) }}{% endblock %} + +{% block content %} +
    + +

    + {{ m.back(url('/angeltypes', {'action': 'view', 'angeltype_id': angel_type.id})) }} + {{ block('title') }} +

    + +
    + {% if qr_data %} +
    +
    + {{ qr(qr_data, 400) }} + {% set expires = session_get('form-data-expires') %} + {% if expires %} +

    + + {{ __('angeltypes.qr.expires') }} + +

    + {% endif %} +
    +
    + + {% endif %} + {{ f.input('url', __('general.url'), {'value': qr_data, 'readonly': true, 'disabled': not qr_data}) }} + + + {{ csrf() }} + +
    + {{ f.input('minutes', __('angeltypes.qr.time'), { + 'type': 'number', + 'required': true, + 'value': f.formData('minutes', 5), + 'min': 1, + 'max': qr_max_expiration_minutes, + 'info': __('angeltypes.qr.time.info'), + }) }} +
    + +
    + {{ f.submit(__('form.generate'), {'icon_left': 'qr-code', 'class': 'mb-3'}) }} +
    + +
    + +
    +{% endblock %} diff --git a/src/Controllers/AngelTypesController.php b/src/Controllers/AngelTypesController.php index 24f4e1202..ef4750131 100644 --- a/src/Controllers/AngelTypesController.php +++ b/src/Controllers/AngelTypesController.php @@ -4,13 +4,45 @@ namespace Engelsystem\Controllers; +use Engelsystem\Config\Config; +use Engelsystem\Helpers\Authenticator; +use Engelsystem\Helpers\Carbon; +use Engelsystem\Http\Exceptions\HttpNotFound; +use Engelsystem\Http\Request; use Engelsystem\Http\Response; use Engelsystem\Models\AngelType; +use Engelsystem\Models\User\User; +use Engelsystem\Models\UserAngelType; +use Exception; +use Firebase\JWT\BeforeValidException; +use Firebase\JWT\ExpiredException; +use Firebase\JWT\JWT; +use Firebase\JWT\Key; +use Illuminate\Support\Str; +use Psr\Http\Message\ServerRequestInterface; +use Psr\Log\LoggerInterface; class AngelTypesController extends BaseController { - public function __construct(protected Response $response) + use HasUserNotifications; + + public function __construct( + protected Response $response, + protected Config $config, + protected Authenticator $auth, + protected LoggerInterface $log, + ) { + } + + public function hasPermission(ServerRequestInterface $request, string $method): ?bool { + return match ($method) { + 'qrCode' => + $this->auth->user()?->isAngelTypeSupporter($this->getAngelType($request)) + || $this->auth->can('admin_user_angeltypes'), + 'join' => (bool) $this->auth->user(), + default => parent::hasPermission($request, $method), + }; } public function about(): Response @@ -22,4 +54,119 @@ public function about(): Response ['angeltypes' => $angeltypes] ); } + + public function qrCode(Request $request): Response + { + $this->qrJoinEnabled(); + $angelType = $this->getAngelType($request); + $jwtExpirationMin = $this->config->get('jwt_expiration_time'); + $qrData = null; + $data = []; + + if ($request->isMethod('post')) { + $data = $this->validate($request, [ + 'minutes' => 'required|int|min:1|max:' . $jwtExpirationMin, + ]); + $minutes = (int) $data['minutes']; + $time = Carbon::now(); + + $key = $this->config->get('app_key'); + $alg = $this->config->get('jwt_algorithm'); + $jti = Str::random(); + + $iat = $time->timestamp; + $exp = $time->addMinutes($minutes)->timestamp; + $data['expires'] = $time; + + $payload = [ + 'sub' => 'join_angel_type', + 'iat' => $iat, + 'exp' => $exp, + 'jti' => $jti, + 'id' => $angelType->id, + 'by' => $this->auth->user()->id, + ]; + $jwt = JWT::encode($payload, $key, $alg); + $qrData = url('/angeltypes/' . $angelType->id . '/join', ['token' => $jwt]); + } + + return $this->response->withInput($data)->withView( + 'pages/angeltypes/qr', + ['angel_type' => $angelType, 'qr_data' => $qrData, 'qr_max_expiration_minutes' => $jwtExpirationMin], + ); + } + + public function join(Request $request): Response + { + $this->qrJoinEnabled(); + $angelType = $this->getAngelType($request); + + $jwt = $request->get('token', ''); + + $key = $this->config->get('app_key'); + $alg = $this->config->get('jwt_algorithm'); + + try { + $decoded = JWT::decode($jwt, new Key($key, $alg)); + } catch (BeforeValidException | ExpiredException) { + throw new HttpNotFound(); + } catch (Exception $e) { + $this->log->error('JWT Error', ['exception' => $e]); + throw new HttpNotFound(); + } + + $type = $decoded->sub ?? null; + $id = $decoded->id ?? null; + $jti = $decoded->jti ?? null; + if ($type !== 'join_angel_type' || $id !== $angelType->id) { + throw new HttpNotFound(); + } + + /** @var User $confirmingUser */ + $confirmingUser = User::findOrFail($decoded->by ?? null); + /** @var UserAngelType $userAngelType */ + $userAngelType = UserAngelType::firstOrNew([ + 'user_id' => $this->auth->user()->id, + 'angel_type_id' => $angelType->id, + ]); + + if (!$userAngelType->confirmUser) { + $userAngelType->confirmUser()->associate($confirmingUser); + + $this->log->info( + 'Joined angel type {type} ({type_id}) via QR token {token_id} ' + . 'created by {confirming_user} ({confirming_id})', + [ + 'type' => $angelType->name, + 'type_id' => $angelType->id, + 'token_id' => $jti, + 'confirming_user' => $confirmingUser->name, + 'confirming_id' => $confirmingUser->id, + ] + ); + + $userAngelType->save(); + } + + $this->addNotification('angeltype.add.success'); + + return redirect(url('/angeltypes', ['action' => 'view', 'angeltype_id' => $angelType->id])); + } + + protected function qrJoinEnabled(): void + { + if ($this->config->get('app_key') && $this->config->get('join_qr_code', true)) { + return; + } + + throw new HttpNotFound(); + } + + protected function getAngelType(ServerRequestInterface $request): AngelType + { + $angelTypeId = (int) $request->getAttribute('angel_type_id'); + /** @var AngelType $angelType */ + $angelType = AngelType::findOrFail($angelTypeId); + return $angelType; + } } diff --git a/tests/Unit/Controllers/AngelTypesControllerTest.php b/tests/Unit/Controllers/AngelTypesControllerTest.php index 5c89bd5e6..a01cd1fd8 100644 --- a/tests/Unit/Controllers/AngelTypesControllerTest.php +++ b/tests/Unit/Controllers/AngelTypesControllerTest.php @@ -4,13 +4,56 @@ namespace Engelsystem\Test\Unit\Controllers; +use Engelsystem\Config\Config; use Engelsystem\Controllers\AngelTypesController; +use Engelsystem\Helpers\Authenticator; +use Engelsystem\Helpers\Carbon; +use Engelsystem\Http\Exceptions\HttpNotFound; +use Engelsystem\Http\Redirector; +use Engelsystem\Http\Request; use Engelsystem\Http\Response; -use Engelsystem\Test\Unit\TestCase; +use Engelsystem\Http\UrlGenerator; +use Engelsystem\Http\Validation\Validator; +use Engelsystem\Models\AngelType; +use Engelsystem\Models\User\User; +use Engelsystem\Test\Unit\HasDatabase; +use Firebase\JWT\JWT; +use Firebase\JWT\Key; +use Illuminate\Database\Eloquent\ModelNotFoundException; use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\NullLogger; +use Psr\Log\Test\TestLogger; -class AngelTypesControllerTest extends TestCase +class AngelTypesControllerTest extends ControllerTest { + use HasDatabase; + + /** + * @covers \Engelsystem\Controllers\AngelTypesController::hasPermission + */ + public function testHasPermission(): void + { + /** @var Response|MockObject $response */ + $response = $this->createMock(Response::class); + /** @var Authenticator|MockObject $auth */ + $auth = $this->createMock(Authenticator::class); + /** @var AngelType $angelType */ + $angelType = AngelType::factory()->create(); + $request = (new Request())->withAttribute('angel_type_id', $angelType->id); + $user = User::factory()->create(); + + $controller = new AngelTypesController($response, $this->app->get(Config::class), $auth, new NullLogger()); + $this->assertFalse($controller->hasPermission($request, 'qrCode')); + $this->assertFalse($controller->hasPermission($request, 'join')); + $this->assertNull($controller->hasPermission($request, 'about')); + + $this->setExpects($auth, 'user', [], $user, $this->atLeastOnce()); + $this->assertTrue($controller->hasPermission($request, 'join')); + + $this->setExpects($auth, 'can', ['admin_user_angeltypes'], true); + $this->assertTrue($controller->hasPermission($request, 'qrCode')); + } + /** * @covers \Engelsystem\Controllers\AngelTypesController::__construct * @covers \Engelsystem\Controllers\AngelTypesController::about @@ -19,6 +62,8 @@ public function testIndex(): void { /** @var Response|MockObject $response */ $response = $this->createMock(Response::class); + /** @var Authenticator|MockObject $auth */ + $auth = $this->createMock(Authenticator::class); $this->setExpects( $response, @@ -26,7 +71,240 @@ public function testIndex(): void ['pages/angeltypes/about'] ); - $controller = new AngelTypesController($response); + $controller = new AngelTypesController($response, new Config(), $auth, new NullLogger()); $controller->about(); } + + /** + * @covers \Engelsystem\Controllers\AngelTypesController::qrCode + */ + public function testQrCode(): void + { + /** @var Response|MockObject $response */ + $response = $this->createMock(Response::class); + /** @var Authenticator|MockObject $auth */ + $auth = $this->createMock(Authenticator::class); + /** @var AngelType $angelType */ + $angelType = AngelType::factory()->create(); + $request = (new Request())->withAttribute('angel_type_id', $angelType->id); + + $this->setExpects($response, 'withInput', [], $response); + $this->setExpects($response, 'withView', ['pages/angeltypes/qr'], $response); + + $controller = new AngelTypesController($response, $this->app->get(Config::class), $auth, new NullLogger()); + $controller->qrCode($request); + } + + /** + * @covers \Engelsystem\Controllers\AngelTypesController::qrCode + * @covers \Engelsystem\Controllers\AngelTypesController::join + * @covers \Engelsystem\Controllers\AngelTypesController::qrJoinEnabled + * @covers \Engelsystem\Controllers\AngelTypesController::getAngelType + */ + public function testQrCodePost(): void + { + /** @var Response|MockObject $response */ + $response = $this->createMock(Response::class); + /** @var Authenticator|MockObject $auth */ + $auth = $this->createMock(Authenticator::class); + /** @var UrlGenerator|MockObject $urlGenerator */ + $urlGenerator = $this->createMock(UrlGenerator::class); + $this->app->instance('http.urlGenerator', $urlGenerator); + /** @var User $user */ + $user = User::factory()->create(); + /** @var User $user2 */ + $user2 = User::factory()->create(); + /** @var AngelType $angelType */ + $angelType = AngelType::factory()->create(); + /** @var Redirector $redirect */ + $redirect = $this->createMock(Redirector::class); + $this->app->instance('redirect', $redirect); + $log = new TestLogger(); + $request = (new Request()) + ->withAttribute('angel_type_id', $angelType->id) + ->withMethod('post') + ->withParsedBody([ + 'minutes' => 42, + ]); + + $token = null; + $urlGenerator->expects($this->exactly(2)) + ->method('to') + ->willReturnCallback(function ($path, $parameters) use ($angelType, $user, &$token) { + if ($path === '/angeltypes') { + return '/angeltypes..'; + } + + $this->assertEquals('/angeltypes/' . $angelType->id . '/join', $path); + $this->assertArrayHasKey('token', $parameters); + $token = $parameters['token']; + $data = (array) JWT::decode( + $token, + new Key($this->config->get('app_key'), $this->config->get('jwt_algorithm')) + ); + $this->assertArrayHasKey('sub', $data); + $this->assertEquals('join_angel_type', $data['sub']); + $this->assertArrayHasKey('iat', $data); + $this->assertArrayHasKey('exp', $data); + $this->assertArrayHasKey('id', $data); + $this->assertArrayHasKey('jti', $data); + $this->assertEquals($angelType->id, $data['id']); + $this->assertArrayHasKey('by', $data); + $this->assertEquals($user->id, $data['by']); + return '/url..'; + }); + $auth->expects($this->exactly(2)) + ->method('user') + ->willReturnOnConsecutiveCalls($user, $user2); + $this->setExpects($response, 'withInput', [], $response); + $response->expects($this->once()) + ->method('withView') + ->willReturnCallback(function ($view, $data) use ($response) { + $this->assertEquals('pages/angeltypes/qr', $view); + $this->assertArrayHasKey('angel_type', $data); + $this->assertArrayHasKey('qr_data', $data); + $this->assertEquals('/url..', $data['qr_data']); + $this->assertArrayHasKey('qr_max_expiration_minutes', $data); + $this->assertEquals(60 * 24 * 5, $data['qr_max_expiration_minutes']); + return $response; + }); + $this->setExpects($redirect, 'to', ['/angeltypes..'], $response); + + $controller = new AngelTypesController($response, $this->config, $auth, $log); + $controller->setValidator(new Validator()); + + $controller->qrCode($request); + + $request = (new Request(['token' => $token])) + ->withAttribute('angel_type_id', $angelType->id); + $controller->join($request); + + $this->assertTrue($log->hasInfoThatContains('Joined angel type')); + $this->assertHasNotification('angeltype.add.success'); + + /** @var AngelType $userAngelType */ + $userAngelType = $user2->userAngelTypes->first(); + $this->assertNotEmpty($userAngelType); + $this->assertEquals($angelType->id, $userAngelType->id); + $this->assertEquals($user->id, $userAngelType->pivot->confirm_user_id); + } + + /** + * @covers \Engelsystem\Controllers\AngelTypesController::join + */ + public function testJoinDecodeError(): void + { + /** @var Authenticator|MockObject $auth */ + $auth = $this->createMock(Authenticator::class); + /** @var AngelType $angelType */ + $angelType = AngelType::factory()->create(); + + $controller = new AngelTypesController(new Response(), $this->config, $auth, new NullLogger()); + $controller->setValidator(new Validator()); + + $request = (new Request(['token' => 'some.test.code'])) + ->withAttribute('angel_type_id', $angelType->id); + + $this->expectException(HttpNotFound::class); + $controller->join($request); + } + + /** + * @covers \Engelsystem\Controllers\AngelTypesController::join + */ + public function testJoinDecodeExpired(): void + { + /** @var Authenticator|MockObject $auth */ + $auth = $this->createMock(Authenticator::class); + /** @var AngelType $angelType */ + $angelType = AngelType::factory()->create(); + $token = JWT::encode( + ['exp' => Carbon::now()->subMinute()->timestamp], + $this->config->get('app_key'), + $this->config->get('jwt_algorithm'), + ); + + $controller = new AngelTypesController(new Response(), $this->config, $auth, new NullLogger()); + $controller->setValidator(new Validator()); + + $request = (new Request(['token' => $token])) + ->withAttribute('angel_type_id', $angelType->id); + + $this->expectException(HttpNotFound::class); + $controller->join($request); + } + + /** + * @covers \Engelsystem\Controllers\AngelTypesController::join + */ + public function testJoinTokenMismatch(): void + { + /** @var Authenticator|MockObject $auth */ + $auth = $this->createMock(Authenticator::class); + /** @var AngelType $angelType */ + $angelType = AngelType::factory()->create(); + $token = JWT::encode( + ['sub' => 'do_something_different'], + $this->config->get('app_key'), + $this->config->get('jwt_algorithm'), + ); + + $controller = new AngelTypesController(new Response(), $this->config, $auth, new NullLogger()); + $controller->setValidator(new Validator()); + + $request = (new Request(['token' => $token])) + ->withAttribute('angel_type_id', $angelType->id); + + $this->expectException(HttpNotFound::class); + $controller->join($request); + } + + /** + * @covers \Engelsystem\Controllers\AngelTypesController::getAngelType + */ + public function testGetAngelTypeNotFound(): void + { + /** @var Response|MockObject $response */ + $response = $this->createMock(Response::class); + /** @var Authenticator|MockObject $auth */ + $auth = $this->createMock(Authenticator::class); + /** @var Request|MockObject $request */ + $request = $this->createMock(Request::class); + + $controller = new AngelTypesController($response, $this->app->get(Config::class), $auth, new NullLogger()); + + $this->expectException(ModelNotFoundException::class); + $controller->qrCode($request); + } + + /** + * @covers \Engelsystem\Controllers\AngelTypesController::qrJoinEnabled + */ + public function testQrJoinEnabledDisabled(): void + { + /** @var Response|MockObject $response */ + $response = $this->createMock(Response::class); + /** @var Authenticator|MockObject $auth */ + $auth = $this->createMock(Authenticator::class); + /** @var Request|MockObject $request */ + $request = $this->createMock(Request::class); + $this->config->set('join_qr_code', false); + + $controller = new AngelTypesController($response, $this->config, $auth, new NullLogger()); + + $this->expectException(HttpNotFound::class); + $controller->qrCode($request); + } + + public function setUp(): void + { + parent::setUp(); + $this->initDatabase(); + + $this->config->set([ + 'app_key' => 'S0me5ecUreTes1K3y', + 'jwt_algorithm' => 'HS256', + 'jwt_expiration_time' => 60 * 24 * 5, + ]); + } } From 1539157f495806ff89c69f768b583269e4926f86 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sat, 22 Nov 2025 21:36:15 +0100 Subject: [PATCH 140/157] Upgrade composer packages --- composer.json | 36 +- composer.lock | 1543 ++++++++++++++++------------- tests/Unit/Helpers/GoodieTest.php | 4 +- 3 files changed, 854 insertions(+), 729 deletions(-) diff --git a/composer.json b/composer.json index 9313c1ede..236796369 100644 --- a/composer.json +++ b/composer.json @@ -40,11 +40,11 @@ "firebase/php-jwt": "^6.11", "gettext/gettext": "^5.7", "gettext/translator": "^1.2", - "guzzlehttp/guzzle": "^7.9", - "illuminate/container": "^11.27", - "illuminate/database": "^11.27", - "illuminate/support": "^11.27", - "laravel/serializable-closure": "^1.3", + "guzzlehttp/guzzle": "^7.10", + "illuminate/container": "^12.39", + "illuminate/database": "^12.39", + "illuminate/support": "^12.39", + "laravel/serializable-closure": "^2.0", "league/oauth2-client": "^2.7", "league/openapi-psr7-validator": "^0.22.0", "nikic/fast-route": "^1.3", @@ -53,24 +53,24 @@ "psr/http-message": "^1.1", "psr/http-server-middleware": "^1.0", "psr/log": "^3.0", - "rcrowe/twigbridge": "^0.14.x-dev", - "respect/validation": "^2.3", - "symfony/http-foundation": "^7.1", - "symfony/mailer": "^7.1", - "symfony/psr-http-message-bridge": "^7.1", - "twig/twig": "^3.14", + "rcrowe/twigbridge": "^0.14", + "respect/validation": "^2.4", + "symfony/http-foundation": "^7.3", + "symfony/mailer": "^7.3", + "symfony/psr-http-message-bridge": "^7.3", + "twig/twig": "^3.22", "vlucas/phpdotenv": "^5.6" }, "require-dev": { "dms/phpunit-arraysubset-asserts": "^0.5.0", - "fakerphp/faker": "^1.23", - "fig/log-test": "^1.1", - "filp/whoops": "^2.16", - "phpstan/phpstan": "^1.12", + "fakerphp/faker": "^1.24", + "fig/log-test": "^1.2", + "filp/whoops": "^2.18", + "phpstan/phpstan": "^2.1", "phpunit/phpunit": "^9.6", - "slevomat/coding-standard": "^8.22", - "squizlabs/php_codesniffer": "^3.10", - "symfony/var-dumper": "^7.1" + "slevomat/coding-standard": "^8.25", + "squizlabs/php_codesniffer": "^4.0", + "symfony/var-dumper": "^7.3" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index db1b8bdc0..62d1f40d5 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "08cd395ef3c606dca6150635943267e6", + "content-hash": "28779c72bc795591fbc99e56a7401fe9", "packages": [ { "name": "bacon/bacon-qr-code", @@ -63,25 +63,25 @@ }, { "name": "brick/math", - "version": "0.12.1", + "version": "0.14.1", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "f510c0a40911935b77b86859eb5223d58d660df1" + "reference": "f05858549e5f9d7bb45875a75583240a38a281d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/f510c0a40911935b77b86859eb5223d58d660df1", - "reference": "f510c0a40911935b77b86859eb5223d58d660df1", + "url": "https://api.github.com/repos/brick/math/zipball/f05858549e5f9d7bb45875a75583240a38a281d0", + "reference": "f05858549e5f9d7bb45875a75583240a38a281d0", "shasum": "" }, "require": { - "php": "^8.1" + "php": "^8.2" }, "require-dev": { "php-coveralls/php-coveralls": "^2.2", - "phpunit/phpunit": "^10.1", - "vimeo/psalm": "5.16.0" + "phpstan/phpstan": "2.1.22", + "phpunit/phpunit": "^11.5" }, "type": "library", "autoload": { @@ -111,7 +111,7 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.12.1" + "source": "https://github.com/brick/math/tree/0.14.1" }, "funding": [ { @@ -119,7 +119,7 @@ "type": "github" } ], - "time": "2023-11-29T23:19:16+00:00" + "time": "2025-11-24T14:40:29+00:00" }, { "name": "carbonphp/carbon-doctrine-types", @@ -318,33 +318,32 @@ }, { "name": "doctrine/inflector", - "version": "2.0.10", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/doctrine/inflector.git", - "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc" + "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/inflector/zipball/5817d0659c5b50c9b950feb9af7b9668e2c436bc", - "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/6d6c96277ea252fc1304627204c3d5e6e15faa3b", + "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b", "shasum": "" }, "require": { "php": "^7.2 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^11.0", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.1", - "phpstan/phpstan-strict-rules": "^1.3", - "phpunit/phpunit": "^8.5 || ^9.5", - "vimeo/psalm": "^4.25 || ^5.4" + "doctrine/coding-standard": "^12.0 || ^13.0", + "phpstan/phpstan": "^1.12 || ^2.0", + "phpstan/phpstan-phpunit": "^1.4 || ^2.0", + "phpstan/phpstan-strict-rules": "^1.6 || ^2.0", + "phpunit/phpunit": "^8.5 || ^12.2" }, "type": "library", "autoload": { "psr-4": { - "Doctrine\\Inflector\\": "lib/Doctrine/Inflector" + "Doctrine\\Inflector\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -389,7 +388,7 @@ ], "support": { "issues": "https://github.com/doctrine/inflector/issues", - "source": "https://github.com/doctrine/inflector/tree/2.0.10" + "source": "https://github.com/doctrine/inflector/tree/2.1.0" }, "funding": [ { @@ -405,7 +404,7 @@ "type": "tidelift" } ], - "time": "2024-02-18T20:23:39+00:00" + "time": "2025-08-10T19:31:58+00:00" }, { "name": "doctrine/lexer", @@ -486,16 +485,16 @@ }, { "name": "egulias/email-validator", - "version": "4.0.3", + "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/egulias/EmailValidator.git", - "reference": "b115554301161fa21467629f1e1391c1936de517" + "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/b115554301161fa21467629f1e1391c1936de517", - "reference": "b115554301161fa21467629f1e1391c1936de517", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", + "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", "shasum": "" }, "require": { @@ -541,7 +540,7 @@ ], "support": { "issues": "https://github.com/egulias/EmailValidator/issues", - "source": "https://github.com/egulias/EmailValidator/tree/4.0.3" + "source": "https://github.com/egulias/EmailValidator/tree/4.0.4" }, "funding": [ { @@ -549,7 +548,7 @@ "type": "github" } ], - "time": "2024-12-27T00:36:43+00:00" + "time": "2025-03-06T22:45:56+00:00" }, { "name": "erusev/parsedown", @@ -740,16 +739,16 @@ }, { "name": "gettext/languages", - "version": "2.10.0", + "version": "2.12.1", "source": { "type": "git", "url": "https://github.com/php-gettext/Languages.git", - "reference": "4d61d67fe83a2ad85959fe6133d6d9ba7dddd1ab" + "reference": "0b0b0851c55168e1dfb14305735c64019732b5f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-gettext/Languages/zipball/4d61d67fe83a2ad85959fe6133d6d9ba7dddd1ab", - "reference": "4d61d67fe83a2ad85959fe6133d6d9ba7dddd1ab", + "url": "https://api.github.com/repos/php-gettext/Languages/zipball/0b0b0851c55168e1dfb14305735c64019732b5f1", + "reference": "0b0b0851c55168e1dfb14305735c64019732b5f1", "shasum": "" }, "require": { @@ -759,7 +758,8 @@ "phpunit/phpunit": "^4.8 || ^5.7 || ^6.5 || ^7.5 || ^8.4" }, "bin": [ - "bin/export-plural-rules" + "bin/export-plural-rules", + "bin/import-cldr-data" ], "type": "library", "autoload": { @@ -798,7 +798,7 @@ ], "support": { "issues": "https://github.com/php-gettext/Languages/issues", - "source": "https://github.com/php-gettext/Languages/tree/2.10.0" + "source": "https://github.com/php-gettext/Languages/tree/2.12.1" }, "funding": [ { @@ -810,7 +810,7 @@ "type": "github" } ], - "time": "2022-10-18T15:00:10+00:00" + "time": "2025-03-19T11:14:02+00:00" }, { "name": "gettext/translator", @@ -950,22 +950,22 @@ }, { "name": "guzzlehttp/guzzle", - "version": "7.9.2", + "version": "7.10.0", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "d281ed313b989f213357e3be1a179f02196ac99b" + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b", - "reference": "d281ed313b989f213357e3be1a179f02196ac99b", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^1.5.3 || ^2.0.3", - "guzzlehttp/psr7": "^2.7.0", + "guzzlehttp/promises": "^2.3", + "guzzlehttp/psr7": "^2.8", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", "symfony/deprecation-contracts": "^2.2 || ^3.0" @@ -1056,7 +1056,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.9.2" + "source": "https://github.com/guzzle/guzzle/tree/7.10.0" }, "funding": [ { @@ -1072,20 +1072,20 @@ "type": "tidelift" } ], - "time": "2024-07-24T11:22:20+00:00" + "time": "2025-08-23T22:36:01+00:00" }, { "name": "guzzlehttp/promises", - "version": "2.0.4", + "version": "2.3.0", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455" + "reference": "481557b130ef3790cf82b713667b43030dc9c957" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/f9c436286ab2892c7db7be8c8da4ef61ccf7b455", - "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455", + "url": "https://api.github.com/repos/guzzle/promises/zipball/481557b130ef3790cf82b713667b43030dc9c957", + "reference": "481557b130ef3790cf82b713667b43030dc9c957", "shasum": "" }, "require": { @@ -1093,7 +1093,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.39 || ^9.6.20" + "phpunit/phpunit": "^8.5.44 || ^9.6.25" }, "type": "library", "extra": { @@ -1139,7 +1139,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.0.4" + "source": "https://github.com/guzzle/promises/tree/2.3.0" }, "funding": [ { @@ -1155,20 +1155,20 @@ "type": "tidelift" } ], - "time": "2024-10-17T10:06:22+00:00" + "time": "2025-08-22T14:34:08+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.7.0", + "version": "2.8.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201" + "reference": "21dc724a0583619cd1652f673303492272778051" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201", - "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/21dc724a0583619cd1652f673303492272778051", + "reference": "21dc724a0583619cd1652f673303492272778051", "shasum": "" }, "require": { @@ -1184,7 +1184,7 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", "http-interop/http-factory-tests": "0.9.0", - "phpunit/phpunit": "^8.5.39 || ^9.6.20" + "phpunit/phpunit": "^8.5.44 || ^9.6.25" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" @@ -1255,7 +1255,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.7.0" + "source": "https://github.com/guzzle/psr7/tree/2.8.0" }, "funding": [ { @@ -1271,151 +1271,36 @@ "type": "tidelift" } ], - "time": "2024-07-18T11:15:46+00:00" - }, - { - "name": "icecave/parity", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/icecave/parity.git", - "reference": "0109fef58b3230d23b20b2ac52ecdf477218d300" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/icecave/parity/zipball/0109fef58b3230d23b20b2ac52ecdf477218d300", - "reference": "0109fef58b3230d23b20b2ac52ecdf477218d300", - "shasum": "" - }, - "require": { - "icecave/repr": "~1", - "php": ">=5.3" - }, - "require-dev": { - "eloquent/liberator": "~1", - "icecave/archer": "~1" - }, - "suggest": { - "eloquent/asplode": "Drop-in exception-based error handling." - }, - "type": "library", - "autoload": { - "psr-0": { - "Icecave\\Parity": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "James Harris", - "email": "james.harris@icecave.com.au", - "homepage": "https://github.com/jmalloc" - } - ], - "description": "A customizable deep comparison library.", - "homepage": "https://github.com/IcecaveStudios/parity", - "keywords": [ - "compare", - "comparison", - "equal", - "equality", - "greater", - "less", - "sort", - "sorting" - ], - "support": { - "issues": "https://github.com/icecave/parity/issues", - "source": "https://github.com/icecave/parity/tree/1.0.0" - }, - "time": "2014-01-17T05:56:27+00:00" - }, - { - "name": "icecave/repr", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/icecave/repr.git", - "reference": "8a3d2953adf5f464a06e3e2587aeacc97e2bed07" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/icecave/repr/zipball/8a3d2953adf5f464a06e3e2587aeacc97e2bed07", - "reference": "8a3d2953adf5f464a06e3e2587aeacc97e2bed07", - "shasum": "" - }, - "require": { - "php": ">=5.3" - }, - "require-dev": { - "icecave/archer": "~1" - }, - "suggest": { - "eloquent/asplode": "Drop-in exception-based error handling." - }, - "type": "library", - "autoload": { - "psr-4": { - "Icecave\\Repr\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "James Harris", - "email": "james.harris@icecave.com.au", - "homepage": "https://github.com/jmalloc" - } - ], - "description": "A library for generating string representations of any value, inspired by Python's reprlib library.", - "homepage": "https://github.com/IcecaveStudios/repr", - "keywords": [ - "human", - "readable", - "repr", - "representation", - "string" - ], - "support": { - "issues": "https://github.com/icecave/repr/issues", - "source": "https://github.com/icecave/repr/tree/1.0.1" - }, - "time": "2014-07-25T05:44:41+00:00" + "time": "2025-08-23T21:21:41+00:00" }, { "name": "illuminate/bus", - "version": "v11.41.3", + "version": "v12.40.2", "source": { "type": "git", "url": "https://github.com/illuminate/bus.git", - "reference": "ed6dd9b36ff18a57a951bd5946f1c3a534f900cb" + "reference": "7845b735651ffb734b8b064e7d0349490adf4564" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/bus/zipball/ed6dd9b36ff18a57a951bd5946f1c3a534f900cb", - "reference": "ed6dd9b36ff18a57a951bd5946f1c3a534f900cb", + "url": "https://api.github.com/repos/illuminate/bus/zipball/7845b735651ffb734b8b064e7d0349490adf4564", + "reference": "7845b735651ffb734b8b064e7d0349490adf4564", "shasum": "" }, "require": { - "illuminate/collections": "^11.0", - "illuminate/contracts": "^11.0", - "illuminate/pipeline": "^11.0", - "illuminate/support": "^11.0", + "illuminate/collections": "^12.0", + "illuminate/contracts": "^12.0", + "illuminate/pipeline": "^12.0", + "illuminate/support": "^12.0", "php": "^8.2" }, "suggest": { - "illuminate/queue": "Required to use closures when chaining jobs (^7.0)." + "illuminate/queue": "Required to use closures when chaining jobs (^12.0)." }, "type": "library", "extra": { "branch-alias": { - "dev-master": "11.x-dev" + "dev-master": "12.x-dev" } }, "autoload": { @@ -1439,35 +1324,38 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-01-22T21:19:28+00:00" + "time": "2025-11-04T15:31:54+00:00" }, { "name": "illuminate/collections", - "version": "v11.41.3", + "version": "v12.40.2", "source": { "type": "git", "url": "https://github.com/illuminate/collections.git", - "reference": "80c85f81573cc4c024da05312119f9149a6b64c1" + "reference": "3a794986bad4caf369d17ae19d4ef20a38dd8b0c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/collections/zipball/80c85f81573cc4c024da05312119f9149a6b64c1", - "reference": "80c85f81573cc4c024da05312119f9149a6b64c1", + "url": "https://api.github.com/repos/illuminate/collections/zipball/3a794986bad4caf369d17ae19d4ef20a38dd8b0c", + "reference": "3a794986bad4caf369d17ae19d4ef20a38dd8b0c", "shasum": "" }, "require": { - "illuminate/conditionable": "^11.0", - "illuminate/contracts": "^11.0", - "illuminate/macroable": "^11.0", - "php": "^8.2" + "illuminate/conditionable": "^12.0", + "illuminate/contracts": "^12.0", + "illuminate/macroable": "^12.0", + "php": "^8.2", + "symfony/polyfill-php84": "^1.33", + "symfony/polyfill-php85": "^1.33" }, "suggest": { - "symfony/var-dumper": "Required to use the dump method (^7.0)." + "illuminate/http": "Required to convert collections to API resources (^12.0).", + "symfony/var-dumper": "Required to use the dump method (^7.2)." }, "type": "library", "extra": { "branch-alias": { - "dev-master": "11.x-dev" + "dev-master": "12.x-dev" } }, "autoload": { @@ -1495,29 +1383,29 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-01-24T15:40:32+00:00" + "time": "2025-11-24T14:13:52+00:00" }, { "name": "illuminate/conditionable", - "version": "v11.41.3", + "version": "v12.40.2", "source": { "type": "git", "url": "https://github.com/illuminate/conditionable.git", - "reference": "911df1bda950a3b799cf80671764e34eede131c6" + "reference": "ec677967c1f2faf90b8428919124d2184a4c9b49" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/conditionable/zipball/911df1bda950a3b799cf80671764e34eede131c6", - "reference": "911df1bda950a3b799cf80671764e34eede131c6", + "url": "https://api.github.com/repos/illuminate/conditionable/zipball/ec677967c1f2faf90b8428919124d2184a4c9b49", + "reference": "ec677967c1f2faf90b8428919124d2184a4c9b49", "shasum": "" }, "require": { - "php": "^8.0.2" + "php": "^8.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "11.x-dev" + "dev-master": "12.x-dev" } }, "autoload": { @@ -1541,34 +1429,44 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-11-21T16:28:56+00:00" + "time": "2025-05-13T15:08:45+00:00" }, { "name": "illuminate/container", - "version": "v11.41.3", + "version": "v12.40.2", "source": { "type": "git", "url": "https://github.com/illuminate/container.git", - "reference": "1caf7d6cf42078fa8d30f26f1e43943ed6b01dae" + "reference": "17ec6c2f741b11564420acc737dea9334d69988c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/container/zipball/1caf7d6cf42078fa8d30f26f1e43943ed6b01dae", - "reference": "1caf7d6cf42078fa8d30f26f1e43943ed6b01dae", + "url": "https://api.github.com/repos/illuminate/container/zipball/17ec6c2f741b11564420acc737dea9334d69988c", + "reference": "17ec6c2f741b11564420acc737dea9334d69988c", "shasum": "" }, "require": { - "illuminate/contracts": "^11.0", + "illuminate/contracts": "^12.0", "php": "^8.2", - "psr/container": "^1.1.1|^2.0.1" + "psr/container": "^1.1.1|^2.0.1", + "symfony/polyfill-php84": "^1.33", + "symfony/polyfill-php85": "^1.33" }, "provide": { "psr/container-implementation": "1.1|2.0" }, + "suggest": { + "illuminate/auth": "Required to use the Auth attribute", + "illuminate/cache": "Required to use the Cache attribute", + "illuminate/config": "Required to use the Config attribute", + "illuminate/database": "Required to use the DB attribute", + "illuminate/filesystem": "Required to use the Storage attribute", + "illuminate/log": "Required to use the Log or Context attributes" + }, "type": "library", "extra": { "branch-alias": { - "dev-master": "11.x-dev" + "dev-master": "12.x-dev" } }, "autoload": { @@ -1592,20 +1490,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-01-28T20:44:34+00:00" + "time": "2025-11-14T15:29:05+00:00" }, { "name": "illuminate/contracts", - "version": "v11.41.3", + "version": "v12.40.2", "source": { "type": "git", "url": "https://github.com/illuminate/contracts.git", - "reference": "534b697fc1dd9fbdd9fbf2f33fc9dcbb943dea75" + "reference": "b97a94df448f196f23d646e21999bfd5d86ae23b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/contracts/zipball/534b697fc1dd9fbdd9fbf2f33fc9dcbb943dea75", - "reference": "534b697fc1dd9fbdd9fbf2f33fc9dcbb943dea75", + "url": "https://api.github.com/repos/illuminate/contracts/zipball/b97a94df448f196f23d646e21999bfd5d86ae23b", + "reference": "b97a94df448f196f23d646e21999bfd5d86ae23b", "shasum": "" }, "require": { @@ -1616,7 +1514,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "11.x-dev" + "dev-master": "12.x-dev" } }, "autoload": { @@ -1640,46 +1538,48 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-01-10T20:57:00+00:00" + "time": "2025-11-26T16:51:20+00:00" }, { "name": "illuminate/database", - "version": "v11.41.3", + "version": "v12.40.2", "source": { "type": "git", "url": "https://github.com/illuminate/database.git", - "reference": "ca7441b61fa56e45286d98f130cb1c71eac3ac7f" + "reference": "0f67c294e46ab2a836adf9a55ab47b05c9d614a6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/database/zipball/ca7441b61fa56e45286d98f130cb1c71eac3ac7f", - "reference": "ca7441b61fa56e45286d98f130cb1c71eac3ac7f", + "url": "https://api.github.com/repos/illuminate/database/zipball/0f67c294e46ab2a836adf9a55ab47b05c9d614a6", + "reference": "0f67c294e46ab2a836adf9a55ab47b05c9d614a6", "shasum": "" }, "require": { - "brick/math": "^0.9.3|^0.10.2|^0.11|^0.12", + "brick/math": "^0.11|^0.12|^0.13|^0.14", "ext-pdo": "*", - "illuminate/collections": "^11.0", - "illuminate/container": "^11.0", - "illuminate/contracts": "^11.0", - "illuminate/macroable": "^11.0", - "illuminate/support": "^11.0", + "illuminate/collections": "^12.0", + "illuminate/container": "^12.0", + "illuminate/contracts": "^12.0", + "illuminate/macroable": "^12.0", + "illuminate/support": "^12.0", "laravel/serializable-closure": "^1.3|^2.0", - "php": "^8.2" + "php": "^8.2", + "symfony/polyfill-php85": "^1.33" }, "suggest": { "ext-filter": "Required to use the Postgres database driver.", "fakerphp/faker": "Required to use the eloquent factory builder (^1.24).", - "illuminate/console": "Required to use the database commands (^11.0).", - "illuminate/events": "Required to use the observers with Eloquent (^11.0).", - "illuminate/filesystem": "Required to use the migrations (^11.0).", - "illuminate/pagination": "Required to paginate the result set (^11.0).", - "symfony/finder": "Required to use Eloquent model factories (^7.0)." + "illuminate/console": "Required to use the database commands (^12.0).", + "illuminate/events": "Required to use the observers with Eloquent (^12.0).", + "illuminate/filesystem": "Required to use the migrations (^12.0).", + "illuminate/http": "Required to convert Eloquent models to API resources (^12.0).", + "illuminate/pagination": "Required to paginate the result set (^12.0).", + "symfony/finder": "Required to use Eloquent model factories (^7.2)." }, "type": "library", "extra": { "branch-alias": { - "dev-master": "11.x-dev" + "dev-master": "12.x-dev" } }, "autoload": { @@ -1709,35 +1609,35 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-01-30T09:49:46+00:00" + "time": "2025-11-26T14:35:45+00:00" }, { "name": "illuminate/events", - "version": "v11.41.3", + "version": "v12.40.2", "source": { "type": "git", "url": "https://github.com/illuminate/events.git", - "reference": "2fcff2a924d1e2d58561f7b5d5078d6847a9eee8" + "reference": "e0de667c68040d59a6ffc09e914536a1186870f0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/events/zipball/2fcff2a924d1e2d58561f7b5d5078d6847a9eee8", - "reference": "2fcff2a924d1e2d58561f7b5d5078d6847a9eee8", + "url": "https://api.github.com/repos/illuminate/events/zipball/e0de667c68040d59a6ffc09e914536a1186870f0", + "reference": "e0de667c68040d59a6ffc09e914536a1186870f0", "shasum": "" }, "require": { - "illuminate/bus": "^11.0", - "illuminate/collections": "^11.0", - "illuminate/container": "^11.0", - "illuminate/contracts": "^11.0", - "illuminate/macroable": "^11.0", - "illuminate/support": "^11.0", + "illuminate/bus": "^12.0", + "illuminate/collections": "^12.0", + "illuminate/container": "^12.0", + "illuminate/contracts": "^12.0", + "illuminate/macroable": "^12.0", + "illuminate/support": "^12.0", "php": "^8.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "11.x-dev" + "dev-master": "12.x-dev" } }, "autoload": { @@ -1764,47 +1664,47 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-12-06T19:16:00+00:00" + "time": "2025-10-21T15:10:34+00:00" }, { "name": "illuminate/filesystem", - "version": "v11.41.3", + "version": "v12.40.2", "source": { "type": "git", "url": "https://github.com/illuminate/filesystem.git", - "reference": "a8768cca697ddf6f0cecc6f5bd4763808d84c0b7" + "reference": "b1fbb20010e868f838feac86aeac8ba439fca10d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/filesystem/zipball/a8768cca697ddf6f0cecc6f5bd4763808d84c0b7", - "reference": "a8768cca697ddf6f0cecc6f5bd4763808d84c0b7", + "url": "https://api.github.com/repos/illuminate/filesystem/zipball/b1fbb20010e868f838feac86aeac8ba439fca10d", + "reference": "b1fbb20010e868f838feac86aeac8ba439fca10d", "shasum": "" }, "require": { - "illuminate/collections": "^11.0", - "illuminate/contracts": "^11.0", - "illuminate/macroable": "^11.0", - "illuminate/support": "^11.0", + "illuminate/collections": "^12.0", + "illuminate/contracts": "^12.0", + "illuminate/macroable": "^12.0", + "illuminate/support": "^12.0", "php": "^8.2", - "symfony/finder": "^7.0.3" + "symfony/finder": "^7.2.0" }, "suggest": { "ext-fileinfo": "Required to use the Filesystem class.", "ext-ftp": "Required to use the Flysystem FTP driver.", "ext-hash": "Required to use the Filesystem class.", - "illuminate/http": "Required for handling uploaded files (^7.0).", + "illuminate/http": "Required for handling uploaded files (^12.0).", "league/flysystem": "Required to use the Flysystem local driver (^3.25.1).", "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.25.1).", "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.25.1).", "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.25.1).", "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", - "symfony/filesystem": "Required to enable support for relative symbolic links (^7.0).", - "symfony/mime": "Required to enable support for guessing extensions (^7.0)." + "symfony/filesystem": "Required to enable support for relative symbolic links (^7.2).", + "symfony/mime": "Required to enable support for guessing extensions (^7.2)." }, "type": "library", "extra": { "branch-alias": { - "dev-master": "11.x-dev" + "dev-master": "12.x-dev" } }, "autoload": { @@ -1831,20 +1731,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-01-24T16:08:55+00:00" + "time": "2025-10-29T15:59:33+00:00" }, { "name": "illuminate/macroable", - "version": "v11.41.3", + "version": "v12.40.2", "source": { "type": "git", "url": "https://github.com/illuminate/macroable.git", - "reference": "e1cb9e51b9ed5d3c9bc1ab431d0a52fe42a990ed" + "reference": "e862e5648ee34004fa56046b746f490dfa86c613" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/macroable/zipball/e1cb9e51b9ed5d3c9bc1ab431d0a52fe42a990ed", - "reference": "e1cb9e51b9ed5d3c9bc1ab431d0a52fe42a990ed", + "url": "https://api.github.com/repos/illuminate/macroable/zipball/e862e5648ee34004fa56046b746f490dfa86c613", + "reference": "e862e5648ee34004fa56046b746f490dfa86c613", "shasum": "" }, "require": { @@ -1853,7 +1753,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "11.x-dev" + "dev-master": "12.x-dev" } }, "autoload": { @@ -1877,31 +1777,35 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-06-28T20:10:30+00:00" + "time": "2024-07-23T16:31:01+00:00" }, { "name": "illuminate/pipeline", - "version": "v11.41.3", + "version": "v12.40.2", "source": { "type": "git", "url": "https://github.com/illuminate/pipeline.git", - "reference": "a784f85ca9d6c37435c542ea487d78206a7df3ad" + "reference": "b6a14c20d69a44bf0a6fba664a00d23ca71770ee" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/pipeline/zipball/a784f85ca9d6c37435c542ea487d78206a7df3ad", - "reference": "a784f85ca9d6c37435c542ea487d78206a7df3ad", + "url": "https://api.github.com/repos/illuminate/pipeline/zipball/b6a14c20d69a44bf0a6fba664a00d23ca71770ee", + "reference": "b6a14c20d69a44bf0a6fba664a00d23ca71770ee", "shasum": "" }, "require": { - "illuminate/contracts": "^11.0", - "illuminate/support": "^11.0", + "illuminate/contracts": "^12.0", + "illuminate/macroable": "^12.0", + "illuminate/support": "^12.0", "php": "^8.2" }, + "suggest": { + "illuminate/database": "Required to use database transactions (^12.0)." + }, "type": "library", "extra": { "branch-alias": { - "dev-master": "11.x-dev" + "dev-master": "12.x-dev" } }, "autoload": { @@ -1925,20 +1829,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-01-07T23:29:34+00:00" + "time": "2025-08-20T13:36:50+00:00" }, { "name": "illuminate/support", - "version": "v11.41.3", + "version": "v12.40.2", "source": { "type": "git", "url": "https://github.com/illuminate/support.git", - "reference": "5dc4a31f34d8a0529cf1fd6b7fe167f6cae91e0c" + "reference": "20a64e34d9ee8bb7b28b242155e9c31f86e5804b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/support/zipball/5dc4a31f34d8a0529cf1fd6b7fe167f6cae91e0c", - "reference": "5dc4a31f34d8a0529cf1fd6b7fe167f6cae91e0c", + "url": "https://api.github.com/repos/illuminate/support/zipball/20a64e34d9ee8bb7b28b242155e9c31f86e5804b", + "reference": "20a64e34d9ee8bb7b28b242155e9c31f86e5804b", "shasum": "" }, "require": { @@ -1946,12 +1850,14 @@ "ext-ctype": "*", "ext-filter": "*", "ext-mbstring": "*", - "illuminate/collections": "^11.0", - "illuminate/conditionable": "^11.0", - "illuminate/contracts": "^11.0", - "illuminate/macroable": "^11.0", - "nesbot/carbon": "^2.72.6|^3.8.4", + "illuminate/collections": "^12.0", + "illuminate/conditionable": "^12.0", + "illuminate/contracts": "^12.0", + "illuminate/macroable": "^12.0", + "nesbot/carbon": "^3.8.4", "php": "^8.2", + "symfony/polyfill-php83": "^1.33", + "symfony/polyfill-php85": "^1.33", "voku/portable-ascii": "^2.0.2" }, "conflict": { @@ -1961,20 +1867,20 @@ "spatie/once": "*" }, "suggest": { - "illuminate/filesystem": "Required to use the Composer class (^11.0).", + "illuminate/filesystem": "Required to use the Composer class (^12.0).", "laravel/serializable-closure": "Required to use the once function (^1.3|^2.0).", - "league/commonmark": "Required to use Str::markdown() and Stringable::markdown() (^2.6).", + "league/commonmark": "Required to use Str::markdown() and Stringable::markdown() (^2.7).", "league/uri": "Required to use the Uri class (^7.5.1).", "ramsey/uuid": "Required to use Str::uuid() (^4.7).", - "symfony/process": "Required to use the Composer class (^7.0).", - "symfony/uid": "Required to use Str::ulid() (^7.0).", - "symfony/var-dumper": "Required to use the dd function (^7.0).", + "symfony/process": "Required to use the Composer class (^7.2).", + "symfony/uid": "Required to use Str::ulid() (^7.2).", + "symfony/var-dumper": "Required to use the dd function (^7.2).", "vlucas/phpdotenv": "Required to use the Env class and env helper (^5.6.1)." }, "type": "library", "extra": { "branch-alias": { - "dev-master": "11.x-dev" + "dev-master": "12.x-dev" } }, "autoload": { @@ -2002,37 +1908,37 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-01-30T09:11:36+00:00" + "time": "2025-11-26T14:47:26+00:00" }, { "name": "illuminate/view", - "version": "v11.41.3", + "version": "v12.40.2", "source": { "type": "git", "url": "https://github.com/illuminate/view.git", - "reference": "9f9bed5b55b1b888a6149860b4b26b20ca4435bf" + "reference": "f065c5fc1ad29aaf5734c5f99f69fc4cde9a255f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/view/zipball/9f9bed5b55b1b888a6149860b4b26b20ca4435bf", - "reference": "9f9bed5b55b1b888a6149860b4b26b20ca4435bf", + "url": "https://api.github.com/repos/illuminate/view/zipball/f065c5fc1ad29aaf5734c5f99f69fc4cde9a255f", + "reference": "f065c5fc1ad29aaf5734c5f99f69fc4cde9a255f", "shasum": "" }, "require": { "ext-tokenizer": "*", - "illuminate/collections": "^11.0", - "illuminate/container": "^11.0", - "illuminate/contracts": "^11.0", - "illuminate/events": "^11.0", - "illuminate/filesystem": "^11.0", - "illuminate/macroable": "^11.0", - "illuminate/support": "^11.0", + "illuminate/collections": "^12.0", + "illuminate/container": "^12.0", + "illuminate/contracts": "^12.0", + "illuminate/events": "^12.0", + "illuminate/filesystem": "^12.0", + "illuminate/macroable": "^12.0", + "illuminate/support": "^12.0", "php": "^8.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "11.x-dev" + "dev-master": "12.x-dev" } }, "autoload": { @@ -2056,31 +1962,34 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-01-22T21:19:28+00:00" + "time": "2025-11-16T14:42:54+00:00" }, { "name": "justinrainbow/json-schema", - "version": "6.0.0", + "version": "6.6.2", "source": { "type": "git", "url": "https://github.com/jsonrainbow/json-schema.git", - "reference": "a38c6198d53b09c0702f440585a4f4a5d9137bd9" + "reference": "3c25fe750c1599716ef26aa997f7c026cee8c4b7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/a38c6198d53b09c0702f440585a4f4a5d9137bd9", - "reference": "a38c6198d53b09c0702f440585a4f4a5d9137bd9", + "url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/3c25fe750c1599716ef26aa997f7c026cee8c4b7", + "reference": "3c25fe750c1599716ef26aa997f7c026cee8c4b7", "shasum": "" }, "require": { - "icecave/parity": "1.0.0", - "marc-mabe/php-enum": "^2.0 || ^3.0 || ^4.0", - "php": ">=5.3.3" + "ext-json": "*", + "marc-mabe/php-enum": "^4.0", + "php": "^7.2 || ^8.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "~2.2.20 || ~2.19.0", - "json-schema/json-schema-test-suite": "1.2.0", - "phpunit/phpunit": "^4.8.35" + "friendsofphp/php-cs-fixer": "3.3.0", + "json-schema/json-schema-test-suite": "^23.2", + "marc-mabe/php-enum-phpstan": "^2.0", + "phpspec/prophecy": "^1.19", + "phpstan/phpstan": "^1.12", + "phpunit/phpunit": "^8.5" }, "bin": [ "bin/validate-json" @@ -2126,38 +2035,38 @@ ], "support": { "issues": "https://github.com/jsonrainbow/json-schema/issues", - "source": "https://github.com/jsonrainbow/json-schema/tree/6.0.0" + "source": "https://github.com/jsonrainbow/json-schema/tree/6.6.2" }, - "time": "2024-07-30T17:49:21+00:00" + "time": "2025-11-28T15:24:03+00:00" }, { "name": "laravel/serializable-closure", - "version": "v1.3.7", + "version": "v2.0.7", "source": { "type": "git", "url": "https://github.com/laravel/serializable-closure.git", - "reference": "4f48ade902b94323ca3be7646db16209ec76be3d" + "reference": "cb291e4c998ac50637c7eeb58189c14f5de5b9dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/4f48ade902b94323ca3be7646db16209ec76be3d", - "reference": "4f48ade902b94323ca3be7646db16209ec76be3d", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/cb291e4c998ac50637c7eeb58189c14f5de5b9dd", + "reference": "cb291e4c998ac50637c7eeb58189c14f5de5b9dd", "shasum": "" }, "require": { - "php": "^7.3|^8.0" + "php": "^8.1" }, "require-dev": { - "illuminate/support": "^8.0|^9.0|^10.0|^11.0", - "nesbot/carbon": "^2.61|^3.0", - "pestphp/pest": "^1.21.3", - "phpstan/phpstan": "^1.8.2", - "symfony/var-dumper": "^5.4.11|^6.2.0|^7.0.0" + "illuminate/support": "^10.0|^11.0|^12.0", + "nesbot/carbon": "^2.67|^3.0", + "pestphp/pest": "^2.36|^3.0|^4.0", + "phpstan/phpstan": "^2.0", + "symfony/var-dumper": "^6.2.0|^7.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.x-dev" + "dev-master": "2.x-dev" } }, "autoload": { @@ -2189,26 +2098,26 @@ "issues": "https://github.com/laravel/serializable-closure/issues", "source": "https://github.com/laravel/serializable-closure" }, - "time": "2024-11-14T18:34:49+00:00" + "time": "2025-11-21T20:52:36+00:00" }, { "name": "league/oauth2-client", - "version": "2.8.0", + "version": "2.9.0", "source": { "type": "git", "url": "https://github.com/thephpleague/oauth2-client.git", - "reference": "3d5cf8d0543731dfb725ab30e4d7289891991e13" + "reference": "26e8c5da4f3d78cede7021e09b1330a0fc093d5e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/oauth2-client/zipball/3d5cf8d0543731dfb725ab30e4d7289891991e13", - "reference": "3d5cf8d0543731dfb725ab30e4d7289891991e13", + "url": "https://api.github.com/repos/thephpleague/oauth2-client/zipball/26e8c5da4f3d78cede7021e09b1330a0fc093d5e", + "reference": "26e8c5da4f3d78cede7021e09b1330a0fc093d5e", "shasum": "" }, "require": { "ext-json": "*", "guzzlehttp/guzzle": "^6.5.8 || ^7.4.5", - "php": "^7.1 || >=8.0.0 <8.5.0" + "php": "^7.1 || >=8.0.0 <8.6.0" }, "require-dev": { "mockery/mockery": "^1.3.5", @@ -2252,9 +2161,9 @@ ], "support": { "issues": "https://github.com/thephpleague/oauth2-client/issues", - "source": "https://github.com/thephpleague/oauth2-client/tree/2.8.0" + "source": "https://github.com/thephpleague/oauth2-client/tree/2.9.0" }, - "time": "2024-12-11T05:05:52+00:00" + "time": "2025-11-25T22:17:17+00:00" }, { "name": "league/openapi-psr7-validator", @@ -2320,33 +2229,38 @@ }, { "name": "league/uri", - "version": "7.5.1", + "version": "7.6.0", "source": { "type": "git", "url": "https://github.com/thephpleague/uri.git", - "reference": "81fb5145d2644324614cc532b28efd0215bda430" + "reference": "f625804987a0a9112d954f9209d91fec52182344" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri/zipball/81fb5145d2644324614cc532b28efd0215bda430", - "reference": "81fb5145d2644324614cc532b28efd0215bda430", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/f625804987a0a9112d954f9209d91fec52182344", + "reference": "f625804987a0a9112d954f9209d91fec52182344", "shasum": "" }, "require": { - "league/uri-interfaces": "^7.5", - "php": "^8.1" + "league/uri-interfaces": "^7.6", + "php": "^8.1", + "psr/http-factory": "^1" }, "conflict": { "league/uri-schemes": "^1.0" }, "suggest": { "ext-bcmath": "to improve IPV4 host parsing", + "ext-dom": "to convert the URI into an HTML anchor tag", "ext-fileinfo": "to create Data URI from file contennts", "ext-gmp": "to improve IPV4 host parsing", "ext-intl": "to handle IDN host with the best performance", + "ext-uri": "to use the PHP native URI class", "jeremykendall/php-domain-parser": "to resolve Public Suffix and Top Level Domain", "league/uri-components": "Needed to easily manipulate URI objects components", + "league/uri-polyfill": "Needed to backport the PHP URI extension for older versions of PHP", "php-64bit": "to improve IPV4 host parsing", + "rowbot/url": "to handle WHATWG URL", "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" }, "type": "library", @@ -2374,6 +2288,7 @@ "description": "URI manipulation library", "homepage": "https://uri.thephpleague.com", "keywords": [ + "URN", "data-uri", "file-uri", "ftp", @@ -2386,9 +2301,11 @@ "psr-7", "query-string", "querystring", + "rfc2141", "rfc3986", "rfc3987", "rfc6570", + "rfc8141", "uri", "uri-template", "url", @@ -2398,7 +2315,7 @@ "docs": "https://uri.thephpleague.com", "forum": "https://thephpleague.slack.com", "issues": "https://github.com/thephpleague/uri-src/issues", - "source": "https://github.com/thephpleague/uri/tree/7.5.1" + "source": "https://github.com/thephpleague/uri/tree/7.6.0" }, "funding": [ { @@ -2406,26 +2323,25 @@ "type": "github" } ], - "time": "2024-12-08T08:40:02+00:00" + "time": "2025-11-18T12:17:23+00:00" }, { "name": "league/uri-interfaces", - "version": "7.5.0", + "version": "7.6.0", "source": { "type": "git", "url": "https://github.com/thephpleague/uri-interfaces.git", - "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742" + "reference": "ccbfb51c0445298e7e0b7f4481b942f589665368" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/08cfc6c4f3d811584fb09c37e2849e6a7f9b0742", - "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/ccbfb51c0445298e7e0b7f4481b942f589665368", + "reference": "ccbfb51c0445298e7e0b7f4481b942f589665368", "shasum": "" }, "require": { "ext-filter": "*", "php": "^8.1", - "psr/http-factory": "^1", "psr/http-message": "^1.1 || ^2.0" }, "suggest": { @@ -2433,6 +2349,7 @@ "ext-gmp": "to improve IPV4 host parsing", "ext-intl": "to handle IDN host with the best performance", "php-64bit": "to improve IPV4 host parsing", + "rowbot/url": "to handle WHATWG URL", "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" }, "type": "library", @@ -2457,7 +2374,7 @@ "homepage": "https://nyamsprod.com" } ], - "description": "Common interfaces and classes for URI representation and interaction", + "description": "Common tools for parsing and resolving RFC3987/RFC3986 URI", "homepage": "https://uri.thephpleague.com", "keywords": [ "data-uri", @@ -2482,7 +2399,7 @@ "docs": "https://uri.thephpleague.com", "forum": "https://thephpleague.slack.com", "issues": "https://github.com/thephpleague/uri-src/issues", - "source": "https://github.com/thephpleague/uri-interfaces/tree/7.5.0" + "source": "https://github.com/thephpleague/uri-interfaces/tree/7.6.0" }, "funding": [ { @@ -2490,20 +2407,20 @@ "type": "github" } ], - "time": "2024-12-08T08:18:47+00:00" + "time": "2025-11-18T12:17:23+00:00" }, { "name": "marc-mabe/php-enum", - "version": "v4.7.1", + "version": "v4.7.2", "source": { "type": "git", "url": "https://github.com/marc-mabe/php-enum.git", - "reference": "7159809e5cfa041dca28e61f7f7ae58063aae8ed" + "reference": "bb426fcdd65c60fb3638ef741e8782508fda7eef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/marc-mabe/php-enum/zipball/7159809e5cfa041dca28e61f7f7ae58063aae8ed", - "reference": "7159809e5cfa041dca28e61f7f7ae58063aae8ed", + "url": "https://api.github.com/repos/marc-mabe/php-enum/zipball/bb426fcdd65c60fb3638ef741e8782508fda7eef", + "reference": "bb426fcdd65c60fb3638ef741e8782508fda7eef", "shasum": "" }, "require": { @@ -2561,22 +2478,22 @@ ], "support": { "issues": "https://github.com/marc-mabe/php-enum/issues", - "source": "https://github.com/marc-mabe/php-enum/tree/v4.7.1" + "source": "https://github.com/marc-mabe/php-enum/tree/v4.7.2" }, - "time": "2024-11-28T04:54:44+00:00" + "time": "2025-09-14T11:18:39+00:00" }, { "name": "nesbot/carbon", - "version": "3.8.4", + "version": "3.10.3", "source": { "type": "git", "url": "https://github.com/CarbonPHP/carbon.git", - "reference": "129700ed449b1f02d70272d2ac802357c8c30c58" + "reference": "8e3643dcd149ae0fe1d2ff4f2c8e4bbfad7c165f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/129700ed449b1f02d70272d2ac802357c8c30c58", - "reference": "129700ed449b1f02d70272d2ac802357c8c30c58", + "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/8e3643dcd149ae0fe1d2ff4f2c8e4bbfad7c165f", + "reference": "8e3643dcd149ae0fe1d2ff4f2c8e4bbfad7c165f", "shasum": "" }, "require": { @@ -2584,9 +2501,9 @@ "ext-json": "*", "php": "^8.1", "psr/clock": "^1.0", - "symfony/clock": "^6.3 || ^7.0", + "symfony/clock": "^6.3.12 || ^7.0", "symfony/polyfill-mbstring": "^1.0", - "symfony/translation": "^4.4.18 || ^5.2.1|| ^6.0 || ^7.0" + "symfony/translation": "^4.4.18 || ^5.2.1 || ^6.0 || ^7.0" }, "provide": { "psr/clock-implementation": "1.0" @@ -2594,14 +2511,13 @@ "require-dev": { "doctrine/dbal": "^3.6.3 || ^4.0", "doctrine/orm": "^2.15.2 || ^3.0", - "friendsofphp/php-cs-fixer": "^3.57.2", + "friendsofphp/php-cs-fixer": "^v3.87.1", "kylekatarnls/multi-tester": "^2.5.3", - "ondrejmirtes/better-reflection": "^6.25.0.4", "phpmd/phpmd": "^2.15.0", - "phpstan/extension-installer": "^1.3.1", - "phpstan/phpstan": "^1.11.2", - "phpunit/phpunit": "^10.5.20", - "squizlabs/php_codesniffer": "^3.9.0" + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^2.1.22", + "phpunit/phpunit": "^10.5.53", + "squizlabs/php_codesniffer": "^3.13.4" }, "bin": [ "bin/carbon" @@ -2652,8 +2568,8 @@ ], "support": { "docs": "https://carbon.nesbot.com/docs", - "issues": "https://github.com/briannesbitt/Carbon/issues", - "source": "https://github.com/briannesbitt/Carbon" + "issues": "https://github.com/CarbonPHP/carbon/issues", + "source": "https://github.com/CarbonPHP/carbon" }, "funding": [ { @@ -2669,7 +2585,7 @@ "type": "tidelift" } ], - "time": "2024-12-27T09:25:35+00:00" + "time": "2025-09-06T13:39:36+00:00" }, { "name": "nikic/fast-route", @@ -2801,16 +2717,16 @@ }, { "name": "phpoption/phpoption", - "version": "1.9.3", + "version": "1.9.4", "source": { "type": "git", "url": "https://github.com/schmittjoh/php-option.git", - "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54" + "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/e3fac8b24f56113f7cb96af14958c0dd16330f54", - "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d", + "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d", "shasum": "" }, "require": { @@ -2818,7 +2734,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" + "phpunit/phpunit": "^8.5.44 || ^9.6.25 || ^10.5.53 || ^11.5.34" }, "type": "library", "extra": { @@ -2860,7 +2776,7 @@ ], "support": { "issues": "https://github.com/schmittjoh/php-option/issues", - "source": "https://github.com/schmittjoh/php-option/tree/1.9.3" + "source": "https://github.com/schmittjoh/php-option/tree/1.9.4" }, "funding": [ { @@ -2872,7 +2788,7 @@ "type": "tidelift" } ], - "time": "2024-07-20T21:41:07+00:00" + "time": "2025-08-21T11:53:16+00:00" }, { "name": "psr/cache", @@ -3494,32 +3410,31 @@ }, { "name": "rcrowe/twigbridge", - "version": "dev-master", + "version": "v0.14.6", "source": { "type": "git", "url": "https://github.com/rcrowe/TwigBridge.git", - "reference": "f6cbdb9c152811fdf602736b4451ba62a34aa4c1" + "reference": "0798ee4b5e5b943d0200850acaa87ccd82e2fe45" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/rcrowe/TwigBridge/zipball/f6cbdb9c152811fdf602736b4451ba62a34aa4c1", - "reference": "f6cbdb9c152811fdf602736b4451ba62a34aa4c1", + "url": "https://api.github.com/repos/rcrowe/TwigBridge/zipball/0798ee4b5e5b943d0200850acaa87ccd82e2fe45", + "reference": "0798ee4b5e5b943d0200850acaa87ccd82e2fe45", "shasum": "" }, "require": { - "illuminate/support": "^9|^10|^11", - "illuminate/view": "^9|^10|^11", + "illuminate/support": "^9|^10|^11|^12", + "illuminate/view": "^9|^10|^11|^12", "php": "^8.1", - "twig/twig": "~3.12" + "twig/twig": "~3.21" }, "require-dev": { "ext-json": "*", - "laravel/framework": "^9|^10|^11", + "laravel/framework": "^9|^10|^11|^12", "mockery/mockery": "^1.3.1", - "phpunit/phpunit": "^8.5.8 || ^9.3.7", + "phpunit/phpunit": "^8.5.8 || ^9.3.7 || ^10.0 || ^11.0 || ^12.0", "squizlabs/php_codesniffer": "^3.6" }, - "default-branch": true, "type": "library", "extra": { "laravel": { @@ -3561,9 +3476,9 @@ ], "support": { "issues": "https://github.com/rcrowe/TwigBridge/issues", - "source": "https://github.com/rcrowe/TwigBridge/tree/master" + "source": "https://github.com/rcrowe/TwigBridge/tree/v0.14.6" }, - "time": "2025-02-01T06:09:05+00:00" + "time": "2025-08-20T11:25:49+00:00" }, { "name": "respect/stringifier", @@ -3621,16 +3536,16 @@ }, { "name": "respect/validation", - "version": "2.4.0", + "version": "2.4.4", "source": { "type": "git", "url": "https://github.com/Respect/Validation.git", - "reference": "48b38bd91e0badbc2c4381dce726b09fd68850d9" + "reference": "f13f10f19978aea33af2a102a2f58f2db1e63619" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Respect/Validation/zipball/48b38bd91e0badbc2c4381dce726b09fd68850d9", - "reference": "48b38bd91e0badbc2c4381dce726b09fd68850d9", + "url": "https://api.github.com/repos/Respect/Validation/zipball/f13f10f19978aea33af2a102a2f58f2db1e63619", + "reference": "f13f10f19978aea33af2a102a2f58f2db1e63619", "shasum": "" }, "require": { @@ -3640,7 +3555,7 @@ }, "require-dev": { "egulias/email-validator": "^3.0", - "giggsey/libphonenumber-for-php-lite": "^8.13", + "giggsey/libphonenumber-for-php-lite": "^8.13 || ^9.0", "malukenho/docheader": "^1.0", "mikey179/vfsstream": "^1.6", "phpstan/phpstan": "^1.9", @@ -3683,22 +3598,22 @@ ], "support": { "issues": "https://github.com/Respect/Validation/issues", - "source": "https://github.com/Respect/Validation/tree/2.4.0" + "source": "https://github.com/Respect/Validation/tree/2.4.4" }, - "time": "2025-01-07T00:34:58+00:00" + "time": "2025-06-07T00:07:21+00:00" }, { "name": "riverline/multipart-parser", - "version": "2.1.2", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/Riverline/multipart-parser.git", - "reference": "7a9f4646db5181516c61b8e0225a343189beedcd" + "reference": "1410f23a8fd416a0cf5c8867ea9c95544016c831" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Riverline/multipart-parser/zipball/7a9f4646db5181516c61b8e0225a343189beedcd", - "reference": "7a9f4646db5181516c61b8e0225a343189beedcd", + "url": "https://api.github.com/repos/Riverline/multipart-parser/zipball/1410f23a8fd416a0cf5c8867ea9c95544016c831", + "reference": "1410f23a8fd416a0cf5c8867ea9c95544016c831", "shasum": "" }, "require": { @@ -3739,22 +3654,22 @@ ], "support": { "issues": "https://github.com/Riverline/multipart-parser/issues", - "source": "https://github.com/Riverline/multipart-parser/tree/2.1.2" + "source": "https://github.com/Riverline/multipart-parser/tree/2.2.0" }, - "time": "2024-03-12T16:46:05+00:00" + "time": "2025-04-29T08:38:14+00:00" }, { "name": "symfony/clock", - "version": "v7.2.0", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/clock.git", - "reference": "b81435fbd6648ea425d1ee96a2d8e68f4ceacd24" + "reference": "9169f24776edde469914c1e7a1442a50f7a4e110" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/clock/zipball/b81435fbd6648ea425d1ee96a2d8e68f4ceacd24", - "reference": "b81435fbd6648ea425d1ee96a2d8e68f4ceacd24", + "url": "https://api.github.com/repos/symfony/clock/zipball/9169f24776edde469914c1e7a1442a50f7a4e110", + "reference": "9169f24776edde469914c1e7a1442a50f7a4e110", "shasum": "" }, "require": { @@ -3799,7 +3714,7 @@ "time" ], "support": { - "source": "https://github.com/symfony/clock/tree/v7.2.0" + "source": "https://github.com/symfony/clock/tree/v7.4.0" }, "funding": [ { @@ -3810,25 +3725,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:21:43+00:00" + "time": "2025-11-12T15:39:26+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.5.1", + "version": "v3.6.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6" + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", - "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", "shasum": "" }, "require": { @@ -3841,7 +3760,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.5-dev" + "dev-main": "3.6-dev" } }, "autoload": { @@ -3866,7 +3785,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" }, "funding": [ { @@ -3882,20 +3801,20 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2024-09-25T14:21:43+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v7.2.0", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "910c5db85a5356d0fea57680defec4e99eb9c8c1" + "reference": "9dddcddff1ef974ad87b3708e4b442dc38b2261d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/910c5db85a5356d0fea57680defec4e99eb9c8c1", - "reference": "910c5db85a5356d0fea57680defec4e99eb9c8c1", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9dddcddff1ef974ad87b3708e4b442dc38b2261d", + "reference": "9dddcddff1ef974ad87b3708e4b442dc38b2261d", "shasum": "" }, "require": { @@ -3912,13 +3831,14 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/error-handler": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/error-handler": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/framework-bundle": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", "symfony/service-contracts": "^2.5|^3", - "symfony/stopwatch": "^6.4|^7.0" + "symfony/stopwatch": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -3946,7 +3866,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v7.2.0" + "source": "https://github.com/symfony/event-dispatcher/tree/v7.4.0" }, "funding": [ { @@ -3957,25 +3877,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:21:43+00:00" + "time": "2025-10-28T09:38:46+00:00" }, { "name": "symfony/event-dispatcher-contracts", - "version": "v3.5.1", + "version": "v3.6.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "7642f5e970b672283b7823222ae8ef8bbc160b9f" + "reference": "59eb412e93815df44f05f342958efa9f46b1e586" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/7642f5e970b672283b7823222ae8ef8bbc160b9f", - "reference": "7642f5e970b672283b7823222ae8ef8bbc160b9f", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586", + "reference": "59eb412e93815df44f05f342958efa9f46b1e586", "shasum": "" }, "require": { @@ -3989,7 +3913,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.5-dev" + "dev-main": "3.6-dev" } }, "autoload": { @@ -4022,7 +3946,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.5.1" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.6.0" }, "funding": [ { @@ -4038,27 +3962,27 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2024-09-25T14:21:43+00:00" }, { "name": "symfony/finder", - "version": "v7.2.2", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "87a71856f2f56e4100373e92529eed3171695cfb" + "reference": "340b9ed7320570f319028a2cbec46d40535e94bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/87a71856f2f56e4100373e92529eed3171695cfb", - "reference": "87a71856f2f56e4100373e92529eed3171695cfb", + "url": "https://api.github.com/repos/symfony/finder/zipball/340b9ed7320570f319028a2cbec46d40535e94bd", + "reference": "340b9ed7320570f319028a2cbec46d40535e94bd", "shasum": "" }, "require": { "php": ">=8.2" }, "require-dev": { - "symfony/filesystem": "^6.4|^7.0" + "symfony/filesystem": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -4086,7 +4010,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.2.2" + "source": "https://github.com/symfony/finder/tree/v7.4.0" }, "funding": [ { @@ -4097,32 +4021,35 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-12-30T19:00:17+00:00" + "time": "2025-11-05T05:42:40+00:00" }, { "name": "symfony/http-foundation", - "version": "v7.3.7", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "db488a62f98f7a81d5746f05eea63a74e55bb7c4" + "reference": "769c1720b68e964b13b58529c17d4a385c62167b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/db488a62f98f7a81d5746f05eea63a74e55bb7c4", - "reference": "db488a62f98f7a81d5746f05eea63a74e55bb7c4", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/769c1720b68e964b13b58529c17d4a385c62167b", + "reference": "769c1720b68e964b13b58529c17d4a385c62167b", "shasum": "" }, "require": { "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3.0", - "symfony/polyfill-mbstring": "~1.1", - "symfony/polyfill-php83": "^1.27" + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "^1.1" }, "conflict": { "doctrine/dbal": "<3.6", @@ -4131,13 +4058,13 @@ "require-dev": { "doctrine/dbal": "^3.6|^4", "predis/predis": "^1.1|^2.0", - "symfony/cache": "^6.4.12|^7.1.5", - "symfony/clock": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/mime": "^6.4|^7.0", - "symfony/rate-limiter": "^6.4|^7.0" + "symfony/cache": "^6.4.12|^7.1.5|^8.0", + "symfony/clock": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/mime": "^6.4|^7.0|^8.0", + "symfony/rate-limiter": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -4165,7 +4092,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.3.7" + "source": "https://github.com/symfony/http-foundation/tree/v7.4.0" }, "funding": [ { @@ -4185,20 +4112,20 @@ "type": "tidelift" } ], - "time": "2025-11-08T16:41:12+00:00" + "time": "2025-11-13T08:49:24+00:00" }, { "name": "symfony/mailer", - "version": "v7.2.3", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/mailer.git", - "reference": "f3871b182c44997cf039f3b462af4a48fb85f9d3" + "reference": "a3d9eea8cfa467ece41f0f54ba28185d74bd53fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/f3871b182c44997cf039f3b462af4a48fb85f9d3", - "reference": "f3871b182c44997cf039f3b462af4a48fb85f9d3", + "url": "https://api.github.com/repos/symfony/mailer/zipball/a3d9eea8cfa467ece41f0f54ba28185d74bd53fd", + "reference": "a3d9eea8cfa467ece41f0f54ba28185d74bd53fd", "shasum": "" }, "require": { @@ -4206,8 +4133,8 @@ "php": ">=8.2", "psr/event-dispatcher": "^1", "psr/log": "^1|^2|^3", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/mime": "^7.2", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/mime": "^7.2|^8.0", "symfony/service-contracts": "^2.5|^3" }, "conflict": { @@ -4218,10 +4145,10 @@ "symfony/twig-bridge": "<6.4" }, "require-dev": { - "symfony/console": "^6.4|^7.0", - "symfony/http-client": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/twig-bridge": "^6.4|^7.0" + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/twig-bridge": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -4249,7 +4176,7 @@ "description": "Helps sending emails", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailer/tree/v7.2.3" + "source": "https://github.com/symfony/mailer/tree/v7.4.0" }, "funding": [ { @@ -4260,29 +4187,34 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-01-27T11:08:17+00:00" + "time": "2025-11-21T15:26:00+00:00" }, { "name": "symfony/mime", - "version": "v7.2.3", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "2fc3b4bd67e4747e45195bc4c98bea4628476204" + "reference": "bdb02729471be5d047a3ac4a69068748f1a6be7a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/2fc3b4bd67e4747e45195bc4c98bea4628476204", - "reference": "2fc3b4bd67e4747e45195bc4c98bea4628476204", + "url": "https://api.github.com/repos/symfony/mime/zipball/bdb02729471be5d047a3ac4a69068748f1a6be7a", + "reference": "bdb02729471be5d047a3ac4a69068748f1a6be7a", "shasum": "" }, "require": { "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-intl-idn": "^1.10", "symfony/polyfill-mbstring": "^1.0" }, @@ -4297,11 +4229,11 @@ "egulias/email-validator": "^2.1.10|^3.1|^4", "league/html-to-markdown": "^5.0", "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/property-access": "^6.4|^7.0", - "symfony/property-info": "^6.4|^7.0", - "symfony/serializer": "^6.4.3|^7.0.3" + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/property-access": "^6.4|^7.0|^8.0", + "symfony/property-info": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4.3|^7.0.3|^8.0" }, "type": "library", "autoload": { @@ -4333,7 +4265,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v7.2.3" + "source": "https://github.com/symfony/mime/tree/v7.4.0" }, "funding": [ { @@ -4344,16 +4276,20 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-01-27T11:08:17+00:00" + "time": "2025-11-16T10:14:42+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.31.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", @@ -4412,7 +4348,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" }, "funding": [ { @@ -4423,6 +4359,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -4432,16 +4372,16 @@ }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.31.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773" + "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/c36586dcf89a12315939e00ec9b4474adcb1d773", - "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/9614ac4d8061dc257ecc64cba1b140873dce8ad3", + "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3", "shasum": "" }, "require": { @@ -4495,7 +4435,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.33.0" }, "funding": [ { @@ -4506,16 +4446,20 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2024-09-10T14:38:51+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.31.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", @@ -4576,7 +4520,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" }, "funding": [ { @@ -4587,6 +4531,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -4596,19 +4544,20 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.31.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", - "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", "shasum": "" }, "require": { + "ext-iconv": "*", "php": ">=7.2" }, "provide": { @@ -4656,7 +4605,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" }, "funding": [ { @@ -4667,25 +4616,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2024-12-23T08:48:59+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.31.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8" + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", - "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608", "shasum": "" }, "require": { @@ -4736,7 +4689,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" }, "funding": [ { @@ -4747,25 +4700,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2025-01-02T08:10:11+00:00" }, { - "name": "symfony/polyfill-php81", - "version": "v1.31.0", + "name": "symfony/polyfill-php83", + "version": "v1.33.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php81.git", - "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c" + "url": "https://github.com/symfony/polyfill-php83.git", + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", - "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/17f6f9a6b1735c0f163024d959f700cfbc5155e5", + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5", "shasum": "" }, "require": { @@ -4783,7 +4740,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Php81\\": "" + "Symfony\\Polyfill\\Php83\\": "" }, "classmap": [ "Resources/stubs" @@ -4803,7 +4760,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", @@ -4812,7 +4769,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-php83/tree/v1.33.0" }, "funding": [ { @@ -4823,25 +4780,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2025-07-08T02:45:35+00:00" }, { - "name": "symfony/polyfill-php83", - "version": "v1.31.0", + "name": "symfony/polyfill-php84", + "version": "v1.33.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php83.git", - "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491" + "url": "https://github.com/symfony/polyfill-php84.git", + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/2fb86d65e2d424369ad2905e83b236a8805ba491", - "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/d8ced4d875142b6a7426000426b8abc631d6b191", + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191", "shasum": "" }, "require": { @@ -4859,7 +4820,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Php83\\": "" + "Symfony\\Polyfill\\Php84\\": "" }, "classmap": [ "Resources/stubs" @@ -4879,7 +4840,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions", + "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", @@ -4888,7 +4849,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php83/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-php84/tree/v1.33.0" }, "funding": [ { @@ -4899,31 +4860,115 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2025-06-24T13:30:11+00:00" + }, + { + "name": "symfony/polyfill-php85", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php85.git", + "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", + "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php85\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.5+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php85/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-06-23T16:12:55+00:00" }, { "name": "symfony/psr-http-message-bridge", - "version": "v7.2.0", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/psr-http-message-bridge.git", - "reference": "03f2f72319e7acaf2a9f6fcbe30ef17eec51594f" + "reference": "0101ff8bd0506703b045b1670960302d302a726c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/03f2f72319e7acaf2a9f6fcbe30ef17eec51594f", - "reference": "03f2f72319e7acaf2a9f6fcbe30ef17eec51594f", + "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/0101ff8bd0506703b045b1670960302d302a726c", + "reference": "0101ff8bd0506703b045b1670960302d302a726c", "shasum": "" }, "require": { "php": ">=8.2", "psr/http-message": "^1.0|^2.0", - "symfony/http-foundation": "^6.4|^7.0" + "symfony/http-foundation": "^6.4|^7.0|^8.0" }, "conflict": { "php-http/discovery": "<1.15", @@ -4933,11 +4978,12 @@ "nyholm/psr7": "^1.1", "php-http/discovery": "^1.15", "psr/log": "^1.1.4|^2|^3", - "symfony/browser-kit": "^6.4|^7.0", - "symfony/config": "^6.4|^7.0", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/framework-bundle": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0" + "symfony/browser-kit": "^6.4|^7.0|^8.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/framework-bundle": "^6.4.13|^7.1.6|^8.0", + "symfony/http-kernel": "^6.4.13|^7.1.6|^8.0", + "symfony/runtime": "^6.4.13|^7.1.6|^8.0" }, "type": "symfony-bridge", "autoload": { @@ -4971,7 +5017,7 @@ "psr-7" ], "support": { - "source": "https://github.com/symfony/psr-http-message-bridge/tree/v7.2.0" + "source": "https://github.com/symfony/psr-http-message-bridge/tree/v7.4.0" }, "funding": [ { @@ -4982,25 +5028,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-26T08:57:56+00:00" + "time": "2025-11-13T08:38:49+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.5.1", + "version": "v3.6.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0" + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e53260aabf78fb3d63f8d79d69ece59f80d5eda0", - "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43", + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43", "shasum": "" }, "require": { @@ -5018,7 +5068,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.5-dev" + "dev-main": "3.6-dev" } }, "autoload": { @@ -5054,7 +5104,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.5.1" + "source": "https://github.com/symfony/service-contracts/tree/v3.6.1" }, "funding": [ { @@ -5065,34 +5115,39 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2025-07-15T11:30:57+00:00" }, { "name": "symfony/translation", - "version": "v7.2.2", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "e2674a30132b7cc4d74540d6c2573aa363f05923" + "reference": "2d01ca0da3f092f91eeedb46f24aa30d2fca8f68" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/e2674a30132b7cc4d74540d6c2573aa363f05923", - "reference": "e2674a30132b7cc4d74540d6c2573aa363f05923", + "url": "https://api.github.com/repos/symfony/translation/zipball/2d01ca0da3f092f91eeedb46f24aa30d2fca8f68", + "reference": "2d01ca0da3f092f91eeedb46f24aa30d2fca8f68", "shasum": "" }, "require": { "php": ">=8.2", "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", - "symfony/translation-contracts": "^2.5|^3.0" + "symfony/translation-contracts": "^2.5.3|^3.3" }, "conflict": { + "nikic/php-parser": "<5.0", "symfony/config": "<6.4", "symfony/console": "<6.4", "symfony/dependency-injection": "<6.4", @@ -5106,19 +5161,19 @@ "symfony/translation-implementation": "2.3|3.0" }, "require-dev": { - "nikic/php-parser": "^4.18|^5.0", + "nikic/php-parser": "^5.0", "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", - "symfony/console": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/finder": "^6.4|^7.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/finder": "^6.4|^7.0|^8.0", "symfony/http-client-contracts": "^2.5|^3.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/intl": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/intl": "^6.4|^7.0|^8.0", "symfony/polyfill-intl-icu": "^1.21", - "symfony/routing": "^6.4|^7.0", + "symfony/routing": "^6.4|^7.0|^8.0", "symfony/service-contracts": "^2.5|^3", - "symfony/yaml": "^6.4|^7.0" + "symfony/yaml": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -5149,7 +5204,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v7.2.2" + "source": "https://github.com/symfony/translation/tree/v7.4.0" }, "funding": [ { @@ -5160,25 +5215,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-12-07T08:18:10+00:00" + "time": "2025-11-27T13:27:24+00:00" }, { "name": "symfony/translation-contracts", - "version": "v3.5.1", + "version": "v3.6.1", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "4667ff3bd513750603a09c8dedbea942487fb07c" + "reference": "65a8bc82080447fae78373aa10f8d13b38338977" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/4667ff3bd513750603a09c8dedbea942487fb07c", - "reference": "4667ff3bd513750603a09c8dedbea942487fb07c", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/65a8bc82080447fae78373aa10f8d13b38338977", + "reference": "65a8bc82080447fae78373aa10f8d13b38338977", "shasum": "" }, "require": { @@ -5191,7 +5250,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.5-dev" + "dev-main": "3.6-dev" } }, "autoload": { @@ -5227,7 +5286,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v3.5.1" + "source": "https://github.com/symfony/translation-contracts/tree/v3.6.1" }, "funding": [ { @@ -5238,37 +5297,41 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2025-07-15T13:41:35+00:00" }, { "name": "symfony/yaml", - "version": "v7.2.3", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "ac238f173df0c9c1120f862d0f599e17535a87ec" + "reference": "6c84a4b55aee4cd02034d1c528e83f69ddf63810" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/ac238f173df0c9c1120f862d0f599e17535a87ec", - "reference": "ac238f173df0c9c1120f862d0f599e17535a87ec", + "url": "https://api.github.com/repos/symfony/yaml/zipball/6c84a4b55aee4cd02034d1c528e83f69ddf63810", + "reference": "6c84a4b55aee4cd02034d1c528e83f69ddf63810", "shasum": "" }, "require": { "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3.0", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "^1.8" }, "conflict": { "symfony/console": "<6.4" }, "require-dev": { - "symfony/console": "^6.4|^7.0" + "symfony/console": "^6.4|^7.0|^8.0" }, "bin": [ "Resources/bin/yaml-lint" @@ -5299,7 +5362,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v7.2.3" + "source": "https://github.com/symfony/yaml/tree/v7.4.0" }, "funding": [ { @@ -5310,33 +5373,36 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-01-07T12:55:42+00:00" + "time": "2025-11-16T10:14:42+00:00" }, { "name": "twig/twig", - "version": "v3.19.0", + "version": "v3.22.1", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "d4f8c2b86374f08efc859323dbcd95c590f7124e" + "reference": "1de2ec1fc43ab58a4b7e80b214b96bfc895750f3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/d4f8c2b86374f08efc859323dbcd95c590f7124e", - "reference": "d4f8c2b86374f08efc859323dbcd95c590f7124e", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/1de2ec1fc43ab58a4b7e80b214b96bfc895750f3", + "reference": "1de2ec1fc43ab58a4b7e80b214b96bfc895750f3", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.1.0", "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-mbstring": "^1.3", - "symfony/polyfill-php81": "^1.29" + "symfony/polyfill-mbstring": "^1.3" }, "require-dev": { "phpstan/phpstan": "^2.0", @@ -5383,7 +5449,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.19.0" + "source": "https://github.com/twigphp/Twig/tree/v3.22.1" }, "funding": [ { @@ -5395,20 +5461,20 @@ "type": "tidelift" } ], - "time": "2025-01-29T07:06:14+00:00" + "time": "2025-11-16T16:01:12+00:00" }, { "name": "vlucas/phpdotenv", - "version": "v5.6.1", + "version": "v5.6.2", "source": { "type": "git", "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "a59a13791077fe3d44f90e7133eb68e7d22eaff2" + "reference": "24ac4c74f91ee2c193fa1aaa5c249cb0822809af" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/a59a13791077fe3d44f90e7133eb68e7d22eaff2", - "reference": "a59a13791077fe3d44f90e7133eb68e7d22eaff2", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/24ac4c74f91ee2c193fa1aaa5c249cb0822809af", + "reference": "24ac4c74f91ee2c193fa1aaa5c249cb0822809af", "shasum": "" }, "require": { @@ -5467,7 +5533,7 @@ ], "support": { "issues": "https://github.com/vlucas/phpdotenv/issues", - "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.1" + "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.2" }, "funding": [ { @@ -5479,7 +5545,7 @@ "type": "tidelift" } ], - "time": "2024-07-20T21:52:34+00:00" + "time": "2025-04-30T23:37:27+00:00" }, { "name": "voku/portable-ascii", @@ -5557,28 +5623,28 @@ }, { "name": "webmozart/assert", - "version": "1.11.0", + "version": "1.12.1", "source": { "type": "git", "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + "reference": "9be6926d8b485f55b9229203f962b51ed377ba68" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/9be6926d8b485f55b9229203f962b51ed377ba68", + "reference": "9be6926d8b485f55b9229203f962b51ed377ba68", "shasum": "" }, "require": { "ext-ctype": "*", + "ext-date": "*", + "ext-filter": "*", "php": "^7.2 || ^8.0" }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" + "suggest": { + "ext-intl": "", + "ext-simplexml": "", + "ext-spl": "" }, "type": "library", "extra": { @@ -5609,9 +5675,9 @@ ], "support": { "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" + "source": "https://github.com/webmozarts/assert/tree/1.12.1" }, - "time": "2022-06-03T18:03:27+00:00" + "time": "2025-10-29T15:56:20+00:00" } ], "packages-dev": [ @@ -5890,16 +5956,16 @@ }, { "name": "fig/log-test", - "version": "1.1.0", + "version": "1.2.1", "source": { "type": "git", "url": "https://github.com/php-fig/log-test.git", - "reference": "02d6eaf8b09784b7adacf57a3c951bad95229a7f" + "reference": "83acb6c12875ea4f349dc4fe8b7d8e239c2b3715" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log-test/zipball/02d6eaf8b09784b7adacf57a3c951bad95229a7f", - "reference": "02d6eaf8b09784b7adacf57a3c951bad95229a7f", + "url": "https://api.github.com/repos/php-fig/log-test/zipball/83acb6c12875ea4f349dc4fe8b7d8e239c2b3715", + "reference": "83acb6c12875ea4f349dc4fe8b7d8e239c2b3715", "shasum": "" }, "require": { @@ -5907,7 +5973,7 @@ "psr/log": "^2.0 | ^3.0" }, "require-dev": { - "phpunit/phpunit": "^8.0 | ^9.0", + "phpunit/phpunit": "^8.0 || ^9.0 || ^10.0 || ^11.0", "squizlabs/php_codesniffer": "^3.6" }, "type": "library", @@ -5928,24 +5994,27 @@ } ], "description": "Test utilities for the psr/log package that backs the PSR-3 specification.", + "keywords": [ + "testing" + ], "support": { "issues": "https://github.com/php-fig/log-test/issues", - "source": "https://github.com/php-fig/log-test/tree/1.1.0" + "source": "https://github.com/php-fig/log-test/tree/1.2.1" }, - "time": "2022-10-18T05:33:27+00:00" + "time": "2025-11-11T10:33:05+00:00" }, { "name": "filp/whoops", - "version": "2.17.0", + "version": "2.18.4", "source": { "type": "git", "url": "https://github.com/filp/whoops.git", - "reference": "075bc0c26631110584175de6523ab3f1652eb28e" + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/075bc0c26631110584175de6523ab3f1652eb28e", - "reference": "075bc0c26631110584175de6523ab3f1652eb28e", + "url": "https://api.github.com/repos/filp/whoops/zipball/d2102955e48b9fd9ab24280a7ad12ed552752c4d", + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d", "shasum": "" }, "require": { @@ -5995,7 +6064,7 @@ ], "support": { "issues": "https://github.com/filp/whoops/issues", - "source": "https://github.com/filp/whoops/tree/2.17.0" + "source": "https://github.com/filp/whoops/tree/2.18.4" }, "funding": [ { @@ -6003,20 +6072,20 @@ "type": "github" } ], - "time": "2025-01-25T12:00:00+00:00" + "time": "2025-08-08T12:00:00+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.12.1", + "version": "1.13.4", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845" + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845", - "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", "shasum": "" }, "require": { @@ -6055,7 +6124,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.12.1" + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" }, "funding": [ { @@ -6063,20 +6132,20 @@ "type": "tidelift" } ], - "time": "2024-11-08T17:47:46+00:00" + "time": "2025-08-01T08:46:24+00:00" }, { "name": "nikic/php-parser", - "version": "v5.4.0", + "version": "v5.6.2", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "447a020a1f875a434d62f2a401f53b82a396e494" + "reference": "3a454ca033b9e06b63282ce19562e892747449bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494", - "reference": "447a020a1f875a434d62f2a401f53b82a396e494", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3a454ca033b9e06b63282ce19562e892747449bb", + "reference": "3a454ca033b9e06b63282ce19562e892747449bb", "shasum": "" }, "require": { @@ -6095,7 +6164,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "5.x-dev" } }, "autoload": { @@ -6119,9 +6188,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.2" }, - "time": "2024-12-30T11:07:19+00:00" + "time": "2025-10-21T19:32:17+00:00" }, { "name": "phar-io/manifest", @@ -6290,20 +6359,15 @@ }, { "name": "phpstan/phpstan", - "version": "1.12.16", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpstan.git", - "reference": "e0bb5cb78545aae631220735aa706eac633a6be9" - }, + "version": "2.1.32", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e0bb5cb78545aae631220735aa706eac633a6be9", - "reference": "e0bb5cb78545aae631220735aa706eac633a6be9", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e126cad1e30a99b137b8ed75a85a676450ebb227", + "reference": "e126cad1e30a99b137b8ed75a85a676450ebb227", "shasum": "" }, "require": { - "php": "^7.2|^8.0" + "php": "^7.4|^8.0" }, "conflict": { "phpstan/phpstan-shim": "*" @@ -6344,7 +6408,7 @@ "type": "github" } ], - "time": "2025-01-21T14:50:05+00:00" + "time": "2025-11-11T15:18:17+00:00" }, { "name": "phpunit/php-code-coverage", @@ -6667,16 +6731,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.22", + "version": "9.6.29", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "f80235cb4d3caa59ae09be3adf1ded27521d1a9c" + "reference": "9ecfec57835a5581bc888ea7e13b51eb55ab9dd3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f80235cb4d3caa59ae09be3adf1ded27521d1a9c", - "reference": "f80235cb4d3caa59ae09be3adf1ded27521d1a9c", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9ecfec57835a5581bc888ea7e13b51eb55ab9dd3", + "reference": "9ecfec57835a5581bc888ea7e13b51eb55ab9dd3", "shasum": "" }, "require": { @@ -6687,7 +6751,7 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.12.1", + "myclabs/deep-copy": "^1.13.4", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=7.3", @@ -6698,11 +6762,11 @@ "phpunit/php-timer": "^5.0.3", "sebastian/cli-parser": "^1.0.2", "sebastian/code-unit": "^1.0.8", - "sebastian/comparator": "^4.0.8", + "sebastian/comparator": "^4.0.9", "sebastian/diff": "^4.0.6", "sebastian/environment": "^5.1.5", - "sebastian/exporter": "^4.0.6", - "sebastian/global-state": "^5.0.7", + "sebastian/exporter": "^4.0.8", + "sebastian/global-state": "^5.0.8", "sebastian/object-enumerator": "^4.0.4", "sebastian/resource-operations": "^3.0.4", "sebastian/type": "^3.2.1", @@ -6750,7 +6814,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.22" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.29" }, "funding": [ { @@ -6761,12 +6825,20 @@ "url": "https://github.com/sebastianbergmann", "type": "github" }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, { "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", "type": "tidelift" } ], - "time": "2024-12-05T13:48:26+00:00" + "time": "2025-09-24T06:29:11+00:00" }, { "name": "sebastian/cli-parser", @@ -6937,16 +7009,16 @@ }, { "name": "sebastian/comparator", - "version": "4.0.8", + "version": "4.0.9", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + "reference": "67a2df3a62639eab2cc5906065e9805d4fd5dfc5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/67a2df3a62639eab2cc5906065e9805d4fd5dfc5", + "reference": "67a2df3a62639eab2cc5906065e9805d4fd5dfc5", "shasum": "" }, "require": { @@ -6999,15 +7071,27 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.9" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" } ], - "time": "2022-09-14T12:41:17+00:00" + "time": "2025-08-10T06:51:50+00:00" }, { "name": "sebastian/complexity", @@ -7197,16 +7281,16 @@ }, { "name": "sebastian/exporter", - "version": "4.0.6", + "version": "4.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72" + "reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72", - "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/14c6ba52f95a36c3d27c835d65efc7123c446e8c", + "reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c", "shasum": "" }, "require": { @@ -7262,28 +7346,40 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.6" + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.8" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" } ], - "time": "2024-03-02T06:33:00+00:00" + "time": "2025-09-24T06:03:27+00:00" }, { "name": "sebastian/global-state", - "version": "5.0.7", + "version": "5.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9" + "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", - "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/b6781316bdcd28260904e7cc18ec983d0d2ef4f6", + "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6", "shasum": "" }, "require": { @@ -7326,15 +7422,27 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.7" + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.8" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/global-state", + "type": "tidelift" } ], - "time": "2024-03-02T06:35:11+00:00" + "time": "2025-08-10T07:10:35+00:00" }, { "name": "sebastian/lines-of-code", @@ -7507,16 +7615,16 @@ }, { "name": "sebastian/recursion-context", - "version": "4.0.5", + "version": "4.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + "reference": "539c6691e0623af6dc6f9c20384c120f963465a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/539c6691e0623af6dc6f9c20384c120f963465a0", + "reference": "539c6691e0623af6dc6f9c20384c120f963465a0", "shasum": "" }, "require": { @@ -7558,15 +7666,27 @@ "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.6" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" } ], - "time": "2023-02-03T06:07:39+00:00" + "time": "2025-08-10T06:57:39+00:00" }, { "name": "sebastian/resource-operations", @@ -7733,32 +7853,32 @@ }, { "name": "slevomat/coding-standard", - "version": "8.22.1", + "version": "8.25.1", "source": { "type": "git", "url": "https://github.com/slevomat/coding-standard.git", - "reference": "1dd80bf3b93692bedb21a6623c496887fad05fec" + "reference": "4caa5ec5a30b84b2305e80159c710d437f40cc40" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/1dd80bf3b93692bedb21a6623c496887fad05fec", - "reference": "1dd80bf3b93692bedb21a6623c496887fad05fec", + "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/4caa5ec5a30b84b2305e80159c710d437f40cc40", + "reference": "4caa5ec5a30b84b2305e80159c710d437f40cc40", "shasum": "" }, "require": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.1.2", + "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.2.0", "php": "^7.4 || ^8.0", "phpstan/phpdoc-parser": "^2.3.0", - "squizlabs/php_codesniffer": "^3.13.4" + "squizlabs/php_codesniffer": "^4.0.1" }, "require-dev": { "phing/phing": "3.0.1|3.1.0", "php-parallel-lint/php-parallel-lint": "1.4.0", - "phpstan/phpstan": "2.1.24", + "phpstan/phpstan": "2.1.32", "phpstan/phpstan-deprecation-rules": "2.0.3", - "phpstan/phpstan-phpunit": "2.0.7", - "phpstan/phpstan-strict-rules": "2.0.6", - "phpunit/phpunit": "9.6.8|10.5.48|11.4.4|11.5.36|12.3.10" + "phpstan/phpstan-phpunit": "2.0.8", + "phpstan/phpstan-strict-rules": "2.0.7", + "phpunit/phpunit": "9.6.8|10.5.48|11.4.4|11.5.36|12.4.4" }, "type": "phpcodesniffer-standard", "extra": { @@ -7782,7 +7902,7 @@ ], "support": { "issues": "https://github.com/slevomat/coding-standard/issues", - "source": "https://github.com/slevomat/coding-standard/tree/8.22.1" + "source": "https://github.com/slevomat/coding-standard/tree/8.25.1" }, "funding": [ { @@ -7794,30 +7914,30 @@ "type": "tidelift" } ], - "time": "2025-09-13T08:53:30+00:00" + "time": "2025-11-25T18:01:43+00:00" }, { "name": "squizlabs/php_codesniffer", - "version": "3.13.5", + "version": "4.0.1", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4" + "reference": "0525c73950de35ded110cffafb9892946d7771b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/0ca86845ce43291e8f5692c7356fccf3bcf02bf4", - "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/0525c73950de35ded110cffafb9892946d7771b5", + "reference": "0525c73950de35ded110cffafb9892946d7771b5", "shasum": "" }, "require": { "ext-simplexml": "*", "ext-tokenizer": "*", "ext-xmlwriter": "*", - "php": ">=5.4.0" + "php": ">=7.2.0" }, "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" + "phpunit/phpunit": "^8.4.0 || ^9.3.4 || ^10.5.32 || 11.3.3 - 11.5.28 || ^11.5.31" }, "bin": [ "bin/phpcbf", @@ -7842,7 +7962,7 @@ "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors" } ], - "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "description": "PHP_CodeSniffer tokenizes PHP files and detects violations of a defined set of coding standards.", "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer", "keywords": [ "phpcs", @@ -7873,35 +7993,35 @@ "type": "thanks_dev" } ], - "time": "2025-11-04T16:30:35+00:00" + "time": "2025-11-10T16:43:36+00:00" }, { "name": "symfony/var-dumper", - "version": "v7.2.3", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "82b478c69745d8878eb60f9a049a4d584996f73a" + "reference": "41fd6c4ae28c38b294b42af6db61446594a0dece" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/82b478c69745d8878eb60f9a049a4d584996f73a", - "reference": "82b478c69745d8878eb60f9a049a4d584996f73a", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/41fd6c4ae28c38b294b42af6db61446594a0dece", + "reference": "41fd6c4ae28c38b294b42af6db61446594a0dece", "shasum": "" }, "require": { "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0" }, "conflict": { "symfony/console": "<6.4" }, "require-dev": { - "ext-iconv": "*", - "symfony/console": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/uid": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/uid": "^6.4|^7.0|^8.0", "twig/twig": "^3.12" }, "bin": [ @@ -7940,7 +8060,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.2.3" + "source": "https://github.com/symfony/var-dumper/tree/v7.4.0" }, "funding": [ { @@ -7951,25 +8071,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-01-17T11:39:41+00:00" + "time": "2025-10-27T20:36:44+00:00" }, { "name": "theseer/tokenizer", - "version": "1.2.3", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", "shasum": "" }, "require": { @@ -7998,7 +8122,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + "source": "https://github.com/theseer/tokenizer/tree/1.3.1" }, "funding": [ { @@ -8006,14 +8130,13 @@ "type": "github" } ], - "time": "2024-03-03T12:36:25+00:00" + "time": "2025-11-17T20:03:58+00:00" } ], "aliases": [], "minimum-stability": "stable", "stability-flags": { - "erusev/parsedown": 20, - "rcrowe/twigbridge": 20 + "erusev/parsedown": 20 }, "prefer-stable": false, "prefer-lowest": false, @@ -8027,5 +8150,5 @@ "ext-xml": "*" }, "platform-dev": {}, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.9.0" } diff --git a/tests/Unit/Helpers/GoodieTest.php b/tests/Unit/Helpers/GoodieTest.php index 06ab809ee..d0615e448 100644 --- a/tests/Unit/Helpers/GoodieTest.php +++ b/tests/Unit/Helpers/GoodieTest.php @@ -10,7 +10,9 @@ use Engelsystem\Models\Worklog; use Engelsystem\Test\Unit\HasDatabase; use Engelsystem\Test\Unit\TestCase; +use Illuminate\Database\Connection; use Illuminate\Database\Query\Grammars\SQLiteGrammar; +use PDO; class GoodieTest extends TestCase { @@ -23,7 +25,7 @@ public function testShiftScoreQuery(): void { $result = Goodie::shiftScoreQuery(); - $this->assertEquals('0', $result->getValue(new SQLiteGrammar())); + $this->assertEquals('0', $result->getValue(new SQLiteGrammar(new Connection(new PDO('sqlite::memory:'))))); } /** From 2d7581de22a3d25e7acaa023dec0c8048d86742a Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sun, 30 Nov 2025 16:34:53 +0100 Subject: [PATCH 141/157] Request: Deprecate using input/has/get --- src/Http/Request.php | 26 ++++++++++++++++++++++++++ tests/Unit/Http/RequestTest.php | 25 +++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/src/Http/Request.php b/src/Http/Request.php index a858e9c80..cf28cbf50 100644 --- a/src/Http/Request.php +++ b/src/Http/Request.php @@ -26,6 +26,8 @@ public function postData(string $key, mixed $default = null): mixed /** * Get input data + * + * @deprecated */ public function input(string $key, mixed $default = null): mixed { @@ -34,6 +36,8 @@ public function input(string $key, mixed $default = null): mixed /** * Checks if the input exists + * + * @deprecated */ public function has(string $key): bool { @@ -42,6 +46,28 @@ public function has(string $key): bool return !is_null($value); } + /** + * Get input from any request part (attributes, GET or POST) + * + * @deprecated + */ + public function get(string $key, mixed $default = null): mixed + { + if ($this !== $result = $this->attributes->get($key, $this)) { + return $result; + } + + if ($this->query->has($key)) { + return $this->query->all()[$key]; + } + + if ($this->request->has($key)) { + return $this->request->all()[$key]; + } + + return $default; + } + /** * Checks if the POST data exists */ diff --git a/tests/Unit/Http/RequestTest.php b/tests/Unit/Http/RequestTest.php index 2af4bf33f..82f51b83b 100644 --- a/tests/Unit/Http/RequestTest.php +++ b/tests/Unit/Http/RequestTest.php @@ -69,6 +69,31 @@ public function testHas(): void $this->assertFalse($request->has('baz')); } + /** + * @covers \Engelsystem\Http\Request::get + */ + public function testGet(): void + { + $request = new Request([ + // Query / GET + 'a' => 'From query', + 'g' => 'From query', + ], [ + // Request / POST + 'a' => 'From request', + 'g' => 'From request', + 'p' => 'From request', + ], [ + // Attributes + 'a' => 'From attributes', + ]); + + $this->assertEquals('From attributes', $request->get('a')); + $this->assertEquals('From query', $request->get('g')); + $this->assertEquals('From request', $request->get('p')); + $this->assertEquals('default value', $request->get('not-existing', 'default value')); + } + /** * @covers \Engelsystem\Http\Request::hasPostData */ From f2efbaf1ac9ffee51d150b54d3ddbcb1db461ec3 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sun, 30 Nov 2025 16:53:55 +0100 Subject: [PATCH 142/157] Fix phpstan --- phpstan.neon.dist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpstan.neon.dist b/phpstan.neon.dist index b9fd6afe0..3fe5fde95 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -11,5 +11,5 @@ parameters: ignoreErrors: - message: '#.*#' - path: config/config.php + path: config/config.php* reportUnmatched: false From 948a84b30750494dbcd63eff12e101773ff3e513 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sun, 2 Nov 2025 13:29:17 +0100 Subject: [PATCH 143/157] Add PDO to container --- src/Database/DatabaseServiceProvider.php | 2 ++ tests/Unit/Database/DatabaseServiceProviderTest.php | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Database/DatabaseServiceProvider.php b/src/Database/DatabaseServiceProvider.php index d6b68d9e9..c5d60adf9 100644 --- a/src/Database/DatabaseServiceProvider.php +++ b/src/Database/DatabaseServiceProvider.php @@ -10,6 +10,7 @@ use Exception; use Illuminate\Database\Capsule\Manager as CapsuleManager; use Illuminate\Database\Connection as DatabaseConnection; +use PDO; use PDOException; use Throwable; @@ -47,6 +48,7 @@ public function register(): void $this->exitOnError($e); } + $this->app->instance(PDO::class, $pdo); $this->app->instance(CapsuleManager::class, $capsule); $this->app->instance(Db::class, $capsule); Db::setDbManager($capsule); diff --git a/tests/Unit/Database/DatabaseServiceProviderTest.php b/tests/Unit/Database/DatabaseServiceProviderTest.php index 15d8b62b4..6100b56f2 100644 --- a/tests/Unit/Database/DatabaseServiceProviderTest.php +++ b/tests/Unit/Database/DatabaseServiceProviderTest.php @@ -36,9 +36,10 @@ public function testRegister(): void ] ); - $app->expects($this->exactly(7)) + $app->expects($this->exactly(8)) ->method('instance') ->withConsecutive( + [PDO::class, $pdo], [CapsuleManager::class, $dbManager], [Db::class, $dbManager], [Connection::class, $connection], From ce8fd6adfaa2be64e0efa08db2672ef25d69f1bc Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sun, 2 Nov 2025 20:23:01 +0100 Subject: [PATCH 144/157] Confirm user when adding as supporter --- includes/controller/user_angeltypes_controller.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/includes/controller/user_angeltypes_controller.php b/includes/controller/user_angeltypes_controller.php index acfa80fa6..7d5791b79 100644 --- a/includes/controller/user_angeltypes_controller.php +++ b/includes/controller/user_angeltypes_controller.php @@ -286,6 +286,14 @@ function user_angeltype_update_controller(): array if ($request->hasPostData('submit')) { $user_angeltype->supporter = $supporter; + if ($supporter && !$user_angeltype->confirm_user_id) { + $user_angeltype->confirmUser()->associate(auth()->user()); + engelsystem_log(sprintf( + '%s confirmed for angel type %s', + User_Nick_render($user_source, true), + AngelType_name_render($angeltype, true) + )); + } $user_angeltype->save(); $msg = $supporter From c1459ae88276f9a26e80a00014a5d16bb66c29c5 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sun, 2 Nov 2025 20:58:37 +0100 Subject: [PATCH 145/157] Prevent ShiCos from removing the Angel group, only allow for Developers --- includes/pages/admin_user.php | 17 +++++++++++++++++ resources/lang/de_DE/default.po | 6 ++++++ 2 files changed, 23 insertions(+) diff --git a/includes/pages/admin_user.php b/includes/pages/admin_user.php index b3f85a691..ec6a02e3a 100644 --- a/includes/pages/admin_user.php +++ b/includes/pages/admin_user.php @@ -242,6 +242,7 @@ function admin_user() } else { switch ($request->input('action')) { case 'save_groups': + /** @var User $angel */ $angel = User::findOrFail($user_id); if ($angel->id != $user->id || auth()->can('admin_groups')) { /** @var Group $my_highest_group */ @@ -269,6 +270,22 @@ function admin_user() $groupsRequest = []; } + $defaultGroup = auth()->getDefaultRole(); + if ( + !in_array($defaultGroup, $groupsRequest) + && $angel->groups->where('id', $defaultGroup)->count() + ) { + if (!auth()->can('admin_groups') && !config('default_group_removable')) { + $html .= error(__('You cannot remove the default group.'), true); + break; + } else { + $html .= warning( + __('You removed the default group, this has unintended side effects!'), + true + ); + } + } + $angel->groups()->detach(); $user_groups_info = []; foreach ($groupsRequest as $group) { diff --git a/resources/lang/de_DE/default.po b/resources/lang/de_DE/default.po index c1317e7ae..64418b4bc 100644 --- a/resources/lang/de_DE/default.po +++ b/resources/lang/de_DE/default.po @@ -1223,6 +1223,12 @@ msgstr "Hier kannst du das Passwort für diesen Engel zurücksetzen:" msgid "Here you can define the user groups of the angel:" msgstr "Hier kannst du die Benutzergruppen des Engels definieren:" +msgid "You cannot remove the default group." +msgstr "Du kannst die Standard-Benutzergruppe nicht entfernen." + +msgid "You removed the default group, this has unintended side effects!" +msgstr "Du hast die Standard-Benutzergruppe entfernt, dies führt zu unerwarteten Nebeneffekten!" + msgid "User groups saved." msgstr "Benutzergruppen gespeichert." From 5afc4f4fa1d6a5a3cba3827a1d0764353ecbd58a Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sun, 2 Nov 2025 22:54:05 +0100 Subject: [PATCH 146/157] Log more info on OAuth registration & question deletion --- src/Controllers/Admin/QuestionsController.php | 29 +++++++++++++++---- src/Factories/User.php | 9 ++++++ 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/Controllers/Admin/QuestionsController.php b/src/Controllers/Admin/QuestionsController.php index d07b905f0..56f77afd2 100644 --- a/src/Controllers/Admin/QuestionsController.php +++ b/src/Controllers/Admin/QuestionsController.php @@ -57,7 +57,15 @@ public function delete(Request $request): Response $question = $this->question->findOrFail($data['id']); $question->delete(); - $this->log->info('Deleted question {question} ({id})', ['question' => $question->text, 'id' => $question->id]); + $this->log->info( + 'Deleted question "{text}" ({id}) by {user} ({user_id})', + [ + 'text' => $question->text, + 'id' => $question->id, + 'user' => $question->user->name, + 'user_id' => $question->user->id, + ] + ); $this->addNotification('question.delete.success'); return $this->redirect->to('/admin/questions'); @@ -90,8 +98,13 @@ public function save(Request $request): Response $question->delete(); $this->log->info( - 'Deleted question {question} ({id})', - ['question' => $question->text, 'id' => $question->id] + 'Deleted question "{text}" ({id}) by {user} ({user_id})', + [ + 'text' => $question->text, + 'id' => $question->id, + 'user' => $question->user->name, + 'user_id' => $question->user->id, + ] ); $this->addNotification('question.delete.success'); @@ -111,8 +124,14 @@ public function save(Request $request): Response $question->save(); $this->log->info( - 'Saved questions "{text}" ({id}): {answer}', - ['text' => $question->text, 'answer' => $question->answer, 'id' => $question->id] + 'Saved questions "{text}" ({id}) by {user} ({user_id}): {answer}', + [ + 'text' => $question->text, + 'answer' => $question->answer, + 'id' => $question->id, + 'user' => $question->user->name, + 'user_id' => $question->user->id, + ] ); $this->addNotification('question.edit.success'); diff --git a/src/Factories/User.php b/src/Factories/User.php index 1b0b5075a..33ca9c0eb 100644 --- a/src/Factories/User.php +++ b/src/Factories/User.php @@ -300,6 +300,15 @@ private function createUser(array $data, array $rawData): EngelsystemUser $this->session->remove('oauth2_access_token'); $this->session->remove('oauth2_refresh_token'); $this->session->remove('oauth2_expires_at'); + + $this->logger->info( + '{user} connected OAuth user {oauth_user} using {provider}', + [ + 'provider' => $oauth->provider, + 'user' => sprintf('%s (%u)', $user->displayName, $user->id), + 'oauth_user' => $oauth->identifier, + ] + ); } $defaultGroup = Group::find($this->authenticator->getDefaultRole()); From c3b062443130d6c63600ded4b9946ef01e48020a Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Tue, 28 Oct 2025 16:31:57 +0100 Subject: [PATCH 147/157] Use league/commonmark for rendering markdown --- .gitlab-ci.yml | 2 +- composer.json | 2 +- composer.lock | 474 ++++++++++++++++-- includes/view/AngelTypes_view.php | 4 +- includes/view/Locations_view.php | 4 +- includes/view/Shifts_view.php | 7 +- resources/views/pages/design.twig | 4 + src/Helpers/Markdown.php | 53 ++ src/Renderer/Twig/Extensions/Markdown.php | 7 +- tests/Unit/Helpers/MarkdownTest.php | 88 ++++ .../Renderer/Twig/Extensions/MarkdownTest.php | 9 +- 11 files changed, 583 insertions(+), 71 deletions(-) create mode 100644 src/Helpers/Markdown.php create mode 100644 tests/Unit/Helpers/MarkdownTest.php diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e97fd3d4e..7fa4cbf8d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -52,7 +52,7 @@ composer validate: image: composer:latest stage: prepare script: - - composer --no-ansi validate + - composer --no-ansi validate --strict composer install: <<: *use_cache diff --git a/composer.json b/composer.json index 236796369..544d7bf6a 100644 --- a/composer.json +++ b/composer.json @@ -36,7 +36,6 @@ "ext-simplexml": "*", "ext-xml": "*", "bacon/bacon-qr-code": "^3.0", - "erusev/parsedown": "1.7.x-dev#f7285e7b2c55039401e9d380741c2dc805edf980", "firebase/php-jwt": "^6.11", "gettext/gettext": "^5.7", "gettext/translator": "^1.2", @@ -45,6 +44,7 @@ "illuminate/database": "^12.39", "illuminate/support": "^12.39", "laravel/serializable-closure": "^2.0", + "league/commonmark": "^2.7", "league/oauth2-client": "^2.7", "league/openapi-psr7-validator": "^0.22.0", "nikic/fast-route": "^1.3", diff --git a/composer.lock b/composer.lock index 62d1f40d5..a8d5d8fe6 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "28779c72bc795591fbc99e56a7401fe9", + "content-hash": "d56d11e5c331bf13196eb1e3c9f36647", "packages": [ { "name": "bacon/bacon-qr-code", @@ -316,6 +316,81 @@ }, "time": "2025-01-16T11:12:34+00:00" }, + { + "name": "dflydev/dot-access-data", + "version": "v3.0.3", + "source": { + "type": "git", + "url": "https://github.com/dflydev/dflydev-dot-access-data.git", + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/a23a2bf4f31d3518f3ecb38660c95715dfead60f", + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.42", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.3", + "scrutinizer/ocular": "1.6.0", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "^4.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Dflydev\\DotAccessData\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dragonfly Development Inc.", + "email": "info@dflydev.com", + "homepage": "http://dflydev.com" + }, + { + "name": "Beau Simensen", + "email": "beau@dflydev.com", + "homepage": "http://beausimensen.com" + }, + { + "name": "Carlos Frutos", + "email": "carlos@kiwing.it", + "homepage": "https://github.com/cfrutos" + }, + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com" + } + ], + "description": "Given a deep data structure, access data by dot notation.", + "homepage": "https://github.com/dflydev/dflydev-dot-access-data", + "keywords": [ + "access", + "data", + "dot", + "notation" + ], + "support": { + "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues", + "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.3" + }, + "time": "2024-07-08T12:26:09+00:00" + }, { "name": "doctrine/inflector", "version": "2.1.0", @@ -550,56 +625,6 @@ ], "time": "2025-03-06T22:45:56+00:00" }, - { - "name": "erusev/parsedown", - "version": "1.7.x-dev", - "source": { - "type": "git", - "url": "https://github.com/erusev/parsedown.git", - "reference": "f7285e7b2c55039401e9d380741c2dc805edf980" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/erusev/parsedown/zipball/f7285e7b2c55039401e9d380741c2dc805edf980", - "reference": "f7285e7b2c55039401e9d380741c2dc805edf980", - "shasum": "" - }, - "require": { - "ext-mbstring": "*", - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8|^5.7|^6.5|^7.5|^8.5|^9.6" - }, - "type": "library", - "autoload": { - "psr-0": { - "Parsedown": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Emanuil Rusev", - "email": "hello@erusev.com", - "homepage": "http://erusev.com" - } - ], - "description": "Parser for Markdown.", - "homepage": "http://parsedown.org", - "keywords": [ - "markdown", - "parser" - ], - "support": { - "issues": "https://github.com/erusev/parsedown/issues", - "source": "https://github.com/erusev/parsedown/tree/1.7.x" - }, - "time": "2024-07-12T14:59:16+00:00" - }, { "name": "firebase/php-jwt", "version": "v6.11.1", @@ -2100,6 +2125,195 @@ }, "time": "2025-11-21T20:52:36+00:00" }, + { + "name": "league/commonmark", + "version": "2.8.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/commonmark.git", + "reference": "4efa10c1e56488e658d10adf7b7b7dcd19940bfb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/4efa10c1e56488e658d10adf7b7b7dcd19940bfb", + "reference": "4efa10c1e56488e658d10adf7b7b7dcd19940bfb", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "league/config": "^1.1.1", + "php": "^7.4 || ^8.0", + "psr/event-dispatcher": "^1.0", + "symfony/deprecation-contracts": "^2.1 || ^3.0", + "symfony/polyfill-php80": "^1.16" + }, + "require-dev": { + "cebe/markdown": "^1.0", + "commonmark/cmark": "0.31.1", + "commonmark/commonmark.js": "0.31.1", + "composer/package-versions-deprecated": "^1.8", + "embed/embed": "^4.4", + "erusev/parsedown": "^1.0", + "ext-json": "*", + "github/gfm": "0.29.0", + "michelf/php-markdown": "^1.4 || ^2.0", + "nyholm/psr7": "^1.5", + "phpstan/phpstan": "^1.8.2", + "phpunit/phpunit": "^9.5.21 || ^10.5.9 || ^11.0.0", + "scrutinizer/ocular": "^1.8.1", + "symfony/finder": "^5.3 | ^6.0 | ^7.0", + "symfony/process": "^5.4 | ^6.0 | ^7.0", + "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0", + "unleashedtech/php-coding-standard": "^3.1.1", + "vimeo/psalm": "^4.24.0 || ^5.0.0 || ^6.0.0" + }, + "suggest": { + "symfony/yaml": "v2.3+ required if using the Front Matter extension" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.9-dev" + } + }, + "autoload": { + "psr-4": { + "League\\CommonMark\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + } + ], + "description": "Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and GitHub-Flavored Markdown (GFM)", + "homepage": "https://commonmark.thephpleague.com", + "keywords": [ + "commonmark", + "flavored", + "gfm", + "github", + "github-flavored", + "markdown", + "md", + "parser" + ], + "support": { + "docs": "https://commonmark.thephpleague.com/", + "forum": "https://github.com/thephpleague/commonmark/discussions", + "issues": "https://github.com/thephpleague/commonmark/issues", + "rss": "https://github.com/thephpleague/commonmark/releases.atom", + "source": "https://github.com/thephpleague/commonmark" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/commonmark", + "type": "tidelift" + } + ], + "time": "2025-11-26T21:48:24+00:00" + }, + { + "name": "league/config", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/config.git", + "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/config/zipball/754b3604fb2984c71f4af4a9cbe7b57f346ec1f3", + "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3", + "shasum": "" + }, + "require": { + "dflydev/dot-access-data": "^3.0.1", + "nette/schema": "^1.2", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.8.2", + "phpunit/phpunit": "^9.5.5", + "scrutinizer/ocular": "^1.8.1", + "unleashedtech/php-coding-standard": "^3.1", + "vimeo/psalm": "^4.7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Config\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + } + ], + "description": "Define configuration arrays with strict schemas and access values with dot notation", + "homepage": "https://config.thephpleague.com", + "keywords": [ + "array", + "config", + "configuration", + "dot", + "dot-access", + "nested", + "schema" + ], + "support": { + "docs": "https://config.thephpleague.com/", + "issues": "https://github.com/thephpleague/config/issues", + "rss": "https://github.com/thephpleague/config/releases.atom", + "source": "https://github.com/thephpleague/config" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + } + ], + "time": "2022-12-11T20:36:23+00:00" + }, { "name": "league/oauth2-client", "version": "2.9.0", @@ -2587,6 +2801,160 @@ ], "time": "2025-09-06T13:39:36+00:00" }, + { + "name": "nette/schema", + "version": "v1.3.3", + "source": { + "type": "git", + "url": "https://github.com/nette/schema.git", + "reference": "2befc2f42d7c715fd9d95efc31b1081e5d765004" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/schema/zipball/2befc2f42d7c715fd9d95efc31b1081e5d765004", + "reference": "2befc2f42d7c715fd9d95efc31b1081e5d765004", + "shasum": "" + }, + "require": { + "nette/utils": "^4.0", + "php": "8.1 - 8.5" + }, + "require-dev": { + "nette/tester": "^2.5.2", + "phpstan/phpstan-nette": "^2.0@stable", + "tracy/tracy": "^2.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Nette\\": "src" + }, + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "📐 Nette Schema: validating data structures against a given Schema.", + "homepage": "https://nette.org", + "keywords": [ + "config", + "nette" + ], + "support": { + "issues": "https://github.com/nette/schema/issues", + "source": "https://github.com/nette/schema/tree/v1.3.3" + }, + "time": "2025-10-30T22:57:59+00:00" + }, + { + "name": "nette/utils", + "version": "v4.1.0", + "source": { + "type": "git", + "url": "https://github.com/nette/utils.git", + "reference": "fa1f0b8261ed150447979eb22e373b7b7ad5a8e0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/utils/zipball/fa1f0b8261ed150447979eb22e373b7b7ad5a8e0", + "reference": "fa1f0b8261ed150447979eb22e373b7b7ad5a8e0", + "shasum": "" + }, + "require": { + "php": "8.2 - 8.5" + }, + "conflict": { + "nette/finder": "<3", + "nette/schema": "<1.2.2" + }, + "require-dev": { + "jetbrains/phpstorm-attributes": "^1.2", + "nette/tester": "^2.5", + "phpstan/phpstan-nette": "^2.0@stable", + "tracy/tracy": "^2.9" + }, + "suggest": { + "ext-gd": "to use Image", + "ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()", + "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()", + "ext-json": "to use Nette\\Utils\\Json", + "ext-mbstring": "to use Strings::lower() etc...", + "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "autoload": { + "psr-4": { + "Nette\\": "src" + }, + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.", + "homepage": "https://nette.org", + "keywords": [ + "array", + "core", + "datetime", + "images", + "json", + "nette", + "paginator", + "password", + "slugify", + "string", + "unicode", + "utf-8", + "utility", + "validation" + ], + "support": { + "issues": "https://github.com/nette/utils/issues", + "source": "https://github.com/nette/utils/tree/v4.1.0" + }, + "time": "2025-12-01T17:49:23+00:00" + }, { "name": "nikic/fast-route", "version": "v1.3.0", @@ -8135,9 +8503,7 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": { - "erusev/parsedown": 20 - }, + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { diff --git a/includes/view/AngelTypes_view.php b/includes/view/AngelTypes_view.php index 6823c3168..843c7f708 100644 --- a/includes/view/AngelTypes_view.php +++ b/includes/view/AngelTypes_view.php @@ -1,6 +1,7 @@ ' . __('general.description') . ''; - $parsedown = new Parsedown(); if ($angeltype->description != '') { - $info[] = $parsedown->parse(htmlspecialchars($angeltype->description)); + $info[] = (new Markdown())->render($angeltype->description); } if ($angeltype->requires_ifsg_certificate && $required_info_show) { $info[] = info(__('angeltype.ifsg.required.info.preview'), true); diff --git a/includes/view/Locations_view.php b/includes/view/Locations_view.php index 6e646b343..f88fdb571 100644 --- a/includes/view/Locations_view.php +++ b/includes/view/Locations_view.php @@ -1,5 +1,6 @@ description) { $description = '

    ' . __('general.description') . '

    '; - $parsedown = new Parsedown(); - $description .= $parsedown->parse(htmlspecialchars($location->description)); + $description .= (new Markdown())->render($location->description); } $neededAngelTypes = ''; diff --git a/includes/view/Shifts_view.php b/includes/view/Shifts_view.php index 86e9e6729..11c5f925b 100644 --- a/includes/view/Shifts_view.php +++ b/includes/view/Shifts_view.php @@ -2,6 +2,7 @@ use Engelsystem\Config\GoodieType; use Engelsystem\Helpers\Carbon; +use Engelsystem\Helpers\Markdown; use Engelsystem\Models\AngelType; use Engelsystem\Models\Location; use Engelsystem\Models\Shifts\Shift; @@ -167,7 +168,7 @@ function Shift_view( ->where('supporter', true) ->pluck('angel_types.id'); - $parsedown = new Parsedown(); + $mdRenderer = new Markdown(); $angeltypes = []; foreach ($angeltypes_source as $angeltype) { @@ -262,8 +263,8 @@ function Shift_view( ]), div('col-sm-6', [ '

    ' . __('general.description') . '

    ', - $parsedown->parse(htmlspecialchars($shifttype->description)), - $parsedown->parse(htmlspecialchars($shift->description)), + $mdRenderer->render($shifttype->description), + $mdRenderer->render($shift->description), ]), ]); diff --git a/resources/views/pages/design.twig b/resources/views/pages/design.twig index 1f5ce1849..c8faa1418 100644 --- a/resources/views/pages/design.twig +++ b/resources/views/pages/design.twig @@ -140,6 +140,10 @@
    {% endfor %}
    + + +

    Markdown {{ m.icon('link') }}

    + {{ 'In _texts_ you can use **Markdown**! (Can be [GitHub Flavoured](https://commonmark.thephpleague.com/2.x/extensions/github-flavored-markdown/) with [Attributes](https://commonmark.thephpleague.com/2.x/extensions/attributes/)).' | md }}
    diff --git a/src/Helpers/Markdown.php b/src/Helpers/Markdown.php new file mode 100644 index 000000000..f00b92130 --- /dev/null +++ b/src/Helpers/Markdown.php @@ -0,0 +1,53 @@ +getRenderer($allowHtml); + $content = $renderer->convert($text) + ->getContent(); + return rtrim($content, PHP_EOL); + } + + protected function getRenderer(bool $allowHtml): MarkdownConverter + { + $config = [ + 'html_input' => $allowHtml ? HtmlFilter::ALLOW : HtmlFilter::ESCAPE, + 'allow_unsafe_links' => false, + 'max_nesting_level' => 42, + 'max_delimiters_per_line' => 42, + 'default_attributes' => [ + Table::class => [ + 'class' => ['table', 'table-striped', 'table-sticky-header', 'data'], + ], + ], + 'table' => [ + 'alignment_attributes' => [ + 'left' => ['class' => 'text-start'], + 'center' => ['class' => 'text-center'], + 'right' => ['class' => 'text-end'], + ], + ], + ]; + + $environment = new Environment($config); + $environment->addExtension(new CommonMarkCoreExtension()); + $environment->addExtension(new GithubFlavoredMarkdownExtension()); + $environment->addExtension(new DefaultAttributesExtension()); + + return new MarkdownConverter($environment); + } +} diff --git a/src/Renderer/Twig/Extensions/Markdown.php b/src/Renderer/Twig/Extensions/Markdown.php index 9e06c9ef5..897cf3b31 100644 --- a/src/Renderer/Twig/Extensions/Markdown.php +++ b/src/Renderer/Twig/Extensions/Markdown.php @@ -4,13 +4,13 @@ namespace Engelsystem\Renderer\Twig\Extensions; -use Parsedown; +use Engelsystem\Helpers\Markdown as MarkdownRenderer; use Twig\Extension\AbstractExtension as TwigExtension; use Twig\TwigFilter; class Markdown extends TwigExtension { - public function __construct(protected Parsedown $renderer) + public function __construct(protected MarkdownRenderer $renderer) { } @@ -26,6 +26,7 @@ public function getFilters(): array public function render(string $text, bool $escapeHtml = true): string { - return $this->renderer->setSafeMode($escapeHtml)->text($text); + return $this->renderer + ->render($text, !$escapeHtml); } } diff --git a/tests/Unit/Helpers/MarkdownTest.php b/tests/Unit/Helpers/MarkdownTest.php new file mode 100644 index 000000000..553417353 --- /dev/null +++ b/tests/Unit/Helpers/MarkdownTest.php @@ -0,0 +1,88 @@ +test', '

    test

    '], + ['bold', '

    <b>bold</b>

    ', '

    bold

    '], + [ + '* test', + '
      ' . PHP_EOL . '
    • test
    • ' . PHP_EOL . '
    ', + '
      ' . PHP_EOL . '
    • test
    • ' . PHP_EOL . '
    ', + ], + ['
    ', '<div></div>', '
    '], + [ + '[Test](https://example.com)', + '

    Test

    ', + '

    Test

    ', + ], + ['[Test](javascript:alert("hi"))', '

    Test

    ', '

    Test

    '], + [ + '', + '<script>alert("ho")</script>', + '<script>alert("ho")</script>', // Looks kind of broken but thats fine + ], + [ + 'https://example.com/link', + '

    https://example.com/link

    ', + '

    https://example.com/link

    ', + ], + ]; + } + + /** + * @covers \Engelsystem\Helpers\Markdown::render + * @covers \Engelsystem\Helpers\Markdown::getRenderer + * @dataProvider mapping + */ + public function testRender(string $text, string $expected): void + { + $uuid = new Markdown(); + $result = $uuid->render($text); + + $this->assertEquals($expected, $result); + } + + /** + * @covers \Engelsystem\Helpers\Markdown::render + * @covers \Engelsystem\Helpers\Markdown::getRenderer + * @dataProvider mapping + */ + public function testRenderRaw(string $text, string $expected, string $withAllowHtml): void + { + $uuid = new Markdown(); + $result = $uuid->render($text, true); + + $this->assertEquals($withAllowHtml, $result); + } + + /** + * @covers \Engelsystem\Helpers\Markdown::render + * @covers \Engelsystem\Helpers\Markdown::getRenderer + */ + public function testRenderTable(): void + { + $text = '| test | value |' . PHP_EOL . '| --- | :--- |' . PHP_EOL . '| row | content |'; + + $uuid = new Markdown(); + $result = $uuid->render($text, true); + + $this->assertStringNotContainsString('|', $result); + $this->assertStringNotContainsString('---', $result); + $this->assertStringContainsString('', $result); + $this->assertStringContainsString('table-striped', $result); + $this->assertStringContainsString('test', $result); + $this->assertStringContainsString('text-start', $result); + $this->assertStringContainsString('content', $result); + } +} diff --git a/tests/Unit/Renderer/Twig/Extensions/MarkdownTest.php b/tests/Unit/Renderer/Twig/Extensions/MarkdownTest.php index 3208097b0..e69a985a5 100644 --- a/tests/Unit/Renderer/Twig/Extensions/MarkdownTest.php +++ b/tests/Unit/Renderer/Twig/Extensions/MarkdownTest.php @@ -4,8 +4,8 @@ namespace Engelsystem\Test\Unit\Renderer\Twig\Extensions; +use Engelsystem\Helpers\Markdown as MarkdownRenderer; use Engelsystem\Renderer\Twig\Extensions\Markdown; -use Parsedown; class MarkdownTest extends ExtensionTest { @@ -14,7 +14,7 @@ class MarkdownTest extends ExtensionTest */ public function testGetFilters(): void { - $extension = new Markdown(new Parsedown()); + $extension = new Markdown(new MarkdownRenderer()); $filters = $extension->getFilters(); $this->assertFilterExists('markdown', [$extension, 'render'], $filters); @@ -27,7 +27,7 @@ public function testGetFilters(): void */ public function testRender(): void { - $extension = new Markdown(new Parsedown()); + $extension = new Markdown(new MarkdownRenderer()); $this->assertEquals( '

    <i>Lorem</i> "Ipsum"

    ', @@ -40,8 +40,7 @@ public function testRender(): void */ public function testRenderHtml(): void { - $renderer = new Parsedown(); - $extension = new Markdown($renderer); + $extension = new Markdown(new MarkdownRenderer()); $this->assertEquals( '

    Lorem "Ipsum"

    ', From 10eda2df9a8f96e913820f4f663b006e60cfda1c Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sun, 2 Feb 2025 21:19:45 +0100 Subject: [PATCH 148/157] Add pagination to "All Users" view --- composer.json | 1 + composer.lock | 52 ++++++++++++++++++- config/app.php | 1 + config/config.default.php | 5 +- includes/controller/users_controller.php | 10 ++-- includes/sys_template.php | 36 +++++++++++++ includes/view/User_view.php | 9 +++- src/Http/PaginationServiceProvider.php | 16 ++++++ .../Http/PaginationServiceProviderTest.php | 29 +++++++++++ 9 files changed, 153 insertions(+), 6 deletions(-) create mode 100644 src/Http/PaginationServiceProvider.php create mode 100644 tests/Unit/Http/PaginationServiceProviderTest.php diff --git a/composer.json b/composer.json index 544d7bf6a..0a77a72c1 100644 --- a/composer.json +++ b/composer.json @@ -42,6 +42,7 @@ "guzzlehttp/guzzle": "^7.10", "illuminate/container": "^12.39", "illuminate/database": "^12.39", + "illuminate/pagination": "^12.39", "illuminate/support": "^12.39", "laravel/serializable-closure": "^2.0", "league/commonmark": "^2.7", diff --git a/composer.lock b/composer.lock index a8d5d8fe6..90a6db544 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d56d11e5c331bf13196eb1e3c9f36647", + "content-hash": "1b1bbcb0ba04a66eb133d7531fef1b18", "packages": [ { "name": "bacon/bacon-qr-code", @@ -1804,6 +1804,56 @@ }, "time": "2024-07-23T16:31:01+00:00" }, + { + "name": "illuminate/pagination", + "version": "v12.41.1", + "source": { + "type": "git", + "url": "https://github.com/illuminate/pagination.git", + "reference": "87e7e3e7b02d6809b1bcd41782e1ca2c6d2a413b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/pagination/zipball/87e7e3e7b02d6809b1bcd41782e1ca2c6d2a413b", + "reference": "87e7e3e7b02d6809b1bcd41782e1ca2c6d2a413b", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "illuminate/collections": "^12.0", + "illuminate/contracts": "^12.0", + "illuminate/support": "^12.0", + "php": "^8.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "12.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Pagination\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Pagination package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2025-11-16T14:36:17+00:00" + }, { "name": "illuminate/pipeline", "version": "v12.40.2", diff --git a/config/app.php b/config/app.php index 52f3b0053..08bae40f3 100644 --- a/config/app.php +++ b/config/app.php @@ -31,6 +31,7 @@ \Engelsystem\Middleware\RequestHandlerServiceProvider::class, \Engelsystem\Http\Validation\ValidationServiceProvider::class, \Engelsystem\Http\RedirectServiceProvider::class, + \Engelsystem\Http\PaginationServiceProvider::class, // Additional services \Engelsystem\Helpers\VersionServiceProvider::class, diff --git a/config/config.default.php b/config/config.default.php index af2a1676c..bec9d462c 100644 --- a/config/config.default.php +++ b/config/config.default.php @@ -278,9 +278,12 @@ // Must be one of news, meetings, user_shifts, angel types, questions 'home_site' => env('HOME_SITE', 'news'), - // Number of News shown on one site and for feed readers (minimum 1) + // Number of news shown on one site and for feed readers (minimum 1) 'display_news' => env('DISPLAY_NEWS', 10), + // Number of users shown on one admin page table + 'display_users' => env('DISPLAY_USERS', 100), + // Users are able to sign up 'registration_enabled' => (bool) env('REGISTRATION_ENABLED', true), diff --git a/includes/controller/users_controller.php b/includes/controller/users_controller.php index c5c482309..a5a494de0 100644 --- a/includes/controller/users_controller.php +++ b/includes/controller/users_controller.php @@ -10,6 +10,7 @@ use Engelsystem\ShiftsFilter; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Str; /** @@ -267,12 +268,14 @@ function users_list_controller() $order_by = $request->input('OrderBy'); } - /** @var User[]|Collection $users */ + $perPage = $request->get('page') == 'all' ? PHP_INT_MAX : config('display_users'); + + /** @var User[]|Collection|LengthAwarePaginator $users */ $users = User::with(['contact', 'personalData', 'state', 'shiftEntries' => function (HasMany $query) { $query->whereNotNull('freeloaded_by'); }]) ->orderBy('name') - ->get(); + ->paginate($perPage); foreach ($users as $user) { $user->setAttribute( 'freeloads', @@ -282,7 +285,7 @@ function users_list_controller() ); } - $users = $users->sortBy(function (User $user) use ($order_by) { + $sortedUsers = $users->sortBy(function (User $user) use ($order_by) { $userData = $user->toArray(); $data = []; array_walk_recursive($userData, function ($value, $key) use (&$data) { @@ -291,6 +294,7 @@ function users_list_controller() return isset($data[$order_by]) ? Str::lower($data[$order_by]) : null; }); + $users->setCollection($sortedUsers); return [ __('All users'), diff --git a/includes/sys_template.php b/includes/sys_template.php index 0287e0bde..c9ec772b6 100644 --- a/includes/sys_template.php +++ b/includes/sys_template.php @@ -1,6 +1,7 @@ '; return $infoIcon; } + +function pagination(LengthAwarePaginator $paginator, bool $withAll = false): string +{ + $items = ''; + foreach ($paginator->getUrlRange(1, $paginator->lastPage()) as $page => $url) { + $active = ''; + + if ($paginator->currentPage() == $page) { + $active = ' active'; + } + + $items .= sprintf( + '
  • %u
  • ', + $active, + $url, + $page + ); + } + + if ($withAll) { + $items .= sprintf( + '
  • %s
  • ', + $paginator->url('all'), + __('All'), + ); + } + + return sprintf(' + + ', $items); +} diff --git a/includes/view/User_view.php b/includes/view/User_view.php index de6f7f37b..099815067 100644 --- a/includes/view/User_view.php +++ b/includes/view/User_view.php @@ -10,6 +10,7 @@ use Engelsystem\Models\User\PasswordReset; use Engelsystem\Models\User\User; use Engelsystem\Models\Worklog; +use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; use Illuminate\Support\Str; @@ -36,7 +37,7 @@ function User_delete_view($user) } /** - * @param User[] $users + * @param User[]|LengthAwarePaginator $users * @param string $order_by * @param int $arrived_count * @param int $active_count @@ -208,10 +209,16 @@ class="btn btn-sm btn-secondary ms-2 js-only" unset($user_table_headers[$key]); } + $pagination = ''; + if ($users instanceof LengthAwarePaginator && $users->total() > $users->perPage()) { + $pagination = pagination($users, true); + } $link = button(url('/register'), icon('plus-lg'), 'btn-sm add'); return page_with_title(__('All users') . ' ' . $link, [ msg(), + $pagination, table($user_table_headers, $usersList), + $pagination, ]); } diff --git a/src/Http/PaginationServiceProvider.php b/src/Http/PaginationServiceProvider.php new file mode 100644 index 000000000..8bd353192 --- /dev/null +++ b/src/Http/PaginationServiceProvider.php @@ -0,0 +1,16 @@ +app); + } +} diff --git a/tests/Unit/Http/PaginationServiceProviderTest.php b/tests/Unit/Http/PaginationServiceProviderTest.php new file mode 100644 index 000000000..a5d839170 --- /dev/null +++ b/tests/Unit/Http/PaginationServiceProviderTest.php @@ -0,0 +1,29 @@ +register(); + + $app->instance('request', new Request()); + + $this->assertTrue(Paginator::resolveCurrentPath('/default') != '/default'); + } +} From b1a2f18bc90e7714c1f547509dac16b962e82b0f Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Fri, 28 Nov 2025 21:20:55 +0100 Subject: [PATCH 149/157] Allow for pagination selection --- includes/controller/users_controller.php | 6 +++- includes/sys_template.php | 36 +++++++++++++++++++++--- includes/view/User_view.php | 4 +-- 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/includes/controller/users_controller.php b/includes/controller/users_controller.php index a5a494de0..90a87ebe1 100644 --- a/includes/controller/users_controller.php +++ b/includes/controller/users_controller.php @@ -268,7 +268,11 @@ function users_list_controller() $order_by = $request->input('OrderBy'); } - $perPage = $request->get('page') == 'all' ? PHP_INT_MAX : config('display_users'); + $perPage = $request->get('c', config('display_users')); + if ($perPage == 'all') { + $perPage = PHP_INT_MAX; + } + $perPage = is_numeric($perPage) ? (int) $perPage : config('display_users'); /** @var User[]|Collection|LengthAwarePaginator $users */ $users = User::with(['contact', 'personalData', 'state', 'shiftEntries' => function (HasMany $query) { diff --git a/includes/sys_template.php b/includes/sys_template.php index c9ec772b6..9848dd6e2 100644 --- a/includes/sys_template.php +++ b/includes/sys_template.php @@ -432,8 +432,10 @@ function user_info_icon(User $user): string return $infoIcon; } -function pagination(LengthAwarePaginator $paginator, bool $withAll = false): string +function pagination(LengthAwarePaginator $paginator, ?int $selectionSteps = null): string { + $paginator->appends(request()->getQueryParams()); + $items = ''; foreach ($paginator->getUrlRange(1, $paginator->lastPage()) as $page => $url) { $active = ''; @@ -450,10 +452,36 @@ function pagination(LengthAwarePaginator $paginator, bool $withAll = false): str ); } - if ($withAll) { + if ($selectionSteps) { + $selections = []; + foreach ([$selectionSteps, $selectionSteps * 5, $selectionSteps * 10] as $selection) { + $url = $paginator->appends('c', $selection)->url(1); + $selections[] = sprintf( + '
  • %s
  • ', + $url, + $selection, + ); + } $items .= sprintf( - '
  • %s
  • ', - $paginator->url('all'), + ' +
  • + +
  • + ', + __('Per page'), + implode(PHP_EOL, $selections), + $paginator->appends('c', 'all')->url(1), __('All'), ); } diff --git a/includes/view/User_view.php b/includes/view/User_view.php index 099815067..b5864f690 100644 --- a/includes/view/User_view.php +++ b/includes/view/User_view.php @@ -210,8 +210,8 @@ class="btn btn-sm btn-secondary ms-2 js-only" } $pagination = ''; - if ($users instanceof LengthAwarePaginator && $users->total() > $users->perPage()) { - $pagination = pagination($users, true); + if ($users instanceof LengthAwarePaginator) { + $pagination = pagination($users, config('display_users')); } $link = button(url('/register'), icon('plus-lg'), 'btn-sm add'); return page_with_title(__('All users') . ' ' . $link, [ From b0243b7fae197098a77f2e40e4c78bf7ee5fa94b Mon Sep 17 00:00:00 2001 From: Xu Date: Wed, 3 Dec 2025 17:26:26 +0100 Subject: [PATCH 150/157] Pagination: Show selected count --- includes/sys_template.php | 38 ++++++++++++++++++--------------- resources/lang/de_DE/default.po | 3 +++ 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/includes/sys_template.php b/includes/sys_template.php index 9848dd6e2..a38563f70 100644 --- a/includes/sys_template.php +++ b/includes/sys_template.php @@ -462,24 +462,26 @@ function pagination(LengthAwarePaginator $paginator, ?int $selectionSteps = null $selection, ); } - $items .= sprintf( + $dropdownValue = request()->get('c', $selectionSteps); + $dropdownValue = $dropdownValue == 'all' || !is_numeric($dropdownValue) ? __('All') : $dropdownValue; + $dropdown = sprintf( ' -
  • - -
  • + %s + ', __('Per page'), + $dropdownValue, implode(PHP_EOL, $selections), $paginator->appends('c', 'all')->url(1), __('All'), @@ -487,10 +489,12 @@ function pagination(LengthAwarePaginator $paginator, ?int $selectionSteps = null } return sprintf(' -