feat(deployed): add sh404SEF 4.4.4.1791 (no-source, vetted live)
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
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- $Id$ -->
|
||||
<access component="com_sh404sef">
|
||||
<section name="component">
|
||||
<action name="core.manage" title="JACTION_MANAGE" description="JACTION_MANAGE_COMPONENT_DESC" />
|
||||
</section>
|
||||
</access>
|
||||
@ -0,0 +1,77 @@
|
||||
<?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
|
||||
*/
|
||||
|
||||
// Security check to ensure this file is being included by a parent file.
|
||||
if (!defined('_JEXEC')) die('Direct Access to this location is not allowed.');
|
||||
|
||||
/**
|
||||
* Implement Joomsef installer
|
||||
*
|
||||
* @author shumisha
|
||||
*
|
||||
*/
|
||||
class Sh404sefAdapterAcesefinstaller extends Sh404sefClassBaseinstalladapter {
|
||||
|
||||
protected $_group = 'sh404sefextacesef';
|
||||
protected $_installType = 'acesef_ext';
|
||||
|
||||
/**
|
||||
* Fix Acesef manifest files, so that their
|
||||
* parameters can be displayed in a regular
|
||||
* plugin screen
|
||||
*
|
||||
*/
|
||||
protected function _fixManifest() {
|
||||
|
||||
jimport( 'joomla.filesystem.file');
|
||||
|
||||
// fix original files
|
||||
$source = $this->parent->getPath( 'source');
|
||||
$path = $source . '/' . $this->_getElement() . '.xml';
|
||||
$fileContent = JFile::read( $path);
|
||||
if(!empty( $fileContent)) {
|
||||
$fileContent = str_replace( 'group="sef"', '', $fileContent);
|
||||
$fileContent = str_replace( 'group="url"', '', $fileContent);
|
||||
// group="seo" is of no use for us, so leave it behind
|
||||
$written = JFile::write( $path, $fileContent);
|
||||
}
|
||||
|
||||
$path = $source . '/' . $this->_getElement() . '.php';
|
||||
$fileContent = JFile::read( $path);
|
||||
if(!empty( $fileContent)) {
|
||||
$defaults = array( '_check', '_sh_is_clever', '_viva_acesef', '_i_love_acesef', '_sh_loves_acesef', '_need_acesef', '_a_b_c_acesef');
|
||||
$remoteConfig = Sh404sefHelperUpdates::getRemoteConfig( $forced = false);
|
||||
$remotes = empty($remoteConfig->config['ace_prefixes']) ? array() : $remoteConfig->config['ace_prefixes'];
|
||||
$prefixes = array_unique( array_merge( $defaults, $remotes));
|
||||
foreach( $prefixes as $prefix) {
|
||||
$fileContent = preg_replace( '/function\s*' . preg_quote( $prefix) . '\s*\(\s*\)\s*\{/isU', 'function ' . $prefix . '() { return;', $fileContent);
|
||||
}
|
||||
// generic replace
|
||||
$defaultReplaces = array( array( 'source' => 'die;', 'target' => ''));
|
||||
$remoteReplaces = empty($remoteConfig->config['ace_replaces']) ? array() : $remoteConfig->config['ace_replaces'];
|
||||
$replaces = array_unique( array_merge( $defaultReplaces, $remoteReplaces));
|
||||
foreach( $replaces as $replace) {
|
||||
$fileContent = preg_replace( '/' . $replace['source'] . '/sU', $replace['target'], $fileContent);
|
||||
}
|
||||
|
||||
// write back manifest file
|
||||
$written = JFile::write( $path, $fileContent);
|
||||
}
|
||||
|
||||
// fix in memory object, by killing it, thus prompting recreation
|
||||
$manifest =& $this->parent->getManifest();
|
||||
$manifest = null;
|
||||
$manifest =& $this->parent->getManifest();
|
||||
$this->manifest =& $manifest->document;
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,371 @@
|
||||
<?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
|
||||
*/
|
||||
|
||||
// Security check to ensure this file is being included by a parent file.
|
||||
if (!defined('_JEXEC'))
|
||||
die('Direct Access to this location is not allowed.');
|
||||
|
||||
jimport('joomla.application.application');
|
||||
|
||||
/**
|
||||
* Implement Google analytics handling
|
||||
*
|
||||
* @author shumisha
|
||||
*
|
||||
*/
|
||||
class Sh404sefAdapterAnalyticsga extends Sh404sefClassBaseanalytics
|
||||
{
|
||||
protected $_endPoint = 'https://www.googleapis.com/analytics/v2.4/';
|
||||
protected $_authPoint = 'https://www.google.com/accounts/ClientLogin';
|
||||
protected $_appKeys = array('AIzaSyCW0BXHyqBdvk_pjjrQzUVn9TGGuSCOo8s', 'AIzaSyAA43aNDAI-G_hLc8ZuDboKcF63lFgBIOE',
|
||||
'AIzaSyBPE9Vv4RMn7j6lxY2ddp-Tz7M9ldALmgk');
|
||||
|
||||
// specific data
|
||||
protected $_SID = '';
|
||||
protected $_LSID = '';
|
||||
|
||||
/**
|
||||
* Get tracking snippet
|
||||
*
|
||||
*/
|
||||
public function getSnippet()
|
||||
{
|
||||
// should we insert tracking code snippet ?
|
||||
if (!$this->_shouldInsertSnippet())
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
//$config->analyticsType = 'uga';
|
||||
switch (Sh404sefFactory::getConfig()->analyticsEdition)
|
||||
{
|
||||
case 'ga':
|
||||
$snippet = $this->_getSnippetGa();
|
||||
break;
|
||||
case 'uga':
|
||||
$snippet = $this->_getSnippetUga();
|
||||
break;
|
||||
case 'ga_and_uga':
|
||||
$snippet = $this->_getSnippetGa() . "\n" . $this->_getSnippetUga();
|
||||
break;
|
||||
case 'gtm':
|
||||
$snippet = $this->_getSnippetGtm();
|
||||
break;
|
||||
default:
|
||||
$snippet = '';
|
||||
break;
|
||||
}
|
||||
|
||||
return $snippet . "\n";
|
||||
}
|
||||
/**
|
||||
* Get Classic Ananlytics tracking snippet
|
||||
*
|
||||
*/
|
||||
protected function _getSnippetGa()
|
||||
{
|
||||
// get config
|
||||
$config = Sh404sefFactory::getConfig();
|
||||
$pageInfo = Sh404sefFactory::getPageInfo();
|
||||
|
||||
// fire event so that plugin(s) attach custom vars
|
||||
$customVars = array();
|
||||
$dispatcher = ShlSystem_factory::dispatcher();
|
||||
$dispatcher->trigger('onShInsertAnalyticsSnippet', array(&$customVars, $config));
|
||||
|
||||
// in case of 404, we use a custom page url so that 404s can also be tracked in GA
|
||||
$customUrl = !empty($pageInfo->httpStatus) && $pageInfo->httpStatus == 404 ? "/__404__" : '';
|
||||
|
||||
$displayData = array();
|
||||
$displayData['tracking_code'] = trim($config->analyticsId);
|
||||
$displayData['custom_vars'] = $customVars;
|
||||
$displayData['custom_url'] = $customUrl;
|
||||
$displayData['anonymize'] = !empty($config->analyticsEnableAnonymization);
|
||||
|
||||
$snippet = ShlMvcLayout_Helper::render('com_sh404sef.analytics.snippet_ga', $displayData);
|
||||
|
||||
return $snippet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Universal Analytics tracking snippet
|
||||
*
|
||||
*/
|
||||
protected function _getSnippetUga()
|
||||
{
|
||||
// get config
|
||||
$config = Sh404sefFactory::getConfig();
|
||||
$pageInfo = Sh404sefFactory::getPageInfo();
|
||||
|
||||
// in case of 404, we use a custom page url so that 404s can also be tracked in GA
|
||||
$customUrl =!empty($pageInfo->httpStatus) && $pageInfo->httpStatus == 404 ? '/__404__' : '';
|
||||
|
||||
$displayData = array();
|
||||
$displayData['tracking_code'] = trim($config->analyticsUgaId);
|
||||
$displayData['custom_domain'] = 'auto';
|
||||
$displayData['custom_url'] = $customUrl;
|
||||
$displayData['anonymize'] = !empty($config->analyticsEnableAnonymization);
|
||||
|
||||
$snippet = ShlMvcLayout_Helper::render('com_sh404sef.analytics.snippet_uga', $displayData);
|
||||
|
||||
return $snippet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Google Tags manager snippet
|
||||
*
|
||||
*/
|
||||
protected function _getSnippetGtm()
|
||||
{
|
||||
// get config
|
||||
$config = Sh404sefFactory::getConfig();
|
||||
$pageInfo = Sh404sefFactory::getPageInfo();
|
||||
|
||||
$displayData = array();
|
||||
$displayData['tracking_code'] = trim($config->analyticsGtmId);
|
||||
|
||||
// finalize snippet : add user tracking code
|
||||
$snippet = ShlMvcLayout_Helper::render('com_sh404sef.analytics.snippet_gtm', $displayData);
|
||||
|
||||
return $snippet;
|
||||
}
|
||||
/**
|
||||
* Set client object to perform request
|
||||
* for connection to analytics service
|
||||
*
|
||||
*/
|
||||
protected function _prepareConnectRequest()
|
||||
{
|
||||
|
||||
$hClient = Sh404sefHelperAnalytics::getHttpClient();
|
||||
|
||||
// set params
|
||||
$hClient->setUri($this->_authPoint);
|
||||
$hClient->setConfig(array('maxredirects' => 0, 'timeout' => 10));
|
||||
|
||||
// request details
|
||||
$hClient->setMethod(Zendshl_Http_Client::POST);
|
||||
$hClient->setEncType('application/x-www-form-urlencoded');
|
||||
|
||||
// request data
|
||||
$postData = array('accountType' => 'GOOGLE', 'Email' => $this->_config->analyticsUser, 'Passwd' => $this->_config->analyticsPassword,
|
||||
'service' => 'analytics', 'source' => JFactory::getApplication()->getCfg('sitename') . '-sh404sef-' . $this->_config->version);
|
||||
|
||||
$hClient->setParameterPost($postData);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Handle response from connect request
|
||||
*
|
||||
*/
|
||||
protected function _handleConnectResponse($response)
|
||||
{
|
||||
|
||||
// check if authentified
|
||||
Sh404sefHelperAnalytics::verifyAuthResponse($response);
|
||||
|
||||
// we are authorized, collect Auth token from body
|
||||
$this->_extractAuthToken($response->getBody());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function _fetchAccountsList()
|
||||
{
|
||||
|
||||
$hClient = Sh404sefHelperAnalytics::getHttpClient();
|
||||
$hClient->resetParameters($clearAll = true);
|
||||
|
||||
// build the request
|
||||
$sefConfig = Sh404sefFactory::getConfig();
|
||||
$accountIdBits = explode('-', trim($sefConfig->analyticsId));
|
||||
if (empty($accountIdBits) || count($accountIdBits) < 3)
|
||||
{
|
||||
throw new Sh404sefExceptionDefault(JText::sprintf('COM_SH404SEF_ERROR_CHECKING_ANALYTICS', 'Invalid account Id'));
|
||||
}
|
||||
$accoundId = $accountIdBits[1];
|
||||
|
||||
// set target API url
|
||||
$hClient
|
||||
->setUri(
|
||||
$this->_endPoint . 'management/accounts/' . $accoundId . '/webproperties/' . trim($sefConfig->analyticsId) . '/profiles?key='
|
||||
. $this->_getAppKey());
|
||||
|
||||
// make sure we use GET
|
||||
$hClient->setMethod(Zendshl_Http_Client::GET);
|
||||
|
||||
// set headers required by Google Analytics
|
||||
$headers = array('GData-Version' => 2, 'Authorization' => 'GoogleLogin auth=' . $this->_Auth);
|
||||
|
||||
$hClient->setHeaders($headers);
|
||||
|
||||
//perform request
|
||||
// establish connection with available methods
|
||||
$adapters = array('Zendshl_Http_Client_Adapter_Curl', 'Zendshl_Http_Client_Adapter_Socket');
|
||||
$rawResponse = null;
|
||||
|
||||
// perform connect request
|
||||
foreach ($adapters as $adapter)
|
||||
{
|
||||
try
|
||||
{
|
||||
$hClient->setAdapter($adapter);
|
||||
$response = $hClient->request();
|
||||
break;
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
// we failed, let's try another method
|
||||
}
|
||||
}
|
||||
|
||||
// return if error
|
||||
if (empty($response))
|
||||
{
|
||||
$msg = 'unknown code';
|
||||
throw new Sh404sefExceptionDefault(JText::sprintf('COM_SH404SEF_ERROR_CHECKING_ANALYTICS', $msg));
|
||||
}
|
||||
if (empty($response) || !is_object($response) || $response->isError())
|
||||
{
|
||||
$msg = method_exists($response, 'getStatus') ? $response->getStatus() : 'unknown code';
|
||||
throw new Sh404sefExceptionDefault(JText::sprintf('COM_SH404SEF_ERROR_CHECKING_ANALYTICS', $msg));
|
||||
}
|
||||
|
||||
// analyze response
|
||||
// check if authentified
|
||||
Sh404sefHelperAnalytics::verifyAuthResponse($response);
|
||||
$xml = simplexml_load_string($response->getBody());
|
||||
|
||||
if (!empty($xml->entry))
|
||||
{
|
||||
foreach ($xml->entry as $entry)
|
||||
{
|
||||
$account = new StdClass();
|
||||
$bits = explode('/', (string) $entry->id);
|
||||
$account->id = array_pop($bits);
|
||||
$account->title = str_replace('Google Analytics Profile ', '', (string) $entry->title);
|
||||
$account->title = str_replace('Google Analytics View (Profile) ', '', $account->title);
|
||||
$this->_accounts[] = clone ($account);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* prepare html filters to allow user to select the way she likes
|
||||
* to view reports
|
||||
*/
|
||||
protected function _prepareFilters()
|
||||
{
|
||||
// array to hold various filters
|
||||
$filters = array();
|
||||
|
||||
// find if we must display all filters. On dashboard, only a reduced set
|
||||
$allFilters = $this->_options['showFilters'] == 'yes';
|
||||
|
||||
// select account to retrieve data for (or rather, profile
|
||||
$customSubmit = ' onchange="shSetupAnalytics({' . ($allFilters ? '' : 'showFilters:\'no\'') . '});"';
|
||||
|
||||
if (version_compare(JVERSION, '3.0', 'ge'))
|
||||
{
|
||||
$select = '<div class="btn-group">';
|
||||
$select .= Sh404sefHelperHtml::buildSelectList($this->_accounts, $this->_options['accountId'], 'accountId', $autoSubmit = false,
|
||||
$addSelectAll = false, $selectAllTitle = '', $customSubmit);
|
||||
$select .= '</div>';
|
||||
$filters[] = $select;
|
||||
|
||||
// dashboard only has account selection, no room for anything else
|
||||
// only shows main selection drop downs on analytics view
|
||||
if ($allFilters)
|
||||
{
|
||||
$select = '<div class="btn-group">';
|
||||
$select .= '<label for="startDate">' . JText::_('COM_SH404SEF_ANALYTICS_START_DATE') . '</label>';
|
||||
$select .= JHTML::_('calendar', $this->_options['startDate'], 'startDate', 'startDate', '%Y-%m-%d', array('class' => 'class="textinput"'));
|
||||
$select .= '</div>';
|
||||
$filters[] = $select;
|
||||
|
||||
$select = '<div class="btn-group">';
|
||||
$select .= '<label for="endDate">' . JText::_('COM_SH404SEF_ANALYTICS_END_DATE') . '</label>';
|
||||
$select .= JHTML::_('calendar', $this->_options['endDate'], 'endDate', 'endDate', '%Y-%m-%d', array('class' => 'class="textinput"'));
|
||||
$select .= '</div>';
|
||||
$filters[] = $select;
|
||||
|
||||
// select groupBy (day, week, month)
|
||||
$select = '<div class="btn-group">';
|
||||
$select .= '<label for="groupBy">' . JText::_('COM_SH404SEF_ANALYTICS_GROUP_BY') . '</label>';
|
||||
$select .= Sh404sefHelperAnalytics::buildAnalyticsGroupBySelectList($this->_options['groupBy'], 'groupBy', $autoSubmit = false,
|
||||
$addSelectAll = false, $selectAllTitle = '', $customSubmit);
|
||||
$select .= '</div>';
|
||||
$filters[] = $select;
|
||||
|
||||
// add a click to update link
|
||||
$filters[] = '<a class="btn btn-link" href="javascript: void(0);" onclick="javascript: shSetupAnalytics({forced:1'
|
||||
. ($allFilters ? '' : ',showFilters:\'no\'') . '});" > [' . JText::_('COM_SH404SEF_CHECK_ANALYTICS') . ']</a>';
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// on dashboard, there is no date select, so we must display the date range
|
||||
$filters[] = ' ' . JText::_('COM_SH404SEF_ANALYTICS_DATE_RANGE') . ' <div class="largertext">'
|
||||
. $this->_options['startDate'] . ' >> ' . $this->_options['endDate'] . '</div>';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$select = Sh404sefHelperHtml::buildSelectList($this->_accounts, $this->_options['accountId'], 'accountId', $autoSubmit = false,
|
||||
$addSelectAll = false, $selectAllTitle = '', $customSubmit);
|
||||
$filters[] = JText::_('COM_SH404SEF_ANALYTICS_ACCOUNT') . ': ' . $select;
|
||||
|
||||
// dashboard only has account selection, no room for anything else
|
||||
// only shows main selection drop downs on analytics view
|
||||
if ($allFilters)
|
||||
{
|
||||
// select start date
|
||||
$select = JHTML::_('calendar', $this->_options['startDate'], 'startDate', 'startDate', '%Y-%m-%d', array('class' => 'class="textinput"'));
|
||||
$filters[] = ' ' . JText::_('COM_SH404SEF_ANALYTICS_START_DATE') . ': ' . $select;
|
||||
|
||||
// select end date
|
||||
$select = JHTML::_('calendar', $this->_options['endDate'], 'endDate', 'endDate', '%Y-%m-%d', array('class' => 'class="textinput"'));
|
||||
$filters[] = ' ' . JText::_('COM_SH404SEF_ANALYTICS_END_DATE') . ': ' . $select;
|
||||
|
||||
// select groupBy (day, week, month)
|
||||
$select = Sh404sefHelperAnalytics::buildAnalyticsGroupBySelectList($this->_options['groupBy'], 'groupBy', $autoSubmit = false,
|
||||
$addSelectAll = false, $selectAllTitle = '', $customSubmit);
|
||||
$filters[] = ' ' . JText::_('COM_SH404SEF_ANALYTICS_GROUP_BY') . ': ' . $select;
|
||||
|
||||
// add a click to update link
|
||||
$filters[] = ' <a href="javascript: void(0);" onclick="javascript: shSetupAnalytics({forced:1'
|
||||
. ($allFilters ? '' : ',showFilters:\'no\'') . '});" > [' . JText::_('COM_SH404SEF_CHECK_ANALYTICS') . ']</a>';
|
||||
}
|
||||
else
|
||||
{
|
||||
// on dashboard, there is no date select, so we must display the date range
|
||||
$filters[] = ' ' . JText::_('COM_SH404SEF_ANALYTICS_DATE_RANGE') . ' <div class="largertext">'
|
||||
. $this->_options['startDate'] . ' >> ' . $this->_options['endDate'] . '</div>';
|
||||
}
|
||||
}
|
||||
// use layout to render
|
||||
return $filters;
|
||||
}
|
||||
|
||||
protected function _extractAuthToken($body)
|
||||
{
|
||||
$SID = explode('LSID=', $body);
|
||||
$this->_SID = trim($SID[0]);
|
||||
$this->_SID = ltrim($this->_SID, 'SID=');
|
||||
|
||||
$LSID = explode('Auth=', $SID[1]);
|
||||
$this->_LSID = trim($LSID[0]);
|
||||
|
||||
$this->_Auth = trim($LSID[1]);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,311 @@
|
||||
<?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
|
||||
*/
|
||||
|
||||
// Security check to ensure this file is being included by a parent file.
|
||||
if (!defined('_JEXEC')) die('Direct Access to this location is not allowed.');
|
||||
|
||||
/**
|
||||
* Implement wizard based exportation of pageids data
|
||||
*
|
||||
* @author shumisha
|
||||
*
|
||||
*/
|
||||
class Sh404sefAdapterExportaliases extends JObject {
|
||||
|
||||
/**
|
||||
* An array holding each step details
|
||||
* A step is defined as a task, a view and a layout
|
||||
* By default, task can be 'display', but still need
|
||||
* to be defined in array
|
||||
* @var array
|
||||
*/
|
||||
public $_stepsMap = array(
|
||||
|
||||
-2 => array( 'task' => 'doTerminate', 'view' => 'wizard', 'layout' => 'default')
|
||||
,-1 => array( 'task' => 'doCancel', 'view' => 'wizard', 'layout' => 'default')
|
||||
, 0 => array( 'task' => 'doStart', 'view' => 'wizard', 'layout' => 'default')
|
||||
, 1 => array( 'task' => 'doExport', 'view' => 'wizard', 'layout' => 'default')
|
||||
, 2 => array( 'task' => 'doDownload', 'view' => 'wizard', 'layout' => 'default')
|
||||
|
||||
);
|
||||
|
||||
public $_stepsCount = 0;
|
||||
public $_steps = array( 'next' => 0, 'previous' => 0, 'cancel' => -1, 'terminate' => -2);
|
||||
public $_button = '';
|
||||
public $_buttonsList = array ('next', 'previous', 'terminate', 'cancel');
|
||||
// visible buttons are displayed as toolbar pressbutton
|
||||
// buttons not on that list are passed as 'hidden' post data
|
||||
public $_visibleButtonsList = array ( 'previous', 'next', 'terminate', 'cancel');
|
||||
|
||||
protected $_context = 'aliases';
|
||||
protected $_total = 0;
|
||||
protected $_parentController = null;
|
||||
protected $_filename = '';
|
||||
|
||||
const MAX_PAGEIDS_PER_STEP = 100;
|
||||
|
||||
/**
|
||||
* Constructor, keep reference to controller
|
||||
* which called the adapter
|
||||
* @param unknown_type $parentController
|
||||
*/
|
||||
public function __construct( $parentController) {
|
||||
|
||||
$this->_parentController = $parentController;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameters for current adapter, to be used by parent controller
|
||||
*
|
||||
*/
|
||||
public function setup() {
|
||||
|
||||
$this->_stepsCount = count( $this->_stepsMap);
|
||||
|
||||
// prepare data for controller
|
||||
$properties = array();
|
||||
|
||||
$properties['_defaultController'] = 'wizard';
|
||||
$properties['_defaultTask'] = '';
|
||||
$properties['_defaultModel'] = '';
|
||||
$properties['_defaultView'] = 'wizard';
|
||||
$properties['_defaultLayout'] = 'default';
|
||||
|
||||
$properties['_returnController'] = 'aliases';
|
||||
$properties['_returnTask'] = '';
|
||||
$properties['_returnView'] = 'aliases';
|
||||
$properties['_returnLayout'] = 'default';
|
||||
$properties['_pageTitle'] = JText::_('COM_SH404SEF_EXPORTING_TITLE');
|
||||
|
||||
return $properties;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* First step, by default a message
|
||||
* and a Terminate button
|
||||
*
|
||||
*/
|
||||
public function doStart() {
|
||||
|
||||
// which button should be displayed ?
|
||||
$this->_visibleButtonsList = array ('next', 'cancel');
|
||||
|
||||
// next steps definition
|
||||
$this->_steps = array( 'next' => 1, 'previous' => 0, 'cancel' => -1, 'terminate' => -2);
|
||||
|
||||
// return results
|
||||
$result = array();
|
||||
|
||||
$result['mainText'] = JText::_('COM_SH404SEF_EXPORT_ALIASES_START');
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Second step, export data
|
||||
*
|
||||
*/
|
||||
public function doExport() {
|
||||
|
||||
// which button should be displayed ?
|
||||
$this->_visibleButtonsList = array ();
|
||||
|
||||
// A customiser :
|
||||
$this->_steps = array( 'next' => 1, 'previous' => 0, 'cancel' => -1, 'terminate' => -2);
|
||||
|
||||
// return results
|
||||
$result = array();
|
||||
|
||||
// exporting a limited set of pageids at a time
|
||||
$nextStart = JRequest::getInt( 'nextstart', 0);
|
||||
|
||||
// are we adding to an existing data file ?
|
||||
$this->_filename = Sh404sefHelperFiles::createFileName( $this->_filename, 'sh404sef_export_' . $this->_context);
|
||||
|
||||
// calculate number of items to export
|
||||
$model = ShlMvcModel_Base::getInstance( 'aliases', 'Sh404sefModel');
|
||||
$model->setContext( 'aliases.default');
|
||||
$options = (object) array('layout' => 'default', 'includeHomeData' => true);
|
||||
$this->_total = $model->getTotal( $options);
|
||||
|
||||
// do we have anything to export ?
|
||||
if (empty( $this->_total)) {
|
||||
$result['mainText'] = JText::_('COM_SH404SEF_NOTHING_TO_EXPORT');
|
||||
// which button should be displayed ?
|
||||
$this->_visibleButtonsList = array ('terminate');
|
||||
// next step so to trigger download, as file is ready now
|
||||
$this->_steps = array( 'next' => -2, 'previous' => 0, 'cancel' => -1, 'terminate' => -2);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
// get new start item
|
||||
if (empty( $nextStart)) {
|
||||
// this is first pass, starting from 0
|
||||
$start = 0;
|
||||
$nextStart = $start + self::MAX_PAGEIDS_PER_STEP;
|
||||
if ($nextStart >= $this->_total) {
|
||||
// reached the end
|
||||
$nextStart = $this->_total;
|
||||
}
|
||||
} else {
|
||||
$start = $nextStart;
|
||||
$nextStart = $start + self::MAX_PAGEIDS_PER_STEP;
|
||||
}
|
||||
|
||||
// are we done ? if so, move to next step
|
||||
$result['hiddenText'] = '';
|
||||
|
||||
if ($start >= $this->_total) {
|
||||
$result['mainText'] = JText::sprintf('COM_SH404SEF_EXPORT_DONE', $this->_total);
|
||||
$result['mainText'] .= $this->_getTerminateOptions();
|
||||
// which button should be displayed ?
|
||||
$this->_visibleButtonsList = array ('terminate');
|
||||
// next step so to trigger download, as file is ready now
|
||||
$this->_steps = array( 'next' => 2, 'previous' => 0, 'cancel' => -1, 'terminate' => -2);
|
||||
} else {
|
||||
|
||||
// actually export items
|
||||
$this->_export( $start);
|
||||
|
||||
// continuing for another round
|
||||
$result['mainText'] = JText::sprintf('COM_SH404SEF_EXPORT_EXPORTING', $start + 1, $this->_total);
|
||||
$result['hiddenText'] = '<input type="hidden" name="nextstart" value="'.$nextStart.'" />';
|
||||
$result['nextStart'] = $nextStart;
|
||||
}
|
||||
|
||||
$result['continue'] = array( 'task' => 'next', 'nextstart' => $nextStart, 'filename' => base64_encode($this->_filename));
|
||||
$result['continue'] = array_merge( $result['continue'], $this->_steps);
|
||||
$result['hiddenText'] .= '<input type="hidden" name="filename" value="'.$this->_filename.'" />';
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Last step, send results as download
|
||||
*
|
||||
*/
|
||||
public function doDownload() {
|
||||
|
||||
|
||||
// fake content
|
||||
$data = '';
|
||||
|
||||
// get the current filename with path
|
||||
$this->_filename = Sh404sefHelperFiles::createFileName( $this->_filename, 'sh404sef_export_' . $this->_context);
|
||||
|
||||
// get a more readable filename
|
||||
$displayName = date('Y-m-d') . '_' . $this->_context . '_export.txt';
|
||||
|
||||
Sh404sefHelperFiles::triggerDownload( $this->_filename, $displayName);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the wizard window and redirect to default page
|
||||
*
|
||||
*/
|
||||
public function doTerminate() {
|
||||
|
||||
// are we set to purge temporary files ?
|
||||
$purgeTempFiles = JRequest::getInt( 'purge_temp_files', 0);
|
||||
if (!empty($purgeTempFiles)) {
|
||||
Sh404sefHelperFiles::purgeTempFiles( 'sh404sef_export_' . $this->_context);
|
||||
}
|
||||
|
||||
// now go back to main page
|
||||
$result = array( 'redirectTo' => true);
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the wizard window and redirect to default page
|
||||
*
|
||||
*/
|
||||
public function doCancel() {
|
||||
|
||||
$result = array();
|
||||
$result['redirectTo'] = true;
|
||||
$result['redirectOptions'] = array( 'sh404sefMsg' => 'COM_SH404SEF_WIZARD_CANCELLED');
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
protected function _export( $start) {
|
||||
|
||||
// do we have a valid filename
|
||||
$this->_filename = Sh404sefHelperFiles::createFileName( $this->_filename, 'sh404sef_export_' . $this->_context);
|
||||
|
||||
// put some data in the file
|
||||
$end = $start + self::MAX_PAGEIDS_PER_STEP + 1;
|
||||
$end = $end > $this->_total ? $this->_total : $end;
|
||||
|
||||
// fetch pageIds record from model
|
||||
$model = ShlMvcModel_Base::getInstance( 'aliases', 'Sh404sefModel');
|
||||
$model->setContext( 'aliases.default');
|
||||
$options = (object) array('layout' => 'default', 'includeHomeData' => true);
|
||||
$records = $model->getList( $options, $returnZeroElement = false, $start, $forcedLimit = self::MAX_PAGEIDS_PER_STEP);
|
||||
|
||||
// do we need a header written to the file, for first record
|
||||
$header = $start == 0 ? Sh404sefHelperGeneral::getExportHeaders( $this->_context) . "\n" : '';
|
||||
|
||||
// format them for text file output
|
||||
$data = '';
|
||||
$counter = $start;
|
||||
$glue = Sh404sefHelperFiles::$stringDelimiter . Sh404sefHelperFiles::$fieldDelimiter . Sh404sefHelperFiles::$stringDelimiter;
|
||||
if (!empty( $records)) {
|
||||
foreach( $records as $record) {
|
||||
$counter++;
|
||||
if ($record->newurl == sh404SEF_HOMEPAGE_CODE) {
|
||||
$record->newurl = '__ Homepage __';
|
||||
}
|
||||
$textRecord = $record->alias . $glue . $record->oldurl . $glue . $record->newurl
|
||||
. $glue . $record->type
|
||||
. $glue . $record->hits . Sh404sefHelperFiles::$stringDelimiter
|
||||
;
|
||||
$line = Sh404sefHelperFiles::$stringDelimiter . $counter . $glue . $textRecord;
|
||||
$data .= $line . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// prepare data for storage
|
||||
if (!empty( $header)) {
|
||||
// first record written to file, prepend header
|
||||
$data = $header . $data;
|
||||
}
|
||||
|
||||
// store in file
|
||||
$status = Sh404sefHelperFiles::appendToFile( $this->_filename, $data);
|
||||
|
||||
// return any error
|
||||
return $status;
|
||||
|
||||
}
|
||||
|
||||
protected function _getTerminateOptions() {
|
||||
|
||||
$options = '<br /><br />';
|
||||
$options .= '<input type="checkbox" name="purge_temp_files" value="1" checked="checked" >';
|
||||
$options .= JText::_('COM_SH404SEF_PURGE_TEMP_FILES');
|
||||
$options .= '<br />';
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,364 @@
|
||||
<?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
|
||||
*/
|
||||
|
||||
// Security check to ensure this file is being included by a parent file.
|
||||
if (!defined('_JEXEC')) die('Direct Access to this location is not allowed.');
|
||||
|
||||
/**
|
||||
* Implement wizard based exportation of page title and metas data
|
||||
*
|
||||
* @author shumisha
|
||||
*
|
||||
*/
|
||||
class Sh404sefAdapterExportmetas extends JObject {
|
||||
|
||||
/**
|
||||
* An array holding each step details
|
||||
* A step is defined as a task, a view and a layout
|
||||
* By default, task can be 'display', but still need
|
||||
* to be defined in array
|
||||
* @var array
|
||||
*/
|
||||
public $_stepsMap = array(
|
||||
|
||||
-2 => array( 'task' => 'doTerminate', 'view' => 'wizard', 'layout' => 'default')
|
||||
,-1 => array( 'task' => 'doCancel', 'view' => 'wizard', 'layout' => 'default')
|
||||
, 0 => array( 'task' => 'doStart', 'view' => 'wizard', 'layout' => 'default')
|
||||
, 1 => array( 'task' => 'doExport', 'view' => 'wizard', 'layout' => 'default')
|
||||
, 2 => array( 'task' => 'doDownload', 'view' => 'wizard', 'layout' => 'default')
|
||||
|
||||
);
|
||||
|
||||
public $_stepsCount = 0;
|
||||
public $_steps = array( 'next' => 0, 'previous' => 0, 'cancel' => -1, 'terminate' => -2);
|
||||
public $_button = '';
|
||||
public $_buttonsList = array ('next', 'previous', 'terminate', 'cancel');
|
||||
// visible buttons are displayed as toolbar pressbutton
|
||||
// buttons not on that list are passed as 'hidden' post data
|
||||
public $_visibleButtonsList = array ( 'previous', 'next', 'terminate', 'cancel');
|
||||
|
||||
|
||||
protected $_context = 'metas';
|
||||
protected $_total = 0;
|
||||
protected $_parentController = null;
|
||||
protected $_filename = '';
|
||||
|
||||
const MAX_PAGEIDS_PER_STEP = 100;
|
||||
|
||||
/**
|
||||
* Constructor, keep reference to controller
|
||||
* which called the adapter
|
||||
* @param unknown_type $parentController
|
||||
*/
|
||||
public function __construct( $parentController) {
|
||||
|
||||
$this->_parentController = $parentController;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameters for current adapter, to be used by parent controller
|
||||
*
|
||||
*/
|
||||
public function setup() {
|
||||
|
||||
$this->_stepsCount = count( $this->_stepsMap);
|
||||
|
||||
// prepare data for controller
|
||||
$properties = array();
|
||||
|
||||
$properties['_defaultController'] = 'wizard';
|
||||
$properties['_defaultTask'] = '';
|
||||
$properties['_defaultModel'] = '';
|
||||
$properties['_defaultView'] = 'wizard';
|
||||
$properties['_defaultLayout'] = 'default';
|
||||
|
||||
$properties['_returnController'] = 'metas';
|
||||
$properties['_returnTask'] = '';
|
||||
$properties['_returnView'] = 'metas';
|
||||
$properties['_returnLayout'] = 'default';
|
||||
$properties['_pageTitle'] = JText::_('COM_SH404SEF_EXPORTING_TITLE');
|
||||
|
||||
return $properties;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* First step, by default a message
|
||||
* and a Terminate button
|
||||
*
|
||||
*/
|
||||
public function doStart() {
|
||||
|
||||
// which button should be displayed ?
|
||||
$this->_visibleButtonsList = array ('next', 'cancel');
|
||||
|
||||
// A customiser :
|
||||
$this->_steps = array( 'next' => 1, 'previous' => 0, 'cancel' => -1, 'terminate' => -2);
|
||||
|
||||
// return results
|
||||
$result = array();
|
||||
|
||||
$result['mainText'] = JText::_('COM_SH404SEF_EXPORT_METAS_START');
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Second step, export data
|
||||
*
|
||||
*/
|
||||
public function doExport() {
|
||||
|
||||
// which button should be displayed ?
|
||||
$this->_visibleButtonsList = array ();
|
||||
|
||||
// next steps definition
|
||||
$this->_steps = array( 'next' => 1, 'previous' => 0, 'cancel' => -1, 'terminate' => -2);
|
||||
|
||||
// return results
|
||||
$result = array();
|
||||
|
||||
// exporting a limited set of pageids at a time
|
||||
$nextStart = JRequest::getInt( 'nextstart', 0);
|
||||
|
||||
// are we adding to an existing data file ?
|
||||
$this->_filename = Sh404sefHelperFiles::createFileName( $this->_filename, 'sh404sef_export_' . $this->_context);
|
||||
|
||||
// calculate number of items to export
|
||||
$model = ShlMvcModel_Base::getInstance( 'urls', 'Sh404sefModel');
|
||||
$model->setContext( 'metas.default');
|
||||
$options = (object) array('layout' => 'export', 'getMetaData' => true, 'onlyWithMetaData' => true, 'simpleUrlList' => true);
|
||||
$this->_total = $model->getTotal( $options);
|
||||
|
||||
// do we have anything to export ?
|
||||
if (empty( $this->_total)) {
|
||||
$result['mainText'] = JText::_('COM_SH404SEF_NOTHING_TO_EXPORT');
|
||||
// which button should be displayed ?
|
||||
$this->_visibleButtonsList = array ('terminate');
|
||||
// next step so to trigger download, as file is ready now
|
||||
$this->_steps = array( 'next' => -2, 'previous' => 0, 'cancel' => -1, 'terminate' => -2);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
// get new start item
|
||||
if (empty( $nextStart)) {
|
||||
// this is first pass, starting from 0
|
||||
$start = 0;
|
||||
$nextStart = $start + self::MAX_PAGEIDS_PER_STEP;
|
||||
if ($nextStart >= $this->_total) {
|
||||
// reached the end
|
||||
$nextStart = $this->_total;
|
||||
}
|
||||
} else {
|
||||
$start = $nextStart;
|
||||
$nextStart = $start + self::MAX_PAGEIDS_PER_STEP;
|
||||
}
|
||||
|
||||
// are we done ? if so, move to next step
|
||||
$result['hiddenText'] = '';
|
||||
|
||||
if ($start >= $this->_total) {
|
||||
// append homepage data. Hard to put them at the beginning, that'd make the whole counting logic more complex
|
||||
$this->_exportHomePageData( $this->_total);
|
||||
|
||||
// keep going to terminate page
|
||||
$result['mainText'] = JText::sprintf('COM_SH404SEF_EXPORT_DONE', $this->_total);
|
||||
$result['mainText'] .= $this->_getTerminateOptions();
|
||||
// which button should be displayed ?
|
||||
$this->_visibleButtonsList = array ('terminate');
|
||||
// next step so to trigger download, as file is ready now
|
||||
$this->_steps = array( 'next' => 2, 'previous' => 0, 'cancel' => -1, 'terminate' => -2);
|
||||
} else {
|
||||
|
||||
// actually export items
|
||||
$this->_export( $start);
|
||||
|
||||
// continuing for another round
|
||||
$result['mainText'] = JText::sprintf('COM_SH404SEF_EXPORT_EXPORTING', $start + 1, $this->_total);
|
||||
$result['hiddenText'] = '<input type="hidden" name="nextstart" value="'.$nextStart.'" />';
|
||||
$result['nextStart'] = $nextStart;
|
||||
}
|
||||
|
||||
$result['continue'] = array( 'task' => 'next', 'nextstart' => $nextStart, 'filename' => base64_encode($this->_filename));
|
||||
$result['continue'] = array_merge( $result['continue'], $this->_steps);
|
||||
$result['hiddenText'] .= '<input type="hidden" name="filename" value="'.$this->_filename.'" />';
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Last step, send results as download
|
||||
*
|
||||
*/
|
||||
public function doDownload() {
|
||||
|
||||
|
||||
// fake content
|
||||
$data = '';
|
||||
|
||||
// get the current filename with path
|
||||
$this->_filename = Sh404sefHelperFiles::createFileName( $this->_filename, 'sh404sef_export_' . $this->_context);
|
||||
|
||||
// get a more readable filename
|
||||
$displayName = date('Y-m-d') . '_' . $this->_context . '_export.txt';
|
||||
|
||||
Sh404sefHelperFiles::triggerDownload( $this->_filename, $displayName);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the wizard window and redirect to default page
|
||||
*
|
||||
*/
|
||||
public function doTerminate() {
|
||||
|
||||
// are we set to purge temporary files ?
|
||||
$purgeTempFiles = JRequest::getInt( 'purge_temp_files', 0);
|
||||
if (!empty($purgeTempFiles)) {
|
||||
Sh404sefHelperFiles::purgeTempFiles( 'sh404sef_export_' . $this->_context);
|
||||
}
|
||||
|
||||
// now go back to main page
|
||||
$result = array( 'redirectTo' => true);
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the wizard window and redirect to default page
|
||||
*
|
||||
*/
|
||||
public function doCancel() {
|
||||
|
||||
$result = array();
|
||||
$result['redirectTo'] = true;
|
||||
$result['redirectOptions'] = array( 'sh404sefMsg' => 'COM_SH404SEF_WIZARD_CANCELLED');
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
protected function _export( $start) {
|
||||
|
||||
// do we have a valid filename
|
||||
$this->_filename = Sh404sefHelperFiles::createFileName( $this->_filename, 'sh404sef_export_' . $this->_context);
|
||||
|
||||
// put some data in the file
|
||||
$end = $start + self::MAX_PAGEIDS_PER_STEP + 1;
|
||||
$end = $end > $this->_total ? $this->_total : $end;
|
||||
|
||||
// fetch pageIds record from model
|
||||
$model = ShlMvcModel_Base::getInstance( 'urls', 'Sh404sefModel');
|
||||
$model->setContext( 'metas.default');
|
||||
$options = (object) array('layout' => 'export', 'getMetaData' => true, 'onlyWithMetaData' => true, 'simpleUrlList' => true);
|
||||
$records = $model->getList( $options, $returnZeroElement = false, $start, $forcedLimit = self::MAX_PAGEIDS_PER_STEP);
|
||||
|
||||
// do we need a header written to the file, for first record
|
||||
$header = $start == 0 ? Sh404sefHelperGeneral::getExportHeaders( $this->_context) . "\n" : '';
|
||||
|
||||
// format them for text file output
|
||||
$data = '';
|
||||
$counter = $start;
|
||||
$glue = Sh404sefHelperFiles::$stringDelimiter . Sh404sefHelperFiles::$fieldDelimiter . Sh404sefHelperFiles::$stringDelimiter;
|
||||
if (!empty( $records)) {
|
||||
foreach( $records as $record) {
|
||||
$counter++;
|
||||
$data .= $this->_createLine( $record, $counter, $glue);
|
||||
}
|
||||
}
|
||||
|
||||
// prepare data for storage
|
||||
if (!empty( $header)) {
|
||||
// first record written to file, prepend header
|
||||
$data = $header . $data;
|
||||
}
|
||||
|
||||
// store in file
|
||||
$status = Sh404sefHelperFiles::appendToFile( $this->_filename, $data);
|
||||
|
||||
// return any error
|
||||
return $status;
|
||||
|
||||
}
|
||||
|
||||
protected function _exportHomePageData( $start) {
|
||||
|
||||
// get data from model
|
||||
$model = ShlMvcModel_Base::getInstance( 'metas', 'Sh404sefModel');
|
||||
$model->setContext( 'metas.default');
|
||||
$options = (object) array('layout' => 'default', 'newurl' => sh404SEF_HOMEPAGE_CODE);
|
||||
$this->_total += 1;
|
||||
$homePageMetaData = $model->getList( $options);
|
||||
|
||||
// format them for text file output
|
||||
$data = '';
|
||||
$counter = $start;
|
||||
$glue = Sh404sefHelperFiles::$stringDelimiter . Sh404sefHelperFiles::$fieldDelimiter . Sh404sefHelperFiles::$stringDelimiter;
|
||||
if (!empty( $homePageMetaData)) {
|
||||
foreach( $homePageMetaData as $record) {
|
||||
$counter++;
|
||||
$record->oldurl = '';
|
||||
$record->newurl = '__ Homepage __';
|
||||
$data .= $this->_createLine( $record, $counter, $glue);
|
||||
}
|
||||
}
|
||||
|
||||
// prepare data for storage
|
||||
if (!empty( $header)) {
|
||||
// first record written to file, prepend header
|
||||
$data = $header . $data;
|
||||
}
|
||||
|
||||
// store in file
|
||||
$status = Sh404sefHelperFiles::appendToFile( $this->_filename, $data);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an export file line, based on db record
|
||||
*
|
||||
* @param $record the data coming from DB
|
||||
* @param $counter, running counter
|
||||
* @param $glue , glue string between elements of records
|
||||
*/
|
||||
protected function _createLine( $record, $counter, $glue) {
|
||||
|
||||
$textRecord = $record->oldurl . $glue . $record->newurl
|
||||
. $glue . (empty( $record->cpt) ? 0 : $record->cpt)
|
||||
. $glue . (empty( $record->rank) ? 0 : $record->rank)
|
||||
. $glue . (!isset( $record->dateadd) ? '0000-00-00' : $record->dateadd)
|
||||
. $glue . Sh404sefHelperFiles::csvQuote( $record->metatitle)
|
||||
. $glue . Sh404sefHelperFiles::csvQuote( $record->metadesc)
|
||||
. $glue . Sh404sefHelperFiles::csvQuote( $record->metakey)
|
||||
. $glue . Sh404sefHelperFiles::csvQuote( $record->metalang)
|
||||
. $glue . Sh404sefHelperFiles::csvQuote( $record->metarobots) . Sh404sefHelperFiles::$stringDelimiter;
|
||||
$line = Sh404sefHelperFiles::$stringDelimiter . $counter . $glue . $textRecord . "\n";
|
||||
|
||||
return $line;
|
||||
}
|
||||
|
||||
protected function _getTerminateOptions() {
|
||||
|
||||
$options = '<br /><br />';
|
||||
$options .= '<input type="checkbox" name="purge_temp_files" value="1" checked="checked" >';
|
||||
$options .= JText::_('COM_SH404SEF_PURGE_TEMP_FILES');
|
||||
$options .= '<br />';
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,308 @@
|
||||
<?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
|
||||
*/
|
||||
|
||||
// Security check to ensure this file is being included by a parent file.
|
||||
if (!defined('_JEXEC')) die('Direct Access to this location is not allowed.');
|
||||
|
||||
/**
|
||||
* Implement wizard based exportation of pageids data
|
||||
*
|
||||
* @author shumisha
|
||||
*
|
||||
*/
|
||||
class Sh404sefAdapterExportpageids extends JObject {
|
||||
|
||||
/**
|
||||
* An array holding each step details
|
||||
* A step is defined as a task, a view and a layout
|
||||
* By default, task can be 'display', but still need
|
||||
* to be defined in array
|
||||
* @var array
|
||||
*/
|
||||
public $_stepsMap = array(
|
||||
|
||||
-2 => array( 'task' => 'doTerminate', 'view' => 'wizard', 'layout' => 'default')
|
||||
,-1 => array( 'task' => 'doCancel', 'view' => 'wizard', 'layout' => 'default')
|
||||
, 0 => array( 'task' => 'doStart', 'view' => 'wizard', 'layout' => 'default')
|
||||
, 1 => array( 'task' => 'doExport', 'view' => 'wizard', 'layout' => 'default')
|
||||
, 2 => array( 'task' => 'doDownload', 'view' => 'wizard', 'layout' => 'default')
|
||||
|
||||
);
|
||||
|
||||
public $_stepsCount = 0;
|
||||
public $_steps = array( 'next' => 0, 'previous' => 0, 'cancel' => -1, 'terminate' => -2);
|
||||
public $_button = '';
|
||||
public $_buttonsList = array ('next', 'previous', 'terminate', 'cancel');
|
||||
// visible buttons are displayed as toolbar pressbutton
|
||||
// buttons not on that list are passed as 'hidden' post data
|
||||
public $_visibleButtonsList = array ( 'previous', 'next', 'terminate', 'cancel');
|
||||
|
||||
protected $_context = 'pageids';
|
||||
protected $_total = 0;
|
||||
protected $_parentController = null;
|
||||
protected $_filename = '';
|
||||
|
||||
const MAX_PAGEIDS_PER_STEP = 100;
|
||||
|
||||
/**
|
||||
* Constructor, keep reference to controller
|
||||
* which called the adapter
|
||||
* @param unknown_type $parentController
|
||||
*/
|
||||
public function __construct( $parentController) {
|
||||
|
||||
$this->_parentController = $parentController;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameters for current adapter, to be used by parent controller
|
||||
*
|
||||
*/
|
||||
public function setup() {
|
||||
|
||||
$this->_stepsCount = count( $this->_stepsMap);
|
||||
|
||||
// prepare data for controller
|
||||
$properties = array();
|
||||
|
||||
$properties['_defaultController'] = 'wizard';
|
||||
$properties['_defaultTask'] = '';
|
||||
$properties['_defaultModel'] = '';
|
||||
$properties['_defaultView'] = 'wizard';
|
||||
$properties['_defaultLayout'] = 'default';
|
||||
|
||||
$properties['_returnController'] = 'pageids';
|
||||
$properties['_returnTask'] = '';
|
||||
$properties['_returnView'] = 'pageids';
|
||||
$properties['_returnLayout'] = 'default';
|
||||
$properties['_pageTitle'] = JText::_('COM_SH404SEF_EXPORTING_TITLE');
|
||||
|
||||
return $properties;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* First step, by default a message
|
||||
* and a Terminate button
|
||||
*
|
||||
*/
|
||||
public function doStart() {
|
||||
|
||||
// which button should be displayed ?
|
||||
$this->_visibleButtonsList = array ('next', 'cancel');
|
||||
|
||||
// next steps definition
|
||||
$this->_steps = array( 'next' => 1, 'previous' => 0, 'cancel' => -1, 'terminate' => -2);
|
||||
|
||||
// return results
|
||||
$result = array();
|
||||
|
||||
$result['mainText'] = JText::_('COM_SH404SEF_EXPORT_PAGEIDS_START');
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Second step, export data
|
||||
*
|
||||
*/
|
||||
public function doExport() {
|
||||
|
||||
// which button should be displayed ?
|
||||
$this->_visibleButtonsList = array ();
|
||||
|
||||
// next steps definition
|
||||
$this->_steps = array( 'next' => 1, 'previous' => 0, 'cancel' => -1, 'terminate' => -2);
|
||||
|
||||
// return results
|
||||
$result = array();
|
||||
|
||||
// exporting a limited set of pageids at a time
|
||||
$nextStart = JRequest::getInt( 'nextstart', 0);
|
||||
|
||||
// are we adding to an existing data file ?
|
||||
$this->_filename = Sh404sefHelperFiles::createFileName( $this->_filename, 'sh404sef_export_' . $this->_context);
|
||||
|
||||
// calculate number of items to export
|
||||
$model = ShlMvcModel_Base::getInstance( 'urls', 'Sh404sefModel');
|
||||
$model->setContext( 'pageids.default');
|
||||
$options = (object) array('layout' => 'default', 'getPageId' => true, 'simpleUrlList' => true,);
|
||||
$this->_total = $model->getTotal( $options);
|
||||
|
||||
// do we have anything to export ?
|
||||
if (empty( $this->_total)) {
|
||||
$result['mainText'] = JText::_('COM_SH404SEF_NOTHING_TO_EXPORT');
|
||||
// which button should be displayed ?
|
||||
$this->_visibleButtonsList = array ('terminate');
|
||||
// next step so to trigger download, as file is ready now
|
||||
$this->_steps = array( 'next' => -2, 'previous' => 0, 'cancel' => -1, 'terminate' => -2);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
// get new start item
|
||||
if (empty( $nextStart)) {
|
||||
// this is first pass, starting from 0
|
||||
$start = 0;
|
||||
$nextStart = $start + self::MAX_PAGEIDS_PER_STEP;
|
||||
if ($nextStart >= $this->_total) {
|
||||
// reached the end
|
||||
$nextStart = $this->_total;
|
||||
}
|
||||
} else {
|
||||
$start = $nextStart;
|
||||
$nextStart = $start + self::MAX_PAGEIDS_PER_STEP;
|
||||
}
|
||||
|
||||
// are we done ? if so, move to next step
|
||||
$result['hiddenText'] = '';
|
||||
|
||||
if ($start >= $this->_total) {
|
||||
$result['mainText'] = JText::sprintf('COM_SH404SEF_EXPORT_DONE', $this->_total);
|
||||
$result['mainText'] .= $this->_getTerminateOptions();
|
||||
|
||||
// which button should be displayed ?
|
||||
$this->_visibleButtonsList = array ('terminate');
|
||||
// next step so to trigger download, as file is ready now
|
||||
$this->_steps = array( 'next' => 2, 'previous' => 0, 'cancel' => -1, 'terminate' => -2);
|
||||
} else {
|
||||
|
||||
// actually export items
|
||||
$this->_export( $start);
|
||||
|
||||
// continuing for another round
|
||||
$result['mainText'] = JText::sprintf('COM_SH404SEF_EXPORT_EXPORTING', $start + 1, $this->_total);
|
||||
$result['hiddenText'] = '<input type="hidden" name="nextstart" value="'.$nextStart.'" />';
|
||||
$result['nextStart'] = $nextStart;
|
||||
}
|
||||
|
||||
$result['continue'] = array( 'task' => 'next', 'nextstart' => $nextStart, 'filename' => base64_encode($this->_filename));
|
||||
$result['continue'] = array_merge( $result['continue'], $this->_steps);
|
||||
$result['hiddenText'] .= '<input type="hidden" name="filename" value="'.$this->_filename.'" />';
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Last step, send results as download
|
||||
*
|
||||
*/
|
||||
public function doDownload() {
|
||||
|
||||
|
||||
// fake content
|
||||
$data = '';
|
||||
|
||||
// get the current filename with path
|
||||
$this->_filename = Sh404sefHelperFiles::createFileName( $this->_filename, 'sh404sef_export_' . $this->_context);
|
||||
|
||||
// get a more readable filename
|
||||
$displayName = date('Y-m-d') . '_' . $this->_context . '_export.txt';
|
||||
|
||||
Sh404sefHelperFiles::triggerDownload( $this->_filename, $displayName);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the wizard window and redirect to default page
|
||||
*
|
||||
*/
|
||||
public function doTerminate() {
|
||||
|
||||
// are we set to purge temporary files ?
|
||||
$purgeTempFiles = JRequest::getInt( 'purge_temp_files', 0);
|
||||
if (!empty($purgeTempFiles)) {
|
||||
Sh404sefHelperFiles::purgeTempFiles( 'sh404sef_export_' . $this->_context);
|
||||
}
|
||||
|
||||
// now go back to main page
|
||||
$result = array( 'redirectTo' => true);
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the wizard window and redirect to default page
|
||||
*
|
||||
*/
|
||||
public function doCancel() {
|
||||
|
||||
$result = array();
|
||||
$result['redirectTo'] = true;
|
||||
$result['redirectOptions'] = array( 'sh404sefMsg' => 'COM_SH404SEF_WIZARD_CANCELLED');
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
protected function _export( $start) {
|
||||
|
||||
// do we have a valid filename
|
||||
$this->_filename = Sh404sefHelperFiles::createFileName( $this->_filename, 'sh404sef_export_' . $this->_context);
|
||||
|
||||
// put some data in the file
|
||||
$end = $start + self::MAX_PAGEIDS_PER_STEP + 1;
|
||||
$end = $end > $this->_total ? $this->_total : $end;
|
||||
|
||||
// fetch pageIds record from model
|
||||
$model = ShlMvcModel_Base::getInstance( 'urls', 'Sh404sefModel');
|
||||
$model->setContext( 'pageids.default');
|
||||
$options = (object) array('layout' => 'default', 'getPageId' => true, 'simpleUrlList' => true);
|
||||
$records = $model->getList( $options, $returnZeroElement = false, $start, $forcedLimit = self::MAX_PAGEIDS_PER_STEP);
|
||||
|
||||
// do we need a header written to the file, for first record
|
||||
$header = $start == 0 ? Sh404sefHelperGeneral::getExportHeaders( $this->_context) . "\n" : '';
|
||||
|
||||
// format them for text file output
|
||||
$data = '';
|
||||
$counter = $start;
|
||||
$glue = Sh404sefHelperFiles::$stringDelimiter . Sh404sefHelperFiles::$fieldDelimiter . Sh404sefHelperFiles::$stringDelimiter;
|
||||
if (!empty( $records)) {
|
||||
foreach( $records as $record) {
|
||||
$counter++;
|
||||
$textRecord = $record->pageid . $glue . $record->oldurl . $glue . $record->nonsefurl
|
||||
. $glue . $record->pageidtype
|
||||
. $glue . $record->pageidhits . Sh404sefHelperFiles::$stringDelimiter
|
||||
;
|
||||
$line = Sh404sefHelperFiles::$stringDelimiter . $counter . $glue . $textRecord;
|
||||
$data .= $line . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// prepare data for storage
|
||||
if (!empty( $header)) {
|
||||
// first record written to file, prepend header
|
||||
$data = $header . $data;
|
||||
}
|
||||
|
||||
// store in file
|
||||
$status = Sh404sefHelperFiles::appendToFile( $this->_filename, $data);
|
||||
|
||||
// return any error
|
||||
return $status;
|
||||
|
||||
}
|
||||
|
||||
protected function _getTerminateOptions() {
|
||||
|
||||
$options = '<br /><br />';
|
||||
$options .= '<input type="checkbox" name="purge_temp_files" value="1" checked="checked" >';
|
||||
$options .= JText::_('COM_SH404SEF_PURGE_TEMP_FILES');
|
||||
$options .= '<br />';
|
||||
|
||||
return $options;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,317 @@
|
||||
<?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
|
||||
*/
|
||||
|
||||
// Security check to ensure this file is being included by a parent file.
|
||||
if (!defined('_JEXEC')) die('Direct Access to this location is not allowed.');
|
||||
|
||||
/**
|
||||
* Implement wizard based exportation of page title and metas data
|
||||
*
|
||||
* @author shumisha
|
||||
*
|
||||
*/
|
||||
class Sh404sefAdapterExporturls extends JObject {
|
||||
|
||||
/**
|
||||
* An array holding each step details
|
||||
* A step is defined as a task, a view and a layout
|
||||
* By default, task can be 'display', but still need
|
||||
* to be defined in array
|
||||
* @var array
|
||||
*/
|
||||
public $_stepsMap = array(
|
||||
|
||||
-2 => array( 'task' => 'doTerminate', 'view' => 'wizard', 'layout' => 'default')
|
||||
,-1 => array( 'task' => 'doCancel', 'view' => 'wizard', 'layout' => 'default')
|
||||
, 0 => array( 'task' => 'doStart', 'view' => 'wizard', 'layout' => 'default')
|
||||
, 1 => array( 'task' => 'doExport', 'view' => 'wizard', 'layout' => 'default')
|
||||
, 2 => array( 'task' => 'doDownload', 'view' => 'wizard', 'layout' => 'default')
|
||||
|
||||
);
|
||||
|
||||
public $_stepsCount = 0;
|
||||
public $_steps = array( 'next' => 0, 'previous' => 0, 'cancel' => -1, 'terminate' => -2);
|
||||
public $_button = '';
|
||||
public $_buttonsList = array ('next', 'previous', 'terminate', 'cancel');
|
||||
// visible buttons are displayed as toolbar pressbutton
|
||||
// buttons not on that list are passed as 'hidden' post data
|
||||
public $_visibleButtonsList = array ( 'previous', 'next', 'terminate', 'cancel');
|
||||
|
||||
|
||||
protected $_context = 'urls';
|
||||
protected $_total = 0;
|
||||
protected $_parentController = null;
|
||||
protected $_filename = '';
|
||||
|
||||
const MAX_PAGEIDS_PER_STEP = 100;
|
||||
|
||||
/**
|
||||
* Constructor, keep reference to controller
|
||||
* which called the adapter
|
||||
* @param unknown_type $parentController
|
||||
*/
|
||||
public function __construct( $parentController) {
|
||||
|
||||
$this->_parentController = $parentController;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameters for current adapter, to be used by parent controller
|
||||
*
|
||||
*/
|
||||
public function setup() {
|
||||
|
||||
$this->_stepsCount = count( $this->_stepsMap);
|
||||
|
||||
// prepare data for controller
|
||||
$properties = array();
|
||||
|
||||
$properties['_defaultController'] = 'wizard';
|
||||
$properties['_defaultTask'] = '';
|
||||
$properties['_defaultModel'] = '';
|
||||
$properties['_defaultView'] = 'wizard';
|
||||
$properties['_defaultLayout'] = 'default';
|
||||
|
||||
$properties['_returnController'] = 'urls';
|
||||
$properties['_returnTask'] = '';
|
||||
$properties['_returnView'] = 'urls';
|
||||
$properties['_returnLayout'] = 'default';
|
||||
$properties['_pageTitle'] = JText::_('COM_SH404SEF_EXPORTING_TITLE');
|
||||
|
||||
return $properties;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* First step, by default a message
|
||||
* and a Terminate button
|
||||
*
|
||||
*/
|
||||
public function doStart() {
|
||||
|
||||
// which button should be displayed ?
|
||||
$this->_visibleButtonsList = array ('next', 'cancel');
|
||||
|
||||
// A customiser :
|
||||
$this->_steps = array( 'next' => 1, 'previous' => 0, 'cancel' => -1, 'terminate' => -2);
|
||||
|
||||
// return results
|
||||
$result = array();
|
||||
|
||||
$result['mainText'] = JText::_('COM_SH404SEF_EXPORT_URLS_START');
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Second step, export data
|
||||
*
|
||||
*/
|
||||
public function doExport() {
|
||||
|
||||
// which button should be displayed ?
|
||||
$this->_visibleButtonsList = array ();
|
||||
|
||||
// next steps definition
|
||||
$this->_steps = array( 'next' => 1, 'previous' => 0, 'cancel' => -1, 'terminate' => -2);
|
||||
|
||||
// return results
|
||||
$result = array();
|
||||
|
||||
// exporting a limited set of pageids at a time
|
||||
$nextStart = JRequest::getInt( 'nextstart', 0);
|
||||
|
||||
// are we adding to an existing data file ?
|
||||
$this->_filename = Sh404sefHelperFiles::createFileName( $this->_filename, 'sh404sef_export_' . $this->_context);
|
||||
|
||||
// calculate number of items to export
|
||||
$model = ShlMvcModel_Base::getInstance( 'urls', 'Sh404sefModel');
|
||||
$model->setContext( 'urls.default');
|
||||
$options = (object) array('layout' => 'export', 'getMetaData' => true, 'simpleUrlList' => true);
|
||||
$this->_total = $model->getTotal( $options);
|
||||
|
||||
// do we have anything to export ?
|
||||
if (empty( $this->_total)) {
|
||||
$result['mainText'] = JText::_('COM_SH404SEF_NOTHING_TO_EXPORT');
|
||||
// which button should be displayed ?
|
||||
$this->_visibleButtonsList = array ('terminate');
|
||||
// next step so to trigger download, as file is ready now
|
||||
$this->_steps = array( 'next' => -2, 'previous' => 0, 'cancel' => -1, 'terminate' => -2);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
// get new start item
|
||||
if (empty( $nextStart)) {
|
||||
// this is first pass, starting from 0
|
||||
$start = 0;
|
||||
$nextStart = $start + self::MAX_PAGEIDS_PER_STEP;
|
||||
if ($nextStart >= $this->_total) {
|
||||
// reached the end
|
||||
$nextStart = $this->_total;
|
||||
}
|
||||
} else {
|
||||
$start = $nextStart;
|
||||
$nextStart = $start + self::MAX_PAGEIDS_PER_STEP;
|
||||
}
|
||||
|
||||
// are we done ? if so, move to next step
|
||||
$result['hiddenText'] = '';
|
||||
|
||||
if ($start >= $this->_total) {
|
||||
$result['mainText'] = JText::sprintf('COM_SH404SEF_EXPORT_DONE', $this->_total);
|
||||
$result['mainText'] .= $this->_getTerminateOptions();
|
||||
|
||||
// which button should be displayed ?
|
||||
$this->_visibleButtonsList = array ('terminate');
|
||||
// next step so to trigger download, as file is ready now
|
||||
$this->_steps = array( 'next' => 2, 'previous' => 0, 'cancel' => -1, 'terminate' => -2);
|
||||
} else {
|
||||
|
||||
// actually export items
|
||||
$this->_export( $start);
|
||||
|
||||
// continuing for another round
|
||||
$result['mainText'] = JText::sprintf('COM_SH404SEF_EXPORT_EXPORTING', $start + 1, $this->_total);
|
||||
$result['hiddenText'] = '<input type="hidden" name="nextstart" value="'.$nextStart.'" />';
|
||||
$result['nextStart'] = $nextStart;
|
||||
}
|
||||
|
||||
$result['continue'] = array( 'task' => 'next', 'nextstart' => $nextStart, 'filename' => base64_encode($this->_filename));
|
||||
$result['continue'] = array_merge( $result['continue'], $this->_steps);
|
||||
$result['hiddenText'] .= '<input type="hidden" name="filename" value="'.$this->_filename.'" />';
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Last step, send results as download
|
||||
*
|
||||
*/
|
||||
public function doDownload() {
|
||||
|
||||
|
||||
// fake content
|
||||
$data = '';
|
||||
|
||||
// get the current filename with path
|
||||
$this->_filename = Sh404sefHelperFiles::createFileName( $this->_filename, 'sh404sef_export_' . $this->_context);
|
||||
|
||||
// get a more readable filename
|
||||
$displayName = date('Y-m-d') . '_' . $this->_context . '_export.txt';
|
||||
|
||||
Sh404sefHelperFiles::triggerDownload( $this->_filename, $displayName);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the wizard window and redirect to default page
|
||||
*
|
||||
*/
|
||||
public function doTerminate() {
|
||||
|
||||
// are we set to purge temporary files ?
|
||||
$purgeTempFiles = JRequest::getInt( 'purge_temp_files', 0);
|
||||
if (!empty($purgeTempFiles)) {
|
||||
Sh404sefHelperFiles::purgeTempFiles( 'sh404sef_export_' . $this->_context);
|
||||
}
|
||||
|
||||
// now go back to main page
|
||||
$result = array( 'redirectTo' => true);
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the wizard window and redirect to default page
|
||||
*
|
||||
*/
|
||||
public function doCancel() {
|
||||
|
||||
$result = array();
|
||||
$result['redirectTo'] = true;
|
||||
$result['redirectOptions'] = array( 'sh404sefMsg' => 'COM_SH404SEF_WIZARD_CANCELLED');
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
protected function _export( $start) {
|
||||
|
||||
// do we have a valid filename
|
||||
$this->_filename = Sh404sefHelperFiles::createFileName( $this->_filename, 'sh404sef_export_' . $this->_context);
|
||||
|
||||
// put some data in the file
|
||||
$end = $start + self::MAX_PAGEIDS_PER_STEP + 1;
|
||||
$end = $end > $this->_total ? $this->_total : $end;
|
||||
|
||||
// fetch pageIds record from model
|
||||
$model = ShlMvcModel_Base::getInstance( 'urls', 'Sh404sefModel');
|
||||
$model->setContext( 'urls.default');
|
||||
$options = (object) array('layout' => 'export', 'getMetaData' => true, 'getPageId' => false, 'simpleUrlList' => true);
|
||||
$records = $model->getList( $options, $returnZeroElement = false, $start, $forcedLimit = self::MAX_PAGEIDS_PER_STEP);
|
||||
|
||||
// do we need a header written to the file, for first record
|
||||
$header = $start == 0 ? Sh404sefHelperGeneral::getExportHeaders( $this->_context) . "\n" : '';
|
||||
|
||||
// format them for text file output
|
||||
$data = '';
|
||||
$counter = $start;
|
||||
$glue = Sh404sefHelperFiles::$stringDelimiter . Sh404sefHelperFiles::$fieldDelimiter . Sh404sefHelperFiles::$stringDelimiter;
|
||||
if (!empty( $records)) {
|
||||
foreach( $records as $record) {
|
||||
$counter++;
|
||||
$textRecord = $record->oldurl . $glue . $record->newurl
|
||||
. $glue . $record->cpt
|
||||
. $glue . $record->rank
|
||||
. $glue . $record->dateadd
|
||||
. $glue . Sh404sefHelperFiles::csvQuote( $record->metatitle)
|
||||
. $glue . Sh404sefHelperFiles::csvQuote( $record->metadesc)
|
||||
. $glue . Sh404sefHelperFiles::csvQuote( $record->metakey)
|
||||
. $glue . Sh404sefHelperFiles::csvQuote( $record->metalang)
|
||||
. $glue . Sh404sefHelperFiles::csvQuote( $record->metarobots)
|
||||
. $glue . Sh404sefHelperFiles::csvQuote( $record->canonical)
|
||||
;
|
||||
$line = Sh404sefHelperFiles::$stringDelimiter . $counter . $glue . $textRecord . Sh404sefHelperFiles::$stringDelimiter;
|
||||
$data .= $line . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// prepare data for storage
|
||||
if (!empty( $header)) {
|
||||
// first record written to file, prepend header
|
||||
$data = $header . $data;
|
||||
}
|
||||
|
||||
// store in file
|
||||
$status = Sh404sefHelperFiles::appendToFile( $this->_filename, $data);
|
||||
|
||||
// return any error
|
||||
return $status;
|
||||
|
||||
}
|
||||
|
||||
protected function _getTerminateOptions() {
|
||||
|
||||
$options = '<br /><br />';
|
||||
$options .= '<input type="checkbox" name="purge_temp_files" value="1" checked="checked" >';
|
||||
$options .= JText::_('COM_SH404SEF_PURGE_TEMP_FILES');
|
||||
$options .= '<br />';
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,315 @@
|
||||
<?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
|
||||
*/
|
||||
|
||||
// Security check to ensure this file is being included by a parent file.
|
||||
if (!defined('_JEXEC')) die('Direct Access to this location is not allowed.');
|
||||
|
||||
/**
|
||||
* Implement wizard based exportation of page title and metas data
|
||||
*
|
||||
* @author shumisha
|
||||
*
|
||||
*/
|
||||
class Sh404sefAdapterExportview404 extends JObject {
|
||||
|
||||
/**
|
||||
* An array holding each step details
|
||||
* A step is defined as a task, a view and a layout
|
||||
* By default, task can be 'display', but still need
|
||||
* to be defined in array
|
||||
* @var array
|
||||
*/
|
||||
public $_stepsMap = array(
|
||||
|
||||
-2 => array( 'task' => 'doTerminate', 'view' => 'wizard', 'layout' => 'default')
|
||||
,-1 => array( 'task' => 'doCancel', 'view' => 'wizard', 'layout' => 'default')
|
||||
, 0 => array( 'task' => 'doStart', 'view' => 'wizard', 'layout' => 'default')
|
||||
, 1 => array( 'task' => 'doExport', 'view' => 'wizard', 'layout' => 'default')
|
||||
, 2 => array( 'task' => 'doDownload', 'view' => 'wizard', 'layout' => 'default')
|
||||
|
||||
);
|
||||
|
||||
public $_stepsCount = 0;
|
||||
public $_steps = array( 'next' => 0, 'previous' => 0, 'cancel' => -1, 'terminate' => -2);
|
||||
public $_button = '';
|
||||
public $_buttonsList = array ('next', 'previous', 'terminate', 'cancel');
|
||||
// visible buttons are displayed as toolbar pressbutton
|
||||
// buttons not on that list are passed as 'hidden' post data
|
||||
public $_visibleButtonsList = array ( 'previous', 'next', 'terminate', 'cancel');
|
||||
|
||||
|
||||
protected $_context = 'view404';
|
||||
protected $_total = 0;
|
||||
protected $_parentController = null;
|
||||
protected $_filename = '';
|
||||
|
||||
const MAX_PAGEIDS_PER_STEP = 100;
|
||||
|
||||
/**
|
||||
* Constructor, keep reference to controller
|
||||
* which called the adapter
|
||||
* @param unknown_type $parentController
|
||||
*/
|
||||
public function __construct( $parentController) {
|
||||
|
||||
$this->_parentController = $parentController;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameters for current adapter, to be used by parent controller
|
||||
*
|
||||
*/
|
||||
public function setup() {
|
||||
|
||||
$this->_stepsCount = count( $this->_stepsMap);
|
||||
|
||||
// prepare data for controller
|
||||
$properties = array();
|
||||
|
||||
$properties['_defaultController'] = 'wizard';
|
||||
$properties['_defaultTask'] = '';
|
||||
$properties['_defaultModel'] = '';
|
||||
$properties['_defaultView'] = 'wizard';
|
||||
$properties['_defaultLayout'] = 'default';
|
||||
|
||||
$properties['_returnController'] = 'urls';
|
||||
$properties['_returnTask'] = '';
|
||||
$properties['_returnView'] = 'urls';
|
||||
$properties['_returnLayout'] = 'view404';
|
||||
$properties['_pageTitle'] = JText::_('COM_SH404SEF_EXPORTING_TITLE');
|
||||
|
||||
return $properties;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* First step, by default a message
|
||||
* and a Terminate button
|
||||
*
|
||||
*/
|
||||
public function doStart() {
|
||||
|
||||
// which button should be displayed ?
|
||||
$this->_visibleButtonsList = array ('next', 'cancel');
|
||||
|
||||
// A customiser :
|
||||
$this->_steps = array( 'next' => 1, 'previous' => 0, 'cancel' => -1, 'terminate' => -2);
|
||||
|
||||
// return results
|
||||
$result = array();
|
||||
|
||||
$result['mainText'] = JText::_('COM_SH404SEF_EXPORT_VIEW404_START');
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Second step, export data
|
||||
*
|
||||
*/
|
||||
public function doExport() {
|
||||
|
||||
// which button should be displayed ?
|
||||
$this->_visibleButtonsList = array ();
|
||||
|
||||
// next steps definition
|
||||
$this->_steps = array( 'next' => 1, 'previous' => 0, 'cancel' => -1, 'terminate' => -2);
|
||||
|
||||
// return results
|
||||
$result = array();
|
||||
|
||||
// exporting a limited set of pageids at a time
|
||||
$nextStart = JRequest::getInt( 'nextstart', 0);
|
||||
|
||||
// are we adding to an existing data file ?
|
||||
$this->_filename = Sh404sefHelperFiles::createFileName( $this->_filename, 'sh404sef_export_' . $this->_context);
|
||||
|
||||
// calculate number of items to export
|
||||
$model = ShlMvcModel_Base::getInstance( 'urls', 'Sh404sefModel');
|
||||
$model->setContext( 'urls.view404');
|
||||
$options = (object) array('layout' => 'view404');
|
||||
$this->_total = $model->getTotal( $options);
|
||||
|
||||
// do we have anything to export ?
|
||||
if (empty( $this->_total)) {
|
||||
$result['mainText'] = JText::_('COM_SH404SEF_NOTHING_TO_EXPORT');
|
||||
// which button should be displayed ?
|
||||
$this->_visibleButtonsList = array ('terminate');
|
||||
// next step so to trigger download, as file is ready now
|
||||
$this->_steps = array( 'next' => -2, 'previous' => 0, 'cancel' => -1, 'terminate' => -2);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
// get new start item
|
||||
if (empty( $nextStart)) {
|
||||
// this is first pass, starting from 0
|
||||
$start = 0;
|
||||
$nextStart = $start + self::MAX_PAGEIDS_PER_STEP;
|
||||
if ($nextStart >= $this->_total) {
|
||||
// reached the end
|
||||
$nextStart = $this->_total;
|
||||
}
|
||||
} else {
|
||||
$start = $nextStart;
|
||||
$nextStart = $start + self::MAX_PAGEIDS_PER_STEP;
|
||||
}
|
||||
|
||||
// are we done ? if so, move to next step
|
||||
$result['hiddenText'] = '';
|
||||
|
||||
if ($start >= $this->_total) {
|
||||
$result['mainText'] = JText::sprintf('COM_SH404SEF_EXPORT_DONE', $this->_total);
|
||||
$result['mainText'] .= $this->_getTerminateOptions();
|
||||
// which button should be displayed ?
|
||||
$this->_visibleButtonsList = array ('terminate');
|
||||
// next step so to trigger download, as file is ready now
|
||||
$this->_steps = array( 'next' => 2, 'previous' => 0, 'cancel' => -1, 'terminate' => -2);
|
||||
} else {
|
||||
|
||||
// actually export items
|
||||
$this->_export( $start);
|
||||
|
||||
// continuing for another round
|
||||
$result['mainText'] = JText::sprintf('COM_SH404SEF_EXPORT_EXPORTING', $start + 1, $this->_total);
|
||||
$result['hiddenText'] = '<input type="hidden" name="nextstart" value="'.$nextStart.'" />';
|
||||
$result['nextStart'] = $nextStart;
|
||||
}
|
||||
|
||||
$result['continue'] = array( 'task' => 'next', 'nextstart' => $nextStart, 'filename' => base64_encode($this->_filename));
|
||||
$result['continue'] = array_merge( $result['continue'], $this->_steps);
|
||||
$result['hiddenText'] .= '<input type="hidden" name="filename" value="'.$this->_filename.'" />';
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Last step, send results as download
|
||||
*
|
||||
*/
|
||||
public function doDownload() {
|
||||
|
||||
|
||||
// fake content
|
||||
$data = '';
|
||||
|
||||
// get the current filename with path
|
||||
$this->_filename = Sh404sefHelperFiles::createFileName( $this->_filename, 'sh404sef_export_' . $this->_context);
|
||||
|
||||
// get a more readable filename
|
||||
$displayName = date('Y-m-d') . '_' . $this->_context . '_export.txt';
|
||||
|
||||
Sh404sefHelperFiles::triggerDownload( $this->_filename, $displayName);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the wizard window and redirect to default page
|
||||
*
|
||||
*/
|
||||
public function doTerminate() {
|
||||
|
||||
// are we set to purge temporary files ?
|
||||
$purgeTempFiles = JRequest::getInt( 'purge_temp_files', 0);
|
||||
if (!empty($purgeTempFiles)) {
|
||||
Sh404sefHelperFiles::purgeTempFiles( 'sh404sef_export_' . $this->_context);
|
||||
}
|
||||
|
||||
// now go back to main page
|
||||
$result = array( 'redirectTo' => true);
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the wizard window and redirect to default page
|
||||
*
|
||||
*/
|
||||
public function doCancel() {
|
||||
|
||||
$result = array();
|
||||
$result['redirectTo'] = true;
|
||||
$result['redirectOptions'] = array( 'sh404sefMsg' => 'COM_SH404SEF_WIZARD_CANCELLED');
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
protected function _export( $start) {
|
||||
|
||||
// do we have a valid filename
|
||||
$this->_filename = Sh404sefHelperFiles::createFileName( $this->_filename, 'sh404sef_export_' . $this->_context);
|
||||
|
||||
// put some data in the file
|
||||
$end = $start + self::MAX_PAGEIDS_PER_STEP + 1;
|
||||
$end = $end > $this->_total ? $this->_total : $end;
|
||||
|
||||
// fetch pageIds record from model
|
||||
$model = ShlMvcModel_Base::getInstance( 'urls', 'Sh404sefModel');
|
||||
$model->setContext( 'urls.view404');
|
||||
$options = (object) array('layout' => 'view404');
|
||||
$records = $model->getList( $options, $returnZeroElement = false, $start, $forcedLimit = self::MAX_PAGEIDS_PER_STEP);
|
||||
|
||||
// do we need a header written to the file, for first record
|
||||
$header = $start == 0 ? Sh404sefHelperGeneral::getExportHeaders( $this->_context) . "\n" : '';
|
||||
|
||||
// format them for text file output
|
||||
$data = '';
|
||||
$counter = $start;
|
||||
$glue = Sh404sefHelperFiles::$stringDelimiter . Sh404sefHelperFiles::$fieldDelimiter . Sh404sefHelperFiles::$stringDelimiter;
|
||||
if (!empty( $records)) {
|
||||
foreach( $records as $record) {
|
||||
$counter++;
|
||||
$textRecord = $record->oldurl . $glue . $record->newurl
|
||||
. $glue . $record->cpt
|
||||
. $glue . $record->rank
|
||||
. $glue . $record->dateadd
|
||||
. $glue . ''//Sh404sefHelperFiles::csvQuote( $record->metatitle)
|
||||
. $glue . ''//Sh404sefHelperFiles::csvQuote( $record->metadesc)
|
||||
. $glue . ''//Sh404sefHelperFiles::csvQuote( $record->metakey)
|
||||
. $glue . ''//Sh404sefHelperFiles::csvQuote( $record->metalang)
|
||||
. $glue . ''//Sh404sefHelperFiles::csvQuote( $record->metarobots)
|
||||
. Sh404sefHelperFiles::$stringDelimiter;
|
||||
$line = Sh404sefHelperFiles::$stringDelimiter . $counter . $glue . $textRecord;
|
||||
$data .= $line . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// prepare data for storage
|
||||
if (!empty( $header)) {
|
||||
// first record written to file, prepend header
|
||||
$data = $header . $data;
|
||||
}
|
||||
|
||||
// store in file
|
||||
$status = Sh404sefHelperFiles::appendToFile( $this->_filename, $data);
|
||||
|
||||
// return any error
|
||||
return $status;
|
||||
|
||||
}
|
||||
|
||||
protected function _getTerminateOptions() {
|
||||
|
||||
$options = '<br /><br />';
|
||||
$options .= '<input type="checkbox" name="purge_temp_files" value="1" checked="checked" >';
|
||||
$options .= JText::_('COM_SH404SEF_PURGE_TEMP_FILES');
|
||||
$options .= '<br />';
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,96 @@
|
||||
<?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
|
||||
*/
|
||||
|
||||
// Security check to ensure this file is being included by a parent file.
|
||||
if (!defined('_JEXEC')) die('Direct Access to this location is not allowed.');
|
||||
|
||||
/**
|
||||
* Implement wizard based exportation of pageids data
|
||||
*
|
||||
* @author shumisha
|
||||
*
|
||||
*/
|
||||
class Sh404sefAdapterImportaliases extends Sh404sefClassImportgeneric {
|
||||
|
||||
/**
|
||||
* Parameters for current adapter, to be used by parent controller
|
||||
*
|
||||
*/
|
||||
public function setup() {
|
||||
|
||||
// let parent do their job
|
||||
$properties = parent::setup();
|
||||
|
||||
// set context record
|
||||
$this->_context = 'aliases';
|
||||
|
||||
// setup a few custom properties
|
||||
$properties['_returnController'] = 'aliases';
|
||||
$properties['_returnTask'] = '';
|
||||
$properties['_returnView'] = 'aliases';
|
||||
$properties['_returnLayout'] = 'default';
|
||||
|
||||
// and return the whole thing
|
||||
return $properties;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a record in the database, based
|
||||
* on data read from import file
|
||||
*
|
||||
* @param array $header an array of fields, as built from the header line
|
||||
* @param string $line raw record obtained from import file
|
||||
*/
|
||||
protected function _createRecord( $header, $line) {
|
||||
|
||||
// extract the record
|
||||
$line = $this->_lineToArray( $line);
|
||||
|
||||
// get table object to store record
|
||||
$model = ShlMvcModel_Base::getInstance( 'aliases', 'Sh404sefModel');
|
||||
|
||||
// bind table to current record
|
||||
$record = array();
|
||||
$record['newurl'] = $line[3];
|
||||
if ($record['newurl'] == '__ Homepage __') {
|
||||
$record['newurl'] = sh404SEF_HOMEPAGE_CODE;
|
||||
}
|
||||
$record['alias'] = $line[1];
|
||||
$record['type'] = $line[4];
|
||||
|
||||
|
||||
// find if there is already same alias record for this non-sef url. If so
|
||||
// we want the imported record to overwrite the existing one.
|
||||
$existingRecords = $model->getByAttr( array( 'newurl' => $record['newurl'], 'alias' => $record['alias']));
|
||||
if( !empty( $existingRecords)) {
|
||||
$existingRecord = $existingRecords[0]; // getByAttr always returns an array
|
||||
|
||||
// use the existing id, this will be enought to override existing record when saving
|
||||
$record['id'] = $existingRecord->id;
|
||||
|
||||
// ensure consistency : delete the remaining records, though there is no reason
|
||||
// there can be more than one record with same alias AND same SEF
|
||||
array_shift( $existingRecords);
|
||||
if (!empty( $existingRecords)) {
|
||||
ShlDbHelper::deleteIn( '#__sh404sef_aliases', 'id', $existingRecords, ShlDbHelper::INTEGER);
|
||||
}
|
||||
}
|
||||
|
||||
// save record : returns the record id, so failure is when 0 is returned
|
||||
$saveId = $model->save( $record);
|
||||
if(empty( $saveId)) {
|
||||
// rethrow a more appropriate error message
|
||||
throw new Sh404sefExceptionDefault( JText::sprintf( 'COM_SH404SEF_IMPORT_ERROR_INSERTING_INTO_DB', $line[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,78 @@
|
||||
<?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
|
||||
*/
|
||||
|
||||
// Security check to ensure this file is being included by a parent file.
|
||||
if (!defined('_JEXEC')) die('Direct Access to this location is not allowed.');
|
||||
|
||||
/**
|
||||
* Implement wizard based exportation of pageids data
|
||||
*
|
||||
* @author shumisha
|
||||
*
|
||||
*/
|
||||
class Sh404sefAdapterImportpageids extends Sh404sefClassImportgeneric {
|
||||
|
||||
/**
|
||||
* Parameters for current adapter, to be used by parent controller
|
||||
*
|
||||
*/
|
||||
public function setup() {
|
||||
|
||||
// let parent do their job
|
||||
$properties = parent::setup();
|
||||
|
||||
// set context record
|
||||
$this->_context = 'pageids';
|
||||
|
||||
// setup a few custom properties
|
||||
$properties['_returnController'] = 'pageids';
|
||||
$properties['_returnTask'] = '';
|
||||
$properties['_returnView'] = 'pageids';
|
||||
$properties['_returnLayout'] = 'default';
|
||||
|
||||
// and return the whole thing
|
||||
return $properties;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a record in the database, based
|
||||
* on data read from import file
|
||||
*
|
||||
* @param array $header an array of fields, as built from the header line
|
||||
* @param string $line raw record obtained from import file
|
||||
*/
|
||||
protected function _createRecord( $header, $line) {
|
||||
|
||||
// extract the record
|
||||
$line = $this->_lineToArray( $line);
|
||||
|
||||
// get table object to store record
|
||||
jimport( 'joomla.database.table');
|
||||
$table = JTable::getInstance( 'pageids', 'Sh404sefTable');
|
||||
|
||||
// bind table to current record
|
||||
$record = array();
|
||||
$record['newurl'] = $line[3];
|
||||
if ($record['newurl'] == '__ Homepage __') {
|
||||
$record['newurl'] = sh404SEF_HOMEPAGE_CODE;
|
||||
}
|
||||
$record['pageid'] = $line[1];
|
||||
$record['type'] = $line[4];
|
||||
|
||||
// save record
|
||||
if (!$table->save( $record)) {
|
||||
throw new Sh404sefExceptionDefault( JText::sprintf( 'COM_SH404SEF_IMPORT_ERROR_INSERTING_INTO_DB', $line[0]));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,123 @@
|
||||
<?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
|
||||
*/
|
||||
|
||||
// Security check to ensure this file is being included by a parent file.
|
||||
if (!defined('_JEXEC')) die('Direct Access to this location is not allowed.');
|
||||
|
||||
/**
|
||||
* Implement wizard based exportation of pageids data
|
||||
*
|
||||
* @author shumisha
|
||||
*
|
||||
*/
|
||||
class Sh404sefAdapterImportsh404sefmetas extends Sh404sefClassImportgeneric {
|
||||
|
||||
/**
|
||||
* Parameters for current adapter, to be used by parent controller
|
||||
*
|
||||
*/
|
||||
public function setup() {
|
||||
|
||||
// let parent do their job
|
||||
$properties = parent::setup();
|
||||
|
||||
// set context record
|
||||
$this->_context = 'sh404sefmetas';
|
||||
|
||||
// setup a few custom properties
|
||||
$properties['_returnController'] = 'urls';
|
||||
$properties['_returnTask'] = '';
|
||||
$properties['_returnView'] = 'urls';
|
||||
$properties['_returnLayout'] = 'default';
|
||||
|
||||
// and return the whole thing
|
||||
return $properties;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a record in the database, based
|
||||
* on data read from import file
|
||||
*
|
||||
* @param array $header an array of fields, as built from the header line
|
||||
* @param string $line raw record obtained from import file
|
||||
*/
|
||||
protected function _createRecord( $header, $line) {
|
||||
|
||||
// extract the record
|
||||
$line = $this->_lineToArray( trim( $line));
|
||||
|
||||
// get table object to store record
|
||||
$model = ShlMvcModel_Base::getInstance( 'metas', 'Sh404sefModel');
|
||||
|
||||
// bind table to current record
|
||||
$record = array();
|
||||
$record['newurl'] = $line[1];
|
||||
$record['metatitle'] = $line[4];
|
||||
$record['metadesc'] = $line[2];
|
||||
$record['metakey'] = $line[3];
|
||||
$record['metalang'] = $line[5];
|
||||
$record['metarobots'] = $line[6];
|
||||
|
||||
// clean up records
|
||||
foreach( $record as $key => $value) {
|
||||
if ($value == ' ') {
|
||||
$record[$key] = '';
|
||||
}
|
||||
}
|
||||
|
||||
// find if there is already an url record for this non-sef url. If so
|
||||
// we want the imported record to overwrite the existing one.
|
||||
// while makinf sure we're doing that with the main url, not one of the duplicates
|
||||
$existingRecords = $model->getByAttr( array( 'newurl' => $record['newurl']));
|
||||
if( !empty( $existingRecords)) {
|
||||
$existingRecord = $existingRecords[0]; // getByAttr always returns an array
|
||||
|
||||
// use the existing id, this will be enought to override existing record when saving
|
||||
$record['id'] = $existingRecord->id;
|
||||
|
||||
// ensure consistency : delete the remaining records, though there is no reason
|
||||
// there can be more than one record with same SEF AND same SEF
|
||||
array_shift( $existingRecords);
|
||||
if (!empty( $existingRecords)) {
|
||||
ShlDbHelper::deleteIn( '#__sh404sef_metas', 'id', $existingRecords, ShlDbHelper::INTEGER);
|
||||
|
||||
}
|
||||
} else {
|
||||
$record['id'] = 0;
|
||||
}
|
||||
|
||||
// save record : returns the record id, so failure is when 0 is returned
|
||||
$status = $model->save( $record);
|
||||
if (!$status) {
|
||||
// rethrow a more appropriate error message
|
||||
throw new Sh404sefExceptionDefault( JText::sprintf( 'COM_SH404SEF_IMPORT_ERROR_INSERTING_INTO_DB', $line[0]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return html for any option that could
|
||||
* be presented to the user on the last
|
||||
* page of the wizard (like clean temp files)
|
||||
* for instance. This will be displayed just after
|
||||
* the mainText text, as prepared by the main
|
||||
* part of this controller
|
||||
*/
|
||||
protected function _getTerminateOptions() {
|
||||
|
||||
$options = JText::_( 'COM_SH404SEF_IMPORT_URLS_WARNING');
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,148 @@
|
||||
<?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
|
||||
*/
|
||||
|
||||
// Security check to ensure this file is being included by a parent file.
|
||||
if (!defined('_JEXEC')) die('Direct Access to this location is not allowed.');
|
||||
|
||||
/**
|
||||
* Implement wizard based exportation of pageids data
|
||||
*
|
||||
* @author shumisha
|
||||
*
|
||||
*/
|
||||
class Sh404sefAdapterImportsh404sefurls extends Sh404sefClassImportgeneric {
|
||||
|
||||
/**
|
||||
* Parameters for current adapter, to be used by parent controller
|
||||
*
|
||||
*/
|
||||
public function setup() {
|
||||
|
||||
// let parent do their job
|
||||
$properties = parent::setup();
|
||||
|
||||
// set context record
|
||||
$this->_context = 'sh404sefurls';
|
||||
|
||||
// setup a few custom properties
|
||||
$properties['_returnController'] = 'urls';
|
||||
$properties['_returnTask'] = '';
|
||||
$properties['_returnView'] = 'urls';
|
||||
$properties['_returnLayout'] = 'default';
|
||||
|
||||
// and return the whole thing
|
||||
return $properties;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a record in the database, based
|
||||
* on data read from import file
|
||||
*
|
||||
* @param array $header an array of fields, as built from the header line
|
||||
* @param string $line raw record obtained from import file
|
||||
*/
|
||||
protected function _createRecord( $header, $line) {
|
||||
|
||||
// extract the record
|
||||
$line = $this->_lineToArray( trim($line));
|
||||
|
||||
// get table object to store record
|
||||
$model = ShlMvcModel_Base::getInstance( 'editurl', 'Sh404sefModel');
|
||||
|
||||
// bind table to current record
|
||||
$record = array();
|
||||
$record['oldurl'] = $line[3];
|
||||
$record['newurl'] = $line[4];
|
||||
if ($record['newurl'] == '__ Homepage __') {
|
||||
$record['newurl'] = sh404SEF_HOMEPAGE_CODE;
|
||||
}
|
||||
$record['cpt'] = $line[1];
|
||||
$record['rank'] = $line[2];
|
||||
$record['dateadd'] = $line[5];
|
||||
$record['metatitle'] = '';
|
||||
$record['metadesc'] = '';
|
||||
$record['metakey'] = '';
|
||||
$record['metalang'] = '';
|
||||
$record['metarobots'] = '';
|
||||
|
||||
// find if there is already an url record for this non-sef url. If so
|
||||
// we want the imported record to overwrite the existing one.
|
||||
// while makinf sure we're doing that with the main url, not one of the duplicates
|
||||
$existingRecords = $model->getByAttr( array( 'newurl' => $record['newurl'], 'oldurl' => $record['oldurl']));
|
||||
if( !empty( $existingRecords)) {
|
||||
$existingRecord = $existingRecords[0]; // getByAttr always returns an array
|
||||
|
||||
// use the existing id, this will be enought to override existing record when saving
|
||||
$record['id'] = $existingRecord->id;
|
||||
|
||||
// ensure consistency : delete the remaining records, though there is no reason
|
||||
// there can be more than one record with same SEF AND same SEF
|
||||
array_shift( $existingRecords);
|
||||
if (!empty( $existingRecords)) {
|
||||
ShlDbHelper::deleteIn( '#__sh404sef_urls', 'id', $existingRecords, ShlDbHelper::INTEGER);
|
||||
}
|
||||
} else {
|
||||
$record['id'] = 0;
|
||||
}
|
||||
|
||||
// find if we already have a meta data record for this non-sef url
|
||||
// as we want to update it if so, instead of creating a new record
|
||||
$metasModel = ShlMvcModel_Base::getInstance( 'metas', 'Sh404sefModel');
|
||||
$existingMetas = $metasModel->getByAttr( array( 'newurl' => $record['newurl']));
|
||||
if( !empty( $existingMetas)) {
|
||||
$existingMeta = $existingMetas[0]; // getByAttr always returns an array
|
||||
|
||||
// use the existing id, this will be enought to override existing record when saving
|
||||
$record['meta_id'] = $existingMeta->id;
|
||||
} else {
|
||||
$record['meta_id'] = 0;
|
||||
}
|
||||
|
||||
// for aliases, we don't import them here, but we need to create a dummy
|
||||
// record so as to preserve possible pre-existing aliases for the same non-sef url
|
||||
$aliasesModel = ShlMvcModel_Base::getInstance( 'editalias', 'Sh404sefModel');
|
||||
$existingAliases = $aliasesModel->getByAttr( array( 'newurl' => $record['newurl']));
|
||||
$record['shAliasList'] = '';
|
||||
if( !empty( $existingAliases)) {
|
||||
foreach( $existingAliases as $existingAlias) {
|
||||
// build up a text list, just as if we were to edit aliases
|
||||
// as this is what the model expect to receive
|
||||
$record['shAliasList'] .= $existingAlias->alias . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// save record : returns the record id, so failure is when 0 is returned
|
||||
$savedId = $model->save( $record, sh404SEF_URLTYPE_AUTO);
|
||||
if (empty( $savedId)) {
|
||||
// rethrow a more appropriate error message
|
||||
throw new Sh404sefExceptionDefault( JText::sprintf( 'COM_SH404SEF_IMPORT_ERROR_INSERTING_INTO_DB', $line[0]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return html for any option that could
|
||||
* be presented to the user on the last
|
||||
* page of the wizard (like clean temp files)
|
||||
* for instance. This will be displayed just after
|
||||
* the mainText text, as prepared by the main
|
||||
* part of this controller
|
||||
*/
|
||||
protected function _getTerminateOptions() {
|
||||
|
||||
$options = JText::_( 'COM_SH404SEF_IMPORT_URLS_WARNING');
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,149 @@
|
||||
<?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
|
||||
*/
|
||||
|
||||
// Security check to ensure this file is being included by a parent file.
|
||||
if (!defined('_JEXEC')) die('Direct Access to this location is not allowed.');
|
||||
|
||||
/**
|
||||
* Implement wizard based exportation of pageids data
|
||||
*
|
||||
* @author shumisha
|
||||
*
|
||||
*/
|
||||
class Sh404sefAdapterImporturls extends Sh404sefClassImportgeneric {
|
||||
|
||||
/**
|
||||
* Parameters for current adapter, to be used by parent controller
|
||||
*
|
||||
*/
|
||||
public function setup() {
|
||||
|
||||
// let parent do their job
|
||||
$properties = parent::setup();
|
||||
|
||||
// set context record
|
||||
$this->_context = 'urls';
|
||||
|
||||
// setup a few custom properties
|
||||
$properties['_returnController'] = 'urls';
|
||||
$properties['_returnTask'] = '';
|
||||
$properties['_returnView'] = 'urls';
|
||||
$properties['_returnLayout'] = 'default';
|
||||
|
||||
// and return the whole thing
|
||||
return $properties;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a record in the database, based
|
||||
* on data read from import file
|
||||
*
|
||||
* @param array $header an array of fields, as built from the header line
|
||||
* @param string $line raw record obtained from import file
|
||||
*/
|
||||
protected function _createRecord( $header, $line) {
|
||||
|
||||
// extract the record
|
||||
$line = $this->_lineToArray( $line);
|
||||
|
||||
// get table object to store record
|
||||
$model = ShlMvcModel_Base::getInstance( 'editurl', 'Sh404sefModel');
|
||||
|
||||
// bind table to current record
|
||||
$record = array();
|
||||
$record['oldurl'] = $line[1];
|
||||
$record['newurl'] = $line[2];
|
||||
if ($record['newurl'] == '__ Homepage __') {
|
||||
$record['newurl'] = sh404SEF_HOMEPAGE_CODE;
|
||||
}
|
||||
$record['cpt'] = $line[3];
|
||||
$record['rank'] = $line[4];
|
||||
$record['dateadd'] = $line[5];
|
||||
$record['metatitle'] = $line[6];
|
||||
$record['metadesc'] = $line[7];
|
||||
$record['metakey'] = $line[8];
|
||||
$record['metalang'] = $line[9];
|
||||
$record['metarobots'] = $line[10];
|
||||
$record['canonical'] = $line[11];
|
||||
|
||||
// find if there is already an url record for this non-sef url. If so
|
||||
// we want the imported record to overwrite the existing one.
|
||||
// while makinf sure we're doing that with the main url, not one of the duplicates
|
||||
$existingRecords = $model->getByAttr( array( 'newurl' => $record['newurl'], 'oldurl' => $record['oldurl']));
|
||||
if( !empty( $existingRecords)) {
|
||||
$existingRecord = $existingRecords[0]; // getByAttr always returns an array
|
||||
|
||||
// use the existing id, this will be enought to override existing record when saving
|
||||
$record['id'] = $existingRecord->id;
|
||||
|
||||
// ensure consistency : delete the remaining records, though there is no reason
|
||||
// there can be more than one record with same SEF AND same SEF
|
||||
array_shift( $existingRecords);
|
||||
if (!empty( $existingRecords)) {
|
||||
ShlDbHelper::deleteIn( '#__sh404sef_urls', 'id', $existingRecords, ShlDbHelper::INTEGER);
|
||||
}
|
||||
} else {
|
||||
$record['id'] = 0;
|
||||
}
|
||||
|
||||
// find if we already have a meta data record for this non-sef url
|
||||
// as we want to update it if so, instead of creating a new record
|
||||
$metasModel = ShlMvcModel_Base::getInstance( 'metas', 'Sh404sefModel');
|
||||
$existingMetas = $metasModel->getByAttr( array( 'newurl' => $record['newurl']));
|
||||
if( !empty( $existingMetas)) {
|
||||
$existingMeta = $existingMetas[0]; // getByAttr always returns an array
|
||||
|
||||
// use the existing id, this will be enought to override existing record when saving
|
||||
$record['meta_id'] = $existingMeta->id;
|
||||
} else {
|
||||
$record['meta_id'] = 0;
|
||||
}
|
||||
|
||||
// for aliases, we don't import them here, but we need to create a dummy
|
||||
// record so as to preserve possible pre-existing aliases for the same non-sef url
|
||||
$aliasesModel = ShlMvcModel_Base::getInstance( 'editalias', 'Sh404sefModel');
|
||||
$existingAliases = $aliasesModel->getByAttr( array( 'newurl' => $record['newurl']));
|
||||
$record['shAliasList'] = '';
|
||||
if( !empty( $existingAliases)) {
|
||||
foreach( $existingAliases as $existingAlias) {
|
||||
// build up a text list, just as if we were to edit aliases
|
||||
// as this is what the model expect to receive
|
||||
$record['shAliasList'] .= $existingAlias->alias . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// save record : returns the record id, so failure is when 0 is returned
|
||||
$savedId = $model->save( $record, sh404SEF_URLTYPE_AUTO);
|
||||
if (empty( $savedId)) {
|
||||
// rethrow a more appropriate error message
|
||||
throw new Sh404sefExceptionDefault( JText::sprintf( 'COM_SH404SEF_IMPORT_ERROR_INSERTING_INTO_DB', $line[0]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return html for any option that could
|
||||
* be presented to the user on the last
|
||||
* page of the wizard (like clean temp files)
|
||||
* for instance. This will be displayed just after
|
||||
* the mainText text, as prepared by the main
|
||||
* part of this controller
|
||||
*/
|
||||
protected function _getTerminateOptions() {
|
||||
|
||||
$options = JText::_( 'COM_SH404SEF_IMPORT_URLS_WARNING');
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
<html><body bgcolor="#FFFFFF"></body></html>
|
||||
@ -0,0 +1,80 @@
|
||||
<?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
|
||||
*/
|
||||
|
||||
// Security check to ensure this file is being included by a parent file.
|
||||
if (!defined('_JEXEC')) die('Direct Access to this location is not allowed.');
|
||||
|
||||
/**
|
||||
* Implement Joomsef installer
|
||||
*
|
||||
* @author shumisha
|
||||
*
|
||||
*/
|
||||
class Sh404sefAdapterJoomsefinstaller extends Sh404sefClassBaseinstalladapter {
|
||||
|
||||
protected $_group = 'sh404sefextjoomsef';
|
||||
protected $_installType = 'sef_ext';
|
||||
|
||||
/**
|
||||
* Fix Joomsef manifest files, to force upgrade method
|
||||
*
|
||||
*/
|
||||
protected function _fixManifest() {
|
||||
|
||||
jimport( 'joomla.filesystem.file');
|
||||
|
||||
// fix original file
|
||||
$source = $this->parent->getPath( 'source');
|
||||
$path = $source . '/' . $this->_getElement() . '.xml';
|
||||
$fileContent = JFile::read( $path);
|
||||
if(!empty( $fileContent)) {
|
||||
$fileContent = str_replace( 'type="sef_ext"', 'type="sef_ext" method="upgrade"', $fileContent);
|
||||
|
||||
$defaults = array();
|
||||
$remoteConfig = Sh404sefHelperUpdates::getRemoteConfig( $forced = false);
|
||||
$remotes = empty($remoteConfig->config['joomsef_prefixes']) ? array() : $remoteConfig->config['joomsef_prefixes'];
|
||||
$prefixes = array_unique( array_merge( $defaults, $remotes));
|
||||
foreach( $prefixes as $prefix) {
|
||||
$fileContent = preg_replace( '/function\s*' . preg_quote( $prefix) . '\s*\(\s*\)\s*\{/isU', 'function ' . $prefix . '() { return;', $fileContent);
|
||||
}
|
||||
// generic replace
|
||||
$defaultReplaces = array();
|
||||
$remoteReplaces = empty($remoteConfig->config['joomsef_prefixes']) ? array() : $remoteConfig->config['joomsef_prefixes'];
|
||||
$replaces = array_unique( array_merge( $defaultReplaces, $remoteReplaces));
|
||||
foreach( $replaces as $replace) {
|
||||
$fileContent = preg_replace( '/' . $replace['source'] . '/sU', $replace['target'], $fileContent);
|
||||
}
|
||||
|
||||
// group="seo" is of no use for us, so leave it behind
|
||||
$written = JFile::write( $path, $fileContent);
|
||||
}
|
||||
|
||||
// fix in memory object, by killing it, thus prompting recreation
|
||||
$manifest = $this->parent->getManifest();
|
||||
$manifest = null;
|
||||
$manifest = $this->parent->getManifest();
|
||||
$this->manifest = & $manifest->document;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get unique element id for the plugin
|
||||
*
|
||||
*/
|
||||
protected function _getElement($xml) {
|
||||
|
||||
$element = parent::_getElement( $xml);
|
||||
|
||||
return 'com_' . $element;
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
<?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
|
||||
*/
|
||||
|
||||
// Security check to ensure this file is being included by a parent file.
|
||||
if (!defined('_JEXEC'))
|
||||
die('Direct Access to this location is not allowed.');
|
||||
if (is_readable(JPATH_ADMINISTRATOR . '/components/com_sh404sef/sh404sef.php'))
|
||||
{
|
||||
require_once 'sh404sef.php';
|
||||
}
|
||||
else
|
||||
{
|
||||
echo 'Missing main sh404SEF file: probably corrupted installation.';
|
||||
}
|
||||
@ -0,0 +1,83 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
#sbox-btn-close {display: none;}
|
||||
|
||||
#sbox-window.sh404sef-popup {
|
||||
background-color: grey;
|
||||
}
|
||||
|
||||
#sbox-overlay.sh404sef-popup {
|
||||
background-color: grey;
|
||||
}
|
||||
|
||||
.sh404sef-popup #content-box {
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.icon-48-sh404sef { background-image: url(../images/icon-48-sh404sef-logo.png); }
|
||||
|
||||
.icon-32-purge { background-image: url(../images/icon-32-purge.png); }
|
||||
.icon-32-purgeselected { background-image: url(../images/icon-32-purgeselected.png); }
|
||||
|
||||
div.sh404sef-toolbar-title {
|
||||
font-size: 22px; font-weight: bold; color: #0B55C4; line-height: 48px;
|
||||
padding-left: 135px;
|
||||
background-repeat: no-repeat;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
table.adminlist input.inputbox,
|
||||
table.adminlist textarea.inputbox,
|
||||
table.adminlist select.inputbox,
|
||||
table.adminlist img.inputbox,
|
||||
table.adminlist button.inputbox {
|
||||
float: left;
|
||||
clear: none;
|
||||
min-width: 0px;
|
||||
margin: 5px 5px 5px 0px;
|
||||
}
|
||||
|
||||
table.adminlist label.radiobtn,
|
||||
table.adminlist label.radiobtn {
|
||||
float: left;
|
||||
clear: none;
|
||||
min-width: 0px;
|
||||
margin: 5px 10px 5px 0px;
|
||||
}
|
||||
|
||||
#sh-message-box ul{
|
||||
color: #0055BB;
|
||||
background-position: 4px top;
|
||||
background-repeat: no-repeat;
|
||||
margin-bottom: 10px;
|
||||
list-style: none;
|
||||
padding: 10px;
|
||||
border-top: 3px solid #84A7DB;
|
||||
border-bottom: 3px solid #84A7DB;
|
||||
background-color: #C3D2E5;
|
||||
background-image: url(../images/notice-info.png);
|
||||
}
|
||||
|
||||
#sh-error-box ul{
|
||||
color: #c00;
|
||||
background-position: 4px top;
|
||||
background-repeat: no-repeat;
|
||||
margin-bottom: 10px;
|
||||
list-style: none;
|
||||
padding: 10px;
|
||||
border-top: 3px solid #84A7DB;
|
||||
border-bottom: 3px solid #84A7DB;
|
||||
background-color: #E6C0C0;
|
||||
background-image: url(../images/notice-alert.png);
|
||||
border-color: #DE7A7B;
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
/*start list by component */
|
||||
div.current ul.listBycomponent li {
|
||||
width: 25%;
|
||||
float: left;
|
||||
display: table-cell;
|
||||
height: 30px;
|
||||
padding-top: 3px !important;
|
||||
}
|
||||
@ -0,0 +1,198 @@
|
||||
.big {
|
||||
height: 25px;
|
||||
font-size: 14px;
|
||||
line-height: 25px;
|
||||
pading-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.hasTip img {
|
||||
vertical-align: baseline;
|
||||
margin-top: 0px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
div.current label,div.current span.faux-label {
|
||||
clear: none !important;
|
||||
float: left !important;
|
||||
min-width: 200px !important;
|
||||
margin-top: 0px !important;
|
||||
}
|
||||
|
||||
div.current fieldset.radio label {
|
||||
min-width: 45px !important;
|
||||
margin-top: 0px !important;
|
||||
}
|
||||
|
||||
div.current span.hasTip {
|
||||
clear: left;
|
||||
float: left;
|
||||
}
|
||||
|
||||
div.current li {
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
div.current li:nth-child(odd) {
|
||||
background-color: #FFF;
|
||||
}
|
||||
|
||||
div.current li:nth-child(even) {
|
||||
background-color: #EFEFEF;
|
||||
}
|
||||
/*div.current li:hover {
|
||||
background-color:#FAFAFA;
|
||||
}*/
|
||||
div.current li span.spacer {
|
||||
float: left;
|
||||
clear: both;
|
||||
width: 100%;
|
||||
border-bottom: 1px dotted lightGrey;
|
||||
padding-bottom: 4px;
|
||||
margin-bottom: 4px;
|
||||
font-size: 14px;
|
||||
}
|
||||
/*start list by component */
|
||||
div.current ul.listBycomponent li {
|
||||
width: 22%;
|
||||
float: left;
|
||||
display: table-cell;
|
||||
height: 30px;
|
||||
padding-top: 3px !important;
|
||||
}
|
||||
|
||||
div.current ul.listBycomponent li.listsroweven {
|
||||
background-color: #EFEFEF;
|
||||
}
|
||||
|
||||
div.current ul.listBycomponent li.listsrowodd {
|
||||
background-color: #FFF;
|
||||
}
|
||||
|
||||
div.current ul.listBycomponent li.listsrowodd:hover {
|
||||
background-color: #FFF;
|
||||
}
|
||||
|
||||
div.current ul.listBycomponent li.listsroweven:hover {
|
||||
background-color: #efefef;
|
||||
}
|
||||
|
||||
div.current ul.listBycomponent li.listsrow0 {
|
||||
height: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div.current li.listBycomponent span.spacer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.current ul.listBycomponent li.listsrow0 span.hasTip {
|
||||
float: none;
|
||||
}
|
||||
|
||||
div.current ul.listBycomponent li input,div.current ul.listBycomponent li select
|
||||
{
|
||||
width: 95%;
|
||||
float: left;
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
div.current ul.listBycomponent li label {
|
||||
min-height: 12px;
|
||||
width: 95%;
|
||||
font-weight: bold;
|
||||
color: #006699;
|
||||
}
|
||||
|
||||
/* start list error document */
|
||||
div.current ul li.errorpage {
|
||||
width: 30%;
|
||||
float: left;
|
||||
background: none;
|
||||
}
|
||||
|
||||
div.current ul li.errorpage ul li {
|
||||
list-style-type: disc;
|
||||
background: none;
|
||||
}
|
||||
|
||||
div.current ul li.errorpage0 {
|
||||
float: left;
|
||||
min-width: 820px;
|
||||
}
|
||||
|
||||
div.current ul li.errorpage1 {
|
||||
|
||||
}
|
||||
|
||||
ul.config-option-list,ul.config-option-list li {
|
||||
padding: 7px 2px 7px 2px !important;
|
||||
}
|
||||
|
||||
div.current input,div.current textarea,div.current select {
|
||||
margin: 0px !important;
|
||||
}
|
||||
|
||||
div.current fieldset.radio {
|
||||
padding: 0px !important;
|
||||
}
|
||||
|
||||
div.current fieldset.radio input {
|
||||
margin-top: 2px !important;
|
||||
}
|
||||
|
||||
span.sh404sef-textinput {
|
||||
font-size: 1.091em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
span.sh404sef-additionaltext {
|
||||
font-size: 1.091em;
|
||||
font-weight: bold;
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
#permissions-sliders ul#rules li {
|
||||
padding: 0px !important;
|
||||
}
|
||||
|
||||
.form-horizontal .shlegend-label .control-label {
|
||||
width: 100%;
|
||||
}
|
||||
.form-horizontal .shlegend-label .control-label legend {
|
||||
margin-bottom: 0.3em;
|
||||
}
|
||||
.form-horizontal .shrules .controls {
|
||||
margin-left:0;
|
||||
}
|
||||
|
||||
div.shinfo-icon-wrapper {
|
||||
width: 14px;
|
||||
height: 25px;
|
||||
line-height: 18px;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
top:0;
|
||||
}
|
||||
|
||||
div.tooltip-inner {
|
||||
max-width: 300px;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
div.tooltip-inner h4 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div.tip-wrap {
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.tip-title {
|
||||
font-weight: bold;
|
||||
font-size: 1.1em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
.tip-text {
|
||||
text-align: justify;
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
.sh404sef-popup #content-box {
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.icon-48-sh404sef { background-image: url(../images/icon-48-sh404sef-logo.png); }
|
||||
.icon-48-warning { background-image: url(../images/icon-48-warning.png); }
|
||||
.icon-32-confirmeddeletepageids { background-image: url(../images/icon-32-delete.png); }
|
||||
.icon-32-confirmedpurgepageids { background-image: url(../images/icon-32-delete.png); }
|
||||
.icon-32-confirmeddeldup { background-image: url(../images/icon-32-delete.png); }
|
||||
|
||||
div.sh404sef-toolbar-title {
|
||||
font-size: 22px; font-weight: bold; color: #0B55C4; line-height: 48px;
|
||||
padding-left: 135px;
|
||||
background-repeat: no-repeat;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
div.headerconfirm {
|
||||
font-size: 22px; font-weight: bold; color: #0B55C4; line-height: 48px;
|
||||
padding-left: 10px;
|
||||
background-repeat: no-repeat;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.sh404sef-popup td.key {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.sh-ajax-loading {
|
||||
background-image: url(../images/sh-ajax-loader-wide.gif);
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
}
|
||||
|
||||
#sh-message-box ul{
|
||||
color: #0055BB;
|
||||
background-position: 4px top;
|
||||
background-repeat: no-repeat;
|
||||
margin-bottom: 10px;
|
||||
list-style: none;
|
||||
padding: 10px;
|
||||
border-top: 3px solid #84A7DB;
|
||||
border-bottom: 3px solid #84A7DB;
|
||||
background-color: #C3D2E5;
|
||||
background-image: url(../images/notice-info.png);
|
||||
}
|
||||
|
||||
#sh-error-box ul{
|
||||
color: #c00;
|
||||
background-position: 4px top;
|
||||
background-repeat: no-repeat;
|
||||
margin-bottom: 10px;
|
||||
list-style: none;
|
||||
padding: 10px;
|
||||
border-top: 3px solid #84A7DB;
|
||||
border-bottom: 3px solid #84A7DB;
|
||||
background-color: #E6C0C0;
|
||||
background-image: url(../images/notice-alert.png);
|
||||
border-color: #DE7A7B;
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
<html><body bgcolor="#FFFFFF"></body></html>
|
||||
@ -0,0 +1,329 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
#sbox-btn-close {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#sbox-window.sh404sef-popup {
|
||||
background-color: grey;
|
||||
}
|
||||
|
||||
#sbox-overlay.sh404sef-popup {
|
||||
background-color: grey;
|
||||
}
|
||||
|
||||
.icon-48-sh404sef {
|
||||
background-image: url(../images/icon-48-sh404sef-logo.png);
|
||||
}
|
||||
|
||||
.icon-48-save {
|
||||
background-image: url(../images/icon-48-save.png);
|
||||
}
|
||||
|
||||
dt#config span {
|
||||
color: #0B55C4;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
dt#management span {
|
||||
color: #0B55C4;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
dt#analytics span {
|
||||
color: #0B55C4;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
dt#qcontrol span {
|
||||
color: #0B55C4;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
dt#security span {
|
||||
color: #0B55C4;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
dt#infos span {
|
||||
color: #0B55C4;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
dt#stats span {
|
||||
color: #0B55C4;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.largertext {
|
||||
display: inline;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.shlargerkey {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
table.qcontrol {
|
||||
border-collapse: collapse;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
table.qcontrol td {
|
||||
padding: 1px 1px 1px 1px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
table.qcontrol table.adminlist tbody tr td {
|
||||
background: #f4f4f4;
|
||||
border: none;
|
||||
}
|
||||
|
||||
table.qcontrol thead td {
|
||||
background: #ffffff;
|
||||
border: none;
|
||||
}
|
||||
|
||||
table.qcontrol td.spacer {
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
table.qcontrol td.divider {
|
||||
border-right: 1px solid #e0e0e0;
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
table.qcontrol span {
|
||||
float: none;
|
||||
width: 80px;
|
||||
height: 32px;
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
background-position: center top;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
table.qcontrol td.qbutton {
|
||||
text-align: center;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
table.qcontrol td.qbutton span {
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
table.qcontrol a {
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
border: 1px solid #fbfbfb;
|
||||
padding: 1px 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
table.qcontrol a:hover {
|
||||
border-left: 1px solid #e0e0e0;
|
||||
border-top: 1px solid #e0e0e0;
|
||||
border-right: 1px solid #ccc;
|
||||
border-bottom: 1px solid #ccc;
|
||||
text-decoration: none;
|
||||
color: #0B55C4;
|
||||
}
|
||||
|
||||
table.qcontrol input.inputbox,table.qcontrol textarea.inputbox,table.qcontrol select.inputbox,table.qcontrol img.inputbox,table.qcontrol button.inputbox
|
||||
{
|
||||
float: left;
|
||||
clear: none;
|
||||
min-width: 0px;
|
||||
margin: 5px 5px 5px 0px;
|
||||
}
|
||||
|
||||
table.qcontrol table.adminlist label {
|
||||
float: left;
|
||||
clear: none;
|
||||
min-width: 0px;
|
||||
margin: 5px 10px 5px 0px;
|
||||
}
|
||||
|
||||
div.sh404sef-toolbar-title {
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
color: #0B55C4;
|
||||
line-height: 48px;
|
||||
padding-left: 135px;
|
||||
background-repeat: no-repeat;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.sh-ajax-loading {
|
||||
background-image: url(../images/sh-ajax-loader-wide.gif);
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
width: 160px;
|
||||
}
|
||||
|
||||
.cp-block {
|
||||
float: left;
|
||||
}
|
||||
|
||||
#left.cp-block {
|
||||
width: 55%;
|
||||
}
|
||||
|
||||
#right.cp-block {
|
||||
float: right;
|
||||
width: 44%;
|
||||
}
|
||||
|
||||
#right-top {
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
div.cp-icons-config {
|
||||
float: left;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
div.cp-icons-others {
|
||||
float: left;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
#cpanel div.iconconfig {
|
||||
text-align: center;
|
||||
margin-right: 5px;
|
||||
float: left;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
#cpanel div.iconconfig a {
|
||||
display: block;
|
||||
float: left;
|
||||
border: 1px solid #e0e0e0;
|
||||
height: 97px;
|
||||
width: 108px;
|
||||
color: green;
|
||||
vertical-align: middle;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#cpanel div.iconconfig a:hover {
|
||||
border-left: 1px solid #eee;
|
||||
border-top: 1px solid #eee;
|
||||
border-right: 1px solid #ccc;
|
||||
border-bottom: 1px solid #ccc;
|
||||
background: #f9f9f9;
|
||||
color: #0B55C4;
|
||||
}
|
||||
|
||||
#cpanel div.iconothers {
|
||||
text-align: center;
|
||||
margin-right: 5px;
|
||||
float: left;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
#cpanel div.iconothers img,#cpanel div.iconconfig img {
|
||||
float: none;
|
||||
}
|
||||
|
||||
#cpanel div.iconothers a {
|
||||
display: block;
|
||||
float: left;
|
||||
border: 1px solid #e0e0e0;
|
||||
height: 97px;
|
||||
width: 108px;
|
||||
/*color: #666; */
|
||||
color: blue;
|
||||
vertical-align: middle;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#cpanel div.iconothers a:hover {
|
||||
border-left: 1px solid #eee;
|
||||
border-top: 1px solid #eee;
|
||||
border-right: 1px solid #ccc;
|
||||
border-bottom: 1px solid #ccc;
|
||||
background: #f9f9f9;
|
||||
color: #0B55C4;
|
||||
}
|
||||
|
||||
#sh404sef-analytics-wrapper {
|
||||
min-width: 300px;
|
||||
}
|
||||
|
||||
.analytics-report-image img {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
div.hasAnalyticsTip {
|
||||
display: inline;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
/*div.current input, div.current textarea, div.current select*/
|
||||
div.sh404sef-analytics select#accountId, div.sh404sef-analytics input#accountId, div.sh404sef-analytics textarea#accountId {
|
||||
float: none;
|
||||
}
|
||||
|
||||
#sh-message-box ul{
|
||||
color: #0055BB;
|
||||
background-position: 4px top;
|
||||
background-repeat: no-repeat;
|
||||
margin-bottom: 10px;
|
||||
list-style: none;
|
||||
padding: 10px;
|
||||
border-top: 3px solid #84A7DB;
|
||||
border-bottom: 3px solid #84A7DB;
|
||||
background-color: #C3D2E5;
|
||||
background-image: url(../images/notice-info.png);
|
||||
}
|
||||
|
||||
#sh-error-box ul{
|
||||
color: #c00;
|
||||
background-position: 4px top;
|
||||
background-repeat: no-repeat;
|
||||
margin-bottom: 10px;
|
||||
list-style: none;
|
||||
padding: 10px;
|
||||
border-top: 3px solid #84A7DB;
|
||||
border-bottom: 3px solid #84A7DB;
|
||||
background-color: #E6C0C0;
|
||||
background-image: url(../images/notice-alert.png);
|
||||
border-color: #DE7A7B;
|
||||
}
|
||||
|
||||
div.sh404sef-footer-container {
|
||||
clear: both;
|
||||
width: 100%;
|
||||
border-top: 1px #DDDDDD solid;
|
||||
background: none repeat scroll 0 0 #EDEDED;
|
||||
text-align: center;
|
||||
font-size: 11px;
|
||||
font-style: italic;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
margin-bottom: 0.5em;
|
||||
margin-top: 1.5em;
|
||||
right: 0;
|
||||
}
|
||||
@ -0,0 +1,70 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
.sh404sef-popup #content-box {
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.icon-48-sh404sef { background-image: url(../images/icon-48-sh404sef-logo.png); }
|
||||
.icon-32-cpanel { background-image: url(../images/icon-32-cpanel.png); }
|
||||
|
||||
div.sh404sef-toolbar-title {
|
||||
font-size: 22px; font-weight: bold; color: #0B55C4; line-height: 48px;
|
||||
padding-left: 135px;
|
||||
background-repeat: no-repeat;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.sh404sef-popup td.key {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.sh-ajax-loading {
|
||||
background-image: url(../images/sh-ajax-loader-wide.gif);
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
}
|
||||
|
||||
div.mainurl {
|
||||
|
||||
font-size: 22px; font-weight: bold; color: #0B55C4; line-height: 48px;
|
||||
background-repeat: no-repeat;
|
||||
text-align: center;
|
||||
|
||||
}
|
||||
|
||||
#sh-message-box ul{
|
||||
color: #0055BB;
|
||||
background-position: 4px top;
|
||||
background-repeat: no-repeat;
|
||||
margin-bottom: 10px;
|
||||
list-style: none;
|
||||
padding: 10px;
|
||||
border-top: 3px solid #84A7DB;
|
||||
border-bottom: 3px solid #84A7DB;
|
||||
background-color: #C3D2E5;
|
||||
background-image: url(../images/notice-info.png);
|
||||
}
|
||||
|
||||
#sh-error-box ul{
|
||||
color: #c00;
|
||||
background-position: 4px top;
|
||||
background-repeat: no-repeat;
|
||||
margin-bottom: 10px;
|
||||
list-style: none;
|
||||
padding: 10px;
|
||||
border-top: 3px solid #84A7DB;
|
||||
border-bottom: 3px solid #84A7DB;
|
||||
background-color: #E6C0C0;
|
||||
background-image: url(../images/notice-alert.png);
|
||||
border-color: #DE7A7B;
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
@ -0,0 +1,119 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
.icon-48-sh404sef {
|
||||
background-image: url(../images/icon-48-sh404sef-logo.png);
|
||||
}
|
||||
|
||||
.icon-48-save {
|
||||
background-image: url(../images/icon-48-save.png);
|
||||
}
|
||||
|
||||
div.largertext {
|
||||
display: inline;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.shlargerkey {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.sh404sef-toolbar-title {
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
color: #0B55C4;
|
||||
line-height: 48px;
|
||||
padding-left: 135px;
|
||||
background-repeat: no-repeat;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
#toolbar-sh-progress-button-cpprogress {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.sh-ajax-loading {
|
||||
background-image: url(../images/sh-ajax-loader-wide.gif);
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
width: 160px;
|
||||
}
|
||||
|
||||
#sh404sef-analytics-wrapper {
|
||||
min-width: 300px;
|
||||
}
|
||||
|
||||
.analytics-report-image img {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
div.hasAnalyticsTip {
|
||||
display: inline;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
/*div.current input, div.current textarea, div.current select*/
|
||||
div.sh404sef-analytics select#accountId,div.sh404sef-analytics input#accountId,div.sh404sef-analytics textarea#accountId
|
||||
{
|
||||
float: none;
|
||||
}
|
||||
|
||||
div.hero-unit {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
div.tooltip-inner {
|
||||
max-width: 300px;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
div.tooltip-inner h4 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div.shl-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.table td.shl-center, td.shl-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div.shl-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
div.shl-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
div.analytics-report-image {
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
/**
|
||||
Fix for toolbar
|
||||
*/
|
||||
@media ( min-width : 480px) {
|
||||
.shl-secondary-toolbar {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media ( max-width : 480px) {
|
||||
.shl-secondary-toolbar {
|
||||
display: inherit !important;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
#editurl-container textarea {
|
||||
width:90%;
|
||||
}
|
||||
|
||||
#editurl-container input {
|
||||
width:90%;
|
||||
}
|
||||
|
||||
div.tooltip-inner {
|
||||
max-width: 300px;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
div.tooltip-inner h4 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div.tip-wrap {
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.tip-title {
|
||||
font-weight: bold;
|
||||
font-size: 1.1em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
.tip-text {
|
||||
text-align: justify;
|
||||
}
|
||||
@ -0,0 +1,571 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
.table td.shl-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.table th.shl-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.table td.shl-left button.btn {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.table td.shl-centered {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.table td.shl-middle {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.table td.shl-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.table th.shl-centered {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.table th.shl-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.table th.shl-list-id {
|
||||
width: 4%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.table td.shl-list-id {
|
||||
text-align: right;
|
||||
padding-top: 12px;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.table th.shl-list-check {
|
||||
width: 2%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.table td.shl-list-check {
|
||||
text-align: center;
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.table th.shl-list-shurl {
|
||||
width: 6%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.table td.shl-list-shurl {
|
||||
text-align: center;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.table th.shl-list-rank {
|
||||
width: 6%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.table td.shl-list-rank {
|
||||
text-align: center;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.table th.shl-list-sef {
|
||||
max-width: 250px !important;
|
||||
width: 35%;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.table td.shl-list-sef {
|
||||
text-align: left;
|
||||
max-width: 250px !important;
|
||||
width: 35%;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.table td.shl-list-sef span {
|
||||
max-width: 250px !important;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.table td.shl-list-sef button {
|
||||
text-align: left;
|
||||
max-width: 250px !important;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.table th.shl-list-nonsef {
|
||||
width: 25%;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.table td.shl-list-nonsef {
|
||||
text-align: left;
|
||||
max-width: 250px !important;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.table th.shl-list-icon {
|
||||
width: 7%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.table td.shl-list-icon {
|
||||
padding-top: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.table th.shl-list-hits {
|
||||
width: 5%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.table td.shl-list-hits {
|
||||
text-align: right;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.table th.shl-list-metatitle {
|
||||
width: 25%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.table td.shl-list-metatitle {
|
||||
text-align: center;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.table th.shl-list-metadesc {
|
||||
width: 25%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.table td.shl-list-metadesc {
|
||||
text-align: center;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.table th.shl-list-large-buttons {
|
||||
width: 27%;
|
||||
}
|
||||
|
||||
button.shl-line-wrap350 {
|
||||
max-width: 350px !important;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
td.shl-line-wrap350 {
|
||||
max-width: 350px !important;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.shmodal-header h3 {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.sh-ajax-loading {
|
||||
background-image: url(../images/sh-ajax-loader-wide.gif);
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
div#sh-progress-cpprogress {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/** z-index fixes **/
|
||||
.shl-z-m-100 {
|
||||
z-index: -100;
|
||||
}
|
||||
|
||||
.shl-z-m-200 {
|
||||
z-index: -200;
|
||||
}
|
||||
|
||||
.shl-z-100 {
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.shl-z-200 {
|
||||
z-index: 200;
|
||||
}
|
||||
/** main content **/
|
||||
.shl-main-content {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.shl-isis-white-top {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: #fff;
|
||||
height: 78px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/** modals **/
|
||||
.shmodal-content {
|
||||
padding-top: 54px;
|
||||
}
|
||||
|
||||
div.shmodal-toolbar {
|
||||
position: fixed;
|
||||
background-color: #fff;
|
||||
height: 54px;
|
||||
z-index: 3000;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.shmodal-toolbar-wrapper {
|
||||
height: 30px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
div.shmodal-toolbar div.alert {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
div.shmodal-toolbar-text {
|
||||
display: inline-block;
|
||||
padding-top: 6px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
div.shmodal-toolbar-buttons {
|
||||
float: right;
|
||||
}
|
||||
|
||||
div#shl-modal-searchbar-right-block,div#shl-main-searchbar-right-block {
|
||||
margin-left: 0;
|
||||
padding-left: 8px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
div#shl-main-searchbar-right-block {
|
||||
/* box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);*/
|
||||
|
||||
}
|
||||
|
||||
div.shl-main-searchbar-wrapper {
|
||||
z-index: 1010;
|
||||
background-color: #fff;
|
||||
height: 48px;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 4px;
|
||||
padding-left: 2%;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
div.shl-modal-searchbar-wrapper {
|
||||
width: 100%;
|
||||
background-color: #fff;
|
||||
height: 34px;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
div.shl-bottom-box-shadow {
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
div.shl-main-list-wrapper div.alert {
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
div.shl-main-list-wrapper-padding {
|
||||
padding-top: 48px;
|
||||
}
|
||||
|
||||
div.shl-modal-list-wrapper {
|
||||
padding-top: 34px;
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
/** Generic **/
|
||||
.shl-left-separator {
|
||||
padding-left: 2%;
|
||||
}
|
||||
|
||||
.shl-right-separator {
|
||||
padding-right: 2%;
|
||||
}
|
||||
|
||||
div.shl-fixed {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.shl-modal-searchbar-wrapper div.pagination-toolbar {
|
||||
float: right;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
form.shl-no-margin,div.shl-no-margin {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div#shl-sidebar-container {
|
||||
padding-top: 10px;
|
||||
margin: 0;
|
||||
z-index: 1020;
|
||||
}
|
||||
|
||||
.shl-no-margin-left,div.shl-no-margin-left {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
div#COM_SH404SEF_DEF_404_PAGE .controls {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
header.shl-fixed-header {
|
||||
background-color: #184A7D;
|
||||
background-image: linear-gradient(to bottom, #17568C, #1A3867);
|
||||
background-repeat: repeat-x;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.2);
|
||||
padding: 5px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 30;
|
||||
position: fixed;
|
||||
z-index: 1020;
|
||||
}
|
||||
|
||||
div.shl-fixed-subheader {
|
||||
background: -moz-linear-gradient(center top, #FFFFFF 0%, #EDEDED 100%)
|
||||
repeat scroll 0 0 transparent;
|
||||
border-bottom: 1px solid #D3D3D3;
|
||||
color: #0C192E;
|
||||
margin-bottom: 10px;
|
||||
text-shadow: 0 1px 0 #FFFFFF;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 77px;
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
div.shl-fixed-top-hidden {
|
||||
height: 90px;
|
||||
}
|
||||
|
||||
/** collapsible main menu **/
|
||||
.shl-navbar {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.shl-navbar-fixed-top .shl-navbar-inner,.shl-navbar-fixed-top .shl-navbar-inner .container-fluid
|
||||
{
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.shl-navbar .brand {
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.shl-navbar .btn-navbar {
|
||||
margin-top: 3px;
|
||||
margin-right: 3px;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.shl-navbar .btn-navbar {
|
||||
background-color: #5BB75B;
|
||||
background-image: linear-gradient(to bottom, #62C462, #51A351);
|
||||
background-repeat: repeat-x;
|
||||
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
|
||||
margin-bottom: 2px;
|
||||
display: block;
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
||||
box-shadow: 0 1px 0 rgba(255, 255, 255, 0.1) inset, 0 1px 0
|
||||
rgba(255, 255, 255, 0.075);
|
||||
float: left;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
padding: 7px 10px;
|
||||
}
|
||||
|
||||
.shl-navbar .btn-navbar.btn:hover {
|
||||
background-color: #5BB75B;
|
||||
background-position: 0;
|
||||
}
|
||||
|
||||
.shl-navbar .btn-navbar .icon-bar {
|
||||
background-color: #F5F5F5;
|
||||
border-radius: 1px 1px 1px 1px;
|
||||
box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25);
|
||||
display: block;
|
||||
height: 2px;
|
||||
width: 18px;
|
||||
}
|
||||
|
||||
.shl-nav-collapse .nav .nav-header {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.shl-nav-collapse.collapse.in {
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
.shl-nav-collapse .nav,.shl-navbar .shl-nav-collapse .nav.pull-right {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.shl-nav-collapse .dropdown-menu {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.shl-nav-collapse .nav>li>a.dropdown-toggle {
|
||||
background-color: rgba(255, 255, 255, 0.07);
|
||||
background-image: -moz-linear-gradient(top, rgba(255, 255, 255, 0.15),
|
||||
rgba(255, 255, 255, 0.05) );
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(rgba(255, 255, 255, 0.15)
|
||||
), to(rgba(255, 255, 255, 0.05) ) );
|
||||
background-image: -webkit-linear-gradient(top, rgba(255, 255, 255, 0.15),
|
||||
rgba(255, 255, 255, 0.05) );
|
||||
background-image: -o-linear-gradient(top, rgba(255, 255, 255, 0.15),
|
||||
rgba(255, 255, 255, 0.05) );
|
||||
background-image: linear-gradient(to bottom, rgba(255, 255, 255, 0.15),
|
||||
rgba(255, 255, 255, 0.05) );
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#26ffffff',
|
||||
endColorstr='#0cffffff', GradientType=0 );
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
color: #eee;
|
||||
text-transform: uppercase;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
.shl-nav-collapse .nav li a {
|
||||
margin-bottom: 0;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.25);
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.shl-nav-collapse .nav>li>a,.shl-nav-collapse .dropdown-menu a {
|
||||
font-size: 15px;
|
||||
font-weight: normal;
|
||||
color: #fff;
|
||||
-webkit-border-radius: 0;
|
||||
-moz-border-radius: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.shl-nav-collapse .dropdown-menu li+li a {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/** at low width, remove stickiness of side and top bars **/
|
||||
@media ( max-width : 768px) {
|
||||
div.shl-fixed,header.shl-fixed-header,div.shl-fixed-subheader {
|
||||
position: inherit !important;
|
||||
}
|
||||
div.shl-fixed-top-hidden {
|
||||
display: none !important;
|
||||
}
|
||||
header.shl-fixed-header {
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: inherit;
|
||||
}
|
||||
div.shl-fixed-subheader {
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: inherit;
|
||||
}
|
||||
div.shl-fixed-top-hidden {
|
||||
height: 90px;
|
||||
}
|
||||
.shl-hidden-low-width,div.shl-hidden-low-width {
|
||||
display: none !important;
|
||||
}
|
||||
#shl-main-menu-coll-container .btn {
|
||||
text-align: left ! important;
|
||||
}
|
||||
div.shl-main-list-wrapper-padding {
|
||||
padding-top: 0 ! important;
|
||||
}
|
||||
div.shmodal-toolbar {
|
||||
position: inherit !important;
|
||||
}
|
||||
div.shl-modal-list-wrapper {
|
||||
padding-left: 0 !important;
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
.shmodal-content {
|
||||
padding-top: 5px !important;
|
||||
}
|
||||
div.shmodal-toolbar-buttons {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
div.shmodal-toolbar-wrapper {
|
||||
height: 100% !important;
|
||||
}
|
||||
div.shmodal-toolbar div.alert {
|
||||
padding: 3px; ! important;
|
||||
margin-bottom: 3px !important;
|
||||
}
|
||||
div.shl-main-searchbar-wrapper {
|
||||
padding: 0 ! important;
|
||||
height: auto !important;
|
||||
clear: left;
|
||||
}
|
||||
div.shl-modal-searchbar-wrapper {
|
||||
padding: 0 ! important;
|
||||
margin-top: 5px ! important;
|
||||
margin-bottom: 5px ! important;
|
||||
height: auto !important;
|
||||
clear: left;
|
||||
}
|
||||
div#shl-main-searchbar-right-block,div#shl-modal-searchbar-right-block {
|
||||
padding-left: 0 !important;
|
||||
}
|
||||
div.shmodal-toolbar-buttons {
|
||||
clear: left !important;
|
||||
}
|
||||
div#j-sidebar-container {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
div.sh404sef-footer-container {
|
||||
clear: both;
|
||||
width: 100%;
|
||||
border-top: 1px #DDDDDD solid;
|
||||
background: none repeat scroll 0 0 #EDEDED;
|
||||
text-align: center;
|
||||
font-size: 11px;
|
||||
font-style: italic;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
margin-bottom: 0;
|
||||
position: fixed;
|
||||
right: 0;
|
||||
z-index: 1030;
|
||||
bottom: 30px;
|
||||
}
|
||||
|
||||
/** at low height, fold main menu into a button **/
|
||||
@media ( min-height : 768px) {
|
||||
#shl-main-menu-coll-container {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media ( max-height : 768px) {
|
||||
#shl-main-menu-full {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,146 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
#sbox-btn-close {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#sbox-window.sh404sef-popup {
|
||||
background-color: grey;
|
||||
}
|
||||
|
||||
#sbox-overlay.sh404sef-popup {
|
||||
background-color: grey;
|
||||
}
|
||||
|
||||
.sh404sef-popup #content-box {
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.icon-32-main {
|
||||
background-image: url(../images/icon-32-default.png);
|
||||
}
|
||||
|
||||
.icon-48-sh404sef {
|
||||
background-image: url(../images/icon-48-sh404sef-logo.png);
|
||||
}
|
||||
|
||||
.icon-48-warning {
|
||||
background-image: url(../images/icon-48-warning.png);
|
||||
}
|
||||
|
||||
.icon-32-purge {
|
||||
background-image: url(../images/icon-32-purge.png);
|
||||
}
|
||||
|
||||
.icon-32-purgeselected {
|
||||
background-image: url(../images/icon-32-purgeselected.png);
|
||||
}
|
||||
|
||||
.icon-32-home {
|
||||
background-image: url(../images/icon-32-frontpage.png);
|
||||
}
|
||||
|
||||
.icon-32-cpanel {
|
||||
background-image: url(../images/icon-32-cpanel.png);
|
||||
}
|
||||
|
||||
.icon-32-import {
|
||||
background-image: url(../images/icon-32-import.png);
|
||||
}
|
||||
|
||||
.icon-32-export {
|
||||
background-image: url(../images/icon-32-export.png);
|
||||
}
|
||||
|
||||
.icon-32-selectnfredirect {
|
||||
background-image: url(../images/icon-32-default.png);
|
||||
}
|
||||
|
||||
.icon-32-deletedeldup {
|
||||
background-image: url(../images/icon-32-delete.png);
|
||||
}
|
||||
|
||||
div.sh404sef-toolbar-title {
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
color: #0B55C4;
|
||||
line-height: 48px;
|
||||
padding-left: 135px;
|
||||
background-repeat: no-repeat;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.sh-ajax-loading {
|
||||
background-image: url(../images/sh-ajax-loader-wide.gif);
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
}
|
||||
|
||||
div.mainurl {
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
color: #0B55C4;
|
||||
line-height: 48px;
|
||||
background-repeat: no-repeat;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div.sh404sef-red {
|
||||
color: red;
|
||||
}
|
||||
|
||||
div.sh404sef-red-center {
|
||||
color: red;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#sh-message-box ul {
|
||||
color: #0055BB;
|
||||
background-position: 4px top;
|
||||
background-repeat: no-repeat;
|
||||
margin-bottom: 10px;
|
||||
list-style: none;
|
||||
padding: 10px;
|
||||
border-top: 3px solid #84A7DB;
|
||||
border-bottom: 3px solid #84A7DB;
|
||||
background-color: #C3D2E5;
|
||||
background-image: url(../images/notice-info.png);
|
||||
}
|
||||
|
||||
#sh-error-box ul {
|
||||
color: #c00;
|
||||
background-position: 4px top;
|
||||
background-repeat: no-repeat;
|
||||
margin-bottom: 10px;
|
||||
list-style: none;
|
||||
padding: 10px;
|
||||
border-top: 3px solid #84A7DB;
|
||||
border-bottom: 3px solid #84A7DB;
|
||||
background-color: #E6C0C0;
|
||||
background-image: url(../images/notice-alert.png);
|
||||
border-color: #DE7A7B;
|
||||
}
|
||||
|
||||
div.sh404sef-footer-container {
|
||||
clear: both;
|
||||
width: 100%;
|
||||
border-top: 1px #DDDDDD solid;
|
||||
background: none repeat scroll 0 0 #EDEDED;
|
||||
text-align: center;
|
||||
font-size: 11px;
|
||||
font-style: italic;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
margin-bottom: 0.5em;
|
||||
margin-top: 1.5em;
|
||||
right: 0;
|
||||
}
|
||||
@ -0,0 +1,83 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
.sh404sef-popup #content-box {
|
||||
border: 1px solid #ccc;
|
||||
-webkit-border-top-right-radius: 10px;
|
||||
-webkit-border-top-left-radius: 10px;
|
||||
-moz-border-radius-topleft: 10px;
|
||||
-moz-border-radius-topright: 10px;
|
||||
border-top-left-radius: 10px;
|
||||
border-top-right-radius: 10px;
|
||||
}
|
||||
|
||||
.sh404sef-popup #toolbar-box {
|
||||
padding: 0;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.icon-48-sh404sef { background-image: url(../images/icon-48-sh404sef-logo.png); }
|
||||
.icon-32-next { background-image: url(../images/icon-32-next.png); }
|
||||
.icon-32-previous { background-image: url(../images/icon-32-previous.png); }
|
||||
.icon-32-cancel { background-image: url(../images/icon-32-cancel-wizard.png); }
|
||||
.icon-32-terminate { background-image: url(../images/icon-32-terminate.png); }
|
||||
|
||||
|
||||
div.sh404sef-toolbar-title {
|
||||
font-size: 22px; font-weight: bold; color: #0B55C4; line-height: 48px;
|
||||
padding-left: 135px;
|
||||
background-repeat: no-repeat;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
div.headerconfirm {
|
||||
font-size: 22px; font-weight: bold; color: #0B55C4; line-height: 48px;
|
||||
padding-left: 10px;
|
||||
background-repeat: no-repeat;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.sh404sef-popup td.key {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.sh-ajax-loading {
|
||||
background-image: url(../images/sh-ajax-loader-wide.gif);
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
}
|
||||
|
||||
#sh-message-box ul{
|
||||
color: #0055BB;
|
||||
background-position: 4px top;
|
||||
background-repeat: no-repeat;
|
||||
margin-bottom: 10px;
|
||||
list-style: none;
|
||||
padding: 10px;
|
||||
border-top: 3px solid #84A7DB;
|
||||
border-bottom: 3px solid #84A7DB;
|
||||
background-color: #C3D2E5;
|
||||
background-image: url(../images/notice-info.png);
|
||||
}
|
||||
|
||||
#sh-error-box ul{
|
||||
color: #c00;
|
||||
background-position: 4px top;
|
||||
background-repeat: no-repeat;
|
||||
margin-bottom: 10px;
|
||||
list-style: none;
|
||||
padding: 10px;
|
||||
border-top: 3px solid #84A7DB;
|
||||
border-bottom: 3px solid #84A7DB;
|
||||
background-color: #E6C0C0;
|
||||
background-image: url(../images/notice-alert.png);
|
||||
border-color: #DE7A7B;
|
||||
}
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 574 B |
|
After Width: | Height: | Size: 683 B |
|
After Width: | Height: | Size: 582 B |
|
After Width: | Height: | Size: 336 B |
|
After Width: | Height: | Size: 567 B |
|
After Width: | Height: | Size: 650 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 977 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
@ -0,0 +1 @@
|
||||
<html><body bgcolor="#FFFFFF"></body></html>
|
||||
|
After Width: | Height: | Size: 692 B |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 404 B |
@ -0,0 +1 @@
|
||||
<html><body bgcolor="#FFFFFF"></body></html>
|
||||
@ -0,0 +1,70 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
function shAjaxHandler(task, options, closewindow) {
|
||||
|
||||
var form = document.id('adminForm');
|
||||
form.task.value = task;
|
||||
|
||||
// Create a progress indicator
|
||||
var update = document.id("sh-message-box").empty();
|
||||
update.set("html", "<div class='sh-ajax-loading'> </div>");
|
||||
document.id("sh-error-box").empty();
|
||||
|
||||
// Set the options of the form"s Request handler.
|
||||
var onSuccessFn = function(response, responseXML) {
|
||||
//alert(response);
|
||||
var root, status, message;
|
||||
try {
|
||||
root = responseXML.documentElement;
|
||||
status = root.getElementsByTagName("status").item(0).firstChild.nodeValue;
|
||||
message = root.getElementsByTagName("message").item(0).firstChild.nodeValue;
|
||||
} catch (err) {
|
||||
status = 'failure';
|
||||
message = "<div id='error-box-content'><ul><li>Sorry, something went wrong on the server while performing this action. Please retry or cancel</li></ul></div>";
|
||||
}
|
||||
|
||||
// remove progress indicator
|
||||
var update = document.id("sh-message-box").empty();
|
||||
|
||||
// insert results
|
||||
if (status == "success") {
|
||||
update.set("html", message);
|
||||
if (closewindow) {
|
||||
setTimeout("window.parent.SqueezeBox.close()", 1500);
|
||||
} else {
|
||||
setTimeout("document.id('sh-message-box').empty()", 3000);
|
||||
}
|
||||
} else if (status == 'redirect') {
|
||||
setTimeout("parent.window.location='" + message + "';", 100);
|
||||
window.parent.shReloadModal = false;
|
||||
window.parent.SqueezeBox.close();
|
||||
} else {
|
||||
document.id('sh-error-box').set("html", message);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Send the form.
|
||||
form.set( 'send', {url: 'index.php', method: 'post', onSuccess: onSuccessFn});
|
||||
form.send();
|
||||
}
|
||||
|
||||
Joomla.submitbutton = function(pressbutton) {
|
||||
if (pressbutton == "cancel") {
|
||||
window.parent.shReloadModal = false;
|
||||
window.parent.SqueezeBox.close();
|
||||
}
|
||||
if (pressbutton == "back") {
|
||||
window.parent.shReloadModal = true;
|
||||
window.parent.SqueezeBox.close();
|
||||
}
|
||||
};
|
||||
@ -0,0 +1 @@
|
||||
<html><body bgcolor="#FFFFFF"></body></html>
|
||||
@ -0,0 +1,376 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
var shQuickControlNeedsUpdate = false;
|
||||
var shAnalyticsCompletedRequestsList = {};
|
||||
var shAnalyticsProgress = null;
|
||||
var shAnalyticsOptions = null;
|
||||
|
||||
function shSetupQuickControl() {
|
||||
var url = "index.php?option=com_sh404sef&c=configuration&view=configuration&layout=qcontrol&format=raw&tmpl=component&noMsg=1";
|
||||
new Request.HTML({
|
||||
url : url,
|
||||
method : 'get',
|
||||
onSuccess : function(responseTree, responseElements, responseHTML,
|
||||
responseJavaScript) {
|
||||
shUpdateQuickControl(responseHTML);
|
||||
},
|
||||
onFailure : function(xhr) {
|
||||
shUpdateQuickControl('Server not responding for Quick control');
|
||||
}
|
||||
}).get();
|
||||
}
|
||||
|
||||
function shUpdateQuickControl(response) {
|
||||
|
||||
document.id('qcontrolcontent').set("html", response);
|
||||
|
||||
shUpdateTooltips();
|
||||
|
||||
setTimeout("document.id('sh-message-box').empty()", 3000);
|
||||
setTimeout("document.id('sh-error-box').empty()", 5000);
|
||||
|
||||
}
|
||||
|
||||
function shUpdateTooltips() {
|
||||
|
||||
$$('.hasTip').each(function(el) {
|
||||
var title = el.get('title');
|
||||
if (title) {
|
||||
var parts = title.split('::', 2);
|
||||
el.store('tip:title', parts[0]);
|
||||
el.store('tip:text', parts[1]);
|
||||
}
|
||||
});
|
||||
|
||||
var AnalyticsTooltips = new Tips($$('.hasTip'), {
|
||||
maxTitleChars : 50,
|
||||
fixed : false
|
||||
});
|
||||
}
|
||||
|
||||
function shSetupSecStats(task) {
|
||||
task = task ? task : 'showsecstats';
|
||||
var url = "index.php?option=com_sh404sef&task=" + task
|
||||
+ "&layout=secstats&format=raw&tmpl=component&noMsg=1";
|
||||
var update = document.id("sh-progress-cpprogress").empty();
|
||||
update.set("html", "<div class='sh-ajax-loading'> </div>");
|
||||
new Request.HTML({
|
||||
url : url,
|
||||
method : 'get',
|
||||
onSuccess : function(responseTree, responseElements, responseHTML,
|
||||
responseJavaScript) {
|
||||
update.empty();
|
||||
shUpdateSecStats(responseHTML);
|
||||
},
|
||||
onFailure : function(xhr) {
|
||||
update.empty();
|
||||
shUpdateSecStats('Server not responding for security stats');
|
||||
}
|
||||
}).get();
|
||||
}
|
||||
|
||||
function shUpdateSecStats(response) {
|
||||
|
||||
document.id('secstatscontent').set("html", response);
|
||||
setTimeout("document.id('sh-message-box').empty()", 3000);
|
||||
setTimeout("document.id('sh-error-box').empty()", 5000);
|
||||
|
||||
}
|
||||
|
||||
function shSetupUpdates(forced) {
|
||||
forced = forced ? "forced=1" : 'forced=0';
|
||||
var url = "index.php?option=com_sh404sef&task=showupdates&layout=updates&format=raw&tmpl=component&noMsg=1&"
|
||||
+ forced;
|
||||
var update = document.id("sh-progress-cpprogress").empty();
|
||||
update.set("html", "<div class='sh-ajax-loading'> </div>");
|
||||
new Request.HTML({
|
||||
url : url,
|
||||
method : 'get',
|
||||
onSuccess : function(responseTree, responseElements, responseHTML,
|
||||
responseJavaScript) {
|
||||
update.empty();
|
||||
shUpdateUpdates(responseHTML);
|
||||
},
|
||||
onFailure : function(xhr) {
|
||||
update.empty();
|
||||
shUpdateUpdates('Server not responding for Updates check');
|
||||
}
|
||||
}).get();
|
||||
}
|
||||
|
||||
function shUpdateButtonClick() {
|
||||
document.id("sh-update-button").empty();
|
||||
var update = document.id("sh-progress-cpprogress").empty();
|
||||
update.set("html", "<div class='sh-ajax-loading'> </div>");
|
||||
}
|
||||
|
||||
function shUpdateUpdates(response) {
|
||||
|
||||
document.id('updatescontent').set("html", response);
|
||||
setTimeout("document.id('sh-message-box').empty()", 3000);
|
||||
setTimeout("document.id('sh-error-box').empty()", 5000);
|
||||
|
||||
}
|
||||
|
||||
function shAnalyticsRequestCompleted(req) {
|
||||
|
||||
shAnalyticsCompletedRequestsList.set(req, true);
|
||||
completed = true;
|
||||
shAnalyticsCompletedRequestsList.each(function(value, key) {
|
||||
completed = completed && value;
|
||||
});
|
||||
if (completed) {
|
||||
shAnalyticsProgress.empty();
|
||||
setTimeout('shRefreshTooltips();', 250);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function shRefreshTooltips() {
|
||||
|
||||
$$('.hasAnalyticsTip').each(function(el) {
|
||||
var title = el.get('title');
|
||||
if (title) {
|
||||
var parts = title.split('::', 2);
|
||||
el.store('tip:title', parts[0]);
|
||||
el.store('tip:text', parts[1]);
|
||||
}
|
||||
});
|
||||
var AnalyticsTooltips = new Tips($$('.hasAnalyticsTip'), {
|
||||
maxTitleChars : 50,
|
||||
fixed : false
|
||||
});
|
||||
}
|
||||
|
||||
function shSetupAnalytics(options) {
|
||||
|
||||
shAnalyticsOptions = options || {};
|
||||
|
||||
shAnalyticsProgress = document.id("sh-progress-analyticsprogress");
|
||||
shAnalyticsProgress
|
||||
.set("html", "<div class='sh-ajax-loading'> </div>");
|
||||
|
||||
var defaultOptions = {
|
||||
forced : 0,
|
||||
showFilters : 'yes',
|
||||
accountId : '',
|
||||
groupBy : '',
|
||||
startDate : '',
|
||||
endDate : '',
|
||||
cpWidth : 0,
|
||||
report : 'dashboard',
|
||||
subrequest : 'visits'
|
||||
};
|
||||
|
||||
forced = "forced="
|
||||
+ (shAnalyticsOptions.forced ? shAnalyticsOptions.forced
|
||||
: defaultOptions.forced);
|
||||
|
||||
shAnalyticsOptions.showFilters = shAnalyticsOptions.showFilters ? shAnalyticsOptions.showFilters : defaultOptions.showFilters;
|
||||
showFilters = "&showFilters="
|
||||
+ (shAnalyticsOptions.showFilters ? shAnalyticsOptions.showFilters
|
||||
: defaultOptions.showFilters);
|
||||
|
||||
// is account Id selected by user ?
|
||||
var accountIdEl = document.id('accountId');
|
||||
accountId = accountIdEl ? "&accountId=" + accountIdEl.value
|
||||
: defaultOptions.accountId;
|
||||
var startDateEl = document.id('startDate');
|
||||
startDate = startDateEl ? "&startDate=" + startDateEl.value
|
||||
: defaultOptions.startDate;
|
||||
var endDateEl = document.id('endDate');
|
||||
endDate = endDateEl ? "&endDate=" + endDateEl.value
|
||||
: defaultOptions.endDate;
|
||||
var reportEl = document.id('report');
|
||||
report = "&report=" + (reportEl ? reportEl.value : defaultOptions.report);
|
||||
var groupByEl = document.id('groupBy');
|
||||
groupBy = "&groupBy="
|
||||
+ (groupByEl ? groupByEl.value : defaultOptions.groupBy);
|
||||
var cpEl = document.id('sh404sef-analytics-wrapper');
|
||||
cpWidth = "&cpWidth=" + (cpEl ? cpEl.offsetWidth : defaultOptions.cpWidth);
|
||||
shAnalyticsOptions.url = "index.php?option=com_sh404sef&view=analytics&format=raw&tmpl=component&noMsg=1&"
|
||||
+ forced
|
||||
+ showFilters
|
||||
+ report
|
||||
+ accountId
|
||||
+ groupBy
|
||||
+ cpWidth
|
||||
+ startDate + endDate;
|
||||
|
||||
if (shAnalyticsOptions.showFilters == 'yes') {
|
||||
shAnalyticsCompletedRequestsList = new Hash({
|
||||
'headers' : false,
|
||||
'visits' : false,
|
||||
'sources' : false,
|
||||
'global' : false,
|
||||
'perf' : false,
|
||||
'topsocialfb': false,
|
||||
'topsocialtweeter': false,
|
||||
'topsocialpinterest': false,
|
||||
'topsocialplusone': false,
|
||||
'topsocialplusonepage': false,
|
||||
'top5urls' : false,
|
||||
'top5referrers' : false
|
||||
});
|
||||
} else {
|
||||
shAnalyticsCompletedRequestsList = new Hash({
|
||||
'headers' : false,
|
||||
'visits' : false,
|
||||
});
|
||||
}
|
||||
|
||||
// don't empty headers!
|
||||
shAnalyticsCompletedRequestsList.each(function(value, key) {
|
||||
if (key != "headers") {
|
||||
try{
|
||||
document.id("analyticscontent_" + key).empty();
|
||||
} catch (e) {
|
||||
//alert(key);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
_shPerformAnalyticsSubRequest('headers');
|
||||
_shPerformAnalyticsSubRequest('visits');
|
||||
|
||||
if (shAnalyticsOptions.showFilters == 'yes') {
|
||||
for ( var i = 1; i < 11; i++) {
|
||||
setTimeout('shContinueAnalytics' + i + '();', 600 * i);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function shContinueAnalytics1() {
|
||||
|
||||
_shPerformAnalyticsSubRequest('sources');
|
||||
|
||||
}
|
||||
|
||||
function shContinueAnalytics2() {
|
||||
|
||||
_shPerformAnalyticsSubRequest('global');
|
||||
}
|
||||
|
||||
function shContinueAnalytics3() {
|
||||
|
||||
_shPerformAnalyticsSubRequest('perf');
|
||||
|
||||
}
|
||||
|
||||
function shContinueAnalytics4() {
|
||||
|
||||
_shPerformAnalyticsSubRequest('top5urls');
|
||||
|
||||
}
|
||||
|
||||
function shContinueAnalytics5() {
|
||||
|
||||
_shPerformAnalyticsSubRequest('top5referrers');
|
||||
}
|
||||
|
||||
function shContinueAnalytics6() {
|
||||
|
||||
_shPerformAnalyticsSubRequest('topsocialfb');
|
||||
}
|
||||
function shContinueAnalytics7() {
|
||||
|
||||
_shPerformAnalyticsSubRequest('topsocialtweeter');
|
||||
}
|
||||
function shContinueAnalytics8() {
|
||||
|
||||
_shPerformAnalyticsSubRequest('topsocialpinterest');
|
||||
}
|
||||
function shContinueAnalytics9() {
|
||||
|
||||
_shPerformAnalyticsSubRequest('topsocialplusone');
|
||||
}
|
||||
function shContinueAnalytics10() {
|
||||
|
||||
_shPerformAnalyticsSubRequest('topsocialplusonepage');
|
||||
}
|
||||
|
||||
function _shPerformAnalyticsSubRequest(subrequestname) {
|
||||
|
||||
new Request.HTML({
|
||||
url : shAnalyticsOptions.url + '&subrequest=' + subrequestname,
|
||||
method : 'get',
|
||||
onSuccess : function(responseTree, responseElements, responseHTML,
|
||||
responseJavaScript) {
|
||||
shAnalyticsRequestCompleted(subrequestname);
|
||||
shUpdateAnalytics(responseHTML, subrequestname);
|
||||
},
|
||||
onFailure : function(xhr) {
|
||||
shAnalyticsRequestCompleted(subrequestname);
|
||||
shUpdateAnalytics('Server not responding for ' + subrequestname, subrequestname);
|
||||
}
|
||||
}).get();
|
||||
}
|
||||
|
||||
function shUpdateAnalytics(response, subrequest) {
|
||||
|
||||
document.id('analyticscontent_' + subrequest).set("html", response);
|
||||
id = document.id('startDate');
|
||||
if (id) {
|
||||
Calendar.setup({
|
||||
inputField : "startDate", // id of the input field
|
||||
ifFormat : "%Y-%m-%d", // format of the input field
|
||||
button : "startDate_img", // trigger for the calendar (button ID)
|
||||
align : "Bl", // alignment (defaults to "Bl")
|
||||
singleClick : true
|
||||
});
|
||||
Calendar.setup({
|
||||
inputField : "endDate", // id of the input field
|
||||
ifFormat : "%Y-%m-%d", // format of the input field
|
||||
button : "endDate_img", // trigger for the calendar (button ID)
|
||||
align : "Tl", // alignment (defaults to "Bl")
|
||||
singleClick : true
|
||||
});
|
||||
}
|
||||
setTimeout("document.id('sh-message-box').empty()", 3000);
|
||||
setTimeout("document.id('sh-error-box').empty()", 5000);
|
||||
|
||||
}
|
||||
|
||||
function shSubmitQuickControl() {
|
||||
|
||||
var form = document.id('adminForm');
|
||||
|
||||
// Create a progress indicator
|
||||
var update = document.id("sh-progress-cpprogress").empty();
|
||||
update.set("html", "<div class='sh-ajax-loading'> </div>");
|
||||
document.id("sh-error-box").empty();
|
||||
|
||||
// Set the options of the form"s Request handler.
|
||||
var onSuccessFn = function(response) {
|
||||
var message;
|
||||
//alert(response);
|
||||
message = "<div id='error-box-content'><ul><li>Sorry, something went wrong on the server while performing this action. Please try again or contact administrator</li></ul></div>";
|
||||
|
||||
// remove progress indicator
|
||||
var update = document.id("sh-progress-cpprogress").empty();
|
||||
|
||||
// insert results
|
||||
shUpdateQuickControl(response);
|
||||
|
||||
};
|
||||
|
||||
form.set('send', {
|
||||
url : 'index.php',
|
||||
method : 'post',
|
||||
onSuccess : onSuccessFn
|
||||
});
|
||||
|
||||
// Send the form.
|
||||
form.send();
|
||||
|
||||
}
|
||||
@ -0,0 +1,73 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
function shAjaxHandler(task, options, closewindow) {
|
||||
|
||||
var form = document.id('adminForm');
|
||||
form.task.value = task;
|
||||
|
||||
// Create a progress indicator
|
||||
var update = document.id("sh-message-box").empty();
|
||||
update.set("html", "<div class='sh-ajax-loading'> </div>");
|
||||
document.id("sh-error-box").empty();
|
||||
|
||||
// Set the options of the form"s Request handler.
|
||||
var onSuccessFn = function(response, responseXML) {
|
||||
//alert(response);
|
||||
var root, status, message;
|
||||
try {
|
||||
root = responseXML.documentElement;
|
||||
status = root.getElementsByTagName("status").item(0).firstChild.nodeValue;
|
||||
message = root.getElementsByTagName("message").item(0).firstChild.nodeValue;
|
||||
} catch (err) {
|
||||
status = 'failure';
|
||||
message = "<div id='error-box-content'><ul><li>Sorry, something went wrong on the server while performing this action. Please retry or cancel</li>" +
|
||||
"<li>" + response + "</li>"
|
||||
"</ul></div>";
|
||||
}
|
||||
|
||||
// remove progress indicator
|
||||
var update = document.id("sh-message-box").empty();
|
||||
|
||||
// insert results
|
||||
if (status == "success") {
|
||||
update.set("html", message);
|
||||
if (closewindow) {
|
||||
setTimeout("window.parent.SqueezeBox.close()", 1500);
|
||||
} else {
|
||||
setTimeout("document.id('sh-message-box').empty()", 3000);
|
||||
}
|
||||
} else if (status == 'redirect') {
|
||||
setTimeout("parent.window.location='" + message + "';", 100);
|
||||
window.parent.shReloadModal = false;
|
||||
window.parent.SqueezeBox.close();
|
||||
} else {
|
||||
document.id('sh-error-box').set("html", message);
|
||||
setTimeout("document.id('sh-error-box').empty();", 5000);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Send the form.
|
||||
form.set( 'send', {url: 'index.php', method: 'post', onSuccess: onSuccessFn});
|
||||
form.send();
|
||||
}
|
||||
|
||||
Joomla.submitbutton = function(pressbutton) {
|
||||
if (pressbutton == "cancel") {
|
||||
window.parent.shReloadModal = false;
|
||||
window.parent.SqueezeBox.close();
|
||||
}
|
||||
if (pressbutton == "back") {
|
||||
window.parent.shReloadModal = true;
|
||||
window.parent.SqueezeBox.close();
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
var __sh404sefJs = (function($) {
|
||||
var tmp = {
|
||||
|
||||
// function to call on document ready
|
||||
onReady : function() {
|
||||
jQuery(window).resize(__sh404sefJs.stickyPagination);
|
||||
__sh404sefJs.stickyPagination();
|
||||
},
|
||||
|
||||
// display the pagination bar at the top, fixed, or let it
|
||||
// at bottom, scrolling
|
||||
stickyPagination : function() {
|
||||
var needed = $("#shl-bottom-pagination-container ul.pagination-list").width();
|
||||
var avail = $("#shl-main-searchbar-right-block").width();
|
||||
var used = 0;
|
||||
$("#shl-main-searchbar-right-block div.btn-group").each(function() {
|
||||
var w = jQuery(this).width();
|
||||
used += w;
|
||||
});
|
||||
if ((avail - used - 20) > needed) {
|
||||
var c = jQuery("#shl-bottom-pagination-container").html();
|
||||
if(c) {
|
||||
$("#shl-bottom-pagination-container").html("");
|
||||
$("#shl-top-pagination-container").html(c);
|
||||
}
|
||||
} else {
|
||||
var t = jQuery("#shl-top-pagination-container").html();
|
||||
if(t) {
|
||||
$("#shl-bottom-pagination-container").html(t);
|
||||
$("#shl-top-pagination-container").html("");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// init
|
||||
jQuery(document).ready(tmp.onReady);
|
||||
return tmp;
|
||||
})(jQuery);
|
||||
@ -0,0 +1,290 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
var shQuickControlNeedsUpdate = false;
|
||||
var shAnalyticsCompletedRequestsList = {};
|
||||
var shAnalyticsProgress = null;
|
||||
var shAnalyticsOptions = null;
|
||||
|
||||
function shSetupQuickControl() {
|
||||
var url = "index.php?option=com_sh404sef&c=configuration&view=configuration&layout=qcontrol&format=raw&tmpl=component&noMsg=1";
|
||||
|
||||
var onSuccess = function(response) {
|
||||
progress.empty();
|
||||
jQuery('#qcontrolcontent').html(response);
|
||||
shlBootstrap.updateBootstrap();
|
||||
};
|
||||
// request quick control panel
|
||||
var onFailure = function() {
|
||||
shUpdateQuickControl('<div class="alert alert-error">Server not responding for Quick control</div>');
|
||||
}
|
||||
|
||||
jQuery.get(url, shUpdateQuickControl).fail(onFailure);
|
||||
|
||||
}
|
||||
|
||||
function shUpdateQuickControl(response) {
|
||||
|
||||
jQuery('#qcontrolcontent').html(response);
|
||||
shlBootstrap.updateBootstrap();
|
||||
|
||||
}
|
||||
|
||||
function shSetupSecStats(task) {
|
||||
task = task || 'showsecstats';
|
||||
var url = "index.php?option=com_sh404sef&task=" + task + "&layout=secstats&format=raw&tmpl=component&noMsg=1";
|
||||
var progress = jQuery("#sh-progress-cpprogress").empty();
|
||||
progress.html("<div class='sh-ajax-loading'> </div>");
|
||||
|
||||
// request quick control panel
|
||||
var onSuccess = function(response) {
|
||||
progress.empty();
|
||||
jQuery('#secstatscontent').html(response);
|
||||
};
|
||||
var onFailure = function() {
|
||||
progress.empty();
|
||||
jQuery('#secstatscontent').html('<div class="alert alert-error">Server not responding for security stats</div>');
|
||||
};
|
||||
|
||||
jQuery.get(url, onSuccess).fail(onFailure);
|
||||
|
||||
}
|
||||
|
||||
function shSetupUpdates(forced) {
|
||||
forced = forced ? "forced=1" : 'forced=0';
|
||||
var url = "index.php?option=com_sh404sef&task=showupdates&layout=updates&format=raw&tmpl=component&noMsg=1&" + forced;
|
||||
var progress = jQuery("#sh-progress-cpprogress").empty();
|
||||
progress.html("<div class='sh-ajax-loading'> </div>");
|
||||
var onSuccess = function(response) {
|
||||
progress.empty();
|
||||
jQuery('#updatescontent').html(response);
|
||||
};
|
||||
var onFailure = function() {
|
||||
progress.empty();
|
||||
jQuery('#updatescontent').html('<div class="alert alert-error">Server not responding for Updates check</div>');
|
||||
};
|
||||
|
||||
jQuery.get(url, onSuccess).fail(onFailure);
|
||||
}
|
||||
|
||||
function shUpdateButtonClick() {
|
||||
jQuery("#sh-update-button").empty();
|
||||
var progress = jQuery("#sh-progress-cpprogress").empty();
|
||||
progress.html("<div class='sh-ajax-loading'> </div>");
|
||||
}
|
||||
|
||||
function shAnalyticsRequestCompleted(req) {
|
||||
|
||||
shAnalyticsCompletedRequestsList[req] = true;
|
||||
completed = true;
|
||||
jQuery.each(shAnalyticsCompletedRequestsList, function(key, value) {
|
||||
completed = completed && value;
|
||||
});
|
||||
if (completed) {
|
||||
shAnalyticsProgress.empty();
|
||||
setTimeout('shlBootstrap.updateBootstrap();', 250);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function shSetupAnalytics(options) {
|
||||
|
||||
shAnalyticsOptions = options || {};
|
||||
|
||||
shAnalyticsProgress = jQuery("#sh-progress-analyticsprogress");
|
||||
shAnalyticsProgress.html("<div class='sh-ajax-loading'> </div>");
|
||||
|
||||
var defaultOptions = {
|
||||
forced : 0,
|
||||
showFilters : 'yes',
|
||||
accountId : '',
|
||||
groupBy : '',
|
||||
startDate : '',
|
||||
endDate : '',
|
||||
cpWidth : 0,
|
||||
report : 'dashboard',
|
||||
subrequest : 'visits'
|
||||
};
|
||||
shAnalyticsOptions.showFilters = shAnalyticsOptions.showFilters ? shAnalyticsOptions.showFilters : defaultOptions.showFilters;
|
||||
forced = "forced=" + (shAnalyticsOptions.forced ? shAnalyticsOptions.forced : defaultOptions.forced);
|
||||
showFilters = "&showFilters=" + shAnalyticsOptions.showFilters;
|
||||
|
||||
accountId = defaultOptions.accountId;
|
||||
var startDateEl = jQuery('#startDate');
|
||||
startDate = startDateEl && startDateEl.val() ? "&startDate=" + startDateEl.val() : defaultOptions.startDate;
|
||||
var endDateEl = jQuery('#endDate');
|
||||
endDate = endDateEl && endDateEl.val() ? "&endDate=" + endDateEl.val() : defaultOptions.endDate;
|
||||
var reportEl = jQuery('#report');
|
||||
report = "&report=" + (reportEl && reportEl.val() ? reportEl.val() : defaultOptions.report);
|
||||
var groupByEl = jQuery('#groupBy');
|
||||
groupBy = "&groupBy=" + (groupByEl && groupByEl.val() ? groupByEl.val() : defaultOptions.groupBy);
|
||||
var cpEl = jQuery('#sh404sef-analytics-wrapper');
|
||||
cpWidth = "&cpWidth=" + (cpEl ? cpEl.width() : defaultOptions.cpWidth);
|
||||
shAnalyticsOptions.url = "index.php?option=com_sh404sef&view=analytics&format=raw&tmpl=component&noMsg=1&" + forced
|
||||
+ showFilters + report + accountId + groupBy + cpWidth + startDate + endDate;
|
||||
|
||||
if (shAnalyticsOptions.showFilters == 'yes') {
|
||||
shAnalyticsCompletedRequestsList = {
|
||||
headers : false,
|
||||
visits : false,
|
||||
sources : false,
|
||||
global : false,
|
||||
perf : false,
|
||||
topsocialfb : false,
|
||||
topsocialtweeter : false,
|
||||
topsocialpinterest : false,
|
||||
topsocialplusone : false,
|
||||
topsocialplusonepage : false,
|
||||
top5urls : false,
|
||||
top5referrers : false
|
||||
};
|
||||
} else {
|
||||
shAnalyticsCompletedRequestsList = {
|
||||
headers : false,
|
||||
visits : false,
|
||||
};
|
||||
}
|
||||
|
||||
// don't empty headers!
|
||||
jQuery.each(shAnalyticsCompletedRequestsList, function(key, value) {
|
||||
if (key != "headers") {
|
||||
try {
|
||||
jQuery("#analyticscontent_" + key).empty();
|
||||
} catch (e) {
|
||||
// alert(key);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
_shPerformAnalyticsSubRequest('headers');
|
||||
_shPerformAnalyticsSubRequest('visits');
|
||||
|
||||
if (shAnalyticsOptions.showFilters == 'yes') {
|
||||
for ( var i = 1; i < 11; i++) {
|
||||
setTimeout('shContinueAnalytics' + i + '();', 600 * i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function shContinueAnalytics1() {
|
||||
|
||||
_shPerformAnalyticsSubRequest('sources');
|
||||
|
||||
}
|
||||
|
||||
function shContinueAnalytics2() {
|
||||
|
||||
_shPerformAnalyticsSubRequest('global');
|
||||
}
|
||||
|
||||
function shContinueAnalytics3() {
|
||||
|
||||
_shPerformAnalyticsSubRequest('perf');
|
||||
|
||||
}
|
||||
|
||||
function shContinueAnalytics4() {
|
||||
|
||||
_shPerformAnalyticsSubRequest('top5urls');
|
||||
|
||||
}
|
||||
|
||||
function shContinueAnalytics5() {
|
||||
|
||||
_shPerformAnalyticsSubRequest('top5referrers');
|
||||
}
|
||||
|
||||
function shContinueAnalytics6() {
|
||||
|
||||
_shPerformAnalyticsSubRequest('topsocialfb');
|
||||
}
|
||||
function shContinueAnalytics7() {
|
||||
|
||||
_shPerformAnalyticsSubRequest('topsocialtweeter');
|
||||
}
|
||||
function shContinueAnalytics8() {
|
||||
|
||||
_shPerformAnalyticsSubRequest('topsocialpinterest');
|
||||
}
|
||||
function shContinueAnalytics9() {
|
||||
|
||||
_shPerformAnalyticsSubRequest('topsocialplusone');
|
||||
}
|
||||
function shContinueAnalytics10() {
|
||||
|
||||
_shPerformAnalyticsSubRequest('topsocialplusonepage');
|
||||
}
|
||||
|
||||
function _shPerformAnalyticsSubRequest(subrequestname) {
|
||||
|
||||
var onSuccess = function(response) {
|
||||
shAnalyticsRequestCompleted(subrequestname);
|
||||
shUpdateAnalytics(response, subrequestname);
|
||||
};
|
||||
var onFailure = function() {
|
||||
shAnalyticsRequestCompleted(subrequestname);
|
||||
shUpdateAnalytics('<div class="alert alert-error">Server not responding for ' + subrequestname, subrequestname + '</div>');
|
||||
};
|
||||
|
||||
jQuery.get(shAnalyticsOptions.url + '&subrequest=' + subrequestname, onSuccess).fail(onFailure);
|
||||
|
||||
}
|
||||
|
||||
function shUpdateAnalytics(response, subrequest) {
|
||||
|
||||
jQuery('#analyticscontent_' + subrequest).html(response);
|
||||
id = jQuery('#startDate');
|
||||
|
||||
if (id.length) {
|
||||
Calendar.setup({
|
||||
inputField : "startDate", // id of the input field
|
||||
ifFormat : "%Y-%m-%d", // format of the input field
|
||||
button : "startDate_img", // trigger for the calendar (button ID)
|
||||
align : "Bl", // alignment (defaults to "Bl")
|
||||
singleClick : true
|
||||
});
|
||||
Calendar.setup({
|
||||
inputField : "endDate", // id of the input field
|
||||
ifFormat : "%Y-%m-%d", // format of the input field
|
||||
button : "endDate_img", // trigger for the calendar (button ID)
|
||||
align : "Tl", // alignment (defaults to "Bl")
|
||||
singleClick : true
|
||||
});
|
||||
}
|
||||
setTimeout("jQuery('#sh-message-box').empty()", 3000);
|
||||
setTimeout("jQuery('#sh-error-box').empty()", 5000);
|
||||
}
|
||||
|
||||
function shSubmitQuickControl() {
|
||||
|
||||
var form = jQuery('#adminForm');
|
||||
|
||||
// Create a progress indicator
|
||||
var update = jQuery("#sh-progress-cpprogress").empty();
|
||||
update.html("<div class='sh-ajax-loading'> </div>");
|
||||
|
||||
// Set the options of the form"s Request handler.
|
||||
var onSuccessFn = function(response) {
|
||||
// alert(response);
|
||||
// remove progress indicator
|
||||
var update = jQuery("#sh-progress-cpprogress").empty();
|
||||
|
||||
// insert results
|
||||
shUpdateQuickControl(response);
|
||||
|
||||
};
|
||||
// request quick control panel
|
||||
var onFailure = function() {
|
||||
shUpdateQuickControl('<div class="alert alert-error">Server not responding for Quick control</div>');
|
||||
}
|
||||
jQuery('#sh404sef-qcontrol .alert').remove();
|
||||
jQuery.post('index.php', form.serialize(), onSuccessFn).fail(onFailure);
|
||||
}
|
||||
@ -0,0 +1,74 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
function shStopEvent(event) {
|
||||
|
||||
// cancel the event
|
||||
new DOMEvent(event).stop();
|
||||
|
||||
}
|
||||
|
||||
function shProcessToolbarClick(id, pressbutton) {
|
||||
|
||||
if (pressbutton != 'cancel') {
|
||||
var el = document.getElementById(id);
|
||||
var options = el.rel;
|
||||
if (typeof this.baseurl == 'undefined') {
|
||||
this.baseurl = [];
|
||||
}
|
||||
if (typeof this.baseurl[pressbutton] == 'undefined') {
|
||||
this.baseurl[pressbutton] = el.href;
|
||||
}
|
||||
var url = baseurl[pressbutton];
|
||||
var cid = document.getElementsByName('cid[]');
|
||||
var list = '';
|
||||
if (cid) {
|
||||
var length = cid.length;
|
||||
for ( var i = 0; i < length; i++) {
|
||||
if (cid[i].checked) {
|
||||
list += '&cid[]=' + cid[i].value;
|
||||
}
|
||||
}
|
||||
}
|
||||
url += list;
|
||||
el.href = url;
|
||||
window.parent.SqueezeBox.fromElement(el, {parse:'rel'});
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function shHideMainMenu() {
|
||||
if (document.adminForm.hidemainmenu) {
|
||||
document.adminForm.hidemainmenu.value=1;
|
||||
}
|
||||
}
|
||||
|
||||
Joomla.submitbutton = function (pressbutton) {
|
||||
if (pressbutton == "cancelPopup") {
|
||||
window.parent.shReloadModal = false;
|
||||
window.parent.SqueezeBox.close();
|
||||
} else if (pressbutton == "backPopup") {
|
||||
window.parent.shReloadModal = true;
|
||||
window.parent.SqueezeBox.close();
|
||||
} else {
|
||||
if (pressbutton == "selectnfredirect") {
|
||||
window.parent.shReloadModal = true;
|
||||
}
|
||||
if (pressbutton) {
|
||||
document.adminForm.task.value = pressbutton;
|
||||
}
|
||||
if (typeof document.adminForm.onsubmit == "function") {
|
||||
document.adminForm.onsubmit();
|
||||
}
|
||||
document.adminForm.submit();
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,74 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
function shMetasClearFields() {
|
||||
|
||||
var form = document.id('adminForm');
|
||||
form.task.value = '';
|
||||
form.format.value = 'html';
|
||||
form.shajax.value = 0;
|
||||
|
||||
}
|
||||
|
||||
function shAjaxHandler(task, options, closewindow) {
|
||||
|
||||
var form = document.id('adminForm');
|
||||
form.task.value = task;
|
||||
form.format.value = "raw";
|
||||
form.shajax.value = 1;
|
||||
|
||||
// Create a progress indicator
|
||||
var update = document.id("sh-message-box").empty();
|
||||
update.set("html", "<div class='sh-ajax-loading'> </div>");
|
||||
document.id("sh-error-box").empty();
|
||||
|
||||
// Set the options of the form"s Request handler.
|
||||
var onSuccessFn = function(response, responseXML) {
|
||||
//alert(response);
|
||||
var root, status, message;
|
||||
try {
|
||||
root = responseXML.documentElement;
|
||||
status = root.getElementsByTagName("status").item(0).firstChild.nodeValue;
|
||||
message = root.getElementsByTagName("message").item(0).firstChild.nodeValue;
|
||||
} catch (err) {
|
||||
status = 'failure';
|
||||
message = "<div id='error-box-content'><ul><li>Sorry, something went wrong on the server while performing this action. Please retry or cancel</li></ul></div>";
|
||||
}
|
||||
|
||||
// remove progress indicator
|
||||
var update = document.id("sh-message-box").empty();
|
||||
|
||||
// reset task and format
|
||||
shMetasClearFields();
|
||||
|
||||
// insert results
|
||||
if (status == "success") {
|
||||
update.set("html", message);
|
||||
if (closewindow) {
|
||||
setTimeout("window.parent.SqueezeBox.close()", 1500);
|
||||
} else {
|
||||
setTimeout("document.id('sh-message-box').empty()", 3000);
|
||||
}
|
||||
} else if (status == 'redirect') {
|
||||
setTimeout("parent.window.location='" + message + "';", 100);
|
||||
window.parent.shReloadModal = false;
|
||||
window.parent.SqueezeBox.close();
|
||||
} else {
|
||||
document.id('sh-error-box').set("html", message);
|
||||
setTimeout("document.id('sh-error-box').empty();", 5000);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Send the form.
|
||||
form.set( 'send', {url: 'index.php', method: 'post', onSuccess: onSuccessFn});
|
||||
form.send();
|
||||
}
|
||||
@ -0,0 +1,105 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
function shAjaxHandler(task, options, closewindow) {
|
||||
|
||||
var form = document.id('adminForm');
|
||||
form.task.value = task;
|
||||
form.format.value = "raw";
|
||||
|
||||
// Create a progress indicator
|
||||
var update = document.id("sh-message-box").empty();
|
||||
update.set("html","<div class='sh-ajax-loading'> </div>");
|
||||
document.id("sh-error-box").empty();
|
||||
|
||||
// Set the options of the form"s Request handler.
|
||||
var onSuccessFn = function(response, responseXML) {
|
||||
var root, status, message;
|
||||
try {
|
||||
root = responseXML.documentElement;
|
||||
status = root.getElementsByTagName("status").item(0).firstChild.nodeValue;
|
||||
message = root.getElementsByTagName("message").item(0).firstChild.nodeValue;
|
||||
} catch (err) {
|
||||
status = 'failure';
|
||||
message = "<div id='error-box-content'><ul><li>Sorry, something went wrong on the server while performing this action. Please retry or cancel</li></ul></div>";
|
||||
}
|
||||
|
||||
// remove progress indicator
|
||||
var update = document.id("sh-message-box").empty();
|
||||
|
||||
// insert results
|
||||
if (status == "success") {
|
||||
update.set("html",message);
|
||||
if (closewindow) {
|
||||
setTimeout("window.parent.SqueezeBox.close()", 1500);
|
||||
} else {
|
||||
setTimeout("document.id('sh-message-box').empty()", 3000);
|
||||
}
|
||||
} else if (status == 'redirect') {
|
||||
setTimeout("parent.window.location='" + message + "';", 100);
|
||||
window.parent.shReloadModal = false;
|
||||
window.parent.SqueezeBox.close();
|
||||
} else {
|
||||
document.id('sh-error-box').set("html",message);
|
||||
setTimeout("document.id('sh-error-box').empty();", 5000);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Send the form.
|
||||
form.set( 'send', {url: 'index.php', method: 'post', onSuccess: onSuccessFn});
|
||||
form.send();
|
||||
}
|
||||
|
||||
function shStopEvent(event) {
|
||||
|
||||
// cancel the event
|
||||
new DOMEvent(event).stop();
|
||||
|
||||
}
|
||||
|
||||
Joomla.submitbutton = function (pressbutton) {
|
||||
if (pressbutton == "cancelPopup") {
|
||||
window.parent.shReloadModal = false;
|
||||
window.parent.SqueezeBox.close();
|
||||
} else if (pressbutton == "backPopup") {
|
||||
window.parent.shReloadModal = true;
|
||||
window.parent.SqueezeBox.close();
|
||||
} else {
|
||||
if (pressbutton == "selectnfredirect") {
|
||||
window.parent.shReloadModal = true;
|
||||
}
|
||||
if (pressbutton) {
|
||||
document.adminForm.task.value = pressbutton;
|
||||
}
|
||||
if (typeof document.adminForm.onsubmit == "function") {
|
||||
document.adminForm.onsubmit();
|
||||
}
|
||||
document.adminForm.submit();
|
||||
}
|
||||
};
|
||||
|
||||
function shListItemAjaxTask(id, task, options, closewindow) {
|
||||
var f = document.adminForm;
|
||||
cb = eval('f.' + id);
|
||||
if (cb) {
|
||||
for (i = 0; true; i++) {
|
||||
cbx = eval('f.cb' + i);
|
||||
if (!cbx)
|
||||
break;
|
||||
cbx.checked = false;
|
||||
} // for
|
||||
cb.checked = true;
|
||||
f.boxchecked.value = 1;
|
||||
shAjaxHandler(task, options, closewindow);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
function shAjaxHandler(task, options, closewindow) {
|
||||
|
||||
var form = jQuery('#adminForm');
|
||||
jQuery('#adminForm input[name=task]').val(task);
|
||||
jQuery('#adminForm input[name=format]').val('raw');
|
||||
jQuery('#adminForm input[name=shajax]').val('1');
|
||||
|
||||
// Create a progress indicator
|
||||
var update = jQuery("#sh-progress-cpprogress")
|
||||
update.empty();
|
||||
update.html("<div class='sh-ajax-loading'> </div>");
|
||||
|
||||
// Set the options of the form"s Request handler.
|
||||
var onSuccessFn = function(response) {
|
||||
|
||||
// restore form
|
||||
jQuery('#adminForm input[name=task]').val('');
|
||||
jQuery('#adminForm input[name=format]').val('html');
|
||||
jQuery('#adminForm input[name=shajax]').val('0');
|
||||
|
||||
//alert(response);
|
||||
var root, status, message;
|
||||
try {
|
||||
root = response.documentElement;
|
||||
status = root.getElementsByTagName("status").item(0).firstChild.nodeValue;
|
||||
message = "<div class='alert alert-success'>" + root.getElementsByTagName("message").item(0).firstChild.nodeValue + '</div>';
|
||||
} catch (err) {
|
||||
status = 'failure';
|
||||
message = "<div class='alert alert-error'>Sorry, something went wrong on the server while performing this action. Please retry or cancel.</div>";
|
||||
}
|
||||
|
||||
// remove progress indicator
|
||||
var update = jQuery("#sh-progress-cpprogress").empty();
|
||||
var update = jQuery("#sh-message-box2").empty();
|
||||
|
||||
// insert results
|
||||
if (status == "success") {
|
||||
update.html(message);
|
||||
if (closewindow) {
|
||||
setTimeout("shlBootstrap.closeModal()", 1500);
|
||||
} else {
|
||||
setTimeout("jQuery('#sh-message-box2').empty()", 3000);
|
||||
}
|
||||
} else if (status == 'redirect') {
|
||||
setTimeout("parent.window.location='" + message + "';", 100);
|
||||
shlBootstrap.closeModal();
|
||||
} else {
|
||||
jQuery('#sh-message-box').html(message);
|
||||
setTimeout("jQuery('#sh-message-box2').empty();", 5000);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Send the form.
|
||||
jQuery.post('index.php', form.serialize())
|
||||
.always(onSuccessFn);
|
||||
};
|
||||
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
function shStopEvent(event) {
|
||||
|
||||
// cancel the event
|
||||
new DOMEvent(event).stop();
|
||||
|
||||
}
|
||||
|
||||
function shProcessToolbarClick(id, pressbutton) {
|
||||
|
||||
if (pressbutton != 'cancel') {
|
||||
var el = document.getElementById(id);
|
||||
if (typeof this.baseurl == 'undefined') {
|
||||
this.baseurl = [];
|
||||
}
|
||||
if (typeof this.baseurl[pressbutton] == 'undefined') {
|
||||
this.baseurl[pressbutton] = el.href;
|
||||
}
|
||||
var url = baseurl[pressbutton];
|
||||
var cid = document.getElementsByName('cid[]');
|
||||
var list = '';
|
||||
if (cid) {
|
||||
var length = cid.length;
|
||||
for ( var i = 0; i < length; i++) {
|
||||
if (cid[i].checked) {
|
||||
list += '&cid[]=' + cid[i].value;
|
||||
}
|
||||
}
|
||||
}
|
||||
url += list;
|
||||
el.href = url;
|
||||
window.parent.SqueezeBox.fromElement(el, {parse:'rel'});
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -0,0 +1,925 @@
|
||||
sh404sef 4 changelog
|
||||
|
||||
(2014-07-01 17:04 - build #1791)
|
||||
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
[ 2014-07-01 ] Version 4.4.4.1791
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
[chg] Added back configuration saving betwen uninstall/install Fixes
|
||||
#327
|
||||
[chg] Canonical tag now includes leading scheme and host See #342
|
||||
|
||||
[bug] Memcache cache driver may eat up all available memcache memory
|
||||
and slow down
|
||||
[bug] Social buttons plugin gets published back on update (if user
|
||||
manually disabled it)
|
||||
[bug] Medium-level security issue for logged-in administrators only, on
|
||||
Joomla! 2.x only, input not sufficiently sanitized
|
||||
[bug] Configuration for type of cache handler is not saved
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
[ 2014-06-06 ] Version 4.4.3.1770
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
[bug] Fatal error on php versions less than 5.3, on multilingual sites
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
[ 2014-06-05 ] Version 4.4.2.1767
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
[bug] Some Virtuemart product details url may be blank under some
|
||||
combinations of menu items linking to categories and products
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
[ 2014-06-04 ] Version 4.4.1.1762
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
[new] Per url Google publisher tag, in addition to the pre-existing
|
||||
global publisher tag
|
||||
[new] Google Analytics IP anonymization
|
||||
|
||||
[chg] More css for Facebook buttons, handling more templates
|
||||
[chg] Clean internal caches upon installation, to avoid wrong new
|
||||
versions display when switching between editions
|
||||
[chg] Added a workaround for bug in Community Builder, not using Joomla
|
||||
API to fetch some request variables
|
||||
[chg] Hack to workaround Kunena custom routing method for some urls,
|
||||
including pagination links, in Kunena 3.0.3+
|
||||
[chg] Added canonical url when exporting urls to file
|
||||
[chg] Moved router encodesegments() method to a helper, avoid issues
|
||||
for some extensions running onAfterInitialise event several times
|
||||
[chg] Added small bottom margin to social buttons, in case they wrap at
|
||||
low width
|
||||
|
||||
[bug] Virtuemart product links can be wrong on multilingual sites under
|
||||
some combinations of menu items direct links Fixes #325
|
||||
[bug] Links to items in Joomla! fake "Uncategorised" category do not
|
||||
comply with "insert menu item title" config setting
|
||||
[bug] Social buttons could be displayed on featured articles view even
|
||||
for articles which category was excluded
|
||||
[bug] Sometimes Itemid-only links may have wrong language code in SEF
|
||||
urls Fixes #323
|
||||
[bug] Cannot switch language when using /index.php/ and Joomla config
|
||||
does not match sh404SEF config Fixes #315
|
||||
[bug] Joomla! language detection doesn't work anymore Fixes #324
|
||||
[bug] Aliases for home page are saved and works, but are not displayed
|
||||
on the edit url popup
|
||||
[bug] Inserted tags for social buttons are not replaced when 'Only on
|
||||
canonical page' param is on Fixes weeblr-dev/sh404sef-issues#319
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
[ 2014-04-09 ] Version 4.4.0.1725
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
[new] Added Linkedin share button to social buttons list
|
||||
[new] Added warning messages on multilingual sites that Joomla!
|
||||
languagefilter system plugin should now be enabled
|
||||
[new] Analytics snippets now in JLayout, can be overriden in site
|
||||
templates
|
||||
[new] Adjust to changes in Joomla! routing API in J! 3.3
|
||||
[new] Now checking Joomla! redirects table before declaring a 404, in
|
||||
case some redirects existed prior to sh404sef usage
|
||||
[new] Support for Google+ authorship information
|
||||
[new] Added Twitter Cards support, global and per page
|
||||
[new] Added optional Facebook Share button to social buttons Fixes 294
|
||||
|
||||
[chg] Simplified social buttons markup and css, better styling
|
||||
[chg] Moved social buttons to JLayout, so that they can be overriden
|
||||
from templates. Works in both J2.x and J3.x
|
||||
[chg] Performance: check if shurl exists only once
|
||||
[chg] Pagination doesn't work any more with Kunena 3.0.4 Fixes #310
|
||||
[chg] Better detection of content urls when displaying social buttons
|
||||
(Twitter, Facebook, G+)
|
||||
[chg] Added toolbar access to configuration on all views
|
||||
[chg] List of components to always leave as non-sef is now in remote
|
||||
configuration
|
||||
[chg] Better 3rd-party extensions compatiblity when "using Joomla!
|
||||
router" or "leaving as non-sef"
|
||||
[chg] Better multilingual compatiblity
|
||||
[chg] Updated German language files - Thanks Alex H.
|
||||
[chg] Updated Russian language files - Thanks Alex
|
||||
[chg] Updated Swedish language files - Thanks Kristian
|
||||
[chg] Updated language files (French, German)
|
||||
[chg] Updated French translation
|
||||
[chg] Increased tweet via account field in social plugin settings
|
||||
[chg] CSS tweaking of social buttons
|
||||
[chg] Warnings in Analytics manager, new in PHP 5.4
|
||||
|
||||
[bug] May display PHP warnings rendering pagination on multilingual
|
||||
sites
|
||||
[bug] Don't display \ in OpenGraph tag when page title has a $n
|
||||
characters
|
||||
[bug] Duplicate urls may disappear from url manager display when
|
||||
customizing main url
|
||||
[bug] Missing files and folders are not displayed as errors on control
|
||||
panel
|
||||
[bug] Showing error message upon url deletion, even though all went
|
||||
fine
|
||||
[bug] Wrong redirect from non-sef to sef on site using /index.php/
|
||||
rewriting mode
|
||||
[bug] Fatal error when exporting meta data Fixes #308
|
||||
[bug] Warning displayed on some purge operations when file cache has
|
||||
not been enabled yet
|
||||
[bug] Wrong path to javascript file in Analytics manager, result in
|
||||
analytics not loading
|
||||
[bug] Remote configuration not read anymore, wrong url
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
[ 2014-01-23 ] Version 4.3.0.1671
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
[new] Addes support for Google Tags Manager
|
||||
[new] One click update either through Joomla! updater or built-in
|
||||
auto-updater
|
||||
[new] Added Google Analytics Universal snippet handling
|
||||
|
||||
[chg] Account for upcoming Googlebot mobile user agent change
|
||||
[chg] Updated Dutch translation - Thanks JoomlaDownloads
|
||||
[chg] Updated Russian translation - Thanks Alex Smirnov
|
||||
[chg] Make sure default ACL record is written to database upon
|
||||
installation
|
||||
[chg] Make sure default ACL record is written to database upon
|
||||
installation
|
||||
[chg] Added back remote configuration option to configuration page
|
||||
[chg] Removed deprecated view code
|
||||
[chg] More explicit error messages when saving config, urls, duplicates
|
||||
|
||||
[bug] Error message not displayed when server is not responding at all
|
||||
to new version or security stats requests
|
||||
[bug] Select a redirect for 404 button is not working Fixe #305
|
||||
[bug] 404 records may not be purged when using the purge button
|
||||
[bug] Urls may not be purged when using the purge button
|
||||
[bug] Customizing url does not add old url to aliases list
|
||||
[bug] Version number of newly installed version is not always displayed
|
||||
on main control panel
|
||||
[bug] Redirect from /index.php/something to /something even if set to
|
||||
NOT use mod_rewrite Fixes #299
|
||||
[bug] Logged in users are not allowed to view sites in offline mode
|
||||
Fixes #240, fixes #297
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
[ 2013-11-02 ] Version 4.2.1.1586
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
[bug] Introduced fatal bug in Joomla! versions pre-3.2 when preparing
|
||||
release for Joomla! 3.2
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
[ 2013-11-01 ] Version 4.2.0.1582
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
[new] Automatic 301 redirect when trying to access an existing url with
|
||||
/index.php/ while url rewriting is enabled
|
||||
|
||||
[chg] J! 3.2 backward compatibility break: ConfigModelComponent
|
||||
refactoring, moving around, now requires to load a different file
|
||||
[chg] Added workaround for J!3.2 API backward compat break
|
||||
[chg] Add language information on urls created using "simple" mode, in
|
||||
case it's missing and we're on a multilingual site Fixes #216
|
||||
[chg] Let Akeeba go through offline mode. Not as secure as could be.
|
||||
[chg] Added a workaround for bug in Virtuemart, creating fake request
|
||||
variables, would prevent custom meta data to be inserted normally
|
||||
Fixes #264
|
||||
[chg] Skip versionning component to allow front end editing in J! 3.2+
|
||||
[chg] Strict standards compliance, don't use JFilterInput statically
|
||||
[chg] Added version check to prevent method name conflict due to change
|
||||
in J! 3.2 router
|
||||
[chg] Adjustments to changes in Joomla! 3.2 API (JTable interface)
|
||||
[chg] Joomla! 3.2 change in API
|
||||
[chg] Updated Pagination class to latest J! 3.1.x code
|
||||
[chg] Pagination not displayed in Joomla! 3 on some pages. Added back
|
||||
HTML prev and next tags onto pagination links Fixes #255
|
||||
[chg] Now using consistently remote_addr. Fixes #198
|
||||
[chg] Adjust to Kunena deprecating kimport function, would cause fatal
|
||||
error
|
||||
[chg] Joomla! does not update cached values in JRequest, resulting in
|
||||
incorrect input if JRequest is used to read the "task" input
|
||||
before a component is rendered, if the controller.task syntax is
|
||||
used
|
||||
[chg] J3.1 changed name of items associations parameter fixes #251
|
||||
|
||||
[bug] Incoming non-sef linking to a non-existing article is redirected
|
||||
to home instead of letting Joomla! return a 404 Fixes #285
|
||||
[bug] Article submitted from frontend may not be stored in the proper
|
||||
category Fixes #291
|
||||
[bug] Manual custom url is not displayed in url manager if there is a
|
||||
404 by the same name and the custom url already has duplicates
|
||||
Fixes #273
|
||||
[bug] Notice error display under some circumstances upon creation of
|
||||
some Virtuemart urls
|
||||
[bug] Wrong default language read from Joomla! config, may result in
|
||||
wrong language used to build sef urls, on multilingual sites
|
||||
[bug] Wrong default language detected with Joomfish on multilingual
|
||||
sites Fixes #267
|
||||
[bug] Not reading cookie path and domain to set language cookie on
|
||||
multilingual sites
|
||||
[bug] Specifying a blacklisted IP with wildcards (*) blocks too many
|
||||
users Fixes #204
|
||||
[bug] Not redirecting anymore from /index.php to / Fixes #188
|
||||
[bug] Only partial analytics reports displayed in analytics manager on
|
||||
Joomla! 2.5.x Fixes #269
|
||||
[bug] Does not comply with LanguageFilter plugin setting "Use default
|
||||
langauge" on multilingual sites fixes #261
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
[ 2013-04-25 ] Version 4.1.0.1559
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
[chg] Workaround for PHP bytecode cache systems that fail on valid PHP
|
||||
code
|
||||
[chg] Workaround for backward compatiblity issue in installer
|
||||
introduced in Joomla! 3.1
|
||||
|
||||
[bug] On multilingual sites (J! 3.0+), hreflang tags are missing
|
||||
[bug] Added check to avoid error if sh404SEF system plugin has been
|
||||
disabled by user
|
||||
[bug] Enforcing www option does not work when site is in a subfolder
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
[ 2013-03-07 ] Version 4.0.3.1552
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
[new] Characters counters are now also added to Joomla! backend pages
|
||||
where page title and page description can be edited by users
|
||||
[new] Added characters counters on page title and page description
|
||||
fields of all sh404SEF admin pages; will display recommended
|
||||
characters count ranges and show remaining characters count.
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
[ 2013-02-26 ] Version 4.0.2-rc.1544
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
[chg] Updated Bulgarian, German, Spanish, French, Hungarian, Italian,
|
||||
Dutch, Romanian, Russian and Swedish translations
|
||||
[chg] Improved canonical tag handling, to manage multiple tags that can
|
||||
be inserted by Joomla! 3+ and other extensions. Added a
|
||||
configuration setting to enable/disable this feature
|
||||
[chg] Program configuration is now overridable from remote
|
||||
configuration
|
||||
|
||||
[bug] Fixed security issue in Open Graph implementation
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
[ 2013-02-18 ] Version 4.0.1 - Joomla! 3 compatible release candidate
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
[chg] Main control panel now only loads global visits graphic, other
|
||||
infos are only in Analytics manager
|
||||
[chg] Added a check to similar urls plugin to prevent warnings
|
||||
appearing under specific but undefined circumstances Fixes #215
|
||||
[chg] Removed useless markup on control panel, under Access stats, when
|
||||
Debug to log file is not activated
|
||||
[chg] Allow aliases with % characters Fixes #217
|
||||
[chg] A canonical tag is now always included in com_content article
|
||||
pages (removes tracking vars, print flag)
|
||||
[chg] Removed unused shSimpleLogger class
|
||||
|
||||
[bug] Error message is not displayed when one tries to save a new SEF
|
||||
identical to an existing one
|
||||
[bug] Security stats are not updated correctly on main control panel
|
||||
[bug] Installable extension plugins are not loaded automatically Fixes
|
||||
#208
|
||||
[bug] Error page document is not saved inside the proper content
|
||||
category
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
[ 2012-11-26 ] Version 3.7.0.1485
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
[bug] Analytics exclude IP list is cleared when saving sh404SEF general
|
||||
configuration fixes #190
|
||||
[bug] Not cleaning up properly requests for Facebook channel urls
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
[ 2012-11-01 ] Version 3.6.4.1481
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
[bug] Analytics snippet not inserted for non-logged in users Fixes #186
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
[ 2012-10-24 ] Version 3.6.3.1478
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
[chg] Updated Swedish translation
|
||||
[chg] Updated Russian translation
|
||||
[chg] Updated French translation
|
||||
[chg] Updated Hungarian translation
|
||||
|
||||
[bug] Adjusted to removal by Joomla of usertype. Would cause analytics
|
||||
snippet to be inserted even if if it was disabled for some user
|
||||
groups. Added a better selector, now showing a direct list of
|
||||
user groups, allowing simple selection. Fixes 174
|
||||
[bug] URLs may not be removed from disk cache when deleted one at a
|
||||
time from backend Url manager
|
||||
[bug] 404 page category created with wrong parent_id, may not be
|
||||
available
|
||||
[bug] Incorrect version check
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
[ 2012-10-12 ] Version 3.6.2.1465
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
[new] Added check and message in case user has disabled either shLib
|
||||
system plugin or sh404sef system plugin
|
||||
|
||||
[chg] Added a protection against invalid Virtuemart non-sef urls,
|
||||
missing product_id
|
||||
[chg] Added protection against pages with invalid UTF-8 characters,
|
||||
that were breaking SEO features
|
||||
|
||||
[bug] Leave URL as non-sef doesn't work (regression)
|
||||
[bug] Posisble PHP fatal error when shLib plugin is not installed
|
||||
before sh404SEF plugin (due to older installations, possibly by
|
||||
other extensions using shLib)
|
||||
[bug] Incorrect date tag in plugins manifest files, release date was
|
||||
not written to manifest when building the release
|
||||
[bug] Automatic redirects from non-sef to sef don't work
|
||||
[bug] Virtuemart router.php file does not create sef links for category
|
||||
pages, when they are linked to a menu item. Instead it returns an
|
||||
Itemid. We now create the SEF link in such cases
|
||||
[bug] Release date is not inserted in manifest file
|
||||
[bug] Insert news site unique id in urls only inserts article id, not
|
||||
full unique if (introduced in 3.6.0)
|
||||
[bug] Possible display of warning when performing security log files
|
||||
garbage collection if security log files folder has not been
|
||||
created yet
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
[ 2012-09-18 ] Version 3.6.0.1422
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
[new] Added Joomla! version check before installing new version
|
||||
[new] Added a resource manager to handle shared programming libraries
|
||||
amongst several extensions
|
||||
[new] Added shared memory caching (memcache, APC, etc) for sef/non-sef
|
||||
url storage, and for database queries
|
||||
[new] Pin it button nows also handle K2 independant images in items
|
||||
Tracker #155
|
||||
[new] Added global Joomla! ACL permissions option
|
||||
[new] Added Virtuemart 2+ plugin Tracker #144
|
||||
[new] Added "gclid" (from Google AdWords) to list of tracking vars that
|
||||
are stripped before doing checks for custom page title,
|
||||
descriptions, etc Added also to remote configuration file, so all
|
||||
users with "Enable remote configuration" set to Yes will benefit
|
||||
immediately, without having to upgrade
|
||||
[new] Open graph url tag is now cleaned from possible Google analytics
|
||||
tracking vars Tracker #146
|
||||
[new] Implemented "Vary" http headers when automatic template switching
|
||||
is enabled for mobile devices Tracker #136
|
||||
[new] Mobile template is now presented to Googlebot-Mobile crawler, if
|
||||
mobile device detection is enabled Tracker #131
|
||||
[new] Added detection of a query var for automatic mobile template
|
||||
switcher. Now webmaster can add
|
||||
"sh404SEF_override_mobile_detection=force_mobile" or
|
||||
"sh404SEF_override_mobile_detection=force_desktop" to a link on a
|
||||
page, allowing user to override mobile device detection by
|
||||
sh404SEF. This is maintained by a cookie for subsequent clicks on
|
||||
the same page, and a rel="canonical" link is added to avoid
|
||||
duplicate content
|
||||
[new] Added method for 3rd-party developers to obtain SEF urls from
|
||||
inside the backend of a Joomla! site:
|
||||
Sh404sefHelperGeneral::getSefFromNonSef( $nonSefUrl,
|
||||
$fullyQualified = true, $xhtml = false, $ssl = null) - Method is
|
||||
autoloaded, no need to include file Tracker #60
|
||||
|
||||
[chg] Removed useless Kunena parameters
|
||||
[chg] Installer now checks version compatibility with any previously
|
||||
installed shared resource (such as shlib)
|
||||
[chg] Updated Russian, Swedish, Italian, Romanian, French, Spanish,
|
||||
Dutch, German and Spanish language files
|
||||
[chg] Updated Open Graph data implementation according to latest
|
||||
Facebook specs Tracker #154
|
||||
[chg] Export urls wizard now uses "slow server" settings, to make sure
|
||||
any number of urls can be exported w/o exceeding server resources
|
||||
Tracker #119
|
||||
[chg] Cleaned new urls storage process (getSefLocation()), use only db
|
||||
helper, exceptions and removed redundant code (should also
|
||||
improve perf in some edge cases)
|
||||
[chg] Moved analytics display temp files to /media dir
|
||||
[chg] Simplified 404 error page edit page, now using consistently the
|
||||
same article category ('sh404sef custom content') Tracker #151
|
||||
[chg] Removed uneeded css
|
||||
[chg] Moved all log files to Joomla! standard log directory Tracker #50
|
||||
[chg] Now using shLib shared programming library
|
||||
[chg] Moved database error handling entirely to either exceptions, or
|
||||
dual exception/errornum handling
|
||||
[chg] sef_404::getCategories() now deprecated, write call to log file.
|
||||
Use Sh404sefModelSlugs model instead
|
||||
[chg] Dropped remaining uses of Joomla! DS constant
|
||||
[chg] Updated plugin to handle Kunena 2.x Tracker #145
|
||||
[chg] Removed uneeded Docman configuration options Tracker #152
|
||||
[chg] Parsing incoming requests with Joomla! router (when user asked
|
||||
so) is now done last, as Joomla! router jumps to 404 page if it
|
||||
does not recognize a request, preventing us from doing shUrl and
|
||||
custom redirects checks
|
||||
[chg] Now disabling Joomla! caching when displaying 404 page, to be
|
||||
able insert our custom text (current url, and similar urls
|
||||
suggestions) Also: language detection may not work when full page
|
||||
caching is on.
|
||||
[chg] Added default value to default language function, in case default
|
||||
language has not been saved in configuration
|
||||
[chg] Comply with PHP Strict standards
|
||||
[chg] Added a workaround for Joomla! loading its platform language
|
||||
strings before the SEF url has been decoded. As pagination
|
||||
strings are stored inside platform language files, this may
|
||||
result under some circumstances in pagination strings (Start,
|
||||
end, ...) not being translated Tracker #143
|
||||
|
||||
[bug] Some regular expressions were not UTF-8 compatible - Thks
|
||||
G.Papadakis
|
||||
[bug] Sef and non-sef urls are not displayed when editing a shurl or
|
||||
meta data
|
||||
[bug] Offline display template lacks css when site is in a
|
||||
sub-directory of server
|
||||
[bug] On multilingual sites, nowonly try to redirect user to her
|
||||
language on homepage, as otherwise we wouldn't know what the
|
||||
correct destination would be
|
||||
[bug] Pagination may not work on some components (those leaving page
|
||||
number out of the SEF url, or not using standard Joomla!
|
||||
JPagination object) when user has not set SEF urls on in global
|
||||
configuration Tracker #142
|
||||
[bug] Possible infinite redirect loop when some custom query vars are
|
||||
added to a homepage request
|
||||
[bug] Wrong page number in page title when a blog view is used as front
|
||||
page Tracker #141
|
||||
[bug] Prevent notice error when displaying social buttons on a totally
|
||||
empty article (!)
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
[ 2012-06-27 ] Version 3.5.1.1299
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
[chg] Updated Bulgarian, French, Hungarian, Russian and Swedish
|
||||
translations
|
||||
|
||||
[bug] Homepage custom meta data is not displayed anymore Tracker #132
|
||||
[bug] Featured articles view does not get proper url on second or more
|
||||
languages, on multilingual sites (reported by Simon Gendrin)
|
||||
Tracker #134
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
[ 2012-06-14 ] Version 3.5.0.1295
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
[new] Reworked rel="prev" and rel="next" head tags, comply now with
|
||||
Google guidelines and improves paginated content handling by
|
||||
search engines
|
||||
[new] Added Pinterest support to social buttons AND Analytics reports.
|
||||
Pinning actions are tracked inside Google Analytics and your site
|
||||
control panel like other social buttons Tracker #102
|
||||
[new] Google Anlytics backend integration now using version 2.4 of API
|
||||
- as version 2.3 will be shut down in july 2012
|
||||
|
||||
[chg] Updated Bulgarian, German, Spanish, French, Hungarian, Italian,
|
||||
Dutch, Romanian, Russian and Swedish translations
|
||||
[chg] Removed uneeded language parameter Tracker #125
|
||||
[chg] Added check to prevent some notice error display when there is no
|
||||
default language version of an article or category, on
|
||||
multilingual sites
|
||||
[chg] Added check to prevent some notice error display when there is no
|
||||
default language version of an article or category, on
|
||||
multilingual sites
|
||||
[chg] Added check to prevent notice error display if user completely
|
||||
delete the "Page" text from configuration Tracker #113
|
||||
[chg] Added workaround for bug in Joomla! front end com_media image
|
||||
browser, probably using relative urls Tracker #120
|
||||
[chg] Adjust 404 errors logging in case server is running IPV6
|
||||
[chg] Some changes for Falang compatibility
|
||||
[chg] CSS tweaking to the wizards display
|
||||
[chg] Updated Russian translation (Alex Smirnov)
|
||||
|
||||
[bug] SEF url is saved to database when doing a 301 automatic redirect
|
||||
from non-sef to sef, when set to use Joomla! router, which should
|
||||
not save sef url to db Tracker #114
|
||||
[bug] Under some circumstances, cache file lock file may not be deleted
|
||||
and cache not used anymore Tracker #115
|
||||
[bug] Incorrect paginated urls on home page of some multilingual sites
|
||||
[bug] Social buttons link is not correct when using "no .htaccess"
|
||||
rewrite mode Tracker #121
|
||||
[bug] QRCode creator does not add /index.php/ prefix when not using a
|
||||
.htaccess file Tracker #116
|
||||
[bug] Under some circumstances, social buttons can be displayed on
|
||||
category pages, even of set to display only on canonical page
|
||||
Tracker #108
|
||||
[bug] Incorrect call to framework getMenu() method. No visible effect
|
||||
though
|
||||
[bug] Collect memory & time would not work, need update to J! 2.5
|
||||
format Tracker #107
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
[ 2012-02-13 ] Version 3.4.6.1269
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
[new] Added 301 auto-redirect to protect against pages of your site
|
||||
which have been Facebook-liked using old method, that can
|
||||
generate fb_xd_fragment and fb_xd_bust duplicate content issue
|
||||
Tracker #95
|
||||
[new] Implemented channelUrl for Facebook like/send button, to protect
|
||||
against fb_xd_fragment and fb_xd_bust SEO issues Tracker #95
|
||||
[new] You can now select exactly which social button to display on a a
|
||||
tag by tag basis, using 'type' attribute in
|
||||
{sh404sef_social_buttons} tags
|
||||
[new] Added canonical tag handling for J! 2.5 new com_finder "Smart
|
||||
search"
|
||||
[new] Workaround for issue in J! 2.5: cannot use categories model from
|
||||
outside of com_content
|
||||
|
||||
[chg] Updated Spanish translation by Hertak
|
||||
[chg] Simple title and better robots meta on 404 page
|
||||
[chg] Don't show Open graph data on 404 pages
|
||||
[chg] Removed Virtuemart 1.x plugin
|
||||
[chg] Better detection of item category, when displaying social
|
||||
buttons, will work also when article is displayed inside a module
|
||||
Tracker #103
|
||||
[chg] Replaced deprecated function in Mosets Tree plugin Tracker #97
|
||||
[chg] Updated Russian translation (Alex Smirnov)
|
||||
[chg] Updated Romanian translation (Valentin Barbu)
|
||||
[chg] Updated Italian translation (Andrea Castrovilli)
|
||||
[chg] Updated German translation (Jürgen Hörmann)
|
||||
[chg] Updated Bulgarian translation (Kiril Yoviev)
|
||||
[chg] Cleaned up all language files, key deduplication, ready to upload
|
||||
to translation server
|
||||
[chg] Moved some of the social buttons javascript links to end of body
|
||||
instead of end of document head Tracker #385
|
||||
[chg] Added a check to handle name conflict between Mighty Touch and
|
||||
JomSocial, avoiding fatal errors Tracker #393
|
||||
[chg] Changed default layout for Twitter and G+, to use shURls and hide
|
||||
clicks count Tracker #81
|
||||
[chg] Can now modify URLs in a case-sensitive manner Tracker #379
|
||||
[chg] Updated Dutch translation by Gerard van Enschut Tracker #88
|
||||
[chg] Update to Swedish translation, by JokrSolutions
|
||||
[chg] Added a check in case user enters a space as an alias to an URL
|
||||
Tracker #87
|
||||
|
||||
[bug] Prevent creating useless links to 404 page, when site is crawled
|
||||
[bug] Outbound links images are not inserted into page if site is in
|
||||
root dir Tracker #96
|
||||
[bug] file cache TTL is multiplied by 86400 seconds (a day) instead of
|
||||
3600 (an hour) Tracker #98
|
||||
[bug] A shurl is created for homepage on multilingual sites Tracker
|
||||
#105
|
||||
[bug] Infinite redirect loop when site is accessed by crawlers on
|
||||
multilingual sites when autoredirect from non-sef to sef is on
|
||||
Tracker #104
|
||||
[bug] May generate loop redirects when adding aliases identical to
|
||||
original url, except for the letter case
|
||||
[bug] Custom canonical URL is not saved if it is the only custom meta
|
||||
data for an url Tracker #94
|
||||
[bug] Kunena plugin: new post from category doesn't have a distinct URL
|
||||
Tracker #93
|
||||
[bug] Rewriting prefix is lost when redirecting incoming url to the
|
||||
same with correct case Tracker #91
|
||||
[bug] Open graph fb:admins tag should use "property" field, not "name"
|
||||
[bug] When "Use default" is selected as Object type option for specific
|
||||
urls, the Opengraph data object type is not inserted Tracker #395
|
||||
[bug] Batch-modifying page title or description would erase other meta
|
||||
data previously set, if any Tracker #86
|
||||
[bug] Added test to avoid duplicating records when Apply and Save
|
||||
button are pressed in sequence when adding custom meta data to a
|
||||
URL Tracker #85
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
[ 2011-12-13 ] Version 3.4.4.1226
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
[new] Added option to use shURL instead of full SEF url in social
|
||||
buttons. Especially useful for Tweeting, to better use those 140
|
||||
characters Tracker #67
|
||||
[new] Can now restrict display of social buttons to only some content
|
||||
categories Tracker #76
|
||||
|
||||
[chg] Removed display of unused hit counter Tracker #80
|
||||
[chg] Updated French translation
|
||||
[chg] Removed autoredirect from Joomla SEF to SEF Tracker #78
|
||||
[chg] When saving data from Title and metas manager, if one url is
|
||||
incorrect, we now attempt to save remaining data, in case some of
|
||||
it is correct and can be saved
|
||||
[chg] We now delete a meta data record if user happens to clear all
|
||||
meta data fields, save space and avoid having a meta data icon
|
||||
show in url manager when meta data have been cleared Tracker #75
|
||||
[chg] Social buttons should not be displayed when printing Joomla!
|
||||
articles Tracker #74
|
||||
[chg] Added some styles to social buttons background
|
||||
[chg] Added a check to prevent recording empty urls in 404 list Tracker
|
||||
#390
|
||||
|
||||
[bug] Display mysql error when using slow server mode and sorting urls
|
||||
by duplicates, meta or alias Tracker #389
|
||||
[bug] Popup window too small when editing some urls, poor display
|
||||
[bug] If Joomla! full page cache is activated, social buttons may
|
||||
sometimes not be inserted, or may not work
|
||||
[bug] Disable social buttons insertion if global "Activate SEO
|
||||
management" is disabled by user
|
||||
[bug] In case of incoming requests for a non-sef url, variables from
|
||||
the home page link could be added to this non-sef
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
[ 2011-11-22 ] Version 3.4.3.1212
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
[new] Updated Romanian translation
|
||||
|
||||
[chg] Updated Swedish translation by BR Kristian
|
||||
[chg] Added workaround for JDispatcher::...handler not recognized when
|
||||
running an opcache system
|
||||
|
||||
[bug] Notices can be displayed if site runs with sh404sef disabled and
|
||||
meta data management is activated
|
||||
[bug] Could display a notice error trying to redirect a 404 url, when
|
||||
there is no suggested sef url found
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
[ 2011-11-19 ] Version 3.4.2.1210
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
[chg] Added protections so that social features are not triggered on
|
||||
404 pages
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
[ 2011-11-19 ] Version 3.4.1.1207
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
[bug] Database error when upgrading from a previous version (site would
|
||||
work fine). Immediately installing a second time fixes the error.
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
[ 2011-11-18 ] Version 3.4.0.1205
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
[new] Analytics reports now includes social engagement information:
|
||||
Facebook likes, Tweet, 1+ actions
|
||||
[new] Social buttons can be inserted automatically in Joomla! articles,
|
||||
below title or at end of article
|
||||
[new] Added creation of social networking buttons, handling for
|
||||
Facebook Like and Send, Tweeter, Google +1 and the new Google
|
||||
Page badge. Will place social buttons anywhere in content, other
|
||||
extensions, modules or even templates
|
||||
[new] Added automatic creation and management of Open Graph meta data,
|
||||
to allow better indexing by Facebook, Google+ and other social
|
||||
networks
|
||||
[new] Added more protection against fatal error if user accidentally
|
||||
disable sh404SEF system plugin Tracker #375
|
||||
[new] Added tracking of 404 pages on Google Analytics - you'll get 404s
|
||||
count on sh404SEF Analytics report, as well as on Google
|
||||
Analytics panel
|
||||
[new] Added "All categories" options to insert article id setting
|
||||
Tracker #53
|
||||
[new] 404 error page mechanism now compatible with Joomla! multilingual
|
||||
feature. Can have 404 error page per language
|
||||
|
||||
[chg] Re-enabled automatic language redirect upon visitor first visit,
|
||||
as workaround to counter bug in Firefox and IE9 was found for
|
||||
this to work properly Tracker #59
|
||||
[chg] Group meta tag insertion when possible, better performance on
|
||||
very large pages
|
||||
[chg] Added support for multilingual menu item associations, and a new
|
||||
option to Enable/disable multilingual support in sh404SEF. Use
|
||||
instead of Enabling/Disabling Joomla! Language filter plugin
|
||||
Tracker #57
|
||||
[chg] Simplified similar urls plugin database query, to reduce overhead
|
||||
Tracker #55
|
||||
[chg] Moved sh404SEF 404 error page content from the "Uncategorised"
|
||||
category to our own, as localized versions of Joomla! would call
|
||||
the "Uncategorise" category differently, making it impossible to
|
||||
save or retrieve our error page. The change is done by install
|
||||
script, and existing error pages will be moved over to new
|
||||
category automatically
|
||||
[chg] Start and end pagination links now also use html5 rel and next
|
||||
tags as needed Tracker #372
|
||||
[chg] Updated css on site main control panel sh404SEFmodule
|
||||
[chg] Clarified one Swedish language tooltip
|
||||
[chg] Moved some options (itemid management) to core instead of plugin,
|
||||
so that they can be activated even if extension-specific plugin
|
||||
does not implement them
|
||||
|
||||
[bug] Whitelisting user agent doesn't prevent anti-flooding to operate
|
||||
[bug] Notice errors could be displayed if no similar urls found when
|
||||
trying to redirect a 404
|
||||
[bug] On multipage articles, when not displaying the table of content,
|
||||
first page has 2 different urls created Tracker #56
|
||||
[bug] under some circumstances, custom page title and meta data may not
|
||||
be inserted in the page Tracker #47
|
||||
[bug] getCategories function called statically, but not declared so,
|
||||
could cause some notices errors on some servers
|
||||
[bug] When auto-redirecting non-sef to SEF url, some get variables may
|
||||
not be passed along on some occasions
|
||||
[bug] Itemid is lost on "Edit article" icons for Joomla content Tracker
|
||||
#49
|
||||
[bug] Kunena plugin: wrong url for editing profile when "Short url to
|
||||
profile" option is enabled Tracker #369
|
||||
[bug] On 404 error pages, if user removes the "similar urls" text from
|
||||
the template, the marker for the faulty requested urls is not
|
||||
replaced either Tracker #370
|
||||
[bug] Regression: custom meta_ext plugin not used anymore
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
[ 2011-09-05 ] Version 3.3.0.1122
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
[new] Now providing a QRCode for each SEF url, in URL manager Tracker
|
||||
#319
|
||||
[new] Integrated Swedish translation, by BR Kristian Tracker #39
|
||||
[new] Improved SEO for paginated content: now use HTML5 prev and next
|
||||
tags on all paginated links Tracker #36
|
||||
[new] Added option to completely exclude category name from page title
|
||||
(on Joomla! content) Tracker #38
|
||||
|
||||
[chg] Added max url lenght on custom urls input fields
|
||||
[chg] Replaced a hardcoded text on control panel with language file
|
||||
string
|
||||
[chg] Removed ability to not translate urls (on multilingual sites), as
|
||||
this would cause errors, and we'll have to wait for Joomfish to
|
||||
build a general solution Tracker #42
|
||||
[chg] Updated com_banners plugin Tracker #43
|
||||
|
||||
[bug] Sometimes a warning could be displayed when redirecting an alias
|
||||
with an added query string
|
||||
[bug] Aliases that are non-sef urls are not taken into account
|
||||
[bug] Always append Itemid to SEF url would not work, moved language
|
||||
string to another file to fix Tracker #44
|
||||
[bug] Exact error message not displayed when saving data in Title and
|
||||
metas manager, displays generic message instead 'Something went
|
||||
wrong on the server' Tracker #41
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
[ 2011-07-19 ] Version 3.2.0.1108
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
[new] Joomla 1.7 compat: load mootols in backend
|
||||
|
||||
[chg] Compat with 1.7: adjusted to modified JDatabase class
|
||||
replacePrefix() method, would prevent shUrls to be created
|
||||
[chg] Added protection against sites with no default menu items, or
|
||||
default menu items set to not be publicly visible. Would cause a
|
||||
fatal error Tracker #33
|
||||
[chg] Reduced url max length, very long urls could cause 404 Tracker
|
||||
#32
|
||||
[chg] Added compatibility layer for shRouter::shGetPageInfo()
|
||||
|
||||
[bug] Installation of 404 page article was commented out!
|
||||
[bug] Aliases with a trailing / are not taken into a account
|
||||
[bug] Preview links in backend does not use /index.php/ if in 'no
|
||||
htaccess' mode
|
||||
[bug] Error doc configuration page would only list sh404SEF default
|
||||
error doc, would miss other Uncategorised articles in select list
|
||||
Tracker #35
|
||||
[bug] Missing shIncludeMetaPlugin() function in page content rewriting
|
||||
feature Tracker #31
|
||||
[bug] When auto-redirect from non-sef to sef is activated, it is not
|
||||
possible to edit articles or weblinks on the front end Tracker
|
||||
#30
|
||||
[bug] Still using old #__redirection table when checking new shurl
|
||||
creation, could prevent creation of shurl in some cases
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
[ 2011-06-30 ] Version 3.1.5.1098
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
[new] Properly handling multilanguage, the SEO way. Warning: Joomla!
|
||||
LanguageFilter system plugin must be DISABLED!! Tracker #26
|
||||
[new] Updated 3rd-party SEF url plugins installer to J! 1.6 format
|
||||
[new] Handling of Joomla! multi-lingual features, still need some
|
||||
tweaking for home page links Tracker #26
|
||||
[new] Automatic handling of canonical urls for print version of joomla
|
||||
articles
|
||||
[new] Added backward compatiblity class 'shRouter' for using J! 1.5
|
||||
plugins in J! 1.6 Tracker #24
|
||||
|
||||
[chg] Kunena plugin largely updated with help from Matias Griese,
|
||||
increased performance and compatibility with all Kunena versions
|
||||
Tracker #355
|
||||
[chg] Some SEF links may no be rewritten when Joomla SEF setting is
|
||||
off, as the SEF plugin checks directly Joomla config instead of
|
||||
asking router for current SEF mode Tracker #27
|
||||
[chg] Updated 'Home page URL' parameter tooltip
|
||||
[chg] Updated links to new support site url
|
||||
|
||||
[bug] Bad query on uninstalling sh404SEF while removing database tables
|
||||
[bug] May generate fatal error on page 2 or more of Blog view, when no
|
||||
specific number of items per page has been set at menu item level
|
||||
(ie using only global params)
|
||||
[bug] Using old $database global in com_newsfeeds and mosets tree
|
||||
plugin, would cause fatal error Tracker #28
|
||||
[bug] Sometimes urls consisting only of an Itemid were not properly
|
||||
handled
|
||||
[bug] Jomsocial frontpage and profile page have same url when user not
|
||||
logged in
|
||||
[bug] SEF url not created when a category is included in url
|
||||
[bug] Missing category for the * language when using more than one
|
||||
language can result in empty sef url
|
||||
[bug] On url list, first attempt to reach page 2 or more after logging
|
||||
in stays on page one Tracker #359
|
||||
[bug] Fatal error creating sef urls for Community Builder Tracker #23
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
[ 2011-06-07 ] Version 3.1.4.1078
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
[chg] Changed SEF url to user edit profile Tracker #22
|
||||
|
||||
[bug] "view" parameter not revoved from SEF urls for Search component
|
||||
Tracker #21
|
||||
[bug] Id not removed from sef url when showing "All categories" Tracker
|
||||
#19
|
||||
[bug] Fatal error in com_contact plugin Tracker #18
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
[ 2011-06-03 ] Version 3.1.3.1073
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
[new] For Uncategorized items, user can now select to either use only
|
||||
item title in url (recommended) or insert menu item title Tracker
|
||||
#12
|
||||
[new] Registration link was not SEF
|
||||
|
||||
[bug] Sometimes Meta description is not inserted by sh404sef Tracker
|
||||
#17
|
||||
[bug] Insert id in article url does not work when "All categories" is
|
||||
selected Tracker #14
|
||||
[bug] Create an account link cause fatal error Tracker #13
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
[ 2011-05-31 ] Version 3.1.2.1071
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
[new] Reworked short urls system, now called shURL, faster and shorter.
|
||||
Can be created without having to purge existing SEF urls. Prevent
|
||||
collisions with existing SEF or aliases, bad words filtering
|
||||
[new] Added shortlinks tag configuration page
|
||||
[new] Component, module and plugins install/uninstall, complying with
|
||||
preverve configuration settings
|
||||
|
||||
[chg] Separated forced update flag between version check and remote
|
||||
config check
|
||||
[chg] Converted old languages files to Joomla! language file format
|
||||
(ini)
|
||||
[chg] Version checker can now handle multiple version file servers
|
||||
|
||||
[bug] Error in database class, would cause purging urls to fail
|
||||
[bug] Can't use pagination links or other actions on meta manager after
|
||||
using the save button Tracker #336
|
||||
[bug] Issue with arrays in urls, when using only non-sef urls
|
||||
@ -0,0 +1,306 @@
|
||||
<?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
|
||||
*/
|
||||
|
||||
// Security check to ensure this file is being included by a parent file.
|
||||
if (!defined('_JEXEC'))
|
||||
die('Direct Access to this location is not allowed.');
|
||||
|
||||
/**
|
||||
* Implement analytics handling
|
||||
*
|
||||
* @author shumisha
|
||||
*
|
||||
*/
|
||||
class Sh404sefClassBaseanalytics
|
||||
{
|
||||
// default end point for the analytics service
|
||||
protected $_endPoint = '';
|
||||
|
||||
// default authorization url
|
||||
protected $_authPoint = '';
|
||||
|
||||
// application key
|
||||
protected $_appKeys = array();
|
||||
|
||||
// account list
|
||||
protected $_accounts = array();
|
||||
|
||||
// SEF configuration
|
||||
protected $_config = null;
|
||||
|
||||
// options for current request (ie account id, format
|
||||
protected $_options = null;
|
||||
|
||||
// authorization token, to be cached
|
||||
protected $_Auth = '';
|
||||
|
||||
public function fetchAnalytics($config, $options)
|
||||
{
|
||||
|
||||
// store parameters
|
||||
$this->_config = $config;
|
||||
$this->_options = $options;
|
||||
|
||||
// prepare a default response object
|
||||
$response = new stdClass();
|
||||
$response->status = true;
|
||||
$response->statusMessage = JText::_('COM_SH404SEF_CLICK_TO_CHECK_ANALYTICS');
|
||||
$response->note = '';
|
||||
|
||||
// connect to server and fetch data
|
||||
try
|
||||
{
|
||||
$rawResponse = $this->_fetchData();
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$response->status = false;
|
||||
$response->statusMessage = $e->getMessage();
|
||||
return $response;
|
||||
}
|
||||
|
||||
// return response
|
||||
$response->analyticsData = $rawResponse;
|
||||
|
||||
// attach html select list or input boxes to response, to allow user to filter the data viewed
|
||||
$response->filters = $this->_prepareFilters();
|
||||
|
||||
// update date/time display
|
||||
$response->statusMessage = JText::sprintf('COM_SH404SEF_UPDATED_ON', strftime('%c'));
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
protected function _fetchData()
|
||||
{
|
||||
|
||||
// first try to connect, if we don't already have a token
|
||||
$this->_getAuthToken();
|
||||
|
||||
// get the http client
|
||||
$hClient = Sh404sefHelperAnalytics::getHttpClient();
|
||||
|
||||
// fetch account list from supplier
|
||||
$this->_fetchAccountsList();
|
||||
|
||||
// and find about which one to use (use first one is none selected from a previous request
|
||||
//if (empty($this->_options['accountId']))
|
||||
//{
|
||||
$this->_options['accountId'] = Sh404sefHelperAnalytics::getDefaultAccountId($this->_accounts);
|
||||
//}
|
||||
|
||||
// check in case we don' have valid account ID
|
||||
if (empty($this->_options['accountId']))
|
||||
{
|
||||
throw new Sh404sefExceptionDefault('Empty account ID to query analytics API. Contact admin.');
|
||||
}
|
||||
|
||||
// create a report object
|
||||
$className = 'Sh404sefAdapterAnalytics' . strtolower($this->_config->analyticsType) . 'report' . strtolower($this->_options['report']);
|
||||
$report = new $className();
|
||||
|
||||
// ask it to perform API requests as needed,
|
||||
$dataResponse = $report->fetchData($this->_config, $this->_options, $this->_Auth, $this->_endPoint, $this->_getAppKey());
|
||||
|
||||
// return data response for further processing
|
||||
return $dataResponse;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch list of accounts, to be overloaded
|
||||
*/
|
||||
protected function _fetchAccountsList()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache authorization token
|
||||
*/
|
||||
protected function _getAuthToken()
|
||||
{
|
||||
|
||||
// create cache Id and get cache object
|
||||
$cacheId = md5($this->_config->analyticsPassword . $this->_config->analyticsUser . 'sdfhk546548-(}=])))');
|
||||
|
||||
$cache = JFactory::getCache('sh404sef_analytics_auth');
|
||||
$cache->setLifetime(10080); // cache result for 7 days
|
||||
$cache->setCaching(1); // force caching on
|
||||
|
||||
$this->_Auth = $cache->get(array($this, 'doGetAuthToken'), $args = array(), $cacheId);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* performs actuall request to get token
|
||||
*/
|
||||
public function doGetAuthToken()
|
||||
{
|
||||
|
||||
$this->_prepareConnectRequest();
|
||||
$connectReponse = $this->_connect();
|
||||
$this->_handleConnectResponse($connectReponse);
|
||||
|
||||
return $this->_Auth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects to analytics supplier
|
||||
*
|
||||
* Meant to be overloaded by adapter
|
||||
*
|
||||
* @param $config , sef config object, holding connecton parameters
|
||||
*/
|
||||
protected function _connect()
|
||||
{
|
||||
|
||||
// get the http client
|
||||
$hClient = Sh404sefHelperAnalytics::getHttpClient();
|
||||
|
||||
// establish connection with available methods
|
||||
$adapters = array('Zendshl_Http_Client_Adapter_Curl', 'Zendshl_Http_Client_Adapter_Socket');
|
||||
$rawResponse = null;
|
||||
|
||||
// perform connect request
|
||||
foreach ($adapters as $adapter)
|
||||
{
|
||||
try
|
||||
{
|
||||
$hClient->setAdapter($adapter);
|
||||
$rawResponse = $hClient->request();
|
||||
break;
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
// we failed, let's try another method
|
||||
}
|
||||
}
|
||||
|
||||
// return if error
|
||||
if (empty($rawResponse))
|
||||
{
|
||||
$msg = 'unknown code';
|
||||
ShlSystem_Log::debug( 'sh404sef', '%s::%s::%d: %s', __CLASS__, __METHOD__, __LINE__, JText::sprintf('COM_SH404SEF_ERROR_CHECKING_ANALYTICS', $msg));
|
||||
throw new Sh404sefExceptionDefault(JText::sprintf('COM_SH404SEF_ERROR_CHECKING_ANALYTICS', $msg));
|
||||
}
|
||||
if (!is_object($rawResponse) || $rawResponse->isError())
|
||||
{
|
||||
$msg = method_exists($rawResponse, 'getStatus') ? $rawResponse->getStatus() : 'unknown code';
|
||||
ShlSystem_Log::debug( 'sh404sef', '%s::%s::%d: %s', __CLASS__, __METHOD__, __LINE__, JText::sprintf('COM_SH404SEF_ERROR_CHECKING_ANALYTICS', $msg));
|
||||
throw new Sh404sefExceptionDefault(JText::sprintf('COM_SH404SEF_ERROR_CHECKING_ANALYTICS', $msg));
|
||||
}
|
||||
|
||||
// success, return response
|
||||
return $rawResponse;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Set client object to perform request
|
||||
* for connection to analytics service
|
||||
*
|
||||
* To be oveloaded
|
||||
*/
|
||||
protected function _prepareConnectRequest()
|
||||
{
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle response from connect request
|
||||
*
|
||||
* To be overloaded
|
||||
*
|
||||
*/
|
||||
protected function _handleConnectResponse($response)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user set parameters and request
|
||||
* data allow inserting tracking snippet
|
||||
*/
|
||||
protected function _shouldInsertSnippet()
|
||||
{
|
||||
// get config
|
||||
$config = &Sh404sefFactory::getConfig();
|
||||
|
||||
// check if we have a tracking code, no need to insert snippet if no tracking code
|
||||
if (empty($config->analyticsId) && ($config->analyticsEdition == 'ga' || $config->analyticsEdition == 'ga_and_uga'))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (empty($config->analyticsUgaId) && ($config->analyticsEdition == 'uga' || $config->analyticsEdition == 'ga_and_uga'))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (empty($config->analyticsGtmId) && $config->analyticsEdition == 'gtm')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if we are set to include tracking code for current user
|
||||
if (!sh404sefHelperGeneral::isInGroupList(JAccess::getGroupsByUser(JFactory::getUser()->id, $recursive = true), $config->analyticsUserGroups))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// check if current IP is on exclusion list
|
||||
if (!empty($config->analyticsExcludeIP))
|
||||
{
|
||||
$ip = empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR'];
|
||||
$exclude = Sh404sefHelperGeneral::checkIPList($ip, $config->analyticsExcludeIP);
|
||||
if ($exclude)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function _prepareCommonHeaders()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* prepare html filters to allow user to select the way she likes
|
||||
* to view reports
|
||||
*/
|
||||
protected function _prepareFilters()
|
||||
{
|
||||
// array to hold various filters
|
||||
$filters = array();
|
||||
|
||||
return $filters;
|
||||
}
|
||||
|
||||
protected function _getAppKey()
|
||||
{
|
||||
static $_key = null;
|
||||
|
||||
if (is_null($_key))
|
||||
{
|
||||
$remoteConfig = Sh404sefHelperUpdates::getRemoteConfig($forced = false);
|
||||
$keys = empty($remoteConfig->config['google_analytics_api_keys']) ? $this->_appKeys : $remoteConfig->config['google_analytics_api_keys'];
|
||||
$_key = $keys[mt_rand(0, count($keys) - 1)];
|
||||
}
|
||||
|
||||
return $_key;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,102 @@
|
||||
<?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
|
||||
*/
|
||||
|
||||
// Security check to ensure this file is being included by a parent file.
|
||||
if (!defined('_JEXEC')) die('Direct Access to this location is not allowed.');
|
||||
|
||||
// load a few constants
|
||||
require_once JPATH_ROOT . '/administrator/components/com_sh404sef/defines.php';
|
||||
|
||||
/**
|
||||
* URL caching
|
||||
*
|
||||
* @author shumisha
|
||||
*
|
||||
*/
|
||||
class Sh404sefClassBasecache {
|
||||
|
||||
protected static $_instance = null;
|
||||
|
||||
// general configuration
|
||||
protected $_config = null;
|
||||
|
||||
// cache content
|
||||
protected $_urlCache = array();
|
||||
protected $_urlCacheCount = 0;
|
||||
protected $_urlCacheCreationDate = null;
|
||||
|
||||
// cache stats
|
||||
protected $_urlCacheMisses = 0;
|
||||
protected $_urlCacheHits = 0;
|
||||
protected $_urlCacheMissesList = array();
|
||||
|
||||
public function __construct( $config) {
|
||||
|
||||
// store sef config
|
||||
$this->_config = $config;
|
||||
|
||||
}
|
||||
|
||||
// fetch an URL from cache, return null if not found
|
||||
public function getSefUrlFromCache( $nonSefUrl, & $sefUrl) {
|
||||
|
||||
return sh404SEF_URLTYPE_NONE;
|
||||
|
||||
}
|
||||
|
||||
// fetch an URL from cache, return null if not found
|
||||
public function getNonSefUrlFromCache( $sefUrl, & $nonSefUrl) {
|
||||
|
||||
return sh404SEF_URLTYPE_NONE;
|
||||
}
|
||||
|
||||
public function addSefUrlToCache( $nonSefUrl, $sefUrl, $UrlType) {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
public function removeUrlFromCache( $nonSefUrlList) {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
public function purge() {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function getCacheStats() {
|
||||
|
||||
$cacheTotal = $this->_urlCacheMisses+$this->_urlCacheHits;
|
||||
$out = 'Cache hits : '. $this->_urlCacheHits . " [".( !empty( $cacheTotal) ? (int)(100*$this->_urlCacheHits/$cacheTotal) . '%' : 'N/A') .']<br />';
|
||||
$out .= 'Cache misses : '. $this->_urlCacheMisses . " [".( !empty( $cacheTotal) ? (int)(100*$this->_urlCacheMisses/$cacheTotal) . '%' : 'N/A'). ']<br />';
|
||||
$out .= 'Cache total : '. $cacheTotal . '<br />';
|
||||
$out .= 'Url added to cache : '. $this->_urlCacheCount . '<br />';
|
||||
$out .= '<br /><br /><br />Misses list';
|
||||
foreach($this->_urlCacheMissesList as $url) {
|
||||
$out .= '<pre>'.$url.'</pre><br />';
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
protected function _varExport( $cache, $start) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,157 @@
|
||||
<?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
|
||||
*/
|
||||
|
||||
// Security check to ensure this file is being included by a parent file.
|
||||
if (!defined('_JEXEC')) die('Direct Access to this location is not allowed.');
|
||||
|
||||
jimport( 'joomla.application.component.controller' );
|
||||
|
||||
Class Sh404sefClassBasecontroller extends ShlMvcController_Base {
|
||||
|
||||
protected $_context = 'com_sh404sef';
|
||||
|
||||
protected $_defaultController = '';
|
||||
protected $_defaultTask = '';
|
||||
protected $_defaultModel = '';
|
||||
protected $_defaultView = 'default';
|
||||
protected $_defaultLayout = 'default';
|
||||
|
||||
protected $_returnController = '';
|
||||
protected $_returnTask = '';
|
||||
protected $_returnView = 'default';
|
||||
protected $_returnLayout = 'default';
|
||||
|
||||
/**
|
||||
* Display the view
|
||||
*/
|
||||
public function display($cachable = false, $urlparams = false) {
|
||||
|
||||
// catch up any result message coming from an
|
||||
// ajax save for instance, and push that into
|
||||
// the application message queue
|
||||
$messageCode = JRequest::getCmd( 'sh404sefMsg');
|
||||
if (!empty($messageCode)) {
|
||||
$msg = JText::_( $messageCode);
|
||||
if ($msg != $messageCode) {
|
||||
// if no language string exists, JText will
|
||||
// return the input string, so only display if
|
||||
// we have something to display
|
||||
$app = JFactory::getApplication();
|
||||
$app->enqueuemessage( $msg);
|
||||
}
|
||||
}
|
||||
// Set the default view name in case it's missing
|
||||
$viewName = JRequest::getWord('view');
|
||||
if (empty( $viewName)) {
|
||||
JRequest::setVar( 'view', $this->_defaultView);
|
||||
}
|
||||
|
||||
$document =JFactory::getDocument();
|
||||
|
||||
$viewType = $document->getType();
|
||||
$viewName = JRequest::getCmd( 'view', $this->_defaultView);
|
||||
$viewLayout = JRequest::getCmd( 'layout', $this->_defaultLayout );
|
||||
|
||||
$view = $this->getView( $viewName, $viewType, '', array( 'base_path'=>$this->basePath));
|
||||
|
||||
// Get/Create the model
|
||||
if ($model = $this->getModel($viewName)) {
|
||||
// store initial context in model
|
||||
$model->setContext( $this->_context);
|
||||
|
||||
// Push the model into the view (as default)
|
||||
$view->setModel($model, true);
|
||||
|
||||
// and push also the default redirect
|
||||
$view->defaultRedirectUrl = $this->_getDefaultRedirect( array( 'layout' => $viewLayout));
|
||||
|
||||
}
|
||||
|
||||
// Set the layout
|
||||
$view->setLayout($viewLayout);
|
||||
|
||||
// push controller errors in the view
|
||||
$error = $this->getError();
|
||||
if (!empty( $error)) {
|
||||
$view->setError( $error);
|
||||
}
|
||||
|
||||
// Display the view
|
||||
$view->display();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Method implementing cancelling the list view
|
||||
* Go back to dashboard
|
||||
*
|
||||
*/
|
||||
public function dashboard() {
|
||||
|
||||
// define where we want to go : dashboard ie all default values
|
||||
$bits = array( 'c' => '', 'task' => '', 'view' => '');
|
||||
|
||||
// get there
|
||||
$this->setRedirect( $this->_getDefaultRedirect( $bits));
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue a series of messages in the application message
|
||||
* queue, while JApplication::enqueuemessage will do it
|
||||
* one at a time
|
||||
* @param array of strings $msgs the messages
|
||||
* @param string $msgType the message type (same for all messages in $msgs)
|
||||
*/
|
||||
public function enqueuemessages( $msgs, $msgType = null) {
|
||||
|
||||
// nothing to do if no messages
|
||||
if (empty( $msgs)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get application
|
||||
$app = JFactory::getApplication();
|
||||
|
||||
// loop messages and enqueue
|
||||
foreach( $msgs as $msg) {
|
||||
$app->enqueuemessage( $msg, $msgType);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Builds a (non routed) Joomla url according to default values
|
||||
* of controller, task, view and layout
|
||||
*
|
||||
* @param array $userVars optional set of variables to override default vars, or add more to url
|
||||
* @return string the target url, not routed
|
||||
*/
|
||||
protected function _getDefaultRedirect( $userVars = array()) {
|
||||
|
||||
// get default values of the controller
|
||||
$defaultVars = array( 'c' => $this->_returnController, 'layout' => $this->_returnLayout, 'view' => $this->_returnView, 'task' => $this->_returnTask);
|
||||
|
||||
// override default vars with user defined vars
|
||||
$vars = array_merge( $defaultVars, $userVars);
|
||||
|
||||
// strip empty vars
|
||||
foreach( $vars as $key => $value) {
|
||||
if ($value == '') {
|
||||
unset( $vars[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
return Sh404sefHelperUrl::buildUrl( $vars);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,213 @@
|
||||
<?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
|
||||
*/
|
||||
|
||||
// Security check to ensure this file is being included by a parent file.
|
||||
if (!defined('_JEXEC')) die('Direct Access to this location is not allowed.');
|
||||
|
||||
class Sh404sefClassBaseEditController extends Sh404sefClassBasecontroller {
|
||||
|
||||
protected $_context = 'com_sh404sef.edit';
|
||||
|
||||
protected $_editController = '';
|
||||
protected $_editTask = 'edit';
|
||||
protected $_editLayout = '';
|
||||
|
||||
protected $_returnController = '';
|
||||
protected $_returnTask = '';
|
||||
protected $_returnView = 'default';
|
||||
protected $_returnLayout = 'default';
|
||||
|
||||
|
||||
/**
|
||||
* Id of the currently edited item
|
||||
* @var integer
|
||||
*/
|
||||
protected $_id = null;
|
||||
|
||||
/**
|
||||
* POST data saved in case of need to edit again
|
||||
* same data (if error detected)
|
||||
* @var mixed
|
||||
*/
|
||||
protected $_editData = null;
|
||||
|
||||
/**
|
||||
* Display the view
|
||||
*/
|
||||
public function display($cachable = false, $urlparams = false) {
|
||||
|
||||
// Set the default view name in case it's missing
|
||||
$viewName = JRequest::getWord('view');
|
||||
if (empty( $viewName)) {
|
||||
JRequest::setVar( 'view', $this->_defaultView);
|
||||
}
|
||||
|
||||
$document =JFactory::getDocument();
|
||||
|
||||
$viewType = $document->getType();
|
||||
$viewName = JRequest::getCmd( 'view');
|
||||
$this->_editView = $viewName;
|
||||
$viewLayout = JRequest::getCmd( 'layout', $this->_defaultLayout );
|
||||
|
||||
$view = $this->getView( $viewName, $viewType, '', array( 'base_path'=>$this->basePath));
|
||||
|
||||
// Get/Create the model
|
||||
if ($model = $this->getModel($viewName)) {
|
||||
// store initial context in model
|
||||
$model->setContext( $this->_context);
|
||||
|
||||
// if we have some stored data from a previous attempt at editing
|
||||
// that failed because of validation, then push saved data into
|
||||
// the model, to redisplay it for user to fix or cancel
|
||||
if (!is_null($this->_editData)) {
|
||||
$model->setData( $this->_editData);
|
||||
}
|
||||
|
||||
// Push the model into the view (as default)
|
||||
$view->setModel($model, true);
|
||||
|
||||
// and push also the default redirect
|
||||
$view->defaultRedirectUrl = $this->_getDefaultRedirect();
|
||||
|
||||
}
|
||||
|
||||
// Set the layout
|
||||
$view->setLayout($viewLayout);
|
||||
|
||||
// push controller errors in the view
|
||||
$error = $this->getError();
|
||||
if (!empty( $error)) {
|
||||
$view->setError( $error);
|
||||
}
|
||||
|
||||
// Display the view
|
||||
$view->display();
|
||||
|
||||
}
|
||||
|
||||
public function edit() {
|
||||
|
||||
// hide the main menu
|
||||
JRequest::setVar('hidemainmenu', 1);
|
||||
|
||||
// find and store edited item id
|
||||
$cid = JRequest::getVar('cid', array(0), 'default', 'array');
|
||||
$this->_id = $cid[0];
|
||||
|
||||
// let the base controller do the rest
|
||||
$this->display();
|
||||
|
||||
}
|
||||
|
||||
public function save() {
|
||||
|
||||
// Check for request forgeries
|
||||
JRequest::checkToken() or jexit( 'Invalid Token' );
|
||||
|
||||
// save incoming data
|
||||
$this->_editData = JRequest::get('post');
|
||||
|
||||
// find and store edited item id
|
||||
$this->_id = JRequest::getInt('id');
|
||||
|
||||
// perform saving of incoming data
|
||||
$savedId = $this->_doSave( $this->_editData);
|
||||
|
||||
// error ?
|
||||
if (empty( $savedId)) {
|
||||
// edit again with same data
|
||||
$errorMsg = $this->getError();
|
||||
$errorMsg = empty( $errorMsg) ? $this->_getMessage( 'failure') : $errorMsg;
|
||||
$this->setError( $errorMsg);
|
||||
JRequest::setVar( 'c', $this->_editController);
|
||||
JRequest::setVar( 'task', $this->_editTask);
|
||||
JRequest::setVar( 'cid', array($this->_id));
|
||||
// in case of error, if this is an ajax call,
|
||||
// we simply return the error to the caller
|
||||
$isAjax = JRequest::getInt( 'shajax') == 1;
|
||||
if(!$isAjax) {
|
||||
// if not ajax, we edit again the same page
|
||||
$this->edit();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// saved, no need to keep them
|
||||
$this->_editData = null;
|
||||
|
||||
// display response
|
||||
$this->display();
|
||||
}
|
||||
|
||||
public function apply() {
|
||||
|
||||
// Check for request forgeries
|
||||
JRequest::checkToken() or jexit( 'Invalid Token' );
|
||||
|
||||
// just an alias for save
|
||||
$this->save();
|
||||
|
||||
}
|
||||
|
||||
public function cancel() {
|
||||
|
||||
// Check for request forgeries
|
||||
JRequest::checkToken() or jexit( 'Invalid Token' );
|
||||
|
||||
$this->setRedirect( $this->_getDefaultRedirect(), $this->_getMessage( 'cancel'));
|
||||
}
|
||||
|
||||
protected function setId( $id) {
|
||||
|
||||
$this->_id = $id;
|
||||
|
||||
}
|
||||
|
||||
protected function _getMessage( $type) {
|
||||
|
||||
switch ($type) {
|
||||
case 'success':
|
||||
$msg = JText::_('COM_SH404SEF_ELEMENT_SAVED');
|
||||
break;
|
||||
case 'failure':
|
||||
$msg = JText::_('COM_SH404SEF_ELEMENT_NOT_SAVED');
|
||||
break;
|
||||
case 'cancel':
|
||||
$msg = JText::_('COM_SH404SEF_CANCELLED');
|
||||
break;
|
||||
default:
|
||||
$msg = '';
|
||||
break;
|
||||
}
|
||||
|
||||
return $msg;
|
||||
|
||||
}
|
||||
|
||||
protected function _doSave( $dataArray, $urlType = sh404SEF_URLTYPE_CUSTOM) {
|
||||
|
||||
// get model
|
||||
$model = $this->getModel( $this->_defaultView);
|
||||
|
||||
// perform save operation
|
||||
$savedId = $model->save( $dataArray, $urlType);
|
||||
|
||||
// store error message
|
||||
if (empty( $savedId)) {
|
||||
$this->setError( $model->getError());
|
||||
}
|
||||
|
||||
// return result
|
||||
return $savedId;
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
<?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
|
||||
*/
|
||||
|
||||
// Security check to ensure this file is being included by a parent file.
|
||||
if (!defined('_JEXEC')) die('Direct Access to this location is not allowed.');
|
||||
|
||||
abstract class Sh404sefClassBaseeditmodel extends Sh404sefClassBaseModel {
|
||||
|
||||
protected $_context = 'sh404sef.edit';
|
||||
|
||||
}
|
||||
@ -0,0 +1,138 @@
|
||||
<?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
|
||||
*/
|
||||
|
||||
// Security check to ensure this file is being included by a parent file.
|
||||
if (!defined('_JEXEC')) die('Direct Access to this location is not allowed.');
|
||||
|
||||
jimport( 'joomla.plugin.plugin' );
|
||||
|
||||
/**
|
||||
* Base extension handler plugin
|
||||
*
|
||||
* @author Yannick Gaultier
|
||||
*/
|
||||
class Sh404sefClassBaseextplugin extends JPlugin {
|
||||
|
||||
|
||||
const TYPE_DEFAULT = 0;
|
||||
const TYPE_SKIP = 1;
|
||||
const TYPE_SIMPLE = 2;
|
||||
|
||||
const TYPE_SH404SEF_ROUTER = 0; // do not change, must be 0 for compat reason
|
||||
const TYPE_JOOMLA_ROUTER = 1; // do not change, must be 1 for compat reason
|
||||
|
||||
const TYPE_JOOMSEF_ROUTER = 30;
|
||||
const TYPE_ACESEF_ROUTER = 40;
|
||||
const TYPE_NO_ROUTER = 100;
|
||||
const TYPE_OPENSEF_ROUTER = 101;
|
||||
|
||||
protected $_componentType = null;
|
||||
protected $_pluginType = null;
|
||||
|
||||
protected $_option = '';
|
||||
protected $_optionNoCom = '';
|
||||
protected $_extName = '';
|
||||
protected $_config = null;
|
||||
protected $_sefPluginPath = null;
|
||||
protected $_metaPluginPath = null;
|
||||
|
||||
protected $_version = '';
|
||||
|
||||
|
||||
public function __construct( $option, $config) {
|
||||
|
||||
$this->_option = $option;
|
||||
$this->_optionNoCom = str_replace('com_', '', $this->_option);
|
||||
$this->_config = $config;
|
||||
$this->_pluginType = self::TYPE_NO_ROUTER;
|
||||
|
||||
}
|
||||
|
||||
public function getComponentType() {
|
||||
|
||||
if(is_null( $this->_componentType)) {
|
||||
switch ($this->_optionNoCom) {
|
||||
// leave urls non-sef
|
||||
case (in_array($this->_optionNoCom, $this->_config->skip)):
|
||||
$this->_componentType = self::TYPE_SKIP;
|
||||
break;
|
||||
// use simple encoding
|
||||
case (in_array($this->_optionNoCom, $this->_config->nocache)):
|
||||
$this->_componentType = self::TYPE_SIMPLE;
|
||||
break;
|
||||
// default handler, full sef
|
||||
default:
|
||||
$this->_componentType = self::TYPE_DEFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
// now validate that type: if required plugin is not available,
|
||||
// we'll need to switch back to "simple
|
||||
if($this->_componentType == self::TYPE_DEFAULT) {
|
||||
$path = $this->getSefPluginPath();
|
||||
if(empty( $path)) {
|
||||
$this->_componentType = self::TYPE_SIMPLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this->_componentType;
|
||||
}
|
||||
|
||||
public function getPluginType() {
|
||||
|
||||
return $this->_pluginType;
|
||||
|
||||
}
|
||||
|
||||
public function getName() {
|
||||
|
||||
return $this->_extName;
|
||||
}
|
||||
|
||||
public function getVersion() {
|
||||
|
||||
return $this->_version;
|
||||
}
|
||||
|
||||
public function getSefPluginPath( $nonSefVars = array()) {
|
||||
|
||||
if(is_null( $this->_sefPluginPath)) {
|
||||
$this->_findSefPluginPath( $nonSefVars);
|
||||
}
|
||||
|
||||
return $this->_sefPluginPath;
|
||||
}
|
||||
|
||||
public function getMetaPluginPath( $nonSefUrl = '') {
|
||||
|
||||
if(is_null( $this->_metaPluginPath)) {
|
||||
$this->_findMetaPluginPath( $nonSefUrl);
|
||||
}
|
||||
|
||||
return $this->_metaPluginPath;
|
||||
|
||||
}
|
||||
|
||||
|
||||
protected function _findSefPluginPath( $nonSefVars = array()) {
|
||||
|
||||
$this->_sefPluginPath = '';
|
||||
|
||||
}
|
||||
|
||||
protected function _findMetaPluginPath( $nonSefUrl = '') {
|
||||
|
||||
$this->_metaPluginPath = '';
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,450 @@
|
||||
<?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
|
||||
*/
|
||||
|
||||
/**
|
||||
* Adapted for SEF module for Joomla!
|
||||
*
|
||||
* @author $Author: shumisha $
|
||||
* @copyright Yannick Gaultier - 2007-2011
|
||||
* @package sh404SEF-16
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU/GPL
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
// No direct access
|
||||
defined('JPATH_BASE') or die;
|
||||
|
||||
require_once JPATH_LIBRARIES . '/joomla/installer/adapters/plugin.php';
|
||||
|
||||
/**
|
||||
* Plugin installer
|
||||
*
|
||||
* @package Joomla.Framework
|
||||
* @subpackage Installer
|
||||
* @since 1.5
|
||||
*/
|
||||
class Sh404sefClassBaseinstalladapter extends JInstallerPlugin
|
||||
{
|
||||
|
||||
protected $_basePath = '';
|
||||
|
||||
protected $_group = 'sh404sefextplugins';
|
||||
protected $_installType = 'plugin';
|
||||
|
||||
/** @var string install function routing */
|
||||
var $route = 'install';
|
||||
|
||||
protected $manifest = null;
|
||||
protected $manifest_script = null;
|
||||
protected $name = null;
|
||||
protected $scriptElement = null;
|
||||
protected $oldFiles = null;
|
||||
|
||||
public function __construct(&$parent, &$db, $options = Array()) {
|
||||
|
||||
parent::__construct($parent, $db, $options);
|
||||
$this->_basePath = JPATH_ROOT . '/' . 'plugins';
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom install method
|
||||
*
|
||||
* @access public
|
||||
* @return boolean True on success
|
||||
* @since 1.5
|
||||
*/
|
||||
public function install()
|
||||
{
|
||||
|
||||
// set our target path
|
||||
$this->parent->setPath( 'extension_root', $this->_getPath());
|
||||
|
||||
// Get a database connector object
|
||||
$db = $this->parent->getDbo();
|
||||
|
||||
// Get the extension manifest object
|
||||
$this->manifest = $this->parent->getManifest();
|
||||
|
||||
$xml = $this->manifest;
|
||||
|
||||
/**
|
||||
* ---------------------------------------------------------------------------------------------
|
||||
* Manifest Document Setup Section
|
||||
* ---------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
// Set the extensions name
|
||||
$name = (string)$xml->name;
|
||||
$name = JFilterInput::getInstance()->clean($name, 'string');
|
||||
$this->set('name', $name);
|
||||
|
||||
// Get the component description
|
||||
$description = (string)$xml->description;
|
||||
if ($description) {
|
||||
$this->parent->set('message', JText::_($description));
|
||||
}
|
||||
else {
|
||||
$this->parent->set('message', '');
|
||||
}
|
||||
|
||||
/*
|
||||
* Backward Compatability
|
||||
* @todo Deprecate in future version
|
||||
*/
|
||||
$type = (string)$xml->attributes()->type;
|
||||
$pname = $this->_getElement($xml);
|
||||
|
||||
$group = $this->_group;
|
||||
if ($type == $this->_installType && !empty( $pname)) {
|
||||
$this->parent->setPath('extension_root', JPATH_PLUGINS. '/' .$group. '/' .$element);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_PLG_INSTALL_NO_FILE', JText::_('JLIB_INSTALLER_'.$this->route)));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Check if we should enable overwrite settings
|
||||
*/
|
||||
// Check to see if a plugin by the same name is already installed
|
||||
$query = 'SELECT `extension_id`' .
|
||||
' FROM `#__extensions`' .
|
||||
' WHERE folder = '.$db->Quote($group) .
|
||||
' AND element = '.$db->Quote($element);
|
||||
$db->setQuery($query);
|
||||
try {
|
||||
$db->execute();
|
||||
}
|
||||
catch(JException $e)
|
||||
{
|
||||
// Install failed, roll back changes
|
||||
$this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_PLG_INSTALL_ROLLBACK', JText::_('JLIB_INSTALLER_'.$this->route), $db->stderr(true)));
|
||||
return false;
|
||||
}
|
||||
$id = $db->loadResult();
|
||||
|
||||
// if its on the fs...
|
||||
if (file_exists($this->parent->getPath('extension_root')) && (!$this->parent->isOverwrite() || $this->parent->isUpgrade()))
|
||||
{
|
||||
$updateElement = $xml->update;
|
||||
// upgrade manually set
|
||||
// update function available
|
||||
// update tag detected
|
||||
if ($this->parent->isUpgrade() || ($this->parent->manifestClass && method_exists($this->parent->manifestClass,'update')) || is_a($updateElement, 'JXMLElement'))
|
||||
{
|
||||
// force these one
|
||||
$this->parent->setOverwrite(true);
|
||||
$this->parent->setUpgrade(true);
|
||||
if ($id) { // if there is a matching extension mark this as an update; semantics really
|
||||
$this->route = 'update';
|
||||
}
|
||||
}
|
||||
else if (!$this->parent->isOverwrite())
|
||||
{
|
||||
// overwrite is set
|
||||
// we didn't have overwrite set, find an udpate function or find an update tag so lets call it safe
|
||||
$this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_PLG_INSTALL_DIRECTORY', JText::_('JLIB_INSTALLER_'.$this->route), $this->parent->getPath('extension_root')));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ---------------------------------------------------------------------------------------------
|
||||
* Installer Trigger Loading
|
||||
* ---------------------------------------------------------------------------------------------
|
||||
*/
|
||||
// If there is an manifest class file, lets load it; we'll copy it later (don't have dest yet)
|
||||
if ((string)$xml->scriptfile)
|
||||
{
|
||||
$manifestScript = (string)$xml->scriptfile;
|
||||
$manifestScriptFile = $this->parent->getPath('source'). '/' .$manifestScript;
|
||||
if (is_file($manifestScriptFile))
|
||||
{
|
||||
// load the file
|
||||
include_once $manifestScriptFile;
|
||||
}
|
||||
// Set the class name
|
||||
$classname = 'plg'.$group.$element.'InstallerScript';
|
||||
if (class_exists($classname))
|
||||
{
|
||||
// create a new instance
|
||||
$this->parent->manifestClass = new $classname($this);
|
||||
// and set this so we can copy it later
|
||||
$this->set('manifest_script', $manifestScript);
|
||||
// Note: if we don't find the class, don't bother to copy the file
|
||||
}
|
||||
}
|
||||
|
||||
// run preflight if possible (since we know we're not an update)
|
||||
ob_start();
|
||||
ob_implicit_flush(false);
|
||||
if ($this->parent->manifestClass && method_exists($this->parent->manifestClass,'preflight'))
|
||||
{
|
||||
if($this->parent->manifestClass->preflight($this->route, $this) === false)
|
||||
{
|
||||
// Install failed, rollback changes
|
||||
$this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_PLG_INSTALL_CUSTOM_INSTALL_FAILURE'));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$msg = ob_get_contents(); // create msg object; first use here
|
||||
ob_end_clean();
|
||||
|
||||
/**
|
||||
* ---------------------------------------------------------------------------------------------
|
||||
* Filesystem Processing Section
|
||||
* ---------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
// If the plugin directory does not exist, lets create it
|
||||
$created = false;
|
||||
if (!file_exists($this->parent->getPath('extension_root')))
|
||||
{
|
||||
if (!$created = JFolder::create($this->parent->getPath('extension_root')))
|
||||
{
|
||||
$this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_PLG_INSTALL_CREATE_DIRECTORY', JText::_('JLIB_INSTALLER_'.$this->route), $this->parent->getPath('extension_root')));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// if we're updating at this point when there is always going to be an extension_root find the old xml files
|
||||
if($this->route == 'update')
|
||||
{
|
||||
// Hunt for the original XML file
|
||||
$old_manifest = null;
|
||||
$tmpInstaller = new JInstaller(); // create a new installer because findManifest sets stuff; side effects!
|
||||
// look in the extension root
|
||||
$tmpInstaller->setPath('source', $this->parent->getPath('extension_root'));
|
||||
if ($tmpInstaller->findManifest())
|
||||
{
|
||||
$old_manifest = $tmpInstaller->getManifest();
|
||||
$this->oldFiles = $old_manifest->files;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If we created the plugin directory and will want to remove it if we
|
||||
* have to roll back the installation, lets add it to the installation
|
||||
* step stack
|
||||
*/
|
||||
if ($created) {
|
||||
$this->parent->pushStep(array ('type' => 'folder', 'path' => $this->parent->getPath('extension_root')));
|
||||
}
|
||||
|
||||
// Copy all necessary files
|
||||
if ($this->parent->parseFiles($xml->files, -1, $this->oldFiles) === false)
|
||||
{
|
||||
// Install failed, roll back changes
|
||||
$this->parent->abort();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse optional tags -- media and language files for plugins go in admin app
|
||||
$this->parent->parseMedia($xml->media, 1);
|
||||
$this->parent->parseLanguages($xml->languages, 1);
|
||||
|
||||
// If there is a manifest script, lets copy it.
|
||||
if ($this->get('manifest_script'))
|
||||
{
|
||||
$path['src'] = $this->parent->getPath('source'). '/' .$this->get('manifest_script');
|
||||
$path['dest'] = $this->parent->getPath('extension_root'). '/' .$this->get('manifest_script');
|
||||
|
||||
if (!file_exists($path['dest']))
|
||||
{
|
||||
if (!$this->parent->copyFiles(array ($path)))
|
||||
{
|
||||
// Install failed, rollback changes
|
||||
$this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_PLG_INSTALL_MANIFEST', JText::_('JLIB_INSTALLER_'.$this->route)));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ---------------------------------------------------------------------------------------------
|
||||
* Database Processing Section
|
||||
* ---------------------------------------------------------------------------------------------
|
||||
*/
|
||||
$row = JTable::getInstance('extension');
|
||||
// Was there a plugin already installed with the same name?
|
||||
if ($id)
|
||||
{
|
||||
if (!$this->parent->isOverwrite())
|
||||
{
|
||||
// Install failed, roll back changes
|
||||
$this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_PLG_INSTALL_ALLREADY_EXISTS', JText::_('JLIB_INSTALLER_'.$this->route), $this->get('name')));
|
||||
return false;
|
||||
}
|
||||
$row->load($id);
|
||||
$row->name = $this->get('name');
|
||||
$row->manifest_cache = $this->parent->generateManifestCache();
|
||||
$row->store(); // update the manifest cache and name
|
||||
}
|
||||
else
|
||||
{
|
||||
// Store in the extensions table (1.6)
|
||||
$row->name = $this->get('name');
|
||||
$row->type = 'plugin';
|
||||
$row->ordering = 0;
|
||||
$row->element = $element;
|
||||
$row->folder = $group;
|
||||
$row->enabled = 1;
|
||||
$row->protected = 0;
|
||||
$row->access = 1;
|
||||
$row->client_id = 0;
|
||||
$row->params = $this->parent->getParams();
|
||||
$row->custom_data = ''; // custom data
|
||||
$row->system_data = ''; // system data
|
||||
$row->manifest_cache = $this->parent->generateManifestCache();
|
||||
|
||||
// Editor plugins are published by default
|
||||
if ($group == 'editors') {
|
||||
$row->enabled = 1;
|
||||
}
|
||||
|
||||
if (!$row->store())
|
||||
{
|
||||
// Install failed, roll back changes
|
||||
$this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_PLG_INSTALL_ROLLBACK', JText::_('JLIB_INSTALLER_'.$this->route), $db->stderr(true)));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Since we have created a plugin item, we add it to the installation step stack
|
||||
// so that if we have to rollback the changes we can undo it.
|
||||
$this->parent->pushStep(array ('type' => 'extension', 'id' => $row->extension_id));
|
||||
$id = $row->extension_id;
|
||||
}
|
||||
|
||||
/*
|
||||
* Let's run the queries for the module
|
||||
* If Joomla 1.5 compatible, with discreet sql files - execute appropriate
|
||||
* file for utf-8 support or non-utf-8 support
|
||||
*/
|
||||
// try for Joomla 1.5 type queries
|
||||
// second argument is the utf compatible version attribute
|
||||
if(strtolower($this->route) == 'install') {
|
||||
$utfresult = $this->parent->parseSQLFiles($this->manifest->install->sql);
|
||||
if ($utfresult === false)
|
||||
{
|
||||
// Install failed, rollback changes
|
||||
$this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_PLG_INSTALL_SQL_ERROR', JText::_('JLIB_INSTALLER_'.$this->route), $db->stderr(true)));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set the schema version to be the latest update version
|
||||
if($this->manifest->update) {
|
||||
$this->parent->setSchemaVersion($this->manifest->update->schemas, $row->extension_id);
|
||||
}
|
||||
} else if(strtolower($this->route) == 'update') {
|
||||
if($this->manifest->update)
|
||||
{
|
||||
$result = $this->parent->parseSchemaUpdates($this->manifest->update->schemas, $row->extension_id);
|
||||
if ($result === false)
|
||||
{
|
||||
// Install failed, rollback changes
|
||||
$this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_PLG_UPDATE_SQL_ERROR', $db->stderr(true)));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start Joomla! 1.6
|
||||
ob_start();
|
||||
ob_implicit_flush(false);
|
||||
if ($this->parent->manifestClass && method_exists($this->parent->manifestClass,$this->route))
|
||||
{
|
||||
if($this->parent->manifestClass->{$this->route}($this) === false)
|
||||
{
|
||||
// Install failed, rollback changes
|
||||
$this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_PLG_INSTALL_CUSTOM_INSTALL_FAILURE'));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$msg .= ob_get_contents(); // append messages
|
||||
ob_end_clean();
|
||||
|
||||
/**
|
||||
* ---------------------------------------------------------------------------------------------
|
||||
* Finalization and Cleanup Section
|
||||
* ---------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
// Lastly, we will copy the manifest file to its appropriate place.
|
||||
if (!$this->parent->copyManifest(-1))
|
||||
{
|
||||
// Install failed, rollback changes
|
||||
$this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_PLG_INSTALL_COPY_SETUP', JText::_('JLIB_INSTALLER_'.$this->route)));
|
||||
return false;
|
||||
}
|
||||
// And now we run the postflight
|
||||
ob_start();
|
||||
ob_implicit_flush(false);
|
||||
if ($this->parent->manifestClass && method_exists($this->parent->manifestClass,'postflight'))
|
||||
{
|
||||
$this->parent->manifestClass->postflight($this->route, $this);
|
||||
}
|
||||
$msg .= ob_get_contents(); // append messages
|
||||
ob_end_clean();
|
||||
if ($msg != '') {
|
||||
$this->parent->set('extension_message', $msg);
|
||||
}
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get unique element id for the plugin
|
||||
*
|
||||
*/
|
||||
protected function _getElement( $xml) {
|
||||
|
||||
static $_element = null;
|
||||
|
||||
if( is_null( $_element)) {
|
||||
// get name, should work for Joomsef, not sure for Aces, as
|
||||
// I have seen some plugin with wrong filename attribute
|
||||
// should use the "extension" field with acesef
|
||||
if (count($xml->files->children()))
|
||||
{
|
||||
foreach ($xml->files->children() as $file)
|
||||
{
|
||||
$type = $this->_installType;
|
||||
if ((string)$file->attributes()->$type)
|
||||
{
|
||||
$_element = (string)$file->attributes()->$type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return $_element;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sub dir of given plugin, usually based on name
|
||||
* of extension. Obtaining that name will vary based
|
||||
* on the type of plugin
|
||||
*/
|
||||
protected function _getPath() {
|
||||
|
||||
return $this->_basePath . '/' . $this->_group;
|
||||
|
||||
}
|
||||
|
||||
protected function _fixManifest() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,444 @@
|
||||
<?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
|
||||
*/
|
||||
|
||||
// Security check to ensure this file is being included by a parent file.
|
||||
if (!defined('_JEXEC')) die('Direct Access to this location is not allowed.');
|
||||
|
||||
|
||||
class Sh404sefClassBaselistmodel extends Sh404sefClassBasemodel {
|
||||
|
||||
/**
|
||||
* Data array
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_data = null;
|
||||
|
||||
/**
|
||||
* Data total
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $_total = null;
|
||||
|
||||
/**
|
||||
* Pagination object
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $_pagination = null;
|
||||
|
||||
/**
|
||||
* Holds current context, ie : controller/model/view/layout hierarchy
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_context = null;
|
||||
|
||||
/**
|
||||
* Method to get lists item data
|
||||
*
|
||||
* @access public
|
||||
* @param object holding options
|
||||
* @param boolea $returnZeroElement . If true, and the list returned is empty, a null object will be returned (as an array)
|
||||
* @return array
|
||||
*/
|
||||
public function getList( $options = null, $returnZeroElement = false, $forcedLimitstart = null, $forcedLimit = null) {
|
||||
|
||||
// make sure we use latest user state
|
||||
$this->_updateContextData();
|
||||
|
||||
// Lets load the content if it doesn't already exist
|
||||
if (is_null($this->_data)) {
|
||||
|
||||
// get pagination values, and check them
|
||||
$total = $this->getTotal($options);
|
||||
$limitstart = is_null( $forcedLimitstart) ? $this->_getState( 'limitstart') : $forcedLimitstart;
|
||||
if( !is_null( $forcedLimitstart) && $limitstart >= $total) {
|
||||
$limitstart = 0;
|
||||
$this->_setState( 'limitstart', 0);
|
||||
}
|
||||
$limit = is_null( $forcedLimit) ? $this->_getState( 'limit') : $forcedLimit;
|
||||
|
||||
// don't allow display 'all' ie $limit = 0, and do sanity check
|
||||
if (empty( $limit) || intval( $limit) > 500) {
|
||||
$limit = 20;
|
||||
$this->_setState( 'limit', $limit);
|
||||
}
|
||||
|
||||
// build the actual query
|
||||
$query = $this->_buildListQuery( $options, $forcedLimitstart, $forcedLimit);
|
||||
$this->_data = $this->_getList( $query);
|
||||
//echo '<br >----------------------<br />data query:<br />' . $query . '<br >----------- '. count( $this->_data).' -----------<br />';
|
||||
if ($returnZeroElement && empty( $this->_data)) {
|
||||
// create an empty record and return it
|
||||
$zeroObject = JTable::getInstance( $this->_defaultTable, 'Sh404sefTable');
|
||||
return array( $zeroObject);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to get the total number of categories
|
||||
*
|
||||
* @access public
|
||||
* @return integer
|
||||
*/
|
||||
public function getTotal( $options = null) {
|
||||
|
||||
// make sure we use latest user state
|
||||
$this->_updateContextData();
|
||||
|
||||
$options = is_null($options) ? new stdClass() : $options;
|
||||
// Lets load the content if it doesn't already exist
|
||||
if (is_null($this->_total)) {
|
||||
$options->onlyCount = true;
|
||||
$query = $this->_buildListQuery( $options);
|
||||
$this->_total = $this->_getListCount( $query);
|
||||
$options->onlyCount = false;
|
||||
|
||||
}
|
||||
|
||||
return $this->_total;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to get a pagination object for the lists
|
||||
*
|
||||
* @access public
|
||||
* @return integer
|
||||
*/
|
||||
public function getPagination( $options = null) {
|
||||
|
||||
// make sure we use latest user state
|
||||
$this->_updateContextData();
|
||||
|
||||
// Lets load the content if it doesn't already exist
|
||||
if (empty($this->_pagination)) {
|
||||
// get pagination values, and check them
|
||||
$total = $this->getTotal($options);
|
||||
$limitstart = $this->_getState( 'limitstart');
|
||||
$limit = $this->_getState( 'limit');
|
||||
|
||||
// create a pagination object
|
||||
jimport('joomla.html.pagination');
|
||||
$this->_pagination = new JPagination( $total, $limitstart, $limit);
|
||||
}
|
||||
|
||||
return $this->_pagination;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets alist of current filters and sort options which have
|
||||
* been applied when building up the data
|
||||
*
|
||||
* @return object the list ov values as object properties
|
||||
*/
|
||||
public function getDisplayOptions() {
|
||||
|
||||
$options = new stdClass();
|
||||
|
||||
// search string applied to either sef or non sef
|
||||
$options->search_all = $this->_getState( 'search_all');
|
||||
// ordering column
|
||||
$options->filter_order = $this->_getState( 'filter_order');
|
||||
// show all/only custom/only automatic
|
||||
$options->filter_order_Dir = $this->_getState( 'filter_order_Dir');
|
||||
|
||||
// return cached instance
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a display option for the current context
|
||||
*
|
||||
* @return mixed previous value of the property
|
||||
*/
|
||||
public function setDisplayOptions( $key, $value) {
|
||||
|
||||
// read previous
|
||||
$previous = $this->_getState( $key);
|
||||
|
||||
// set new
|
||||
$this->_setState( $key, $value);
|
||||
|
||||
// return previous
|
||||
return $previous;
|
||||
}
|
||||
|
||||
protected function _buildListQuery( $options, $forcedLimitstart = null, $forcedLimit = null) {
|
||||
|
||||
// Collect the various parts of the query
|
||||
$select = $this->_buildListSelect( $options);
|
||||
$join = $this->_buildListJoin( $options);
|
||||
$where = $this->_buildListWhere( $options);
|
||||
$limit = $this->_buildListLimit( $options, $forcedLimitstart, $forcedLimit);
|
||||
$orderBy = $this->_buildListOrderBy( $options);
|
||||
$groupBy = $this->_buildListGroupBy( $options);
|
||||
|
||||
// complete query
|
||||
$query = $select . $join . $where . $groupBy;
|
||||
|
||||
// wrap if combined queries
|
||||
$query = $this->_buildListCombinedQuery( $query, $options);
|
||||
|
||||
// add sorting
|
||||
$query .= $orderBy;
|
||||
|
||||
// limit number of items
|
||||
$query .= $limit;
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
|
||||
protected function _buildListSelect( $options) {
|
||||
|
||||
// array to hold select clause parts
|
||||
$select = array();
|
||||
|
||||
// get options
|
||||
$select[] = ' select *';
|
||||
|
||||
// add from clause
|
||||
$select[] = 'from ' . $this->_getTableName();
|
||||
|
||||
// aggregate clauses
|
||||
$select = ( count( $select ) ? implode( ' ', $select ) : '' );
|
||||
|
||||
return $select;
|
||||
}
|
||||
|
||||
protected function _buildListJoin( $options) {
|
||||
|
||||
// array to hold join clause parts
|
||||
$join = array();
|
||||
|
||||
// aggregate clauses
|
||||
$join = ( count( $join ) ? ' ' . implode( ' ', $join ) : '' );
|
||||
|
||||
return $join;
|
||||
|
||||
}
|
||||
|
||||
protected function _buildListWhere( $options) {
|
||||
|
||||
// array to hold where clause parts
|
||||
$where = array();
|
||||
|
||||
// aggregate clauses
|
||||
$where = ( count( $where ) ? ' WHERE '. implode( ' AND ', $where ) : '' );
|
||||
|
||||
return $where;
|
||||
}
|
||||
|
||||
protected function _buildListLimit( $options, $forcedLimitstart = null, $forcedLimit = null) {
|
||||
|
||||
// build query fragment
|
||||
$limitString = '';
|
||||
|
||||
// get the layout options from param
|
||||
$layout = $this->_getOption( 'layout', $options);
|
||||
|
||||
// in some case, we only need a simple list of urls, with no
|
||||
// additionnal date, pageids, aliases, etc
|
||||
$simpleUrlList = $this->_getOption( 'simpleUrlList', $options, false);
|
||||
|
||||
// various cases of layouts
|
||||
switch ($layout) {
|
||||
case 'view404':
|
||||
default:
|
||||
// build query fragment
|
||||
$onlyCount = $this->_getOption( 'onlyCount', $options, false);
|
||||
if (!$onlyCount) {
|
||||
$limitstart = is_null( $forcedLimitstart) ? ( $this->_getState( 'limitstart')) : intval($forcedLimitstart);
|
||||
$limit = is_null( $forcedLimit) ? intval( $this->_getState( 'limit')) : intval( $forcedLimit);
|
||||
if(empty($limit) && empty( $limitstart)) {
|
||||
$limitstring = '';
|
||||
} else {
|
||||
$limitString = ' limit ' . $limitstart . ',' . $limit;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $limitString;
|
||||
}
|
||||
|
||||
protected function _buildListGroupBy( $options) {
|
||||
|
||||
// build query fragment
|
||||
$groupBy = '';
|
||||
|
||||
return $groupBy;
|
||||
}
|
||||
|
||||
protected function _buildListOrderBy( $options) {
|
||||
|
||||
// get set of filters applied to the current view
|
||||
$filters = $this->getDisplayOptions();
|
||||
|
||||
// build query fragment
|
||||
if(!empty($filters->filter_order)) {
|
||||
$orderBy = ' order by ' . $this->_db->quoteName( $filters->filter_order);
|
||||
$orderBy .= ' ' . $filters->filter_order_Dir;
|
||||
}
|
||||
|
||||
return $orderBy;
|
||||
}
|
||||
|
||||
protected function _buildListCombinedQuery( $query, $options) {
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides context data definition, to be used by context handler
|
||||
* Should be overriden by descendant
|
||||
*/
|
||||
protected function _getContextDataDef() {
|
||||
|
||||
$application = JFactory::getApplication();
|
||||
|
||||
// define context data to be retrieved. Cannot be done at class level,
|
||||
// as some default values are dynamic
|
||||
$contextData = array(
|
||||
|
||||
array( 'name' => 'limit', 'html_name' => 'limit', 'default' => $application->getCfg('list_limit'), 'type' => 'int')
|
||||
, array( 'name' => 'limitstart', 'html_name' => 'limitstart', 'default' => 0, 'type' => 'int')
|
||||
// search string applied to either sef or non sef
|
||||
, array( 'name' => 'search_all', 'html_name' => 'search_all', 'default' => '', 'type' => 'string')
|
||||
// ordering column
|
||||
, array( 'name' => 'filter_order', 'html_name' => 'filter_order', 'default' => 'oldurl', 'type' => 'string')
|
||||
// ordering direction
|
||||
, array( 'name' => 'filter_order_Dir', 'html_name' => 'filter_order_Dir', 'default' => 'ASC', 'type' => 'string')
|
||||
|
||||
);
|
||||
|
||||
return $contextData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset model internal cached data
|
||||
* used after changing context for instance
|
||||
*/
|
||||
protected function _resetData() {
|
||||
|
||||
// clean data, total and pagination, as we need them rebuilt
|
||||
$this->_data = null;
|
||||
$this->_total = null;
|
||||
$this->_pagination = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read application user state stored by
|
||||
* Joomla application object for the current context
|
||||
* context represents current controller/model/view hierarchy
|
||||
* and has been set by each of those elements
|
||||
*/
|
||||
protected function _updateContextData() {
|
||||
|
||||
// if not been there before, or context has changed since last visit
|
||||
if (is_null($this->_context) || $this->_context != $this->getState( 'context')) {
|
||||
|
||||
// read context name and store inclass variabel, easier to access later on
|
||||
$this->_context = $this->getState( 'context', $this->_context);
|
||||
|
||||
// get an application instance
|
||||
$application = JFactory::getApplication();
|
||||
|
||||
// define context data to be retrieved. Cannot be done at class level,
|
||||
// as some default values are dynamic
|
||||
$contextData = $this->_getContextDataDef();
|
||||
|
||||
// must reset limistart if limit is changed
|
||||
$mustResetLimitstart = false;
|
||||
|
||||
// get the values from session and store them for future reuse
|
||||
foreach( $contextData as $contextDataItem) {
|
||||
|
||||
// must reset limistart if limit is changed, so store previous value of limit
|
||||
if ($contextDataItem['name'] == 'limit') {
|
||||
// search for previous value
|
||||
$previousLimit = $application->getUserState( $this->_context . '.limit');
|
||||
$previousLimitstart = $application->getUserState( $this->_context . '.limitstart');
|
||||
}
|
||||
|
||||
// get value
|
||||
$value = $application->getUserStateFromRequest( $this->_context . '.' . $contextDataItem['name'], $contextDataItem['html_name'], $contextDataItem['default'], $contextDataItem['type'] );
|
||||
|
||||
if ($contextDataItem['name'] == 'limit' && isset($previousLimit) && $value != $previousLimit) {
|
||||
$mustResetLimitstart = true;
|
||||
$newLimit = $value;
|
||||
}
|
||||
|
||||
// and store it into this model
|
||||
$this->setState( $this->_context . '.' . $contextDataItem['name'], $value);
|
||||
|
||||
}
|
||||
|
||||
// now check if we should reset limitstart
|
||||
if ($mustResetLimitstart) {
|
||||
|
||||
$newLimitstart = empty( $newLimit) ? 0 : $newLimit * floor( $previousLimitstart / $newLimit);
|
||||
// store it into the session and the model
|
||||
$application->setUserState( $this->_context . '.limitstart', $newLimitstart);
|
||||
$this->setState( $this->_context . '.limitstart', $newLimitstart);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected function _getOption( $name, $options, $default = null) {
|
||||
|
||||
if (empty( $options) || !is_object( $options)) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
$value = isset( $options->$name) ? $options->$name : $default;
|
||||
|
||||
return $value;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Short cut to get current state of value
|
||||
* @param string $key
|
||||
*/
|
||||
protected function _getState( $key) {
|
||||
|
||||
return $this->getState( $this->_context . '.' . $key);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* short cut to set the state of a value
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
*/
|
||||
protected function _setState( $key, $value) {
|
||||
|
||||
return $this->setState( $this->_context . '.' . $key, $value);
|
||||
|
||||
}
|
||||
|
||||
|
||||
protected function _cleanForQuery( $string) {
|
||||
|
||||
return $this->_db->escape( JString::trim( JString::strtolower( $string )));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,315 @@
|
||||
<?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
|
||||
*/
|
||||
|
||||
// Security check to ensure this file is being included by a parent file.
|
||||
if (!defined('_JEXEC')) die('Direct Access to this location is not allowed.');
|
||||
|
||||
jimport('joomla.application.component.model');
|
||||
|
||||
abstract class Sh404sefClassBasemodel extends ShlMvcModel_Base {
|
||||
|
||||
/**
|
||||
* Data object
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $_data = null;
|
||||
|
||||
/**
|
||||
* Holds current context, ie : controller/model/view/layout hierarchy
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_context = null;
|
||||
|
||||
/**
|
||||
* Holds default table name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_defaultTable = '';
|
||||
|
||||
|
||||
/**
|
||||
* Returns an item as an object
|
||||
* identified by its db id
|
||||
*
|
||||
* @param integer $id
|
||||
*/
|
||||
public function getById( $id) {
|
||||
|
||||
// if no cached data, fetch from DB or create
|
||||
if (is_null( $this->_data) || $this->_data->id != $id) {
|
||||
|
||||
jimport('joomla.database.table');
|
||||
|
||||
// get a table instance
|
||||
$this->_data = JTable::getInstance( $this->_defaultTable, 'Sh404sefTable');
|
||||
|
||||
// load from table
|
||||
try {
|
||||
$this->_data->load( $id);
|
||||
} catch (Exception $e) {
|
||||
ShlSystem_Log::error( 'sh404sef', '%s::%s::%d: %s', __CLASS__, __METHOD__, __LINE__, $e->getMessage());
|
||||
$this->_data->setError( $e->getMessage());
|
||||
}
|
||||
|
||||
// set error
|
||||
$error = $this->_data->getError();
|
||||
if (!empty($error)) {
|
||||
$this->setError( $error);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $this->_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of items as objects
|
||||
* as selected in the db based on passed
|
||||
* options (ie $options = array( 'id' => 12);)
|
||||
*
|
||||
* Data is not cached!
|
||||
*
|
||||
* @param array $options an array of key/ value pairs representing select params
|
||||
* @param boolean if true, method will only count matching records
|
||||
*
|
||||
* @return array of objects
|
||||
*/
|
||||
public function getByAttr( $options, $countOnly = false) {
|
||||
|
||||
try {
|
||||
if($countOnly) {
|
||||
$list = ShlDbHelper::count( $this->_getTableName(), '*', $options);
|
||||
} else {
|
||||
$list = ShlDbHelper::selectObjectList( $this->_getTableName(), '*', $options);
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
$list = $countOnly ? 0 : array();
|
||||
ShlSystem_Log::error( 'sh404sef', '%s::%s::%d: %s', __CLASS__, __METHOD__, __LINE__, $e->getMessage());
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
public function setData( $data) {
|
||||
|
||||
// get a table instance
|
||||
$this->_data = JTable::getInstance( $this->_defaultTable, 'Sh404sefTable');
|
||||
|
||||
// bind data
|
||||
try {
|
||||
$this->_data->bind( $data);
|
||||
} catch (Exception $e) {
|
||||
ShlSystem_Log::error( 'sh404sef', '%s::%s::%d: %s', __CLASS__, __METHOD__, __LINE__, $e->getMessage());
|
||||
$this->_data->setError( $e->getMessage());
|
||||
}
|
||||
|
||||
// set error
|
||||
$error = $this->_data->getError();
|
||||
if (!empty($error)) {
|
||||
$this->setError( $error);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Create or update a record to
|
||||
* DB from POST data or input array of data
|
||||
*
|
||||
* @param array $dataArray an array holding data to save. If empty, $_POST is used
|
||||
* @return integer id of created or updated record
|
||||
*/
|
||||
public function save( $dataArray = null) {
|
||||
|
||||
// get required tools
|
||||
jimport( 'joomla.database.table');
|
||||
$this->_data = JTable::getInstance( $this->_defaultTable, 'Sh404sefTable');
|
||||
$post = is_null( $dataArray) ? JRequest::get('post') : $dataArray;
|
||||
|
||||
// use table save method
|
||||
try {
|
||||
$status = $this->_data->save( $post);
|
||||
} catch (Exception $e) {
|
||||
ShlSystem_Log::error( 'sh404sef', '%s::%s::%d: %s', __CLASS__, __METHOD__, __LINE__, $e->getMessage());
|
||||
$this->_data->setError( $e->getMessage());
|
||||
$status = false;
|
||||
}
|
||||
|
||||
// report error
|
||||
if (!$status) {
|
||||
JFactory::getApplication()->enqueuemessage( $this->_data->getError());
|
||||
return 0;
|
||||
}
|
||||
|
||||
// if success, fetch last insert id and return that
|
||||
$tableDb = $this->_data->getDBO();
|
||||
$keyName = $this->_data->getKeyName();
|
||||
$id = empty($post[$keyName]) ? 0 : intval($post[$keyName]);
|
||||
$savedId = empty( $id) ? $tableDb->insertid() : $id;
|
||||
|
||||
return $savedId;
|
||||
}
|
||||
|
||||
/**
|
||||
* set current context string, replacing existing one
|
||||
*
|
||||
* @param string $newContext
|
||||
* @return string updated context
|
||||
*/
|
||||
public function setContext( $newContext = '') {
|
||||
|
||||
if (!empty( $newContext)) {
|
||||
$this->setState( 'context', $newContext);
|
||||
|
||||
// reset object data as context has changed
|
||||
$this->_resetData();
|
||||
}
|
||||
|
||||
return $this->getState( 'context');
|
||||
}
|
||||
|
||||
/**
|
||||
* Update current context string, appending
|
||||
* new context to existing one, separated by '.'
|
||||
*
|
||||
* @param string $newContextPart
|
||||
* @return string updated context
|
||||
*/
|
||||
public function updateContext( $newContextPart = '') {
|
||||
|
||||
$currentContext = $this->getState('context');
|
||||
|
||||
if (!empty( $newContextPart)) {
|
||||
$this->setState( 'context', $currentContext . '.' . $newContextPart);
|
||||
|
||||
// reset object data as context has changed
|
||||
$this->_resetData();
|
||||
}
|
||||
|
||||
return $this->getState( 'context');
|
||||
|
||||
}
|
||||
|
||||
protected function _getTableName() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an object list
|
||||
* Overriden to check on respective values
|
||||
* of limit and limit start
|
||||
* If limit is 0, limitstart should be reset to 0 as well
|
||||
*
|
||||
* @param string The query
|
||||
* @param int Offset
|
||||
* @param int The number of records
|
||||
* @return array
|
||||
* @access protected
|
||||
* @since 1.5
|
||||
*/
|
||||
protected function _getList( $query, $limitstart=0, $limit=0 )
|
||||
{
|
||||
|
||||
// check limit and lim
|
||||
$limitstart = $limit === 0 ? 0 : $limitstart;
|
||||
|
||||
// do the job
|
||||
$result = parent::_getList( $query, $limitstart, $limit);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides context data definition, to be used by context handler
|
||||
* Should be overriden by descendant
|
||||
*/
|
||||
protected function _getContextDataDef() {
|
||||
|
||||
$application = JFactory::getApplication();
|
||||
|
||||
// define context data to be retrieved. Cannot be done at class level,
|
||||
// as some default values are dynamic
|
||||
$contextData = array();
|
||||
|
||||
return $contextData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset model internal cached data
|
||||
* used after changing context for instance
|
||||
*/
|
||||
protected function _resetData() {
|
||||
|
||||
// clean data, total and pagination, as we need them rebuilt
|
||||
$this->_data = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read application user state stored by
|
||||
* Joomla application object for the current context
|
||||
* context represents current controller/model/view hierarchy
|
||||
* and has been set by each of those elements
|
||||
*/
|
||||
protected function _updateContextData() {
|
||||
|
||||
// if not been there before, or context has changed since last visit
|
||||
if (is_null($this->_context) || $this->_context != $this->getState( 'context')) {
|
||||
|
||||
// read context name and store inclass variabel, easier to access later on
|
||||
$this->_context = $this->getState( 'context');
|
||||
|
||||
// get an application instance
|
||||
$application = JFactory::getApplication();
|
||||
|
||||
// define context data to be retrieved. Cannot be done at class level,
|
||||
// as some default values are dynamic
|
||||
$contextData = $this->_getContextDataDef();
|
||||
|
||||
// get the values from session and store them for future reuse
|
||||
if (!empty( $contextData)) {
|
||||
foreach( $contextData as $contextDataItem) {
|
||||
// get value
|
||||
$value = $application->getUserStateFromRequest( $this->_context . '.' . $contextDataItem['name'], $contextDataItem['html_name'], $contextDataItem['default'], $contextDataItem['type'] );
|
||||
|
||||
// and store
|
||||
$this->setState( $this->_context . '.' . $contextDataItem['name'], $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function _getOption( $name, $options, $default = null) {
|
||||
|
||||
$value = isset( $options->$name) ? $options->$name : $default;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Short cut to get current state of value
|
||||
* @param string $key
|
||||
*/
|
||||
protected function _getState( $key) {
|
||||
|
||||
return $this->getState( $this->_context . '.' . $key);
|
||||
|
||||
}
|
||||
|
||||
protected function _cleanForQuery( $string) {
|
||||
|
||||
return $this->_db->escape( JString::trim( JString::strtolower( $string )));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,296 @@
|
||||
<?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
|
||||
*/
|
||||
|
||||
// Security check to ensure this file is being included by a parent file.
|
||||
if (!defined('_JEXEC')) die('Direct Access to this location is not allowed.');
|
||||
|
||||
Class Sh404sefClassBasewizcontroller extends Sh404sefClassBasecontroller {
|
||||
|
||||
protected $_context = 'com_sh404sef.wiz';
|
||||
|
||||
protected $_defaultController = '';
|
||||
protected $_defaultTask = '';
|
||||
protected $_defaultModel = '';
|
||||
protected $_defaultView = 'wizard';
|
||||
protected $_defaultLayout = 'default';
|
||||
|
||||
protected $_returnController = '';
|
||||
protected $_returnTask = '';
|
||||
protected $_returnView = 'default';
|
||||
protected $_returnLayout = 'default';
|
||||
|
||||
protected $_opType = '';
|
||||
protected $_opSubject = '';
|
||||
|
||||
protected $_mainText = '';
|
||||
protected $_hiddenText = '';
|
||||
protected $_stepTitle = '';
|
||||
protected $_setFormEncType = '';
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @access protected
|
||||
* @param array An optional associative array of configuration settings.
|
||||
* Recognized key values include 'name', 'default_task', 'model_path', and
|
||||
* 'view_path' (this list is not meant to be comprehensive).
|
||||
* @since 1.5
|
||||
*/
|
||||
public function __construct( $config = array() ) {
|
||||
|
||||
// call parent, doing main construct job
|
||||
parent::__construct( $config);
|
||||
|
||||
// attach appropriate adapter
|
||||
$this->_opType = strtolower( JRequest::getCmd( 'optype')); //ex : 'import'
|
||||
$this->_opSubject = strtolower( JRequest::getCmd( 'opsubject')); // ex: pageids
|
||||
|
||||
// update context
|
||||
$this->_context .= '.' . $this->_opType . '.' . $this->_opSubject;
|
||||
|
||||
// create adapter object
|
||||
$adapterName = 'Sh404sefAdapter' . ucfirst( $this->_opType) . $this->_opSubject;
|
||||
|
||||
if (!class_exists( $adapterName)) {
|
||||
$this->_fail( 'COM_SH404SEF_INVALID_WIZARD_ADAPTER');
|
||||
} else {
|
||||
$this->_adapter = new $adapterName( $this);
|
||||
}
|
||||
|
||||
// collect properties from adapter
|
||||
$properties = $this->_adapter->setup();
|
||||
|
||||
// and store them
|
||||
foreach($properties as $key => $value) {
|
||||
$this->$key = $value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the view
|
||||
*/
|
||||
public function display( $cachable = false, $urlparams = false) {
|
||||
|
||||
// catch up any result message coming from an
|
||||
// ajax save for instance, and push that into
|
||||
// the application message queue
|
||||
$messageCode = JRequest::getCmd( 'sh404sefMsg');
|
||||
if (!empty($messageCode)) {
|
||||
$msg = JText::_( $messageCode);
|
||||
if ($msg != $messageCode) {
|
||||
// if no language string exists, JText will
|
||||
// return the input string, so only display if
|
||||
// we have something to display
|
||||
$app = JFactory::getApplication();
|
||||
$app->enqueuemessage( $msg);
|
||||
}
|
||||
}
|
||||
|
||||
// get/create the view
|
||||
$document =JFactory::getDocument();
|
||||
$viewType = $document->getType();
|
||||
$view = $this->getView( $this->_adapter->_stepsMap[$this->_adapter->_step]['view'], $viewType, '', array( 'base_path'=>$this->basePath));
|
||||
|
||||
// Set the layout
|
||||
$view->setLayout( $this->_adapter->_stepsMap[$this->_adapter->_step]['layout']);
|
||||
|
||||
// push button list into the view
|
||||
$view->buttonsList = $this->_adapter->_buttonsList;
|
||||
$view->visibleButtonsList = $this->_adapter->_visibleButtonsList;
|
||||
|
||||
// push list of steps in to the view
|
||||
foreach( $this->_adapter->_buttonsList as $button) {
|
||||
$view->$button = $this->_adapter->_steps[$button];
|
||||
}
|
||||
|
||||
// push controller name into view
|
||||
$view->actionController = $this->_defaultController;
|
||||
|
||||
// push operation type and subject into the view
|
||||
$view->opType = $this->_opType;
|
||||
$view->opSubject = $this->_opSubject;
|
||||
|
||||
// push title, main and hidden text
|
||||
$view->pageTitle = $this->_pageTitle;
|
||||
$view->mainText = $this->_mainText;
|
||||
$view->hiddenText = $this->_hiddenText;
|
||||
$view->setFormEncType = $this->_setFormEncType;
|
||||
|
||||
// check if we are done, and need to set a redirect
|
||||
if (!empty( $this->_redirectTo)) {
|
||||
$view->redirectTo = $this->_redirectTo;
|
||||
}
|
||||
|
||||
// check if we are continuein to another step, and need to set a redirect
|
||||
if (!empty( $this->_continue)) {
|
||||
$view->continue = $this->_continue;
|
||||
}
|
||||
if (!empty( $this->_nextStart)) {
|
||||
$view->nextstart = $this->_nextStart;
|
||||
}
|
||||
|
||||
// push controller errors in the view
|
||||
$error = $this->getError();
|
||||
if (!empty( $error)) {
|
||||
$view->setError( $error);
|
||||
}
|
||||
|
||||
// Display the view
|
||||
$view->display();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry point for first screen of wizard
|
||||
* without token check
|
||||
*/
|
||||
public function start() {
|
||||
|
||||
$this->_adapter->_button = 'next';
|
||||
$this->_dispatch();
|
||||
}
|
||||
|
||||
|
||||
public function next() {
|
||||
|
||||
// Check for request forgeries
|
||||
//JRequest::checkToken() or jexit( 'Invalid Token' );
|
||||
|
||||
// if not form submit, just display current step
|
||||
$this->_adapter->_button = 'next';
|
||||
$this->_dispatch();
|
||||
}
|
||||
|
||||
public function previous() {
|
||||
|
||||
// Check for request forgeries
|
||||
JRequest::checkToken() or jexit( 'Invalid Token' );
|
||||
|
||||
$this->_adapter->_button = 'previous';
|
||||
$this->_dispatch();
|
||||
}
|
||||
|
||||
public function terminate() {
|
||||
|
||||
// Check for request forgeries
|
||||
JRequest::checkToken() or jexit( 'Invalid Token' );
|
||||
|
||||
$this->_adapter->_button = 'terminate';
|
||||
$this->_dispatch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the wizard window and display an error message
|
||||
* in the parent window
|
||||
*/
|
||||
public function cancel() {
|
||||
|
||||
// Check for request forgeries
|
||||
JRequest::checkToken() or jexit( 'Invalid Token' );
|
||||
|
||||
$this->_adapter->_button = 'cancel';
|
||||
$this->_dispatch();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Main entry point
|
||||
*/
|
||||
protected function _dispatch() {
|
||||
|
||||
// collect and store request params
|
||||
// what is the step id to run depending on what button was pressed
|
||||
// ie : next = 3, previous = 1, terminate = 6
|
||||
foreach( $this->_adapter->_buttonsList as $button) {
|
||||
$this->_adapter->_steps[$button] = JRequest::getInt( $button, $this->_adapter->_steps[$button]);
|
||||
}
|
||||
|
||||
// check request params
|
||||
if(!in_array( $this->_adapter->_button, $this->_adapter->_buttonsList)) {
|
||||
$this->_fail( 'COM_SH404SEF_INVALID_WIZARD_STEP');
|
||||
}
|
||||
|
||||
// _dispatch request to appropriate method
|
||||
|
||||
// _step contains numerical id of _stepsMap record to use
|
||||
$this->_adapter->_step = $this->_adapter->_steps[$this->_adapter->_button];
|
||||
|
||||
// call the 'task' method listed in the _stepsMap array item
|
||||
if (empty( $this->_adapter->_stepsMap[$this->_adapter->_step])) {
|
||||
$this->_fail( 'COM_SH404SEF_INVALID_WIZARD_STEP');
|
||||
} else {
|
||||
$methodName = $this->_adapter->_stepsMap[$this->_adapter->_step]['task'];
|
||||
if (is_callable (array( $this->_adapter, $methodName))) {
|
||||
$result = $this->_adapter->$methodName();
|
||||
$this->_useAdapterResult( $result);
|
||||
$this->display();
|
||||
} else {
|
||||
$this->_fail( 'COM_SH404SEF_INVALID_WIZARD_STEP');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* invalid internal parameters, errors in parameters
|
||||
*/
|
||||
protected function _fail( $msg) {
|
||||
|
||||
$app = JFactory::getApplication();
|
||||
$app->enqueuemessage( $msg, 'error');
|
||||
$this->display();
|
||||
|
||||
}
|
||||
|
||||
protected function _useAdapterResult( $result) {
|
||||
|
||||
// use results : the adapter may have change the suject
|
||||
// of this operation, after analyzing incoming data in an import
|
||||
// for instance
|
||||
if( !empty( $result['opSubject'])) {
|
||||
$this->_opSubject = $result['opSubject'];
|
||||
}
|
||||
|
||||
// redirections results
|
||||
$redirectOptions = empty( $result['redirectOptions']) ? array() : $result['redirectOptions'];
|
||||
|
||||
if (!empty( $result['redirectTo'])) {
|
||||
$this->_redirectTo = $this->_getDefaultRedirect( $redirectOptions);
|
||||
}
|
||||
|
||||
// display results
|
||||
if( !empty( $result['mainText'])) {
|
||||
$this->_mainText = $result['mainText'];
|
||||
}
|
||||
|
||||
if( !empty( $result['hiddenText'])) {
|
||||
$this->_hiddenText = $result['hiddenText'];
|
||||
}
|
||||
|
||||
if (!empty( $result['setFormEncType'])) {
|
||||
$this->_setFormEncType = $result['setFormEncType'];
|
||||
}
|
||||
|
||||
if( !empty( $result['continue'])) {
|
||||
// calculate redirect url to next step
|
||||
$vars = array( 'option' => 'com_sh404sef', 'c' => $this->_defaultController, 'tmpl' => 'component', 'optype' => $this->_opType, 'opsubject' => $this->_opSubject);
|
||||
$vars = array_merge( $vars, $result['continue']);
|
||||
$this->_continue = Sh404sefHelperUrl::buildUrl( $vars);
|
||||
}
|
||||
|
||||
if( !empty( $result['nextStart'])) {
|
||||
$this->_nextStart = $result['nextStart'];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
<?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
|
||||
*/
|
||||
|
||||
// Security check to ensure this file is being included by a parent file.
|
||||
if (!defined('_JEXEC'))
|
||||
die('Direct Access to this location is not allowed.');
|
||||
|
||||
@ -0,0 +1,443 @@
|
||||
<?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
|
||||
*/
|
||||
|
||||
// Security check to ensure this file is being included by a parent file.
|
||||
if (!defined('_JEXEC')) die('Direct Access to this location is not allowed.');
|
||||
|
||||
// load a few constants
|
||||
require_once JPATH_ROOT . '/administrator/components/com_sh404sef/defines.php';
|
||||
|
||||
//define('SH_SHOW_CACHE_STATS', 0);
|
||||
|
||||
/**
|
||||
* URL caching
|
||||
*
|
||||
* @author shumisha
|
||||
*
|
||||
*/
|
||||
class Sh404sefClassFilecache extends Sh404sefClassBasecache {
|
||||
|
||||
protected $_newUrlsCache = array();
|
||||
protected $_newUrlsCacheCount = 0;
|
||||
protected $_cacheFilename = '';
|
||||
protected $_cacheFilefullpath = '';
|
||||
protected $_lockFilefullpath = '';
|
||||
protected $_cacheFileLocked = false;
|
||||
protected $_urlCacheRam = 0;
|
||||
protected $_lockTtl = 35; // usual exec time is 30sec
|
||||
|
||||
protected $_fileHeader = '<?php // shCache : URL cache file for sh404SEF
|
||||
// version %s
|
||||
if (!defined(\'_JEXEC\')) die(\'Direct Access to this location is not allowed.\');
|
||||
$shURLCacheCreationDate = %s;
|
||||
';
|
||||
|
||||
public function __construct( $config) {
|
||||
|
||||
parent::__construct( $config);
|
||||
|
||||
// init cache filename, must be unique per live site, in case of multisite
|
||||
$liveSite = str_replace( 'administrator/', '', JURI::base());
|
||||
$this->_cacheFilename = 'shCacheContent.' . md5( $liveSite);
|
||||
$this->_cacheFilefullpath = sh404SEF_FRONT_ABS_PATH . 'cache' . '/' . $this->_cacheFilename . '.php';
|
||||
$this->_lockFilefullpath = sh404SEF_FRONT_ABS_PATH . 'cache' . '/' . $this->_cacheFilename . '.shlock';
|
||||
|
||||
// every now and then we clear the cache to let it refill with more recent data
|
||||
$this->_itsTimeToCheckTTL = mt_rand(1, SH404SEF_URL_CACHE_WRITES_TO_CHECK_TTL) == 1;
|
||||
|
||||
// register method that will store urls created during
|
||||
// current page request into cache file
|
||||
if (!empty($this->_config->shUseURLCache)) {
|
||||
register_shutdown_function( array( $this, 'writeURLCacheToDisk'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function getSefUrlFromCache( $nonSefUrl, & $sefUrl) {
|
||||
|
||||
if (!$this->_config->shUseURLCache) {
|
||||
$sefUrl = null;
|
||||
$this->_urlCacheMisses += 1;
|
||||
return sh404SEF_URLTYPE_NONE;
|
||||
}
|
||||
|
||||
// make sure cache is loaded, in case this method is called first
|
||||
$this->_loadURLCache();
|
||||
|
||||
// no urls in cache, no need to go further
|
||||
if (empty($this->_urlCacheCount) && empty($this->_newUrlsCacheCount)) {
|
||||
$sefUrl = null;
|
||||
$this->_urlCacheMisses += 1;
|
||||
return sh404SEF_URLTYPE_NONE;
|
||||
}
|
||||
$key = md5( $nonSefUrl);
|
||||
if(!empty( $this->_urlCache[$key])) {
|
||||
$tmp = explode('#', $this->_urlCache[$key]); // cache format : non-sef#sef#type
|
||||
$sefUrl = $tmp[1];
|
||||
ShlSystem_Log::debug( 'sh404sef', 'Retrieved SEF from disk cache : '.$sefUrl.' => '.html_entity_decode( $tmp[0], ENT_QUOTES).'('.$tmp[2].')');
|
||||
$this->_urlCacheHits += 1;
|
||||
return $tmp[2];
|
||||
}
|
||||
|
||||
if(!empty( $this->_newUrlsCache[$key])) {
|
||||
$tmp = explode('#', $this->_newUrlsCache[$key]); // cache format : non-sef#sef#type
|
||||
$sefUrl = $tmp[1];
|
||||
ShlSystem_Log::debug( 'sh404sef', 'Retrieved SEF from disk cache : '.$sefUrl.' => '.html_entity_decode( $tmp[0], ENT_QUOTES).'('.$tmp[2].')');
|
||||
$this->_urlCacheHits += 1;
|
||||
return $tmp[2];
|
||||
}
|
||||
|
||||
$this->_urlCacheMisses += 1;
|
||||
$this->_urlCacheMissesList[] = $nonSefUrl;
|
||||
return sh404SEF_URLTYPE_NONE;
|
||||
|
||||
}
|
||||
|
||||
// fetch an URL from cache, return null if not found
|
||||
public function getNonSefUrlFromCache( $sefUrl, & $nonSefUrl) {
|
||||
|
||||
if (!$this->_config->shUseURLCache) {
|
||||
$nonSefUrl = null;
|
||||
return sh404SEF_URLTYPE_NONE;
|
||||
}
|
||||
|
||||
// make sure cache is loaded, in case this method is called first
|
||||
$this->_loadURLCache();
|
||||
|
||||
// no urls in cache, no need to go further
|
||||
if (empty($this->_urlCacheCount) && empty($this->_newUrlsCacheCount)) {
|
||||
$nonSefUrl = null;
|
||||
return sh404SEF_URLTYPE_NONE;
|
||||
}
|
||||
|
||||
// search for requested sef url in disk stored cache
|
||||
foreach( $this->_urlCache as $key => $value) {
|
||||
if (strpos( $value, $sefUrl) !== false) {
|
||||
$tmp = explode('#', $value); // cache format : non-sef#sef#type
|
||||
$nonSef = html_entity_decode( $tmp[0], ENT_QUOTES);
|
||||
if ($sefUrl == $tmp[1]) {
|
||||
$nonSefUrl = $nonSef;
|
||||
ShlSystem_Log::debug( 'sh404sef', 'Retrieved Non SEF from disk cache : '.$nonSefUrl.' => '.$tmp[1].'('.$tmp[2].')');
|
||||
return $tmp[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
// search also in urls waiting to be stored to disk
|
||||
foreach( $this->_newUrlsCache as $key => $value) {
|
||||
if (strpos( $value, $sefUrl) !== false) {
|
||||
$tmp = explode('#', $value); // cache format : non-sef#sef#type
|
||||
$nonSef = html_entity_decode( $tmp[0], ENT_QUOTES);
|
||||
if ($sefUrl == $tmp[1]) {
|
||||
$nonSefUrl = $nonSef;
|
||||
ShlSystem_Log::debug( 'sh404sef', 'Retrieved Non SEF from mem cache : '.$nonSefUrl.' => '.$tmp[1].'('.$tmp[2].')');
|
||||
return $tmp[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
return sh404SEF_URLTYPE_NONE;
|
||||
}
|
||||
|
||||
public function addSefUrlToCache( $nonSefUrl, $sefUrl, $UrlType) {
|
||||
|
||||
if (!$this->_config->shUseURLCache) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// make sure cache is loaded, in case this method is called first
|
||||
$this->_loadURLCache();
|
||||
|
||||
// insertion key
|
||||
$key = md5( $nonSefUrl);
|
||||
|
||||
// already there?
|
||||
if(!empty($this->_urlCache[$key]) || !empty( $this->_newUrlsCache[$key])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (($this->_urlCacheCount + $this->_newUrlsCacheCount) >= $this->_config->shMaxURLInCache) {
|
||||
return null; // v 1.2.4.c added total cache size control
|
||||
}
|
||||
// Filter out non sef url which include &mosmsg, as I don't want to have a cache entry for every single msg
|
||||
// that can be thrown at me, including every 404 error
|
||||
if (strpos(strtolower($nonSefUrl), '&mosmsg')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// new cache format : non-sef#sef#type
|
||||
$this->_newUrlsCache[md5($nonSefUrl)] = htmlentities( $nonSefUrl, ENT_QUOTES).'#'.$sefUrl.'#'.$UrlType;
|
||||
ShlSystem_Log::debug( 'sh404sef', 'Adding to URL cache : '.$sefUrl.' <= '.$nonSefUrl);
|
||||
$this->_newUrlsCacheCount++;
|
||||
return true;
|
||||
}
|
||||
|
||||
public function removeUrlFromCache( $nonSefUrlList) {
|
||||
|
||||
if (!$this->_config->shUseURLCache || empty($nonSefUrlList)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// make sure cache is loaded, in case this method is called first
|
||||
$this->_loadURLCache();
|
||||
|
||||
$foundInDiskCache = false;
|
||||
$foundInMemCache = false;
|
||||
foreach ($nonSefUrlList as $nonSefURL) {
|
||||
if (!empty($this->_newUrlsCache)) {
|
||||
foreach ($this->_newUrlsCache as $key => $cacheItem) { // look up in memory cache
|
||||
$tmp = explode('#', $cacheItem);
|
||||
$cacheNonSef = html_entity_decode( $tmp[0], ENT_QUOTES);
|
||||
if ($cacheNonSef == $nonSefURL) {
|
||||
unset($this->_newUrlsCache[$key]);
|
||||
$this->_newUrlsCacheCount--;
|
||||
$foundInMemCache = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: rewrite using md5 key
|
||||
if (!empty($this->_urlCache)) {
|
||||
foreach ($this->_urlCache as $key => $cacheItem) { // look up disk cache
|
||||
$tmp = explode('#', $cacheItem);
|
||||
$cacheNonSef = html_entity_decode( $tmp[0], ENT_QUOTES);
|
||||
if ($cacheNonSef == $nonSefURL) {
|
||||
unset($this->_urlCache[$key]);
|
||||
$this->_urlCacheCount--;
|
||||
$foundInDiskCache = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($foundInMemCache) {
|
||||
$this->_newUrlsCache = array_values($this->_newUrlsCache); // simply reindex mem cache
|
||||
return;
|
||||
}
|
||||
if ($foundInDiskCache) { // we need to remove these url from the disk cache file
|
||||
// to make it simpler, I simply rewrite the complete file
|
||||
$this->_newUrlsCache = (empty($this->_newUrlsCache) ?
|
||||
array_values($this->_urlCache)
|
||||
:array_merge($this->_urlCache, $this->_newUrlsCache));
|
||||
$this->_urlCache = array(); // don't need disk cache anymore, as all URL are in mem cache
|
||||
// so we remove both on disk cache and in memory copy of on disk cache
|
||||
$this->purge();
|
||||
// no need to write new URL list in disk file, as this will be done automatically at shutdown
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function writeURLCacheToDisk() {
|
||||
|
||||
// If we acquired lock at page load, we can write
|
||||
if (count($this->_newUrlsCache) && $this->_cacheFileLocked) {
|
||||
$cache = '';
|
||||
$now = time();
|
||||
if (!file_exists($this->_cacheFilefullpath)) {
|
||||
$cache = sprintf( $this->_fileHeader, $this->_config->version, $now);
|
||||
$this->_urlCacheCreationDate = $now;
|
||||
} else {
|
||||
// check cache TTL
|
||||
if (empty($this->_urlCacheCreationDate)){ // file exists, but creation date is missing : we are upgrading from a previous version
|
||||
$status = stat($this->_cacheFilefullpath); // lets's read from file status : use last change date as creation date
|
||||
if (!empty($status)) {
|
||||
$this->_urlCacheCreationDate = $status[9];
|
||||
}
|
||||
}
|
||||
if (SH404SEF_URL_CACHE_TTL && $this->_itsTimeToCheckTTL) { // probability = 1/SH404SEF_WRITES_TO_CLEAN_LOGS
|
||||
if (!empty($this->_urlCacheCreationDate)){ // if we have a valid creation date, check TTL
|
||||
if (($now-$this->_urlCacheCreationDate) > SH404SEF_URL_CACHE_TTL*3600) { // cache must be cleared
|
||||
$this->_urlCache = array();
|
||||
unlink($this->_cacheFilefullpath);
|
||||
$this->_urlCacheCreationDate = $now;
|
||||
$cache = sprintf( $this->_fileHeader, $this->_config->version, $now);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$count = count( $this->_urlCache);
|
||||
$cache .= $this->_varExport( $this->_newUrlsCache, $count); // only need to write memory cache, ie: those URL added since last read of cache from disk
|
||||
$cache .= "\n".'$shURLCacheCreationDate='.$now.';';
|
||||
$cacheFile = fopen( $this->_cacheFilefullpath,'ab');
|
||||
if ($cacheFile) {
|
||||
fwrite( $cacheFile, $cache);
|
||||
fclose( $cacheFile);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// in any case, release lock to file
|
||||
$this->_releaseLock();
|
||||
}
|
||||
|
||||
public function purge( $allCaches = true) {
|
||||
|
||||
jimport( 'joomla.filesystem.file');
|
||||
jimport( 'joomla.filesystem.folder');
|
||||
if(!JFolder::exists(sh404SEF_FRONT_ABS_PATH . 'cache'))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if($allCaches) {
|
||||
// delete all sh404SEF cache files in the dir
|
||||
// so as to clear also potential https, or alternate domains caches
|
||||
jimport( 'joomla.filesystem.folder');
|
||||
$files = JFolder::files( sh404SEF_FRONT_ABS_PATH . 'cache', 'shCacheContent\.', $recurse = false, $fullpath = true);
|
||||
if(!empty( $files)) {
|
||||
foreach( $files as $file) {
|
||||
if (JFile::exists( $file)) {
|
||||
JFile::delete( $file);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// only delete cache for current live site url
|
||||
if (JFile::exists( $this->_cacheFilefullpath)) {
|
||||
JFile::delete( $this->_cacheFilefullpath);
|
||||
}
|
||||
if (JFile::exists( $this->_lockFilefullpath)) {
|
||||
JFile::delete( $this->_lockFilefullpath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getCacheStats() {
|
||||
|
||||
// collect basic data
|
||||
$out = parent::getCacheStats();
|
||||
|
||||
// add urls waiting to be written
|
||||
$out .= 'In memory, waiting to be written : '. $this->_newUrlsCacheCount . '<br />';
|
||||
|
||||
// add ram usage of cached urls read from disk file
|
||||
// and stored in ram
|
||||
$out .= 'Ram used : '. $this->_urlCacheRam . '<br />';
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
// load cached URL from disk into an array in memory
|
||||
protected function _loadURLCache() {
|
||||
|
||||
static $shDiskCacheLoaded = false;
|
||||
|
||||
if (!$shDiskCacheLoaded) {
|
||||
ShlSystem_Log::debug( 'sh404sef', 'Cache not loaded - trying to load '.$this->_cacheFilefullpath);
|
||||
if (file_exists( $this->_cacheFilefullpath)) {
|
||||
$startMem = function_exists('memory_get_usage')? memory_get_usage():'unavailable';
|
||||
ShlSystem_Log::debug( 'sh404sef', 'Including cache file (mem = '.$startMem.')');
|
||||
$this->_urlCache = array();
|
||||
// we try lock the cache file until the end of the request
|
||||
// so as to avoid other concurrent requests writing to it
|
||||
// while we have some pending data
|
||||
$this->_acquireLock();
|
||||
|
||||
// read cache file content
|
||||
include($this->_cacheFilefullpath);
|
||||
$this->_urlCacheCreationDate = $shURLCacheCreationDate;
|
||||
|
||||
$endMem = function_exists('memory_get_usage')? memory_get_usage():'unavailable';
|
||||
$this->_urlCacheRam = $startMem == 'unavailable' ? $startMem: $endMem - $startMem;
|
||||
$shDiskCacheLoaded = !empty($this->_urlCache);
|
||||
$this->_urlCacheCount = !empty($this->_urlCache) ? count($this->_urlCache) : 0;
|
||||
ShlSystem_Log::debug( 'sh404sef', 'Cache file included : '.($startMem == 'unavailable' ? $startMem: $endMem-$startMem).' bytes used, '.$this->_urlCacheCount.' URLs');
|
||||
} else {
|
||||
// cache file not there, create it
|
||||
$now = time();
|
||||
$cache = sprintf( $this->_fileHeader, $this->_config->version, $now);
|
||||
|
||||
// lock cache file before using it
|
||||
if ( $this->_acquireLock()) {
|
||||
$cacheFile = fopen( $this->_cacheFilefullpath,'ab');
|
||||
if ($cacheFile) {
|
||||
fwrite( $cacheFile, $cache);
|
||||
fclose( $cacheFile);
|
||||
}
|
||||
}
|
||||
$this->_urlCache = array();
|
||||
$this->_urlCacheCount = 0;
|
||||
$shDiskCacheLoaded = true; // we don't want to try again if it failed first time
|
||||
$this->_urlCacheCreationDate = $now;
|
||||
ShlSystem_Log::debug( 'sh404sef', 'Cache file does not exists');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function _acquireLock() {
|
||||
|
||||
$attempts = 0;
|
||||
$tryAgain = false;
|
||||
$now = time();
|
||||
do {
|
||||
// directly attempts to create the lock. If it fails, then element is already locked
|
||||
$handle = @fopen( $this->_lockFilefullpath, 'x');
|
||||
if ($handle) {
|
||||
// we could create file for writing, element is not locked.
|
||||
// store time, close and return
|
||||
$written = fwrite( $handle, $now);
|
||||
$closed = fclose($handle);
|
||||
// return true if we could write the time stamp
|
||||
$this->_cacheFileLocked = !empty($written) && $closed;
|
||||
// we don't try again, even if we failed writing -> something is not working anyway
|
||||
} else {
|
||||
// we could not create lock file, element is already locked
|
||||
// by another process. Check for timeout
|
||||
// read lock file, and check TTL
|
||||
if ($this->_itsTimeToCheckTTL) {
|
||||
$lockTime = JFile::exists($this->_lockFilefullpath) ? file_get_contents( $this->_lockFilefullpath) : 0;
|
||||
$lockTime = (int) trim( $lockTime);
|
||||
if (($now - $lockTime) > $this->_lockTtl) {
|
||||
// existing lock has timed out, we can release it
|
||||
$this->_cacheFileLocked = $this->_releaseLock( true);
|
||||
// we have released the lock file, we can try again grabbing it
|
||||
// as we test also for _cacheFileLocked, this will happen only if the release was successful
|
||||
$tryAgain = true;
|
||||
} else {
|
||||
$tryAgain = false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} while (!$this->_cacheFileLocked && $tryAgain);
|
||||
|
||||
return $this->_cacheFileLocked;
|
||||
}
|
||||
|
||||
protected function _releaseLock( $forced = false) {
|
||||
|
||||
if ($this->_cacheFileLocked || $forced) {
|
||||
jimport( 'joomla.filesystem.file');
|
||||
if (JFile::exists( $this->_lockFilefullpath)) {
|
||||
$this->_cacheFileLocked = !JFile::delete( $this->_lockFilefullpath);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->_cacheFileLocked;
|
||||
|
||||
}
|
||||
|
||||
protected function _varExport( $cache, $start) {
|
||||
|
||||
// export content of array $cache, inserting a numeric key starting at $start
|
||||
$size = count( $cache);
|
||||
if (empty($size)) {
|
||||
return '';
|
||||
}
|
||||
$ret = '';
|
||||
|
||||
foreach($cache as $key => $value) { // use for instead of foreach to reduce memory usage
|
||||
$tmp = explode('#', $value); // cache format : non-sef#sef#type
|
||||
$nonSef = html_entity_decode( $tmp[0], ENT_QUOTES);
|
||||
$ret .= "\n".'$this->_urlCache[\''.md5($nonSef).'\']=\''.$value.'\';';
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,411 @@
|
||||
<?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
|
||||
*/
|
||||
|
||||
// Security check to ensure this file is being included by a parent file.
|
||||
if (!defined('_JEXEC')) die('Direct Access to this location is not allowed.');
|
||||
|
||||
/**
|
||||
* Implement wizard based exportation of generic data
|
||||
*
|
||||
* @author shumisha
|
||||
*
|
||||
*/
|
||||
class Sh404sefClassImportgeneric extends JObject {
|
||||
|
||||
/**
|
||||
* An array holding each step details
|
||||
* A step is defined as a task, a view and a layout
|
||||
* By default, task can be 'display', but still need
|
||||
* to be defined in array
|
||||
* @var array
|
||||
*/
|
||||
public $_stepsMap = array(
|
||||
|
||||
-2 => array( 'task' => 'doTerminate', 'view' => 'wizard', 'layout' => 'default')
|
||||
,-1 => array( 'task' => 'doCancel', 'view' => 'wizard', 'layout' => 'default')
|
||||
, 0 => array( 'task' => 'doStart', 'view' => 'wizard', 'layout' => 'default')
|
||||
, 1 => array( 'task' => 'doUpload', 'view' => 'wizard', 'layout' => 'default')
|
||||
, 2 => array( 'task' => 'doValidate', 'view' => 'wizard', 'layout' => 'default')
|
||||
, 3 => array( 'task' => 'doImport', 'view' => 'wizard', 'layout' => 'default')
|
||||
|
||||
);
|
||||
|
||||
public $_stepsCount = 0;
|
||||
public $_steps = array( 'next' => 0, 'previous' => 0, 'cancel' => -1, 'terminate' => -2);
|
||||
public $_button = '';
|
||||
public $_buttonsList = array ('next', 'previous', 'terminate', 'cancel');
|
||||
// visible buttons are displayed as toolbar pressbutton
|
||||
// buttons not on that list are passed as 'hidden' post data
|
||||
public $_visibleButtonsList = array ( 'previous', 'next', 'terminate', 'cancel');
|
||||
|
||||
protected $_context = '';
|
||||
protected $_total = 0;
|
||||
protected $_parentController = null;
|
||||
protected $_filename = '';
|
||||
protected $_result = array();
|
||||
|
||||
const MAX_PAGEIDS_PER_STEP = 20;
|
||||
|
||||
/**
|
||||
* Constructor, keep reference to controller
|
||||
* which called the adapter
|
||||
* @param unknown_type $parentController
|
||||
*/
|
||||
public function __construct( $parentController) {
|
||||
|
||||
$this->_parentController = $parentController;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameters for current adapter, to be used by parent controller
|
||||
*
|
||||
*/
|
||||
public function setup() {
|
||||
|
||||
$this->_stepsCount = count( $this->_stepsMap);
|
||||
|
||||
// prepare data for controller
|
||||
$properties = array();
|
||||
|
||||
$properties['_defaultController'] = 'wizard';
|
||||
$properties['_defaultTask'] = '';
|
||||
$properties['_defaultModel'] = '';
|
||||
$properties['_defaultView'] = 'wizard';
|
||||
$properties['_defaultLayout'] = 'default';
|
||||
|
||||
$properties['_returnController'] = 'default';
|
||||
$properties['_returnTask'] = '';
|
||||
$properties['_returnView'] = 'default';
|
||||
$properties['_returnLayout'] = 'default';
|
||||
$properties['_pageTitle'] = JText::_('COM_SH404SEF_IMPORTING_TITLE');
|
||||
|
||||
return $properties;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* First step, by default a message
|
||||
* and a Terminate button
|
||||
*
|
||||
*/
|
||||
public function doStart() {
|
||||
|
||||
// which button should be displayed ?
|
||||
$this->_visibleButtonsList = array ('next', 'cancel');
|
||||
|
||||
// next steps definition
|
||||
$this->_steps = array( 'next' => 1, 'previous' => 0, 'cancel' => -1, 'terminate' => -2);
|
||||
|
||||
// return results
|
||||
$this->_result['mainText'] = JText::_('COM_SH404SEF_IMPORT_' . strtoupper( $this->_context). '_START');
|
||||
|
||||
return $this->_result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Second step, let user upload file
|
||||
*
|
||||
*/
|
||||
public function doUpload() {
|
||||
|
||||
// which button should be displayed ?
|
||||
$this->_visibleButtonsList = array ( 'next', 'cancel');
|
||||
|
||||
// next steps definition
|
||||
$this->_steps = array( 'next' => 2, 'previous' => 0, 'cancel' => -1, 'terminate' => -2);
|
||||
|
||||
// return results
|
||||
|
||||
// make sure we can upload, ie set the correct encoding type for the form
|
||||
$this->_result['setFormEncType'] = 'multipart/form-data';
|
||||
|
||||
// prepare display
|
||||
$this->_result['mainText'] = JText::sprintf('COM_SH404SEF_IMPORT_UPLOAD_FILE', Sh404sefHelperFiles::getMaxUploadSize());
|
||||
|
||||
// add a file browse button
|
||||
$this->_result['mainText'] .= '<div style="text-align:center;width:100%;" ><input type="file" name="wizardfile" size="70" /></div>';
|
||||
|
||||
return $this->_result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Second step, read file content, validate and display for user go ahead
|
||||
*
|
||||
*/
|
||||
public function doValidate() {
|
||||
|
||||
// get file name
|
||||
$fileRecord = JRequest::getVar( 'wizardfile', null, 'files');
|
||||
|
||||
// move uploaded file, to get access to it
|
||||
$this->_filename = Sh404sefHelperFiles::createFileName( $this->_filename, 'sh404sef_import_' . $this->_context);
|
||||
|
||||
try {
|
||||
if (!move_uploaded_file($fileRecord['tmp_name'], $this->_filename)) {
|
||||
// could not write to web space temp dir
|
||||
throw new Sh404sefExceptionDefault( JText::_('COM_SH404SEF_WRITE_FAILED'));
|
||||
}
|
||||
|
||||
// which button should be displayed ?
|
||||
$this->_visibleButtonsList = array ( 'previous', 'next', 'cancel');
|
||||
|
||||
// next steps definition
|
||||
$this->_steps = array( 'next' => 3, 'previous' => 1, 'cancel' => -1, 'terminate' => -2);
|
||||
|
||||
// analyse file content, returning itemsCount
|
||||
$importType = $this->_analyzeImportFileContent( $this->_filename);
|
||||
|
||||
// we may have to change the opSubject and related data. If user asked for instance to import
|
||||
// aliases, from the aliases page, but actually loaded an import file
|
||||
// containing urls or pageids
|
||||
if ($this->_context != $importType) {
|
||||
$this->_result['opSubject'] = $importType;
|
||||
// update filename
|
||||
$oldFileName = $this->_filename;
|
||||
$this->_filename = str_replace( $this->_context, $importType, $this->_filename);
|
||||
// and rename the temp file
|
||||
JFile::move( $oldFileName, $this->_filename);
|
||||
// tell parent controller that we should go to new target afer this import
|
||||
$this->_parentController->set( '_returnController', $importType);
|
||||
$this->_parentController->set( '_returnView', $importType);
|
||||
}
|
||||
|
||||
// save current file for next steps
|
||||
$this->_result['mainText'] = JText::sprintf( 'COM_SH404SEF_IMPORT_VALIDATE_IMPORT', $importType, $this->_total);
|
||||
$this->_result['hiddenText'] = '<input type="hidden" name="filename" value="' . $this->_filename . '" />';
|
||||
|
||||
} catch (Exception $e) {
|
||||
$this->_handleException( $e);
|
||||
}
|
||||
|
||||
return $this->_result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Last step, actually perform importation
|
||||
*
|
||||
*/
|
||||
public function doImport() {
|
||||
|
||||
// collect file and import type information
|
||||
$this->_filename = JRequest::getString( 'filename');
|
||||
|
||||
try {
|
||||
// read file content in an array
|
||||
$lines = file( $this->_filename, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
||||
|
||||
// start analysing
|
||||
if (empty( $lines)) {
|
||||
throw new Sh404sefExceptionDefault( 'COM_SH404SEF_ERROR_IMPORT');
|
||||
}
|
||||
|
||||
// extract header line
|
||||
$header = array_shift( $lines);
|
||||
if (empty( $header)) {
|
||||
throw new Sh404sefExceptionDefault( 'COM_SH404SEF_ERROR_IMPORT');
|
||||
}
|
||||
|
||||
// turn header into an array
|
||||
$header = $this->_lineToArray( $header);
|
||||
|
||||
// count items
|
||||
$this->_total = count( $lines);
|
||||
|
||||
// iterate through file content and create each record
|
||||
foreach( $lines as $line) {
|
||||
$this->_createRecord( $header, $line);
|
||||
}
|
||||
|
||||
// get back memory
|
||||
unset( $lines);
|
||||
|
||||
// delete temporary uploaded file
|
||||
JFile::delete( $this->_filename);
|
||||
|
||||
// which button should be displayed ?
|
||||
$this->_visibleButtonsList = array ( 'terminate');
|
||||
|
||||
// next steps definition
|
||||
$this->_steps = array( 'next' => 3, 'previous' => 0, 'cancel' => -1, 'terminate' => -2);
|
||||
|
||||
// setup display of wizard last page
|
||||
$this->_result['hiddenText'] = '';
|
||||
$this->_result['mainText'] = JText::sprintf('COM_SH404SEF_IMPORT_DONE', $this->_total, $this->_context);
|
||||
$this->_result['mainText'] .= $this->_getTerminateOptions();
|
||||
|
||||
} catch (Exception $e) {
|
||||
|
||||
$this->_handleException( $e);
|
||||
|
||||
}
|
||||
|
||||
return $this->_result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the wizard window and redirect to default page
|
||||
*
|
||||
*/
|
||||
public function doTerminate() {
|
||||
|
||||
// now go back to main page
|
||||
$this->_result = array( 'redirectTo' => true);
|
||||
|
||||
return $this->_result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the wizard window and redirect to default page
|
||||
*
|
||||
*/
|
||||
public function doCancel() {
|
||||
|
||||
$this->_result['redirectTo'] = true;
|
||||
$this->_result['redirectOptions'] = array();
|
||||
|
||||
return $this->_result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyze the content of a potential import file
|
||||
* to try recognize its content. Also find the
|
||||
* number of records in the file
|
||||
* @param string $filename fully pathed file name
|
||||
*/
|
||||
protected function _analyzeImportFileContent( $filename) {
|
||||
|
||||
// read file content in an array
|
||||
$lines = file( $filename, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
||||
|
||||
// start analysing
|
||||
if (empty( $lines)) {
|
||||
throw new Sh404sefExceptionDefault( JText::_('COM_SH404SEF_IMPORT_UNRECOGNIZED_CONTENT'));
|
||||
}
|
||||
|
||||
// extract header line
|
||||
jimport( 'joomla.utilities.string');
|
||||
$header = JString::trim(array_shift( $lines));
|
||||
if (empty( $header)) {
|
||||
throw new Sh404sefExceptionDefault( JText::_('COM_SH404SEF_IMPORT_UNRECOGNIZED_CONTENT'));
|
||||
}
|
||||
|
||||
// check against known headers
|
||||
$headers = Sh404sefHelperGeneral::getExportHeaders();
|
||||
foreach( $headers as $key => $value) {
|
||||
if( $header == $value) {
|
||||
$importType = $key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// have we found something ?
|
||||
if (empty( $importType)) {
|
||||
throw new Sh404sefExceptionDefault( JText::_('COM_SH404SEF_IMPORT_UNRECOGNIZED_CONTENT'));
|
||||
}
|
||||
|
||||
// count items
|
||||
$this->_total = count( $lines);
|
||||
|
||||
// clear memory
|
||||
unset( $lines);
|
||||
|
||||
// return record
|
||||
return $importType;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return html for any option that could
|
||||
* be presented to the user on the last
|
||||
* page of the wizard (like clean temp files)
|
||||
* for instance. This will be displayed just after
|
||||
* the mainText text, as prepared by the main
|
||||
* part of this controller
|
||||
*/
|
||||
protected function _getTerminateOptions() {
|
||||
|
||||
$options = '';
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an exception by returning to step 2, where
|
||||
* user can select a file
|
||||
*
|
||||
* @param Sh404sefExceptionDefault $e
|
||||
*/
|
||||
protected function _handleException( Exception $e) {
|
||||
|
||||
// unable to get the file, display error and go back to step 2, "doUpload"
|
||||
$this->_parentController->setError( $e->getMessage());
|
||||
|
||||
// try delete the uploaded file
|
||||
if (JFile::exists( $this->_filename)) {
|
||||
JFile::delete( $this->_filename);
|
||||
}
|
||||
|
||||
// go back to previous step
|
||||
$this->doUpload();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a record in the database, based
|
||||
* on data read from import file
|
||||
*
|
||||
* @param array $header an array of fields, as built from the header line
|
||||
* @param string $line raw record obtained from import file
|
||||
*/
|
||||
protected function _createRecord( $header, $line) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
protected function _lineToArray( $line, $glue = null) {
|
||||
|
||||
$glue = is_null( $glue) ? Sh404sefHelperFiles::$stringDelimiter . Sh404sefHelperFiles::$fieldDelimiter . Sh404sefHelperFiles::$stringDelimiter : $glue;
|
||||
|
||||
// remove opening and closing quotes - can't use trim or ltrim, as this would remove several occurences
|
||||
if (substr( $line, 0, 1) == Sh404sefHelperFiles::$stringDelimiter) {
|
||||
$line = substr( $line, 1);
|
||||
}
|
||||
if (substr( $line, -1, 1) == Sh404sefHelperFiles::$stringDelimiter) {
|
||||
$line = substr( $line, 0, -1);
|
||||
}
|
||||
|
||||
// break up the line
|
||||
$records = explode( $glue, $line);
|
||||
if (empty( $records)) {
|
||||
return $records;
|
||||
}
|
||||
|
||||
// now clean up a bit
|
||||
foreach( $records as $i => $value) {
|
||||
|
||||
// remove double quotes, and store back in array
|
||||
$records[$i] = Sh404sefHelperFiles::csvUnquote( $value);
|
||||
|
||||
}
|
||||
|
||||
return $records;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
<html><body bgcolor="#FFFFFF"></body></html>
|
||||