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
This commit is contained in:
LÁZÁR Imre AI Agent 2026-07-16 11:24:39 +02:00
parent ff342a4c18
commit b2bc981aab
939 changed files with 129760 additions and 0 deletions

View File

@ -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>

View File

@ -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;
}
}

View File

@ -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[] = '&nbsp;' . JText::_('COM_SH404SEF_ANALYTICS_DATE_RANGE') . '&nbsp;<div class="largertext">'
. $this->_options['startDate'] . '&nbsp;&nbsp;>>&nbsp;&nbsp;' . $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') . ':&nbsp;' . $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[] = '&nbsp;' . JText::_('COM_SH404SEF_ANALYTICS_START_DATE') . ':&nbsp;' . $select;
// select end date
$select = JHTML::_('calendar', $this->_options['endDate'], 'endDate', 'endDate', '%Y-%m-%d', array('class' => 'class="textinput"'));
$filters[] = '&nbsp;' . JText::_('COM_SH404SEF_ANALYTICS_END_DATE') . ':&nbsp;' . $select;
// select groupBy (day, week, month)
$select = Sh404sefHelperAnalytics::buildAnalyticsGroupBySelectList($this->_options['groupBy'], 'groupBy', $autoSubmit = false,
$addSelectAll = false, $selectAllTitle = '', $customSubmit);
$filters[] = '&nbsp;' . JText::_('COM_SH404SEF_ANALYTICS_GROUP_BY') . ':&nbsp;' . $select;
// add a click to update link
$filters[] = '&nbsp;<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[] = '&nbsp;' . JText::_('COM_SH404SEF_ANALYTICS_DATE_RANGE') . '&nbsp;<div class="largertext">'
. $this->_options['startDate'] . '&nbsp;&nbsp;>>&nbsp;&nbsp;' . $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]);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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]));
}
}
}

View File

@ -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]));
}
}
}

View File

@ -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 == '&nbsp') {
$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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1 @@
<html><body bgcolor="#FFFFFF"></body></html>

View File

@ -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;
}
}

View File

@ -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.';
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -0,0 +1 @@
<html><body bgcolor="#FFFFFF"></body></html>

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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
*/

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 574 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 683 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 567 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 650 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 977 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1 @@
<html><body bgcolor="#FFFFFF"></body></html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 692 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 404 B

View File

@ -0,0 +1 @@
<html><body bgcolor="#FFFFFF"></body></html>

View File

@ -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'>&nbsp;</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();
}
};

View File

@ -0,0 +1 @@
<html><body bgcolor="#FFFFFF"></body></html>

View File

@ -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'>&nbsp;</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'>&nbsp;</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'>&nbsp;</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'>&nbsp;</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'>&nbsp;</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();
}

View File

@ -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'>&nbsp;</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();
}
};

View File

@ -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);

View File

@ -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'>&nbsp;</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'>&nbsp;</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'>&nbsp;</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'>&nbsp;</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'>&nbsp;</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);
}

View File

@ -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();
}
};

View File

@ -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'>&nbsp;</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();
}

View File

@ -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'>&nbsp;</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;
}

View File

@ -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'>&nbsp;</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);
};

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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';
}

View File

@ -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 = '';
}
}

View File

@ -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() {
}
}

View File

@ -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 )));
}
}

View File

@ -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 )));
}
}

View File

@ -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'];
}
}
}

View File

@ -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.');

File diff suppressed because it is too large Load Diff

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1 @@
<html><body bgcolor="#FFFFFF"></body></html>

Some files were not shown because too many files have changed in this diff Show More