...
 
Commits (147)
This diff is collapsed.
{
"name": "drupal/simple_sitemap",
"description": "Simple XML sitemap creates a standard conform XML sitemap of your content.",
"description": "Creates a standard conform hreflang XML sitemap of the site content and provides a framework for developing other sitemap types.",
"type": "drupal-module",
"homepage": "https://drupal.org/project/simple_sitemap",
"authors": [
......@@ -9,11 +9,6 @@
"email": "contact@gbyte.co",
"homepage": "https://www.drupal.org/u/gbyte.co",
"role": "Maintainer"
},
{
"name": "Sam Becker (Sam152)",
"homepage": "https://www.drupal.org/u/sam152",
"role": "Co-maintainer"
}
],
"support": {
......@@ -23,7 +18,9 @@
},
"license": "GPL-2.0+",
"minimum-stability": "dev",
"require": { },
"require": {
"ext-xmlwriter": "*"
},
"extra": {
"drush": {
"services": {
......@@ -32,4 +29,3 @@
}
}
}
max_links: 2000
cron_generate: true
cron_generate_interval: 0
generate_duration: 10000
remove_duplicates: true
skip_untranslated: false
batch_process_limit: 1500
skip_untranslated: true
xsl: true
base_url: ''
default_variant: 'default'
custom_links_include_images: false
excluded_languages: []
enabled_entity_types:
......
variants:
default:
label: 'Default'
weight: 0
......@@ -5,10 +5,13 @@ simple_sitemap.settings:
label: 'Max links'
type: integer
cron_generate:
label: 'Cron generate'
label: 'Cron generation'
type: boolean
cron_generate_interval:
label: 'Cron generate interval'
label: 'Cron generation interval'
type: integer
generate_duration:
label: 'Generation duration'
type: integer
remove_duplicates:
label: 'Remove duplicates'
......@@ -16,33 +19,36 @@ simple_sitemap.settings:
skip_untranslated:
label: 'Skip untranslated'
type: boolean
batch_process_limit:
label: 'Batch process limit'
type: integer
xsl:
label: 'Include a stylesheet in the sitemaps for humans'
type: boolean
base_url:
label: 'Base URL'
type: string
default_variant:
label: 'Default variant'
type: string
custom_links_include_images:
label: 'Include images of custom links'
type: boolean
enabled_entity_types:
label: 'Enabled entity types'
excluded_languages:
label: 'Excluded languages'
type: sequence
sequence:
type: string
excluded_languages:
label: 'Excluded languages'
enabled_entity_types:
label: 'Enabled entity types'
type: sequence
sequence:
type: string
simple_sitemap.bundle_settings.*.*:
simple_sitemap.bundle_settings.*.*.*:
label: 'Entity bundle settings'
type: config_object
mapping:
index:
label: 'Index'
type: integer
type: boolean
priority:
label: 'Priority'
type: string
......@@ -51,15 +57,15 @@ simple_sitemap.bundle_settings.*.*:
type: string
include_images:
label: 'Include images'
type: integer
type: boolean
simple_sitemap.custom:
simple_sitemap.custom_links.*:
label: 'Custom links'
type: config_object
mapping:
links:
type: sequence
label: 'Custom links'
type: sequence
sequence:
type: mapping
mapping:
......@@ -72,3 +78,20 @@ simple_sitemap.custom:
changefreq:
label: 'Change frequency'
type: string
simple_sitemap.variants.*:
label: 'Sitemap variants'
type: config_object
mapping:
variants:
label: 'Sitemap variants'
type: sequence
sequence:
type: mapping
mapping:
label:
label: 'Variant label'
type: string
weight:
label: 'Weight'
type: integer
#simple-sitemap-settings-form .progress__bar {
background-image: none;
}
build:
assessment:
validate_codebase:
phplint:
container_composer:
phpcs:
# phpcs will use core's specified version of Coder.
sniff-all-files: true
halt-on-fail: false
testing:
# run_tests task is executed several times in order of performance speeds.
# halt-on-fail can be set on the run_tests tasks in order to fail fast.
# suppress-deprecations is false in order to be alerted to usages of
# deprecated code.
run_tests.standard:
types: 'Simpletest,PHPUnit-Unit,PHPUnit-Kernel,PHPUnit-Functional'
testgroups: '--all'
suppress-deprecations: false
run_tests.js:
types: 'PHPUnit-FunctionalJavascript'
testgroups: '--all'
suppress-deprecations: false
nightwatchjs: { }
......@@ -7,19 +7,22 @@
"use strict";
Drupal.behaviors.simple_sitemapFieldsetSummaries = {
attach: function(context) {
attach: function(context, settings) {
$(context).find('#edit-simple-sitemap').drupalSetSummary(function(context) {
var vals = [];
if ($(context).find('#edit-simple-sitemap-index-content-1').is(':checked')) {
vals.push(Drupal.t('Included in sitemap'));
vals.push(Drupal.t('Priority') + ': ' + $('#edit-simple-sitemap-priority option:selected', context).text());
vals.push(Drupal.t('Change frequency') + ': ' + $('#edit-simple-sitemap-changefreq option:selected', context).text());
vals.push(Drupal.t('Include images') + ': ' + $('#edit-simple-sitemap-include-images option:selected', context).text());
var enabledVariants = [];
$('input:radio.enabled-for-variant').each(function() {
if ($(this).is(':checked') && $(this).val() == 1) {
enabledVariants.push($(this).attr('class').split(' ')[1])
}
});
if (enabledVariants.length > 0) {
return Drupal.t('Included in sitemap variants: ') + enabledVariants.join(', ');
}
else {
vals.push(Drupal.t('Excluded from sitemap'));
return Drupal.t('Excluded from all sitemap variants');
}
return vals.join('<br />');
});
}
};
......
/**
* @file
* Attaches simple_sitemap behaviors to the entity form.
*/
(function($) {
"use strict";
Drupal.behaviors.simple_sitemapForm = {
attach: function(context) {
// On load: Hide the 'Regenerate sitemap' field to only display it if settings have changed.
$('.form-item-simple-sitemap-regenerate-now').hide();
// On load: Show or hide settings dependant on 'enabled' setting.
if ($('#edit-simple-sitemap-index-content-1').is(':checked')) {
$('.form-item-simple-sitemap-priority').show();
$('.form-item-simple-sitemap-changefreq').show();
$('.form-item-simple-sitemap-include-images').show();
}
else {
$('.form-item-simple-sitemap-priority').hide();
$('.form-item-simple-sitemap-changefreq').hide();
$('.form-item-simple-sitemap-include-images').hide();
}
// On change: Show or hide settings dependant on 'enabled' setting.
$("#edit-simple-sitemap-index-content").change(function() {
if ($('#edit-simple-sitemap-index-content-1').is(':checked')) {
$('.form-item-simple-sitemap-priority').show();
$('.form-item-simple-sitemap-changefreq').show();
$('.form-item-simple-sitemap-include-images').show();
}
else {
$('.form-item-simple-sitemap-priority').hide();
$('.form-item-simple-sitemap-changefreq').hide();
$('.form-item-simple-sitemap-include-images').hide();
}
// Show 'Regenerate sitemap' field if 'enabled' setting has changed.
$('.form-item-simple-sitemap-regenerate-now').show();
});
// Show 'Regenerate sitemap' field if settings have changed.
$("#edit-simple-sitemap-priority, #edit-simple-sitemap-changefreq, #edit-simple-sitemap-include-images").change(function() {
$('.form-item-simple-sitemap-regenerate-now').show();
});
}
};
})(jQuery);
......@@ -8,72 +8,25 @@
Drupal.behaviors.simple_sitemapSitemapEntities = {
attach: function(context, settings) {
var allEntities = settings.simple_sitemap.all_entities;
var atomicEntities = settings.simple_sitemap.atomic_entities;
$.each(settings.simple_sitemap.all_entities, function(index, entityId) {
var target = '#edit-' + entityId + '-enabled';
triggerVisibility(target, entityId);
// Hide the 'Regenerate sitemap' field to only display it if settings have changed.
$('.form-item-simple-sitemap-regenerate-now').hide();
$.each(allEntities, function(index, value) {
// On load: hide all warning messages.
$('#warning-' + value).hide();
// On change: Show or hide warning message dependent on 'enabled' checkbox.
var enabledId = '#edit-' + value + '-enabled';
$(enabledId).change(function() {
if ($(enabledId).is(':checked')) {
$('#warning-' + value).hide();
$('#indexed-bundles-' + value).show();
}
else {
$('#warning-' + value).show();
$('#indexed-bundles-' + value).hide();
}
// Show 'Regenerate sitemap' field if 'enabled' setting has changed.
$('.form-item-simple-sitemap-regenerate-now').show();
$(target).change(function() {
triggerVisibility(target, entityId);
});
});
// Show settings if atomic entity enabled on form load.
$.each(atomicEntities, function(index, value) {
var enabledId = '#edit-' + value + '-enabled';
var priorityId = '.form-item-' + value + '-simple-sitemap-priority';
var changefreqId = '.form-item-' + value + '-simple-sitemap-changefreq';
var includeImagesId = '.form-item-' + value + '-simple-sitemap-include-images';
// On load: Show or hide settings dependent on 'enabled' checkbox.
if ($(enabledId).is(':checked')) {
$(priorityId).show();
$(changefreqId).show();
$(includeImagesId).show();
function triggerVisibility(target, entityId) {
if ($(target).is(':checked')) {
$('#warning-' + entityId).hide();
$('#indexed-bundles-' + entityId).show();
}
else {
$(priorityId).hide();
$(changefreqId).hide();
$(includeImagesId).hide();
$('#warning-' + entityId).show();
$('#indexed-bundles-' + entityId).hide();
}
// On change: Show or hide settings dependent on 'enabled' checkbox.
$(enabledId).change(function() {
if ($(enabledId).is(':checked')) {
$(priorityId).show();
$(changefreqId).show();
$(includeImagesId).show();
}
else {
$(priorityId).hide();
$(changefreqId).hide();
$(includeImagesId).hide();
}
});
// Show 'Regenerate sitemap' field if settings have changed.
$(priorityId, changefreqId, includeImagesId).change(function() {
$('.form-item-simple-sitemap-regenerate-now').show();
});
});
}
}
};
})(jQuery);
views.display_extender.simple_sitemap_display_extender:
type: views_display_extender
mapping:
index:
label: 'Index'
type: boolean
variant:
label: 'Sitemap variant'
type: string
priority:
label: 'Priority'
type: string
changefreq:
label: 'Change frequency'
type: string
arguments:
label: 'Indexed arguments'
type: sequence
sequence:
type: string
max_links:
label: 'Max links'
type: integer
/**
* @file
* Views UI helpers for Simple XML Sitemap display extender.
*/
(function ($, Drupal) {
Drupal.simpleSitemapViewsUi = {};
Drupal.behaviors.simpleSitemapViewsUiCheckboxify = {
attach: function attach() {
var $button = $('[data-drupal-selector="edit-index-button"]').once('simple-sitemap-views-ui-checkboxify');
if ($button.length) {
new Drupal.simpleSitemapViewsUi.Checkboxifier($button);
}
}
};
Drupal.behaviors.simpleSitemapViewsUiArguments = {
attach: function attach() {
var $arguments = $('.indexed-arguments').once('simple-sitemap-views-ui-arguments');
var $checkboxes = $arguments.find('input[type="checkbox"]');
if ($checkboxes.length) {
new Drupal.simpleSitemapViewsUi.Arguments($checkboxes);
}
}
};
Drupal.simpleSitemapViewsUi.Checkboxifier = function ($button) {
this.$button = $button;
this.$parent = this.$button.parent('div.simple-sitemap-views-index');
this.$input = this.$parent.find('input:checkbox');
this.$button.hide();
this.$input.on('click', $.proxy(this, 'clickHandler'));
};
Drupal.simpleSitemapViewsUi.Checkboxifier.prototype.clickHandler = function () {
this.$button.trigger('click').trigger('submit');
};
Drupal.simpleSitemapViewsUi.Arguments = function ($checkboxes) {
this.$checkboxes = $checkboxes;
this.$checkboxes.on('change', $.proxy(this, 'changeHandler'));
};
Drupal.simpleSitemapViewsUi.Arguments.prototype.changeHandler = function (e) {
var $checkbox = $(e.target), index = this.$checkboxes.index($checkbox);
$checkbox.prop('checked') ? this.check(index) : this.uncheck(index);
};
Drupal.simpleSitemapViewsUi.Arguments.prototype.check = function (index) {
this.$checkboxes.slice(0, index).prop('checked', true);
};
Drupal.simpleSitemapViewsUi.Arguments.prototype.uncheck = function (index) {
this.$checkboxes.slice(index).prop('checked', false);
};
})(jQuery, Drupal);
\ No newline at end of file
name: 'Simple XML Sitemap (Views)'
type: module
description: 'Provides integration of the Simple XML Sitemap module with the Views module.'
configure: simple_sitemap.settings_views
package: SEO
core: 8.x
dependencies:
- simple_sitemap:simple_sitemap
- drupal:views
<?php
/**
* @file
* Install and uninstall hooks for the simple_sitemap_views module.
*/
/**
* Implements hook_install().
*/
function simple_sitemap_views_install() {
// Enable views display extender plugin.
\Drupal::service('simple_sitemap.views')->enable();
}
/**
* Implements hook_uninstall().
*/
function simple_sitemap_views_uninstall() {
// Disable views display extender plugin.
\Drupal::service('simple_sitemap.views')->disable();
}
/**
* Implements hook_schema().
*/
function simple_sitemap_views_schema() {
$schema['simple_sitemap_views'] = [
'description' => 'Index of argument values for view pages.',
'fields' => [
'id' => [
'type' => 'serial',
'not null' => TRUE,
'description' => 'Primary Key: Unique ID for argument values.',
],
'view_id' => [
'type' => 'varchar_ascii',
'not null' => TRUE,
'default' => '',
'length' => 128,
'description' => 'The ID of the view.',
],
'display_id' => [
'type' => 'varchar_ascii',
'not null' => TRUE,
'default' => '',
'length' => 128,
'description' => 'The ID of the view display.',
],
'arguments_ids' => [
'type' => 'varchar',
'not null' => TRUE,
'default' => '',
'length' => 1024,
'description' => 'A string representation of the set of argument identifiers.',
],
'arguments_values' => [
'type' => 'varchar',
'not null' => TRUE,
'default' => '',
'length' => 1024,
'description' => 'A string representation of the set of argument values.',
],
],
'primary key' => ['id'],
'indexes' => [
'view' => ['view_id'],
'display' => ['view_id', 'display_id'],
'arguments_ids' => ['view_id', 'display_id', 'arguments_ids'],
],
];
return $schema;
}
viewsUi:
version: VERSION
js:
js/simple_sitemap.viewsUi.js: {}
dependencies:
- core/jquery
- core/drupal
- core/jquery.once
simple_sitemap.settings_views:
route_name: simple_sitemap.settings_views
title: 'Sitemap views'
base_route: simple_sitemap.settings
weight: 1
<?php
/**
* @file
* Contains simple_sitemap_views.module.
*/
/**
* Implements hook_cron().
*/
function simple_sitemap_views_cron() {
// Create tasks in the garbage collection queue.
\Drupal::service('simple_sitemap.views')->executeGarbageCollection();
}
/**
* Implements hook_simple_sitemap_sitemap_types_alter().
*/
function simple_sitemap_views_simple_sitemap_sitemap_types_alter(array &$sitemap_types) {
// Add a 'views' UrlGenerator plugin to the default hreflang sitemap type.
if (isset($sitemap_types['default_hreflang'])) {
$sitemap_types['default_hreflang']['urlGenerators'][] = 'views';
}
}
simple_sitemap.settings_views:
path: '/admin/config/search/simplesitemap/views'
defaults:
_controller: '\Drupal\simple_sitemap_views\Controller\SimpleSitemapViewsController::content'
_title: 'Simple XML Sitemap Settings'
requirements:
_permission: 'administer sitemap settings'
services:
simple_sitemap.views:
class: Drupal\simple_sitemap_views\SimpleSitemapViews
arguments: ['@entity_type.manager', '@config.factory', '@queue', '@database']
simple_sitemap.views.argument_collector:
class: Drupal\simple_sitemap_views\EventSubscriber\ArgumentCollector
arguments: ['@entity_type.manager', '@simple_sitemap.views', '@current_route_match']
tags:
- {name: 'event_subscriber'}
<?php
namespace Drupal\simple_sitemap_views\Controller;
use Drupal\simple_sitemap\Form\FormHelper;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\simple_sitemap_views\SimpleSitemapViews;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Url;
/**
* Controller for Simple XML Sitemap Views admin page.
*/
class SimpleSitemapViewsController extends ControllerBase {
/**
* Views sitemap data.
*
* @var \Drupal\simple_sitemap_views\SimpleSitemapViews
*/
protected $sitemapViews;
/**
* SimpleSitemapViewsController constructor.
*
* @param \Drupal\simple_sitemap_views\SimpleSitemapViews $sitemap_views
* Views sitemap data.
*/
public function __construct(SimpleSitemapViews $sitemap_views) {
$this->sitemapViews = $sitemap_views;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('simple_sitemap.views')
);
}
/**
* Builds a listing of indexed views displays.
*
* @return array
* A render array.
*/
public function content() {
$table = [
'#type' => 'table',
'#header' => [
$this->t('View'),
$this->t('Display'),
$this->t('Arguments'),
$this->t('Operations'),
],
'#empty' => $this->t('No view displays are set to be indexed yet. <a href="@url">Edit a view.</a>', ['@url' => $GLOBALS['base_url'] . '/admin/structure/views']),
];
foreach ($this->sitemapViews->getIndexableViews() as $index => $view) {
$table[$index]['view'] = ['#markup' => $view->storage->label()];
$table[$index]['display'] = ['#markup' => $view->display_handler->display['display_title']];
// Determine whether view display arguments are indexed.
$arguments_status = $this->sitemapViews->getIndexableArguments($view) ? $this->t('Yes') : $this->t('No');
$table[$index]['arguments'] = ['#markup' => $arguments_status];
// Link to view display edit form.
$display_edit_url = Url::fromRoute('entity.view.edit_display_form', [
'view' => $view->id(),
'display_id' => $view->current_display,
]);
$table[$index]['operations'] = [
'#type' => 'operations',
'#links' => [
'display_edit' => [
'title' => $this->t('Edit'),
'url' => $display_edit_url,
],
],
];
}
// Show information about indexed displays.
$build['simple_sitemap_views'] = [
'#prefix' => FormHelper::getDonationText(),
'#title' => $this->t('Indexed view displays'),
'#type' => 'fieldset',
'table' => $table,
];
return $build;
}
}
<?php
namespace Drupal\simple_sitemap_views\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\PostResponseEvent;
use Drupal\simple_sitemap_views\SimpleSitemapViews;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Drupal\Core\Routing\RouteMatchInterface;
/**
* Collect information about views arguments.
*/
class ArgumentCollector implements EventSubscriberInterface {
/**
* View entities storage.
*
* @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface
*/
protected $viewStorage;
/**
* Views sitemap data.
*
* @var \Drupal\simple_sitemap_views\SimpleSitemapViews
*/
protected $sitemapViews;
/**
* The currently active route match object.
*
* @var \Drupal\Core\Routing\RouteMatchInterface
*/
protected $routeMatch;
/**
* ArgumentCollector constructor.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* Entity type manager.
* @param \Drupal\simple_sitemap_views\SimpleSitemapViews $sitemap_views
* Views sitemap data.
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The currently active route match object.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager, SimpleSitemapViews $sitemap_views, RouteMatchInterface $route_match) {
$this->viewStorage = $entity_type_manager->getStorage('view');
$this->sitemapViews = $sitemap_views;
$this->routeMatch = $route_match;
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
$events[KernelEvents::TERMINATE] = 'onTerminate';
return $events;
}
/**
* Collect information about views arguments.
*
* @param \Symfony\Component\HttpKernel\Event\PostResponseEvent $event
* Object of event after a response was sent.
*/
public function onTerminate(PostResponseEvent $event) {
// Only successful requests are interesting.
// Collect information about arguments only if views support is enabled.
if (!$event->getResponse()->isSuccessful() || !$this->sitemapViews->isEnabled()) {
return;
}
// Get view ID from route.
$view_id = $this->routeMatch->getParameter('view_id');
/** @var \Drupal\views\ViewEntityInterface $view_entity */
if ($view_id && $view_entity = $this->viewStorage->load($view_id)) {
// Get display ID from route.
$display_id = $this->routeMatch->getParameter('display_id');
// Get a set of view arguments and try to add them to the index.
$view = $view_entity->getExecutable();
$args = $this->getViewArgumentsFromRoute();
$this->sitemapViews->addArgumentsToIndex($view, $args, $display_id);
// Destroy a view instance.
$view->destroy();
}
}
/**
* Get view arguments from current route.
*
* @return array
* View arguments array.
*/
protected function getViewArgumentsFromRoute() {
// The code of this function is taken in part from the view page controller
// method (Drupal\views\Routing\ViewPageController::handle()).
$route = $this->routeMatch->getRouteObject();
$map = $route->hasOption('_view_argument_map') ? $route->getOption('_view_argument_map') : [];
$args = [];
foreach ($map as $attribute => $parameter_name) {
$parameter_name = isset($parameter_name) ? $parameter_name : $attribute;
if (($arg = $this->routeMatch->getRawParameter($parameter_name)) !== NULL) {
$args[] = $arg;
}
}
return $args;
}
}
<?php
namespace Drupal\simple_sitemap_views\Plugin\QueueWorker;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\simple_sitemap_views\SimpleSitemapViews;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Database\Query\Condition;
use Drupal\Core\Queue\QueueWorkerBase;
/**
* Executes garbage collection in the simple_sitemap_views table.
*
* @QueueWorker(
* id = "simple_sitemap.views.garbage_collector",
* title = @Translation("Garbage collection in the simple_sitemap_views table"),
* cron = {"time" = 30}
* )
*/
class GarbageCollector extends QueueWorkerBase implements ContainerFactoryPluginInterface {
/**
* View entities storage.
*
* @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface
*/
protected $viewStorage;
/**
* Views sitemap data.
*
* @var \Drupal\simple_sitemap_views\SimpleSitemapViews
*/
protected $sitemapViews;
/**
* GarbageCollector constructor.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* Entity type manager.
* @param \Drupal\simple_sitemap_views\SimpleSitemapViews $sitemap_views
* Views sitemap data.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, SimpleSitemapViews $sitemap_views) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->viewStorage = $entity_type_manager->getStorage('view');
$this->sitemapViews = $sitemap_views;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity_type.manager'),
$container->get('simple_sitemap.views')
);
}
/**
* {@inheritdoc}
*/
public function processItem($data) {
$view_id = $data['view_id'];
/** @var \Drupal\views\ViewEntityInterface $view_entity */
$view_entity = $this->viewStorage->load($view_id);
$display_ids = [];
// Check that the view exists and it is enabled.
if ($view_entity && $view_entity->status()) {
$view = $view_entity->getExecutable();
foreach ($this->sitemapViews->getRouterDisplayIds($view_entity) as $display_id) {
// Ensure the display was correctly set.
// Check that the display is enabled.
if (!$view->setDisplay($display_id) || !$view->display_handler->isEnabled()) {
continue;
}
// Check that the display has indexable arguments.
$args_ids = $this->sitemapViews->getIndexableArguments($view);
if (empty($args_ids)) {
continue;
}
$display_ids[] = $display_id;
// Delete records about sets of arguments that are no longer indexed.
$args_ids = $this->sitemapViews->getArgumentsStringVariations($args_ids);
$condition = new Condition('AND');
$condition->condition('view_id', $view_id);
$condition->condition('display_id', $display_id);
$condition->condition('arguments_ids', $args_ids, 'NOT IN');
$this->sitemapViews->removeArgumentsFromIndex($condition);
// Check if the records limit for display is exceeded.
$settings = $this->sitemapViews->getSitemapSettings($view);
$max_links = is_numeric($settings['max_links']) ? $settings['max_links'] : 0;
if ($max_links > 0) {
$condition = new Condition('AND');
$condition->condition('view_id', $view_id);
$condition->condition('display_id', $display_id);
// Delete records that exceed the limit.
if ($index_id = $this->sitemapViews->getIndexIdByPosition($max_links, $condition)) {
$condition->condition('id', $index_id, '>');
$this->sitemapViews->removeArgumentsFromIndex($condition);
}
}
}
// Delete records about view displays that do not exist or are disabled.
if (!empty($display_ids)) {
$condition = new Condition('AND');
$condition->condition('view_id', $view_id);
$condition->condition('display_id', $display_ids, 'NOT IN');
$this->sitemapViews->removeArgumentsFromIndex($condition);
}
// Destroy a view instance.
$view->destroy();
}
// Delete records about the view, if it does not exist, is disabled or it
// does not have a display whose arguments are indexed.
if (empty($display_ids)) {
$condition = new Condition('AND');
$condition->condition('view_id', $view_id);
$this->sitemapViews->removeArgumentsFromIndex($condition);
}
}
}
<?php
namespace Drupal\simple_sitemap_views\Plugin\simple_sitemap\UrlGenerator;
use Drupal\simple_sitemap\Plugin\simple_sitemap\UrlGenerator\EntityUrlGeneratorBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\simple_sitemap_views\SimpleSitemapViews;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Routing\RouteProviderInterface;
use Drupal\Core\Database\Query\Condition;
use Drupal\simple_sitemap\Simplesitemap;
use Drupal\simple_sitemap\EntityHelper;
use Drupal\simple_sitemap\Logger;
use Drupal\views\Views;
use Drupal\Core\Url;
/**
* Views URL generator plugin.
*
* @UrlGenerator(
* id = "views",
* label = @Translation("Views URL generator"),
* description = @Translation("Generates URLs for views."),
* )
*/
class ViewsUrlGenerator extends EntityUrlGeneratorBase {
/**
* Views sitemap data.
*
* @var \Drupal\simple_sitemap_views\SimpleSitemapViews
*/
protected $sitemapViews;
/**
* The route provider.
*
* @var \Drupal\Core\Routing\RouteProviderInterface
*/
protected $routeProvider;
/**
* ViewsUrlGenerator constructor.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\simple_sitemap\Simplesitemap $generator
* The simple_sitemap.generator service.
* @param \Drupal\simple_sitemap\Logger $logger
* The simple_sitemap.logger service.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* The language manager.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\simple_sitemap\EntityHelper $entity_helper
* The simple_sitemap.entity_helper service.
* @param \Drupal\simple_sitemap_views\SimpleSitemapViews $sitemap_views
* Views sitemap data.
* @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
* The route provider.
*/
public function __construct(
array $configuration,
$plugin_id,
$plugin_definition,
Simplesitemap $generator,
Logger $logger,
LanguageManagerInterface $language_manager,
EntityTypeManagerInterface $entity_type_manager,
EntityHelper $entity_helper,
SimpleSitemapViews $sitemap_views,
RouteProviderInterface $route_provider
) {
parent::__construct(
$configuration,
$plugin_id,
$plugin_definition,
$generator,
$logger,
$language_manager,
$entity_type_manager,
$entity_helper
);
$this->sitemapViews = $sitemap_views;
$this->routeProvider = $route_provider;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('simple_sitemap.generator'),
$container->get('simple_sitemap.logger'),
$container->get('language_manager'),
$container->get('entity_type.manager'),
$container->get('simple_sitemap.entity_helper'),
$container->get('simple_sitemap.views'),
$container->get('router.route_provider')
);
}
/**
* {@inheritdoc}
*/
public function getDataSets() {
$data_sets = [];
// Get data sets.
foreach ($this->sitemapViews->getIndexableViews() as $view) {
$settings = $this->sitemapViews->getSitemapSettings($view);
if ($settings['variant'] != $this->sitemapVariant) {
// Destroy a view instance.
$view->destroy();
continue;
}
$base_data_set = [
'view_id' => $view->id(),
'display_id' => $view->current_display,
];
// View path without arguments.
$data_sets[] = $base_data_set + ['arguments' => NULL];
// Process indexed arguments.
if ($args_ids = $this->sitemapViews->getIndexableArguments($view)) {
// Form the condition according to the variants of the
// indexable arguments.
$args_ids = $this->sitemapViews->getArgumentsStringVariations($args_ids);
$condition = new Condition('AND');
$condition->condition('view_id', $view->id());
$condition->condition('display_id', $view->current_display);
$condition->condition('arguments_ids', $args_ids, 'IN');
// Get the arguments values from the index.
$max_links = is_numeric($settings['max_links']) ? $settings['max_links'] : NULL;
$indexed_arguments = $this->sitemapViews->getArgumentsFromIndex($condition, $max_links, TRUE);
// Add the arguments values for processing.
foreach ($indexed_arguments as $index_id => $arguments_info) {
$data_sets[] = $base_data_set + [
'index_id' => $index_id,
'arguments' => $arguments_info['arguments'],
];
}
}
// Destroy a view instance.
$view->destroy();
}
return $data_sets;
}
/**
* {@inheritdoc}
*/
protected function processDataSet($data_set) {
// Get information from data set.
$view_id = $data_set['view_id'];
$display_id = $data_set['display_id'];
$args = $data_set['arguments'];
try {
// Trying to get an instance of the view.
$view = Views::getView($view_id);
if (empty($view)) {
throw new \UnexpectedValueException('Failed to get an instance of the view.');
}
// Trying to set the view display.
$view->initDisplay();
if (!$view->displayHandlers->has($display_id) || !$view->setDisplay($display_id)) {
throw new \UnexpectedValueException('Failed to set the view display.');
}
// Trying to get the sitemap settings.
$settings = $this->sitemapViews->getSitemapSettings($view);
if (empty($settings)) {
throw new \UnexpectedValueException('Failed to get the sitemap settings.');
}
// Trying to get the view URL.
$url = $view->getUrl($args);
$url->setAbsolute();
if (is_array($args)) {
$params = array_merge([$view_id, $display_id], $args);
$view_result = call_user_func_array('views_get_view_result', $params);
// Do not include paths on which the view returns an empty result.
if (empty($view_result)) {
throw new \UnexpectedValueException('The view returned an empty result.');
}
// Remove empty arguments from URL.
$this->cleanRouteParameters($url, $args);
}
$path = $url->getInternalPath();
// Destroy a view instance.
$view->destroy();
}
catch (\Exception $e) {
// Delete records about arguments that are not added to the sitemap.
if (!empty($data_set['index_id'])) {
$condition = new Condition('AND');
$condition->condition('id', $data_set['index_id']);
$this->sitemapViews->removeArgumentsFromIndex($condition);
}
return FALSE;
}
return [
'url' => $url,
'lastmod' => NULL,
'priority' => isset($settings['priority']) ? $settings['priority'] : NULL,
'changefreq' => !empty($settings['changefreq']) ? $settings['changefreq'] : NULL,
'images' => [],
// Additional info useful in hooks.
'meta' => [
'path' => $path,
'view_info' => [
'view_id' => $view_id,
'display_id' => $display_id,
'arguments' => $args,
],
],
];
}
/**
* Clears the URL from parameters that are not present in the arguments.
*
* @param \Drupal\Core\Url $url
* The URL object.
* @param array $args
* Array of arguments.
*
* @throws \UnexpectedValueException.
* If this is a URI with no corresponding route.
*/
protected function cleanRouteParameters(Url $url, array $args) {
$parameters = $url->getRouteParameters();
// Check that the number of params does not match the number of arguments.
if (count($parameters) != count($args)) {
$route_name = $url->getRouteName();
$route = $this->routeProvider->getRouteByName($route_name);
$variables = $route->compile()->getVariables();
// Remove params that are not present in the arguments.
foreach ($variables as $variable_name) {
if (empty($args)) {
unset($parameters[$variable_name]);
}
else {
array_shift($args);
}
}
// Set new route params.
$url->setRouteParameters($parameters);
}
}
}
This diff is collapsed.
langcode: en
status: true
dependencies:
module:
- node
- user
id: simple_sitemap_views_test_view
label: 'Test view'
module: views
description: ''
tag: ''
base_table: node_field_data
base_field: nid
core: 8.x
display:
default:
display_plugin: default
id: default
display_title: Master
position: 0
display_options:
access:
type: perm
options:
perm: 'access content'
cache:
type: tag
options: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: none
options:
items_per_page: null
offset: 0
style:
type: default
row:
type: fields
options:
default_field_elements: true
inline: { }
separator: ''
hide_empty: false
fields:
title:
id: title
table: node_field_data
field: title
entity_type: node
entity_field: title
label: ''
alter:
alter_text: false
make_link: false
absolute: false
trim: false
word_boundary: false
ellipsis: false
strip_tags: false
html: false
hide_empty: false
empty_zero: false
settings:
link_to_entity: true
plugin_id: field
relationship: none
group_type: group
admin_label: ''
exclude: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_alter_empty: true
click_sort_column: value
type: string
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
filters:
status:
value: '1'
table: node_field_data
field: status
plugin_id: boolean
entity_type: node
entity_field: status
id: status
expose:
operator: ''
group: 1
sorts: { }
title: 'Test view'
header: { }
footer: { }
empty: { }
relationships: { }
arguments:
type:
id: type
table: node_field_data
field: type
relationship: none
group_type: group
admin_label: ''
default_action: ignore
exception:
value: all
title_enable: false
title: All
title_enable: false
title: ''
default_argument_type: fixed
default_argument_options:
argument: ''
default_argument_skip_url: false
summary_options:
base_path: ''
count: true
items_per_page: 25
override: false
summary:
sort_order: asc
number_of_records: 0
format: default_summary
specify_validation: false
validate:
type: none
fail: 'not found'
validate_options: { }
glossary: false
limit: 0
case: none
path_case: none
transform_dash: false
break_phrase: false
entity_type: node
entity_field: type
plugin_id: node_type
title:
id: title
table: node_field_data
field: title
relationship: none
group_type: group
admin_label: ''
default_action: ignore
exception:
value: all
title_enable: false
title: All
title_enable: false
title: ''
default_argument_type: fixed
default_argument_options:
argument: ''
default_argument_skip_url: false
summary_options:
base_path: ''
count: true
items_per_page: 25
override: false
summary:
sort_order: asc
number_of_records: 0
format: default_summary
specify_validation: false
validate:
type: none