Your IP : 10.10.0.253


Current Path : /var/www/components/com_jlsitemap/models/
Upload File :
Current File : /var/www/components/com_jlsitemap/models/sitemap.php

<?php
/**
 * @package    JLSitemap Component
 * @version    1.12.0
 * @author     Joomline - joomline.ru
 * @copyright  Copyright (c) 2010 - 2022 Joomline. All rights reserved.
 * @license    GNU/GPL license: http://www.gnu.org/copyleft/gpl.html
 * @link       https://joomline.ru/
 */

defined('_JEXEC') or die;

use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\Filesystem\File;
use Joomla\CMS\Filesystem\Folder;
use Joomla\CMS\Filesystem\Path;
use Joomla\CMS\Language\Multilanguage;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Layout\LayoutHelper;
use Joomla\CMS\MVC\Model\BaseDatabaseModel;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Uri\Uri;
use Joomla\Registry\Registry;
use Joomla\Utilities\ArrayHelper;

class JLSitemapModelSitemap extends BaseDatabaseModel
{
	/**
	 * JLSitemap component configuration.
	 *
	 * @var  Registry
	 *
	 * @since  1.9.0
	 */
	protected $_configuration = null;

	/**
	 * Object with urls array.
	 *
	 * @var  object
	 *
	 * @since  1.1.0
	 */
	protected $_urls = null;

	/**
	 * Menu items array.
	 *
	 * @var  array
	 *
	 * @since  1.1.0
	 */
	protected $_menuItems = null;

	/**
	 * Sitemap stylesheet.
	 *
	 * @var  string
	 *
	 * @since  1.10.0
	 */
	protected $_xsl = null;

	/**
	 * Constructor.
	 *
	 * @param   array  $config  An array of configuration options (name, state, dbo, table_path, ignore_request).
	 *
	 * @throws  Exception
	 *
	 * @since   __DEPLOY_VERSION__
	 */
	public function __construct($config = array())
	{
		// Import plugins
		PluginHelper::importPlugin('jlsitemap');

		parent::__construct($config);
	}

	/**
	 * Method to generate sitemap.
	 *
	 * @param   bool  $debug  Debug generation.
	 *
	 * @throws  Exception
	 *
	 * @return  object|false Array if successful, false otherwise and internal error is set.
	 *
	 * @since  1.1.0
	 */
	public function generate($debug = false)
	{
		$app = Factory::getApplication();

		// Trigger before generate event
		$params = $this->getConfiguration();
		$app->triggerEvent('onBeforeGenerate', array($params));

		// Get urs
		$result = $this->getUrls();

		// Trigger after get urls event
		$app->triggerEvent('onAfterGetUrls', array(&$result, $params));

		// Generate sitemap file
		if (!$debug)
		{
			$result->files = array();
			$xmlLimit      = (int) $params->get('xml_limit', 50000);

			// Generate xml
			$xml = (count($result->includes) <= $xmlLimit) ? $this->generateSingleXML($result->includes)
				: $this->generateMultiXML($result->includes, $xmlLimit);
			if ($xml)
			{
				if (is_array($xml))
				{
					$result->files = array_merge($result->files, $xml);
					if ($sitemapindex = $this->generateXSL('sitemapindex'))
					{
						$result->files[] = Path::clean(JPATH_ROOT . '/' . $sitemapindex);
					}
					if ($urlset = $this->generateXSL('urlset'))
					{
						$result->files[] = Path::clean(JPATH_ROOT . '/' . $urlset);
					}
				}
				else
				{
					$result->files[] = $xml;
					if ($urlset = $this->generateXSL('urlset'))
					{
						$result->files[] = Path::clean(JPATH_ROOT . '/' . $urlset);
					}
				}
			}
			else
			{
				throw new Exception(Text::_('COM_JLSITEMAP_ERROR_SITEMAP_XML_CREATE_FAILED'), 500);
			}

			// Generate json
			if ($json = $this->generateJSON($result->includes))
			{
				$result->files[] = $json;
			}
			else
			{
				throw new Exception(Text::_('COM_JLSITEMAP_ERROR_SITEMAP_JSON_CREATE_FAILED'), 500);
			}

			// Trigger after generate event
			$app->triggerEvent('onAfterGenerate', array(&$result, $params));
		}

		return $result;
	}

