<?php
/**
 * @author Joomla! Extensions Store
 * @package JMAP::plugins::system
 * @copyright (C) 2015 - Joomla! Extensions Store
 * @license GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html
 */
defined ( '_JEXEC' ) or die ( 'Restricted access' );
jimport ( 'joomla.plugin.plugin' );

/**
 * Observer class notified on events
 *
 * @author Joomla! Extensions Store
 * @package JMAP::plugins::system
 * @since 2.1
 */
class plgSystemJMap extends JPlugin {
	/**
	 * Joomla config object
	 *
	 * @access private
	 * @var Object
	 */
	private $joomlaConfig;
	
	/**
	 * JSitemap config object
	 *
	 * @access private
	 * @var Object
	 */
	private $jmapConfig;
	
	/**
	 * JMap calculate URI
	 *
	 * @access private
	 * @var String
	 */
	private $jmapUri;
	
	/**
	 * Render module triggering
	 *
	 * @access private
	 * @var bool
	 */
	private static $renderModuleTrigger;
	
	/**
	 * Process content plugins
	 * 
	 * @access private
	 * @param string $custom404Text
	 * @param Object &$cParams
	 * @return string
	 */
	private function processContentPlugins($custom404Text, &$cParams) {
		// Process only if html mode is enabled and process plugins param is enabled
		if($cParams->get('custom_404_page_mode', 'html') == 'html' && $cParams->get('custom_404_process_content_plugins', 0)) {
			JPluginHelper::importPlugin('content');
			
			$dispatcher = JEventDispatcher::getInstance();
			
			$dummyParams = new JRegistry();
			$elm = new stdClass();
			$elm->text = $custom404Text;
			self::$renderModuleTrigger = true;
			
			$dispatcher->trigger('onContentPrepare', array ('com_content.article', &$elm, &$dummyParams, 0));
			$custom404Text = $elm->text;
		}
		
		// Always return input text, processed or not
		return $custom404Text;
	}
	
	/**
	 * Detect mobile requests
	 *
	 * @access private
	 * @return boolean
	 */
	private function isBotRequest() {
		$crawlers = array (
				'Google' => 'Google',
				'MSN' => 'msnbot',
				'Rambler' => 'Rambler',
				'Yahoo' => 'Yahoo',
				'Yandex' => 'Yandex',
				'AbachoBOT' => 'AbachoBOT',
				'accoona' => 'Accoona',
				'AcoiRobot' => 'AcoiRobot',
				'ASPSeek' => 'ASPSeek',
				'CrocCrawler' => 'CrocCrawler',
				'Dumbot' => 'Dumbot',
				'FAST-WebCrawler' => 'FAST-WebCrawler',
				'GeonaBot' => 'GeonaBot',
				'Gigabot' => 'Gigabot',
				'Lycos spider' => 'Lycos',
				'MSRBOT' => 'MSRBOT',
				'Altavista robot' => 'Scooter',
				'AltaVista robot' => 'Altavista',
				'ID-Search Bot' => 'IDBot',
				'eStyle Bot' => 'eStyle',
				'Scrubby robot' => 'Scrubby',
				'Facebook' => 'facebookexternalhit' 
		);
		// to get crawlers string used in function uncomment it
		// global $crawlers
		if (isset ( $_SERVER ['HTTP_USER_AGENT'] )) {
			$currentUserAgent = $_SERVER ['HTTP_USER_AGENT'];
			// it is better to save it in string than use implode every time
			$crawlers_agents = '/' . implode ( "|", $crawlers ) . '/';
			if (preg_match ( $crawlers_agents, $currentUserAgent, $matches )) {
				return true;
			}
		}
		
		return false;
	}
	
