diff --git a/assets/controllers/elements/select_controller.js b/assets/controllers/elements/select_controller.js index a96bca10..1740c8fb 100644 --- a/assets/controllers/elements/select_controller.js +++ b/assets/controllers/elements/select_controller.js @@ -40,6 +40,7 @@ export default class extends Controller { let settings = { + plugins: [], allowEmptyOption: true, selectOnTab: true, maxOptions: null, @@ -50,7 +51,18 @@ export default class extends Controller { } }; + //Load the drag_drop plugin if the select is ordered + if (this.element.dataset.orderedValue) { + settings.plugins.push('drag_drop'); + } + this._tomSelect = new TomSelect(this.element, settings); + + //If the select is ordered, we need to update the value field (with the decoded value from the orderedValue field) + if (this.element.dataset.orderedValue) { + const data = JSON.parse(this.element.dataset.orderedValue); + this._tomSelect.setValue(data); + } } getTomSelect() { diff --git a/composer.lock b/composer.lock index 017f93e5..f1678cc2 100644 --- a/composer.lock +++ b/composer.lock @@ -889,16 +889,16 @@ }, { "name": "amphp/sync", - "version": "v2.2.0", + "version": "v2.3.0", "source": { "type": "git", "url": "https://github.com/amphp/sync.git", - "reference": "375ef5b54a0d12c38e12728dde05a55e30f2fbec" + "reference": "217097b785130d77cfcc58ff583cf26cd1770bf1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/amphp/sync/zipball/375ef5b54a0d12c38e12728dde05a55e30f2fbec", - "reference": "375ef5b54a0d12c38e12728dde05a55e30f2fbec", + "url": "https://api.github.com/repos/amphp/sync/zipball/217097b785130d77cfcc58ff583cf26cd1770bf1", + "reference": "217097b785130d77cfcc58ff583cf26cd1770bf1", "shasum": "" }, "require": { @@ -952,7 +952,7 @@ ], "support": { "issues": "https://github.com/amphp/sync/issues", - "source": "https://github.com/amphp/sync/tree/v2.2.0" + "source": "https://github.com/amphp/sync/tree/v2.3.0" }, "funding": [ { @@ -960,7 +960,7 @@ "type": "github" } ], - "time": "2024-03-12T01:00:01+00:00" + "time": "2024-08-03T19:31:26+00:00" }, { "name": "amphp/windows-registry", @@ -2908,16 +2908,16 @@ }, { "name": "doctrine/sql-formatter", - "version": "1.4.0", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/doctrine/sql-formatter.git", - "reference": "d1ac84aef745c69ea034929eb6d65a6908b675cc" + "reference": "7f83911cc5eba870de7ebb11283972483f7e2891" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/sql-formatter/zipball/d1ac84aef745c69ea034929eb6d65a6908b675cc", - "reference": "d1ac84aef745c69ea034929eb6d65a6908b675cc", + "url": "https://api.github.com/repos/doctrine/sql-formatter/zipball/7f83911cc5eba870de7ebb11283972483f7e2891", + "reference": "7f83911cc5eba870de7ebb11283972483f7e2891", "shasum": "" }, "require": { @@ -2957,9 +2957,9 @@ ], "support": { "issues": "https://github.com/doctrine/sql-formatter/issues", - "source": "https://github.com/doctrine/sql-formatter/tree/1.4.0" + "source": "https://github.com/doctrine/sql-formatter/tree/1.4.1" }, - "time": "2024-05-08T08:12:09+00:00" + "time": "2024-08-05T20:32:22+00:00" }, { "name": "dompdf/dompdf", @@ -4278,12 +4278,12 @@ "source": { "type": "git", "url": "https://github.com/jbtronics/settings-bundle.git", - "reference": "c1de2c5f47bcb92568f0cc80ea2af5421aaa2234" + "reference": "deb51a945cc6c7a2004584e2ac0c92e3841b22f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jbtronics/settings-bundle/zipball/c1de2c5f47bcb92568f0cc80ea2af5421aaa2234", - "reference": "c1de2c5f47bcb92568f0cc80ea2af5421aaa2234", + "url": "https://api.github.com/repos/jbtronics/settings-bundle/zipball/deb51a945cc6c7a2004584e2ac0c92e3841b22f6", + "reference": "deb51a945cc6c7a2004584e2ac0c92e3841b22f6", "shasum": "" }, "require": { @@ -4356,7 +4356,7 @@ "type": "github" } ], - "time": "2024-07-17T07:38:54+00:00" + "time": "2024-08-06T22:36:40+00:00" }, { "name": "jfcherng/php-color-output", @@ -16591,16 +16591,16 @@ }, { "name": "phpstan/phpstan-doctrine", - "version": "1.4.8", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-doctrine.git", - "reference": "fa497c5cf8a3f9cd3db8cb4033daf5244793d3e1" + "reference": "caa046bd6152818e781260fb3a7a96d6b0fadeed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/fa497c5cf8a3f9cd3db8cb4033daf5244793d3e1", - "reference": "fa497c5cf8a3f9cd3db8cb4033daf5244793d3e1", + "url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/caa046bd6152818e781260fb3a7a96d6b0fadeed", + "reference": "caa046bd6152818e781260fb3a7a96d6b0fadeed", "shasum": "" }, "require": { @@ -16657,9 +16657,9 @@ "description": "Doctrine extensions for PHPStan", "support": { "issues": "https://github.com/phpstan/phpstan-doctrine/issues", - "source": "https://github.com/phpstan/phpstan-doctrine/tree/1.4.8" + "source": "https://github.com/phpstan/phpstan-doctrine/tree/1.5.0" }, - "time": "2024-07-16T11:31:01+00:00" + "time": "2024-08-05T13:47:07+00:00" }, { "name": "phpstan/phpstan-strict-rules", @@ -17269,12 +17269,12 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "25b3df97450d4b8ae46ab6c6a2441db776b3e00d" + "reference": "176422aa2c339a0f4e56b92862c67a94e2b584fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/25b3df97450d4b8ae46ab6c6a2441db776b3e00d", - "reference": "25b3df97450d4b8ae46ab6c6a2441db776b3e00d", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/176422aa2c339a0f4e56b92862c67a94e2b584fb", + "reference": "176422aa2c339a0f4e56b92862c67a94e2b584fb", "shasum": "" }, "conflict": { @@ -17392,7 +17392,7 @@ "datatables/datatables": "<1.10.10", "david-garcia/phpwhois": "<=4.3.1", "dbrisinajumi/d2files": "<1", - "dcat/laravel-admin": "<=2.1.3.0-beta", + "dcat/laravel-admin": "<=2.1.3", "derhansen/fe_change_pwd": "<2.0.5|>=3,<3.0.3", "derhansen/sf_event_mgt": "<4.3.1|>=5,<5.1.1|>=7,<7.4", "desperado/xml-bundle": "<=0.1.7", @@ -17542,6 +17542,7 @@ "innologi/typo3-appointments": "<2.0.6", "intelliants/subrion": "<4.2.2", "inter-mediator/inter-mediator": "==5.5", + "ipl/web": "<0.10.1", "islandora/islandora": ">=2,<2.4.1", "ivankristianto/phpwhois": "<=4.3", "jackalope/jackalope-doctrine-dbal": "<1.7.4", @@ -17624,7 +17625,7 @@ "microsoft/microsoft-graph": ">=1.16,<1.109.1|>=2,<2.0.1", "microsoft/microsoft-graph-beta": "<2.0.1", "microsoft/microsoft-graph-core": "<2.0.2", - "microweber/microweber": "<=2.0.4", + "microweber/microweber": "<=2.0.16", "mikehaertl/php-shellcommand": "<1.6.1", "miniorange/miniorange-saml": "<1.4.3", "mittwald/typo3_forum": "<1.2.1", @@ -18075,7 +18076,7 @@ "type": "tidelift" } ], - "time": "2024-08-02T16:05:08+00:00" + "time": "2024-08-05T22:04:39+00:00" }, { "name": "sebastian/cli-parser", diff --git a/config/packages/twig.yaml b/config/packages/twig.yaml index 3f1e94fb..674aa317 100644 --- a/config/packages/twig.yaml +++ b/config/packages/twig.yaml @@ -12,7 +12,6 @@ twig: label_profile_dropdown_helper: '@App\Services\LabelSystem\LabelProfileDropdownHelper' error_page_admin_email: '%partdb.error_pages.admin_email%' error_page_show_help: '%partdb.error_pages.show_help%' - sidebar_items: '%partdb.sidebar.items%' sidebar_tree_updater: '@App\Services\Trees\SidebarTreeUpdater' avatar_helper: '@App\Services\UserSystem\UserAvatarHelper' available_themes: '%partdb.available_themes%' diff --git a/config/parameters.yaml b/config/parameters.yaml index 95e78773..f694410a 100644 --- a/config/parameters.yaml +++ b/config/parameters.yaml @@ -49,17 +49,6 @@ parameters: partdb.table.default_page_size: '%env(int:TABLE_DEFAULT_PAGE_SIZE)%' # The default number of entries shown per page in tables partdb.table.parts.default_columns: '%env(trim:string:TABLE_PARTS_DEFAULT_COLUMNS)%' # The default columns in part tables and their order - ###################################################################################################################### - # Sidebar - ###################################################################################################################### - # You can configures the default shown tree items in the sidebar here. You can add or remove entries here, to change the number of trees in the sidebar. The possible entries are: categories, locations, footprints, manufacturers, suppliers, devices, tools - partdb.sidebar.items: - - categories - - devices - - tools - partdb.sidebar.root_expanded: true # If this is set to true, the root node of the sidebar is expanded by default - partdb.sidebar.root_node_enable: true # Put all entities below a root node in the sidebar - ###################################################################################################################### # Miscellaneous ###################################################################################################################### diff --git a/config/services.yaml b/config/services.yaml index 88df6b71..e43f2485 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -195,14 +195,6 @@ services: $fontDirectory: '%kernel.project_dir%/var/dompdf/fonts/' $tmpDirectory: '%kernel.project_dir%/var/dompdf/tmp/' - #################################################################################################################### - # Trees - #################################################################################################################### - App\Services\Trees\TreeViewGenerator: - arguments: - $rootNodeExpandedByDefault: '%partdb.sidebar.root_expanded%' - $rootNodeEnabled: '%partdb.sidebar.root_node_enable%' - #################################################################################################################### # Part info provider system #################################################################################################################### diff --git a/src/Controller/SettingsController.php b/src/Controller/SettingsController.php index b48b66ff..3b0eb15c 100644 --- a/src/Controller/SettingsController.php +++ b/src/Controller/SettingsController.php @@ -30,6 +30,7 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Contracts\Cache\TagAwareCacheInterface; class SettingsController extends AbstractController { @@ -37,7 +38,7 @@ class SettingsController extends AbstractController {} #[Route("/settings", name: "system_settings")] - public function systemSettings(Request $request): Response + public function systemSettings(Request $request, TagAwareCacheInterface $cache): Response { //Create a clone of the settings object $settings = $this->settingsManager->createTemporaryCopy(AppSettings::class); @@ -56,8 +57,14 @@ class SettingsController extends AbstractController if ($form->isSubmitted() && $form->isValid()) { $this->settingsManager->mergeTemporaryCopy($settings); $this->settingsManager->save($settings); + + //It might be possible, that the tree settings have changed, so clear the cache + $cache->invalidateTags(['tree_treeview', 'sidebar_tree_update']); } + + + //Render the form return $this->render('settings/settings.html.twig', [ 'form' => $form diff --git a/src/Form/SelectTypeOrderExtension.php b/src/Form/SelectTypeOrderExtension.php new file mode 100644 index 00000000..bc7ca82f --- /dev/null +++ b/src/Form/SelectTypeOrderExtension.php @@ -0,0 +1,60 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Form; + +use Symfony\Component\Form\AbstractTypeExtension; +use Symfony\Component\Form\Extension\Core\Type\ChoiceType; +use Symfony\Component\Form\Extension\Core\Type\EnumType; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormView; +use Symfony\Component\OptionsResolver\Options; +use Symfony\Component\OptionsResolver\OptionsResolver; + +class SelectTypeOrderExtension extends AbstractTypeExtension +{ + public static function getExtendedTypes(): iterable + { + return [ + ChoiceType::class, + EnumType::class + ]; + } + + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefault('ordered', false); + $resolver->setDefault('by_reference', function (Options $options) { + //Disable by_reference if the field is ordered (otherwise the order will be lost) + return !$options['ordered']; + }); + } + + public function buildView(FormView $view, FormInterface $form, array $options) + { + //Pass the data in ordered form to the frontend controller, so it can make the items appear in the correct order. + if ($options['ordered']) { + $view->vars['attr']['data-ordered-value'] = json_encode($form->getViewData(), JSON_THROW_ON_ERROR); + } + } +} \ No newline at end of file diff --git a/src/Services/Trees/TreeViewGenerator.php b/src/Services/Trees/TreeViewGenerator.php index 81b64840..3779fdc3 100644 --- a/src/Services/Trees/TreeViewGenerator.php +++ b/src/Services/Trees/TreeViewGenerator.php @@ -37,6 +37,7 @@ use App\Repository\StructuralDBElementRepository; use App\Services\Cache\ElementCacheTagGenerator; use App\Services\Cache\UserCacheKeyGenerator; use App\Services\EntityURLGenerator; +use App\Settings\BehaviorSettings\SidebarSettings; use Doctrine\ORM\EntityManagerInterface; use InvalidArgumentException; use RecursiveIteratorIterator; @@ -52,6 +53,10 @@ use function count; */ class TreeViewGenerator { + + private readonly bool $rootNodeExpandedByDefault; + private readonly bool $rootNodeEnabled; + public function __construct( protected EntityURLGenerator $urlGenerator, protected EntityManagerInterface $em, @@ -60,10 +65,10 @@ class TreeViewGenerator protected UserCacheKeyGenerator $keyGenerator, protected TranslatorInterface $translator, private readonly UrlGeneratorInterface $router, - protected bool $rootNodeExpandedByDefault, - protected bool $rootNodeEnabled, - + private readonly SidebarSettings $sidebarSettings, ) { + $this->rootNodeEnabled = $this->sidebarSettings->rootNodeEnabled; + $this->rootNodeExpandedByDefault = $this->sidebarSettings->rootNodeExpanded; } /** diff --git a/src/Settings/AppSettings.php b/src/Settings/AppSettings.php index 1522ad11..1695638a 100644 --- a/src/Settings/AppSettings.php +++ b/src/Settings/AppSettings.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace App\Settings; +use App\Settings\BehaviorSettings\BehaviorSettings; use App\Settings\InfoProviderSystem\InfoProviderSettings; use App\Settings\MiscSettings\MiscSettings; use App\Settings\SystemSettings\AttachmentsSettings; @@ -31,6 +32,7 @@ use Jbtronics\SettingsBundle\Settings\Settings; use Jbtronics\SettingsBundle\Settings\SettingsTrait; #[Settings] +#[SettingsIcon('folder-tree')] class AppSettings { use SettingsTrait; @@ -39,6 +41,9 @@ class AppSettings #[EmbeddedSettings()] public ?SystemSettings $system = null; + #[EmbeddedSettings()] + public ?BehaviorSettings $behavior = null; + #[EmbeddedSettings()] public ?InfoProviderSettings $infoProviders = null; diff --git a/src/Settings/BehaviorSettings/BehaviorSettings.php b/src/Settings/BehaviorSettings/BehaviorSettings.php new file mode 100644 index 00000000..9f611eab --- /dev/null +++ b/src/Settings/BehaviorSettings/BehaviorSettings.php @@ -0,0 +1,37 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Settings\BehaviorSettings; + +use Jbtronics\SettingsBundle\Settings\EmbeddedSettings; +use Jbtronics\SettingsBundle\Settings\Settings; +use Jbtronics\SettingsBundle\Settings\SettingsTrait; + +#[Settings] +class BehaviorSettings +{ + use SettingsTrait; + + #[EmbeddedSettings] + public ?SidebarSettings $sidebar = null; +} \ No newline at end of file diff --git a/src/Settings/BehaviorSettings/SidebarItems.php b/src/Settings/BehaviorSettings/SidebarItems.php new file mode 100644 index 00000000..cb0e60be --- /dev/null +++ b/src/Settings/BehaviorSettings/SidebarItems.php @@ -0,0 +1,53 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Settings\BehaviorSettings; + +use Symfony\Contracts\Translation\TranslatableInterface; +use Symfony\Contracts\Translation\TranslatorInterface; + +enum SidebarItems: string implements TranslatableInterface +{ + case TOOLS = "tools"; + case CATEGORIES = "categories"; + case LOCATIONS = "locations"; + case FOOTPRINTS = "footprints"; + case MANUFACTURERS = "manufacturers"; + case SUPPLIERS = "suppliers"; + case PROJECTS = "projects"; + + public function trans(TranslatorInterface $translator, ?string $locale = null): string + { + $key = match($this) { + self::TOOLS => 'tools.label', + self::CATEGORIES => 'category.labelp', + self::LOCATIONS => 'storelocation.labelp', + self::FOOTPRINTS => 'footprint.labelp', + self::MANUFACTURERS => 'manufacturer.labelp', + self::SUPPLIERS => 'supplier.labelp', + self::PROJECTS => 'project.labelp', + }; + + return $translator->trans($key, locale: $locale); + } +} \ No newline at end of file diff --git a/src/Settings/BehaviorSettings/SidebarSettings.php b/src/Settings/BehaviorSettings/SidebarSettings.php new file mode 100644 index 00000000..cba99b4f --- /dev/null +++ b/src/Settings/BehaviorSettings/SidebarSettings.php @@ -0,0 +1,69 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Settings\BehaviorSettings; + +use App\Settings\SettingsIcon; +use Jbtronics\SettingsBundle\ParameterTypes\ArrayType; +use Jbtronics\SettingsBundle\ParameterTypes\EnumType; +use Jbtronics\SettingsBundle\Settings\Settings; +use Jbtronics\SettingsBundle\Settings\SettingsParameter; +use Jbtronics\SettingsBundle\Settings\SettingsTrait; +use Symfony\Component\Translation\TranslatableMessage as TM; +use Symfony\Component\Validator\Constraints as Assert; + +#[Settings(label: new TM("settings.behavior.sidebar"))] +#[SettingsIcon('fa-border-top-left')] +class SidebarSettings +{ + use SettingsTrait; + + + /** + * @var SidebarItems[] The items to show in the sidebar. + */ + #[SettingsParameter(ArrayType::class, + label: new TM("settings.behavior.sidebar.items"), + description: new TM("settings.behavior.sidebar.items.help"), + options: ['type' => EnumType::class, 'options' => ['class' => SidebarItems::class]], + formType: \Symfony\Component\Form\Extension\Core\Type\EnumType::class, + formOptions: ['class' => SidebarItems::class, 'multiple' => true, 'ordered' => true] + )] + #[Assert\NotBlank()] + public array $items = [SidebarItems::CATEGORIES, SidebarItems::PROJECTS, SidebarItems::TOOLS]; + + /** + * @var bool Whether categories, etc. should be grouped under a root node or put directly into the sidebar trees. + */ + #[SettingsParameter( + label: new TM("settings.behavior.sidebar.rootNodeEnabled"), + description: new TM("settings.behavior.sidebar.rootNodeEnabled.help") + )] + public bool $rootNodeEnabled = true; + + /** + * @var bool Whether the root node should be expanded by default, or not. + */ + #[SettingsParameter(label: new TM("settings.behavior.sidebar.rootNodeExpanded"))] + public bool $rootNodeExpanded = true; +} \ No newline at end of file diff --git a/templates/_sidebar.html.twig b/templates/_sidebar.html.twig index 99f035d7..697000cc 100644 --- a/templates/_sidebar.html.twig +++ b/templates/_sidebar.html.twig @@ -2,8 +2,8 @@ \ No newline at end of file diff --git a/templates/components/tree_macros.html.twig b/templates/components/tree_macros.html.twig index 12bef78f..366d42fe 100644 --- a/templates/components/tree_macros.html.twig +++ b/templates/components/tree_macros.html.twig @@ -6,7 +6,7 @@ ['footprints', path('tree_footprint_root'), 'footprint.labelp', is_granted('@footprints.read') and is_granted('@parts.read')], ['manufacturers', path('tree_manufacturer_root'), 'manufacturer.labelp', is_granted('@manufacturers.read') and is_granted('@parts.read')], ['suppliers', path('tree_supplier_root'), 'supplier.labelp', is_granted('@suppliers.read') and is_granted('@parts.read')], - ['devices', path('tree_device_root'), 'project.labelp', is_granted('@projects.read')], + ['projects', path('tree_device_root'), 'project.labelp', is_granted('@projects.read')], ['tools', path('tree_tools'), 'tools.label', true], ] %} diff --git a/translations/messages.en.xlf b/translations/messages.en.xlf index 8bbe1eb3..a5a4bdfd 100644 --- a/translations/messages.en.xlf +++ b/translations/messages.en.xlf @@ -12612,5 +12612,41 @@ Please note, that you can not impersonate a disabled user. If you try you will g 0 to show more levels. Set to -1, to show all parts of Part-DB inside a sigle cnategory in KiCad.]]> + + + settings.behavior.sidebar + Sidebar + + + + + settings.behavior.sidebar.items + Sidebar items + + + + + settings.behavior.sidebar.items.help + + + + + + settings.behavior.sidebar.rootNodeEnabled + Use root node + + + + + settings.behavior.sidebar.rootNodeEnabled.help + If this is enabled, all top-level categories, footprints, etc. will be put under a single root node. If disabled, the top-level categories will be put directly into the menu. + + + + + settings.behavior.sidebar.rootNodeExpanded + Expand root node by default + +