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 $_aSeoCache = array();
00065 
00071     protected $_aFixedCache = array();
00072 
00078     public static function getInstance()
00079     {
00080         if ( defined( 'OXID_PHP_UNIT' ) ) {
00081             self::$_instance = modInstances::getMod( __CLASS__ );
00082         }
00083 
00084         if (!self::$_instance) {
00085             self::$_instance = oxNew("oxSeoEncoder");
00086             if ( defined( 'OXID_PHP_UNIT' ) ) {
00087                 modInstances::addMod( __CLASS__, self::$_instance);
00088             }
00089         }
00090 
00091         if ( defined( 'OXID_PHP_UNIT' ) ) {
00092             // resetting cache
00093             self::$_instance->_aSeoCache = array();
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         return $this->_getUniqueSeoUrl( $blExclude ? $sSeoUrl : $this->addLanguageParam( $sSeoUrl, $iLang ), $sObjectId, $iLang );
00136     }
00137 
00143     public function resetCache()
00144     {
00145         $this->_aSeoCache = array();
00146     }
00147 
00151     public function __construct()
00152     {
00153         $myConfig = $this->getConfig();
00154         if (!self::$_sSeparator) {
00155             $this->setSeparator( $myConfig->getConfigParam( 'sSEOSeparator' ) );
00156         }
00157         if (!self::$_sPrefix) {
00158             $this->setPrefix( $myConfig->getConfigParam( 'sSEOuprefix' ) );
00159         }
00160         $this->setReservedWords( $myConfig->getConfigParam( 'aSEOReservedWords' ) );
00161     }
00162 
00174     protected function _copyToHistory( $sId, $iShopId, $iLang, $sType = null, $sNewId = null )
00175     {
00176         $oDb = oxDb::getDb();
00177         $sObjectid = $sNewId?$oDb->quote( $sNewId ):'oxobjectid';
00178         $sType     = $sType?"oxtype =".$oDb->quote( $sType )." and":'';
00179         $iLang     = (int) $iLang;
00180 
00181         // moving
00182         $sSub = "select $sObjectid, MD5( LOWER( oxseourl ) ), oxshopid, oxlang, now() from oxseo
00183                  where {$sType} oxobjectid = ".$oDb->quote( $sId )." and oxshopid = ".$oDb->quote( $iShopId )." and
00184                  oxlang = {$iLang} and oxexpired = '1'";
00185         $sQ   = "replace oxseohistory ( oxobjectid, oxident, oxshopid, oxlang, oxinsert ) {$sSub}";
00186         $oDb->execute( $sQ );
00187     }
00188 
00197     protected function _getAddParams()
00198     {
00199         // performance
00200         if ( $this->_sAddParams === null ) {
00201             $this->_sAddParams = $this->_getAddParamsFnc( oxConfig::getParameter('currency'), $this->getConfig()->getShopId() );
00202         }
00203         return $this->_sAddParams;
00204     }
00205 
00217     protected function _getAddParamsFnc( $iCur, $iActShop )
00218     {
00219         $sParams = '';
00220         if ( count( $aParams = oxUtilsUrl::getInstance()->getAddUrlParams() ) ) {
00221             foreach ( $aParams as $sName => $sValue ) {
00222                 if ( $sValue ) {
00223                     if ( $sParams ) {
00224                         $sParams .= "&amp;";
00225                     }
00226                     $sParams .= $sName . "=" . $sValue;
00227                 }
00228             }
00229             if ( $sParams ) {
00230                 $sParams = '?'.$sParams;
00231             }
00232         }
00233         return $sParams;
00234     }
00235 
00246     protected function _getDynamicObjectId( $iShopId, $sStdUrl )
00247     {
00248         return $this->getDynamicObjectId( $iShopId, $sStdUrl );
00249     }
00250 
00259     public function getDynamicObjectId( $iShopId, $sStdUrl )
00260     {
00261         return $this->_getStaticObjectId( $iShopId, $sStdUrl );
00262     }
00263 
00273     protected function _getDynamicUri( $sStdUrl, $sSeoUrl, $iLang )
00274     {
00275         $iShopId = $this->getConfig()->getShopId();
00276 
00277         $sStdUrl   = $this->_trimUrl( $sStdUrl );
00278         $sObjectId = $this->getDynamicObjectId( $iShopId, $sStdUrl );
00279         $sSeoUrl   = $this->_prepareUri( $sSeoUrl );
00280 
00281         //load details link from DB
00282         $sOldSeoUrl = $this->_loadFromDb( 'dynamic', $sObjectId, $iLang );
00283         if ( $sOldSeoUrl === $sSeoUrl ) {
00284             $sSeoUrl = $sOldSeoUrl;
00285         } else {
00286 
00287             if ( $sOldSeoUrl ) {
00288                 // old must be transferred to history
00289                 $this->_copyToHistory( $sObjectId, $iShopId, $iLang, 'dynamic' );
00290             }
00291 
00292             // creating unique
00293             $sSeoUrl = $this->_processSeoUrl( $sSeoUrl, $sObjectId, $iLang );
00294 
00295             // inserting
00296             $this->_saveToDb( 'dynamic', $sObjectId, $sStdUrl, $sSeoUrl, $iLang, $iShopId );
00297         }
00298 
00299         return $sSeoUrl;
00300     }
00301 
00311     protected function _getFullUrl( $sSeoUrl, $iLang = null, $blSsl = false )
00312     {
00313         $sProcessedUrl = false;
00314         if ( $sSeoUrl ) {
00315             $sFullUrl = ( $blSsl ? $this->getConfig()->getSslShopUrl( $iLang ) : $this->getConfig()->getShopUrl( $iLang ) ) . $sSeoUrl;
00316             $sProcessedUrl =  oxUtilsUrl::getInstance()->processSeoUrl( $sFullUrl );
00317         }
00318         return $sProcessedUrl;
00319     }
00320 
00331     protected function _getSeoIdent( $sSeoUrl, $iLang = null )
00332     {
00333         return md5( strtolower( $sSeoUrl ) );
00334     }
00335 
00345     protected function _getStaticUri( $sStdUrl, $iShopId, $iLang )
00346     {
00347         $sStdUrl = $this->_trimUrl( $sStdUrl, $iLang );
00348         return $this->_loadFromDb( 'static', $this->_getStaticObjectId( $iShopId, $sStdUrl ), $iLang );
00349     }
00350 
00356     protected function _getUrlExtension()
00357     {
00358         return;
00359     }
00360 
00373     protected function _getUniqueSeoUrl( $sSeoUrl, $sObjectId = null, $iObjectLang = null )
00374     {
00375         $oStr = getStr();
00376         $sConstEnd = $this->_getUrlExtension();
00377         if ($sConstEnd === null) {
00378             $aMatched = array();
00379             if ( $oStr->preg_match( '/\.html?$/i', $sSeoUrl, $aMatched ) ) {
00380                 $sConstEnd = $aMatched[0];
00381             } else {
00382                 if ($sSeoUrl{$oStr->strlen($sSeoUrl)-1} != '/') {
00383                     $sSeoUrl .= '/';
00384                 }
00385                 $sConstEnd = '/';
00386             }
00387         }
00388 
00389         // fix for not having url, which executes through /other/ script then seo decoder
00390         $sAdd = ' ';
00391         if ('/' != self::$_sSeparator) {
00392             $sAdd = self::$_sSeparator . self::$_sPrefix;
00393         } else {
00394             $sAdd = '_' . self::$_sPrefix;
00395         }
00396         $sSeoUrl = $oStr->preg_replace( "#^(/*)(".implode('|', $this->_getReservedEntryKeys()).")/#i", "\$1\$2$sAdd/", $sSeoUrl );
00397 
00398         $sBaseSeoUrl = $sSeoUrl;
00399         if ( $sConstEnd && $oStr->substr( $sSeoUrl, 0 - $oStr->strlen( $sConstEnd ) ) == $sConstEnd ) {
00400             $sBaseSeoUrl = $oStr->substr( $sSeoUrl, 0, $oStr->strlen( $sSeoUrl ) - $oStr->strlen( $sConstEnd ) );
00401         }
00402 
00403         $oDb = oxDb::getDb();
00404         $iShopId = $this->getConfig()->getShopId();
00405         $iCnt = 0;
00406         $sCheckSeoUrl = $this->_trimUrl( $sSeoUrl );
00407         $sQ = "select 1 from oxseo where oxshopid = '{$iShopId}'";
00408 
00409         // skipping self
00410         if ( $sObjectId && isset($iObjectLang) ) {
00411             $iObjectLang = (int) $iObjectLang;
00412             $sQ .= " and not (oxobjectid = " . $oDb->quote( $sObjectId ) . " and oxlang = $iObjectLang)";
00413         }
00414 
00415         while ( $oDb->getOne( $sQ ." and oxident= " . $oDb->quote( $this->_getSeoIdent( $sCheckSeoUrl ) ) ) ) {
00416             $sAdd = '';
00417             if ( self::$_sPrefix ) {
00418                 $sAdd = self::$_sSeparator . self::$_sPrefix;
00419             }
00420             if ( $iCnt ) {
00421                 $sAdd .= self::$_sSeparator . $iCnt;
00422             }
00423             ++$iCnt;
00424 
00425             $sSeoUrl = $sBaseSeoUrl . $sAdd . $sConstEnd;
00426             $sCheckSeoUrl = $this->_trimUrl( $sSeoUrl );
00427         }
00428         return $sSeoUrl;
00429     }
00430 
00445     protected function _isFixed( $sType, $sId, $iLang, $iShopId = null, $sParams = null, $blStrictParamsCheck = true)
00446     {
00447         if ( $iShopId === null ) {
00448             $iShopId = $this->getConfig()->getShopId();
00449         }
00450         $iLang = (int) $iLang;
00451 
00452         if ( !isset( $this->_aFixedCache[$sType][$sShopId][$sId][$iLang] ) ) {
00453             $oDb = oxDb::getDb( true );
00454 
00455             $sQ = "select oxfixed from oxseo where oxtype = ".$oDb->quote( $sType )."
00456                    and oxobjectid = ".$oDb->quote( $sId ) ." and oxshopid = ".$oDb->quote( $iShopId )." and oxlang = '{$iLang}'";
00457 
00458             $sParams = $sParams ? $oDb->quote( $sParams ) : "''";
00459             if ( $sParams && $blStrictParamsCheck ) {
00460                 $sQ .= " and oxparams = {$sParams}";
00461             } else {
00462                 $sQ .= " order by oxparams = {$sParams} desc";
00463             }
00464             $sQ .= " limit 1";
00465 
00466             $this->_aFixedCache[$sType][$sShopId][$sId][$iLang] = (bool) $oDb->getOne( $sQ );
00467         }
00468         return $this->_aFixedCache[$sType][$sShopId][$sId][$iLang];
00469     }
00470 
00486     protected function _loadFromDb( $sType, $sId, $iLang, $iShopId = null, $sParams = null, $blStrictParamsCheck = true)
00487     {
00488         $oDb = oxDb::getDb( true );
00489         if ( $iShopId === null ) {
00490             $iShopId = $this->getConfig()->getShopId();
00491         }
00492 
00493         $iLang = (int) $iLang;
00494 
00495         $sQ = "select oxfixed, oxseourl, oxexpired, oxtype from oxseo where oxtype = ".$oDb->quote( $sType )."
00496                and oxobjectid = ".$oDb->quote( $sId ) ." and oxshopid = ".$oDb->quote( $iShopId )." and oxlang = '{$iLang}'";
00497 
00498         $sParams = $sParams ? $sParams : '';
00499         if ( $sParams && $blStrictParamsCheck ) {
00500             $sQ .= " and oxparams = '{$sParams}'";
00501         } else {
00502             $sQ .= " order by oxparams = '{$sParams}' desc";
00503         }
00504         $sQ .= " limit 1";
00505 
00506         // caching to avoid same queries..
00507         $sIdent = md5($sQ);
00508         if ( isset( $this->_aSeoCache[$sIdent] ) ) {
00509             return $this->_aSeoCache[$sIdent];
00510         }
00511 
00512         $sSeoUrl = false;
00513         $oRs = $oDb->execute( $sQ );
00514         if ( $oRs && $oRs->recordCount() > 0 && !$oRs->EOF ) {
00515             // moving expired static urls to history ..
00516             if ( $oRs->fields['oxexpired'] && ( $oRs->fields['oxtype'] == 'static' || $oRs->fields['oxtype'] == 'dynamic' ) ) {
00517                 // if expired - copying to history, marking as not expired
00518                 $this->_copyToHistory( $sId, $iShopId, $iLang );
00519                 $oDb->execute( "update oxseo set oxexpired = 0 where oxobjectid = ".$oDb->quote( $sId )." and oxlang = '{$iLang}'" );
00520                 $sSeoUrl = $oRs->fields['oxseourl'];
00521             } elseif ( !$oRs->fields['oxexpired'] || $oRs->fields['oxfixed'] ) {
00522                 // if seo url is available and is valid
00523                 $sSeoUrl = $oRs->fields['oxseourl'];
00524             }
00525 
00526             // store cache
00527             $this->_aSeoCache[$sIdent] = $sSeoUrl;
00528         }
00529         return $sSeoUrl;
00530     }
00531 
00538     protected function _getReservedEntryKeys()
00539     {
00540         if ( !isset( self::$_aReservedEntryKeys ) && !is_array( self::$_aReservedEntryKeys ) ) {
00541             $sDir = getShopBasePath();
00542             self::$_aReservedEntryKeys = array();
00543             $oStr = getStr();
00544             foreach ( glob( "$sDir/*" ) as $sFile ) {
00545                 if ( $oStr->preg_match( '/^(.+)\.php[0-9]*$/i', basename( $sFile ), $aMatches ) ) {
00546                     self::$_aReservedEntryKeys[] = preg_quote( $aMatches[0] );
00547                     self::$_aReservedEntryKeys[] = preg_quote( $aMatches[1] );
00548                 } elseif ( is_dir( $sFile ) ) {
00549                     self::$_aReservedEntryKeys[] = preg_quote( basename( $sFile ) );
00550                 }
00551             }
00552         }
00553         return self::$_aReservedEntryKeys;
00554     }
00555 
00563     protected function _prepareUri( $sUri )
00564     {
00565         // decoding entities
00566         $sUri = $this->encodeString( $sUri );
00567 
00568         // basic string preparation
00569         $sUri = strip_tags( $sUri );
00570         $oStr = getStr();
00571 
00572         $sSeparator = self::$_sSeparator;
00573         $sPrefix    = self::$_sPrefix;
00574         // 'fixing' reserved words
00575         foreach ( self::$_aReservedWords as $sWord ) {
00576             $sWord = preg_quote( $sWord );
00577             $sUri = $oStr->preg_replace( array( "/(\s{$sWord})$/i", "/^({$sWord}\s)/i", "/(\s{$sWord}\s)/i", "/^({$sWord})$/i",
00578                                          "/(\/{$sWord})$/i", "/^({$sWord}\/)/i", "/(\/{$sWord}\/)/i"),
00579                                          " \$1{$sSeparator}{$sPrefix}{$sSeparator} ", $sUri );
00580         }
00581 
00582         // if found ".html" at the end - removing it temporary
00583         $sExt = '';
00584         $oStr = getStr();
00585         $aMatched = array();
00586         if ( $oStr->preg_match( '/\.html?$/i', $sUri, $aMatched ) ) {
00587             $sExt   = $oStr->substr( $sUri, 0 - $oStr->strlen( $aMatched[0] ) );
00588             $sUri = $oStr->substr( $sUri, 0, $oStr->strlen( $sUri ) - $oStr->strlen( $aMatched[0] ) );
00589         }
00590 
00591         // removing any special characters
00592         $sRegExp = '/[^A-Za-z0-9'.preg_quote( self::$_sSeparator, '/').preg_quote( self::$_sPrefix, '/').'\/]+/';
00593         $sUri  = trim( $oStr->preg_replace( array( "/\W*\/\W*/", $sRegExp ), array( "/", self::$_sSeparator ), $sUri ), self::$_sSeparator );
00594 
00595         // SEO id is empty ?
00596         if ( !$sUri ) {
00597             $sUri = $this->_prepareUri( self::$_sPrefix );
00598         }
00599 
00600         // binding ".html" back
00601         $sUri .= $sExt;
00602 
00603         // cleaning
00604         return $oStr->preg_replace( array( '|//+|', '/' . preg_quote( self::$_sSeparator . self::$_sSeparator, '/' ) .'+/' ),
00605                              array( '/', self::$_sSeparator ), $sUri );
00606     }
00607 
00616     protected function _prepareTitle( $sTitle, $blSkipTruncate = false )
00617     {
00618         $sTitle = $this->_prepareUri( str_replace( "/", self::$_sSeparator, $sTitle ) );
00619 
00620         $oStr = getStr();
00621         // smart truncate
00622         if ( !$blSkipTruncate && $oStr->strlen( $sTitle ) > $this->_iIdLength ) {
00623             if ( ( $iFirstSpace = $oStr->strstr( $oStr->substr( $sTitle, $this->_iIdLength ), self::$_sSeparator ) !== false ) ) {
00624                 $sTitle = trim( $oStr->substr( $sTitle, 0, $this->_iIdLength + $iFirstSpace ), self::$_sSeparator );
00625             }
00626         }
00627 
00628         // cleaning
00629         return $sTitle;
00630     }
00631 
00632 
00649     protected function _saveToDb( $sType, $sObjectId, $sStdUrl, $sSeoUrl, $iLang, $iShopId = null, $blFixed = null, $sParams = null )
00650     {
00651         $oDb = oxDb::getDb( true );
00652         if ( $iShopId === null ) {
00653             $iShopId = $this->getConfig()->getShopId();
00654         }
00655 
00656         $iLang = (int) $iLang;
00657 
00658         $sStdUrl = $this->_trimUrl( $sStdUrl );
00659         $sSeoUrl = $this->_trimUrl( $sSeoUrl );
00660         $sIdent  = $this->_getSeoIdent( $sSeoUrl );
00661 
00662         // transferring old url, thus current url will be regenerated
00663         $sQtedObjectId = $oDb->quote( $sObjectId );
00664         $iQtedShopId   = $oDb->quote( $iShopId );
00665         $sQtedType     = $oDb->quote( $sType );
00666         $sQtedSeoUrl   = $oDb->quote( $sSeoUrl );
00667         $sQtedStdUrl   = $oDb->quote( $sStdUrl );
00668         $sQtedParams   = $oDb->quote( $sParams );
00669         $sQtedIdent    = $oDb->quote( $sIdent );
00670 
00671         // transferring old url, thus current url will be regenerated
00672         $sQ  = "select oxfixed, oxexpired, ( oxstdurl like {$sQtedStdUrl} ) as samestdurl,
00673                 oxseourl like {$sQtedSeoUrl} as sameseourl from oxseo where oxtype = {$sQtedType} and
00674                 oxobjectid = {$sQtedObjectId} and oxshopid = {$iQtedShopId}  and oxlang = {$iLang} ";
00675 
00676         $sQ .= $sParams ? " and oxparams = {$sQtedParams} " : '';
00677         //$sQ .= isset( $blFixed ) ? " and oxfixed = " . ( (int) $blFixed ) . " " : '';
00678         $sQ .= "limit 1";
00679 
00680         $oRs = $oDb->execute( $sQ );
00681         if ( $oRs && $oRs->recordCount() > 0 && !$oRs->EOF ) {
00682             if ( $oRs->fields['samestdurl'] && $oRs->fields['sameseourl'] && $oRs->fields['oxexpired'] ) {
00683                 // fixed state change
00684                 $sFixed = isset( $blFixed ) ? ", oxfixed = " . ( (int) $blFixed ) . " " : '';
00685                 // nothing was changed - setting expired status back to 0
00686                 $sSql  = "update oxseo set oxexpired = 0 {$sFixed} where oxtype = {$sQtedType} and
00687                           oxobjectid = {$sQtedObjectId} and oxshopid = {$iQtedShopId} and oxlang = {$iLang} ";
00688                 $sSql .= $sParams ? " and oxparams = {$sQtedParams} " : '';
00689                 $sSql .= " limit 1";
00690 
00691                 return $oDb->execute( $sSql );
00692             } elseif ( $oRs->fields['oxexpired'] ) {
00693                 // copy to history
00694                 $this->_copyToHistory( $sObjectId, $iShopId, $iLang, $sType );
00695             }
00696         }
00697 
00698         // inserting new or updating
00699         $sParams = $sParams ? $oDb->quote( $sParams ) :'""';
00700         $blFixed = (int) $blFixed;
00701 
00702         $sQ  = "insert into oxseo
00703                     (oxobjectid, oxident, oxshopid, oxlang, oxstdurl, oxseourl, oxtype, oxfixed, oxexpired, oxparams)
00704                 values
00705                     ( {$sQtedObjectId}, {$sQtedIdent}, {$iQtedShopId}, {$iLang}, {$sQtedStdUrl}, {$sQtedSeoUrl}, {$sQtedType}, '$blFixed', '0', {$sParams} )
00706                 on duplicate key update
00707                     oxident = {$sQtedIdent}, oxstdurl = {$sQtedStdUrl}, oxseourl = {$sQtedSeoUrl}, oxfixed = '$blFixed', oxexpired = '0'";
00708 
00709         return $oDb->execute( $sQ );
00710     }
00711 
00722     protected function _trimUrl( $sUrl, $iLang = null )
00723     {
00724         $myConfig = $this->getConfig();
00725         $oStr = getStr();
00726         $sUrl = str_replace( array( $myConfig->getShopUrl( $iLang ), $myConfig->getSslShopUrl( $iLang ) ), '', $sUrl );
00727         $sUrl = $oStr->preg_replace( '/(\?|&(amp;)?)(force_)?(admin_)?sid=[a-z0-9\.]+&?(amp;)?/i', '\1', $sUrl );
00728         $sUrl = $oStr->preg_replace( '/(\?|&(amp;)?)shp=[0-9]+&?(amp;)?/i', '\1', $sUrl );
00729         $sUrl = $oStr->preg_replace( '/(\?|&(amp;)?)lang=[0-9]+&?(amp;)?/i', '\1', $sUrl );
00730         $sUrl = $oStr->preg_replace( '/(\?|&(amp;)?)&(amp;)?/i', '\1', $sUrl );
00731         $sUrl = $oStr->preg_replace( '/(\?|&(amp;)?)+$/i', '', $sUrl );
00732         return $sUrl;
00733     }
00734 
00743     public function encodeString( $sString, $blReplaceChars = true )
00744     {
00745         // decoding entities
00746         $sString = getStr()->html_entity_decode( $sString );
00747 
00748         if ( $blReplaceChars ) {
00749             $aReplaceChars = $this->getConfig()->getConfigParam( 'aSeoReplaceChars' );
00750             $sString = str_replace( array_keys( $aReplaceChars ), array_values( $aReplaceChars ), $sString );
00751         }
00752 
00753         // special chars
00754         $aReplaceWhat = array( '&amp;', '&quot;', '&#039;', '&lt;', '&gt;' );
00755         return str_replace( $aReplaceWhat, '', $sString );
00756     }
00757 
00765     public function setSeparator( $sSeparator = null )
00766     {
00767         self::$_sSeparator = $sSeparator;
00768         if ( !self::$_sSeparator ) {
00769             self::$_sSeparator = '-';
00770         }
00771     }
00772 
00780     public function setPrefix( $sPrefix )
00781     {
00782         if ($sPrefix) {
00783             self::$_sPrefix = $sPrefix;
00784         } else {
00785             self::$_sPrefix = 'oxid';
00786         }
00787     }
00788 
00796     public function setIdLength( $iIdlength = null )
00797     {
00798         if ( isset( $iIdlength ) ) {
00799             $this->_iIdLength = $iIdlength;
00800         }
00801     }
00802 
00810     public function setReservedWords( $aReservedWords )
00811     {
00812         self::$_aReservedWords = array_merge( self::$_aReservedWords, $aReservedWords );
00813     }
00814 
00815 
00827     public function markAsExpired( $sId, $iShopId = null, $iExpStat = 1, $iLang = null, $sParams = null )
00828     {
00829         $oDb = oxDb::getDb();
00830         $sWhere  = $sId ? "where oxobjectid =  " . $oDb->quote( $sId ) : '';
00831         $sWhere .= isset( $iShopId ) ? ( $sWhere ? " and oxshopid = ". $oDb->quote( $iShopId ) : "where oxshopid = ". $oDb->quote( $iShopId ) ) : '';
00832         $sWhere .= $iLang ? ( $sWhere ? " and oxlang = '{$iLang}'" : "where oxlang = '{$iLang}'" ) : '';
00833         $sWhere .= $sParams ? ( $sWhere ? " and {$sParams}" : "where {$sParams}" ) : '';
00834 
00835         $sQ = "update oxseo set oxexpired =  " . $oDb->quote( $iExpStat ) . " $sWhere ";
00836         $oDb->execute( $sQ );
00837     }
00838 
00852     protected function _getPageUri( $oObject, $sType, $sStdUrl, $sSeoUrl, $sParams, $iLang = null, $blFixed = false )
00853     {
00854         if ( !isset( $iLang ) ) {
00855             $iLang = $oObject->getLanguage();
00856         }
00857         $iShopId = $this->getConfig()->getShopId();
00858 
00859         //load page link from DB
00860         $sOldSeoUrl = $this->_loadFromDb( $sType, $oObject->getId(), $iLang, $iShopId, $sParams );
00861         if ( !$sOldSeoUrl ) {
00862             // generating new..
00863             $sSeoUrl = $this->_processSeoUrl( $sSeoUrl, $oObject->getId(), $iLang );
00864             $this->_saveToDb( $sType, $oObject->getId(), $sStdUrl, $sSeoUrl, $iLang, $iShopId, (int) $blFixed, $sParams );
00865         } else {
00866             // using old
00867             $sSeoUrl = $sOldSeoUrl;
00868         }
00869         return $sSeoUrl;
00870     }
00871 
00880     protected function _getStaticObjectId( $iShopId, $sStdUrl )
00881     {
00882         return md5( strtolower ( $iShopId . $this->_trimUrl( $sStdUrl ) ) );
00883     }
00884 
00894     public function encodeStaticUrls( $aStaticUrl, $iShopId, $iLang )
00895     {
00896         $oDb = oxDb::getDb();
00897         $sValues = '';
00898         $sOldObjectId = null;
00899 
00900         // standard url
00901         $sStdUrl = $this->_trimUrl( trim( $aStaticUrl['oxseo__oxstdurl'] ) );
00902         $sObjectId = $aStaticUrl['oxseo__oxobjectid'];
00903 
00904         if ( !$sObjectId || $sObjectId == '-1' ) {
00905             $sObjectId = $this->_getStaticObjectId( $iShopId, $sStdUrl );
00906         } else {
00907             // marking entry as needs to move to history
00908             $sOldObjectId = $sObjectId;
00909 
00910             // if std url does not match old
00911             if ( $this->_getStaticObjectId( $iShopId, $sStdUrl ) != $sObjectId ) {
00912                 $sObjectId = $this->_getStaticObjectId( $iShopId, $sStdUrl );
00913             }
00914         }
00915 
00916         foreach ( $aStaticUrl['oxseo__oxseourl'] as $iLang => $sSeoUrl ) {
00917 
00918             $iLang = (int) $iLang;
00919 
00920             // generating seo url
00921             if ( ( $sSeoUrl = trim( $sSeoUrl ) ) ) {
00922                 $sSeoUrl = $this->_prepareUri( $this->_trimUrl( $sSeoUrl ) );
00923                 $sSeoUrl = $this->_processSeoUrl( $sSeoUrl, $sObjectId, $iLang );
00924             }
00925 
00926 
00927             if ( $sOldObjectId ) {
00928                 // move changed records to history
00929                 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}' " ) ) {
00930                     $this->_copyToHistory( $sOldObjectId, $iShopId, $iLang, 'static', $sObjectId );
00931                 }
00932             }
00933 
00934             if ( !$sSeoUrl || !$sStdUrl ) {
00935                 continue;
00936             }
00937 
00938             $sIdent = $this->_getSeoIdent( $sSeoUrl );
00939 
00940             if ( $sValues ) {
00941                 $sValues .= ', ';
00942             }
00943 
00944             $sValues .= "( " . $oDb->quote( $sObjectId ) . ", " . $oDb->quote( $sIdent ) . ", " . $oDb->quote( $iShopId ).", '{$iLang}', " . $oDb->quote( $sStdUrl ) . ", " . $oDb->quote( $sSeoUrl ) . ", 'static' )";
00945         }
00946 
00947         // must delete old before insert/update
00948         if ( $sOldObjectId ) {
00949             $oDb->execute( "delete from oxseo where oxobjectid in ( " . $oDb->quote( $sOldObjectId ) . ", " . $oDb->quote( $sObjectId ) . " )" );
00950         }
00951 
00952         // (re)inserting
00953         if ( $sValues ) {
00954 
00955             $sQ = "insert into oxseo ( oxobjectid, oxident, oxshopid, oxlang, oxstdurl, oxseourl, oxtype ) values {$sValues} ";
00956             $oDb->execute( $sQ );
00957         }
00958 
00959         return $sObjectId;
00960     }
00961 
00969     public function copyStaticUrls( $iShopId )
00970     {
00971         $iBaseShopId = $this->getConfig()->getBaseShopId();
00972         if ( $iShopId != $iBaseShopId ) {
00973             $oDb = oxDb::getDb();
00974             foreach (array_keys(oxLang::getInstance()->getLanguageIds()) as $iLang) {
00975                 $sQ = "insert into oxseo ( oxobjectid, oxident, oxshopid, oxlang, oxstdurl, oxseourl, oxtype )
00976                        select MD5( LOWER( CONCAT( " . $oDb->quote( $iShopId ) . ", oxstdurl ) ) ), MD5( LOWER( oxseourl ) ),
00977                        " . $oDb->quote( $iShopId ) . ", oxlang, oxstdurl, oxseourl, oxtype from oxseo where oxshopid = '{$iBaseShopId}' and oxtype = 'static' and oxlang='$iLang' ";
00978                 $oDb->execute( $sQ );
00979             }
00980         }
00981     }
00982 
00992     public function getStaticUrl( $sStdUrl, $iLang = null, $iShopId = null )
00993     {
00994         if (!isset($iShopId)) {
00995             $iShopId = $this->getConfig()->getShopId();
00996         }
00997         if (!isset($iLang)) {
00998             $iLang   = oxLang::getInstance()->getEditLanguage();
00999         }
01000 
01001         if ( isset($this->_aStaticUrlCache[$sStdUrl][$iLang][$iShopId])) {
01002             return $this->_aStaticUrlCache[$sStdUrl][$iLang][$iShopId];
01003         }
01004 
01005         $sFullUrl = '';
01006         if ( ( $sSeoUrl = $this->_getStaticUri( $sStdUrl, $iShopId, $iLang ) ) ) {
01007             $sFullUrl = $this->_getFullUrl( $sSeoUrl, $iLang, strpos( $sStdUrl, "https:" ) === 0 );
01008         }
01009 
01010 
01011         $this->_aStaticUrlCache[$sStdUrl][$iLang][$iShopId] = $sFullUrl;
01012 
01013         return $sFullUrl;
01014     }
01015 
01034     public function addSeoEntry( $sObjectId, $iShopId, $iLang, $sStdUrl, $sSeoUrl, $sType, $blFixed = 1, $sKeywords = '', $sDescription = '', $sParams = '', $blExclude = false, $sAltObjectId = null )
01035     {
01036         $sSeoUrl = $this->_processSeoUrl( $this->_prepareUri( $this->_trimUrl( $sSeoUrl ? $sSeoUrl : $this->_getAltUri( $sAltObjectId ? $sAltObjectId : $sObjectId, $iLang ) ) ), $sObjectId, $iLang, $blExclude );
01037         if ( $this->_saveToDb( $sType, $sObjectId, $sStdUrl, $sSeoUrl, $iLang, $iShopId, $blFixed, $sParams ) ) {
01038 
01039             $oDb = oxDb::getDb( true );
01040 
01041             //
01042             $sQtedObjectId = $oDb->quote( $sAltObjectId ? $sAltObjectId : $sObjectId );
01043             $iQtedShopId   = $oDb->quote( $iShopId );
01044 
01045             $oStr = getStr();
01046             if ( $sKeywords !== false ) {
01047                 $sKeywords = $oDb->quote( $oStr->htmlspecialchars( $this->encodeString( strip_tags( $sKeywords ), false ) ) );
01048             }
01049 
01050             if ( $sDescription !== false ) {
01051                 $sDescription = $oDb->quote( $oStr->htmlspecialchars( strip_tags( $sDescription ) ) );
01052             }
01053 
01054             $sQ = "insert into oxobject2seodata
01055                        ( oxobjectid, oxshopid, oxlang, oxkeywords, oxdescription )
01056                    values
01057                        ( {$sQtedObjectId}, {$iQtedShopId}, {$iLang}, ".( $sKeywords ? $sKeywords : "''" ).", ".( $sDescription ? $sDescription : "''" )." )
01058                    on duplicate key update
01059                        oxkeywords = ".( $sKeywords ? $sKeywords : "oxkeywords" ).", oxdescription = ".( $sDescription ? $sDescription : "oxdescription" );
01060             $oDb->execute( $sQ );
01061         }
01062     }
01063 
01072     protected function _getAltUri( $sObjectId, $iLang )
01073     {
01074     }
01075 
01086     public function deleteSeoEntry( $sObjectId, $iShopId, $iLang, $sType )
01087     {
01088         $oDb = oxDb::getDb();
01089         $sQ = "delete from oxseo where oxobjectid = " . $oDb->quote( $sObjectId ) . " and oxshopid = " . $oDb->quote( $iShopId ) . " and oxlang = " . $oDb->quote( $iLang ) . " and oxtype = " . $oDb->quote( $sType ) . " ";
01090         oxDb::getDb()->execute( $sQ );
01091     }
01092 
01103     public function getMetaData( $sObjectId, $sMetaType, $iShopId = null, $iLang = null )
01104     {
01105         $oDb = oxDb::getDb();
01106 
01107         $iShopId = ( !isset( $iShopId ) ) ? $this->getConfig()->getShopId():$iShopId;
01108         $iLang   = ( !isset( $iLang ) ) ? oxLang::getInstance()->getObjectTplLanguage():((int) $iLang);
01109         return $oDb->getOne( "select {$sMetaType} from oxobject2seodata where oxobjectid = " . $oDb->quote( $sObjectId ) . " and oxshopid = " . $oDb->quote( $iShopId )." and oxlang = '{$iLang}'" );
01110     }
01111 
01125     public function getDynamicUrl( $sStdUrl, $sSeoUrl, $iLang )
01126     {
01127         startProfile("getDynamicUrl");
01128         $sDynUrl = $this->_getFullUrl( $this->_getDynamicUri( $sStdUrl, $sSeoUrl, $iLang ), strpos( $sStdUrl, "https:" ) === 0 );
01129         stopProfile("getDynamicUrl");
01130         return $sDynUrl;
01131     }
01132 
01141     public function fetchSeoUrl( $sStdUrl, $iLanguage = null )
01142     {
01143         $oDb = oxDb::getDb( true );
01144         $iLanguage = isset( $iLanguage ) ? ( (int) $iLanguage ) : oxLang::getInstance()->getBaseLanguage();
01145         $sSeoUrl   = false;
01146 
01147         $sShopId = $this->getConfig()->getShopId();
01148 
01149         $sQ = "select oxseourl, oxlang from oxseo where oxstdurl = ".$oDb->quote( $sStdUrl )." and oxlang = '$iLanguage' and oxshopid = '$sShopId' limit 1";
01150         $oRs = $oDb->execute( $sQ );
01151         if ( !$oRs->EOF ) {
01152             $sSeoUrl = $oRs->fields['oxseourl'];
01153         }
01154 
01155         return $sSeoUrl;
01156     }
01157 }