/**
  * Singleton method
  *
  * @return oxseoencodercategory
  */
 public static function getInstance()
 {
     if (defined('OXID_PHP_UNIT')) {
         self::$_instance = modInstances::getMod(__CLASS__);
     }
     if (!self::$_instance) {
         self::$_instance = oxNew("oxSeoEncoderCategory");
         if (defined('OXID_PHP_UNIT')) {
             modInstances::addMod(__CLASS__, self::$_instance);
         }
     }
     if (defined('OXID_PHP_UNIT')) {
         // resetting cache
         self::$_instance->_aSeoCache = array();
     }
     return self::$_instance;
 }
 /**
  * Returns SEO uri for content object. Includes parent category path info if
  * content is assigned to it
  *
  * @param oxcontent $oCont        content category object
  * @param int       $iLang        language
  * @param bool      $blRegenerate if TRUE forces seo url regeneration
  *
  * @return string
  */
 public function getContentUri($oCont, $iLang = null, $blRegenerate = false)
 {
     if (!isset($iLang)) {
         $iLang = $oCont->getLanguage();
     }
     //load details link from DB
     if ($blRegenerate || !($sSeoUrl = $this->_loadFromDb('oxcontent', $oCont->getId(), $iLang))) {
         if ($iLang != $oCont->getLanguage()) {
             $sId = $oCont->getId();
             $oCont = oxNew('oxcontent');
             $oCont->loadInLang($iLang, $sId);
         }
         $sSeoUrl = '';
         if ($oCont->oxcontents__oxcatid->value) {
             $oCat = oxNew('oxcategory');
             if ($oCat->loadInLang($iLang, $oCont->oxcontents__oxcatid->value)) {
                 if ($oCat->oxcategories__oxparentid->value && $oCat->oxcategories__oxparentid->value != 'oxrootid') {
                     $oParentCat = oxNew('oxcategory');
                     if ($oParentCat->loadInLang($iLang, $oCat->oxcategories__oxparentid->value)) {
                         $sSeoUrl .= oxSeoEncoderCategory::getInstance()->getCategoryUri($oParentCat);
                     }
                 }
             }
         }
         $sSeoUrl .= $this->_prepareTitle($oCont->oxcontents__oxtitle->value) . '/';
         $sSeoUrl = $this->_processSeoUrl($sSeoUrl, $oCont->getId(), $iLang);
         $this->_saveToDb('oxcontent', $oCont->getId(), $oCont->getBaseStdLink($iLang), $sSeoUrl, $iLang);
     }
     return $sSeoUrl;
 }
 /**
  * Returns current object type seo encoder object
  *
  * @return oxSeoEncoderCategory
  */
 protected function _getEncoder()
 {
     return oxSeoEncoderCategory::getInstance();
 }
 /**
  * Updates category tree, returns true on success.
  *
  * @return bool
  */
 protected function _update()
 {
     $oDB = oxDb::getDb();
     $sOldParentID = $oDB->getOne("select oxparentid from oxcategories where oxid = " . $oDB->quote($this->getId()));
     if ($this->_blIsSeoObject && $this->isAdmin()) {
         oxSeoEncoderCategory::getInstance()->markRelatedAsExpired($this);
     }
     $blRes = parent::_update();
     // #872C - need to update category tree oxleft and oxright values (nested sets),
     // then sub trees are moved inside one root, or to another root.
     // this is done in 3 basic steps
     // 1. increase oxleft and oxright values of target root tree by $iTreeSize, where oxleft>=$iMoveAfter , oxright>=$iMoveAfter
     // 2. modify current subtree, we want to move by adding $iDelta to it's oxleft and oxright,  where oxleft>=$sOldParentLeft and oxright<=$sOldParentRight values,
     //    in this step we also modify rootid's if they were changed
     // 3. decreasing oxleft and oxright values of current root tree, where oxleft >= $sOldParentRight+1 , oxright >= $sOldParentRight+1
     // did we change position in tree ?
     if ($this->oxcategories__oxparentid->value != $sOldParentID) {
         $sOldParentLeft = $this->oxcategories__oxleft->value;
         $sOldParentRight = $this->oxcategories__oxright->value;
         $iTreeSize = $sOldParentRight - $sOldParentLeft + 1;
         $sNewRootID = $oDB->getOne("select oxrootid from oxcategories where oxid = " . $oDB->quote($this->oxcategories__oxparentid->value));
         //If empty rootID, we set it to categorys oxid
         if ($sNewRootID == "") {
             //echo "<br>* ) Creating new root tree ( {$this->_sOXID} )";
             $sNewRootID = $this->getId();
         }
         $sNewParentLeft = $oDB->getOne("select oxleft from oxcategories where oxid = " . $oDB->quote($this->oxcategories__oxparentid->value));
         //if(!$sNewParentLeft){
         //the current node has become root node, (oxrootid == "oxrootid")
         //    $sNewParentLeft = 0;
         //}
         $iMoveAfter = $sNewParentLeft + 1;
         //New parentid can not be set to it's child
         if ($sNewParentLeft > $sOldParentLeft && $sNewParentLeft < $sOldParentRight && $this->oxcategories__oxrootid->value == $sNewRootID) {
             //echo "<br>* ) Can't asign category to it's child";
             //Restoring old parentid, stoping further actions
             $sRestoreOld = "UPDATE oxcategories SET OXPARENTID = " . $oDB->quote($sOldParentID) . " WHERE oxid = " . $oDB->quote($this->getId());
             $oDB->execute($sRestoreOld);
             return false;
         }
         //Old parent will be shifted too, if it is in the same tree
         if ($sOldParentLeft > $iMoveAfter && $this->oxcategories__oxrootid->value == $sNewRootID) {
             $sOldParentLeft += $iTreeSize;
             $sOldParentRight += $iTreeSize;
         }
         $iDelta = $iMoveAfter - $sOldParentLeft;
         //echo "Size=$iTreeSize, NewStart=$iMoveAfter, delta=$iDelta";
         $sAddOld = " and oxshopid = '" . $this->getShopId() . "' and OXROOTID = " . $oDB->quote($this->oxcategories__oxrootid->value) . ";";
         $sAddNew = " and oxshopid = '" . $this->getShopId() . "' and OXROOTID = " . $oDB->quote($sNewRootID) . ";";
         //Updating everything after new position
         $oDB->execute("UPDATE oxcategories SET OXLEFT = (OXLEFT + " . $iTreeSize . ") WHERE OXLEFT >= " . $iMoveAfter . $sAddNew);
         $oDB->execute("UPDATE oxcategories SET OXRIGHT = (OXRIGHT + " . $iTreeSize . ") WHERE OXRIGHT >= " . $iMoveAfter . $sAddNew);
         //echo "<br>1.) + $iTreeSize, >= $iMoveAfter";
         $sChangeRootID = "";
         if ($this->oxcategories__oxrootid->value != $sNewRootID) {
             //echo "<br>* ) changing root IDs ( {$this->oxcategories__oxrootid->value} -> {$sNewRootID} )";
             $sChangeRootID = ", OXROOTID=" . $oDB->quote($sNewRootID);
         }
         //Updating subtree
         $oDB->execute("UPDATE oxcategories SET OXLEFT = (OXLEFT + " . $iDelta . "), OXRIGHT = (OXRIGHT + " . $iDelta . ") " . $sChangeRootID . " WHERE OXLEFT >= " . $sOldParentLeft . " AND OXRIGHT <= " . $sOldParentRight . $sAddOld);
         //echo "<br>2.) + $iDelta, >= $sOldParentLeft and <= $sOldParentRight";
         //Updating everything after old position
         $oDB->execute("UPDATE oxcategories SET OXLEFT = (OXLEFT - " . $iTreeSize . ") WHERE OXLEFT >=   " . ($sOldParentRight + 1) . $sAddOld);
         $oDB->execute("UPDATE oxcategories SET OXRIGHT = (OXRIGHT - " . $iTreeSize . ") WHERE OXRIGHT >=   " . ($sOldParentRight + 1) . $sAddOld);
         //echo "<br>3.) - $iTreeSize, >= ".($sOldParentRight+1);
     }
     if ($blRes && $this->_blIsSeoObject && $this->isAdmin()) {
         oxSeoEncoderCategory::getInstance()->markRelatedAsExpired($this);
     }
     return $blRes;
 }
 /**
  * Sets details locator data for articles that came from regular list.
  *
  * @param oxubase   $oLocatorTarget view object
  * @param oxarticle $oCurrArticle   current article
  *
  * @return null
  */
 protected function _setListLocatorData($oLocatorTarget, $oCurrArticle)
 {
     // if no active category is loaded - lets check for category passed by post/get
     if ($oCategory = $oLocatorTarget->getActCategory()) {
         $sCatId = $oCategory->getId();
         $sOrderBy = null;
         if ($oLocatorTarget->showSorting()) {
             $aSorting = $oLocatorTarget->getSorting($sCatId);
             // checking if we have defined sorting parameters in the sessions
             if (!$aSorting && $oCategory->oxcategories__oxdefsort->value) {
                 // if no sorting parameters are set in the session and the category has default
                 // sorting parameters, we use them instead
                 $sSortBy = getViewName('oxarticles') . ".{$oCategory->oxcategories__oxdefsort->value}";
                 $sSortDir = $oCategory->oxcategories__oxdefsortmode->value ? "desc" : null;
                 $oLocatorTarget->setItemSorting($sCatId, $sSortBy, $sSortDir);
             }
             $oLocatorTarget->prepareSortColumns();
             $sOrderBy = $oLocatorTarget->getSortingSql($sCatId);
         }
         $oIdList = $this->_loadIdsInList($oCategory, $oCurrArticle, $sOrderBy);
         //page number
         $iPage = $this->_findActPageNumber($oLocatorTarget->getActPage(), $oIdList, $oCurrArticle);
         // setting product position in list, amount of articles etc
         $oCategory->iCntOfProd = $oIdList->count();
         $oCategory->iProductPos = $this->_getProductPos($oCurrArticle, $oIdList, $oLocatorTarget);
         if (oxUtils::getInstance()->seoIsActive() && $iPage) {
             $oCategory->toListLink = oxSeoEncoderCategory::getInstance()->getCategoryPageUrl($oCategory, $iPage);
         } else {
             $oCategory->toListLink = $this->_makeLink($oCategory->getLink(), $this->_getPageNumber($iPage));
         }
         $oCategory->nextProductLink = $this->_oNextProduct ? $this->_makeLink($this->_oNextProduct->getLink(), '') : null;
         $oCategory->prevProductLink = $this->_oBackProduct ? $this->_makeLink($this->_oBackProduct->getLink(), '') : null;
         // active category
         $oLocatorTarget->setActiveCategory($oCategory);
         // category path
         if ($oCatTree = $oLocatorTarget->getCategoryTree()) {
             $oLocatorTarget->setCatTreePath($oCatTree->getPath());
         }
     }
 }
 /**
  * create article uri for given category and save it
  *
  * @param oxArticle  $oArticle  article object
  * @param oxCategory $oCategory category object
  * @param int        $iLang     language to generate uri for
  *
  * @return string
  */
 protected function _createArticleCategoryUri($oArticle, $oCategory, $iLang)
 {
     startProfile(__FUNCTION__);
     $oArticle = $this->_getProductForLang($oArticle, $iLang);
     // create title part for uri
     $sTitle = $this->_prepareArticleTitle($oArticle);
     // writing category path
     $sSeoUri = $this->_processSeoUrl(oxSeoEncoderCategory::getInstance()->getCategoryUri($oCategory, $iLang) . $sTitle, $oArticle->getId(), $iLang);
     $sCatId = $oCategory->getId();
     $this->_saveToDb('oxarticle', $oArticle->getId(), oxUtilsUrl::getInstance()->appendUrl($oArticle->getBaseStdLink($iLang), array('cnid' => $sCatId)), $sSeoUri, $iLang, null, 0, $sCatId);
     stopProfile(__FUNCTION__);
     return $sSeoUri;
 }