diff --git a/Classes/Frontend/RssFeed/BlogPostsDataProvider.php b/Classes/Frontend/RssFeed/BlogPostsDataProvider.php
new file mode 100644
index 0000000..84249d7
--- /dev/null
+++ b/Classes/Frontend/RssFeed/BlogPostsDataProvider.php
@@ -0,0 +1,148 @@
+
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+namespace DanielSiepmann\DsSite\Frontend\RssFeed;
+
+use Exception;
+use Psr\Http\Message\ServerRequestInterface;
+use TYPO3\CMS\Core\Database\ConnectionPool;
+use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
+use TYPO3\CMS\Frontend\ContentObject\DataProcessorInterface;
+use TYPO3\CMS\Frontend\DataProcessing\DatabaseQueryProcessor;
+
+final class BlogPostsDataProvider implements DataProcessorInterface
+{
+ public function __construct(
+ private readonly DatabaseQueryProcessor $databaseQueryProcessor,
+ private readonly ConnectionPool $connectionPool
+ ) {
+ }
+
+ public function process(
+ ContentObjectRenderer $contentObjectRenderer,
+ array $contentObjectConfiguration,
+ array $processorConfiguration,
+ array $processedData
+ ) {
+ $processedData = $this->databaseQueryProcessor->process(
+ $contentObjectRenderer,
+ $contentObjectConfiguration,
+ array_merge($processorConfiguration, [
+ 'where' => 'AND no_index = 0' . $this->getAdditionalWhere($contentObjectRenderer->getRequest()),
+ ]),
+ $processedData
+ );
+
+ foreach ($processedData['pages'] as &$page) {
+ $page['description'] = $page['data']['abstract']
+ . $this->getContent(
+ $contentObjectRenderer,
+ $page['data']['uid']
+ );
+ }
+
+ return $processedData;
+ }
+
+ private function getAdditionalWhere(ServerRequestInterface $request): string
+ {
+ $categoryUid = intval($request->getQueryParams()['category_uid'] ?? 0);
+ if ($categoryUid === 0) {
+ return '';
+ }
+
+ $pageUids = $this->getPageUidsWithRelationToCategory($categoryUid);
+ $where = $this->createAdditionalWhereForPageUids($pageUids);
+
+ if ($where !== '') {
+ return '';
+ }
+ return ' ' . $where;
+ }
+
+ private function getPageUidsWithRelationToCategory(int $categoryUid): array
+ {
+ $queryBuilder = $this->connectionPool->getQueryBuilderForTable('pages');
+
+ $queryBuilder->select('uid');
+ $queryBuilder->from('pages');
+ $queryBuilder->leftJoin(
+ 'pages',
+ 'sys_category_record_mm',
+ 'mm',
+ 'pages.uid = mm.uid_foreign'
+ );
+ $queryBuilder->where(
+ $queryBuilder->expr()->eq(
+ 'mm.tablenames',
+ $queryBuilder->createNamedParameter('pages')
+ ),
+ $queryBuilder->expr()->eq(
+ 'mm.fieldname',
+ $queryBuilder->createNamedParameter('categories')
+ ),
+ $queryBuilder->expr()->in(
+ 'mm.uid_local',
+ $queryBuilder->createNamedParameter($categoryUid)
+ )
+ );
+
+ return array_map(function (array $row) {
+ if (is_numeric($row['uid'])) {
+ return (int) $row['uid'];
+ }
+ throw new Exception('UID was not numeric: ' . var_export($row['uid'], true), 1707325559);
+ }, $queryBuilder->executeQuery()->fetchAllAssociative());
+ }
+
+ private function createAdditionalWhereForPageUids(array $pageUids): string
+ {
+ return ' AND uid IN(' . implode(',', $pageUids) . ')';
+ }
+
+ private function getContent(
+ ContentObjectRenderer $contentObjectRenderer,
+ int $pageUid
+ ): string {
+ $colPositions = [
+ 50,
+ 0,
+ 100,
+ 200,
+ ];
+
+ $content = '';
+ foreach ($colPositions as $colPos) {
+ $content .= $contentObjectRenderer->cObjGetSingle('CONTENT', [
+ 'table' => 'tt_content',
+ 'select.' => [
+ 'orderBy' => 'sorting',
+ 'where' => '{#colPos}=' . $colPos,
+ 'pidInList' => $pageUid,
+ ],
+ ]);
+ }
+
+ return $content;
+ }
+}
diff --git a/Classes/Frontend/RssFeed/ContentRendering.php b/Classes/Frontend/RssFeed/ContentRendering.php
deleted file mode 100644
index c07740e..0000000
--- a/Classes/Frontend/RssFeed/ContentRendering.php
+++ /dev/null
@@ -1,64 +0,0 @@
-
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- */
-
-namespace DanielSiepmann\DsSite\Frontend\RssFeed;
-
-use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
-
-class ContentRendering
-{
- public function __construct(
- private readonly ContentObjectRenderer $contentObjectRenderer
- ) {
- }
-
- public function extend(array $row): array
- {
- $row['description'] = $row['data']['abstract'] . $this->getContent($row['data']['uid']);
- return $row;
- }
-
- private function getContent(int $pageUid): string
- {
- $colPositions = [
- 50,
- 0,
- 100,
- 200,
- ];
-
- $content = '';
- foreach ($colPositions as $colPos) {
- $content .= $this->contentObjectRenderer->cObjGetSingle('CONTENT', [
- 'table' => 'tt_content',
- 'select.' => [
- 'orderBy' => 'sorting',
- 'where' => '{#colPos}=' . $colPos,
- 'pidInList' => $pageUid,
- ],
- ]);
- }
-
- return $content;
- }
-}
diff --git a/Classes/Frontend/RssFeed/SitemapDataProvider.php b/Classes/Frontend/RssFeed/SitemapDataProvider.php
deleted file mode 100644
index aaf21d5..0000000
--- a/Classes/Frontend/RssFeed/SitemapDataProvider.php
+++ /dev/null
@@ -1,105 +0,0 @@
-
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- */
-
-namespace DanielSiepmann\DsSite\Frontend\RssFeed;
-
-use Psr\Http\Message\ServerRequestInterface;
-use TYPO3\CMS\Core\Database\Connection;
-use TYPO3\CMS\Core\Database\ConnectionPool;
-use TYPO3\CMS\Core\Database\Query\QueryBuilder;
-use TYPO3\CMS\Core\Utility\GeneralUtility;
-use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
-use TYPO3\CMS\Seo\XmlSitemap\RecordsXmlSitemapDataProvider;
-
-/**
- * Provides dynamic for additionalWhere,
- * to only list pages for current requested category.
- */
-class SitemapDataProvider extends RecordsXmlSitemapDataProvider
-{
- public function __construct(
- ServerRequestInterface $request,
- string $key,
- array $config = [],
- ContentObjectRenderer $cObj = null
- ) {
- $categoryUid = intval($request->getQueryParams()['category_uid'] ?? 0);
- if ($categoryUid > 0) {
- $pageUids = $this->getPageUidsWithRelationToCategory($categoryUid);
- $config['additionalWhere'] = ($config['additionalWhere'] ?? '')
- . $this->createAdditionalWhereForPageUids($pageUids)
- ;
- }
-
- parent::__construct($request, $key, $config, $cObj);
- }
-
- public function generateItems(): void
- {
- parent::generateItems();
-
- $contentRendering = new ContentRendering($this->cObj);
- foreach ($this->items as $key => $item) {
- $this->items[$key] = $contentRendering->extend($item);
- }
- }
-
- private function getPageUidsWithRelationToCategory(int $categoryUid): array
- {
- $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
- ->getQueryBuilderForTable('pages');
- /* @var QueryBuilder $queryBuilder */
-
- $queryBuilder->select('uid');
- $queryBuilder->from('pages');
- $queryBuilder->leftJoin(
- 'pages',
- 'sys_category_record_mm',
- 'mm',
- 'pages.uid = mm.uid_foreign'
- );
- $queryBuilder->where(
- $queryBuilder->expr()->eq(
- 'mm.tablenames',
- $queryBuilder->createNamedParameter('pages')
- ),
- $queryBuilder->expr()->eq(
- 'mm.fieldname',
- $queryBuilder->createNamedParameter('categories')
- ),
- $queryBuilder->expr()->in(
- 'mm.uid_local',
- $queryBuilder->createNamedParameter($categoryUid)
- )
- );
-
- return array_map(function (array $row) {
- return (int) $row['uid'];
- }, $queryBuilder->executeQuery()->fetchAllAssociative());
- }
-
- private function createAdditionalWhereForPageUids(array $pageUids): string
- {
- return ' AND uid IN(' . implode(',', $pageUids) . ')';
- }
-}
diff --git a/Classes/Frontend/RssFeed/XmlSitemapRenderer.php b/Classes/Frontend/RssFeed/XmlSitemapRenderer.php
deleted file mode 100644
index 2796326..0000000
--- a/Classes/Frontend/RssFeed/XmlSitemapRenderer.php
+++ /dev/null
@@ -1,167 +0,0 @@
-
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- */
-
-namespace DanielSiepmann\DsSite\Frontend\RssFeed;
-
-use Psr\Http\Message\ServerRequestInterface;
-use TYPO3Fluid\Fluid\View\TemplateView;
-use TYPO3\CMS\Core\Http\PropagateResponseException;
-use TYPO3\CMS\Core\TypoScript\TypoScriptService;
-use TYPO3\CMS\Core\Utility\GeneralUtility;
-use TYPO3\CMS\Fluid\Core\Rendering\RenderingContextFactory;
-use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
-use TYPO3\CMS\Frontend\Controller\ErrorController;
-use TYPO3\CMS\Seo\XmlSitemap\Exception\InvalidConfigurationException;
-use TYPO3\CMS\Seo\XmlSitemap\XmlSitemapDataProviderInterface;
-
-class XmlSitemapRenderer
-{
- private array $typoScriptConfiguration = [];
-
- protected array $configuration;
-
- private ContentObjectRenderer $contentObjectRenderer;
-
- protected TemplateView $view;
-
- public function __construct(
- protected TypoScriptService $typoScriptService,
- protected RenderingContextFactory $renderingContextFactory,
- ) {
- }
-
- public function setContentObjectRenderer(ContentObjectRenderer $contentObjectRenderer): void
- {
- $this->contentObjectRenderer = $contentObjectRenderer;
- }
-
- protected function initialize(array $fullConfiguration): void
- {
- $this->configuration = $this->typoScriptService->convertTypoScriptArrayToPlainArray($fullConfiguration['plugin.']['tx_seo.'] ?? []);
- $renderingContext = $this->renderingContextFactory->create();
- $templatePaths = $renderingContext->getTemplatePaths();
- $templatePaths->setTemplateRootPaths($this->configuration['view']['templateRootPaths']);
- $templatePaths->setLayoutRootPaths($this->configuration['view']['layoutRootPaths']);
- $templatePaths->setPartialRootPaths($this->configuration['view']['partialRootPaths']);
- $templatePaths->setFormat('xml');
- $this->view = GeneralUtility::makeInstance(TemplateView::class, $renderingContext);
- $this->view->assign('settings', $this->getSettings());
- }
-
- public function render(string $_, array $typoScriptConfiguration, ServerRequestInterface $request): string
- {
- $this->typoScriptConfiguration = $typoScriptConfiguration;
-
- $this->initialize($GLOBALS['TSFE']->tmpl->setup);
- $this->view->assign('type', $request->getAttribute('routing')?->getPageType() ?? 0);
- $sitemapType = $typoScriptConfiguration['sitemapType'] ?? 'xmlSitemap';
- if (!empty($sitemap = ($request->getQueryParams()['sitemap'] ?? null))) {
- return $this->renderSitemap($request, $sitemap, $sitemapType);
- }
-
- return $this->renderIndex($request, $sitemapType);
- }
-
- protected function renderIndex(ServerRequestInterface $request, string $sitemapType): string
- {
- $sitemaps = [];
- foreach ($this->configuration['config'][$sitemapType]['sitemaps'] ?? [] as $sitemap => $config) {
- if (!empty($config['provider']) && is_string($config['provider'])
- && class_exists($config['provider'])
- && is_subclass_of($config['provider'], XmlSitemapDataProviderInterface::class)
- ) {
- /** @var XmlSitemapDataProviderInterface $provider */
- $provider = GeneralUtility::makeInstance(
- $config['provider'],
- $request,
- $sitemap,
- (array)($config['config'] ?? [])
- );
-
- $pages = $provider->getNumberOfPages();
-
- for ($page = 0; $page < $pages; $page++) {
- $sitemaps[] = [
- 'key' => $sitemap,
- 'page' => $page,
- 'lastMod' => $provider->getLastModified(),
- ];
- }
- }
- }
-
- $this->view->assign('sitemapType', $sitemapType);
- $this->view->assign('sitemaps', $sitemaps);
-
- return $this->view->render('Index');
- }
-
- protected function renderSitemap(ServerRequestInterface $request, string $sitemap, string $sitemapType): string
- {
- if (!empty($sitemapConfig = $this->configuration['config'][$sitemapType]['sitemaps'][$sitemap] ?? null)) {
- if (class_exists($sitemapConfig['provider']) &&
- is_subclass_of($sitemapConfig['provider'], XmlSitemapDataProviderInterface::class)) {
- /** @var XmlSitemapDataProviderInterface $provider */
- $provider = GeneralUtility::makeInstance(
- $sitemapConfig['provider'],
- $request,
- $sitemap,
- (array)($sitemapConfig['config'] ?? [])
- );
-
- $items = $provider->getItems();
-
- $this->view->assign('items', $items);
- $this->view->assign('sitemapType', $sitemapType);
-
- $template = ($sitemapConfig['config']['template'] ?? false) ?: 'Sitemap';
- return $this->view->render($template);
- }
- throw new InvalidConfigurationException('No valid provider set for ' . $sitemap, 1535578522);
- }
-
- throw new PropagateResponseException(
- GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
- $request,
- 'No valid configuration found for sitemap ' . $sitemap
- ),
- 1535578569
- );
- }
-
- private function getSettings(): array
- {
- $settings = [];
- foreach (array_keys($this->typoScriptConfiguration['userFunc.']['variables.'] ?? []) as $variableName) {
- if (!is_string($variableName) || substr($variableName, -1) === '.') {
- continue;
- }
- $settings[$variableName] = $this->contentObjectRenderer->cObjGetSingle(
- $this->typoScriptConfiguration['userFunc.']['variables.'][$variableName] ?? '',
- $this->typoScriptConfiguration['userFunc.']['variables.'][$variableName . '.'] ?? []
- );
- }
-
- return $settings;
- }
-}
diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml
index b962b1f..a83eb84 100644
--- a/Configuration/Services.yaml
+++ b/Configuration/Services.yaml
@@ -18,7 +18,7 @@ services:
arguments:
- 'pages'
- DanielSiepmann\DsSite\Frontend\RssFeed\XmlSitemapRenderer:
+ DanielSiepmann\DsSite\Frontend\RssFeed\BlogPostsDataProvider:
public: true
DanielSiepmann\DsSite\Backend\PreviewRenderer\Video:
diff --git a/Configuration/TypoScript/Setup/RssFeed.typoscript b/Configuration/TypoScript/Setup/RssFeed.typoscript
index f4fd429..e5984c4 100644
--- a/Configuration/TypoScript/Setup/RssFeed.typoscript
+++ b/Configuration/TypoScript/Setup/RssFeed.typoscript
@@ -1,30 +1,3 @@
-plugin.tx_seo {
- view {
- templateRootPaths {
- 20 = EXT:ds_site/Resources/Private/Templates/Sitemaps/
- }
- }
- config {
- xmlSitemap {
- sitemaps {
- blog-posts {
- provider = DanielSiepmann\DsSite\Frontend\RssFeed\SitemapDataProvider
- config {
- table = pages
- sortField = lastUpdated
- sortOrder = DESC
- lastModifiedField = tstamp
- additionalWhere = AND no_index = 0
- pid = {$pageUids.blogPosts}
- recursive = 3
- template = RssFeed
- }
- }
- }
- }
- }
-}
-
page {
headerData {
10 = TEXT
@@ -32,7 +5,7 @@ page {
wrap =
typolink {
parameter = t3://page?uid=1
- additionalParams = &type=1533906435&sitemap=blog-posts
+ additionalParams = &type=1707321482&feed=blog-posts
returnLast = url
}
}
@@ -58,7 +31,7 @@ page {
additionalParams.stdWrap.cObject = COA
additionalParams.stdWrap.cObject {
10 = TEXT
- 10.value = &type=1533906435&sitemap=blog-posts
+ 10.value = &type=1707321482&feed=blog-posts
11 = TEXT
11.value = &category_uid=
12 = TEXT
@@ -71,18 +44,44 @@ page {
}
}
}
+rssFeed = PAGE
+rssFeed {
+ typeNum = 1707321482
-seo_sitemap {
- 10 {
- userFunc = DanielSiepmann\DsSite\Frontend\RssFeed\XmlSitemapRenderer->render
- userFunc {
- variables {
- categoryId = TEXT
- categoryId.data = GP:category_uid
- categoryId.intval = 1
- categoryTitle = TEXT
- categoryTitle.data.dataWrap = DB : sys_category:{GP:category_uid}:title
+ config {
+ admPanel = 0
+ debug = 0
+ disableAllHeaderCode = 1
+ additionalHeaders {
+ 10 {
+ header = Content-Type: application/xml;charset=utf-8
}
}
}
+
+ 10 = FLUIDTEMPLATE
+ 10 {
+ dataProcessing {
+ 10 = DanielSiepmann\DsSite\Frontend\RssFeed\BlogPostsDataProvider
+ 10 {
+ as = pages
+ table = pages
+ orderBy = lastUpdated DESC
+ selectFields = pages.uid, pages.abstract, pages.title, pages.lastUpdated
+ where = AND no_index = 0
+ pidInList = {$pageUids.blogPosts}
+ recursive = 3
+ }
+ }
+
+ variables {
+ categoryId = TEXT
+ categoryId.data = GP:category_uid
+ categoryId.intval = 1
+ categoryTitle = TEXT
+ categoryTitle.data.dataWrap = DB : sys_category:{GP:category_uid}:title
+ }
+
+ file = EXT:ds_site/Resources/Private/Templates/RssFeed.xml
+ }
}
diff --git a/Resources/Private/Templates/Sitemaps/RssFeed.xml b/Resources/Private/Templates/RssFeed.xml
similarity index 65%
rename from Resources/Private/Templates/Sitemaps/RssFeed.xml
rename to Resources/Private/Templates/RssFeed.xml
index b151360..efa3cc4 100644
--- a/Resources/Private/Templates/Sitemaps/RssFeed.xml
+++ b/Resources/Private/Templates/RssFeed.xml
@@ -1,28 +1,28 @@
-
+
- Daniel Siepmann - Coding is Art - Blog Posts {settings.categoryTitle}
- List of {settings.categoryTitle} blog posts at daniel-siepmann.de
- {f:uri.typolink(parameter: 't3://page?uid=11', additionalParams: '&topic_uid={settings.categoryId}', absolute: 1)}
-
+ Daniel Siepmann - Coding is Art - Blog Posts {categoryTitle}
+ List of {categoryTitle} blog posts at daniel-siepmann.de
+ {f:uri.typolink(parameter: 't3://page?uid=11', additionalParams: '&topic_uid={categoryId}', absolute: 1)}
+
Daniel Siepmann - Coding is Art - All Blog Posts
List of blog posts at daniel-siepmann.de
{f:uri.typolink(parameter: 't3://page?uid=1', absolute: 1)}
-
+
{f:format.date(date: 'now', format: 'D, d M Y H:i:s O')}
1800
-
-
+
+
{f:render(section: 'Item', arguments: {
- item: item.data,
- description: item.description
+ item: post.data,
+ description: post.description
})}