Current Path : /var/www/www-root/data/www/monolith-realty.ru/bitrix/modules/main/lib/controller/ |
Current File : /var/www/www-root/data/www/monolith-realty.ru/bitrix/modules/main/lib/controller/export.php |
<?php namespace Bitrix\Main\Controller; use Bitrix\Main; use Bitrix\Main\Application; use Bitrix\Main\Error; use Bitrix\Main\Localization\Loc; use Bitrix\Main\Engine\Response\AjaxJson; use Bitrix\Main\Security\Sign\Signer; use Bitrix\Main\Web\Json; use Bitrix\Main\ArgumentException; use Bitrix\Main\Security\Sign\BadSignatureException; class Export extends Main\Engine\Controller { /** @var string - Module Id. */ protected $module = ''; /** Where to store cloud files. */ const EXPORT_PATH = '/export/'; /** How many day keep files in the cloud. */ const EXPIRE_DAYS = 24; const ACTION_EXPORT = 'export'; const ACTION_UPLOAD = 'upload'; const ACTION_CANCEL = 'cancel'; const ACTION_FINISH = 'finish'; const ACTION_DOWNLOAD = 'download'; const ACTION_CLEAR = 'clear'; const ACTION_VOID = 'void'; const ACTION_PURGE = 'purge'; const STATUS_COMPLETED = 'COMPLETED'; const STATUS_PROGRESS = 'PROGRESS'; /** @var string */ protected $componentName = ''; /** @var array */ protected $componentParameters = []; /** @var string - Exporting file type csv|excel. */ protected $exportType; const EXPORT_TYPE_CSV = 'csv'; const EXPORT_TYPE_EXCEL = 'excel'; /** @var string */ protected $processToken; /** @var boolean */ protected $isNewProcess = true; /** @var string */ protected $fileName = ''; /** @var string */ protected $filePath = ''; /** @var int */ protected $fileSize = 0; /** @var string */ protected $fileType = 'application/csv'; /** How long to keep temporally files. */ const KEEP_FILE_HOURS = 24; /** @var int */ protected $processedItems = 0; /** @var int */ protected $totalItems = 0; /** @var int */ protected $pageSize = 0; /** Default limitation query result per page. */ const ROWS_PER_PAGE = 100; /** @var int */ protected $lastExportedId = -1; /** @var \CCloudStorageBucket */ protected $bucket; /** @var int */ protected $bucketId = -1; /** @var string */ protected $uploadPath = ''; /** @var int */ protected $uploadedSize = 0; /** @var int */ protected $uploadedPart = 0; /** @var int */ protected $cloudMinChunkSize = 5 * 1024 * 1024; // 5M /** @var string[] */ protected $fieldToStoreInProcess = array( 'exportType', 'fileName', 'filePath', 'fileSize', 'fileType', 'uploadedSize', 'uploadPath', 'uploadedPart', 'pageSize', 'processedItems', 'totalItems', 'lastExportedId', 'cloudChunkSize', 'bucketId', 'isExportCompleted', 'isUploadFinished', 'timeStart', 'stepCount', 'useCloud', ); /** @var boolean */ protected $isBitrix24 = false; /** @var boolean */ protected $isCloudAvailable = false; /** @var boolean */ protected $isExportCompleted = false; /** @var boolean */ protected $isUploadFinished = false; /** @var integer */ protected $timeStart = 0; /** @var integer */ protected $stepCount = 0; /** @var boolean */ protected $useCloud = null; /** @var float seconds */ protected $timeLimit = -1; /** @var float seconds */ protected $hitStartTime = -1; /** @var boolean */ protected $timeLimitReached = false; /** @const int */ const TIME_LIMIT = 20; // 20 seconds /** * Action * @return array */ public function configureActions() { $configureActions = parent::configureActions(); $configureActions[self::ACTION_DOWNLOAD] = array( '-prefilters' => array( Main\Engine\ActionFilter\Csrf::class ) ); return $configureActions; } /** * Initializes controller. * * @return void */ protected function init() { parent::init(); Loc::loadMessages(__FILE__); $this->isBitrix24 = Main\ModuleManager::isModuleInstalled('bitrix24'); $this->isCloudAvailable = Main\ModuleManager::isModuleInstalled('clouds') && Main\Loader::includeModule('clouds'); $this->processToken = $this->request->get('PROCESS_TOKEN'); $this->exportType = $this->request->get('EXPORT_TYPE'); $this->componentName = $this->request->get('COMPONENT_NAME'); $signedParameters = $this->request->getPost('signedParameters'); if (!empty($signedParameters)) { $this->componentParameters = $this->decodeSignedParameters($this->componentName, $signedParameters); } $initialOptions = $this->request->getPost('INITIAL_OPTIONS'); if (!empty($initialOptions)) { $this->componentParameters['STEXPORT_INITIAL_OPTIONS'] = $initialOptions; } $progressData = $this->getProgressParameters(); if (!empty($progressData)) { $this->isNewProcess = (empty($progressData['processToken']) || $progressData['processToken'] !== $this->processToken); if (!$this->isNewProcess) { // restore state foreach ($this->fieldToStoreInProcess as $fieldName) { if (isset($progressData[$fieldName])) { $this->{$fieldName} = $progressData[$fieldName]; } } } } if ($this->isCloudAvailable) { $bucketList = $this->getBucketList(); if (empty($bucketList)) { $this->isCloudAvailable = false; } } } /** * Common operations before process action. * @param Main\Engine\Action $action Action. * @return bool If method will return false, then action will not execute. */ protected function processBeforeAction(Main\Engine\Action $action) { if (parent::processBeforeAction($action)) { if (!$this->checkCommonErrors($action)) { return false; } } return true; } /** * Add name on filed to keep during in the process. * * @param string $fieldName Field name. * * @return self */ protected function keepFieldInProcess($fieldName) { $this->fieldToStoreInProcess[] = $fieldName; return $this; } /** * @return array|Main\Engine\Response\AjaxJson */ public function dispatcherAction() { // can we use cloud if ($this->isCloudAvailable && $this->useCloud === null) { if ( ($this->bucketId < 0) && ($this->totalItems > 0) && ($this->processedItems > 0) && ($this->fileName !== '') && ($this->getSizeTempFile() > 0) && (($this->getSizeTempFile() > $this->cloudMinChunkSize) || $this->isExportCompleted) ) { if ($this->isExportCompleted) { $predictedFileSize = $this->getSizeTempFile(); } else { $predictedFileSize = round($this->getSizeTempFile() * ceil($this->totalItems / $this->processedItems) * 1.5); } // check cloud bucket rules if ($this->findInitBucket(array('fileSize' => $predictedFileSize, 'fileName' => $this->fileName)) === true) { // allow $this->useCloud = true; } else { // deny $this->useCloud = false; $this->isCloudAvailable = false; } } } if ($this->isNewProcess) { // start export $this->stepCount = 0; $this->timeStart = time(); $nextAction = self::ACTION_EXPORT; } elseif ($this->isBitrix24) { if ($this->isExportCompleted && !$this->isUploadFinished) { // upload last chunk $nextAction = self::ACTION_UPLOAD; } elseif ($this->isExportCompleted && $this->isUploadFinished) { // purge cloud and finish $nextAction = self::ACTION_PURGE; } elseif ($this->getSizeTempFile() > $this->cloudMinChunkSize) { // upload chunk $nextAction = self::ACTION_UPLOAD; } else { // continue export into temporally file $nextAction = self::ACTION_EXPORT; } } else { // non b24 if ($this->isExportCompleted && ($this->useCloud === true) && !$this->isUploadFinished) { // upload last chunk $nextAction = self::ACTION_UPLOAD; } elseif ($this->isExportCompleted && ($this->useCloud === true) && $this->isUploadFinished) { // purge cloud and finish $nextAction = self::ACTION_PURGE; } elseif ($this->isExportCompleted && ($this->useCloud === false || $this->useCloud === null)) { // completed $nextAction = self::ACTION_FINISH; } elseif (($this->useCloud === true) && ($this->getSizeTempFile() > $this->cloudMinChunkSize)) { // upload chunk $nextAction = self::ACTION_UPLOAD; } else { // continue export into temporally file $nextAction = self::ACTION_EXPORT; } } if ($nextAction === self::ACTION_PURGE) { return $this->purgeAction(); } if ($nextAction === self::ACTION_EXPORT) { $this->stepCount ++; return $this->exportAction(); } if ($nextAction === self::ACTION_UPLOAD) { $this->stepCount ++; return $this->uploadAction(); } if ($nextAction === self::ACTION_FINISH) { return $this->finishAction(); } return $this->cancelAction(); } /** * @return Main\HttpResponse|void */ public function downloadAction() { if ( $this->filePath !== '' && $this->fileName !== '' ) { $path = new Main\IO\File($this->filePath); if ($path->isExists()) { $response = new Main\Engine\Response\File( $path->getPath(), $this->fileName, $this->getFileType() ); return $response; } } $this->addError(new Error('File not found')); } /** * Performs exporting action. * * @return array|Main\Engine\Response\AjaxJson */ public function exportAction() { /** @global \CMain */ global $APPLICATION; if ($this->isNewProcess) { $this->fileType = $this->getFileType(); $this->fileName = $this->generateExportFileName(); $this->filePath = $this->generateTempDirPath(). $this->fileName; $this->processedItems = 0; $this->totalItems = 0; $this->pageSize = self::ROWS_PER_PAGE; $this->saveProgressParameters(); } $this->startTimer(self::TIME_LIMIT); do { $nextPage = (int)floor($this->processedItems / $this->pageSize) + 1; $componentParameters = array_merge( $this->componentParameters, array( 'STEXPORT_MODE' => 'Y', 'EXPORT_TYPE' => $this->exportType, 'STEXPORT_PAGE_SIZE' => $this->pageSize, 'STEXPORT_TOTAL_ITEMS' => $this->totalItems, 'STEXPORT_LAST_EXPORTED_ID' => $this->lastExportedId, 'PAGE_NUMBER' => $nextPage, ) ); ob_start(); $componentResult = $APPLICATION->IncludeComponent( $this->componentName, '', $componentParameters ); $exportData = ob_get_contents(); ob_end_clean(); $processedItemsOnStep = 0; if (is_array($componentResult)) { if (isset($componentResult['ERROR'])) { $this->addError(new Error($componentResult['ERROR'])); break; } else { if (isset($componentResult['PROCESSED_ITEMS'])) { $processedItemsOnStep = (int)$componentResult['PROCESSED_ITEMS']; } // Get total items quantity on 1st step. if ($nextPage === 1 && isset($componentResult['TOTAL_ITEMS'])) { $this->totalItems = (int)$componentResult['TOTAL_ITEMS']; } if (isset($componentResult['LAST_EXPORTED_ID'])) { $this->lastExportedId = (int)$componentResult['LAST_EXPORTED_ID']; } } } if ($this->totalItems == 0) { break; } if ($processedItemsOnStep > 0) { $this->processedItems += $processedItemsOnStep; $this->writeTempFile($exportData, ($nextPage === 1)); unset($exportData); $this->isExportCompleted = ($this->processedItems >= $this->totalItems); if ($this->isExportCompleted && !$this->isCloudAvailable) { // adjust completed file size $this->fileSize = $this->getSizeTempFile(); } } elseif ($processedItemsOnStep == 0) { // Smth went wrong - terminate process. $this->isExportCompleted = true; if (!$this->isCloudAvailable) { $this->fileSize = $this->getSizeTempFile(); } } if ($this->isExportCompleted === true) { // finish break; } if ($nextPage === 1) { // to answer faster break; } } while ($this->hasTimeLimitReached() !== true); if ($this->totalItems == 0) { $this->isExportCompleted = true; // Save progress $this->saveProgressParameters(); // finish $result = $this->preformAnswer(self::ACTION_VOID); $result['STATUS'] = self::STATUS_COMPLETED; } else { // Save progress $this->saveProgressParameters(); $result = $this->preformAnswer(self::ACTION_EXPORT); $result['STATUS'] = self::STATUS_PROGRESS; } return $result; } /** * Performs of the action uploading into the cloud. * * @return array */ public function uploadAction() { $chunkSize = $this->getSizeTempFile(); $isUploadStarted = ($this->uploadedPart > 0); if (!$isUploadStarted) { $reservedSize = round($chunkSize * ceil($this->totalItems / $this->pageSize) * 1.5); $this->uploadPath = $this->generateUploadDir(). $this->fileName; $this->findInitBucket(array( 'fileSize' => $reservedSize, 'fileName' => $this->uploadPath, )); if ($this->checkCloudErrors()) { $this->saveProgressParameters(); if ($this->bucket->FileExists($this->uploadPath)) { if(!$this->bucket->DeleteFile($this->uploadPath)) { $this->addError(new Error('File exists in a cloud.')); } } } } else { $this->instanceBucket(); $this->checkCloudErrors(); } if (!file_exists($this->filePath)) { $this->addError(new Error('Uploading file not exists.')); } if (!empty($this->getErrors())) { return AjaxJson::createError($this->errorCollection); } $isSuccess = false; if ($this->isExportCompleted && !$isUploadStarted) { // just only one file $uploadFile = array( 'name' => $this->fileName, 'size' => $this->fileSize, 'type' => $this->fileType, 'tmp_name' => $this->filePath, ); if ($this->bucket->SaveFile($this->uploadPath, $uploadFile)) { $this->uploadedPart ++; $this->uploadedSize += $chunkSize; $isSuccess = true; $this->isUploadFinished = true; } else { $this->addError(new Error('Uploading error.')); } } else { $uploader = new \CCloudStorageUpload($this->uploadPath); if (!$uploader->isStarted()) { if (!$uploader->Start($this->bucketId, $reservedSize, $this->fileType)) { $this->addError(new Error('Start uploading error.')); return AjaxJson::createError($this->errorCollection); } } $part = $this->getContentTempFile(); while ($uploader->hasRetries()) { if ($uploader->Next($part, $this->bucket)) { $this->uploadedPart ++; $this->uploadedSize += $chunkSize; $isSuccess = true; break; } } unset($part); // finish if ($isSuccess && $this->isExportCompleted) { if ($uploader->Finish()) { $this->isUploadFinished = true; } else { $this->addError(new Error('FILE_UNKNOWN_ERROR')); } } } if ($isSuccess) { $this->dropTempFile(); } // Save progress $this->saveProgressParameters(); // continue export into temporally file $result = $this->preformAnswer(self::ACTION_UPLOAD); $result['STATUS'] = self::STATUS_PROGRESS; return $result; } /** * Drops temporally and cloud files. * * @return array */ public function clearAction() { $this->cancelAction(); $result = $this->preformAnswer(self::ACTION_CLEAR); $result['STATUS'] = self::STATUS_COMPLETED; return $result; } /** * Cancels exporting and uploading processes. Drops temporally and cloud files. * * @return array */ public function cancelAction() { $this->processToken = null; $this->isNewProcess = true; $this->dropTempFile(); $this->instanceBucket(); if ($this->bucket instanceof \CCloudStorageBucket) { if ($this->bucket->FileExists($this->uploadPath)) { if (!$this->bucket->DeleteFile($this->uploadPath)) { $this->addError(new Error('Cloud drop error.')); } } } $this->clearProgressParameters(); $result = $this->preformAnswer(self::ACTION_CANCEL); $result['STATUS'] = self::STATUS_COMPLETED; return $result; } /** * Drops all outdated cloud files. * * @return array */ public function purgeAction() { $this->instanceBucket(); if ($this->bucket instanceof \CCloudStorageBucket) { $dirFiles = $this->bucket->ListFiles($this->generateUploadDir(), true); $now = new \DateTime('now'); foreach ($dirFiles['file'] as $fileName) { if (preg_match('/^([^_]+)_([0-9]{8})_([^_]+)_.+$/i', $fileName, $parts)) { //$type = $parts[1]; //$hash = $parts[3]; $date = \DateTime::createFromFormat('Ymd', $parts[2]); $interval = $now->diff($date); if ($interval->d > self::EXPIRE_DAYS) { $this->bucket->DeleteFile($this->generateUploadDir(). $fileName); } } } } return $this->finishAction(); } /** * Finishes exporting and uploading processes. Drops temporally. * * @return array */ public function finishAction() { if ($this->isExportCompleted && !$this->isCloudAvailable) { // adjust completed local file size $this->fileSize = $this->getSizeTempFile(); } $result = $this->preformAnswer(self::ACTION_FINISH); $result['STATUS'] = self::STATUS_COMPLETED; return $result; } /** * Preforms action answer. * @param string $action Action. * @return array */ protected function preformAnswer($action) { if ($action == self::ACTION_CLEAR || $action == self::ACTION_CANCEL || $action == self::ACTION_PURGE) { $result = []; } else { $fileSize = $this->fileSize; if (!$this->isExportCompleted && $this->uploadedSize > 0) { $fileSize = $this->uploadedSize + $this->fileSize; } elseif ($this->isExportCompleted && $this->uploadedSize > $this->fileSize) { $fileSize = $this->uploadedSize; } $result = array( 'STATUS' => ($this->isExportCompleted ? self::STATUS_COMPLETED : self::STATUS_PROGRESS), 'PROCESSED_ITEMS' => $this->processedItems, 'TOTAL_ITEMS' => $this->totalItems, 'UPLOADED_PART' => $this->uploadedPart, 'UPLOADED_SIZE' => $this->uploadedSize, 'UPLOADED_SIZE_FORMAT' => \CFile::FormatSize($this->uploadedSize), 'FILE_SIZE_FORMAT' => \CFile::FormatSize($fileSize), ); $messagePlaceholders = array( '#PROCESSED_ITEMS#' => $this->processedItems, '#TOTAL_ITEMS#' => $this->totalItems, '#UPLOADED_PART#' => $this->uploadedPart, '#UPLOADED_SIZE#' => $this->uploadedSize, '#UPLOADED_SIZE_FORMAT#' => \CFile::FormatSize($this->uploadedSize), '#FILE_SIZE_FORMAT#' => \CFile::FormatSize($fileSize), ); } if ($action == self::ACTION_VOID) { $message = htmlspecialcharsbx(Loc::getMessage('MAIN_EXPORT_VOID')); } elseif ($action == self::ACTION_PURGE) { $message = htmlspecialcharsbx(Loc::getMessage('MAIN_EXPORT_PURGE')); } elseif ($action == self::ACTION_CLEAR) { $message = htmlspecialcharsbx(Loc::getMessage('MAIN_EXPORT_FILE_DROPPED')); } elseif ($action == self::ACTION_CANCEL) { $message = htmlspecialcharsbx(Loc::getMessage('MAIN_EXPORT_ACTION_CANCEL')); } elseif ($action == self::ACTION_UPLOAD) { $message = htmlspecialcharsbx(Loc::getMessage('MAIN_EXPORT_ACTION_UPLOAD', $messagePlaceholders)). '<br>'. $this->preformExpectedDuration(); } elseif ($action == self::ACTION_EXPORT) { $message = htmlspecialcharsbx(Loc::getMessage('MAIN_EXPORT_ACTION_EXPORT', $messagePlaceholders)). '<br>'. $this->preformExpectedDuration(); } elseif ($this->isExportCompleted) { $message = htmlspecialcharsbx(Loc::getMessage('MAIN_EXPORT_COMPLETED')). '<br>'. htmlspecialcharsbx(Loc::getMessage('MAIN_EXPORT_ACTION_EXPORT', $messagePlaceholders)); $downloadLink = ''; if ($this->isBitrix24 || $this->useCloud === true) { if ($this->isUploadFinished) { $downloadLink = $this->generateCloudLink(); } } else { $downloadLink = $this->generateDownloadLink(); } if ($downloadLink !== '') { $result['DOWNLOAD_LINK'] = $downloadLink; $result['FILE_NAME'] = $this->fileName; $result['DOWNLOAD_LINK_NAME'] = htmlspecialcharsbx(Loc::getMessage('MAIN_EXPORT_DOWNLOAD')); $result['CLEAR_LINK_NAME'] = htmlspecialcharsbx(Loc::getMessage('MAIN_EXPORT_CLEAR')); } } else { $message = htmlspecialcharsbx(Loc::getMessage('MAIN_EXPORT_ACTION_EXPORT', $messagePlaceholders)). '<br>'. $this->preformExpectedDuration(); } $result['SUMMARY_HTML'] = $message; return $result; } /** * Preform expected duration message. * * @return string */ protected function preformExpectedDuration() { $message = ''; $avgStepDuration = $predictedStepCount = $predictedTimeDuration = 0; $avgRowsPerStep = self::ROWS_PER_PAGE; if ($this->stepCount > 0 && $this->timeStart > 0) { $avgStepDuration = round((time() - $this->timeStart) / $this->stepCount); if ($this->processedItems > 0) { $avgRowsPerStep = round($this->processedItems / $this->stepCount); } } if ($this->totalItems > 0) { $predictedStepCount = round(($this->totalItems - $this->processedItems) / $avgRowsPerStep); if ($this->useCloud === true) { $predictedStepCount *= 2; } } if ($avgStepDuration > 0 && $predictedStepCount > 0) { $predictedTimeDuration = $avgStepDuration * $predictedStepCount * 1.1; } if ($predictedTimeDuration > 0) { $predictedTimeDurationHours = floor($predictedTimeDuration / 3600); if ($predictedTimeDurationHours > 0) { $predictedTimeDurationMinutes = ceil(($predictedTimeDuration - $predictedTimeDurationHours * 3600) / 60); $message = Loc::getMessage('MAIN_EXPORT_EXPECTED_DURATION').' '. Loc::getMessage('MAIN_EXPORT_EXPECTED_DURATION_HOURS', array( '#HOURS#' => $predictedTimeDurationHours, '#MINUTES#' => $predictedTimeDurationMinutes, )); } else { $predictedTimeDurationMinutes = round($predictedTimeDuration / 60); $message = Loc::getMessage('MAIN_EXPORT_EXPECTED_DURATION').' '. Loc::getMessage('MAIN_EXPORT_EXPECTED_DURATION_MINUTES', array( '#MINUTES#' => ($predictedTimeDurationMinutes < 1 ? "< 1" : $predictedTimeDurationMinutes), )); } } return $message; } /** * Generate link to download local exported temporally file. * * @return string */ protected function generateDownloadLink() { $params = array( 'PROCESS_TOKEN' => $this->processToken, 'EXPORT_TYPE' => $this->exportType, 'COMPONENT_NAME' => $this->componentName, ); return $this->getActionUri(self::ACTION_DOWNLOAD, $params); } /** * Generate link to download exported file from cloud. * * @return string */ protected function generateCloudLink() { $this->instanceBucket(); if ($this->checkCloudErrors()) { return $this->bucket->GetFileSRC($this->uploadPath); } return ''; } /** * Checks for common errors. * * @param \Bitrix\Main\Engine\Action $action Action. * @return bool - True if errors not exist. */ protected function checkCommonErrors($action) { if (empty($this->module)) { $this->addError(new Error('Module Id property is not filled.')); } if (!Main\Loader::includeModule($this->module)) { $this->addError(new Error('Module '.$this->module.' is not included.')); } if ($this->isBitrix24) { if ($this->isCloudAvailable !== true) { $this->addError(new Error(Loc::getMessage('MAIN_EXPORT_ERROR_NO_CLOUD_BUCKET'))); } } if ($action->getName() === self::ACTION_PURGE) { return true; } if ($this->componentName === '') { $this->addError(new Error('Component name is not specified.')); } if (!in_array($this->exportType, array(self::EXPORT_TYPE_CSV, self::EXPORT_TYPE_EXCEL), true)) { $this->addError(new Error('The export type is not supported.')); } if($this->processToken === '') { $this->addError(new Error('Process token is not specified.')); } return empty($this->getErrors()); } /** * Checks for cloud errors. * * @return bool - True if errors not exist. */ protected function checkCloudErrors() { if (!($this->bucket instanceof \CCloudStorageBucket)) { $this->errorCollection[] = new Error(Loc::getMessage('MAIN_EXPORT_ERROR_NO_CLOUD_BUCKET')); } return empty($this->getErrors()); } /** * Finds and instantiates the cloud bucket for file attributes. * * @param array $attributes File attributes for bucket search. * * @return bool */ protected function findInitBucket(array $attributes) { if (!($this->bucket instanceof \CCloudStorageBucket)) { $this->bucket = \CCloudStorage::FindBucketForFile( array( 'FILE_SIZE' => $attributes['fileSize'], 'MODULE_ID' => $this->module, ), $attributes['fileName'] ); if ( $this->bucket === null || !($this->bucket instanceof \CCloudStorageBucket) || !$this->bucket->init() ) { return false; } $this->bucketId = $this->bucket->ID; $this->cloudMinChunkSize = $this->bucket->GetService()->GetMinUploadPartSize(); //5M } return true; } /** * Returns the cloud bucket for file attributes. * * @return \CCloudStorageBucket */ protected function instanceBucket() { if (!($this->bucket instanceof \CCloudStorageBucket)) { if ($this->bucketId > 0) { $this->bucket = new \CCloudStorageBucket($this->bucketId); $this->bucket->init(); } } return $this->bucket; } /** * Returns active cloud bucket list. * * @param array $filter Filter params. * * @return array */ protected function getBucketList($filter = []) { $result = []; $res = \CCloudStorageBucket::GetList([], array_merge(array('ACTIVE' => 'Y', 'READ_ONLY' => 'N'), $filter)); while($bucket = $res->Fetch()) { $result[] = $bucket; } return $result; } /** * Save progress parameters. * * @return void */ protected function saveProgressParameters(): void { // store state $progressData = [ 'processToken' => $this->processToken, ]; foreach ($this->fieldToStoreInProcess as $fieldName) { $progressData[$fieldName] = $this->{$fieldName}; } $progressData = (new Signer())->sign(Json::encode($progressData)); $localStorage = Application::getInstance()->getLocalSession($this->module.'_export_stepper'); $localStorage->set($this->getProgressParameterOptionName(), $progressData); } /** * Load progress parameters. * * @return array */ protected function getProgressParameters() { $localStorage = Application::getInstance()->getLocalSession($this->module.'_export_stepper'); $progressData = $localStorage->get($this->getProgressParameterOptionName()); if ($progressData) { try { $progressData = Json::decode((new Signer())->unsign($progressData)); } catch (BadSignatureException $exception) { $progressData = []; } catch (ArgumentException $exception) { $progressData = []; } } if (!is_array($progressData)) { $progressData = []; } return $progressData; } /** * Removes progress parameters. * * @return void */ protected function clearProgressParameters(): void { $localStorage = Application::getInstance()->getLocalSession($this->module.'_export_stepper'); $localStorage->offsetUnset($this->getProgressParameterOptionName()); } /** * Returns progress option name * * @return string */ protected function getProgressParameterOptionName() { return $this->module. '_cloud_export'; } /** * Returns file name * * @return string */ protected function generateExportFileName() { if ($this->exportType === self::EXPORT_TYPE_CSV) { $fileExt = 'csv'; } elseif ($this->exportType === self::EXPORT_TYPE_EXCEL) { $fileExt = 'xls'; } $prefix = date('Ymd'); $hash = str_pad(dechex(crc32($prefix)), 8, '0', STR_PAD_LEFT); return uniqid($prefix. '_'. $hash. '_', false).'.'.$fileExt; } /** * Returns file mime/type * * @return string */ protected function getFileType() { if ($this->exportType === self::EXPORT_TYPE_CSV) { $fileType = 'application/csv'; } elseif ($this->exportType === self::EXPORT_TYPE_EXCEL) { $fileType = 'application/vnd.ms-excel'; } return $fileType; } /** * Returns temporally directory * * @return string */ protected function generateTempDirPath() { $tempDir = \CTempFile::GetDirectoryName(self::KEEP_FILE_HOURS, array($this->module, uniqid( 'export_', true))); \CheckDirPath($tempDir); return $tempDir; } /** * Returns path to upload * * @return string */ protected function generateUploadDir() { return self::EXPORT_PATH; } /** * Sings and returns parameters as string in format "{message}{separator}{signature}". * * @param string $componentName Component name. * @param array $params Parameters of component. * * @return string * @throws \Bitrix\Main\ArgumentTypeException */ protected function getSignedParameters($componentName, array $params) { return Main\Component\ParameterSigner::signParameters($componentName, $params); } /** * Sings and stores parameters. * * @param string $componentName Component name. * @param string $signedParameters Signed parameters of component as string. * * @return array * @throws \Bitrix\Main\ArgumentTypeException */ protected function decodeSignedParameters($componentName, $signedParameters) { return Main\Component\ParameterSigner::unsignParameters($componentName, $signedParameters); } /** * @param string $data Bytes to write. * @param boolean $precedeUtf8Bom Precede first bites with UTF8 BOM mark. * @return void */ protected function writeTempFile($data, $precedeUtf8Bom = true) { $file = fopen($this->filePath, 'ab'); if(is_resource($file)) { // add UTF-8 BOM marker if (\Bitrix\Main\Application::isUtfMode() || defined('BX_UTF')) { if($precedeUtf8Bom === true && (filesize($this->filePath) === 0)) { fwrite($file, chr(239).chr(187).chr(191)); } } fwrite($file, $data); fclose($file); unset($file); $this->fileSize = filesize($this->filePath); } } /** * @return void */ protected function dropTempFile() { if (file_exists($this->filePath)) { @unlink($this->filePath); } $this->fileSize = 0; } /** * @return string */ protected function getContentTempFile() { return file_get_contents($this->filePath); } /** * @return int */ protected function getSizeTempFile() { $this->fileSize = filesize($this->filePath); return $this->fileSize; } /** * Start up timer. * * @param int $timeLimit Time limit. * @return void */ protected function startTimer($timeLimit = 25) { $this->timeLimit = $timeLimit; if (defined('START_EXEC_TIME') && START_EXEC_TIME > 0) { $this->hitStartTime = (int)START_EXEC_TIME; } else { $this->hitStartTime = time(); } } /** * Tells true if time limit reached. * * @return boolean */ protected function hasTimeLimitReached() { if ($this->timeLimit > 0) { if ($this->timeLimitReached) { return true; } if ((time() - $this->hitStartTime) >= $this->timeLimit) { $this->timeLimitReached = true; return true; } } return false; } }