View source
<?php
namespace Drupal\xmlsitemap;
use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\Database\Connection;
use Drupal\Core\Database\Query\Merge;
use Drupal\Core\Entity\EntityChangedInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Session\AnonymousUserSession;
use Drupal\Core\State\StateInterface;
use Drupal\menu_link_content\MenuLinkContentInterface;
use Symfony\Component\Routing\Exception\RouteNotFoundException;
class XmlSitemapLinkStorage implements XmlSitemapLinkStorageInterface {
protected $state;
protected $moduleHandler;
protected $anonymousUser;
protected $connection;
public function __construct(StateInterface $state, ModuleHandlerInterface $module_handler, Connection $connection) {
$this->state = $state;
$this->moduleHandler = $module_handler;
$this->anonymousUser = new AnonymousUserSession();
$this->connection = $connection;
}
public function create(EntityInterface $entity) {
if (!isset($entity->xmlsitemap)) {
$entity->xmlsitemap = [];
if ($entity
->id() && ($link = $this
->load($entity
->getEntityTypeId(), $entity
->id()))) {
$entity->xmlsitemap = $link;
}
}
$settings = xmlsitemap_link_bundle_load($entity
->getEntityTypeId(), $entity
->bundle());
$entity->xmlsitemap += [
'type' => $entity
->getEntityTypeId(),
'id' => (string) $entity
->id(),
'subtype' => $entity
->bundle(),
'status' => (int) $settings['status'],
'status_default' => (int) $settings['status'],
'status_override' => 0,
'priority' => $settings['priority'],
'priority_default' => $settings['priority'],
'priority_override' => 0,
'changefreq' => isset($settings['changefreq']) ? $settings['changefreq'] : 0,
];
if ($entity instanceof EntityChangedInterface) {
$entity->xmlsitemap['lastmod'] = $entity
->getChangedTime();
}
try {
if ($entity instanceof MenuLinkContentInterface) {
$url = $entity
->getUrlObject();
if ($url
->isRouted()) {
if ($url
->getRouteName() === '<nolink>') {
$loc = '';
}
else {
$loc = $url
->getInternalPath();
}
}
else {
$loc = file_url_transform_relative($url
->toString());
if (UrlHelper::isExternal($loc)) {
$loc = '';
}
}
$access = $url
->access($this->anonymousUser);
}
else {
$loc = $entity
->id() && $entity
->hasLinkTemplate('canonical') ? $entity
->toUrl()
->getInternalPath() : '';
$access = $entity
->access('view', $this->anonymousUser);
}
} catch (RouteNotFoundException $e) {
$loc = '';
}
$entity->xmlsitemap['loc'] = '/' . ltrim($loc, '/');
$entity->xmlsitemap['access'] = $loc && $access;
$language = $entity
->language();
$entity->xmlsitemap['language'] = !empty($language) ? $language
->getId() : LanguageInterface::LANGCODE_NOT_SPECIFIED;
return $entity->xmlsitemap;
}
public function save(array $link, array $context = []) {
$link += [
'access' => 1,
'status' => 1,
'status_override' => 0,
'lastmod' => 0,
'priority' => XMLSITEMAP_PRIORITY_DEFAULT,
'priority_override' => 0,
'changefreq' => 0,
'changecount' => 0,
'language' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
];
$this->moduleHandler
->alter('xmlsitemap_link', $link, $context);
if ($link['priority'] < 0 || $link['priority'] > 1) {
trigger_error("The XML sitemap link for {$link['type']} {$link['id']} has an invalid priority of {$link['priority']}.<br/>" . var_export($link, TRUE), E_USER_ERROR);
}
if ($link['changecount'] < 0) {
trigger_error("The XML sitemap link for {$link['type']} {$link['id']} has a negative changecount value. Please report this to https://www.drupal.org/node/516928.<br/>" . var_export($link, TRUE), E_USER_ERROR);
$link['changecount'] = 0;
}
if ($link['loc'][0] !== '/') {
trigger_error("The XML sitemap link path {$link['loc']} for {$link['type']} {$link['id']} is invalid because it does not start with a slash.", E_USER_ERROR);
}
if (!$this->state
->get('xmlsitemap_regenerate_needed')) {
$this
->checkChangedLink($link, NULL, TRUE);
}
$queryStatus = $this->connection
->merge('xmlsitemap')
->keys([
'type' => $link['type'],
'id' => $link['id'],
'language' => $link['language'],
])
->fields([
'loc' => $link['loc'],
'subtype' => $link['subtype'],
'access' => (int) $link['access'],
'status' => (int) $link['status'],
'status_override' => $link['status_override'],
'lastmod' => $link['lastmod'],
'priority' => $link['priority'],
'priority_override' => $link['priority_override'],
'changefreq' => $link['changefreq'],
'changecount' => $link['changecount'],
])
->execute();
switch ($queryStatus) {
case Merge::STATUS_INSERT:
$this->moduleHandler
->invokeAll('xmlsitemap_link_insert', [
$link,
$context,
]);
break;
case Merge::STATUS_UPDATE:
$this->moduleHandler
->invokeAll('xmlsitemap_link_update', [
$link,
$context,
]);
break;
}
return $link;
}
public function checkChangedLink(array $link, array $original_link = NULL, $flag = FALSE) {
$changed = FALSE;
if ($original_link === NULL) {
$original_link = $this->connection
->queryRange("SELECT loc, access, status, lastmod, priority, changefreq, changecount, language FROM {xmlsitemap} WHERE type = :type AND id = :id", 0, 1, [
':type' => $link['type'],
':id' => $link['id'],
])
->fetchAssoc();
}
if (!$original_link) {
if ($link['access'] && $link['status']) {
$changed = TRUE;
}
}
else {
if (!($original_link['access'] && $original_link['status']) && $link['access'] && $link['status']) {
$changed = TRUE;
}
elseif ($original_link['access'] && $original_link['status'] && array_diff_assoc($original_link, $link)) {
$changed = TRUE;
}
}
if ($changed && $flag) {
$this->state
->set('xmlsitemap_regenerate_needed', TRUE);
}
return $changed;
}
public function checkChangedLinks(array $conditions = [], array $updates = [], $flag = FALSE) {
$conditions['status'] = !empty($updates['status']) && empty($conditions['status']) ? 0 : 1;
$conditions['access'] = !empty($updates['access']) && empty($conditions['access']) ? 0 : 1;
$query = $this->connection
->select('xmlsitemap');
$query
->addExpression('1');
foreach ($conditions as $field => $value) {
$operator = is_array($value) ? 'IN' : '=';
$query
->condition($field, $value, $operator);
}
$query
->range(0, 1);
$changed = $query
->execute()
->fetchField();
if ($changed && $flag) {
$this->state
->set('xmlsitemap_regenerate_needed', TRUE);
}
return $changed;
}
public function delete($entity_type, $entity_id, $langcode = NULL) {
$conditions = [
'type' => $entity_type,
'id' => $entity_id,
];
if ($langcode) {
$conditions['language'] = $langcode;
}
return $this
->deleteMultiple($conditions);
}
public function deleteMultiple(array $conditions) {
if (!$this->state
->get('xmlsitemap_regenerate_needed')) {
$this
->checkChangedLinks($conditions, [], TRUE);
}
$query = $this->connection
->delete('xmlsitemap');
foreach ($conditions as $field => $value) {
$operator = is_array($value) ? 'IN' : '=';
$query
->condition($field, $value, $operator);
}
return $query
->execute();
}
public function updateMultiple(array $updates = [], array $conditions = [], $check_flag = TRUE) {
if ($check_flag && !$this->state
->get('xmlsitemap_regenerate_needed')) {
$this
->checkChangedLinks($conditions, $updates, TRUE);
}
$query = $this->connection
->update('xmlsitemap');
$query
->fields($updates);
foreach ($conditions as $field => $value) {
$operator = is_array($value) ? 'IN' : '=';
$query
->condition($field, $value, $operator);
}
return $query
->execute();
}
public function load($entity_type, $entity_id) {
$link = $this
->loadMultiple([
'type' => $entity_type,
'id' => $entity_id,
]);
return $link ? reset($link) : FALSE;
}
public function loadMultiple(array $conditions = []) {
$query = $this->connection
->select('xmlsitemap');
$query
->fields('xmlsitemap');
foreach ($conditions as $field => $value) {
$operator = is_array($value) ? 'IN' : '=';
$query
->condition($field, $value, $operator);
}
$links = $query
->execute()
->fetchAll(\PDO::FETCH_ASSOC);
return $links;
}
}