Pull merge.
[yaffs-website] / web / modules / contrib / simple_sitemap / src / SitemapGenerator.php
1 <?php
2
3 namespace Drupal\simple_sitemap;
4
5 use Drupal\Core\Database\Connection;
6 use Drupal\Core\Extension\ModuleHandler;
7 use Drupal\Core\Language\LanguageManagerInterface;
8 use Drupal\Component\Datetime\Time;
9
10 /**
11  * Class SitemapGenerator
12  * @package Drupal\simple_sitemap
13  */
14 class SitemapGenerator {
15
16   const XML_VERSION = '1.0';
17   const ENCODING = 'UTF-8';
18   const XMLNS = 'http://www.sitemaps.org/schemas/sitemap/0.9';
19   const XMLNS_XHTML = 'http://www.w3.org/1999/xhtml';
20   const GENERATED_BY = 'Generated by the Simple XML sitemap Drupal module: https://drupal.org/project/simple_sitemap.';
21   const FIRST_CHUNK_INDEX = 1;
22   const XMLNS_IMAGE = 'http://www.google.com/schemas/sitemap-image/1.1';
23
24   /**
25    * @var \Drupal\simple_sitemap\EntityHelper
26    */
27   protected $entityHelper;
28
29   /**
30    * @var \Drupal\Core\Database\Connection
31    */
32   protected $db;
33
34   /**
35    * @var \Drupal\Core\Language\LanguageManagerInterface
36    */
37   protected $languageManager;
38
39   /**
40    * @var \Drupal\Core\Extension\ModuleHandler
41    */
42   protected $moduleHandler;
43
44   /**
45    * @var bool
46    */
47   protected $isHreflangSitemap;
48
49   /**
50    * @var \Drupal\Component\Datetime\Time
51    */
52   protected $time;
53
54   /**
55    * @var array
56    */
57   protected $settings;
58
59   /**
60    * @var \XMLWriter
61    */
62   protected $writer;
63
64   /**
65    * @var array
66    */
67   protected static $attributes = [
68     'xmlns' => self::XMLNS,
69     'xmlns:xhtml' => self::XMLNS_XHTML,
70     'xmlns:image' => self::XMLNS_IMAGE,
71   ];
72
73   /**
74    * @var array
75    */
76   protected static $indexAttributes = [
77     'xmlns' => self::XMLNS,
78   ];
79
80   /**
81    * SitemapGenerator constructor.
82    * @param \Drupal\simple_sitemap\EntityHelper $entityHelper
83    * @param \Drupal\Core\Database\Connection $database
84    * @param \Drupal\Core\Extension\ModuleHandler $module_handler
85    * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
86    * @param \Drupal\Component\Datetime\Time $time
87    * @param \Drupal\simple_sitemap\SitemapWriter $sitemapWriter
88    */
89   public function __construct(
90     EntityHelper $entityHelper,
91     Connection $database,
92     ModuleHandler $module_handler,
93     LanguageManagerInterface $language_manager,
94     Time $time,
95     SitemapWriter $sitemapWriter
96   ) {
97     $this->entityHelper = $entityHelper;
98     $this->db = $database;
99     $this->moduleHandler = $module_handler;
100     $this->languageManager = $language_manager;
101     $this->time = $time;
102     $this->writer = $sitemapWriter;
103   }
104
105   /**
106    * @return bool
107    */
108   protected function isHreflangSitemap() {
109     if (NULL === $this->isHreflangSitemap) {
110       $this->isHreflangSitemap = count(array_diff_key($this->languageManager->getLanguages(), $this->settings['excluded_languages'])) > 1;
111     }
112     return $this->isHreflangSitemap;
113   }
114
115   /**
116    * @param array $settings
117    * @return $this
118    */
119   public function setSettings(array $settings) {
120     $this->settings = $settings;
121     return $this;
122   }
123
124   /**
125    * Wrapper method which takes links along with their options and then
126    * generates and saves the sitemap.
127    *
128    * @param array $links
129    *   All links with their multilingual versions and settings.
130    * @param bool $remove_sitemap
131    *   Remove old sitemap from database before inserting the new one.
132    */
133   public function generateSitemap(array $links, $remove_sitemap = FALSE) {
134     $values = [
135       'id' => $remove_sitemap ? self::FIRST_CHUNK_INDEX
136         : $this->db->query('SELECT MAX(id) FROM {simple_sitemap}')
137           ->fetchField() + 1,
138       'sitemap_string' => $this->generateSitemapChunk($links),
139       'sitemap_created' => $this->time->getRequestTime(),
140     ];
141     if ($remove_sitemap) {
142       $this->db->truncate('simple_sitemap')->execute();
143     }
144     $this->db->insert('simple_sitemap')->fields($values)->execute();
145   }
146
147   /**
148    * Generates and returns the sitemap index for all sitemap chunks.
149    *
150    * @param array $chunk_info
151    *   Array containing chunk creation timestamps keyed by chunk ID.
152    *
153    * @return string sitemap index
154    */
155   public function generateSitemapIndex(array $chunk_info) {
156     $this->writer->openMemory();
157     $this->writer->setIndent(TRUE);
158     $this->writer->startDocument(self::XML_VERSION, self::ENCODING);
159     $this->writer->writeComment(self::GENERATED_BY);
160     $this->writer->startElement('sitemapindex');
161
162     // Add attributes to document.
163     $this->moduleHandler->alter('simple_sitemap_index_attributes', self::$indexAttributes);
164     foreach (self::$indexAttributes as $name => $value) {
165       $this->writer->writeAttribute($name, $value);
166     }
167
168     // Add sitemap locations to document.
169     foreach ($chunk_info as $chunk_id => $chunk_data) {
170       $this->writer->startElement('sitemap');
171       $this->writer->writeElement('loc', $this->getCustomBaseUrl() . '/sitemaps/' . $chunk_id . '/' . 'sitemap.xml');
172       $this->writer->writeElement('lastmod', date_iso8601($chunk_data->sitemap_created));
173       $this->writer->endElement();
174     }
175
176     $this->writer->endElement();
177     $this->writer->endDocument();
178
179     return $this->writer->outputMemory();
180   }
181
182   /**
183    * @return string
184    */
185   public function getCustomBaseUrl() {
186     $customBaseUrl = $this->settings['base_url'];
187     return !empty($customBaseUrl) ? $customBaseUrl : $GLOBALS['base_url'];
188   }
189
190   /**
191    * Generates and returns a sitemap chunk.
192    *
193    * @param array $links
194    *   All links with their multilingual versions and settings.
195    *
196    * @return string
197    *   Sitemap chunk
198    */
199   protected function generateSitemapChunk(array $links) {
200     $this->writer->openMemory();
201     $this->writer->setIndent(TRUE);
202     $this->writer->startDocument(self::XML_VERSION, self::ENCODING);
203     $this->writer->writeComment(self::GENERATED_BY);
204     $this->writer->startElement('urlset');
205
206     // Add attributes to document.
207     if (!$this->isHreflangSitemap()) {
208       unset(self::$attributes['xmlns:xhtml']);
209     }
210     $this->moduleHandler->alter('simple_sitemap_attributes', self::$attributes);
211     foreach (self::$attributes as $name => $value) {
212       $this->writer->writeAttribute($name, $value);
213     }
214
215     // Add URLs to document.
216     $this->moduleHandler->alter('simple_sitemap_links', $links);
217     foreach ($links as $link) {
218
219       // Add each translation variant URL as location to the sitemap.
220       $this->writer->startElement('url');
221       $this->writer->writeElement('loc', $link['url']);
222
223       // If more than one language is enabled, add all translation variant URLs
224       // as alternate links to this location turning the sitemap into a hreflang
225       // sitemap.
226       if (isset($link['alternate_urls']) && $this->isHreflangSitemap()) {
227         foreach ($link['alternate_urls'] as $language_id => $alternate_url) {
228           $this->writer->startElement('xhtml:link');
229           $this->writer->writeAttribute('rel', 'alternate');
230           $this->writer->writeAttribute('hreflang', $language_id);
231           $this->writer->writeAttribute('href', $alternate_url);
232           $this->writer->endElement();
233         }
234       }
235
236       // Add lastmod if any.
237       if (isset($link['lastmod'])) {
238         $this->writer->writeElement('lastmod', $link['lastmod']);
239       }
240
241       // Add changefreq if any.
242       if (isset($link['changefreq'])) {
243         $this->writer->writeElement('changefreq', $link['changefreq']);
244       }
245
246       // Add priority if any.
247       if (isset($link['priority'])) {
248         $this->writer->writeElement('priority', $link['priority']);
249       }
250
251       // Add images if any.
252       if (!empty($link['images'])) {
253         foreach ($link['images'] as $image) {
254           $this->writer->startElement('image:image');
255           $this->writer->writeElement('image:loc', $image['path']);
256           $this->writer->endElement();
257         }
258       }
259
260       $this->writer->endElement();
261     }
262     $this->writer->endElement();
263     $this->writer->endDocument();
264
265     return $this->writer->outputMemory();
266   }
267
268 }