oxseoencoder.php

Go to the documentation of this file.
00001 <?php
00002 
00007 class oxSeoEncoder extends oxSuperCfg
00008 {
00015     protected static $_aReservedWords = array( 'admin' );
00016 
00022     protected static $_aReservedEntryKeys = null;
00023 
00029     protected static $_sSeparator = null;
00030 
00036     protected $_iIdLength = 255;
00037 
00043     protected static $_sPrefix = null;
00044 
00050     protected $_sAddParams = null;
00051 
00057     protected static $_instance = null;
00058 
00064     protected static $_aFixedCache = array();
00065 
00070     protected static $_sCacheKey = null;
00071 
00076     protected static $_aCache = null;
00077 
00083     public static function getInstance()
00084     {
00085         if ( defined( 'OXID_PHP_UNIT' ) ) {
00086             self::$_instance = modInstances::getMod( __CLASS__ );
00087         }
00088 
00089         if (!self::$_instance) {
00090             self::$_instance = oxNew("oxSeoEncoder");
00091             if ( defined( 'OXID_PHP_UNIT' ) ) {
00092                 modInstances::addMod( __CLASS__, self::$_instance);
00093             }
00094         }
00095 
00096         return self::$_instance;
00097 
00098     }
00099 
00108     public function addLanguageParam( $sSeoUrl, $iLang )
00109     {
00110         $iLang    = (int) $iLang;
00111         $iDefLang = (int) $this->getConfig()->getConfigParam( 'iDefSeoLang' );
00112         $aLangIds = oxLang::getInstance()->getLanguageIds();
00113 
00114         if ( $iLang != $iDefLang && isset( $aLangIds[$iLang] ) && getStr()->strpos( $sSeoUrl, $aLangIds[$iLang] . '/' ) !== 0 ) {
00115             $sSeoUrl = $aLangIds[$iLang] . '/'.$sSeoUrl;
00116         }
00117 
00118         return $sSeoUrl;
00119     }
00120 
00133     protected function _processSeoUrl( $sSeoUrl, $sObjectId = null, $iLang = null, $blExclude = false )
00134     {
00135         if (!$blExclude) {
00136             $sSeoUrl = $this->addLanguageParam( $sSeoUrl, $iLang );
00137         }
00138         return $this->_getUniqueSeoUrl( $sSeoUrl, $sObjectId, $iLang );
00139     }
00140 
00144     public function __construct()
00145     {
00146         $myConfig = $this->getConfig();
00147         if (!self::$_sSeparator) {
00148             $this->setSeparator( $myConfig->getConfigParam( 'sSEOSeparator' ) );
00149         }
00150         if (!self::$_sPrefix) {
00151             $this->setPrefix( $myConfig->getConfigParam( 'sSEOuprefix' ) );
00152         }
00153         $this->setReservedWords( $myConfig->getConfigParam( 'aSEOReservedWords' ) );
00154     }
00155 
00167     protected function _copyToHistory( $sId, $iShopId, $iLang, $sType = null, $sNewId = null )
00168     {
00169         $oDb = oxDb::getDb();
00170         $sObjectid = $sNewId?$oDb->quote( $sNewId ):'oxobjectid';
00171         $sType     = $sType?"oxtype =".$oDb->quote( $sType )." and":'';
00172         $iLang     = (int) $iLang;
00173 
00174         // moving
00175         $sSub = "select $sObjectid, MD5( LOWER( oxseourl ) ), oxshopid, oxlang, now() from oxseo
00176                  where {$sType} oxobjectid = ".$oDb->quote( $sId )." and oxshopid = ".$oDb->quote( $iShopId )." and
00177                  oxlang = {$iLang} and oxexpired = '1'";
00178         $sQ   = "replace oxseohistory ( oxobjectid, oxident, oxshopid, oxlang, oxinsert ) {$sSub}";
00179         $oDb->execute( $sQ );
00180     }
00181 
00190     public function getDynamicObjectId( $iShopId, $sStdUrl )
00191     {
00192         return $this->_getStaticObjectId( $iShopId, $sStdUrl );
00193     }
00194 
00204     protected function _getDynamicUri( $sStdUrl, $sSeoUrl, $iLang )
00205     {
00206         $iShopId = $this->getConfig()->getShopId();
00207 
00208         $sStdUrl   = $this->_trimUrl( $sStdUrl );
00209         $sObjectId = $this->getDynamicObjectId( $iShopId, $sStdUrl );
00210         $sSeoUrl   = $this->_prepareUri( $this->addLanguageParam( $sSeoUrl, $iLang ) );
00211 
00212         //load details link from DB
00213         $sOldSeoUrl = $this->_loadFromDb( 'dynamic', $sObjectId, $iLang );
00214         if ( $sOldSeoUrl === $sSeoUrl ) {
00215             $sSeoUrl = $sOldSeoUrl;
00216         } else {
00217 
00218             if ( $sOldSeoUrl ) {
00219                 // old must be transferred to history
00220                 $this->_copyToHistory( $sObjectId, $iShopId, $iLang, 'dynamic' );
00221             }
00222 
00223             // creating unique
00224             $sSeoUrl = $this->_processSeoUrl( $sSeoUrl, $sObjectId, $iLang );
00225 
00226             // inserting
00227             $this->_saveToDb( 'dynamic', $sObjectId, $sStdUrl, $sSeoUrl, $iLang, $iShopId );
00228         }
00229 
00230         return $sSeoUrl;
00231     }
00232 
00242     protected function _getFullUrl( $sSeoUrl, $iLang = null, $blSsl = false )
00243     {
00244         if ( $sSeoUrl ) {
00245             $sFullUrl = ( $blSsl ? $this->getConfig()->getSslShopUrl( $iLang ) : $this->getConfig()->getShopUrl( $iLang, false ) ) . $sSeoUrl;
00246             return oxUtilsUrl::getInstance()->processSeoUrl( $sFullUrl );
00247         }
00248         return false;
00249     }
00250 
00260     protected function _getSeoIdent( $sSeoUrl )
00261     {
00262         return md5( strtolower( $sSeoUrl ) );
00263     }
00264 
00274     protected function _getStaticUri( $sStdUrl, $iShopId, $iLang )
00275     {
00276         $sStdUrl = $this->_trimUrl( $sStdUrl, $iLang );
00277         return $this->_loadFromDb( 'static', $this->_getStaticObjectId( $iShopId, $sStdUrl ), $iLang );
00278     }
00279 
00285     protected function _getUrlExtension()
00286     {
00287         return;
00288     }
00289 
00302     protected function _getUniqueSeoUrl( $sSeoUrl, $sObjectId = null, $iObjectLang = null )
00303     {
00304         $sSeoUrl = $this->_prepareUri( $sSeoUrl );
00305         $oStr = getStr();
00306         $sExt = '';
00307         if ( $oStr->preg_match( '/(\.html?|\/)$/i', $sSeoUrl, $aMatched ) ) {
00308             $sExt = $aMatched[0];
00309         }
00310         $sBaseSeoUrl = $sSeoUrl;
00311         if ( $sExt && $oStr->substr( $sSeoUrl, 0 - $oStr->strlen( $sExt ) ) == $sExt ) {
00312             $sBaseSeoUrl = $oStr->substr( $sSeoUrl, 0, $oStr->strlen( $sSeoUrl ) - $oStr->strlen( $sExt ) );
00313         }
00314 
00315         $oDb = oxDb::getDb();
00316         $iShopId = $this->getConfig()->getShopId();
00317         $iCnt = 0;
00318         $sCheckSeoUrl = $this->_trimUrl( $sSeoUrl );
00319         $sQ = "select 1 from oxseo where oxshopid = '{$iShopId}'";
00320 
00321         // skipping self
00322         if ( $sObjectId && isset($iObjectLang) ) {
00323             $iObjectLang = (int) $iObjectLang;
00324             $sQ .= " and not (oxobjectid = " . $oDb->quote( $sObjectId ) . " and oxlang = $iObjectLang)";
00325         }
00326 
00327         while ( $oDb->getOne( $sQ ." and oxident= " . $oDb->quote( $this->_getSeoIdent( $sCheckSeoUrl ) ) ) ) {
00328             $sAdd = '';
00329             if ( self::$_sPrefix ) {
00330                 $sAdd = self::$_sSeparator . self::$_sPrefix;
00331             }
00332             if ( $iCnt ) {
00333                 $sAdd .= self::$_sSeparator . $iCnt;
00334             }
00335             ++$iCnt;
00336 
00337             $sSeoUrl = $sBaseSeoUrl . $sAdd . $sExt;
00338             $sCheckSeoUrl = $this->_trimUrl( $sSeoUrl );
00339         }
00340         return $sSeoUrl;
00341     }
00342 
00357     protected function _isFixed( $sType, $sId, $iLang, $iShopId = null, $sParams = null, $blStrictParamsCheck = true)
00358     {
00359         if ( $iShopId === null ) {
00360             $iShopId = $this->getConfig()->getShopId();
00361         }
00362         $iLang = (int) $iLang;
00363 
00364         if ( !isset( self::$_aFixedCache[$sType][$sShopId][$sId][$iLang] ) ) {
00365             $oDb = oxDb::getDb( true );
00366 
00367             $sQ = "select oxfixed from oxseo where oxtype = ".$oDb->quote( $sType )."
00368                    and oxobjectid = ".$oDb->quote( $sId ) ." and oxshopid = ".$oDb->quote( $iShopId )." and oxlang = '{$iLang}'";
00369 
00370             $sParams = $sParams ? $oDb->quote( $sParams ) : "''";
00371             if ( $sParams && $blStrictParamsCheck ) {
00372                 $sQ .= " and oxparams = {$sParams}";
00373             } else {
00374                 $sQ .= " order by oxparams = {$sParams} desc";
00375             }
00376             $sQ .= " limit 1";
00377 
00378             self::$_aFixedCache[$sType][$sShopId][$sId][$iLang] = (bool) $oDb->getOne( $sQ );
00379         }
00380         return self::$_aFixedCache[$sType][$sShopId][$sId][$iLang];
00381     }
00382 
00388     protected function _getCacheKey()
00389     {
00390         // use cache in non admin mode
00391         if ( self::$_sCacheKey === null ) {
00392             self::$_sCacheKey = false;
00393             if ( !$this->isAdmin() && ( $oView = $this->getConfig()->getActiveView() ) ) {
00394                 self::$_sCacheKey = md5( $oView->getViewId() ) . "seo";
00395             }
00396         }
00397         return self::$_sCacheKey;
00398     }
00399 
00407     protected function _loadFromCache( $sCacheIdent )
00408     {
00409         startProfile( "seoencoder_loadFromCache" );
00410 
00411         $sCache = false;
00412         if ( self::$_aCache === null ) {
00413             self::$_aCache = false;
00414             if ( ( $sCacheKey = $this->_getCacheKey() ) !== false ) {
00415                 self::$_aCache = oxUtils::getInstance()->fromFileCache( $sCacheKey );
00416             }
00417         }
00418 
00419         if ( self::$_aCache && isset( self::$_aCache[$sCacheIdent] ) ) {
00420             $sCache = self::$_aCache[$sCacheIdent];
00421         }
00422 
00423         stopProfile( "seoencoder_loadFromCache" );
00424         return $sCache;
00425     }
00426 
00435     protected function _saveInCache( $sCacheIdent, $sCache )
00436     {
00437         startProfile( "seoencoder_saveInCache" );
00438 
00439         $blSaved = false;
00440         if ( $sCache && ( $sCacheKey = $this->_getCacheKey() ) !== false ) {
00441             self::$_aCache[$sCacheIdent] = $sCache;
00442             $blSaved = oxUtils::getInstance()->toFileCache( $sCacheKey, self::$_aCache );
00443         }
00444 
00445         stopProfile( "seoencoder_saveInCache" );
00446         return $blSaved;
00447     }
00448 
00464     protected function _loadFromDb( $sType, $sId, $iLang, $iShopId = null, $sParams = null, $blStrictParamsCheck = true)
00465     {
00466         if ( $iShopId === null ) {
00467             $iShopId = $this->getConfig()->getShopId();
00468         }
00469 
00470         $iLang = (int) $iLang;
00471 
00472         $oDb = oxDb::getDb( true );
00473         $sQ = "select oxfixed, oxseourl, oxexpired, oxtype from oxseo where oxtype = ".$oDb->quote( $sType )."
00474                and oxobjectid = ".$oDb->quote( $sId ) ." and oxshopid = ".$oDb->quote( $iShopId )." and oxlang = '{$iLang}'";
00475 
00476         $sParams = $sParams ? $sParams : '';
00477         if ( $sParams && $blStrictParamsCheck ) {
00478             $sQ .= " and oxparams = '{$sParams}'";
00479         } else {
00480             $sQ .= " order by oxparams = '{$sParams}' desc";
00481         }
00482         $sQ .= " limit 1";
00483 
00484         // caching to avoid same queries..
00485         $sIdent = md5( $sQ );
00486 
00487         // looking in cache
00488         if ( ( $sSeoUrl = $this->_loadFromCache( $sIdent ) ) === false ) {
00489 
00490             $oRs = $oDb->execute( $sQ );
00491             if ( $oRs && $oRs->recordCount() > 0 && !$oRs->EOF ) {
00492                 // moving expired static urls to history ..
00493                 if ( $oRs->fields['oxexpired'] && ( $oRs->fields['oxtype'] == 'static' || $oRs->fields['oxtype'] == 'dynamic' ) ) {
00494                     // if expired - copying to history, marking as not expired
00495                     $this->_copyToHistory( $sId, $iShopId, $iLang );
00496                     $oDb->execute( "update oxseo set oxexpired = 0 where oxobjectid = ".$oDb->quote( $sId )." and oxlang = '{$iLang}'" );
00497                     $sSeoUrl = $oRs->fields['oxseourl'];
00498                 } elseif ( !$oRs->fields['oxexpired'] || $oRs->fields['oxfixed'] ) {
00499                     // if seo url is available and is valid
00500                     $sSeoUrl = $oRs->fields['oxseourl'];
00501                 }
00502 
00503                 // storing in cache
00504                 $this->_saveInCache( $sIdent, $sSeoUrl );
00505             }
00506         }
00507         return $sSeoUrl;
00508     }
00509 
00516     protected function _getReservedEntryKeys()
00517     {
00518         if ( !isset( self::$_aReservedEntryKeys ) || !is_array( self::$_aReservedEntryKeys ) ) {
00519             $sDir = getShopBasePath();
00520             self::$_aReservedEntryKeys = array_map('preg_quote', self::$_aReservedWords, array('#'));
00521             $oStr = getStr();
00522             foreach ( glob( "$sDir/*" ) as $sFile ) {
00523                 if ( $oStr->preg_match( '/^(.+)\.php[0-9]*$/i', basename( $sFile ), $aMatches ) ) {
00524                     self::$_aReservedEntryKeys[] = preg_quote( $aMatches[0], '#' );
00525                     self::$_aReservedEntryKeys[] = preg_quote( $aMatches[1], '#' );
00526                 } elseif ( is_dir( $sFile ) ) {
00527                     self::$_aReservedEntryKeys[] = preg_quote( basename( $sFile ), '#' );
00528                 }
00529             }
00530             self::$_aReservedEntryKeys = array_unique(self::$_aReservedEntryKeys);
00531         }
00532         return self::$_aReservedEntryKeys;
00533     }
00534 
00542     protected function _prepareUri( $sUri )
00543     {
00544         // decoding entities
00545         $sUri = $this->encodeString( $sUri );
00546 
00547         // basic string preparation
00548         $sUri = strip_tags( $sUri );
00549         $oStr = getStr();
00550 
00551 
00552         // if found ".html" or "/" at the end - removing it temporary
00553         $oStr = getStr();
00554         $sExt = $this->_getUrlExtension();
00555         if ($sExt === null) {
00556             $aMatched = array();
00557             if ( $oStr->preg_match( '/(\.html?|\/)$/i', $sUri, $aMatched ) ) {
00558                 $sExt = $aMatched[0];
00559             } else {
00560                 $sExt = '/';
00561             }
00562         }
00563         if ( $sExt && $oStr->substr( $sUri, 0 - $oStr->strlen( $sExt ) ) == $sExt ) {
00564             $sUri = $oStr->substr( $sUri, 0, $oStr->strlen( $sUri ) - $oStr->strlen( $sExt ) );
00565         }
00566 
00567         // removing any special characters
00568         $sRegExp = '/[^A-Za-z0-9'.preg_quote( self::$_sSeparator, '/').preg_quote( self::$_sPrefix, '/').'\/]+/';
00569         $sUri  = $oStr->preg_replace( array( "/\W*\/\W*/", $sRegExp ), array( "/", self::$_sSeparator ), $sUri );
00570 
00571         // SEO id is empty ?
00572         if ( !$sUri && self::$_sPrefix ) {
00573             $sUri = $this->_prepareUri( self::$_sPrefix );
00574         }
00575 
00576         $sAdd = '';
00577         if ('/' != self::$_sSeparator) {
00578             $sAdd = self::$_sSeparator . self::$_sPrefix;
00579             $sUri = trim($sUri, self::$_sSeparator);
00580         } else {
00581             $sAdd = '_' . self::$_sPrefix;
00582         }
00583 
00584         // binding the ending back
00585         $sUri .= $sExt;
00586 
00587         // fix for not having url, which executes through /other/ script then seo decoder
00588         $sUri = $oStr->preg_replace( "#^(/*)(".implode('|', $this->_getReservedEntryKeys()).")(/|$)#i", "\$1\$2$sAdd\$3", $sUri );
00589 
00590         // cleaning
00591         return $oStr->preg_replace( array( '|//+|', '/' . preg_quote( self::$_sSeparator . self::$_sSeparator, '/' ) .'+/' ),
00592                              array( '/', self::$_sSeparator ), $sUri );
00593     }
00594 
00595 
00604     protected function _prepareTitle( $sTitle, $blSkipTruncate = false )
00605     {
00606         $sTitle = $this->encodeString( $sTitle );
00607         $sSep = self::$_sSeparator;
00608         if (!$sSep || ('/' == $sSep)) {
00609             $sSep = '_';
00610         }
00611 
00612         $sRegExp = '/[^A-Za-z0-9\/'.preg_quote( self::$_sPrefix, '/').preg_quote($sSep, '/').']+/';
00613         $sTitle = preg_replace( array("#/+#", $sRegExp, "# +#", "#(".preg_quote($sSep, '/').")+#"), $sSep, $sTitle );
00614 
00615         $oStr = getStr();
00616         // smart truncate
00617         if ( !$blSkipTruncate && $oStr->strlen( $sTitle ) > $this->_iIdLength ) {
00618             $iFirstSpace = $oStr->strpos( $sTitle, $sSep, $this->_iIdLength);
00619             if ( $iFirstSpace !== false ) {
00620                 $sTitle = $oStr->substr( $sTitle, 0, $iFirstSpace );
00621             }
00622         }
00623 
00624         $sTitle = trim( $sTitle, $sSep );
00625 
00626         if (!$sTitle) {
00627             return self::$_sPrefix;
00628         }
00629         // cleaning
00630         return $sTitle;
00631     }
00632 
00633 
00650     protected function _saveToDb( $sType, $sObjectId, $sStdUrl, $sSeoUrl, $iLang, $iShopId = null, $blFixed = null, $sParams = null )
00651     {
00652         $oDb = oxDb::getDb( true );
00653         if ( $iShopId === null ) {
00654             $iShopId = $this->getConfig()->getShopId();
00655         }
00656 
00657         $iLang = (int) $iLang;
00658 
00659         $sStdUrl = $this->_trimUrl( $sStdUrl );
00660         $sSeoUrl = $this->_trimUrl( $sSeoUrl );
00661         $sIdent  = $this->_getSeoIdent( $sSeoUrl );
00662 
00663         // transferring old url, thus current url will be regenerated
00664         $sQtedObjectId = $oDb->quote( $sObjectId );
00665         $iQtedShopId   = $oDb->quote( $iShopId );
00666         $sQtedType     = $oDb->quote( $sType );
00667         $sQtedSeoUrl   = $oDb->quote( $sSeoUrl );
00668         $sQtedStdUrl   = $oDb->quote( $sStdUrl );
00669         $sQtedParams   = $oDb->quote( $sParams );
00670         $sQtedIdent    = $oDb->quote( $sIdent );
00671 
00672         // transferring old url, thus current url will be regenerated
00673         $sQ  = "select oxfixed, oxexpired, ( oxstdurl like {$sQtedStdUrl} ) as samestdurl,
00674                 oxseourl like {$sQtedSeoUrl} as sameseourl from oxseo where oxtype = {$sQtedType} and
00675                 oxobjectid = {$sQtedObjectId} and oxshopid = {$iQtedShopId}  and oxlang = {$iLang} ";
00676 
00677         $sQ .= $sParams ? " and oxparams = {$sQtedParams} " : '';
00678         //$sQ .= isset( $blFixed ) ? " and oxfixed = " . ( (int) $blFixed ) . " " : '';
00679         $sQ .= "limit 1";
00680 
00681         $oRs = $oDb->execute( $sQ );
00682         if ( $oRs && $oRs->recordCount() > 0 && !$oRs->EOF ) {
00683             if ( $oRs->fields['samestdurl'] && $oRs->fields['sameseourl'] && $oRs->fields['oxexpired'] ) {
00684                 // fixed state change
00685                 $sFixed = isset( $blFixed ) ? ", oxfixed = " . ( (int) $blFixed ) . " " : '';
00686                 // nothing was changed - setting expired status back to 0
00687                 $sSql  = "update oxseo set oxexpired = 0 {$sFixed} where oxtype = {$sQtedType} and
00688                           oxobjectid = {$sQtedObjectId} and oxshopid = {$iQtedShopId} and oxlang = {$iLang} ";
00689                 $sSql .= $sParams ? " and oxparams = {$sQtedParams} " : '';
00690                 $sSql .= " limit 1";
00691 
00692                 return $oDb->execute( $sSql );
00693             } elseif ( $oRs->fields['oxexpired'] ) {
00694                 // copy to history
00695                 $this->_copyToHistory( $sObjectId, $iShopId, $iLang, $sType );
00696             }
00697         }
00698 
00699         // inserting new or updating
00700         $sParams = $sParams ? $oDb->quote( $sParams ) :'""';
00701         $blFixed = (int) $blFixed;
00702 
00703         $sQ  = "insert into oxseo
00704                     (oxobjectid, oxident, oxshopid, oxlang, oxstdurl, oxseourl, oxtype, oxfixed, oxexpired, oxparams)
00705                 values
00706                     ( {$sQtedObjectId}, {$sQtedIdent}, {$iQtedShopId}, {$iLang}, {$sQtedStdUrl}, {$sQtedSeoUrl}, {$sQtedType}, '$blFixed', '0', {$sParams} )
00707                 on duplicate key update
00708                     oxident = {$sQtedIdent}, oxstdurl = {$sQtedStdUrl}, oxseourl = {$sQtedSeoUrl}, oxfixed = '$blFixed', oxexpired = '0'";
00709 
00710         return $oDb->execute( $sQ );
00711     }
00712 
00723     protected function _trimUrl( $sUrl, $iLang = null )
00724     {
00725         $myConfig = $this->getConfig();
00726         $oStr = getStr();
00727         $sUrl = str_replace( array( $myConfig->getShopUrl( $iLang, false ), $myConfig->getSslShopUrl( $iLang ) ), '', $sUrl );
00728         $sUrl = $oStr->preg_replace( '/(\?|&(amp;)?)(force_)?(admin_)?sid=[a-z0-9\.]+&?(amp;)?/i', '\1', $sUrl );
00729         $sUrl = $oStr->preg_replace( '/(\?|&(amp;)?)shp=[0-9]+&?(amp;)?/i', '\1', $sUrl );
00730         $sUrl = $oStr->preg_replace( '/(\?|&(amp;)?)lang=[0-9]+&?(amp;)?/i', '\1', $sUrl );
00731         $sUrl = $oStr->preg_replace( '/(\?|&(amp;)?)cur=[0-9]+&?(amp;)?/i', '\1', $sUrl );
00732         $sUrl = $oStr->preg_replace( '/(\?|&(amp;)?)stoken=[a-z0-9]+&?(amp;)?/i', '\1', $sUrl );
00733         $sUrl = $oStr->preg_replace( '/(\?|&(amp;)?)&(amp;)?/i', '\1', $sUrl );
00734         $sUrl = $oStr->preg_replace( '/(\?|&(amp;)?)+$/i', '', $sUrl );
00735         return trim($sUrl);
00736     }
00737 
00746     public function encodeString( $sString, $blReplaceChars = true )
00747     {
00748         // decoding entities
00749         $sString = getStr()->html_entity_decode( $sString );
00750 
00751         if ( $blReplaceChars ) {
00752             if ( $aReplaceChars = $this->getConfig()->getConfigParam( 'aSeoReplaceChars' ) ) {
00753                 $sString = str_replace( array_keys( $aReplaceChars ), array_values( $aReplaceChars ), $sString );
00754             }
00755         }
00756 
00757         // special chars
00758         $aReplaceWhat = array( '&amp;', '&quot;', '&#039;', '&lt;', '&gt;' );
00759         return str_replace( $aReplaceWhat, '', $sString );
00760     }
00761 
00769     public function setSeparator( $sSeparator = null )
00770     {
00771         self::$_sSeparator = $sSeparator;
00772         if ( !self::$_sSeparator ) {
00773             self::$_sSeparator = '-';
00774         }
00775     }
00776 
00784     public function setPrefix( $sPrefix )
00785     {
00786         if ($sPrefix) {
00787             self::$_sPrefix = $sPrefix;
00788         } else {
00789             self::$_sPrefix = 'oxid';
00790         }
00791     }
00792 
00800     public function setIdLength( $iIdlength = null )
00801     {
00802         if ( isset( $iIdlength ) ) {
00803             $this->_iIdLength = $iIdlength;
00804         }
00805     }
00806 
00815     public function setReservedWords( $aReservedWords )
00816     {
00817         self::$_aReservedWords = array_merge( self::$_aReservedWords, $aReservedWords );
00818     }
00819 
00820 
00832     public function markAsExpired( $sId, $iShopId = null, $iExpStat = 1, $iLang = null, $sParams = null )
00833     {
00834         $oDb = oxDb::getDb();
00835         $sWhere  = $sId ? "where oxobjectid =  " . $oDb->quote( $sId ) : '';
00836         $sWhere .= isset( $iShopId ) ? ( $sWhere ? " and oxshopid = ". $oDb->quote( $iShopId ) : "where oxshopid = ". $oDb->quote( $iShopId ) ) : '';
00837         $sWhere .= $iLang ? ( $sWhere ? " and oxlang = '{$iLang}'" : "where oxlang = '{$iLang}'" ) : '';
00838         $sWhere .= $sParams ? ( $sWhere ? " and {$sParams}" : "where {$sParams}" ) : '';
00839 
00840         $sQ = "update oxseo set oxexpired =  " . $oDb->quote( $iExpStat ) . " $sWhere ";
00841         $oDb->execute( $sQ );
00842     }
00843 
00857     protected function _getPageUri( $oObject, $sType, $sStdUrl, $sSeoUrl, $sParams, $iLang = null, $blFixed = false )
00858     {
00859         if ( !isset( $iLang ) ) {
00860             $iLang = $oObject->getLanguage();
00861         }
00862         $iShopId = $this->getConfig()->getShopId();
00863 
00864         //load page link from DB
00865         $sOldSeoUrl = $this->_loadFromDb( $sType, $oObject->getId(), $iLang, $iShopId, $sParams );
00866         if ( !$sOldSeoUrl ) {
00867             // generating new..
00868             $sSeoUrl = $this->_processSeoUrl( $sSeoUrl, $oObject->getId(), $iLang );
00869             $this->_saveToDb( $sType, $oObject->getId(), $sStdUrl, $sSeoUrl, $iLang, $iShopId, (int) $blFixed, $sParams );
00870         } else {
00871             // using old
00872             $sSeoUrl = $sOldSeoUrl;
00873         }
00874         return $sSeoUrl;
00875     }
00876 
00885     protected function _getStaticObjectId( $iShopId, $sStdUrl )
00886     {
00887         return md5( strtolower ( $iShopId . $this->_trimUrl( $sStdUrl ) ) );
00888     }
00889 
00899     public function encodeStaticUrls( $aStaticUrl, $iShopId, $iLang )
00900     {
00901         $oDb = oxDb::getDb();
00902         $sValues = '';
00903         $sOldObjectId = null;
00904 
00905         // standard url
00906         $sStdUrl = $this->_trimUrl( trim( $aStaticUrl['oxseo__oxstdurl'] ) );
00907         $sObjectId = $aStaticUrl['oxseo__oxobjectid'];
00908 
00909         if ( !$sObjectId || $sObjectId == '-1' ) {
00910             $sObjectId = $this->_getStaticObjectId( $iShopId, $sStdUrl );
00911         } else {
00912             // marking entry as needs to move to history
00913             $sOldObjectId = $sObjectId;
00914 
00915             // if std url does not match old
00916             if ( $this->_getStaticObjectId( $iShopId, $sStdUrl ) != $sObjectId ) {
00917                 $sObjectId = $this->_getStaticObjectId( $iShopId, $sStdUrl );
00918             }
00919         }
00920 
00921         foreach ( $aStaticUrl['oxseo__oxseourl'] as $iLang => $sSeoUrl ) {
00922 
00923             $iLang = (int) $iLang;
00924 
00925             // generating seo url
00926             $sSeoUrl = $this->_trimUrl( $sSeoUrl );
00927             if ( $sSeoUrl ) {
00928                 $sSeoUrl = $this->_processSeoUrl( $sSeoUrl, $sObjectId, $iLang );
00929             }
00930 
00931 
00932             if ( $sOldObjectId ) {
00933                 // move changed records to history
00934                 if ( !$oDb->getOne( "select (" . $oDb->quote( $sSeoUrl ) . " like oxseourl) & (" . $oDb->quote( $sStdUrl ) . " like oxstdurl) from oxseo where oxobjectid = ".$oDb->quote( $sOldObjectId )." and oxshopid = '{$iShopId}' and oxlang = '{$iLang}' " ) ) {
00935                     $this->_copyToHistory( $sOldObjectId, $iShopId, $iLang, 'static', $sObjectId );
00936                 }
00937             }
00938 
00939             if ( !$sSeoUrl || !$sStdUrl ) {
00940                 continue;
00941             }
00942 
00943             $sIdent = $this->_getSeoIdent( $sSeoUrl );
00944 
00945             if ( $sValues ) {
00946                 $sValues .= ', ';
00947             }
00948 
00949             $sValues .= "( " . $oDb->quote( $sObjectId ) . ", " . $oDb->quote( $sIdent ) . ", " . $oDb->quote( $iShopId ).", '{$iLang}', " . $oDb->quote( $sStdUrl ) . ", " . $oDb->quote( $sSeoUrl ) . ", 'static' )";
00950         }
00951 
00952         // must delete old before insert/update
00953         if ( $sOldObjectId ) {
00954             $oDb->execute( "delete from oxseo where oxobjectid in ( " . $oDb->quote( $sOldObjectId ) . ", " . $oDb->quote( $sObjectId ) . " )" );
00955         }
00956 
00957         // (re)inserting
00958         if ( $sValues ) {
00959 
00960             $sQ = "insert into oxseo ( oxobjectid, oxident, oxshopid, oxlang, oxstdurl, oxseourl, oxtype ) values {$sValues} ";
00961             $oDb->execute( $sQ );
00962         }
00963 
00964         return $sObjectId;
00965     }
00966 
00974     public function copyStaticUrls( $iShopId )
00975     {
00976         $iBaseShopId = $this->getConfig()->getBaseShopId();
00977         if ( $iShopId != $iBaseShopId ) {
00978             $oDb = oxDb::getDb();
00979             foreach (array_keys(oxLang::getInstance()->getLanguageIds()) as $iLang) {
00980                 $sQ = "insert into oxseo ( oxobjectid, oxident, oxshopid, oxlang, oxstdurl, oxseourl, oxtype )
00981                        select MD5( LOWER( CONCAT( " . $oDb->quote( $iShopId ) . ", oxstdurl ) ) ), MD5( LOWER( oxseourl ) ),
00982                        " . $oDb->quote( $iShopId ) . ", oxlang, oxstdurl, oxseourl, oxtype from oxseo where oxshopid = '{$iBaseShopId}' and oxtype = 'static' and oxlang='$iLang' ";
00983                 $oDb->execute( $sQ );
00984             }
00985         }
00986     }
00987 
00997     public function getStaticUrl( $sStdUrl, $iLang = null, $iShopId = null )
00998     {
00999         if (!isset($iShopId)) {
01000             $iShopId = $this->getConfig()->getShopId();
01001         }
01002         if (!isset($iLang)) {
01003             $iLang   = oxLang::getInstance()->getEditLanguage();
01004         }
01005 
01006         if ( isset($this->_aStaticUrlCache[$sStdUrl][$iLang][$iShopId])) {
01007             return $this->_aStaticUrlCache[$sStdUrl][$iLang][$iShopId];
01008         }
01009 
01010         $sFullUrl = '';
01011         if ( ( $sSeoUrl = $this->_getStaticUri( $sStdUrl, $iShopId, $iLang ) ) ) {
01012             $sFullUrl = $this->_getFullUrl( $sSeoUrl, $iLang, strpos( $sStdUrl, "https:" ) === 0 );
01013         }
01014 
01015 
01016         $this->_aStaticUrlCache[$sStdUrl][$iLang][$iShopId] = $sFullUrl;
01017 
01018         return $sFullUrl;
01019     }
01020 
01039     public function addSeoEntry( $sObjectId, $iShopId, $iLang, $sStdUrl, $sSeoUrl, $sType, $blFixed = 1, $sKeywords = '', $sDescription = '', $sParams = '', $blExclude = false, $sAltObjectId = null )
01040     {
01041         $sSeoUrl = $this->_processSeoUrl( $this->_trimUrl( $sSeoUrl ? $sSeoUrl : $this->_getAltUri( $sAltObjectId ? $sAltObjectId : $sObjectId, $iLang ) ), $sObjectId, $iLang, $blExclude );
01042         if ( $this->_saveToDb( $sType, $sObjectId, $sStdUrl, $sSeoUrl, $iLang, $iShopId, $blFixed, $sParams ) ) {
01043 
01044             $oDb = oxDb::getDb( true );
01045 
01046             //
01047             $sQtedObjectId = $oDb->quote( $sAltObjectId ? $sAltObjectId : $sObjectId );
01048             $iQtedShopId   = $oDb->quote( $iShopId );
01049 
01050             $oStr = getStr();
01051             if ( $sKeywords !== false ) {
01052                 $sKeywords = $oDb->quote( $oStr->htmlspecialchars( $this->encodeString( strip_tags( $sKeywords ), false ) ) );
01053             }
01054 
01055             if ( $sDescription !== false ) {
01056                 $sDescription = $oDb->quote( $oStr->htmlspecialchars( strip_tags( $sDescription ) ) );
01057             }
01058 
01059             $sQ = "insert into oxobject2seodata
01060                        ( oxobjectid, oxshopid, oxlang, oxkeywords, oxdescription )
01061                    values
01062                        ( {$sQtedObjectId}, {$iQtedShopId}, {$iLang}, ".( $sKeywords ? $sKeywords : "''" ).", ".( $sDescription ? $sDescription : "''" )." )
01063                    on duplicate key update
01064                        oxkeywords = ".( $sKeywords ? $sKeywords : "oxkeywords" ).", oxdescription = ".( $sDescription ? $sDescription : "oxdescription" );
01065             $oDb->execute( $sQ );
01066         }
01067     }
01068 
01077     protected function _getAltUri( $sObjectId, $iLang )
01078     {
01079     }
01080 
01091     public function deleteSeoEntry( $sObjectId, $iShopId, $iLang, $sType )
01092     {
01093         $oDb = oxDb::getDb();
01094         $sQ = "delete from oxseo where oxobjectid = " . $oDb->quote( $sObjectId ) . " and oxshopid = " . $oDb->quote( $iShopId ) . " and oxlang = " . $oDb->quote( $iLang ) . " and oxtype = " . $oDb->quote( $sType ) . " ";
01095         oxDb::getDb()->execute( $sQ );
01096     }
01097 
01108     public function getMetaData( $sObjectId, $sMetaType, $iShopId = null, $iLang = null )
01109     {
01110         $oDb = oxDb::getDb();
01111 
01112         $iShopId = ( !isset( $iShopId ) ) ? $this->getConfig()->getShopId():$iShopId;
01113         $iLang   = ( !isset( $iLang ) ) ? oxLang::getInstance()->getObjectTplLanguage():((int) $iLang);
01114         return $oDb->getOne( "select {$sMetaType} from oxobject2seodata where oxobjectid = " . $oDb->quote( $sObjectId ) . " and oxshopid = " . $oDb->quote( $iShopId )." and oxlang = '{$iLang}'" );
01115     }
01116 
01130     public function getDynamicUrl( $sStdUrl, $sSeoUrl, $iLang )
01131     {
01132         startProfile("getDynamicUrl");
01133         $sDynUrl = $this->_getFullUrl( $this->_getDynamicUri( $sStdUrl, $sSeoUrl, $iLang ), strpos( $sStdUrl, "https:" ) === 0 );
01134         stopProfile("getDynamicUrl");
01135         return $sDynUrl;
01136     }
01137 
01146     public function fetchSeoUrl( $sStdUrl, $iLanguage = null )
01147     {
01148         $oDb = oxDb::getDb( true );
01149         $iLanguage = isset( $iLanguage ) ? ( (int) $iLanguage ) : oxLang::getInstance()->getBaseLanguage();
01150         $sSeoUrl   = false;
01151 
01152         $sShopId = $this->getConfig()->getShopId();
01153 
01154         $sQ = "select oxseourl, oxlang from oxseo where oxstdurl = ".$oDb->quote( $sStdUrl )." and oxlang = '$iLanguage' and oxshopid = '$sShopId' limit 1";
01155         $oRs = $oDb->execute( $sQ );
01156         if ( !$oRs->EOF ) {
01157             $sSeoUrl = $oRs->fields['oxseourl'];
01158         }
01159 
01160         return $sSeoUrl;
01161     }
01162 }