/** * Returns list of id of objects which act by positive rights on descendants objects by the same ACCESS_CODE. * @param int $objectId Object id. * @param string $accessCode Access code. * @return array */ private function getConflictRightsInSubTree($objectId, $accessCode) { $objectId = (int) $objectId; $accessCode = $this->sqlHelper->forSql($accessCode); $rights = $this->connection->query("\n\t\t\tSELECT r.OBJECT_ID\n\t\t\tFROM b_disk_right r\n\t\t\t\tINNER JOIN b_disk_object_path p ON p.OBJECT_ID = r.OBJECT_ID\n\t\t\tWHERE\n\t\t\t\tp.PARENT_ID = {$objectId} AND\n\t\t\t\tr.NEGATIVE = 0 AND\n\t\t\t\tr.ACCESS_CODE = '{$accessCode}'\n\t\t")->fetchAll(); $ids = array(); foreach ($rights as $i => $right) { $ids[] = $right['OBJECT_ID']; } unset($right); return $ids; }
protected function migrateTrashFolders() { if ($this->isStepFinished(__METHOD__)) { return; } $lastId = $this->getStorageId(); //first migrate folder from trash $trashQuery = $this->connection->query("\n\t\t\tSELECT\n\t\t\t\tobj.*,\n\t\t\t\ts.ROOT_OBJECT_ID ROOT_OBJECT_ID,\n\t\t\t\tsecta.DESCRIPTION DESCRIPTION\n\t\t\tFROM b_disk_object obj\n\t\t\t\tINNER JOIN b_disk_storage s ON s.ID = obj.STORAGE_ID\n\t\t\t\tINNER JOIN b_disk_object trash ON trash.ID = obj.PARENT_ID AND trash.PARENT_ID = s.ROOT_OBJECT_ID\n\t\t\t\tINNER JOIN b_iblock_section secta ON secta.ID = obj.ID\n\n\t\t\tWHERE obj.TYPE=2 AND trash.NAME = '.Trash' AND obj.ID > {$lastId} ORDER BY s.ID\n\t\t"); while ($trashChild = $trashQuery->fetch()) { $this->abortIfNeeded(); $undeletePath = $this->getSectionInTrash($trashChild); if (empty($undeletePath)) { $this->log(array("Skip item {$trashChild['ID']} from .Trash", "Empty WEBDAV_INFO (DESCRIPTION)")); $this->storeStorageId($trashChild['ID']); continue; } $undeletePath = trim($undeletePath, '/'); $filter = array('TYPE' => \Bitrix\Disk\Internals\ObjectTable::TYPE_FOLDER, 'STORAGE_ID' => $trashChild['STORAGE_ID'], 'PARENT_ID' => $trashChild['ROOT_OBJECT_ID']); $mustCreatePathPieces = array(); $undeletePathPieces = explode('/', $undeletePath); $parentId = $filter['PARENT_ID']; $parentDeletedType = null; $filename = array_pop($undeletePathPieces); foreach ($undeletePathPieces as $i => $pieceOfPath) { $filter['NAME'] = $this->sqlHelper->forSql($pieceOfPath); $folder = $this->connection->query("\n\t\t\t\t\tSELECT ID, NAME, REAL_OBJECT_ID, STORAGE_ID, PARENT_ID, DELETED_TYPE FROM b_disk_object obj\n\t\t\t\t\tWHERE obj.PARENT_ID = {$filter['PARENT_ID']} AND obj.STORAGE_ID = {$filter['STORAGE_ID']} AND obj.TYPE = {$filter['TYPE']} AND obj.NAME = '{$filter['NAME']}'\n\t\t\t\t")->fetch(); if ($folder) { $filter['PARENT_ID'] = $parentId = $folder['ID']; if (!empty($folder['DELETED_TYPE'])) { $parentDeletedType = $folder['DELETED_TYPE']; } continue; } if (!$folder) { $this->log(array("Folder with name {$pieceOfPath} does not exist")); $mustCreatePathPieces = array_slice($undeletePathPieces, $i); break; } } unset($pieceOfPath); if ($parentId) { $success = true; /** @var Folder $parentFolder */ $parentFolder = Folder::loadById($parentId); $folderData = array('CREATE_TIME' => $trashChild['CREATE_TIME'], 'UPDATE_TIME' => $trashChild['UPDATE_TIME'], 'CREATED_BY' => $trashChild['CREATED_BY'], 'UPDATED_BY' => $trashChild['UPDATED_BY']); foreach ($mustCreatePathPieces as $i => $pieceOfPath) { $deletedType = FolderTable::DELETED_TYPE_CHILD; if ($i == 0 && !$parentDeletedType) { $deletedType = FolderTable::DELETED_TYPE_ROOT; } $folderData['NAME'] = Text::correctFilename($pieceOfPath); if ($deletedType == FolderTable::DELETED_TYPE_ROOT) { $folderData['NAME'] = self::appendTrashCanSuffix($folderData['NAME']); } $folder = $parentFolder->addSubFolder($folderData, array(), true); if (!$folder) { $this->log(array("Skip item {$trashChild['ID']} from .Trash", "Could not create subfolder {$folderData['NAME']}")); $this->storeStorageId($trashChild['ID']); $success = false; break; } $updateResult = FolderTable::update($folder->getId(), array('DELETED_TYPE' => $deletedType, 'DELETE_TIME' => $folder->getUpdateTime(), 'DELETED_BY' => $folder->getUpdatedBy())); if (!$updateResult->isSuccess()) { $this->log(array("Skip item {$trashChild['ID']} from .Trash", "Could not markDeleted subfolder {$folder->getId()}")); $this->storeStorageId($trashChild['ID']); $success = false; break; } $parentFolder = $folder; } unset($pieceOfPath, $folder, $undeletePath); if ($success) { //move trashChild into new folder if ($this->isMysql || $this->isMssql) { $this->connection->queryExecute("\n\t\t\t\t\t\t\tDELETE a FROM b_disk_object_path a\n\t\t\t\t\t\t\t\tJOIN b_disk_object_path d\n\t\t\t\t\t\t\t\t\tON a.OBJECT_ID = d.OBJECT_ID\n\t\t\t\t\t\t\t\tLEFT JOIN b_disk_object_path x\n\t\t\t\t\t\t\t\t\tON x.PARENT_ID = d.PARENT_ID AND x.OBJECT_ID = a.PARENT_ID\n\t\t\t\t\t\t\t\tWHERE d.PARENT_ID = {$trashChild['ID']} AND x.PARENT_ID IS NULL;\n\t\t\t\t\t\t"); } elseif ($this->isOracle) { $this->connection->queryExecute("\n\t\t\t\t\t\t\tDELETE FROM b_disk_object_path WHERE ID IN (SELECT a.ID FROM b_disk_object_path a\n\t\t\t\t\t\t\t\tJOIN b_disk_object_path d\n\t\t\t\t\t\t\t\t\tON a.OBJECT_ID = d.OBJECT_ID\n\t\t\t\t\t\t\t\tLEFT JOIN b_disk_object_path x\n\t\t\t\t\t\t\t\t\tON x.PARENT_ID = d.PARENT_ID AND x.OBJECT_ID = a.PARENT_ID\n\t\t\t\t\t\t\t\tWHERE d.PARENT_ID = {$trashChild['ID']} AND x.PARENT_ID IS NULL)\n\t\t\t\t\t\t"); } $this->connection->queryExecute("\n\t\t\t\t\t\tINSERT INTO b_disk_object_path (PARENT_ID, OBJECT_ID, DEPTH_LEVEL)\n\t\t\t\t\t\t\tSELECT stree.PARENT_ID, subtree.OBJECT_ID, stree.DEPTH_LEVEL+subtree.DEPTH_LEVEL+1\n\t\t\t\t\t\t\tFROM b_disk_object_path stree INNER JOIN b_disk_object_path subtree\n\t\t\t\t\t\t\t\tON subtree.PARENT_ID = {$trashChild['ID']} AND stree.OBJECT_ID = {$parentFolder->getId()};\n\t\t\t\t\t"); //update all objects under trashChild (DELETED_TYPE) $deletedType = FolderTable::DELETED_TYPE_CHILD; if ($this->isMysql) { $sql = "\n\t\t\t\t\t\t\tUPDATE b_disk_object obj\n\t\t\t\t\t\t\t\tINNER JOIN b_disk_object_path p ON obj.ID = p.OBJECT_ID\n\t\t\t\t\t\t\t\tSET obj.DELETED_TYPE = {$deletedType}, obj.DELETED_BY=obj.UPDATED_BY, obj.DELETE_TIME=obj.UPDATE_TIME\n\t\t\t\t\t\t\tWHERE p.PARENT_ID = {$trashChild['ID']}\n\t\t\t\t\t\t"; } elseif ($this->isOracle || $this->isMssql) { $sql = "\n\t\t\t\t\t\t\tUPDATE b_disk_object\n\t\t\t\t\t\t\t\tSET\n\t\t\t\t\t\t\t\t\tDELETED_TYPE = {$deletedType},\n\t\t\t\t\t\t\t\t\tDELETED_BY=(SELECT obj.UPDATED_BY FROM b_disk_object obj INNER JOIN b_disk_object_path p ON obj.ID = p.OBJECT_ID\n\t\t\t\t\t\t\tWHERE p.PARENT_ID = {$trashChild['ID']} and obj.ID = b_disk_object.ID),\n\n\t\t\t\t\t\t\t\t\tDELETE_TIME=(SELECT obj.UPDATE_TIME FROM b_disk_object obj INNER JOIN b_disk_object_path p ON obj.ID = p.OBJECT_ID\n\t\t\t\t\t\t\tWHERE p.PARENT_ID = {$trashChild['ID']} and obj.ID = b_disk_object.ID)\n\n\t\t\t\t\t\t\tWHERE EXISTS((SELECT 'x' FROM b_disk_object obj INNER JOIN b_disk_object_path p ON obj.ID = p.OBJECT_ID\n\t\t\t\t\t\t\tWHERE p.PARENT_ID = {$trashChild['ID']} and obj.ID = b_disk_object.ID))\n\t\t\t\t\t\t"; } $this->connection->queryExecute($sql); $deletedType = FolderTable::DELETED_TYPE_ROOT; $newName = $this->sqlHelper->forSql(self::appendTrashCanSuffix(Text::correctFilename($filename))); $this->connection->queryExecute("\n\t\t\t\t\t\tUPDATE b_disk_object SET NAME='{$newName}', PARENT_ID = {$parentFolder->getId()}, DELETED_TYPE = {$deletedType}, DELETED_BY=UPDATED_BY, DELETE_TIME=UPDATE_TIME\n\t\t\t\t\t\tWHERE ID={$trashChild['ID']} AND STORAGE_ID={$trashChild['STORAGE_ID']}\n\t\t\t\t\t"); $this->connection->queryExecute("\n\t\t\t\t\t\tINSERT INTO b_disk_deleted_log (STORAGE_ID, OBJECT_ID, TYPE, USER_ID, CREATE_TIME)\n\t\t\t\t\t\tSELECT {$trashChild['STORAGE_ID']}, p.OBJECT_ID, obj.TYPE, obj.UPDATED_BY, " . $this->sqlHelper->getCurrentDateTimeFunction() . " FROM b_disk_object obj\n\t\t\t\t\t\t\tINNER JOIN b_disk_object_path p ON obj.ID = p.OBJECT_ID\n\t\t\t\t\t\tWHERE p.PARENT_ID = {$trashChild['ID']}\n\t\t\t\t\t"); $this->storeStorageId($trashChild['ID']); } } $this->storeStorageId($trashChild['ID']); } $this->abortIfNeeded(); $this->storeStorageId(0); $this->setStepFinished(__METHOD__); }
private function appendOneNegative(array $right, $hadOppositeRight = false) { $isValidNegaviteRight = $this->validateNegaviteRight($right); if (!$isValidNegaviteRight && !$hadOppositeRight) { $this->errorCollection->addOne(new Error('Invalid negative right')); return false; } //we don't have to add negative right. We must only delete old simple rights. condition($hadOppositeRight && !$isValidNegaviteRight) if ($isValidNegaviteRight) { //May we have to add record to b_disk_right in final. $right['OBJECT_ID'] = $this->object->getId(); $result = RightTable::add($right); if (!$result->isSuccess()) { $this->errorCollection->addFromResult($result); return false; } } $rightsManager = Driver::getInstance()->getRightsManager(); if (!$rightsManager->containsOperationInTask($rightsManager::OP_READ, $right['TASK_ID'])) { return true; } if (!$this->hasAlreadySimpleRight($right['ACCESS_CODE'])) { //below we already have negative rights, which deleted simple rights. return true; } //need to delete simple rights from descendants $conflictRightsInSubTree = $this->getConflictRightsInSubTree($right['ACCESS_CODE'], $right['TASK_ID']); $accessCode = $this->sqlHelper->forSql($right['ACCESS_CODE']); if (empty($conflictRightsInSubTree)) { //we have to destroy simple right from all descendants and from current OBJECT_ID if ($this->connection instanceof OracleConnection) { $this->connection->queryExecute("\n\t\t\t\t\tDELETE FROM (SELECT simple.* FROM b_disk_simple_right simple\n\t\t\t\t\t\tINNER JOIN b_disk_object_path p ON p.OBJECT_ID = simple.OBJECT_ID\n\t\t\t\t\tWHERE p.PARENT_ID = {$this->object->getId()} AND simple.ACCESS_CODE = '{$accessCode}')\n\t\t\t\t"); } else { $this->connection->queryExecute("\n\t\t\t\t\tDELETE simple FROM b_disk_simple_right simple\n\t\t\t\t\t\tINNER JOIN b_disk_object_path p ON p.OBJECT_ID = simple.OBJECT_ID\n\t\t\t\t\tWHERE p.PARENT_ID = {$this->object->getId()} AND simple.ACCESS_CODE = '{$accessCode}'\n\t\t\t\t"); } } else { $objectIds = array(); foreach ($conflictRightsInSubTree as $conflictRight) { $objectIds[] = $conflictRight['OBJECT_ID']; } unset($conflictRight); //we have to destroy simple right from all descendants and from current OBJECT_ID without nodes with conflict rights in path. if ($this->connection instanceof OracleConnection) { $this->connection->queryExecute("\n\t\t\t\t\tDELETE FROM (SELECT simple.* FROM b_disk_simple_right simple\n\t\t\t\t\t\tINNER JOIN b_disk_object_path p ON p.OBJECT_ID = simple.OBJECT_ID\n\t\t\t\t\tWHERE\n\t\t\t\t\t\tp.PARENT_ID = {$this->object->getId()} AND simple.ACCESS_CODE = '{$accessCode}' AND\n\t\t\t\t\t\tNOT EXISTS(\n\t\t\t\t\t\t\tSELECT 'x' FROM b_disk_object_path pp\n\t\t\t\t\t\t\t\tWHERE pp.OBJECT_ID = p.OBJECT_ID AND\n\t\t\t\t\t\t\t\tpp.PARENT_ID IN (" . implode(',', $objectIds) . ") ))\n\t\t\t\t"); } else { $this->connection->queryExecute("\n\t\t\t\t\tDELETE simple FROM b_disk_simple_right simple\n\t\t\t\t\t\tINNER JOIN b_disk_object_path p ON p.OBJECT_ID = simple.OBJECT_ID\n\t\t\t\t\tWHERE\n\t\t\t\t\t\tp.PARENT_ID = {$this->object->getId()} AND simple.ACCESS_CODE = '{$accessCode}' AND\n\t\t\t\t\t\tNOT EXISTS(\n\t\t\t\t\t\t\tSELECT 'x' FROM b_disk_object_path pp\n\t\t\t\t\t\t\t\tWHERE pp.OBJECT_ID = p.OBJECT_ID AND\n\t\t\t\t\t\t\t\tpp.PARENT_ID IN (" . implode(',', $objectIds) . ") )\n\t\t\t\t"); } } return true; }