	/**
	 * Handle the addition of the Google Analytics tracking code
	 *
	 * @access private
	 * @param Object $app
	 * @param Object $doc
	 * @param string $location
	 * @return boolean
	 */
	private function addGoogleAnalyticsTrackingCode($app, $doc, $location = 'body') {
		// Get component params
		$injectGaJs = $this->jmapConfig->get ( 'inject_gajs', 0 );
		$gajsCode = trim ( $this->jmapConfig->get ( 'gajs_code', '' ) );
		$gajsVersion = trim ( $this->jmapConfig->get ( 'inject_gajs_version', 'analytics' ) );
		$anonymizeIp = '';
		$anonymizeGtagIp = '';
		if( $this->jmapConfig->get ( 'gajs_anonymize', 0) ) {
			$anonymizeIp = "ga('set', 'anonymizeIp', true);";
			$anonymizeGtagIp = ", { 'anonymize_ip': true }";
		}
		
		if ($gajsVersion == 'analytics') {
			$script = <<<JS
			<!-- Google Analytics -->
			<script>
			(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
			(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
			m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
			})(window,document,'script','//www.google-analytics.com/analytics.js','ga');

			ga('create', '$gajsCode', 'auto');
			ga('send', 'pageview');
			$anonymizeIp
			</script>
			<!-- End Google Analytics -->
JS;
		} elseif ($gajsVersion == 'gtag') {
			$script = <<<JS2
			<!-- Global Site Tag (gtag.js) - Google Analytics -->
			<script async src="https://www.googletagmanager.com/gtag/js?id=$gajsCode"></script>
			<script>
			  window.dataLayer = window.dataLayer || [];
			  function gtag(){dataLayer.push(arguments);}
			  gtag('js', new Date());
			  gtag('config', '$gajsCode' $anonymizeGtagIp);
			</script>
JS2;
		}

		// Check if the tracking code must be injected, manipulate output JResponse
		if ($injectGaJs && $gajsCode) {
			if ($location == 'body') {
				$body = $app->getBody ();

				// Replace buffered main view contents at the body end
				$body = preg_replace ( '/<\/body>/i', $script . '</body>', $body, 1 );

				// Set the new JResponse contents
				$app->setBody ( $body );
			} elseif ($location == 'head') {
				if ($doc->getType () === 'html') {
					$doc->addCustomTag ( $script );
				}
			}
		}
	}
	
	/**
	 * Main dispatch method
	 *
	 * @access public
	 * @return boolean
	 */
	public function onAfterInitialise() {
		$app = JFactory::getApplication ();
		
		// Avoid operations if plugin is executed in backend
		if ( $app->getClientId ()) {
			return;
		}
		
		// Security safe 1 - If Joomla 3.4+ and JMAP internal link force always the lang url param using the cookie workaround
		if( $app->input->get ( 'option' ) == 'com_jmap' && version_compare(JVERSION, '3.4', '>=') && $this->jmapConfig->get('advanced_multilanguage', 0)) {
			$lang = $app->input->get('lang');
		
			$sefs = JLanguageHelper::getLanguages('sef');
			$lang_codes = JLanguageHelper::getLanguages('lang_code');
		
			if (isset($sefs[$lang])) {
				$lang_code = $sefs[$lang]->lang_code;
		
				// Create a cookie.
				$conf = JFactory::getConfig();
				$cookie_domain 	= $conf->get('config.cookie_domain', '');
				$cookie_path 	= $conf->get('config.cookie_path', '/');
				setcookie(JApplicationHelper::getHash('language'), $lang_code, 86400, $cookie_path, $cookie_domain);
				$app->input->cookie->set(JApplicationHelper::getHash('language'), $lang_code);
		
				// Set the request var.
				$app->input->set('language', $lang_code);

				// Check if remove default prefix is active and the default language is not the current one
				$defaultSiteLanguage = JComponentHelper::getParams('com_languages')->get('site', 'en-GB');
				$pluginLangFilter = JPluginHelper::getPlugin('system', 'languagefilter');
				$removeDefaultPrefix = @json_decode($pluginLangFilter->params)->remove_default_prefix;
				if($removeDefaultPrefix && $defaultSiteLanguage != $lang_code) {
					$uri = JUri::getInstance();
					$path = $uri->getPath();
					// Force the language SEF code in the path
					$path = str_replace('/index.php', '/' . $lang . '/index.php', $path);
					$uri->setPath($path);
				}
			}
		}
		
		// Detect if current request come from a bot user agent
		if ($this->isBotRequest () && $app->input->get ( 'option' ) == 'com_jmap') {
			$_SERVER ['REQUEST_METHOD'] = 'POST';
			
			// Set dummy nobot var
			$app->input->post->set ( 'nobotsef', true );
			$GLOBALS['_' . strtoupper('post')] ['nobotsef'] = true;
		}
	}
	
