[TASK] Limit number of items per XML sitemap 27/58227/16
authorRichard Haeser <richard@maxserv.com>
Sat, 8 Sep 2018 11:45:05 +0000 (13:45 +0200)
committerSusanne Moog <susanne.moog@typo3.org>
Wed, 19 Sep 2018 21:43:34 +0000 (23:43 +0200)
Sitemaps of pages are now by default limited to 1000 pages per sitemap
to avoid exceeding Google limits. It will generate multiple (paginated)
sitemaps if the number of items exceeds the number of items per sitemap.

Resolves: #86169
Releases: master
Change-Id: I006656239aa05632b1f58a2286475fcba0295b9b
Reviewed-on: https://review.typo3.org/58227
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Richard Haeser <richard@maxserv.com>
Tested-by: Richard Haeser <richard@maxserv.com>
Reviewed-by: Guido Schmechel <guido.schmechel@brandung.de>
Tested-by: Guido Schmechel <guido.schmechel@brandung.de>
Reviewed-by: Susanne Moog <susanne.moog@typo3.org>
Tested-by: Susanne Moog <susanne.moog@typo3.org>
14 files changed:
composer.json
composer.lock
typo3/sysext/core/composer.json
typo3/sysext/seo/Classes/XmlSitemap/AbstractXmlSitemapDataProvider.php
typo3/sysext/seo/Classes/XmlSitemap/PagesXmlSitemapDataProvider.php
typo3/sysext/seo/Classes/XmlSitemap/RecordsXmlSitemapDataProvider.php
typo3/sysext/seo/Classes/XmlSitemap/XmlSitemapDataProviderInterface.php
typo3/sysext/seo/Classes/XmlSitemap/XmlSitemapRenderer.php
typo3/sysext/seo/Configuration/TypoScript/XmlSitemap/constants.typoscript
typo3/sysext/seo/Configuration/TypoScript/XmlSitemap/setup.typoscript
typo3/sysext/seo/Resources/Private/Templates/XmlSitemap/Index.xml
typo3/sysext/seo/Resources/Private/Templates/XmlSitemap/Sitemap.xml
typo3/sysext/seo/Tests/Functional/XmlSitemap/XmlSitemapIndexTest.php
typo3/sysext/seo/Tests/Unit/XmlSitemap/PagesXmlSitemapDataProviderTest.php

index 37781c9..76ba8d6 100644 (file)
@@ -68,7 +68,7 @@
                "fiunchinho/phpunit-randomizer": "^4.0",
                "friendsofphp/php-cs-fixer": "^2.12.2",
                "typo3/cms-styleguide": "~9.2.0",
