sh404SEF SEF/URL component (Weeblr), vetted clean from live; upstream gated and discontinued for Joomla 3 -> deployed files. Signed-off-by: LÁZÁR Imre <imre@illusion.hu> Assisted-by: claude-code@claude-opus-4-8
440 lines
13 KiB
PHP
440 lines
13 KiB
PHP
<?php
|
|
/**
|
|
* sh404SEF - SEO extension for Joomla!
|
|
*
|
|
* @author Yannick Gaultier
|
|
* @copyright (c) Yannick Gaultier 2014
|
|
* @package sh404SEF
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU/GPL
|
|
* @version 4.4.4.1791
|
|
* @date 2014-07-01
|
|
*/
|
|
|
|
// no direct access
|
|
defined('_JEXEC') or die('Restricted access');
|
|
|
|
/**
|
|
* sh404SEF system plugin
|
|
*
|
|
* @author
|
|
*/
|
|
class plgSystemSh404sef extends JPlugin
|
|
{
|
|
static $_template = '';
|
|
|
|
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'))
|
|
{
|
|
$app
|
|
->enqueuemessage(
|
|
'sh404SEF requires the shLib system plugin to be enabled, but you appear to have disabled it. Please enable it again!', 'error');
|
|
return;
|
|
}
|
|
|
|
// register our autoloader
|
|
$this->_registerAutoloader();
|
|
|
|
// create the unique page info object, and initialize it
|
|
$pageInfo = Sh404sefFactory::getPageInfo();
|
|
$pageInfo->init();
|
|
|
|
// base class
|
|
require_once(JPATH_ADMINISTRATOR . '/components/com_sh404sef/sh404sef.class.php');
|
|
|
|
// 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 ($app->isSite())
|
|
{
|
|
$this->_hacks();
|
|
}
|
|
|
|
// security layer
|
|
if (!$app->isAdmin() && $sefConfig->shSecEnableSecurity)
|
|
{
|
|
require_once(JPATH_ROOT . '/components/com_sh404sef/shSec.php');
|
|
// do security checks
|
|
shDoSecurityChecks();
|
|
shCleanUpSecLogFiles(); // see setting in class file for clean up frequency
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
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();
|
|
$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);
|
|
|
|
// we use opposite setting from J!
|
|
$mode = 1 - $sefConfig->shRewriteMode;
|
|
JFactory::$config->set('sef_rewrite', $mode);
|
|
|
|
// 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'))
|
|
{
|
|
// 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();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* page rewriting features */
|
|
public function onAfterRender()
|
|
{
|
|
if (defined('SH404SEF_IS_RUNNING'))
|
|
{
|
|
if (JFactory::getApplication()->isAdmin())
|
|
{
|
|
if (version_compare(JVERSION, '3.0', 'ge'))
|
|
{
|
|
// are we on an edit page?
|
|
$option = JRequest::getCmd('option');
|
|
$view = JRequest::getCmd('view');
|
|
$layout = JRequest::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
|
|
$document = JFactory::getDocument();
|
|
if ($document->getType() == 'html')
|
|
{
|
|
$page = JResponse::getBody();
|
|
// insert css and js
|
|
$link = '';
|
|
if (strpos($page, 'media/plg_shlib/css/bs.css') === false)
|
|
{
|
|
$link .= '<link rel="stylesheet" href="' . JURI::root(true) . '/media/plg_shlib/css/bs.css" type="text/css" />';
|
|
}
|
|
if (strpos($page, 'media/plg_shlib/js/bs.js') === false)
|
|
{
|
|
$link .= "\n" . '<script src="' . JURI::root(true) . '/media/plg_shlib/js/bs.js" type="text/javascript" ></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;
|
|
}
|
|
|
|
$sefConfig = Sh404sefFactory::getConfig();
|
|
|
|
// return if no seo optim to perform
|
|
if ($sefConfig->shMetaManagementActivated || Sh404sefHelperAnalytics::isEnabled())
|
|
{
|
|
// go away if not enabled
|
|
$include = JPATH_ROOT . '/components/com_sh404sef/shPageRewrite.php';
|
|
require_once $include;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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()
|
|
{
|
|
// facebook: provide a channelUrl to like/Send buttons
|
|
$option = JRequest::getCmd('option', '');
|
|
$view = JRequest::getCmd('view', '');
|
|
$format = JRequest::getCmd('format', 'html');
|
|
|
|
if ($option == 'com_sh404sef' && $view == 'channelurl' && $format == 'raw')
|
|
{
|
|
// this is a request for the channelUrl
|
|
$langtag = JRequest::getCmd('langtag', 'en_GB');
|
|
$pageContent = '<script src="//connect.facebook.net/' . htmlspecialchars($langtag) . '/all.js"></script>';
|
|
if (!headers_sent())
|
|
{
|
|
$cacheExpire = 60 * 60 * 24 * 365;
|
|
header("Pragma: public");
|
|
header("Cache-Control: max-age=" . $cacheExpire);
|
|
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $cacheExpire) . ' GMT');
|
|
}
|
|
echo $pageContent;
|
|
jexit();
|
|
}
|
|
|
|
// multilingual hack: set cookie on request to homepage, as Joomla doesn't
|
|
// associate / with default language
|
|
$app = JFactory::getApplication();
|
|
if ($app->getLanguageFilter())
|
|
{
|
|
if ($format == 'html' && $this->_shouldSetHomePageLanguage($app))
|
|
{
|
|
$defaultLangCode = Sh404sefHelperLanguage::getDefaultLanguageTag();
|
|
Sh404sefHelperLanguage::setLanguageCookie($defaultLangCode, $this->getLangCookieTime());
|
|
// set the request var
|
|
$app->input->set('language', $defaultLangCode);
|
|
JRequest::setVar('language', $defaultLangCode);
|
|
// now set language object
|
|
JFactory::$language = JLanguage::getInstance($defaultLangCode, JFactory::getConfig()->get('debug_lang'));
|
|
$pageInfo = Sh404sefFactory::getPageInfo();
|
|
$pageInfo->setCurrentLanguage($defaultLangCode);
|
|
}
|
|
}
|
|
}
|
|
|
|
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 = str_replace($base, '', $pageInfo->currentSefUrl);
|
|
|
|
// 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()
|
|
{
|
|
// get Joomla autloader out
|
|
spl_autoload_unregister("__autoload");
|
|
|
|
// add our own
|
|
include JPATH_ADMINISTRATOR . '/components/com_sh404sef/helpers/autoloader.php';
|
|
$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);
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|