	/**
	 * Hook for the auto Pingomatic third party extensions that have not its own
	 * route helper and work with the universal JSitemap route helper framework
	 *
	 * @access public
	 * @return boolean
	 */
	public function onAfterRoute() {
		$this->app = JFactory::getApplication ();
		
		// Security safe 2 - If Joomla 3.4+ and JMAP internal link revert back the query string 'lang' param to the sef lang code 'en' instead of the iso lang code 'en-GB'
		$lang = $this->app->input->get('lang');
		if( $this->app->input->get ( 'option' ) == 'com_jmap' && version_compare(JVERSION, '3.4', '>=') && strlen($lang) > 2) {
			$languageCode = $this->app->input->get('language');
			$lang_codes = JLanguageHelper::getLanguages('lang_code');
			if(isset($lang_codes[$languageCode])) {
				$sefLang = $lang_codes[$languageCode]->sef;
				$this->app->input->set('lang', $sefLang);
			}
		}
		
		// Avoid below operations if the plugin is executed in frontend
		if (! $this->app->getClientId ()) {
			return;
		}
		
		// Get component params
		$this->cParams = JComponentHelper::getParams ( 'com_jmap' );
		if (! $this->cParams->get ( 'default_autoping', 0 ) && ! $this->cParams->get ( 'autoping', 0 )) {
			return;
		}
		
		// Retrieve more informations as much as possible from the current POST array
		$option = $this->app->input->get ( 'option' );
		$view = $this->app->input->get ( 'view' );
		$controller = $this->app->input->get ( 'controller' );
		$task = $this->app->input->get ( 'task' );
		$id = $this->app->input->getInt ( 'id' );
		$catid = $this->app->input->get ( 'cid', null, 'array' );
		$language = $this->app->input->get ( 'language' );
		$name = $this->app->input->getString ( 'name' );
		if (is_array ( $catid )) {
			$catid = $catid [0];
		}
		
		// Valid execution mapping
		$arrayExecution = array (
				'com_zoo' => array (
						'controller' => 'item',
						'task' => array (
								'apply',
								'save',
								'save2new',
								'save2copy' 
						) 
				) 
		);
		
		// Test against valid execution, discard all invalid extensions operations
		if (array_key_exists ( $option, $arrayExecution )) {
			$testIfExecute = $arrayExecution [$option];
			foreach ( $testIfExecute as $property => $value ) {
				$evaluated = $$property;
				
				if (is_array ( $value )) {
					if (! in_array ( $evaluated, $value )) {
						return;
					}
				} else {
					if ($evaluated != $value) {
						return;
					}
				}
			}
		} else {
			return;
		}
		
		// Valid execution success! Go on to route the request to the content plugin, mimic the native Joomla onContentAfterSave
		
		// Auto loader setup
		// Register autoloader prefix
		require_once JPATH_ROOT . '/administrator/components/com_jmap/framework/loader.php';
		JMapLoader::setup ();
		JMapLoader::registerPrefix ( 'JMap', JPATH_ROOT . '/administrator/components/com_jmap/framework' );
		
		JPluginHelper::importPlugin ( 'content', 'pingomatic' );
		
		// Simulate the jsitemap_category_id object for the JSitemap route helper
		$elm = new stdClass ();
		$zooParams = $this->app->input->get ( 'params', null, 'array' );
		$elm->jsitemap_category_id = (int)$zooParams['primary_category'];
		
		// Simulate the $article Joomla object passed to the content observers
		$itemObject = new stdClass ();
		$itemObject->id = $id;
		$itemObject->catid = $elm;
		$itemObject->option = $option;
		$itemObject->view = $view ? $view : $controller;
		$itemObject->language = $language;
		$itemObject->title = $name;
		
		// Trigger the content plugin event
		$this->_subject->trigger ( 'onContentAfterSave', array (
				'com_zoo.item',
				$itemObject,
				false 
		) );
	}

