Commit b357d270 authored by gbyte.co's avatar gbyte.co

Restructuring the creation of multiligual sitemap to adhere to google standards.

parent 36a501d3
......@@ -12,11 +12,10 @@ function simplesitemap_schema() {
$schema['simplesitemap'] = array(
'description' => 'Holds XML sitemaps as strings for quick retrieval.',
'fields' => array(
'language_code' => array(
'description' => 'Language code of the sitemap used as unique identifier.',
'type' => 'varchar',
'id' => array(
'description' => 'Sitemap chunk unique identifier.',
'type' => 'serial',
'not null' => TRUE,
'length' => 12,
),
'sitemap_string' => array(
'description' => 'XML sitemap string.',
......@@ -25,7 +24,7 @@ function simplesitemap_schema() {
'not null' => TRUE,
),
),
'primary key' => array('language_code'),
'primary key' => array('id'),
);
return $schema;
}
......@@ -49,7 +48,7 @@ function simplesitemap_update_8101() {
}
/**
* Update: Changing config settings according to new config structure.
* Changing config settings according to new config structure.
*/
function simplesitemap_update_8102() {
$entity_types = \Drupal::config('simplesitemap.settings')->get('entity_types');
......@@ -64,3 +63,16 @@ function simplesitemap_update_8102() {
}
\Drupal::service('config.factory')->getEditable('simplesitemap.settings')->set('entity_types', $entity_types)->save();
}
/**
* Modifying database structure to hold one multilingual sitemap.
*/
function simplesitemap_update_8103() {
db_truncate('simplesitemap')->execute();
db_drop_primary_key('simplesitemap');
db_change_field('simplesitemap', 'language_code', 'id', array(
'type' => 'serial',
'not null' => TRUE
), array('primary key' => array('id')));
drupal_set_message('The sitemap needs to be regenerated manually or via cron run.');
}
......@@ -90,7 +90,7 @@ function simplesitemap_entity_form_submit($form, &$form_state) {
// Regenerate sitemaps according to user setting.
if ($form['simplesitemap']['simplesitemap_rebuild_now']['#value']) {
$sitemap->generate_all_sitemaps();
$sitemap->generate_sitemap();
}
}
......@@ -101,6 +101,6 @@ function simplesitemap_cron() {
// Regenerate sitemaps for all languages and save them into strings for performance reasons (pseudo caching).
$sitemap = new Simplesitemap;
$sitemap->generate_all_sitemaps();
$sitemap->generate_sitemap();
}
......@@ -115,7 +115,7 @@ class SimplesitemapCustomLinksForm extends ConfigFormBase {
// Regenerate sitemaps according to user setting.
if ($form_state->getValue('simplesitemap_rebuild_now')) {
$sitemap->generate_all_sitemaps();
$sitemap->generate_sitemap();
}
}
}
......@@ -16,14 +16,18 @@ use Drupal\simplesitemap\SitemapGenerator;
*/
class CustomLinkGenerator {
public function get_custom_links($custom_paths, $language) {
public function get_custom_links($custom_paths, $languages) {
$links = array();
foreach($custom_paths as $custom_path) {
foreach($custom_paths as $i => $custom_path) {
if (!isset($custom_path['index']) || $custom_path['index']) {
$links[] = SitemapGenerator::add_xml_link_markup(Url::fromUserInput($custom_path['path'], array(
'language' => $language,
'absolute' => TRUE
))->toString(), isset($custom_path['priority']) ? $custom_path['priority'] : SitemapGenerator::PRIORITY_DEFAULT);
foreach($languages as $language) {
$links[$i]['url'][$language->getId()] = Url::fromUserInput($custom_path['path'], array(
'language' => $language,
'absolute' => TRUE
))->toString();
}
$links[$i]['priority'] = isset($custom_path['priority']) ? $custom_path['priority'] : NULL;
$links[$i]['lastmod'] = NULL; //todo: implement
}
}
return $links;
......
......@@ -18,14 +18,18 @@ abstract class EntityLinkGenerator {
private $entity_links = array();
public function get_entity_links($entity_type, $bundles, $language) {
public function get_entity_links($entity_type, $bundles, $languages) {
$i = 0;
foreach($bundles as $bundle => $bundle_settings) {
if (!$bundle_settings['index']) {
continue;
}
$links = $this->get_entity_bundle_links($bundle, $language);
$links = $this->get_entity_bundle_links($bundle, $languages);
foreach ($links as $id => $link) {
$this->entity_links[] = SitemapGenerator::add_xml_link_markup($link, $bundle_settings['priority'], $this->get_lastmod($entity_type, $id));
$this->entity_links[$i]['url'] = $link;
$this->entity_links[$i]['priority'] = $bundle_settings['priority'];
$this->entity_links[$i]['lastmod'] = $this->get_lastmod($entity_type, $id);
$i++;
}
}
return $this->entity_links;
......@@ -45,5 +49,5 @@ abstract class EntityLinkGenerator {
return isset($lastmod[0]) ? date_iso8601($lastmod[0]) : NULL;
}
abstract function get_entity_bundle_links($bundle, $language);
abstract function get_entity_bundle_links($bundle, $languages);
}
......@@ -18,7 +18,7 @@ use Drupal\Core\Url;
*/
class menu extends EntityLinkGenerator {
function get_entity_bundle_links($bundle, $language) {
function get_entity_bundle_links($bundle, $languages) {
$routes = db_query("SELECT mlid, route_name, route_parameters FROM {menu_tree} WHERE menu_name = :menu_name and enabled = 1", array(':menu_name' => $bundle))
->fetchAllAssoc('mlid');
......@@ -27,10 +27,12 @@ class menu extends EntityLinkGenerator {
if (empty($entity->route_name))
continue;
$options = !empty($route_parameters = unserialize($entity->route_parameters)) ? array(key($route_parameters) => $route_parameters[key($route_parameters)]) : array();
$urls[] = Url::fromRoute($entity->route_name, $options, array(
'language' => $language,
'absolute' => TRUE
))->toString();
foreach($languages as $language) {
$urls[$id][$language->getId()] = Url::fromRoute($entity->route_name, $options, array(
'language' => $language,
'absolute' => TRUE
))->toString();
}
}
return $urls;
}
......
......@@ -25,17 +25,17 @@ use Drupal\Core\Url;
*/
class node_type extends EntityLinkGenerator {
function get_entity_bundle_links($bundle, $language) {
function get_entity_bundle_links($bundle, $languages) {
$results = db_query("SELECT nid FROM {node_field_data} WHERE status = 1 AND type = :type", array(':type' => $bundle))
->fetchAllAssoc('nid');
$urls = array();
foreach ($results as $id => $changed) {
$urls[$id] = Url::fromRoute("entity.node.canonical", array('node' => $id), array(
'language' => $language,
'absolute' => TRUE
))->toString();
foreach($languages as $language) {
$urls[$id][$language->getId()] = Url::fromRoute("entity.node.canonical", array('node' => $id), array(
'language' => $language,
'absolute' => TRUE
))->toString();
}
}
return $urls;
}
......
......@@ -18,17 +18,19 @@ use Drupal\Core\Url;
*/
class taxonomy_vocabulary extends EntityLinkGenerator {
function get_entity_bundle_links($bundle, $language) {
function get_entity_bundle_links($bundle, $languages) {
$results = db_query("SELECT tid FROM {taxonomy_term_field_data} WHERE vid = :vid", array(':vid' => $bundle))
->fetchAllAssoc('tid');
$urls = array();
foreach ($results as $id => $changed) {
$urls[$id] = Url::fromRoute("entity.taxonomy_term.canonical", array('taxonomy_term' => $id), array(
'language' => $language,
'absolute' => TRUE
))->toString();
foreach($languages as $language) {
$urls[$id][$language->getId()] = Url::fromRoute("entity.taxonomy_term.canonical", array('taxonomy_term' => $id), array(
'language' => $language,
'absolute' => TRUE
))->toString();
}
}
return $urls;
}
......
......@@ -15,15 +15,14 @@ class Simplesitemap {
private $config;
private $sitemap;
private $language;
function __construct() {
$this->set_current_lang();
$this->set_config();
}
public static function get_form_entity($form_state) {
if (!is_null($form_state->getFormObject()) && method_exists($form_state->getFormObject(), 'getEntity')) {
if (!is_null($form_state->getFormObject())
&& method_exists($form_state->getFormObject(), 'getEntity')) {
$entity = $form_state->getFormObject()->getEntity();
return $entity;
}
......@@ -31,17 +30,14 @@ class Simplesitemap {
}
public static function get_plugin_path($entity_type_name) {
$class_path = drupal_get_path('module', 'simplesitemap') . '/' . self::SITEMAP_PLUGIN_PATH . '/' . $entity_type_name . '.php';
$class_path = drupal_get_path('module', 'simplesitemap')
. '/' . self::SITEMAP_PLUGIN_PATH . '/' . $entity_type_name . '.php';
if (file_exists($class_path)) {
return $class_path;
}
return FALSE;
}
private function set_current_lang($language = NULL) {
$this->language = is_null($language) ? \Drupal::languageManager()->getCurrentLanguage() : $language;
}
private function set_config() {
$this->get_config_from_db();
$this->get_sitemap_from_db();
......@@ -49,10 +45,8 @@ class Simplesitemap {
// Get sitemap from database.
private function get_sitemap_from_db() {
$result = db_select('simplesitemap', 's')
->fields('s', array('sitemap_string'))
->condition('language_code', $this->language->getId())
->execute()->fetchAll();
//todo: update for chunked sitemaps
$result = db_query("SELECT sitemap_string FROM {simplesitemap}")->fetchAll();
$this->sitemap = !empty($result[0]->sitemap_string) ? $result[0]->sitemap_string : NULL;
}
......@@ -70,7 +64,8 @@ class Simplesitemap {
}
private function save_config($key, $value) {
\Drupal::service('config.factory')->getEditable('simplesitemap.settings')->set($key, $value)->save();
\Drupal::service('config.factory')->getEditable('simplesitemap.settings')
->set($key, $value)->save();
$this->set_config();
}
......@@ -81,58 +76,25 @@ class Simplesitemap {
return $this->sitemap;
}
private function generate_sitemap() {
public function generate_sitemap() {
$generator = new SitemapGenerator();
$generator->set_sitemap_lang($this->language);
$generator->set_custom_links($this->config->get('custom'));
$generator->set_entity_types($this->config->get('entity_types'));
$this->sitemap = $generator->generate_sitemap();
$this->save_sitemap();
}
public function generate_all_sitemaps() {
$generator = new SitemapGenerator();
$generator->set_custom_links($this->config->get('custom'));
$generator->set_entity_types($this->config->get('entity_types'));
foreach(\Drupal::languageManager()->getLanguages() as $language) {
$generator->set_sitemap_lang($language);
$this->language = $language;
$this->sitemap = $generator->generate_sitemap();
$this->save_sitemap();
}
drupal_set_message(t('XML sitemaps have been regenerated for all languages.'));
drupal_set_message(t("The <a href='@url' target='_blank'>XML sitemap</a> has been regenerated for all languages.",
array('@url' => $GLOBALS['base_url'] . '/sitemap.xml')));
}
private function save_sitemap() {
//todo: db_merge not working in D8(?), this is why the following queries are needed:
// db_merge('simplesitemap')
// ->key(array('language_code', $this->lang))
// ->fields(array(
// 'language_code' => $this->lang,
// 'sitemap_string' => $this->sitemap,
// ))
// ->execute();
$exists_query = db_select('simplesitemap')
->condition('language_code', $this->language->getId())
->countQuery()->execute()->fetchField();
if ($exists_query > 0) {
db_update('simplesitemap')
->fields(array(
'sitemap_string' => $this->sitemap,
))
->condition('language_code', $this->language->getId())
->execute();
}
else {
db_insert('simplesitemap')
->fields(array(
'language_code' => $this->language->getId(),
'sitemap_string' => $this->sitemap,
))
->execute();
}
//todo: update for chunked sitemaps
db_merge('simplesitemap')
->key(array('id' => 1))
->fields(array(
'id' => 1,
'sitemap_string' => $this->sitemap,
))
->execute();
}
public function get_entity_types() {
......
......@@ -9,7 +9,7 @@
namespace Drupal\simplesitemap;
use Drupal\simplesitemap\LinkGenerators\CustomLinkGenerator;
use \Drupal\Component\Utility\Html;
use \XMLWriter;
/**
* SitemapGenerator class.
......@@ -21,12 +21,18 @@ class SitemapGenerator {
const PRIORITY_DIVIDER = 10;
const XML_VERSION = '1.0';
const ENCODING = 'UTF-8';
const XMLNS_URL = 'http://www.sitemaps.org/schemas/sitemap/0.9';
const XMLNS = 'http://www.sitemaps.org/schemas/sitemap/0.9';
const XMLNS_XHTML = 'http://www.w3.org/1999/xhtml';
private $entity_types;
private $custom;
private $language;
private $links;
private $languages;
function __construct() {
$this->languages = \Drupal::languageManager()->getLanguages();
$this->links = array();
}
public static function get_priority_select_values() {
$options = array();
......@@ -37,13 +43,6 @@ class SitemapGenerator {
return $options;
}
public static function add_xml_link_markup($url, $priority = NULL, $lastmod = NULL) {
return "<url><loc>" . Html::escape($url) . "</loc>"
. (!is_null($priority) ? ("<priority>" . $priority . "</priority>") : '')
. (!is_null($lastmod) ? ("<lastmod>" . $lastmod . "</lastmod>") : '')
. "</url>";
}
public function set_entity_types($entity_types) {
$this->entity_types = is_array($entity_types) ? $entity_types : array();
}
......@@ -52,26 +51,57 @@ class SitemapGenerator {
$this->custom = is_array($custom) ? $custom : array();
}
public function set_sitemap_lang($language) {
// Reset links array to make space for a sitemap with a different language.
$this->links = array();
$this->language = $language;
}
public function generate_sitemap() {
$this->generate_custom_links();
$this->generate_entity_links();
$sitemap = implode($this->links);
return $this->add_xml_sitemap_markup($sitemap);
$default_language_id = \Drupal::languageManager()->getDefaultLanguage()->getId();
$writer = new XMLWriter();
$writer->openMemory();
$writer->setIndent(TRUE);
$writer->startDocument(self::XML_VERSION, self::ENCODING);
$writer->startElement('urlset');
$writer->writeAttribute('xmlns', self::XMLNS);
$writer->writeAttribute('xmlns:xhtml', self::XMLNS_XHTML);
foreach ($this->links as $link) {
$writer->startElement('url');
// Adding url to standard language.
$writer->writeElement('loc', $link['url'][$default_language_id]);
// Adding alternate urls (other languages).
if (count($link['url']) > 1) {
foreach($link['url'] as $language_id => $localised_url) {
$writer->startElement('xhtml:link');
$writer->writeAttribute('rel', 'alternate');
$writer->writeAttribute('hreflang', $language_id);
$writer->writeAttribute('href', $localised_url);
$writer->endElement();
}
}
// Add priority.
if (!is_null($link['priority'])) {
$writer->writeElement('priority', $link['priority']);
}
// Add lastmod.
if (!is_null($link['lastmod'])) {
$writer->writeElement('lastmod', $link['lastmod']);
}
$writer->endElement();
}
$writer->endDocument();
return $writer->outputMemory();
}
// Add custom links.
private function generate_custom_links() {
$link_generator = new CustomLinkGenerator();
$links = $link_generator->get_custom_links($this->custom , $this->language);
$links = $link_generator->get_custom_links($this->custom , $this->languages);
$this->links = array_merge($this->links, $links);
}
......@@ -83,14 +113,9 @@ class SitemapGenerator {
require_once $class_path;
$class_name = "Drupal\\simplesitemap\\LinkGenerators\\EntityTypeLinkGenerators\\$entity_type";
$link_generator = new $class_name();
$links = $link_generator->get_entity_links($entity_type, $bundles, $this->language);
$links = $link_generator->get_entity_links($entity_type, $bundles, $this->languages);
$this->links = array_merge($this->links, $links);
}
}
}
// Add sitemap markup.
private function add_xml_sitemap_markup($sitemap) {
return "<?xml version=\"" . self::XML_VERSION . "\" encoding=\"" . self::ENCODING . "\"?><urlset xmlns=\"" . self::XMLNS_URL . "\">" . $sitemap . "</urlset>";
}
}
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