Skip to content

Commit 8056ef6

Browse files
committed
Add new elements and extend settings
1 parent ac0a2bb commit 8056ef6

14 files changed

Lines changed: 299 additions & 3 deletions

File tree

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
<?php
2+
declare(strict_types=1);
3+
namespace SIMONKOEHLER\ContentBlocksBootstrap\Preview;
4+
5+
use TYPO3\CMS\Backend\Routing\UriBuilder;
6+
use TYPO3\CMS\Backend\Utility\BackendUtility;
7+
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
8+
use \TYPO3\CMS\Backend\Preview\PreviewRendererInterface;
9+
use TYPO3\CMS\Backend\View\BackendLayout\Grid\GridColumnItem;
10+
use TYPO3\CMS\Core\Domain\Record;
11+
use TYPO3\CMS\Core\Domain\RecordFactory;
12+
use TYPO3\CMS\Core\Localization\LanguageService;
13+
use TYPO3\CMS\Core\Utility\GeneralUtility;
14+
use TYPO3\CMS\Core\Utility\DebugUtility;
15+
use TYPO3\CMS\Core\Type\Bitmask\Permission;
16+
use TYPO3\CMS\Core\Domain\Repository\PageRepository;
17+
use TYPO3\CMS\Core\Resource\FileRepository;
18+
use TYPO3\CMS\Extbase\Service\ImageService;
19+
20+
class CardsOfSubpages implements PreviewRendererInterface{
21+
22+
public function renderPageModulePreviewHeader(GridColumnItem $item): string
23+
{
24+
$record = $item->getRecord();
25+
// Render a custom header for the content element
26+
return '<strong>' . htmlspecialchars($item->getRecord()['header']) . '</strong>';
27+
}
28+
29+
public function renderPageModulePreviewContent(GridColumnItem $item): string
30+
{
31+
$pageRepository = GeneralUtility::makeInstance(PageRepository::class);
32+
$fileRepository = GeneralUtility::makeInstance(FileRepository::class);
33+
$imageService = GeneralUtility::makeInstance(ImageService::class);
34+
35+
$languageService = $this->getLanguageService();
36+
$table = $item->getTable();
37+
$record = $item->getRecord();
38+
$recordObj = GeneralUtility::makeInstance(RecordFactory::class)->createResolvedRecordFromDatabaseRow($table, $record);
39+
$recordType = $recordObj->getRecordType();
40+
$output = '';
41+
$output .= $this->linkEditContent($this->generateListForMenuContentTypes($record, $recordType), $record);
42+
$output .= '<div class="row">';
43+
44+
$subpages = $pageRepository->getMenu(explode(',', $record['pages'])[0]);
45+
46+
foreach ($subpages as $page) {
47+
$media = $fileRepository->findByRelation('pages', 'media', $page['uid']);
48+
$output .= '<div class="col"><div class="card">';
49+
//DebugUtility::debug($media, 'recordObj');
50+
if($media){
51+
foreach ($media as $fileReference) {
52+
$publicUrl = $fileReference->getPublicUrl();
53+
try {
54+
// Create a processed image with a width of 300px
55+
$processedImage = $imageService->applyProcessingInstructions(
56+
$fileReference,
57+
['width' => '200'] // 300 pixels, cropped
58+
);
59+
$thumbnailUrl = $imageService->getImageUri($processedImage);
60+
61+
$output .= '<img class="card-img-top" src="' . htmlspecialchars($thumbnailUrl) . '" alt="">';
62+
} catch (\Exception $e) {
63+
// Handle broken file reference or processing error
64+
echo 'Error loading thumbnail: ' . $e->getMessage();
65+
}
66+
}
67+
}
68+
$output .= '<div class="card-body">';
69+
$output .= '<h6 class="card-title fw-bold">' . $page['title'] . '</h6>';
70+
$output .= '<div class="card-text">' . $page['abstract'] . '</div>';
71+
$output .= '</div>';
72+
$output .= '</div></div>';
73+
}
74+
75+
$output .= '</div>';
76+
77+
//DebugUtility::debug($subpages, 'recordObj');
78+
return $output;
79+
}
80+
81+
public function renderPageModulePreviewFooter(GridColumnItem $item): string
82+
{
83+
// Optional footer content
84+
return '';
85+
}
86+
87+
public function wrapPageModulePreview(string $previewHeader, string $previewContent, GridColumnItem $item): string
88+
{
89+
// Combine header, content, and footer into a full preview box
90+
return sprintf(
91+
'<div class="element-preview">
92+
<div class="element-preview-header mb-2">%s</div>
93+
<div class="element-preview-content">%s</div>
94+
</div>',
95+
$previewHeader,
96+
$previewContent
97+
);
98+
}
99+
100+
/**
101+
* Generates a list of selected pages or categories for the menu content types
102+
*
103+
* @param array $record row from pages
104+
*/
105+
protected function generateListForMenuContentTypes(array $record, string $contentType): string
106+
{
107+
$table = 'pages';
108+
$field = 'pages';
109+
110+
if (trim($record[$field] ?? '') === '') {
111+
return '';
112+
}
113+
$content = '';
114+
$uidList = explode(',', $record[$field]);
115+
foreach ($uidList as $uid) {
116+
$uid = (int)$uid;
117+
$pageRecord = BackendUtility::getRecord($table, $uid, 'title');
118+
if ($pageRecord) {
119+
$content .= '<li class="list-group-item">' . htmlspecialchars($pageRecord['title']) . ' <span class="text-body-secondary">[' . $uid . ']</span></li>';
120+
}
121+
}
122+
return $content ? '<ul class="list-group mb-3">' . $content . '</ul>' : '';
123+
}
124+
125+
protected function linkEditContent(string $linkText, array $row, string $table = 'tt_content'): string
126+
{
127+
if (empty($linkText)) {
128+
return $linkText;
129+
}
130+
131+
$backendUser = $this->getBackendUser();
132+
if ($backendUser->check('tables_modify', $table)
133+
&& $backendUser->recordEditAccessInternals($table, $row)
134+
&& (new Permission($backendUser->calcPerms(BackendUtility::getRecord('pages', $row['pid']) ?? [])))->editContentPermissionIsGranted()
135+
) {
136+
$urlParameters = [
137+
'edit' => [
138+
$table => [
139+
$row['uid'] => 'edit',
140+
],
141+
],
142+
'returnUrl' => $GLOBALS['TYPO3_REQUEST']->getAttribute('normalizedParams')->getRequestUri() . '#element-' . $table . '-' . $row['uid'],
143+
];
144+
$uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
145+
$url = (string)$uriBuilder->buildUriFromRoute('record_edit', $urlParameters);
146+
return '<a href="' . htmlspecialchars($url) . '" title="' . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_layout.xlf:edit')) . '">' . $linkText . '</a>';
147+
}
148+
return $linkText;
149+
}
150+
151+
protected function getBackendUser(): BackendUserAuthentication
152+
{
153+
return $GLOBALS['BE_USER'];
154+
}
155+
156+
protected function getLanguageService(): LanguageService
157+
{
158+
return $GLOBALS['LANG'];
159+
}
160+
161+
}