	/**
	 * Hook for the management injection of the custom meta tags informations
	 *
	 * @access public
	 * @return void
	 */
	public function onBeforeCompileHead() {
		$app = JFactory::getApplication ();
		$document = JFactory::getDocument();

		// Avoid operations if plugin is executed in backend
		if ( $app->getClientId ()) {
			return;
		}

		// Checkpoint for Google Analytics tracking code addition
		if($this->jmapConfig->get('inject_gajs_location', 'body') == 'head') {
			$this->addGoogleAnalyticsTrackingCode($app, $document, 'head');
		}
		
		// Get the current URI and check for an entry in the DB table
		if($this->jmapConfig->get('metainfo_urldecode', 1)) {
			$uri = urldecode(JUri::current());
		} else {
			// Preserver URLs character encoding if any
			$uri = JUri::current();
		}

		// Apply same metadata even to the corresponding AMP pages
		if($this->jmapConfig->get('amp_sitemap_enabled', 0)) {
			$ampSuffix = $this->jmapConfig->get('amp_suffix', 'amp');
			if(preg_match("/\.$ampSuffix\./i", $uri)) {
				$uri = preg_replace("/\.$ampSuffix\./i", '.', $uri, 1);
			}
			if(preg_match('/\/' . $ampSuffix . '$/i', $uri)) {
				$uri = preg_replace('/\/' . $ampSuffix . '$/i', '', $uri, 1);
			}
		}
		
		// Store for later stage processing
		$this->jmapUri = $uri;
		
		// Setup the query
		$db = JFactory::getDbo();
		$query = "SELECT *" .
				 "\n FROM #__jmap_metainfo" .
				 "\n WHERE " . $db->quoteName('linkurl') . " = " . $db->quote($uri) .
				 "\n AND " . $db->quoteName('published') . " = 1";
		try {
			$metaInfoForThisUri = $db->setQuery($query)->loadObject();
		} catch(Exception $e) {}

		// Yes! Found some metainfo set for this uri, let's inject them into the document
		if(isset($metaInfoForThisUri->id)) {
			$title = trim($metaInfoForThisUri->meta_title);
			$description = trim($metaInfoForThisUri->meta_desc);
			$image = trim($metaInfoForThisUri->meta_image);
			$robots = $metaInfoForThisUri->robots;
			$ogTagsInclude = $this->jmapConfig->get('metainfo_ogtags', 1);
			$twitterCardsTagsInclude = $this->jmapConfig->get('metainfo_twitter_card_enable', 0);

			// Title and og:graph title
			if($title) {
				// Append site name, Joomla 3.2+ support
				if(method_exists($app, 'get')) {
					if ($app->get('sitename_pagetitles', 0) == 2 && trim($app->get('sitename'))) {
						$title = $title . ' - ' . trim($app->get('sitename'));
					} elseif ($app->get('sitename_pagetitles', 0) == 1 && trim($app->get('sitename'))) { // Prepend site name
						$title = trim($app->get('sitename')) . ' - ' . $title;
					}
				}
				
				$document->setTitle($title);
				$document->setMetaData('title', $title);
				$document->setMetaData('metatitle', $title);
				if($ogTagsInclude) {
					$document->setMetaData('og:title', $title, 'property');
					$document->setMetaData('twitter:title', $title);
				}
			}

			// Description and og:graph meta description
			if($description) {
				$document->setDescription($description);
				if($ogTagsInclude) {
					$document->setMetaData('og:description', $description, 'property');
					$document->setMetaData('twitter:description', $description);
				}
			}

			// Set always social share uri
			if($ogTagsInclude) {
				$document->setMetaData('og:url', $uri, 'property');
			}
			
			// Image for social share og:image and twitter:image
			if($image && $ogTagsInclude) {
				$imageLink = preg_match('/http/i', $image) ? $image : JUri::base() . ltrim($image, '/');
				$document->setMetaData('og:image', $imageLink, 'property');
				$document->setMetaData('twitter:image', $imageLink);
			}

			// Robots directive
			if($robots) {
				$document->setMetaData('robots', $robots);
			}
			
			// Additional Twitter cards tags
			if($ogTagsInclude && $twitterCardsTagsInclude) {
				$document->setMetaData('twitter:card', 'summary');
				$twitterCardSite = trim($this->jmapConfig->get('metainfo_twitter_card_site', ''));
				if($twitterCardSite) {
					$document->setMetaData('twitter:site', $twitterCardSite);
				}
				$twitterCardCreator = trim($this->jmapConfig->get('metainfo_twitter_card_creator', ''));
				if($twitterCardCreator) {
					$document->setMetaData('twitter:creator', $twitterCardCreator);
				}
			}
		}
		
		// Check if the override canonical feature is enabled and if so go on and check a url matching for some custom canonical
		if($this->jmapConfig->get('seospider_override_canonical', 1)) {
			$query = "SELECT *" .
					 "\n FROM #__jmap_canonicals" .
					 "\n WHERE " . $db->quoteName('linkurl') . " = " . $db->quote($uri);
			try {
				$canonicalForThisUri = $db->setQuery($query)->loadObject();
			} catch(Exception $e) {}
			
			// Yes! Found a canonical override set for this uri, let's replace them into the document
			if(isset($canonicalForThisUri->id)) {
				// Remove the current canonical tag from the document
				$header = $document->getHeadData();
				foreach($header['links'] as $key => $array) {
					if($array['relation'] == 'canonical') {
						unset($document->_links[$key]);
					}
				}
				
				// Add the new overridden canonical link
				$document->addHeadLink(htmlspecialchars($canonicalForThisUri->canonical), 'canonical', 'rel', array('data-jmap-canonical-override'=>1));
			}
		}
		
		// Fix pagination links if detected adding a page number/results suffix to make them unique and not duplicated
		$isPagination = $app->input->get->get('start', null, 'int');
		$isPage = $app->input->get->get('page', null, 'int');
		if($isPagination || $isPage) {
			$jmapParams = JComponentHelper::getParams('com_jmap');

			// Fix pagination is enabled
			if($jmapParams->get('unique_pagination', 1)) {
				// Get dispatched component params with view overrides
				$contentParams = $app->getParams();

				// Load JMap language translations
				$jLang = JFactory::getLanguage ();
				$jLang->load ( 'com_jmap', JPATH_ROOT . '/components/com_jmap', 'en-GB', true, true );
				if ($jLang->getTag () != 'en-GB') {
					$jLang->load ( 'com_jmap', JPATH_SITE, null, true, false );
					$jLang->load ( 'com_jmap', JPATH_SITE . '/components/com_jmap', null, true, false );
				}

				// Check if pagination params are detected otherwise fallback
				$leadingNum = $contentParams->get('num_leading_articles', null);
				$introNum = $contentParams->get('num_intro_articles', null);
				if($leadingNum && $introNum) {
					$articlesPerPage = (int)($leadingNum + $introNum);
					$pageNum = ' - ' . JText::_('COM_JMAP_PAGE_NUMBER') . ((int)($isPagination / $articlesPerPage) + 1);
				} else {
					// Fallback for generic components staring from xxx
					if($isPage) {
						$pageNum = ' - ' . JText::_('COM_JMAP_PAGE_NUMBER') . (int)$isPage;
					} else {
						$pageNum = ' - ' . JText::_('COM_JMAP_RESULTS_FROM') . $isPagination;
					}
				}

				$currentTitle = $document->getTitle();
				$document->setTitle($currentTitle . $pageNum);
			}
		}
	}
	
