From c9ac56397c325274b190ced0bd4ed69c18335888 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Malling?= Date: Wed, 29 May 2024 13:48:12 +0200 Subject: [PATCH 1/3] TASK: Set supported TYPO3 version --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 05142f9..2583168 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,7 @@ "GPL-2.0-or-later" ], "require": { - "typo3/cms-backend": "^11.5" + "typo3/cms-backend": "^12.4" }, "autoload": { "psr-4": { From da18f0fd641fd5cefecb45153e912be09f9fbaa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Malling?= Date: Wed, 29 May 2024 13:48:47 +0200 Subject: [PATCH 2/3] TASK: Set supported TYPO3 version --- ext_emconf.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext_emconf.php b/ext_emconf.php index 38cfb37..74e21e1 100644 --- a/ext_emconf.php +++ b/ext_emconf.php @@ -11,7 +11,7 @@ 'version' => '1.0.0', 'constraints' => [ 'depends' => [ - 'typo3' => '11.5.0-11.5.99', + 'typo3' => '12.4.0-12.4.99', ], 'conflicts' => [], 'suggests' => [], From 94526d1f2a95fbd0c704732a86e754c868052c6c Mon Sep 17 00:00:00 2001 From: Soren Malling Date: Thu, 30 May 2024 09:00:58 +0200 Subject: [PATCH 3/3] FEATURE: Collapsible content elements and columns --- .../ModifyPageLayoutContentEventListener.php | 16 ++++ Classes/PageModuleModifier.php | 77 ------------------ .../Collapse/CollapsibleViewHelper.php | 26 +++++++ .../ViewHelpers/Collapse/ColumnViewHelper.php | 54 +++++++++++++ .../Collapse/ContentViewHelper.php | 78 +++++++++++++++++++ Configuration/JavaScriptModules.php | 8 ++ Configuration/Services.yaml | 5 ++ Configuration/page.tsconfig | 3 + .../PageLayout/Grid/ColumnHeader.html | 44 +++++++++++ .../PageLayout/RecordDefault/Header.html | 57 ++++++++++++++ Resources/Public/Css/pagemodule.css | 12 +-- .../Public/JavaScript/PageModuleCollapse.js | 54 ++++++------- ext_localconf.php | 6 -- 13 files changed, 324 insertions(+), 116 deletions(-) create mode 100644 Classes/EventListener/ModifyPageLayoutContentEventListener.php delete mode 100644 Classes/PageModuleModifier.php create mode 100644 Classes/ViewHelpers/Collapse/CollapsibleViewHelper.php create mode 100644 Classes/ViewHelpers/Collapse/ColumnViewHelper.php create mode 100644 Classes/ViewHelpers/Collapse/ContentViewHelper.php create mode 100644 Configuration/JavaScriptModules.php create mode 100644 Configuration/page.tsconfig create mode 100644 Resources/Private/TemplateOverrides/Partials/PageLayout/Grid/ColumnHeader.html create mode 100644 Resources/Private/TemplateOverrides/Partials/PageLayout/RecordDefault/Header.html delete mode 100644 ext_localconf.php diff --git a/Classes/EventListener/ModifyPageLayoutContentEventListener.php b/Classes/EventListener/ModifyPageLayoutContentEventListener.php new file mode 100644 index 0000000..a9147fa --- /dev/null +++ b/Classes/EventListener/ModifyPageLayoutContentEventListener.php @@ -0,0 +1,16 @@ +loadRequireJsModule('TYPO3/CMS/Collapse/PageModuleCollapse'); + } +} \ No newline at end of file diff --git a/Classes/PageModuleModifier.php b/Classes/PageModuleModifier.php deleted file mode 100644 index f054348..0000000 --- a/Classes/PageModuleModifier.php +++ /dev/null @@ -1,77 +0,0 @@ -pageRenderer = $pageRenderer; - $this->iconFactory = $iconFactory; - } - - public function addCollapseButton(array $parameters, $parentObject): string - { - if ($parentObject instanceof PageLayoutView || $parentObject instanceof GridColumnItem) { - $contentElementId = (int)$parameters[1]; - $row = $parameters[2]; - $recordTitle = BackendUtility::getRecordTitle('tt_content', $row); - $typeLabel = $this->getTypeLabel($row); - $isCollapsed = in_array($contentElementId, $this->getCollapsedItems(), true); - return ''; - } - return ''; - } - - public function addJavaScript(): string - { - $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Collapse/PageModuleCollapse'); - return ''; - } - - public function getCollapsedItems(): array - { - $result = $GLOBALS['BE_USER']->uc['B13']['Collapse'] ?? ''; - $collapsedItems = GeneralUtility::intExplode(',', $result); - return array_filter($collapsedItems); - } - - protected function getTypeLabel(array $row): string - { - $typeValue = BackendUtility::getTCAtypeValue('tt_content', $row); - $label = ''; - foreach ($GLOBALS['TCA']['tt_content']['columns']['CType']['config']['items'] as $itm) { - if ($itm[1] == $typeValue) { - $label = $itm[0]; - break; - } - } - if ($label !== '') { - return $GLOBALS['LANG']->sL($label); - } - return ''; - } -} diff --git a/Classes/ViewHelpers/Collapse/CollapsibleViewHelper.php b/Classes/ViewHelpers/Collapse/CollapsibleViewHelper.php new file mode 100644 index 0000000..db18508 --- /dev/null +++ b/Classes/ViewHelpers/Collapse/CollapsibleViewHelper.php @@ -0,0 +1,26 @@ +uc['B13']['Collapse'] ?? ''; + $collapsedItems = GeneralUtility::trimExplode(',', $result); + + return array_filter($collapsedItems); + } +} diff --git a/Classes/ViewHelpers/Collapse/ColumnViewHelper.php b/Classes/ViewHelpers/Collapse/ColumnViewHelper.php new file mode 100644 index 0000000..4f80c81 --- /dev/null +++ b/Classes/ViewHelpers/Collapse/ColumnViewHelper.php @@ -0,0 +1,54 @@ +registerArgument('page', 'array', 'Page record array', true); + $this->registerArgument('column', GridColumn::class, '', true); + } + + public function render(): string + { + $page = $this->arguments['page']; + /** @var GridColumn $column */ + $column = $this->arguments['column']; + + $iconFactory = GeneralUtility::makeInstance(IconFactory::class); + + if ($column instanceof GridColumn) { + $identifier = sprintf('column_%s_%s', $column->getColumnNumber(), $page['uid']); + $isCollapsed = in_array($identifier, $this->getCollapsedItems(), true); + + return ''; + } + + return ''; + } + +} diff --git a/Classes/ViewHelpers/Collapse/ContentViewHelper.php b/Classes/ViewHelpers/Collapse/ContentViewHelper.php new file mode 100644 index 0000000..c8119ae --- /dev/null +++ b/Classes/ViewHelpers/Collapse/ContentViewHelper.php @@ -0,0 +1,78 @@ +registerArgument('row', 'array', 'Content record array', true); + $this->registerArgument('type', AbstractGridObject::class, '', true); + } + + public function render(): string + { + $type = $this->arguments['type']; + $row = $this->arguments['row']; + $contentElementId = $row['uid']; + $identifier = 'tt_content_' . $contentElementId; + + $iconFactory = GeneralUtility::makeInstance(IconFactory::class); + + if ($type instanceof GridColumnItem && !$type instanceof ContainerGridColumnItem) { + $recordTitle = BackendUtility::getRecordTitle('tt_content', $row); + $typeLabel = $this->getTypeLabel($row); + + $isCollapsed = in_array($identifier, $this->getCollapsedItems(), true); + + return ''; + } + + return ''; + } + + public function getCollapsedItems(): array + { + $collapsedItems = parent::getCollapsedItems(); + return array_map(fn(int|string $item) => str_starts_with((string) $item, 'tt_content_') ? (string) $item : 'tt_content_' . $item, $collapsedItems); + } + + protected function getTypeLabel(array $row): string + { + $typeValue = BackendUtility::getTCAtypeValue('tt_content', $row); + $label = ''; + foreach ($GLOBALS['TCA']['tt_content']['columns']['CType']['config']['items'] as $itm) { + if ($itm['value'] == $typeValue) { + $label = $itm['label']; + break; + } + } + if ($label !== '') { + return $GLOBALS['LANG']->sL($label); + } + + return ''; + } +} diff --git a/Configuration/JavaScriptModules.php b/Configuration/JavaScriptModules.php new file mode 100644 index 0000000..10958cc --- /dev/null +++ b/Configuration/JavaScriptModules.php @@ -0,0 +1,8 @@ + ['backend'], + 'imports' => [ + 'b13/collapse' => 'EXT:collapse/Resources/Public/JavaScript/', + ], +]; \ No newline at end of file diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml index ae726ff..148f409 100644 --- a/Configuration/Services.yaml +++ b/Configuration/Services.yaml @@ -9,3 +9,8 @@ services: B13\Collapse\PageModuleModifier: public: true + + B13\Collapse\EventListener\ModifyPageLayoutContentEventListener: + tags: + - name: event.listener + identifier: 'paste-reference/backend/modify-page-layout-content' \ No newline at end of file diff --git a/Configuration/page.tsconfig b/Configuration/page.tsconfig new file mode 100644 index 0000000..3d7dcad --- /dev/null +++ b/Configuration/page.tsconfig @@ -0,0 +1,3 @@ +templates.typo3/cms-backend { + 1691683586 = b13/collapse:Resources/Private/TemplateOverrides +} \ No newline at end of file diff --git a/Resources/Private/TemplateOverrides/Partials/PageLayout/Grid/ColumnHeader.html b/Resources/Private/TemplateOverrides/Partials/PageLayout/Grid/ColumnHeader.html new file mode 100644 index 0000000..44f5a57 --- /dev/null +++ b/Resources/Private/TemplateOverrides/Partials/PageLayout/Grid/ColumnHeader.html @@ -0,0 +1,44 @@ + + +
+ + +
+ + + + + + + +
+ {column.title} +
+ + + + + {column.titleUnassigned} + + + {column.titleInaccessible} + +
+
+{column.beforeSectionMarkup} + +
+
+ + + {newContentTitleShort} + +
+
+
+
diff --git a/Resources/Private/TemplateOverrides/Partials/PageLayout/RecordDefault/Header.html b/Resources/Private/TemplateOverrides/Partials/PageLayout/RecordDefault/Header.html new file mode 100644 index 0000000..4ed25b4 --- /dev/null +++ b/Resources/Private/TemplateOverrides/Partials/PageLayout/RecordDefault/Header.html @@ -0,0 +1,57 @@ + + +
+
+ {item.icons -> f:format.raw()} + + + + + +
+
+ {item.contentTypeLabel} +
+
+ +
+
+ + + + + + + + + + + + + + +
+ +
+
+
+
diff --git a/Resources/Public/Css/pagemodule.css b/Resources/Public/Css/pagemodule.css index 8300a98..cb5264f 100644 --- a/Resources/Public/Css/pagemodule.css +++ b/Resources/Public/Css/pagemodule.css @@ -1,10 +1,10 @@ -.t3-page-ce-header-icons-left button[data-b13-collapse] { +.t3-page-ce-header-right button[data-b13-collapse][aria-expanded=false] > span:first-child, +.t3-page-column-header button[data-b13-collapse][aria-expanded=false] > span:first-child +{ display: none; } - -.t3-page-ce-header-icons-right button[data-b13-collapse][aria-expanded=false] > span:first-child { - display: none; -} -.t3-page-ce-header-icons-right button[data-b13-collapse][aria-expanded=true] > span:last-child { +.t3-page-ce-header-right button[data-b13-collapse][aria-expanded=true] > span:last-child, +.t3-page-column-header button[data-b13-collapse][aria-expanded=true] > span:last-child +{ display: none; } diff --git a/Resources/Public/JavaScript/PageModuleCollapse.js b/Resources/Public/JavaScript/PageModuleCollapse.js index d434cbb..d4c0c04 100644 --- a/Resources/Public/JavaScript/PageModuleCollapse.js +++ b/Resources/Public/JavaScript/PageModuleCollapse.js @@ -6,42 +6,42 @@ define([ 'TYPO3/CMS/Backend/Storage/Persistent', ], function (DocumentService, PersistentStorage) { let selectors = { - button: 'button[data-b13-collapse]', + contentButton: '.t3-page-ce-header button[data-b13-collapse]', toolbarContainer: '.t3-page-ce-header', - rightToolbarContainer: '.t3-page-ce-header-icons-right > .btn-toolbar' + rightToolbarContainer: '.t3-page-ce-header-right > .btn-toolbar .btn-group', + columnButton: '.t3-page-column-header button[data-b13-collapse]' + }; + + let initialize = (btn) => { + // Update the correct status as we are unable to modify the templates + document.querySelector(btn.dataset.bsTarget).classList.add('collapse'); + + if (btn.ariaExpanded == 'true') { + document.querySelector(btn.dataset.bsTarget).classList.add('show'); + } + // Add event handles to update BE_USERs->uc when collapse/show is used + // The CE is expanded again + document.querySelector(btn.dataset.bsTarget).addEventListener('show.bs.collapse', () => { + PersistentStorage.removeFromList('B13.Collapse', btn.dataset.b13Collapse); + }); + // The CE is about to be collapsed + document.querySelector(btn.dataset.bsTarget).addEventListener('hide.bs.collapse', () => { + PersistentStorage.addToList('B13.Collapse', btn.dataset.b13Collapse); + }); }; DocumentService.ready().then(() => { - document.querySelectorAll(selectors.button).forEach((btn) => { - const substituteContent = JSON.parse(btn.dataset.b13Title); - // move each element to the right spot first. + document.querySelectorAll(selectors.contentButton).forEach((btn) => { let toolbar = btn.closest(selectors.toolbarContainer); btn.remove(); toolbar.querySelector(selectors.rightToolbarContainer).append(btn); - const substituteNode = document.createElement('div'); - substituteNode.innerHTML = '' + substituteContent['title'] + '' + ' ' + substituteContent['type']; - substituteNode.classList.add('p-2'); - // Update the correct status as we are unable to modify the templates - document.querySelector(btn.dataset.bsTarget).classList.add('collapse'); - if (btn.ariaExpanded == 'true') { - document.querySelector(btn.dataset.bsTarget).classList.add('show'); - substituteNode.classList.add('d-none'); - } - // Add event handles to update BE_USERs->uc when collapse/show is used - // The CE is expanded again - document.querySelector(btn.dataset.bsTarget).addEventListener('show.bs.collapse', () => { - PersistentStorage.removeFromList('B13.Collapse', btn.dataset.b13Collapse); - substituteNode.classList.add('d-none'); - }); - // The CE is about to be collapsed - document.querySelector(btn.dataset.bsTarget).addEventListener('hide.bs.collapse', () => { - PersistentStorage.addToList('B13.Collapse', btn.dataset.b13Collapse); - substituteNode.classList.remove('d-none'); - }); + initialize(btn); + }); + - // Add the substitute content - document.querySelector(btn.dataset.bsTarget).parentNode.prepend(substituteNode); + document.querySelectorAll(selectors.columnButton).forEach((btn) => { + initialize(btn); }); }); }); diff --git a/ext_localconf.php b/ext_localconf.php deleted file mode 100644 index fe3b597..0000000 --- a/ext_localconf.php +++ /dev/null @@ -1,6 +0,0 @@ -addJavaScript'; -$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['GLOBAL']['recStatInfoHooks']['collapse'] = \B13\Collapse\PageModuleModifier::class . '->addCollapseButton';