Initial commit
This commit is contained in:
@@ -0,0 +1,209 @@
|
||||
<?php
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2013-2014 Dmitry Dulepov <dmitry.dulepov@gmail.com>
|
||||
* All rights reserved
|
||||
*
|
||||
* This script is part of the TYPO3 project. The TYPO3 project 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.
|
||||
*
|
||||
* The GNU General Public License can be found at
|
||||
* http://www.gnu.org/copyleft/gpl.html.
|
||||
*
|
||||
* This script 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.
|
||||
*
|
||||
* This copyright notice MUST APPEAR in all copies of the script!
|
||||
***************************************************************/
|
||||
|
||||
namespace DmitryDulepov\DdGooglesitemap\Scheduler;
|
||||
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* This class provides information about additional fields for the scheduler
|
||||
* task of this extension. Additional fields are:
|
||||
* - the URL of the eID script (users can use different parameters for the script!)
|
||||
* - index file path (users will submit that to Google)
|
||||
* - maximum number of URLs in the sitemap (to prevent out of memory errors)
|
||||
*
|
||||
* WARNING! Due to incompatible TYPO3 6.2 changes this class now shows PHP errors
|
||||
* in TYPO3 4.5 and TYPO3 6.2 in the PhpStorm. These errors however do not happen
|
||||
* at runtime.
|
||||
*
|
||||
* @author Dmitry Dulepov <dmitry.dulepov@gmail.com>
|
||||
*/
|
||||
class AdditionalFieldsProvider implements \TYPO3\CMS\Scheduler\AdditionalFieldProviderInterface {
|
||||
|
||||
/**
|
||||
* Gets additional fields to render in the form to add/edit a task
|
||||
*
|
||||
* @param array $taskInfo Values of the fields from the add/edit task form
|
||||
* @param \TYPO3\CMS\Scheduler\Task\AbstractTask $task The task object being edited. Null when adding a task!
|
||||
* @param \TYPO3\CMS\Scheduler\Controller\SchedulerModuleController $schedulerModule Reference to the scheduler backend module
|
||||
* @return array A two dimensional array, array('Identifier' => array('fieldId' => array('code' => '', 'label' => '', 'cshKey' => '', 'cshLabel' => ''))
|
||||
*/
|
||||
public function getAdditionalFields(array &$taskInfo, $task, \TYPO3\CMS\Scheduler\Controller\SchedulerModuleController $schedulerModule) {
|
||||
/** @var \DmitryDulepov\DdGooglesitemap\Scheduler\Task $task */
|
||||
$additionalFields = array();
|
||||
|
||||
if (!$task) {
|
||||
$url = GeneralUtility::locationHeaderUrl('/index.php?eID=dd_googlesitemap');
|
||||
$task = GeneralUtility::makeInstance('DmitryDulepov\\DdGooglesitemap\\Scheduler\\Task');
|
||||
}
|
||||
else {
|
||||
$url = $task->getEIdScriptUrl();
|
||||
}
|
||||
$indexFilePath = $task->getIndexFilePath();
|
||||
$maxUrlsPerSitemap = $task->getMaxUrlsPerSitemap();
|
||||
|
||||
$additionalFields['eIdUrl'] = array(
|
||||
'code' => '<textarea style="width:350px;height:200px" name="tx_scheduler[eIdUrl]" wrap="off">' . htmlspecialchars($url) . '</textarea>',
|
||||
'label' => 'LLL:EXT:dd_googlesitemap/locallang.xml:scheduler.eIDFieldLabel',
|
||||
'cshKey' => '',
|
||||
'cshLabel' => ''
|
||||
);
|
||||
$additionalFields['indexFilePath'] = array(
|
||||
'code' => '<input class="wide" type="text" name="tx_scheduler[indexFilePath]" value="' . htmlspecialchars($indexFilePath) . '" />',
|
||||
'label' => 'LLL:EXT:dd_googlesitemap/locallang.xml:scheduler.indexFieldLabel',
|
||||
'cshKey' => '',
|
||||
'cshLabel' => ''
|
||||
);
|
||||
$additionalFields['maxUrlsPerSitemap'] = array(
|
||||
'code' => '<input type="text" name="tx_scheduler[maxUrlsPerSitemap]" value="' . $maxUrlsPerSitemap . '" />',
|
||||
'label' => 'LLL:EXT:dd_googlesitemap/locallang.xml:scheduler.maxUrlsPerSitemapLabel',
|
||||
'cshKey' => '',
|
||||
'cshLabel' => ''
|
||||
);
|
||||
|
||||
return $additionalFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the additional fields' values
|
||||
*
|
||||
* @param array $submittedData An array containing the data submitted by the add/edit task form
|
||||
* @param \TYPO3\CMS\Scheduler\Controller\SchedulerModuleController $schedulerModule Reference to the scheduler backend module
|
||||
* @return boolean TRUE if validation was ok (or selected class is not relevant), FALSE otherwise
|
||||
*/
|
||||
public function validateAdditionalFields(array &$submittedData, \TYPO3\CMS\Scheduler\Controller\SchedulerModuleController $schedulerModule) {
|
||||
$errors = array();
|
||||
|
||||
$this->validateEIdUrl($submittedData, $errors);
|
||||
$this->validateMaxUrlsPerSitemap($submittedData, $errors);
|
||||
$this->validateIndexFilePath($submittedData, $errors);
|
||||
|
||||
foreach ($errors as $error) {
|
||||
/** @noinspection PhpUndefinedMethodInspection */
|
||||
$error = $GLOBALS['LANG']->sL('LLL:EXT:dd_googlesitemap/locallang.xml:' . $error);
|
||||
$this->addErrorMessage($error);
|
||||
}
|
||||
|
||||
return count($errors) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes care of saving the additional fields' values in the task's object
|
||||
*
|
||||
* @param array $submittedData An array containing the data submitted by the add/edit task form
|
||||
* @param \TYPO3\CMS\Scheduler\Task\AbstractTask $task Reference to the scheduler backend module
|
||||
* @return void
|
||||
*/
|
||||
public function saveAdditionalFields(array $submittedData, \TYPO3\CMS\Scheduler\Task\AbstractTask $task) {
|
||||
/** @var \DmitryDulepov\DdGooglesitemap\Scheduler\Task $task */
|
||||
$task->setEIdScriptUrl($submittedData['eIdUrl']);
|
||||
$task->setMaxUrlsPerSitemap($submittedData['maxUrlsPerSitemap']);
|
||||
$task->setIndexFilePath($submittedData['indexFilePath']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a error message as a flash message.
|
||||
*
|
||||
* @param string $message
|
||||
* @return void
|
||||
*/
|
||||
protected function addErrorMessage($message) {
|
||||
$flashMessage = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Messaging\\FlashMessage',
|
||||
$message, '', \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR
|
||||
);
|
||||
/** @var \TYPO3\CMS\Core\Messaging\FlashMessage $flashMessage */
|
||||
$flashMessageService = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Messaging\\FlashMessageService');
|
||||
/** @var \TYPO3\CMS\Core\Messaging\FlashMessageService $flashMessageService */
|
||||
$flashMessageService->getMessageQueueByIdentifier()->enqueue($flashMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the number of urls per sitemap.
|
||||
*
|
||||
* @param array $submittedData
|
||||
* @param array $errors
|
||||
* @return void
|
||||
*/
|
||||
protected function validateMaxUrlsPerSitemap(array &$submittedData, array &$errors) {
|
||||
$submittedData['maxUrlsPerSitemap'] = intval($submittedData['maxUrlsPerSitemap']);
|
||||
if ($submittedData['maxUrlsPerSitemap'] <= 0) {
|
||||
$errors[] = 'scheduler.error.badNumberOfUrls';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates index file path.
|
||||
*
|
||||
* @param array $submittedData
|
||||
* @param array $errors
|
||||
* @return void
|
||||
*/
|
||||
protected function validateIndexFilePath(array &$submittedData, array &$errors) {
|
||||
if (GeneralUtility::isAbsPath($submittedData['indexFilePath'])) {
|
||||
$errors[] = 'scheduler.error.badIndexFilePath';
|
||||
}
|
||||
else {
|
||||
$testPath = GeneralUtility::getFileAbsFileName($submittedData['indexFilePath'], TRUE);
|
||||
if (!file_exists($testPath)) {
|
||||
if (!@touch($testPath)) {
|
||||
$errors[] = 'scheduler.error.badIndexFilePath';
|
||||
}
|
||||
else {
|
||||
unlink($testPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Valies the URL of the eID script.
|
||||
*
|
||||
* @param array $submittedData
|
||||
* @param array $errors
|
||||
*/
|
||||
protected function validateEIdUrl(array &$submittedData, array &$errors) {
|
||||
foreach (GeneralUtility::trimExplode(chr(10), $submittedData['eIdUrl']) as $url) {
|
||||
if (FALSE !== ($urlParts = parse_url($url))) {
|
||||
if (!$urlParts['host']) {
|
||||
$errors[] = 'scheduler.error.missingHost';
|
||||
}
|
||||
else {
|
||||
/** @noinspection PhpUndefinedMethodInspection */
|
||||
list($count) = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('COUNT(*) AS counter', 'sys_domain',
|
||||
'domainName=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($urlParts['host'], 'sys_domain')
|
||||
);
|
||||
if ($count['counter'] == 0) {
|
||||
$errors[] = 'scheduler.error.missingHost';
|
||||
}
|
||||
}
|
||||
if (!preg_match('/(?:^|&)eID=dd_googlesitemap/', $urlParts['query'])) {
|
||||
$errors[] = 'scheduler.error.badPath';
|
||||
}
|
||||
if (preg_match('/(?:^|&)(?:offset|limit)=/', $urlParts['query'])) {
|
||||
$errors[] = 'scheduler.error.badParameters';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
300
typo3conf/ext/dd_googlesitemap/Classes/Scheduler/Task.php
Normal file
300
typo3conf/ext/dd_googlesitemap/Classes/Scheduler/Task.php
Normal file
@@ -0,0 +1,300 @@
|
||||
<?php
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2013 Dmitry Dulepov <dmitry.dulepov@gmail.com>
|
||||
* All rights reserved
|
||||
*
|
||||
* This script is part of the TYPO3 project. The TYPO3 project 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.
|
||||
*
|
||||
* The GNU General Public License can be found at
|
||||
* http://www.gnu.org/copyleft/gpl.html.
|
||||
*
|
||||
* This script 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.
|
||||
*
|
||||
* This copyright notice MUST APPEAR in all copies of the script!
|
||||
***************************************************************/
|
||||
|
||||
namespace DmitryDulepov\DdGooglesitemap\Scheduler;
|
||||
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* This class provides a scheduler task to create sitemap index as required
|
||||
* by the Google sitemap protocol.
|
||||
*
|
||||
* @author Dmitry Dulepov <dmitry.dulepov@gmail.com>
|
||||
* @see http://support.google.com/webmasters/bin/answer.py?hl=en&answer=71453
|
||||
*/
|
||||
class Task extends \TYPO3\CMS\Scheduler\Task\AbstractTask {
|
||||
|
||||
const DEFAULT_FILE_PATH = 'typo3temp/dd_googlesitemap';
|
||||
|
||||
/** @var string */
|
||||
private $baseUrl;
|
||||
|
||||
/** @var string */
|
||||
protected $eIdScriptUrl;
|
||||
|
||||
/** @var string */
|
||||
protected $indexFilePath;
|
||||
|
||||
/** @var int */
|
||||
protected $maxUrlsPerSitemap = 50000;
|
||||
|
||||
/** @var string */
|
||||
private $sitemapFileFormat;
|
||||
|
||||
/** @var int */
|
||||
private $offset;
|
||||
|
||||
/**
|
||||
* Creates the instance of the class. This call initializes the index file
|
||||
* path to the random value. After the task is configured, the user may
|
||||
* change the file and the file name will be serialized with the task and
|
||||
* used later.
|
||||
*
|
||||
* @see __sleep
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
$this->indexFilePath = self::DEFAULT_FILE_PATH . '/' . GeneralUtility::getRandomHexString(24) . '.xml';
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconstructs some variables after the object is unserialized.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __wakeup() {
|
||||
$this->buildSitemapFileFormat();
|
||||
$this->buildBaseUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the main method that is called when a task is executed
|
||||
* It MUST be implemented by all classes inheriting from this one
|
||||
* Note that there is no error handling, errors and failures are expected
|
||||
* to be handled and logged by the client implementations.
|
||||
* Should return true on successful execution, false on error.
|
||||
*
|
||||
* @return boolean Returns true on successful execution, false on error
|
||||
*/
|
||||
public function execute() {
|
||||
$indexFilePathTemp = PATH_site . $this->indexFilePath . '.tmp';
|
||||
$indexFile = fopen($indexFilePathTemp, 'wt');
|
||||
fwrite($indexFile, '<?xml version="1.0" encoding="UTF-8"?>' . chr(10));
|
||||
fwrite($indexFile, '<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . chr(10));
|
||||
|
||||
$eIDscripts = GeneralUtility::trimExplode(chr(10), $this->eIdScriptUrl);
|
||||
$eIdIndex = 1;
|
||||
foreach ($eIDscripts as $eIdScriptUrl) {
|
||||
$this->offset = 0;
|
||||
$currentFileNumber = 1;
|
||||
$lastFileHash = '';
|
||||
do {
|
||||
$sitemapFileName = sprintf($this->sitemapFileFormat, $eIdIndex, $currentFileNumber++);
|
||||
$this->buildSitemap($eIdScriptUrl, $sitemapFileName);
|
||||
|
||||
$isSitemapEmpty = $this->isSitemapEmpty($sitemapFileName);
|
||||
$currentFileHash = $isSitemapEmpty ? -1 : md5_file(PATH_site . $sitemapFileName);
|
||||
$stopLoop = $isSitemapEmpty || ($currentFileHash == $lastFileHash);
|
||||
|
||||
if ($stopLoop) {
|
||||
@unlink(PATH_site . $sitemapFileName);
|
||||
}
|
||||
else {
|
||||
fwrite($indexFile, '<sitemap><loc>' . htmlspecialchars($this->makeSitemapUrl($sitemapFileName)) . '</loc></sitemap>' . chr(10));
|
||||
$lastFileHash = $currentFileHash;
|
||||
}
|
||||
} while (!$stopLoop);
|
||||
$eIdIndex++;
|
||||
}
|
||||
|
||||
fwrite($indexFile, '</sitemapindex>' . chr(10));
|
||||
fclose($indexFile);
|
||||
|
||||
@unlink(PATH_site . $this->indexFilePath);
|
||||
rename($indexFilePathTemp, PATH_site . $this->indexFilePath);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is designed to return some additional information about the task,
|
||||
* that may help to set it apart from other tasks from the same class
|
||||
* This additional information is used - for example - in the Scheduler's BE module
|
||||
* This method should be implemented in most task classes
|
||||
*
|
||||
* @return string Information to display
|
||||
*/
|
||||
public function getAdditionalInformation() {
|
||||
/** @noinspection PhpUndefinedMethodInspection */
|
||||
$format = $GLOBALS['LANG']->sL('LLL:EXT:dd_googlesitemap/locallang.xml:scheduler.extra_info');
|
||||
return sprintf($format, $this->getIndexFileUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the url of the eID script. This is called from the task
|
||||
* configuration inside scheduler.
|
||||
*
|
||||
* @return string
|
||||
* @see tx_ddgooglesitemap_additionalfieldsprovider
|
||||
*/
|
||||
public function getEIdScriptUrl() {
|
||||
return $this->eIdScriptUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index file path. This is called from the task
|
||||
* configuration inside scheduler.
|
||||
*
|
||||
* @return string
|
||||
* @see tx_ddgooglesitemap_additionalfieldsprovider
|
||||
*/
|
||||
public function getIndexFilePath() {
|
||||
return $this->indexFilePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the number of urls per sitemap. This is called from the task
|
||||
* configuration inside scheduler.
|
||||
*
|
||||
* @return int
|
||||
* @see tx_ddgooglesitemap_additionalfieldsprovider
|
||||
*/
|
||||
public function getMaxUrlsPerSitemap() {
|
||||
return $this->maxUrlsPerSitemap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the URl of the eID script. This is called from the task
|
||||
* configuration inside scheduler.
|
||||
*
|
||||
* @param $url
|
||||
* @see tx_ddgooglesitemap_additionalfieldsprovider
|
||||
*/
|
||||
public function setEIdScriptUrl($url) {
|
||||
$this->eIdScriptUrl = $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the URL of the eID script. This is called from the task
|
||||
* configuration inside scheduler.
|
||||
*
|
||||
* @param string $path
|
||||
* @see tx_ddgooglesitemap_additionalfieldsprovider
|
||||
*/
|
||||
public function setIndexFilePath($path) {
|
||||
$this->indexFilePath = $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of URLs per sitemap. This is called from the task
|
||||
* configuration inside scheduler.
|
||||
*
|
||||
* @param int $maxUrlsPerSitemap
|
||||
* @see tx_ddgooglesitemap_additionalfieldsprovider
|
||||
*/
|
||||
public function setMaxUrlsPerSitemap($maxUrlsPerSitemap) {
|
||||
$this->maxUrlsPerSitemap = $maxUrlsPerSitemap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a base url for sitemaps.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function buildBaseUrl() {
|
||||
$urlParts = parse_url($this->eIdScriptUrl);
|
||||
$this->baseUrl = $urlParts['scheme'] . '://';
|
||||
if ($urlParts['user']) {
|
||||
$this->baseUrl .= $urlParts['user'];
|
||||
if ($urlParts['pass']) {
|
||||
$this->baseUrl .= ':' . $urlParts['pass'];
|
||||
}
|
||||
$this->baseUrl .= '@';
|
||||
}
|
||||
$this->baseUrl .= $urlParts['host'];
|
||||
if ($urlParts['port']) {
|
||||
$this->baseUrl .= ':' . $urlParts['port'];
|
||||
}
|
||||
$this->baseUrl .= '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the sitemap.
|
||||
*
|
||||
* @param string $eIdScriptUrl
|
||||
* @param string $sitemapFileName
|
||||
* @see tx_ddgooglesitemap_additionalfieldsprovider
|
||||
*/
|
||||
protected function buildSitemap($eIdScriptUrl, $sitemapFileName) {
|
||||
$url = $eIdScriptUrl . sprintf('&offset=%d&limit=%d', $this->offset, $this->maxUrlsPerSitemap);
|
||||
|
||||
$content = GeneralUtility::getURL($url);
|
||||
if ($content) {
|
||||
file_put_contents(PATH_site . $sitemapFileName, $content);
|
||||
$this->offset += $this->maxUrlsPerSitemap;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the format string for the sitemap files.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function buildSitemapFileFormat() {
|
||||
$fileParts = pathinfo($this->indexFilePath);
|
||||
$this->sitemapFileFormat = $fileParts['dirname'] . '/' . $fileParts['filename'] . '_sitemap_%05d_%05d.xml';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index file url.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getIndexFileUrl() {
|
||||
return $this->baseUrl . $this->indexFilePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current sitemap has no entries. The function reads a chunk
|
||||
* of the file, which is large enough to have a '<url>' token in it and
|
||||
* examines the chunk. If the token is not found, than the sitemap is either
|
||||
* empty or corrupt.
|
||||
*
|
||||
* @param string $sitemapFileName
|
||||
* @return bool
|
||||
*/
|
||||
protected function isSitemapEmpty($sitemapFileName) {
|
||||
$result = TRUE;
|
||||
|
||||
$fileDescriptor = @fopen(PATH_site . $sitemapFileName, 'rt');
|
||||
if ($fileDescriptor) {
|
||||
$chunkSizeToCheck = 10240;
|
||||
$testString = fread($fileDescriptor, $chunkSizeToCheck);
|
||||
fclose($fileDescriptor);
|
||||
$result = (strpos($testString, '<url>') === FALSE);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a url to the sitemap.
|
||||
*
|
||||
* @param string $siteMapPath
|
||||
* @return string
|
||||
*/
|
||||
protected function makeSitemapUrl($siteMapPath) {
|
||||
return $this->baseUrl . $siteMapPath;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user