	/**
	 * Support for Joomla 3.8+ new routing throwing 404 exception in the parse function of the base router
	 *
	 * @access public
	 * @return boolean
	 */
	public function postProcessParseRule(&$router, &$uri) {
		$contentParams = JComponentHelper::getParams('com_content');
		if($contentParams->get('sef_advanced', 0)) {
			$siteRouter = JRouterSite::getInstance ( 'site', array (
					'mode' => JROUTER_MODE_SEF
			) );
			$option = $siteRouter->getVar('option');
			
			// Check if all parts of the URL have been parsed.
			// Otherwise we have an invalid URL
			if ($option == 'com_content' && strlen($uri->getPath()) > 0) {
				// Get component params and ensure that the custom 404 page is enabled
				$cParams = JComponentHelper::getParams('com_jmap');
			
				// Generate and set a new custom error message based on custom text/html
				$custom404Text = $cParams->get('custom_404_page_text', null);
				
				// Process contents
				$custom404Text = $this->processContentPlugins($custom404Text, $cParams);
			
				// Check if a strip tags is required
				if($cParams->get('custom_404_page_mode', 'html') == 'text') {
					$custom404Text = strip_tags($custom404Text);
				}
				
				throw new Exception($custom404Text, 404);
			}
		}
	}
	
	/**
	 * Hook for the management of the custom 404 page
	 *
	 * @access public
	 * @return boolean
	 */
	public function onRenderModule() {
		static $custom404Handled = false;
		if(self::$renderModuleTrigger) {
			return false;
		}
		
		if($custom404Handled) {
			return false;
		}

		// Mark as handled for next execution cycles
		$custom404Handled = true;

		// Get component params and ensure that the custom 404 page is enabled
		$cParams = JComponentHelper::getParams('com_jmap');
		if(!$cParams->get('custom_404_page_status', 0)) {
			return false;
		}

		// 404 custom page managed as an override by the handleError
		if($cParams->get('custom_404_page_override', 1)) {
			return false;
		}

		// Execute only in frontend
		$app = JFactory::getApplication ();
		if ($app->isAdmin ()) {
			return false;
		}
	
		// Ensure that the JDocumentError class is instantiated as singleton from the legacy J Error class
		if(	version_compare ( JVERSION, '3.6', '>=' )) {
			$attributes = array (
					'charset'   => 'utf-8',
					'lineend'   => 'unix',
					'tab'       => "\t",
					'language'  => 'en-GB',
					'direction' => 'ltr',
			);
			// If there is a JLanguage instance in JFactory then let's pull the language and direction from its metadata
			if (JFactory::$language) {
				$attributes['language']  = JFactory::getLanguage()->getTag();
				$attributes['direction'] = JFactory::getLanguage()->isRtl() ? 'rtl' : 'ltr';
			}
			$document = JDocument::getInstance('error', $attributes);
		} else {
			$document = JDocument::getInstance('error');
		}
		
		if (! isset ( $document->error ) || ! is_object ( $document->error )) {
			return false;
		}
	
		// Dispatched format, apply only to html document
		$documentFormat = $app->input->get ( 'format', null );
		if ($documentFormat && $documentFormat != 'html') {
			return false;
		}
	
		// Dispatched template file, ignores component tmpl
		if ($app->input->get ( 'tmpl', null ) === 'component') {
			return false;
		}

		// Evaluate the error code, 404 only is of our interest and ignore everything else
		$documentExceptionCode = $document->error->getCode ();
		if($documentExceptionCode == 404) {
			// Generate and set a new custom error message based on custom text/html
			$custom404Text = $cParams->get('custom_404_page_text', null);

			// Process contents
			$custom404Text = $this->processContentPlugins($custom404Text, $cParams);

			// Check if a strip tags is required
			if($cParams->get('custom_404_page_mode', 'html') == 'text') {
				$custom404Text = strip_tags($custom404Text);
			}

			// Set the new Exception message supporting HTML and hoping that htmlspecialchars in not used by the error.php of the template
			$newException = new JException($custom404Text, 404);
			$document->setError($newException);
			$document->error = $newException;
		}
	}

