Exemplo n.º 1
0
 /**
  * @return $this
  * @throws \Exception
  */
 public function save()
 {
     $isUpdate = false;
     if ($this->getId()) {
         $isUpdate = true;
         \Pimcore::getEventManager()->trigger("document.preUpdate", $this);
     } else {
         \Pimcore::getEventManager()->trigger("document.preAdd", $this);
     }
     // we wrap the save actions in a loop here, so that we can restart the database transactions in the case it fails
     // if a transaction fails it gets restarted $maxRetries times, then the exception is thrown out
     // this is especially useful to avoid problems with deadlocks in multi-threaded environments (forked workers, ...)
     $maxRetries = 5;
     for ($retries = 0; $retries < $maxRetries; $retries++) {
         $this->beginTransaction();
         try {
             // check for a valid key, home has no key, so omit the check
             if (!Tool::isValidKey($this->getKey()) && $this->getId() != 1) {
                 throw new \Exception("invalid key for document with id [ " . $this->getId() . " ] key is: [" . $this->getKey() . "]");
             }
             $this->correctPath();
             // set date
             $this->setModificationDate(time());
             if (!$this->getCreationDate()) {
                 $this->setCreationDate(time());
             }
             if (!$isUpdate) {
                 $this->getResource()->create();
             }
             // get the old path from the database before the update is done
             $oldPath = null;
             if ($isUpdate) {
                 $oldPath = $this->getResource()->getCurrentFullPath();
             }
             $this->update();
             // if the old path is different from the new path, update all children
             $updatedChildren = array();
             if ($oldPath && $oldPath != $this->getFullPath()) {
                 $this->getResource()->updateWorkspaces();
                 $updatedChildren = $this->getResource()->updateChildsPaths($oldPath);
             }
             $this->commit();
             break;
             // transaction was successfully completed, so we cancel the loop here -> no restart required
         } catch (\Exception $e) {
             try {
                 $this->rollBack();
             } catch (\Exception $er) {
                 // PDO adapter throws exceptions if rollback fails
                 \Logger::error($er);
             }
             // we try to start the transaction $maxRetries times again (deadlocks, ...)
             if ($retries < $maxRetries - 1) {
                 $run = $retries + 1;
                 $waitTime = 100000;
                 // microseconds
                 \Logger::warn("Unable to finish transaction (" . $run . ". run) because of the following reason '" . $e->getMessage() . "'. --> Retrying in " . $waitTime . " microseconds ... (" . ($run + 1) . " of " . $maxRetries . ")");
                 usleep($waitTime);
                 // wait specified time until we restart the transaction
             } else {
                 // if the transaction still fail after $maxRetries retries, we throw out the exception
                 throw $e;
             }
         }
     }
     $additionalTags = array();
     if (isset($updatedChildren) && is_array($updatedChildren)) {
         foreach ($updatedChildren as $documentId) {
             $tag = "document_" . $documentId;
             $additionalTags[] = $tag;
             // remove the child also from registry (internal cache) to avoid path inconsistencies during long running scripts, such as CLI
             \Zend_Registry::set($tag, null);
         }
     }
     $this->clearDependentCache($additionalTags);
     if ($isUpdate) {
         \Pimcore::getEventManager()->trigger("document.postUpdate", $this);
     } else {
         \Pimcore::getEventManager()->trigger("document.postAdd", $this);
     }
     return $this;
 }
