File manager - Edit - /home/opticamezl/www/newok/administrator/components/com_osmap/library/Alledia/OSMap/Sitemap/Collector.php
Back
<?php /** * @package OSMap * @contact www.joomlashack.com, help@joomlashack.com * @copyright 2007-2014 XMap - Joomla! Vargas - Guillermo Vargas. All rights reserved. * @copyright 2016-2025 Joomlashack.com. All rights reserved. * @license https://www.gnu.org/licenses/gpl.html GNU/GPL * * This file is part of OSMap. * * OSMap is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * OSMap is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OSMap. If not, see <https://www.gnu.org/licenses/>. */ namespace Alledia\OSMap\Sitemap; use Alledia\OSMap\Factory; use Alledia\OSMap\Helper\General; use Joomla\CMS\Component\ComponentHelper; use Joomla\Registry\Registry; defined('_JEXEC') or die(); /** * Sitemap items collector */ class Collector { /** * @var SitemapInterface */ protected $sitemap; /** * @var array */ protected $uidList = []; /** * @var array */ protected $urlHashList = []; /** * Callback used to trigger the desired action while fetching items. * This is only used in the legacy method printNode, which is called by * the osmap plugins to process the additional items. * * @var callable */ protected $printNodeCallback; /** * The current view: xml or html. Kept for backward compatibility with * the legacy plugins. It is always XML since the collector is generic now * and needs to have the information about the item's level even for the * XML view in the Pro version * * @var string */ public $view = 'xml'; /** * Legacy property used by some plugins. True if we are collecting news. * * @var string * * @deprecated */ public $isNews = false; /** * The items counter. * * @var int */ protected $counter = 0; /** * The custom settings for items * * @var array */ protected $itemsSettings = null; /** * The legacy custom settings for items. Which will be upgraded * * @var array */ protected $legacyItemsSettings = null; /** * If false, say that any next sub-level should be unpublished * * @var mixed */ protected $unpublishLevel = false; /** * @var array */ protected $tmpItemDefaultSettings = [ 'changefreq' => 'weekly', 'priority' => '0.5' ]; /** * The current items level * * @var int */ protected $currentLevel = 0; /** * The reference for the instance of the current menu for item and its * subitems. * * @var object */ protected $currentMenu; /** * The ID of the current menu item for nodes * * @var int */ protected $currentMenuItemId = null; /** * The component's params * * @var Registry */ public $params; /** * Collector constructor. * * @param SitemapInterface $sitemap * * @return void */ public function __construct(SitemapInterface $sitemap) { $this->sitemap = $sitemap; $this->params = ComponentHelper::getParams('com_osmap'); /* * Try to detect the current view. This is just for backward compatibility * for legacy plugins. New plugins doesn't need to know what is the view. * They always calculate the visibility for both views and the view is * the one who decides to whow or not. If not equals HTML, is always XML. */ $inputView = Factory::getPimpleContainer()->input->get('view', 'xml'); if ($inputView === 'html') { $this->view = 'html'; } } /** * Collects sitemap items based on the selected menus. This is the main * method of this class. For each found item, it will call the given * callback, so it can manipulate the data in many ways. It returns the * total of found items. * * @param callable $callback * * @return int */ public function fetch(callable $callback): int { $menus = $this->getSitemapMenus(); $this->counter = 0; if ($menus) { $this->getLegacyItemsSettings(); $this->getItemsSettings(); foreach ($menus as $menu) { $this->currentMenu = &$menu; $items = $this->getMenuItems($menu); foreach ($items as $item) { if ($this->itemIsBlackListed($item)) { $item = null; continue; } // Store the current menu item id. Added to use it while defining the node's settings hash, so same // items, but from different menus can have individual settings $this->currentMenuItemId = $item['id']; // Set the menu item UID. The UID can be changed by 3rd party plugins, according to the content $item['uid'] = 'menuitem.' . $item['id']; // Store the menu settings to use in the submitItemToCallback called by callbacks $this->tmpItemDefaultSettings['changefreq'] = $menu->changefreq; $this->tmpItemDefaultSettings['priority'] = $menu->priority; // Check the level of menu $level = (int)$item['level'] - 1; if ($level !== $this->currentLevel) { $this->changeLevel($level - $this->currentLevel); } // Submit the item and prepare it calling the plugins $this->submitItemToCallback($item, $callback, true); // Internal links can trigger plugins to grab more items // The child items are not displayed if the parent item is ignored if ($item->isInternal && !$item->ignore) { // Call the plugin to get additional items related to it $this->callPluginsGetItemTree($item, $callback); } // Make sure the memory is cleaned up $item = null; } $items = []; unset($items); } $menu = null; } $this->currentMenu = null; $this->tmpItemDefaultSettings = []; return $this->counter; } /** * Submit the item to the callback, checking duplicity and incrementing * the counter. It can receive an array or object and returns true or false * according to the result of the callback. * * @param array|object $item * @param callable $callback * @param bool $prepareItem * * @return bool */ public function submitItemToCallback(&$item, callable $callback, bool $prepareItem = false): bool { $currentMenuItemId = $this->getCurrentMenuItemId(); $item = (object)$item; // Add the menu information $item->menuItemId = $this->currentMenu->id; $item->menuItemTitle = $this->currentMenu->name; $item->menuItemType = $this->currentMenu->menutype; // Converts to an Item instance, setting internal attributes $item = new Item($item, $currentMenuItemId); if ($prepareItem) { // Call the plugins to prepare the item $this->callPluginsPreparingTheItem($item); } // Make sure to have the correct date format (UTC) $item->setModificationDate(); $item->setAdapter(); $item->visibleForRobots = $item->adapter->checkVisibilityForRobots(); // Set the current level to the item $item->level = $this->currentLevel; $this->setItemCustomSettings($item); $this->checkParentIsUnpublished($item); $this->checkDuplicatedUIDToIgnore($item); // Verify if the item can be displayed to count as unique for the XML sitemap if ( !$item->ignore && $item->published && $item->visibleForRobots && (!$item->duplicate || !$this->params->get('ignore_duplicated_uids', 1)) ) { // Check if the URL is not duplicated (specially for the XML sitemap) $this->checkDuplicatedURLToIgnore($item); if (!$item->duplicate || !$this->params->get('ignore_duplicated_uids', 1)) { ++$this->counter; } } return (bool)call_user_func_array($callback, [&$item]); } /** * Gets the list of selected menus for the sitemap. * It returns a list of objects with the attributes: * - name * - menutype * - priority * - changefrq * - ordering * * @return object[]; */ protected function getSitemapMenus(): array { $db = Factory::getDbo(); $query = $db->getQuery(true) ->select([ 'mt.id', 'mt.title AS ' . $db->quoteName('name'), 'mt.menutype', 'osm.changefreq', 'osm.priority', 'osm.ordering' ]) ->from('#__osmap_sitemap_menus AS osm') ->join('LEFT', '#__menu_types AS mt ON (osm.menutype_id = mt.id)') ->where('osm.sitemap_id = ' . $db->quote($this->sitemap->id)) ->order('osm.ordering'); return $db->setQuery($query)->loadObjectList('menutype'); } /** * Get the menu items as a tree * * @param object $menu * * @return array */ protected function getMenuItems(object $menu): array { $container = Factory::getPimpleContainer(); $db = $container->db; $app = $container->app; $lang = $container->language; $query = $db->getQuery(true) ->select([ 'm.id', 'm.title AS ' . $db->quoteName('name'), 'm.alias', 'm.path', 'm.level', 'm.type', 'm.home', 'm.params', 'm.parent_id', 'm.browserNav', 'm.link', '1 AS ' . $db->quoteName('isMenuItem'), // Say that the menu came from a menu '0 AS ' . $db->quoteName('ignore') // Flag that allows child classes choose to ignore items ]) ->from('#__menu AS m') ->join('INNER', '#__menu AS p ON (p.lft = 0)') ->where([ 'm.menutype = ' . $db->quote($menu->menutype), 'm.published = 1', sprintf('m.access IN (%s)', General::getAuthorisedViewLevels()), 'm.lft > p.lft', 'm.lft < p.rgt' ]) ->order('m.lft'); if ($app->isClient('site')) { if ($app->getLanguageFilter()) { $languageTags = array_map([$db, 'quote'], [$lang->getTag(), '*']); $query->where(sprintf('m.language IN (%s)', join(',', $languageTags))); } } $items = $db->setQuery($query)->loadAssocList(); if ($this->params->get('ignore_hidden_menus', false)) { $items = array_filter( $items, function ($menu) { $params = json_decode($menu['params']); if (isset($params->menu_show) && $params->menu_show == 0) { return false; } return true; } ); } return $items; } /** * Checks if the item's uid was already registered. If positive, set the * item to be ignored and return true. If negative, register the item and * return false. * * @param Item $item * * @return bool */ protected function checkDuplicatedUIDToIgnore(Item $item): bool { // If is already set, interrupt the flux and ignore the item if (isset($this->uidList[$item->uid])) { $item->duplicate = true; if ($this->params->get('ignore_duplicated_uids', 1)) { $item->addAdminNote('COM_OSMAP_ADMIN_NOTE_DUPLICATED_IGNORED'); } else { $item->addAdminNote('COM_OSMAP_ADMIN_NOTE_DUPLICATED'); } return true; } // Not set and published, so let's register if ($item->published && $item->visibleForRobots && !$item->ignore) { $this->uidList[$item->uid] = 1; } return false; } /** * Checks if the item's full link was already registered. If positive, * set the item to be ignored and return true. If negative, register the item and return false * * @param Item $item * * @return bool */ protected function checkDuplicatedURLToIgnore(Item $item): bool { if (!empty($item->fullLink)) { $container = Factory::getPimpleContainer(); // We need to make sure to have a URL free of hash chars $url = $container->router->removeHashFromURL($item->fullLink); $hash = $container->router->createUrlHash($url); if (isset($this->urlHashList[$hash])) { $item->duplicate = true; $item->addAdminNote('COM_OSMAP_ADMIN_NOTE_DUPLICATED_URL_IGNORED'); return true; } // Not set and published, so let's register if ($item->published && $item->visibleForRobots && !$item->ignore) { $this->urlHashList[$hash] = 1; } } return false; } /** * Calls the respective OSMap and XMap plugin, according to the item's * component/option. If the plugin's method returns false, it will set * the item's ignore attribute to true. * * @param Item $item * * @return void */ protected function callPluginsPreparingTheItem(Item $item) { $plugins = General::getPluginsForComponent($item->component); foreach ($plugins as $plugin) { $className = '\\' . $plugin->className; if (method_exists($className, 'prepareMenuItem')) { if ($plugin->isLegacy) { $params = $plugin->params->toArray(); } else { $params =& $plugin->params; } $arguments = [ &$item, &$params ]; // If a legacy plugin doesn't specify this method as static, fix the plugin to avoid warnings $result = General::callUserFunc( $className, $plugin->instance, 'prepareMenuItem', $arguments ); // If a plugin doesn't return true we ignore the item and break if ($result === false) { $item->set('ignore', true); break; } } $plugin = null; } } /** * Calls the respective OSMap and XMap plugin, according to the item's * component/option. Get additional items and send to the callback. * * @param Item $item * @param callable $callback * * @return void */ protected function callPluginsGetItemTree(Item $item, callable $callback) { $this->printNodeCallback = $callback; // Call the OSMap and XMap legacy plugins $plugins = General::getPluginsForComponent($item->component); foreach ($plugins as $plugin) { $className = '\\' . $plugin->className; if (method_exists($className, 'getTree')) { if ($plugin->isLegacy) { $params = $plugin->params->toArray(); } else { $params = $plugin->params; } $arguments = [ &$this, &$item, &$params ]; General::callUserFunc( $className, $plugin->instance, 'getTree', $arguments ); } } } /** * Returns true if the link of the item is in the blacklist array. * * @param array $item * * @return bool */ protected function itemIsBlackListed(array $item): bool { $blackList = [ 'administrator' => 1 ]; $link = $item['link']; return isset($blackList[$link]); } /** * This method is used for backward compatibility. The plugins will call * it. In the legacy XMap, its behavior depends on the sitemap view type, * only changing the level in the HTML view. OSMap will always consider the * level of the item, even for XML view. XML will just ignore that. * * @param int $step * * @return void */ public function changeLevel(int $step) { $this->currentLevel += $step; } /** * Method called by legacy plugins, which will pass the new item to the * callback. Returns the result of the callback converted to boolean. * * @param array|object $node * * @return bool */ public function printNode($node): bool { return $this->submitItemToCallback($node, $this->printNodeCallback); } /** * This method gets the settings for all items which have custom settings. * * @return array; */ protected function getItemsSettings(): array { if (empty($this->itemsSettings)) { $db = Factory::getDbo(); $query = $db->getQuery(true) ->select([ '*', sprintf( 'IF (IFNULL(settings_hash, %1$s) = %1$s, uid, CONCAT(uid, %2$s, settings_hash)) AS %3$s', $db->quote(''), $db->quote(':'), $db->quoteName('key') ) ]) ->from('#__osmap_items_settings') ->where('sitemap_id = ' . $db->quote($this->sitemap->id)) ->where($db->quoteName('format') . ' = 2'); $this->itemsSettings = $db->setQuery($query)->loadAssocList('key'); } return $this->itemsSettings; } /** * Gets the item custom settings if set. If not set, returns false. * * @param string $key * * @return ?array[] */ public function getItemCustomSettings(string $key): ?array { if (isset($this->itemsSettings[$key])) { return $this->itemsSettings[$key]; } return null; } /** * This method gets the legacy settings for all items to be loaded avoiding * lost the custom settings for items after the migration to v4.2.1. * * @return array */ protected function getLegacyItemsSettings(): array { if ($this->legacyItemsSettings === null) { $db = Factory::getDbo(); $query = $db->getQuery(true) ->select('*') ->from('#__osmap_items_settings') ->where('sitemap_id = ' . $db->quote($this->sitemap->id)) ->where($db->quoteName('format') . ' IS NULL'); $this->legacyItemsSettings = $db->setQuery($query)->loadAssocList('uid'); } return $this->legacyItemsSettings; } /** * Returns the settings based on the UID only. Used when we have legacy * settings on the database. * * @param string $uid * * @return ?array[] */ protected function getLegacyItemCustomSettings(string $uid): ?array { if (isset($this->legacyItemsSettings[$uid])) { return $this->legacyItemsSettings[$uid]; } return null; } /** * Sets the item's custom settings if exists. If no custom settings are * found and is a menu item, use the menu's settings. If is s subitem * (from plugins), we consider it already set the respective settings. But * if there is a custom setting for the item, we use that overriding what * was set in the plugin. * * @param Item $item * * @return void */ public function setItemCustomSettings(Item $item) { // Check if the menu item has custom settings. If not, use the values from the menu // Check if there is a custom settings specific for this URL. Sometimes the same page has different URLs. // We can have different settings for items with the same UID, but different URLs $key = $item->uid . ':' . $item->settingsHash; $settings = $this->getItemCustomSettings($key); // Check if there is a custom settings for all links with that UID (happens right after a migration from // versions before 4.0.0 or before 4.2.1) if (empty($settings)) { $settings = $this->getLegacyItemCustomSettings($item->uid); // The Joomla plugin changed the UID // from joomla.archive => joomla.archive.[id] and joomla.featured => joomla.featured[id] // So we need to try getting the settings from the old UID if ($settings === null) { if (preg_match('/^joomla.(archive|featured)/', $item->uid, $matches)) { $settings = $this->getLegacyItemCustomSettings('joomla.' . $matches[1]); } } } if (empty($settings)) { // No custom settings, so let's use the menu's settings if ($item->isMenuItem) { $item->changefreq = $this->tmpItemDefaultSettings['changefreq']; $item->priority = $this->tmpItemDefaultSettings['priority']; } } else { // Apply the custom settings $item->changefreq = $settings['changefreq'] ?? 'weekly'; $item->priority = (float)$settings['priority'] ?? .5; $item->published = (bool)$settings['published'] ?? true; } } /** * Check if the parent is unpublished or ignored and makes sure to ignore any item on it's sublevel * * @param Item $item * * @return void */ protected function checkParentIsUnpublished(Item $item) { // Check if this item belongs to a sub-level which needs to be unpublished if ($this->unpublishLevel !== false && $item->level > $this->unpublishLevel) { $item->set('published', false); $item->addAdminNote('COM_OSMAP_ADMIN_NOTE_PARENT_UNPUBLISHED'); } // If the item is unpublished and the 'ignore' level is false, mark the level to ignore sub-items $displayable = $item->published && !$item->ignore && (!$item->duplicate || !$this->params->get('ignore_duplicated_uids', 1)); if (!$displayable && $this->unpublishLevel === false) { $this->unpublishLevel = $item->level; } // If the item won't be ignored, make sure to reset the 'ignore' level if ( $item->published && !$item->ignore && (!$item->duplicate || !$this->params->get('ignore_duplicated_uids', 1)) ) { $this->unpublishLevel = false; } } /** * Returns the current menu item id * * @return int */ public function getCurrentMenuItemId(): int { return (int)$this->currentMenuItemId; } /** * Removes circular reference */ public function cleanup() { $this->sitemap = null; $this->printNodeCallback = null; $this->params = null; $this->currentMenu = null; } }
| ver. 1.4 |
Github
|
.
| PHP 8.3.23 | Generation time: 0.02 |
proxy
|
phpinfo
|
Settings