	/**
	 * Hook override for the management of the custom 404 error page
	 *
	 * @access public
	 * @return boolean
	 */
	static public function handleError(&$error) {
		// Get the application object.
		$app = JFactory::getApplication();

		// Dispatched format, apply only to html document
		$documentFormat = $app->input->get ( 'format', null );
		if ($documentFormat && $documentFormat != 'html') {
			return false;
		}

		// Dispatched template file, ignores component tmpl
		if ($app->input->get ( 'tmpl', null ) === 'component') {
			return false;
		}

		// Make sure the error is a 404 and we are not in the administrator.
		if (!$app->isAdmin () && $error->getCode () == 404) {
			// Get component params and ensure that the custom 404 page is enabled
			$cParams = JComponentHelper::getParams('com_jmap');
	
			// Generate and set a new custom error message based on custom text/html
			$custom404Text = $cParams->get('custom_404_page_text', null);

			// Process contents
			$custom404Text = self::processContentPlugins($custom404Text, $cParams);

			// Check if a strip tags is required
			if($cParams->get('custom_404_page_mode', 'html') == 'text') {
				$custom404Text = strip_tags($custom404Text);
			}

			$newException = new JException($custom404Text, $error->getCode());
			// Render the error page.
			JError::customErrorPage ( $newException );
		}
	}
	
