• Модуль: webdav
  • Путь к файлу: ~/bitrix/modules/webdav/classes/iblock.php
  • Класс: CWebDavIblock
  • Вызов: CWebDavIblock::COPY
function COPY($options, $drop = false)
{
	$statusSymlinkDelete = false;
	$arCacheCleanID = array();
	if(!$this->CheckWebRights("", array("action" => "create"), true))
	{
		return $this->ThrowAccessDenied(__LINE__);
	}
	elseif($_SERVER['REQUEST_METHOD'] == "MOVE" && !empty($_SERVER["CONTENT_LENGTH"]))
	{
		return "415 Unsupported media type";
	}
	elseif($options["path"] == $options["dest_url"])
	{
		return "204 No Content";
	}
	elseif(empty($options["dest_url"]))
	{
		return $this->ThrowError("502 bad gateway", "EMPTY_DESTINATION_URL", GetMessage("WD_FILE_ERROR2"), __LINE__);
	}

	$destUrl = $options["dest_url"];
	if(mb_substr($destUrl, -1) === "/")
	{
		$destUrl = mb_substr($destUrl, 0, -1);
	}
	$destName = GetFileName($destUrl);
	if($destUrl !== "" && $destName !== "")
	{
		$destParentDir  = GetDirPath($destUrl);
		$destParentDir = $destParentDir ?: '/';

		$o = array("path" => $destParentDir, "depth" => 1);
		$result = $this->PROPFIND($o, $files, array("COLUMNS" => array("ID", "NAME"), "return" => "array"));
		if (!empty($result["RESULT"]))
		{
			foreach ($result["RESULT"] as $key => $res)
			{
				if($res["NAME"] === $destName)
				{
					if(GetFileExtension($destName) <> '')
					{
						return $this->ThrowError("400 Bad Request", "FOLDER_IS_EXISTS", str_replace("#FILE#", '"' .$res["NAME"] . '"', GetMessage("WD_FILE_ERROR8")), __LINE__);
					}
					elseif(isset($options['section_id']) && $res['ID'] == $options['section_id'])
					{
						return $this->ThrowError("400 Bad Request", "SAME_FOLDER_IS_EXISTS", str_replace("#FOLDER#", '"' .$res["NAME"] . '"', GetMessage("WD_FILE_ERROR5")), __LINE__);
					}
					else
					{
						return $this->ThrowError("400 Bad Request", "FOLDER_IS_EXISTS", str_replace("#FOLDER#", '"' .$res["NAME"] . '"', GetMessage("WD_FILE_ERROR5")), __LINE__);
					}
				}
			}
		}
	}

	//$this->CheckUniqueName($basename, $section_id, &$res)
	//GetFileName()

	$arFrom = array();
	$arTo = array();

	$is_dir = false;

	////////////// CHECK FROM
	$this->IsDir($options);
	$arFrom = $this->arParams;
	if($this->arParams["not_found"])
	{
		return $this->ThrowError("404 Not Found", "DESTINATION_FILE_OR_FOLDER_IS_NOT_FOUND", GetMessage("WD_FILE_ERROR3"), __LINE__);
	}
	elseif($this->arParams["is_dir"] === true)
	{
		$is_dir = true;
		if ($_SERVER['REQUEST_METHOD'] == "MOVE" && ($options["depth"] != "infinity"))
		{
			return "400 Bad request";
		}
		elseif($this->check_creator)
		{
			return $this->ThrowAccessDenied("USER_IS_NOT_CREATOR", __LINE__);
		}
		elseif(empty($options["path"]))
		{
			$options["path"] = $this->_get_path($arFrom["item_id"], false);
		}

		$res = $this->_udecode($options["dest_url"]);
		$res2 = str_replace("//", "/", $res."/"); $res1 = str_replace("//", "/", $options["path"]."/");
		if ($res1 === $res2)
		{
			return "204 No Content";
		}
		elseif (
			mb_strtolower(mb_substr($res2, 0, mb_strlen($res1))) == mb_strtolower($res1)
			&& (mb_strlen($res1) != mb_strlen($res2)) // is not the same dir rename
		)
		{
			return $this->ThrowError("400 Bad Request", "SECTION_IS_NOT_UPDATED", GetMessage("WD_FILE_ERROR100"), __LINE__);
		}
	}
	else
	{ // found and is_file
	}

	if(!empty($arFrom['parent_id']))
	{
		list($contextType, $contextEntityId) = $this->getContextData();
		$sectionData = $this->getSectionDataForLinkAnalyze($arFrom['parent_id']);
		if(CWebDavSymlinkHelper::isLink($contextType, $contextEntityId, $sectionData))
		{
			$arFrom['is_symlink'] = true;
			$arFrom['symlink_section_data'] = $sectionData;
			$arFrom['symlink_section_data_link'] = CWebDavSymlinkHelper::getLinkData($contextType, $contextEntityId, $sectionData);
		}
	}

	////////////// CHECK TO
	$arToParams = array("path" => $options['dest_url']);
	if (mb_strpos($options['dest_url'], '.Trash') !== false)
	{
		$arToParams['check_permissions'] = false;
	}
	$this->IsDir($arToParams);
	$arTo = $this->arParams;

	if(!empty($arTo['parent_id']))
	{
		list($contextType, $contextEntityId) = $this->getContextData();
		$sectionData = $this->getSectionDataForLinkAnalyze($arTo['parent_id']);
		if(CWebDavSymlinkHelper::isLink($contextType, $contextEntityId, $sectionData))
		{
			$arTo['is_symlink'] = true;
			$arTo['symlink_section_data'] = $sectionData;
			$arTo['symlink_section_data_link'] = CWebDavSymlinkHelper::getLinkData($contextType, $contextEntityId, $sectionData);
		}
	}

	if($this->arParams["not_found"] == true)
	{
		if (
			$this->e_rights
			&& (mb_strpos($options['dest_url'], '.Trash') === false)
			&& !$this->CheckWebRights(
				"COPY",
				array(
					'action' => ($drop?'move':'copy'),
					'from' => array($arFrom),
					'to' => array($arTo)
				),
				false
			)
		)
		{
			return $this->ThrowAccessDenied(__LINE__);
		}
		//$arTo = false;
	}
	elseif (
		$arFrom["is_dir"] === true
		&& $arTo["is_file"] === true
		|| $arFrom["is_file"] === true
		&& $arTo["is_dir"] === true
	)
	{
		return $this->ThrowError("400 Bad Request", "FOLDER_IS_EXISTS", str_replace("#FOLDER#", $this->arParams["item_id"], GetMessage("WD_FILE_ERROR5")), __LINE__);
	}
	elseif (
		!$this->CheckWebRights(
			"COPY",
			array(
				'action' => ($drop?'move':'copy'),
				'from' => array($arFrom),
				'to' => array($arTo)
			),
			false)
	)
	{
		return $this->ThrowAccessDenied(__LINE__);
	}
	elseif(($arFrom["item_id"] == $arTo["item_id"]) && ($arFrom['basename'] == $arTo['basename']))
	{
		// else - trying to change case in name
		return "204 No Content";
	}
	elseif($arFrom["element_array"]["WF_PARENT_ELEMENT_ID"] > 0)
	{
		unset($arTo["item_id"]);
	}
	elseif(isset($options['rename']) && $options['rename'] === true)
	{
		// fix fast delete to trash from different folders with the same file name
			$nameSuffix = 1;

			do
			{
				$tmpName = $options["dest_url"]." (". $nameSuffix++ .")";
				$this->IsDir(array("path" => $tmpName));
				$arTo = $this->arParams;
			} while ($arTo["not_found"] !== true);

			$options['dest_url'] = $tmpName;
	}
	elseif(!$options["overwrite"])
	{
			return $this->ThrowError('412 Precondition failed', "FILE_OR_FOLDER_ALREADY_EXISTS", GetMessage("WD_FILE_ERROR4"), __LINE__);
	}
	elseif(!$this->CheckName($arTo["basename"]))
	{
			return $this->ThrowError("400 bad request", "BAD_NAME", GetMessage("WD_FILE_ERROR101"), __LINE__);
	}
	elseif(
		$arTo["is_file"]
		&& $this->check_creator
		&& $arTo["element_array"]["CREATED_BY"] != $GLOBALS["USER"]->GetID()
	)
	{
			return $this->ThrowAccessDenied("USER_IS_NOT_CREATOR", __LINE__);
	}

	if(
		($this->workflow == 'workflow')
		&& $arFrom["is_file"]
		&& (! CWorkflow::IsAdmin())
		&& (! $GLOBALS['USER']->CanDoOperation('webdav_change_settings'))
	)
	{
		$bNeedCheckWfRights = false;

		if ($this->e_rights)
		{
			$arToParent = $this->GetObject(array('section_id' => $arTo['parent_id']));
			if ($arToParent['is_dir'])
			{
				$bNeedCheckWfRights = ! $this->GetPermission('SECTION', $arToParent['item_id'], 'element_edit_any_wf_status');
			}
		}
		else
		{
			$bNeedCheckWfRights = ($this->permission < 'W');
		}

		if (
			$bNeedCheckWfRights
			&& (CIBlockElement::WF_GetStatusPermission($arFrom["element_array"]["WF_STATUS_ID"]) != 2)
		)
		{
			return $this->ThrowError("400 bad request", "BAD_WF_RIGHTS", GetMessage("WD_FILE_ERROR110"), __LINE__);
		}
	}

	if ($arTo['parent_id'] == $this->GetMetaID('TRASH'))
	{
		$arCheckTrashElement = $arFrom[($arFrom['is_dir']?'dir_array':'element_array')];
		if ( $this->_parse_webdav_info($arCheckTrashElement) && (! isset($arCheckTrashElement['PROPS']['BX:']['UNDELETE'])))
		{
			return $this->ThrowAccessDenied("BAD_NAME", __LINE__);
		}
	}
	if ($arFrom["is_file"])
	{
		$el = new CIBlockElement();

		if (
			$arTo["item_id"]
			&& ($arTo['item_id'] !== $arFrom['item_id']) // rename
		)
		{
			$this->_ib_elm_delete($arTo['item_id']); // TODO: need to check permissions ?
		}

		//drop == true if this action is @move@
		//is file
		if ($drop)
		{
			$actionRename = $arFrom['parent_id'] == $arTo['parent_id'];

			$arFields = array(
				"NAME" => $arTo["basename"],
				"MODIFIED_BY" => $GLOBALS['USER']->GetID(),
				"IBLOCK_SECTION_ID" => $arTo["parent_id"]);

			$this->_onEvent(
					(($arFrom['parent_id'] != $arTo['parent_id'])?'Move':'Rename'),
					$arFrom['element_id'],
					'FILE',
					array( 'TO' => ( ($arFrom['parent_id'] != $arTo['parent_id']) ? $arTo["parent_id"] : $arTo["basename"] ))
				);

			//from symlink move. Not rename!!!!
			if(!$actionRename && (!empty($arFrom['is_symlink']) || !empty($arTo['is_symlink'])))
			{
				$targetIblockId = $this->IBLOCK_ID;
				if(!empty($arTo['is_symlink']))
				{
					$targetIblockId = $arTo['symlink_section_data']['IBLOCK_ID'];
				}

				//move and don't delete item
				if(self::_move_from_iblock_to_iblock($arFrom['item_id'], $targetIblockId, $arTo['parent_id'], false, true))
				{
					$statusSymlinkDelete = $this->DELETE(array("element_id" => $arFrom['item_id']));
				}
			}
			else
			{
				if ($this->workflow == 'workflow')
				{
					if ($arTo["parent_id"] != $arFrom["parent_id"])
					{
						$arFields["WF_COMMENTS"] = GetMessage("WD_FILE_IS_MOVED");
						$el->SetElementSection($arFrom["item_id"], $arTo["parent_id"]); // TODO: need to check permissions ???
					}
					else
					{
						$arFields["WF_COMMENTS"] = GetMessage("WD_FILE_IS_RENAMED");
					}

					if ($arTo["parent_id"] != $arFrom["parent_id"] && $arTo["basename"] != $arFrom["element_name"])
						$arFields["WF_COMMENTS"] = GetMessage("WD_FILE_IS_MOVED_AND_RENAMED");
				}
				if ($this->workflow == 'bizproc' || $this->workflow == 'bizproc_limited')
				{
					$this->AddDocumentToHistory($arFrom['item_id'], $arFrom['element_name']);
				}
				$el->Update($arFrom["item_id"], $arFields, $this->workflow == 'workflow', true, false, false); // TODO: need to check permissions ???
				$arCacheCleanID[] = 'element'.$arFrom["item_id"];
				if ($this->workflow == 'bizproc' || $this->workflow == 'bizproc_limited')
				{
					$db_res2 = CIBlockElement::GetList(array(), array("WF_PARENT_ELEMENT_ID" => $arFrom["item_id"], "SHOW_HISTORY" => "Y"), false, false, array("ID"));
					if ($db_res2 && $res2 = $db_res2->Fetch())
					{
						do
						{
							$res = $el->Update($res2["ID"], array("IBLOCK_SECTION_ID" => $arFields["IBLOCK_SECTION_ID"]), false, true, false, false);
							$arCacheCleanID[] = 'element'.$res2["ID"];
						} while ($res2 = $db_res2->Fetch());
					}
				}
			}
		}
		else
		{
			//from symlink copy
			if(!empty($arFrom['is_symlink']) || !empty($arTo['is_symlink']))
			{
				$targetIblockId = $this->IBLOCK_ID;
				if(!empty($arTo['is_symlink']))
				{
					$targetIblockId = $arTo['symlink_section_data']['IBLOCK_ID'];
				}

				//move and don't delete item
				if(!self::_move_from_iblock_to_iblock($arFrom['item_id'], $targetIblockId, $arTo['parent_id'], false, true))
				{
					return '403 Forbidden';
				}
			}
			else
			{
				$options = array(
					'path' => $options["dest_url"],
					'content_length' => $arFrom["file_array"]['FILE_SIZE'],
					'content_type' => $arFrom["file_array"]['CONTENT_TYPE']);
				$stat = $this->PUT($options);
				if ($stat === false)
				{
					return '403 Forbidden';
				}
				elseif (is_resource($stat) && get_resource_type($stat) == 'stream')
				{
					fclose($stat);

					$arTmpFile = CFile::MakeFileArray($arFrom['element_array']['PROPERTY_FILE_VALUE']); // since CopyDirFiles doesn't support clouds
					if (!(is_array($arTmpFile) && is_set($arTmpFile, 'tmp_name')))
						return false;

					CopyDirFiles($arTmpFile['tmp_name'], $options["TMP_FILE"]);
					clearstatcache();

					$options['USER_FIELDS'] = $this->GetUfFieldsSimpleArray($arFrom['item_id']);

					if (!$this->put_commit($options))
					{
						return $this->ThrowError('409 Conflict', "BAD_BP_PERMISSIONS", GetMessage("WD_FILE_ERROR110"), __LINE__);
					}
				}
			}
		}

		$this->_onEvent(
			(($arFrom['parent_id'] != $arTo['parent_id'])?'Move':'Rename').'Finished',
			$arFrom['element_id'],
			'FILE'
		);
	}
	else
	{
		$se = new CIBlockSection();

		$actionRename = $arFrom['parent_id'] == $arTo['parent_id'];
		$actionWithSymlink = !empty($arFrom['is_symlink']) || !empty($arTo['is_symlink']);
		$actionMoveInSymlink = false;
		if($actionWithSymlink)
		{
			$actionMoveInSymlink = $arFrom['symlink_section_data_link'] == $arTo['symlink_section_data_link'];
		}

		//drop == true if this action is @move@
		//not symlink and move! but if action rename in symlink - run this code block
		if (!$actionWithSymlink && $drop || $actionWithSymlink && $actionRename || $actionMoveInSymlink)
		{
			$this->_onEvent(
				(($arFrom['parent_id'] != $arTo['parent_id'])?'Move':'Rename'),
				$arFrom['item_id'],
				'FOLDER',
				array('TO' => (	($arFrom['parent_id'] != $arTo['parent_id']) ? $arTo["parent_id"] : $arTo["basename"]) )
			);

			$GLOBALS['DB']->StartTransaction();

			if (
				isset($options['overwrite'])
				&& ($arTo['is_dir'] === true)
				&& ($arTo['item_id'] !== $arFrom['item_id']) // rename
			)
			{
				$se->Delete($arTo['item_id']);
			}

			$result = $se->Update($arFrom["item_id"], array("NAME" => $arTo["basename"], "IBLOCK_SECTION_ID" => $arTo["parent_id"])); // TODO: need to check permissions ???

			if ($result == false)
			{
				$GLOBALS['DB']->Rollback();

				return $this->ThrowError("409 Conflict",  "SECTION_IS_NOT_UPDATED", ($se->LAST_ERROR ? $se->LAST_ERROR : GetMessage("WD_FILE_ERROR102")), __LINE__);
			}
			else
			{
				$arCacheCleanID[] = 'section'.$arFrom["item_id"];
				$this->ClearCache("section");

				$GLOBALS['DB']->Commit();
			}
		}
		else
		{
			if (isset($options['overwrite']) && ($arTo['is_dir'] === true))
				$se->Delete($arTo['item_id']);

			if ($arTo["item_id"] === false)
			{
				$arPath = explode("/", $options["dest_url"]);
				$this->IsDir(array('path' => "/".implode("/", array_slice($arPath, 0, -1))));

				if ($this->arParams["not_found"] === false)
				{
					if ($this->arParams["item_id"] == 0) // root
						$arTo["dir_array"] = array("LEFT_MARGIN" => 0, "RIGHT_MARGIN" => $this->INT_MAX);

					if (($arTo["dir_array"]["LEFT_MARGIN"] - 1) < $arFrom["dir_array"]["LEFT_MARGIN"] &&
						$arFrom["dir_array"]["RIGHT_MARGIN"] < ($arTo["dir_array"]["RIGHT_MARGIN"] + 1))
					{
						// If folder moved to upper folder
					}
					elseif (
						$arTo["dir_array"]["RIGHT_MARGIN"] < $arFrom["dir_array"]["LEFT_MARGIN"] ||
						$arFrom["dir_array"]["RIGHT_MARGIN"] < $arTo["dir_array"]["LEFT_MARGIN"])
					{
						// if folder moved to neighbourhood folder
					}
					elseif (
						(
							(($arFrom["dir_array"]["LEFT_MARGIN"] - 1) <= $arTo["dir_array"]["LEFT_MARGIN"])
							&&
							($arTo["dir_array"]["RIGHT_MARGIN"] <= ($arFrom["dir_array"]["RIGHT_MARGIN"] + 1))
						)
						||
						(
							$arTo["dir_array"]["ID"] == $arFrom["dir_array"]["ID"]
						)
					)
					{
						return $this->ThrowError( "400 Bad Request", "SECTION_IS_NOT_UPDATED", GetMessage("WD_FILE_ERROR100"), __LINE__);
					}

					$pathBasename = array_slice($arPath, -1, 1);
					if(!empty($arTo['is_symlink']))
					{
						$parentSectionId = $this->arParams["item_id"];
						if($this->arParams["item_id"] == $arTo['symlink_section_data_link']['ID'])
						{
							$parentSectionId = $arTo['symlink_section_data_link'][self::UF_LINK_SECTION_ID];
						}
						$arTo["dir_array"]["ID"] = $se->Add(array(
							"IBLOCK_ID" => $arTo['symlink_section_data']['IBLOCK_ID'],
							"IBLOCK_SECTION_ID" => $parentSectionId,
							"NAME" => end($pathBasename)
						));
						$arTo["dir_array"]['IBLOCK_ID'] = $arTo['symlink_section_data']['IBLOCK_ID'];
					}
					else
					{
						$arTo["dir_array"]["ID"] = $se->Add(array(
							"IBLOCK_ID" => $this->IBLOCK_ID,
							"IBLOCK_SECTION_ID" => $this->arParams["item_id"],
							"NAME" => end($pathBasename)
						));
					}

					if ($arTo["dir_array"]["ID"] === false)
					{
						return $this->ThrowError("409 Conflict", "FOLDER_IS_NOT_MOVED", str_replace(array(
								"#FOLDER#",
								"#TEXT_ERROR#"
							), array(
								"/" . implode("/", $arPath),
								$se->LAST_ERROR
							), GetMessage("WD_FILE_ERROR103")), __LINE__);
					}
					else
					{
						$returnSection = $arTo["dir_array"]["ID"];
						$this->_onEvent('Add', $returnSection, 'FOLDER');
					}
				}
			}
			else
			{
				return $this->ThrowError( "409 Conflict", "FOLDER_IS_NOT_MOVED", str_replace(
					array("#FOLDER#", "#TEXT_ERROR#"),
					array($options["dest_url"], $se->LAST_ERROR), GetMessage("WD_FILE_ERROR103")), __LINE__);
			}

			$arFrom["dir_array"]['is_symlink'] = !empty($arFrom['is_symlink']);
			$arFrom["dir_array"]['symlink_section_data'] = empty($arFrom['symlink_section_data'])? array() : $arFrom['symlink_section_data'];

			$arTo["dir_array"]['is_symlink'] = !empty($arTo['is_symlink']);
			$arTo["dir_array"]['symlink_section_data'] = empty($arTo['symlink_section_data'])? array() : $arTo['symlink_section_data'];

			$result = $this->copy_commit($arFrom["dir_array"], $arTo["dir_array"], $options, $drop);

			if ($result === true && $drop === true)
			{
				if($actionWithSymlink)
				{
					$this::$lastActionMoveWithSymlink = true;
					$this->DELETE(array("section_id" => $arFrom["item_id"]));
				}
				else
				{
					CIBlockSection::Delete($arFrom["item_id"]);
				}

				$this->ClearCache("section");
			}
			elseif (is_string($result) && mb_strpos($result, "403") !== false)
				return $this->ThrowAccessDenied(__LINE__);
		}

		if ($result !== true)
			return $result;
	}

	if($arFrom['element_id'])
	{
		CWebDavDiskDispatcher::sendEventToOwners($arFrom['element_array'], null, 'copy');
	}
	elseif($arFrom['is_dir'])
	{
		CWebDavDiskDispatcher::sendEventToOwners(null, $arFrom['dir_array'], 'copy');
	}
	$this->ClearCache($arCacheCleanID, 'local');

	if (isset($returnSection))
	{
		$this->arParams["changed_section_id"] = $returnSection;
	}

	if($statusSymlinkDelete !== false)
	{
		return $statusSymlinkDelete;
	}
	return ($arTo["not_found"]) ? "201 Created" : "204 No Content";
}