-               "typo3/testing-framework": "~4.8.2"
+               "typo3/testing-framework": "~4.9.0"
        },
        "suggest": {
                "ext-gd": "GDlib/Freetype is required for building images with text (GIFBUILDER) and can also be used to scale images",
index d67d403..acfbaa5 100644 (file)
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "b0884fb27b7941f4dd4000f98af986af",
+    "content-hash": "d55491a7a908c7f4e344ced17283bcd0",
     "packages": [
         {
             "name": "cogpowered/finediff",
         },
         {
             "name": "typo3/testing-framework",
-            "version": "4.8.2",
+            "version": "4.9.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/TYPO3/testing-framework.git",
-                "reference": "487197e0a178cf28c1fe60f643a34f919260890d"
+                "reference": "32f7d2eced0c7930c6535d7b60eef24775e4c207"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/TYPO3/testing-framework/zipball/487197e0a178cf28c1fe60f643a34f919260890d",
-                "reference": "487197e0a178cf28c1fe60f643a34f919260890d",
+                "url": "https://api.github.com/repos/TYPO3/testing-framework/zipball/32f7d2eced0c7930c6535d7b60eef24775e4c207",
+                "reference": "32f7d2eced0c7930c6535d7b60eef24775e4c207",
                 "shasum": ""
             },
             "require": {
                 "tests",
                 "typo3"
             ],
-            "time": "2018-09-01T13:52:19+00:00"
+            "time": "2018-09-17T14:26:30+00:00"
         },
         {
             "name": "webmozart/assert",
index dc87059..fa41ab2 100644 (file)
@@ -50,7 +50,7 @@
                "fiunchinho/phpunit-randomizer": "^4.0",
                "friendsofphp/php-cs-fixer": "^2.12.2",
                "typo3/cms-styleguide": "~9.2.0",
-               "typo3/testing-framework": "~4.8.2"
+               "typo3/testing-framework": "~4.9.0"
        },
        "suggest": {
                "ext-fileinfo": "Used for proper file type detection in the file abstraction layer",
index acaca3e..70cd3aa 100644 (file)
@@ -1,7 +1,21 @@
 <?php
 declare(strict_types = 1);
+
 namespace TYPO3\CMS\Seo\XmlSitemap;
 
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
 use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
@@ -37,6 +51,16 @@ abstract class AbstractXmlSitemapDataProvider implements XmlSitemapDataProviderI
     protected $cObj;
 
     /**
+     * @var int
+     */
+    protected $numberOfItemsPerPage = 1000;
+
+    /**
+     * @var ServerRequestInterface
+     */
+    protected $request;
+
+    /**
      * AbstractXmlSitemapDataProvider constructor
      *
      * @param \Psr\Http\Message\ServerRequestInterface $request
@@ -48,6 +72,7 @@ abstract class AbstractXmlSitemapDataProvider implements XmlSitemapDataProviderI
     {
         $this->key = $key;
         $this->config = $config;
+        $this->request = $request;
 
         if ($cObj === null) {
             $cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
@@ -66,6 +91,14 @@ abstract class AbstractXmlSitemapDataProvider implements XmlSitemapDataProviderI
     /**
      * @return int
      */
+    public function getNumberOfPages(): int
+    {
+        return (int)ceil(count($this->items) / $this->numberOfItemsPerPage);
+    }
+
+    /**
+     * @return int
+     */
     public function getLastModified(): int
     {
         $lastMod = 0;
@@ -79,10 +112,27 @@ abstract class AbstractXmlSitemapDataProvider implements XmlSitemapDataProviderI
     }
 
     /**
+     * @param array $data
+     * @return array
+     */
+    protected function defineUrl(array $data): array
+    {
+        return $data;
+    }
+
+    /**
      * @return array
      */
     public function getItems(): array
     {
-        return (array)$this->items;
+        $pageNumber = (int)($this->request->getQueryParams()['page'] ?? 0);
+        $page = $pageNumber > 0 ? $pageNumber : 0;
+        $items = array_slice(
+            $this->items,
+            $page * $this->numberOfItemsPerPage,
+            $this->numberOfItemsPerPage
+        );
+
+        return array_map([$this, 'defineUrl'], $items);
     }
 }
index b93e115..608b052 100644 (file)
@@ -1,5 +1,6 @@
 <?php
 declare(strict_types = 1);
+
 namespace TYPO3\CMS\Seo\XmlSitemap;
 
 /*
@@ -18,9 +19,12 @@ namespace TYPO3\CMS\Seo\XmlSitemap;
 use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Core\Context\Context;
 use TYPO3\CMS\Core\Context\LanguageAspect;
+use TYPO3\CMS\Core\Database\ConnectionPool;
+use TYPO3\CMS\Core\Database\Query\QueryHelper;
+use TYPO3\CMS\Core\Database\QueryGenerator;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
-use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
+use TYPO3\CMS\Frontend\Page\PageRepository;
 
 /**
  * Class to generate a XML sitemap for pages
@@ -31,39 +35,17 @@ class PagesXmlSitemapDataProvider extends AbstractXmlSitemapDataProvider
     {
         parent::__construct($request, $key, $config, $cObj);
 
-        $this->generateItems($request);
+        $this->generateItems($this->request);
     }
 
     /**
-     * @param \Psr\Http\Message\ServerRequestInterface $request
+     * @param ServerRequestInterface $request
+     * @throws \TYPO3\CMS\Core\Context\Exception\AspectNotFoundException
      */
     public function generateItems(ServerRequestInterface $request): void
     {
-        $site = $request->getAttribute('site');
-        $rootPageId = $site->getRootPageId();
-
-        $additionalWhere = $this->config['additionalWhere'] ?? '';
-        if (!empty($this->config['excludedDoktypes'])) {
-            $excludedDoktypes = GeneralUtility::trimExplode(',', $this->config['excludedDoktypes']);
-            if (!empty($excludedDoktypes)) {
-                $additionalWhere .= ' AND doktype NOT IN (' . implode(',', $excludedDoktypes) . ')';
-            }
-        }
-
-        $rootPage = $this->getTypoScriptFrontendController()->page;
-        $pages = [
-            [
-                'uid' => $rootPage['uid'],
-                'tstamp' => $rootPage['tstamp'],
-                'l18n_cfg' => $rootPage['l18n_cfg'],
-                'SYS_LASTCHANGED' => $rootPage['SYS_LASTCHANGED']
-            ]
-        ];
-
-        $pages = $this->getSubPages($rootPageId, $pages, ltrim($additionalWhere));
-
         $languageId = $this->getCurrentLanguageAspect()->getId();
-        foreach ($pages as $page) {
+        foreach ($this->getPages() as $page) {
             /**
              * @todo Checking if the page has to be shown/hidden should normally be handled by the
              * PageRepository but to prevent major breaking changes this is checked here for now
@@ -80,16 +62,10 @@ class PagesXmlSitemapDataProvider extends AbstractXmlSitemapDataProvider
                     && !$page['_PAGES_OVERLAY']
                 )
             ) {
-                $typoLinkConfig = [
-                    'parameter' => $page['uid'],
-                    'forceAbsoluteUrl' => 1,
-                ];
-
-                $loc = $this->cObj->typoLink_URL($typoLinkConfig);
                 $lastMod = $page['SYS_LASTCHANGED'] ?: $page['tstamp'];
 
                 $this->items[] = [
-                    'loc' => $loc,
+                    'uid' => $page['uid'],
                     'lastMod' => (int)$lastMod
                 ];
             }
@@ -97,37 +73,46 @@ class PagesXmlSitemapDataProvider extends AbstractXmlSitemapDataProvider
     }
 
     /**
-     * Get subpages
-     *
-     * @param int $parentPageId
-     * @param array $pages
-     * @param string $additionalWhere
      * @return array
      */
-    protected function getSubPages(int $parentPageId, array $pages = [], $additionalWhere = ''): array
+    protected function getPages(): array
     {
-        $subPages = $this->getTypoScriptFrontendController()->sys_page->getMenu(
-            $parentPageId,
-            'uid, tstamp, SYS_LASTCHANGED, l18n_cfg',
-            'sorting',
-            $additionalWhere,
-            false
-        );
-        $pages = array_merge($pages, $subPages);
-
-        foreach ($subPages as $subPage) {
-            $pages = $this->getSubPages((int)$subPage['uid'], $pages, $additionalWhere);
+        if (!empty($this->config['rootPage'])) {
+            $rootPageId = (int)$this->config['rootPage'];
+        } else {
+            $site = $this->request->getAttribute('site');
+            $rootPageId = $site->getRootPageId();
         }
 
-        return $pages;
-    }
+        $queryGenerator = GeneralUtility::makeInstance(QueryGenerator::class);
+        $treeList = $queryGenerator->getTreeList($rootPageId, 99);
 
-    /**
-     * @return TypoScriptFrontendController
-     */
-    protected function getTypoScriptFrontendController(): TypoScriptFrontendController
-    {
-        return $GLOBALS['TSFE'];
+        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+            ->getQueryBuilderForTable('pages');
+
+        $constraints = [
+            $queryBuilder->expr()->in('uid', $treeList)
+        ];
+
+        if (!empty($this->config['additionalWhere'])) {
+            $constraints[] = QueryHelper::stripLogicalOperatorPrefix($this->config['additionalWhere']);
+        }
+
+        if (!empty($this->config['excludedDoktypes'])) {
+            $excludedDoktypes = GeneralUtility::intExplode(',', $this->config['excludedDoktypes']);
+            if (!empty($excludedDoktypes)) {
+                $constraints[] = $queryBuilder->expr()->notIn('doktype', implode(',', $excludedDoktypes));
+            }
+        }
+        $pages = $queryBuilder->select('*')
+            ->from('pages')
+            ->where(...$constraints)
+            ->orderBy('uid', 'ASC')
+            ->execute()
+            ->fetchAll();
+
+        $pageRepository = GeneralUtility::makeInstance(PageRepository::class);
+        return $pageRepository->getPagesOverlay($pages);
     }
 
     /**
@@ -138,4 +123,20 @@ class PagesXmlSitemapDataProvider extends AbstractXmlSitemapDataProvider
     {
         return GeneralUtility::makeInstance(Context::class)->getAspect('language');
     }
+
+    /**
+     * @param array $data
+     * @return array
+     */
+    protected function defineUrl(array $data): array
+    {
+        $typoLinkConfig = [
+            'parameter' => $data['uid'],
+            'forceAbsoluteUrl' => 1,
+        ];
+
+        $data['loc'] = $this->cObj->typoLink_URL($typoLinkConfig);
+
+        return $data;
+    }
 }
index 5e0bf0a..055cf02 100644 (file)
@@ -1,7 +1,21 @@
 <?php
 declare(strict_types = 1);
+
 namespace TYPO3\CMS\Seo\XmlSitemap;
 
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
 use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Core\Database\ConnectionPool;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
@@ -39,33 +53,39 @@ class RecordsXmlSitemapDataProvider extends AbstractXmlSitemapDataProvider
             );
         }
 
-        $pids = GeneralUtility::intExplode(',', $this->config['pid']) ?? $GLOBALS['TSFE']->id;
+        $pids = !empty($this->config['pid']) ? GeneralUtility::intExplode(',', $this->config['pid']) : [];
         $lastModifiedField = $this->config['lastModifiedField'] ?? 'tstamp';
         $sortField = $this->config['sortField'] ?? 'sorting';
 
         $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
             ->getQueryBuilderForTable($this->config['table']);
 
-        $constraints = [
-            $queryBuilder->expr()->in('pid', $pids)
-        ];
+        $constraints = [];
+
+        if (!empty($pids)) {
+            $constraints[] = $queryBuilder->expr()->in('pid', $pids);
+        }
 
         if (!empty($this->config['additionalWhere'])) {
             $constraints[] = $this->config['additionalWhere'];
         }
 
-        $rows = $queryBuilder->select('*')
-            ->from($this->config['table'])
-            ->where(
+        $queryBuilder->select('*')
+            ->from($this->config['table']);
+
+        if (!empty($constraints)) {
+            $queryBuilder->where(
                 ...$constraints
-            )
-            ->orderBy($sortField)
+            );
+        }
+
+        $rows = $queryBuilder->orderBy($sortField)
             ->execute()
             ->fetchAll();
 
         foreach ($rows as $row) {
             $this->items[] = [
-                'loc' => $this->defineUrl($row),
+                'data' => $row,
                 'lastMod' => $row[$lastModifiedField]
             ];
         }
@@ -73,9 +93,9 @@ class RecordsXmlSitemapDataProvider extends AbstractXmlSitemapDataProvider
 
     /**
      * @param array $data
-     * @return string
+     * @return array
      */
-    protected function defineUrl(array $data): string
+    protected function defineUrl(array $data): array
     {
         $pageId = $this->config['url']['pageId'] ?? $GLOBALS['TSFE']->id;
         $additionalParams = [];
@@ -97,7 +117,9 @@ class RecordsXmlSitemapDataProvider extends AbstractXmlSitemapDataProvider
             'useCacheHash' => $this->config['url']['useCacheHash'] ?? 0
         ];
 
-        return $this->cObj->typoLink_URL($typoLinkConfig);
+        $data['loc'] = $this->cObj->typoLink_URL($typoLinkConfig);
+
+        return $data;
     }
 
     /**
index 152c88a..0f5d9e4 100644 (file)
@@ -1,7 +1,21 @@
 <?php
 declare(strict_types = 1);
+
 namespace TYPO3\CMS\Seo\XmlSitemap;
 
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
 use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
 
@@ -14,4 +28,5 @@ interface XmlSitemapDataProviderInterface
     public function getKey(): string;
     public function getItems(): array;
     public function getLastModified(): int;
+    public function getNumberOfPages(): int;
 }
index 0d2bc30..6067b7d 100644 (file)
@@ -1,7 +1,21 @@
 <?php
 declare(strict_types = 1);
+
 namespace TYPO3\CMS\Seo\XmlSitemap;
 
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
 use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
@@ -72,10 +86,15 @@ class XmlSitemapRenderer
                     (array)$config['config']
                 );
 
-                $sitemaps[] = [
-                    'key' => $sitemap,
-                    'lastMod' => $provider->getLastModified()
-                ];
+                $pages = $provider->getNumberOfPages();
+
+                for ($page = 0; $page < $pages; $page++) {
+                    $sitemaps[] = [
+                        'key' => $sitemap,
+                        'page' => $page,
+                        'lastMod' => $provider->getLastModified()
+                    ];
+                }
             }
         }
 
index 4a1cfa5..26dde5b 100644 (file)
@@ -7,4 +7,15 @@ plugin.tx_seo {
     # cat=plugin.tx_seo/file; type=string; label=Path to template layouts (FE)
     layoutRootPath = EXT:seo/Resources/Private/Layouts/
   }
+
+  settings {
+    xmlSitemap {
+      sitemaps {
+        pages {
+          excludedDoktypes = 7, 3, 254, 255, 199
+          additionalWhere = no_index = 0
+        }
+      }
+    }
+  }
 }
index 5c77a7e..338f685 100644 (file)
@@ -39,8 +39,8 @@ plugin.tx_seo {
         pages {
           provider = TYPO3\CMS\Seo\XmlSitemap\PagesXmlSitemapDataProvider
           config {
-            excludedDoktypes =
-            additionalWhere = AND no_index = 0
+            excludedDoktypes = {$plugin.tx_seo.settings.xmlSitemap.sitemaps.pages.excludedDoktypes}
+            additionalWhere = {$plugin.tx_seo.settings.xmlSitemap.sitemaps.pages.additionalWhere}
           }
         }
       }
index 224f45b..a2eacac 100644 (file)
@@ -3,7 +3,7 @@
 <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
     <f:for each="{sitemaps}" as="sitemap">
         <sitemap>
-            <loc><f:uri.page additionalParams="{type: type, sitemap: sitemap.key }" absolute="true" noCacheHash="true" /></loc>
+            <loc><f:uri.page additionalParams="{type: type, sitemap: sitemap.key, page: sitemap.page}" absolute="true" noCacheHash="true" /></loc>
             <lastmod>{sitemap.lastMod -> f:format.date(format: 'c')}</lastmod>
         </sitemap>
     </f:for>
index 6c6bfcb..7e80a83 100644 (file)
@@ -2,9 +2,11 @@
 
 <urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd http://www.google.com/schemas/sitemap-image/1.1 http://www.google.com/schemas/sitemap-image/1.1/sitemap-image.xsd" xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
     <f:for each="{items}" as="item">
-        <url>
-            <loc>{item.loc}</loc>
-            <lastmod>{item.lastMod -> f:format.date(format: 'c')}</lastmod>
-        </url>
+        <f:if condition="{item.loc}">
+            <url>
+                <loc>{item.loc}</loc>
+                <lastmod>{item.lastMod -> f:format.date(format: 'c')}</lastmod>
+            </url>
+        </f:if>
     </f:for>
 </urlset>
index 1c90840..bb39ea5 100644 (file)
@@ -3,6 +3,19 @@ declare(strict_types = 1);
 
 namespace TYPO3\CMS\Frontend\Tests\Functional\XmlSitemap;
 
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
 use TYPO3\CMS\Frontend\Tests\Functional\SiteHandling\AbstractTestCase;
 use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\InternalRequest;
 
@@ -24,7 +37,10 @@ class XmlSitemapIndexTest extends AbstractTestCase
         $this->importDataSet('EXT:seo/Tests/Functional/Fixtures/pages-sitemap.xml');
         $this->setUpFrontendRootPage(
             1,
-            ['EXT:seo/Configuration/TypoScript/XmlSitemap/setup.typoscript']
+            [
+                'constants' => ['EXT:seo/Configuration/TypoScript/XmlSitemap/constants.typoscript'],
+                'setup' => ['EXT:seo/Configuration/TypoScript/XmlSitemap/setup.typoscript']
+            ]
         );
     }
 
@@ -46,9 +62,9 @@ class XmlSitemapIndexTest extends AbstractTestCase
         );
 
         $expectedHeaders = [
-            'Content-Length' => [0 => '451']
+            'Content-Length' => [0 => '462']
         ];
-        $expectedBody = '#<loc>http://localhost/\?id=1&amp;type=1533906435&amp;sitemap=pages</loc>#';
+        $expectedBody = '#<loc>http://localhost/\?id=1&amp;type=1533906435&amp;sitemap=pages&amp;page=0</loc>#';
         $this->assertEquals(200, $response->getStatusCode());
         $this->assertEquals($expectedHeaders, $response->getHeaders());
         $this->assertRegExp($expectedBody, (string)$response->getBody());
index 8abe2f7..be2cae0 100644 (file)
@@ -1,7 +1,21 @@
 <?php
 declare(strict_types = 1);
+
 namespace TYPO3\CMS\Seo\Tests\Unit\XmlSitemap;
 
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
 use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
 use TYPO3\CMS\Seo\XmlSitemap\PagesXmlSitemapDataProvider;
@@ -10,6 +24,33 @@ use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
 class PagesXmlSitemapDataProviderTest extends UnitTestCase
 {
     /**
+     * @var array
+     */
+    protected $items;
+
+    public function setUp()
+    {
+        $this->items = [
+            [
+                'loc' => 'https://yourdomain.com/page-1',
+                'lastMod' => 1535655601
+            ],
+            [
+                'loc' => 'https://yourdomain.com/page-2',
+                'lastMod' => 1530432000
+            ],
+            [
+                'loc' => 'https://yourdomain.com/page-3',
+                'lastMod' => 1535655756
+            ],
+            [
+                'loc' => 'https://yourdomain.com/page-4',
+                'lastMod' => 1530432001
+            ],
+        ];
+    }
+
+    /**
      * @test
      */
     public function checkIfCorrectKeyIsGivenAfterConstruct(): void
@@ -29,24 +70,39 @@ class PagesXmlSitemapDataProviderTest extends UnitTestCase
     }
 
     /**
+     * @dataProvider numberOfItemsPerPageProvider
      * @test
      */