	/**
	 * Application event
	 *
	 * @access public
	 */
	public function onAfterRender() {
		// Framework reference
		$app = JFactory::getApplication ();
		$doc = JFactory::getDocument ();
	
		// Check if the app can start
		if ($app->isAdmin ()) {
			return false;
		}
	
		// Check if the app can start
		if ($doc->getType () !== 'html') {
			return false;
		}
	
		$option = $app->input->get('option', null);
		if ( $option == 'com_jmap' && $app->input->get('format') ) {
			return false;
		}

		// Check if the override headings feature is enabled and if so go on and check a url matching for some heading
		if($this->jmapConfig->get('seospider_override_headings', 1)) {
			$db = JFactory::getDbo();
			// Search an headings override for this URL
			$query = "SELECT *" .
					 "\n FROM #__jmap_headings" .
					 "\n WHERE " . $db->quoteName('linkurl') . " = " . $db->quote($this->jmapUri);
			try {
				$headingsForThisUri = $db->setQuery($query)->loadObject();
			} catch(Exception $e) {}
				
			// Yes! Found some headings override set for this uri, let's replace them into the document
			if(isset($headingsForThisUri->id)) {
				// Go on only if there is at least one valid heading override
				if($headingsForThisUri->h1 || $headingsForThisUri->h2 || $headingsForThisUri->h3) {
					// Include DOM parser class
					require_once (JPATH_ROOT . '/plugins/system/jmap/simplehtmldom.php');
						
					$simpleHtmlDomInstance = new JMapSimpleHtmlDom();
					$simpleHtmlDomInstance->load( $app->getBody () );
						
					// Find and replace the first encountered H1 tag
					if($headingsForThisUri->h1) {
						$domElementsH1 = $simpleHtmlDomInstance->find( 'h1' );
		
						// Replace the original H1 header with the overridden one
						if(isset($domElementsH1[0])) {
							$element = $domElementsH1[0];
							$nodeText = $element->text(true);
							$nodeText = $headingsForThisUri->h1;
							$element->innertext = $nodeText;
							$element->setAttribute('data-jmap-heading-override', 1);
						}
					}
						
					// Find and replace the first encountered H2 tag
					if($headingsForThisUri->h2) {
						$domElementsH2 = $simpleHtmlDomInstance->find( 'h2' );
							
						// Replace the original H2 header with the overridden one
						if(isset($domElementsH2[0])) {
							$element = $domElementsH2[0];
							$nodeText = $element->text(true);
							$nodeText = $headingsForThisUri->h2;
							$element->innertext = $nodeText;
							$element->setAttribute('data-jmap-heading-override', 1);
						}
					}
						
					// Find and replace the first encountered H3 tag
					if($headingsForThisUri->h3) {
						$domElementsH3 = $simpleHtmlDomInstance->find( 'h3' );
							
						// Replace the original H3 header with the overridden one
						if(isset($domElementsH3[0])) {
							$element = $domElementsH3[0];
							$nodeText = $element->text(true);
							$nodeText = $headingsForThisUri->h3;
							$element->innertext = $nodeText;
							$element->setAttribute('data-jmap-heading-override', 1);
						}
					}
						
					$body = $simpleHtmlDomInstance->save();
						
					// Final assignment
					$app->setBody ( $body );
				}
			}
		}

		// Checkpoint for Google Analytics tracking code addition
		if($this->jmapConfig->get('inject_gajs_location', 'body') == 'body') {
			$this->addGoogleAnalyticsTrackingCode($app, $doc, 'body');
		}
	}
	
