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
00089 public static function getInstance()
00090 {
00091 if ( defined( 'OXID_PHP_UNIT' ) ) {
00092 self::$_instance = modInstances::getMod( __CLASS__ );
00093 }
00094
00095 if (!self::$_instance) {
00096 self::$_instance = oxNew("oxSeoEncoder");
00097 if ( defined( 'OXID_PHP_UNIT' ) ) {
00098 modInstances::addMod( __CLASS__, self::$_instance);
00099 }
00100 }
00101
00102 return self::$_instance;
00103
00104 }
00105
00114 public function addLanguageParam( $sSeoUrl, $iLang )
00115 {
00116 $iLang = (int) $iLang;
00117 $iDefLang = (int) $this->getConfig()->getConfigParam( 'iDefSeoLang' );
00118 $aLangIds = oxLang::getInstance()->getLanguageIds();
00119
00120 if ( $iLang != $iDefLang && isset( $aLangIds[$iLang] ) && getStr()->strpos( $sSeoUrl, $aLangIds[$iLang] . '/' ) !== 0 ) {
00121 $sSeoUrl = $aLangIds[$iLang] . '/'.$sSeoUrl;
00122 }
00123
00124 return $sSeoUrl;
00125 }
00126
00139 protected function _processSeoUrl( $sSeoUrl, $sObjectId = null, $iLang = null, $blExclude = false )
00140 {
00141 if (!$blExclude) {
00142 $sSeoUrl = $this->addLanguageParam( $sSeoUrl, $iLang );
00143 }
00144 return $this->_getUniqueSeoUrl( $sSeoUrl, $sObjectId, $iLang );
00145 }
00146
00150 public function __construct()
00151 {
00152 $myConfig = $this->getConfig();
00153 if (!self::$_sSeparator) {
00154 $this->setSeparator( $myConfig->getConfigParam( 'sSEOSeparator' ) );
00155 }
00156 if (!self::$_sPrefix) {
00157 $this->setPrefix( $myConfig->getConfigParam( 'sSEOuprefix' ) );
00158 }
00159 $this->setReservedWords( $myConfig->getConfigParam( 'aSEOReservedWords' ) );
00160 }
00161
00173 protected function _copyToHistory( $sId, $iShopId, $iLang, $sType = null, $sNewId = null )
00174 {
00175 $oDb = oxDb::getDb();
00176 $sObjectid = $sNewId?$oDb->quote( $sNewId ):'oxobjectid';
00177 $sType = $sType?"oxtype =".$oDb->quote( $sType )." and":'';
00178 $iLang = (int) $iLang;
00179
00180
00181 $sSub = "select $sObjectid, MD5( LOWER( oxseourl ) ), oxshopid, oxlang, now() from oxseo
00182 where {$sType} oxobjectid = ".$oDb->quote( $sId )." and oxshopid = ".$oDb->quote( $iShopId )." and
00183 oxlang = {$iLang} and oxexpired = '1'";
00184 $sQ = "replace oxseohistory ( oxobjectid, oxident, oxshopid, oxlang, oxinsert ) {$sSub}";
00185 $oDb->execute( $sQ );
00186 }
00187
00196 public function getDynamicObjectId( $iShopId, $sStdUrl )
00197 {
00198 return $this->_getStaticObjectId( $iShopId, $sStdUrl );
00199 }
00200
00210 protected function _getDynamicUri( $sStdUrl, $sSeoUrl, $iLang )
00211 {
00212 $iShopId = $this->getConfig()->getShopId();
00213
00214 $sStdUrl = $this->_trimUrl( $sStdUrl );
00215 $sObjectId = $this->getDynamicObjectId( $iShopId, $sStdUrl );
00216 $sSeoUrl = $this->_prepareUri( $this->addLanguageParam( $sSeoUrl, $iLang ), $iLang );
00217
00218
00219 $sOldSeoUrl = $this->_loadFromDb( 'dynamic', $sObjectId, $iLang );
00220 if ( $sOldSeoUrl === $sSeoUrl ) {
00221 $sSeoUrl = $sOldSeoUrl;
00222 } else {
00223
00224 if ( $sOldSeoUrl ) {
00225
00226 $this->_copyToHistory( $sObjectId, $iShopId, $iLang, 'dynamic' );
00227 }
00228
00229
00230 $sSeoUrl = $this->_processSeoUrl( $sSeoUrl, $sObjectId, $iLang );
00231
00232
00233 $this->_saveToDb( 'dynamic', $sObjectId, $sStdUrl, $sSeoUrl, $iLang, $iShopId );
00234 }
00235
00236 return $sSeoUrl;
00237 }
00238
00248 protected function _getFullUrl( $sSeoUrl, $iLang = null, $blSsl = false )
00249 {
00250 if ( $sSeoUrl ) {
00251 $sFullUrl = ( $blSsl ? $this->getConfig()->getSslShopUrl( $iLang ) : $this->getConfig()->getShopUrl( $iLang, false ) ) . $sSeoUrl;
00252 return oxUtilsUrl::getInstance()->processSeoUrl( $sFullUrl );
00253 }
00254 return false;
00255 }
00256
00266 protected function _getSeoIdent( $sSeoUrl )
00267 {
00268 return md5( strtolower( $sSeoUrl ) );
00269 }
00270
00280 protected function _getStaticUri( $sStdUrl, $iShopId, $iLang )
00281 {
00282 $sStdUrl = $this->_trimUrl( $sStdUrl, $iLang );
00283 return $this->_loadFromDb( 'static', $this->_getStaticObjectId( $iShopId, $sStdUrl ), $iLang );
00284 }
00285
00291 protected function _getUrlExtension()
00292 {
00293 return;
00294 }
00295
00308 protected function _getUniqueSeoUrl( $sSeoUrl, $sObjectId = null, $iObjectLang = null )
00309 {
00310 $sSeoUrl = $this->_prepareUri( $sSeoUrl, $iObjectLang );
00311 $oStr = getStr();
00312 $sExt = '';
00313 if ( $oStr->preg_match( '/(\.html?|\/)$/i', $sSeoUrl, $aMatched ) ) {
00314 $sExt = $aMatched[0];
00315 }
00316 $sBaseSeoUrl = $sSeoUrl;
00317 if ( $sExt && $oStr->substr( $sSeoUrl, 0 - $oStr->strlen( $sExt ) ) == $sExt ) {
00318 $sBaseSeoUrl = $oStr->substr( $sSeoUrl, 0, $oStr->strlen( $sSeoUrl ) - $oStr->strlen( $sExt ) );
00319 }
00320
00321 $iShopId = $this->getConfig()->getShopId();
00322 $iCnt = 0;
00323 $sCheckSeoUrl = $this->_trimUrl( $sSeoUrl );
00324 $sQ = "select 1 from oxseo where oxshopid = '{$iShopId}'";
00325
00326 $oDb = oxDb::getDb();
00327
00328 if ( $sObjectId && isset($iObjectLang) ) {
00329 $iObjectLang = (int) $iObjectLang;
00330 $sQ .= " and not (oxobjectid = " . $oDb->quote( $sObjectId ) . " and oxlang = $iObjectLang)";
00331 }
00332
00333 while ( $oDb->getOne( $sQ ." and oxident= " . $oDb->quote( $this->_getSeoIdent( $sCheckSeoUrl ) ) ) ) {
00334 $sAdd = '';
00335 if ( self::$_sPrefix ) {
00336 $sAdd = self::$_sSeparator . self::$_sPrefix;
00337 }
00338 if ( $iCnt ) {
00339 $sAdd .= self::$_sSeparator . $iCnt;
00340 }
00341 ++$iCnt;
00342
00343 $sSeoUrl = $sBaseSeoUrl . $sAdd . $sExt;
00344 $sCheckSeoUrl = $this->_trimUrl( $sSeoUrl );
00345 }
00346 return $sSeoUrl;
00347 }
00348
00363 protected function _isFixed( $sType, $sId, $iLang, $iShopId = null, $sParams = null, $blStrictParamsCheck = true)
00364 {
00365 if ( $iShopId === null ) {
00366 $iShopId = $this->getConfig()->getShopId();
00367 }
00368 $iLang = (int) $iLang;
00369
00370 if ( !isset( self::$_aFixedCache[$sType][$sShopId][$sId][$iLang] ) ) {
00371 $oDb = oxDb::getDb();
00372
00373 $sQ = "SELECT `oxfixed`
00374 FROM `oxseo`
00375 WHERE `oxtype` = ".$oDb->quote( $sType )."
00376 AND `oxobjectid` = ".$oDb->quote( $sId ) ."
00377 AND `oxshopid` = ".$oDb->quote( $iShopId )."
00378 AND `oxlang` = '{$iLang}'";
00379
00380 $sParams = $sParams ? $oDb->quote( $sParams ) : "''";
00381 if ( $sParams && $blStrictParamsCheck ) {
00382 $sQ .= " AND `oxparams` = {$sParams}";
00383 } else {
00384 $sQ .= " ORDER BY `oxparams` ASC";
00385 }
00386 $sQ .= " LIMIT 1";
00387
00388 self::$_aFixedCache[$sType][$sShopId][$sId][$iLang] = (bool) $oDb->getOne( $sQ );
00389 }
00390 return self::$_aFixedCache[$sType][$sShopId][$sId][$iLang];
00391 }
00392
00403 protected function _getCacheKey( $sType, $iLang = null, $iShopId = null, $sParams = null )
00404 {
00405 $blAdmin = $this->isAdmin();
00406 if ( !$blAdmin && $sType !== "oxarticle" ) {
00407 return $sType . ( (int) $iLang ) . ( (int) $iShopId ) . "seo";
00408 }
00409
00410
00411 if ( self::$_sCacheKey === null ) {
00412 self::$_sCacheKey = false;
00413 if ( !$blAdmin && ( $oView = $this->getConfig()->getActiveView() ) ) {
00414 self::$_sCacheKey = md5( $oView->getViewId() ) . "seo";
00415 }
00416 }
00417 return self::$_sCacheKey;
00418 }
00419
00431 protected function _loadFromCache( $sCacheIdent, $sType, $iLang = null, $iShopId = null, $sParams = null )
00432 {
00433 if ( !$this->getConfig()->getConfigParam( 'blEnableSeoCache' ) ) {
00434 return false;
00435 }
00436
00437 startProfile( "seoencoder_loadFromCache" );
00438
00439 $sCacheKey = $this->_getCacheKey( $sType, $iLang, $iShopId, $sParams );
00440 $sCache = false;
00441
00442 if ( $sCacheKey && !isset( self::$_aCache[$sCacheKey] ) ) {
00443 self::$_aCache[$sCacheKey] = oxUtils::getInstance()->fromFileCache( $sCacheKey );
00444 }
00445
00446 if ( isset( self::$_aCache[$sCacheKey] ) && isset( self::$_aCache[$sCacheKey][$sCacheIdent] ) ) {
00447 $sCache = self::$_aCache[$sCacheKey][$sCacheIdent];
00448 }
00449
00450 stopProfile( "seoencoder_loadFromCache" );
00451 return $sCache;
00452 }
00453
00466 protected function _saveInCache( $sCacheIdent, $sCache, $sType, $iLang = null, $iShopId = null, $sParams = null )
00467 {
00468 if ( !$this->getConfig()->getConfigParam( 'blEnableSeoCache' ) ) {
00469 return false;
00470 }
00471
00472 startProfile( "seoencoder_saveInCache" );
00473
00474 $blSaved = false;
00475 if ( $sCache && ( $sCacheKey = $this->_getCacheKey( $sType, $iLang, $iShopId, $sParams ) ) !== false ) {
00476 self::$_aCache[$sCacheKey][$sCacheIdent] = $sCache;
00477 $blSaved = oxUtils::getInstance()->toFileCache( $sCacheKey, self::$_aCache[$sCacheKey] );
00478 }
00479
00480 stopProfile( "seoencoder_saveInCache" );
00481 return $blSaved;
00482 }
00483
00499 protected function _loadFromDb( $sType, $sId, $iLang, $iShopId = null, $sParams = null, $blStrictParamsCheck = true)
00500 {
00501
00502 if ( $iShopId === null ) {
00503 $iShopId = $this->getConfig()->getShopId();
00504 }
00505
00506 $iLang = (int) $iLang;
00507 $oDb = oxDb::getDb( oxDb::FETCH_MODE_ASSOC );
00508
00509 $sQ = "
00510 SELECT
00511 `oxfixed`,
00512 `oxseourl`,
00513 `oxexpired`,
00514 `oxtype`
00515 FROM `oxseo`
00516 WHERE `oxtype` = ".$oDb->quote( $sType )."
00517 AND `oxobjectid` = ".$oDb->quote( $sId ) ."
00518 AND `oxshopid` = ".$oDb->quote( $iShopId )."
00519 AND `oxlang` = '{$iLang}'";
00520
00521 $sParams = $sParams ? $sParams : '';
00522 if ( $sParams && $blStrictParamsCheck ) {
00523 $sQ .= " AND `oxparams` = '{$sParams}'";
00524 } else {
00525 $sQ .= " ORDER BY `oxparams` ASC";
00526 }
00527 $sQ .= " LIMIT 1";
00528
00529
00530
00531 $sIdent = md5( $sQ );
00532
00533
00534 if ( ( $sSeoUrl = $this->_loadFromCache( $sIdent, $sType, $iLang, $iShopId, $sParams ) ) === false ) {
00535 $oRs = $oDb->select( $sQ );
00536
00537 if ( $oRs && $oRs->recordCount() > 0 && !$oRs->EOF ) {
00538
00539 if ( $oRs->fields['oxexpired'] && ( $oRs->fields['oxtype'] == 'static' || $oRs->fields['oxtype'] == 'dynamic' ) ) {
00540
00541 $this->_copyToHistory( $sId, $iShopId, $iLang );
00542 $oDb->execute( "update oxseo set oxexpired = 0 where oxobjectid = ".$oDb->quote( $sId )." and oxlang = '{$iLang}'" );
00543 $sSeoUrl = $oRs->fields['oxseourl'];
00544 } elseif ( !$oRs->fields['oxexpired'] || $oRs->fields['oxfixed'] ) {
00545
00546 $sSeoUrl = $oRs->fields['oxseourl'];
00547 }
00548
00549
00550 $this->_saveInCache( $sIdent, $sSeoUrl, $sType, $iLang, $iShopId, $sParams );
00551 }
00552 }
00553 return $sSeoUrl;
00554 }
00555
00562 protected function _getReservedEntryKeys()
00563 {
00564 if ( !isset( self::$_aReservedEntryKeys ) || !is_array( self::$_aReservedEntryKeys ) ) {
00565 $sDir = getShopBasePath();
00566 self::$_aReservedEntryKeys = array_map('preg_quote', self::$_aReservedWords, array('#'));
00567 $oStr = getStr();
00568 foreach ( glob( "$sDir/*" ) as $sFile ) {
00569 if ( $oStr->preg_match( '/^(.+)\.php[0-9]*$/i', basename( $sFile ), $aMatches ) ) {
00570 self::$_aReservedEntryKeys[] = preg_quote( $aMatches[0], '#' );
00571 self::$_aReservedEntryKeys[] = preg_quote( $aMatches[1], '#' );
00572 } elseif ( is_dir( $sFile ) ) {
00573 self::$_aReservedEntryKeys[] = preg_quote( basename( $sFile ), '#' );
00574 }
00575 }
00576 self::$_aReservedEntryKeys = array_unique(self::$_aReservedEntryKeys);
00577 }
00578 return self::$_aReservedEntryKeys;
00579 }
00580
00589 protected function _prepareUri( $sUri, $iLang = false )
00590 {
00591
00592 $sUri = $this->encodeString( $sUri, true, $iLang );
00593
00594
00595 $oStr = getStr();
00596 $sUri = $oStr->strip_tags( $sUri );
00597
00598
00599 $sExt = $this->_getUrlExtension();
00600 if ($sExt === null) {
00601 $aMatched = array();
00602 if ( $oStr->preg_match( '/(\.html?|\/)$/i', $sUri, $aMatched ) ) {
00603 $sExt = $aMatched[0];
00604 } else {
00605 $sExt = '/';
00606 }
00607 }
00608 if ( $sExt && $oStr->substr( $sUri, 0 - $oStr->strlen( $sExt ) ) == $sExt ) {
00609 $sUri = $oStr->substr( $sUri, 0, $oStr->strlen( $sUri ) - $oStr->strlen( $sExt ) );
00610 }
00611
00612
00613
00614 $sQuotedPrefix = preg_quote( self::$_sSeparator . self::$_sPrefix, '/');
00615 if ( phpversion() < '5.3' ) {
00616 $sQuotedPrefix = str_replace( '-', '\-', $sQuotedPrefix );
00617 }
00618 $sRegExp = '/[^A-Za-z0-9' . $sQuotedPrefix . '\/]+/';
00619 $sUri = $oStr->preg_replace( array( "/\W*\/\W*/", $sRegExp ), array( "/", self::$_sSeparator ), $sUri );
00620
00621
00622 if ( !$sUri && self::$_sPrefix ) {
00623 $sUri = $this->_prepareUri( self::$_sPrefix, $iLang );
00624 }
00625
00626 $sAdd = '';
00627 if ('/' != self::$_sSeparator) {
00628 $sAdd = self::$_sSeparator . self::$_sPrefix;
00629 $sUri = trim($sUri, self::$_sSeparator);
00630 } else {
00631 $sAdd = '_' . self::$_sPrefix;
00632 }
00633
00634
00635 $sUri .= $sExt;
00636
00637
00638 $sUri = $oStr->preg_replace( "#^(/*)(".implode('|', $this->_getReservedEntryKeys()).")(/|$)#i", "\$1\$2$sAdd\$3", $sUri );
00639
00640
00641
00642
00643 if ( phpversion() < '5.3' ) {
00644 $sQuotedSeparator = str_replace( '-', '\-', $sQuotedSeparator );
00645 }
00646 return $oStr->preg_replace( array( '|//+|', '/' . $sQuotedSeparator . $sQuotedSeparator .'+/' ),
00647 array( '/', self::$_sSeparator ), $sUri );
00648 }
00649
00650
00660 protected function _prepareTitle( $sTitle, $blSkipTruncate = false, $iLang = false )
00661 {
00662 $sTitle = $this->encodeString( $sTitle, true, $iLang );
00663 $sSep = self::$_sSeparator;
00664 if (!$sSep || ('/' == $sSep)) {
00665 $sSep = '_';
00666 }
00667
00668 $sRegExp = '/[^A-Za-z0-9\/'.preg_quote( self::$_sPrefix, '/').preg_quote($sSep, '/').']+/';
00669 $sTitle = preg_replace( array("#/+#", $sRegExp, "# +#", "#(".preg_quote($sSep, '/').")+#"), $sSep, $sTitle );
00670
00671 $oStr = getStr();
00672
00673 if ( !$blSkipTruncate && $oStr->strlen( $sTitle ) > $this->_iIdLength ) {
00674 $iFirstSpace = $oStr->strpos( $sTitle, $sSep, $this->_iIdLength);
00675 if ( $iFirstSpace !== false ) {
00676 $sTitle = $oStr->substr( $sTitle, 0, $iFirstSpace );
00677 }
00678 }
00679
00680 $sTitle = trim( $sTitle, $sSep );
00681
00682 if (!$sTitle) {
00683 return self::$_sPrefix;
00684 }
00685
00686 return $sTitle;
00687 }
00688
00689
00706 protected function _saveToDb( $sType, $sObjectId, $sStdUrl, $sSeoUrl, $iLang, $iShopId = null, $blFixed = null, $sParams = null )
00707 {
00708 $oDb = oxDb::getDb( oxDb::FETCH_MODE_ASSOC );
00709 if ( $iShopId === null ) {
00710 $iShopId = $this->getConfig()->getShopId();
00711 }
00712
00713 $iLang = (int) $iLang;
00714
00715 $sStdUrl = $this->_trimUrl( $sStdUrl );
00716 $sSeoUrl = $this->_trimUrl( $sSeoUrl );
00717 $sIdent = $this->_getSeoIdent( $sSeoUrl );
00718
00719
00720 $sQtedObjectId = $oDb->quote( $sObjectId );
00721 $iQtedShopId = $oDb->quote( $iShopId );
00722 $sQtedType = $oDb->quote( $sType );
00723 $sQtedSeoUrl = $oDb->quote( $sSeoUrl );
00724 $sQtedStdUrl = $oDb->quote( $sStdUrl );
00725 $sQtedParams = $oDb->quote( $sParams );
00726 $sQtedIdent = $oDb->quote( $sIdent );
00727
00728
00729 $sQ = "select oxfixed, oxexpired, ( oxstdurl like {$sQtedStdUrl} ) as samestdurl,
00730 oxseourl like {$sQtedSeoUrl} as sameseourl from oxseo where oxtype = {$sQtedType} and
00731 oxobjectid = {$sQtedObjectId} and oxshopid = {$iQtedShopId} and oxlang = {$iLang} ";
00732
00733 $sQ .= $sParams ? " and oxparams = {$sQtedParams} " : '';
00734 $sQ .= "limit 1";
00735
00736 $oRs = $oDb->select( $sQ );
00737 if ( $oRs && $oRs->recordCount() > 0 && !$oRs->EOF ) {
00738 if ( $oRs->fields['samestdurl'] && $oRs->fields['sameseourl'] && $oRs->fields['oxexpired'] ) {
00739
00740 $sFixed = isset( $blFixed ) ? ", oxfixed = " . ( (int) $blFixed ) . " " : '';
00741
00742 $sSql = "update oxseo set oxexpired = 0 {$sFixed} where oxtype = {$sQtedType} and
00743 oxobjectid = {$sQtedObjectId} and oxshopid = {$iQtedShopId} and oxlang = {$iLang} ";
00744 $sSql .= $sParams ? " and oxparams = {$sQtedParams} " : '';
00745 $sSql .= " limit 1";
00746
00747 return $oDb->execute( $sSql );
00748 } elseif ( $oRs->fields['oxexpired'] ) {
00749
00750 $this->_copyToHistory( $sObjectId, $iShopId, $iLang, $sType );
00751 }
00752 }
00753
00754
00755 $sParams = $sParams ? $oDb->quote( $sParams ) :'""';
00756 $blFixed = (int) $blFixed;
00757
00758 $sQ = "insert into oxseo
00759 (oxobjectid, oxident, oxshopid, oxlang, oxstdurl, oxseourl, oxtype, oxfixed, oxexpired, oxparams)
00760 values
00761 ( {$sQtedObjectId}, {$sQtedIdent}, {$iQtedShopId}, {$iLang}, {$sQtedStdUrl}, {$sQtedSeoUrl}, {$sQtedType}, '$blFixed', '0', {$sParams} )
00762 on duplicate key update
00763 oxident = {$sQtedIdent}, oxstdurl = {$sQtedStdUrl}, oxseourl = {$sQtedSeoUrl}, oxfixed = '$blFixed', oxexpired = '0'";
00764
00765 return $oDb->execute( $sQ );
00766 }
00767
00778 protected function _trimUrl( $sUrl, $iLang = null )
00779 {
00780 $myConfig = $this->getConfig();
00781 $oStr = getStr();
00782 $sUrl = str_replace( array( $myConfig->getShopUrl( $iLang, false ), $myConfig->getSslShopUrl( $iLang ) ), '', $sUrl );
00783 $sUrl = $oStr->preg_replace( '/(\?|&(amp;)?)(force_)?(admin_)?sid=[a-z0-9\.]+&?(amp;)?/i', '\1', $sUrl );
00784 $sUrl = $oStr->preg_replace( '/(\?|&(amp;)?)shp=[0-9]+&?(amp;)?/i', '\1', $sUrl );
00785 $sUrl = $oStr->preg_replace( '/(\?|&(amp;)?)lang=[0-9]+&?(amp;)?/i', '\1', $sUrl );
00786 $sUrl = $oStr->preg_replace( '/(\?|&(amp;)?)cur=[0-9]+&?(amp;)?/i', '\1', $sUrl );
00787 $sUrl = $oStr->preg_replace( '/(\?|&(amp;)?)stoken=[a-z0-9]+&?(amp;)?/i', '\1', $sUrl );
00788 $sUrl = $oStr->preg_replace( '/(\?|&(amp;)?)&(amp;)?/i', '\1', $sUrl );
00789 $sUrl = $oStr->preg_replace( '/(\?|&(amp;)?)+$/i', '', $sUrl );
00790 $sUrl = trim( $sUrl );
00791
00792
00793 $iLength = $this->_getMaxUrlLength();
00794 if ( $oStr->strlen( $sUrl ) > $iLength ) {
00795 $sUrl = $oStr->substr( $sUrl, 0, $iLength );
00796 }
00797
00798 return $sUrl;
00799 }
00800
00806 protected function _getMaxUrlLength()
00807 {
00808 if ( $this->_iMaxUrlLength === null ) {
00809
00810 $this->_iMaxUrlLength = $this->getConfig()->getConfigParam( "iMaxSeoUrlLength" ) ;
00811 if ( !$this->_iMaxUrlLength ) {
00812 $this->_iMaxUrlLength = 2048;
00813 }
00814 }
00815 return $this->_iMaxUrlLength;
00816 }
00817
00827 public function encodeString( $sString, $blReplaceChars = true, $iLang = false )
00828 {
00829
00830 $sString = getStr()->html_entity_decode( $sString );
00831
00832 if ( $blReplaceChars ) {
00833 if ($iLang === false || !is_numeric($iLang)) {
00834 $iLang = oxLang::getInstance()->getEditLanguage();
00835 }
00836
00837 if ( $aReplaceChars = oxLang::getInstance()->getSeoReplaceChars($iLang) ) {
00838 $sString = str_replace( array_keys( $aReplaceChars ), array_values( $aReplaceChars ), $sString );
00839 }
00840 }
00841
00842
00843
00844 $aReplaceWhat = array( '&', '"', ''', '<', '>' );
00845 return str_replace( $aReplaceWhat, '', $sString );
00846 }
00847
00855 public function setSeparator( $sSeparator = null )
00856 {
00857 self::$_sSeparator = $sSeparator;
00858 if ( !self::$_sSeparator ) {
00859 self::$_sSeparator = '-';
00860 }
00861 }
00862
00870 public function setPrefix( $sPrefix )
00871 {
00872 if ($sPrefix) {
00873 self::$_sPrefix = $sPrefix;
00874 } else {
00875 self::$_sPrefix = 'oxid';
00876 }
00877 }
00878
00886 public function setIdLength( $iIdlength = null )
00887 {
00888 if ( isset( $iIdlength ) ) {
00889 $this->_iIdLength = $iIdlength;
00890 }
00891 }
00892
00901 public function setReservedWords( $aReservedWords )
00902 {
00903 self::$_aReservedWords = array_merge( self::$_aReservedWords, $aReservedWords );
00904 }
00905
00906
00918 public function markAsExpired( $sId, $iShopId = null, $iExpStat = 1, $iLang = null, $sParams = null )
00919 {
00920 $oDb = oxDb::getDb();
00921 $sWhere = $sId ? "where oxobjectid = " . $oDb->quote( $sId ) : '';
00922 $sWhere .= isset( $iShopId ) ? ( $sWhere ? " and oxshopid = ". $oDb->quote( $iShopId ) : "where oxshopid = ". $oDb->quote( $iShopId ) ) : '';
00923 $sWhere .= $iLang ? ( $sWhere ? " and oxlang = '{$iLang}'" : "where oxlang = '{$iLang}'" ) : '';
00924 $sWhere .= $sParams ? ( $sWhere ? " and {$sParams}" : "where {$sParams}" ) : '';
00925
00926 $sQ = "update oxseo set oxexpired = " . $oDb->quote( $iExpStat ) . " $sWhere ";
00927 $oDb->execute( $sQ );
00928 }
00929
00943 protected function _getPageUri( $oObject, $sType, $sStdUrl, $sSeoUrl, $sParams, $iLang = null, $blFixed = false )
00944 {
00945 if ( !isset( $iLang ) ) {
00946 $iLang = $oObject->getLanguage();
00947 }
00948 $iShopId = $this->getConfig()->getShopId();
00949
00950
00951 $sOldSeoUrl = $this->_loadFromDb( $sType, $oObject->getId(), $iLang, $iShopId, $sParams );
00952 if ( !$sOldSeoUrl ) {
00953
00954 $sSeoUrl = $this->_processSeoUrl( $sSeoUrl, $oObject->getId(), $iLang );
00955 $this->_saveToDb( $sType, $oObject->getId(), $sStdUrl, $sSeoUrl, $iLang, $iShopId, (int) $blFixed, $sParams );
00956 } else {
00957
00958 $sSeoUrl = $sOldSeoUrl;
00959 }
00960 return $sSeoUrl;
00961 }
00962
00971 protected function _getStaticObjectId( $iShopId, $sStdUrl )
00972 {
00973 return md5( strtolower ( $iShopId . $this->_trimUrl( $sStdUrl ) ) );
00974 }
00975
00985 public function encodeStaticUrls( $aStaticUrl, $iShopId, $iLang )
00986 {
00987 $oDb = oxDb::getDb();
00988 $sValues = '';
00989 $sOldObjectId = null;
00990
00991
00992 $sStdUrl = $this->_trimUrl( trim( $aStaticUrl['oxseo__oxstdurl'] ) );
00993 $sObjectId = $aStaticUrl['oxseo__oxobjectid'];
00994
00995 if ( !$sObjectId || $sObjectId == '-1' ) {
00996 $sObjectId = $this->_getStaticObjectId( $iShopId, $sStdUrl );
00997 } else {
00998
00999 $sOldObjectId = $sObjectId;
01000
01001
01002 if ( $this->_getStaticObjectId( $iShopId, $sStdUrl ) != $sObjectId ) {
01003 $sObjectId = $this->_getStaticObjectId( $iShopId, $sStdUrl );
01004 }
01005 }
01006
01007 foreach ( $aStaticUrl['oxseo__oxseourl'] as $iLang => $sSeoUrl ) {
01008
01009 $iLang = (int) $iLang;
01010
01011
01012 $sSeoUrl = $this->_trimUrl( $sSeoUrl );
01013 if ( $sSeoUrl ) {
01014 $sSeoUrl = $this->_processSeoUrl( $sSeoUrl, $sObjectId, $iLang );
01015 }
01016
01017
01018 if ( $sOldObjectId ) {
01019
01020 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) ) {
01021 $this->_copyToHistory( $sOldObjectId, $iShopId, $iLang, 'static', $sObjectId );
01022 }
01023 }
01024
01025 if ( !$sSeoUrl || !$sStdUrl ) {
01026 continue;
01027 }
01028
01029 $sIdent = $this->_getSeoIdent( $sSeoUrl );
01030
01031 if ( $sValues ) {
01032 $sValues .= ', ';
01033 }
01034
01035 $sValues .= "( " . $oDb->quote( $sObjectId ) . ", " . $oDb->quote( $sIdent ) . ", " . $oDb->quote( $iShopId ).", '{$iLang}', " . $oDb->quote( $sStdUrl ) . ", " . $oDb->quote( $sSeoUrl ) . ", 'static' )";
01036 }
01037
01038
01039 if ( $sOldObjectId ) {
01040 $oDb->execute( "delete from oxseo where oxobjectid in ( " . $oDb->quote( $sOldObjectId ) . ", " . $oDb->quote( $sObjectId ) . " )" );
01041 }
01042
01043
01044 if ( $sValues ) {
01045
01046 $sQ = "insert into oxseo ( oxobjectid, oxident, oxshopid, oxlang, oxstdurl, oxseourl, oxtype ) values {$sValues} ";
01047 $oDb->execute( $sQ );
01048 }
01049
01050 return $sObjectId;
01051 }
01052
01060 public function copyStaticUrls( $iShopId )
01061 {
01062 $iBaseShopId = $this->getConfig()->getBaseShopId();
01063 if ( $iShopId != $iBaseShopId ) {
01064 $oDb = oxDb::getDb();
01065 foreach (array_keys(oxLang::getInstance()->getLanguageIds()) as $iLang) {
01066 $sQ = "insert into oxseo ( oxobjectid, oxident, oxshopid, oxlang, oxstdurl, oxseourl, oxtype )
01067 select MD5( LOWER( CONCAT( " . $oDb->quote( $iShopId ) . ", oxstdurl ) ) ), MD5( LOWER( oxseourl ) ),
01068 " . $oDb->quote( $iShopId ) . ", oxlang, oxstdurl, oxseourl, oxtype from oxseo where oxshopid = '{$iBaseShopId}' and oxtype = 'static' and oxlang='$iLang' ";
01069 $oDb->execute( $sQ );
01070 }
01071 }
01072 }
01073
01083 public function getStaticUrl( $sStdUrl, $iLang = null, $iShopId = null )
01084 {
01085 if (!isset($iShopId)) {
01086 $iShopId = $this->getConfig()->getShopId();
01087 }
01088 if (!isset($iLang)) {
01089 $iLang = oxLang::getInstance()->getEditLanguage();
01090 }
01091
01092 if ( isset($this->_aStaticUrlCache[$sStdUrl][$iLang][$iShopId])) {
01093 return $this->_aStaticUrlCache[$sStdUrl][$iLang][$iShopId];
01094 }
01095
01096 $sFullUrl = '';
01097 if ( ( $sSeoUrl = $this->_getStaticUri( $sStdUrl, $iShopId, $iLang ) ) ) {
01098 $sFullUrl = $this->_getFullUrl( $sSeoUrl, $iLang, strpos( $sStdUrl, "https:" ) === 0 );
01099 }
01100
01101
01102 $this->_aStaticUrlCache[$sStdUrl][$iLang][$iShopId] = $sFullUrl;
01103
01104 return $sFullUrl;
01105 }
01106
01125 public function addSeoEntry( $sObjectId, $iShopId, $iLang, $sStdUrl, $sSeoUrl, $sType, $blFixed = 1, $sKeywords = '', $sDescription = '', $sParams = '', $blExclude = false, $sAltObjectId = null )
01126 {
01127 $sSeoUrl = $this->_processSeoUrl( $this->_trimUrl( $sSeoUrl ? $sSeoUrl : $this->_getAltUri( $sAltObjectId ? $sAltObjectId : $sObjectId, $iLang ) ), $sObjectId, $iLang, $blExclude );
01128 if ( $this->_saveToDb( $sType, $sObjectId, $sStdUrl, $sSeoUrl, $iLang, $iShopId, $blFixed, $sParams ) ) {
01129
01130 $oDb = oxDb::getDb();
01131
01132
01133 $sQtedObjectId = $oDb->quote( $sAltObjectId ? $sAltObjectId : $sObjectId );
01134 $iQtedShopId = $oDb->quote( $iShopId );
01135
01136 $oStr = getStr();
01137 if ( $sKeywords !== false ) {
01138 $sKeywords = $oDb->quote( $oStr->htmlspecialchars( $this->encodeString( $oStr->strip_tags( $sKeywords ), false, $iLang ) ) );
01139 }
01140
01141 if ( $sDescription !== false ) {
01142 $sDescription = $oDb->quote( $oStr->htmlspecialchars( $oStr->strip_tags( $sDescription ) ) );
01143 }
01144
01145 $sQ = "insert into oxobject2seodata
01146 ( oxobjectid, oxshopid, oxlang, oxkeywords, oxdescription )
01147 values
01148 ( {$sQtedObjectId}, {$iQtedShopId}, {$iLang}, ".( $sKeywords ? $sKeywords : "''" ).", ".( $sDescription ? $sDescription : "''" )." )
01149 on duplicate key update
01150 oxkeywords = ".( $sKeywords ? $sKeywords : "oxkeywords" ).", oxdescription = ".( $sDescription ? $sDescription : "oxdescription" );
01151 $oDb->execute( $sQ );
01152 }
01153 }
01154
01163 protected function _getAltUri( $sObjectId, $iLang )
01164 {
01165 }
01166
01177 public function deleteSeoEntry( $sObjectId, $iShopId, $iLang, $sType )
01178 {
01179 $oDb = oxDb::getDb();
01180 $sQ = "delete from oxseo where oxobjectid = " . $oDb->quote( $sObjectId ) . " and oxshopid = " . $oDb->quote( $iShopId ) . " and oxlang = " . $oDb->quote( $iLang ) . " and oxtype = " . $oDb->quote( $sType ) . " ";
01181 $oDb->execute( $sQ );
01182 }
01183
01194 public function getMetaData( $sObjectId, $sMetaType, $iShopId = null, $iLang = null )
01195 {
01196 $oDb = oxDb::getDb();
01197
01198 $iShopId = ( !isset( $iShopId ) ) ? $this->getConfig()->getShopId():$iShopId;
01199 $iLang = ( !isset( $iLang ) ) ? oxLang::getInstance()->getObjectTplLanguage():((int) $iLang);
01200 return $oDb->getOne( "select {$sMetaType} from oxobject2seodata where oxobjectid = " . $oDb->quote( $sObjectId ) . " and oxshopid = " . $oDb->quote( $iShopId )." and oxlang = '{$iLang}'" );
01201 }
01202
01216 public function getDynamicUrl( $sStdUrl, $sSeoUrl, $iLang )
01217 {
01218 startProfile("getDynamicUrl");
01219 $sDynUrl = $this->_getFullUrl( $this->_getDynamicUri( $sStdUrl, $sSeoUrl, $iLang ), strpos( $sStdUrl, "https:" ) === 0 );
01220 stopProfile("getDynamicUrl");
01221 return $sDynUrl;
01222 }
01223
01232 public function fetchSeoUrl( $sStdUrl, $iLanguage = null )
01233 {
01234 $oDb = oxDb::getDb( oxDb::FETCH_MODE_ASSOC );
01235 $iLanguage = isset( $iLanguage ) ? ( (int) $iLanguage ) : oxLang::getInstance()->getBaseLanguage();
01236 $sSeoUrl = false;
01237
01238 $sShopId = $this->getConfig()->getShopId();
01239
01240 $sQ = "SELECT `oxseourl`, `oxlang` FROM `oxseo` WHERE `oxstdurl` = " . $oDb->quote( $sStdUrl ) . " AND `oxlang` = '$iLanguage' AND `oxshopid` = '$sShopId' LIMIT 1";
01241
01242 $oRs = $oDb->select( $sQ );
01243 if ( !$oRs->EOF ) {
01244 $sSeoUrl = $oRs->fields['oxseourl'];
01245 }
01246
01247 return $sSeoUrl;
01248 }
01249 }