...
 
Commits (7)
......@@ -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',
]);
}
}
......@@ -72,16 +72,7 @@ class XmlSitemapLinkBundleSettingsForm extends ConfigFormBase {
$this->bundle_type = $bundle;
$request = $this->getRequest();
if (!$request->isXmlHttpRequest() && $admin_path = xmlsitemap_get_bundle_path($entity, $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]);
}
$form['#title'] = $this->t('@bundle XML sitemap settings', ['@bundle' => $bundle]);
xmlsitemap_add_link_bundle_settings($form, $form_state, $entity, $bundle);
$form['xmlsitemap']['#type'] = 'markup';
......
......@@ -8,6 +8,7 @@ use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\File\Exception\DirectoryNotReadyException;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Language\LanguageManagerInterface;
......@@ -402,7 +403,9 @@ class XmlSitemapGenerator implements XmlSitemapGeneratorInterface {
$context['sandbox']['max'] = XMLSITEMAP_MAX_SITEMAP_LINKS;
// 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']);
}
......@@ -523,17 +526,18 @@ class XmlSitemapGenerator implements XmlSitemapGeneratorInterface {
}
$info = $context['sandbox']['info'];
$entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
$query = $this->entityTypeManager->getStorage($entity_type_id)->getQuery();
$query->condition($entity_type->getKey('id'), $context['sandbox']['last_id'], '>');
if ($entity_type->hasKey('bundle')) {
$query->condition($entity_type->getKey('bundle'), $context['sandbox']['bundles'], 'IN');
$query->condition($info['entity keys']['id'], $context['sandbox']['last_id'], '>');
if (!empty($info['entity keys']['bundle'])) {
$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->addMetaData('entity_type_id', $entity_type_id);
$query->addMetaData('entity_info', $info);
if (!isset($context['sandbox']['max'])) {
$count_query = clone $query;
......@@ -546,7 +550,7 @@ class XmlSitemapGenerator implements XmlSitemapGeneratorInterface {
}
// 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.
$limit = $this->config->get('batch_limit');
......
......@@ -29,23 +29,8 @@ class DirectoryTest extends KernelTestBase {
$fileSystem->saveData('File unrelated to XML sitemap', 'public://file.txt');
$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.
$result = xmlsitemap_clear_directory(NULL, TRUE);
$this->assertDirectoryNotExists('public://xmlsitemap/test');
$this->assertDirectoryExists('public://not-xmlsitemap');
$this->assertFileExists('public://file.txt');
......
......@@ -28,11 +28,6 @@ function hook_xmlsitemap_link_info() {
'bundles' => [
'mysubtype1' => [
'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' => [
'status' => XMLSITEMAP_STATUS_DEFAULT,
'priority' => XMLSITEMAP_PRIORITY_DEFAULT,
......
This diff is collapsed.
......@@ -6,9 +6,7 @@
*/
/**
* Force cache to be cleared with new hook_entity_type_build().
*
* @see https://www.drupal.org/project/xmlsitemap/issues/3079398
* Force cache clear for new hook_entity_type_build().
*/
function xmlsitemap_post_update_entity_type_build_hook() {
// Empty post-update hook.
......
......@@ -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'
......