Unverified Commit 51e0081e authored by Dave Reid's avatar Dave Reid

Merge branch '8.x-1.x' into 2.x

parents 9c5ca515 db623fda
...@@ -3,13 +3,15 @@ ...@@ -3,13 +3,15 @@
namespace Drupal\xmlsitemap\Controller; namespace Drupal\xmlsitemap\Controller;
use Drupal\Component\Serialization\Json; use Drupal\Component\Serialization\Json;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Controller\ControllerBase; use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\State\StateInterface;
use Drupal\xmlsitemap\Entity\XmlSitemap; use Drupal\xmlsitemap\Entity\XmlSitemap;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\State\StateInterface; use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/** /**
* Class for Xml Sitemap Controller. * Class for Xml Sitemap Controller.
...@@ -26,14 +28,24 @@ class XmlSitemapController extends ControllerBase { ...@@ -26,14 +28,24 @@ class XmlSitemapController extends ControllerBase {
*/ */
protected $state; protected $state;
/**
* The configuration factory.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;
/** /**
* Constructs a new XmlSitemapController object. * Constructs a new XmlSitemapController object.
* *
* @param \Drupal\Core\State\StateInterface $state * @param \Drupal\Core\State\StateInterface $state
* The state service. * 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->state = $state;
$this->configFactory = $config_factory;
} }
/** /**
...@@ -41,7 +53,8 @@ class XmlSitemapController extends ControllerBase { ...@@ -41,7 +53,8 @@ class XmlSitemapController extends ControllerBase {
*/ */
public static function create(ContainerInterface $container) { public static function create(ContainerInterface $container) {
return new static( return new static(
$container->get('state') $container->get('state'),
$container->get('config.factory')
); );
} }
...@@ -51,11 +64,12 @@ class XmlSitemapController extends ControllerBase { ...@@ -51,11 +64,12 @@ class XmlSitemapController extends ControllerBase {
* @param \Symfony\Component\HttpFoundation\Request $request * @param \Symfony\Component\HttpFoundation\Request $request
* The request object. * The request object.
* *
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException * @return \Symfony\Component\HttpFoundation\BinaryFileResponse
*
* @return \Symfony\Component\HttpFoundation\Response
* The sitemap in XML format or plain text if xmlsitemap_developer_mode flag * The sitemap in XML format or plain text if xmlsitemap_developer_mode flag
* is set. * 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) { public function renderSitemapXml(Request $request) {
$headers = []; $headers = [];
...@@ -82,13 +96,59 @@ class XmlSitemapController extends ControllerBase { ...@@ -82,13 +96,59 @@ class XmlSitemapController extends ControllerBase {
$headers['X-XmlSitemap-Cache-Hit'] = file_exists($file) ? 'HIT' : 'MISS'; $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)) { if (!is_file($file) || !is_readable($file)) {
$exception = new NotFoundHttpException(); $exception = new NotFoundHttpException();
$exception->setHeaders($headers); $exception->setHeaders($headers);
throw $exception; 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 { ...@@ -122,10 +182,10 @@ class XmlSitemapController extends ControllerBase {
$xsl_content = strtr($xsl_content, $replacements); $xsl_content = strtr($xsl_content, $replacements);
// Output the XSL content. // Output the XSL content.
$response = new Response($xsl_content); return new Response($xsl_content, 200, [
$response->headers->set('Content-type', 'application/xml; charset=utf-8'); 'Content-Type' => 'application/xml; charset=utf-8',
$response->headers->set('X-Robots-Tag', 'noindex, nofollow'); 'X-Robots-Tag' => 'noindex, nofollow',
return $response; ]);
} }
} }
...@@ -72,16 +72,7 @@ class XmlSitemapLinkBundleSettingsForm extends ConfigFormBase { ...@@ -72,16 +72,7 @@ class XmlSitemapLinkBundleSettingsForm extends ConfigFormBase {
$this->bundle_type = $bundle; $this->bundle_type = $bundle;
$request = $this->getRequest(); $request = $this->getRequest();
if (!$request->isXmlHttpRequest() && $admin_path = xmlsitemap_get_bundle_path($entity, $bundle)) { $form['#title'] = $this->t('@bundle XML sitemap settings', ['@bundle' => $bundle]);
// If this is a non-ajax form, redirect to the bundle administration page.
$destination = $this->getDestinationArray();
$request->query->remove('destination');
$url = Url::fromUri($admin_path, ['query' => [$destination]]);
return new RedirectResponse($url);
}
else {
$form['#title'] = $this->t('@bundle XML sitemap settings', ['@bundle' => $bundle]);
}
xmlsitemap_add_link_bundle_settings($form, $form_state, $entity, $bundle); xmlsitemap_add_link_bundle_settings($form, $form_state, $entity, $bundle);
$form['xmlsitemap']['#type'] = 'markup'; $form['xmlsitemap']['#type'] = 'markup';
......
...@@ -8,6 +8,7 @@ use Drupal\Core\Config\ConfigFactoryInterface; ...@@ -8,6 +8,7 @@ use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Database\Connection; use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\File\Exception\DirectoryNotReadyException;
use Drupal\Core\File\FileSystemInterface; use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Language\LanguageManagerInterface;
...@@ -402,7 +403,9 @@ class XmlSitemapGenerator implements XmlSitemapGeneratorInterface { ...@@ -402,7 +403,9 @@ class XmlSitemapGenerator implements XmlSitemapGeneratorInterface {
$context['sandbox']['max'] = XMLSITEMAP_MAX_SITEMAP_LINKS; $context['sandbox']['max'] = XMLSITEMAP_MAX_SITEMAP_LINKS;
// Clear the cache directory for this sitemap before generating any files. // Clear the cache directory for this sitemap before generating any files.
xmlsitemap_check_directory($context['sandbox']['sitemap']); if (!xmlsitemap_check_directory($context['sandbox']['sitemap'])) {
throw new DirectoryNotReadyException("The sitemap directory could not be created or is not writable.");
}
xmlsitemap_clear_directory($context['sandbox']['sitemap']); xmlsitemap_clear_directory($context['sandbox']['sitemap']);
} }
...@@ -523,17 +526,18 @@ class XmlSitemapGenerator implements XmlSitemapGeneratorInterface { ...@@ -523,17 +526,18 @@ class XmlSitemapGenerator implements XmlSitemapGeneratorInterface {
} }
$info = $context['sandbox']['info']; $info = $context['sandbox']['info'];
$entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
$query = $this->entityTypeManager->getStorage($entity_type_id)->getQuery(); $query = $this->entityTypeManager->getStorage($entity_type_id)->getQuery();
$query->condition($entity_type->getKey('id'), $context['sandbox']['last_id'], '>'); $query->condition($info['entity keys']['id'], $context['sandbox']['last_id'], '>');
if ($entity_type->hasKey('bundle')) { if (!empty($info['entity keys']['bundle'])) {
$query->condition($entity_type->getKey('bundle'), $context['sandbox']['bundles'], 'IN'); $query->condition($info['entity keys']['bundle'], $context['sandbox']['bundles'], 'IN');
} }
$query->addTag('xmlsitemap_link_bundle_access');
// Access for entities is checked individually for the anonymous user
// when each item is processed. We can skip the access check for the
// query.
$query->accessCheck(FALSE);
$query->addTag('xmlsitemap_rebuild'); $query->addTag('xmlsitemap_rebuild');
$query->addMetaData('entity_type_id', $entity_type_id);
$query->addMetaData('entity_info', $info);
if (!isset($context['sandbox']['max'])) { if (!isset($context['sandbox']['max'])) {
$count_query = clone $query; $count_query = clone $query;
...@@ -546,7 +550,7 @@ class XmlSitemapGenerator implements XmlSitemapGeneratorInterface { ...@@ -546,7 +550,7 @@ class XmlSitemapGenerator implements XmlSitemapGeneratorInterface {
} }
// PostgreSQL cannot have the ORDERED BY in the count query. // PostgreSQL cannot have the ORDERED BY in the count query.
$query->sort($entity_type->getKey('id')); $query->sort($info['entity keys']['id']);
// Get batch limit. // Get batch limit.
$limit = $this->config->get('batch_limit'); $limit = $this->config->get('batch_limit');
......
...@@ -29,23 +29,8 @@ class DirectoryTest extends KernelTestBase { ...@@ -29,23 +29,8 @@ class DirectoryTest extends KernelTestBase {
$fileSystem->saveData('File unrelated to XML sitemap', 'public://file.txt'); $fileSystem->saveData('File unrelated to XML sitemap', 'public://file.txt');
$fileSystem->saveData('Test contents', 'public://xmlsitemap/test/index.xml'); $fileSystem->saveData('Test contents', 'public://xmlsitemap/test/index.xml');
// Set the directory to an empty value.
\Drupal::configFactory()->getEditable('xmlsitemap.settings')->clear('path')->save();
drupal_static_reset('xmlsitemap_get_directory');
$result = xmlsitemap_clear_directory(NULL, TRUE);
// Test that nothing was deleted.
$this->assertFileExists('public://xmlsitemap/test/index.xml');
$this->assertDirectoryExists('public://not-xmlsitemap');
$this->assertFileExists('public://file.txt');
$this->assertFalse($result);
// Reset the value back to the default.
\Drupal::configFactory()->getEditable('xmlsitemap.settings')->set('path', 'xmlsitemap')->save();
drupal_static_reset('xmlsitemap_get_directory');
$result = xmlsitemap_clear_directory(NULL, TRUE);
// Test that only the xmlsitemap directory was deleted. // Test that only the xmlsitemap directory was deleted.
$result = xmlsitemap_clear_directory(NULL, TRUE);
$this->assertDirectoryNotExists('public://xmlsitemap/test'); $this->assertDirectoryNotExists('public://xmlsitemap/test');
$this->assertDirectoryExists('public://not-xmlsitemap'); $this->assertDirectoryExists('public://not-xmlsitemap');
$this->assertFileExists('public://file.txt'); $this->assertFileExists('public://file.txt');
......
...@@ -28,11 +28,6 @@ function hook_xmlsitemap_link_info() { ...@@ -28,11 +28,6 @@ function hook_xmlsitemap_link_info() {
'bundles' => [ 'bundles' => [
'mysubtype1' => [ 'mysubtype1' => [
'label' => t('My subtype 1'), 'label' => t('My subtype 1'),
// If your bundles have an administrative UI, list it.
'admin' => [
'real path' => 'admin/settings/mymodule/mysubtype1/edit',
'access arguments' => ['administer mymodule'],
],
'xmlsitemap' => [ 'xmlsitemap' => [
'status' => XMLSITEMAP_STATUS_DEFAULT, 'status' => XMLSITEMAP_STATUS_DEFAULT,
'priority' => XMLSITEMAP_PRIORITY_DEFAULT, 'priority' => XMLSITEMAP_PRIORITY_DEFAULT,
......
This diff is collapsed.
...@@ -6,9 +6,7 @@ ...@@ -6,9 +6,7 @@
*/ */
/** /**
* Force cache to be cleared with new hook_entity_type_build(). * Force cache clear for new hook_entity_type_build().
*
* @see https://www.drupal.org/project/xmlsitemap/issues/3079398
*/ */
function xmlsitemap_post_update_entity_type_build_hook() { function xmlsitemap_post_update_entity_type_build_hook() {
// Empty post-update hook. // Empty post-update hook.
......
...@@ -78,6 +78,9 @@ xmlsitemap.sitemap_xml: ...@@ -78,6 +78,9 @@ xmlsitemap.sitemap_xml:
requirements: requirements:
# Access is open because crawlers need to be able to access the sitemap. # Access is open because crawlers need to be able to access the sitemap.
_access: 'TRUE' _access: 'TRUE'
options:
# Bypass the page cache for this route because it may contain large files.
no_cache: 'TRUE'
xmlsitemap.sitemap_xsl: xmlsitemap.sitemap_xsl:
path: '/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