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 = array();
00077
00082 protected $_iMaxUrlLength = null;
00083
00091 public static function getInstance()
00092 {
00093 return oxRegistry::get("oxSeoEncoder");
00094 }
00095
00104 public function addLanguageParam( $sSeoUrl, $iLang )
00105 {
00106 $iLang = (int) $iLang;
00107 $iDefLang = (int) $this->getConfig()->getConfigParam( 'iDefSeoLang' );
00108 $aLangIds = oxRegistry::getLang()->getLanguageIds();
00109
00110 if ( $iLang != $iDefLang && isset( $aLangIds[$iLang] ) && getStr()->strpos( $sSeoUrl, $aLangIds[$iLang] . '/' ) !== 0 ) {
00111 $sSeoUrl = $aLangIds[$iLang] . '/'.$sSeoUrl;
00112 }
00113
00114 return $sSeoUrl;
00115 }
00116
00129 protected function _processSeoUrl( $sSeoUrl, $sObjectId = null, $iLang = null, $blExclude = false )
00130 {
00131 if (!$blExclude) {
00132 $sSeoUrl = $this->addLanguageParam( $sSeoUrl, $iLang );
00133 }
00134 return $this->_getUniqueSeoUrl( $sSeoUrl, $sObjectId, $iLang );
00135 }
00136
00140 public function __construct()
00141 {
00142 $myConfig = $this->getConfig();
00143 if (!self::$_sSeparator) {
00144 $this->setSeparator( $myConfig->getConfigParam( 'sSEOSeparator' ) );
00145 }
00146 if (!self::$_sPrefix) {
00147 $this->setPrefix( $myConfig->getConfigParam( 'sSEOuprefix' ) );
00148 }
00149 $this->setReservedWords( $myConfig->getConfigParam( 'aSEOReservedWords' ) );
00150 }
00151
00163 protected function _copyToHistory( $sId, $iShopId, $iLang, $sType = null, $sNewId = null )
00164 {
00165 $oDb = oxDb::getDb();
00166 $sObjectid = $sNewId?$oDb->quote( $sNewId ):'oxobjectid';
00167 $sType = $sType?"oxtype =".$oDb->quote( $sType )." and":'';
00168 $iLang = (int) $iLang;
00169
00170
00171 $sSub = "select $sObjectid, MD5( LOWER( oxseourl ) ), oxshopid, oxlang, now() from oxseo
00172 where {$sType} oxobjectid = ".$oDb->quote( $sId )." and oxshopid = ".$oDb->quote( $iShopId )." and
00173 oxlang = {$iLang} and oxexpired = '1'";
00174 $sQ = "replace oxseohistory ( oxobjectid, oxident, oxshopid, oxlang, oxinsert ) {$sSub}";
00175 $oDb->execute( $sQ );
00176 }
00177
00186 public function getDynamicObjectId( $iShopId, $sStdUrl )
00187 {
00188 return $this->_getStaticObjectId( $iShopId, $sStdUrl );
00189 }
00190
00200 protected function _getDynamicUri( $sStdUrl, $sSeoUrl, $iLang )
00201 {
00202 $iShopId = $this->getConfig()->getShopId();
00203
00204 $sStdUrl = $this->_trimUrl( $sStdUrl );
00205 $sObjectId = $this->getDynamicObjectId( $iShopId, $sStdUrl );
00206 $sSeoUrl = $this->_prepareUri( $this->addLanguageParam( $sSeoUrl, $iLang ), $iLang );
00207
00208
00209 $sOldSeoUrl = $this->_loadFromDb( 'dynamic', $sObjectId, $iLang );
00210 if ( $sOldSeoUrl === $sSeoUrl ) {
00211 $sSeoUrl = $sOldSeoUrl;
00212 } else {
00213
00214 if ( $sOldSeoUrl ) {
00215
00216 $this->_copyToHistory( $sObjectId, $iShopId, $iLang, 'dynamic' );
00217 }
00218
00219
00220 $sSeoUrl = $this->_processSeoUrl( $sSeoUrl, $sObjectId, $iLang );
00221
00222
00223 $this->_saveToDb( 'dynamic', $sObjectId, $sStdUrl, $sSeoUrl, $iLang, $iShopId );
00224 }
00225
00226 return $sSeoUrl;
00227 }
00228
00238 protected function _getFullUrl( $sSeoUrl, $iLang = null, $blSsl = false )
00239 {
00240 if ( $sSeoUrl ) {
00241 $sFullUrl = ( $blSsl ? $this->getConfig()->getSslShopUrl( $iLang ) : $this->getConfig()->getShopUrl( $iLang, false ) ) . $sSeoUrl;
00242 return oxRegistry::get("oxUtilsUrl")->processSeoUrl( $sFullUrl );
00243 }
00244 return false;
00245 }
00246
00256 protected function _getSeoIdent( $sSeoUrl )
00257 {
00258 return md5( strtolower( $sSeoUrl ) );
00259 }
00260
00270 protected function _getStaticUri( $sStdUrl, $iShopId, $iLang )
00271 {
00272 $sStdUrl = $this->_trimUrl( $sStdUrl, $iLang );
00273 return $this->_loadFromDb( 'static', $this->_getStaticObjectId( $iShopId, $sStdUrl ), $iLang );
00274 }
00275
00281 protected function _getUrlExtension()
00282 {
00283 return;
00284 }
00285
00298 protected function _getUniqueSeoUrl( $sSeoUrl, $sObjectId = null, $iObjectLang = null )
00299 {
00300 $sSeoUrl = $this->_prepareUri( $sSeoUrl, $iObjectLang );
00301 $oStr = getStr();
00302 $sExt = '';
00303 if ( $oStr->preg_match( '/(\.html?|\/)$/i', $sSeoUrl, $aMatched ) ) {
00304 $sExt = $aMatched[0];
00305 }
00306 $sBaseSeoUrl = $sSeoUrl;
00307 if ( $sExt && $oStr->substr( $sSeoUrl, 0 - $oStr->strlen( $sExt ) ) == $sExt ) {
00308 $sBaseSeoUrl = $oStr->substr( $sSeoUrl, 0, $oStr->strlen( $sSeoUrl ) - $oStr->strlen( $sExt ) );
00309 }
00310
00311 $iShopId = $this->getConfig()->getShopId();
00312 $iCnt = 0;
00313 $sCheckSeoUrl = $this->_trimUrl( $sSeoUrl );
00314 $sQ = "select 1 from oxseo where oxshopid = '{$iShopId}'";
00315
00316 $oDb = oxDb::getDb();
00317
00318 if ( $sObjectId && isset($iObjectLang) ) {
00319 $iObjectLang = (int) $iObjectLang;
00320 $sQ .= " and not (oxobjectid = " . $oDb->quote( $sObjectId ) . " and oxlang = $iObjectLang)";
00321 }
00322
00323 while ( $oDb->getOne( $sQ ." and oxident= " . $oDb->quote( $this->_getSeoIdent( $sCheckSeoUrl ) ) ) ) {
00324 $sAdd = '';
00325 if ( self::$_sPrefix ) {
00326 $sAdd = self::$_sSeparator . self::$_sPrefix;
00327 }
00328 if ( $iCnt ) {
00329 $sAdd .= self::$_sSeparator . $iCnt;
00330 }
00331 ++$iCnt;
00332
00333 $sSeoUrl = $sBaseSeoUrl . $sAdd . $sExt;
00334 $sCheckSeoUrl = $this->_trimUrl( $sSeoUrl );
00335 }
00336 return $sSeoUrl;
00337 }
00338
00353 protected function _isFixed( $sType, $sId, $iLang, $iShopId = null, $sParams = null, $blStrictParamsCheck = true)
00354 {
00355 if ( $iShopId === null ) {
00356 $iShopId = $this->getConfig()->getShopId();
00357 }
00358 $iLang = (int) $iLang;
00359
00360 if ( !isset( self::$_aFixedCache[$sType][$sShopId][$sId][$iLang] ) ) {
00361 $oDb = oxDb::getDb();
00362
00363 $sQ = "SELECT `oxfixed`
00364 FROM `oxseo`
00365 WHERE `oxtype` = ".$oDb->quote( $sType )."
00366 AND `oxobjectid` = ".$oDb->quote( $sId ) ."
00367 AND `oxshopid` = ".$oDb->quote( $iShopId )."
00368 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` ASC";
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
00393 protected function _getCacheKey( $sType, $iLang = null, $iShopId = null, $sParams = null )
00394 {
00395 $blAdmin = $this->isAdmin();
00396 if ( !$blAdmin && $sType !== "oxarticle" ) {
00397 return $sType . ( (int) $iLang ) . ( (int) $iShopId ) . "seo";
00398 }
00399
00400
00401 if ( self::$_sCacheKey === null ) {
00402 self::$_sCacheKey = false;
00403 if ( !$blAdmin && ( $oView = $this->getConfig()->getActiveView() ) ) {
00404 self::$_sCacheKey = md5( $oView->getViewId() ) . "seo";
00405 }
00406 }
00407 return self::$_sCacheKey;
00408 }
00409
00421 protected function _loadFromCache( $sCacheIdent, $sType, $iLang = null, $iShopId = null, $sParams = null )
00422 {
00423 if ( !$this->getConfig()->getConfigParam( 'blEnableSeoCache' ) ) {
00424 return false;
00425 }
00426
00427 startProfile( "seoencoder_loadFromCache" );
00428
00429 $sCacheKey = $this->_getCacheKey( $sType, $iLang, $iShopId, $sParams );
00430 $sCache = false;
00431
00432 if ( $sCacheKey && !isset( self::$_aCache[$sCacheKey] ) ) {
00433 self::$_aCache[$sCacheKey] = oxRegistry::getUtils()->fromFileCache( $sCacheKey );
00434 }
00435
00436 if ( isset( self::$_aCache[$sCacheKey] ) && isset( self::$_aCache[$sCacheKey][$sCacheIdent] ) ) {
00437 $sCache = self::$_aCache[$sCacheKey][$sCacheIdent];
00438 }
00439
00440 stopProfile( "seoencoder_loadFromCache" );
00441 return $sCache;
00442 }
00443
00456 protected function _saveInCache( $sCacheIdent, $sCache, $sType, $iLang = null, $iShopId = null, $sParams = null )
00457 {
00458 if ( !$this->getConfig()->getConfigParam( 'blEnableSeoCache' ) ) {
00459 return false;
00460 }
00461
00462 startProfile( "seoencoder_saveInCache" );
00463
00464 $blSaved = false;
00465 if ( $sCache && ( $sCacheKey = $this->_getCacheKey( $sType, $iLang, $iShopId, $sParams ) ) !== false ) {
00466 self::$_aCache[$sCacheKey][$sCacheIdent] = $sCache;
00467 $blSaved = oxRegistry::getUtils()->toFileCache( $sCacheKey, self::$_aCache[$sCacheKey] );
00468 }
00469
00470 stopProfile( "seoencoder_saveInCache" );
00471 return $blSaved;
00472 }
00473
00489 protected function _loadFromDb( $sType, $sId, $iLang, $iShopId = null, $sParams = null, $blStrictParamsCheck = true)
00490 {
00491
00492 if ( $iShopId === null ) {
00493 $iShopId = $this->getConfig()->getShopId();
00494 }
00495
00496 $iLang = (int) $iLang;
00497 $oDb = oxDb::getDb( oxDb::FETCH_MODE_ASSOC );
00498
00499 $sQ = "
00500 SELECT
00501 `oxfixed`,
00502 `oxseourl`,
00503 `oxexpired`,
00504 `oxtype`
00505 FROM `oxseo`
00506 WHERE `oxtype` = ".$oDb->quote( $sType )."
00507 AND `oxobjectid` = ".$oDb->quote( $sId ) ."
00508 AND `oxshopid` = ".$oDb->quote( $iShopId )."
00509 AND `oxlang` = '{$iLang}'";
00510
00511 $sParams = $sParams ? $sParams : '';
00512 if ( $sParams && $blStrictParamsCheck ) {
00513 $sQ .= " AND `oxparams` = '{$sParams}'";
00514 } else {
00515 $sQ .= " ORDER BY `oxparams` ASC";
00516 }
00517 $sQ .= " LIMIT 1";
00518
00519
00520
00521 $sIdent = md5( $sQ );
00522
00523
00524 if ( ( $sSeoUrl = $this->_loadFromCache( $sIdent, $sType, $iLang, $iShopId, $sParams ) ) === false ) {
00525 $oRs = $oDb->select( $sQ );
00526
00527 if ( $oRs && $oRs->recordCount() > 0 && !$oRs->EOF ) {
00528
00529 if ( $oRs->fields['oxexpired'] && ( $oRs->fields['oxtype'] == 'static' || $oRs->fields['oxtype'] == 'dynamic' ) ) {
00530
00531 $this->_copyToHistory( $sId, $iShopId, $iLang );
00532 $oDb->execute( "update oxseo set oxexpired = 0 where oxobjectid = ".$oDb->quote( $sId )." and oxlang = '{$iLang}'" );
00533 $sSeoUrl = $oRs->fields['oxseourl'];
00534 } elseif ( !$oRs->fields['oxexpired'] || $oRs->fields['oxfixed'] ) {
00535
00536 $sSeoUrl = $oRs->fields['oxseourl'];
00537 }
00538
00539
00540 $this->_saveInCache( $sIdent, $sSeoUrl, $sType, $iLang, $iShopId, $sParams );
00541 }
00542 }
00543 return $sSeoUrl;
00544 }
00545
00552 protected function _getReservedEntryKeys()
00553 {
00554 if ( !isset( self::$_aReservedEntryKeys ) || !is_array( self::$_aReservedEntryKeys ) ) {
00555 $sDir = getShopBasePath();
00556 self::$_aReservedEntryKeys = array_map('preg_quote', self::$_aReservedWords, array('#'));
00557 $oStr = getStr();
00558 foreach ( glob( "$sDir/*" ) as $sFile ) {
00559 if ( $oStr->preg_match( '/^(.+)\.php[0-9]*$/i', basename( $sFile ), $aMatches ) ) {
00560 self::$_aReservedEntryKeys[] = preg_quote( $aMatches[0], '#' );
00561 self::$_aReservedEntryKeys[] = preg_quote( $aMatches[1], '#' );
00562 } elseif ( is_dir( $sFile ) ) {
00563 self::$_aReservedEntryKeys[] = preg_quote( basename( $sFile ), '#' );
00564 }
00565 }
00566 self::$_aReservedEntryKeys = array_unique(self::$_aReservedEntryKeys);
00567 }
00568 return self::$_aReservedEntryKeys;
00569 }
00570
00579 protected function _prepareUri( $sUri, $iLang = false )
00580 {
00581
00582 $sUri = $this->encodeString( $sUri, true, $iLang );
00583
00584
00585 $oStr = getStr();
00586 $sUri = $oStr->strip_tags( $sUri );
00587
00588
00589 $sExt = $this->_getUrlExtension();
00590 if ($sExt === null) {
00591 $aMatched = array();
00592 if ( $oStr->preg_match( '/(\.html?|\/)$/i', $sUri, $aMatched ) ) {
00593 $sExt = $aMatched[0];
00594 } else {
00595 $sExt = '/';
00596 }
00597 }
00598 if ( $sExt && $oStr->substr( $sUri, 0 - $oStr->strlen( $sExt ) ) == $sExt ) {
00599 $sUri = $oStr->substr( $sUri, 0, $oStr->strlen( $sUri ) - $oStr->strlen( $sExt ) );
00600 }
00601
00602
00603
00604 $sQuotedPrefix = preg_quote( self::$_sSeparator . self::$_sPrefix, '/');
00605 if ( phpversion() < '5.3' ) {
00606 $sQuotedPrefix = str_replace( '-', '\-', $sQuotedPrefix );
00607 }
00608 $sRegExp = '/[^A-Za-z0-9' . $sQuotedPrefix . '\/]+/';
00609 $sUri = $oStr->preg_replace( array( "/\W*\/\W*/", $sRegExp ), array( "/", self::$_sSeparator ), $sUri );
00610
00611
00612 if ( !$sUri && self::$_sPrefix ) {
00613 $sUri = $this->_prepareUri( self::$_sPrefix, $iLang );
00614 }
00615
00616 $sAdd = '';
00617 if ('/' != self::$_sSeparator) {
00618 $sAdd = self::$_sSeparator . self::$_sPrefix;
00619 $sUri = trim($sUri, self::$_sSeparator);
00620 } else {
00621 $sAdd = '_' . self::$_sPrefix;
00622 }
00623
00624
00625 $sUri .= $sExt;
00626
00627
00628 $sUri = $oStr->preg_replace( "#^(/*)(".implode('|', $this->_getReservedEntryKeys()).")(/|$)#i", "\$1\$2$sAdd\$3", $sUri );
00629
00630
00631
00632
00633 if ( phpversion() < '5.3' ) {
00634 $sQuotedSeparator = str_replace( '-', '\-', $sQuotedSeparator );
00635 }
00636 return $oStr->preg_replace( array( '|//+|', '/' . $sQuotedSeparator . $sQuotedSeparator .'+/' ),
00637 array( '/', self::$_sSeparator ), $sUri );
00638 }
00639
00640
00650 protected function _prepareTitle( $sTitle, $blSkipTruncate = false, $iLang = false )
00651 {
00652 $sTitle = $this->encodeString( $sTitle, true, $iLang );
00653 $sSep = self::$_sSeparator;
00654 if (!$sSep || ('/' == $sSep)) {
00655 $sSep = '_';
00656 }
00657
00658 $sRegExp = '/[^A-Za-z0-9\/'.preg_quote( self::$_sPrefix, '/').preg_quote($sSep, '/').']+/';
00659 $sTitle = preg_replace( array("#/+#", $sRegExp, "# +#", "#(".preg_quote($sSep, '/').")+#"), $sSep, $sTitle );
00660
00661 $oStr = getStr();
00662
00663 if ( !$blSkipTruncate && $oStr->strlen( $sTitle ) > $this->_iIdLength ) {
00664 $iFirstSpace = $oStr->strpos( $sTitle, $sSep, $this->_iIdLength);
00665 if ( $iFirstSpace !== false ) {
00666 $sTitle = $oStr->substr( $sTitle, 0, $iFirstSpace );
00667 }
00668 }
00669
00670 $sTitle = trim( $sTitle, $sSep );
00671
00672 if (!$sTitle) {
00673 return self::$_sPrefix;
00674 }
00675
00676 return $sTitle;
00677 }
00678
00679
00696 protected function _saveToDb( $sType, $sObjectId, $sStdUrl, $sSeoUrl, $iLang, $iShopId = null, $blFixed = null, $sParams = null )
00697 {
00698 $oDb = oxDb::getDb( oxDb::FETCH_MODE_ASSOC );
00699 if ( $iShopId === null ) {
00700 $iShopId = $this->getConfig()->getShopId();
00701 }
00702
00703 $iLang = (int) $iLang;
00704
00705 $sStdUrl = $this->_trimUrl( $sStdUrl );
00706 $sSeoUrl = $this->_trimUrl( $sSeoUrl );
00707 $sIdent = $this->_getSeoIdent( $sSeoUrl );
00708
00709
00710 $sQtedObjectId = $oDb->quote( $sObjectId );
00711 $iQtedShopId = $oDb->quote( $iShopId );
00712 $sQtedType = $oDb->quote( $sType );
00713 $sQtedSeoUrl = $oDb->quote( $sSeoUrl );
00714 $sQtedStdUrl = $oDb->quote( $sStdUrl );
00715 $sQtedParams = $oDb->quote( $sParams );
00716 $sQtedIdent = $oDb->quote( $sIdent );
00717
00718
00719 $sQ = "select oxfixed, oxexpired, ( oxstdurl like {$sQtedStdUrl} ) as samestdurl,
00720 oxseourl like {$sQtedSeoUrl} as sameseourl from oxseo where oxtype = {$sQtedType} and
00721 oxobjectid = {$sQtedObjectId} and oxshopid = {$iQtedShopId} and oxlang = {$iLang} ";
00722
00723 $sQ .= $sParams ? " and oxparams = {$sQtedParams} " : '';
00724 $sQ .= "limit 1";
00725 $oDb = oxDb::getDb( oxDb::FETCH_MODE_ASSOC );
00726 $oRs = $oDb->select( $sQ );
00727 if ( $oRs && $oRs->recordCount() > 0 && !$oRs->EOF ) {
00728 if ( $oRs->fields['samestdurl'] && $oRs->fields['sameseourl'] && $oRs->fields['oxexpired'] ) {
00729
00730 $sFixed = isset( $blFixed ) ? ", oxfixed = " . ( (int) $blFixed ) . " " : '';
00731
00732 $sSql = "update oxseo set oxexpired = 0 {$sFixed} where oxtype = {$sQtedType} and
00733 oxobjectid = {$sQtedObjectId} and oxshopid = {$iQtedShopId} and oxlang = {$iLang} ";
00734 $sSql .= $sParams ? " and oxparams = {$sQtedParams} " : '';
00735 $sSql .= " limit 1";
00736
00737 return $oDb->execute( $sSql );
00738 } elseif ( $oRs->fields['oxexpired'] ) {
00739
00740 $this->_copyToHistory( $sObjectId, $iShopId, $iLang, $sType );
00741 }
00742 }
00743
00744
00745 $sParams = $sParams ? $oDb->quote( $sParams ) :'""';
00746 $blFixed = (int) $blFixed;
00747
00748 $sQ = "insert into oxseo
00749 (oxobjectid, oxident, oxshopid, oxlang, oxstdurl, oxseourl, oxtype, oxfixed, oxexpired, oxparams)
00750 values
00751 ( {$sQtedObjectId}, {$sQtedIdent}, {$iQtedShopId}, {$iLang}, {$sQtedStdUrl}, {$sQtedSeoUrl}, {$sQtedType}, '$blFixed', '0', {$sParams} )
00752 on duplicate key update
00753 oxident = {$sQtedIdent}, oxstdurl = {$sQtedStdUrl}, oxseourl = {$sQtedSeoUrl}, oxfixed = '$blFixed', oxexpired = '0'";
00754
00755 return $oDb->execute( $sQ );
00756 }
00757
00768 protected function _trimUrl( $sUrl, $iLang = null )
00769 {
00770 $myConfig = $this->getConfig();
00771 $oStr = getStr();
00772 $sUrl = str_replace( array( $myConfig->getShopUrl( $iLang, false ), $myConfig->getSslShopUrl( $iLang ) ), '', $sUrl );
00773 $sUrl = $oStr->preg_replace( '/(\?|&(amp;)?)(force_)?(admin_)?sid=[a-z0-9\.]+&?(amp;)?/i', '\1', $sUrl );
00774 $sUrl = $oStr->preg_replace( '/(\?|&(amp;)?)shp=[0-9]+&?(amp;)?/i', '\1', $sUrl );
00775 $sUrl = $oStr->preg_replace( '/(\?|&(amp;)?)lang=[0-9]+&?(amp;)?/i', '\1', $sUrl );
00776 $sUrl = $oStr->preg_replace( '/(\?|&(amp;)?)cur=[0-9]+&?(amp;)?/i', '\1', $sUrl );
00777 $sUrl = $oStr->preg_replace( '/(\?|&(amp;)?)stoken=[a-z0-9]+&?(amp;)?/i', '\1', $sUrl );
00778 $sUrl = $oStr->preg_replace( '/(\?|&(amp;)?)&(amp;)?/i', '\1', $sUrl );
00779 $sUrl = $oStr->preg_replace( '/(\?|&(amp;)?)+$/i', '', $sUrl );
00780 $sUrl = trim( $sUrl );
00781
00782
00783 $iLength = $this->_getMaxUrlLength();
00784 if ( $oStr->strlen( $sUrl ) > $iLength ) {
00785 $sUrl = $oStr->substr( $sUrl, 0, $iLength );
00786 }
00787
00788 return $sUrl;
00789 }
00790
00796 protected function _getMaxUrlLength()
00797 {
00798 if ( $this->_iMaxUrlLength === null ) {
00799
00800 $this->_iMaxUrlLength = $this->getConfig()->getConfigParam( "iMaxSeoUrlLength" ) ;
00801 if ( !$this->_iMaxUrlLength ) {
00802 $this->_iMaxUrlLength = 2048;
00803 }
00804 }
00805 return $this->_iMaxUrlLength;
00806 }
00807
00817 public function encodeString( $sString, $blReplaceChars = true, $iLang = false )
00818 {
00819
00820 $sString = getStr()->html_entity_decode( $sString );
00821
00822 if ( $blReplaceChars ) {
00823 if ($iLang === false || !is_numeric($iLang)) {
00824 $iLang = oxRegistry::getLang()->getEditLanguage();
00825 }
00826
00827 if ( $aReplaceChars = oxRegistry::getLang()->getSeoReplaceChars($iLang) ) {
00828 $sString = str_replace( array_keys( $aReplaceChars ), array_values( $aReplaceChars ), $sString );
00829 }
00830 }
00831
00832
00833
00834 $aReplaceWhat = array( '&', '"', ''', '<', '>' );
00835 return str_replace( $aReplaceWhat, '', $sString );
00836 }
00837
00845 public function setSeparator( $sSeparator = null )
00846 {
00847 self::$_sSeparator = $sSeparator;
00848 if ( !self::$_sSeparator ) {
00849 self::$_sSeparator = '-';
00850 }
00851 }
00852
00860 public function setPrefix( $sPrefix )
00861 {
00862 if ($sPrefix) {
00863 self::$_sPrefix = $sPrefix;
00864 } else {
00865 self::$_sPrefix = 'oxid';
00866 }
00867 }
00868
00876 public function setIdLength( $iIdlength = null )
00877 {
00878 if ( isset( $iIdlength ) ) {
00879 $this->_iIdLength = $iIdlength;
00880 }
00881 }
00882
00891 public function setReservedWords( $aReservedWords )
00892 {
00893 self::$_aReservedWords = array_merge( self::$_aReservedWords, $aReservedWords );
00894 }
00895
00896
00908 public function markAsExpired( $sId, $iShopId = null, $iExpStat = 1, $iLang = null, $sParams = null )
00909 {
00910 $oDb = oxDb::getDb();
00911 $sWhere = $sId ? "where oxobjectid = " . $oDb->quote( $sId ) : '';
00912 $sWhere .= isset( $iShopId ) ? ( $sWhere ? " and oxshopid = ". $oDb->quote( $iShopId ) : "where oxshopid = ". $oDb->quote( $iShopId ) ) : '';
00913 $sWhere .= $iLang ? ( $sWhere ? " and oxlang = '{$iLang}'" : "where oxlang = '{$iLang}'" ) : '';
00914 $sWhere .= $sParams ? ( $sWhere ? " and {$sParams}" : "where {$sParams}" ) : '';
00915
00916 $sQ = "update oxseo set oxexpired = " . $oDb->quote( $iExpStat ) . " $sWhere ";
00917 $oDb->execute( $sQ );
00918 }
00919
00933 protected function _getPageUri( $oObject, $sType, $sStdUrl, $sSeoUrl, $sParams, $iLang = null, $blFixed = false )
00934 {
00935 if ( !isset( $iLang ) ) {
00936 $iLang = $oObject->getLanguage();
00937 }
00938 $iShopId = $this->getConfig()->getShopId();
00939
00940
00941 $sOldSeoUrl = $this->_loadFromDb( $sType, $oObject->getId(), $iLang, $iShopId, $sParams );
00942 if ( !$sOldSeoUrl ) {
00943
00944 $sSeoUrl = $this->_processSeoUrl( $sSeoUrl, $oObject->getId(), $iLang );
00945 $this->_saveToDb( $sType, $oObject->getId(), $sStdUrl, $sSeoUrl, $iLang, $iShopId, (int) $blFixed, $sParams );
00946 } else {
00947
00948 $sSeoUrl = $sOldSeoUrl;
00949 }
00950 return $sSeoUrl;
00951 }
00952
00961 protected function _getStaticObjectId( $iShopId, $sStdUrl )
00962 {
00963 return md5( strtolower ( $iShopId . $this->_trimUrl( $sStdUrl ) ) );
00964 }
00965
00975 public function encodeStaticUrls( $aStaticUrl, $iShopId, $iLang )
00976 {
00977 $oDb = oxDb::getDb();
00978 $sValues = '';
00979 $sOldObjectId = null;
00980
00981
00982 $sStdUrl = $this->_trimUrl( trim( $aStaticUrl['oxseo__oxstdurl'] ) );
00983 $sObjectId = $aStaticUrl['oxseo__oxobjectid'];
00984
00985 if ( !$sObjectId || $sObjectId == '-1' ) {
00986 $sObjectId = $this->_getStaticObjectId( $iShopId, $sStdUrl );
00987 } else {
00988
00989 $sOldObjectId = $sObjectId;
00990
00991
00992 if ( $this->_getStaticObjectId( $iShopId, $sStdUrl ) != $sObjectId ) {
00993 $sObjectId = $this->_getStaticObjectId( $iShopId, $sStdUrl );
00994 }
00995 }
00996
00997 foreach ( $aStaticUrl['oxseo__oxseourl'] as $iLang => $sSeoUrl ) {
00998
00999 $iLang = (int) $iLang;
01000
01001
01002 $sSeoUrl = $this->_trimUrl( $sSeoUrl );
01003 if ( $sSeoUrl ) {
01004 $sSeoUrl = $this->_processSeoUrl( $sSeoUrl, $sObjectId, $iLang );
01005 }
01006
01007
01008 if ( $sOldObjectId ) {
01009
01010 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}' ", false, false) ) {
01011 $this->_copyToHistory( $sOldObjectId, $iShopId, $iLang, 'static', $sObjectId );
01012 }
01013 }
01014
01015 if ( !$sSeoUrl || !$sStdUrl ) {
01016 continue;
01017 }
01018
01019 $sIdent = $this->_getSeoIdent( $sSeoUrl );
01020
01021 if ( $sValues ) {
01022 $sValues .= ', ';
01023 }
01024
01025 $sValues .= "( " . $oDb->quote( $sObjectId ) . ", " . $oDb->quote( $sIdent ) . ", " . $oDb->quote( $iShopId ).", '{$iLang}', " . $oDb->quote( $sStdUrl ) . ", " . $oDb->quote( $sSeoUrl ) . ", 'static' )";
01026 }
01027
01028
01029 if ( $sOldObjectId ) {
01030 $oDb->execute( "delete from oxseo where oxobjectid in ( " . $oDb->quote( $sOldObjectId ) . ", " . $oDb->quote( $sObjectId ) . " )" );
01031 }
01032
01033
01034 if ( $sValues ) {
01035
01036 $sQ = "insert into oxseo ( oxobjectid, oxident, oxshopid, oxlang, oxstdurl, oxseourl, oxtype ) values {$sValues} ";
01037 $oDb->execute( $sQ );
01038 }
01039
01040 return $sObjectId;
01041 }
01042
01050 public function copyStaticUrls( $iShopId )
01051 {
01052 $iBaseShopId = $this->getConfig()->getBaseShopId();
01053 if ( $iShopId != $iBaseShopId ) {
01054 $oDb = oxDb::getDb();
01055 foreach (array_keys(oxRegistry::getLang()->getLanguageIds()) as $iLang) {
01056 $sQ = "insert into oxseo ( oxobjectid, oxident, oxshopid, oxlang, oxstdurl, oxseourl, oxtype )
01057 select MD5( LOWER( CONCAT( " . $oDb->quote( $iShopId ) . ", oxstdurl ) ) ), MD5( LOWER( oxseourl ) ),
01058 " . $oDb->quote( $iShopId ) . ", oxlang, oxstdurl, oxseourl, oxtype from oxseo where oxshopid = '{$iBaseShopId}' and oxtype = 'static' and oxlang='$iLang' ";
01059 $oDb->execute( $sQ );
01060 }
01061 }
01062 }
01063
01073 public function getStaticUrl( $sStdUrl, $iLang = null, $iShopId = null )
01074 {
01075 if (!isset($iShopId)) {
01076 $iShopId = $this->getConfig()->getShopId();
01077 }
01078 if (!isset($iLang)) {
01079 $iLang = oxRegistry::getLang()->getEditLanguage();
01080 }
01081
01082 if ( isset($this->_aStaticUrlCache[$sStdUrl][$iLang][$iShopId])) {
01083 return $this->_aStaticUrlCache[$sStdUrl][$iLang][$iShopId];
01084 }
01085
01086 $sFullUrl = '';
01087 if ( ( $sSeoUrl = $this->_getStaticUri( $sStdUrl, $iShopId, $iLang ) ) ) {
01088 $sFullUrl = $this->_getFullUrl( $sSeoUrl, $iLang, strpos( $sStdUrl, "https:" ) === 0 );
01089 }
01090
01091
01092 $this->_aStaticUrlCache[$sStdUrl][$iLang][$iShopId] = $sFullUrl;
01093
01094 return $sFullUrl;
01095 }
01096
01115 public function addSeoEntry( $sObjectId, $iShopId, $iLang, $sStdUrl, $sSeoUrl, $sType, $blFixed = 1, $sKeywords = '', $sDescription = '', $sParams = '', $blExclude = false, $sAltObjectId = null )
01116 {
01117 $sSeoUrl = $this->_processSeoUrl( $this->_trimUrl( $sSeoUrl ? $sSeoUrl : $this->_getAltUri( $sAltObjectId ? $sAltObjectId : $sObjectId, $iLang ) ), $sObjectId, $iLang, $blExclude );
01118 if ( $this->_saveToDb( $sType, $sObjectId, $sStdUrl, $sSeoUrl, $iLang, $iShopId, $blFixed, $sParams ) ) {
01119
01120 $oDb = oxDb::getDb();
01121
01122
01123 $sQtedObjectId = $oDb->quote( $sAltObjectId ? $sAltObjectId : $sObjectId );
01124 $iQtedShopId = $oDb->quote( $iShopId );
01125
01126 $oStr = getStr();
01127 if ( $sKeywords !== false ) {
01128 $sKeywords = $oDb->quote( $oStr->htmlspecialchars( $this->encodeString( $oStr->strip_tags( $sKeywords ), false, $iLang ) ) );
01129 }
01130
01131 if ( $sDescription !== false ) {
01132 $sDescription = $oDb->quote( $oStr->htmlspecialchars( $oStr->strip_tags( $sDescription ) ) );
01133 }
01134
01135 $sQ = "insert into oxobject2seodata
01136 ( oxobjectid, oxshopid, oxlang, oxkeywords, oxdescription )
01137 values
01138 ( {$sQtedObjectId}, {$iQtedShopId}, {$iLang}, ".( $sKeywords ? $sKeywords : "''" ).", ".( $sDescription ? $sDescription : "''" )." )
01139 on duplicate key update
01140 oxkeywords = ".( $sKeywords ? $sKeywords : "oxkeywords" ).", oxdescription = ".( $sDescription ? $sDescription : "oxdescription" );
01141 $oDb->execute( $sQ );
01142 }
01143 }
01144
01153 protected function _getAltUri( $sObjectId, $iLang )
01154 {
01155 }
01156
01167 public function deleteSeoEntry( $sObjectId, $iShopId, $iLang, $sType )
01168 {
01169 $oDb = oxDb::getDb();
01170 $sQ = "delete from oxseo where oxobjectid = " . $oDb->quote( $sObjectId ) . " and oxshopid = " . $oDb->quote( $iShopId ) . " and oxlang = " . $oDb->quote( $iLang ) . " and oxtype = " . $oDb->quote( $sType ) . " ";
01171 $oDb->execute( $sQ );
01172 }
01173
01184 public function getMetaData( $sObjectId, $sMetaType, $iShopId = null, $iLang = null )
01185 {
01186 $oDb = oxDb::getDb();
01187
01188 $iShopId = ( !isset( $iShopId ) ) ? $this->getConfig()->getShopId():$iShopId;
01189 $iLang = ( !isset( $iLang ) ) ? oxRegistry::getLang()->getObjectTplLanguage():((int) $iLang);
01190 return $oDb->getOne( "select {$sMetaType} from oxobject2seodata where oxobjectid = " . $oDb->quote( $sObjectId ) . " and oxshopid = " . $oDb->quote( $iShopId )." and oxlang = '{$iLang}'" );
01191 }
01192
01206 public function getDynamicUrl( $sStdUrl, $sSeoUrl, $iLang )
01207 {
01208 startProfile("getDynamicUrl");
01209 $sDynUrl = $this->_getFullUrl( $this->_getDynamicUri( $sStdUrl, $sSeoUrl, $iLang ), strpos( $sStdUrl, "https:" ) === 0 );
01210 stopProfile("getDynamicUrl");
01211 return $sDynUrl;
01212 }
01213
01222 public function fetchSeoUrl( $sStdUrl, $iLanguage = null )
01223 {
01224 $oDb = oxDb::getDb( oxDb::FETCH_MODE_ASSOC );
01225 $iLanguage = isset( $iLanguage ) ? ( (int) $iLanguage ) : oxRegistry::getLang()->getBaseLanguage();
01226 $sSeoUrl = false;
01227
01228 $sShopId = $this->getConfig()->getShopId();
01229
01230 $sQ = "SELECT `oxseourl`, `oxlang` FROM `oxseo` WHERE `oxstdurl` = " . $oDb->quote( $sStdUrl ) . " AND `oxlang` = '$iLanguage' AND `oxshopid` = '$sShopId' LIMIT 1";
01231
01232 $oDb = oxDb::getDb( oxDb::FETCH_MODE_ASSOC );
01233 $oRs = $oDb->select( $sQ );
01234
01235 if ( !$oRs->EOF ) {
01236 $sSeoUrl = $oRs->fields['oxseourl'];
01237 }
01238
01239 return $sSeoUrl;
01240 }
01241 }