	/**
	 * Method to generate single sitemap.
	 *
	 * @param   array  $rows  Include urls array
	 *
	 * @throws  Exception
	 *
	 * @return  string|false Sitemap file path on success, False on failure.
	 *
	 * @since  1.7
	 */
	public function generateSingleXML($rows = array())
	{
		$xml      = $this->filterRegexp($this->getXML($rows));
		$filename = $this->getConfiguration()->get('filename', 'sitemap');
		$file     = Path::clean(JPATH_ROOT . '/' . $filename . '.xml');

		if (File::exists($file))
		{
			File::delete($file);
		}

		return (File::append($file, $xml)) ? $file : false;
	}

	/**
	 * Method to generate multi sitemap.
	 *
	 * @param   array  $rows      Include urls array
	 * @param   int    $xmlLimit  Limit in xml file
	 *
	 * @throws  Exception
	 *
	 * @return  string[]|false Sitemap XML sting on success, Exception on failure.
	 *
	 * @since  1.7
	 */
	public function generateMultiXML($rows = array(), $xmlLimit = 50000)
	{
		// Clean old files
		$filename = $this->getConfiguration()->get('filename', 'sitemap');
		$files    = Folder::files(JPATH_ROOT, $filename . '_[0-9]*\.xml', false, true);
		foreach ($files as $file)
		{
			File::delete($file);
		}

		// Generate files
		$result   = array();
		$i        = 0;
		$t        = 0;
		$f        = 0;
		$total    = count($rows);
		$includes = array();
		foreach ($rows as $row)
		{
			$includes[] = $row;
			$i++;
			$t++;

			if ($i === $xmlLimit || $t === $total)
			{
				$f++;

				$xml  = $this->filterRegexp($this->getXML($includes));
				$file = Path::clean(JPATH_ROOT . '/' . $filename . '_' . $f . '.xml');
				if (File::append($file, $xml))
				{
					$result[] = $file;
				}
				else
				{
					return false;
				}

				// Reset
				$i        = 0;
				$includes = array();
			}
		}

		// Main sitemap
		$date       = Factory::getDate();
		$xsl        = $this->generateXSL('sitemapindex');
		$stylesheet = ($xsl) ? '<?xml-stylesheet type="text/xsl" href="' . Uri::root() . $xsl . '"?>' : '';
		$comment    = '<!-- JLSitemap ' . $date->toSql() . ' -->';
		$sitemap    = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?>'
			. $comment . $stylesheet
			. '<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" />');
		for ($i = 1; $i <= $f; $i++)
		{
			$child = $sitemap->addChild('sitemap');
			$child->addChild('loc', Uri::root() . $filename . '_' . $i . '.xml');
			$child->addChild('lastmod', $date->toISO8601());
		}
		$xml = $sitemap->asXML();

		// Filter regexp
		$xml = $this->filterRegexp($xml);

		// Put to file
		$file = Path::clean(JPATH_ROOT . '/' . $filename . '.xml');
		if (File::exists($file))
		{
			File::delete($file);
		}
		if (File::append($file, $xml))
		{
			$result[] = $file;
		}
		else
		{
			return false;
		}

