vendor/pimcore/pimcore/models/Asset/Dao.php line 552

Open in your IDE?
  1. <?php
  2. /**
  3.  * Pimcore
  4.  *
  5.  * This source file is available under two different licenses:
  6.  * - GNU General Public License version 3 (GPLv3)
  7.  * - Pimcore Commercial License (PCL)
  8.  * Full copyright and license information is available in
  9.  * LICENSE.md which is distributed with this source code.
  10.  *
  11.  *  @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  12.  *  @license    http://www.pimcore.org/license     GPLv3 and PCL
  13.  */
  14. namespace Pimcore\Model\Asset;
  15. use Pimcore\Db\Helper;
  16. use Pimcore\Loader\ImplementationLoader\Exception\UnsupportedException;
  17. use Pimcore\Logger;
  18. use Pimcore\Model;
  19. use Pimcore\Model\Asset\MetaData\ClassDefinition\Data\Data;
  20. use Pimcore\Model\User;
  21. use Pimcore\Tool\Serialize;
  22. /**
  23.  * @internal
  24.  *
  25.  * @property \Pimcore\Model\Asset $model
  26.  */
  27. class Dao extends Model\Element\Dao
  28. {
  29.     use Model\Element\Traits\ScheduledTasksDaoTrait;
  30.     use Model\Element\Traits\VersionDaoTrait;
  31.     /**
  32.      * @internal
  33.      *
  34.      * @var array
  35.      */
  36.     public static array $thumbnailStatusCache = [];
  37.     /**
  38.      * Get the data for the object by id from database and assign it to the object (model)
  39.      *
  40.      * @param int $id
  41.      *
  42.      * @throws Model\Exception\NotFoundException
  43.      */
  44.     public function getById($id)
  45.     {
  46.         $data $this->db->fetchAssociative("SELECT assets.*, tree_locks.locked FROM assets
  47.             LEFT JOIN tree_locks ON assets.id = tree_locks.id AND tree_locks.type = 'asset'
  48.                 WHERE assets.id = ?", [$id]);
  49.         if (!empty($data['id'])) {
  50.             $this->assignVariablesToModel($data);
  51.             if ($data['hasMetaData']) {
  52.                 $metadataRaw $this->db->fetchAllAssociative('SELECT * FROM assets_metadata WHERE cid = ?', [$data['id']]);
  53.                 $metadata = [];
  54.                 foreach ($metadataRaw as $md) {
  55.                     $loader \Pimcore::getContainer()->get('pimcore.implementation_loader.asset.metadata.data');
  56.                     $transformedData $md['data'];
  57.                     $md['type'] = $md['type'] ?? 'input';
  58.                     try {
  59.                         /** @var Data $instance */
  60.                         $instance $loader->build($md['type']);
  61.                         $transformedData $instance->getDataFromResource($md['data'], $md);
  62.                     } catch (UnsupportedException $e) {
  63.                     }
  64.                     $md['data'] = $transformedData;
  65.                     unset($md['cid']);
  66.                     $metadata[] = $md;
  67.                 }
  68.                 $this->model->setMetadataRaw($metadata);
  69.             }
  70.         } else {
  71.             throw new Model\Exception\NotFoundException('Asset with ID ' $id " doesn't exists");
  72.         }
  73.     }
  74.     /**
  75.      * Get the data for the asset from database for the given path
  76.      *
  77.      * @param string $path
  78.      *
  79.      * @throws Model\Exception\NotFoundException
  80.      */
  81.     public function getByPath($path)
  82.     {
  83.         $params $this->extractKeyAndPath($path);
  84.         $data $this->db->fetchAssociative('SELECT id FROM assets WHERE path = :path AND `filename` = :key'$params);
  85.         if (!empty($data['id'])) {
  86.             $this->assignVariablesToModel($data);
  87.         } else {
  88.             throw new Model\Exception\NotFoundException('asset with path: ' $path " doesn't exist");
  89.         }
  90.     }
  91.     public function create()
  92.     {
  93.         $this->db->insert('assets', [
  94.             'filename' => $this->model->getFilename(),
  95.             'path' => $this->model->getRealPath(),
  96.             'parentId' => $this->model->getParentId(),
  97.         ]);
  98.         $this->model->setId((int) $this->db->lastInsertId());
  99.     }
  100.     public function update()
  101.     {
  102.         $asset $this->model->getObjectVars();
  103.         foreach ($asset as $key => $value) {
  104.             if (in_array($key$this->getValidTableColumns('assets'))) {
  105.                 if (is_array($value)) {
  106.                     $value Serialize::serialize($value);
  107.                 }
  108.                 $data[$key] = $value;
  109.             }
  110.         }
  111.         // metadata
  112.         $dataExists $this->db->fetchOne('SELECT `name` FROM assets_metadata WHERE cid = ? LIMIT 1', [$this->model->getId()]);
  113.         if ($dataExists) {
  114.             $this->db->delete('assets_metadata', ['cid' => $this->model->getId()]);
  115.         }
  116.         /** @var array $metadata */
  117.         $metadata $this->model->getMetadata(nullnullfalsetrue);
  118.         $data['hasMetaData'] = 0;
  119.         $metadataItems = [];
  120.         if (!empty($metadata)) {
  121.             foreach ($metadata as $metadataItem) {
  122.                 $metadataItem['cid'] = $this->model->getId();
  123.                 unset($metadataItem['config']);
  124.                 $loader \Pimcore::getContainer()->get('pimcore.implementation_loader.asset.metadata.data');
  125.                 $dataForResource $metadataItem['data'];
  126.                 try {
  127.                     /** @var Data $instance */
  128.                     $instance $loader->build($metadataItem['type']);
  129.                     $dataForResource $instance->getDataForResource($metadataItem['data'], $metadataItem);
  130.                 } catch (UnsupportedException $e) {
  131.                 }
  132.                 $metadataItem['data'] = $dataForResource;
  133.                 $metadataItem['language'] = (string) $metadataItem['language']; // language column cannot be NULL -> see SQL schema
  134.                 if (is_scalar($metadataItem['data'])) {
  135.                     $data['hasMetaData'] = 1;
  136.                     $metadataItems[] = $metadataItem;
  137.                 }
  138.             }
  139.         }
  140.         Helper::insertOrUpdate($this->db'assets'$data);
  141.         if ($data['hasMetaData'] && count($metadataItems)) {
  142.             foreach ($metadataItems as $metadataItem) {
  143.                 $this->db->insert('assets_metadata'$metadataItem);
  144.             }
  145.         }
  146.         // tree_locks
  147.         $this->db->delete('tree_locks', ['id' => $this->model->getId(), 'type' => 'asset']);
  148.         if ($this->model->getLocked()) {
  149.             $this->db->insert('tree_locks', [
  150.                 'id' => $this->model->getId(),
  151.                 'type' => 'asset',
  152.                 'locked' => $this->model->getLocked(),
  153.             ]);
  154.         }
  155.     }
  156.     public function delete()
  157.     {
  158.         $this->db->delete('assets', ['id' => $this->model->getId()]);
  159.     }
  160.     public function updateWorkspaces()
  161.     {
  162.         $this->db->update('users_workspaces_asset', [
  163.             'cpath' => $this->model->getRealFullPath(),
  164.         ], [
  165.             'cid' => $this->model->getId(),
  166.         ]);
  167.     }
  168.     /**
  169.      * @internal
  170.      *
  171.      * @param string $oldPath
  172.      *
  173.      * @return array
  174.      */
  175.     public function updateChildPaths($oldPath)
  176.     {
  177.         //get assets to empty their cache
  178.         $assets $this->db->fetchFirstColumn('SELECT id FROM assets WHERE path LIKE ' $this->db->quote(Helper::escapeLike($oldPath) . '%'));
  179.         $userId '0';
  180.         if ($user \Pimcore\Tool\Admin::getCurrentUser()) {
  181.             $userId $user->getId();
  182.         }
  183.         //update assets child paths
  184.         // we don't update the modification date here, as this can have side-effects when there's an unpublished version for an element
  185.         $this->db->executeQuery('update assets set path = replace(path,' $this->db->quote($oldPath '/') . ',' $this->db->quote($this->model->getRealFullPath() . '/') . "), userModification = '" $userId "' where path like " $this->db->quote(Helper::escapeLike($oldPath) . '/%') . ';');
  186.         //update assets child permission paths
  187.         $this->db->executeQuery('update users_workspaces_asset set cpath = replace(cpath,' $this->db->quote($oldPath '/') . ',' $this->db->quote($this->model->getRealFullPath() . '/') . ') where cpath like ' $this->db->quote(Helper::escapeLike($oldPath) . '/%') . ';');
  188.         //update assets child properties paths
  189.         $this->db->executeQuery('update properties set cpath = replace(cpath,' $this->db->quote($oldPath '/') . ',' $this->db->quote($this->model->getRealFullPath() . '/') . ') where cpath like ' $this->db->quote(Helper::escapeLike($oldPath) . '/%') . ';');
  190.         return $assets;
  191.     }
  192.     /**
  193.      * Get the properties for the object from database and assign it
  194.      *
  195.      * @param bool $onlyInherited
  196.      *
  197.      * @return array
  198.      */
  199.     public function getProperties($onlyInherited false)
  200.     {
  201.         $properties = [];
  202.         // collect properties via parent - ids
  203.         $parentIds $this->getParentIds();
  204.         $propertiesRaw $this->db->fetchAllAssociative('SELECT * FROM properties WHERE ((cid IN (' implode(','$parentIds) . ") AND inheritable = 1) OR cid = ? )  AND ctype='asset'", [$this->model->getId()]);
  205.         // because this should be faster than mysql
  206.         usort($propertiesRaw, function ($left$right) {
  207.             return strcmp($left['cpath'], $right['cpath']);
  208.         });
  209.         foreach ($propertiesRaw as $propertyRaw) {
  210.             try {
  211.                 $property = new Model\Property();
  212.                 $property->setType($propertyRaw['type']);
  213.                 $property->setCid($this->model->getId());
  214.                 $property->setName($propertyRaw['name']);
  215.                 $property->setCtype('asset');
  216.                 $property->setDataFromResource($propertyRaw['data']);
  217.                 $property->setInherited(true);
  218.                 if ($propertyRaw['cid'] == $this->model->getId()) {
  219.                     $property->setInherited(false);
  220.                 }
  221.                 $property->setInheritable(false);
  222.                 if ($propertyRaw['inheritable']) {
  223.                     $property->setInheritable(true);
  224.                 }
  225.                 if ($onlyInherited && !$property->getInherited()) {
  226.                     continue;
  227.                 }
  228.                 $properties[$propertyRaw['name']] = $property;
  229.             } catch (\Exception $e) {
  230.                 Logger::error("can't add property " $propertyRaw['name'] . ' to asset ' $this->model->getRealFullPath());
  231.             }
  232.         }
  233.         // if only inherited then only return it and dont call the setter in the model
  234.         if ($onlyInherited) {
  235.             return $properties;
  236.         }
  237.         $this->model->setProperties($properties);
  238.         return $properties;
  239.     }
  240.     /**
  241.      * deletes all properties for the object from database
  242.      */
  243.     public function deleteAllProperties()
  244.     {
  245.         $this->db->delete('properties', ['cid' => $this->model->getId(), 'ctype' => 'asset']);
  246.     }
  247.     /**
  248.      * @return string|null retrieves the current full set path from DB
  249.      */
  250.     public function getCurrentFullPath()
  251.     {
  252.         $path null;
  253.         try {
  254.             $path $this->db->fetchOne('SELECT CONCAT(path,filename) as path FROM assets WHERE id = ?', [$this->model->getId()]);
  255.         } catch (\Exception $e) {
  256.             Logger::error('could not get  current asset path from DB');
  257.         }
  258.         return $path;
  259.     }
  260.     /**
  261.      * @return int
  262.      */
  263.     public function getVersionCountForUpdate(): int
  264.     {
  265.         if (!$this->model->getId()) {
  266.             return 0;
  267.         }
  268.         $versionCount = (int) $this->db->fetchOne('SELECT versionCount FROM assets WHERE id = ? FOR UPDATE', [$this->model->getId()]);
  269.         if (!$this->model instanceof Folder) {
  270.             $versionCount2 = (int) $this->db->fetchOne("SELECT MAX(versionCount) FROM versions WHERE cid = ? AND ctype = 'asset'", [$this->model->getId()]);
  271.             $versionCount max($versionCount$versionCount2);
  272.         }
  273.         return (int) $versionCount;
  274.     }
  275.     /**
  276.      * quick test if there are children
  277.      *
  278.      * @param Model\User $user
  279.      *
  280.      * @return bool
  281.      */
  282.     public function hasChildren($user null)
  283.     {
  284.         if (!$this->model->getId()) {
  285.             return false;
  286.         }
  287.         $query 'SELECT `a`.`id` FROM `assets` a  WHERE parentId = ? ';
  288.         if ($user && !$user->isAdmin()) {
  289.             $userIds $user->getRoles();
  290.             $currentUserId $user->getId();
  291.             $userIds[] = $currentUserId;
  292.             $inheritedPermission $this->isInheritingPermission('list'$userIds);
  293.             $anyAllowedRowOrChildren 'EXISTS(SELECT list FROM users_workspaces_asset uwa WHERE userId IN (' implode(','$userIds) . ') AND list=1 AND LOCATE(CONCAT(path,filename),cpath)=1 AND
  294.                 NOT EXISTS(SELECT list FROM users_workspaces_asset WHERE userId =' $currentUserId '  AND list=0 AND cpath = uwa.cpath))';
  295.             $isDisallowedCurrentRow 'EXISTS(SELECT list FROM users_workspaces_asset WHERE userId IN (' implode(','$userIds) . ')  AND cid = id AND list=0)';
  296.             $query .= ' AND IF(' $anyAllowedRowOrChildren ',1,IF(' $inheritedPermission ', ' $isDisallowedCurrentRow ' = 0, 0)) = 1';
  297.         }
  298.         $query .= ' LIMIT 1;';
  299.         $c $this->db->fetchOne($query, [$this->model->getId()]);
  300.         return (bool)$c;
  301.     }
  302.     /**
  303.      * Quick test if there are siblings
  304.      *
  305.      * @return bool
  306.      */
  307.     public function hasSiblings()
  308.     {
  309.         if (!$this->model->getParentId()) {
  310.             return false;
  311.         }
  312.         $sql 'SELECT 1 FROM assets WHERE parentId = ?';
  313.         $params = [$this->model->getParentId()];
  314.         if ($this->model->getId()) {
  315.             $sql .= ' AND id != ?';
  316.             $params[] = $this->model->getId();
  317.         }
  318.         $sql .= ' LIMIT 1';
  319.         $c $this->db->fetchOne($sql$params);
  320.         return (bool)$c;
  321.     }
  322.     /**
  323.      * returns the amount of directly children (not recursivly)
  324.      *
  325.      * @param Model\User $user
  326.      *
  327.      * @return int
  328.      */
  329.     public function getChildAmount($user null)
  330.     {
  331.         if (!$this->model->getId()) {
  332.             return 0;
  333.         }
  334.         $query 'SELECT COUNT(*) AS count FROM assets WHERE parentId = ?';
  335.         if ($user && !$user->isAdmin()) {
  336.             $userIds $user->getRoles();
  337.             $currentUserId $user->getId();
  338.             $userIds[] = $currentUserId;
  339.             $inheritedPermission $this->isInheritingPermission('list'$userIds);
  340.             $anyAllowedRowOrChildren 'EXISTS(SELECT list FROM users_workspaces_asset uwa WHERE userId IN (' implode(','$userIds) . ') AND list=1 AND LOCATE(CONCAT(path,filename),cpath)=1 AND
  341.                 NOT EXISTS(SELECT list FROM users_workspaces_asset WHERE userId =' $currentUserId '  AND list=0 AND cpath = uwa.cpath))';
  342.             $isDisallowedCurrentRow 'EXISTS(SELECT list FROM users_workspaces_asset WHERE userId IN (' implode(','$userIds) . ')  AND cid = id AND list=0)';
  343.             $query .= ' AND IF(' $anyAllowedRowOrChildren ',1,IF(' $inheritedPermission ', ' $isDisallowedCurrentRow ' = 0, 0)) = 1';
  344.         }
  345.         return (int) $this->db->fetchOne($query, [$this->model->getId()]);
  346.     }
  347.     /**
  348.      * @return bool
  349.      */
  350.     public function isLocked()
  351.     {
  352.         // check for an locked element below this element
  353.         $belowLocks $this->db->fetchOne("SELECT tree_locks.id FROM tree_locks INNER JOIN assets ON tree_locks.id = assets.id WHERE assets.path LIKE ? AND tree_locks.type = 'asset' AND tree_locks.locked IS NOT NULL AND tree_locks.locked != '' LIMIT 1", [Helper::escapeLike($this->model->getRealFullPath()) . '/%']);
  354.         if ($belowLocks 0) {
  355.             return true;
  356.         }
  357.         $parentIds $this->getParentIds();
  358.         $inhertitedLocks $this->db->fetchOne('SELECT id FROM tree_locks WHERE id IN (' implode(','$parentIds) . ") AND type='asset' AND locked = 'propagate' LIMIT 1");
  359.         if ($inhertitedLocks 0) {
  360.             return true;
  361.         }
  362.         return false;
  363.     }
  364.     /**
  365.      * @return array
  366.      */
  367.     public function unlockPropagate()
  368.     {
  369.         $lockIds $this->db->fetchFirstColumn('SELECT id from assets WHERE path LIKE ' $this->db->quote(Helper::escapeLike($this->model->getRealFullPath()) . '/%') . ' OR id = ' $this->model->getId());
  370.         $this->db->executeQuery("DELETE FROM tree_locks WHERE type = 'asset' AND id IN (" implode(','$lockIds) . ')');
  371.         return $lockIds;
  372.     }
  373.     /**
  374.      * @param string $type
  375.      * @param array $userIds
  376.      *
  377.      * @return int
  378.      *
  379.      * @throws \Doctrine\DBAL\Exception
  380.      */
  381.     public function isInheritingPermission(string $type, array $userIds): int
  382.     {
  383.         return $this->InheritingPermission($type$userIds'asset');
  384.     }
  385.     /**
  386.      * @param string $type
  387.      * @param Model\User $user
  388.      *
  389.      * @return bool
  390.      */
  391.     public function isAllowed($type$user)
  392.     {
  393.         // collect properties via parent - ids
  394.         $parentIds = [1];
  395.         $obj $this->model->getParent();
  396.         if ($obj) {
  397.             while ($obj) {
  398.                 $parentIds[] = $obj->getId();
  399.                 $obj $obj->getParent();
  400.             }
  401.         }
  402.         if ($id $this->model->getId()) {
  403.             $parentIds[] = $id;
  404.         }
  405.         $userIds $user->getRoles();
  406.         $userIds[] = $user->getId();
  407.         try {
  408.             $permissionsParent $this->db->fetchOne('SELECT ' $this->db->quoteIdentifier($type) . ' FROM users_workspaces_asset WHERE cid IN (' implode(','$parentIds) . ') AND userId IN (' implode(','$userIds) . ') ORDER BY LENGTH(cpath) DESC, FIELD(userId, ' $user->getId() . ') DESC, ' $this->db->quoteIdentifier($type) . ' DESC  LIMIT 1');
  409.             if ($permissionsParent) {
  410.                 return true;
  411.             }
  412.             // exception for list permission
  413.             if (empty($permissionsParent) && $type == 'list') {
  414.                 // check for children with permissions
  415.                 $path $this->model->getRealFullPath() . '/';
  416.                 if ($this->model->getId() == 1) {
  417.                     $path '/';
  418.                 }
  419.                 $permissionsChildren $this->db->fetchOne('SELECT list FROM users_workspaces_asset WHERE cpath LIKE ? AND userId IN (' implode(','$userIds) . ') AND list = 1 LIMIT 1', [Helper::escapeLike($path) . '%']);
  420.                 if ($permissionsChildren) {
  421.                     return true;
  422.                 }
  423.             }
  424.         } catch (\Exception $e) {
  425.             Logger::warn('Unable to get permission ' $type ' for asset ' $this->model->getId());
  426.         }
  427.         return false;
  428.     }
  429.     /**
  430.      * @param array $columns
  431.      * @param User $user
  432.      *
  433.      * @return array<string, int>
  434.      *
  435.      */
  436.     public function areAllowed(array $columnsUser $user)
  437.     {
  438.         return $this->permissionByTypes($columns$user'asset');
  439.     }
  440.     public function updateCustomSettings()
  441.     {
  442.         $customSettingsData Serialize::serialize($this->model->getCustomSettings());
  443.         $this->db->update('assets', ['customSettings' => $customSettingsData], ['id' => $this->model->getId()]);
  444.     }
  445.     /**
  446.      * @return bool
  447.      */
  448.     public function __isBasedOnLatestData()
  449.     {
  450.         $data $this->db->fetchAssociative('SELECT modificationDate, versionCount from assets WHERE id = ?', [$this->model->getId()]);
  451.         if ($data['modificationDate'] == $this->model->__getDataVersionTimestamp() && $data['versionCount'] == $this->model->getVersionCount()) {
  452.             return true;
  453.         }
  454.         return false;
  455.     }
  456.     public function addToThumbnailCache(string $namestring $filenameint $filesizeint $widthint $height): void
  457.     {
  458.         $assetId $this->model->getId();
  459.         $thumb = [
  460.             'cid' => $assetId,
  461.             'name' => $name,
  462.             'filename' => $filename,
  463.             'modificationDate' => time(),
  464.             'filesize' => $filesize,
  465.             'width' => $width,
  466.             'height' => $height,
  467.         ];
  468.         Helper::insertOrUpdate($this->db'assets_image_thumbnail_cache'$thumb);
  469.         if (isset(self::$thumbnailStatusCache[$assetId])) {
  470.             $hash $name $filename;
  471.             self::$thumbnailStatusCache[$assetId][$hash] = $thumb;
  472.         }
  473.     }
  474.     public function getCachedThumbnailModificationDate(string $namestring $filename): ?int
  475.     {
  476.         return $this->getCachedThumbnail($name$filename)['modificationDate'] ?? null;
  477.     }
  478.     public function getCachedThumbnail(string $namestring $filename): ?array
  479.     {
  480.         $assetId $this->model->getId();
  481.         // we use a static var here, because it could be that an asset is serialized in the cache,
  482.         // so this runtime cache wouldn't be as efficient
  483.         if (!isset(self::$thumbnailStatusCache[$assetId])) {
  484.             self::$thumbnailStatusCache[$assetId] = [];
  485.             $thumbs $this->db->fetchAllAssociative('SELECT * FROM assets_image_thumbnail_cache WHERE cid = :cid', [
  486.                 'cid' => $this->model->getId(),
  487.             ]);
  488.             foreach ($thumbs as $thumb) {
  489.                 $hash $thumb['name'] . $thumb['filename'];
  490.                 self::$thumbnailStatusCache[$assetId][$hash] = $thumb;
  491.             }
  492.         }
  493.         $hash $name $filename;
  494.         return self::$thumbnailStatusCache[$assetId][$hash] ?? null;
  495.     }
  496.     public function deleteFromThumbnailCache(?string $name null, ?string $filename null): void
  497.     {
  498.         $assetId $this->model->getId();
  499.         $where = [
  500.             'cid' => $assetId,
  501.         ];
  502.         if ($name) {
  503.             $where['name'] = $name;
  504.         }
  505.         if ($filename) {
  506.             $where['filename'] = $filename;
  507.         }
  508.         $this->db->delete('assets_image_thumbnail_cache'$where);
  509.         unset(self::$thumbnailStatusCache[$assetId]);
  510.     }
  511. }