Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
sitemapiblock.php
1<?php
8namespace Bitrix\Seo;
9
12
29class SitemapIblockTable extends Entity\DataManager
30{
31 const ACTIVE = 'Y';
32 const INACTIVE = 'N';
33
34 const TYPE_ELEMENT = 'E';
35 const TYPE_SECTION = 'S';
36
37 protected static $iblockCache = array();
38
44 public static function getTableName()
45 {
46 return 'b_seo_sitemap_iblock';
47 }
48
54 public static function getMap()
55 {
56 $fieldsMap = array(
57 'ID' => array(
58 'data_type' => 'integer',
59 'primary' => true,
60 'autocomplete' => true,
61 ),
62 'SITEMAP_ID' => array(
63 'data_type' => 'integer',
64 'required' => true,
65 ),
66 'IBLOCK_ID' => array(
67 'data_type' => 'integer',
68 'required' => true,
69 ),
70 'SITEMAP' => array(
71 'data_type' => 'Bitrix\Seo\SitemapTable',
72 'reference' => array('=this.SITEMAP_ID' => 'ref.ID'),
73 ),
74 'IBLOCK' => array(
75 'data_type' => 'Bitrix\Iblock\IblockTable',
76 'reference' => array('=this.IBLOCK_ID' => 'ref.ID'),
77 ),
78 );
79
80 return $fieldsMap;
81 }
82
90 public static function clearBySitemap($sitemapId)
91 {
92 $connection = \Bitrix\Main\Application::getConnection();
93 $query = $connection->query("
94DELETE
95FROM ".self::getTableName()."
96WHERE SITEMAP_ID='".intval($sitemapId)."'
97");
98 }
99
109 public static function getByIblock($fields, $itemType)
110 {
111 $sitemaps = array();
112
113 if(!isset(self::$iblockCache[$fields['IBLOCK_ID']]))
114 {
115 self::$iblockCache[$fields['IBLOCK_ID']] = array();
116
117 $dbRes = self::getList(array(
118 'filter' => array(
119 'IBLOCK_ID' => $fields['IBLOCK_ID']
120 ),
121 'select' => array('SITEMAP_ID',
122 'SITE_ID' => 'SITEMAP.SITE_ID', 'SITEMAP_SETTINGS' => 'SITEMAP.SETTINGS',
123 'IBLOCK_CODE' => 'IBLOCK.CODE', 'IBLOCK_XML_ID' => 'IBLOCK.XML_ID',
124 'DETAIL_PAGE_URL' => 'IBLOCK.DETAIL_PAGE_URL',
125 'SECTION_PAGE_URL' => 'IBLOCK.SECTION_PAGE_URL',
126 ),
127 ));
128
129 while($res = $dbRes->fetch())
130 {
131 self::$iblockCache[$fields['IBLOCK_ID']][] = $res;
132 }
133 }
134
135 foreach(self::$iblockCache[$fields['IBLOCK_ID']] as $res)
136 {
137 $sitemapSettings = unserialize($res['SITEMAP_SETTINGS'], ['allowed_classes' => false]);
138
139 $add = false;
140
141 if($itemType == self::TYPE_SECTION)
142 {
143 $add = self::checkSection(
144 $fields['ID'],
145 $sitemapSettings['IBLOCK_SECTION_SECTION'][$fields['IBLOCK_ID']],
146 $sitemapSettings['IBLOCK_SECTION'][$fields['IBLOCK_ID']]
147 );
148 }
149 else
150 {
151 if(is_array($fields['IBLOCK_SECTION']) && count($fields['IBLOCK_SECTION']) > 0)
152 {
153 foreach($fields['IBLOCK_SECTION'] as $sectionId)
154 {
155 $add = self::checkSection(
156 $sectionId,
157 $sitemapSettings['IBLOCK_SECTION_ELEMENT'][$fields['IBLOCK_ID']],
158 $sitemapSettings['IBLOCK_ELEMENT'][$fields['IBLOCK_ID']]
159 );
160
161 if($add)
162 {
163 break;
164 }
165 }
166 }
167 else
168 {
169 $add = $sitemapSettings['IBLOCK_ELEMENT'][$fields['IBLOCK_ID']] == 'Y';
170 }
171 }
172
173 if($add)
174 {
175 $sitemaps[] = array(
176 'IBLOCK_CODE' => $res['IBLOCK_CODE'],
177 'IBLOCK_XML_ID' => $res['IBLOCK_XML_ID'],
178 'DETAIL_PAGE_URL' => $res['DETAIL_PAGE_URL'],
179 'SECTION_PAGE_URL' => $res['SECTION_PAGE_URL'],
180 'SITE_ID' => $res['SITE_ID'],
181 'PROTOCOL' => $sitemapSettings['PROTO'] == 1 ? 'https' : 'http',
182 'DOMAIN' => $sitemapSettings['DOMAIN'],
183 'ROBOTS' => $sitemapSettings['ROBOTS'],
184 'SITEMAP_DIR' => $sitemapSettings['DIR'],
185 'SITEMAP_FILE' => $sitemapSettings['FILENAME_INDEX'],
186 'SITEMAP_FILE_IBLOCK' => $sitemapSettings['FILENAME_IBLOCK'],
187 );
188 }
189 }
190
191 return $sitemaps;
192 }
193
203 public static function checkSection($sectionId, $sectionSettings, $defaultValue)
204 {
205 $value = $defaultValue;
206
207 if(is_array($sectionSettings) && count($sectionSettings) > 0)
208 {
209 while ($sectionId > 0)
210 {
211 if(isset($sectionSettings[$sectionId]))
212 {
213 $value = $sectionSettings[$sectionId];
214 break;
215 }
216
217 $dbRes = \CIBlockSection::getList(array(), array('ID' => $sectionId), false, array('ID', 'IBLOCK_SECTION_ID'));
218 $section = $dbRes->fetch();
219
220 $sectionId = $section["IBLOCK_SECTION_ID"];
221 }
222 }
223
224 return $value === 'Y';
225 }
226}
227
229{
230 private static $beforeActions = array(
231 'BEFOREDELETEELEMENT' => array(array(),array()),
232 'BEFOREDELETESECTION' => array(array(),array()),
233 'BEFOREUPDATEELEMENT' => array(array(),array()),
234 'BEFOREUPDATESECTION' => array(array(),array()),
235 );
236
240 public static function __callStatic($name, $arguments)
241 {
242 $name = ToUpper($name);
243
244 switch($name)
245 {
246 case 'ADDELEMENT':
247 case 'ADDSECTION':
248 if(
249 $arguments[0]["ID"] > 0
250 && $arguments[0]['IBLOCK_ID'] > 0
251 && (
252 !isset($arguments[0]['ACTIVE'])
253 || $arguments[0]['ACTIVE'] == 'Y'
254 )
255 )
256 {
257 // we recieve array reference here
258 $fields = array();
259 foreach($arguments[0] as $key => $value)
260 {
261 $fields[$key] = $value;
262 }
263
264 if(!isset($fields['EXTERNAL_ID']) && isset($fields['XML_ID']))
265 {
266 $fields['EXTERNAL_ID'] = $fields['XML_ID'];
267 }
268
269 self::actionAdd($name, $fields);
270 }
271 break;
272
273 case 'BEFOREDELETEELEMENT':
274 case 'BEFOREDELETESECTION':
275 case 'BEFOREUPDATEELEMENT':
276 case 'BEFOREUPDATESECTION':
277 $ID = $arguments[0];
278 if(is_array($ID))
279 $ID = $ID['ID'];
280
281 if($ID > 0)
282 {
283 $element = $name == 'BEFOREDELETEELEMENT' || $name == 'BEFOREUPDATEELEMENT';
284
285 $dbFields = $element
286 ? \CIBlockElement::getByID($ID)
287 : \CIBlockSection::getByID($ID);
288
289 $fields = $dbFields->getNext();
290 if($fields)
291 {
292 if($element && !self::checkElement($fields))
293 {
294 return;
295 }
296
298 $fields,
300 );
301
302 if (count($sitemaps) > 0)
303 {
304// URL from $fields may be incorrect, if using #SERVER_NAME# template in iblock URL-template
305// And we must generate true URL
306 $dbIblock = \CIBlock::GetByID($fields['IBLOCK_ID']);
307 $iblock = $dbIblock->GetNext();
308 $url = $element
309 ? $iblock['~DETAIL_PAGE_URL']
310 : $iblock['~SECTION_PAGE_URL'];
311 $url = self::prepareUrlToReplace($url);
312 $url = \CIBlock::replaceDetailUrl($url, $fields, false, $element ? 'E' : 'S');
313
314 self::$beforeActions[$name][intval($element)][$ID] = array(
315 'URL' => $url,
316 'FIELDS' => $fields,
317 'SITEMAPS' => $sitemaps,
318 );
319 }
320 }
321 }
322 break;
323
324 case 'DELETEELEMENT':
325 case 'DELETESECTION':
326 case 'UPDATEELEMENT':
327 case 'UPDATESECTION':
328
329 $fields = $arguments[0];
330 $element = $name == 'DELETEELEMENT' || $name == 'UPDATEELEMENT';
331
332 if(
333 is_array($fields)
334 && $fields['ID'] > 0
335 && isset(self::$beforeActions['BEFORE'.$name][intval($element)][$fields['ID']])
336 )
337 {
338 if($fields['RESULT'] !== false)
339 {
340 if($name == 'DELETEELEMENT' || $name == 'DELETESECTION')
341 {
342 self::actionDelete(self::$beforeActions['BEFORE'.$name][intval($element)][$fields['ID']]);
343 }
344 else
345 {
346 self::actionUpdate(self::$beforeActions['BEFORE'.$name][intval($element)][$fields['ID']], $element);
347 }
348 }
349
350 unset(self::$beforeActions['BEFORE'.$name][intval($element)][$fields['ID']]);
351 }
352
353 break;
354
355 }
356 }
357
365 protected static function checkElement(&$fields)
366 {
367 if($fields['WF'] === 'Y')
368 {
369 if(
370 $fields['WF_PARENT_ELEMENT_ID'] > 0
371 && $fields['WF_PARENT_ELEMENT_ID'] != $fields['ID']
372 && $fields['WF_STATUS_ID'] == 1
373 )
374 {
375 $fields['ID'] = $fields['WF_PARENT_ELEMENT_ID'];
376 }
377 else
378 {
379 return false;
380 }
381 }
382
383 return true;
384 }
385
392 protected static function actionUpdate($data, $element)
393 {
394 $fields = $data['FIELDS'];
395 $siteDirs = self::createSiteDirs();
396
397 foreach ($data['SITEMAPS'] as $sitemap)
398 {
399 $fileName = str_replace(
400 array('#IBLOCK_ID#', '#IBLOCK_CODE#', '#IBLOCK_XML_ID#'),
401 array($fields['IBLOCK_ID'], $sitemap['IBLOCK_CODE'], $sitemap['IBLOCK_XML_ID']),
402 $sitemap['SITEMAP_FILE_IBLOCK']
403 );
404
405 if($element)
406 $dbRes = \CIBlockElement::getByID($fields["ID"]);
407 else
408 $dbRes = \CIBlockSection::getByID($fields["ID"]);
409
410 $newFields = $dbRes->fetch();
411
412 $sitemapFile = new SitemapFile($fileName, $sitemap);
413// try remove entry from original file, to not create temp files to all parts
414// name may was changed in removeEntry
415 $fileName = $sitemapFile->removeEntry($data['URL']);
416
417// write changes to temp file to preserve collisions
418 $sitemapRuntimeId = $sitemap['SITE_ID'] . '-' . uniqid();
419 $sitemapRuntimeId .= isset($fields['ID']) ? '-' . $fields['ID'] . '-' : '';
420 $sitemapRuntimeFile = new SitemapRuntime($sitemapRuntimeId, $fileName, $sitemap);
421
422// check ACTIVITY by active, date or etc, add entry only for active
423 if(self::checkActivity($element, $newFields))
424 {
425 $newFields['LANG_DIR'] = $siteDirs[$sitemap['SITE_ID']];
426
427// use just date(). it is not true, but because we use BEFORE event, we cant use real lastmod date, only previous value
428 $date = date('d.m.Y H:i:s');
429
430 $url = $element ? $sitemap['DETAIL_PAGE_URL'] : $sitemap['SECTION_PAGE_URL'];
431 $urlType = $element ? 'E' : 'S';
432// remove or replace SERVER_NAME
433 $url = self::prepareUrlToReplace($url, $sitemap['SITE_ID']);
434 $rule = array(
435 'url' => \CIBlock::replaceDetailUrl($url, $newFields, false, $urlType),
436 'lastmod' => MakeTimeStamp($date),
437 );
438
439 $sitemapRuntimeFile->setOriginalFile($sitemapFile);
440 $sitemapRuntimeFile->appendIblockEntry($rule['url'], $rule['lastmod']);
441 }
442
443// rename RUNTIME file to original SITEMAPFILE name, or just remove TMP file
444// after this in original file will be added always changes
445 if ($sitemapRuntimeFile->isNotEmpty() && $sitemapRuntimeFile->isCurrentPartNotEmpty())
446 $sitemapRuntimeFile->finish();
447 else
448 $sitemapRuntimeFile->delete();
449
450 $sitemapIndex = new SitemapIndex($sitemap['SITEMAP_FILE'], $sitemap);
451 $sitemapIndex->appendIndexEntry($sitemapFile);
452
453 if ($sitemap['ROBOTS'] == 'Y')
454 {
455 $robotsFile = new RobotsFile($sitemap['SITE_ID']);
456 $robotsFile->addRule(
457 array(RobotsFile::SITEMAP_RULE, $sitemapIndex->getUrl())
458 );
459 }
460 }
461 }
462
468 protected static function actionDelete($data)
469 {
470 $fields = $data['FIELDS'];
471 foreach ($data['SITEMAPS'] as $sitemap)
472 {
473 $fileName = str_replace(
474 array('#IBLOCK_ID#', '#IBLOCK_CODE#', '#IBLOCK_XML_ID#'),
475 array($fields['IBLOCK_ID'], $sitemap['IBLOCK_CODE'], $sitemap['IBLOCK_XML_ID']),
476 $sitemap['SITEMAP_FILE_IBLOCK']
477 );
478
479 $sitemapFile = new SitemapFile($fileName, $sitemap);
480 $sitemapFile->removeEntry($data['URL']);
481
482 $sitemapIndex = new SitemapIndex($sitemap['SITEMAP_FILE'], $sitemap);
483 $sitemapIndex->appendIndexEntry($sitemapFile);
484 }
485 }
486
493 protected static function actionAdd($name, $fields)
494 {
495 if($name == 'ADDELEMENT')
496 {
497 if(!self::checkElement($fields))
498 {
499 return;
500 }
501
502 // we don't have the GLOBAL_ACTIVE flag in fields so we should check it manually
503 if(is_array($fields['IBLOCK_SECTION']) && count($fields['IBLOCK_SECTION']) > 0)
504 {
505 $newSections = array();
506 $filter = array(
507 'ID' => $fields['IBLOCK_SECTION'],
508 'IBLOCK_ID' => $fields['IBLOCK_ID'],
509 'GLOBAL_ACTIVE' => 'Y'
510 );
511
512 $dbRes = \CIBlockSection::getList(array(), $filter, false, array('ID', 'IBLOCK_TYPE_ID', 'IBLOCK_CODE'));
513 while ($ar = $dbRes->fetch())
514 {
515 $newSections[] = $ar['ID'];
516 $iblockTypeId = $ar['IBLOCK_TYPE_ID'] ? $ar['IBLOCK_TYPE_ID'] : null;
517 $iblockCode = $ar['IBLOCK_CODE'] ? $ar['IBLOCK_CODE'] : null;
518 }
519
520 if(count($newSections) <= 0)
521 {
522 // element is added to inactive sections
523 return;
524 }
525
526 $fields['IBLOCK_SECTION'] = $newSections;
527 }
528 }
529 elseif($name == 'ADDSECTION')
530 {
531 $dbRes = \CIBlockSection::getList(
532 array(),
533 array('ID' => $fields['ID'], 'GLOBAL_ACTIVE' => 'Y'),
534 false,
535 array('ID', 'IBLOCK_TYPE_ID', 'IBLOCK_CODE')
536 );
537
538 $inactiveBranch = true;
539 while ($ar = $dbRes->fetch())
540 {
541 $iblockTypeId = $ar['IBLOCK_TYPE_ID'] ? $ar['IBLOCK_TYPE_ID'] : null;
542 $iblockCode = $ar['IBLOCK_CODE'] ? $ar['IBLOCK_CODE'] : null;
543 $inactiveBranch = false;
544 }
545
546 if ($inactiveBranch)
547 {
548 // section is added to inactive branch
549 return;
550 }
551 }
552
553 $fields['IBLOCK_TYPE_ID'] = $iblockTypeId;
554 $fields['IBLOCK_CODE'] = $iblockCode;
555
557 $fields,
559 );
560
561 $fields['TIMESTAMP_X'] = ConvertTimeStamp(false, "FULL");
562
563 if(isset($fields['IBLOCK_SECTION']) && is_array($fields['IBLOCK_SECTION']) && count($fields['IBLOCK_SECTION']) > 0)
564 {
565 $fields['IBLOCK_SECTION_ID'] = min($fields['IBLOCK_SECTION']);
566 }
567
568 if(count($sitemaps) > 0)
569 {
570 $siteDirs = self::createSiteDirs();
571
572 foreach ($sitemaps as $sitemap)
573 {
574 $fileName = str_replace(
575 array('#IBLOCK_ID#', '#IBLOCK_CODE#', '#IBLOCK_XML_ID#'),
576 array($fields['IBLOCK_ID'], $sitemap['IBLOCK_CODE'], $sitemap['IBLOCK_XML_ID']),
577 $sitemap['SITEMAP_FILE_IBLOCK']
578 );
579
580 $sitemapFile = new SitemapFile($fileName, $sitemap);
581
582// write changes to temp file to preserve collisions
583 $sitemapRuntimeId = $sitemap['SITE_ID'] . '-' . uniqid();
584 $sitemapRuntimeId .= isset($fields['ID']) ? '-' . $fields['ID'] . '-' : '';
585 $sitemapRuntimeFile = new SitemapRuntime($sitemapRuntimeId, $fileName, $sitemap);
586
587 if(self::checkActivity($name == 'ADDELEMENT' ? true : false, $fields))
588 {
589 $fields['LANG_DIR'] = $siteDirs[$sitemap['SITE_ID']];
590
591 $url = $name == 'ADDSECTION' ? $sitemap['SECTION_PAGE_URL'] : $sitemap['DETAIL_PAGE_URL'];
592 $urlType = $name == 'ADDSECTION' ? 'S' : 'E';
593// remove or replace SERVER_NAME
594 $url = self::prepareUrlToReplace($url, $sitemap['SITE_ID']);
595 $rule = array(
596 'url' => \CIBlock::replaceDetailUrl($url, $fields, false, $urlType),
597 'lastmod' => MakeTimeStamp($fields['TIMESTAMP_X']),
598 );
599
600 $sitemapRuntimeFile->setOriginalFile($sitemapFile);
601 $sitemapRuntimeFile->appendIblockEntry($rule['url'], $rule['lastmod']);
602 }
603
604// after this in original file will be added always changes
605 if ($sitemapRuntimeFile->isNotEmpty() && $sitemapRuntimeFile->isCurrentPartNotEmpty())
606 $sitemapRuntimeFile->finish();
607 else
608 $sitemapRuntimeFile->delete();
609
610 $sitemapIndex = new SitemapIndex($sitemap['SITEMAP_FILE'], $sitemap);
611 $sitemapIndex->appendIndexEntry($sitemapFile);
612
613 if ($sitemap['ROBOTS'] == 'Y')
614 {
615 $robotsFile = new RobotsFile($sitemap['SITE_ID']);
616 $robotsFile->addRule(
617 array(RobotsFile::SITEMAP_RULE, $sitemapIndex->getUrl())
618 );
619 }
620 }
621 }
622 }
623
624
625 private static function createSiteDirs()
626 {
627 $siteDirs = array();
628 $dbSite = SiteTable::getList(array('select' => array('LID', 'DIR', 'SERVER_NAME')));
629 while ($site = $dbSite->fetch())
630 {
631 $siteDirs[$site['LID']] = $site['DIR'];
632 }
633
634 return $siteDirs;
635 }
636
637
638 private static function checkActivity($isElement, $fields)
639 {
640 if(array_key_exists("ACTIVE", $fields) && $fields["ACTIVE"] == "N")
641 return false;
642
643// for iblock element and iblock section we check different fields
644 if($isElement)
645 {
646// activity may be in field DATE_ACTIVE_ or ACTIVE_, check both
647 if(
648 array_key_exists("DATE_ACTIVE_FROM", $fields) && $fields["DATE_ACTIVE_FROM"] &&
649 new \DateTime($fields["DATE_ACTIVE_FROM"]) > new \DateTime($fields["TIMESTAMP_X"])
650 )
651 return false;
652 if(
653 array_key_exists("ACTIVE_FROM", $fields) && $fields["ACTIVE_FROM"] &&
654 new \DateTime($fields["ACTIVE_FROM"]) > new \DateTime($fields["TIMESTAMP_X"])
655 )
656 return false;
657
658 if(
659 array_key_exists("DATE_ACTIVE_TO", $fields) && $fields["DATE_ACTIVE_TO"] &&
660 new \DateTime($fields["DATE_ACTIVE_TO"]) < new \DateTime($fields["TIMESTAMP_X"])
661 )
662 return false;
663 if(
664 array_key_exists("ACTIVE_TO", $fields) && $fields["ACTIVE_TO"] &&
665 new \DateTime($fields["ACTIVE_TO"]) < new \DateTime($fields["TIMESTAMP_X"])
666 )
667 return false;
668 }
669 else
670 {
671 if(array_key_exists("GLOBAL_ACTIVE", $fields) && $fields["GLOBAL_ACTIVE"] == "N")
672 return false;
673 }
674
675 return true;
676 }
677
678
686 public static function prepareUrlToReplace($url, $siteId = NULL)
687 {
688// REMOVE PROTOCOL - we put them later, based on user settings
689 $url = str_replace('http://', '', $url);
690 $url = str_replace('https://', '', $url);
691
692// REMOVE SERVER_NAME from start position, because we put server_url later
693 if (mb_substr($url, 0, mb_strlen('#SERVER_NAME#')) == '#SERVER_NAME#')
694 $url = mb_substr($url, mb_strlen('#SERVER_NAME#'));
695
696// get correct SERVER_URL
697 if ($siteId)
698 {
699 $filter = array('=LID' => $siteId);
700 $dbSite = SiteTable::getList(array(
701 'filter' => $filter,
702 'select' => array('LID', 'DIR', 'SERVER_NAME'),
703 ));
704 $currentSite = $dbSite->fetch();
705 $serverName = $currentSite['SERVER_NAME'];
706 $url = str_replace('#SERVER_NAME#', $serverName, $url);
707 }
708
709 return $url;
710 }
711}
static checkElement(&$fields)
static actionAdd($name, $fields)
static __callStatic($name, $arguments)
static actionUpdate($data, $element)
static prepareUrlToReplace($url, $siteId=NULL)
static getByIblock($fields, $itemType)
static clearBySitemap($sitemapId)
static checkSection($sectionId, $sectionSettings, $defaultValue)