first commit
This commit is contained in:
@@ -0,0 +1,794 @@
|
||||
<?php
|
||||
/**
|
||||
* @package OSMap
|
||||
* @contact www.joomlashack.com, help@joomlashack.com
|
||||
* @copyright 2007-2014 XMap - Joomla! Vargas - Guillermo Vargas. All rights reserved.
|
||||
* @copyright 2016-2024 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/**
|
||||
* @package OSMap
|
||||
* @contact www.joomlashack.com, help@joomlashack.com
|
||||
* @copyright 2007-2014 XMap - Joomla! Vargas - Guillermo Vargas. All rights reserved.
|
||||
* @copyright 2016-2024 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;
|
||||
|
||||
defined('_JEXEC') or die();
|
||||
|
||||
class Images extends Standard
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'images';
|
||||
}
|
||||
@@ -0,0 +1,609 @@
|
||||
<?php
|
||||
/**
|
||||
* @package OSMap
|
||||
* @contact www.joomlashack.com, help@joomlashack.com
|
||||
* @copyright 2007-2014 XMap - Joomla! Vargas - Guillermo Vargas. All rights reserved.
|
||||
* @copyright 2016-2024 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\Date\Date;
|
||||
use Joomla\CMS\Language\Multilanguage;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\Object\CMSObject;
|
||||
use Joomla\Registry\Registry;
|
||||
|
||||
defined('_JEXEC') or die();
|
||||
|
||||
/**
|
||||
* Sitemap item
|
||||
*/
|
||||
class Item extends CMSObject
|
||||
{
|
||||
/**
|
||||
* @var int;
|
||||
*/
|
||||
public $id = null;
|
||||
|
||||
/**
|
||||
* @var string;
|
||||
*/
|
||||
public $uid = null;
|
||||
|
||||
/**
|
||||
* Item's link, which can be relative or un-routed
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $link = null;
|
||||
|
||||
/**
|
||||
* Routed full link, sanitized and without any hash segment
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $fullLink = null;
|
||||
|
||||
/**
|
||||
* Routed full link, sanitized but can contain a hash segment
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $rawLink = null;
|
||||
|
||||
/**
|
||||
* @var Registry
|
||||
*/
|
||||
public $params = null;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $priority = null;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $changefreq = null;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $created = null;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $modified = null;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $publishUp = null;
|
||||
|
||||
/**
|
||||
* The component associated to the option URL param
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $component = null;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $ignore = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $duplicate = false;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $browserNav = null;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $isInternal = true;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $home = false;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $type = null;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $expandible = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $secure = false;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $isMenuItem = 0;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $published = 1;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $name = '';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $images = [];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $settingsHash = null;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $level = null;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $adapterName = 'Generic';
|
||||
|
||||
/**
|
||||
* @var object
|
||||
*/
|
||||
public $adapter = null;
|
||||
|
||||
/**
|
||||
* If true, says the item is visible for robots
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $visibleForRobots = true;
|
||||
|
||||
/**
|
||||
* If true, says the item's parent is visible for robots
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $parentIsVisibleForRobots = true;
|
||||
|
||||
/**
|
||||
* Stores a list of notes generated by the collector to display in the admin
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public $adminNotes = null;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $visibleForXML = true;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $visibleForHTML = true;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $menuItemId = 0;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $menuItemName = null;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $menuItemType = null;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $subnodes = null;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $slug = null;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function __construct($itemData, $currentMenuItemId)
|
||||
{
|
||||
parent::__construct($itemData);
|
||||
|
||||
if (class_exists('\\Alledia\\OSMap\\Sitemap\\ItemAdapter\\GenericPro')) {
|
||||
$this->adapterName = 'GenericPro';
|
||||
}
|
||||
|
||||
$this->published = (bool)$this->published;
|
||||
$this->isMenuItem = (bool)$this->isMenuItem;
|
||||
$this->params = new Registry($this->params);
|
||||
|
||||
// Check if the link is an internal link
|
||||
$this->isInternal = $this->checkLinkIsInternal();
|
||||
|
||||
$this->prepareDate('created');
|
||||
$this->prepareDate('modified');
|
||||
|
||||
$defaultDate = is_null($this->created) ? $this->modified : $this->created;
|
||||
$this->prepareDate('publishUp', $defaultDate);
|
||||
|
||||
$this->setLink();
|
||||
$this->extractComponentFromLink();
|
||||
$this->setFullLink();
|
||||
|
||||
// Sanitize internal links
|
||||
if ($this->isInternal) {
|
||||
$this->sanitizeFullLink();
|
||||
}
|
||||
|
||||
$this->rawLink = $this->fullLink;
|
||||
|
||||
// Removes the hash segment from the Full link, if exists
|
||||
$container = Factory::getPimpleContainer();
|
||||
$this->fullLink = $container->router->removeHashFromURL($this->fullLink);
|
||||
|
||||
// Make sure to have a unique hash for the settings
|
||||
$this->settingsHash = $container->router->createUrlHash($this->fullLink . $currentMenuItemId);
|
||||
|
||||
/*
|
||||
* Do not use a "prepare" method because we need to make sure it will
|
||||
* be calculated after the link is set.
|
||||
*/
|
||||
$this->calculateUID();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the note to the admin note attribute and initialize the variable
|
||||
* if needed
|
||||
*
|
||||
* @param string $note
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addAdminNote(string $note)
|
||||
{
|
||||
if (!is_array($this->adminNotes)) {
|
||||
$this->adminNotes = [];
|
||||
}
|
||||
|
||||
$this->adminNotes[] = Text::_($note);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected function checkLinkIsInternal(): bool
|
||||
{
|
||||
$container = Factory::getPimpleContainer();
|
||||
|
||||
return $container->router->isInternalURL($this->link)
|
||||
|| in_array(
|
||||
$this->type,
|
||||
[
|
||||
'separator',
|
||||
'heading',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the correct date for the attribute
|
||||
*
|
||||
* @param string $attributeName
|
||||
* @param ?string $default
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function prepareDate(string $attributeName, ?string $default = null)
|
||||
{
|
||||
if ($default !== null && empty($this->{$attributeName})) {
|
||||
$this->{$attributeName} = $default;
|
||||
}
|
||||
|
||||
if (General::isEmptyDate($this->{$attributeName})) {
|
||||
$this->{$attributeName} = null;
|
||||
}
|
||||
|
||||
if (!General::isEmptyDate($this->{$attributeName})) {
|
||||
if (!is_numeric($this->{$attributeName})) {
|
||||
$date = new Date($this->{$attributeName});
|
||||
$this->{$attributeName} = $date->toUnix();
|
||||
}
|
||||
|
||||
// Convert dates from UTC
|
||||
if (intval($this->{$attributeName})) {
|
||||
if ($this->{$attributeName} < 0) {
|
||||
$this->{$attributeName} = null;
|
||||
} else {
|
||||
$date = new Date($this->{$attributeName});
|
||||
$this->{$attributeName} = $date->toISO8601();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function hasCompatibleLanguage(): bool
|
||||
{
|
||||
// Check the language
|
||||
if (Multilanguage::isEnabled() && isset($this->language)) {
|
||||
if ($this->language === '*' || $this->language === Factory::getLanguage()->getTag()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate a hash based on the link, to avoid duplicated links. It will
|
||||
* set the new UID to the item.
|
||||
*
|
||||
* @param bool $force
|
||||
* @param string $prefix
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function calculateUID(bool $force = false, string $prefix = '')
|
||||
{
|
||||
if (empty($this->uid) || $force) {
|
||||
$this->uid = $prefix . md5($this->fullLink);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the link in special cases, like alias, where the link doesn't have
|
||||
* the correct related menu id.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setLink()
|
||||
{
|
||||
// If is an alias, use the Itemid stored in the parameters to get the correct url
|
||||
if ($this->type === 'alias') {
|
||||
// Get the related menu item's link
|
||||
$db = Factory::getDbo();
|
||||
|
||||
$query = $db->getQuery(true)
|
||||
->select('link')
|
||||
->from('#__menu')
|
||||
->where('id = ' . $db->quote($this->params->get('aliasoptions')));
|
||||
|
||||
$this->link = $db->setQuery($query)->loadResult();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize the link removing double slashes and trailing slash
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function sanitizeFullLink()
|
||||
{
|
||||
$container = Factory::getPimpleContainer();
|
||||
|
||||
$this->fullLink = $container->router->sanitizeURL($this->fullLink);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the current link to a full URL, including the base URI.
|
||||
* If the item is the home menu, will return the base URI. If internal,
|
||||
* will return a routed full URL (SEF, if enabled). If it is an external
|
||||
* URL, won't change the link.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setFullLink()
|
||||
{
|
||||
$container = Factory::getPimpleContainer();
|
||||
|
||||
if ($this->home) {
|
||||
// Correct the URL for the home page.
|
||||
// Check if multi-language is enabled to use the proper route
|
||||
if (Multilanguage::isEnabled()) {
|
||||
$lang = Factory::getLanguage();
|
||||
$tag = $lang->getTag();
|
||||
$lang = null;
|
||||
|
||||
$homes = Multilanguage::getSiteHomePages();
|
||||
|
||||
$home = $homes[$tag] ?? $homes['*'];
|
||||
|
||||
$this->fullLink = $container->router->routeURL('index.php?Itemid=' . $home->id, true);
|
||||
|
||||
} else {
|
||||
$this->fullLink = $container->uri::root();
|
||||
}
|
||||
|
||||
$this->fullLink = $container->router->sanitizeURL($this->fullLink);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->type === 'separator' || $this->type === 'heading') {
|
||||
// Not a url so disable browser nav
|
||||
$this->browserNav = 3;
|
||||
$this->uid = $this->type . '.' . md5($this->name . $this->id);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->type === 'url') {
|
||||
$this->link = trim($this->link);
|
||||
// Check if it is a single Hash char, the user doesn't want to point to any URL
|
||||
if ($this->link === '#' || empty($this->link)) {
|
||||
$this->fullLink = '';
|
||||
$this->visibleForXML = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if it is a relative URI
|
||||
if ($container->router->isRelativeUri($this->link)) {
|
||||
$this->fullLink = $container->router->sanitizeURL(
|
||||
$container->router->convertRelativeUriToFullUri($this->link)
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->fullLink = $this->link;
|
||||
|
||||
if (!$this->isInternal) {
|
||||
// External URLS have UID as a hash as part of its url
|
||||
$this->calculateUID(true, 'external.');
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->type === 'alias') {
|
||||
// Use the destination itemid, instead of the alias' item id.
|
||||
// This will make sure we have the correct routed url.
|
||||
$this->fullLink = 'index.php?Itemid=' . $this->params->get('aliasoptions');
|
||||
}
|
||||
|
||||
// If is a menu item but not an alias, force to use the current menu's item id
|
||||
if ($this->isMenuItem && $this->type !== 'alias' && $this->type !== 'url') {
|
||||
$this->fullLink = 'index.php?Itemid=' . $this->id;
|
||||
}
|
||||
|
||||
// If is not a menu item, use as base for the fullLink, the item link
|
||||
if (!$this->isMenuItem) {
|
||||
$this->fullLink = $this->link;
|
||||
}
|
||||
|
||||
if ($this->isInternal) {
|
||||
$this->fullLink = $container->router->routeURL($this->fullLink, true);
|
||||
}
|
||||
|
||||
$this->fullLink = $container->router->sanitizeURL($this->fullLink);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the item adapter according to the type of content. The adapter can
|
||||
* extract custom params from the item like params and metadata.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setAdapter()
|
||||
{
|
||||
// Check if there is class for the option
|
||||
$adapterClass = '\\Alledia\\OSMap\\Sitemap\\ItemAdapter\\' . $this->adapterName;
|
||||
if (class_exists($adapterClass)) {
|
||||
$this->adapter = new $adapterClass($this);
|
||||
} else {
|
||||
$this->adapter = new ItemAdapter\Generic($this);
|
||||
}
|
||||
}
|
||||
|
||||
public function cleanup()
|
||||
{
|
||||
$this->adapter->cleanup();
|
||||
|
||||
$this->adapter = null;
|
||||
$this->params = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the option from the link, to identify the component called by
|
||||
* the link.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function extractComponentFromLink()
|
||||
{
|
||||
$this->component = null;
|
||||
|
||||
if (preg_match('#^/?index.php.*option=(com_[^&]+)#', $this->link, $matches)) {
|
||||
$this->component = $matches[1];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the admin notes as a string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAdminNotesString(): string
|
||||
{
|
||||
if (is_array($this->adminNotes)) {
|
||||
return implode("\n", $this->adminNotes);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the correct modification date.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setModificationDate()
|
||||
{
|
||||
if (General::isEmptyDate($this->modified)) {
|
||||
$this->modified = null;
|
||||
}
|
||||
|
||||
if (!General::isEmptyDate($this->modified)) {
|
||||
if (!is_numeric($this->modified)) {
|
||||
$date = new Date($this->modified);
|
||||
$this->modified = $date->toUnix();
|
||||
}
|
||||
|
||||
// Convert dates from UTC
|
||||
if (intval($this->modified)) {
|
||||
if ($this->modified < 0) {
|
||||
$this->modified = null;
|
||||
} else {
|
||||
$date = new \JDate($this->modified);
|
||||
$this->modified = $date->toISO8601();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/**
|
||||
* @package OSMap
|
||||
* @contact www.joomlashack.com, help@joomlashack.com
|
||||
* @copyright 2007-2014 XMap - Joomla! Vargas - Guillermo Vargas. All rights reserved.
|
||||
* @copyright 2016-2024 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\ItemAdapter;
|
||||
|
||||
defined('_JEXEC') or die();
|
||||
|
||||
interface AdapterInterface
|
||||
{
|
||||
/**
|
||||
* Gets the visible state for robots. Each adapter will check specific params. Returns
|
||||
* true if the item is visible.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function checkVisibilityForRobots();
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/**
|
||||
* @package OSMap
|
||||
* @contact www.joomlashack.com, help@joomlashack.com
|
||||
* @copyright 2007-2014 XMap - Joomla! Vargas - Guillermo Vargas. All rights reserved.
|
||||
* @copyright 2016-2024 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\ItemAdapter;
|
||||
|
||||
use Alledia\OSMap\Sitemap\Item;
|
||||
|
||||
defined('_JEXEC') or die();
|
||||
|
||||
class Generic implements AdapterInterface
|
||||
{
|
||||
/**
|
||||
* @var Item
|
||||
*/
|
||||
protected $item;
|
||||
|
||||
/**
|
||||
* @param Item $item
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Item $item)
|
||||
{
|
||||
$this->item = $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function checkVisibilityForRobots()
|
||||
{
|
||||
// Always visible for the Free version
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/**
|
||||
* @package OSMap
|
||||
* @contact www.joomlashack.com, help@joomlashack.com
|
||||
* @copyright 2007-2014 XMap - Joomla! Vargas - Guillermo Vargas. All rights reserved.
|
||||
* @copyright 2016-2024 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;
|
||||
|
||||
defined('_JEXEC') or die();
|
||||
|
||||
class News extends Standard
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'news';
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
/**
|
||||
* @package OSMap
|
||||
* @contact www.joomlashack.com, help@joomlashack.com
|
||||
* @copyright 2007-2014 XMap - Joomla! Vargas - Guillermo Vargas. All rights reserved.
|
||||
* @copyright 2016-2024 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;
|
||||
|
||||
defined('_JEXEC') or die();
|
||||
|
||||
interface SitemapInterface
|
||||
{
|
||||
/**
|
||||
* Traverse the sitemap items recursively and call the given callback,
|
||||
* passing each node as parameter.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @param bool $triggerEvents
|
||||
* @param bool $updateCount
|
||||
*
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function traverse(callable $callback, bool $triggerEvents = true, bool $updateCount = false);
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
<?php
|
||||
/**
|
||||
* @package OSMap
|
||||
* @contact www.joomlashack.com, help@joomlashack.com
|
||||
* @copyright 2007-2014 XMap - Joomla! Vargas - Guillermo Vargas. All rights reserved.
|
||||
* @copyright 2016-2024 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 Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\Plugin\PluginHelper;
|
||||
use Joomla\Registry\Registry;
|
||||
|
||||
defined('_JEXEC') or die();
|
||||
|
||||
class Standard implements SitemapInterface
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* @var Registry
|
||||
*/
|
||||
public $params;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $isDefault = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $isPublished = true;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $createdOn;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $linksCount = 0;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'standard';
|
||||
|
||||
/**
|
||||
* @var Collector
|
||||
*/
|
||||
protected $collector;
|
||||
|
||||
/**
|
||||
* Limit in days for news sitemap items
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $newsDateLimit = 2;
|
||||
|
||||
/**
|
||||
* The constructor
|
||||
*
|
||||
* @param int $id
|
||||
*
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct(int $id)
|
||||
{
|
||||
/** @var \OSMapTableSitemap $sitemap */
|
||||
$sitemap = Factory::getTable('Sitemap');
|
||||
$sitemap->load($id);
|
||||
|
||||
if (empty($sitemap) || !$sitemap->id) {
|
||||
throw new \Exception(Text::_('COM_OSMAP_SITEMAP_NOT_FOUND'), 404);
|
||||
}
|
||||
|
||||
$this->id = $sitemap->id;
|
||||
$this->name = $sitemap->name;
|
||||
$this->isDefault = $sitemap->is_default == 1;
|
||||
$this->isPublished = $sitemap->published == 1;
|
||||
$this->createdOn = $sitemap->created_on;
|
||||
$this->linksCount = (int)$sitemap->links_count;
|
||||
$this->params = new Registry($sitemap->params);
|
||||
|
||||
$this->initCollector();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to initialize the items collector
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function initCollector()
|
||||
{
|
||||
$this->collector = new Collector($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function traverse(callable $callback, $triggerEvents = true, $updateCount = false)
|
||||
{
|
||||
if ($triggerEvents) {
|
||||
// Call the plugins, allowing to interact or override the collector
|
||||
PluginHelper::importPlugin('osmap');
|
||||
|
||||
$eventParams = [$this, $callback];
|
||||
$results = Factory::getApplication()->triggerEvent('osmapOnBeforeCollectItems', $eventParams);
|
||||
|
||||
// A plugin asked to stop the traverse
|
||||
if (in_array(true, $results)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$results = null;
|
||||
}
|
||||
|
||||
// Fetch the sitemap items
|
||||
$count = $this->collector->fetch($callback);
|
||||
|
||||
if ($updateCount) {
|
||||
// Update the links count in the sitemap
|
||||
$this->updateLinksCount($count);
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
$this->collector->cleanup();
|
||||
$this->collector = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the count of links in the database
|
||||
*
|
||||
* @param int $count
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function updateLinksCount(int $count)
|
||||
{
|
||||
$db = Factory::getDbo();
|
||||
|
||||
$updateObject = (object)[
|
||||
'id' => $this->id,
|
||||
'links_count' => $count
|
||||
];
|
||||
|
||||
$db->updateObject('#__osmap_sitemaps', $updateObject, ['id']);
|
||||
}
|
||||
|
||||
public function cleanup()
|
||||
{
|
||||
$this->collector = null;
|
||||
$this->params = null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user