diff --git a/.docker/frankenphp/Caddyfile b/.docker/frankenphp/Caddyfile
index f26b6f22..293ab18e 100644
--- a/.docker/frankenphp/Caddyfile
+++ b/.docker/frankenphp/Caddyfile
@@ -51,6 +51,15 @@
# Disable Topics tracking if not enabled explicitly: https://github.com/jkarlin/topics
header ?Permissions-Policy "browsing-topics=()"
+ # Set a strict CSP and nosniff for all static assets not handled by PHP.
+ # ? means "set only if not already present", so PHP responses carrying a Nelmio CSP are left untouched.
+ header ?Content-Security-Policy "default-src 'self'; script-src 'none'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; sandbox;"
+ header ?X-Content-Type-Options "nosniff"
+
+ # SVG files get a slightly different CSP because they can embed resources and must not be framed.
+ @svg path *.svg *.svg.gz *.svg.br
+ header @svg Content-Security-Policy "default-src 'self'; script-src 'none'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; frame-ancestors 'none'; sandbox;"
+
# Prevent PHP execution in the media upload directory
@php_in_media path_regexp (?i)^/media/.*\.(php[3-8]?|phar|phtml|pht|phps)$
respond @php_in_media 403
diff --git a/.github/workflows/assets_artifact_build.yml b/.github/workflows/assets_artifact_build.yml
index a74ae7cc..5da304ec 100644
--- a/.github/workflows/assets_artifact_build.yml
+++ b/.github/workflows/assets_artifact_build.yml
@@ -27,7 +27,7 @@ jobs:
APP_ENV: prod
steps:
- - uses: actions/checkout@v6
+ - uses: actions/checkout@v7
- name: Setup PHP
uses: shivammathur/setup-php@v2
diff --git a/.github/workflows/docker_build.yml b/.github/workflows/docker_build.yml
index 210dbc18..d1f53c38 100644
--- a/.github/workflows/docker_build.yml
+++ b/.github/workflows/docker_build.yml
@@ -32,7 +32,7 @@ jobs:
steps:
-
name: Checkout
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
-
name: Docker meta
id: docker_meta
diff --git a/.github/workflows/docker_frankenphp.yml b/.github/workflows/docker_frankenphp.yml
index 36ec322d..fc69b29b 100644
--- a/.github/workflows/docker_frankenphp.yml
+++ b/.github/workflows/docker_frankenphp.yml
@@ -32,7 +32,7 @@ jobs:
steps:
-
name: Checkout
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
-
name: Docker meta
id: docker_meta
diff --git a/.github/workflows/static_analysis.yml b/.github/workflows/static_analysis.yml
index f47ce87b..cbc42d27 100644
--- a/.github/workflows/static_analysis.yml
+++ b/.github/workflows/static_analysis.yml
@@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- - uses: actions/checkout@v6
+ - uses: actions/checkout@v7
- name: Setup PHP
uses: shivammathur/setup-php@v2
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 27dfe173..66086104 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -46,7 +46,7 @@ jobs:
if: matrix.db-type == 'postgres'
- name: Checkout
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
- name: Setup PHP
uses: shivammathur/setup-php@v2
diff --git a/Dockerfile b/Dockerfile
index e848acc1..049de283 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -193,7 +193,7 @@ RUN a2dissite 000-default.conf && \
a2enmod proxy_fcgi setenvif && \
a2enconf php${PHP_VERSION}-fpm && \
a2enconf docker-php && \
- a2enmod rewrite
+ a2enmod rewrite headers
# Install composer and yarn dependencies for Part-DB
USER www-data
diff --git a/VERSION b/VERSION
index 371a952d..94f15e9c 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.12.2
+2.13.1
diff --git a/assets/controllers/common/dirty_form_controller.js b/assets/controllers/common/dirty_form_controller.js
index aad2e6b0..8e560a3f 100644
--- a/assets/controllers/common/dirty_form_controller.js
+++ b/assets/controllers/common/dirty_form_controller.js
@@ -19,8 +19,7 @@
import {Controller} from "@hotwired/stimulus";
import {visit} from "@hotwired/turbo";
-import * as bootbox from "bootbox";
-import "../../css/components/bootbox_extensions.css";
+import {ConfirmSwal} from "../../helpers/swal";
import "../../css/components/dirty_form.css";
/**
@@ -207,11 +206,10 @@ export default class extends Controller {
}
_confirmNavigation(onConfirm) {
- bootbox.confirm({
- title: this.confirmTitleValue,
- message: this.confirmMessageValue,
- callback: (result) => { if (result) onConfirm(); }
- });
+ ConfirmSwal.fire({
+ titleText: this.confirmTitleValue,
+ text: this.confirmMessageValue,
+ }).then(({isConfirmed}) => { if (isConfirmed) onConfirm(); });
}
_handleLinkClick(event) {
diff --git a/assets/controllers/elements/collection_type_controller.js b/assets/controllers/elements/collection_type_controller.js
index 647ed5e5..caeb4122 100644
--- a/assets/controllers/elements/collection_type_controller.js
+++ b/assets/controllers/elements/collection_type_controller.js
@@ -19,8 +19,7 @@
import {Controller} from "@hotwired/stimulus";
-import * as bootbox from "bootbox";
-import "../../css/components/bootbox_extensions.css";
+import {AlertSwal, ConfirmSwal} from "../../helpers/swal";
import accept from "attr-accept";
export default class extends Controller {
@@ -62,7 +61,7 @@ export default class extends Controller {
if(!prototype) {
console.warn("Prototype is not set, we cannot create a new element. This is most likely due to missing permissions.");
- bootbox.alert("You do not have the permissions to create a new element. (No protoype element is set)");
+ AlertSwal.fire({"text": "You do not have the permissions to create a new element. (No protoype element is set)"});
return;
}
@@ -226,8 +225,10 @@ export default class extends Controller {
}
if(this.deleteMessageValue) {
- bootbox.confirm(this.deleteMessageValue, (result) => {
- if (result) {
+ ConfirmSwal.fire({
+ text: this.deleteMessageValue,
+ }).then(({isConfirmed}) => {
+ if (isConfirmed) {
del();
}
});
diff --git a/assets/controllers/elements/datatables/datatables_controller.js b/assets/controllers/elements/datatables/datatables_controller.js
index d945004b..4b84a834 100644
--- a/assets/controllers/elements/datatables/datatables_controller.js
+++ b/assets/controllers/elements/datatables/datatables_controller.js
@@ -38,9 +38,7 @@ import 'datatables.net-colreorder-bs5';
import 'datatables.net-responsive-bs5';
import '../../../js/lib/datatables';
-//import 'datatables.net-select-bs5';
-//Use the local version containing the fix for the select extension
-import '../../../js/lib/dataTables.select.mjs';
+import 'datatables.net-select-bs5';
const EVENT_DT_LOADED = 'dt:loaded';
diff --git a/assets/controllers/elements/datatables/parts_controller.js b/assets/controllers/elements/datatables/parts_controller.js
index c43fa276..cfa386cc 100644
--- a/assets/controllers/elements/datatables/parts_controller.js
+++ b/assets/controllers/elements/datatables/parts_controller.js
@@ -20,7 +20,7 @@
import DatatablesController from "./datatables_controller.js";
import TomSelect from "tom-select";
-import * as bootbox from "bootbox";
+import {ConfirmSwal} from "../../../helpers/swal";
/**
* This is the datatables controller for parts lists
@@ -146,15 +146,17 @@ export default class extends DatatablesController {
bubbles: true, //This line is important, otherwise Turbo will not receive the event
});
- const confirm = bootbox.confirm({
- message: message, title: title, callback: function (result) {
- //If the dialog was confirmed, then submit the form.
- if (result) {
- that._confirmed = true;
- form.dispatchEvent(that._our_event);
- } else {
- that._confirmed = false;
- }
+ ConfirmSwal.fire({
+ titleText: title,
+ text: message,
+ icon: "warning"
+ }).then(({isConfirmed}) => {
+ //If the dialog was confirmed, then submit the form.
+ if (isConfirmed) {
+ that._confirmed = true;
+ form.dispatchEvent(that._our_event);
+ } else {
+ that._confirmed = false;
}
});
}
diff --git a/assets/controllers/elements/delete_btn_controller.js b/assets/controllers/elements/delete_btn_controller.js
index 9ab15f7d..e1b37bcc 100644
--- a/assets/controllers/elements/delete_btn_controller.js
+++ b/assets/controllers/elements/delete_btn_controller.js
@@ -19,8 +19,7 @@
import {Controller} from "@hotwired/stimulus";
-import * as bootbox from "bootbox";
-import "../../css/components/bootbox_extensions.css";
+import {ConfirmSwal} from "../../helpers/swal";
export default class extends Controller
{
@@ -48,32 +47,33 @@ export default class extends Controller
const submitter = event.submitter;
const that = this;
- const confirm = bootbox.confirm({
- message: message, title: title, callback: function (result) {
- //If the dialog was confirmed, then submit the form.
- if (result) {
- //Set a flag to prevent the dialog from popping up again and allowing turbo to submit the form
- that._confirmed = true;
+ ConfirmSwal.fire({
+ titleText: title,
+ html: message, //Message contains a tag and no user injectable HTML
+ }).then(({isConfirmed}) => {
+ //If the dialog was confirmed, then submit the form.
+ if (isConfirmed) {
+ //Set a flag to prevent the dialog from popping up again and allowing turbo to submit the form
+ that._confirmed = true;
- //Create a submit button in the form and click it to submit the form
- //Before a submit event was dispatched, but this caused weird issues on Firefox causing the delete request being posted twice (and the second time was returning 404). See https://github.com/Part-DB/Part-DB-server/issues/273
- const submit_btn = document.createElement('button');
- submit_btn.type = 'submit';
- submit_btn.style.display = 'none';
+ //Create a submit button in the form and click it to submit the form
+ //Before a submit event was dispatched, but this caused weird issues on Firefox causing the delete request being posted twice (and the second time was returning 404). See https://github.com/Part-DB/Part-DB-server/issues/273
+ const submit_btn = document.createElement('button');
+ submit_btn.type = 'submit';
+ submit_btn.style.display = 'none';
- //If the clicked button has a value, set it on the submit button
- if (submitter.value) {
- submit_btn.value = submitter.value;
- }
- if (submitter.name) {
- submit_btn.name = submitter.name;
- }
- form.appendChild(submit_btn);
- submit_btn.click();
- } else {
- that._confirmed = false;
+ //If the clicked button has a value, set it on the submit button
+ if (submitter.value) {
+ submit_btn.value = submitter.value;
}
+ if (submitter.name) {
+ submit_btn.name = submitter.name;
+ }
+ form.appendChild(submit_btn);
+ submit_btn.click();
+ } else {
+ that._confirmed = false;
}
});
}
-}
\ No newline at end of file
+}
diff --git a/assets/controllers/elements/link_confirm_controller.js b/assets/controllers/elements/link_confirm_controller.js
index 3d59b492..be226517 100644
--- a/assets/controllers/elements/link_confirm_controller.js
+++ b/assets/controllers/elements/link_confirm_controller.js
@@ -19,8 +19,7 @@
import {Controller} from "@hotwired/stimulus";
-import * as bootbox from "bootbox";
-import "../../css/components/bootbox_extensions.css";
+import {ConfirmSwal} from "../../helpers/swal";
export default class extends Controller
{
@@ -53,20 +52,19 @@ export default class extends Controller
const that = this;
- bootbox.confirm({
- title: this.titleValue,
- message: this.messageValue,
- callback: (result) => {
- if (result) {
- //Set a flag to prevent the dialog from popping up again and allowing turbo to submit the form
- that._confirmed = true;
+ ConfirmSwal.fire({
+ titleText: this.titleValue,
+ text: this.messageValue,
+ }).then(({isConfirmed}) => {
+ if (isConfirmed) {
+ //Set a flag to prevent the dialog from popping up again and allowing turbo to submit the form
+ that._confirmed = true;
- //Click the link
- that.element.click();
- } else {
- that._confirmed = false;
- }
+ //Click the link
+ that.element.click();
+ } else {
+ that._confirmed = false;
}
});
}
-}
\ No newline at end of file
+}
diff --git a/assets/controllers/elements/password_strength_estimate_controller.js b/assets/controllers/elements/password_strength_estimate_controller.js
index d9cfbc87..9ad2da1c 100644
--- a/assets/controllers/elements/password_strength_estimate_controller.js
+++ b/assets/controllers/elements/password_strength_estimate_controller.js
@@ -19,12 +19,14 @@
import {Controller} from "@hotwired/stimulus";
-import { zxcvbn, zxcvbnOptions } from '@zxcvbn-ts/core';
+import { ZxcvbnFactory } from '@zxcvbn-ts/core';
import * as zxcvbnCommonPackage from '@zxcvbn-ts/language-common';
import * as zxcvbnEnPackage from '@zxcvbn-ts/language-en';
import * as zxcvbnDePackage from '@zxcvbn-ts/language-de';
import * as zxcvbnFrPackage from '@zxcvbn-ts/language-fr';
import * as zxcvbnJaPackage from '@zxcvbn-ts/language-ja';
+import * as zxcvbnItPackage from '@zxcvbn-ts/language-it';
+import * as zxcvbnPlPackage from '@zxcvbn-ts/language-pl';
import {trans} from '../../translator.js';
/* stimulusFetch: 'lazy' */
@@ -34,6 +36,8 @@ export default class extends Controller {
static targets = ["badge", "warning"]
+ _zxcvbnFactory;
+
_getTranslations() {
//Get the current locale
const locale = document.documentElement.lang;
@@ -43,6 +47,10 @@ export default class extends Controller {
return zxcvbnFrPackage.translations;
} else if (locale.includes('ja')) {
return zxcvbnJaPackage.translations;
+ } else if (locale.includes('it')) {
+ return zxcvbnItPackage.translations;
+ } else if (locale.includes('pl')) {
+ return zxcvbnPlPackage.translations;
}
//Fallback to english
@@ -56,34 +64,39 @@ export default class extends Controller {
//Configure zxcvbn
const options = {
graphs: zxcvbnCommonPackage.adjacencyGraphs,
+ useLevenshtein: true,
dictionary: {
...zxcvbnCommonPackage.dictionary,
// We could use the english dictionary here too, but it is very big. So we just use the common words
- //...zxcvbnEnPackage.dictionary,
+ ...zxcvbnEnPackage.dictionary,
+ ...zxcvbnDePackage.dictionary,
+
+ "partdb": ['part-db', 'partdb', 'part_db', 'part-db-symfony', 'partdb-symfony', 'part_db_symfony'],
},
translations: this._getTranslations(),
};
- zxcvbnOptions.setOptions(options);
+
+ this._zxcvbnFactory = new ZxcvbnFactory(options);
//Add event listener to the password input field
this._passwordInput.addEventListener('input', this._onPasswordInput.bind(this));
}
- _onPasswordInput() {
+ async _onPasswordInput() {
//Retrieve the password
const password = this._passwordInput.value;
//Estimate the password strength
- const result = zxcvbn(password);
+ const result = await this._zxcvbnFactory.checkAsync(password);
//Update the badge
this.badgeTarget.parentElement.classList.remove("d-none");
- this._setBadgeToLevel(result.score);
+ this._setBadgeToLevel(result.score, result.crackTimes.onlineNoThrottlingXPerSecond.display);
this.warningTarget.innerHTML = result.feedback.warning;
}
- _setBadgeToLevel(level) {
+ _setBadgeToLevel(level, time = null) {
let text, classes;
switch (level) {
@@ -118,5 +131,11 @@ export default class extends Controller {
//Re-add the classes
this.badgeTarget.classList.add("badge");
this.badgeTarget.classList.add(...classes.split(" "));
+
+ if (time) {
+ this.badgeTarget.setAttribute("title", trans("user.password_strength.crack_time", {"%time%": time}));
+ } else {
+ this.badgeTarget.removeAttribute("title");
+ }
}
}
diff --git a/assets/controllers/pages/reelCalculator_controller.js b/assets/controllers/pages/reelCalculator_controller.js
index a134bf9b..e0b2c4ba 100644
--- a/assets/controllers/pages/reelCalculator_controller.js
+++ b/assets/controllers/pages/reelCalculator_controller.js
@@ -18,7 +18,7 @@
*/
import {Controller} from "@hotwired/stimulus";
-import * as bootbox from "bootbox";
+import {AlertSwal} from "../../helpers/swal";
export default class extends Controller {
@@ -35,12 +35,12 @@ export default class extends Controller {
const part_distance = document.getElementById('reel_part_distance').value;
if (dia_inner == "" || dia_outer == "" || tape_thickness == "") {
- bootbox.alert(this.errorMissingValuesValue);
+ AlertSwal.fire({title: this.errorMissingValuesValue});
return;
}
if (dia_outer**dia_outer < dia_inner**dia_inner) {
- bootbox.alert(this.errorOuterGreaterInnerValue);
+ AlertSwal.fire({title: this.errorOuterGreaterInnerValue});
return;
}
@@ -61,12 +61,12 @@ export default class extends Controller {
return;
}
- var parts_per_meter = 1 / (part_distance / 1000);
+ const parts_per_meter = 1 / (part_distance / 1000);
document.getElementById('result_parts_per_meter').textContent = parts_per_meter.toFixed(2) + ' 1/m';
- var parts_amount = (length/1000) * parts_per_meter;
+ const parts_amount = (length / 1000) * parts_per_meter;
- document.getElementById('result_amount').textContent = Math.floor(parts_amount);
+ document.getElementById('result_amount').textContent = Math.floor(parts_amount).toString();
}
-}
\ No newline at end of file
+}
diff --git a/assets/css/components/swal.css b/assets/css/components/swal.css
new file mode 100644
index 00000000..4c2302a9
--- /dev/null
+++ b/assets/css/components/swal.css
@@ -0,0 +1,50 @@
+/*
+ * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
+ *
+ * Copyright (C) 2019 - 2026 Jan Böhmer (https://github.com/jbtronics)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+/**
+ * Respect the dark mode of Bootstrap 5 set via data-bs-theme="dark" on the element. This is done by overriding the CSS variables of the bootstrap-5 theme of SweetAlert2.
+ */
+
+html[data-bs-theme="dark"] [data-swal2-theme='bootstrap-5'] {
+ /* POPUP */
+ --swal2-background: #212529;
+ --swal2-color: #fff;
+ --swal2-border: 1px solid #495057;
+
+ /* INPUT */
+ --swal2-input-background: #2b3035;
+ --swal2-input-border: 1px solid #495057;
+ --swal2-input-focus-border: 1px solid #86b7fe;
+ --swal2-input-focus-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.075), 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
+
+ /* VALIDATION MESSAGE */
+ --swal2-validation-message-background: #2c0b0e;
+ --swal2-validation-message-color: #ea868f;
+
+ /* FOOTER */
+ --swal2-footer-border-color: #495057;
+ --swal2-footer-background: #343a40;
+ --swal2-footer-color: #adb5bd;
+
+ /* CLOSE BUTTON */
+ --swal2-close-button-color: #fff;
+
+ /* TOASTS */
+ --swal2-toast-border: 1px solid #495057;
+}
diff --git a/assets/helpers/swal.js b/assets/helpers/swal.js
new file mode 100644
index 00000000..e370a9ed
--- /dev/null
+++ b/assets/helpers/swal.js
@@ -0,0 +1,44 @@
+/*
+ * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
+ *
+ * Copyright (C) 2019 - 2022 Jan Böhmer (https://github.com/jbtronics)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+import Swal from 'sweetalert2';
+import 'sweetalert2/themes/bootstrap-5.css';
+import '../css/components/swal.css'
+import { trans } from '../translator';
+
+const BaseSwal = Swal.mixin({
+ position: "top",
+ theme: "bootstrap-5",
+ confirmButtonText: trans('dialog.btn.ok'),
+ cancelButtonText: trans('dialog.btn.cancel'),
+ denyButtonText: trans('dialog.btn.deny'),
+});
+
+const ConfirmSwal = BaseSwal.mixin({
+ showCancelButton: true,
+ showCloseButton: true,
+ icon: "warning",
+});
+
+const AlertSwal = BaseSwal.mixin({
+ showCloseButton: true,
+ icon: "info",
+});
+
+export { ConfirmSwal, AlertSwal, BaseSwal, BaseSwal as default,};
diff --git a/assets/js/app.js b/assets/js/app.js
index 4dd39581..355fe919 100644
--- a/assets/js/app.js
+++ b/assets/js/app.js
@@ -30,21 +30,21 @@ import '../css/app/images.css';
// start the Stimulus application
import '../stimulus_bootstrap';
-// Need jQuery? Install it with "yarn add jquery", then uncomment to require it.
-const $ = require('jquery');
+import $ from 'jquery';
//Only include javascript
import '@fortawesome/fontawesome-free/css/all.css'
-require('bootstrap');
+import 'bootstrap';
import "./error_handler";
import "./tab_remember";
import "./register_events";
import "./tristate_checkboxes";
-//Define jquery globally
-global.$ = global.jQuery = require("jquery");
+// Expose jQuery globally so legacy plugins and Bootstrap's jQuery integration
+// can find it on window at runtime.
+global.$ = global.jQuery = $;
//Use the local WASM file for the ZXing library
import {
diff --git a/assets/js/error_handler.js b/assets/js/error_handler.js
index 7f047af9..67695fb9 100644
--- a/assets/js/error_handler.js
+++ b/assets/js/error_handler.js
@@ -17,7 +17,7 @@
* along with this program. If not, see .
*/
-import * as bootbox from "bootbox";
+import Swal from "../helpers/swal";
/**
* If this class is imported the user is shown an error dialog if he calls an page via Turbo and an error is responded.
@@ -40,21 +40,6 @@ class ErrorHandlerHelper {
_showAlert(statusText, statusCode, location, responseHTML)
{
const httpStatusToText = {
- '200': 'OK',
- '201': 'Created',
- '202': 'Accepted',
- '203': 'Non-Authoritative Information',
- '204': 'No Content',
- '205': 'Reset Content',
- '206': 'Partial Content',
- '300': 'Multiple Choices',
- '301': 'Moved Permanently',
- '302': 'Found',
- '303': 'See Other',
- '304': 'Not Modified',
- '305': 'Use Proxy',
- '306': 'Unused',
- '307': 'Temporary Redirect',
'400': 'Bad Request',
'401': 'Unauthorized',
'402': 'Payment Required',
@@ -83,49 +68,67 @@ class ErrorHandlerHelper {
'505': 'HTTP Version Not Supported',
};
- //If the statusText is empty, we use the status code as text
- if (!statusText) {
- statusText = httpStatusToText[statusCode];
- }
-
- //Create error text
- const title = statusText + ' (Status ' + statusCode + ')';
-
- let trimString = function (string, length) {
- return string.length > length ?
- string.substring(0, length) + '...' :
- string;
+ const userFriendlyMessages = {
+ '400': 'The request was invalid or malformed.',
+ '401': 'You need to log in to access this resource.',
+ '403': 'You don\'t have permission to access this resource.',
+ '404': 'The requested page or resource could not be found.',
+ '408': 'The request timed out. Please check your connection and try again.',
+ '409': 'There was a conflict with the current state of the resource.',
+ '429': 'Too many requests sent. Please wait a moment and try again.',
+ '500': 'An internal server error occurred. This is not your fault.',
+ '502': 'The server received an invalid response from an upstream service.',
+ '503': 'The service is temporarily unavailable. Please try again later.',
+ '504': 'The server did not respond in time. Please try again later.',
};
- const short_location = trimString(location, 50);
+ if (!statusText) {
+ statusText = httpStatusToText[String(statusCode)] ?? 'Unknown Error';
+ }
- const alert = bootbox.alert(
- {
- size: 'large',
- message: function() {
- let url = location;
- let msg = `Error calling ${short_location} . `;
- msg += 'Try to reload the page or contact the administrator if this error persists. ';
+ const title = `${statusText} (HTTP ${statusCode}) `;
+ const friendlyMsg = userFriendlyMessages[String(statusCode)]
+ ?? 'An unexpected error occurred. Please try again or contact the administrator.';
- msg += '' + 'View details' + " ";
- msg += "
";
+ const short_location = location.length > 80
+ ? location.substring(0, 80) + '…'
+ : location;
- return msg;
- },
- title: title,
- callback: function () {
- //Remove blur
- $('#content').removeClass('loading-content');
- }
+ const msg = `
+ ${friendlyMsg}
+ If this error keeps happening, please contact your administrator.
+
+ Technical details
+
+
+
+
`;
- });
+ const footer = `Error while loading: ${short_location} `;
- alert.init(function (){
- var dstFrame = document.getElementById('error-iframe');
- //@ts-ignore
- var dstDoc = dstFrame.contentDocument || dstFrame.contentWindow.document;
- dstDoc.write(responseHTML)
- dstDoc.close();
+ Swal.fire({
+ icon: 'error',
+ title: title,
+ html: msg,
+ footer: footer,
+ width: '90%',
+ confirmButtonText: ' Reload page',
+ showCancelButton: true,
+ cancelButtonText: 'Close',
+ showCloseButton: true,
+ reverseButtons: true,
+ didOpen: () => {
+ const dstFrame = document.getElementById('error-iframe');
+ //@ts-ignore
+ const dstDoc = dstFrame.contentDocument || dstFrame.contentWindow.document;
+ dstDoc.write(responseHTML);
+ dstDoc.close();
+ },
+ }).then((result) => {
+ document.getElementById('content').classList.remove('loading-content');
+ if (result.isConfirmed) {
+ window.location.reload();
+ }
});
}
@@ -171,4 +174,4 @@ class ErrorHandlerHelper {
}
}
-export default new ErrorHandlerHelper();
\ No newline at end of file
+export default new ErrorHandlerHelper();
diff --git a/assets/js/lib/dataTables.select.mjs b/assets/js/lib/dataTables.select.mjs
deleted file mode 100644
index bba97692..00000000
--- a/assets/js/lib/dataTables.select.mjs
+++ /dev/null
@@ -1,1538 +0,0 @@
-/*********************
- * This is the fixed version of the select extension for DataTables with the fix for the issue with the select extension
- * (https://github.com/DataTables/Select/issues/51)
- * We use this instead of the yarn version until the PR (https://github.com/DataTables/Select/pull/52) is merged and released
- * /*******************/
-
-
-/*! Select for DataTables 2.0.0
- * © SpryMedia Ltd - datatables.net/license/mit
- */
-
-import jQuery from 'jquery';
-import DataTable from 'datatables.net';
-
-// Allow reassignment of the $ variable
-let $ = jQuery;
-
-
-// Version information for debugger
-DataTable.select = {};
-
-DataTable.select.version = '2.0.0';
-
-DataTable.select.init = function (dt) {
- var ctx = dt.settings()[0];
-
- if (!DataTable.versionCheck('2')) {
- throw 'Warning: Select requires DataTables 2 or newer';
- }
-
- if (ctx._select) {
- return;
- }
-
- var savedSelected = dt.state.loaded();
-
- var selectAndSave = function (e, settings, data) {
- if (data === null || data.select === undefined) {
- return;
- }
-
- // Clear any currently selected rows, before restoring state
- // None will be selected on first initialisation
- if (dt.rows({ selected: true }).any()) {
- dt.rows().deselect();
- }
- if (data.select.rows !== undefined) {
- dt.rows(data.select.rows).select();
- }
-
- if (dt.columns({ selected: true }).any()) {
- dt.columns().deselect();
- }
- if (data.select.columns !== undefined) {
- dt.columns(data.select.columns).select();
- }
-
- if (dt.cells({ selected: true }).any()) {
- dt.cells().deselect();
- }
- if (data.select.cells !== undefined) {
- for (var i = 0; i < data.select.cells.length; i++) {
- dt.cell(data.select.cells[i].row, data.select.cells[i].column).select();
- }
- }
-
- dt.state.save();
- };
-
- dt.on('stateSaveParams', function (e, settings, data) {
- data.select = {};
- data.select.rows = dt.rows({ selected: true }).ids(true).toArray();
- data.select.columns = dt.columns({ selected: true })[0];
- data.select.cells = dt.cells({ selected: true })[0].map(function (coords) {
- return { row: dt.row(coords.row).id(true), column: coords.column };
- });
- })
- .on('stateLoadParams', selectAndSave)
- .one('init', function () {
- selectAndSave(undefined, undefined, savedSelected);
- });
-
- var init = ctx.oInit.select;
- var defaults = DataTable.defaults.select;
- var opts = init === undefined ? defaults : init;
-
- // Set defaults
- var items = 'row';
- var style = 'api';
- var blurable = false;
- var toggleable = true;
- var info = true;
- var selector = 'td, th';
- var className = 'selected';
- var headerCheckbox = true;
- var setStyle = false;
-
- ctx._select = {
- infoEls: []
- };
-
- // Initialisation customisations
- if (opts === true) {
- style = 'os';
- setStyle = true;
- }
- else if (typeof opts === 'string') {
- style = opts;
- setStyle = true;
- }
- else if ($.isPlainObject(opts)) {
- if (opts.blurable !== undefined) {
- blurable = opts.blurable;
- }
-
- if (opts.toggleable !== undefined) {
- toggleable = opts.toggleable;
- }
-
- if (opts.info !== undefined) {
- info = opts.info;
- }
-
- if (opts.items !== undefined) {
- items = opts.items;
- }
-
- if (opts.style !== undefined) {
- style = opts.style;
- setStyle = true;
- }
- else {
- style = 'os';
- setStyle = true;
- }
-
- if (opts.selector !== undefined) {
- selector = opts.selector;
- }
-
- if (opts.className !== undefined) {
- className = opts.className;
- }
-
- if (opts.headerCheckbox !== undefined) {
- headerCheckbox = opts.headerCheckbox;
- }
- }
-
- dt.select.selector(selector);
- dt.select.items(items);
- dt.select.style(style);
- dt.select.blurable(blurable);
- dt.select.toggleable(toggleable);
- dt.select.info(info);
- ctx._select.className = className;
-
- // If the init options haven't enabled select, but there is a selectable
- // class name, then enable
- if (!setStyle && $(dt.table().node()).hasClass('selectable')) {
- dt.select.style('os');
- }
-
- // Insert a checkbox into the header if needed - might need to wait
- // for init complete, or it might already be done
- if (headerCheckbox) {
- initCheckboxHeader(dt);
-
- dt.on('init', function () {
- initCheckboxHeader(dt);
- });
- }
-};
-
-/*
-
-Select is a collection of API methods, event handlers, event emitters and
-buttons (for the `Buttons` extension) for DataTables. It provides the following
-features, with an overview of how they are implemented:
-
-## Selection of rows, columns and cells. Whether an item is selected or not is
- stored in:
-
-* rows: a `_select_selected` property which contains a boolean value of the
- DataTables' `aoData` object for each row
-* columns: a `_select_selected` property which contains a boolean value of the
- DataTables' `aoColumns` object for each column
-* cells: a `_selected_cells` property which contains an array of boolean values
- of the `aoData` object for each row. The array is the same length as the
- columns array, with each element of it representing a cell.
-
-This method of using boolean flags allows Select to operate when nodes have not
-been created for rows / cells (DataTables' defer rendering feature).
-
-## API methods
-
-A range of API methods are available for triggering selection and de-selection
-of rows. Methods are also available to configure the selection events that can
-be triggered by an end user (such as which items are to be selected). To a large
-extent, these of API methods *is* Select. It is basically a collection of helper
-functions that can be used to select items in a DataTable.
-
-Configuration of select is held in the object `_select` which is attached to the
-DataTables settings object on initialisation. Select being available on a table
-is not optional when Select is loaded, but its default is for selection only to
-be available via the API - so the end user wouldn't be able to select rows
-without additional configuration.
-
-The `_select` object contains the following properties:
-
-```
-{
- items:string - Can be `rows`, `columns` or `cells`. Defines what item
- will be selected if the user is allowed to activate row
- selection using the mouse.
- style:string - Can be `none`, `single`, `multi` or `os`. Defines the
- interaction style when selecting items
- blurable:boolean - If row selection can be cleared by clicking outside of
- the table
- toggleable:boolean - If row selection can be cancelled by repeated clicking
- on the row
- info:boolean - If the selection summary should be shown in the table
- information elements
- infoEls:element[] - List of HTML elements with info elements for a table
-}
-```
-
-In addition to the API methods, Select also extends the DataTables selector
-options for rows, columns and cells adding a `selected` option to the selector
-options object, allowing the developer to select only selected items or
-unselected items.
-
-## Mouse selection of items
-
-Clicking on items can be used to select items. This is done by a simple event
-handler that will select the items using the API methods.
-
- */
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Local functions
- */
-
-/**
- * Add one or more cells to the selection when shift clicking in OS selection
- * style cell selection.
- *
- * Cell range is more complicated than row and column as we want to select
- * in the visible grid rather than by index in sequence. For example, if you
- * click first in cell 1-1 and then shift click in 2-2 - cells 1-2 and 2-1
- * should also be selected (and not 1-3, 1-4. etc)
- *
- * @param {DataTable.Api} dt DataTable
- * @param {object} idx Cell index to select to
- * @param {object} last Cell index to select from
- * @private
- */
-function cellRange(dt, idx, last) {
- var indexes;
- var columnIndexes;
- var rowIndexes;
- var selectColumns = function (start, end) {
- if (start > end) {
- var tmp = end;
- end = start;
- start = tmp;
- }
-
- var record = false;
- return dt
- .columns(':visible')
- .indexes()
- .filter(function (i) {
- if (i === start) {
- record = true;
- }
-
- if (i === end) {
- // not else if, as start might === end
- record = false;
- return true;
- }
-
- return record;
- });
- };
-
- var selectRows = function (start, end) {
- var indexes = dt.rows({ search: 'applied' }).indexes();
-
- // Which comes first - might need to swap
- if (indexes.indexOf(start) > indexes.indexOf(end)) {
- var tmp = end;
- end = start;
- start = tmp;
- }
-
- var record = false;
- return indexes.filter(function (i) {
- if (i === start) {
- record = true;
- }
-
- if (i === end) {
- record = false;
- return true;
- }
-
- return record;
- });
- };
-
- if (!dt.cells({ selected: true }).any() && !last) {
- // select from the top left cell to this one
- columnIndexes = selectColumns(0, idx.column);
- rowIndexes = selectRows(0, idx.row);
- }
- else {
- // Get column indexes between old and new
- columnIndexes = selectColumns(last.column, idx.column);
- rowIndexes = selectRows(last.row, idx.row);
- }
-
- indexes = dt.cells(rowIndexes, columnIndexes).flatten();
-
- if (!dt.cells(idx, { selected: true }).any()) {
- // Select range
- dt.cells(indexes).select();
- }
- else {
- // Deselect range
- dt.cells(indexes).deselect();
- }
-}
-
-/**
- * Disable mouse selection by removing the selectors
- *
- * @param {DataTable.Api} dt DataTable to remove events from
- * @private
- */
-function disableMouseSelection(dt) {
- var ctx = dt.settings()[0];
- var selector = ctx._select.selector;
-
- $(dt.table().container())
- .off('mousedown.dtSelect', selector)
- .off('mouseup.dtSelect', selector)
- .off('click.dtSelect', selector);
-
- $('body').off('click.dtSelect' + _safeId(dt.table().node()));
-}
-
-/**
- * Attach mouse listeners to the table to allow mouse selection of items
- *
- * @param {DataTable.Api} dt DataTable to remove events from
- * @private
- */
-function enableMouseSelection(dt) {
- var container = $(dt.table().container());
- var ctx = dt.settings()[0];
- var selector = ctx._select.selector;
- var matchSelection;
-
- container
- .on('mousedown.dtSelect', selector, function (e) {
- // Disallow text selection for shift clicking on the table so multi
- // element selection doesn't look terrible!
- if (e.shiftKey || e.metaKey || e.ctrlKey) {
- container
- .css('-moz-user-select', 'none')
- .one('selectstart.dtSelect', selector, function () {
- return false;
- });
- }
-
- if (window.getSelection) {
- matchSelection = window.getSelection();
- }
- })
- .on('mouseup.dtSelect', selector, function () {
- // Allow text selection to occur again, Mozilla style (tested in FF
- // 35.0.1 - still required)
- container.css('-moz-user-select', '');
- })
- .on('click.dtSelect', selector, function (e) {
- var items = dt.select.items();
- var idx;
-
- // If text was selected (click and drag), then we shouldn't change
- // the row's selected state
- if (matchSelection) {
- var selection = window.getSelection();
-
- // If the element that contains the selection is not in the table, we can ignore it
- // This can happen if the developer selects text from the click event
- if (
- !selection.anchorNode ||
- $(selection.anchorNode).closest('table')[0] === dt.table().node()
- ) {
- if (selection !== matchSelection) {
- return;
- }
- }
- }
-
- var ctx = dt.settings()[0];
- var container = dt.table().container();
-
- // Ignore clicks inside a sub-table
- if ($(e.target).closest('div.dt-container')[0] != container) {
- return;
- }
-
- var cell = dt.cell($(e.target).closest('td, th'));
-
- // Check the cell actually belongs to the host DataTable (so child
- // rows, etc, are ignored)
- if (!cell.any()) {
- return;
- }
-
- var event = $.Event('user-select.dt');
- eventTrigger(dt, event, [items, cell, e]);
-
- if (event.isDefaultPrevented()) {
- return;
- }
-
- var cellIndex = cell.index();
- if (items === 'row') {
- idx = cellIndex.row;
- typeSelect(e, dt, ctx, 'row', idx);
- }
- else if (items === 'column') {
- idx = cell.index().column;
- typeSelect(e, dt, ctx, 'column', idx);
- }
- else if (items === 'cell') {
- idx = cell.index();
- typeSelect(e, dt, ctx, 'cell', idx);
- }
-
- ctx._select_lastCell = cellIndex;
- });
-
- // Blurable
- $('body').on('click.dtSelect' + _safeId(dt.table().node()), function (e) {
- if (ctx._select.blurable) {
- // If the click was inside the DataTables container, don't blur
- if ($(e.target).parents().filter(dt.table().container()).length) {
- return;
- }
-
- // Ignore elements which have been removed from the DOM (i.e. paging
- // buttons)
- if ($(e.target).parents('html').length === 0) {
- return;
- }
-
- // Don't blur in Editor form
- if ($(e.target).parents('div.DTE').length) {
- return;
- }
-
- var event = $.Event('select-blur.dt');
- eventTrigger(dt, event, [e.target, e]);
-
- if (event.isDefaultPrevented()) {
- return;
- }
-
- clear(ctx, true);
- }
- });
-}
-
-/**
- * Trigger an event on a DataTable
- *
- * @param {DataTable.Api} api DataTable to trigger events on
- * @param {boolean} selected true if selected, false if deselected
- * @param {string} type Item type acting on
- * @param {boolean} any Require that there are values before
- * triggering
- * @private
- */
-function eventTrigger(api, type, args, any) {
- if (any && !api.flatten().length) {
- return;
- }
-
- if (typeof type === 'string') {
- type = type + '.dt';
- }
-
- args.unshift(api);
-
- $(api.table().node()).trigger(type, args);
-}
-
-/**
- * Update the information element of the DataTable showing information about the
- * items selected. This is done by adding tags to the existing text
- *
- * @param {DataTable.Api} api DataTable to update
- * @private
- */
-function info(api, node) {
- if (api.select.style() === 'api' || api.select.info() === false) {
- return;
- }
-
- var rows = api.rows({ selected: true }).flatten().length;
- var columns = api.columns({ selected: true }).flatten().length;
- var cells = api.cells({ selected: true }).flatten().length;
-
- var add = function (el, name, num) {
- el.append(
- $(' ').append(
- api.i18n(
- 'select.' + name + 's',
- { _: '%d ' + name + 's selected', 0: '', 1: '1 ' + name + ' selected' },
- num
- )
- )
- );
- };
-
- var el = $(node);
- var output = $(' ');
-
- add(output, 'row', rows);
- add(output, 'column', columns);
- add(output, 'cell', cells);
-
- var existing = el.children('span.select-info');
-
- if (existing.length) {
- existing.remove();
- }
-
- if (output.text() !== '') {
- el.append(output);
- }
-}
-
-/**
- * Add a checkbox to the header for checkbox columns, allowing all rows to
- * be selected, deselected or just to show the state.
- *
- * @param {*} dt API
- */
-function initCheckboxHeader( dt ) {
- // Find any checkbox column(s)
- dt.columns('.dt-select').every(function () {
- var header = this.header();
-
- if (! $('input', header).length) {
- // If no checkbox yet, insert one
- var input = $(' ')
- .attr({
- class: 'dt-select-checkbox',
- type: 'checkbox',
- 'aria-label': dt.i18n('select.aria.headerCheckbox') || 'Select all rows'
- })
- .appendTo(header)
- .on('change', function () {
- if (this.checked) {
- dt.rows({search: 'applied'}).select();
- }
- else {
- dt.rows({selected: true}).deselect();
- }
- })
- .on('click', function (e) {
- e.stopPropagation();
- });
-
- // Update the header checkbox's state when the selection in the
- // table changes
- dt.on('draw select deselect', function (e, pass, type) {
- if (type === 'row' || ! type) {
- var count = dt.rows({selected: true}).count();
- var search = dt.rows({search: 'applied', selected: true}).count();
- var available = dt.rows({search: 'applied'}).count();
-
- if (search && search <= count && search === available) {
- input
- .prop('checked', true)
- .prop('indeterminate', false);
- }
- else if (search === 0 && count === 0) {
- input
- .prop('checked', false)
- .prop('indeterminate', false);
- }
- else {
- input
- .prop('checked', false)
- .prop('indeterminate', true);
- }
- }
- });
- }
- });
-}
-
-/**
- * Initialisation of a new table. Attach event handlers and callbacks to allow
- * Select to operate correctly.
- *
- * This will occur _after_ the initial DataTables initialisation, although
- * before Ajax data is rendered, if there is ajax data
- *
- * @param {DataTable.settings} ctx Settings object to operate on
- * @private
- */
-function init(ctx) {
- var api = new DataTable.Api(ctx);
- ctx._select_init = true;
-
- // Row callback so that classes can be added to rows and cells if the item
- // was selected before the element was created. This will happen with the
- // `deferRender` option enabled.
- //
- // This method of attaching to `aoRowCreatedCallback` is a hack until
- // DataTables has proper events for row manipulation If you are reviewing
- // this code to create your own plug-ins, please do not do this!
- ctx.aoRowCreatedCallback.push(function (row, data, index) {
- var i, ien;
- var d = ctx.aoData[index];
-
- // Row
- if (d._select_selected) {
- $(row).addClass(ctx._select.className);
- }
-
- // Cells and columns - if separated out, we would need to do two
- // loops, so it makes sense to combine them into a single one
- for (i = 0, ien = ctx.aoColumns.length; i < ien; i++) {
- if (
- ctx.aoColumns[i]._select_selected ||
- (d._selected_cells && d._selected_cells[i])
- ) {
- $(d.anCells[i]).addClass(ctx._select.className);
- }
- }
- }
- );
-
- // On Ajax reload we want to reselect all rows which are currently selected,
- // if there is an rowId (i.e. a unique value to identify each row with)
- api.on('preXhr.dt.dtSelect', function (e, settings) {
- if (settings !== api.settings()[0]) {
- // Not triggered by our DataTable!
- return;
- }
-
- // note that column selection doesn't need to be cached and then
- // reselected, as they are already selected
- var rows = api
- .rows({ selected: true })
- .ids(true)
- .filter(function (d) {
- return d !== undefined;
- });
-
- var cells = api
- .cells({ selected: true })
- .eq(0)
- .map(function (cellIdx) {
- var id = api.row(cellIdx.row).id(true);
- return id ? { row: id, column: cellIdx.column } : undefined;
- })
- .filter(function (d) {
- return d !== undefined;
- });
-
- // On the next draw, reselect the currently selected items
- api.one('draw.dt.dtSelect', function () {
- api.rows(rows).select();
-
- // `cells` is not a cell index selector, so it needs a loop
- if (cells.any()) {
- cells.each(function (id) {
- api.cells(id.row, id.column).select();
- });
- }
- });
- });
-
- // Update the table information element with selected item summary
- api.on('info.dt', function (e, ctx, node) {
- // Store the info node for updating on select / deselect
- if (!ctx._select.infoEls.includes(node)) {
- ctx._select.infoEls.push(node);
- }
-
- info(api, node);
- });
-
- api.on('select.dtSelect.dt deselect.dtSelect.dt', function () {
- ctx._select.infoEls.forEach(function (el) {
- info(api, el);
- });
-
- api.state.save();
- });
-
- // Clean up and release
- api.on('destroy.dtSelect', function () {
- // Remove class directly rather than calling deselect - which would trigger events
- $(api.rows({ selected: true }).nodes()).removeClass(api.settings()[0]._select.className);
-
- disableMouseSelection(api);
- api.off('.dtSelect');
- $('body').off('.dtSelect' + _safeId(api.table().node()));
- });
-}
-
-/**
- * Add one or more items (rows or columns) to the selection when shift clicking
- * in OS selection style
- *
- * @param {DataTable.Api} dt DataTable
- * @param {string} type Row or column range selector
- * @param {object} idx Item index to select to
- * @param {object} last Item index to select from
- * @private
- */
-function rowColumnRange(dt, type, idx, last) {
- // Add a range of rows from the last selected row to this one
- var indexes = dt[type + 's']({ search: 'applied' }).indexes();
- var idx1 = indexes.indexOf(last);
- var idx2 = indexes.indexOf(idx);
-
- if (!dt[type + 's']({ selected: true }).any() && idx1 === -1) {
- // select from top to here - slightly odd, but both Windows and Mac OS
- // do this
- indexes.splice(indexes.indexOf(idx) + 1, indexes.length);
- }
- else {
- // reverse so we can shift click 'up' as well as down
- if (idx1 > idx2) {
- var tmp = idx2;
- idx2 = idx1;
- idx1 = tmp;
- }
-
- indexes.splice(idx2 + 1, indexes.length);
- indexes.splice(0, idx1);
- }
-
- if (!dt[type](idx, { selected: true }).any()) {
- // Select range
- dt[type + 's'](indexes).select();
- }
- else {
- // Deselect range - need to keep the clicked on row selected
- indexes.splice(indexes.indexOf(idx), 1);
- dt[type + 's'](indexes).deselect();
- }
-}
-
-/**
- * Clear all selected items
- *
- * @param {DataTable.settings} ctx Settings object of the host DataTable
- * @param {boolean} [force=false] Force the de-selection to happen, regardless
- * of selection style
- * @private
- */
-function clear(ctx, force) {
- if (force || ctx._select.style === 'single') {
- var api = new DataTable.Api(ctx);
-
- api.rows({ selected: true }).deselect();
- api.columns({ selected: true }).deselect();
- api.cells({ selected: true }).deselect();
- }
-}
-
-/**
- * Select items based on the current configuration for style and items.
- *
- * @param {object} e Mouse event object
- * @param {DataTables.Api} dt DataTable
- * @param {DataTable.settings} ctx Settings object of the host DataTable
- * @param {string} type Items to select
- * @param {int|object} idx Index of the item to select
- * @private
- */
-function typeSelect(e, dt, ctx, type, idx) {
- var style = dt.select.style();
- var toggleable = dt.select.toggleable();
- var isSelected = dt[type](idx, { selected: true }).any();
-
- if (isSelected && !toggleable) {
- return;
- }
-
- if (style === 'os') {
- if (e.ctrlKey || e.metaKey) {
- // Add or remove from the selection
- dt[type](idx).select(!isSelected);
- }
- else if (e.shiftKey) {
- if (type === 'cell') {
- cellRange(dt, idx, ctx._select_lastCell || null);
- }
- else {
- rowColumnRange(
- dt,
- type,
- idx,
- ctx._select_lastCell ? ctx._select_lastCell[type] : null
- );
- }
- }
- else {
- // No cmd or shift click - deselect if selected, or select
- // this row only
- var selected = dt[type + 's']({ selected: true });
-
- if (isSelected && selected.flatten().length === 1) {
- dt[type](idx).deselect();
- }
- else {
- selected.deselect();
- dt[type](idx).select();
- }
- }
- }
- else if (style == 'multi+shift') {
- if (e.shiftKey) {
- if (type === 'cell') {
- cellRange(dt, idx, ctx._select_lastCell || null);
- }
- else {
- rowColumnRange(
- dt,
- type,
- idx,
- ctx._select_lastCell ? ctx._select_lastCell[type] : null
- );
- }
- }
- else {
- dt[type](idx).select(!isSelected);
- }
- }
- else {
- dt[type](idx).select(!isSelected);
- }
-}
-
-function _safeId(node) {
- return node.id.replace(/[^a-zA-Z0-9\-\_]/g, '-');
-}
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * DataTables selectors
- */
-
-// row and column are basically identical just assigned to different properties
-// and checking a different array, so we can dynamically create the functions to
-// reduce the code size
-$.each(
- [
- { type: 'row', prop: 'aoData' },
- { type: 'column', prop: 'aoColumns' }
- ],
- function (i, o) {
- DataTable.ext.selector[o.type].push(function (settings, opts, indexes) {
- var selected = opts.selected;
- var data;
- var out = [];
-
- if (selected !== true && selected !== false) {
- return indexes;
- }
-
- for (var i = 0, ien = indexes.length; i < ien; i++) {
- data = settings[o.prop][indexes[i]];
-
- if (
- data && (
- (selected === true && data._select_selected === true) ||
- (selected === false && !data._select_selected)
- )
- ) {
- out.push(indexes[i]);
- }
- }
-
- return out;
- });
- }
-);
-
-DataTable.ext.selector.cell.push(function (settings, opts, cells) {
- var selected = opts.selected;
- var rowData;
- var out = [];
-
- if (selected === undefined) {
- return cells;
- }
-
- for (var i = 0, ien = cells.length; i < ien; i++) {
- rowData = settings.aoData[cells[i].row];
-
- if (
- rowData && (
- (selected === true &&
- rowData._selected_cells &&
- rowData._selected_cells[cells[i].column] === true) ||
- (selected === false &&
- (!rowData._selected_cells || !rowData._selected_cells[cells[i].column]))
- )
- ) {
- out.push(cells[i]);
- }
- }
-
- return out;
-});
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * DataTables API
- *
- * For complete documentation, please refer to the docs/api directory or the
- * DataTables site
- */
-
-// Local variables to improve compression
-var apiRegister = DataTable.Api.register;
-var apiRegisterPlural = DataTable.Api.registerPlural;
-
-apiRegister('select()', function () {
- return this.iterator('table', function (ctx) {
- DataTable.select.init(new DataTable.Api(ctx));
- });
-});
-
-apiRegister('select.blurable()', function (flag) {
- if (flag === undefined) {
- return this.context[0]._select.blurable;
- }
-
- return this.iterator('table', function (ctx) {
- ctx._select.blurable = flag;
- });
-});
-
-apiRegister('select.toggleable()', function (flag) {
- if (flag === undefined) {
- return this.context[0]._select.toggleable;
- }
-
- return this.iterator('table', function (ctx) {
- ctx._select.toggleable = flag;
- });
-});
-
-apiRegister('select.info()', function (flag) {
- if (flag === undefined) {
- return this.context[0]._select.info;
- }
-
- return this.iterator('table', function (ctx) {
- ctx._select.info = flag;
- });
-});
-
-apiRegister('select.items()', function (items) {
- if (items === undefined) {
- return this.context[0]._select.items;
- }
-
- return this.iterator('table', function (ctx) {
- ctx._select.items = items;
-
- eventTrigger(new DataTable.Api(ctx), 'selectItems', [items]);
- });
-});
-
-// Takes effect from the _next_ selection. None disables future selection, but
-// does not clear the current selection. Use the `deselect` methods for that
-apiRegister('select.style()', function (style) {
- if (style === undefined) {
- return this.context[0]._select.style;
- }
-
- return this.iterator('table', function (ctx) {
- if (!ctx._select) {
- DataTable.select.init(new DataTable.Api(ctx));
- }
-
- if (!ctx._select_init) {
- init(ctx);
- }
-
- ctx._select.style = style;
-
- // Add / remove mouse event handlers. They aren't required when only
- // API selection is available
- var dt = new DataTable.Api(ctx);
- disableMouseSelection(dt);
-
- if (style !== 'api') {
- enableMouseSelection(dt);
- }
-
- eventTrigger(new DataTable.Api(ctx), 'selectStyle', [style]);
- });
-});
-
-apiRegister('select.selector()', function (selector) {
- if (selector === undefined) {
- return this.context[0]._select.selector;
- }
-
- return this.iterator('table', function (ctx) {
- disableMouseSelection(new DataTable.Api(ctx));
-
- ctx._select.selector = selector;
-
- if (ctx._select.style !== 'api') {
- enableMouseSelection(new DataTable.Api(ctx));
- }
- });
-});
-
-apiRegister('select.last()', function (set) {
- let ctx = this.context[0];
-
- if (set) {
- ctx._select_lastCell = set;
- return this;
- }
-
- return ctx._select_lastCell;
-});
-
-apiRegisterPlural('rows().select()', 'row().select()', function (select) {
- var api = this;
-
- if (select === false) {
- return this.deselect();
- }
-
- this.iterator('row', function (ctx, idx) {
- clear(ctx);
-
- // There is a good amount of knowledge of DataTables internals in
- // this function. It _could_ be done without that, but it would hurt
- // performance (or DT would need new APIs for this work)
- var dtData = ctx.aoData[idx];
- var dtColumns = ctx.aoColumns;
-
- $(dtData.nTr).addClass(ctx._select.className);
- dtData._select_selected = true;
-
- for (var i=0 ; i 0);
- });
-
- this.disable();
- },
- destroy: function (dt, node, config) {
- dt.off(config._eventNamespace);
- }
- },
- showSelected: {
- text: i18n('showSelected', 'Show only selected'),
- className: 'buttons-show-selected',
- action: function (e, dt) {
- if (dt.search.fixed('dt-select')) {
- // Remove existing function
- dt.search.fixed('dt-select', null);
-
- this.active(false);
- }
- else {
- // Use a fixed filtering function to match on selected rows
- // This needs to reference the internal aoData since that is
- // where Select stores its reference for the selected state
- var dataSrc = dt.settings()[0].aoData;
-
- dt.search.fixed('dt-select', function (text, data, idx) {
- // _select_selected is set by Select on the data object for the row
- return dataSrc[idx]._select_selected;
- });
-
- this.active(true);
- }
-
- dt.draw();
- }
- }
-});
-
-$.each(['Row', 'Column', 'Cell'], function (i, item) {
- var lc = item.toLowerCase();
-
- DataTable.ext.buttons['select' + item + 's'] = {
- text: i18n('select' + item + 's', 'Select ' + lc + 's'),
- className: 'buttons-select-' + lc + 's',
- action: function () {
- this.select.items(lc);
- },
- init: function (dt) {
- var that = this;
-
- dt.on('selectItems.dt.DT', function (e, ctx, items) {
- that.active(items === lc);
- });
- }
- };
-});
-
-DataTable.type('select-checkbox', {
- className: 'dt-select',
- detect: function (data) {
- // Rendering function will tell us if it is a checkbox type
- return data === 'select-checkbox' ? data : false;
- },
- order: {
- pre: function (d) {
- return d === 'X' ? -1 : 0;
- }
- }
-});
-
-$.extend(true, DataTable.defaults.oLanguage, {
- select: {
- aria: {
- rowCheckbox: 'Select row'
- }
- }
-});
-
-DataTable.render.select = function (valueProp, nameProp) {
- var valueFn = valueProp ? DataTable.util.get(valueProp) : null;
- var nameFn = nameProp ? DataTable.util.get(nameProp) : null;
-
- return function (data, type, row, meta) {
- var dtRow = meta.settings.aoData[meta.row];
- var selected = dtRow._select_selected;
- var ariaLabel = meta.settings.oLanguage.select.aria.rowCheckbox;
-
- if (type === 'display') {
- return $(' ')
- .attr({
- 'aria-label': ariaLabel,
- class: 'dt-select-checkbox',
- name: nameFn ? nameFn(row) : null,
- type: 'checkbox',
- value: valueFn ? valueFn(row) : null,
- checked: selected
- })[0];
- }
- else if (type === 'type') {
- return 'select-checkbox';
- }
- else if (type === 'filter') {
- return '';
- }
-
- return selected ? 'X' : '';
- }
-}
-
-// Legacy checkbox ordering
-DataTable.ext.order['select-checkbox'] = function (settings, col) {
- return this.api()
- .column(col, { order: 'index' })
- .nodes()
- .map(function (td) {
- if (settings._select.items === 'row') {
- return $(td).parent().hasClass(settings._select.className);
- }
- else if (settings._select.items === 'cell') {
- return $(td).hasClass(settings._select.className);
- }
- return false;
- });
-};
-
-$.fn.DataTable.select = DataTable.select;
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Initialisation
- */
-
-// DataTables creation - check if select has been defined in the options. Note
-// this required that the table be in the document! If it isn't then something
-// needs to trigger this method unfortunately. The next major release of
-// DataTables will rework the events and address this.
-$(document).on('preInit.dt.dtSelect', function (e, ctx) {
- if (e.namespace !== 'dt') {
- return;
- }
-
- DataTable.select.init(new DataTable.Api(ctx));
-});
-
-
-export default DataTable;
diff --git a/assets/js/register_events.js b/assets/js/register_events.js
index 547742ea..1c6ed09b 100644
--- a/assets/js/register_events.js
+++ b/assets/js/register_events.js
@@ -19,15 +19,14 @@
'use strict';
-import {Dropdown} from "bootstrap";
+import {Dropdown, Modal, Tooltip} from "bootstrap";
import ClipboardJS from "clipboard";
-import {Modal} from "bootstrap";
class RegisterEventHelper {
constructor() {
this.registerTooltips();
this.configureDropdowns();
-
+
// Only register special character input if enabled in configuration
const keybindingsEnabled = document.body.dataset.keybindingsSpecialCharacters !== 'false';
if (keybindingsEnabled) {
@@ -40,8 +39,6 @@ class RegisterEventHelper {
});
this.registerModalDropRemovalOnFormSubmit();
-
-
}
registerModalDropRemovalOnFormSubmit() {
@@ -83,11 +80,17 @@ class RegisterEventHelper {
registerTooltips() {
const handler = () => {
- $(".tooltip").remove();
+ document.querySelectorAll('.tooltip').forEach(el => el.remove());
+
//Exclude dropdown buttons from tooltips, otherwise we run into endless errors from bootstrap (bootstrap.esm.js:614 Bootstrap doesn't allow more than one instance per element. Bound instance: bs.dropdown.)
- $('a[title], label[title], button[title]:not([data-bs-toggle="dropdown"]), p[title], span[title], h6[title], h3[title], i[title], small[title]')
- //@ts-ignore
- .tooltip("hide").tooltip({container: "body", placement: "auto", boundary: 'window'});
+ const tooltipSelector = 'a[title], label[title], button[title]:not([data-bs-toggle="dropdown"]), p[title], span[title], h6[title], h3[title], i[title], small[title]';
+ document.querySelectorAll(tooltipSelector).forEach(el => {
+ const existing = Tooltip.getInstance(el);
+ if (existing) {
+ existing.dispose();
+ }
+ new Tooltip(el, {container: 'body', placement: 'auto', boundary: 'window'});
+ });
};
this.registerLoadHandler(handler);
@@ -95,242 +98,240 @@ class RegisterEventHelper {
}
registerSpecialCharInput() {
- this.registerLoadHandler(() => {
- //@ts-ignore
- $("input[type=text], input[type=search]").unbind("keydown").keydown(function (event) {
- let use_special_char = event.altKey;
-
- let greek_char = "";
- if (use_special_char){
- //Use the key property to determine the greek letter (as it is independent of the keyboard layout)
- switch(event.key) {
- //Greek letters
- case "a": //Alpha (lowercase)
- greek_char = "\u03B1";
- break;
- case "A": //Alpha (uppercase)
- greek_char = "\u0391";
- break;
- case "b": //Beta (lowercase)
- greek_char = "\u03B2";
- break;
- case "B": //Beta (uppercase)
- greek_char = "\u0392";
- break;
- case "g": //Gamma (lowercase)
- greek_char = "\u03B3";
- break;
- case "G": //Gamma (uppercase)
- greek_char = "\u0393";
- break;
- case "d": //Delta (lowercase)
- greek_char = "\u03B4";
- break;
- case "D": //Delta (uppercase)
- greek_char = "\u0394";
- break;
- case "e": //Epsilon (lowercase)
- greek_char = "\u03B5";
- break;
- case "E": //Epsilon (uppercase)
- greek_char = "\u0395";
- break;
- case "z": //Zeta (lowercase)
- greek_char = "\u03B6";
- break;
- case "Z": //Zeta (uppercase)
- greek_char = "\u0396";
- break;
- case "h": //Eta (lowercase)
- greek_char = "\u03B7";
- break;
- case "H": //Eta (uppercase)
- greek_char = "\u0397";
- break;
- case "q": //Theta (lowercase)
- greek_char = "\u03B8";
- break;
- case "Q": //Theta (uppercase)
- greek_char = "\u0398";
- break;
- case "i": //Iota (lowercase)
- greek_char = "\u03B9";
- break;
- case "I": //Iota (uppercase)
- greek_char = "\u0399";
- break;
- case "k": //Kappa (lowercase)
- greek_char = "\u03BA";
- break;
- case "K": //Kappa (uppercase)
- greek_char = "\u039A";
- break;
- case "l": //Lambda (lowercase)
- greek_char = "\u03BB";
- break;
- case "L": //Lambda (uppercase)
- greek_char = "\u039B";
- break;
- case "m": //Mu (lowercase)
- greek_char = "\u03BC";
- break;
- case "M": //Mu (uppercase)
- greek_char = "\u039C";
- break;
- case "n": //Nu (lowercase)
- greek_char = "\u03BD";
- break;
- case "N": //Nu (uppercase)
- greek_char = "\u039D";
- break;
- case "x": //Xi (lowercase)
- greek_char = "\u03BE";
- break;
- case "X": //Xi (uppercase)
- greek_char = "\u039E";
- break;
- case "o": //Omicron (lowercase)
- greek_char = "\u03BF";
- break;
- case "O": //Omicron (uppercase)
- greek_char = "\u039F";
- break;
- case "p": //Pi (lowercase)
- greek_char = "\u03C0";
- break;
- case "P": //Pi (uppercase)
- greek_char = "\u03A0";
- break;
- case "r": //Rho (lowercase)
- greek_char = "\u03C1";
- break;
- case "R": //Rho (uppercase)
- greek_char = "\u03A1";
- break;
- case "s": //Sigma (lowercase)
- greek_char = "\u03C3";
- break;
- case "S": //Sigma (uppercase)
- greek_char = "\u03A3";
- break;
- case "t": //Tau (lowercase)
- greek_char = "\u03C4";
- break;
- case "T": //Tau (uppercase)
- greek_char = "\u03A4";
- break;
- case "u": //Upsilon (lowercase)
- greek_char = "\u03C5";
- break;
- case "U": //Upsilon (uppercase)
- greek_char = "\u03A5";
- break;
- case "f": //Phi (lowercase)
- greek_char = "\u03C6";
- break;
- case "F": //Phi (uppercase)
- greek_char = "\u03A6";
- break;
- case "c": //Chi (lowercase)
- greek_char = "\u03C7";
- break;
- case "C": //Chi (uppercase)
- greek_char = "\u03A7";
- break;
- case "y": //Psi (lowercase)
- greek_char = "\u03C8";
- break;
- case "Y": //Psi (uppercase)
- greek_char = "\u03A8";
- break;
- case "w": //Omega (lowercase)
- greek_char = "\u03C9";
- break;
- case "W": //Omega (uppercase)
- greek_char = "\u03A9";
- break;
- }
-
- //Use keycodes for special characters as the shift char on the number keys are layout dependent
- switch (event.keyCode) {
- case 49: //1 key
- //Product symbol on shift, sum on no shift
- greek_char = event.shiftKey ? "\u220F" : "\u2211";
- break;
- case 50: //2 key
- //Integral on no shift, partial derivative on shift
- greek_char = event.shiftKey ? "\u2202" : "\u222B";
- break;
- case 51: //3 key
- //Less than or equal on no shift, greater than or equal on shift
- greek_char = event.shiftKey ? "\u2265" : "\u2264";
- break;
- case 52: //4 key
- //Empty set on shift, infinity on no shift
- greek_char = event.shiftKey ? "\u2205" : "\u221E";
- break;
- case 53: //5 key
- //Not equal on shift, approx equal on no shift
- greek_char = event.shiftKey ? "\u2260" : "\u2248";
- break;
- case 54: //6 key
- //Element of on no shift, not element of on shift
- greek_char = event.shiftKey ? "\u2209" : "\u2208";
- break;
- case 55: //7 key
- //And on shift, or on no shift
- greek_char = event.shiftKey ? "\u2227" : "\u2228";
- break;
- case 56: //8 key
- //Proportional to on shift, angle on no shift
- greek_char = event.shiftKey ? "\u221D" : "\u2220";
- break;
- case 57: //9 key
- //Cube root on shift, square root on no shift
- greek_char = event.shiftKey ? "\u221B" : "\u221A";
- break;
- case 48: //0 key
- //Minus-Plus on shift, plus-minus on no shift
- greek_char = event.shiftKey ? "\u2213" : "\u00B1";
- break;
-
- //Special characters
- case 219: //hyphen (or ß on german layout)
- //Copyright on no shift, TM on shift
- greek_char = event.shiftKey ? "\u2122" : "\u00A9";
- break;
- case 191: //forward slash (or # on german layout)
- //Generic currency on no shift, paragraph on shift
- greek_char = event.shiftKey ? "\u00B6" : "\u00A4";
- break;
-
- //Currency symbols
- case 192: //: or (ö on german layout)
- //Euro on no shift, pound on shift
- greek_char = event.shiftKey ? "\u00A3" : "\u20AC";
- break;
- case 221: //; or (ä on german layout)
- //Yen on no shift, dollar on shift
- greek_char = event.shiftKey ? "\u0024" : "\u00A5";
- break;
-
-
- }
-
- if(greek_char=="") return;
-
- let $txt = $(this);
- //@ts-ignore
- let caretPos = $txt[0].selectionStart;
- let textAreaTxt = $txt.val().toString();
- $txt.val(textAreaTxt.substring(0, caretPos) + greek_char + textAreaTxt.substring(caretPos) );
+ const keydownHandler = function(event) {
+ let use_special_char = event.altKey;
+ let greek_char = "";
+ if (use_special_char){
+ //Use the key property to determine the greek letter (as it is independent of the keyboard layout)
+ switch(event.key) {
+ //Greek letters
+ case "a": //Alpha (lowercase)
+ greek_char = "α";
+ break;
+ case "A": //Alpha (uppercase)
+ greek_char = "Α";
+ break;
+ case "b": //Beta (lowercase)
+ greek_char = "β";
+ break;
+ case "B": //Beta (uppercase)
+ greek_char = "Β";
+ break;
+ case "g": //Gamma (lowercase)
+ greek_char = "γ";
+ break;
+ case "G": //Gamma (uppercase)
+ greek_char = "Γ";
+ break;
+ case "d": //Delta (lowercase)
+ greek_char = "δ";
+ break;
+ case "D": //Delta (uppercase)
+ greek_char = "Δ";
+ break;
+ case "e": //Epsilon (lowercase)
+ greek_char = "ε";
+ break;
+ case "E": //Epsilon (uppercase)
+ greek_char = "Ε";
+ break;
+ case "z": //Zeta (lowercase)
+ greek_char = "ζ";
+ break;
+ case "Z": //Zeta (uppercase)
+ greek_char = "Ζ";
+ break;
+ case "h": //Eta (lowercase)
+ greek_char = "η";
+ break;
+ case "H": //Eta (uppercase)
+ greek_char = "Η";
+ break;
+ case "q": //Theta (lowercase)
+ greek_char = "θ";
+ break;
+ case "Q": //Theta (uppercase)
+ greek_char = "Θ";
+ break;
+ case "i": //Iota (lowercase)
+ greek_char = "ι";
+ break;
+ case "I": //Iota (uppercase)
+ greek_char = "Ι";
+ break;
+ case "k": //Kappa (lowercase)
+ greek_char = "κ";
+ break;
+ case "K": //Kappa (uppercase)
+ greek_char = "Κ";
+ break;
+ case "l": //Lambda (lowercase)
+ greek_char = "λ";
+ break;
+ case "L": //Lambda (uppercase)
+ greek_char = "Λ";
+ break;
+ case "m": //Mu (lowercase)
+ greek_char = "μ";
+ break;
+ case "M": //Mu (uppercase)
+ greek_char = "Μ";
+ break;
+ case "n": //Nu (lowercase)
+ greek_char = "ν";
+ break;
+ case "N": //Nu (uppercase)
+ greek_char = "Ν";
+ break;
+ case "x": //Xi (lowercase)
+ greek_char = "ξ";
+ break;
+ case "X": //Xi (uppercase)
+ greek_char = "Ξ";
+ break;
+ case "o": //Omicron (lowercase)
+ greek_char = "ο";
+ break;
+ case "O": //Omicron (uppercase)
+ greek_char = "Ο";
+ break;
+ case "p": //Pi (lowercase)
+ greek_char = "π";
+ break;
+ case "P": //Pi (uppercase)
+ greek_char = "Π";
+ break;
+ case "r": //Rho (lowercase)
+ greek_char = "ρ";
+ break;
+ case "R": //Rho (uppercase)
+ greek_char = "Ρ";
+ break;
+ case "s": //Sigma (lowercase)
+ greek_char = "σ";
+ break;
+ case "S": //Sigma (uppercase)
+ greek_char = "Σ";
+ break;
+ case "t": //Tau (lowercase)
+ greek_char = "τ";
+ break;
+ case "T": //Tau (uppercase)
+ greek_char = "Τ";
+ break;
+ case "u": //Upsilon (lowercase)
+ greek_char = "υ";
+ break;
+ case "U": //Upsilon (uppercase)
+ greek_char = "Υ";
+ break;
+ case "f": //Phi (lowercase)
+ greek_char = "φ";
+ break;
+ case "F": //Phi (uppercase)
+ greek_char = "Φ";
+ break;
+ case "c": //Chi (lowercase)
+ greek_char = "χ";
+ break;
+ case "C": //Chi (uppercase)
+ greek_char = "Χ";
+ break;
+ case "y": //Psi (lowercase)
+ greek_char = "ψ";
+ break;
+ case "Y": //Psi (uppercase)
+ greek_char = "Ψ";
+ break;
+ case "w": //Omega (lowercase)
+ greek_char = "ω";
+ break;
+ case "W": //Omega (uppercase)
+ greek_char = "Ω";
+ break;
}
+
+ //Use keycodes for special characters as the shift char on the number keys are layout dependent
+ switch (event.keyCode) {
+ case 49: //1 key
+ //Product symbol on shift, sum on no shift
+ greek_char = event.shiftKey ? "∏" : "∑";
+ break;
+ case 50: //2 key
+ //Integral on no shift, partial derivative on shift
+ greek_char = event.shiftKey ? "∂" : "∫";
+ break;
+ case 51: //3 key
+ //Less than or equal on no shift, greater than or equal on shift
+ greek_char = event.shiftKey ? "≥" : "≤";
+ break;
+ case 52: //4 key
+ //Empty set on shift, infinity on no shift
+ greek_char = event.shiftKey ? "∅" : "∞";
+ break;
+ case 53: //5 key
+ //Not equal on shift, approx equal on no shift
+ greek_char = event.shiftKey ? "≠" : "≈";
+ break;
+ case 54: //6 key
+ //Element of on no shift, not element of on shift
+ greek_char = event.shiftKey ? "∉" : "∈";
+ break;
+ case 55: //7 key
+ //And on shift, or on no shift
+ greek_char = event.shiftKey ? "∧" : "∨";
+ break;
+ case 56: //8 key
+ //Proportional to on shift, angle on no shift
+ greek_char = event.shiftKey ? "∝" : "∠";
+ break;
+ case 57: //9 key
+ //Cube root on shift, square root on no shift
+ greek_char = event.shiftKey ? "∛" : "√";
+ break;
+ case 48: //0 key
+ //Minus-Plus on shift, plus-minus on no shift
+ greek_char = event.shiftKey ? "∓" : "±";
+ break;
+
+ //Special characters
+ case 219: //hyphen (or ß on german layout)
+ //Copyright on no shift, TM on shift
+ greek_char = event.shiftKey ? "™" : "©";
+ break;
+ case 191: //forward slash (or # on german layout)
+ //Generic currency on no shift, paragraph on shift
+ greek_char = event.shiftKey ? "¶" : "¤";
+ break;
+
+ //Currency symbols
+ case 192: //: or (ö on german layout)
+ //Euro on no shift, pound on shift
+ greek_char = event.shiftKey ? "£" : "€";
+ break;
+ case 221: //; or (ä on german layout)
+ //Yen on no shift, dollar on shift
+ greek_char = event.shiftKey ? "$" : "¥";
+ break;
+ }
+
+ if(greek_char=="") return;
+
+ const txt = event.currentTarget;
+ const caretPos = txt.selectionStart;
+ const textAreaTxt = txt.value;
+ txt.value = textAreaTxt.substring(0, caretPos) + greek_char + textAreaTxt.substring(caretPos);
+ }
+ };
+
+ this.registerLoadHandler(() => {
+ document.querySelectorAll('input[type=text], input[type=search]').forEach(input => {
+ input.removeEventListener('keydown', keydownHandler);
+ input.addEventListener('keydown', keydownHandler);
});
- //@ts-ignore
- this.greek_once = true;
- })
+ });
}
}
-export default new RegisterEventHelper();
\ No newline at end of file
+export default new RegisterEventHelper();
diff --git a/assets/css/components/bootbox_extensions.css b/assets/themes/brite.js
similarity index 58%
rename from assets/css/components/bootbox_extensions.css
rename to assets/themes/brite.js
index 42bbd78d..41b82e93 100644
--- a/assets/css/components/bootbox_extensions.css
+++ b/assets/themes/brite.js
@@ -1,7 +1,7 @@
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
- * Copyright (C) 2019 - 2022 Jan Böhmer (https://github.com/jbtronics)
+ * Copyright (C) 2019 - 2026 Jan Böhmer (https://github.com/jbtronics)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
@@ -17,30 +17,4 @@
* along with this program. If not, see .
*/
-.modal-body > .bootbox-close-button {
- position: absolute;
- top: 0;
- right: 0;
- padding: 0.5rem 0.75rem;
- z-index: 1;
-}
-.modal .bootbox-close-button {
- font-weight: 100;
-}
-
-button.bootbox-close-button {
- padding: 0;
- background-color: transparent;
- border: 0;
- -webkit-appearance: none;
-}
-
-.bootbox-close-button {
- /* float: right; */
- font-size: 1.40625rem;
- font-weight: 600;
- line-height: 1;
- color: #000;
- text-shadow: none;
- opacity: .5;
-}
\ No newline at end of file
+import "bootswatch/dist/brite/bootstrap.css";
diff --git a/composer.json b/composer.json
index f08f824d..10c9b702 100644
--- a/composer.json
+++ b/composer.json
@@ -57,9 +57,10 @@
"scheb/2fa-trusted-device": "^v7.11.0",
"shivas/versioning-bundle": "^4.0",
"spatie/db-dumper": "^3.3.1",
- "symfony/ai-bundle": "^0.9.0",
- "symfony/ai-lm-studio-platform": "^0.9.0",
- "symfony/ai-open-router-platform": "^0.9.0",
+ "symfony/ai-bundle": "^0.10.0",
+ "symfony/ai-lm-studio-platform": "^v0.10.0",
+ "symfony/ai-ollama-platform": "^0.10.0",
+ "symfony/ai-open-router-platform": "^0.10.0",
"symfony/apache-pack": "^1.0",
"symfony/asset": "7.4.*",
"symfony/console": "7.4.*",
diff --git a/composer.lock b/composer.lock
index 71918b0f..dc8dc699 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,20 +4,20 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "1307bf553d1543704e3c03398dec06c7",
+ "content-hash": "6a79fa73091c2e15bce035c85c2d61ed",
"packages": [
{
"name": "amphp/amp",
- "version": "v3.1.1",
+ "version": "v3.1.2",
"source": {
"type": "git",
"url": "https://github.com/amphp/amp.git",
- "reference": "fa0ab33a6f47a82929c38d03ca47ebb71086a93f"
+ "reference": "2f3ebed5a4f663968a0590dbb7654a8b32cb63cb"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/amphp/amp/zipball/fa0ab33a6f47a82929c38d03ca47ebb71086a93f",
- "reference": "fa0ab33a6f47a82929c38d03ca47ebb71086a93f",
+ "url": "https://api.github.com/repos/amphp/amp/zipball/2f3ebed5a4f663968a0590dbb7654a8b32cb63cb",
+ "reference": "2f3ebed5a4f663968a0590dbb7654a8b32cb63cb",
"shasum": ""
},
"require": {
@@ -27,7 +27,7 @@
"require-dev": {
"amphp/php-cs-fixer-config": "^2",
"phpunit/phpunit": "^9",
- "psalm/phar": "5.23.1"
+ "psalm/phar": "6.16.1"
},
"type": "library",
"autoload": {
@@ -77,7 +77,7 @@
],
"support": {
"issues": "https://github.com/amphp/amp/issues",
- "source": "https://github.com/amphp/amp/tree/v3.1.1"
+ "source": "https://github.com/amphp/amp/tree/v3.1.2"
},
"funding": [
{
@@ -85,7 +85,7 @@
"type": "github"
}
],
- "time": "2025-08-27T21:42:00+00:00"
+ "time": "2026-06-21T13:59:44+00:00"
},
{
"name": "amphp/byte-stream",
@@ -615,16 +615,16 @@
},
{
"name": "amphp/pipeline",
- "version": "v1.2.4",
+ "version": "v1.2.5",
"source": {
"type": "git",
"url": "https://github.com/amphp/pipeline.git",
- "reference": "a044733e080940d1483f56caff0c412ad6982776"
+ "reference": "92f121dde31cd1d89d5d0f9eba64ac40271b236e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/amphp/pipeline/zipball/a044733e080940d1483f56caff0c412ad6982776",
- "reference": "a044733e080940d1483f56caff0c412ad6982776",
+ "url": "https://api.github.com/repos/amphp/pipeline/zipball/92f121dde31cd1d89d5d0f9eba64ac40271b236e",
+ "reference": "92f121dde31cd1d89d5d0f9eba64ac40271b236e",
"shasum": ""
},
"require": {
@@ -670,7 +670,7 @@
],
"support": {
"issues": "https://github.com/amphp/pipeline/issues",
- "source": "https://github.com/amphp/pipeline/tree/v1.2.4"
+ "source": "https://github.com/amphp/pipeline/tree/v1.2.5"
},
"funding": [
{
@@ -678,7 +678,7 @@
"type": "github"
}
],
- "time": "2026-05-06T05:37:57+00:00"
+ "time": "2026-06-27T14:17:20+00:00"
},
{
"name": "amphp/process",
@@ -976,16 +976,16 @@
},
{
"name": "api-platform/doctrine-common",
- "version": "v4.3.13",
+ "version": "v4.3.15",
"source": {
"type": "git",
"url": "https://github.com/api-platform/doctrine-common.git",
- "reference": "a342c7e4cd4a7545d355b8eaae6d2f46de4f8936"
+ "reference": "e4dee10c45bd701c5984321bc98adc0c3760ec48"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/api-platform/doctrine-common/zipball/a342c7e4cd4a7545d355b8eaae6d2f46de4f8936",
- "reference": "a342c7e4cd4a7545d355b8eaae6d2f46de4f8936",
+ "url": "https://api.github.com/repos/api-platform/doctrine-common/zipball/e4dee10c45bd701c5984321bc98adc0c3760ec48",
+ "reference": "e4dee10c45bd701c5984321bc98adc0c3760ec48",
"shasum": ""
},
"require": {
@@ -1060,22 +1060,22 @@
"rest"
],
"support": {
- "source": "https://github.com/api-platform/doctrine-common/tree/v4.3.13"
+ "source": "https://github.com/api-platform/doctrine-common/tree/v4.3.15"
},
- "time": "2026-06-05T09:05:29+00:00"
+ "time": "2026-06-16T14:59:18+00:00"
},
{
"name": "api-platform/doctrine-orm",
- "version": "v4.3.13",
+ "version": "v4.3.15",
"source": {
"type": "git",
"url": "https://github.com/api-platform/doctrine-orm.git",
- "reference": "7d8851966a55d2f53e4d86b281ba543fa14e953b"
+ "reference": "6af3eeefc7d483b83e56bcbdbff0dd0dde3c7fc1"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/api-platform/doctrine-orm/zipball/7d8851966a55d2f53e4d86b281ba543fa14e953b",
- "reference": "7d8851966a55d2f53e4d86b281ba543fa14e953b",
+ "url": "https://api.github.com/repos/api-platform/doctrine-orm/zipball/6af3eeefc7d483b83e56bcbdbff0dd0dde3c7fc1",
+ "reference": "6af3eeefc7d483b83e56bcbdbff0dd0dde3c7fc1",
"shasum": ""
},
"require": {
@@ -1149,13 +1149,13 @@
"rest"
],
"support": {
- "source": "https://github.com/api-platform/doctrine-orm/tree/v4.3.13"
+ "source": "https://github.com/api-platform/doctrine-orm/tree/v4.3.15"
},
- "time": "2026-06-13T04:46:03+00:00"
+ "time": "2026-06-16T13:14:05+00:00"
},
{
"name": "api-platform/documentation",
- "version": "v4.3.13",
+ "version": "v4.3.15",
"source": {
"type": "git",
"url": "https://github.com/api-platform/documentation.git",
@@ -1212,13 +1212,13 @@
],
"description": "API Platform documentation controller.",
"support": {
- "source": "https://github.com/api-platform/documentation/tree/v4.3.13"
+ "source": "https://github.com/api-platform/documentation/tree/v4.4.0-alpha.1"
},
"time": "2026-04-30T12:21:24+00:00"
},
{
"name": "api-platform/http-cache",
- "version": "v4.3.13",
+ "version": "v4.3.15",
"source": {
"type": "git",
"url": "https://github.com/api-platform/http-cache.git",
@@ -1292,22 +1292,22 @@
"rest"
],
"support": {
- "source": "https://github.com/api-platform/http-cache/tree/v4.3.13"
+ "source": "https://github.com/api-platform/http-cache/tree/v4.4.0-alpha.1"
},
"time": "2026-06-09T14:20:49+00:00"
},
{
"name": "api-platform/hydra",
- "version": "v4.3.13",
+ "version": "v4.3.15",
"source": {
"type": "git",
"url": "https://github.com/api-platform/hydra.git",
- "reference": "c9c53cfc9cdf9a2b8499d49dc70cf3ea9dbf8463"
+ "reference": "2570883edf78970ad845f6eb7d69ae7894e6c480"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/api-platform/hydra/zipball/c9c53cfc9cdf9a2b8499d49dc70cf3ea9dbf8463",
- "reference": "c9c53cfc9cdf9a2b8499d49dc70cf3ea9dbf8463",
+ "url": "https://api.github.com/repos/api-platform/hydra/zipball/2570883edf78970ad845f6eb7d69ae7894e6c480",
+ "reference": "2570883edf78970ad845f6eb7d69ae7894e6c480",
"shasum": ""
},
"require": {
@@ -1379,22 +1379,22 @@
"rest"
],
"support": {
- "source": "https://github.com/api-platform/hydra/tree/v4.3.13"
+ "source": "https://github.com/api-platform/hydra/tree/v4.3.15"
},
- "time": "2026-06-13T05:11:46+00:00"
+ "time": "2026-06-22T16:14:53+00:00"
},
{
"name": "api-platform/json-api",
- "version": "v4.3.13",
+ "version": "v4.3.15",
"source": {
"type": "git",
"url": "https://github.com/api-platform/json-api.git",
- "reference": "dbff4fd044b131c63fe7540da796e01cda2814d2"
+ "reference": "30f70ddc6d865e9c36d99c0255bb1f407c4d4258"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/api-platform/json-api/zipball/dbff4fd044b131c63fe7540da796e01cda2814d2",
- "reference": "dbff4fd044b131c63fe7540da796e01cda2814d2",
+ "url": "https://api.github.com/repos/api-platform/json-api/zipball/30f70ddc6d865e9c36d99c0255bb1f407c4d4258",
+ "reference": "30f70ddc6d865e9c36d99c0255bb1f407c4d4258",
"shasum": ""
},
"require": {
@@ -1461,13 +1461,13 @@
"rest"
],
"support": {
- "source": "https://github.com/api-platform/json-api/tree/v4.3.13"
+ "source": "https://github.com/api-platform/json-api/tree/v4.3.15"
},
- "time": "2026-06-13T05:11:46+00:00"
+ "time": "2026-06-17T18:14:46+00:00"
},
{
"name": "api-platform/json-schema",
- "version": "v4.3.13",
+ "version": "v4.3.15",
"source": {
"type": "git",
"url": "https://github.com/api-platform/json-schema.git",
@@ -1542,13 +1542,13 @@
"swagger"
],
"support": {
- "source": "https://github.com/api-platform/json-schema/tree/v4.3.13"
+ "source": "https://github.com/api-platform/json-schema/tree/v4.4.0-alpha.1"
},
"time": "2026-06-13T05:06:55+00:00"
},
{
"name": "api-platform/jsonld",
- "version": "v4.3.13",
+ "version": "v4.3.15",
"source": {
"type": "git",
"url": "https://github.com/api-platform/jsonld.git",
@@ -1622,13 +1622,13 @@
"rest"
],
"support": {
- "source": "https://github.com/api-platform/jsonld/tree/v4.3.13"
+ "source": "https://github.com/api-platform/jsonld/tree/v4.3.15"
},
"time": "2026-06-13T05:11:46+00:00"
},
{
"name": "api-platform/metadata",
- "version": "v4.3.13",
+ "version": "v4.3.15",
"source": {
"type": "git",
"url": "https://github.com/api-platform/metadata.git",
@@ -1720,22 +1720,22 @@
"swagger"
],
"support": {
- "source": "https://github.com/api-platform/metadata/tree/v4.3.13"
+ "source": "https://github.com/api-platform/metadata/tree/v4.3.15"
},
"time": "2026-06-13T05:03:21+00:00"
},
{
"name": "api-platform/openapi",
- "version": "v4.3.13",
+ "version": "v4.3.15",
"source": {
"type": "git",
"url": "https://github.com/api-platform/openapi.git",
- "reference": "07c9e5c7325f810cea0cc1821ff564dbae6e57d5"
+ "reference": "c72470132f2eb35a4f8f252e60342f0f7c487704"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/api-platform/openapi/zipball/07c9e5c7325f810cea0cc1821ff564dbae6e57d5",
- "reference": "07c9e5c7325f810cea0cc1821ff564dbae6e57d5",
+ "url": "https://api.github.com/repos/api-platform/openapi/zipball/c72470132f2eb35a4f8f252e60342f0f7c487704",
+ "reference": "c72470132f2eb35a4f8f252e60342f0f7c487704",
"shasum": ""
},
"require": {
@@ -1811,22 +1811,22 @@
"swagger"
],
"support": {
- "source": "https://github.com/api-platform/openapi/tree/v4.3.13"
+ "source": "https://github.com/api-platform/openapi/tree/v4.3.15"
},
- "time": "2026-06-13T05:11:46+00:00"
+ "time": "2026-06-16T10:01:53+00:00"
},
{
"name": "api-platform/serializer",
- "version": "v4.3.13",
+ "version": "v4.3.15",
"source": {
"type": "git",
"url": "https://github.com/api-platform/serializer.git",
- "reference": "69042d5861779066b54bca1f6356bf5fe0eed466"
+ "reference": "4c8f7507a13f8a9c0cace60efc245fece2259233"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/api-platform/serializer/zipball/69042d5861779066b54bca1f6356bf5fe0eed466",
- "reference": "69042d5861779066b54bca1f6356bf5fe0eed466",
+ "url": "https://api.github.com/repos/api-platform/serializer/zipball/4c8f7507a13f8a9c0cace60efc245fece2259233",
+ "reference": "4c8f7507a13f8a9c0cace60efc245fece2259233",
"shasum": ""
},
"require": {
@@ -1905,13 +1905,13 @@
"serializer"
],
"support": {
- "source": "https://github.com/api-platform/serializer/tree/v4.3.13"
+ "source": "https://github.com/api-platform/serializer/tree/v4.3.15"
},
- "time": "2026-06-13T05:11:46+00:00"
+ "time": "2026-06-26T09:59:36+00:00"
},
{
"name": "api-platform/state",
- "version": "v4.3.13",
+ "version": "v4.3.15",
"source": {
"type": "git",
"url": "https://github.com/api-platform/state.git",
@@ -2002,22 +2002,22 @@
"swagger"
],
"support": {
- "source": "https://github.com/api-platform/state/tree/v4.3.13"
+ "source": "https://github.com/api-platform/state/tree/v4.3.15"
},
"time": "2026-06-13T05:11:46+00:00"
},
{
"name": "api-platform/symfony",
- "version": "v4.3.13",
+ "version": "v4.3.15",
"source": {
"type": "git",
"url": "https://github.com/api-platform/symfony.git",
- "reference": "358668b0de7c4a214d477a30fe83deab2a5da96c"
+ "reference": "8b20ef6262b2d557e2c286df48b78e66e7f8c000"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/api-platform/symfony/zipball/358668b0de7c4a214d477a30fe83deab2a5da96c",
- "reference": "358668b0de7c4a214d477a30fe83deab2a5da96c",
+ "url": "https://api.github.com/repos/api-platform/symfony/zipball/8b20ef6262b2d557e2c286df48b78e66e7f8c000",
+ "reference": "8b20ef6262b2d557e2c286df48b78e66e7f8c000",
"shasum": ""
},
"require": {
@@ -2131,13 +2131,13 @@
"symfony"
],
"support": {
- "source": "https://github.com/api-platform/symfony/tree/v4.3.13"
+ "source": "https://github.com/api-platform/symfony/tree/v4.3.15"
},
- "time": "2026-06-13T05:21:29+00:00"
+ "time": "2026-06-17T18:14:46+00:00"
},
{
"name": "api-platform/validator",
- "version": "v4.3.13",
+ "version": "v4.3.15",
"source": {
"type": "git",
"url": "https://github.com/api-platform/validator.git",
@@ -2207,7 +2207,7 @@
"validator"
],
"support": {
- "source": "https://github.com/api-platform/validator/tree/v4.3.13"
+ "source": "https://github.com/api-platform/validator/tree/v4.3.15"
},
"time": "2026-05-07T11:45:31+00:00"
},
@@ -4635,26 +4635,26 @@
},
{
"name": "guzzlehttp/guzzle",
- "version": "7.11.1",
+ "version": "7.12.3",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
- "reference": "5af96f374e0ab4ebd747b8310888c99d3adb0a8c"
+ "reference": "9aa17bcdd777ee31df9fc83c337ca4ca2340def3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/guzzle/guzzle/zipball/5af96f374e0ab4ebd747b8310888c99d3adb0a8c",
- "reference": "5af96f374e0ab4ebd747b8310888c99d3adb0a8c",
+ "url": "https://api.github.com/repos/guzzle/guzzle/zipball/9aa17bcdd777ee31df9fc83c337ca4ca2340def3",
+ "reference": "9aa17bcdd777ee31df9fc83c337ca4ca2340def3",
"shasum": ""
},
"require": {
"ext-json": "*",
"guzzlehttp/promises": "^2.5",
- "guzzlehttp/psr7": "^2.11",
+ "guzzlehttp/psr7": "^2.12.3",
"php": "^7.2.5 || ^8.0",
"psr/http-client": "^1.0",
"symfony/deprecation-contracts": "^2.5 || ^3.0",
- "symfony/polyfill-php80": "^1.24"
+ "symfony/polyfill-php80": "^1.25"
},
"provide": {
"psr/http-client-implementation": "1.0"
@@ -4663,7 +4663,7 @@
"bamarni/composer-bin-plugin": "^1.8.2",
"ext-curl": "*",
"guzzle/client-integration-tests": "3.0.2",
- "guzzlehttp/test-server": "^0.5",
+ "guzzlehttp/test-server": "^0.5.1",
"php-http/message-factory": "^1.1",
"phpunit/phpunit": "^8.5.52 || ^9.6.34",
"psr/log": "^1.1 || ^2.0 || ^3.0"
@@ -4743,7 +4743,7 @@
],
"support": {
"issues": "https://github.com/guzzle/guzzle/issues",
- "source": "https://github.com/guzzle/guzzle/tree/7.11.1"
+ "source": "https://github.com/guzzle/guzzle/tree/7.12.3"
},
"funding": [
{
@@ -4759,7 +4759,7 @@
"type": "tidelift"
}
],
- "time": "2026-06-07T22:54:06+00:00"
+ "time": "2026-06-23T15:29:02+00:00"
},
{
"name": "guzzlehttp/promises",
@@ -4847,16 +4847,16 @@
},
{
"name": "guzzlehttp/psr7",
- "version": "2.11.0",
+ "version": "2.12.3",
"source": {
"type": "git",
"url": "https://github.com/guzzle/psr7.git",
- "reference": "bbb5e61349fa5cb822b3e87842b951088b76b81f"
+ "reference": "7ec62dc3f44aa218487dbed81a9bf9bc647be55d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/guzzle/psr7/zipball/bbb5e61349fa5cb822b3e87842b951088b76b81f",
- "reference": "bbb5e61349fa5cb822b3e87842b951088b76b81f",
+ "url": "https://api.github.com/repos/guzzle/psr7/zipball/7ec62dc3f44aa218487dbed81a9bf9bc647be55d",
+ "reference": "7ec62dc3f44aa218487dbed81a9bf9bc647be55d",
"shasum": ""
},
"require": {
@@ -4865,7 +4865,7 @@
"psr/http-message": "^1.1 || ^2.0",
"ralouphie/getallheaders": "^3.0",
"symfony/deprecation-contracts": "^2.5 || ^3.0",
- "symfony/polyfill-php80": "^1.24"
+ "symfony/polyfill-php80": "^1.25"
},
"provide": {
"psr/http-factory-implementation": "1.0",
@@ -4946,7 +4946,7 @@
],
"support": {
"issues": "https://github.com/guzzle/psr7/issues",
- "source": "https://github.com/guzzle/psr7/tree/2.11.0"
+ "source": "https://github.com/guzzle/psr7/tree/2.12.3"
},
"funding": [
{
@@ -4962,7 +4962,7 @@
"type": "tidelift"
}
],
- "time": "2026-06-02T12:30:48+00:00"
+ "time": "2026-06-23T15:21:08+00:00"
},
{
"name": "hshn/base64-encoded-file",
@@ -5362,16 +5362,16 @@
},
{
"name": "jfcherng/php-diff",
- "version": "6.16.2",
+ "version": "6.16.3",
"source": {
"type": "git",
"url": "https://github.com/jfcherng/php-diff.git",
- "reference": "7f46bcfc582e81769237d0b3f6b8a548efe8799d"
+ "reference": "6d7332b6080cdd50011a364b58f24c8d0cdeb5da"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/jfcherng/php-diff/zipball/7f46bcfc582e81769237d0b3f6b8a548efe8799d",
- "reference": "7f46bcfc582e81769237d0b3f6b8a548efe8799d",
+ "url": "https://api.github.com/repos/jfcherng/php-diff/zipball/6d7332b6080cdd50011a364b58f24c8d0cdeb5da",
+ "reference": "6d7332b6080cdd50011a364b58f24c8d0cdeb5da",
"shasum": ""
},
"require": {
@@ -5416,7 +5416,7 @@
],
"support": {
"issues": "https://github.com/jfcherng/php-diff/issues",
- "source": "https://github.com/jfcherng/php-diff/tree/6.16.2"
+ "source": "https://github.com/jfcherng/php-diff/tree/6.16.3"
},
"funding": [
{
@@ -5424,7 +5424,7 @@
"type": "custom"
}
],
- "time": "2024-03-10T17:40:29+00:00"
+ "time": "2026-06-22T13:08:56+00:00"
},
{
"name": "jfcherng/php-mb-string",
@@ -7006,16 +7006,16 @@
},
{
"name": "masterminds/html5",
- "version": "2.10.0",
+ "version": "2.10.1",
"source": {
"type": "git",
"url": "https://github.com/Masterminds/html5-php.git",
- "reference": "fcf91eb64359852f00d921887b219479b4f21251"
+ "reference": "fd5018f6815fff903946d0564977b44ce8010e29"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/fcf91eb64359852f00d921887b219479b4f21251",
- "reference": "fcf91eb64359852f00d921887b219479b4f21251",
+ "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/fd5018f6815fff903946d0564977b44ce8010e29",
+ "reference": "fd5018f6815fff903946d0564977b44ce8010e29",
"shasum": ""
},
"require": {
@@ -7023,7 +7023,7 @@
"php": ">=5.3.0"
},
"require-dev": {
- "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9"
+ "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9 || ^10"
},
"type": "library",
"extra": {
@@ -7067,9 +7067,9 @@
],
"support": {
"issues": "https://github.com/Masterminds/html5-php/issues",
- "source": "https://github.com/Masterminds/html5-php/tree/2.10.0"
+ "source": "https://github.com/Masterminds/html5-php/tree/2.10.1"
},
- "time": "2025-07-25T09:04:22+00:00"
+ "time": "2026-06-23T18:43:15+00:00"
},
{
"name": "mf2/mf2",
@@ -9997,16 +9997,16 @@
},
{
"name": "sabberworm/php-css-parser",
- "version": "v9.3.0",
+ "version": "v9.4.0",
"source": {
"type": "git",
"url": "https://github.com/MyIntervals/PHP-CSS-Parser.git",
- "reference": "88dbd0f7f91abbfe4402d0a3071e9ff4d81ed949"
+ "reference": "fd3bf9fb173e0df649bc4e3e0d088a1b2417c08f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/88dbd0f7f91abbfe4402d0a3071e9ff4d81ed949",
- "reference": "88dbd0f7f91abbfe4402d0a3071e9ff4d81ed949",
+ "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/fd3bf9fb173e0df649bc4e3e0d088a1b2417c08f",
+ "reference": "fd3bf9fb173e0df649bc4e3e0d088a1b2417c08f",
"shasum": ""
},
"require": {
@@ -10017,15 +10017,15 @@
"require-dev": {
"php-parallel-lint/php-parallel-lint": "1.4.0",
"phpstan/extension-installer": "1.4.3",
- "phpstan/phpstan": "1.12.32 || 2.1.32",
- "phpstan/phpstan-phpunit": "1.4.2 || 2.0.8",
- "phpstan/phpstan-strict-rules": "1.6.2 || 2.0.7",
+ "phpstan/phpstan": "1.12.33 || 2.2.2",
+ "phpstan/phpstan-phpunit": "1.4.2 || 2.0.16",
+ "phpstan/phpstan-strict-rules": "1.6.2 || 2.0.11",
"phpunit/phpunit": "8.5.52",
"rawr/phpunit-data-provider": "3.3.1",
- "rector/rector": "1.2.10 || 2.2.8",
- "rector/type-perfect": "1.0.0 || 2.1.0",
+ "rector/rector": "1.2.10 || 2.4.6",
+ "rector/type-perfect": "1.0.0 || 2.1.3",
"squizlabs/php_codesniffer": "4.0.1",
- "thecodingmachine/phpstan-safe-rule": "1.2.0 || 1.4.1"
+ "thecodingmachine/phpstan-safe-rule": "1.2.0 || 1.4.3"
},
"suggest": {
"ext-mbstring": "for parsing UTF-8 CSS"
@@ -10033,7 +10033,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "9.4.x-dev"
+ "dev-main": "9.5.x-dev"
}
},
"autoload": {
@@ -10071,9 +10071,9 @@
],
"support": {
"issues": "https://github.com/MyIntervals/PHP-CSS-Parser/issues",
- "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v9.3.0"
+ "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v9.4.0"
},
- "time": "2026-03-03T17:31:43+00:00"
+ "time": "2026-06-18T15:10:53+00:00"
},
{
"name": "sabre/uri",
@@ -10733,21 +10733,21 @@
},
{
"name": "symfony/ai-bundle",
- "version": "v0.9.0",
+ "version": "v0.10.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/ai-bundle.git",
- "reference": "77fd1b513174770acf49abd68effa995fa518f7c"
+ "reference": "5f6d218ca26a4ac3c2b743e4bfae769c41c556c0"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/ai-bundle/zipball/77fd1b513174770acf49abd68effa995fa518f7c",
- "reference": "77fd1b513174770acf49abd68effa995fa518f7c",
+ "url": "https://api.github.com/repos/symfony/ai-bundle/zipball/5f6d218ca26a4ac3c2b743e4bfae769c41c556c0",
+ "reference": "5f6d218ca26a4ac3c2b743e4bfae769c41c556c0",
"shasum": ""
},
"require": {
"php": ">=8.2",
- "symfony/ai-platform": "^0.9",
+ "symfony/ai-platform": "^0.10",
"symfony/clock": "^7.3|^8.0",
"symfony/config": "^7.3|^8.0",
"symfony/console": "^7.3|^8.0",
@@ -10762,74 +10762,74 @@
"phpstan/phpstan-phpunit": "^2.0",
"phpstan/phpstan-strict-rules": "^2.0",
"phpunit/phpunit": "^11.5.53",
- "symfony/ai-agent": "^0.9",
- "symfony/ai-ai-ml-api-platform": "^0.9",
- "symfony/ai-albert-platform": "^0.9",
- "symfony/ai-amazee-ai-platform": "^0.9",
- "symfony/ai-anthropic-platform": "^0.9",
- "symfony/ai-azure-platform": "^0.9",
- "symfony/ai-azure-search-store": "^0.9",
- "symfony/ai-bedrock-platform": "^0.9",
- "symfony/ai-cache-message-store": "^0.9",
- "symfony/ai-cache-platform": "^0.9",
- "symfony/ai-cache-store": "^0.9",
- "symfony/ai-cartesia-platform": "^0.9",
- "symfony/ai-cerebras-platform": "^0.9",
- "symfony/ai-chat": "^0.9",
- "symfony/ai-chroma-db-store": "^0.9",
- "symfony/ai-click-house-store": "^0.9",
- "symfony/ai-cloudflare-message-store": "^0.9",
- "symfony/ai-cloudflare-store": "^0.9",
- "symfony/ai-cohere-platform": "^0.9",
- "symfony/ai-decart-platform": "^0.9",
- "symfony/ai-deep-seek-platform": "^0.9",
- "symfony/ai-docker-model-runner-platform": "^0.9",
- "symfony/ai-doctrine-message-store": "^0.9",
- "symfony/ai-elasticsearch-store": "^0.9",
- "symfony/ai-eleven-labs-platform": "^0.9",
- "symfony/ai-failover-platform": "^0.9",
- "symfony/ai-gemini-platform": "^0.9",
- "symfony/ai-generic-platform": "^0.9",
- "symfony/ai-hugging-face-platform": "^0.9",
- "symfony/ai-lm-studio-platform": "^0.9",
- "symfony/ai-manticore-search-store": "^0.9",
- "symfony/ai-maria-db-store": "^0.9",
- "symfony/ai-meilisearch-message-store": "^0.9",
- "symfony/ai-meilisearch-store": "^0.9",
- "symfony/ai-meta-platform": "^0.9",
- "symfony/ai-milvus-store": "^0.9",
- "symfony/ai-mistral-platform": "^0.9",
- "symfony/ai-mongo-db-message-store": "^0.9",
- "symfony/ai-mongo-db-store": "^0.9",
- "symfony/ai-neo4j-store": "^0.9",
- "symfony/ai-ollama-platform": "^0.9",
- "symfony/ai-open-ai-platform": "^0.9",
- "symfony/ai-open-responses-platform": "^0.9",
- "symfony/ai-open-router-platform": "^0.9",
- "symfony/ai-open-search-store": "^0.9",
- "symfony/ai-ovh-platform": "^0.9",
- "symfony/ai-perplexity-platform": "^0.9",
- "symfony/ai-pinecone-store": "^0.9",
- "symfony/ai-pogocache-message-store": "^0.9",
- "symfony/ai-postgres-store": "^0.9",
- "symfony/ai-qdrant-store": "^0.9",
- "symfony/ai-redis-message-store": "^0.9",
- "symfony/ai-redis-store": "^0.9",
- "symfony/ai-replicate-platform": "^0.9",
- "symfony/ai-s3vectors-store": "^0.9",
- "symfony/ai-scaleway-platform": "^0.9",
- "symfony/ai-session-message-store": "^0.9",
- "symfony/ai-sqlite-store": "^0.9",
- "symfony/ai-store": "^0.9",
- "symfony/ai-supabase-store": "^0.9",
- "symfony/ai-surreal-db-message-store": "^0.9",
- "symfony/ai-surreal-db-store": "^0.9",
- "symfony/ai-transformers-php-platform": "^0.9",
- "symfony/ai-typesense-store": "^0.9",
- "symfony/ai-vektor-store": "^0.9",
- "symfony/ai-vertex-ai-platform": "^0.9",
- "symfony/ai-voyage-platform": "^0.9",
- "symfony/ai-weaviate-store": "^0.9",
+ "symfony/ai-agent": "^0.10",
+ "symfony/ai-ai-ml-api-platform": "^0.10",
+ "symfony/ai-albert-platform": "^0.10",
+ "symfony/ai-amazee-ai-platform": "^0.10",
+ "symfony/ai-anthropic-platform": "^0.10",
+ "symfony/ai-azure-platform": "^0.10",
+ "symfony/ai-azure-search-store": "^0.10",
+ "symfony/ai-bedrock-platform": "^0.10",
+ "symfony/ai-cache-message-store": "^0.10",
+ "symfony/ai-cache-platform": "^0.10",
+ "symfony/ai-cache-store": "^0.10",
+ "symfony/ai-cartesia-platform": "^0.10",
+ "symfony/ai-cerebras-platform": "^0.10",
+ "symfony/ai-chat": "^0.10",
+ "symfony/ai-chroma-db-store": "^0.10",
+ "symfony/ai-click-house-store": "^0.10",
+ "symfony/ai-cloudflare-message-store": "^0.10",
+ "symfony/ai-cloudflare-store": "^0.10",
+ "symfony/ai-cohere-platform": "^0.10",
+ "symfony/ai-decart-platform": "^0.10",
+ "symfony/ai-deep-seek-platform": "^0.10",
+ "symfony/ai-docker-model-runner-platform": "^0.10",
+ "symfony/ai-doctrine-message-store": "^0.10",
+ "symfony/ai-elasticsearch-store": "^0.10",
+ "symfony/ai-eleven-labs-platform": "^0.10",
+ "symfony/ai-failover-platform": "^0.10",
+ "symfony/ai-gemini-platform": "^0.10",
+ "symfony/ai-generic-platform": "^0.10",
+ "symfony/ai-hugging-face-platform": "^0.10",
+ "symfony/ai-lm-studio-platform": "^0.10",
+ "symfony/ai-manticore-search-store": "^0.10",
+ "symfony/ai-maria-db-store": "^0.10",
+ "symfony/ai-meilisearch-message-store": "^0.10",
+ "symfony/ai-meilisearch-store": "^0.10",
+ "symfony/ai-meta-platform": "^0.10",
+ "symfony/ai-milvus-store": "^0.10",
+ "symfony/ai-mistral-platform": "^0.10",
+ "symfony/ai-mongo-db-message-store": "^0.10",
+ "symfony/ai-mongo-db-store": "^0.10",
+ "symfony/ai-neo4j-store": "^0.10",
+ "symfony/ai-ollama-platform": "^0.10",
+ "symfony/ai-open-ai-platform": "^0.10",
+ "symfony/ai-open-responses-platform": "^0.10",
+ "symfony/ai-open-router-platform": "^0.10",
+ "symfony/ai-open-search-store": "^0.10",
+ "symfony/ai-ovh-platform": "^0.10",
+ "symfony/ai-perplexity-platform": "^0.10",
+ "symfony/ai-pinecone-store": "^0.10",
+ "symfony/ai-pogocache-message-store": "^0.10",
+ "symfony/ai-postgres-store": "^0.10",
+ "symfony/ai-qdrant-store": "^0.10",
+ "symfony/ai-redis-message-store": "^0.10",
+ "symfony/ai-redis-store": "^0.10",
+ "symfony/ai-replicate-platform": "^0.10",
+ "symfony/ai-s3vectors-store": "^0.10",
+ "symfony/ai-scaleway-platform": "^0.10",
+ "symfony/ai-session-message-store": "^0.10",
+ "symfony/ai-sqlite-store": "^0.10",
+ "symfony/ai-store": "^0.10",
+ "symfony/ai-supabase-store": "^0.10",
+ "symfony/ai-surreal-db-message-store": "^0.10",
+ "symfony/ai-surreal-db-store": "^0.10",
+ "symfony/ai-transformers-php-platform": "^0.10",
+ "symfony/ai-typesense-store": "^0.10",
+ "symfony/ai-vektor-store": "^0.10",
+ "symfony/ai-vertex-ai-platform": "^0.10",
+ "symfony/ai-voyage-platform": "^0.10",
+ "symfony/ai-weaviate-store": "^0.10",
"symfony/expression-language": "^7.3|^8.0",
"symfony/security-core": "^7.3|^8.0",
"symfony/translation": "^7.3|^8.0",
@@ -10867,7 +10867,7 @@
],
"description": "Integration bundle for Symfony AI components",
"support": {
- "source": "https://github.com/symfony/ai-bundle/tree/v0.9.0"
+ "source": "https://github.com/symfony/ai-bundle/tree/v0.10.0"
},
"funding": [
{
@@ -10887,25 +10887,25 @@
"type": "tidelift"
}
],
- "time": "2026-05-16T08:40:45+00:00"
+ "time": "2026-06-16T07:10:08+00:00"
},
{
"name": "symfony/ai-generic-platform",
- "version": "v0.9.0",
+ "version": "v0.10.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/ai-generic-platform.git",
- "reference": "8887d12b8ea97d079c5c97de4aebb19f42c58dc5"
+ "reference": "a099d8a35ea9f7254a159b9c17bc7a4927f9ebb2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/ai-generic-platform/zipball/8887d12b8ea97d079c5c97de4aebb19f42c58dc5",
- "reference": "8887d12b8ea97d079c5c97de4aebb19f42c58dc5",
+ "url": "https://api.github.com/repos/symfony/ai-generic-platform/zipball/a099d8a35ea9f7254a159b9c17bc7a4927f9ebb2",
+ "reference": "a099d8a35ea9f7254a159b9c17bc7a4927f9ebb2",
"shasum": ""
},
"require": {
"php": ">=8.2",
- "symfony/ai-platform": "^0.9",
+ "symfony/ai-platform": "^0.10",
"symfony/http-client": "^7.3|^8.0"
},
"require-dev": {
@@ -10952,7 +10952,7 @@
"platform"
],
"support": {
- "source": "https://github.com/symfony/ai-generic-platform/tree/v0.9.0"
+ "source": "https://github.com/symfony/ai-generic-platform/tree/v0.10.0"
},
"funding": [
{
@@ -10972,26 +10972,26 @@
"type": "tidelift"
}
],
- "time": "2026-05-16T01:01:33+00:00"
+ "time": "2026-06-16T07:10:08+00:00"
},
{
"name": "symfony/ai-lm-studio-platform",
- "version": "v0.9.0",
+ "version": "v0.10.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/ai-lm-studio-platform.git",
- "reference": "9e53e56c8c3a04dddb955088b40904e747ec3981"
+ "reference": "b6c4a3a5bedf2fd613953447602945c104e75a30"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/ai-lm-studio-platform/zipball/9e53e56c8c3a04dddb955088b40904e747ec3981",
- "reference": "9e53e56c8c3a04dddb955088b40904e747ec3981",
+ "url": "https://api.github.com/repos/symfony/ai-lm-studio-platform/zipball/b6c4a3a5bedf2fd613953447602945c104e75a30",
+ "reference": "b6c4a3a5bedf2fd613953447602945c104e75a30",
"shasum": ""
},
"require": {
"php": ">=8.2",
- "symfony/ai-generic-platform": "^0.9",
- "symfony/ai-platform": "^0.9",
+ "symfony/ai-generic-platform": "^0.10",
+ "symfony/ai-platform": "^0.10",
"symfony/http-client": "^7.3|^8.0"
},
"require-dev": {
@@ -11039,7 +11039,7 @@
"platform"
],
"support": {
- "source": "https://github.com/symfony/ai-lm-studio-platform/tree/v0.9.0"
+ "source": "https://github.com/symfony/ai-lm-studio-platform/tree/v0.10.0"
},
"funding": [
{
@@ -11059,26 +11059,112 @@
"type": "tidelift"
}
],
- "time": "2026-05-16T01:01:33+00:00"
+ "time": "2026-06-15T22:48:31+00:00"
},
{
- "name": "symfony/ai-open-router-platform",
- "version": "v0.9.0",
+ "name": "symfony/ai-ollama-platform",
+ "version": "v0.10.0",
"source": {
"type": "git",
- "url": "https://github.com/symfony/ai-open-router-platform.git",
- "reference": "7e2b560c86f618cd5d33f9f0c581d83bebc9802f"
+ "url": "https://github.com/symfony/ai-ollama-platform.git",
+ "reference": "1542f19b78362cafc034c219f5bc9a5a239a0ffb"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/ai-open-router-platform/zipball/7e2b560c86f618cd5d33f9f0c581d83bebc9802f",
- "reference": "7e2b560c86f618cd5d33f9f0c581d83bebc9802f",
+ "url": "https://api.github.com/repos/symfony/ai-ollama-platform/zipball/1542f19b78362cafc034c219f5bc9a5a239a0ffb",
+ "reference": "1542f19b78362cafc034c219f5bc9a5a239a0ffb",
"shasum": ""
},
"require": {
"php": ">=8.2",
- "symfony/ai-generic-platform": "^0.9",
- "symfony/ai-platform": "^0.9",
+ "symfony/ai-platform": "^0.10",
+ "symfony/http-client": "^7.3|^8.0"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^2.1",
+ "phpstan/phpstan-phpunit": "^2.0",
+ "phpstan/phpstan-strict-rules": "^2.0",
+ "phpunit/phpunit": "^11.5.53"
+ },
+ "type": "symfony-ai-platform",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/ai",
+ "name": "symfony/ai"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\AI\\Platform\\Bridge\\Ollama\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christopher Hertel",
+ "email": "mail@christopher-hertel.de"
+ },
+ {
+ "name": "Oskar Stark",
+ "email": "oskarstark@googlemail.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Ollama platform bridge for Symfony AI",
+ "keywords": [
+ "Bridge",
+ "ai",
+ "local",
+ "ollama",
+ "platform"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/ai-ollama-platform/tree/v0.10.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": "2026-06-15T22:48:31+00:00"
+ },
+ {
+ "name": "symfony/ai-open-router-platform",
+ "version": "v0.10.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/ai-open-router-platform.git",
+ "reference": "cfadb7858fca98b28b968b032b1bbeb5c9cc985b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/ai-open-router-platform/zipball/cfadb7858fca98b28b968b032b1bbeb5c9cc985b",
+ "reference": "cfadb7858fca98b28b968b032b1bbeb5c9cc985b",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/ai-generic-platform": "^0.10",
+ "symfony/ai-platform": "^0.10",
"symfony/http-client": "^7.3|^8.0"
},
"require-dev": {
@@ -11126,7 +11212,7 @@
"platform"
],
"support": {
- "source": "https://github.com/symfony/ai-open-router-platform/tree/v0.9.0"
+ "source": "https://github.com/symfony/ai-open-router-platform/tree/v0.10.0"
},
"funding": [
{
@@ -11146,20 +11232,20 @@
"type": "tidelift"
}
],
- "time": "2026-05-16T01:01:33+00:00"
+ "time": "2026-06-16T08:09:36+00:00"
},
{
"name": "symfony/ai-platform",
- "version": "v0.9.0",
+ "version": "v0.10.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/ai-platform.git",
- "reference": "fb55ebdf20bbe30af6752a0ce6a25abc56b2b625"
+ "reference": "8100507aa9c46f3ad56d0272e8e46b558f451052"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/ai-platform/zipball/fb55ebdf20bbe30af6752a0ce6a25abc56b2b625",
- "reference": "fb55ebdf20bbe30af6752a0ce6a25abc56b2b625",
+ "url": "https://api.github.com/repos/symfony/ai-platform/zipball/8100507aa9c46f3ad56d0272e8e46b558f451052",
+ "reference": "8100507aa9c46f3ad56d0272e8e46b558f451052",
"shasum": ""
},
"require": {
@@ -11258,7 +11344,7 @@
"voyage"
],
"support": {
- "source": "https://github.com/symfony/ai-platform/tree/v0.9.0"
+ "source": "https://github.com/symfony/ai-platform/tree/v0.10.0"
},
"funding": [
{
@@ -11278,7 +11364,7 @@
"type": "tidelift"
}
],
- "time": "2026-05-15T19:15:50+00:00"
+ "time": "2026-06-16T06:39:24+00:00"
},
{
"name": "symfony/apache-pack",
@@ -11381,16 +11467,16 @@
},
{
"name": "symfony/cache",
- "version": "v7.4.13",
+ "version": "v7.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/cache.git",
- "reference": "4c09e18a92cce126cc0d1155825279fca8cd0673"
+ "reference": "9adfcb2a7fc3924473b09f5a0b058dcc2cc7be9a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/cache/zipball/4c09e18a92cce126cc0d1155825279fca8cd0673",
- "reference": "4c09e18a92cce126cc0d1155825279fca8cd0673",
+ "url": "https://api.github.com/repos/symfony/cache/zipball/9adfcb2a7fc3924473b09f5a0b058dcc2cc7be9a",
+ "reference": "9adfcb2a7fc3924473b09f5a0b058dcc2cc7be9a",
"shasum": ""
},
"require": {
@@ -11461,7 +11547,7 @@
"psr6"
],
"support": {
- "source": "https://github.com/symfony/cache/tree/v7.4.13"
+ "source": "https://github.com/symfony/cache/tree/v7.4.14"
},
"funding": [
{
@@ -11481,20 +11567,20 @@
"type": "tidelift"
}
],
- "time": "2026-05-24T08:43:14+00:00"
+ "time": "2026-06-17T14:44:48+00:00"
},
{
"name": "symfony/cache-contracts",
- "version": "v3.7.0",
+ "version": "v3.7.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/cache-contracts.git",
- "reference": "225e8a254166bd3442e370c6f50145465db63831"
+ "reference": "9789738bc19af1106dc54d6afba9a0b467516cf2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/225e8a254166bd3442e370c6f50145465db63831",
- "reference": "225e8a254166bd3442e370c6f50145465db63831",
+ "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/9789738bc19af1106dc54d6afba9a0b467516cf2",
+ "reference": "9789738bc19af1106dc54d6afba9a0b467516cf2",
"shasum": ""
},
"require": {
@@ -11541,7 +11627,7 @@
"standards"
],
"support": {
- "source": "https://github.com/symfony/cache-contracts/tree/v3.7.0"
+ "source": "https://github.com/symfony/cache-contracts/tree/v3.7.1"
},
"funding": [
{
@@ -11561,7 +11647,7 @@
"type": "tidelift"
}
],
- "time": "2026-05-05T15:33:14+00:00"
+ "time": "2026-06-05T06:23:12+00:00"
},
{
"name": "symfony/clock",
@@ -11643,16 +11729,16 @@
},
{
"name": "symfony/config",
- "version": "v7.4.10",
+ "version": "v7.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/config.git",
- "reference": "d91b6c7cd2a8c9a9c2b8d26c8f5ed48edf99ef57"
+ "reference": "7b665e443381ea7c4db03eb03b4bf79ea2b020eb"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/config/zipball/d91b6c7cd2a8c9a9c2b8d26c8f5ed48edf99ef57",
- "reference": "d91b6c7cd2a8c9a9c2b8d26c8f5ed48edf99ef57",
+ "url": "https://api.github.com/repos/symfony/config/zipball/7b665e443381ea7c4db03eb03b4bf79ea2b020eb",
+ "reference": "7b665e443381ea7c4db03eb03b4bf79ea2b020eb",
"shasum": ""
},
"require": {
@@ -11698,7 +11784,7 @@
"description": "Helps you find, load, combine, autofill and validate configuration values of any kind",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/config/tree/v7.4.10"
+ "source": "https://github.com/symfony/config/tree/v7.4.14"
},
"funding": [
{
@@ -11718,20 +11804,20 @@
"type": "tidelift"
}
],
- "time": "2026-05-03T14:20:49+00:00"
+ "time": "2026-06-09T07:51:57+00:00"
},
{
"name": "symfony/console",
- "version": "v7.4.13",
+ "version": "v7.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
- "reference": "85095d2573eaefaf35e40b9513a9bf09f72cd217"
+ "reference": "92f58bc4bf97a92ed1b9f367f0cd44f20bde0e87"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/console/zipball/85095d2573eaefaf35e40b9513a9bf09f72cd217",
- "reference": "85095d2573eaefaf35e40b9513a9bf09f72cd217",
+ "url": "https://api.github.com/repos/symfony/console/zipball/92f58bc4bf97a92ed1b9f367f0cd44f20bde0e87",
+ "reference": "92f58bc4bf97a92ed1b9f367f0cd44f20bde0e87",
"shasum": ""
},
"require": {
@@ -11796,7 +11882,7 @@
"terminal"
],
"support": {
- "source": "https://github.com/symfony/console/tree/v7.4.13"
+ "source": "https://github.com/symfony/console/tree/v7.4.14"
},
"funding": [
{
@@ -11816,7 +11902,7 @@
"type": "tidelift"
}
],
- "time": "2026-05-24T08:56:14+00:00"
+ "time": "2026-06-16T11:50:14+00:00"
},
{
"name": "symfony/css-selector",
@@ -11889,16 +11975,16 @@
},
{
"name": "symfony/dependency-injection",
- "version": "v7.4.13",
+ "version": "v7.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/dependency-injection.git",
- "reference": "f299e20ce983be6c0744952533c6dfeaaa1448e2"
+ "reference": "2c8c64a33e2e6911579e1ff79a8e06c27d48d402"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/f299e20ce983be6c0744952533c6dfeaaa1448e2",
- "reference": "f299e20ce983be6c0744952533c6dfeaaa1448e2",
+ "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/2c8c64a33e2e6911579e1ff79a8e06c27d48d402",
+ "reference": "2c8c64a33e2e6911579e1ff79a8e06c27d48d402",
"shasum": ""
},
"require": {
@@ -11949,7 +12035,7 @@
"description": "Allows you to standardize and centralize the way objects are constructed in your application",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/dependency-injection/tree/v7.4.13"
+ "source": "https://github.com/symfony/dependency-injection/tree/v7.4.14"
},
"funding": [
{
@@ -11969,20 +12055,20 @@
"type": "tidelift"
}
],
- "time": "2026-05-20T14:07:29+00:00"
+ "time": "2026-06-24T07:41:05+00:00"
},
{
"name": "symfony/deprecation-contracts",
- "version": "v3.7.0",
+ "version": "v3.7.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
- "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b"
+ "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/50f59d1f3ca46d41ac911f97a78626b6756af35b",
- "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b",
+ "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/f3202fa1b5097b0af062dc978b32ecf63404e31d",
+ "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d",
"shasum": ""
},
"require": {
@@ -12020,7 +12106,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.7.0"
+ "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.1"
},
"funding": [
{
@@ -12040,20 +12126,20 @@
"type": "tidelift"
}
],
- "time": "2026-04-13T15:52:40+00:00"
+ "time": "2026-06-05T06:23:12+00:00"
},
{
"name": "symfony/doctrine-bridge",
- "version": "v7.4.9",
+ "version": "v7.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/doctrine-bridge.git",
- "reference": "7a87c85853f3069e3657a823c62b02952de46b0a"
+ "reference": "f65262a834d1117617d6e072cb180a0b79428789"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/7a87c85853f3069e3657a823c62b02952de46b0a",
- "reference": "7a87c85853f3069e3657a823c62b02952de46b0a",
+ "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/f65262a834d1117617d6e072cb180a0b79428789",
+ "reference": "f65262a834d1117617d6e072cb180a0b79428789",
"shasum": ""
},
"require": {
@@ -12133,7 +12219,7 @@
"description": "Provides integration for Doctrine with various Symfony components",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/doctrine-bridge/tree/v7.4.9"
+ "source": "https://github.com/symfony/doctrine-bridge/tree/v7.4.14"
},
"funding": [
{
@@ -12153,7 +12239,7 @@
"type": "tidelift"
}
],
- "time": "2026-04-29T14:19:39+00:00"
+ "time": "2026-06-11T07:31:44+00:00"
},
{
"name": "symfony/dom-crawler",
@@ -12229,16 +12315,16 @@
},
{
"name": "symfony/dotenv",
- "version": "v7.4.11",
+ "version": "v7.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/dotenv.git",
- "reference": "82e9b1355c68ef7b96397dbd34cc75a92eebae7c"
+ "reference": "9b9c7a00e565238857eea0040dc9745e3576402e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/dotenv/zipball/82e9b1355c68ef7b96397dbd34cc75a92eebae7c",
- "reference": "82e9b1355c68ef7b96397dbd34cc75a92eebae7c",
+ "url": "https://api.github.com/repos/symfony/dotenv/zipball/9b9c7a00e565238857eea0040dc9745e3576402e",
+ "reference": "9b9c7a00e565238857eea0040dc9745e3576402e",
"shasum": ""
},
"require": {
@@ -12283,7 +12369,7 @@
"environment"
],
"support": {
- "source": "https://github.com/symfony/dotenv/tree/v7.4.11"
+ "source": "https://github.com/symfony/dotenv/tree/v7.4.14"
},
"funding": [
{
@@ -12303,20 +12389,20 @@
"type": "tidelift"
}
],
- "time": "2026-05-11T13:02:51+00:00"
+ "time": "2026-06-05T06:22:21+00:00"
},
{
"name": "symfony/error-handler",
- "version": "v7.4.8",
+ "version": "v7.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/error-handler.git",
- "reference": "8dd79d8af777ee6cba2fd4d98da6ffb839f3c0fa"
+ "reference": "4e1a093b481f323e6e326451f9760c3868430673"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/error-handler/zipball/8dd79d8af777ee6cba2fd4d98da6ffb839f3c0fa",
- "reference": "8dd79d8af777ee6cba2fd4d98da6ffb839f3c0fa",
+ "url": "https://api.github.com/repos/symfony/error-handler/zipball/4e1a093b481f323e6e326451f9760c3868430673",
+ "reference": "4e1a093b481f323e6e326451f9760c3868430673",
"shasum": ""
},
"require": {
@@ -12365,7 +12451,7 @@
"description": "Provides tools to manage errors and ease debugging PHP code",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/error-handler/tree/v7.4.8"
+ "source": "https://github.com/symfony/error-handler/tree/v7.4.14"
},
"funding": [
{
@@ -12385,20 +12471,20 @@
"type": "tidelift"
}
],
- "time": "2026-03-24T13:12:05+00:00"
+ "time": "2026-06-05T06:22:21+00:00"
},
{
"name": "symfony/event-dispatcher",
- "version": "v7.4.9",
+ "version": "v7.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
- "reference": "e4a2e29753c7801f7a8340e066cfa788f3bc8101"
+ "reference": "51fe3d170227be8d1772214b82ae506e15ed78ff"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/e4a2e29753c7801f7a8340e066cfa788f3bc8101",
- "reference": "e4a2e29753c7801f7a8340e066cfa788f3bc8101",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/51fe3d170227be8d1772214b82ae506e15ed78ff",
+ "reference": "51fe3d170227be8d1772214b82ae506e15ed78ff",
"shasum": ""
},
"require": {
@@ -12450,7 +12536,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.4.9"
+ "source": "https://github.com/symfony/event-dispatcher/tree/v7.4.14"
},
"funding": [
{
@@ -12470,20 +12556,20 @@
"type": "tidelift"
}
],
- "time": "2026-04-18T13:18:21+00:00"
+ "time": "2026-06-06T11:10:32+00:00"
},
{
"name": "symfony/event-dispatcher-contracts",
- "version": "v3.7.0",
+ "version": "v3.7.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher-contracts.git",
- "reference": "ccba7060602b7fed0b03c85bf025257f76d9ef32"
+ "reference": "c7de7a00ffb67842132da02ea92988a39ccd9f4e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/ccba7060602b7fed0b03c85bf025257f76d9ef32",
- "reference": "ccba7060602b7fed0b03c85bf025257f76d9ef32",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/c7de7a00ffb67842132da02ea92988a39ccd9f4e",
+ "reference": "c7de7a00ffb67842132da02ea92988a39ccd9f4e",
"shasum": ""
},
"require": {
@@ -12530,7 +12616,7 @@
"standards"
],
"support": {
- "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.7.0"
+ "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.7.1"
},
"funding": [
{
@@ -12550,20 +12636,20 @@
"type": "tidelift"
}
],
- "time": "2026-01-05T13:30:16+00:00"
+ "time": "2026-06-05T06:23:12+00:00"
},
{
"name": "symfony/expression-language",
- "version": "v7.4.8",
+ "version": "v7.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/expression-language.git",
- "reference": "87ff95687748f4af65e4d5a6e917d448ec52aa83"
+ "reference": "bd5763f92959201816ecc31defdf352d2ea473be"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/expression-language/zipball/87ff95687748f4af65e4d5a6e917d448ec52aa83",
- "reference": "87ff95687748f4af65e4d5a6e917d448ec52aa83",
+ "url": "https://api.github.com/repos/symfony/expression-language/zipball/bd5763f92959201816ecc31defdf352d2ea473be",
+ "reference": "bd5763f92959201816ecc31defdf352d2ea473be",
"shasum": ""
},
"require": {
@@ -12598,7 +12684,7 @@
"description": "Provides an engine that can compile and evaluate expressions",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/expression-language/tree/v7.4.8"
+ "source": "https://github.com/symfony/expression-language/tree/v7.4.14"
},
"funding": [
{
@@ -12618,7 +12704,7 @@
"type": "tidelift"
}
],
- "time": "2026-03-24T13:12:05+00:00"
+ "time": "2026-06-08T20:24:16+00:00"
},
{
"name": "symfony/filesystem",
@@ -12692,16 +12778,16 @@
},
{
"name": "symfony/finder",
- "version": "v7.4.8",
+ "version": "v7.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
- "reference": "e0be088d22278583a82da281886e8c3592fbf149"
+ "reference": "13b38720174286f55d1761152b575a8d1436fc25"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/finder/zipball/e0be088d22278583a82da281886e8c3592fbf149",
- "reference": "e0be088d22278583a82da281886e8c3592fbf149",
+ "url": "https://api.github.com/repos/symfony/finder/zipball/13b38720174286f55d1761152b575a8d1436fc25",
+ "reference": "13b38720174286f55d1761152b575a8d1436fc25",
"shasum": ""
},
"require": {
@@ -12736,7 +12822,7 @@
"description": "Finds files and directories via an intuitive fluent interface",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/finder/tree/v7.4.8"
+ "source": "https://github.com/symfony/finder/tree/v7.4.14"
},
"funding": [
{
@@ -12756,7 +12842,7 @@
"type": "tidelift"
}
],
- "time": "2026-03-24T13:12:05+00:00"
+ "time": "2026-06-27T08:31:18+00:00"
},
{
"name": "symfony/flex",
@@ -12833,16 +12919,16 @@
},
{
"name": "symfony/form",
- "version": "v7.4.9",
+ "version": "v7.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/form.git",
- "reference": "b6c107af659106abec1771d9d7d22da528644d3a"
+ "reference": "355e9567b60254deef62392829a1784e66322041"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/form/zipball/b6c107af659106abec1771d9d7d22da528644d3a",
- "reference": "b6c107af659106abec1771d9d7d22da528644d3a",
+ "url": "https://api.github.com/repos/symfony/form/zipball/355e9567b60254deef62392829a1784e66322041",
+ "reference": "355e9567b60254deef62392829a1784e66322041",
"shasum": ""
},
"require": {
@@ -12912,7 +12998,7 @@
"description": "Allows to easily create, process and reuse HTML forms",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/form/tree/v7.4.9"
+ "source": "https://github.com/symfony/form/tree/v7.4.14"
},
"funding": [
{
@@ -12932,20 +13018,20 @@
"type": "tidelift"
}
],
- "time": "2026-04-29T14:39:19+00:00"
+ "time": "2026-06-16T15:54:05+00:00"
},
{
"name": "symfony/framework-bundle",
- "version": "v7.4.13",
+ "version": "v7.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/framework-bundle.git",
- "reference": "8be39c7bf9e6f58fe49c07927572a9df7c961c95"
+ "reference": "4d11fd50d0a3d2c43b400154ad7ec35b84ea3e5b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/8be39c7bf9e6f58fe49c07927572a9df7c961c95",
- "reference": "8be39c7bf9e6f58fe49c07927572a9df7c961c95",
+ "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/4d11fd50d0a3d2c43b400154ad7ec35b84ea3e5b",
+ "reference": "4d11fd50d0a3d2c43b400154ad7ec35b84ea3e5b",
"shasum": ""
},
"require": {
@@ -12995,7 +13081,7 @@
"symfony/twig-bundle": "<6.4",
"symfony/validator": "<6.4",
"symfony/web-profiler-bundle": "<6.4",
- "symfony/webhook": "<7.2",
+ "symfony/webhook": "<7.4",
"symfony/workflow": "<7.4"
},
"require-dev": {
@@ -13039,7 +13125,7 @@
"symfony/uid": "^6.4|^7.0|^8.0",
"symfony/validator": "^7.4|^8.0",
"symfony/web-link": "^6.4|^7.0|^8.0",
- "symfony/webhook": "^7.2|^8.0",
+ "symfony/webhook": "^7.4|^8.0",
"symfony/workflow": "^7.4|^8.0",
"symfony/yaml": "^7.3|^8.0",
"twig/twig": "^3.12"
@@ -13070,7 +13156,7 @@
"description": "Provides a tight integration between Symfony components and the Symfony full-stack framework",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/framework-bundle/tree/v7.4.13"
+ "source": "https://github.com/symfony/framework-bundle/tree/v7.4.14"
},
"funding": [
{
@@ -13090,20 +13176,20 @@
"type": "tidelift"
}
],
- "time": "2026-05-23T18:04:28+00:00"
+ "time": "2026-06-27T08:31:38+00:00"
},
{
"name": "symfony/html-sanitizer",
- "version": "v7.4.13",
+ "version": "v7.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/html-sanitizer.git",
- "reference": "761f6c49dfd103ee08b3cd09ece588b069e18ec9"
+ "reference": "c328df69f5b6f44a0d031d757903d955bebb23b3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/html-sanitizer/zipball/761f6c49dfd103ee08b3cd09ece588b069e18ec9",
- "reference": "761f6c49dfd103ee08b3cd09ece588b069e18ec9",
+ "url": "https://api.github.com/repos/symfony/html-sanitizer/zipball/c328df69f5b6f44a0d031d757903d955bebb23b3",
+ "reference": "c328df69f5b6f44a0d031d757903d955bebb23b3",
"shasum": ""
},
"require": {
@@ -13144,7 +13230,7 @@
"sanitizer"
],
"support": {
- "source": "https://github.com/symfony/html-sanitizer/tree/v7.4.13"
+ "source": "https://github.com/symfony/html-sanitizer/tree/v7.4.14"
},
"funding": [
{
@@ -13164,20 +13250,20 @@
"type": "tidelift"
}
],
- "time": "2026-05-24T11:20:33+00:00"
+ "time": "2026-06-06T11:10:32+00:00"
},
{
"name": "symfony/http-client",
- "version": "v7.4.13",
+ "version": "v7.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-client.git",
- "reference": "e8a112b8415707265a7e614278136a9d92989a6a"
+ "reference": "f6bc6b5a54ff5afac4725cacec9bf2f52eb15920"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-client/zipball/e8a112b8415707265a7e614278136a9d92989a6a",
- "reference": "e8a112b8415707265a7e614278136a9d92989a6a",
+ "url": "https://api.github.com/repos/symfony/http-client/zipball/f6bc6b5a54ff5afac4725cacec9bf2f52eb15920",
+ "reference": "f6bc6b5a54ff5afac4725cacec9bf2f52eb15920",
"shasum": ""
},
"require": {
@@ -13245,7 +13331,7 @@
"http"
],
"support": {
- "source": "https://github.com/symfony/http-client/tree/v7.4.13"
+ "source": "https://github.com/symfony/http-client/tree/v7.4.14"
},
"funding": [
{
@@ -13265,20 +13351,20 @@
"type": "tidelift"
}
],
- "time": "2026-05-24T09:57:54+00:00"
+ "time": "2026-06-16T11:50:14+00:00"
},
{
"name": "symfony/http-client-contracts",
- "version": "v3.7.0",
+ "version": "v3.7.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-client-contracts.git",
- "reference": "4a2d00c37651c0bdc2b9e1c773487a8bf4edb12d"
+ "reference": "41fc42d276aeff21192465331ebbab7d83a743c0"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/4a2d00c37651c0bdc2b9e1c773487a8bf4edb12d",
- "reference": "4a2d00c37651c0bdc2b9e1c773487a8bf4edb12d",
+ "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/41fc42d276aeff21192465331ebbab7d83a743c0",
+ "reference": "41fc42d276aeff21192465331ebbab7d83a743c0",
"shasum": ""
},
"require": {
@@ -13327,7 +13413,7 @@
"standards"
],
"support": {
- "source": "https://github.com/symfony/http-client-contracts/tree/v3.7.0"
+ "source": "https://github.com/symfony/http-client-contracts/tree/v3.7.1"
},
"funding": [
{
@@ -13347,20 +13433,20 @@
"type": "tidelift"
}
],
- "time": "2026-03-06T13:17:50+00:00"
+ "time": "2026-06-05T06:23:12+00:00"
},
{
"name": "symfony/http-foundation",
- "version": "v7.4.13",
+ "version": "v7.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
- "reference": "bc354f47c62301e990b7874fa662326368508e2c"
+ "reference": "06db5ae1552177bf8572f8908839f12e3c06aed3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-foundation/zipball/bc354f47c62301e990b7874fa662326368508e2c",
- "reference": "bc354f47c62301e990b7874fa662326368508e2c",
+ "url": "https://api.github.com/repos/symfony/http-foundation/zipball/06db5ae1552177bf8572f8908839f12e3c06aed3",
+ "reference": "06db5ae1552177bf8572f8908839f12e3c06aed3",
"shasum": ""
},
"require": {
@@ -13409,7 +13495,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.4.13"
+ "source": "https://github.com/symfony/http-foundation/tree/v7.4.14"
},
"funding": [
{
@@ -13429,20 +13515,20 @@
"type": "tidelift"
}
],
- "time": "2026-05-24T11:20:33+00:00"
+ "time": "2026-06-11T07:31:44+00:00"
},
{
"name": "symfony/http-kernel",
- "version": "v7.4.13",
+ "version": "v7.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-kernel.git",
- "reference": "9df847980c436451f4f51d1284491bb4356dd989"
+ "reference": "e99af79b1e776646eda0e1c23b7b45c184ff99be"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-kernel/zipball/9df847980c436451f4f51d1284491bb4356dd989",
- "reference": "9df847980c436451f4f51d1284491bb4356dd989",
+ "url": "https://api.github.com/repos/symfony/http-kernel/zipball/e99af79b1e776646eda0e1c23b7b45c184ff99be",
+ "reference": "e99af79b1e776646eda0e1c23b7b45c184ff99be",
"shasum": ""
},
"require": {
@@ -13528,7 +13614,7 @@
"description": "Provides a structured process for converting a Request into a Response",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/http-kernel/tree/v7.4.13"
+ "source": "https://github.com/symfony/http-kernel/tree/v7.4.14"
},
"funding": [
{
@@ -13548,20 +13634,20 @@
"type": "tidelift"
}
],
- "time": "2026-05-27T08:31:43+00:00"
+ "time": "2026-06-27T09:14:35+00:00"
},
{
"name": "symfony/intl",
- "version": "v7.4.8",
+ "version": "v7.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/intl.git",
- "reference": "7cfb7792d580dea833647420afd5f2f98df8457b"
+ "reference": "02d77a81a198788444a8371658ef98e2e20c8c2b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/intl/zipball/7cfb7792d580dea833647420afd5f2f98df8457b",
- "reference": "7cfb7792d580dea833647420afd5f2f98df8457b",
+ "url": "https://api.github.com/repos/symfony/intl/zipball/02d77a81a198788444a8371658ef98e2e20c8c2b",
+ "reference": "02d77a81a198788444a8371658ef98e2e20c8c2b",
"shasum": ""
},
"require": {
@@ -13618,7 +13704,7 @@
"localization"
],
"support": {
- "source": "https://github.com/symfony/intl/tree/v7.4.8"
+ "source": "https://github.com/symfony/intl/tree/v7.4.14"
},
"funding": [
{
@@ -13638,20 +13724,20 @@
"type": "tidelift"
}
],
- "time": "2026-03-30T12:55:43+00:00"
+ "time": "2026-06-05T06:22:21+00:00"
},
{
"name": "symfony/mailer",
- "version": "v7.4.12",
+ "version": "v7.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/mailer.git",
- "reference": "5cefb712a25f320579615ba9e1942abaeade7dff"
+ "reference": "f88ce03ae73e3edb5c176ce1f337709996e88495"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/mailer/zipball/5cefb712a25f320579615ba9e1942abaeade7dff",
- "reference": "5cefb712a25f320579615ba9e1942abaeade7dff",
+ "url": "https://api.github.com/repos/symfony/mailer/zipball/f88ce03ae73e3edb5c176ce1f337709996e88495",
+ "reference": "f88ce03ae73e3edb5c176ce1f337709996e88495",
"shasum": ""
},
"require": {
@@ -13702,7 +13788,7 @@
"description": "Helps sending emails",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/mailer/tree/v7.4.12"
+ "source": "https://github.com/symfony/mailer/tree/v7.4.14"
},
"funding": [
{
@@ -13722,7 +13808,7 @@
"type": "tidelift"
}
],
- "time": "2026-05-20T07:20:23+00:00"
+ "time": "2026-06-13T08:51:35+00:00"
},
{
"name": "symfony/mime",
@@ -15196,16 +15282,16 @@
},
{
"name": "symfony/rate-limiter",
- "version": "v7.4.13",
+ "version": "v7.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/rate-limiter.git",
- "reference": "8b162768544e5a8895c52161d63c999aca91f4a9"
+ "reference": "5c0af3fdc93e6115d9b806a6ea73e7de040e711d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/rate-limiter/zipball/8b162768544e5a8895c52161d63c999aca91f4a9",
- "reference": "8b162768544e5a8895c52161d63c999aca91f4a9",
+ "url": "https://api.github.com/repos/symfony/rate-limiter/zipball/5c0af3fdc93e6115d9b806a6ea73e7de040e711d",
+ "reference": "5c0af3fdc93e6115d9b806a6ea73e7de040e711d",
"shasum": ""
},
"require": {
@@ -15246,7 +15332,7 @@
"rate-limiter"
],
"support": {
- "source": "https://github.com/symfony/rate-limiter/tree/v7.4.13"
+ "source": "https://github.com/symfony/rate-limiter/tree/v7.4.14"
},
"funding": [
{
@@ -15266,7 +15352,7 @@
"type": "tidelift"
}
],
- "time": "2026-05-23T16:05:06+00:00"
+ "time": "2026-06-08T20:24:16+00:00"
},
{
"name": "symfony/routing",
@@ -15355,16 +15441,16 @@
},
{
"name": "symfony/runtime",
- "version": "v7.4.13",
+ "version": "v7.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/runtime.git",
- "reference": "1a24cf8aab3a9378117718b35525c4126ad3adec"
+ "reference": "15497f743dd714f3167cb6a56509b9d42e6417b3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/runtime/zipball/1a24cf8aab3a9378117718b35525c4126ad3adec",
- "reference": "1a24cf8aab3a9378117718b35525c4126ad3adec",
+ "url": "https://api.github.com/repos/symfony/runtime/zipball/15497f743dd714f3167cb6a56509b9d42e6417b3",
+ "reference": "15497f743dd714f3167cb6a56509b9d42e6417b3",
"shasum": ""
},
"require": {
@@ -15372,7 +15458,8 @@
"php": ">=8.2"
},
"conflict": {
- "symfony/dotenv": "<6.4"
+ "symfony/dotenv": "<6.4",
+ "symfony/http-foundation": "<6.4"
},
"require-dev": {
"composer/composer": "^2.6",
@@ -15415,7 +15502,7 @@
"runtime"
],
"support": {
- "source": "https://github.com/symfony/runtime/tree/v7.4.13"
+ "source": "https://github.com/symfony/runtime/tree/v7.4.14"
},
"funding": [
{
@@ -15435,20 +15522,20 @@
"type": "tidelift"
}
],
- "time": "2026-05-23T18:04:28+00:00"
+ "time": "2026-06-05T06:22:21+00:00"
},
{
"name": "symfony/security-bundle",
- "version": "v7.4.13",
+ "version": "v7.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/security-bundle.git",
- "reference": "0cbc6528aa583795ab44e43b4e92a09acf927c6f"
+ "reference": "6ec147e67262c6f5ac5dbb8b2ccbb34af14c4d79"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/security-bundle/zipball/0cbc6528aa583795ab44e43b4e92a09acf927c6f",
- "reference": "0cbc6528aa583795ab44e43b4e92a09acf927c6f",
+ "url": "https://api.github.com/repos/symfony/security-bundle/zipball/6ec147e67262c6f5ac5dbb8b2ccbb34af14c4d79",
+ "reference": "6ec147e67262c6f5ac5dbb8b2ccbb34af14c4d79",
"shasum": ""
},
"require": {
@@ -15527,7 +15614,7 @@
"description": "Provides a tight integration of the Security component into the Symfony full-stack framework",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/security-bundle/tree/v7.4.13"
+ "source": "https://github.com/symfony/security-bundle/tree/v7.4.14"
},
"funding": [
{
@@ -15547,20 +15634,20 @@
"type": "tidelift"
}
],
- "time": "2026-05-23T16:05:06+00:00"
+ "time": "2026-06-16T15:54:05+00:00"
},
{
"name": "symfony/security-core",
- "version": "v7.4.13",
+ "version": "v7.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/security-core.git",
- "reference": "25db686fcf2a3fe00e1cf6dcab1fcb7aac71ba9b"
+ "reference": "880bb18eff6188d55115e795f06e4185373c35fd"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/security-core/zipball/25db686fcf2a3fe00e1cf6dcab1fcb7aac71ba9b",
- "reference": "25db686fcf2a3fe00e1cf6dcab1fcb7aac71ba9b",
+ "url": "https://api.github.com/repos/symfony/security-core/zipball/880bb18eff6188d55115e795f06e4185373c35fd",
+ "reference": "880bb18eff6188d55115e795f06e4185373c35fd",
"shasum": ""
},
"require": {
@@ -15618,7 +15705,7 @@
"description": "Symfony Security Component - Core Library",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/security-core/tree/v7.4.13"
+ "source": "https://github.com/symfony/security-core/tree/v7.4.14"
},
"funding": [
{
@@ -15638,7 +15725,7 @@
"type": "tidelift"
}
],
- "time": "2026-05-23T16:05:06+00:00"
+ "time": "2026-06-09T07:51:57+00:00"
},
{
"name": "symfony/security-csrf",
@@ -15716,16 +15803,16 @@
},
{
"name": "symfony/security-http",
- "version": "v7.4.13",
+ "version": "v7.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/security-http.git",
- "reference": "da3c28025a664e6a88e1af104a74457d99301161"
+ "reference": "148e038b91c8cc3e42aee177f8b0117437077c9b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/security-http/zipball/da3c28025a664e6a88e1af104a74457d99301161",
- "reference": "da3c28025a664e6a88e1af104a74457d99301161",
+ "url": "https://api.github.com/repos/symfony/security-http/zipball/148e038b91c8cc3e42aee177f8b0117437077c9b",
+ "reference": "148e038b91c8cc3e42aee177f8b0117437077c9b",
"shasum": ""
},
"require": {
@@ -15784,7 +15871,7 @@
"description": "Symfony Security Component - HTTP Integration",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/security-http/tree/v7.4.13"
+ "source": "https://github.com/symfony/security-http/tree/v7.4.14"
},
"funding": [
{
@@ -15804,20 +15891,20 @@
"type": "tidelift"
}
],
- "time": "2026-05-25T06:06:12+00:00"
+ "time": "2026-06-19T08:40:54+00:00"
},
{
"name": "symfony/serializer",
- "version": "v7.4.10",
+ "version": "v7.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/serializer.git",
- "reference": "268c5aa6c4bd675eddd89348e7ecac292a843ddd"
+ "reference": "55acb01b9c8a5211dfbaf68c314d90d0ed2cc3d1"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/serializer/zipball/268c5aa6c4bd675eddd89348e7ecac292a843ddd",
- "reference": "268c5aa6c4bd675eddd89348e7ecac292a843ddd",
+ "url": "https://api.github.com/repos/symfony/serializer/zipball/55acb01b9c8a5211dfbaf68c314d90d0ed2cc3d1",
+ "reference": "55acb01b9c8a5211dfbaf68c314d90d0ed2cc3d1",
"shasum": ""
},
"require": {
@@ -15888,7 +15975,7 @@
"description": "Handles serializing and deserializing data structures, including object graphs, into array structures or other formats like XML and JSON.",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/serializer/tree/v7.4.10"
+ "source": "https://github.com/symfony/serializer/tree/v7.4.14"
},
"funding": [
{
@@ -15908,20 +15995,20 @@
"type": "tidelift"
}
],
- "time": "2026-05-03T13:03:28+00:00"
+ "time": "2026-06-27T08:31:18+00:00"
},
{
"name": "symfony/service-contracts",
- "version": "v3.7.0",
+ "version": "v3.7.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/service-contracts.git",
- "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a"
+ "reference": "c0a284bab1ed8aa0417e3d69250ab437739563a0"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d25d82433a80eba6aa0e6c24b61d7370d99e444a",
- "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a",
+ "url": "https://api.github.com/repos/symfony/service-contracts/zipball/c0a284bab1ed8aa0417e3d69250ab437739563a0",
+ "reference": "c0a284bab1ed8aa0417e3d69250ab437739563a0",
"shasum": ""
},
"require": {
@@ -15975,7 +16062,7 @@
"standards"
],
"support": {
- "source": "https://github.com/symfony/service-contracts/tree/v3.7.0"
+ "source": "https://github.com/symfony/service-contracts/tree/v3.7.1"
},
"funding": [
{
@@ -15995,7 +16082,7 @@
"type": "tidelift"
}
],
- "time": "2026-03-28T09:44:51+00:00"
+ "time": "2026-06-16T09:55:08+00:00"
},
{
"name": "symfony/stimulus-bundle",
@@ -16229,16 +16316,16 @@
},
{
"name": "symfony/translation",
- "version": "v7.4.10",
+ "version": "v7.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/translation.git",
- "reference": "ada7578c30dd5feaa8259cff3e885069ea81ddde"
+ "reference": "a1af4dacb24eb7ef4f1ca71b94da8ddbce572281"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/translation/zipball/ada7578c30dd5feaa8259cff3e885069ea81ddde",
- "reference": "ada7578c30dd5feaa8259cff3e885069ea81ddde",
+ "url": "https://api.github.com/repos/symfony/translation/zipball/a1af4dacb24eb7ef4f1ca71b94da8ddbce572281",
+ "reference": "a1af4dacb24eb7ef4f1ca71b94da8ddbce572281",
"shasum": ""
},
"require": {
@@ -16305,7 +16392,7 @@
"description": "Provides tools to internationalize your application",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/translation/tree/v7.4.10"
+ "source": "https://github.com/symfony/translation/tree/v7.4.14"
},
"funding": [
{
@@ -16325,20 +16412,20 @@
"type": "tidelift"
}
],
- "time": "2026-05-06T11:19:24+00:00"
+ "time": "2026-06-06T09:33:19+00:00"
},
{
"name": "symfony/translation-contracts",
- "version": "v3.7.0",
+ "version": "v3.7.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/translation-contracts.git",
- "reference": "0ab302977a952b42fd51475c4ebac81f8da0a95d"
+ "reference": "ccb206b98faccc511ebae8e5fad50f2dc0b30621"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/0ab302977a952b42fd51475c4ebac81f8da0a95d",
- "reference": "0ab302977a952b42fd51475c4ebac81f8da0a95d",
+ "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/ccb206b98faccc511ebae8e5fad50f2dc0b30621",
+ "reference": "ccb206b98faccc511ebae8e5fad50f2dc0b30621",
"shasum": ""
},
"require": {
@@ -16387,7 +16474,7 @@
"standards"
],
"support": {
- "source": "https://github.com/symfony/translation-contracts/tree/v3.7.0"
+ "source": "https://github.com/symfony/translation-contracts/tree/v3.7.1"
},
"funding": [
{
@@ -16407,20 +16494,20 @@
"type": "tidelift"
}
],
- "time": "2026-01-05T13:30:16+00:00"
+ "time": "2026-06-05T06:23:12+00:00"
},
{
"name": "symfony/twig-bridge",
- "version": "v7.4.12",
+ "version": "v7.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/twig-bridge.git",
- "reference": "81663873d946531129c76c65e80b681ce99c0e89"
+ "reference": "e4574ab4d5411a7c495d4189b15a8ecfbc720332"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/81663873d946531129c76c65e80b681ce99c0e89",
- "reference": "81663873d946531129c76c65e80b681ce99c0e89",
+ "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/e4574ab4d5411a7c495d4189b15a8ecfbc720332",
+ "reference": "e4574ab4d5411a7c495d4189b15a8ecfbc720332",
"shasum": ""
},
"require": {
@@ -16502,7 +16589,7 @@
"description": "Provides integration for Twig with various Symfony components",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/twig-bridge/tree/v7.4.12"
+ "source": "https://github.com/symfony/twig-bridge/tree/v7.4.14"
},
"funding": [
{
@@ -16522,20 +16609,20 @@
"type": "tidelift"
}
],
- "time": "2026-04-29T17:13:54+00:00"
+ "time": "2026-06-17T13:16:29+00:00"
},
{
"name": "symfony/twig-bundle",
- "version": "v7.4.8",
+ "version": "v7.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/twig-bundle.git",
- "reference": "ba1e06d7ff1ebb1d1799b6608d925f4eaba88d95"
+ "reference": "11b69c64efdd0c3465403bb2747bf4585add1ec7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/ba1e06d7ff1ebb1d1799b6608d925f4eaba88d95",
- "reference": "ba1e06d7ff1ebb1d1799b6608d925f4eaba88d95",
+ "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/11b69c64efdd0c3465403bb2747bf4585add1ec7",
+ "reference": "11b69c64efdd0c3465403bb2747bf4585add1ec7",
"shasum": ""
},
"require": {
@@ -16592,7 +16679,7 @@
"description": "Provides a tight integration of Twig into the Symfony full-stack framework",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/twig-bundle/tree/v7.4.8"
+ "source": "https://github.com/symfony/twig-bundle/tree/v7.4.14"
},
"funding": [
{
@@ -16612,7 +16699,7 @@
"type": "tidelift"
}
],
- "time": "2026-03-24T13:12:05+00:00"
+ "time": "2026-06-05T06:22:21+00:00"
},
{
"name": "symfony/type-info",
@@ -16961,16 +17048,16 @@
},
{
"name": "symfony/validator",
- "version": "v7.4.10",
+ "version": "v7.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/validator.git",
- "reference": "c76458623af9a3fe3b2e5b09b36453f334c2a361"
+ "reference": "306d904336166d001751759351d40d5e82312596"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/validator/zipball/c76458623af9a3fe3b2e5b09b36453f334c2a361",
- "reference": "c76458623af9a3fe3b2e5b09b36453f334c2a361",
+ "url": "https://api.github.com/repos/symfony/validator/zipball/306d904336166d001751759351d40d5e82312596",
+ "reference": "306d904336166d001751759351d40d5e82312596",
"shasum": ""
},
"require": {
@@ -17041,7 +17128,7 @@
"description": "Provides tools to validate values",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/validator/tree/v7.4.10"
+ "source": "https://github.com/symfony/validator/tree/v7.4.14"
},
"funding": [
{
@@ -17061,20 +17148,20 @@
"type": "tidelift"
}
],
- "time": "2026-05-05T15:30:56+00:00"
+ "time": "2026-06-27T06:16:12+00:00"
},
{
"name": "symfony/var-dumper",
- "version": "v7.4.8",
+ "version": "v7.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-dumper.git",
- "reference": "9510c3966f749a1d1ff0059e1eabef6cc621e7fd"
+ "reference": "9a3a56a4a1e65a5cb4f8d13801fe8ab0a170e358"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/var-dumper/zipball/9510c3966f749a1d1ff0059e1eabef6cc621e7fd",
- "reference": "9510c3966f749a1d1ff0059e1eabef6cc621e7fd",
+ "url": "https://api.github.com/repos/symfony/var-dumper/zipball/9a3a56a4a1e65a5cb4f8d13801fe8ab0a170e358",
+ "reference": "9a3a56a4a1e65a5cb4f8d13801fe8ab0a170e358",
"shasum": ""
},
"require": {
@@ -17128,7 +17215,7 @@
"dump"
],
"support": {
- "source": "https://github.com/symfony/var-dumper/tree/v7.4.8"
+ "source": "https://github.com/symfony/var-dumper/tree/v7.4.14"
},
"funding": [
{
@@ -17148,20 +17235,20 @@
"type": "tidelift"
}
],
- "time": "2026-03-30T13:44:50+00:00"
+ "time": "2026-06-08T20:24:16+00:00"
},
{
"name": "symfony/var-exporter",
- "version": "v7.4.9",
+ "version": "v7.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-exporter.git",
- "reference": "22e03a49c95ef054a43601cd159b222bfab1c701"
+ "reference": "0118811b1d59f323bf131250b3fb919febfece28"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/var-exporter/zipball/22e03a49c95ef054a43601cd159b222bfab1c701",
- "reference": "22e03a49c95ef054a43601cd159b222bfab1c701",
+ "url": "https://api.github.com/repos/symfony/var-exporter/zipball/0118811b1d59f323bf131250b3fb919febfece28",
+ "reference": "0118811b1d59f323bf131250b3fb919febfece28",
"shasum": ""
},
"require": {
@@ -17209,7 +17296,7 @@
"serialize"
],
"support": {
- "source": "https://github.com/symfony/var-exporter/tree/v7.4.9"
+ "source": "https://github.com/symfony/var-exporter/tree/v7.4.14"
},
"funding": [
{
@@ -17229,7 +17316,7 @@
"type": "tidelift"
}
],
- "time": "2026-04-18T13:18:21+00:00"
+ "time": "2026-06-27T08:41:53+00:00"
},
{
"name": "symfony/web-link",
@@ -17320,16 +17407,16 @@
},
{
"name": "symfony/webpack-encore-bundle",
- "version": "v2.4.0",
+ "version": "v2.4.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/webpack-encore-bundle.git",
- "reference": "5b932e0feddd81aaf0ecd7d5fcd2e450e5a7817e"
+ "reference": "cac8d6c722999c8add9272f9de6e8079628df4f5"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/webpack-encore-bundle/zipball/5b932e0feddd81aaf0ecd7d5fcd2e450e5a7817e",
- "reference": "5b932e0feddd81aaf0ecd7d5fcd2e450e5a7817e",
+ "url": "https://api.github.com/repos/symfony/webpack-encore-bundle/zipball/cac8d6c722999c8add9272f9de6e8079628df4f5",
+ "reference": "cac8d6c722999c8add9272f9de6e8079628df4f5",
"shasum": ""
},
"require": {
@@ -17372,7 +17459,7 @@
"description": "Integration of your Symfony app with Webpack Encore",
"support": {
"issues": "https://github.com/symfony/webpack-encore-bundle/issues",
- "source": "https://github.com/symfony/webpack-encore-bundle/tree/v2.4.0"
+ "source": "https://github.com/symfony/webpack-encore-bundle/tree/v2.4.1"
},
"funding": [
{
@@ -17392,20 +17479,20 @@
"type": "tidelift"
}
],
- "time": "2025-11-27T13:41:46+00:00"
+ "time": "2026-06-24T07:21:58+00:00"
},
{
"name": "symfony/yaml",
- "version": "v7.4.13",
+ "version": "v7.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
- "reference": "a7ec3b1156faf8815db7683ec7c1e7338e6f977c"
+ "reference": "f8f328665ace2370d1e10645b807ba1646dc7dcc"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/yaml/zipball/a7ec3b1156faf8815db7683ec7c1e7338e6f977c",
- "reference": "a7ec3b1156faf8815db7683ec7c1e7338e6f977c",
+ "url": "https://api.github.com/repos/symfony/yaml/zipball/f8f328665ace2370d1e10645b807ba1646dc7dcc",
+ "reference": "f8f328665ace2370d1e10645b807ba1646dc7dcc",
"shasum": ""
},
"require": {
@@ -17448,7 +17535,7 @@
"description": "Loads and dumps YAML files",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/yaml/tree/v7.4.13"
+ "source": "https://github.com/symfony/yaml/tree/v7.4.14"
},
"funding": [
{
@@ -17468,20 +17555,20 @@
"type": "tidelift"
}
],
- "time": "2026-05-25T06:06:12+00:00"
+ "time": "2026-06-08T20:24:16+00:00"
},
{
"name": "symplify/easy-coding-standard",
- "version": "13.2.2",
+ "version": "13.2.3",
"source": {
"type": "git",
"url": "https://github.com/ecsphp/ecs.git",
- "reference": "3da31a9c267ead69664eb1258aaea62bf9b66f79"
+ "reference": "94f56bce0420d4e837a85c4b2c6501293a5974eb"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/ecsphp/ecs/zipball/3da31a9c267ead69664eb1258aaea62bf9b66f79",
- "reference": "3da31a9c267ead69664eb1258aaea62bf9b66f79",
+ "url": "https://api.github.com/repos/ecsphp/ecs/zipball/94f56bce0420d4e837a85c4b2c6501293a5974eb",
+ "reference": "94f56bce0420d4e837a85c4b2c6501293a5974eb",
"shasum": ""
},
"require": {
@@ -17516,22 +17603,22 @@
"static analysis"
],
"support": {
- "source": "https://github.com/ecsphp/ecs/tree/13.2.2"
+ "source": "https://github.com/ecsphp/ecs/tree/13.2.3"
},
- "time": "2026-06-13T19:45:53+00:00"
+ "time": "2026-06-15T22:08:41+00:00"
},
{
"name": "tecnickcom/tc-lib-barcode",
- "version": "2.7.0",
+ "version": "2.11.1",
"source": {
"type": "git",
"url": "https://github.com/tecnickcom/tc-lib-barcode.git",
- "reference": "4e53047a4ba4ed592ae677b3729ce9bfeae1cfbb"
+ "reference": "69238f94a1e46332ebc057fddf5bba2f776d56f6"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/tecnickcom/tc-lib-barcode/zipball/4e53047a4ba4ed592ae677b3729ce9bfeae1cfbb",
- "reference": "4e53047a4ba4ed592ae677b3729ce9bfeae1cfbb",
+ "url": "https://api.github.com/repos/tecnickcom/tc-lib-barcode/zipball/69238f94a1e46332ebc057fddf5bba2f776d56f6",
+ "reference": "69238f94a1e46332ebc057fddf5bba2f776d56f6",
"shasum": ""
},
"require": {
@@ -17540,12 +17627,11 @@
"ext-gd": "*",
"ext-pcre": "*",
"php": ">=8.2",
- "tecnickcom/tc-lib-color": "^2.7"
+ "tecnickcom/tc-lib-color": "^2.12"
},
"require-dev": {
"pdepend/pdepend": "^2.16",
- "phpcompatibility/php-compatibility": "^10.0.0@dev",
- "phpunit/phpunit": "^13.1 || ^12.5 || ^11.5"
+ "phpunit/phpunit": "^11.5 || ^12.5 || ^13.2"
},
"type": "library",
"autoload": {
@@ -17617,20 +17703,20 @@
"type": "github"
}
],
- "time": "2026-05-22T07:09:18+00:00"
+ "time": "2026-06-26T10:37:56+00:00"
},
{
"name": "tecnickcom/tc-lib-color",
- "version": "2.8.0",
+ "version": "2.12.2",
"source": {
"type": "git",
"url": "https://github.com/tecnickcom/tc-lib-color.git",
- "reference": "6947cc9fffe23a21642279b8ab73a43f3311c5f9"
+ "reference": "ba7d43d9bb06946c9717a4e3c6aa73346266cdbf"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/tecnickcom/tc-lib-color/zipball/6947cc9fffe23a21642279b8ab73a43f3311c5f9",
- "reference": "6947cc9fffe23a21642279b8ab73a43f3311c5f9",
+ "url": "https://api.github.com/repos/tecnickcom/tc-lib-color/zipball/ba7d43d9bb06946c9717a4e3c6aa73346266cdbf",
+ "reference": "ba7d43d9bb06946c9717a4e3c6aa73346266cdbf",
"shasum": ""
},
"require": {
@@ -17639,8 +17725,7 @@
},
"require-dev": {
"pdepend/pdepend": "^2.16",
- "phpcompatibility/php-compatibility": "^10.0.0@dev",
- "phpunit/phpunit": "^13.1 || ^12.5 || ^11.5"
+ "phpunit/phpunit": "^11.5 || ^12.5 || ^13.2"
},
"type": "library",
"autoload": {
@@ -17685,7 +17770,7 @@
"type": "github"
}
],
- "time": "2026-05-22T06:55:57+00:00"
+ "time": "2026-06-26T10:26:00+00:00"
},
{
"name": "thecodingmachine/safe",
@@ -18802,16 +18887,16 @@
},
{
"name": "webmozart/assert",
- "version": "2.4.0",
+ "version": "2.4.1",
"source": {
"type": "git",
"url": "https://github.com/webmozarts/assert.git",
- "reference": "9007ea6f45ecf352a9422b36644e4bfc039b9155"
+ "reference": "2ccb7c2e821038c03a3e6e1700c570c158c55f70"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/webmozarts/assert/zipball/9007ea6f45ecf352a9422b36644e4bfc039b9155",
- "reference": "9007ea6f45ecf352a9422b36644e4bfc039b9155",
+ "url": "https://api.github.com/repos/webmozarts/assert/zipball/2ccb7c2e821038c03a3e6e1700c570c158c55f70",
+ "reference": "2ccb7c2e821038c03a3e6e1700c570c158c55f70",
"shasum": ""
},
"require": {
@@ -18862,9 +18947,9 @@
],
"support": {
"issues": "https://github.com/webmozarts/assert/issues",
- "source": "https://github.com/webmozarts/assert/tree/2.4.0"
+ "source": "https://github.com/webmozarts/assert/tree/2.4.1"
},
- "time": "2026-05-20T13:07:01+00:00"
+ "time": "2026-06-15T15:31:57+00:00"
},
{
"name": "willdurand/negotiation",
@@ -19693,16 +19778,16 @@
},
{
"name": "phpstan/phpstan-symfony",
- "version": "2.0.19",
+ "version": "2.0.20",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-symfony.git",
- "reference": "546071ed7f80a89ec30909346eb7cc741800740a"
+ "reference": "53f1a6462dbe71fad36ce054caf5e1b725b740fd"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/546071ed7f80a89ec30909346eb7cc741800740a",
- "reference": "546071ed7f80a89ec30909346eb7cc741800740a",
+ "url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/53f1a6462dbe71fad36ce054caf5e1b725b740fd",
+ "reference": "53f1a6462dbe71fad36ce054caf5e1b725b740fd",
"shasum": ""
},
"require": {
@@ -19761,9 +19846,9 @@
],
"support": {
"issues": "https://github.com/phpstan/phpstan-symfony/issues",
- "source": "https://github.com/phpstan/phpstan-symfony/tree/2.0.19"
+ "source": "https://github.com/phpstan/phpstan-symfony/tree/2.0.20"
},
- "time": "2026-05-29T12:52:44+00:00"
+ "time": "2026-06-16T09:17:35+00:00"
},
{
"name": "phpunit/php-code-coverage",
@@ -20224,21 +20309,21 @@
},
{
"name": "rector/rector",
- "version": "2.4.5",
+ "version": "2.5.2",
"source": {
"type": "git",
"url": "https://github.com/rectorphp/rector.git",
- "reference": "cbd86024be5014d3c14d9f0b3f7aae8ecbffd62c"
+ "reference": "49ff6339174bdbdf50b0b35ecbcff14a05ac9e24"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/rectorphp/rector/zipball/cbd86024be5014d3c14d9f0b3f7aae8ecbffd62c",
- "reference": "cbd86024be5014d3c14d9f0b3f7aae8ecbffd62c",
+ "url": "https://api.github.com/repos/rectorphp/rector/zipball/49ff6339174bdbdf50b0b35ecbcff14a05ac9e24",
+ "reference": "49ff6339174bdbdf50b0b35ecbcff14a05ac9e24",
"shasum": ""
},
"require": {
"php": "^7.4|^8.0",
- "phpstan/phpstan": "^2.1.56"
+ "phpstan/phpstan": "^2.2.2"
},
"conflict": {
"rector/rector-doctrine": "*",
@@ -20272,7 +20357,7 @@
],
"support": {
"issues": "https://github.com/rectorphp/rector/issues",
- "source": "https://github.com/rectorphp/rector/tree/2.4.5"
+ "source": "https://github.com/rectorphp/rector/tree/2.5.2"
},
"funding": [
{
@@ -20280,7 +20365,7 @@
"type": "github"
}
],
- "time": "2026-05-26T21:03:22+00:00"
+ "time": "2026-06-22T11:39:33+00:00"
},
{
"name": "roave/security-advisories",
@@ -20288,12 +20373,12 @@
"source": {
"type": "git",
"url": "https://github.com/Roave/SecurityAdvisories.git",
- "reference": "810e1d8cfbc718df8dacf74b62b7b9a1780b9698"
+ "reference": "36ba91e82e1b493faef2c13277d6bd2669ea9f31"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/810e1d8cfbc718df8dacf74b62b7b9a1780b9698",
- "reference": "810e1d8cfbc718df8dacf74b62b7b9a1780b9698",
+ "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/36ba91e82e1b493faef2c13277d6bd2669ea9f31",
+ "reference": "36ba91e82e1b493faef2c13277d6bd2669ea9f31",
"shasum": ""
},
"conflict": {
@@ -20310,6 +20395,7 @@
"aimeos/aimeos-core": ">=2022.04.1,<2022.10.17|>=2023.04.1,<2023.10.17|>=2024.04.1,<2024.04.7",
"aimeos/aimeos-laravel": "==2021.10",
"aimeos/aimeos-typo3": "<19.10.12|>=20,<20.10.5",
+ "aimeos/pagible": "<0.10.4",
"airesvsg/acf-to-rest-api": "<=3.1",
"akaunting/akaunting": "<2.1.13",
"akeneo/pim-community-dev": "<5.0.119|>=6,<6.0.53",
@@ -20394,13 +20480,14 @@
"bytefury/crater": "<6.0.2",
"cachethq/cachet": "<2.5.1",
"cadmium-org/cadmium-cms": "<=0.4.9",
- "cakephp/cakephp": "<3.10.3|>=4,<4.0.10|>=4.1,<4.1.4|>=4.2,<4.2.12|>=4.3,<4.3.11|>=4.4,<4.4.10|>=5.2.10,<5.2.12|==5.3",
+ "cakephp/authentication": "<3.3.6|>=4,<4.1.1",
+ "cakephp/cakephp": "<4.5.11|>=4.6,<4.6.4|>=5,<5.1.7|>=5.2,<5.2.13|>=5.3,<5.3.6",
"cakephp/database": ">=4.2,<4.2.12|>=4.3,<4.3.11|>=4.4,<4.4.10",
"cardgate/magento2": "<2.0.33",
"cardgate/woocommerce": "<=3.1.15",
- "cart2quote/module-quotation": ">=4.1.6,<=4.4.5|>=5,<5.4.4",
+ "cart2quote/module-quotation": ">=4.1.6,<4.4.6|>=5,<5.4.4",
"cart2quote/module-quotation-encoded": ">=4.1.6,<=4.4.5|>=5,<5.4.4",
- "cartalyst/sentry": "<=2.1.6",
+ "cartalyst/sentry": "<2.1.7",
"catfan/medoo": "<1.7.5",
"causal/oidc": "<4",
"cecil/cecil": "<7.47.1",
@@ -20426,7 +20513,7 @@
"commerceteam/commerce": ">=0.9.6,<0.9.9",
"components/jquery": ">=1.0.3,<3.5",
"composer/composer": "<2.2.28|>=2.3,<2.9.8",
- "concrete5/concrete5": "<9.4.8",
+ "concrete5/concrete5": "<9.5.1",
"concrete5/core": "<8.5.8|>=9,<9.1",
"contao-components/mediaelement": ">=2.14.2,<2.21.1",
"contao/comments-bundle": ">=2,<4.13.40|>=5.0.0.0-RC1-dev,<5.3.4",
@@ -20438,12 +20525,13 @@
"coreshop/core-shop": "<4.1.9|==5",
"corveda/phpsandbox": "<1.3.5",
"cosenary/instagram": "<=2.3",
+ "cotonti/cotonti": "<=1",
"couleurcitron/tarteaucitron-wp": "<0.3",
"cpsit/typo3-mailqueue": "<0.4.5|>=0.5,<0.5.2",
"craftcms/aws-s3": ">=2.0.2,<=2.2.4",
"craftcms/azure-blob": ">=2.0.0.0-beta1,<=2.1",
- "craftcms/cms": "<4.17.12|>=5,<5.9.18",
- "craftcms/commerce": ">=4,<4.11|>=5,<5.6",
+ "craftcms/cms": "<4.18|>=5,<5.10",
+ "craftcms/commerce": ">=4,<=4.11.1|>=5,<=5.6.4",
"craftcms/composer": ">=4.0.0.0-RC1-dev,<=4.10|>=5.0.0.0-RC1-dev,<=5.5.1",
"craftcms/craft": ">=3.5,<=4.16.17|>=5.0.0.0-RC1-dev,<=5.8.21",
"craftcms/google-cloud": ">=2.0.0.0-beta1,<=2.2",
@@ -20498,7 +20586,7 @@
"drupal/commerce_alphabank_redirect": "<1.0.3",
"drupal/commerce_eurobank_redirect": "<2.1.1",
"drupal/config_split": "<1.10|>=2,<2.0.2",
- "drupal/core": ">=6,<6.38|>=7,<7.103|>=8,<10.5.9|>=10.6,<10.6.7|>=11,<11.2.11|>=11.3,<11.3.7",
+ "drupal/core": ">=6,<6.38|>=7,<7.103|>=8,<10.5.10|>=10.6,<10.6.9|>=11,<11.2.12|>=11.3,<11.3.10",
"drupal/core-recommended": ">=7,<7.102|>=8,<10.2.11|>=10.3,<10.3.9|>=11,<11.0.8",
"drupal/currency": "<3.5",
"drupal/drupal": ">=5,<5.11|>=6,<6.38|>=7,<7.102|>=8,<10.2.11|>=10.3,<10.3.9|>=11,<11.0.8",
@@ -20570,9 +20658,10 @@
"feehi/feehicms": "<=2.1.1",
"fenom/fenom": "<=2.12.1",
"filament/actions": ">=3.2,<3.2.123|>=4,<=4.11.3|>=5,<=5.6.3",
- "filament/filament": ">=4,<4.3.1",
- "filament/infolists": ">=3,<3.2.115",
- "filament/tables": ">=3,<=3.3.50|>=4,<4.8.5|>=5,<5.3.5",
+ "filament/filament": ">=3,<=3.3.51|>=4,<4.11.5|>=5,<5.6.5",
+ "filament/forms": ">=3,<=3.3.52",
+ "filament/infolists": ">=3,<3.2.115|>=4,<=4.11.4|>=5,<=5.6.4",
+ "filament/tables": ">=3,<=3.3.50|>=4,<=4.11.4|>=5,<=5.6.4",
"filegator/filegator": "<7.8",
"filp/whoops": "<2.1.13",
"fineuploader/php-traditional-server": "<=1.2.2",
@@ -20618,10 +20707,10 @@
"georgringer/news": "<10.0.4|>=11,<11.4.4|>=12,<12.3.2|>=13,<13.0.2|>=14,<14.0.3",
"geshi/geshi": "<=1.0.9.1",
"getformwork/formwork": "<=2.3.3",
- "getgrav/grav": "<=2.0.0.0-RC1",
+ "getgrav/grav": "<=2.0.0.0-RC8",
"getgrav/grav-plugin-api": "<1.0.0.0-beta15",
"getgrav/grav-plugin-form": "<9.1",
- "getkirby/cms": "<=4.9|>=5,<=5.4",
+ "getkirby/cms": "<=4.9.3|>=5,<=5.4.3",
"getkirby/kirby": "<3.9.8.3-dev|>=3.10,<3.10.1.2-dev|>=4,<4.7.1",
"getkirby/panel": "<2.5.14",
"getkirby/starterkit": "<=3.7.0.2",
@@ -20638,10 +20727,10 @@
"gregwar/rst": "<1.0.3",
"grumpydictator/firefly-iii": "<=6.6.2",
"gugoan/economizzer": "<=0.9.0.0-beta1",
- "guzzlehttp/guzzle": "<6.5.8|>=7,<7.4.5",
+ "guzzlehttp/guzzle": "<7.12.1",
"guzzlehttp/guzzle-services": "<1.5.4",
"guzzlehttp/oauth-subscriber": "<0.8.1",
- "guzzlehttp/psr7": "<2.10.2",
+ "guzzlehttp/psr7": "<2.12.1",
"haffner/jh_captcha": "<=2.1.3|>=3,<=3.0.2",
"handcraftedinthealps/goodby-csv": "<1.4.3",
"harvesthq/chosen": "<1.8.7",
@@ -20696,6 +20785,7 @@
"jasig/phpcas": "<1.3.3",
"jbartels/wec-map": "<3.0.3",
"jcbrand/converse.js": "<3.3.3",
+ "jleehr/canto-saas-api": "<=2",
"joedolson/my-calendar": "<3.7.7",
"joelbutcher/socialstream": "<5.6|>=6,<6.2",
"johnbillion/query-monitor": "<3.20.4",
@@ -20739,7 +20829,7 @@
"lara-zeus/artemis": ">=1,<=1.0.6",
"lara-zeus/dynamic-dashboard": ">=3,<=3.0.1",
"laravel/fortify": "<1.11.1",
- "laravel/framework": "<12.60|>=13,<13.10",
+ "laravel/framework": "<12.61.1|>=13,<13.12",
"laravel/laravel": ">=5.4,<5.4.22",
"laravel/passport": ">=13,<13.7.1",
"laravel/pulse": "<1.3.1",
@@ -20830,6 +20920,7 @@
"movim/moxl": ">=0.8,<=0.10",
"movingbytes/social-network": "<=1.2.1",
"mpdf/mpdf": "<=7.1.7",
+ "mtdowling/jmespath.php": "<2.9.1",
"munkireport/comment": "<4",
"munkireport/managedinstalls": "<2.6",
"munkireport/munki_facts": "<1.5",
@@ -20899,6 +20990,7 @@
"paragonie/random_compat": "<2",
"paragonie/sodium_compat": "<1.24|>=2,<2.5",
"passbolt/passbolt_api": "<4.6.2",
+ "paymenter/paymenter": "<1.5",
"paypal/adaptivepayments-sdk-php": "<=3.9.2",
"paypal/invoice-sdk-php": "<=3.9",
"paypal/merchant-sdk-php": "<3.12",
@@ -20916,19 +21008,21 @@
"phenx/php-svg-lib": "<0.5.2",
"php-censor/php-censor": "<2.0.13|>=2.1,<2.1.5",
"php-mod/curl": "<2.3.2",
- "phpbb/phpbb": "<3.3.11",
+ "php-standard-library/h2": ">=6.1,<6.1.2|>=6.2,<6.2.1",
+ "php-standard-library/php-standard-library": ">=6.1,<6.1.2|>=6.2,<6.2.1",
+ "phpbb/phpbb": "<3.3.16|==4.0.0.0-alpha1",
"phpems/phpems": ">=6,<=6.1.3",
"phpfastcache/phpfastcache": "<6.1.5|>=7,<7.1.2|>=8,<8.0.7",
"phpmailer/phpmailer": "<6.5",
"phpmussel/phpmussel": ">=1,<1.6",
"phpmyadmin/phpmyadmin": "<5.2.2",
- "phpmyfaq/phpmyfaq": "<4.1.3",
+ "phpmyfaq/phpmyfaq": "<4.1.4",
"phpoffice/common": "<0.2.9",
"phpoffice/math": "<=0.2",
"phpoffice/phpexcel": "<=1.8.2",
"phpoffice/phpspreadsheet": "<=1.30.4|>=2,<=2.1.15|>=2.2,<=2.4.4|>=3,<=3.10.4|>=4,<=5.6",
"phppgadmin/phppgadmin": "<=7.13",
- "phpseclib/phpseclib": "<=2.0.53|>=3,<=3.0.51",
+ "phpseclib/phpseclib": "<=2.0.54|>=3,<=3.0.53",
"phpservermon/phpservermon": "<3.6",
"phpsysinfo/phpsysinfo": "<3.4.3",
"phpunit/phpunit": "<8.5.52|>=9,<9.6.33|>=10,<10.5.62|>=11,<11.5.50|>=12,<12.5.8|>=12.5.21,<12.5.22|>=13.1.5,<13.1.6",
@@ -20944,7 +21038,7 @@
"pimcore/demo": "<10.3",
"pimcore/ecommerce-framework-bundle": "<1.0.10",
"pimcore/perspective-editor": "<1.5.1",
- "pimcore/pimcore": "<=12.3.6",
+ "pimcore/pimcore": "<=12.3.8",
"pimcore/web2print-tools-bundle": "<=5.2.1|>=6.0.0.0-RC1-dev,<=6.1",
"piwik/piwik": "<1.11",
"pixelfed/pixelfed": "<0.12.5",
@@ -20952,6 +21046,7 @@
"pocketmine/bedrock-protocol": "<8.0.2",
"pocketmine/pocketmine-mp": "<5.42.1",
"pocketmine/raklib": ">=0.14,<0.14.6|>=0.15,<0.15.1",
+ "pontedilana/php-weasyprint": "<=2.5.1",
"poweradmin/poweradmin": "<4.2.4|>=4.3,<4.3.3",
"pressbooks/pressbooks": "<5.18",
"prestashop/autoupgrade": ">=4,<4.10.1",
@@ -20968,8 +21063,8 @@
"prestashop/ps_linklist": "<3.1",
"privatebin/privatebin": "<1.4|>=1.5,<1.7.4|>=1.7.7,<2.0.3",
"processwire/processwire": "<=3.0.255",
- "propel/propel": ">=2.0.0.0-alpha1,<=2.0.0.0-alpha7",
- "propel/propel1": ">=1,<=1.7.1",
+ "propel/propel": ">=2.0.0.0-alpha1,<2.0.0.0-alpha8",
+ "propel/propel1": ">=1,<1.7.2",
"psy/psysh": "<=0.11.22|>=0.12,<=0.12.18",
"pterodactyl/panel": "<1.12.3",
"ptheofan/yii2-statemachine": ">=2.0.0.0-RC1-dev,<=2",
@@ -21030,10 +21125,10 @@
"silverstripe-australia/advancedreports": ">=1,<=2",
"silverstripe/admin": "<1.13.19|>=2,<2.1.8",
"silverstripe/assets": "<2.4.5|>=3,<3.1.3",
- "silverstripe/cms": "<4.11.3",
+ "silverstripe/cms": "<6.2.1",
"silverstripe/comments": ">=1.3,<3.1.1",
- "silverstripe/forum": "<=0.6.1|>=0.7,<=0.7.3",
- "silverstripe/framework": "<5.3.23",
+ "silverstripe/forum": "<0.6.2|>=0.7,<0.7.4",
+ "silverstripe/framework": "<6.2.2",
"silverstripe/graphql": ">=2,<2.0.5|>=3,<3.8.2|>=4,<4.3.7|>=5,<5.1.3",
"silverstripe/hybridsessions": ">=1,<2.4.1|>=2.5,<2.5.1",
"silverstripe/recipe-cms": ">=4.5,<4.5.3",
@@ -21043,7 +21138,8 @@
"silverstripe/silverstripe-omnipay": "<2.5.2|>=3,<3.0.2|>=3.1,<3.1.4|>=3.2,<3.2.1",
"silverstripe/subsites": ">=2,<2.6.1",
"silverstripe/taxonomy": ">=1.3,<1.3.1|>=2,<2.0.1",
- "silverstripe/userforms": "<3|>=5,<5.4.2",
+ "silverstripe/userforms": "<6.4.9|>=7,<7.0.7|>=7.1,<7.1.1",
+ "silverstripe/versioned": "<3.2.1",
"silverstripe/versioned-admin": ">=1,<1.11.1",
"simogeo/filemanager": "<=2.5",
"simple-updates/phpwhois": "<=1",
@@ -21062,12 +21158,13 @@
"sjbr/sr-freecap": "<2.4.6|>=2.5,<2.5.3",
"sjbr/static-info-tables": "<2.3.1",
"slim/psr7": "<1.4.1|>=1.5,<1.5.1|>=1.6,<1.6.1",
- "slim/slim": "<2.6",
+ "slim/slim": "<2.6|>=4.4,<=4.15.1",
"slub/slub-events": "<3.0.3",
"smarty/smarty": "<4.5.3|>=5,<5.1.1",
- "snipe/snipe-it": "<8.4.1",
+ "snipe/snipe-it": "<=8.6.1",
"socalnick/scn-social-auth": "<1.15.2",
"socialiteproviders/steam": "<1.1",
+ "solidinvoice/solidinvoice": "<=2.3.15",
"solspace/craft-freeform": "<4.1.29|>=5,<=5.14.6",
"soosyze/soosyze": "<=2",
"spatie/browsershot": "<5.0.5",
@@ -21076,6 +21173,7 @@
"spencer14420/sp-php-email-handler": "<1",
"spipu/html2pdf": "<5.2.8",
"spiral/roadrunner": "<2025.1",
+ "spomky-labs/otphp": "<11.4.3",
"spoon/library": "<1.4.1",
"spoonity/tcpdf": "<6.2.22",
"squizlabs/php_codesniffer": ">=1,<2.8.1|>=3,<3.0.1",
@@ -21084,7 +21182,7 @@
"starcitizentools/short-description": ">=4,<4.0.1",
"starcitizentools/tabber-neue": ">=1.9.1,<2.7.2|>=3,<3.1.1",
"starcitizenwiki/embedvideo": "<=4",
- "statamic/cms": "<5.73.22|>=6,<6.18.1",
+ "statamic/cms": "<5.74|>=6,<6.20.3",
"stormpath/sdk": "<9.9.99",
"studio-42/elfinder": "<=2.1.67",
"studiomitte/friendlycaptcha": "<0.1.4",
@@ -21104,6 +21202,7 @@
"sylius/paypal-plugin": "<1.6.2|>=1.7,<1.7.2|>=2,<2.0.2",
"sylius/resource-bundle": ">=1,<1.3.14|>=1.4,<1.4.7|>=1.5,<1.5.2|>=1.6,<1.6.4",
"sylius/sylius": "<1.9.12|>=1.10,<1.10.16|>=1.11,<1.11.17|>=1.12,<=1.12.22|>=1.13,<=1.13.14|>=1.14,<=1.14.17|>=2,<=2.0.15|>=2.1,<=2.1.11|>=2.2,<=2.2.2",
+ "symbiote/silverstripe-advancedworkflow": "<6.4.5|>=7,<7.1.3|>=7.2,<7.2.1",
"symbiote/silverstripe-multivaluefield": ">=3,<3.1",
"symbiote/silverstripe-queuedjobs": ">=3,<3.0.2|>=3.1,<3.1.4|>=4,<4.0.7|>=4.1,<4.1.2|>=4.2,<4.2.4|>=4.3,<4.3.3|>=4.4,<4.4.3|>=4.5,<4.5.1|>=4.6,<4.6.4",
"symbiote/silverstripe-seed": "<6.0.3",
@@ -21149,7 +21248,9 @@
"symfony/twig-bridge": ">=2,<4.4.51|>=5,<5.4.31|>=6,<6.3.8|>=6.4.24,<6.4.40",
"symfony/twilio-notifier": ">=6.4,<6.4.40|>=7,<7.4.12|>=8,<8.0.12",
"symfony/ux-autocomplete": "<2.36|>=3,<3.1",
+ "symfony/ux-icons": ">=2.17,<2.36.1|>=3,<3.2",
"symfony/ux-live-component": "<2.36|>=3,<3.1",
+ "symfony/ux-toolkit": ">=2.32,<2.36.1|>=3,<3.2",
"symfony/ux-twig-component": "<2.25.1",
"symfony/validator": "<5.4.43|>=6,<6.4.11|>=7,<7.1.4",
"symfony/var-exporter": ">=4.2,<4.2.12|>=4.3,<4.3.8",
@@ -21169,7 +21270,7 @@
"thelia/thelia": ">=2.1,<2.1.3",
"theonedemon/phpwhois": "<=4.2.5",
"thinkcmf/thinkcmf": "<6.0.8",
- "thorsten/phpmyfaq": "<4.1.3",
+ "thorsten/phpmyfaq": "<4.1.4",
"tikiwiki/tiki-manager": "<=17.1",
"timber/timber": ">=0.16.6,<1.23.1|>=1.24,<1.24.1|>=2,<2.1",
"tinymce/tinymce": "<7.9.3|>=8,<8.5.1",
@@ -21249,8 +21350,11 @@
"wapplersystems/a21glossary": "<=0.4.10",
"web-auth/webauthn-framework": ">=3.3,<3.3.4|>=4.5,<4.9|>=5.2,<5.2.4|>=5.3,<5.3.1",
"web-auth/webauthn-lib": ">=4.5,<4.9|>=5.2,<5.2.4",
- "web-auth/webauthn-symfony-bundle": ">=5.2,<5.2.4",
+ "web-auth/webauthn-symfony-bundle": "<5.3.4",
"web-feet/coastercms": "==5.5",
+ "web-token/jwt-experimental": "<=4.1.6",
+ "web-token/jwt-framework": "<=4.2.99",
+ "web-token/jwt-library": "<3.4.10|>=4,<4.0.7|>=4.1,<4.1.7",
"web-tp3/wec_map": "<3.0.3",
"webbuilders-group/silverstripe-kapost-bridge": "<0.4",
"webcoast/deferred-image-processing": "<1.0.2",
@@ -21376,7 +21480,7 @@
"type": "tidelift"
}
],
- "time": "2026-06-12T20:48:39+00:00"
+ "time": "2026-06-26T23:29:05+00:00"
},
{
"name": "sebastian/cli-parser",
@@ -22418,16 +22522,16 @@
},
{
"name": "symfony/browser-kit",
- "version": "v7.4.8",
+ "version": "v7.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/browser-kit.git",
- "reference": "41850d8f8ddef9a9cd7314fa9f4902cf48885521"
+ "reference": "bb28e8761a6c33975972948010f00d4a10f0a634"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/browser-kit/zipball/41850d8f8ddef9a9cd7314fa9f4902cf48885521",
- "reference": "41850d8f8ddef9a9cd7314fa9f4902cf48885521",
+ "url": "https://api.github.com/repos/symfony/browser-kit/zipball/bb28e8761a6c33975972948010f00d4a10f0a634",
+ "reference": "bb28e8761a6c33975972948010f00d4a10f0a634",
"shasum": ""
},
"require": {
@@ -22467,7 +22571,7 @@
"description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/browser-kit/tree/v7.4.8"
+ "source": "https://github.com/symfony/browser-kit/tree/v7.4.14"
},
"funding": [
{
@@ -22487,7 +22591,7 @@
"type": "tidelift"
}
],
- "time": "2026-03-24T13:12:05+00:00"
+ "time": "2026-06-08T20:24:16+00:00"
},
{
"name": "symfony/debug-bundle",
@@ -22665,16 +22769,16 @@
},
{
"name": "symfony/phpunit-bridge",
- "version": "v7.4.8",
+ "version": "v7.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/phpunit-bridge.git",
- "reference": "140bbbe1cd1c21a084494ccddeee33f3c3381d7d"
+ "reference": "11eeee9d109963145e66f5b1919e5cf5411da58b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/140bbbe1cd1c21a084494ccddeee33f3c3381d7d",
- "reference": "140bbbe1cd1c21a084494ccddeee33f3c3381d7d",
+ "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/11eeee9d109963145e66f5b1919e5cf5411da58b",
+ "reference": "11eeee9d109963145e66f5b1919e5cf5411da58b",
"shasum": ""
},
"require": {
@@ -22726,7 +22830,7 @@
"testing"
],
"support": {
- "source": "https://github.com/symfony/phpunit-bridge/tree/v7.4.8"
+ "source": "https://github.com/symfony/phpunit-bridge/tree/v7.4.14"
},
"funding": [
{
@@ -22746,20 +22850,20 @@
"type": "tidelift"
}
],
- "time": "2026-03-24T13:12:05+00:00"
+ "time": "2026-06-08T20:24:16+00:00"
},
{
"name": "symfony/web-profiler-bundle",
- "version": "v7.4.13",
+ "version": "v7.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/web-profiler-bundle.git",
- "reference": "153076bb3f0690fff0e95e55cc06358b22f236a5"
+ "reference": "5dead36a9202a6008b54b95308bce7ab97a41fe0"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/153076bb3f0690fff0e95e55cc06358b22f236a5",
- "reference": "153076bb3f0690fff0e95e55cc06358b22f236a5",
+ "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/5dead36a9202a6008b54b95308bce7ab97a41fe0",
+ "reference": "5dead36a9202a6008b54b95308bce7ab97a41fe0",
"shasum": ""
},
"require": {
@@ -22816,7 +22920,7 @@
"dev"
],
"support": {
- "source": "https://github.com/symfony/web-profiler-bundle/tree/v7.4.13"
+ "source": "https://github.com/symfony/web-profiler-bundle/tree/v7.4.14"
},
"funding": [
{
@@ -22836,7 +22940,7 @@
"type": "tidelift"
}
],
- "time": "2026-05-23T16:05:06+00:00"
+ "time": "2026-06-05T06:22:21+00:00"
},
{
"name": "theseer/tokenizer",
diff --git a/config/packages/ai_lm_studio_platform.yaml b/config/packages/ai_lm_studio_platform.yaml
index 0e4287e0..1d832763 100644
--- a/config/packages/ai_lm_studio_platform.yaml
+++ b/config/packages/ai_lm_studio_platform.yaml
@@ -2,3 +2,4 @@ ai:
platform:
lmstudio:
host_url: '%env(string:settings:ai_lmstudio:hostURL)%'
+ http_client: 'app.http_client.ai_lmstudio'
diff --git a/config/packages/ai_ollama_platform.yaml b/config/packages/ai_ollama_platform.yaml
new file mode 100644
index 00000000..67ebe190
--- /dev/null
+++ b/config/packages/ai_ollama_platform.yaml
@@ -0,0 +1,6 @@
+ai:
+ platform:
+ ollama:
+ endpoint: '%env(string:settings:ai_ollama:endpoint)%'
+ api_key: '%env(string:settings:ai_ollama:apiKey)%'
+ http_client: 'app.http_client.ai_ollama'
diff --git a/config/packages/ai_open_router_platform.yaml b/config/packages/ai_open_router_platform.yaml
index d34de592..53eb20b9 100644
--- a/config/packages/ai_open_router_platform.yaml
+++ b/config/packages/ai_open_router_platform.yaml
@@ -2,3 +2,4 @@ ai:
platform:
openrouter:
api_key: '%env(string:settings:ai_openrouter:apiKey)%'
+ http_client: 'app.http_client.ai_openrouter'
diff --git a/config/packages/dev/easy_log_handler.yaml b/config/packages/dev/easy_log_handler.yaml
deleted file mode 100644
index 27bfc608..00000000
--- a/config/packages/dev/easy_log_handler.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-services:
- EasyCorp\EasyLog\EasyLogHandler:
- public: false
- arguments: ['%kernel.logs_dir%/%kernel.environment%.log']
-
-#// FIXME: How to add this configuration automatically without messing up with the monolog configuration?
-#monolog:
-# handlers:
-# buffered:
-# type: buffer
-# handler: easylog
-# channels: ['!event']
-# level: debug
-# easylog:
-# type: service
-# id: EasyCorp\EasyLog\EasyLogHandler
diff --git a/config/packages/doctrine.php b/config/packages/doctrine.php
index e5be011f..f62cdcfb 100644
--- a/config/packages/doctrine.php
+++ b/config/packages/doctrine.php
@@ -20,16 +20,16 @@
declare(strict_types=1);
-use Symfony\Config\DoctrineConfig;
-
/**
- * This class extends the default doctrine ORM configuration to enable native lazy objects on PHP 8.4+.
+ * This file enables native lazy objects on PHP 8.4+.
* We have to do this in a PHP file, because the yaml file does not support conditionals on PHP version.
+ *
+ * TODO: Remove this file when we drop support for PHP < 8.4
*/
-return static function(DoctrineConfig $doctrine) {
- //On PHP 8.4+ we can use native lazy objects, which are much more efficient than proxies.
- if (PHP_VERSION_ID >= 80400) {
- $doctrine->orm()->enableNativeLazyObjects(true);
- }
-};
+// On PHP 8.4+ we can use native lazy objects, which are much more efficient than proxies.
+if (PHP_VERSION_ID >= 80400) {
+ return ['doctrine' => ['orm' => ['enable_native_lazy_objects' => true]]];
+}
+
+return [];
diff --git a/config/parameters.yaml b/config/parameters.yaml
index b1aa5314..e654a9b5 100644
--- a/config/parameters.yaml
+++ b/config/parameters.yaml
@@ -53,6 +53,7 @@ parameters:
# Themes commented here by default, are not really usable, because of display problems. Enable them at your own risk!
partdb.available_themes:
- bootstrap
+ - brite
- cerulean
- cosmo
- cyborg
diff --git a/config/reference.php b/config/reference.php
index 71b08412..461a3578 100644
--- a/config/reference.php
+++ b/config/reference.php
@@ -121,7 +121,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
* }
* @psalm-type ServicesConfig = array{
* _defaults?: DefaultsType,
- * _instanceof?: InstanceofType,
+ * _instanceof?: array,
* ...
* }
* @psalm-type ExtensionType = array
@@ -2874,8 +2874,8 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
* enable_translation?: bool|Param, // Enable translation for the system prompt // Default: false
* translation_domain?: string|Param, // The translation domain for the system prompt // Default: null
* },
- * tools?: bool|array{
- * enabled?: bool|Param, // Default: true
+ * tools?: bool|array{ // Tools are opt-in: set to true to inject all services tagged with "ai.tool", or configure an explicit list of tools. When the option is omitted (or set to null or false), no tools are registered.
+ * enabled?: bool|Param, // Default: false
* services?: list
-# Set Content-Security-Policy for svg files (and compressed variants), to block embedded javascript in there
+ # Set a strict CSP for all static assets not handled by PHP.
+ # PHP responses already carry their own CSP via NelmioSecurityBundle, so setifempty leaves those untouched.
+ Header always setifempty Content-Security-Policy "default-src 'self'; script-src 'none'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; sandbox;"
+ Header always setifempty X-Content-Type-Options "nosniff"
+
+ # SVG files get a slightly different CSP because they can embed resources and must not be framed.
- Header set Content-Security-Policy "default-src 'self'; script-src 'none'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; frame-ancestors 'none';"
+ Header always set Content-Security-Policy "default-src 'self'; script-src 'none'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; frame-ancestors 'none'; sandbox;"
-
\ No newline at end of file
+
diff --git a/public/kicad/footprints.txt b/public/kicad/footprints.txt
index fe86093f..08380de1 100644
--- a/public/kicad/footprints.txt
+++ b/public/kicad/footprints.txt
@@ -1,4 +1,4 @@
-# Generated on Mon Jun 8 06:51:27 UTC 2026
+# Generated on Mon Jun 22 07:31:48 UTC 2026
# This file contains all footprints available in the offical KiCAD library
Audio_Module:Reverb_BTDR-1H
Audio_Module:Reverb_BTDR-1V
@@ -8293,6 +8293,7 @@ Converter_DCDC:Converter_DCDC_Hamamatsu_C11204-1_THT
Converter_DCDC:Converter_DCDC_MeanWell_NID30_THT
Converter_DCDC:Converter_DCDC_MeanWell_NID60_THT
Converter_DCDC:Converter_DCDC_MeanWell_NSD10_THT
+Converter_DCDC:Converter_DCDC_MeanWell_SMU02x-xxN_THT
Converter_DCDC:Converter_DCDC_Murata_CRE1xxxxxx3C_THT
Converter_DCDC:Converter_DCDC_Murata_CRE1xxxxxxDC_THT
Converter_DCDC:Converter_DCDC_Murata_CRE1xxxxxxSC_THT
@@ -13030,8 +13031,6 @@ Package_SON:WSON-6-1EP_2x2mm_P0.65mm_EP1x1.6mm
Package_SON:WSON-6-1EP_2x2mm_P0.65mm_EP1x1.6mm_ThermalVias
Package_SON:WSON-6-1EP_3x3mm_P0.95mm
Package_SON:WSON-6_1.5x1.5mm_P0.5mm
-Package_SON:WSON-8-1EP_2x2mm_P0.5mm_EP0.9x1.6mm
-Package_SON:WSON-8-1EP_2x2mm_P0.5mm_EP0.9x1.6mm_ThermalVias
Package_SON:WSON-8-1EP_3x2.5mm_P0.5mm_EP1.2x1.5mm_PullBack
Package_SON:WSON-8-1EP_3x2.5mm_P0.5mm_EP1.2x1.5mm_PullBack_ThermalVias
Package_SON:WSON-8-1EP_3x3mm_P0.5mm_EP1.2x2mm
diff --git a/public/kicad/symbols.txt b/public/kicad/symbols.txt
index e1eb246a..a24a1785 100644
--- a/public/kicad/symbols.txt
+++ b/public/kicad/symbols.txt
@@ -1,4 +1,4 @@
-# Generated on Mon Jun 8 06:52:04 UTC 2026
+# Generated on Mon Jun 22 07:32:26 UTC 2026
# This file contains all symbols available in the offical KiCAD library
4xxx:14528
4xxx:14529
@@ -4954,6 +4954,7 @@ Converter_DCDC:RPMH15-1.5
Converter_DCDC:RPMH24-1.5
Converter_DCDC:RPMH3.3-1.5
Converter_DCDC:RPMH5.0-1.5
+Converter_DCDC:SMU02L-24N
Converter_DCDC:TBA1-0310
Converter_DCDC:TBA1-0311
Converter_DCDC:TBA1-0510
@@ -5300,21 +5301,39 @@ Converter_DCDC:TMR-4812
Converter_DCDC:TMR-4821
Converter_DCDC:TMR-4822
Converter_DCDC:TMR-4823
+Converter_DCDC:TMR10-1211WI
+Converter_DCDC:TMR10-1212WI
+Converter_DCDC:TMR10-1213WI
+Converter_DCDC:TMR10-1215WI
+Converter_DCDC:TMR10-1222WI
+Converter_DCDC:TMR10-1223WI
Converter_DCDC:TMR10-2410WIR
+Converter_DCDC:TMR10-2411WI
Converter_DCDC:TMR10-2411WIR
+Converter_DCDC:TMR10-2412WI
Converter_DCDC:TMR10-2412WIR
+Converter_DCDC:TMR10-2413WI
Converter_DCDC:TMR10-2413WIR
+Converter_DCDC:TMR10-2415WI
Converter_DCDC:TMR10-2415WIR
Converter_DCDC:TMR10-2421WIR
+Converter_DCDC:TMR10-2422WI
Converter_DCDC:TMR10-2422WIR
+Converter_DCDC:TMR10-2423WI
Converter_DCDC:TMR10-2423WIR
Converter_DCDC:TMR10-4810WIR
+Converter_DCDC:TMR10-4811WI
Converter_DCDC:TMR10-4811WIR
+Converter_DCDC:TMR10-4812WI
Converter_DCDC:TMR10-4812WIR
+Converter_DCDC:TMR10-4813WI
Converter_DCDC:TMR10-4813WIR
+Converter_DCDC:TMR10-4815WI
Converter_DCDC:TMR10-4815WIR
Converter_DCDC:TMR10-4821WIR
+Converter_DCDC:TMR10-4822WI
Converter_DCDC:TMR10-4822WIR
+Converter_DCDC:TMR10-4823WI
Converter_DCDC:TMR10-4823WIR
Converter_DCDC:TMR10-7210WIR
Converter_DCDC:TMR10-7211WIR
@@ -6124,6 +6143,7 @@ Device:SparkGap
Device:Speaker
Device:Speaker_Crystal
Device:Speaker_Ultrasound
+Device:Thermal_Jumper
Device:Thermistor
Device:Thermistor_NTC
Device:Thermistor_NTC_3Wire
@@ -14593,6 +14613,8 @@ MCU_Texas:LM4F111C4QR
MCU_Texas:LM4F111E5QR
MCU_Texas:LM4F111H5QR
MCU_Texas:MSP432E401Y
+MCU_Texas:MSPM0C110xSDDF
+MCU_Texas:MSPM0C110xSDSG
MCU_Texas:TM4C1230C3PM
MCU_Texas:TM4C1230D5PM
MCU_Texas:TM4C1230E6PM
@@ -15671,6 +15693,7 @@ Power_Management:LM5069MM-1
Power_Management:LM5069MM-2
Power_Management:LM66100DCK
Power_Management:LM74700
+Power_Management:LM74701-Q1
Power_Management:LMG3410
Power_Management:LMG5200
Power_Management:LT1641-1
@@ -15772,6 +15795,7 @@ Power_Management:TPS22810DBV
Power_Management:TPS22810DRV
Power_Management:TPS22917DBV
Power_Management:TPS22917LDBV
+Power_Management:TPS22919DCK
Power_Management:TPS22929D
Power_Management:TPS22993
Power_Management:TPS2412D
@@ -16683,6 +16707,19 @@ Reference_Voltage:LM4040LP-4.1
Reference_Voltage:LM4040LP-5
Reference_Voltage:LM4040LP-8.2
Reference_Voltage:LM4041LP-ADJ
+Reference_Voltage:LM4050xEM3-2.1
+Reference_Voltage:LM4050xEM3-2.5
+Reference_Voltage:LM4050xEM3-3.0
+Reference_Voltage:LM4050xEM3-3.3
+Reference_Voltage:LM4050xEM3-4.1
+Reference_Voltage:LM4050xEM3-5.0
+Reference_Voltage:LM4050xEX3-2.1
+Reference_Voltage:LM4050xEX3-2.5
+Reference_Voltage:LM4050xEX3-3.3
+Reference_Voltage:LM4050xEX3-4.1
+Reference_Voltage:LM4050xEX3-5.0
+Reference_Voltage:LM4051xEM3-1.2
+Reference_Voltage:LM4051xEX3-1.2
Reference_Voltage:LM4125AIM5-2.5
Reference_Voltage:LM4125IM5-2.0
Reference_Voltage:LM4125IM5-2.5
@@ -16830,7 +16867,6 @@ Reference_Voltage:MCP1501-25xCH
Reference_Voltage:MCP1501-25xRW
Reference_Voltage:MCP1501-25xSN
Reference_Voltage:MCP1501-30xCH
-Reference_Voltage:MCP1501-30xRW
Reference_Voltage:MCP1501-30xSN
Reference_Voltage:MCP1501-33xCH
Reference_Voltage:MCP1501-33xRW
@@ -19263,6 +19299,7 @@ Regulator_Switching:LT1373HVCN8
Regulator_Switching:LT1373HVCS8
Regulator_Switching:LT1377CN8
Regulator_Switching:LT1377CS8
+Regulator_Switching:LT1931
Regulator_Switching:LT1945
Regulator_Switching:LT3430
Regulator_Switching:LT3430-1
@@ -20991,6 +21028,7 @@ Sensor_Temperature:MCP9501
Sensor_Temperature:MCP9502
Sensor_Temperature:MCP9503
Sensor_Temperature:MCP9504
+Sensor_Temperature:MCP96xx01x-x-MX
Sensor_Temperature:MCP9700Ax-ELT
Sensor_Temperature:MCP9700Ax-ETT
Sensor_Temperature:MCP9700Ax-HLT
@@ -22481,6 +22519,7 @@ Transistor_FET_Other:Q_NMOS_Depletion_GDS
Transistor_FET_Other:Q_NMOS_Depletion_GSD
Transistor_FET_Other:Q_NMOS_Depletion_SDG
Transistor_FET_Other:Q_NMOS_Depletion_SGD
+Transistor_FET_Other:SP010N70T8
Transistor_FET_Other:VNB35N07xx-E
Transistor_FET_Other:VNP10N07
Transistor_FET_Other:VNP35N07xx-E
diff --git a/src/Controller/AttachmentFileController.php b/src/Controller/AttachmentFileController.php
index 01aeab11..7f48e661 100644
--- a/src/Controller/AttachmentFileController.php
+++ b/src/Controller/AttachmentFileController.php
@@ -93,6 +93,8 @@ class AttachmentFileController extends AbstractController
//Set header content disposition, so that the file will be downloaded
$response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $attachment->getFilename());
+ $this->setAttachmentCSPHeaders($response);
+
return $response;
}
@@ -112,6 +114,16 @@ class AttachmentFileController extends AbstractController
//Set header content disposition, so that the file will be downloaded
$response->setContentDisposition(ResponseHeaderBag::DISPOSITION_INLINE, $attachment->getFilename());
+ $this->setAttachmentCSPHeaders($response);
+
+ return $response;
+ }
+
+ private function setAttachmentCSPHeaders(Response $response): Response
+ {
+ //Set an CSP that disallow to run any scripts, styles or images from the attachment render page, as it is not used anywhere else for now and can be a security risk if used without proper precautions, so it should be opt-in
+ $response->headers->set('Content-Security-Policy', "default-src 'self'; script-src 'none'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; sandbox;");
+
return $response;
}
diff --git a/src/Doctrine/Middleware/SQLiteRegexExtensionMiddlewareDriver.php b/src/Doctrine/Middleware/SQLiteRegexExtensionMiddlewareDriver.php
index aa6108c9..f721f986 100644
--- a/src/Doctrine/Middleware/SQLiteRegexExtensionMiddlewareDriver.php
+++ b/src/Doctrine/Middleware/SQLiteRegexExtensionMiddlewareDriver.php
@@ -27,6 +27,7 @@ use App\Doctrine\Functions\SiValueSort;
use App\Exceptions\InvalidRegexException;
use Doctrine\DBAL\Driver\Connection;
use Doctrine\DBAL\Driver\Middleware\AbstractDriverMiddleware;
+use Pdo\Sqlite;
/**
* This middleware is used to add the regexp operator to the SQLite platform.
@@ -44,17 +45,30 @@ class SQLiteRegexExtensionMiddlewareDriver extends AbstractDriverMiddleware
if ($params['driver'] === 'pdo_sqlite') {
$native_connection = $connection->getNativeConnection();
- //Ensure that the function really exists on the connection, as it is marked as experimental according to PHP documentation
if($native_connection instanceof \PDO) {
- $native_connection->sqliteCreateFunction('REGEXP', self::regexp(...), 2, \PDO::SQLITE_DETERMINISTIC);
- $native_connection->sqliteCreateFunction('FIELD', self::field(...), -1, \PDO::SQLITE_DETERMINISTIC);
- $native_connection->sqliteCreateFunction('FIELD2', self::field2(...), 2, \PDO::SQLITE_DETERMINISTIC);
- //Create a new collation for natural sorting
- $native_connection->sqliteCreateCollation('NATURAL_CMP', strnatcmp(...));
+ //Use the new PDO::createFunction and PDO::createCollation methods if available (PHP 8.4+)
+ if (is_a($native_connection, Sqlite::class)) { #TODO: Remove this check when PHP 8.4 is the minimum requirement
+ $native_connection->createFunction('REGEXP', self::regexp(...), 2, Sqlite::DETERMINISTIC);
+ $native_connection->createFunction('FIELD', self::field(...), -1, Sqlite::DETERMINISTIC);
+ $native_connection->createFunction('FIELD2', self::field2(...), 2, Sqlite::DETERMINISTIC);
- //Create a function for SI prefix value sorting
- $native_connection->sqliteCreateFunction('SI_VALUE', SiValueSort::sqliteSiValue(...), 1, \PDO::SQLITE_DETERMINISTIC);
+ //Create a new collation for natural sorting
+ $native_connection->createCollation('NATURAL_CMP', strnatcmp(...));
+
+ //Create a function for SI prefix value sorting
+ $native_connection->createFunction('SI_VALUE', SiValueSort::sqliteSiValue(...), 1, Sqlite::DETERMINISTIC);
+ } else {
+ $native_connection->sqliteCreateFunction('REGEXP', self::regexp(...), 2, \PDO::SQLITE_DETERMINISTIC);
+ $native_connection->sqliteCreateFunction('FIELD', self::field(...), -1, \PDO::SQLITE_DETERMINISTIC);
+ $native_connection->sqliteCreateFunction('FIELD2', self::field2(...), 2, \PDO::SQLITE_DETERMINISTIC);
+
+ //Create a new collation for natural sorting
+ $native_connection->sqliteCreateCollation('NATURAL_CMP', strnatcmp(...));
+
+ //Create a function for SI prefix value sorting
+ $native_connection->sqliteCreateFunction('SI_VALUE', SiValueSort::sqliteSiValue(...), 1, \PDO::SQLITE_DETERMINISTIC);
+ }
}
}
@@ -118,4 +132,4 @@ class SQLiteRegexExtensionMiddlewareDriver extends AbstractDriverMiddleware
return $index + 1;
}
-}
\ No newline at end of file
+}
diff --git a/src/Entity/Attachments/AttachmentType.php b/src/Entity/Attachments/AttachmentType.php
index 7a314ffe..03bb8031 100644
--- a/src/Entity/Attachments/AttachmentType.php
+++ b/src/Entity/Attachments/AttachmentType.php
@@ -65,21 +65,16 @@ use Symfony\Component\Validator\Constraints as Assert;
new Post(securityPostDenormalize: 'is_granted("create", object)'),
new Patch(security: 'is_granted("edit", object)'),
new Delete(security: 'is_granted("delete", object)'),
+ new GetCollection(
+ uriTemplate: '/attachment_types/{id}/children.{_format}',
+ uriVariables: ['id' => new Link(fromProperty: 'children', fromClass: AttachmentType::class)],
+ openapi: new Operation(summary: 'Retrieves the children elements of an attachment type.'),
+ security: 'is_granted("@attachment_types.read")'
+ ),
],
normalizationContext: ['groups' => ['attachment_type:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'],
denormalizationContext: ['groups' => ['attachment_type:write', 'api:basic:write', 'attachment:write', 'parameter:write'], 'openapi_definition_name' => 'Write'],
)]
-#[ApiResource(
- uriTemplate: '/attachment_types/{id}/children.{_format}',
- operations: [
- new GetCollection(openapi: new Operation(summary: 'Retrieves the children elements of an attachment type.'),
- security: 'is_granted("@attachment_types.read")')
- ],
- uriVariables: [
- 'id' => new Link(fromProperty: 'children', fromClass: AttachmentType::class)
- ],
- normalizationContext: ['groups' => ['attachment_type:read', 'api:basic:read'], 'openapi_definition_name' => 'Read']
-)]
#[ApiFilter(PropertyFilter::class)]
#[ApiFilter(LikeFilter::class, properties: ["name", "comment"])]
#[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)]
diff --git a/src/Entity/Parts/Category.php b/src/Entity/Parts/Category.php
index 22f8a3e4..80fcc43c 100644
--- a/src/Entity/Parts/Category.php
+++ b/src/Entity/Parts/Category.php
@@ -68,23 +68,16 @@ use Symfony\Component\Validator\Constraints as Assert;
new Post(securityPostDenormalize: 'is_granted("create", object)'),
new Patch(security: 'is_granted("edit", object)'),
new Delete(security: 'is_granted("delete", object)'),
+ new GetCollection(
+ uriTemplate: '/categories/{id}/children.{_format}',
+ uriVariables: ['id' => new Link(fromProperty: 'children', fromClass: Category::class)],
+ openapi: new Operation(summary: 'Retrieves the children elements of a category.'),
+ security: 'is_granted("@categories.read")'
+ ),
],
normalizationContext: ['groups' => ['category:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'],
denormalizationContext: ['groups' => ['category:write', 'api:basic:write', 'attachment:write', 'parameter:write'], 'openapi_definition_name' => 'Write'],
)]
-#[ApiResource(
- uriTemplate: '/categories/{id}/children.{_format}',
- operations: [
- new GetCollection(
- openapi: new Operation(summary: 'Retrieves the children elements of a category.'),
- security: 'is_granted("@categories.read")'
- )
- ],
- uriVariables: [
- 'id' => new Link(fromProperty: 'children', fromClass: Category::class)
- ],
- normalizationContext: ['groups' => ['category:read', 'api:basic:read'], 'openapi_definition_name' => 'Read']
-)]
#[ApiFilter(PropertyFilter::class)]
#[ApiFilter(LikeFilter::class, properties: ["name", "comment"])]
#[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)]
diff --git a/src/Entity/Parts/Footprint.php b/src/Entity/Parts/Footprint.php
index 3d8be686..2027a310 100644
--- a/src/Entity/Parts/Footprint.php
+++ b/src/Entity/Parts/Footprint.php
@@ -67,23 +67,16 @@ use Symfony\Component\Validator\Constraints as Assert;
new Post(securityPostDenormalize: 'is_granted("create", object)'),
new Patch(security: 'is_granted("edit", object)'),
new Delete(security: 'is_granted("delete", object)'),
+ new GetCollection(
+ uriTemplate: '/footprints/{id}/children.{_format}',
+ uriVariables: ['id' => new Link(fromProperty: 'children', fromClass: Footprint::class)],
+ openapi: new Operation(summary: 'Retrieves the children elements of a footprint.'),
+ security: 'is_granted("@footprints.read")'
+ ),
],
normalizationContext: ['groups' => ['footprint:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'],
denormalizationContext: ['groups' => ['footprint:write', 'api:basic:write', 'attachment:write', 'parameter:write'], 'openapi_definition_name' => 'Write'],
)]
-#[ApiResource(
- uriTemplate: '/footprints/{id}/children.{_format}',
- operations: [
- new GetCollection(
- openapi: new Operation(summary: 'Retrieves the children elements of a footprint.'),
- security: 'is_granted("@footprints.read")'
- )
- ],
- uriVariables: [
- 'id' => new Link(fromProperty: 'children', fromClass: Footprint::class)
- ],
- normalizationContext: ['groups' => ['footprint:read', 'api:basic:read'], 'openapi_definition_name' => 'Read']
-)]
#[ApiFilter(PropertyFilter::class)]
#[ApiFilter(LikeFilter::class, properties: ["name", "comment"])]
#[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)]
diff --git a/src/Entity/Parts/InfoProviderReference.php b/src/Entity/Parts/InfoProviderReference.php
index 810aef0c..8f6874b9 100644
--- a/src/Entity/Parts/InfoProviderReference.php
+++ b/src/Entity/Parts/InfoProviderReference.php
@@ -28,9 +28,11 @@ use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\Embeddable;
use Symfony\Component\Serializer\Annotation\Groups;
+use Symfony\Component\Validator\Constraints as Assert;
+use Symfony\Component\Validator\Context\ExecutionContextInterface;
/**
- * This class represents a reference to a info provider inside a part.
+ * This class represents a reference to an info provider inside a part.
* @see \App\Tests\Entity\Parts\InfoProviderReferenceTest
*/
#[Embeddable]
@@ -157,4 +159,44 @@ class InfoProviderReference
$ref->last_updated = new \DateTimeImmutable();
return $ref;
}
+
+ /**
+ * Creates a reference to an info provider based on the given parameters.
+ * @param string|null $provider_key
+ * @param string|null $provider_id
+ * @param string|null $provider_url
+ * @param \DateTimeImmutable|null $last_updated
+ * @return self
+ */
+ public static function create(?string $provider_key, ?string $provider_id, ?string $provider_url, ?\DateTimeImmutable $last_updated): self
+ {
+ $ref = new InfoProviderReference();
+ $ref->provider_key = $provider_key;
+ $ref->provider_id = $provider_id;
+ $ref->provider_url = $provider_url;
+ $ref->last_updated = $last_updated;
+ return $ref;
+ }
+
+ #[Assert\Callback()]
+ public function validate(ExecutionContextInterface $context, mixed $payload): void
+ {
+ if ($this->provider_key === null && $this->provider_id !== null) {
+ $context->buildViolation('info_providers.validation.provider_id_without_key')
+ ->atPath('provider_key')
+ ->addViolation();
+ }
+
+ if ($this->provider_key === null && $this->provider_url !== null) {
+ $context->buildViolation('info_providers.validation.provider_url_without_key')
+ ->atPath('provider_url')
+ ->addViolation();
+ }
+
+ if ($this->provider_key !== null && $this->provider_id === null) {
+ $context->buildViolation('info_providers.validation.provider_key_without_id')
+ ->atPath('provider_id')
+ ->addViolation();
+ }
+ }
}
diff --git a/src/Entity/Parts/Manufacturer.php b/src/Entity/Parts/Manufacturer.php
index 0edf8232..76526c31 100644
--- a/src/Entity/Parts/Manufacturer.php
+++ b/src/Entity/Parts/Manufacturer.php
@@ -66,23 +66,16 @@ use Symfony\Component\Validator\Constraints as Assert;
new Post(securityPostDenormalize: 'is_granted("create", object)'),
new Patch(security: 'is_granted("edit", object)'),
new Delete(security: 'is_granted("delete", object)'),
+ new GetCollection(
+ uriTemplate: '/manufacturers/{id}/children.{_format}',
+ uriVariables: ['id' => new Link(fromProperty: 'children', fromClass: Manufacturer::class)],
+ openapi: new Operation(summary: 'Retrieves the children elements of a manufacturer.'),
+ security: 'is_granted("@manufacturers.read")'
+ ),
],
normalizationContext: ['groups' => ['manufacturer:read', 'company:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'],
denormalizationContext: ['groups' => ['manufacturer:write', 'company:write', 'api:basic:write', 'attachment:write', 'parameter:write'], 'openapi_definition_name' => 'Write'],
)]
-#[ApiResource(
- uriTemplate: '/manufacturers/{id}/children.{_format}',
- operations: [
- new GetCollection(
- openapi: new Operation(summary: 'Retrieves the children elements of a manufacturer.'),
- security: 'is_granted("@manufacturers.read")'
- )
- ],
- uriVariables: [
- 'id' => new Link(fromProperty: 'children', fromClass: Manufacturer::class)
- ],
- normalizationContext: ['groups' => ['manufacturer:read', 'company:read', 'api:basic:read'], 'openapi_definition_name' => 'Read']
-)]
#[ApiFilter(PropertyFilter::class)]
#[ApiFilter(LikeFilter::class, properties: ["name", "comment"])]
#[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)]
diff --git a/src/Entity/Parts/MeasurementUnit.php b/src/Entity/Parts/MeasurementUnit.php
index 6dd0b9f2..e775b65b 100644
--- a/src/Entity/Parts/MeasurementUnit.php
+++ b/src/Entity/Parts/MeasurementUnit.php
@@ -71,23 +71,16 @@ use Symfony\Component\Validator\Constraints\Length;
new Post(securityPostDenormalize: 'is_granted("create", object)'),
new Patch(security: 'is_granted("edit", object)'),
new Delete(security: 'is_granted("delete", object)'),
+ new GetCollection(
+ uriTemplate: '/measurement_units/{id}/children.{_format}',
+ uriVariables: ['id' => new Link(fromProperty: 'children', fromClass: MeasurementUnit::class)],
+ openapi: new Operation(summary: 'Retrieves the children elements of a MeasurementUnit.'),
+ security: 'is_granted("@measurement_units.read")'
+ ),
],
normalizationContext: ['groups' => ['measurement_unit:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'],
denormalizationContext: ['groups' => ['measurement_unit:write', 'api:basic:write', 'attachment:write', 'parameter:write'], 'openapi_definition_name' => 'Write'],
)]
-#[ApiResource(
- uriTemplate: '/measurement_units/{id}/children.{_format}',
- operations: [
- new GetCollection(
- openapi: new Operation(summary: 'Retrieves the children elements of a MeasurementUnit.'),
- security: 'is_granted("@measurement_units.read")'
- )
- ],
- uriVariables: [
- 'id' => new Link(fromProperty: 'children', fromClass: MeasurementUnit::class)
- ],
- normalizationContext: ['groups' => ['measurement_unit:read', 'api:basic:read'], 'openapi_definition_name' => 'Read']
-)]
#[ApiFilter(PropertyFilter::class)]
#[ApiFilter(LikeFilter::class, properties: ["name", "comment", "unit"])]
#[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)]
diff --git a/src/Entity/Parts/PartTraits/AdvancedPropertyTrait.php b/src/Entity/Parts/PartTraits/AdvancedPropertyTrait.php
index 065469b5..9fa41f93 100644
--- a/src/Entity/Parts/PartTraits/AdvancedPropertyTrait.php
+++ b/src/Entity/Parts/PartTraits/AdvancedPropertyTrait.php
@@ -75,6 +75,7 @@ trait AdvancedPropertyTrait
*/
#[ORM\Embedded(class: InfoProviderReference::class, columnPrefix: 'provider_reference_')]
#[Groups(['full', 'part:read'])]
+ #[Assert\Valid()]
protected InfoProviderReference $providerReference;
/**
diff --git a/src/Entity/Parts/StorageLocation.php b/src/Entity/Parts/StorageLocation.php
index 7ba400d9..9571da6e 100644
--- a/src/Entity/Parts/StorageLocation.php
+++ b/src/Entity/Parts/StorageLocation.php
@@ -67,23 +67,16 @@ use Symfony\Component\Validator\Constraints as Assert;
new Post(securityPostDenormalize: 'is_granted("create", object)'),
new Patch(security: 'is_granted("edit", object)'),
new Delete(security: 'is_granted("delete", object)'),
+ new GetCollection(
+ uriTemplate: '/storage_locations/{id}/children.{_format}',
+ uriVariables: ['id' => new Link(fromProperty: 'children', fromClass: StorageLocation::class)],
+ openapi: new Operation(summary: 'Retrieves the children elements of a storage location.'),
+ security: 'is_granted("@storelocations.read")'
+ ),
],
normalizationContext: ['groups' => ['location:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'],
denormalizationContext: ['groups' => ['location:write', 'api:basic:write', 'attachment:write', 'parameter:write'], 'openapi_definition_name' => 'Write'],
)]
-#[ApiResource(
- uriTemplate: '/storage_locations/{id}/children.{_format}',
- operations: [
- new GetCollection(
- openapi: new Operation(summary: 'Retrieves the children elements of a storage location.'),
- security: 'is_granted("@storelocations.read")'
- )
- ],
- uriVariables: [
- 'id' => new Link(fromProperty: 'children', fromClass: StorageLocation::class)
- ],
- normalizationContext: ['groups' => ['location:read', 'api:basic:read'], 'openapi_definition_name' => 'Read']
-)]
#[ApiFilter(PropertyFilter::class)]
#[ApiFilter(LikeFilter::class, properties: ["name", "comment"])]
#[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)]
diff --git a/src/Entity/Parts/Supplier.php b/src/Entity/Parts/Supplier.php
index 2c004e9e..75cf62d1 100644
--- a/src/Entity/Parts/Supplier.php
+++ b/src/Entity/Parts/Supplier.php
@@ -71,21 +71,16 @@ use Symfony\Component\Validator\Constraints as Assert;
new Post(securityPostDenormalize: 'is_granted("create", object)'),
new Patch(security: 'is_granted("edit", object)'),
new Delete(security: 'is_granted("delete", object)'),
+ new GetCollection(
+ uriTemplate: '/suppliers/{id}/children.{_format}',
+ uriVariables: ['id' => new Link(fromProperty: 'children', fromClass: Supplier::class)],
+ openapi: new Operation(summary: 'Retrieves the children elements of a supplier.'),
+ security: 'is_granted("@manufacturers.read")'
+ ),
],
normalizationContext: ['groups' => ['supplier:read', 'company:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'],
denormalizationContext: ['groups' => ['supplier:write', 'company:write', 'api:basic:write', 'attachment:write', 'parameter:write'], 'openapi_definition_name' => 'Write'],
)]
-#[ApiResource(
- uriTemplate: '/suppliers/{id}/children.{_format}',
- operations: [new GetCollection(
- openapi: new Operation(summary: 'Retrieves the children elements of a supplier.'),
- security: 'is_granted("@manufacturers.read")'
- )],
- uriVariables: [
- 'id' => new Link(fromProperty: 'children', fromClass: Supplier::class)
- ],
- normalizationContext: ['groups' => ['supplier:read', 'company:read', 'api:basic:read'], 'openapi_definition_name' => 'Read']
-)]
#[ApiFilter(PropertyFilter::class)]
#[ApiFilter(LikeFilter::class, properties: ["name", "comment"])]
#[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)]
diff --git a/src/Entity/PriceInformations/Currency.php b/src/Entity/PriceInformations/Currency.php
index 4a811aa0..507ec810 100644
--- a/src/Entity/PriceInformations/Currency.php
+++ b/src/Entity/PriceInformations/Currency.php
@@ -71,23 +71,16 @@ use Symfony\Component\Validator\Constraints as Assert;
new Post(securityPostDenormalize: 'is_granted("create", object)'),
new Patch(security: 'is_granted("edit", object)'),
new Delete(security: 'is_granted("delete", object)'),
+ new GetCollection(
+ uriTemplate: '/currencies/{id}/children.{_format}',
+ uriVariables: ['id' => new Link(fromProperty: 'children', fromClass: Currency::class)],
+ openapi: new Operation(summary: 'Retrieves the children elements of a currency.'),
+ security: 'is_granted("@currencies.read")'
+ ),
],
normalizationContext: ['groups' => ['currency:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'],
denormalizationContext: ['groups' => ['currency:write', 'api:basic:write', 'attachment:write', 'parameter:write'], 'openapi_definition_name' => 'Write'],
)]
-#[ApiResource(
- uriTemplate: '/currencies/{id}/children.{_format}',
- operations: [
- new GetCollection(
- openapi: new Operation(summary: 'Retrieves the children elements of a currency.'),
- security: 'is_granted("@currencies.read")'
- )
- ],
- uriVariables: [
- 'id' => new Link(fromProperty: 'children', fromClass: Currency::class)
- ],
- normalizationContext: ['groups' => ['currency:read', 'api:basic:read'], 'openapi_definition_name' => 'Read']
-)]
#[ApiFilter(PropertyFilter::class)]
#[ApiFilter(LikeFilter::class, properties: ["name", "comment", "iso_code"])]
#[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)]
diff --git a/src/Entity/PriceInformations/Orderdetail.php b/src/Entity/PriceInformations/Orderdetail.php
index 56428e3a..8b5c9f7b 100644
--- a/src/Entity/PriceInformations/Orderdetail.php
+++ b/src/Entity/PriceInformations/Orderdetail.php
@@ -71,23 +71,17 @@ use Symfony\Component\Validator\Constraints\Length;
new Post(securityPostDenormalize: 'is_granted("create", object)'),
new Patch(security: 'is_granted("edit", object)'),
new Delete(security: 'is_granted("delete", object)'),
+ new GetCollection(
+ uriTemplate: '/parts/{id}/orderdetails.{_format}',
+ uriVariables: ['id' => new Link(toProperty: 'part', fromClass: Part::class)],
+ normalizationContext: ['groups' => ['orderdetail:read', 'pricedetail:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'],
+ openapi: new Operation(summary: 'Retrieves the orderdetails of a part.'),
+ security: 'is_granted("@parts.read")'
+ ),
],
normalizationContext: ['groups' => ['orderdetail:read', 'orderdetail:read:standalone', 'api:basic:read', 'pricedetail:read'], 'openapi_definition_name' => 'Read'],
denormalizationContext: ['groups' => ['orderdetail:write', 'api:basic:write'], 'openapi_definition_name' => 'Write'],
)]
-#[ApiResource(
- uriTemplate: '/parts/{id}/orderdetails.{_format}',
- operations: [
- new GetCollection(
- openapi: new Operation(summary: 'Retrieves the orderdetails of a part.'),
- security: 'is_granted("@parts.read")'
- )
- ],
- uriVariables: [
- 'id' => new Link(toProperty: 'part', fromClass: Part::class)
- ],
- normalizationContext: ['groups' => ['orderdetail:read', 'pricedetail:read', 'api:basic:read'], 'openapi_definition_name' => 'Read']
-)]
#[ApiFilter(PropertyFilter::class)]
#[ApiFilter(PropertyFilter::class)]
#[ApiFilter(LikeFilter::class, properties: ["supplierpartnr", "supplier_product_url"])]
diff --git a/src/Entity/ProjectSystem/Project.php b/src/Entity/ProjectSystem/Project.php
index a103d694..34e77051 100644
--- a/src/Entity/ProjectSystem/Project.php
+++ b/src/Entity/ProjectSystem/Project.php
@@ -66,23 +66,16 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
new Post(securityPostDenormalize: 'is_granted("create", object)'),
new Patch(security: 'is_granted("edit", object)'),
new Delete(security: 'is_granted("delete", object)'),
+ new GetCollection(
+ uriTemplate: '/projects/{id}/children.{_format}',
+ uriVariables: ['id' => new Link(fromProperty: 'children', fromClass: Project::class)],
+ openapi: new Operation(summary: 'Retrieves the children elements of a project.'),
+ security: 'is_granted("@projects.read")'
+ ),
],
normalizationContext: ['groups' => ['project:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'],
denormalizationContext: ['groups' => ['project:write', 'api:basic:write', 'attachment:write', 'parameter:write'], 'openapi_definition_name' => 'Write'],
)]
-#[ApiResource(
- uriTemplate: '/projects/{id}/children.{_format}',
- operations: [
- new GetCollection(
- openapi: new Operation(summary: 'Retrieves the children elements of a project.'),
- security: 'is_granted("@projects.read")'
- )
- ],
- uriVariables: [
- 'id' => new Link(fromProperty: 'children', fromClass: Project::class)
- ],
- normalizationContext: ['groups' => ['project:read', 'api:basic:read'], 'openapi_definition_name' => 'Read']
-)]
#[ApiFilter(PropertyFilter::class)]
#[ApiFilter(LikeFilter::class, properties: ["name", "comment"])]
#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])]
@@ -117,7 +110,7 @@ class Project extends AbstractStructuralDBElement
/**
* @var string|null The current status of the project
*/
- #[Assert\Choice(['draft', 'planning', 'in_production', 'finished', 'archived'])]
+ #[Assert\Choice(choices: ['draft', 'planning', 'in_production', 'finished', 'archived'])]
#[Groups(['extended', 'full', 'project:read', 'project:write', 'import'])]
#[ORM\Column(type: Types::STRING, length: 64, nullable: true)]
protected ?string $status = null;
diff --git a/src/Entity/ProjectSystem/ProjectBOMEntry.php b/src/Entity/ProjectSystem/ProjectBOMEntry.php
index 2a7862ec..c016d741 100644
--- a/src/Entity/ProjectSystem/ProjectBOMEntry.php
+++ b/src/Entity/ProjectSystem/ProjectBOMEntry.php
@@ -63,23 +63,16 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
new Post(uriTemplate: '/project_bom_entries.{_format}', securityPostDenormalize: 'is_granted("create", object)',),
new Patch(uriTemplate: '/project_bom_entries/{id}.{_format}', security: 'is_granted("edit", object)',),
new Delete(uriTemplate: '/project_bom_entries/{id}.{_format}', security: 'is_granted("delete", object)',),
+ new GetCollection(
+ uriTemplate: '/projects/{id}/bom.{_format}',
+ uriVariables: ['id' => new Link(fromProperty: 'bom_entries', fromClass: Project::class)],
+ openapi: new Operation(summary: 'Retrieves the BOM entries of the given project.'),
+ security: 'is_granted("@projects.read")'
+ ),
],
normalizationContext: ['groups' => ['bom_entry:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'],
denormalizationContext: ['groups' => ['bom_entry:write', 'api:basic:write'], 'openapi_definition_name' => 'Write'],
)]
-#[ApiResource(
- uriTemplate: '/projects/{id}/bom.{_format}',
- operations: [
- new GetCollection(
- openapi: new Operation(summary: 'Retrieves the BOM entries of the given project.'),
- security: 'is_granted("@projects.read")'
- )
- ],
- uriVariables: [
- 'id' => new Link(fromProperty: 'bom_entries', fromClass: Project::class)
- ],
- normalizationContext: ['groups' => ['bom_entry:read', 'api:basic:read'], 'openapi_definition_name' => 'Read']
-)]
#[ApiFilter(PropertyFilter::class)]
#[ApiFilter(LikeFilter::class, properties: ["name", "comment", 'mountnames'])]
#[ApiFilter(RangeFilter::class, properties: ['quantity'])]
diff --git a/src/Form/Extension/DateTimeModelTimezoneExtension.php b/src/Form/Extension/DateTimeModelTimezoneExtension.php
new file mode 100644
index 00000000..3c4818ea
--- /dev/null
+++ b/src/Form/Extension/DateTimeModelTimezoneExtension.php
@@ -0,0 +1,79 @@
+.
+ */
+
+declare(strict_types=1);
+
+namespace App\Form\Extension;
+
+use Symfony\Component\Form\AbstractTypeExtension;
+use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
+use Symfony\Component\Form\Extension\Core\Type\DateType;
+use Symfony\Component\Form\FormBuilderInterface;
+use Symfony\Component\Form\FormEvent;
+use Symfony\Component\Form\FormEvents;
+
+/**
+ * Catches timezone mismatches between a DateTimeInterface model value and the effective
+ * model_timezone configured on the field.
+ *
+ * Doctrine's UTCDateTimeImmutableType always returns UTC DateTimeImmutable objects, so any
+ * date/datetime field that omits `model_timezone: 'UTC'` will silently corrupt stored values
+ * (the transformer treats the UTC instant as if it were in the user's local timezone).
+ * This extension throws a \LogicException early so the mistake is caught at development time.
+ */
+class DateTimeModelTimezoneExtension extends AbstractTypeExtension
+{
+ public static function getExtendedTypes(): iterable
+ {
+ return [DateTimeType::class, DateType::class];
+ }
+
+ public function buildForm(FormBuilderInterface $builder, array $options): void
+ {
+ $builder->addEventListener(FormEvents::POST_SET_DATA, static function (FormEvent $event) use ($options): void {
+ $data = $event->getData();
+
+ if (!$data instanceof \DateTimeInterface) {
+ return;
+ }
+
+ // Resolve the effective model timezone: explicit option or the PHP default set at build time.
+ // This mirrors what BaseDateTimeTransformer does in its constructor.
+ $modelTimezone = $options['model_timezone'] ?? date_default_timezone_get();
+
+ $dataOffset = $data->getTimezone()->getOffset($data);
+ $modelOffset = (new \DateTimeZone($modelTimezone))->getOffset($data);
+
+ if ($dataOffset !== $modelOffset) {
+ throw new \LogicException(sprintf(
+ 'Form field "%s" received a %s with timezone "%s" (UTC offset %+d s), '
+ . 'but the effective model_timezone is "%s" (UTC offset %+d s). '
+ . 'Set the "model_timezone" option to match the timezone of your data source.',
+ $event->getForm()->getName(),
+ get_debug_type($data),
+ $data->getTimezone()->getName(),
+ $dataOffset,
+ $modelTimezone,
+ $modelOffset
+ ));
+ }
+ });
+ }
+}
diff --git a/src/Form/InfoProviderSystem/InfoProviderReferenceType.php b/src/Form/InfoProviderSystem/InfoProviderReferenceType.php
new file mode 100644
index 00000000..73fc8fe3
--- /dev/null
+++ b/src/Form/InfoProviderSystem/InfoProviderReferenceType.php
@@ -0,0 +1,113 @@
+.
+ */
+
+declare(strict_types=1);
+
+
+namespace App\Form\InfoProviderSystem;
+
+use App\Entity\Parts\InfoProviderReference;
+use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\DataMapperInterface;
+use Symfony\Component\Form\Extension\Core\Type\TextType;
+use Symfony\Component\Form\Extension\Core\Type\UrlType;
+use Symfony\Component\Form\FormBuilderInterface;
+use Symfony\Component\Form\FormInterface;
+use Symfony\Component\OptionsResolver\OptionsResolver;
+
+class InfoProviderReferenceType extends AbstractType implements DataMapperInterface
+{
+ public function buildForm(FormBuilderInterface $builder, array $options): void
+ {
+ $builder
+ ->setDataMapper($this)
+ ->add('provider_key', ProviderSelectType::class, [
+ 'label' => 'info_providers.provider_key',
+ 'input' => 'string',
+ 'multiple' => false,
+ 'required' => false,
+ 'only_active' => false,
+ ])
+ ->add('provider_id', TextType::class, [
+ 'label' => 'info_providers.provider_id',
+ 'required' => false,
+ ])
+ ->add('provider_url', UrlType::class, [
+ 'label' => 'info_providers.provider_url',
+ 'required' => false,
+ ])
+ ;
+ }
+
+ public function configureOptions(OptionsResolver $resolver): void
+ {
+ $resolver->setDefaults([
+ 'data_class' => InfoProviderReference::class,
+ ]);
+ }
+
+
+ public function mapDataToForms(mixed $viewData, \Traversable $forms): void
+ {
+ if ($viewData === null) {
+ return;
+ }
+
+ if (!$viewData instanceof InfoProviderReference) {
+ return;
+ }
+
+ /** @var FormInterface[] $forms */
+ $forms = iterator_to_array($forms);
+
+ $forms['provider_key']->setData($viewData->getProviderKey());
+ $forms['provider_id']->setData($viewData->getProviderId());
+ $forms['provider_url']->setData($viewData->getProviderUrl());
+ }
+
+ public function mapFormsToData(\Traversable $forms, mixed &$viewData): void
+ {
+ /** @var FormInterface[] $forms */
+ $forms = iterator_to_array($forms);
+
+ $providerKey = $forms['provider_key']->getData();
+ $providerId = $forms['provider_id']->getData();
+ $providerUrl = $forms['provider_url']->getData();
+
+ if ($viewData === null) {
+ $viewData = InfoProviderReference::noProvider();
+ }
+
+ if (!$viewData instanceof InfoProviderReference) {
+ return;
+ }
+
+ $oldDate = $viewData->getLastUpdated();
+
+ //If all fields are empty, we set the view data to a new instance without provider information
+ if ($providerKey === null && $providerId === null && $providerUrl === null) {
+ $viewData = InfoProviderReference::noProvider();
+ return;
+ }
+
+ $viewData = InfoProviderReference::create($providerKey, $providerId, $providerUrl, $oldDate);
+
+ }
+}
diff --git a/src/Form/InfoProviderSystem/ProviderSelectType.php b/src/Form/InfoProviderSystem/ProviderSelectType.php
index bad3edaa..e59dc85f 100644
--- a/src/Form/InfoProviderSystem/ProviderSelectType.php
+++ b/src/Form/InfoProviderSystem/ProviderSelectType.php
@@ -31,12 +31,12 @@ use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Translation\StaticMessage;
+use Symfony\Component\Translation\TranslatableMessage;
class ProviderSelectType extends AbstractType
{
public function __construct(private readonly ProviderRegistry $providerRegistry)
{
-
}
public function getParent(): string
@@ -46,17 +46,22 @@ class ProviderSelectType extends AbstractType
public function configureOptions(OptionsResolver $resolver): void
{
- $providers = $this->providerRegistry->getActiveProviders();
-
$resolver->setDefault('input', 'object');
$resolver->setAllowedTypes('input', 'string');
//Either the form returns the provider objects or their keys
$resolver->setAllowedValues('input', ['object', 'string']);
$resolver->setDefault('multiple', true);
- $resolver->setDefault('choices', function (Options $options) use ($providers) {
+ //Only show active providers in the list, or also inactive ones
+ $resolver->setDefault('only_active', true);
+ $resolver->setAllowedTypes('only_active', 'bool');
+
+
+ $resolver->setDefault('choices', function (Options $options) {
+ $providers = $options['only_active'] ? $this->providerRegistry->getActiveProviders() : $this->providerRegistry->getProviders();
+
if ('object' === $options['input']) {
- return $this->providerRegistry->getActiveProviders();
+ return $providers;
}
$tmp = [];
@@ -69,20 +74,35 @@ class ProviderSelectType extends AbstractType
});
//The choice_label and choice_value only needs to be set if we want the objects
- $resolver->setDefault('choice_label', function (Options $options){
+ $resolver->setDefault('choice_label', function (Options $options) {
if ('object' === $options['input']) {
- return ChoiceList::label($this, static fn (?InfoProviderInterface $choice) => new StaticMessage($choice?->getProviderInfo()['name']));
+ return ChoiceList::label($this, static fn(?InfoProviderInterface $choice
+ ) => new StaticMessage($choice?->getProviderInfo()['name']));
}
- return static fn ($choice, $key, $value) => new StaticMessage($key);
+ return static fn($choice, $key, $value) => new StaticMessage($key);
});
$resolver->setDefault('choice_value', function (Options $options) {
if ('object' === $options['input']) {
- return ChoiceList::value($this, static fn(?InfoProviderInterface $choice) => $choice?->getProviderKey());
+ return ChoiceList::value($this,
+ static fn(?InfoProviderInterface $choice) => $choice?->getProviderKey());
}
return null;
});
+ $resolver->setDefault('group_by', function (Options $options) {
+ //Do not show groups when only active providers are shown, because then all providers are active and the group would be useless
+ if ($options['only_active']) {
+ return null;
+ }
+
+ return function ($choice, $key, string $value) {
+ if ($this->providerRegistry->getProviderByKey($value)->isActive()) {
+ return new TranslatableMessage('info_providers.providers_list.active');
+ }
+ return new TranslatableMessage('info_providers.providers_list.disabled');
+ };
+ });
}
}
diff --git a/src/Form/Part/PartBaseType.php b/src/Form/Part/PartBaseType.php
index a31f2469..afef8fdb 100644
--- a/src/Form/Part/PartBaseType.php
+++ b/src/Form/Part/PartBaseType.php
@@ -33,6 +33,7 @@ use App\Entity\Parts\Part;
use App\Entity\Parts\PartCustomState;
use App\Entity\PriceInformations\Orderdetail;
use App\Form\AttachmentFormType;
+use App\Form\InfoProviderSystem\InfoProviderReferenceType;
use App\Form\ParameterType;
use App\Form\Part\EDA\EDAPartInfoType;
use App\Form\Type\MasterPictureAttachmentType;
@@ -225,6 +226,10 @@ class PartBaseType extends AbstractType
'empty_data' => null,
'label' => 'part.gtin',
])
+ ->add('providerReference', InfoProviderReferenceType::class, [
+ 'label' => false,
+ 'required' => false,
+ ])
;
//Comment section
diff --git a/src/Form/Part/PartLotType.php b/src/Form/Part/PartLotType.php
index fc330bb1..ef49c57e 100644
--- a/src/Form/Part/PartLotType.php
+++ b/src/Form/Part/PartLotType.php
@@ -115,8 +115,10 @@ class PartLotType extends AbstractType
$builder->add('last_stocktake_at', DateTimeType::class, [
'label' => 'part_lot.edit.last_stocktake_at',
'widget' => 'single_text',
+ 'model_timezone' => 'UTC', // The database stores the datetime in UTC, so we need to set the model timezone to UTC
'disabled' => !$this->security->isGranted('@parts_stock.stocktake'),
'required' => false,
+ 'with_seconds' => true,
]);
}
diff --git a/src/Form/Type/AttachmentTypeType.php b/src/Form/Type/AttachmentTypeType.php
index 099ed282..95b5a254 100644
--- a/src/Form/Type/AttachmentTypeType.php
+++ b/src/Form/Type/AttachmentTypeType.php
@@ -38,7 +38,7 @@ class AttachmentTypeType extends AbstractType
return StructuralEntityType::class;
}
- public function configureOptions(OptionsResolver $resolver)
+ public function configureOptions(OptionsResolver $resolver): void
{
$resolver->define('attachment_filter_class')->allowedTypes('null', 'string')->default(null);
diff --git a/src/Helpers/Projects/ProjectBuildRequest.php b/src/Helpers/Projects/ProjectBuildRequest.php
index 430d37b5..d2dfe139 100644
--- a/src/Helpers/Projects/ProjectBuildRequest.php
+++ b/src/Helpers/Projects/ProjectBuildRequest.php
@@ -84,8 +84,10 @@ final class ProjectBuildRequest
$remaining_amount = $this->getNeededAmountForBOMEntry($bom_entry);
foreach($this->getPartLotsForBOMEntry($bom_entry) as $lot) {
//If the lot has instock use it for the build
- $this->withdraw_amounts[$lot->getID()] = min($remaining_amount, $lot->getAmount());
- $remaining_amount -= max(0, $this->withdraw_amounts[$lot->getID()]);
+ $id = $lot->getID() ?? throw new \RuntimeException("Part lot needs to have an ID!");
+
+ $this->withdraw_amounts[$id] = min($remaining_amount, $lot->getAmount());
+ $remaining_amount -= max(0, $this->withdraw_amounts[$id]);
}
}
}
@@ -176,6 +178,10 @@ final class ProjectBuildRequest
{
$lot_id = $lot instanceof PartLot ? $lot->getID() : $lot;
+ if ($lot_id === null) {
+ throw new \InvalidArgumentException('The given lot must have an ID!');
+ }
+
if (! array_key_exists($lot_id, $this->withdraw_amounts)) {
throw new \InvalidArgumentException('The given lot is not in the withdraw amounts array!');
}
@@ -192,10 +198,12 @@ final class ProjectBuildRequest
{
if ($lot instanceof PartLot) {
$lot_id = $lot->getID();
- } elseif (is_int($lot)) {
- $lot_id = $lot;
} else {
- throw new \InvalidArgumentException('The given lot must be an instance of PartLot or an ID of a PartLot!');
+ $lot_id = $lot;
+ }
+
+ if ($lot_id === null) {
+ throw new \InvalidArgumentException('The given lot must have an ID!');
}
$this->withdraw_amounts[$lot_id] = $amount;
@@ -296,7 +304,7 @@ final class ProjectBuildRequest
* @param bool $dont_check_quantity
* @return $this
*/
- public function setDontCheckQuantity(bool $dont_check_quantity): ProjectBuildRequest
+ public function setDontCheckQuantity(bool $dont_check_quantity): self
{
$this->dont_check_quantity = $dont_check_quantity;
return $this;
diff --git a/src/Repository/DBElementRepository.php b/src/Repository/DBElementRepository.php
index f737d91d..6e13aff7 100644
--- a/src/Repository/DBElementRepository.php
+++ b/src/Repository/DBElementRepository.php
@@ -158,7 +158,6 @@ class DBElementRepository extends EntityRepository
{
$reflection = new ReflectionClass($element::class);
$property = $reflection->getProperty($field);
- $property->setAccessible(true);
$property->setValue($element, $new_value);
}
}
diff --git a/src/Services/AI/AIPlatforms.php b/src/Services/AI/AIPlatforms.php
index 2f4d6317..318819a2 100644
--- a/src/Services/AI/AIPlatforms.php
+++ b/src/Services/AI/AIPlatforms.php
@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace App\Services\AI;
use App\Settings\AISettings\LMStudioSettings;
+use App\Settings\AISettings\OllamaSettings;
use App\Settings\AISettings\OpenRouterSettings;
use Symfony\Contracts\Translation\TranslatableInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
@@ -32,6 +33,7 @@ enum AIPlatforms: string implements TranslatableInterface
{
case OPENROUTER = 'openrouter';
case LMSTUDIO = 'lmstudio';
+ case OLLAMA = 'ollama';
/**
* Returns the name attribute of the service tag for this platform, which is used to register the platform in the AIPlatformRegistry
@@ -52,6 +54,7 @@ enum AIPlatforms: string implements TranslatableInterface
return match ($this) {
self::LMSTUDIO => LMStudioSettings::class,
self::OPENROUTER => OpenRouterSettings::class,
+ self::OLLAMA => OllamaSettings::class,
};
}
diff --git a/src/Services/Attachments/AttachmentSubmitHandler.php b/src/Services/Attachments/AttachmentSubmitHandler.php
index 2e40f1f5..1b90091f 100644
--- a/src/Services/Attachments/AttachmentSubmitHandler.php
+++ b/src/Services/Attachments/AttachmentSubmitHandler.php
@@ -543,8 +543,10 @@ class AttachmentSubmitHandler
return $attachment;
}
+ $guessed_mime_type = $this->mimeTypes->guessMimeType($path);
+
//Check if the file is an SVG
- if ($attachment->getExtension() === "svg") {
+ if ($guessed_mime_type === "image/svg+xml" || $attachment->getExtension() === "svg") {
$this->SVGSanitizer->sanitizeFile($path);
}
diff --git a/src/Services/ImportExportSystem/PartKeeprImporter/PKImportHelperTrait.php b/src/Services/ImportExportSystem/PartKeeprImporter/PKImportHelperTrait.php
index 08b1c301..cbfb4ee0 100644
--- a/src/Services/ImportExportSystem/PartKeeprImporter/PKImportHelperTrait.php
+++ b/src/Services/ImportExportSystem/PartKeeprImporter/PKImportHelperTrait.php
@@ -233,7 +233,6 @@ trait PKImportHelperTrait
$reflectionClass = new \ReflectionClass($entity);
$property = $reflectionClass->getProperty('addedDate');
- $property->setAccessible(true);
$property->setValue($entity, $date);
}
diff --git a/src/Services/InfoProviderSystem/DTOJsonSchemaConverter.php b/src/Services/InfoProviderSystem/DTOJsonSchemaConverter.php
index a61e7465..3c2cbf64 100644
--- a/src/Services/InfoProviderSystem/DTOJsonSchemaConverter.php
+++ b/src/Services/InfoProviderSystem/DTOJsonSchemaConverter.php
@@ -42,7 +42,7 @@ final class DTOJsonSchemaConverter
public function getJSONSchema(): array
{
return [
- 'name' => 'clock',
+ 'name' => 'part_detail',
'strict' => true,
'schema' => [
'type' => 'object',
diff --git a/src/Services/InfoProviderSystem/Providers/AIWebProvider.php b/src/Services/InfoProviderSystem/Providers/AIWebProvider.php
index 6539e69b..be91041e 100644
--- a/src/Services/InfoProviderSystem/Providers/AIWebProvider.php
+++ b/src/Services/InfoProviderSystem/Providers/AIWebProvider.php
@@ -282,6 +282,10 @@ final class AIWebProvider implements InfoProviderInterface
try {
$aiPlatform = $this->AIPlatformRegistry->getPlatform($this->settings->platform ?? throw new \RuntimeException('No AI platform selected') );
+ // AI inference can take much longer than PHP's default max_execution_time (typically 30s).
+ // The HTTP client timeout already enforces the configured limit; disable PHP's constraint here.
+ set_time_limit(0);
+
//'openai/gpt-5-mini'
$result = $aiPlatform->invoke($this->settings->model ?? throw new \RuntimeException('No model selected'), $input, [
'response_format' => [
diff --git a/src/Settings/AISettings/AISettings.php b/src/Settings/AISettings/AISettings.php
index 732eb597..659577b6 100644
--- a/src/Settings/AISettings/AISettings.php
+++ b/src/Settings/AISettings/AISettings.php
@@ -35,9 +35,14 @@ class AISettings
{
use SettingsTrait;
+ public const TIMEOUT_LIMIT = 600;
+
#[EmbeddedSettings]
public ?OpenRouterSettings $openRouter = null;
#[EmbeddedSettings]
public ?LMStudioSettings $lmstudio = null;
+
+ #[EmbeddedSettings]
+ public ?OllamaSettings $ollama = null;
}
diff --git a/src/Settings/AISettings/LMStudioSettings.php b/src/Settings/AISettings/LMStudioSettings.php
index 2bdad06e..d92ce97e 100644
--- a/src/Settings/AISettings/LMStudioSettings.php
+++ b/src/Settings/AISettings/LMStudioSettings.php
@@ -23,16 +23,17 @@ declare(strict_types=1);
namespace App\Settings\AISettings;
-use App\Form\Type\APIKeyType;
use App\Services\AI\AIPlatformSettingsInterface;
use App\Settings\SettingsIcon;
use Jbtronics\SettingsBundle\Metadata\EnvVarMode;
use Jbtronics\SettingsBundle\Settings\Settings;
use Jbtronics\SettingsBundle\Settings\SettingsParameter;
use Jbtronics\SettingsBundle\Settings\SettingsTrait;
+use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\Extension\Core\Type\UrlType;
use Symfony\Component\Translation\StaticMessage;
use Symfony\Component\Translation\TranslatableMessage as TM;
+use Symfony\Component\Validator\Constraints as Assert;
#[Settings(name: 'ai_lmstudio', label: new TM("settings.ai.lmstudio"))]
#[SettingsIcon("fa-robot")]
@@ -46,6 +47,14 @@ class LMStudioSettings implements AIPlatformSettingsInterface
envVar: "AI_LMSTUDIO_HOSTURL", envVarMode: EnvVarMode::OVERWRITE)]
public ?string $hostURL = null;
+ #[SettingsParameter(label: new TM("settings.ai.timeout"),
+ description: new TM("settings.ai.timeout.help"),
+ formType: NumberType::class,
+ formOptions: ["scale" => 0, "attr" => ["min" => 1]],
+ )]
+ #[Assert\Range(min: 1, max: AISettings::TIMEOUT_LIMIT)]
+ public int $timeout = 180;
+
public function isAIPlatformEnabled(): bool
{
return $this->hostURL !== null && $this->hostURL !== "";
diff --git a/src/Settings/AISettings/OllamaSettings.php b/src/Settings/AISettings/OllamaSettings.php
new file mode 100644
index 00000000..7ca0e5a0
--- /dev/null
+++ b/src/Settings/AISettings/OllamaSettings.php
@@ -0,0 +1,68 @@
+.
+ */
+
+declare(strict_types=1);
+
+
+namespace App\Settings\AISettings;
+
+use App\Form\Type\APIKeyType;
+use App\Services\AI\AIPlatformSettingsInterface;
+use App\Settings\SettingsIcon;
+use Jbtronics\SettingsBundle\Metadata\EnvVarMode;
+use Jbtronics\SettingsBundle\Settings\Settings;
+use Jbtronics\SettingsBundle\Settings\SettingsParameter;
+use Jbtronics\SettingsBundle\Settings\SettingsTrait;
+use Symfony\Component\Form\Extension\Core\Type\NumberType;
+use Symfony\Component\Form\Extension\Core\Type\UrlType;
+use Symfony\Component\Translation\StaticMessage;
+use Symfony\Component\Translation\TranslatableMessage as TM;
+use Symfony\Component\Validator\Constraints as Assert;
+
+#[Settings(name: 'ai_ollama', label: new TM("settings.ai.ollama"))]
+#[SettingsIcon("fa-robot")]
+class OllamaSettings implements AIPlatformSettingsInterface
+{
+ use SettingsTrait;
+
+ #[SettingsParameter(label: new TM("settings.ai.ollama.endpoint"),
+ formType: UrlType::class,
+ formOptions: ["attr" => ["placeholder" => new StaticMessage("http://localhost:11434")]],
+ envVar: "AI_OLLAMA_ENDPOINT", envVarMode: EnvVarMode::OVERWRITE)]
+ public ?string $endpoint = null;
+
+ #[SettingsParameter(label: new TM("settings.ai.ollama.apiKey"),
+ formType: APIKeyType::class,
+ envVar: "AI_OLLAMA_API_KEY", envVarMode: EnvVarMode::OVERWRITE)]
+ public ?string $apiKey = null;
+
+ #[SettingsParameter(label: new TM("settings.ai.timeout"),
+ description: new TM("settings.ai.timeout.help"),
+ formType: NumberType::class,
+ formOptions: ["scale" => 0, "attr" => ["min" => 1]]
+ )]
+ #[Assert\Range(min: 1, max: AISettings::TIMEOUT_LIMIT)]
+ public int $timeout = 180;
+
+ public function isAIPlatformEnabled(): bool
+ {
+ return $this->endpoint !== null && $this->endpoint !== "";
+ }
+}
diff --git a/src/Settings/AISettings/OpenRouterSettings.php b/src/Settings/AISettings/OpenRouterSettings.php
index e083513a..16665554 100644
--- a/src/Settings/AISettings/OpenRouterSettings.php
+++ b/src/Settings/AISettings/OpenRouterSettings.php
@@ -30,7 +30,9 @@ use Jbtronics\SettingsBundle\Metadata\EnvVarMode;
use Jbtronics\SettingsBundle\Settings\Settings;
use Jbtronics\SettingsBundle\Settings\SettingsParameter;
use Jbtronics\SettingsBundle\Settings\SettingsTrait;
+use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Translation\TranslatableMessage as TM;
+use Symfony\Component\Validator\Constraints as Assert;
#[Settings(name: 'ai_openrouter', label: new TM("settings.ai.openrouter"), description: "settings.ai.openrouter.help")]
#[SettingsIcon("fa-robot")]
@@ -43,6 +45,14 @@ class OpenRouterSettings implements AIPlatformSettingsInterface
formOptions: ["help_html" => true], envVar: "AI_OPENROUTER_KEY", envVarMode: EnvVarMode::OVERWRITE)]
public ?string $apiKey = null;
+ #[SettingsParameter(label: new TM("settings.ai.timeout"),
+ description: new TM("settings.ai.timeout.help"),
+ formType: NumberType::class,
+ formOptions: ["scale" => 0, "attr" => ["min" => 1]],
+ envVar: "int:AI_OPENROUTER_TIMEOUT", envVarMode: EnvVarMode::OVERWRITE)]
+ #[Assert\Range(min: 1, max: AISettings::TIMEOUT_LIMIT)]
+ public int $timeout = 90;
+
public function isAIPlatformEnabled(): bool
{
return $this->apiKey !== null && $this->apiKey !== "";
diff --git a/symfony.lock b/symfony.lock
index f8f88675..94af6e6a 100644
--- a/symfony.lock
+++ b/symfony.lock
@@ -411,6 +411,18 @@
"config/packages/ai_lm_studio_platform.yaml"
]
},
+ "symfony/ai-ollama-platform": {
+ "version": "0.10",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "main",
+ "version": "0.1",
+ "ref": "2f0ac0a8bc59c4e46b47a962a3ad7fe8104457d6"
+ },
+ "files": [
+ "config/packages/ai_ollama_platform.yaml"
+ ]
+ },
"symfony/ai-open-router-platform": {
"version": "0.8",
"recipe": {
diff --git a/templates/log_system/details/_extra_collection_element_deleted.html.twig b/templates/log_system/details/_extra_collection_element_deleted.html.twig
index 221fae95..1c2ce2f3 100644
--- a/templates/log_system/details/_extra_collection_element_deleted.html.twig
+++ b/templates/log_system/details/_extra_collection_element_deleted.html.twig
@@ -4,7 +4,7 @@
{% trans %}log.collection_deleted.deleted{% endtrans %} :
- {{ entity_type_label(entry.deletedElementClass) }} #{{ entry.deletedElementID }}
+ {{ type_label(entry.deletedElementClass) }} #{{ entry.deletedElementID }}
{% if entry.oldName is not empty %}
({{ entry.oldName }})
{% endif %}
@@ -12,4 +12,4 @@
{% trans %}log.collection_deleted.on_collection{% endtrans %} :
{{ log_helper.translate_field(entry.collectionName) }}
-
\ No newline at end of file
+
diff --git a/templates/parts/edit/_advanced.html.twig b/templates/parts/edit/_advanced.html.twig
index 30479d11..f18dba58 100644
--- a/templates/parts/edit/_advanced.html.twig
+++ b/templates/parts/edit/_advanced.html.twig
@@ -15,3 +15,24 @@
{{ form_row(form.partUnit) }}
{{ form_row(form.partCustomState) }}
{{ form_row(form.gtin) }}
+
+
diff --git a/templates/parts/info/_extended_infos.html.twig b/templates/parts/info/_extended_infos.html.twig
index 9cb4e4e5..acc197e3 100644
--- a/templates/parts/info/_extended_infos.html.twig
+++ b/templates/parts/info/_extended_infos.html.twig
@@ -76,7 +76,7 @@
{% endif %}
{{ info_provider_label(part.providerReference.providerKey)|default(part.providerReference.providerKey) }} : {{ part.providerReference.providerId }}
- ({{ part.providerReference.lastUpdated | format_datetime() }})
+ ({{ part.providerReference.lastUpdated ? (part.providerReference.lastUpdated | format_datetime()) : ("part.info_provider_reference.updated_never"|trans) }})
{% if part.providerReference.providerUrl %}
{% endif %}
diff --git a/templates/parts/info/_picture.html.twig b/templates/parts/info/_picture.html.twig
index e6aa74b3..db6c59ca 100644
--- a/templates/parts/info/_picture.html.twig
+++ b/templates/parts/info/_picture.html.twig
@@ -19,7 +19,7 @@
{{ pic.name }}
{% if pic.filename %}({{ pic.filename }}) {% endif %}
-
{{ entity_type_label(pic.element) }}
+
{{ type_label(pic.element) }}
{% endif %}
@@ -41,4 +41,4 @@
{% else %}
-{% endif %}
\ No newline at end of file
+{% endif %}
diff --git a/templates/parts/info/show_part_info.html.twig b/templates/parts/info/show_part_info.html.twig
index 96b5e209..b36ab047 100644
--- a/templates/parts/info/show_part_info.html.twig
+++ b/templates/parts/info/show_part_info.html.twig
@@ -22,7 +22,7 @@
({{ timeTravel | format_datetime('short') }})
{% endif %}
{% if part.projectBuildPart %}
- ({{ entity_type_label(part.builtProject) }} : {{ part.builtProject.name }} )
+ ({{ type_label(part.builtProject) }} : {{ part.builtProject.name }} )
{% endif %}
diff --git a/tests/Doctrine/Functions/SiValueSortTest.php b/tests/Doctrine/Functions/SiValueSortTest.php
index dbdd9d28..4ef8a397 100644
--- a/tests/Doctrine/Functions/SiValueSortTest.php
+++ b/tests/Doctrine/Functions/SiValueSortTest.php
@@ -26,6 +26,7 @@ use App\Doctrine\Functions\SiValueSort;
use Doctrine\DBAL\Platforms\MySQLPlatform;
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
use Doctrine\DBAL\Platforms\SQLitePlatform;
+use PHPUnit\Framework\Attributes\DataProvider;
final class SiValueSortTest extends AbstractDoctrineFunctionTestCase
{
@@ -71,9 +72,7 @@ final class SiValueSortTest extends AbstractDoctrineFunctionTestCase
$this->assertSame('SI_VALUE(part_name)', $sql);
}
- /**
- * @dataProvider sqliteSiValueProvider
- */
+ #[DataProvider('sqliteSiValueProvider')]
public function testSqliteSiValue(?string $input, ?float $expected): void
{
$result = SiValueSort::sqliteSiValue($input);
diff --git a/tests/Entity/Attachments/AttachmentTest.php b/tests/Entity/Attachments/AttachmentTest.php
index 9e912b97..bef2df16 100644
--- a/tests/Entity/Attachments/AttachmentTest.php
+++ b/tests/Entity/Attachments/AttachmentTest.php
@@ -276,7 +276,6 @@ final class AttachmentTest extends TestCase
{
$reflection = new ReflectionClass($object);
$reflection_property = $reflection->getProperty($property);
- $reflection_property->setAccessible(true);
$reflection_property->setValue($object, $value);
}
diff --git a/tests/Helpers/Projects/ProjectBuildRequestTest.php b/tests/Helpers/Projects/ProjectBuildRequestTest.php
index c1fd1498..3014f762 100644
--- a/tests/Helpers/Projects/ProjectBuildRequestTest.php
+++ b/tests/Helpers/Projects/ProjectBuildRequestTest.php
@@ -82,7 +82,12 @@ final class ProjectBuildRequestTest extends TestCase
$part2->setName('Part 2');
$part2->setPartUnit($float_unit);
- $this->lot2 = new PartLot();
+ $this->lot2 = new class extends PartLot {
+ public function getID(): ?int
+ {
+ return 3;
+ }
+ };;
$part2->addPartLot($this->lot2);
$this->lot2->setAmount(2.5);
$this->lot2->setDescription('Lot 2');
diff --git a/tests/Services/InfoProviderSystem/Providers/BuerklinProviderTest.php b/tests/Services/InfoProviderSystem/Providers/BuerklinProviderTest.php
index ef446c9a..d466f048 100644
--- a/tests/Services/InfoProviderSystem/Providers/BuerklinProviderTest.php
+++ b/tests/Services/InfoProviderSystem/Providers/BuerklinProviderTest.php
@@ -77,7 +77,6 @@ final class BuerklinProviderTest extends TestCase
public function testAttributesToParametersParsesUnitsAndValues(): void
{
$method = new \ReflectionMethod(BuerklinProvider::class, 'attributesToParameters');
- $method->setAccessible(true);
$features = [
[
@@ -127,7 +126,6 @@ final class BuerklinProviderTest extends TestCase
public function testComplianceParameters(): void
{
$method = new \ReflectionMethod(BuerklinProvider::class, 'complianceToParameters');
- $method->setAccessible(true);
$product = [
'labelRoHS' => 'Yes',
@@ -158,7 +156,6 @@ final class BuerklinProviderTest extends TestCase
public function testImageSelectionPrefersZoomAndDeduplicates(): void
{
$method = new \ReflectionMethod(BuerklinProvider::class, 'getProductImages');
- $method->setAccessible(true);
$images = [
['format' => 'product', 'url' => '/img/a.webp'],
@@ -176,7 +173,6 @@ final class BuerklinProviderTest extends TestCase
public function testFootprintExtraction(): void
{
$method = new \ReflectionMethod(BuerklinProvider::class, 'getPartDetail');
- $method->setAccessible(true);
$product = [
'code' => 'TEST1',
@@ -212,7 +208,6 @@ final class BuerklinProviderTest extends TestCase
];
$method = new \ReflectionMethod(BuerklinProvider::class, 'pricesToVendorInfo');
- $method->setAccessible(true);
$vendorInfo = $method->invoke($this->provider, 'SKU1', 'https://x', $detailPrice);
@@ -260,7 +255,6 @@ final class BuerklinProviderTest extends TestCase
);
$method = new \ReflectionMethod(BuerklinProvider::class, 'convertPartDetailToSearchResult');
- $method->setAccessible(true);
$dto = $method->invoke($this->provider, $detail);
@@ -273,13 +267,13 @@ final class BuerklinProviderTest extends TestCase
{
$this->assertSame(['buerklin.com'], $this->provider->getHandledDomains());
}
-
+
#[DataProvider('buerklinIdFromUrlProvider')]
public function testGetIDFromURLExtractsId(string $url, ?string $expected): void
{
$this->assertSame($expected, $this->provider->getIDFromURL($url));
}
-
+
public static function buerklinIdFromUrlProvider(): \Iterator
{
yield 'de long path' => [
diff --git a/tests/Services/InfoProviderSystem/Providers/LCSCProviderTest.php b/tests/Services/InfoProviderSystem/Providers/LCSCProviderTest.php
index dc19de6b..9ec3abe1 100644
--- a/tests/Services/InfoProviderSystem/Providers/LCSCProviderTest.php
+++ b/tests/Services/InfoProviderSystem/Providers/LCSCProviderTest.php
@@ -367,7 +367,6 @@ final class LCSCProviderTest extends TestCase
{
$reflection = new \ReflectionClass($this->provider);
$method = $reflection->getMethod('sanitizeField');
- $method->setAccessible(true);
$this->assertNull($method->invokeArgs($this->provider, [null]));
$this->assertEquals('Clean text', $method->invokeArgs($this->provider, ['Clean text']));
@@ -378,7 +377,6 @@ final class LCSCProviderTest extends TestCase
{
$reflection = new \ReflectionClass($this->provider);
$method = $reflection->getMethod('getUsedCurrency');
- $method->setAccessible(true);
$this->assertEquals('USD', $method->invokeArgs($this->provider, ['US$']));
$this->assertEquals('USD', $method->invokeArgs($this->provider, ['$']));
@@ -391,7 +389,6 @@ final class LCSCProviderTest extends TestCase
{
$reflection = new \ReflectionClass($this->provider);
$method = $reflection->getMethod('getProductShortURL');
- $method->setAccessible(true);
$result = $method->invokeArgs($this->provider, ['C123456']);
$this->assertEquals('https://www.lcsc.com/product-detail/C123456.html', $result);
@@ -401,7 +398,6 @@ final class LCSCProviderTest extends TestCase
{
$reflection = new \ReflectionClass($this->provider);
$method = $reflection->getMethod('getProductDatasheets');
- $method->setAccessible(true);
$result = $method->invokeArgs($this->provider, [null]);
$this->assertIsArray($result);
@@ -417,7 +413,6 @@ final class LCSCProviderTest extends TestCase
{
$reflection = new \ReflectionClass($this->provider);
$method = $reflection->getMethod('getProductImages');
- $method->setAccessible(true);
$result = $method->invokeArgs($this->provider, [null]);
$this->assertIsArray($result);
@@ -434,7 +429,6 @@ final class LCSCProviderTest extends TestCase
{
$reflection = new \ReflectionClass($this->provider);
$method = $reflection->getMethod('attributesToParameters');
- $method->setAccessible(true);
$attributes = [
['paramNameEn' => 'Resistance', 'paramValueEn' => '1kΩ'],
@@ -454,7 +448,6 @@ final class LCSCProviderTest extends TestCase
{
$reflection = new \ReflectionClass($this->provider);
$method = $reflection->getMethod('pricesToVendorInfo');
- $method->setAccessible(true);
$prices = [
['ladder' => 1, 'productPrice' => '0.10', 'currencySymbol' => 'US$'],
diff --git a/translations/frontend.cs.xlf b/translations/frontend.cs.xlf
index 4ba1f913..6417033b 100644
--- a/translations/frontend.cs.xlf
+++ b/translations/frontend.cs.xlf
@@ -55,5 +55,23 @@
Jdi!
+
+
+ dialog.btn.ok
+ OK
+
+
+
+
+ dialog.btn.cancel
+ Zrušit
+
+
+
+
+ dialog.btn.deny
+ Ne
+
+
diff --git a/translations/frontend.da.xlf b/translations/frontend.da.xlf
index 4b6a15b9..817c703f 100644
--- a/translations/frontend.da.xlf
+++ b/translations/frontend.da.xlf
@@ -55,5 +55,23 @@
Kom nu!
+
+
+ dialog.btn.ok
+ OK
+
+
+
+
+ dialog.btn.cancel
+ Annuller
+
+
+
+
+ dialog.btn.deny
+ Nej
+
+
diff --git a/translations/frontend.de.xlf b/translations/frontend.de.xlf
index 9ebd0d32..317b7c7f 100644
--- a/translations/frontend.de.xlf
+++ b/translations/frontend.de.xlf
@@ -55,5 +55,29 @@
Los!
+
+
+ user.password_strength.crack_time
+ Geschätzte Zeit bis zum Knacken: %time%
+
+
+
+
+ dialog.btn.ok
+ OK
+
+
+
+
+ dialog.btn.cancel
+ Abbrechen
+
+
+
+
+ dialog.btn.deny
+ Nein
+
+
diff --git a/translations/frontend.el.xlf b/translations/frontend.el.xlf
index bab41358..fff724d2 100644
--- a/translations/frontend.el.xlf
+++ b/translations/frontend.el.xlf
@@ -7,5 +7,23 @@
Αναζήτηση
+
+
+ dialog.btn.ok
+ OK
+
+
+
+
+ dialog.btn.cancel
+ Ακύρωση
+
+
+
+
+ dialog.btn.deny
+ Όχι
+
+
\ No newline at end of file
diff --git a/translations/frontend.en.xlf b/translations/frontend.en.xlf
index 91617f79..d0099493 100644
--- a/translations/frontend.en.xlf
+++ b/translations/frontend.en.xlf
@@ -55,5 +55,29 @@
Go!
+
+
+ user.password_strength.crack_time
+ Estimated time to crack: %time%
+
+
+
+
+ dialog.btn.ok
+ OK
+
+
+
+
+ dialog.btn.cancel
+ Cancel
+
+
+
+
+ dialog.btn.deny
+ No
+
+
diff --git a/translations/frontend.es.xlf b/translations/frontend.es.xlf
index 7d339959..02c52715 100644
--- a/translations/frontend.es.xlf
+++ b/translations/frontend.es.xlf
@@ -55,5 +55,23 @@
¡Vamos!
+
+
+ dialog.btn.ok
+ OK
+
+
+
+
+ dialog.btn.cancel
+ Cancelar
+
+
+
+
+ dialog.btn.deny
+ No
+
+
diff --git a/translations/frontend.fr.xlf b/translations/frontend.fr.xlf
index 5ebfca51..2a459052 100644
--- a/translations/frontend.fr.xlf
+++ b/translations/frontend.fr.xlf
@@ -55,5 +55,23 @@
Rechercher !
+
+
+ dialog.btn.ok
+ OK
+
+
+
+
+ dialog.btn.cancel
+ Annuler
+
+
+
+
+ dialog.btn.deny
+ Non
+
+
diff --git a/translations/frontend.hu.xlf b/translations/frontend.hu.xlf
index c303dedc..7724ce66 100644
--- a/translations/frontend.hu.xlf
+++ b/translations/frontend.hu.xlf
@@ -55,5 +55,23 @@
Indítás!
+
+
+ dialog.btn.ok
+ OK
+
+
+
+
+ dialog.btn.cancel
+ Mégse
+
+
+
+
+ dialog.btn.deny
+ Nem
+
+
diff --git a/translations/frontend.it.xlf b/translations/frontend.it.xlf
index f163e3e2..c13ab47a 100644
--- a/translations/frontend.it.xlf
+++ b/translations/frontend.it.xlf
@@ -55,5 +55,23 @@
Cerca!
+
+
+ dialog.btn.ok
+ OK
+
+
+
+
+ dialog.btn.cancel
+ Annulla
+
+
+
+
+ dialog.btn.deny
+ No
+
+
diff --git a/translations/frontend.ja.xlf b/translations/frontend.ja.xlf
index 90ffdf5f..d8612e5b 100644
--- a/translations/frontend.ja.xlf
+++ b/translations/frontend.ja.xlf
@@ -19,5 +19,23 @@
検索
+
+
+ dialog.btn.ok
+ OK
+
+
+
+
+ dialog.btn.cancel
+ キャンセル
+
+
+
+
+ dialog.btn.deny
+ いいえ
+
+
\ No newline at end of file
diff --git a/translations/frontend.nl.xlf b/translations/frontend.nl.xlf
index d14f5a81..4c90e933 100644
--- a/translations/frontend.nl.xlf
+++ b/translations/frontend.nl.xlf
@@ -55,5 +55,23 @@
Ga!
+
+
+ dialog.btn.ok
+ OK
+
+
+
+
+ dialog.btn.cancel
+ Annuleren
+
+
+
+
+ dialog.btn.deny
+ Nee
+
+
diff --git a/translations/frontend.pl.xlf b/translations/frontend.pl.xlf
index fface684..e748286c 100644
--- a/translations/frontend.pl.xlf
+++ b/translations/frontend.pl.xlf
@@ -55,5 +55,23 @@
Idź!
+
+
+ dialog.btn.ok
+ OK
+
+
+
+
+ dialog.btn.cancel
+ Anuluj
+
+
+
+
+ dialog.btn.deny
+ Nie
+
+
diff --git a/translations/frontend.pt_BR.xlf b/translations/frontend.pt_BR.xlf
index fb2f2335..9053c8c3 100644
--- a/translations/frontend.pt_BR.xlf
+++ b/translations/frontend.pt_BR.xlf
@@ -55,5 +55,23 @@
Vá!
+
+
+ dialog.btn.ok
+ OK
+
+
+
+
+ dialog.btn.cancel
+ Cancelar
+
+
+
+
+ dialog.btn.deny
+ Não
+
+
diff --git a/translations/frontend.ru.xlf b/translations/frontend.ru.xlf
index f4665a74..6e1879d8 100644
--- a/translations/frontend.ru.xlf
+++ b/translations/frontend.ru.xlf
@@ -55,5 +55,23 @@
Поехали!
+
+
+ dialog.btn.ok
+ ОК
+
+
+
+
+ dialog.btn.cancel
+ Отмена
+
+
+
+
+ dialog.btn.deny
+ Нет
+
+
diff --git a/translations/frontend.uk.xlf b/translations/frontend.uk.xlf
index fee1b03e..a28941fa 100644
--- a/translations/frontend.uk.xlf
+++ b/translations/frontend.uk.xlf
@@ -55,5 +55,23 @@
Почати!
+
+
+ dialog.btn.ok
+ ОК
+
+
+
+
+ dialog.btn.cancel
+ Скасувати
+
+
+
+
+ dialog.btn.deny
+ Ні
+
+
diff --git a/translations/frontend.zh.xlf b/translations/frontend.zh.xlf
index 8bb063b8..39ef238b 100644
--- a/translations/frontend.zh.xlf
+++ b/translations/frontend.zh.xlf
@@ -10,7 +10,7 @@
part.labelp
- 部件
+ 物料
@@ -34,7 +34,7 @@
user.password_strength.medium
- 中
+ 中等
@@ -52,7 +52,31 @@
search.submit
- GO!
+ 搜索
+
+
+
+
+ user.password_strength.crack_time
+ 预计破解时间:%time%
+
+
+
+
+ dialog.btn.ok
+ 确定
+
+
+
+
+ dialog.btn.cancel
+ 取消
+
+
+
+
+ dialog.btn.deny
+ 否
diff --git a/translations/messages.de.xlf b/translations/messages.de.xlf
index 26f6d40f..5542a6ed 100644
--- a/translations/messages.de.xlf
+++ b/translations/messages.de.xlf
@@ -13617,6 +13617,36 @@ Buerklin-API-Authentication-Server:
Host-URL
+
+
+ settings.ai.ollama
+ Ollama
+
+
+
+
+ settings.ai.ollama.endpoint
+ Endpoint URL
+
+
+
+
+ settings.ai.ollama.apiKey
+ API Key
+
+
+
+
+ settings.ai.timeout
+ Timeout
+
+
+
+
+ settings.ai.timeout.help
+ Maximale Wartezeit in Sekunden auf eine Antwort. Die lokale KI-Inferenz kann mehrere Minuten dauern, die Inferenz in der Cloud ist in der Regel schneller.
+
+
browser_plugin.recent_pages.title
@@ -13665,5 +13695,53 @@ Buerklin-API-Authentication-Server:
Sie können diesen zufällig generierten Wert verwenden (geben Sie ihn niemandem weiter):
+
+
+ info_providers.provider_key
+ Informationsquelle
+
+
+
+
+ info_providers.provider_id
+ Provider ID
+
+
+
+
+ info_providers.provider_url
+ Provider URL
+
+
+
+
+ part.edit.provider_reference
+ Referenz auf Informationsquelle
+
+
+
+
+ log.element_edited.changed_fields.providerReference.provider_key
+ Informationsquelle
+
+
+
+
+ log.element_edited.changed_fields.providerReference.provider_id
+ Provider ID
+
+
+
+
+ part.info_provider_reference.updated_never
+ Niemals aktualisiert
+
+
+
+
+ part.edit.provider_reference.warning
+ Warnung: Das Ändern der Werte an dieser Stelle kann den Mechanismus zum Abrufen von Informationen beeinträchtigen! Sie sollten nach Möglichkeit die Funktion „Von Informationsquelle aktualisieren“ verwenden.
+
+
diff --git a/translations/messages.en.xlf b/translations/messages.en.xlf
index 117e529b..36021868 100644
--- a/translations/messages.en.xlf
+++ b/translations/messages.en.xlf
@@ -490,7 +490,7 @@ The user will have to set up all two-factor authentication methods again and pri
entity.delete.confirm_title
- You really want to delete %name%?
+ Do you really want to delete %name%?
@@ -13619,6 +13619,36 @@ Buerklin-API Authentication server:
Host URL
+
+
+ settings.ai.ollama
+ Ollama
+
+
+
+
+ settings.ai.ollama.endpoint
+ Endpoint URL
+
+
+
+
+ settings.ai.ollama.apiKey
+ API Key
+
+
+
+
+ settings.ai.timeout
+ Timeout
+
+
+
+
+ settings.ai.timeout.help
+ Maximum time in seconds to wait for a response. Local AI inference might take multiple minutes, cloud inference is normally faster.
+
+
browser_plugin.recent_pages.title
@@ -13667,5 +13697,53 @@ Buerklin-API Authentication server:
You can use this randomly generated value (share it with nobody):
+
+
+ info_providers.provider_key
+ Info provider
+
+
+
+
+ info_providers.provider_id
+ Provider ID
+
+
+
+
+ info_providers.provider_url
+ Provider URL
+
+
+
+
+ part.edit.provider_reference
+ Info provider reference
+
+
+
+
+ log.element_edited.changed_fields.providerReference.provider_key
+ Information provider
+
+
+
+
+ log.element_edited.changed_fields.providerReference.provider_id
+ Provider ID
+
+
+
+
+ part.info_provider_reference.updated_never
+ Never updated
+
+
+
+
+ part.edit.provider_reference.warning
+ Warning: Changing values here can break the info retrieval mechanism! You should use the "update from info provider" functionality whenever possible.
+
+
diff --git a/translations/messages.zh.xlf b/translations/messages.zh.xlf
index fde08cfe..5cd93041 100644
--- a/translations/messages.zh.xlf
+++ b/translations/messages.zh.xlf
@@ -1,49 +1,49 @@
-
+
-
+
attachment_type.caption
- 附件类型
+ 附件的文件类型
-
+
new
attachment_type.edit
- 编辑文件类型
+ 编辑附件类型
-
+
new
attachment_type.new
- 新建类型
+ 新建附件类型
-
+
category.labelp
类别
-
+
admin.options
选项
-
+
admin.advanced
高级
-
+
new
@@ -52,7 +52,7 @@
编辑类别
-
+
new
@@ -61,25 +61,19 @@
新建类别
-
-
- currency.caption
- 货币
-
-
-
+
currency.iso_code.caption
- 货币ISO代码
+ ISO代码
-
+
currency.symbol.caption
货币符号
-
+
new
@@ -88,7 +82,7 @@
编辑货币
-
+
new
@@ -97,7 +91,7 @@
新建货币
-
+
new
@@ -106,7 +100,7 @@
编辑项目
-
+
new
@@ -115,118 +109,118 @@
新建项目
-
+
search.placeholder
搜索
-
+
expandAll
全部展开
-
+
reduceAll
全部收起
-
-
+
+
part.info.timetravel_hint
- 在 %timestamp% 之前,部件是这样显示的。 <i>请注意,这是试验性功能,信息可能不正确。</i>
+ 这是物料在 %timestamp% 之前的数据。<i>请注意,此功能是实验性的,信息可能不正确。</i>
-
+
standard.label
属性
-
+
infos.label
信息
-
+
new
-
+
history.label
历史
-
+
export.label
导出
-
+
import_export.label
导入/导出
-
+
mass_creation.label
- 大量创建
+ 批量创建
-
+
admin.common
- 基本
+ 通用
-
+
admin.attachments
附件
-
+
admin.parameters
参数
-
+
export_all.label
导出所有元素
-
+
mass_creation.help
- 每一行将被解析为新建元素的名称。可以通过缩进创建嵌套元素。
+ 每行将被解释为要创建的元素的名称。可以通过缩进创建嵌套结构。
-
-
+
+
edit.caption
- 编辑元素 "%name"
+ 编辑元素%name%
-
-
+
+
new.caption
- 新建元素
+ 新元素
-
+
footprint.labelp
封装
-
+
new
@@ -235,7 +229,7 @@
编辑封装
-
+
new
@@ -244,79 +238,61 @@
新建封装
-
-
- group.edit.caption
- 组
-
-
-
+
user.edit.permissions
权限
-
+
new
group.edit
- 编辑组
+ 编辑用户组
-
+
new
group.new
- 新建组
+ 新建用户组
-
-
- label_profile.caption
- 标签配置
-
-
-
+
label_profile.advanced
高级
-
+
label_profile.comment
- 注释
+ 备注
-
+
new
label_profile.edit
- 编辑标签配置
+ 编辑标签模板
-
+
new
label_profile.new
- 新建标签配置
+ 新建标签模板
-
-
- manufacturer.caption
- 制造商
-
-
-
+
new
@@ -325,7 +301,7 @@
编辑制造商
-
+
new
@@ -334,43 +310,31 @@
新建制造商
-
-
- measurement_unit.caption
- 计量单位
-
-
-
-
- part_custom_state.caption
- 部件的自定义状态
-
-
-
+
storelocation.labelp
- 储存位置
+ 存储位置
-
+
new
storelocation.edit
- 编辑存储位置
+ 编辑储存位置
-
+
new
storelocation.new
- 新建存储位置
+ 新建储存位置
-
+
new
@@ -379,7 +343,7 @@
编辑供应商
-
+
new
@@ -388,82 +352,73 @@
新建供应商
-
-
- user.edit.caption
- Users
-
-
-
+
user.edit.configuration
配置
-
+
user.edit.password
密码
-
+
user.edit.tfa.caption
- 2FA身份验证
+ 双因素认证
-
+
user.edit.tfa.google_active
- 身份验证器应用处于活动状态
+ 身份验证器应用已激活
-
+
tfa_backup.remaining_tokens
- 剩余备份代码计数
+ 剩余备份代码数量
-
+
tfa_backup.generation_date
- 备份代码的生成日期
+ 备份生成日期
-
+
user.edit.tfa.disabled
方法未启用
-
+
user.edit.tfa.u2f_keys_count
- 主动安全密钥
+ 活跃的安全密钥
-
+
user.edit.tfa.disable_tfa_title
- 确定继续?
+ 确定要继续?
-
+
user.edit.tfa.disable_tfa_message
- 这将禁用 <b>账号的所有活动2FA身份验证方法</b> 并删除 <b>备份代码</b>!
-<br>
-该账号将必须重新设置所有2FA身份验证方法并获取新的备份代码。 <br><br>
-<b>应在完全确定 申请者身份 时才执行此操作,否则该账号可能会受到攻击。</b>
+ 这将<b>禁用用户所有活动的双因素认证方法</b>并删除<b>备份代码</b>!<br>用户将需要重新设置所有双因素认证方法并打印新的备份代码!<br><br><b>只有在绝对确定用户身份(寻求帮助)的情况下才执行此操作,否则账号可能被攻击者入侵!</b>
-
+
user.edit.tfa.disable_tfa.btn
- 禁用所有2FA身份验证方法
+ 禁用所有双因素认证方法
-
+
new
@@ -472,7 +427,7 @@
编辑用户
-
+
new
@@ -481,1347 +436,1363 @@
新建用户
-
+
attachment.delete
删除
-
+
- attachment.external
- 外部
+ attachment.external_only
+ 仅外部
-
+
attachment.preview.alt
附件缩略图
-
+
- attachment.view
- 查看
+ attachment.view_local
+ 查看本地副本
-
+
attachment.file_not_found
- 文件未找到
+ 未找到文件
-
+
attachment.secure
私有附件
-
+
attachment.create
- 添加附件
+ 增加附件
-
+
part_lot.edit.delete.confirm
- 确认删除 该库存 ? 该操作不能被撤消
+ 确定要删除此库存?此操作无法撤消!
-
+
entity.delete.confirm_title
- 确定删除 %name%?
+ 确定要删除%name%?
-
+
entity.delete.message
- 该操作不能被撤销
-<br>
-子元素将向上移动。
+ 此操作无法撤消!<br>子元素将向上移动</br>。
-
+
entity.delete
- Delete element
+ 删除元素
-
+
new
-
+
edit.log_comment
- 更改评论
+ 更改备注
-
+
entity.delete.recursive
- 递归删除(所有子元素)
+ 递归删除(所有子元素)
-
+
entity.duplicate
- 重复元素
+ 复制此元素并新建
-
+
export.format
文件格式
-
+
export.level
详细程度
-
+
export.level.simple
简单
-
+
export.level.extended
扩展
-
+
export.level.full
- 完全
+ 完整
-
+
export.include_children
- 在导出中包含子元素
+ 导出时包含子元素
-
+
export.btn
导出
-
+
id.label
ID
-
+
createdAt
- 创建于
+ 创建时间
-
+
lastModified
- 上次修改
+ 最后修改时间
-
+
entity.info.parts_count
- 具有该元素的部件数量
+ 具有此元素的物料数量
-
+
specifications.property
参数
-
+
specifications.symbol
符号
-
+
specifications.value_min
- 最小.
+ 最小
-
+
specifications.value_typ
- 标称.
+ 标称
-
+
specifications.value_max
- 最大.
+ 最大
-
+
specifications.unit
单位
-
+
specifications.text
文本
-
+
specifications.group
- Group
+ 用户组
-
+
+
+ specifications.eda_visibility.help
+ 将此参数导出为EDA字段
+
+
+
specification.create
- 新建参数
+ 新参数
-
+
parameter.delete.confirm
- 确实删除该参数?
+ 确定要删除此参数?
-
+
attachment.list.title
附件列表
-
+
part_list.loading.caption
- 加载中...
+ 加载中
-
+
part_list.loading.message
- 这可能需要一些时间。如果此消息没有消失,请尝试重新加载页面。
+ 这可能需要等待一些时间。如果此消息未消失请尝试重新加载页面。
-
+
vendor.base.javascript_hint
- 需要激活 Javascript 才能使用所有功能
+ 请启用Javascript以使用所有功能!
-
+
sidebar.big.toggle
- 显示/隐藏 侧边栏
+ 显示/隐藏侧边栏
-
+
loading.caption
- 加载中:
+ 加载中:
-
+
loading.message
- 这可能需要一段时间。如果此消息长时间存在,请尝试重新加载页面。
+ 这可能需要等待一段时间。如果此消息长时间存在请尝试重新加载页面。
-
+
loading.bar
加载中...
-
+
back_to_top
返回页面顶部
-
+
permission.edit.permission
权限
-
+
permission.edit.value
- 配置
+ 值
-
+
permission.legend.title
状态说明
-
+
permission.legend.disallow
禁止
-
+
permission.legend.allow
- 允许
+ 已允许
-
+
permission.legend.inherit
- 继承
+ 从父级用户组继承
-
+
bool.true
- TRUE
+ true
-
+
bool.false
- FALSE
+ false
-
+
Yes
YES
-
+
No
NO
-
+
specifications.value
值
-
+
version.caption
版本
-
+
homepage.license
- 许可信息
+ 许可证信息
-
+
homepage.github.caption
项目页面
-
-
+
+
homepage.github.text
- 源代码、下载、错误报告、待办事项列表等可以在 <a href="%href%" class="link-external" target="_blank">项目仓库</a> 上找到。
+ 源代码、下载、错误报告、待办列表等可在 <a href="%href%"class="link-external"target="_blank">GitHub项目页面</a> 上找到
-
+
homepage.help.caption
帮助
-
+
homepage.help.text
- 帮助和提示可以在 <a href="%href%" class="link-external" target="_blank">文档</a>中找到。
+ 帮助和技巧可在 Wiki 和 <a href="%href%"class="link-external"target="_blank">GitHub页面</a> 中找到
-
+
homepage.forum.caption
论坛
-
+
new
homepage.last_activity
- 最近的事件
+ 最近活跃事件
-
+
label_generator.title
标签生成器
-
-
+
+
label_generator.common
- 基本
+ 通用
-
+
label_generator.advanced
高级
-
+
label_generator.profiles
- 标签配置
+ 模板
-
+
label_generator.selected_profile
- 当前选择的配置
+ 当前选中的模板
-
+
label_generator.edit_profile
- 编辑配置
+ 编辑模板
-
+
label_generator.load_profile
- 载入配置
+ 加载模板
-
+
label_generator.download
下载
-
+
label_generator.label_btn
生成标签
-
+
label_generator.label_empty
- 新建空标签
+ 新的空白标签
-
+
label_scanner.title
标签扫描器
-
+
label_scanner.no_cam_found.title
- 未找到摄像头
+ 未找到网络摄像头
-
+
label_scanner.no_cam_found.text
- 您需要一个摄像头并授予权限。或在下面手动输入条形码。
+ 需要网络摄像头并授权。也可以在下方手动输入条码。
-
+
label_scanner.source_select
选择源
-
+
log.list.title
系统日志
-
+
new
-
+
log.undo.confirm_title
- 确定撤消更改/恢复到时间戳?
+ 确实要撤消更改/恢复到时间戳?
-
+
new
-
+
log.undo.confirm_message
- 确定撤消给定的更改/将元素重置到给定的时间戳?
+ 确定要撤消该更改/将元素重置到该时间戳?
-
+
mail.footer.email_sent_by
- 这封电子邮件是自动发送的,由
+ 此邮件由以下系统自动发送
-
+
mail.footer.dont_reply
- 不要回复此电子邮件。
+ 请勿回复此邮件。
-
+
email.hi %name%
你好 %name%
-
+
email.pw_reset.message
- 有人请求重置您的密码。 如果此请求不是您提出的,请忽略此邮件。
+ 重置密码请求。如果对此请求不知情,请忽略此邮件。
-
+
email.pw_reset.button
- 单击此处重置密码
+ 点击重置密码
-
+
email.pw_reset.fallback
- 如果这对您不起作用,请转到 <a href="%url%">%url%</a> 并输入以下信息
+ 如果按钮不起作用,请访问<a href="%url%">%url%</a>并输入以下信息
-
+
email.pw_reset.username
用户名
-
+
email.pw_reset.token
- Token
+ 令牌
-
+
email.pw_reset.valid_unit %date%
- 重置令牌将在 <i>%date%</i> 之前有效。
+ 重置令牌有效期至 <i>%date%</i> 。
-
+
orderdetail.delete
- Delete
+ 删除
-
+
pricedetails.edit.min_qty
- 最低折扣数量
+ 最小折扣数量
-
+
pricedetails.edit.price
价格
-
+
pricedetails.edit.price_qty
数量
-
+
pricedetail.create
- 添加价格
+ 增加价格
-
+
part.edit.title
- 编辑部件
+ 编辑物料
-
+
part.edit.card_title
- 编辑部件
+ 编辑物料
-
+
+
+ form.dirty_form.unsaved_changes.title
+ 未保存的更改
+
+
+
+
+ form.dirty_form.unsaved_changes.message
+ 有未保存的更改,如果离开此页面更改将丢弃。确定要继续?
+
+
+
part.edit.tab.common
- 基础
+ 通用
-
+
part.edit.tab.manufacturer
制造商
-
+
part.edit.tab.advanced
高级
-
+
part.edit.tab.advanced.ipn.commonSectionHeader
- Sugestie bez zwiększenia części
+ 不包含物料增量的建议
-
+
part.edit.tab.advanced.ipn.partIncrementHeader
- 包含部件数值增量的建议
+ 包含数字物料增量的建议
-
+
part.edit.tab.advanced.ipn.prefix.description.current-increment
- 部件的当前IPN规格
+ 物料的当前IPN规范
-
+
part.edit.tab.advanced.ipn.prefix.description.increment
- 基于相同部件描述的下一个可能的IPN规格
+ 基于相同物料描述的下一个可能的IPN规范
-
+
part.edit.tab.advanced.ipn.prefix_empty.direct_category
- 直接类别的 IPN 前缀为空,请在类别“%name%”中指定。
+ 直接类别的IPN前缀为空,请在类别%name%中指定
-
+
part.edit.tab.advanced.ipn.prefix.direct_category
直接类别的IPN前缀
-
+
part.edit.tab.advanced.ipn.prefix.direct_category.increment
- 直接类别的IPN前缀和部件特定的增量
+ 直接类别的IPN前缀和物料特定增量
-
+
part.edit.tab.advanced.ipn.prefix.hierarchical.no_increment
- 具有父级前缀层级类别顺序的IPN前缀
+ 具有父前缀层次结构的IPN前缀
-
+
part.edit.tab.advanced.ipn.prefix.hierarchical.increment
- 具有父级前缀层级类别顺序和组件特定增量的IPN前缀
+ 具有父前缀层次结构和物料特定增量的IPN前缀
-
+
part.edit.tab.advanced.ipn.prefix.not_saved
- 请先创建组件并将其分配到类别:基于现有类别及其专属的IPN前缀,可以自动建议组件的IPN
+ 请先创建物料并分配类别:利用现有的类别及其各自的IPN前缀,系统会自动为该物料建议IPN。
-
+
part.edit.tab.part_lots
库存
-
+
part.edit.tab.attachments
附件
-
+
part.edit.tab.orderdetails
采购信息
-
+
part.edit.tab.specifications
参数
-
+
part.edit.tab.comment
- 注释
+ 备注
-
+
part.new.card_title
- 创建新部件
+ 创建新物料
-
+
part_lot.delete
删除
-
+
part_lot.create
增加库存
-
+
orderdetail.create
- 添加经销商
+ 增加经销商
-
+
pricedetails.edit.delete.confirm
- 确实删除此价格? 该操作不能被撤消
+ 确定要删除此价格?此操作无法撤消。
-
+
orderdetails.edit.delete.confirm
- 确实要删除此经销商信息? 该操作不能被撤消
+ 确定要删除此经销商信息?此操作无法撤消!
-
+
part.info.title
- 部件的详细信息
+ 物料详细信息
-
+
part.part_lots.label
库存
-
+
comment.label
- 注释
+ 备注
-
+
part.info.specifications
参数
-
+
attachment.labelp
附件
-
+
vendor.partinfo.shopping_infos
- 采购信息
+ 购物信息
-
+
vendor.partinfo.history
历史
-
+
tools.label
工具
-
+
extended_info.label
扩展信息
-
+
attachment.name
名称
-
+
attachment.attachment_type
附件类型
-
+
attachment.file_name
文件名
-
+
attachment.file_size
文件大小
-
+
attachment.preview
预览图片
-
+
- attachment.download
- 下载
+ attachment.download_local
+ 下载本地副本
-
+
new
-
+
user.creating_user
- 创建此部件的用户
+ 创建此物料的用户
-
+
Unknown
未知
-
+
new
-
+
accessDenied
- 拒绝访问
+ 访问被拒绝
-
+
new
-
+
user.last_editing_user
- 最后编辑此部件的用户
+ 最后编辑此物料的用户
-
+
part.isFavorite
- Favorite
+ 收藏
-
+
part.minOrderAmount
- 最低订购量
+ 最小订购数量
-
+
manufacturer.label
制造商
-
+
name.label
名称
-
+
new
-
+
part.back_to_info
- 返回当前版本
+ 返回信息页面
-
+
description.label
描述
-
+
category.label
类别
-
+
instock.label
在库
-
+
mininstock.label
- 最低库存
+ 最小库存量
-
+
footprint.label
封装
-
+
part.avg_price.label
平均价格
-
+
part.supplier.name
名称
-
+
part.supplier.partnr
- 合作伙伴.
+ 物料编号
-
+
part.order.minamount
- 最低数量
+ 最小数量
-
+
part.order.price
价格
-
+
part.order.single_price
单价
-
+
part_lots.description
描述
-
+
part_lots.storage_location
存储位置
-
+
part_lots.amount
数量
-
+
part_lots.location_unknown
存储位置未知
-
+
part_lots.instock_unknown
数量未知
-
+
part_lots.expiration_date
- 到期时间
+ 过期日期
-
+
part_lots.is_expired
- 已到期
+ 已过期
-
+
part_lots.need_refill
- 需要补充
+ 需要补货
-
+
part.info.prev_picture
上一张图片
-
+
part.info.next_picture
下一张图片
-
+
part.mass.tooltip
- 重量
+ 质量
-
+
part.needs_review.badge
- 需要审查
+ 需要检查
-
+
part.favorite.badge
收藏
-
+
part.obsolete.badge
- 不再可用
+ 已停产
-
+
parameters.extracted_from_description
从描述中自动提取
-
+
parameters.auto_extracted_from_comment
- 从注释中自动提取
+ 从备注中自动提取
-
+
part.edit.btn
- 编辑部件
+ 编辑物料
-
+
part.clone.btn
- 克隆部件
+ 克隆物料
-
+
part.create.btn
- 新建部件
+ 创建新物料
-
+
part.delete.confirm_title
- 确定删除该部件?
+ 确定要删除此物料?
-
+
part.delete.message
- 此部件与它的任何相关信息(如附件、价格信息等)将被删除。 该操作不能被撤消
+ 此物料及所有关联信息(如附件、价格信息等)将被删除。此操作无法撤消!
-
+
part.delete
- 删除部件
+ 删除物料
-
+
parts_list.all.title
- 所有部件
+ 所有物料
-
+
parts_list.category.title
- 部件(根据类别)
+ 属于类别的物料
-
+
parts_list.footprint.title
- 部件(根据封装)
+ 使用封装的物料
-
+
parts_list.manufacturer.title
- 部件(根据制造商)
+ 属于制造商的物料
-
+
parts_list.search.title
- 搜索部件
+ 搜索物料
-
+
parts_list.storelocation.title
- 部件(根据存储位置)
+ 位于存储位置的物料
-
+
parts_list.supplier.title
- 部件(根据供应商)
+ 属于供应商的物料
-
+
parts_list.tags.title
- 部件(根据标签)
+ 拥有标签的物料
-
+
entity.info.common.tab
- 基本
+ 通用
-
+
entity.info.statistics.tab
- 统计数据
+ 统计
-
+
entity.info.attachments.tab
附件
-
+
entity.info.parameters.tab
参数
-
+
entity.info.name
名称
-
+
entity.info.parent
- 父元素
+ 父级
-
+
entity.edit.btn
编辑
-
+
entity.info.children_count
- 子元素计数
+ 子元素数量
-
+
tfa.check.title
- 需要2FA身份验证
+ 需要双因素认证
-
+
tfa.code.trusted_pc
- 这是受信任的计算机(如果启用此功能,则不会在此计算机上执行进一步的2FA查询)
+ 这是一台受信任的计算机(如果启用,将不会在此计算机上执行进一步的双因素查询)
-
+
login.btn
登录
-
+
user.logout
- 注销
+ 登出
-
+
tfa.check.code.label
身份验证器应用代码
-
+
tfa.check.code.help
- 输入身份验证器应用中的6位代码,如果身份验证器不可用,请输入备用代码之一。
+ 输入身份验证器应用中的6位代码,或如果身份验证器不可用,输入备份代码。
-
+
login.title
登录
-
+
login.card_title
登录
-
+
login.username.label
用户名
-
+
login.username.placeholder
用户名
-
+