	/* Manage the Joomla updater based on the user license
	 *
	 * @access public
	 * @return void
	 */
	public function onInstallerBeforePackageDownload(&$url, &$headers) {
		$uri 	= JUri::getInstance($url);
		$parts 	= explode('/', $uri->getPath());
		$app = JFactory::getApplication();
		if ($uri->getHost() == 'storejextensions.org' && in_array('com_jsitemap.zip', $parts)) {
			// Init as false unless the license is valid
			$validUpdate = false;
			
			// Manage partial language translations
			$jLang = JFactory::getLanguage();
			$jLang->load('com_jmap', JPATH_BASE . '/components/com_jmap', 'en-GB', true, true);
			if($jLang->getTag() != 'en-GB') {
				$jLang->load('com_jmap', JPATH_BASE, null, true, false);
				$jLang->load('com_jmap', JPATH_BASE . '/components/com_jmap', null, true, false);
			}
			
			// Email license validation API call and &$url building construction override
			$cParams = JComponentHelper::getParams('com_jmap');
			$registrationEmail = $cParams->get('registration_email', null);
			
			// License
			if($registrationEmail) {
				$prodCode = 'jsitemappro';
				$cdFuncUsed = 'str_' . 'ro' . 't' . '13';
				
				// Retrieve license informations from the remote REST API
				$apiResponse = null;
				$apiEndpoint = $cdFuncUsed('uggc' . '://' . 'fgberwrkgrafvbaf' . '.bet') . "/option,com_easycommerce/action,licenseCode/email,$registrationEmail/productcode,$prodCode";
				if (function_exists('curl_init')){
					$ch = curl_init();
					curl_setopt($ch, CURLOPT_URL, $apiEndpoint);
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
					$apiResponse = curl_exec($ch);
					curl_close($ch);
				}
				$objectApiResponse = json_decode($apiResponse);
				
				if(!is_object($objectApiResponse)) {
					// Message user about error retrieving license informations
					$app->enqueueMessage(JText::_('COM_JMAP_ERROR_RETRIEVING_LICENSE_INFO'));
				} else {
					if(!$objectApiResponse->success) {
						switch ($objectApiResponse->reason) {
							// Message user about the reason the license is not valid
							case 'nomatchingcode':
								$app->enqueueMessage(JText::_('COM_JMAP_LICENSE_NOMATCHING'));
								break;
								
							case 'expired':
								// Message user about license expired on $objectApiResponse->expireon
								$app->enqueueMessage(JText::sprintf('COM_JMAP_LICENSE_EXPIRED', $objectApiResponse->expireon));
								break;
						}	
							
					}
					
					// Valid license found, builds the URL update link and message user about the license expiration validity
					if($objectApiResponse->success) {
						$url = $cdFuncUsed('uggc' . '://' . 'fgberwrkgrafvbaf' . '.bet' . '/XZY1306TSPQnifs3243560923kfuxnj35td1rtt45663f.ugzy');
						
						$validUpdate = true;
						$app->enqueueMessage(JText::sprintf('COM_JMAP_EXTENSION_UPDATED_SUCCESS', $objectApiResponse->expireon));
					}
				}
			} else {
				// Message user about missing email license code
				$app->enqueueMessage(JText::sprintf('COM_JMAP_MISSING_REGISTRATION_EMAIL_ADDRESS', JFilterOutput::ampReplace('index.php?option=com_jmap&task=config.display#_licensepreferences')));
			}
			
			if(!$validUpdate) {
				$app->enqueueMessage(JText::_('COM_JMAP_UPDATER_STANDARD_ADVISE'), 'notice');
			}
		}
	}
	
	/**
	 * Class constructor, manage params from component
	 *
	 * @access private
	 * @return boolean
	 */
	public function __construct(&$subject) {
		parent::__construct ( $subject );
		$this->joomlaConfig = JFactory::getConfig ();
		
		// Manage partial language translations if editing modules jmap in backend
		$app = JFactory::getApplication ();
		if(($app->input->get('option') == 'com_modules' || $app->input->get('option') == 'com_advancedmodules') &&
			$app->input->get('view') == 'module' &&
			$app->input->get('layout') == 'edit' &&
			$app->isAdmin ()) {
			$jLang = JFactory::getLanguage ();
			$jLang->load ( 'com_jmap', JPATH_ADMINISTRATOR . '/components/com_jmap', 'en-GB', true, true );
			if ($jLang->getTag () != 'en-GB') {
				$jLang->load ( 'com_jmap', JPATH_ADMINISTRATOR, null, true, false );
				$jLang->load ( 'com_jmap', JPATH_ADMINISTRATOR . '/components/com_jmap', null, true, false );
			}
		}
		
		// Set the error handler for E_ERROR to be the class handleError method.
		$cParams = JComponentHelper::getParams('com_jmap');
		$this->jmapConfig = $cParams;
		if($cParams->get('custom_404_page_status', 0) && $cParams->get('custom_404_page_override', 1) && !$app->isAdmin ()) {
			// Legacy error class system for legacy stable router
			JError::setErrorHandling(E_ERROR, 'callback', array('plgSystemJMap', 'handleError'));
			
			// Add compatibility support for J3.8 new router management
			if(version_compare(JVERSION, '3.8', '>=')) {
				$joomlaRouter = JFactory::getApplication()->getRouter();
				$joomlaRouter->attachParseRule ( array (
						$this,
						'postProcessParseRule'
				), JRouter::PROCESS_AFTER );
			}
		}
	}
}