		return $result;
	}

	/**
	 * Method to get sitemap xml string
	 *
	 * @param   array  $rows  Include urls array
	 *
	 * @throws  Exception
	 *
	 * @return  string Sitemap XML sting on success, Exception on failure.
	 *
	 * @since  1.1.0
	 */
	public function getXML($rows = array())
	{
		$rows       = (empty($rows)) ? $this->getUrls()->includes : $rows;
		$date       = Factory::getDate()->toSql();
		$comment    = '<!-- JLSitemap ' . $date . ' -->';
		$xsl        = $this->generateXSL('urlset');
		$stylesheet = ($xsl) ? '<?xml-stylesheet type="text/xsl" href="' . Uri::root() . $xsl . '"?>' : '';

		// Create sitemap
		$sitemap = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?>'
			. $comment . $stylesheet
			. '<urlset'
			. ' xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"'
			. ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"'
			. ' xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd http://www.w3.org/1999/xhtml http://www.w3.org/2002/08/xhtml/xhtml1-strict.xsd"'
			. ' xmlns:xhtml="http://www.w3.org/1999/xhtml"'
			. ' xhtml="http://www.w3.org/1999/xhtml"'
			. ' xmlns:image="http://www.google.com/schemas/sitemap-image/1.1"'
			. '/>');

		// Add urls
		foreach ($rows as $row)
		{
			if ($loc = $row->get('loc', false))
			{
				$url = $sitemap->addChild('url');

				// Loc
				$url->addChild('loc', $loc);

				// Changefreq
				if ($changefreq = $row->get('changefreq', false))
				{
					$url->addChild('changefreq', $changefreq);
				}

				// Priority
				if ($priority = $row->get('priority', false))
				{
					$url->addChild('priority', $row->get('priority'));
				}

				// Lastmod
				if ($lastmod = $row->get('lastmod', false))
				{
					$url->addChild('lastmod', Factory::getDate($lastmod)->toISO8601());
				}

				// Alternates
				if ($alternates = $row->get('alternates', false))
				{
					// Add x-default
					if (!isset($alternates['x-default']) && isset($alternates[Factory::getLanguage()->getDefault()]))
					{
						$alternates['x-default'] = $alternates[Factory::getLanguage()->getDefault()];
					}

					foreach ($alternates as $lang => $href)
					{
						$alternate = $url->addChild('xhtml:link', '', 'http://www.w3.org/1999/xhtml');
						$alternate->addAttribute('rel', 'alternate');
						$alternate->addAttribute('hreflang', $lang);
						$alternate->addAttribute('href', $href);
					}
				}

				// Images
				if ($images = $row->get('images', false))
				{
					foreach ($images as $href)
					{
						$image = $url->addChild('image', '', 'http://www.google.com/schemas/sitemap-image/1.1');
						$image->addChild('loc', $href, 'http://www.google.com/schemas/sitemap-image/1.1');
					}
				}
			}
		}
		$xml = $sitemap->asXML();

		return $xml;
	}

	/**
	 * Method to generate sitemap xsl style files.
	 *
	 * @param   string  $type  Layout type.
	 *
	 * @throws  Exception
	 *
	 * @return  string|false Style src on success, false on failure.
	 *
	 * @since  1.10.0
	 */
	public function generateXSL($type = null)
	{
		if (empty($type)) return false;

		if ($this->_xsl === null)
		{
			$this->_xsl = array();
		}

		if (!isset($this->_xsl[$type]))
		{
			$config = $this->getConfiguration();
			$src    = false;

			// Generate file
			if ($config->get('xsl', 1))
			{
				Factory::getLanguage()->load('com_jlsitemap', JPATH_SITE,
					ComponentHelper::getParams('com_languages')->get('site', 'en-GB'), true);
				$xsl = '<?xml version="1.0" encoding="UTF-8"?>'
					. PHP_EOL . LayoutHelper::render('components.jlsitemap.xsl.' . $type, array('date' => Factory::getDate()->toSql()));

				$filename = $config->get('filename', 'sitemap');
				$file     = $filename . '_' . $type . '.xsl';
				$path     = Path::clean(JPATH_ROOT . '/' . $file);

				if (File::exists($path))
				{
					File::delete($path);
				}

				$src = (File::append($file, $xsl)) ? $file : false;
			}

			$this->_xsl[$type] = $src;
		}

		return $this->_xsl[$type];
	}

	/**
	 * Method to create json sitemap.
	 *
	 * @param   array  $rows  Include urls array
	 *
	 * @throws  Exception
	 *
	 * @return  string|false Sitemap file path on success, False on failure.
	 *
	 * @since  1.6.0
	 */
	public
	function generateJSON($rows = array())
	{
		// Get json
		foreach ($rows as &$row)
		{
			$row = $row->toObject();
		}
		$registry = new Registry($rows);
		$json     = $registry->toString('json', array('bitmask' => JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));

		// Filter regexp
		$json = $this->filterRegexp($json);

		// Create sitemap json file
		$filename = $this->getConfiguration()->get('filename', 'sitemap');
		$file     = Path::clean(JPATH_ROOT . '/' . $filename . '.json');
		if (File::exists($file))
		{
			File::delete($file);
		}

		return (File::append($file, $json)) ? $file : false;
	}

	/**
	 * Method to get sitemap urls array
	 *
	 * @throws  Exception
	 *
	 * @return  object
	 *
	 * @since  1.1.0
	 */
	protected function getUrls()
	{
		if ($this->_urls === null)
		{
			$config           = $this->getConfiguration();
			$siteConfig       = Factory::getConfig();
			$siteSef          = ($siteConfig->get('sef') == 1);
			$siteRobots       = $siteConfig->get('robots');
			$siteRoot         = Uri::getInstance()->toString(array('scheme', 'host', 'port'));
			$guestAccess      = array_unique(Factory::getUser(0)->getAuthorisedViewLevels());
			$multilanguage    = Multilanguage::isEnabled();
			$defaultLanguage  = ComponentHelper::getParams('com_languages')->get('site', 'en-GB');
			$changefreqValues = array('always'  => 1, 'hourly' => 2, 'daily' => 3, 'weekly' => 4,
			                          'monthly' => 5, 'yearly' => 6, 'never' => 7);

			// Prepare menus filter
			$filterMenus = ($config->get('filter_menu')) ? $config->get('filter_menu_menus', array()) : false;

			// Prepare menu items filter;
			$filterMenuItems = (is_array($filterMenus)) ? array() : false;

			// Prepare menu home filter
			$filterMenuHomes = array();

			// Prepare raw filter
			$filterRaw = ($config->get('filter_raw_index') || $config->get('filter_raw_component')
				|| $config->get('filter_raw_get')) ? array() : false;
			if ($config->get('filter_raw_index'))
			{
				$filterRaw[] = 'index.php';
			}
			if ($config->get('filter_raw_component'))
			{
				$filterRaw[] = 'component/';
			}
			if ($config->get('filter_raw_get'))
			{
				$filterRaw[] = '?';
			}

			// Prepare strpos filter
			$filterStrpos = false;
			if (!empty(trim($config->get('filter_strpos'))))
			{
				$filterStrpos = preg_split('/\r\n|\r|\n/', $config->get('filter_strpos'));
				$filterStrpos = array_filter(array_map('trim', $filterStrpos), function ($string) {
					return !empty($string);
				});

				if (empty($filterStrpos))
				{
					$filterStrpos = false;
				}
			}

			// Create urls arrays
			$all      = array();
			$includes = array();
			$excludes = array();
			$menus    = array();

			// Add home page
			$type            = array(Text::_('COM_JLSITEMAP_TYPES_MENU'));
			$title           = $siteConfig->get('sitename');
			$link            = ($siteSef) ? '/' : '/index.php';
			$level           = count(explode('/', $link)) - 1;
			$key             = (empty($link)) ? '/' : $link;
			$loc             = $siteRoot . $link;
			$changefreq      = $config->get('changefreq', 'weekly');
			$changefreqValue = $changefreqValues[$changefreq];
			$priority        = $config->get('priority', '0.5');
			$exclude         = false;

			$url = new Registry();
			$url->set('type', $type);
			$url->set('title', $title);
			$url->set('link', $link);
			$url->set('level', ($level > 0) ? $level : 1);
			$url->set('loc', $loc);
			$url->set('changefreq', $changefreq);
			$url->set('changefreqValue', $changefreqValue);
			$url->set('priority', $priority);
			$url->set('exclude', $exclude);

			$all [$key]        = $url;
			$includes[$key]    = $url;
			$filterMenuHomes[] = $key;

			// Add menu items to urls arrays
			foreach ($this->getMenuItems($multilanguage, $filterMenus, $siteRobots, $guestAccess) as $item)
			{
				$type            = array($item->type);
				$link            = ($siteSef) ? Route::_($item->loc) : $item->loc;
				$level           = count(explode('/', $link)) - 1;
				$key             = (empty($link)) ? '/' : $link;
				$loc             = $siteRoot . $link;
				$changefreq      = $config->get('changefreq', 'weekly');
				$changefreqValue = $changefreqValues[$changefreq];
				$priority        = $config->get('priority', '0.5');

				// Prepare exclude
				$exclude = array();
				if (!$item->home)
				{
					$exclude = ($item->exclude) ? $item->exclude : array();
					$exclude = array_merge($exclude, $this->filtering($link, $filterRaw, $filterStrpos));

					foreach ($exclude as &$value)
					{
						$value = new Registry($value);
					}
				}

				// Prepare alternates
				$alternates = array();
				if (is_array($item->alternates))
				{
					foreach ($item->alternates as $lang => $href)
					{
						$href = ($siteSef) ? Route::_($href) : $href;
						if (empty($href))
						{
							$href = '/';
						}
						if (!isset($excludes[$href]) && empty($this->filtering($href, $filterRaw, $filterStrpos)))
						{
							$alternates[$lang] = rtrim(Uri::root(), '/') . $href;
						}
					}

					if (!empty($alternates) && !isset($alternates['x-default']) && isset($alternates[$defaultLanguage]))
					{
						$alternates['x-default'] = $alternates[$defaultLanguage];
					}
				}

				// Create url Registry
				$url = new Registry();
				$url->set('type', $type);
				$url->set('title', $item->title);
				$url->set('link', $link);
				$url->set('level', ($level > 0) ? $level : 1);
				$url->set('loc', $loc);
				$url->set('changefreq', $changefreq);
				$url->set('changefreqValue', $changefreqValue);
				$url->set('priority', $priority);
				$url->set('exclude', (!empty($exclude)) ? $exclude : false);
				$url->set('alternates', (!empty($alternates)) ? $alternates : false);

				// Add url to arrays
				$all[$key] = $url;
				if (!empty($exclude))
				{
					$excludes[$key] = $url;
				}
				else
				{
					$includes[$key] = $url;
				}
				$menus[$key] = $url;

				if ($item->home)
				{
					$filterMenuHomes[] = $key;
				}
				elseif (is_array($filterMenuItems) && empty($exclude))
				{
					$filterMenuItems[] = $key;
				}
			}

			// Prepare config
			$config->set('siteConfig', $siteConfig);
			$config->set('siteSef', $siteSef);
			$config->set('siteRobots', $siteRobots);
			$config->set('siteRoot', $siteRoot);
			$config->set('guestAccess', $guestAccess);
			$config->set('multilanguage', $multilanguage);
			$config->set('defaultLanguage', $defaultLanguage);
			$config->set('changefreqValues', $changefreqValues);
			$config->set('filterMenus', $filterMenus);
			$config->set('filterMenuItems', $filterMenuItems);
			$config->set('filterMenuHomes', $filterMenuHomes);
			$config->set('filterRaw', $filterRaw);
			$config->set('filterStrpos', $filterStrpos);

			// Add urls from jlsitemap plugins
			$rows = array();
			Factory::getApplication()->triggerEvent('onGetUrls', array(&$rows, &$config));
			foreach ($rows as $row)
			{
				$item = new Registry($row);
				if (!$loc = $item->get('loc', false)) continue;
				$type       = array($item->get('type', Text::_('COM_JLSITEMAP_TYPES_UNKNOWN')));
				$title      = $item->get('title');
				$link       = ($siteSef && !$item->get('noRoute', false)) ? Route::_($item->get('loc')) : $item->get('loc');
				$level      = count(explode('/', $link)) - 1;
				$key        = (empty($link)) ? '/' : $link;
				$loc        = $siteRoot . $link;
				$changefreq = $item->get('changefreq', $config->get('changefreq', 'weekly'));
				if (empty($changefreq))
				{
					$changefreq = $config->get('changefreq', 'weekly');
				}
				$changefreqValue = $changefreqValues[$changefreq];
				$priority        = $item->get('priority', $config->get('priority', '0.5'));
				if (empty($priority))
				{
					$priority = $config->get('priority', '0.5');
				}
				$lastmod = ($item->get('lastmod', false)
					&& $item->get('lastmod') != Factory::getDbo()->getNullDate()) ?
					Factory::getDate($item->get('lastmod'))->toUnix() : false;

				// Prepare title
				if (!empty($menus[$key]))
				{
					$title = $menus[$key]->get('title');
				}

				// Prepare exclude
				$exclude = array();
				if (!in_array($key, $filterMenuHomes))
				{
					// Legacy old plugins
					if (is_string($item->get('exclude')))
					{
						$exclude[] = array('type' => Text::_('COM_JLSITEMAP_EXCLUDE_UNKNOWN'),
						                   'msg'  => $item->get('exclude', ''));
					}
					elseif ($item->get('exclude'))
					{
						$exclude = $item->get('exclude');
					}

					$exclude = array_merge($exclude, $this->filtering($link, $filterRaw, $filterStrpos, $filterMenuItems));

					foreach ($exclude as &$value)
					{
						$value = new Registry($value);
					}
				}

				// Prepare alternates
				$alternates = array();
				if ($item->get('alternates', false))
				{
					foreach ($item->get('alternates') as $lang => $href)
					{
						$href = ($siteSef) ? Route::_($href) : $href;
						if (empty($href))
						{
							$href = '/';
						}
						if (!isset($excludes[$href]) && empty($this->filtering($href, $filterRaw, $filterStrpos)))
						{
							$alternates[$lang] = rtrim(Uri::root(), '/') . $href;
						}
					}

					if (!empty($alternates) && !isset($alternates['x-default']) && isset($alternates[$defaultLanguage]))
					{
						$alternates['x-default'] = $alternates[$defaultLanguage];
					}
				}

				// Prepare images
				$images = array();
				if ($item->get('images', false))
				{
					foreach ($item->get('images') as $href)
					{
						if (!empty($href))
						{
							$images[] = $href;
						}
					}
				}

				// Exist url override
				if (isset($all[$key]))
				{
					$exist = $all[$key];
					$type  = array_merge($exist->get('type'), $type);

					if (empty($title) && !empty($exist->get('title')))
					{
						$title = $exist->get('title');
					}

					if ($exist->get('changefreqValue') < $changefreqValue)
					{
						$changefreq      = $exist->get('changefreq');
						$changefreqValue = $exist->get('changefreqValue');
					}

					if ((float) $priority < (float) $exist->get('priority'))
					{
						$priority = $exist->get('priority');
					}

					if ($exist->get('lastmod', false))
					{
						if (!$lastmod)
						{
							$lastmod = $exist->get('lastmod');
						}
						elseif ($exist->get('lastmod') > $lastmod)
						{
							$lastmod = $exist->get('lastmod');
						}
					}

					if ($exist->get('exclude'))
					{
						$exclude = array_merge($exist->get('exclude'), $exclude);
					}

					if (is_array($exist->get('alternates')))
					{
						$alternates = $alternates + $exist->get('alternates');
					}
				}

				// Create url Registry
				$url = new Registry();
				$url->set('type', $type);
				$url->set('title', $title);
				$url->set('link', $link);
				$url->set('level', ($level > 0) ? $level : 1);
				$url->set('loc', $loc);
				$url->set('changefreq', $changefreq);
				$url->set('changefreqValue', $changefreqValue);
				$url->set('priority', $priority);
				$url->set('exclude', (!empty($exclude)) ? $exclude : false);
				$url->set('lastmod', $lastmod);
				$url->set('alternates', (!empty($alternates)) ? $alternates : false);
				$url->set('images', (!empty($images)) ? $images : false);

				// Add url to arrays
				$all[$key] = $url;
				if (!empty($exclude))
				{
					$excludes[$key] = $url;

					// Exclude item if already in array (last item has priority)
					unset($includes[$key]);
				}
				else
				{
					$includes[$key] = $url;
				}
			}

			// Sort urls arrays
			ksort($all);
			ksort($includes);
			ksort($excludes);

			// Prepare urls object
			$urls           = new stdClass();
			$urls->includes = $includes;
			$urls->excludes = $excludes;
			$urls->all      = $all;

			// Set urls object
			$this->_urls = $urls;
		}

		return $this->_urls;
	}

	/**
	 * Method to get menu items array
	 *
	 * @param   bool         $multilanguage  Enable multilanguage
	 * @param   array|false  $menutypes      Menutypes filter
	 * @param   string       $siteRobots     Site config robots
	 * @param   array        $guestAccess    Guest access levels
	 *
	 * @return  array
	 *
	 * @since  1.1.0
	 */
	protected function getMenuItems($multilanguage = false, $menutypes = false, $siteRobots = null, $guestAccess = array())
	{
		if ($this->_menuItems === null)
		{
			// Get menu items
			$db    = Factory::getDbo();
			$query = $db->getQuery(true)
				->select(array('m.id', 'm.menutype', 'm.title', 'm.link', 'm.type', 'm.published', 'm.access', 'm.home', 'm.params', 'm.language',
					'e.extension_id as component_exist', 'e.enabled as component_enabled', 'e.element as component'))
				->from($db->quoteName('#__menu', 'm'))
				->join('LEFT', '#__extensions AS e ON e.extension_id = m.component_id')
				->where('m.client_id = 0')
				->where('m.id > 1')
				->order($db->escape('m.lft') . ' ' . $db->escape('asc'));

			// Join over associations
			if ($multilanguage)
			{
				$query->select('assoc.key as association')
					->join('LEFT', '#__associations AS assoc ON assoc.id = m.id AND assoc.context = ' .
						$db->quote('com_menus.item'));
			}

			$db->setQuery($query);
			$rows = $db->loadObjectList('id');

			// Create menu items array
			$items         = array();
			$alternates    = array();
			$excludeTypes  = array('alias', 'separator', 'heading', 'url');
			$excludeStates = array(
				0  => Text::_('COM_JLSITEMAP_EXCLUDE_MENU_UNPUBLISH'),
				-2 => Text::_('COM_JLSITEMAP_EXCLUDE_MENU_TRASH'));
			foreach ($rows as $row)
			{
				$params    = new Registry($row->params);
				$home      = ($row->home && !isset($excludeStates[$row->published]));
				$component = $row->component;
				if ($row->type == 'component' && empty($row->component))
				{
					preg_match("/^index.php\?option=([a-zA-Z\-0-9_]*)/", $row->link, $matches);
					$component = (!empty($matches[1])) ? $matches[1] : 'unknown';
				}

				// Prepare title attribute
				$title = ($params->get('page_title', false)) ? $params->get('page_title') : $row->title;

				// Prepare loc attribute
				$loc = 'index.php?Itemid=' . $row->id;
				if (!empty($row->language) && $row->language !== '*' && $multilanguage)
				{
					$loc .= '&lang=' . $row->language;
				}

				// Prepare exclude attribute
				$exclude = array();
				if (!$home)
				{
					if ($menutypes && !empty($menutypes) && !in_array($row->menutype, $menutypes))
					{
						$exclude[] = array('type' => Text::_('COM_JLSITEMAP_EXCLUDE_MENU'),
						                   'msg'  => Text::_('COM_JLSITEMAP_EXCLUDE_MENU_MENUTYPES'));

					}

					if (preg_match('/noindex/', $params->get('robots', $siteRobots)))
					{
						$exclude[] = array('type' => Text::_('COM_JLSITEMAP_EXCLUDE_MENU'),
						                   'msg'  => Text::_('COM_JLSITEMAP_EXCLUDE_MENU_ROBOTS'));
					}

					if (isset($excludeStates[$row->published]))
					{
						$exclude[] = array('type' => Text::_('COM_JLSITEMAP_EXCLUDE_MENU'),
						                   'msg'  => $excludeStates[$row->published]);
					}

					if (in_array($row->type, $excludeTypes))
					{
						$exclude[] = array('type' => Text::_('COM_JLSITEMAP_EXCLUDE_MENU'),
						                   'msg'  => Text::sprintf('COM_JLSITEMAP_EXCLUDE_MENU_SYSTEM_TYPE', $row->type));
					}

					if ($row->type == 'component' && empty($row->component_exist))
					{
						$exclude[] = array('type' => Text::_('COM_JLSITEMAP_EXCLUDE_MENU'),
						                   'msg'  => Text::sprintf('COM_JLSITEMAP_EXCLUDE_MENU_COMPONENT_EXIST',
							                   $component));
					}
					elseif ($row->type == 'component' && empty($row->component_enabled))
					{
						$exclude[] = array('type' => Text::_('COM_JLSITEMAP_EXCLUDE_MENU'),
						                   'msg'  => Text::sprintf('COM_JLSITEMAP_EXCLUDE_MENU_COMPONENT_ENABLED',
							                   $component));
					}

					if (!in_array($row->access, $guestAccess))
					{
						$exclude[] = array('type' => Text::_('COM_JLSITEMAP_EXCLUDE_MENU'),
						                   'msg'  => Text::_('COM_JLSITEMAP_EXCLUDE_MENU_ACCESS'));
					}
				}

				// Prepare menu item object
				$item             = new stdClass();
				$item->loc        = $loc;
				$item->type       = Text::_('COM_JLSITEMAP_TYPES_MENU');
				$item->title      = $title;
				$item->home       = $home;
				$item->exclude    = (!empty($exclude)) ? $exclude : false;
				$item->alternates = ($multilanguage && !empty($row->association)) ? $row->association : false;

				// Add menu item to array
				$items[] = $item;

				// Add menu items to alternates array
				if ($multilanguage && !empty($row->association) && empty($exclude))
				{
					if (!isset($alternates[$row->association]))
					{
						$alternates[$row->association] = array();
					}

					$alternates[$row->association][$row->language] = $loc;
				};
			}

			// Add alternates to menu items
			if (!empty($alternates))
			{
				foreach ($items as &$item)
				{
					$item->alternates = ($item->alternates && !empty($alternates[$item->alternates])) ?
						$alternates[$item->alternates] : false;
				}
			}

			$this->_menuItems = $items;
		}

		return $this->_menuItems;
	}

	/**
	 * Method to filtering urls
	 *
	 * @param   string      $link    Url
	 * @param   array|bool  $raw     Raw filter
	 * @param   array|bool  $strpos  Strpos filter
	 * @param   array|bool  $menu    Menu items filter
	 *
	 * @return  array Excludes array
	 *
	 * @since  1.3.0
	 */
	public function filtering($link = null, $raw = false, $strpos = false, $menu = false)
	{
		$exclude = array();

		// Check empty url
		if (empty($link))
		{
			$exclude['filter_null'] = array('type' => Text::_('COM_JLSITEMAP_EXCLUDE_FILTER'),
			                                'msg'  => Text::_('COM_JLSITEMAP_EXCLUDE_FILTER_NULL'));

			return $exclude;
		}

		// Filter by raw
		if ($raw && is_array($raw))
		{
			foreach ($raw as $filter)
			{
				if (mb_stripos($link, $filter, 0, 'UTF-8') !== false)
				{
					$exclude['filter_raw_' . $filter] = array('type' => Text::_('COM_JLSITEMAP_EXCLUDE_FILTER'),
					                                          'msg'  => Text::sprintf('COM_JLSITEMAP_EXCLUDE_FILTER_RAW', $filter));
					break;
				}
			}
		}

		// Filter by strpos
		if ($strpos && is_array($strpos))
		{
			foreach ($strpos as $filter)
			{
				if (mb_stripos($link, $filter, 0, 'UTF-8') !== false)
				{
					$exclude['filter_strpos_' . $filter] = array('type' => Text::_('COM_JLSITEMAP_EXCLUDE_FILTER'),
					                                             'msg'  => Text::sprintf('COM_JLSITEMAP_EXCLUDE_FILTER_STRPOS', $filter));
					break;
				}
			}
		}

		// Filter by menu
		if (is_array($menu))
		{
			$excludeMenu = true;
			foreach ($menu as $filter)
			{
				$filter   = str_replace(array('.html'), '', $filter);
				$filter   = str_replace(array('/'), '\/', $filter);
				$patterns = array(
					'/^' . $filter . '\//',
					'/^' . $filter . '$/',
					'/^' . $filter . '\.html/',
				);
				foreach ($patterns as $pattern)
				{
					if (preg_match($pattern, $link))
					{
						$excludeMenu = false;
						break;
					}
				}
				if (!$excludeMenu)
				{
					break;
				}
			}

			if ($excludeMenu)
			{
				$exclude['filter_menu'] = array(
					'type' => Text::_('COM_JLSITEMAP_EXCLUDE_FILTER'),
					'msg'  => Text::_('COM_JLSITEMAP_EXCLUDE_FILTER_MENU'));
			}
		}

		return $exclude;
	}

	/**
	 * Method to filtering sitemap string via regexp
	 *
	 * @param   string  $string  Sitemap string
	 *
	 * @throws  Exception
	 *
	 * @return  string Sitemap string.
	 *
	 * @since  1.7
	 */
	protected function filterRegexp($string = '')
	{
		if (empty($string)) return $string;

		// Regexp filter
		$filterRegexp = $this->getConfiguration()->get('filter_regexp');
		if (!empty($filterRegexp))
		{
			foreach (ArrayHelper::fromObject($filterRegexp) as $regexp)
			{
				if (!empty($regexp['pattern']))
				{
					$string = preg_replace($regexp['pattern'], $regexp['replacement'], $string);
				}
			}
		}

		return $string;
	}

	/**
	 * Method to delete sitemap
	 *
	 * @throws  Exception
	 *
	 * @return  bool True on success, False on failure.
	 *
	 * @since  1.4.1
	 */
	public function delete()
	{
		// Delete single sitemap
		$filename = $this->getConfiguration()->get('filename', 'sitemap');
		$file     = Path::clean(JPATH_ROOT . '/' . $filename . '.xml');
		if (File::exists($file) && !File::delete($file)) return false;

		// Delete multi sitemap
		$files = Folder::files(JPATH_ROOT, $filename . '_[0-9]*\.xml');
		foreach ($files as $file)
		{
			if (!File::delete($file)) return false;
		}

		// Delete xsl stylesheets
		$files = array($filename.'_sitemapindex.xsl', $filename.'_urlset.xsl');
		foreach ($files as $file)
		{
			$path = Path::clean(JPATH_ROOT.'/'.$file);
			if (File::exists($path) && !File::delete($path)) return false;
		}

		// Delete json sitemap
		$file = Path::clean(JPATH_ROOT . '/' . $filename . '.json');
		if (File::exists($file) && !File::delete($file)) return false;

		return true;
	}

	/**
	 * Method to get component configuration.
	 *
	 * @throws  Exception
	 *
	 * @return  Registry JLSitemap component configuration.
	 *
	 * @since  1.9.0
	 */
	public function getConfiguration()
	{
		if ($this->_configuration === null)
		{
			$configuration = ComponentHelper::getParams('com_jlsitemap');

			Factory::getApplication()->triggerEvent('onGetConfiguration', array(&$configuration));

			$this->_configuration = $configuration;
		}

		return $this->_configuration;
	}

	/**
	 * Method to set component configuration parameter.
	 *
	 * @param   string  $path       Registry Path (e.g. joomla.content.showauthor)
	 * @param   mixed   $value      Value of entry
	 * @param   string  $separator  The key separator
	 *
	 * @throws  Exception
	 *
	 * @since  1.9.0
	 */
	public function setConfigurationParameter($path, $value, $separator = null)
	{
		if ($this->_configuration === null)
		{
			$this->getConfiguration();
		}

		$this->_configuration->set($path, $value, $separator);
	}
}