Configuration/TCA/Overrides/tt_content.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,5 @@
1010
'Bootstrap Components',
1111
'before:default',
1212
);
13+
14+
$GLOBALS['TCA']['tt_content']['types']['bootstrap_cards_subpages']['previewRenderer'] = SIMONKOEHLER\ContentBlocksBootstrap\Preview\CardsOfSubpages::class;

Configuration/page.tsconfig

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
TCEFORM {
22
tt_content{
33

4+
layout{
5+
types{
6+
bootstrap_menu_subpages{
7+
removeItems = 1,2,3
8+
addItems.10 = Base Navigation Centered
9+
addItems.15 = Base Navigation Right
10+
addItems.20 = Base Navigation Vertical
11+
}
12+
}
13+
}
14+
415
header_layout {
516
types{
617
bootstrap_accordion{
@@ -15,6 +26,7 @@ TCEFORM {
1526
bootstrap_jumbotron < .bootstrap_accordion
1627
bootstrap_text < .bootstrap_accordion
1728
bootstrap_images < .bootstrap_accordion
29+
bootstrap_menu_subpages < .bootstrap_accordion
1830
}
1931
}
2032

@@ -31,6 +43,7 @@ TCEFORM {
3143
bootstrap_jumbotron < .bootstrap_accordion
3244
bootstrap_text < .bootstrap_accordion
3345
bootstrap_images < .bootstrap_accordion
46+
bootstrap_menu_subpages < .bootstrap_accordion
3447
}
3548
}
3649

@@ -49,6 +62,7 @@ TCEFORM {
4962
bootstrap_jumbotron < .bootstrap_accordion
5063
bootstrap_text < .bootstrap_accordion
5164
bootstrap_images < .bootstrap_accordion
65+
bootstrap_menu_subpages < .bootstrap_accordion
5266
}
5367
}
5468
space_after_class {
@@ -66,6 +80,7 @@ TCEFORM {
6680
bootstrap_jumbotron < .bootstrap_accordion
6781
bootstrap_text < .bootstrap_accordion
6882
bootstrap_images < .bootstrap_accordion
83+
bootstrap_menu_subpages < .bootstrap_accordion
6984
}
7085
}
7186

Configuration/setup.typoscript

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,23 @@ lib.contentBlock {
33
100 = EXT:content_blocks_bootstrap/Resources/Private/Partials/ContentElements/
44
}
55
}
6+
7+
tt_content.bootstrap_menu_subpages =< lib.contentElement
8+
tt_content.bootstrap_menu_subpages {
9+
#templateName = MenuSubpages
10+
dataProcessing {
11+
10 = menu
12+
10 {
13+
special = directory
14+
special.value.field = pages
15+
dataProcessing {
16+
10 = files
17+
10 {
18+
references.fieldName = media
19+
}
20+
}
21+
}
22+
}
23+
}
24+
25+
tt_content.bootstrap_cards_subpages < tt_content.bootstrap_menu_subpages
Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: sk/bootstrap-cards-subpages
2+
title: LLL:EXT:content_blocks_bootstrap/Resources/Private/Language/locallang.xlf:bootstrap_cards_subpages.title
3+
typeName: bootstrap_cards_subpages
4+
description: LLL:EXT:content_blocks_bootstrap/Resources/Private/Language/locallang.xlf:bootstrap_cards_subpages.description
5+
group: content_blocks_bootstrap
6+
basics:
7+
- TYPO3/Appearance
8+
fields:
9+
- identifier: sk/bootstrap_header_palette
10+
type: Basic
11+
- identifier: pages
12+
useExistingField: true
13+
type: Relation
14+
allowed: 'pages'
15+
maxitems: 1
16+
suggestOptions:
17+
default:
18+
additionalSearchFields: 'nav_title, url'
19+
addWhere: 'AND pages.doktype = 1'
20+
- identifier: sk/bootstrap_columns
21+
type: Basic
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<ul class="list-group">
2+
<f:debug inline="true">{data}</f:debug>
3+
<f:for each="{data.pages}" as="page">
4+
<li class="list-group-item">
5+
<span class="text-body-secondary">
6+
<f:link.page pageUid="{page.uid}"/>
7+
</span>
8+
</li>
9+
</f:for>
10+
</ul>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<div id="c{data.uid}">
2+
<f:render partial="Header" arguments="{_all}"/>
3+
<f:if condition="{menu}">
4+
<f:render partial="card" arguments="{_all}"/>
5+
</f:if>
6+
</div>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<div class="row g-3 row-cols-{data.bootstrap_columns} row-cols-md-{data.bootstrap_columns_medium} row-cols-lg-{data.bootstrap_columns_large}">
2+
<f:for each="{menu}" as="page">
3+
<div class="col">
4+
<div class="card h-100">
5+
<f:if condition="{page.files.0}">
6+
<a href="{page.link}">
7+
<f:image image="{page.files.0}" class="img-fluid card-img-top"/>
8+
</a>
9+
</f:if>
10+
<div class="card-body">
11+
<div class="card-title fw-bold">
12+
{page.title}
13+
</div>
14+
<p class="card-text">{page.data.abstract}</p>
15+
<a class="btn btn-primary" href="{page.link}">{f:translate(extensionName:'content_blocks_bootstrap',key:'bootstrap_cards_subpages.more',default:'more')}</a>
16+
</div>
17+
</div>
18+
</div>
19+
</f:for>
20+
</div>

ContentBlocks/ContentElements/bootstrap-menu-subpages/config.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ group: content_blocks_bootstrap
66
basics:
77
- TYPO3/Appearance
88
fields:
9+
- identifier: sk/bootstrap_header_palette
10+
type: Basic
911
- identifier: pages
1012
useExistingField: true
1113
type: Relation

0 commit comments

Comments
 (0)