Compare commits

..

28 Commits

Author SHA1 Message Date
Jan Böhmer
ebcda8b75a Added documentation to our twig class variables 2026-03-07 16:13:18 +01:00
copilot-swe-agent[bot]
17e83a9da5 Rename Twig globals: col_label, col_input, offset_label
Co-authored-by: jbtronics <5410681+jbtronics@users.noreply.github.com>
2026-03-07 15:10:32 +00:00
Jan Böhmer
815b2b6c7d Fixed margin of delete button on admin forms 2026-03-07 15:57:38 +01:00
Jan Böhmer
3aa7480fe4 Fixed remaining places where offsets where used 2026-03-07 15:54:56 +01:00
copilot-swe-agent[bot]
b508d57094 Rename form column Twig globals to shorter names: label_col, input_col, offset_col
Co-authored-by: jbtronics <5410681+jbtronics@users.noreply.github.com>
2026-03-07 13:11:16 +00:00
copilot-swe-agent[bot]
2769772877 Make form column layout configurable with global Twig variables
Co-authored-by: jbtronics <5410681+jbtronics@users.noreply.github.com>
2026-03-07 11:47:34 +00:00
copilot-swe-agent[bot]
aed1c4dc09 Initial plan 2026-03-07 11:34:39 +00:00
Jan Böhmer
e8d90487d2 Added "show password" toggle to all password fields 2026-03-07 00:47:31 +01:00
Jan Böhmer
598cf3ed80 Use a symfony form for login form
This allows us to reuse the global form renderings
2026-03-07 00:46:34 +01:00
Jan Böhmer
30e3bc3153 Fixed highlight on url change for tools sidebar tree 2026-03-07 00:26:33 +01:00
Jan Böhmer
f95a58087b Select the respective node in the sidebar treeviews, when navigating Part-DB
When you open a category page from everywhere in Part-DB, the respective node will be opened
2026-03-06 23:23:38 +01:00
Jan Böhmer
83608fffcf Do not scroll up the sidebar when clicking on a treeview 2026-03-06 22:28:42 +01:00
Jan Böhmer
78b1d41cf8 Merge remote-tracking branch 'origin/master' 2026-03-05 00:22:11 +01:00
Jan Böhmer
616c3a6742 Bumped version to 2.8.1 2026-03-05 00:22:08 +01:00
Copilot
d24a50a696 Auto-upload built assets as release attachments on version tag push (#1287)
* Initial plan

* Upload built assets as release attachments on version tag push

Co-authored-by: jbtronics <5410681+jbtronics@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: jbtronics <5410681+jbtronics@users.noreply.github.com>
2026-03-05 00:04:16 +01:00
Jan Böhmer
3480dd146e Do not use customer reference for digikey barcode creation info as it likely contains not the part number
Fixes #1285
2026-03-05 00:02:35 +01:00
Jan Böhmer
dbe49b5f00 Merge remote-tracking branch 'origin/master' 2026-03-04 23:58:47 +01:00
Jan Böhmer
1c28efb12e Updated dependencies 2026-03-04 23:58:41 +01:00
Jan Böhmer
a6ee68d75a Ensure that user has read permission to part and category to prevent IPN info leakage
issue #1283
2026-03-04 23:54:18 +01:00
Jan Böhmer
30ece64423 Update KiCad symbols and footprints lists (#1282)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-04 23:39:26 +01:00
dependabot[bot]
77ef77961d Bump actions/upload-artifact from 6 to 7 (#1277)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6 to 7.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-04 23:38:48 +01:00
dependabot[bot]
a629949479 Bump actions/download-artifact from 7 to 8 (#1278)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 7 to 8.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v7...v8)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '8'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-04 23:38:32 +01:00
Jan Böhmer
af6ddffa1d Check that user has general access rights to partdb
See #1283
2026-03-04 23:38:06 +01:00
Jan Böhmer
f15979ed11 Run cache:pool:clear --all instead of cache:clear in updater to clear really all cache pools, even app ones 2026-03-04 23:33:10 +01:00
Jan Böhmer
df3262a3f7 Moved cache.settings to cache.system adapter to ensure it is cleared on updating
Fixes #1279
2026-03-04 23:31:16 +01:00
Jan Böhmer
a071701870 Add cache pool clear hint to error pages
Related to #1279
2026-03-04 23:27:06 +01:00
Jan Böhmer
c549665578 Fixed flash messages in admin pages 2026-03-04 23:24:29 +01:00
Jan Böhmer
2137eecddf Check for good measure again, that a user is able to edit an entity in an admin form
issue #1283
2026-03-04 23:06:01 +01:00
55 changed files with 1116 additions and 1027 deletions

View File

@@ -8,6 +8,9 @@ on:
branches:
- '*'
- "!l10n_*" # Dont test localization branches
tags:
- 'v*.*.*'
- 'v*.*.*-**'
pull_request:
branches:
- '*'
@@ -17,6 +20,8 @@ jobs:
assets_artifact_build:
name: Build assets artifact
runs-on: ubuntu-22.04
permissions:
contents: write
env:
APP_ENV: prod
@@ -80,13 +85,20 @@ jobs:
run: zip -r /tmp/partdb_assets.zip public/build/ vendor/
- name: Upload assets artifact
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
name: Only dependencies and built assets
path: /tmp/partdb_assets.zip
- name: Upload full artifact
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
name: Full Part-DB including dependencies and built assets
path: /tmp/partdb_with_assets.zip
- name: Upload assets as release attachment
if: startsWith(github.ref, 'refs/tags/')
run: |
gh release upload "${{ github.ref_name }}" /tmp/partdb_assets.zip /tmp/partdb_with_assets.zip --clobber
env:
GH_TOKEN: ${{ github.token }}

View File

@@ -98,7 +98,7 @@ jobs:
-
name: Upload digest
if: github.event_name != 'pull_request'
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
name: digests-${{ matrix.platform-slug }}
path: /tmp/digests/*
@@ -113,7 +113,7 @@ jobs:
steps:
-
name: Download digests
uses: actions/download-artifact@v7
uses: actions/download-artifact@v8
with:
path: /tmp/digests
pattern: digests-*

View File

@@ -99,7 +99,7 @@ jobs:
-
name: Upload digest
if: github.event_name != 'pull_request'
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
name: digests-${{ matrix.platform-slug }}
path: /tmp/digests/*
@@ -114,7 +114,7 @@ jobs:
steps:
-
name: Download digests
uses: actions/download-artifact@v7
uses: actions/download-artifact@v8
with:
path: /tmp/digests
pattern: digests-*

View File

@@ -1 +1 @@
2.8.0
2.8.1

View File

@@ -19,6 +19,7 @@
import {Controller} from "@hotwired/stimulus";
import {default as TreeController} from "./tree_controller";
import {EVENT_INITIALIZED} from "@jbtronics/bs-treeview";
export default class extends TreeController {
static targets = [ "tree", 'sourceText' ];
@@ -40,6 +41,8 @@ export default class extends TreeController {
//Check if we have a saved mode
const stored_mode = localStorage.getItem(this._storage_key);
this._frame = this.element.dataset.frame || "content"; //By default, navigate in the content frame, if a frame is defined
//Use stored mode if possible, otherwise use default
if(stored_mode) {
try {
@@ -55,6 +58,39 @@ export default class extends TreeController {
//Register an event listener which checks if the tree needs to be updated
document.addEventListener('turbo:render', this.doUpdateIfNeeded.bind(this));
//Register an event listener, to check if we end up on a page we can highlight in the tree, if so then higlight it
document.addEventListener('turbo:load', this._onTurboLoad.bind(this));
//On initial page load the tree is not available yet, so do another check after the tree is initialized
this.treeTarget.addEventListener(EVENT_INITIALIZED, (event) => {
this.selectNodeWithURL(document.location)
});
}
_onTurboLoad(event) {
this.selectNodeWithURL(event.detail.url);
}
selectNodeWithURL(url) {
//Get path from url
const path = new URL(url).pathname;
if (!this._tree) {
return;
}
//Unselect all nodes
this._tree.unselectAll({silent: true, ignorePreventUnselect: true});
//Try to find a node with this path as data-path
const nodes = this._tree.findNodes(path, "href");
if (nodes.length !== 1) {
return; //We can only work with exactly one node, if there are multiple nodes with the same path, we cannot know which one to select, so we do nothing
}
const node = nodes[0];
node.setSelected(true, {ignorePreventUnselect: true, silent: true});
this._tree.revealNode(node);
}
doUpdateIfNeeded()

View File

@@ -39,6 +39,8 @@ export default class extends Controller {
*/
_tree = null;
_frame = "frame";
connect() {
const treeElement = this.treeTarget;
if (!treeElement) {
@@ -48,6 +50,7 @@ export default class extends Controller {
this._url = this.element.dataset.treeUrl;
this._data = this.element.dataset.treeData;
this._frame = this.element.dataset.frame || "content"; //By default, navigate in the content frame, if a frame is defined
if(this.element.dataset.treeShowTags === "true") {
this._showTags = true;
@@ -99,8 +102,7 @@ export default class extends Controller {
onNodeSelected: (event) => {
const node = event.detail.node;
if (node.href) {
window.Turbo.visit(node.href, {action: "advance"});
this._registerURLWatcher(node);
window.Turbo.visit(node.href, {action: "advance", frame: this._frame});
}
},
}, [BS5Theme, BS53Theme, FAIconTheme]);
@@ -110,41 +112,12 @@ export default class extends Controller {
const treeView = event.detail.treeView;
treeView.revealNode(treeView.getSelected());
//Add the url watcher to all selected nodes
for (const node of treeView.getSelected()) {
this._registerURLWatcher(node);
}
//Add contextmenu event listener to the tree, which allows us to open the links in a new tab with a right click
treeView.getTreeElement().addEventListener("contextmenu", this._onContextMenu.bind(this));
});
}
_registerURLWatcher(node)
{
//Register a watcher for a location change, which will unselect the node, if the location changes
const desired_url = node.href;
//Ensure that the node is unselected, if the location changes
const unselectNode = () => {
//Parse url so we can properly compare them
const desired = new URL(node.href, window.location.origin);
//We only compare the pathname, because the hash and parameters should not matter
if(window.location.pathname !== desired.pathname) {
//The ignore parameter is important here, otherwise the node will not be unselected
node.setSelected(false, {silent: true, ignorePreventUnselect: true});
//Unregister the watcher
document.removeEventListener('turbo:load', unselectNode);
}
};
//Register the watcher via hotwire turbo
//We must just load to have the new url in window.location
document.addEventListener('turbo:load', unselectNode);
}
_onContextMenu(event)
{
@@ -198,4 +171,4 @@ export default class extends Controller {
return myResolve(this._data);
});
}
}
}

44
composer.lock generated
View File

@@ -4130,16 +4130,16 @@
},
{
"name": "dompdf/dompdf",
"version": "v3.1.4",
"version": "v3.1.5",
"source": {
"type": "git",
"url": "https://github.com/dompdf/dompdf.git",
"reference": "db712c90c5b9868df3600e64e68da62e78a34623"
"reference": "f11ead23a8a76d0ff9bbc6c7c8fd7e05ca328496"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dompdf/dompdf/zipball/db712c90c5b9868df3600e64e68da62e78a34623",
"reference": "db712c90c5b9868df3600e64e68da62e78a34623",
"url": "https://api.github.com/repos/dompdf/dompdf/zipball/f11ead23a8a76d0ff9bbc6c7c8fd7e05ca328496",
"reference": "f11ead23a8a76d0ff9bbc6c7c8fd7e05ca328496",
"shasum": ""
},
"require": {
@@ -4188,9 +4188,9 @@
"homepage": "https://github.com/dompdf/dompdf",
"support": {
"issues": "https://github.com/dompdf/dompdf/issues",
"source": "https://github.com/dompdf/dompdf/tree/v3.1.4"
"source": "https://github.com/dompdf/dompdf/tree/v3.1.5"
},
"time": "2025-10-29T12:43:30+00:00"
"time": "2026-03-03T13:54:37+00:00"
},
{
"name": "dompdf/php-font-lib",
@@ -9502,16 +9502,16 @@
},
{
"name": "sabberworm/php-css-parser",
"version": "v9.2.0",
"version": "v9.3.0",
"source": {
"type": "git",
"url": "https://github.com/MyIntervals/PHP-CSS-Parser.git",
"reference": "59373045e11ad47b5c18fc615feee0219e42f6d3"
"reference": "88dbd0f7f91abbfe4402d0a3071e9ff4d81ed949"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/59373045e11ad47b5c18fc615feee0219e42f6d3",
"reference": "59373045e11ad47b5c18fc615feee0219e42f6d3",
"url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/88dbd0f7f91abbfe4402d0a3071e9ff4d81ed949",
"reference": "88dbd0f7f91abbfe4402d0a3071e9ff4d81ed949",
"shasum": ""
},
"require": {
@@ -9538,7 +9538,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "9.3.x-dev"
"dev-main": "9.4.x-dev"
}
},
"autoload": {
@@ -9576,9 +9576,9 @@
],
"support": {
"issues": "https://github.com/MyIntervals/PHP-CSS-Parser/issues",
"source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v9.2.0"
"source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v9.3.0"
},
"time": "2026-02-21T17:12:03+00:00"
"time": "2026-03-03T17:31:43+00:00"
},
{
"name": "sabre/uri",
@@ -19148,12 +19148,12 @@
"source": {
"type": "git",
"url": "https://github.com/Roave/SecurityAdvisories.git",
"reference": "89525190c449738e468ee27e77f9fdc1bc160e08"
"reference": "da19d9c4572f2adae57b28b2111d7fa667cd6dcb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/89525190c449738e468ee27e77f9fdc1bc160e08",
"reference": "89525190c449738e468ee27e77f9fdc1bc160e08",
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/da19d9c4572f2adae57b28b2111d7fa667cd6dcb",
"reference": "da19d9c4572f2adae57b28b2111d7fa667cd6dcb",
"shasum": ""
},
"conflict": {
@@ -19282,7 +19282,7 @@
"commerceteam/commerce": ">=0.9.6,<0.9.9",
"components/jquery": ">=1.0.3,<3.5",
"composer/composer": "<1.10.27|>=2,<2.2.26|>=2.3,<2.9.3",
"concrete5/concrete5": "<9.4.3",
"concrete5/concrete5": "<9.4.8",
"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",
@@ -19296,7 +19296,7 @@
"cosenary/instagram": "<=2.3",
"couleurcitron/tarteaucitron-wp": "<0.3",
"cpsit/typo3-mailqueue": "<0.4.3|>=0.5,<0.5.1",
"craftcms/cms": "<4.17.0.0-beta1|>=5,<5.9.0.0-beta1",
"craftcms/cms": "<4.17.0.0-beta2|>=5,<5.9.0.0-beta2",
"craftcms/commerce": ">=4.0.0.0-RC1-dev,<=4.10|>=5,<=5.5.1",
"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",
@@ -19454,7 +19454,7 @@
"friendsoftypo3/openid": ">=4.5,<4.5.31|>=4.7,<4.7.16|>=6,<6.0.11|>=6.1,<6.1.6",
"froala/wysiwyg-editor": "<=4.3",
"frosh/adminer-platform": "<2.2.1",
"froxlor/froxlor": "<=2.2.5",
"froxlor/froxlor": "<=2.3.3",
"frozennode/administrator": "<=5.0.12",
"fuel/core": "<1.8.1",
"funadmin/funadmin": "<=7.1.0.0-RC4",
@@ -19504,7 +19504,7 @@
"ibexa/solr": ">=4.5,<4.5.4",
"ibexa/user": ">=4,<4.4.3|>=5,<5.0.4",
"icecoder/icecoder": "<=8.1",
"idno/known": "<=1.6.2",
"idno/known": "<1.6.4",
"ilicmiljan/secure-props": ">=1.2,<1.2.2",
"illuminate/auth": "<5.5.10",
"illuminate/cookie": ">=4,<=4.0.11|>=4.1,<6.18.31|>=7,<7.22.4",
@@ -19554,7 +19554,7 @@
"kelvinmo/simplexrd": "<3.1.1",
"kevinpapst/kimai2": "<1.16.7",
"khodakhah/nodcms": "<=3",
"kimai/kimai": "<2.46",
"kimai/kimai": "<=2.50",
"kitodo/presentation": "<3.2.3|>=3.3,<3.3.4",
"klaviyo/magento2-extension": ">=1,<3",
"knplabs/knp-snappy": "<=1.4.2",
@@ -20167,7 +20167,7 @@
"type": "tidelift"
}
],
"time": "2026-03-01T01:36:02+00:00"
"time": "2026-03-04T22:09:37+00:00"
},
{
"name": "sebastian/cli-parser",

View File

@@ -25,5 +25,5 @@ framework:
adapter: cache.app
cache.settings:
adapter: cache.app
adapter: cache.system
tags: true

View File

@@ -18,6 +18,11 @@ twig:
saml_enabled: '%partdb.saml.enabled%'
part_preview_generator: '@App\Services\Attachments\PartPreviewGenerator'
# Bootstrap grid classes used for horizontal form layouts
col_label: 'col-sm-3 col-lg-2' # The column classes for form labels
col_input: 'col-sm-9 col-lg-10' # The column classes for form input fields
offset_label: 'offset-sm-3 offset-lg-2' # Offset classes for elements that should be aligned with the input fields (e.g., submit buttons)
when@test:
twig:
strict_variables: true

View File

@@ -1,4 +1,4 @@
# Generated on Sun Mar 1 11:46:09 UTC 2026
# Generated on Tue Mar 3 14:26:21 UTC 2026
# This file contains all footprints available in the offical KiCAD library
Audio_Module:Reverb_BTDR-1H
Audio_Module:Reverb_BTDR-1V

View File

@@ -1,4 +1,4 @@
# Generated on Sun Mar 1 11:46:51 UTC 2026
# Generated on Tue Mar 3 14:27:05 UTC 2026
# This file contains all symbols available in the offical KiCAD library
4xxx:14528
4xxx:14529
@@ -20842,6 +20842,9 @@ Sensor_Pressure:40PC015G
Sensor_Pressure:40PC100G
Sensor_Pressure:40PC150G
Sensor_Pressure:40PC250G
Sensor_Pressure:ABPxxxxxxxxx0
Sensor_Pressure:ABPxxxxxxxxxA
Sensor_Pressure:ABPxxxxxxxxxS
Sensor_Pressure:BMP280
Sensor_Pressure:ILPS28QSW
Sensor_Pressure:LPS22DF

View File

@@ -195,6 +195,8 @@ abstract class BaseAdminController extends AbstractController
$this->commentHelper->setMessage($form['log_comment']->getData());
//In principle, the form should be disabled, if the edit permission is not granted, but for good measure, we also check it here, before saving changes.
$this->denyAccessUnlessGranted('edit', $entity);
$em->persist($entity);
$em->flush();
$this->addFlash('success', 'entity.edit_flash');

View File

@@ -25,6 +25,7 @@ namespace App\Controller;
use App\Entity\UserSystem\User;
use App\Events\SecurityEvent;
use App\Events\SecurityEvents;
use App\Form\Security\LoginFormType;
use App\Services\UserSystem\PasswordResetManager;
use Doctrine\ORM\EntityManagerInterface;
use Gregwar\CaptchaBundle\Type\CaptchaType;
@@ -61,7 +62,12 @@ class SecurityController extends AbstractController
// last username entered by the user
$lastUsername = $authenticationUtils->getLastUsername();
$form = $this->createForm(LoginFormType::class, [
'_username' => $lastUsername,
]);
return $this->render('security/login.html.twig', [
'form' => $form,
'last_username' => $lastUsername,
'error' => $error,
]);

View File

@@ -71,7 +71,10 @@ class TypeaheadController extends AbstractController
#[Route(path: '/builtInResources/search', name: 'typeahead_builtInRessources')]
public function builtInResources(Request $request, BuiltinAttachmentsFinder $finder): JsonResponse
{
$query = $request->get('query');
//Ensure that the user can access Part-DB at all
$this->denyAccessUnlessGranted('HAS_ACCESS_PERMISSIONS');
$query = $request->query->getString('query');
$array = $finder->find($query);
$result = [];
@@ -205,9 +208,16 @@ class TypeaheadController extends AbstractController
/** @var Category|null $category */
$category = $entityManager->getRepository(Category::class)->find($categoryId);
//Ensure the user has access to both the part and the category
$this->denyAccessUnlessGranted('read', $part);
if ($category !== null) {
$this->denyAccessUnlessGranted('read', $category);
}
$clonedPart = clone $part;
$clonedPart->setCategory($category);
$partRepository = $entityManager->getRepository(Part::class);
$ipnSuggestions = $partRepository->autoCompleteIpn($clonedPart, $description, $this->ipnSuggestSettings->suggestPartDigits);

View File

@@ -121,6 +121,7 @@ class BaseEntityAdminForm extends AbstractType
'label' => 'entity.edit.alternative_names.label',
'help' => 'entity.edit.alternative_names.help',
'empty_data' => null,
'disabled' => !$this->security->isGranted($is_new ? 'create' : 'edit', $entity),
'attr' => [
'class' => 'tagsinput',
'data-controller' => 'elements--tagsinput',

View File

@@ -45,7 +45,7 @@ final class TogglePasswordTypeExtension extends AbstractTypeExtension
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'toggle' => false,
'toggle' => true,
'hidden_label' => new TranslatableMessage('password_toggle.hide'),
'visible_label' => new TranslatableMessage('password_toggle.show'),
'hidden_icon' => 'Default',

View File

@@ -0,0 +1,83 @@
<?php
/*
* 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 <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace App\Form\Security;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use function Symfony\Component\Translation\t;
class LoginFormType extends AbstractType
{
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder, array $options): void
{
$builder
->add('_username', TextType::class, [
'label' => t('login.username.label'),
'attr' => [
'autofocus' => 'autofocus',
'autocomplete' => 'username',
'placeholder' => t('login.username.placeholder'),
]
])
->add('_password', PasswordType::class, [
'label' => t('login.password.label'),
'attr' => [
'autocomplete' => 'current-password',
'placeholder' => t('login.password.placeholder'),
]
])
->add('_remember_me', CheckboxType::class, [
'label' => t('login.rememberme'),
'required' => false,
])
->add('submit', \Symfony\Component\Form\Extension\Core\Type\SubmitType::class, [
'label' => t('login.btn'),
])
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
// This ensures CSRF protection is active for the login
'csrf_protection' => true,
'csrf_field_name' => '_csrf_token',
'csrf_token_id' => 'authenticate',
'attr' => [
'data-turbo' => 'false', // Disable Turbo for the login form to ensure proper redirection after login
]
]);
}
public function getBlockPrefix(): string
{
// This removes the "login_form_" prefix from field names
// so that Security can find "_username" directly.
return '';
}
}

View File

@@ -304,11 +304,11 @@ final readonly class BarcodeScanResultHandler
return null;
}
// Digi-Key: can use customerPartNumber or supplierPartNumber directly
// Digi-Key: supplierPartNumber directly
if ($vendor === 'digikey') {
return [
'providerKey' => 'digikey',
'providerId' => $scanResult->customerPartNumber ?? $scanResult->supplierPartNumber,
'providerId' => $scanResult->supplierPartNumber ?? throw new \RuntimeException('Digikey barcode does not contain required supplier part number'),
];
}
@@ -316,7 +316,7 @@ final readonly class BarcodeScanResultHandler
if ($vendor === 'element14') {
return [
'providerKey' => 'element14',
'providerId' => $scanResult->supplierPartNumber,
'providerId' => $scanResult->supplierPartNumber ?? throw new \RuntimeException('Element14 barcode does not contain required supplier part number'),
];
}

View File

@@ -420,7 +420,7 @@ class UpdateExecutor
// Step 11: Clear cache
$stepStart = microtime(true);
$this->runCommand([
'php', 'bin/console', 'cache:clear',
'php', 'bin/console', 'cache:pool:clear', '--all',
'--env=prod',
'--no-interaction',
], 'Clear cache', 120);
@@ -489,7 +489,7 @@ class UpdateExecutor
// Clear cache after rollback
$this->runCommand([
'php', 'bin/console', 'cache:clear',
'php', 'bin/console', 'cache:pool:clear', '--all',
'--env=prod',
], 'Clear cache after rollback', 120);
$log('rollback_cache', 'Cleared cache after rollback', true);

View File

@@ -1,23 +1,19 @@
{% block flashes %}
{# Insert flashes #}
<turbo-stream action="replace" action="morph" target="toast-container">
<turbo-stream action="update" action="morph" target="toast-container">
<template>
<div class="toast-container" id="toast-container">
{% for label, messages in app.flashes() %}
{% for message in messages %}
{{ include('_toast.html.twig', {
'label': label,
'message': message
}) }}
{% endfor %}
{% for label, messages in app.flashes() %}
{% for message in messages %}
{{ include('_toast.html.twig', {
'label': label,
'message': message
}) }}
{% endfor %}
</div>
{% endfor %}
</template>
</turbo-stream>
{% endblock %}
{# Insert info about when the sidebar trees were updated last time, so the sidebar_tree_controller can decide if it needs to reload the tree #}
<span id="sidebar-last-time-updated" style="display: none;" data-last-update="{{ sidebar_tree_updater.lastTreeUpdate.format("Y-m-d\\TH:i:sP") }}"></span>

View File

@@ -5,7 +5,7 @@
<input type="hidden" name="_token" value="{{ csrf_token('delete' ~ entity.id) }}">
<div class="form-group">
<div class=""></div>
<div class="col-sm offset-sm-3 ps-2">
<div class="{{ col_input }} {{ offset_label }} ps-1">
{% set delete_disabled = (not is_granted("delete", entity)) or (entity.group is defined and entity.id == 1) or entity == app.user %}
<div class="btn-group">
<button class="btn btn-danger" {% if delete_disabled %}disabled{% endif %}>{% trans %}entity.delete{% endtrans %}</button>
@@ -20,7 +20,7 @@
</div>
</div>
{% if entity.parent is defined %}
<div class="ms-2 custom-control custom-checkbox custom-control-inline">
<div class="ms-1 custom-control custom-checkbox custom-control-inline">
<input type="checkbox" class="form-check-input" id="recursive" name="delete_recursive">
<label class="form-check-label" for="recursive">{% trans %}entity.delete.recursive{% endtrans %}</label>
</div>

View File

@@ -1,5 +1,5 @@
<div class="row mb-2">
<div class="offset-3 col">
<div class="{{ offset_label }} {{ col_input }}">
<a class="btn btn-info {% if not is_granted('create', entity) %}disabled{% endif %}" href="{{ entity_url(entity, 'clone') }}">{% trans %}entity.duplicate{% endtrans %}</a>
</div>
</div>
</div>

View File

@@ -35,8 +35,8 @@
</div>
<div class="row mt-2">
<div class="offset-sm-3 col-sm">
<div class="{{ offset_label }} col-sm">
<button type="submit" class="btn btn-primary">{% trans %}export.btn{% endtrans %}</button>
</div>
</div>
</form>
</form>

View File

@@ -129,7 +129,7 @@
</div>
<div class="form-group row">
<div class="col-sm-9 offset-sm-3">
<div class="{{ col_input }} {{ offset_label }}">
<div class="btn-group">
{{ form_widget(form.save) }}
<button type="button" class="btn {% if entity.id is not null %}btn-primary{% else %}btn-success{% endif %} dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
@@ -186,7 +186,7 @@
<div id="mass_creation" class="tab-pane fade">
<div class="row">
<p class="text-muted offset-sm-3 col-sm-9">{% trans %}mass_creation.help{% endtrans %}</p>
<p class="text-muted {{ offset_label }} {{ col_input }}">{% trans %}mass_creation.help{% endtrans %}</p>
</div>
{{ form(mass_creation_form) }}
</div>
@@ -195,10 +195,7 @@
{% endif %}
</div>
</fieldset>
{# Include turbo control things, so we can still control page title and reloading #}
{% include "_turbo_control.html.twig" %}
</turbo-frame>
</div>
</div>
{% endblock %}
{% endblock %}

View File

@@ -41,13 +41,13 @@
{{ form_row(form.eda_info.reference_prefix) }}
<div class="row">
<div class="col-sm-9 offset-sm-3">
<div class="{{ col_input }} {{ offset_label }}">
{{ form_row(form.eda_info.visibility) }}
</div>
</div>
<div class="row mb-2">
<div class="col-sm-9 offset-sm-3">
<div class="{{ col_input }} {{ offset_label }}">
{{ form_widget(form.eda_info.exclude_from_bom) }}
{{ form_widget(form.eda_info.exclude_from_board) }}
{{ form_widget(form.eda_info.exclude_from_sim) }}
@@ -55,7 +55,7 @@
</div>
<div class="row">
<div class="col-sm-9 offset-sm-3">
<div class="{{ col_input }} {{ offset_label }}">
<h6>{% trans %}eda_info.kicad_section.title{% endtrans %}:</h6>
</div>
</div>

View File

@@ -10,10 +10,10 @@
{{ form_row(form.iso_code) }}
{% if entity.isoCode %}
<div class="mt-0 mb-3">
<span class="form-text text-muted offset-3 col-9">
<span class="form-text text-muted {{ offset_label }} {{ col_input }}">
<b>{% trans %}currency.iso_code.caption{% endtrans %}:</b> {{ entity.isoCode }}
</span>
<span class="form-text text-muted offset-3 col-9">
<span class="form-text text-muted {{ offset_label }} {{ col_input }}">
<b>{% trans %}currency.symbol.caption{% endtrans %}:</b> {{ entity.isoCode | currency_symbol }}
</span>
</div>
@@ -21,7 +21,7 @@
{{ form_row(form.exchange_rate) }}
{% if entity.inverseExchangeRate %}
<p class="form-text text-muted offset-3 col-9">
<p class="form-text text-muted {{ offset_label }} {{ col_input }}">
{{ '1'|format_currency(vars.base_currency()) }} = {{ entity.inverseExchangeRate.tofloat | format_currency(entity.isoCode, {fraction_digit: 5}) }}<br>
{{ '1'|format_currency(entity.isoCode) }} = {{ entity.exchangeRate.tofloat | format_currency(vars.base_currency(), {fraction_digit: 5}) }}
</p>

View File

@@ -28,7 +28,7 @@
{% block additional_panes %}
<div class="tab-pane" id="eda">
<div class="row">
<div class="col-sm-9 offset-sm-3">
<div class="{{ col_input }} {{ offset_label }}">
<h6>{% trans %}eda_info.kicad_section.title{% endtrans %}:</h6>
</div>
</div>

View File

@@ -27,7 +27,7 @@
{{ form_row(form.options.supported_element) }}
<div class="mb-2 row">
{{ form_label(form.options.width) }}
<div class="col-sm-9">
<div class="{{ col_input }}">
<div class="input-group">
{{ form_widget(form.options.width) }}

View File

@@ -31,8 +31,8 @@
{{ form_row(form.status) }}
{% if entity.id %}
<div class="mb-2 row">
<label class="col-form-label col-sm-3">{% trans %}project.edit.associated_build_part{% endtrans %}</label>
<div class="col-sm-9">
<label class="col-form-label {{ col_label }}">{% trans %}project.edit.associated_build_part{% endtrans %}</label>
<div class="{{ col_input }}">
{% if entity.buildPart %}
<span class="form-control-static"><a href="{{ entity_url(entity.buildPart) }}">{{ entity.buildPart.name }}</a></span>
{% else %}

View File

@@ -8,7 +8,7 @@
{% block additional_controls %}
{% if entity.id %}
<div class="row form-group">
<div class="offset-sm-3 col-sm-9">
<div class="{{ offset_label }} {{ col_input }}">
{{ dropdown.profile_dropdown('storelocation', entity.id) }}
</div>
</div>

View File

@@ -50,7 +50,7 @@
<div class="tab-pane" id="password">
{% if entity.samlUser %}
<div class="offset-3 mb-3 col-9">
<div class="mb-3 {{ offset_label }} {{ col_input }}">
<span class="badge badge-warning bg-warning"><i class="fa-solid fa-house-user"></i> {% trans %}user.saml_user{% endtrans %}</span>
</div>
{% endif %}
@@ -60,7 +60,7 @@
{{ form_row(form.disabled) }}
{% if entity.id is not null %}
<div class="offset-3 mb-3">
<div class="{{ offset_label }} {{ col_input }} mb-3">
<hr>
<h6>{% trans %}user.edit.tfa.caption{% endtrans %}</h6>

View File

@@ -44,7 +44,7 @@
{{ form_row(filterForm.discard) }}
<div class="row mb-3">
<div class="col-sm-9 offset-sm-3">
<div class="{{ col_input }} {{ offset_label }}">
<button type="button" class="btn btn-danger" {{ stimulus_action('helpers/form_cleanup', 'clearAll') }}>{% trans %}filter.clear_filters{% endtrans %}</button>
</div>
</div>

View File

@@ -17,7 +17,7 @@
<b><i>Can not load frontend assets.</i></b><p>Try following things:</p>
<ul>
<li>Run <kbd>yarn install</kbd> and <kbd>yarn build</kbd> in Part-DB folder.</li>
<li>Run <kbd>php bin/console cache:clear</kbd></li>
<li>Run <kbd>php bin/console cache:clear</kbd> and <kbd>php bin/console cache:pool:clear --all</kbd></li>
</ul>
{% elseif exception.class == "Doctrine\\DBAL\\Exception\\InvalidFieldNameException"
or exception.class == "Doctrine\\DBAL\\Exception\\TableNotFoundException"
@@ -26,21 +26,21 @@
<ul>
<li>Check if the <code>DATABASE_URL</code> in <code>.env.local</code> (or docker configure) is correct</li>
<li>Run <kbd>php bin/console doctrine:migrations:migrate</kbd> to upgrade database schema</li>
<li>Run <kbd>php bin/console cache:clear</kbd></li>
<li>Run <kbd>php bin/console cache:clear</kbd> and <kbd>php bin/console cache:pool:clear --all</kbd></li>
</ul>
{% elseif exception.class == "Doctrine\\DBAL\\Exception\\DriverException" %}
<b><i>Error while executing database query.</i></b><br>This is maybe caused by an old database schema.<br><p>Try following things:</p>
<ul>
<li>Check if the <code>DATABASE_URL</code> in <code>.env.local</code> (or docker configure) is correct</li>
<li>Run <kbd>php bin/console doctrine:migrations:migrate</kbd> to upgrade database schema (if upgrade is available)</li>
<li>Run <kbd>php bin/console cache:clear</kbd></li>
<li>Run <kbd>php bin/console cache:clear</kbd> and <kbd>php bin/console cache:pool:clear --all</kbd></li>
<li>If this issue persist, create a ticket at <a href="https://github.com/Part-DB/Part-DB-symfony/issues" rel="noopener">GitHub</a>.</li>
</ul>
{% else %}
You could try following things, if this error is unexpected:
<ul>
<li>Check <code>var/log/prod.log</code> (or <code>docker logs</code> when Part-DB is running inside a docker container) for additional informations</li>
<li>Run <kbd>php bin/console cache:clear</kbd> to clear cache</li>
<li>Run <kbd>php bin/console cache:clear</kbd> and <kbd>php bin/console cache:pool:clear --all</kbd> to clear caches</li>
</ul>
{% endif %}
{% endblock %}
{% endblock %}

View File

@@ -63,8 +63,8 @@
<div class="">
{{ form_row(form.mountnames) }}
<div class="row mb-2">
<label class="col-form-label col-sm-3">{% trans %}project.bom.price{% endtrans %}</label>
<div class="col-sm-9">
<label class="col-form-label {{ col_label }}">{% trans %}project.bom.price{% endtrans %}</label>
<div class="{{ col_input }}">
<div class="input-group">
{{ form_widget(form.price) }}
{{ form_widget(form.priceCurrency) }}

View File

@@ -17,11 +17,11 @@
{% block form_label_class -%}
col-sm-3
{{ col_label }}
{%- endblock form_label_class %}
{% block form_group_class -%}
col-sm-9
{{ col_input }}
{%- endblock form_group_class %}
{% block si_unit_widget %}

View File

@@ -12,7 +12,7 @@
{% endblock %}
{% block card_content %}
<p class="text-muted offset-3">{% trans %}info_providers.from_url.help{% endtrans %}</p>
<p class="text-muted {{ offset_label }}">{% trans %}info_providers.from_url.help{% endtrans %}</p>
{{ form_start(form) }}
{{ form_row(form.url) }}

View File

@@ -28,7 +28,7 @@
{{ form_row(form.providers) }}
<div class="row mb-2">
<div class="col-sm-9 offset-sm-3">
<div class="{{ col_input }} {{ offset_label }}">
<a href="{{ path('info_providers_list') }}">{% trans %}info_providers.search.info_providers_list{% endtrans %}</a>
</div>
</div>

View File

@@ -8,7 +8,7 @@
{% block card_title %}<i class="fa-solid fa-gear fa-fw"></i> {% trans %}info_providers.settings.title{% endtrans %}: <b>{{ info_provider_info.name }}</b>{% endblock %}
{% block card_content %}
<div class="offset-sm-3">
<div class="{{ offset_label }}">
<h3>
{% if info_provider_info.url is defined %}
<a href="{{ info_provider_info.url }}" class="link-external" target="_blank" rel="nofollow">{{ info_provider_info.name }}</a>
@@ -23,7 +23,7 @@
{{ form_start(form) }}
<div class="row">
<div class="offset-sm-3 col mb-3 ps-2">
<div class="{{ offset_label }} col mb-3 ps-2">
<b>{{ form_help(form) }}</b>
</div>
</div>

View File

@@ -36,7 +36,7 @@
{{ form_row(form.options.supported_element) }}
<div class="mb-2 row">
{{ form_label(form.options.width) }}
<div class="col-sm-9">
<div class="{{ col_input }}">
<div class="input-group">
{{ form_widget(form.options.width) }}
@@ -59,8 +59,8 @@
<div class="tab-pane" id="profiles" role="tabpanel" aria-labelledby="profiles-tab">
<div class="form-group row">
<label class="col-sm-3 col-form-label">{% trans %}label_generator.selected_profile{% endtrans %}</label>
<div class="col-sm-9">
<label class="{{ col_label }} col-form-label">{% trans %}label_generator.selected_profile{% endtrans %}</label>
<div class="{{ col_input }}">
<span class="form-control-plaintext">{{ profile.name ?? '-' }}
{% if profile and is_granted("edit", profile) %}
<a href="{{ entity_url(profile, 'edit') }}" title="{% trans %}label_generator.edit_profile{% endtrans %}"
@@ -71,7 +71,7 @@
</div>
<div class="form-group row">
<div class="offset-sm-3 col-sm-9">
<div class="{{ offset_label }} {{ col_input }}">
<div class="dropdown">
<button class="btn btn-info dropdown-toggle" type="button" id="loadProfilesButton"
{% if not is_granted("@labels.create_labels") %}disabled{% endif %}
@@ -97,7 +97,7 @@
{% if is_granted("@labels.read_profiles") %}
<div class="form-group row">
<div class="offset-sm-3 col-sm-9">
<div class="{{ offset_label }} {{ col_input }}">
<a class="btn btn-link" href="{{ path('label_profile_new') }}">{% trans %}label_generator.edit_profiles{% endtrans %}</a>
</div>
</div>
@@ -108,7 +108,7 @@
{% endif %}
<div class="form-group row">
<div class="offset-sm-3 col-sm-9">
<div class="{{ offset_label }} {{ col_input }}">
<div class="input-group">
{{ form_widget(form.save_profile_name) }}
{{ form_widget(form.save_profile) }}
@@ -124,7 +124,7 @@
{{ form_end(form) }}
{% if pdf_data %}
<div class="row">
<div class="col-sm-9 offset-sm-3">
<div class="{{ col_input }} {{ offset_label }}">
<a data-turbo="false" class="btn btn-secondary" href="#" {{ stimulus_controller('pages/label_download_btn')}} {{ stimulus_action('pages/label_download_btn', 'download')}} download="{{ filename ?? '' }}">
{% trans %}label_generator.download{% endtrans %}
</a>

View File

@@ -9,7 +9,7 @@
<div class="">
<div class="form-group row">
<div class="offset-sm-3 col-sm-9">
<div class="{{ offset_label }} {{ col_input }}">
<div class="img-thumbnail" style="max-width: 600px;">
<div id="reader-box" {{ stimulus_controller('pages/barcode_scan') }}></div>
</div>

View File

@@ -40,7 +40,7 @@
{{ form_row(filterForm.discard) }}
<div class="row mb-3">
<div class="col-sm-9 offset-sm-3">
<div class="{{ col_input }} {{ offset_label }}">
<button type="button" class="btn btn-danger" {{ stimulus_action('helpers/form_cleanup', 'clearAll') }}>{% trans %}filter.clear_filters{% endtrans %}</button>
</div>
</div>

View File

@@ -4,7 +4,7 @@
{{ form_row(form.eda_info.visibility) }}
<div class="row mb-2">
<div class="col-sm-9 offset-sm-3">
<div class="{{ col_input }} {{ offset_label }}">
{{ form_widget(form.eda_info.exclude_from_bom) }}
{{ form_widget(form.eda_info.exclude_from_board) }}
{{ form_widget(form.eda_info.exclude_from_sim) }}
@@ -12,7 +12,7 @@
</div>
<div class="row">
<div class="col-sm-9 offset-sm-3">
<div class="{{ col_input }} {{ offset_label }}">
<h6>{% trans %}eda_info.kicad_section.title{% endtrans %}:</h6>
</div>
</div>

View File

@@ -1,7 +1,7 @@
{{ form_row(form.name) }}
{% if part.category is not null and part.category.partnameHint is not empty %}
<div class="row">
<div class="col-sm-9 offset-sm-3">
<div class="{{ col_input }} {{ offset_label }}">
<p class="form-text help-text"><b>{% trans %}part.edit.name.category_hint{% endtrans %}:</b> {{ part.category.partnameHint }}</p>
</div>
</div>

View File

@@ -107,7 +107,7 @@
{% set id = 'collapse_' ~ random() %}
<a class="btn btn-link offset-sm-3 btn-sm" data-bs-toggle="collapse" href="#{{ id }}" role="button" aria-expanded="false" aria-controls="{{ id }}">
<a class="btn btn-link {{ offset_label }} btn-sm" data-bs-toggle="collapse" href="#{{ id }}" role="button" aria-expanded="false" aria-controls="{{ id }}">
{% trans %}part_lot.edit.advanced{% endtrans %}
</a>
<div class="collapse" id="{{ id }}">
@@ -142,7 +142,7 @@
<div class="mb-2 row">
{{ form_label(form.file) }}
<div class="col-sm-9">
<div class="{{ col_input }}">
{{ form_widget(form.file) }}
{{ form_errors(form.file) }}
<small class="text-muted">{% trans %}attachment.max_file_size{% endtrans %}: {{ max_upload_size | format_bytes }}</small>

View File

@@ -140,7 +140,7 @@
</div>
<div class="form-group row">
<div class="col-sm-9 offset-sm-3">
<div class="{{ col_input }} {{ offset_label }}">
<div class="btn-group">
{{ form_widget(form.save) }}
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">

View File

@@ -25,7 +25,7 @@
{% endblock %}
{% block card_content %}
<p class="text-muted offset-sm-3">
<p class="text-muted {{ offset_label }}">
{% trans %}parts.import.help{% endtrans %}<br>
{% trans with {'%link%': 'https://docs.part-db.de/usage/import_export.html'} %}parts.import.help_documentation{% endtrans %}
</p>

View File

@@ -13,39 +13,39 @@
<input type="hidden" name="_redirect" value="{{ uri_without_host(app.request) }}">
<div class="row mb-2">
<label class="col-form-label col-sm-3">
<label class="col-form-label {{ col_label }}">
{% trans %}part.info.stocktake_modal.expected_amount{% endtrans %}
</label>
<div class="col-sm-9">
<div class="{{ col_input }}">
<span id="stocktake-modal-expected-amount" class="form-control-plaintext">0</span>
</div>
</div>
<div class="row mb-2">
<label class="col-form-label col-sm-3">
<label class="col-form-label {{ col_label }}">
{% trans %}part.info.stocktake_modal.actual_amount{% endtrans %}
</label>
<div class="col-sm-9">
<div class="{{ col_input }}">
<input type="number" required class="form-control" min="0" step="{{ (part.partUnit and not part.partUnit.integer) ? 'any' : '1' }}" name="actual_amount" value="">
</div>
</div>
<div class="row mb-2">
<label class="col-form-label col-sm-3">
<label class="col-form-label {{ col_label }}">
{% trans %}part.info.withdraw_modal.comment{% endtrans %}
</label>
<div class="col-sm-9">
<div class="{{ col_input }}">
<input type="text" class="form-control" name="comment" value="">
<div class="form-text">{% trans %}part.info.withdraw_modal.comment.hint{% endtrans %}</div>
</div>
</div>
<div class="row mb-2">
<label class="col-form-label col-sm-3">
<label class="col-form-label {{ col_label }}">
{% trans %}part.info.withdraw_modal.timestamp{% endtrans %}
</label>
<div class="col-sm-9">
<div class="{{ col_input }}">
{# The timestamp must be between a year ago and 1 hour in the future #}
<input type="datetime-local" class="form-control" name="timestamp" value=""
max="{{ "+10mins"|date('Y-m-d\\TH:i') }}" min="{{ "-1year"|date('Y-m-d\\TH:i') }}">

View File

@@ -18,17 +18,17 @@
<input type="hidden" name="_redirect" value="{{ uri_without_host(app.request) }}">
<div class="row mb-2">
<label class="col-form-label col-sm-3">
<label class="col-form-label {{ col_label }}">
{% trans %}part.info.withdraw_modal.amount{% endtrans %}
</label>
<div class="col-sm-9">
<div class="{{ col_input }}">
<input type="number" required class="form-control" min="0" step="{{ (part.partUnit and not part.partUnit.integer) ? 'any' : '1' }}" name="amount" value="">
</div>
</div>
<div class="row mb-2 d-none" id="withdraw-modal-move-to">
<label class="col-form-label col-sm-3">{% trans %}part.info.withdraw_modal.move_to{% endtrans %}</label>
<div class="col-sm-9">
<label class="col-form-label {{ col_label }}">{% trans %}part.info.withdraw_modal.move_to{% endtrans %}</label>
<div class="{{ col_input }}">
{% for lots in part.partLots|filter(l => l.instockUnknown == false) %}
<div class="form-check">
@@ -42,20 +42,20 @@
</div>
<div class="row mb-2">
<label class="col-form-label col-sm-3">
<label class="col-form-label {{ col_label }}">
{% trans %}part.info.withdraw_modal.comment{% endtrans %}
</label>
<div class="col-sm-9">
<div class="{{ col_input }}">
<input type="text" class="form-control" name="comment" value="" {% if event_comment_needed('part_stock_operation') %}required{% endif %}>
<div id="emailHelp" class="form-text">{% trans %}part.info.withdraw_modal.comment.hint{% endtrans %}</div>
</div>
</div>
<div class="row mb-2">
<label class="col-form-label col-sm-3">
<label class="col-form-label {{ col_label }}">
{% trans %}part.info.withdraw_modal.timestamp{% endtrans %}
</label>
<div class="col-sm-9">
<div class="{{ col_input }}">
{# The timestamp must be between a year ago and 1 hour in the future #}
<input type="datetime-local" class="form-control" name="timestamp" value=""
max="{{ "+10mins"|date('Y-m-d\\TH:i') }}" min="{{ "-1year"|date('Y-m-d\\TH:i') }}">
@@ -64,7 +64,7 @@
</div>
<div class="row mb-2">
<div class="col-sm-9 offset-sm-3">
<div class="{{ col_input }} {{ offset_label }}">
{# The timestamp must be between a year ago and 1 hour in the future #}
<div class="form-check">
<input class="form-check-input" type="checkbox" name="delete_lot_if_empty" value="true" id="withdraw_modal_delete_if_empty">

View File

@@ -148,7 +148,7 @@
{{ form_row(filterForm.discard) }}
<div class="row mb-3">
<div class="col-sm-9 offset-sm-3">
<div class="{{ col_input }} {{ offset_label }}">
<button type="button" class="btn btn-danger" {{ stimulus_action('helpers/form_cleanup', 'clearAll') }}>{% trans %}filter.clear_filters{% endtrans %}</button>
</div>
</div>

View File

@@ -33,7 +33,7 @@
{% if displayTrustedOption %}
<div class="form-group row mt-3">
<div class="offset-3">
<div class="{{ offset_label }} {{ col_input }}">
<div class="custom-checkbox custom-control ms-2">
<input id="_trusted" class="form-check-input" type="checkbox" name="{{ trustedParameterName }}" />
<label class="form-check-label" for="_trusted">{% trans %}tfa.code.trusted_pc{% endtrans %}</label>
@@ -48,11 +48,11 @@
{% block submit_btn %}
<div class="form-group-row">
<div class="offset-3">
<div class="{{ offset_label }} {{ col_input }}">
<button type="submit" class="btn btn-primary" value="{{ "login"|trans({}, 'SchebTwoFactorBundle') }}">{% trans %}login.btn{% endtrans %}</button>
<a class="ms-2" href="{{ logoutPath }}">{% trans %}user.logout{% endtrans %}</a>
</div>
</div>
{% endblock %}
</form>
{% endblock %}
{% endblock %}

View File

@@ -20,55 +20,25 @@
{% endblock %}
{% block card_content %}
<form action="{{ path('login') }}" method="post" data-turbo="false" class="form-horizontal">
<input type="hidden" name="_csrf_token" data-controller="csrf-protection" value="{{ csrf_token('authenticate') }}">
{% if saml_enabled %}
<div class="{{ offset_label }} {{ col_input }}">
<a class="btn btn-secondary" href="{{ path('saml_login') }}"><i class="fa-solid fa-house-user"></i> {% trans %}login.sso_saml_login{% endtrans %}</a>
<input type="hidden" name="_target_path" value="{{ app.request.query.get('_target_path') }}" />
{% if saml_enabled %}
<div class="col-md-9 offset-md-3 col-lg-10 offset-lg-2">
<a class="btn btn-secondary" href="{{ path('saml_login') }}"><i class="fa-solid fa-house-user"></i> {% trans %}login.sso_saml_login{% endtrans %}</a>
<p class="text-muted">{% trans %}login.local_login_hint{% endtrans %}</p>
</div>
{% endif %}
<div class="form-group row">
<label class="col-form-label col-md-3 col-lg-2">{% trans %}login.username.label{% endtrans %}</label>
<div class="col-md-9 col-lg-10">
<input type="text" class="form-control" name="_username" value="{{ last_username }}"
placeholder="{% trans %}login.username.placeholder{% endtrans %}" autocomplete="username">
</div>
</div>
<div class="form-group row">
<label class="col-form-label col-md-3 col-lg-2">{% trans %}login.password.label{% endtrans %}</label>
<div class="col-md-9 col-lg-10">
<input type="password" class="form-control" placeholder="{% trans %}login.password.placeholder{% endtrans %}" name="_password"
autocomplete="current-password">
</div>
<p class="text-muted">{% trans %}login.local_login_hint{% endtrans %}</p>
</div>
{% endif %}
<div class="form-group row">
<div class="col-md-9 offset-md-3 col-lg-10 offset-lg-2">
<div class="custom-control custom-checkbox custom-control-inline">
<input class="form-check-input" name="_remember_me" id="remember_me" type="checkbox">
<label class="form-check-label" for="remember_me">
{% trans %}login.rememberme{% endtrans %}
</label>
</div>
</div>
</div>
{{ form_start(form) }}
<div class="form-group row mt-3">
<div class="col-md-9 offset-md-3 col-lg-10 offset-lg-2">
<button type="submit" class="btn btn-primary">{% trans %}login.btn{% endtrans %}</button>
</div>
</div>
</form>
{{ form_row(form._username) }}
{{ form_row(form._password) }}
{{ form_row(form._remember_me) }}
{{ form_row(form.submit) }}
{{ form_end(form) }}
{% if allow_email_pw_reset %}
<a class="offset-sm-2" href="{{ path('pw_reset_request') }}">{% trans %}pw_reset.password_forget{% endtrans %}</a>
<a class="{{ offset_label }}" href="{{ path('pw_reset_request') }}">{% trans %}pw_reset.password_forget{% endtrans %}</a>
{% endif %}
{% endblock %}

View File

@@ -38,12 +38,12 @@
{% if section_widget.vars.embedded_settings_metadata is defined %} {# Check if we have nested embedded settings or not #}
<fieldset>
<legend class="offset-3">
<legend class="{{ offset_label }}">
<i class="fa-solid {{ settings_icon(settings_object)|default('fa-sliders') }} fa-fw"></i>
{{ (section_widget.vars.label ?? section_widget.vars.name|humanize)|trans }}
</legend>
<div class="row">
<div class="offset-sm-3 col mb-3 ps-2">
<div class="{{ offset_label }} col mb-3 ps-2">
<b>{{ form_help(section_widget) }}</b>
{{ form_errors(section_widget) }}
</div>

View File

@@ -37,11 +37,11 @@
{{ form_start(google_form, { 'attr': google_form_attr}) }}
{% if not tfa_google.enabled %}
<div class="offset-sm-3">
<div class="{{ offset_label }}">
<h6>{% trans %}tfa_google.disabled_message{% endtrans %}</h6>
</div>
<div class="offset-sm-3 row">
<div class="{{ offset_label }} row">
<div class="col-sm-3">
<img width="100%" class="img-fluid bg-white p-2" alt="{{ tfa_google.qrContent }}" src="{{ barcode_svg(tfa_google.qrContent) | data_uri("image/svg+xml") }}">
</div>
@@ -55,7 +55,7 @@
</div>
</div>
<div class="offset-sm-3">
<div class="{{ offset_label }}">
<button class="btn btn-link" type="button" data-bs-toggle="collapse" data-bs-target="#manualSetupCollapse" aria-expanded="false" aria-controls="manualSetupCollapse">
{% trans %}tfa_google.manual_setup{% endtrans %}
</button>
@@ -72,7 +72,7 @@
{{ form_row(google_form.google_confirmation) }}
{% else %}
<div class="offset-sm-3">
<div class="{{ offset_label }}">
<h6>{% trans %}tfa_google.enabled_message{% endtrans %}</h6>
</div>
{% endif %}
@@ -81,7 +81,7 @@
</div>
<div class="tab-pane fade" id="tfa-backup" role="tabpanel" aria-labelledby="backup-tab">
{% if user.backupCodes is empty %}
<div class="offset-sm-3">
<div class="{{ offset_label }}">
<h6>{% trans %}tfa_backup.disabled{% endtrans %}</h6>
<span>{% trans %}tfa_backup.explanation{% endtrans %}</span>
</div>
@@ -89,19 +89,19 @@
{% set backup_form_attr = { 'data-delete-form': true, 'data-controller': 'elements--delete-btn', 'data-action': 'submit->elements--delete-btn#submit',
'data-delete-title': 'tfa_backup.reset_codes.confirm_title' | trans, 'data-delete-message': 'tfa_backup.reset_codes.confirm_message' | trans} %}
{{ form_start(backup_form, { 'attr': backup_form_attr}) }}
<div class="offset-sm-3">
<div class="{{ offset_label }}">
<h6>{% trans %}tfa_backup.enabled{% endtrans %}</h6>
<span>{% trans %}tfa_backup.explanation{% endtrans %}</span>
</div>
<div class="offset-sm-3 mt-2">
<div class="{{ offset_label }} mt-2">
<p class="mb-0"><b>{% trans %}tfa_backup.remaining_tokens{% endtrans %}:</b> {{ user.backupCodes | length }}</p>
<p><b>{% trans %}tfa_backup.generation_date{% endtrans %}:</b> {{ user.backupCodesGenerationDate | format_datetime }}</p>
</div>
<div class="offset-sm-3">
<div class="{{ offset_label }}">
<a href="{{ path('show_backup_codes') }}" target="_blank" data-turbo="false" class="btn btn-primary">{% trans %}tfa_backup.show_codes{% endtrans %}</a>
</div>
<div class="offset-sm-3 mt-2">
<div class="{{ offset_label }} mt-2">
{{ form_widget(backup_form.reset_codes) }}
</div>
{{ form_end(backup_form) }}

View File

@@ -28,7 +28,7 @@
{{ form_row(settings_form.showEmailOnProfile) }}
{{ form_row(settings_form.avatar_file) }}
<div class="mb-3 row {% if user.masterPictureAttachment is null %}d-none{% endif %}">
<div class="offset-sm-3 col-sm-9">
<div class="{{ offset_label }} {{ col_input }}">
{% if user.masterPictureAttachment %}
<img src="{{ attachment_thumbnail(user.masterPictureAttachment, 'thumbnail_md') }}" alt="avatar" class="rounded" style="height: 75px;">
{% endif %}

1621
yarn.lock

File diff suppressed because it is too large Load Diff