Unverified Commit 55ac401b authored by Dave Reid's avatar Dave Reid Committed by Dave Reid

Issue #2869214 by Dave Reid, Chewie: Removed xmlsitemap_file_transfer() since...

Issue #2869214 by Dave Reid, Chewie: Removed xmlsitemap_file_transfer() since it conflicts with File Entity's hook_file_transfer(). Deprecated xmlsitemap_file_output() in favor of BinaryFileResponse.
parent e9531bc5
......@@ -3,13 +3,15 @@
namespace Drupal\xmlsitemap\Controller;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\State\StateInterface;
use Drupal\xmlsitemap\Entity\XmlSitemap;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\State\StateInterface;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class for Xml Sitemap Controller.
......@@ -26,14 +28,24 @@ class XmlSitemapController extends ControllerBase {
*/
protected $state;
/**
* The configuration factory.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;
/**
* Constructs a new XmlSitemapController object.
*
* @param \Drupal\Core\State\StateInterface $state
* The state service.
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The configuration factory.
*/
public function __construct(StateInterface $state) {
public function __construct(StateInterface $state, ConfigFactoryInterface $config_factory) {
$this->state = $state;
$this->configFactory = $config_factory;
}
/**
......@@ -41,7 +53,8 @@ class XmlSitemapController extends ControllerBase {
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('state')
$container->get('state'),
$container->get('config.factory')
);
}
......@@ -51,11 +64,12 @@ class XmlSitemapController extends ControllerBase {
* @param \Symfony\Component\HttpFoundation\Request $request
* The request object.
*
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
*
* @return \Symfony\Component\HttpFoundation\Response
* @return \Symfony\Component\HttpFoundation\BinaryFileResponse
* The sitemap in XML format or plain text if xmlsitemap_developer_mode flag
* is set.
*
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
* If the sitemap is not found or the sitemap file is not readable.
*/
public function renderSitemapXml(Request $request) {
$headers = [];
......@@ -82,13 +96,59 @@ class XmlSitemapController extends ControllerBase {
$headers['X-XmlSitemap-Cache-Hit'] = file_exists($file) ? 'HIT' : 'MISS';
}
return $this->getSitemapResponse($file, $request, $headers);
}
/**
* Creates a response object that will output the sitemap file.
*
* @param string $file
* File uri.
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request.
* @param array $headers
* An array of response headers
*
* @return \Symfony\Component\HttpFoundation\BinaryFileResponse
* The sitemap response object.
*
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
* If the sitemap is not found or the sitemap file is not readable.
*/
public function getSitemapResponse($file, Request $request, array $headers = []) {
if (!is_file($file) || !is_readable($file)) {
$exception = new NotFoundHttpException();
$exception->setHeaders($headers);
throw $exception;
}
return xmlsitemap_output_file(new Response(), $file, $headers);
$headers += [
'Content-Type' => 'text/xml; charset=utf-8',
'X-Robots-Tag' => 'noindex, follow',
];
$lifetime = $this->configFactory->get('xmlsitemap.settings')->get('minimum_lifetime');
$response = new BinaryFileResponse($file, 200, $headers);
$response->setPrivate();
$response->headers->addCacheControlDirective('must-revalidate');
//if ($lifetime) {
// $response->headers->addCacheControlDirective('max-age', $lifetime);
//}
// Manually set the etag value instead of hashing the contents of the file.
$last_modified = $response->getFile()->getMTime();
$response->setEtag(md5($last_modified));
// Set expiration using the minimum lifetime.
$response->setExpires(new \DateTime('@' . ($last_modified + $lifetime)));
// Because we do not want this page to be cached, we manually check the
// modified headers.
$response->isNotModified($request);
return $response;
}
/**
......@@ -122,10 +182,10 @@ class XmlSitemapController extends ControllerBase {
$xsl_content = strtr($xsl_content, $replacements);
// Output the XSL content.
$response = new Response($xsl_content);
$response->headers->set('Content-type', 'application/xml; charset=utf-8');
$response->headers->set('X-Robots-Tag', 'noindex, nofollow');
return $response;
return new Response($xsl_content, 200, [
'Content-Type' => 'application/xml; charset=utf-8',
'X-Robots-Tag' => 'noindex, nofollow',
]);
}
}
......@@ -30,11 +30,10 @@ use Drupal\Core\Link;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Site\Settings;
use Drupal\Core\Url;
use Drupal\xmlsitemap\Controller\XmlSitemapController;
use Drupal\xmlsitemap\Entity\XmlSitemap;
use Drupal\xmlsitemap\XmlSitemapInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* The maximum number of links in one sitemap chunk file.
......@@ -2340,90 +2339,23 @@ function xmlsitemap_get_current_chunk(XmlSitemapInterface $sitemap, Request $req
* @param array $headers
* Headers of the response.
*
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
* Throws an exception when sitemap xml file does not exist.
*
* @return \Symfony\Component\HttpFoundation\Response
* Complete response object.
*/
function xmlsitemap_output_file(Response $response, $file, array $headers = []) {
if (!file_exists($file) || !is_readable($file)) {
$exception = new NotFoundHttpException();
$exception->setHeaders($headers);
throw $exception;
}
$mtime = filemtime($file);
$last_modified = gmdate(DATE_RFC1123, $mtime);
$etag = '"' . md5($last_modified) . '"';
// See if the client has provided the required HTTP headers.
$request = \Drupal::request();
$if_modified_since = $request->server->has('HTTP_IF_MODIFIED_SINCE') ? stripslashes($request->server->get('HTTP_IF_MODIFIED_SINCE')) : FALSE;
$if_none_match = $request->server->has('HTTP_IF_NONE_MATCH') ? stripslashes($request->server->get('HTTP_IF_NONE_MATCH')) : FALSE;
if ($if_modified_since && $if_none_match && $if_none_match == $etag && $if_modified_since == $last_modified) {
$response->headers->add($headers);
$response->setNotModified();
// All 304 responses must send an etag if the 200 response for the same
// object contained an etag.
$response->headers->set('Etag', $etag);
return $response;
}
$headers += [
'Content-type' => 'text/xml; charset=utf-8',
'Content-length' => filesize($file),
'Last-modified' => $last_modified,
'Etag' => $etag,
'Expires' => gmdate(DATE_RFC1123, $mtime + \Drupal::config('xmlsitemap.settings')->get('minimum_lifetime')),
'Cache-Control' => 'must-revalidate',
'X-Robots-Tag' => 'noindex, follow',
];
$response = xmlsitemap_transfer_file($response, $file, $headers);
return $response;
}
/**
* Read a file and put content into response.
* @return \Symfony\Component\HttpFoundation\BinaryFileResponse
* The sitemap response object.
*
* @param \Symfony\Component\HttpFoundation\Response $response
* Response object.
* @param string $uri
* File uri.
* @param array $headers
* Response headers.
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
* If the sitemap is not found or the sitemap file is not readable.
*
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException::__construct
* Throws an exception when file is not readable.
* @deprecated in xmlsitemap:8.x-1.0 and is removed from xmlsitemap:2.0.0. Use
* \Drupal\xmlsitemap\Controller\XmlSitemapController::getSitemapResponse()
* instead.
*
* @return \Symfony\Component\HttpFoundation\Response
* Updated response.
* @see https://www.drupal.org/project/xmlsitemap/issues/2869214
*/
function xmlsitemap_transfer_file(Response $response, $uri, array $headers) {
if (ob_get_level()) {
ob_end_clean();
}
foreach ($headers as $name => $value) {
$response->headers->set($name, $value);
}
$content = '';
// Attempt to increase time to transfer file.
Environment::setTimeLimit(240);
$scheme = \Drupal::config('system.file')->get('default_scheme');
// Transfer file in 16 KB chunks to save memory usage.
if ($scheme && \Drupal::service('stream_wrapper_manager')->isValidScheme($scheme) && $fd = fopen($uri, 'rb')) {
while (!feof($fd)) {
$content .= fread($fd, 1024 * 16);
}
fclose($fd);
$response->setContent($content);
}
else {
throw new NotFoundHttpException();
}
return $response;
function xmlsitemap_output_file(Response $response, $file, array $headers = []) {
@trigger_error(__FUNCTION__ . ' is deprecated in xmlsitemap:8.x-1.0 and will be removed in xmlsitemap:2.0.0. Use \Drupal\xmlsitemap\Controller\XmlSitemapController::getSitemapResponse. See https://www.drupal.org/project/xmlsitemap/issues/2869214', E_USER_DEPRECATED);
/** @var \Drupal\xmlsitemap\Controller\XmlSitemapController $controller */
$controller = \Drupal::classResolver(XmlSitemapController::class);
return $controller->getSitemapResponse($file, \Drupal::request(), $headers + $response->headers->all());
}
/**
......
......@@ -78,6 +78,9 @@ xmlsitemap.sitemap_xml:
requirements:
# Access is open because crawlers need to be able to access the sitemap.
_access: 'TRUE'
options:
# Bypass the page cache for this route because it may contain large files.
no_cache: 'TRUE'
xmlsitemap.sitemap_xsl:
path: '/sitemap.xsl'
......
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