Commit e235a8c2 authored by Dominique Feyer's avatar Dominique Feyer

[TASK] Refactor to use SitemapBuilder service

This change introduce the SitemapBuilder service and the
SitemapBuilderInterface, so you can implement your own SitemapBuilder.
parent af08304d
<?php
namespace Ttree\Neos\Sitemap\Command;
use Tackk\Cartographer\ChangeFrequency;
use TYPO3\Eel\FlowQuery\FlowQuery;
use Ttree\Neos\Sitemap\Domain\Model\SitemapDefinition;
use Ttree\Neos\Sitemap\Service\SitemapBuilder;
use TYPO3\Flow\Annotations as Flow;
use TYPO3\Flow\Cli\CommandController;
use TYPO3\Flow\Http\Request;
use TYPO3\Flow\Http\Response;
use TYPO3\Flow\Http\Uri;
use TYPO3\Flow\Mvc\ActionRequest;
use TYPO3\Flow\Mvc\Controller\Arguments;
use TYPO3\Flow\Mvc\Controller\ControllerContext;
use TYPO3\Flow\Mvc\Routing\UriBuilder;
use TYPO3\Flow\Utility\Files;
use TYPO3\Neos\Domain\Repository\SiteRepository;
use TYPO3\Neos\Service\LinkingService;
use TYPO3\TYPO3CR\Domain\Model\NodeInterface;
use TYPO3\TYPO3CR\Domain\Service\ContextFactoryInterface;
class SitemapCommandController extends CommandController {
/**
* @var ContextFactoryInterface
* @Flow\Inject
* @var SitemapBuilder
*/
protected $contextFactory;
/**
* @var SiteRepository
* @Flow\Inject
*/
protected $siteRepository;
/**
* @Flow\Inject
* @var LinkingService
*/
protected $linkingService;
/**
* @Flow\Inject
* @var UriBuilder
*/
protected $uriBuilder;
/**
* @Flow\InjectConfiguration("presets")
* @var array
*/
protected $presets;
protected $sitemapBuilder;
/**
* @param string $siteNodeName
......@@ -56,101 +21,12 @@ class SitemapCommandController extends CommandController {
* @param string $preset
*/
public function generateCommand($siteNodeName, $baseUrl, $preset = 'default') {
if (!isset($this->presets[$preset])) {
$this->outputLine('Preset "%s" not found ...', [$preset]);
$this->sendAndExit(1);
}
$preset = $this->presets[$preset];
$processedUri = [];
$sitemap = new \Tackk\Cartographer\Sitemap();
$context = $this->createContext('live', $siteNodeName);
$controllerContext = $this->createControllerContext($baseUrl);
$definition = new SitemapDefinition($siteNodeName, $baseUrl, $preset);
$sitemap = $this->sitemapBuilder->build($definition);
foreach ($preset['include'] as $nodeType => $nodeTypeStatus) {
if ($nodeTypeStatus !== TRUE) {
continue;
}
$flowQuery = new FlowQuery(array($context->getRootNode()));
/** @var NodeInterface $node */
foreach ($flowQuery->find(sprintf('[instanceof %s]', $nodeType)) as $node) {
if ($this->needToSkipNode($preset, $node)) {
continue;
}
if (trim($node->getProperty('uriPathSegment')) === '') {
$this->outputLine('Skip "%s" (%s), uriPathSegment empty', [$node->getLabel(), $node->getPath()]);
continue;
}
$uri = $this->linkingService->createNodeUri($controllerContext, $node, NULL, 'html', TRUE);
$hash = md5($uri);
if (isset($processedUri[$hash])) {
$this->outputLine('Skip "%s" (%s), duplicate URI (%s)', [$node->getLabel(), $node->getPath(), $uri]);
continue;
}
$lastPublicationDateTime = $node->getNodeData()->getLastPublicationDateTime() ?: $node->getNodeData()->getLastModificationDateTime();
$sitemap->add($uri, $lastPublicationDateTime ? $lastPublicationDateTime->format('Y-m-d') : null, ChangeFrequency::WEEKLY, 1.0);
$processedUri[$hash] = TRUE;
}
}
$pathAndFilename = FLOW_PATH_DATA . '/Persistent/Sitemaps/sitemap.xml';
$pathAndFilename = FLOW_PATH_DATA . '/Persistent/Sitemaps/Sitemap.xml';
Files::createDirectoryRecursively(dirname($pathAndFilename));
file_put_contents($pathAndFilename, (string)$sitemap);
}
/**
* @param array $preset
* @param NodeInterface $node
* @return boolean
*/
protected function needToSkipNode(array $preset, NodeInterface $node) {
if (isset($preset['skip']) && is_array($preset['skip'])) {
foreach ($preset['skip'] as $nodeType => $nodeTypeStatus) {
if ($nodeTypeStatus !== TRUE) {
continue;
}
if ($node->getNodeType()->isOfType($nodeType) === TRUE) {
return TRUE;
}
}
}
return FALSE;
}
/**
* @param string $baseUrl
* @return ControllerContext
*/
protected function createControllerContext($baseUrl) {
$httpRequest = Request::create(new Uri($baseUrl));
$response = new Response();
$arguments = new Arguments([]);
return new ControllerContext(new ActionRequest($httpRequest), $response, $arguments, $this->uriBuilder);
}
/**
* Creates a content context for given workspace and language identifiers
*
* @param string $workspaceName
* @param string $siteNodeName
* @param array $languageIdentifiers
* @return \TYPO3\TYPO3CR\Domain\Service\Context
*/
protected function createContext($workspaceName, $siteNodeName, array $languageIdentifiers = NULL) {
$site = $this->siteRepository->findOneByNodeName($siteNodeName);
$contextProperties = array(
'workspaceName' => $workspaceName,
'currentSite' => $site,
'invisibleContentShown' => TRUE,
'inaccessibleContentShown' => TRUE
);
if ($languageIdentifiers !== NULL) {
$contextProperties = array_merge($contextProperties, array(
'dimensions' => array('language' => $languageIdentifiers)
));
}
return $this->contextFactory->create($contextProperties);
}
}
\ No newline at end of file
<?php
namespace Ttree\Neos\Sitemap\Domain\Model;
use TYPO3\Flow\Annotations as Flow;
use TYPO3\Flow\Exception;
/**
* @api
*/
class SitemapDefinition {
/**
* @var string
*/
protected $siteNodeName;
/**
* @var string
*/
protected $baseUrl;
/**
* @var string
*/
protected $presetName;
/**
* @Flow\InjectConfiguration("presets")
* @var array
*/
protected $presets;
/**
* SitemapDefinition constructor.
* @param string $siteNodeName
* @param string $baseUrl
* @param string $presetName
* @throws Exception
*/
public function __construct($siteNodeName, $baseUrl, $presetName) {
$this->siteNodeName = $siteNodeName;
$this->baseUrl = $baseUrl;
$this->presetName = $presetName;
}
/**
* Initialize Object
*
* @throws Exception
*/
public function initializeObject() {
if (!isset($this->presets[$this->presetName]) || !is_array($this->presets[$this->presetName])) {
throw new Exception('Preset not found', 1442496982);
}
}
/**
* @return string
*/
public function getSiteNodeName() {
return $this->siteNodeName;
}
/**
* @return string
*/
public function getBaseUrl() {
return $this->baseUrl;
}
/**
* @return array
*/
public function getPreset() {
return $this->presets[$this->presetName];
}
}
\ No newline at end of file
<?php
namespace Ttree\Neos\Sitemap\Service;
use Tackk\Cartographer\ChangeFrequency;
use Tackk\Cartographer\Sitemap;
use Ttree\Neos\Sitemap\Domain\Model\SitemapDefinition;
use TYPO3\Eel\FlowQuery\FlowQuery;
use TYPO3\Flow\Annotations as Flow;
use TYPO3\Flow\Http\Request;
use TYPO3\Flow\Http\Response;
use TYPO3\Flow\Http\Uri;
use TYPO3\Flow\Mvc\ActionRequest;
use TYPO3\Flow\Mvc\Controller\Arguments;
use TYPO3\Flow\Mvc\Controller\ControllerContext;
use TYPO3\Flow\Mvc\Routing\UriBuilder;
use TYPO3\Neos\Domain\Repository\SiteRepository;
use TYPO3\Neos\Service\LinkingService;
use TYPO3\TYPO3CR\Domain\Model\NodeInterface;
use TYPO3\TYPO3CR\Domain\Service\ContextFactoryInterface;
/**
* @Flow\Scope("singleton")
* @api
*/
class SitemapBuilder implements SitemapBuilderInterface {
/**
* @var ContextFactoryInterface
* @Flow\Inject
*/
protected $contextFactory;
/**
* @var SiteRepository
* @Flow\Inject
*/
protected $siteRepository;
/**
* @Flow\Inject
* @var LinkingService
*/
protected $linkingService;
/**
* @Flow\Inject
* @var UriBuilder
*/
protected $uriBuilder;
/**
* @param SitemapDefinition $definiton
* @return string
*/
public function build(SitemapDefinition $definiton) {
$preset = $definiton->getPreset();
$processedUri = [];
$sitemap = new Sitemap();
$context = $this->createContext('live', $definiton->getSiteNodeName());
$controllerContext = $this->createControllerContext($definiton->getBaseUrl());
foreach ($preset['include'] as $nodeType => $nodeTypeStatus) {
if ($nodeTypeStatus !== TRUE) {
continue;
}
$flowQuery = new FlowQuery(array($context->getRootNode()));
/** @var NodeInterface $node */
foreach ($flowQuery->find(sprintf('[instanceof %s]', $nodeType)) as $node) {
if ($this->needToSkipNode($preset, $node)) {
continue;
}
if (trim($node->getProperty('uriPathSegment')) === '') {
continue;
}
$uri = $this->linkingService->createNodeUri($controllerContext, $node, NULL, 'html', TRUE);
$hash = md5($uri);
if (isset($processedUri[$hash])) {
continue;
}
$lastPublicationDateTime = $node->getNodeData()->getLastPublicationDateTime() ?: $node->getNodeData()->getLastModificationDateTime();
$sitemap->add($uri, $lastPublicationDateTime ? $lastPublicationDateTime->format('Y-m-d') : null, ChangeFrequency::WEEKLY, 1.0);
$processedUri[$hash] = TRUE;
}
}
return (string)$sitemap;
}
/**
* @param array $preset
* @param NodeInterface $node
* @return boolean
*/
protected function needToSkipNode(array $preset, NodeInterface $node) {
if (isset($preset['skip']) && is_array($preset['skip'])) {
foreach ($preset['skip'] as $nodeType => $nodeTypeStatus) {
if ($nodeTypeStatus !== TRUE) {
continue;
}
if ($node->getNodeType()->isOfType($nodeType) === TRUE) {
return TRUE;
}
}
}
return FALSE;
}
/**
* @param string $baseUrl
* @return ControllerContext
*/
protected function createControllerContext($baseUrl) {
$httpRequest = Request::create(new Uri($baseUrl));
$response = new Response();
$arguments = new Arguments([]);
return new ControllerContext(new ActionRequest($httpRequest), $response, $arguments, $this->uriBuilder);
}
/**
* Creates a content context for given workspace and language identifiers
*
* @param string $workspaceName
* @param string $siteNodeName
* @param array $languageIdentifiers
* @return \TYPO3\TYPO3CR\Domain\Service\Context
*/
protected function createContext($workspaceName, $siteNodeName, array $languageIdentifiers = NULL) {
$site = $this->siteRepository->findOneByNodeName($siteNodeName);
$contextProperties = array(
'workspaceName' => $workspaceName,
'currentSite' => $site,
'invisibleContentShown' => TRUE,
'inaccessibleContentShown' => TRUE
);
if ($languageIdentifiers !== NULL) {
$contextProperties = array_merge($contextProperties, array(
'dimensions' => array('language' => $languageIdentifiers)
));
}
return $this->contextFactory->create($contextProperties);
}
}
\ No newline at end of file
<?php
namespace Ttree\Neos\Sitemap\Service;
use Ttree\Neos\Sitemap\Domain\Model\SitemapDefinition;
use TYPO3\Flow\Annotations as Flow;
/**
* Sitemap Builder Interface
*/
interface SitemapBuilderInterface {
/**
* @param SitemapDefinition $definiton
* @return string
*/
public function build(SitemapDefinition $definiton);
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment