Current Directory: /home/astoriaah/www/plugins/system/sh404sef
Viewing File: /home/astoriaah/www/plugins/system/sh404sef/sh404sef.php
<?php
/**
* sh404SEF - SEO extension for Joomla!
*
* @author Yannick Gaultier
* @copyright (c) Yannick Gaultier - Weeblr llc - 2022
* @package sh404SEF
* @license http://www.gnu.org/copyleft/gpl.html GNU/GPL
* @version 4.24.1.4308
* @date 2022-05-31
*/
use Weeblr\Wblib\V_SH4_4308\Factory;
// no direct access
defined('_JEXEC') || die;
/**
* sh404SEF system plugin
*
* @author
*/
class plgSystemSh404sef extends JPlugin
{
static $_template = '';
public function __construct($subject, array $config = array())
{
parent::__construct($subject, $config);
// init wbLib
defined('WBLIB_EXEC') or define('WBLIB_EXEC', true);
if (file_exists(__DIR__ . '/bootstrap.php'))
{
include_once __DIR__ . '/bootstrap.php';
}
// @TODO log this error
}
public function onAfterInitialise()
{
// prevent warning on php5.3+
$this->_fixTimeWarning();
// get joomla application object
$app = JFactory::getApplication();
// check shLib is available
if (!defined('SHLIB_VERSION'))
{
$msg = 'sh404SEF requires the shLib system plugin to be enabled, but you appear to have disabled it. Please enable it again!';
if ($app->isAdmin())
{
$app->enqueuemessage($msg, 'error');
}
return;
}
// register our autoloader
$this->_registerAutoloader();
if (!defined('SH404SEF_AUTOLOADER_LOADED'))
{
$msg = 'sh404SEF autoloader failed to initialize, sh404SEF cannot start.';
if ($app->isAdmin())
{
$app->enqueuemessage($msg, 'error');
}
ShlSystem_Log::error('sh404sef', $msg);
return;
}
// init hooks
ShlHook::load('', 'sh404sef_functions.php');
// init the sh404SEF wbalib app - post sh404SEF version 4.20
if (defined('SH404SEF_APP_PATH'))
{
try
{
$this->sh4App = Factory::get()->getThis(
'app',
'sh404sef',
array(
'id' => 'sh404sef',
'namespace' => '\Weeblr\Sh404sef',
'rootpath' => SH404SEF_APP_PATH
)
);
}
catch (\Exception $e)
{
ShlSystem_Log::error('sh404sef', 'Error loading wbLib sh404SEF app: ' . $e->getMessage());
}
}
else
{
ShlSystem_Log::error('sh404sef', 'Unable to load wbLib sh404SEF app, SH404SEF_APP_PATH is not defined.');
}
// create the unique page info object, and initialize it
$pageInfo = Sh404sefFactory::getPageInfo();
$pageInfo->init();
// base class
$baseClassFile = JPATH_ADMINISTRATOR . '/components/com_sh404sef/sh404sef.class.php';
if (file_exists($baseClassFile))
{
include_once($baseClassFile);
}
else
{
$msg = 'sh404SEF base class file is missing or unreadable: ' . $baseClassFile . ', sh404SEF cannot start.';
if ($app->isAdmin())
{
$app->enqueueMessage($msg, 'error');
}
ShlSystem_Log::error('sh404sef', $msg);
return;
}
// define a default path for loading layouts
ShlMvcLayout_Helper::$defaultBasePath = sh404SEF_LAYOUTS;
// get our configuration
$sefConfig = Sh404sefFactory::getConfig();
// hook for a few SEO hacks
if ($sefConfig->Enabled && $app->isSite())
{
$this->_hacks();
}
// security layer
$secLayerFile = JPATH_ROOT . '/components/com_sh404sef/shSec.php';
if (!$app->isAdmin() && $sefConfig->shSecEnableSecurity && shFileExists($secLayerFile))
{
include_once($secLayerFile);
// do security checks
shDoSecurityChecks();
shCleanUpSecLogFiles(); // see setting in class file for clean up frequency
}
else
{
if ($sefConfig->shSecEnableSecurity && !shFileExists($secLayerFile))
{
$msg = 'sh404SEF security layer file is missing or unreadable: ' . $secLayerFile . ', sh404SEF security feature will not operate properly.';
if ($app->isAdmin())
{
$app->enqueueMessage($msg);
}
ShlSystem_Log::error('sh404sef', $msg);
return;
}
}
// optionnally collect page creation time
if (!$app->isAdmin() && $sefConfig->analyticsEnableTimeCollection)
{
jimport('joomla.error.profiler');
// creating the profiler object will start the counter
$profiler = JProfiler::getInstance('sh404sef_profiler');
}
// load plugins, as per configuration
$this->_loadPlugins($type = 'sh404sefcore');
// load extension plugins, created by others
$this->_loadPlugins($type = 'sh404sefextplugins');
// hook to be able to install other SEF extension plugins
//Sh404sefHelperExtplugins::loadInstallAdapters();
// another hook to allow other SEF extensions language file to be loaded
Sh404sefHelperExtplugins::loadLanguageFiles();
if (!$sefConfig->Enabled)
{
// go away if not enabled
return;
}
if (!defined('SH404SEF_IS_RUNNING'))
{
DEFINE('SH404SEF_IS_RUNNING', 1);
}
/**
* Hook to run the registered API handlers.
*
* @api
* @package wbLib\action\api
* @var wblib_api_process_request
* @since 0.0.1
*
*/
Factory::get()->getThe('hook')->run(
'wblib_api_process_request'
);
if (!$app->isAdmin())
{
// setup our JPagination replacement, so as to bring
// back # of items per page in the url, in order
// to properly calculate pagination
// will only work if php > 5, so test for that
if (version_compare(phpversion(), '5.0') >= 0)
{
// this register the old file, but do not load it if PHP5
// will prevent further calls to the same jimport()
// to actually do anything, because the 'joomla.html.pagination' key
// is now registered statically in Jloader::import()
jimport('joomla.html.pagination');
// now we can register our own path
JLoader::register('JPagination', JPATH_ADMINISTRATOR . '/components/com_sh404sef/pagination.php');
}
// attach parse and build rules to Joomla router
$joomlaRouter = $app->getRouter();
$pageInfo->router = new Sh404sefClassRouter();
if (version_compare(JVERSION, '3.4', 'ge'))
{
$joomlaRouter->attachParseRule(
function (&$router, &$uri) {
if (Sh404sefHelperAnalytics::shouldDisplayFrontend($uri, $router))
{
Factory::get()
->getA('Weeblr\Sh404sef\Helper\Analytics')
->displayFrontend();
}
},
JRouter::PROCESS_BEFORE
);
$joomlaRouter->attachParseRule(array($pageInfo->router, 'preprocessParseRule'), JRouter::PROCESS_BEFORE);
}
$joomlaRouter->attachParseRule(array($pageInfo->router, 'parseRule'));
$joomlaRouter->attachBuildRule(array($pageInfo->router, 'buildRule'));
// pretend SEF is on, mostly for Joomla SEF plugin to work
// as it checks directly 'sef' value in config, instead of
// using $router->getMode()
JFactory::$config->set('sef', 1);
// perform startup operations, such as detecting request caracteristics
// and checking redirections
$uri = JURI::getInstance();
$pageInfo->router->startup($uri);
}
}
public function onAfterRoute()
{
if (!defined('SH404SEF_IS_RUNNING'))
{
return;
}
// set template, to perform alternate template output, if set to
$app = JFactory::getApplication();
if (!$app->isAdmin())
{
$this->_setAlternateTemplate();
}
}
public function onAfterDispatch()
{
if (defined('SH404SEF_IS_RUNNING'))
{
$app = JFactory::getApplication();
if (!$app->isAdmin())
{
// reset alternate template
$this->_resetAlternateTemplate();
// create shurl on the fly for this page
// if not already done
if (Sh404sefConfigurationEdition::$id == 'full' && JFactory::getDocument()->getType() == 'html')
{
// shortlinks
Sh404sefHelperShurl::updateShurls();
}
// insert (some) meta data
if (
JFactory::getDocument()->getType() == 'html'
&&
$app->input->getCmd('format') != 'raw'
&&
Sh404sefFactory::getConfig()->shMetaManagementActivated
)
{
Sh404sefHelperMetadata::includeMetaPlugin();
$pageInfo = Sh404sefFactory::getPageInfo();
if (!preg_match('/(&|\?)lang=[a-zA-Z]{2,3}/iuU', $pageInfo->currentNonSefUrl))
{
// no lang string, let's add default
$shTemp = explode('-', $pageInfo->currentLanguageTag);
$shLangTemp = $shTemp[0] ? $shTemp[0] : 'en';
$pageInfo->currentNonSefUrl .= '&lang=' . $shLangTemp;
}
Sh404sefHelperMetadata::insertMetaData();
}
}
}
}
/**
* Optionally operate searches of adjustment on page content
*
* @param string $context The context of the content being passed to the plugin.
* @param mixed &$row An object with a "text" property or the string to be processed.
* @param mixed &$params Additional parameters.
* @param integer $page Optional page number. Unused. Defaults to zero.
*
* @return boolean True on success.
* @throws Exception
*/
public function onContentPrepare($context, &$row, &$params, $page = 0)
{
// Don't run this plugin when the content is being indexed
if ($context == 'com_finder.indexer')
{
return true;
}
if (JFactory::getApplication()->isAdmin())
{
return true;
}
if (!defined('SH404SEF_IS_RUNNING'))
{
return true;
}
if (JFactory::getApplication()->input->getCmd('format') == 'raw')
{
return true;
}
// should we spend time on this?
$config = Sh404sefFactory::getConfig();
if (is_object($row) && !empty($row->text))
{
$row->text = $this->searchOGPImage($row->text, $config, $context, $row);
if ($config->autoBuildDescription)
{
$row->text = Sh404sefHelperMetadata::buildAutoDescription($context, $row->text, $params, $page);
}
}
else if (!is_object($row))
{
$row = $this->searchOGPImage($row, $config);
if ($config->autoBuildDescription)
{
$row = Sh404sefHelperMetadata::buildAutoDescription($context, $row, $params, $page);
}
}
return true;
}
public function onAfterRender()
{
if (defined('SH404SEF_IS_RUNNING'))
{
$config = Sh404sefFactory::getConfig();
$document = JFactory::getDocument();
if (JFactory::getApplication()->isAdmin())
{
if (version_compare(JVERSION, '3.0', 'ge'))
{
// are we on an edit page?
$app = JFactory::getApplication();
$option = $app->input->getCmd('option');
$view = $app->input->getCmd('view');
$layout = $app->input->getCmd('layout');
if ($layout == 'edit'
&& (($option == 'com_content' && $view == 'article') || ($option == 'com_categories' && $view == 'category')
|| ($option == 'com_contact' && $view == 'contact') || ($option == 'com_newsfeeds' && $view == 'newsfeed')
|| ($option == 'com_weblinks' && $view == 'weblink'))
)
{
// variations in field name
if ($option == 'com_newsfeeds' || $option == 'com_contact')
{
$titleFieldName = 'jform_name';
}
else
{
$titleFieldName = 'jform_title';
}
// attach an input counter to the title input boxes
if ($document->getType() == 'html')
{
$page = JResponse::getBody();
// insert css and js
$link = '';
$cssLink = ShlHtml_Manager::getInstance()->getMediaLink('bs', 'css');
if (strpos($page, $cssLink) === false)
{
$link .= '<link rel="stylesheet" href="' . $cssLink . '" type="text/css" />';
}
$jsLink = ShlHtml_Manager::getInstance()->getMediaLink('bs', 'js');
if (strpos($page, $jsLink) === false)
{
$link .= "\n" . '<script src="' . $jsLink . '"></script>';
}
if (!empty($link))
{
$page = str_replace('</head>', $link . '</head>', $page);
}
// insert custom js to attach counters to title and metadesc fields
$script = ShlHtmlBs_Helper::renderInputCounter(
$titleFieldName,
Sh404sefFactory::getPConfig()->metaDataSpecs['metatitle-joomla-be']
);
$script .= ShlHtmlBs_Helper::renderInputCounter(
'jform_metadesc',
Sh404sefFactory::getPConfig()->metaDataSpecs['metadesc-joomla-be']
);
$page = str_replace('</body>', $script . '</body>', $page);
JResponse::setBody($page);
}
}
}
return;
}
// modify page content on the fly
if ($document->getType() == 'html')
{
// search for images for OGP, only if previous attempts have failed
if ($config->shMetaManagementActivated && ($config->enableOpenGraphData || $config->enableTwitterCards))
{
$content = $this->searchOGPImage(JResponse::getBody(), $config);
JResponse::setBody($content);
}
// return if no seo optim to perform
if ($config->shMetaManagementActivated || Sh404sefHelperAnalytics::isEnabled())
{
// now execute most content rewriting
$include = JPATH_ROOT . '/components/com_sh404sef/shPageRewrite.php';
include_once $include;
}
// insert redirect message after automatic 404 redirect
$message404 = JFactory::getSession()->get('wb_sh404sef_404_redirect_message');
JFactory::getSession()->set('wb_sh404sef_404_redirect_message', null);
if (!empty($message404))
{
$renderedMessage = ShlMvcLayout_Helper::render('com_sh404sef.general.error_404_redirect_msg', $message404, sh404SEF_LAYOUTS);
if (!empty($renderedMessage))
{
$page = JResponse::getBody();
$page = str_replace('</body>', $renderedMessage . '</body>', $page);
JResponse::setBody($page);
}
}
}
}
}
/**
* Search for an OGP image in some content.
*
* @param string $content Content to search.
* @param SEFConfig $config sh404SEF config.
* @param string $context Context passed by Joomla, if any.
* @param null|Object $contentObject Optional original data object.
*
* @return mixed|string
*/
private function searchOGPImage($content, $config, $context = '', $contentObject = null)
{
/**
* Filter contexts where we should not look for OGP images.
*
* @param array $allowedContexts List of contexts which we are allowed to search for ogp images.
* @param string $content Content to search.
* @param SEFConfig $config sh404SEF config.
* @param string $context Context passed by Joomla, if any.
* @param null|Object $contentObject Optional original data object.
*
* @return array
* @api
* @package sh404SEF\filter\metadata
* @var sh404sef_ogp_image_excluded_search_contexts
* @since 4.16.2
*
*/
$excludedContexts = ShlHook::filter(
'sh404sef_ogp_image_excluded_search_contexts',
array(),
$content,
$config,
$context,
$contentObject
);
if (in_array(
$context,
$excludedContexts
))
{
return $content;
}
// 1 - custom meta data
$customData = Sh404sefHelperMetadata::getCustomMetaDataFromDb();
if ($customData->og_enable == SH404SEF_OPTION_VALUE_NO
|| ($customData->og_enable == SH404SEF_OPTION_VALUE_USE_DEFAULT && $config->enableOpenGraphData == SH404SEF_OPTION_VALUE_NO)
)
{
// OGP is disabled on this page
return $content;
}
if (!empty($customData->og_image))
{
// user set an image already
return $content;
}
// 1.5 OGP info cached in content?
if (strpos($content, 'sh404sef_tag_ogp_image') != false)
{
$cachedImage = Sh404sefHelperGeneral::getCommentedTag(
$content,
'sh404sef_tag_ogp_image'
);
$cachedImage = wbArrayGet($cachedImage, 0, '');
if (!empty($cachedImage))
{
$customData->og_image = $cachedImage;
}
if (!empty($customData->og_image))
{
return $content;
}
}
// 2 - Full or Intro image (only for com_content)
if ('com_content.article' == $context)
{
$bestImage = Sh404sefHelperOgp::detectComContentImages($contentObject);
if (!empty($bestImage))
{
$customData->og_image = $bestImage;
}
}
// 2Bis - Same for K2 -
if ('com_k2.item' == $context)
{
$bestImage = Sh404sefHelperOgp::detectComK2Images($contentObject);
if (!empty($bestImage))
{
$customData->og_image = $bestImage;
}
}
if (!empty($customData->og_image))
{
// found an image
$content = Sh404sefHelperGeneral::addCommentedTag(
$content,
'sh404sef_tag_ogp_image',
$customData->og_image,
'split',
array(
'split_on' => '{K2Splitter}',
'split_position' => 'after',
'if_no_split_text' => 'before'
)
);
return $content;
}
// 3 - Automatic detection, largest or first
$bestImage = ShlHtmlContent_Image::getBestImage($content, $config->ogImageDetection, Sh404sefFactory::getPConfig()->facebookImageSize);
if (!empty($bestImage))
{
$customData->og_image = $bestImage;
}
// in all case, remove the optional disable tag
$content = str_replace('{disable_auto_meta_image_detection}', '', $content);
// store image in content itself so as to be able to retrieve it when Joomla cache is enabled
if (!empty($customData->og_image))
{
$content = Sh404sefHelperGeneral::addCommentedTag(
$content,
'sh404sef_tag_ogp_image',
$bestImage,
'before'
);
}
return $content;
}
/**
* A set of SEO hacks that don't fit elsewhere
* as we usually want a very quick response and
* avoid wasted resources
*
*/
protected function _hacks()
{
}
/**
* Decide whether we should force language (to default)
* to counter Joomla! incorrect language selection
*
* @param $app
*
* @return bool
*/
protected function _shouldForceLanguage($app)
{
// we should force language if:
// 1 - lang code is removed on default language
// update 2015-04: no, even if language is not removed, we must force language
// as Joomla always accept any language for / !!!
// 2 - the request doesn't start with a (non-default) language code
// don't break browser language detection
$langCookie = $app->input->cookie->get(JApplication::getHash('language'), null);
if (empty($langCookie) && $app->getDetectBrowser())
{
return false;
}
// don't set lang on (forms) POST
if ($app->input->getMethod() == "POST" && count($app->input->post) != 0)
{
return false;
}
// now does request start with a valid language code
$pageInfo = Sh404sefFactory::getPageInfo();
$base = JUri::getInstance()->base();
$request = wbLTrim($pageInfo->currentSefUrl, $base);
$urlLanguageCode = explode('/', $request);
$urlLanguageCode = $urlLanguageCode[0];
if (Sh404sefHelperLanguage::validateSefLanguageCode($urlLanguageCode))
{
return false;
}
return true;
}
protected function _shouldSetHomePageLanguage($app)
{
// don't break browser language detection
$langCookie = $app->input->cookie->get(JApplication::getHash('language'), null);
if (empty($langCookie) && $app->getDetectBrowser())
{
return false;
}
// don't set lang on (forms) POST
if ($app->input->getMethod() == "POST" && count($app->input->post) == 0)
{
return false;
}
$pageInfo = Sh404sefFactory::getPageInfo();
$base = JUri::getInstance()->base();
$request = wbLTrim($pageInfo->currentSefUrl, $base);
// easy, just home page
if ($request == '')
{
return true;
}
// might be some home page variation: simple, just /index.php
if ($request == 'index.php')
{
return true;
}
// other home page variation: index.php?lang=xx: comply with that
$langCodeInUrl = Sh404sefHelperUrl::getUrlLang($request);
if (!empty($langCodeInUrl))
{
return false;
}
// other home page variation: index.php?val1=xx&val2=yy, we should set language
$bits = explode('?', $request);
if (!empty($bits) && $bits['0'] == 'index.php')
{
return true;
}
return false;
}
/**
* Load and register the plugins currently activated by webmaster
*
* @return none
*/
protected function _loadPlugins($type)
{
// required joomla library
jimport('joomla.plugin.helper.php');
// import the plugin files
$status = JPluginHelper::importPlugin($type);
return $status;
}
/**
* Register our autoloader function with PHP
*/
protected function _registerAutoloader()
{
$autoloaderFile = JPATH_ADMINISTRATOR . '/components/com_sh404sef/helpers/autoloader.php';
// add our own
if (file_exists($autoloaderFile))
{
// get Joomla autloader out
if (function_exists("__autoload"))
{
spl_autoload_unregister("__autoload");
}
include $autoloaderFile;
$registered = spl_autoload_register(array('Sh404sefAutoloader', 'doAutoload'));
// stitch back Joomla's at the end of the list
if (function_exists("__autoload"))
{
spl_autoload_register("__autoload");
}
if (!defined('SH404SEF_AUTOLOADER_LOADED'))
{
define('SH404SEF_AUTOLOADER_LOADED', 1);
}
}
else
{
$app = JFactory::getApplication();
if ($app->isAdmin())
{
$app->enqueueMessage('sh404SEF autoloader file is missing or unreadable: ' . $autoloaderFile, 'error');
}
}
}
protected function _fixTimeWarning()
{
// prevent timezone not set warnings to appear all over,
// especially for PHP 5.3.3+
$serverTimezone = @date_default_timezone_get();
@date_default_timezone_set($serverTimezone);
}
protected function _setAlternateTemplate()
{
$app = JFactory::getApplication();
$sefConfig = Sh404sefFactory::getConfig();
if (!defined('SHMOBILE_MOBILE_TEMPLATE_SWITCHED') && !empty($sefConfig->alternateTemplate))
{
// global on/off switch
self::$_template = $app->getTemplate(); // save current template
$app->setTemplate($sefConfig->alternateTemplate);
}
}
protected function _resetAlternateTemplate()
{
$app = JFactory::getApplication();
$sefConfig = Sh404sefFactory::getConfig();
if (!defined('SHMOBILE_MOBILE_TEMPLATE_SWITCHED') && !empty($sefConfig->alternateTemplate))
{
// global on/off switch
if (empty(self::$_template))
{
return;
}
$app->setTemplate(self::$_template); // restore old template
}
}
private function getLangCookieTime()
{
$cookieTime = 0;
$languageFilterPlugin = JPluginHelper::getPlugin('system', 'languagefilter');
if (!empty($languageFilterPlugin))
{
$params = new JRegistry($languageFilterPlugin->params);
if ($params->get('lang_cookie', 1) == 1)
{
$cookieTime = time() + 365 * 86400;
}
}
return $cookieTime;
}
}