Exemplo n.º 2
0
 /**
  * @return $this
  * @throws \Exception
  */
 public function save()
 {
     $isUpdate = false;
     if ($this->getId()) {
         $isUpdate = true;
         \Pimcore::getEventManager()->trigger("object.preUpdate", $this);
     } else {
         \Pimcore::getEventManager()->trigger("object.preAdd", $this);
     }
     // we wrap the save actions in a loop here, so that we can restart the database transactions in the case it fails
     // if a transaction fails it gets restarted $maxRetries times, then the exception is thrown out
     // this is especially useful to avoid problems with deadlocks in multi-threaded environments (forked workers, ...)
     $maxRetries = 5;
     for ($retries = 0; $retries < $maxRetries; $retries++) {
         // be sure that unpublished objects in relations are saved also in frontend mode, eg. in importers, ...
         $hideUnpublishedBackup = self::getHideUnpublished();
         self::setHideUnpublished(false);
         $this->beginTransaction();
         try {
             if (!Tool::isValidKey($this->getKey()) && $this->getId() != 1) {
                 throw new \Exception("invalid key for object with id [ " . $this->getId() . " ] key is: [" . $this->getKey() . "]");
             }
             if (!in_array($this->getType(), self::$types)) {
                 throw new \Exception("invalid object type given: [" . $this->getType() . "]");
             }
             $this->correctPath();
             if (!$isUpdate) {
                 $this->getResource()->create();
             }
             // get the old path from the database before the update is done
             $oldPath = null;
             if ($isUpdate) {
                 $oldPath = $this->getResource()->getCurrentFullPath();
             }
             // if the old path is different from the new path, update all children
             // we need to do the update of the children's path before $this->update() because the
             // inheritance helper needs the correct paths of the children in InheritanceHelper::buildTree()
             $updatedChildren = array();
             if ($oldPath && $oldPath != $this->getFullPath()) {
                 $this->getResource()->updateWorkspaces();
                 $updatedChildren = $this->getResource()->updateChildsPaths($oldPath);
             }
             $this->update();
             self::setHideUnpublished($hideUnpublishedBackup);
             $this->commit();
             break;
             // transaction was successfully completed, so we cancel the loop here -> no restart required
         } catch (\Exception $e) {
             try {
                 $this->rollBack();
             } catch (\Exception $er) {
                 // PDO adapter throws exceptions if rollback fails
                 \Logger::info($er);
             }
             // set "HideUnpublished" back to the value it was originally
             self::setHideUnpublished($hideUnpublishedBackup);
             // we try to start the transaction $maxRetries times again (deadlocks, ...)
             if ($retries < $maxRetries - 1) {
                 $run = $retries + 1;
                 $waitTime = 100000;
                 // microseconds
                 \Logger::warn("Unable to finish transaction (" . $run . ". run) because of the following reason '" . $e->getMessage() . "'. --> Retrying in " . $waitTime . " microseconds ... (" . ($run + 1) . " of " . $maxRetries . ")");
                 usleep($waitTime);
                 // wait specified time until we restart the transaction
             } else {
                 // if the transaction still fail after $maxRetries retries, we throw out the exception
                 \Logger::error("Finally giving up restarting the same transaction again and again, last message: " . $e->getMessage());
                 throw $e;
             }
         }
     }
     $additionalTags = array();
     if (isset($updatedChildren) && is_array($updatedChildren)) {
         foreach ($updatedChildren as $objectId) {
             $tag = "object_" . $objectId;
             $additionalTags[] = $tag;
             // remove the child also from registry (internal cache) to avoid path inconsistencies during long running scripts, such as CLI
             \Zend_Registry::set($tag, null);
         }
     }
     $this->clearDependentCache($additionalTags);
     if ($isUpdate) {
         \Pimcore::getEventManager()->trigger("object.postUpdate", $this);
     } else {
         \Pimcore::getEventManager()->trigger("object.postAdd", $this);
     }
     return $this;
 }
Exemplo n.º 3
0
 /**
  * @throws \Exception
  */
 public function correctPath()
 {
     // set path
     if ($this->getId() != 1) {
         // not for the root node
         if ($this->getParentId() == $this->getId()) {
             throw new \Exception("ParentID and ID is identical, an element can't be the parent of itself.");
         }
         if ($this->getFilename() === '..' || $this->getFilename() === '.') {
             throw new \Exception('Cannot create asset called ".." or "."');
         }
         if (!Tool::isValidKey($this->getKey())) {
             throw new \Exception("invalid filename '" . $this->getKey() . "' for asset with id [ " . $this->getId() . " ]");
         }
         $parent = Asset::getById($this->getParentId());
         if ($parent) {
             // use the parent's path from the database here (getCurrentFullPath), to ensure the path really exists and does not rely on the path
             // that is currently in the parent object (in memory), because this might have changed but wasn't not saved
             $this->setPath(str_replace("//", "/", $parent->getCurrentFullPath() . "/"));
         } else {
             // parent document doesn't exist anymore, set the parent to to root
             $this->setParentId(1);
             $this->setPath("/");
         }
     } elseif ($this->getId() == 1) {
         // some data in root node should always be the same
         $this->setParentId(0);
         $this->setPath("/");
         $this->setFilename("");
         $this->setType("folder");
     }
     // do not allow PHP and .htaccess files
     if (preg_match("@\\.ph(p[345]?|t|tml|ps)\$@i", $this->getFilename()) || $this->getFilename() == ".htaccess") {
         $this->setFilename($this->getFilename() . ".txt");
     }
     if (Asset\Service::pathExists($this->getRealFullPath())) {
         $duplicate = Asset::getByPath($this->getRealFullPath());
         if ($duplicate instanceof Asset and $duplicate->getId() != $this->getId()) {
             throw new \Exception("Duplicate full path [ " . $this->getRealFullPath() . " ] - cannot save asset");
         }
     }
     if (strlen($this->getRealFullPath()) > 765) {
         throw new \Exception("Full path is limited to 765 characters, reduce the length of your parent's path");
     }
 }