mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-03-19 05:38:54 +01:00
Compare commits
28 Commits
v2.8.0
...
copilot/co
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ebcda8b75a | ||
|
|
17e83a9da5 | ||
|
|
815b2b6c7d | ||
|
|
3aa7480fe4 | ||
|
|
b508d57094 | ||
|
|
2769772877 | ||
|
|
aed1c4dc09 | ||
|
|
e8d90487d2 | ||
|
|
598cf3ed80 | ||
|
|
30e3bc3153 | ||
|
|
f95a58087b | ||
|
|
83608fffcf | ||
|
|
78b1d41cf8 | ||
|
|
616c3a6742 | ||
|
|
d24a50a696 | ||
|
|
3480dd146e | ||
|
|
dbe49b5f00 | ||
|
|
1c28efb12e | ||
|
|
a6ee68d75a | ||
|
|
30ece64423 | ||
|
|
77ef77961d | ||
|
|
a629949479 | ||
|
|
af6ddffa1d | ||
|
|
f15979ed11 | ||
|
|
df3262a3f7 | ||
|
|
a071701870 | ||
|
|
c549665578 | ||
|
|
2137eecddf |
16
.github/workflows/assets_artifact_build.yml
vendored
16
.github/workflows/assets_artifact_build.yml
vendored
@@ -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 }}
|
||||
|
||||
4
.github/workflows/docker_build.yml
vendored
4
.github/workflows/docker_build.yml
vendored
@@ -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-*
|
||||
|
||||
4
.github/workflows/docker_frankenphp.yml
vendored
4
.github/workflows/docker_frankenphp.yml
vendored
@@ -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-*
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
44
composer.lock
generated
@@ -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",
|
||||
|
||||
@@ -25,5 +25,5 @@ framework:
|
||||
adapter: cache.app
|
||||
|
||||
cache.settings:
|
||||
adapter: cache.app
|
||||
adapter: cache.system
|
||||
tags: true
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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,
|
||||
]);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
83
src/Form/Security/LoginFormType.php
Normal file
83
src/Form/Security/LoginFormType.php
Normal 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 '';
|
||||
}
|
||||
}
|
||||
@@ -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'),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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) }}
|
||||
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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) }}
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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) }}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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') }}">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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) }}
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
Reference in New Issue
Block a user