-    public function checkGetItemsReturnsDefinedItems(): void
+    public function checkGetItemsReturnsDefinedItems($numberOfItemsPerPage): void
     {
         $key = 'dummyKey';
         $cObj = $this->prophesize(ContentObjectRenderer::class);
 
         $subject = $this->getAccessibleMock(
             PagesXmlSitemapDataProvider::class,
-            ['generateItems'],
-            [$key, [], $cObj->reveal()],
+            ['generateItems', 'defineUrl'],
+            [$this->prophesize(ServerRequestInterface::class)->reveal(), $key, [], $cObj->reveal()],
             '',
             false
         );
-        $items = ['foo' => 'bar'];
-        $subject->_set('items', $items);
+        $subject->_set('request', $this->prophesize(ServerRequestInterface::class)->reveal());
+        $subject->_set('items', $this->items);
+        $subject->_set('numberOfItemsPerPage', $numberOfItemsPerPage);
 
-        $this->assertEquals($items, $subject->getItems());
+        $subject->expects($this->any())->method('defineUrl')->will(
+            $this->returnCallback(
+                function ($input) {
+                    return $input;
+                }
+            )
+        );
+
+        $returnedItems = $subject->getItems();
+        $expectedReturnedItems = array_slice($this->items, 0, $numberOfItemsPerPage);
+
+        $this->assertLessThanOrEqual($numberOfItemsPerPage, count($returnedItems));
+
+        $this->assertEquals($expectedReturnedItems, $returnedItems);
     }
 
     /**
@@ -60,26 +116,25 @@ class PagesXmlSitemapDataProviderTest extends UnitTestCase
         $subject = $this->getAccessibleMock(
             PagesXmlSitemapDataProvider::class,
             ['generateItems'],
-            [$key, [], $cObj->reveal()],
+            [$this->prophesize(ServerRequestInterface::class)->reveal(), $key, [], $cObj->reveal()],
             '',
             false
         );
-        $items = [
-            [
-                'loc' => 'https://yourdomain.com/page-1',
-                'lastMod' => 1535655601
-            ],
-            [
-                'loc' => 'https://yourdomain.com/page-2',
-                'lastMod' => 1530432000
-            ],
-            [
-                'loc' => 'https://yourdomain.com/page-3',
-                'lastMod' => 1535655756
-            ],
-        ];
-        $subject->_set('items', $items);
+
+        $subject->_set('items', $this->items);
 
         $this->assertEquals(1535655756, $subject->getLastModified());
     }
+
+    /**
+     * @return array
+     */
+    public function numberOfItemsPerPageProvider(): array
+    {
+        return [
+            '1 items per page' => [1],
+            '3 items per page' => [3],
+            '100 items per page' => [100],
+        ];
+    }
 }