uawdijnntqw1x1x1
IP : 3.128.190.174
Hostname : axolotl
Kernel : Linux axolotl 4.9.0-13-amd64 #1 SMP Debian 4.9.228-1 (2020-07-05) x86_64
Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,
OS : Linux
PATH:
/
var
/
www
/
axolotl
/
data
/
www
/
samara.axolotls.ru
/
bitrix
/
modules
/
disk
/
lib
/
baseobject.php
/
/
<?php namespace Bitrix\Disk; use Bitrix\Disk\Internals\Entity\ModelSynchronizer; use Bitrix\Disk\Internals\Error\Error; use Bitrix\Disk\Internals\ObjectPathTable; use Bitrix\Disk\Internals\ObjectTable; use Bitrix\Disk\Internals\SharingTable; use Bitrix\Disk\Security\SecurityContext; use Bitrix\Disk\Ui\Avatar; use Bitrix\Main\Application; use Bitrix\Main\ArgumentTypeException; use Bitrix\Main\Entity\AddResult; use Bitrix\Main\Entity\Result; use Bitrix\Main\Event; use Bitrix\Main\InvalidOperationException; use Bitrix\Main\Loader; use Bitrix\Main\Localization\Loc; use Bitrix\Main\Type\Collection; use Bitrix\Main\Type\DateTime; use Bitrix\Main\UserTable; Loc::loadMessages(__FILE__); abstract class BaseObject extends Internals\Model implements \JsonSerializable { const ERROR_NON_UNIQUE_NAME = 'DISK_OBJ_22000'; const ERROR_RESTORE_UNDER_LINK_WRONG_TYPE = 'DISK_OBJ_22001'; /** @var string */ protected $name; /** @var string */ protected $label; /** @var string */ protected $code; /** @var string */ protected $xmlId; /** @var int */ protected $storageId; /** @var Storage */ protected $storage; /** @var int */ protected $type; /** @var int */ protected $realObjectId; /** @var BaseObject */ protected $realObject; /** @var int */ protected $parentId; /** @var Folder */ protected $parent; /** @var Document\CloudImport\Entry */ protected $lastCloudImport; /** @var string */ protected $contentProvider; /** @var int */ protected $deletedType; /** @var ObjectLock */ protected $lock; /** @var ObjectTtl */ protected $ttl; /** @var DateTime */ protected $createTime; /** @var DateTime */ protected $updateTime; /** @var DateTime */ protected $syncUpdateTime; /** @var DateTime */ protected $deleteTime; /** @var int */ protected $createdBy; /** @var User */ protected $createUser; /** @var int */ protected $updatedBy; /** @var User */ protected $updateUser; /** @var int */ protected $deletedBy; /** @var User */ protected $deleteUser; /** * Returns the fully qualified name of table class which belongs to current model. * @throws \Bitrix\Main\NotImplementedException * @return string */ public static function getTableClassName() { return ObjectTable::className(); } /** * Checks rights to change rights on current object. * @param SecurityContext $securityContext Security context. * @return bool */ public function canChangeRights(SecurityContext $securityContext) { return $securityContext->canChangeRights($this->id); } /** * Checks rights to delete current object. * @param SecurityContext $securityContext Security context. * @return bool */ public function canDelete(SecurityContext $securityContext) { return $securityContext->canDelete($this->id); } /** * Checks rights to mark deleted current object. * @param SecurityContext $securityContext Security context. * @return bool */ public function canMarkDeleted(SecurityContext $securityContext) { return $securityContext->canMarkDeleted($this->id); } /** * Checks rights to move current object in new destination object. * @param SecurityContext $securityContext Security context. * @param BaseObject $targetObject New destination object. * @return bool */ public function canMove(SecurityContext $securityContext, BaseObject $targetObject) { return $securityContext->canMove($this->id, $targetObject->getRealObjectId()); } /** * Checks rights to read current object. * @param SecurityContext $securityContext Security context. * @return bool */ public function canRead(SecurityContext $securityContext) { return $securityContext->canRead($this->id); } /** * Checks rights to rename current object. * @param SecurityContext $securityContext Security context. * @return bool */ public function canRename(SecurityContext $securityContext) { return $securityContext->canRename($this->id); } /** * Checks rights to restore current object from trash can. * @param SecurityContext $securityContext Security context. * @return bool */ public function canRestore(SecurityContext $securityContext) { return $securityContext->canRestore($this->id) || ( $securityContext->canMarkDeleted($this->id) && $this->deletedBy == $securityContext->getUserId() && $this->deletedBy ) ; } /** * Checks rights to share current object. * @param SecurityContext $securityContext Security context. * @return bool */ public function canShare(SecurityContext $securityContext) { return $securityContext->canShare($this->id); } /** * Checks rights to update (content) current object. * @param SecurityContext $securityContext Security context. * @return bool */ public function canUpdate(SecurityContext $securityContext) { return $securityContext->canUpdate($this->id); } /** * Checks rights to update current object by cloud import. * @param SecurityContext $securityContext Security context. * @return bool */ public function canUpdateByCloudImport(SecurityContext $securityContext) { return $this->getContentProvider() && $this->getCreatedBy() == $securityContext->getUserId() && $securityContext->canUpdate($this->id) ; } /** * Checks rights to lock (content) the object. * @param SecurityContext $securityContext Security context. * @return bool */ public function canLock(SecurityContext $securityContext) { return $securityContext->canUpdate($this->id); } /** * Checks rights to unlock (content) the object. * @param SecurityContext $securityContext Security context. * @return bool */ public function canUnlock(SecurityContext $securityContext) { return $securityContext->canUpdate($this->id); } /** * Returns time of create object. * @return DateTime */ public function getCreateTime() { return $this->createTime; } /** * Returns id of user, who created object. * @return int */ public function getCreatedBy() { return $this->createdBy; } /** * Returns user model, who created object. * @return User */ public function getCreateUser() { if($this->isLoadedAttribute('createUser') && $this->createUser && $this->createdBy == $this->createUser->getId()) { return $this->createUser; } $this->createUser = User::getModelForReferenceField($this->createdBy, $this->createUser); $this->setAsLoadedAttribute('createUser'); return $this->createUser; } /** * Returns time of delete object. * @return DateTime */ public function getDeleteTime() { return $this->deleteTime; } /** * Returns id of user, who deleted object. * @return int */ public function getDeletedBy() { return $this->deletedBy; } /** * Returns deleted type (@see ObjectTable). * @return int */ public function getDeletedType() { return $this->deletedType; } /** * Returns user model, who deleted object. * @return User */ public function getDeleteUser() { if($this->isLoadedAttribute('deleteUser') && $this->deleteUser && $this->deletedBy == $this->deleteUser->getId()) { return $this->deleteUser; } $this->deleteUser = User::getModelForReferenceField($this->deletedBy, $this->deleteUser); $this->setAsLoadedAttribute('deleteUser'); return $this->deleteUser; } /** * Returns real object of object. * * For example if object is link (@see FolderLink, @see FileLink), then method returns original object. * @return BaseObject|Folder|File */ public function getRealObject() { if(!$this->isLink()) { return $this; } if($this->isLoadedAttribute('realObject') && $this->realObject && $this->realObjectId === $this->realObject->getId()) { return $this->realObject; } $this->realObject = BaseObject::loadById($this->realObjectId); $this->setAsLoadedAttribute('realObject'); return $this->realObject; } /** * Returns id of real object of object. * * For example if object is link (@see FolderLink, @see FileLink), then method returns id of original object. * @return int */ public function getRealObjectId() { return $this->isLink()? $this->realObjectId : $this->id; } /** * Returns true if object is moved in trash can. * @return boolean */ public function isDeleted() { return $this->deletedType != ObjectTable::DELETED_TYPE_NONE; } /** * Returns name of object. * Be careful: the method returns name without possible trash can suffix. * @return string */ public function getName() { if($this->label === null) { $this->label = $this->getNameWithoutTrashCanSuffix(); } return $this->label; } /** * Returns original name of object. * May contains trash can suffix. * @return string */ public function getOriginalName() { return $this->name; } protected function getNameWithTrashCanSuffix() { return Ui\Text::appendTrashCanSuffix($this->name); } protected function getNameWithoutTrashCanSuffix() { return Ui\Text::cleanTrashCanSuffix($this->name); } /** * Returns code of object. * Code is used for working with object by symbolic name. * @return string */ public function getCode() { return $this->code; } /** * Returns xml id of object. * @return string */ public function getXmlId() { return $this->xmlId; } /** * Returns id of parent object. * If object is root, then method returns null. * @return int */ public function getParentId() { return $this->parentId; } /** * Returns content provider of object. * Content provider determines service which provided object (for ex. Dropbox). * @return string */ public function getContentProvider() { return $this->contentProvider; } /** * Returns id of storage. * @return int */ public function getStorageId() { return $this->storageId; } /** * Returns storage model. * @return Storage|null */ public function getStorage() { if(!$this->storageId) { return null; } if($this->isLoadedAttribute('storage') && $this->storage && $this->storageId == $this->storage->getId()) { return $this->storage; } $this->storage = Storage::loadById($this->storageId, array('ROOT_OBJECT')); $this->setAsLoadedAttribute('storage'); return $this->storage; } /** * Returns type (folder or file). * @see ObjectTable::TYPE_FOLDER, ObjectTable::TYPE_FILE. * @return int */ public function getType() { return $this->type; } /** * Returns size in bytes. * * @param null $filter * * @return int */ abstract public function getSize($filter = null); /** * Returns time of update object. If not set returns create time. * @return DateTime */ public function getUpdateTime() { return $this->updateTime?: $this->createTime; } /** * Returns sync time object. * @return DateTime */ public function getSyncUpdateTime() { return $this->syncUpdateTime; } /** * Returns id of user, who updated object. * @return int */ public function getUpdatedBy() { return $this->updatedBy; } /** * Returns user model, who created object. * @return User */ public function getUpdateUser() { if($this->isLoadedAttribute('updateUser') && $this->updateUser && $this->updatedBy == $this->updateUser->getId()) { return $this->updateUser; } $this->updateUser = User::getModelForReferenceField($this->updatedBy, $this->updateUser); $this->setAsLoadedAttribute('updateUser'); return $this->updateUser; } /** * Adds external link. * @param array $data Data to create new external link (@see ExternalLink). * @return bool|ExternalLink * @throws \Bitrix\Main\ArgumentException */ public function addExternalLink(array $data) { $this->errorCollection->clear(); $data['OBJECT_ID'] = $this->id; return ExternalLink::add($data, $this->errorCollection); } /** * Returns external links by file. * @param array $parameters Parameters. * @return ExternalLink[] */ public function getExternalLinks(array $parameters = array()) { if (!isset($parameters['filter'])) { $parameters['filter'] = array(); } $parameters['filter']['OBJECT_ID'] = $this->id; if (!isset($parameters['order'])) { $parameters['order'] = array( 'CREATE_TIME' => 'DESC', ); } return ExternalLink::getModelList($parameters); } /** * Tells if object is a link on another object. * @return bool */ public function isLink() { return isset($this->realObjectId) && $this->realObjectId != $this->id; } /** * Returns last cloud import entry of object. * @see Document\CloudImport\ImportManager, @see Document\CloudImport\Entry. * @return Document\CloudImport\Entry| */ public function getLastCloudImportEntry() { if($this->lastCloudImport === null) { $lastCloudImport = Document\CloudImport\Entry::getModelList(array( 'filter' => array( 'OBJECT_ID' => $this->getRealObjectId(), ), 'order' => array( 'ID' => 'DESC', ), 'limit' => 1, )); if(!$lastCloudImport) { return null; } $this->lastCloudImport = array_pop($lastCloudImport); } return $this->lastCloudImport; } /** * Renames object. * @param string $newName New name. * @param bool $generateUniqueName Generates unique name for object in directory. * @return bool * @internal */ public function renameInternal($newName, $generateUniqueName = false) { $this->errorCollection->clear(); if(!$newName) { $this->errorCollection->addOne(new Error('Empty name.')); return false; } if($this->name === $newName) { return true; } if($generateUniqueName) { $newName = $this->generateUniqueName($newName, $this->getParentId()); } $opponentId = null; if(!$this->isUniqueName($newName, $this->parentId, null, $opponentId)) { if($opponentId != $this->id) { $this->errorCollection->add(array(new Error(Loc::getMessage('DISK_OBJECT_MODEL_ERROR_NON_UNIQUE_NAME'), self::ERROR_NON_UNIQUE_NAME))); return false; } } $oldName = $this->name; $success = $this->update(array('NAME' => $newName, 'SYNC_UPDATE_TIME' => new DateTime())); if(!$success) { return false; } $this->label = null; Driver::getInstance()->getIndexManager()->changeName($this); Driver::getInstance()->sendChangeStatusToSubscribers($this, 'quick'); $event = new Event(Driver::INTERNAL_MODULE_ID, "onAfterRenameObject", array($this, $oldName, $newName)); $event->send(); return true; } /** * Renames object. * @param string $newName New name. * @return bool */ public function rename($newName) { $success = $this->renameInternal($newName, false); if($success) { $this->changeParentUpdateTime(); } return $success; } /** * Changes field update time of parent. * @param DateTime|null $datetime Datetime. * @return bool */ protected function changeParentUpdateTime(DateTime $datetime = null, $updatedBy = null) { $parent = $this->getParent(); if (!$parent) { return false; } $data = [ 'UPDATE_TIME' => $datetime ? : new DateTime(), ]; if ($updatedBy) { $data['UPDATED_BY'] = $updatedBy; } return $parent->update($data); } /** * Changes field update time. * @param DateTime|null $datetime Datetime. * @return bool */ protected function changeSelfUpdateTime(DateTime $datetime = null) { return $this->update(array( 'UPDATE_TIME' => $datetime?: new DateTime(), )); } /** * Changes xml id on current element. * @param string $newXmlId New xml id. * @return bool */ public function changeXmlId($newXmlId) { return $this->update(array('XML_ID' => $newXmlId)); } /** * Changes code on current element. * @param string $newCode New code. * @return bool */ public function changeCode($newCode) { return $this->update(array('CODE' => $newCode)); } /** * Copies object to target folder. * @param Folder $targetFolder Target folder. * @param int $updatedBy Id of user. * @param bool $generateUniqueName Generates unique name for object in directory. * @return BaseObject|null */ abstract public function copyTo(Folder $targetFolder, $updatedBy, $generateUniqueName = false); /** * Moves object to another folder. * Support cross-storage move (mark deleted + create new) * @param Folder $folder Destination folder. * @param int $movedBy User id of user, which move file. * @param bool $generateUniqueName Generates unique name for object if in destination directory. * @return BaseObject|null */ public function moveTo(Folder $folder, $movedBy, $generateUniqueName = false) { $this->errorCollection->clear(); if($this->getId() == $folder->getId()) { return $this; } if (!$this->getParentId() && $this instanceof Folder) { return $this; } $realStorageIdSource = $this->getStorageId(); $realStorageIdTarget = $folder->getRealObject()->getStorageId(); $realFolderId = $folder->getRealObject()->getId(); if($this->getParentId() == $realFolderId) { return $this; } $ancestors = ObjectPathTable::getAncestors($folder->getId()); if (in_array($this->getRealObjectId(), array_column($ancestors, 'PARENT_ID'), true)) { $this->errorCollection[] = new Error(Loc::getMessage('DISK_OBJECT_MODEL_INVALID_MOVEMENT_TO_CHILD')); return null; } $possibleNewName = $this->name; if($generateUniqueName) { $possibleNewName = $this->generateUniqueName($this->name, $realFolderId); } $needToRename = $possibleNewName != $this->name; if(!$this->isUniqueName($possibleNewName, $realFolderId)) { $this->errorCollection->add(array(new Error(Loc::getMessage('DISK_OBJECT_MODEL_ERROR_NON_UNIQUE_NAME'), self::ERROR_NON_UNIQUE_NAME))); return null; } $this->name = $possibleNewName; if($needToRename) { $successUpdate = $this->update(array( 'NAME' => $possibleNewName )); if(!$successUpdate) { return null; } } //simple move if($realStorageIdSource == $realStorageIdTarget) { $object = $this->moveInSameStorage($folder, $movedBy); } else { $object = $this->moveInAnotherStorage($folder, $movedBy); } if($object !== null) { $folder->changeSelfUpdateTime(); $event = new Event(Driver::INTERNAL_MODULE_ID, "onAfterMoveObject", array($this)); $event->send(); } return $object; } /** * @param Folder $folder * @param int $movedBy * @return $this|null * @throws \Bitrix\Main\ArgumentException */ protected function moveInSameStorage(Folder $folder, $movedBy) { $driver = Driver::getInstance(); $subscriberManager = $driver->getSubscriberManager(); $subscribersBeforeMove = $subscriberManager->collectSubscribersSmart($this); $realFolderId = $folder->getRealObject()->getId(); /** @var ObjectTable $tableClassName */ $tableClassName = $this->getTableClassName(); $moveResult = $tableClassName::move($this->id, $realFolderId); if(!$moveResult->isSuccess()) { $this->errorCollection->addFromResult($moveResult); return null; } $this->setAttributesFromResult($moveResult); $driver->getRightsManager()->setAfterMove($this); $subscribersAfterMove = $subscriberManager->collectSubscribersSmart($this); $driver->getDeletedLogManager()->markAfterMove( $this, array_unique(array_diff($subscribersBeforeMove, $subscribersAfterMove)), $movedBy ); //notify new subscribers (in DeletedLog we notify subscribers only missed access) if($this instanceof Folder) { $driver->cleanCacheTreeBitrixDisk(array_keys($subscribersAfterMove)); } $driver->sendChangeStatus($subscribersAfterMove); ObjectTable::updateSyncTime($this->id, new DateTime()); $driver->sendChangeStatus($subscriberManager->collectSubscribersFromSubtree($this)); return $this; } /** * Simple logic: create copy in another storage and move in trash can. * If we have problem - stop. * Return new object. * @param Folder $targetFolder * @param int $movedBy * @return null|BaseObject */ protected function moveInAnotherStorage(Folder $targetFolder, $movedBy) { $newObject = $this->copyTo($targetFolder, $movedBy); if(!$newObject) { return null; } $newObject->update([ 'UPDATE_TIME' => $this->getUpdateTime(), 'CREATE_TIME' => $this->getCreateTime(), ]); if($newObject->getErrors()) { $this->errorCollection->add($newObject->getErrors()); return $newObject; } $rightsManager = Driver::getInstance()->getRightsManager(); $specificRights = $rightsManager->getSpecificRights($this); $rightsManager->set($newObject, $specificRights); $this->markDeleted($movedBy); return $newObject; } /** * Builds model from array. * @param array $attributes Model attributes. * @param array &$aliases Aliases. * @internal * @return static */ public static function buildFromArray(array $attributes, array &$aliases = null) { /** @var BaseObject $className */ $className = static::getClassNameModel($attributes); /** @var BaseObject $model */ $model = new $className; return $model->setAttributes($attributes, $aliases); } /** * Builds model from \Bitrix\Main\Entity\Result. * @param Result $result Query result. * @return static * @throws \Bitrix\Main\ArgumentException */ public static function buildFromResult(Result $result) { $data = $result->getData(); if($result instanceof AddResult) { $data['ID'] = $result->getId(); } $className = static::getClassNameModel($data); /** @var BaseObject $model */ $model = new $className; return $model->setAttributesFromResult($result); } protected static function getClassNameModel(array $row) { if(!isset($row['ID'])) { throw new ArgumentTypeException('Invalid ID'); } if(!isset($row['TYPE'])) { throw new ArgumentTypeException('Invalid TYPE'); } if(empty($row['REAL_OBJECT_ID']) || $row['REAL_OBJECT_ID'] == $row['ID']) { if($row['TYPE'] == ObjectTable::TYPE_FILE) { return File::className(); } return Folder::className(); } if($row['TYPE'] == ObjectTable::TYPE_FILE) { return FileLink::className(); } return FolderLink::className(); } /** * Returns once model by specific filter. * @param array $filter Filter. * @param array $with List of eager loading. * @throws \Bitrix\Main\NotImplementedException * @return static */ public static function load(array $filter, array $with = array()) { $objectData = static::getList(array( 'with' => $with, 'filter'=> $filter, 'limit' => 1, ))->fetch(); if(empty($objectData)) { return null; } /** @var BaseObject $className */ $className = static::getClassNameModel($objectData); return $className::buildFromArray($objectData); } /** * Marks deleted object. It equals to move in trash can. * @param int $deletedBy Id of user (or SystemUser::SYSTEM_USER_ID). * @return bool */ abstract public function markDeleted($deletedBy); /** * Restores object from trash can. * @param int $restoredBy Id of user (or SystemUser::SYSTEM_USER_ID). * @return bool */ abstract public function restore($restoredBy); /** * Restores object under link and destroy itself. * It's special case. @see \Bitrix\Disk\BaseObject::markDeletedInternal. * There we put file/folder in original trash can and in trash can of user, who delete the object. * * @param int $restoredBy Id of user (or SystemUser::SYSTEM_USER_ID). * @return bool */ protected function restoreByLinkObject($restoredBy) { if ($this->deletedType != ObjectTable::DELETED_TYPE_ROOT) { $this->errorCollection[] = new Error( "It's not possible to restore object, which was deleted with TYPE_CHILD", self::ERROR_RESTORE_UNDER_LINK_WRONG_TYPE ); return false; } $realObject = $this->getRealObject(); if (!$realObject) { $this->errorCollection[] = new Error('Could not find real object of link to restore it'); return false; } $status = $realObject->restore($restoredBy); $this->deleteInternal(); return $status; } /** * @param int $deletedBy * @param int $deletedType * @throws \Bitrix\Main\ArgumentException * @return bool */ protected function markDeletedInternal($deletedBy, $deletedType = ObjectTable::DELETED_TYPE_ROOT) { if ($this->deletedType == $deletedType) { return true; } $this->errorCollection->clear(); $subscriberManager = Driver::getInstance()->getSubscriberManager(); foreach($subscriberManager->getSharingsByObject($this) as $sharing) { $sharing->delete($deletedBy); } //with status unreplied, declined (not approved) SharingTable::deleteByFilter(array( 'REAL_OBJECT_ID' => $this->id, )); if ($deletedType == ObjectTable::DELETED_TYPE_CHILD) { $nameAfterDelete = $this->getNameWithoutTrashCanSuffix(); } elseif ($deletedType == ObjectTable::DELETED_TYPE_ROOT) { //we want to delete object as root. It means it has to have unique name. In this way we clean the name, //and after we make it unique. $nameAfterDelete = Ui\Text::appendTrashCanSuffix($this->getNameWithoutTrashCanSuffix()); } else { $nameAfterDelete = $this->getNameWithoutTrashCanSuffix(); } if (!static::isUniqueName($nameAfterDelete, $this->getParentId(), $this->getId())) { $nameAfterDelete = $this->generateUniqueName($nameAfterDelete, $this->getParentId()); } $data = [ 'CODE' => null, 'NAME' => $nameAfterDelete, 'DELETED_TYPE' => $deletedType, ]; $alreadyDeleted = $this->isDeleted(); if (!$alreadyDeleted) { $data['DELETE_TIME'] = new DateTime(); $data['DELETED_BY'] = $deletedBy; } $status = $this->update($data); if ($status) { $driver = Driver::getInstance(); $driver->getDeletionNotifyManager()->put($this, $deletedBy); $driver->getIndexManager()->dropIndexByModuleSearch($this); $event = new Event(Driver::INTERNAL_MODULE_ID, "onAfterMarkDeletedObject", array($this, $deletedBy, $deletedType)); $event->send(); } if ($deletedType == ObjectTable::DELETED_TYPE_ROOT) { $this->createLinkInTrashcan($deletedBy); } return $status; } private function createLinkInTrashcan($deletedBy) { $userStorage = Driver::getInstance()->getStorageByUserId($deletedBy); if (!$userStorage || $userStorage->getId() == $this->getStorageId()) { return; } if ($this instanceof File) { $userStorage->addFileLink( $this, array( 'CREATED_BY' => $deletedBy, 'DELETED_BY' => $deletedBy, 'DELETED_TYPE' => ObjectTable::DELETED_TYPE_ROOT, 'DELETE_TIME' => new DateTime(), ) ); } elseif ($this instanceof Folder) { $userStorage->addFolderLink( $this, array( 'CREATED_BY' => $deletedBy, 'DELETED_BY' => $deletedBy, 'DELETED_TYPE' => ObjectTable::DELETED_TYPE_ROOT, 'DELETE_TIME' => new DateTime(), ) ); } } /** * Restores object from trash can. * @param int $restoredBy Id of user (or SystemUser::SYSTEM_USER_ID). * @return bool * @internal */ public function restoreInternal($restoredBy) { if(!$this->isUniqueName($this->getNameWithoutTrashCanSuffix(), $this->parentId, $this->id)) { $this->name = $this->generateUniqueName($this->getNameWithoutTrashCanSuffix(), $this->parentId); } /** @var ObjectTable $tableClassName */ $tableClassName = $this->getTableClassName(); $status = $this->update(array( 'NAME' => $this->getNameWithoutTrashCanSuffix(), 'DELETED_TYPE' => $tableClassName::DELETED_TYPE_NONE, 'UPDATE_TIME' => new DateTime(), 'UPDATED_BY' => $restoredBy, )); if($status) { //we have to delete links which are in trashcan. It's special case. @see \Bitrix\Disk\BaseObject::markDeletedInternal. $links = BaseObject::getModelList(array( 'filter' => array( 'REAL_OBJECT_ID' => $this->id, '!=DELETED_TYPE' => ObjectTable::DELETED_TYPE_NONE, ) )); foreach($links as $link) { if ($link instanceof FileLink) { $link->delete($restoredBy); } elseif ($link instanceof FolderLink) { $link->deleteTree($restoredBy); } } $event = new Event(Driver::INTERNAL_MODULE_ID, "onAfterRestoreObject", array($this, $restoredBy)); $event->send(); } return $status; } protected function recalculateDeletedTypeAfterRestore($restoredBy) { $fakeContext = Storage::getFakeSecurityContext(); $parents = $this->getParents($fakeContext, ['filter' => ['MIXED_SHOW_DELETED' => true]], SORT_ASC); foreach ($parents as $parent) { if(!$parent instanceof Folder || !$parent->isDeleted()) { continue; } /** @var $parent Folder */ foreach ($parent->getChildren($fakeContext, array('filter' => array('!==DELETED_TYPE' => ObjectTable::DELETED_TYPE_NONE,))) as $childPotentialRoot) { if($childPotentialRoot->getId() == $this->getId()) { continue; } if($childPotentialRoot instanceof Folder) { /** @var $childPotentialRoot Folder */ $childPotentialRoot->markDeletedNonRecursiveInternal($childPotentialRoot->getDeletedBy()); } elseif($childPotentialRoot instanceof File) { $childPotentialRoot->markDeletedInternal($childPotentialRoot->getDeletedBy()); } } } foreach ($parents as $parent) { if (!$parent instanceof Folder || !$parent->isDeleted()) { continue; } $parent->restoreNonRecursive($restoredBy); } } /** * Returns list parents of object. * @param SecurityContext $securityContext Security context. * @param array $parameters Parameters. * @param int $orderDepthLevel Order for depth level (default asc). * @return array|Folder[]|File[]|FileLink[]|FolderLink[] * @throws \Bitrix\Main\ArgumentOutOfRangeException */ public function getParents(SecurityContext $securityContext, array $parameters = array(), $orderDepthLevel = SORT_ASC) { if(!isset($parameters['filter'])) { $parameters['filter'] = array(); } if(!isset($parameters['select'])) { $parameters['select'] = array('*'); } if(!empty($parameters['filter']['MIXED_SHOW_DELETED'])) { unset($parameters['filter']['DELETED_TYPE'], $parameters['filter']['MIXED_SHOW_DELETED']); } elseif ( !array_key_exists('DELETED_TYPE', $parameters['filter']) && !array_key_exists('!DELETED_TYPE', $parameters['filter']) && !array_key_exists('!=DELETED_TYPE', $parameters['filter']) && !array_key_exists('!==DELETED_TYPE', $parameters['filter']) ) { $parameters['filter']['DELETED_TYPE'] = ObjectTable::DELETED_TYPE_NONE; } $parameters['select']['DEPTH_LEVEL'] = 'PATH_PARENT.DEPTH_LEVEL'; $parameters = Driver::getInstance()->getRightsManager()->addRightsCheck($securityContext, $parameters, array('ID', 'CREATED_BY')); /** @var ObjectTable $tableClassName */ $tableClassName = $this->getTableClassName(); $data = $tableClassName::getAncestors($this->id, static::prepareGetListParameters($parameters))->fetchAll(); Collection::sortByColumn($data, array('DEPTH_LEVEL' => $orderDepthLevel)); $modelData = array(); foreach($data as $item) { $modelData[] = BaseObject::buildFromArray($item); } unset($item); return $modelData; } /** * Returns parent model. * If object does not have parent, then returns null. * @return Folder|null * @throws \Bitrix\Main\NotImplementedException */ public function getParent() { if(!$this->parentId) { return null; } if($this->isLoadedAttribute('parent') && $this->parent && $this->parentId === $this->parent->getId()) { return $this->parent; } //todo - BaseObject - knows about Folder ^( Nu i pust' $this->parent = Folder::loadById($this->getParentId()); $this->setAsLoadedAttribute('parent'); return $this->parent; } /** * Tells if object is root folder. * @return bool */ public function isRoot() { return !$this->parentId && $this instanceof Folder && !$this->isLink(); } /** * Tells if name is not unique in object. * @param string $name Name. * @param int $underObjectId Id of parent object. * @param null $excludeId Id which will be excluded from query. * @param null &$opponentId Opponent object which has same name. * @return bool * @throws \Bitrix\Main\ArgumentException */ public static function isUniqueName($name, $underObjectId, $excludeId = null, &$opponentId = null) { $parameters = array( 'select' => array('NAME'), 'filter' => array( 'PARENT_ID' => $underObjectId, '=NAME' => $name, ), 'limit' => 1, ); if($excludeId !== null) { $parameters['filter']['!ID'] = $excludeId; } if(func_num_args() >= 4) { $parameters['select'][] = 'ID'; } $opponent = ObjectTable::getList($parameters)->fetch(); if(!$opponent) { return true; } if(func_num_args() >= 4) { $opponentId = $opponent['ID']; } return false; } /** * Appends (1), (2), etc. if name is non unique in target dir * @param string $potentialName Potential name. * @param int $underObjectId Id of parent object. * @return string */ protected static function generateUniqueName($potentialName, $underObjectId) { $count = 0; $newName = $mainPartName = $potentialName; $suffix = null; $mainParts = explode('.', $mainPartName); if (count($mainParts) > 1) { $suffix = array_pop($mainParts); $mainPartName = implode('.', $mainParts); } while (!static::isUniqueName($newName, $underObjectId)) { $count++; [$newName] = static::getNextGeneratedName($mainPartName, $suffix, $underObjectId, $count > 2); if ($count > 10) { return static::generateFallbackName($mainPartName, $suffix); } } return $newName; } private static function generateFallbackName($mainPartName, $suffix) { return implode('.', array_filter([ $mainPartName . ' ' . time(), $suffix ])); } private static function getNextGeneratedName($mainPartName, $suffix, $underObjectId, $withoutLike = false) { $underObjectId = (int)$underObjectId; $left = $mainPartName . ' ('; $right = ')'; if ($suffix) { $right = ').'. $suffix; } $lengthR = strlen($right); $lengthL = strlen($left); $connection = Application::getConnection(); $sqlHelper = $connection->getSqlHelper(); $filterByLike = "NAME LIKE '" . $sqlHelper->forSql($left) . "%' AND"; if ($withoutLike) { $filterByLike = ''; } $sql = " SELECT NAME FROM b_disk_object WHERE PARENT_ID = {$underObjectId} AND {$filterByLike} LEFT(NAME, {$lengthL}) = '" . $sqlHelper->forSql($left) . "' AND MID(NAME, {$lengthL} + 1, CHAR_LENGTH(NAME) - {$lengthL} - {$lengthR}) regexp '^[1-9][[:digit:]]*$' AND RIGHT(NAME, {$lengthR}) = '" . $sqlHelper->forSql($right) . "' ORDER BY CHAR_LENGTH(NAME) DESC, NAME DESC LIMIT 1; "; $row = $connection->query($sql)->fetch(); if (!$row) { $newName = $mainPartName . " (1)"; if ($suffix) { $newName .= "." . $suffix; } return [ $newName, null, 1 ]; } $counter = substr($row['NAME'], $lengthL, strlen($row['NAME']) - $lengthL - $lengthR); $counter = (int)$counter + 1; return [ $left . $counter . $right, $row['NAME'], $counter ]; } protected function updateLinksAttributes(array $attr) { /** @var ObjectTable $tableClassName */ $tableClassName = $this->getTableClassName(); //todo don't update object with REAL_OBJECT_ID == ID. Exlucde form update. It is not necessary. $tableClassName::updateAttributesByFilter($attr, array('REAL_OBJECT_ID' => $this->id)); ModelSynchronizer::getInstance()->trigger($this, $attr); } /** * Returns all sharings where the object is as source (real_object_id). * @return Sharing[] * @throws \Bitrix\Main\NotImplementedException */ public function getSharingsAsReal() { /** @var Sharing[] $sharings */ $sharings = Sharing::getModelList(array( 'with' => array('LINK_OBJECT'), 'filter' => array( 'REAL_OBJECT_ID' => $this->id, 'REAL_STORAGE_ID' => $this->storageId, '!=STATUS' => SharingTable::STATUS_IS_DECLINED, ) )); foreach($sharings as $sharing) { $sharing->setAttributes(array('REAL_OBJECT' => $this)); } unset($sharing); return $sharings; } /** * Returns all sharing where the object is as link (link_object_id). * @return Sharing[] * @throws \Bitrix\Main\NotImplementedException */ public function getSharingsAsLink() { /** @var Sharing[] $sharings */ $sharings = Sharing::getModelList(array( 'filter' => array( 'LINK_OBJECT_ID' => $this->id, 'LINK_STORAGE_ID' => $this->storageId, ) )); foreach($sharings as $sharing) { $sharing->setAttributes(array('LINK_OBJECT' => $this)); } unset($sharing); return $sharings; } /** * Returns list users who have sharing on this object. * @return array * @throws \Bitrix\Main\ArgumentException * @throws \Bitrix\Main\LoaderException */ public function getMembersOfSharing() { $realObject = $this->getRealObject(); if(!$realObject) { return array(); } $sharings = $realObject->getSharingsAsReal(); $members = array(); $membersToSharing = array(); foreach($sharings as $sharing) { if($sharing->isToDepartmentChild()) { continue; } [$type, $id] = Sharing::parseEntityValue($sharing->getToEntity()); $members[$type][] = $id; $membersToSharing[$type . '|' . $id] = $sharing; } unset($sharing); $enabledSocialnetwork = Loader::includeModule('socialnetwork'); $entityList = array(); foreach(SharingTable::getListOfTypeValues() as $type) { if(empty($members[$type])) { continue; } if($type == SharingTable::TYPE_TO_USER) { $query = UserTable::getList(array( 'select' => array('ID', 'PERSONAL_PHOTO', 'NAME', 'LOGIN', 'LAST_NAME', 'SECOND_NAME'), 'filter' => array('ID' => array_values($members[$type])), )); while($userRow = $query->fetch()) { /** @var Sharing $sharing */ $sharing = $membersToSharing[$type . '|' . $userRow['ID']]; $entityList[] = array( 'sharingId' => $sharing->getId(), 'entityId' => Sharing::CODE_USER . $userRow['ID'], 'name' => \CUser::formatName('#NAME# #LAST_NAME#', array( "NAME" => $userRow['NAME'], "LAST_NAME" => $userRow['LAST_NAME'], "SECOND_NAME" => $userRow['SECOND_NAME'], "LOGIN" => $userRow['LOGIN'], ), false, false), 'right' => $sharing->getTaskName(), 'avatar' => Avatar::getPerson($userRow['PERSONAL_PHOTO']), 'type' => 'users', ); } } elseif($type == SharingTable::TYPE_TO_GROUP && $enabledSocialnetwork) { $query = \CSocNetGroup::getList(array(), array('ID' => array_values($members[$type])), false, false, array( 'ID', 'IMAGE_ID', 'NAME' )); while($query && $groupRow = $query->fetch()) { /** @var Sharing $sharing */ $sharing = $membersToSharing[$type . '|' . $groupRow['ID']]; $entityList[] = array( 'sharingId' => $sharing->getId(), 'entityId' => Sharing::CODE_SOCNET_GROUP . $groupRow['ID'], 'name' => $groupRow['NAME'], 'right' => $sharing->getTaskName(), 'avatar' => Avatar::getGroup($groupRow['IMAGE_ID']), 'type' => 'groups', ); } } elseif($type == SharingTable::TYPE_TO_DEPARTMENT && $enabledSocialnetwork) { // intranet structure $structure = \CSocNetLogDestination::getStucture(); foreach(array_values($members[$type]) as $departmentId) { if(empty($structure['department']['DR' . $departmentId])) { continue; } /** @var Sharing $sharing */ $sharing = $membersToSharing[$type . '|' . $departmentId]; $entityList[] = array( 'sharingId' => $sharing->getId(), 'entityId' => Sharing::CODE_DEPARTMENT . $departmentId, 'name' => $structure['department']['DR' . $departmentId]['name'], 'right' => $sharing->getTaskName(), 'avatar' => Avatar::getDefaultGroup(), 'type' => 'department', ); } unset($departmentId); } } unset($type); return $entityList; } /** * Returns object ttl model. * * @return ObjectTtl */ public function getTtl() { if($this->isLoadedAttribute('ttl')) { return $this->ttl; } $this->ttl = ObjectTtl::load(array('OBJECT_ID' => $this->id)); $this->setAsLoadedAttribute('ttl'); return $this->ttl; } /** * Creates ttl object. * * @param int $ttl Seconds to live. * @return ObjectTtl */ public function setTtl($ttl) { $ttl = (int)$ttl; $deathTime = DateTime::createFromTimestamp(time() + $ttl); $objectTtl = ObjectTtl::loadByObjectId($this->id); if ($objectTtl) { if($objectTtl->getDeathTime()->getTimestamp() > $deathTime->getTimestamp()) { $objectTtl->changeDeathTime($deathTime); return $objectTtl; } else { return $objectTtl; } } return ObjectTtl::add( array( 'OBJECT_ID' => $this->id, 'DEATH_TIME' => $deathTime, ), $this->errorCollection ); } /** * Returns the list of pair for mapping. * Key is field in DataManager, value is object property. * @return array */ public static function getMapAttributes() { return array( 'ID' => 'id', 'NAME' => 'name', 'CODE' => 'code', 'XML_ID' => 'xmlId', 'STORAGE_ID' => 'storageId', 'STORAGE' => 'storage', 'TYPE' => 'type', 'REAL_OBJECT_ID' => 'realObjectId', 'REAL_OBJECT' => 'realObject', 'PARENT_ID' => 'parentId', 'PARENT' => 'parent', 'LOCK' => 'lock', 'TTL' => 'ttl', 'CONTENT_PROVIDER' => 'contentProvider', 'DELETED_TYPE' => 'deletedType', 'TYPE_FILE' => null, 'GLOBAL_CONTENT_VERSION' => null, 'FILE_ID' => null, 'SIZE' => null, 'EXTERNAL_HASH' => null, 'ETAG' => null, 'CREATE_TIME' => 'createTime', 'UPDATE_TIME' => 'updateTime', 'SYNC_UPDATE_TIME' => 'syncUpdateTime', 'DELETE_TIME' => 'deleteTime', 'CREATED_BY' => 'createdBy', 'CREATE_USER' => 'createUser', 'UPDATED_BY' => 'updatedBy', 'UPDATE_USER' => 'updateUser', 'DELETED_BY' => 'deletedBy', 'DELETE_USER' => 'deleteUser', 'HAS_SUBFOLDERS' => null, ); } /** * Returns the list attributes which is connected with another models. * @return array */ public static function getMapReferenceAttributes() { $userClassName = User::className(); $fields = User::getFieldsForSelect(); return array( 'CREATE_USER' => array( 'class' => $userClassName, 'select' => $fields, ), 'UPDATE_USER' => array( 'class' => $userClassName, 'select' => $fields, ), 'DELETE_USER' => array( 'class' => $userClassName, 'select' => $fields, ), 'REAL_OBJECT' => BaseObject::className(), 'STORAGE' => Storage::className(), 'LOCK' => ObjectLock::className(), 'TTL' => ObjectTtl::className(), ); } /** * Specify data which should be serialized to JSON * @link http://php.net/manual/en/jsonserializable.jsonserialize.php * @return mixed data which can be serialized by <b>json_encode</b>, * which is a value of any type other than a resource. * @since 5.4.0 */ public function jsonSerialize() { return [ 'id' => (int)$this->getId(), 'name' => $this->getName(), 'createTime' => $this->getCreateTime(), 'updateTime' => $this->getUpdateTime(), 'deleteTime' => $this->getDeleteTime(), 'code' => $this->getCode(), 'xmlId' => $this->getXmlId(), 'storageId' => (int)$this->getStorageId(), 'realObjectId' => (int)$this->getRealObjectId(), 'parentId' => (int)$this->getParentId(), 'deletedType' => (int)$this->getDeletedType(), 'createdBy' => $this->getCreatedBy(), 'updatedBy' => $this->getUpdatedBy(), 'deletedBy' => $this->getDeletedBy(), ]; } }
/var/www/axolotl/data/www/samara.axolotls.ru/bitrix/modules/disk/lib/baseobject.php