1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
export.php
См. документацию.
1<?php
2namespace Bitrix\Main\Controller;
3
4use Bitrix\Main;
5use Bitrix\Main\Application;
6use Bitrix\Main\Error;
7use Bitrix\Main\Localization\Loc;
8use Bitrix\Main\Engine\Response\AjaxJson;
9use Bitrix\Main\Security\Sign\Signer;
10use Bitrix\Main\Web\Json;
11use Bitrix\Main\ArgumentException;
12use Bitrix\Main\Security\Sign\BadSignatureException;
13
15{
17 protected $module = '';
18
20 const EXPORT_PATH = '/export/';
21
23 const EXPIRE_DAYS = 24;
24
25 const ACTION_EXPORT = 'export';
26 const ACTION_UPLOAD = 'upload';
27 const ACTION_CANCEL = 'cancel';
28 const ACTION_FINISH = 'finish';
29 const ACTION_DOWNLOAD = 'download';
30 const ACTION_CLEAR = 'clear';
31 const ACTION_VOID = 'void';
32 const ACTION_PURGE = 'purge';
33
34 const STATUS_COMPLETED = 'COMPLETED';
35 const STATUS_PROGRESS = 'PROGRESS';
36
37
39 protected $componentName = '';
40
42 protected $componentParameters = [];
43
45 protected $exportType;
46
47 const EXPORT_TYPE_CSV = 'csv';
48 const EXPORT_TYPE_EXCEL = 'excel';
49
51 protected $processToken;
52
54 protected $isNewProcess = true;
55
57 protected $fileName = '';
58
60 protected $filePath = '';
61
63 protected $fileSize = 0;
64
66 protected $fileType = 'application/csv';
67
69 const KEEP_FILE_HOURS = 24;
70
72 protected $processedItems = 0;
73
75 protected $totalItems = 0;
76
78 protected $pageSize = 0;
79
81 const ROWS_PER_PAGE = 100;
82
84 protected $lastExportedId = -1;
85
87 protected $bucket;
88
90 protected $bucketId = -1;
91
93 protected $uploadPath = '';
94
96 protected $uploadedSize = 0;
97
99 protected $uploadedPart = 0;
100
102 protected $cloudMinChunkSize = 5 * 1024 * 1024; // 5M
103
106 'exportType',
107 'fileName',
108 'filePath',
109 'fileSize',
110 'fileType',
111 'uploadedSize',
112 'uploadPath',
113 'uploadedPart',
114 'pageSize',
115 'processedItems',
116 'totalItems',
117 'lastExportedId',
118 'cloudChunkSize',
119 'bucketId',
120 'isExportCompleted',
121 'isUploadFinished',
122 'timeStart',
123 'stepCount',
124 'useCloud',
125 );
126
128 protected $isBitrix24 = false;
129
131 protected $isCloudAvailable = false;
132
134 protected $isExportCompleted = false;
135
137 protected $isUploadFinished = false;
138
140 protected $timeStart = 0;
141
143 protected $stepCount = 0;
144
146 protected $useCloud = null;
147
149 protected $timeLimit = -1;
150
152 protected $hitStartTime = -1;
153
155 protected $timeLimitReached = false;
156
158 const TIME_LIMIT = 20; // 20 seconds
159
164 public function configureActions()
165 {
166 $configureActions = parent::configureActions();
167 $configureActions[self::ACTION_DOWNLOAD] = array(
168 '-prefilters' => array(
169 Main\Engine\ActionFilter\Csrf::class
170 )
171 );
172
173 return $configureActions;
174 }
175
181 protected function init()
182 {
183 parent::init();
184
185 Loc::loadMessages(__FILE__);
186
187 $this->isBitrix24 = Main\ModuleManager::isModuleInstalled('bitrix24');
188
189 $this->isCloudAvailable =
192
193 $this->processToken = $this->request->get('PROCESS_TOKEN');
194 $this->exportType = $this->request->get('EXPORT_TYPE');
195 $this->componentName = $this->request->get('COMPONENT_NAME');
196
197 $signedParameters = $this->request->getPost('signedParameters');
198 if (!empty($signedParameters))
199 {
200 $this->componentParameters = $this->decodeSignedParameters($this->componentName, $signedParameters);
201 }
202
203 $initialOptions = $this->request->getPost('INITIAL_OPTIONS');
204 if (!empty($initialOptions))
205 {
206 $this->componentParameters['STEXPORT_INITIAL_OPTIONS'] = $initialOptions;
207 }
208
209 $progressData = $this->getProgressParameters();
210 if (!empty($progressData))
211 {
212 $this->isNewProcess = (empty($progressData['processToken']) || $progressData['processToken'] !== $this->processToken);
213 if (!$this->isNewProcess)
214 {
215 // restore state
216 foreach ($this->fieldToStoreInProcess as $fieldName)
217 {
218 if (isset($progressData[$fieldName]))
219 {
220 $this->{$fieldName} = $progressData[$fieldName];
221 }
222 }
223 }
224 }
225
226 if ($this->isCloudAvailable)
227 {
228 $bucketList = $this->getBucketList();
229 if (empty($bucketList))
230 {
231 $this->isCloudAvailable = false;
232 }
233 }
234 }
235
236
243 {
244 if (parent::processBeforeAction($action))
245 {
246 if (!$this->checkCommonErrors($action))
247 {
248 return false;
249 }
250 }
251
252 return true;
253 }
254
262 protected function keepFieldInProcess($fieldName)
263 {
264 $this->fieldToStoreInProcess[] = $fieldName;
265
266 return $this;
267 }
268
272 public function dispatcherAction()
273 {
274 // can we use cloud
275 if ($this->isCloudAvailable && $this->useCloud === null)
276 {
277 if (
278 ($this->bucketId < 0) &&
279 ($this->totalItems > 0) &&
280 ($this->processedItems > 0) &&
281 ($this->fileName !== '') &&
282 ($this->getSizeTempFile() > 0) &&
283 (($this->getSizeTempFile() > $this->cloudMinChunkSize) || $this->isExportCompleted)
284 )
285 {
286 if ($this->isExportCompleted)
287 {
288 $predictedFileSize = $this->getSizeTempFile();
289 }
290 else
291 {
292 $predictedFileSize = round($this->getSizeTempFile() * ceil($this->totalItems / $this->processedItems) * 1.5);
293 }
294 // check cloud bucket rules
295 if ($this->findInitBucket(array('fileSize' => $predictedFileSize, 'fileName' => $this->fileName)) === true)
296 {
297 // allow
298 $this->useCloud = true;
299 }
300 else
301 {
302 // deny
303 $this->useCloud = false;
304 $this->isCloudAvailable = false;
305 }
306 }
307 }
308
309 if ($this->isNewProcess)
310 {
311 // start export
312 $this->stepCount = 0;
313 $this->timeStart = time();
314 $nextAction = self::ACTION_EXPORT;
315 }
316 elseif ($this->isBitrix24)
317 {
318 if ($this->isExportCompleted && !$this->isUploadFinished)
319 {
320 // upload last chunk
321 $nextAction = self::ACTION_UPLOAD;
322 }
323 elseif ($this->isExportCompleted && $this->isUploadFinished)
324 {
325 // purge cloud and finish
326 $nextAction = self::ACTION_PURGE;
327 }
328 elseif ($this->getSizeTempFile() > $this->cloudMinChunkSize)
329 {
330 // upload chunk
331 $nextAction = self::ACTION_UPLOAD;
332 }
333 else
334 {
335 // continue export into temporally file
336 $nextAction = self::ACTION_EXPORT;
337 }
338 }
339 else
340 {
341 // non b24
342 if ($this->isExportCompleted && ($this->useCloud === true) && !$this->isUploadFinished)
343 {
344 // upload last chunk
345 $nextAction = self::ACTION_UPLOAD;
346 }
347 elseif ($this->isExportCompleted && ($this->useCloud === true) && $this->isUploadFinished)
348 {
349 // purge cloud and finish
350 $nextAction = self::ACTION_PURGE;
351 }
352 elseif ($this->isExportCompleted && ($this->useCloud === false || $this->useCloud === null))
353 {
354 // completed
355 $nextAction = self::ACTION_FINISH;
356 }
357 elseif (($this->useCloud === true) && ($this->getSizeTempFile() > $this->cloudMinChunkSize))
358 {
359 // upload chunk
360 $nextAction = self::ACTION_UPLOAD;
361 }
362 else
363 {
364 // continue export into temporally file
365 $nextAction = self::ACTION_EXPORT;
366 }
367 }
368
369 if ($nextAction === self::ACTION_PURGE)
370 {
371 return $this->purgeAction();
372 }
373 if ($nextAction === self::ACTION_EXPORT)
374 {
375 $this->stepCount ++;
376 return $this->exportAction();
377 }
378 if ($nextAction === self::ACTION_UPLOAD)
379 {
380 $this->stepCount ++;
381 return $this->uploadAction();
382 }
383 if ($nextAction === self::ACTION_FINISH)
384 {
385 return $this->finishAction();
386 }
387
388 return $this->cancelAction();
389 }
390
391
395 public function downloadAction()
396 {
397 if (
398 $this->filePath !== '' &&
399 $this->fileName !== ''
400 )
401 {
402 $path = new Main\IO\File($this->filePath);
403 if ($path->isExists())
404 {
405
407 $path->getPath(),
408 $this->fileName,
409 $this->getFileType()
410 );
411
412 return $response;
413 }
414 }
415
416 $this->addError(new Error('File not found'));
417 }
418
419
425 public function exportAction()
426 {
428 global $APPLICATION;
429
430 if ($this->isNewProcess)
431 {
432 $this->fileType = $this->getFileType();
433 $this->fileName = $this->generateExportFileName();
434 $this->filePath = $this->generateTempDirPath(). $this->fileName;
435 $this->processedItems = 0;
436 $this->totalItems = 0;
437 $this->pageSize = $this->getRowsPerPage();
438 $this->saveProgressParameters();
439 }
440
441 $this->startTimer(self::TIME_LIMIT);
442 do
443 {
444 $nextPage = (int)floor($this->processedItems / $this->pageSize) + 1;
445
446 $componentParameters = array_merge(
447 $this->componentParameters,
448 array(
449 'STEXPORT_MODE' => 'Y',
450 'EXPORT_TYPE' => $this->exportType,
451 'STEXPORT_PAGE_SIZE' => $this->pageSize,
452 'STEXPORT_TOTAL_ITEMS' => $this->totalItems,
453 'STEXPORT_LAST_EXPORTED_ID' => $this->lastExportedId,
454 'PAGE_NUMBER' => $nextPage,
455 )
456 );
457
458 ob_start();
459 $componentResult = $APPLICATION->IncludeComponent(
460 $this->componentName,
461 '',
463 );
464 $exportData = ob_get_contents();
465 ob_end_clean();
466
467 $processedItemsOnStep = 0;
468
469 if (is_array($componentResult))
470 {
471 if (isset($componentResult['ERROR']))
472 {
473 $this->addError(new Error($componentResult['ERROR']));
474 break;
475 }
476 else
477 {
478 if (isset($componentResult['PROCESSED_ITEMS']))
479 {
480 $processedItemsOnStep = (int)$componentResult['PROCESSED_ITEMS'];
481 }
482
483 // Get total items quantity on 1st step.
484 if ($nextPage === 1 && isset($componentResult['TOTAL_ITEMS']))
485 {
486 $this->totalItems = (int)$componentResult['TOTAL_ITEMS'];
487 }
488
489 if (isset($componentResult['LAST_EXPORTED_ID']))
490 {
491 $this->lastExportedId = (int)$componentResult['LAST_EXPORTED_ID'];
492 }
493 }
494 }
495
496 if ($this->totalItems == 0)
497 {
498 break;
499 }
500
501 if ($processedItemsOnStep > 0)
502 {
503 $this->processedItems += $processedItemsOnStep;
504
505 $this->writeTempFile($exportData, ($nextPage === 1));
506 unset($exportData);
507
508 $this->isExportCompleted = ($this->processedItems >= $this->totalItems);
509
510 if ($this->isExportCompleted && !$this->isCloudAvailable)
511 {
512 // adjust completed file size
513 $this->fileSize = $this->getSizeTempFile();
514 }
515 }
516 elseif ($processedItemsOnStep == 0)
517 {
518 // Smth went wrong - terminate process.
519 $this->isExportCompleted = true;
520
521 if (!$this->isCloudAvailable)
522 {
523 $this->fileSize = $this->getSizeTempFile();
524 }
525 }
526
527 if ($this->isExportCompleted === true)
528 {
529 // finish
530 break;
531 }
532 if ($nextPage === 1)
533 {
534 // to answer faster
535 break;
536 }
537 }
538 while ($this->hasTimeLimitReached() !== true);
539
540 if ($this->totalItems == 0)
541 {
542 $this->isExportCompleted = true;
543
544 // Save progress
545 $this->saveProgressParameters();
546
547 // finish
548 $result = $this->preformAnswer(self::ACTION_VOID);
549 $result['STATUS'] = self::STATUS_COMPLETED;
550 }
551 else
552 {
553 // Save progress
554 $this->saveProgressParameters();
555
556 $result = $this->preformAnswer(self::ACTION_EXPORT);
557 $result['STATUS'] = self::STATUS_PROGRESS;
558 }
559
560 return $result;
561 }
562
563
569 public function uploadAction()
570 {
571 $chunkSize = $this->getSizeTempFile();
572
573 $isUploadStarted = ($this->uploadedPart > 0);
574 if (!$isUploadStarted)
575 {
576 $reservedSize = round($chunkSize * ceil($this->totalItems / $this->pageSize) * 1.5);
577
578 $this->uploadPath = $this->generateUploadDir(). $this->fileName;
579
580 $this->findInitBucket(array(
581 'fileSize' => $reservedSize,
582 'fileName' => $this->uploadPath,
583 ));
584
585 if ($this->checkCloudErrors())
586 {
587 $this->saveProgressParameters();
588
589 if ($this->bucket->FileExists($this->uploadPath))
590 {
591 if(!$this->bucket->DeleteFile($this->uploadPath))
592 {
593 $this->addError(new Error('File exists in a cloud.'));
594 }
595 }
596 }
597 }
598 else
599 {
600 $this->instanceBucket();
601
602 $this->checkCloudErrors();
603 }
604
605
606 if (!file_exists($this->filePath))
607 {
608 $this->addError(new Error('Uploading file not exists.'));
609 }
610
611 if (!empty($this->getErrors()))
612 {
613 return AjaxJson::createError($this->errorCollection);
614 }
615
616
617 $isSuccess = false;
618
619 if ($this->isExportCompleted && !$isUploadStarted)
620 {
621 // just only one file
622 $uploadFile = array(
623 'name' => $this->fileName,
624 'size' => $this->fileSize,
625 'type' => $this->fileType,
626 'tmp_name' => $this->filePath,
627 );
628
629 if ($this->bucket->SaveFile($this->uploadPath, $uploadFile))
630 {
631 $this->uploadedPart ++;
632 $this->uploadedSize += $chunkSize;
633 $isSuccess = true;
634 $this->isUploadFinished = true;
635 }
636 else
637 {
638 $this->addError(new Error('Uploading error.'));
639 }
640 }
641 else
642 {
643 $uploader = new \CCloudStorageUpload($this->uploadPath);
644 if (!$uploader->isStarted())
645 {
646 if (!$uploader->Start($this->bucketId, $reservedSize, $this->fileType))
647 {
648 $this->addError(new Error('Start uploading error.'));
649
650 return AjaxJson::createError($this->errorCollection);
651 }
652 }
653
654 $part = $this->getContentTempFile();
655
656 while ($uploader->hasRetries())
657 {
658 if ($uploader->Next($part, $this->bucket))
659 {
660 $this->uploadedPart ++;
661 $this->uploadedSize += $chunkSize;
662 $isSuccess = true;
663 break;
664 }
665 }
666 unset($part);
667
668 // finish
669 if ($isSuccess && $this->isExportCompleted)
670 {
671 if ($uploader->Finish())
672 {
673 $this->isUploadFinished = true;
674 }
675 else
676 {
677 $this->addError(new Error('FILE_UNKNOWN_ERROR'));
678 }
679 }
680 }
681
682 if ($isSuccess)
683 {
684 $this->dropTempFile();
685 }
686
687 // Save progress
688 $this->saveProgressParameters();
689
690 // continue export into temporally file
691 $result = $this->preformAnswer(self::ACTION_UPLOAD);
692 $result['STATUS'] = self::STATUS_PROGRESS;
693
694 return $result;
695 }
696
697
703 public function clearAction()
704 {
705 $this->cancelAction();
706
707 $result = $this->preformAnswer(self::ACTION_CLEAR);
708
709 $result['STATUS'] = self::STATUS_COMPLETED;
710
711 return $result;
712
713 }
714
715
721 public function cancelAction()
722 {
723 $this->processToken = null;
724 $this->isNewProcess = true;
725
726 $this->dropTempFile();
727
728 $this->instanceBucket();
729
730 if ($this->bucket instanceof \CCloudStorageBucket)
731 {
732 if ($this->bucket->FileExists($this->uploadPath))
733 {
734 if (!$this->bucket->DeleteFile($this->uploadPath))
735 {
736 $this->addError(new Error('Cloud drop error.'));
737 }
738 }
739 }
740
742
743 $result = $this->preformAnswer(self::ACTION_CANCEL);
744
745 $result['STATUS'] = self::STATUS_COMPLETED;
746
747 return $result;
748 }
749
750
756 public function purgeAction()
757 {
758 $this->instanceBucket();
759
760 if ($this->bucket instanceof \CCloudStorageBucket)
761 {
762 $dirFiles = $this->bucket->ListFiles($this->generateUploadDir(), true);
763
764 $now = new \DateTime('now');
765 foreach ($dirFiles['file'] as $fileName)
766 {
767 if (preg_match('/^([^_]+)_([0-9]{8})_([^_]+)_.+$/i', $fileName, $parts))
768 {
769 //$type = $parts[1];
770 //$hash = $parts[3];
771 $date = \DateTime::createFromFormat('Ymd', $parts[2]);
772 $interval = $now->diff($date);
773 if ($interval->d > self::EXPIRE_DAYS)
774 {
775 $this->bucket->DeleteFile($this->generateUploadDir(). $fileName);
776 }
777 }
778 }
779 }
780
781 return $this->finishAction();
782 }
783
784
790 public function finishAction()
791 {
792 if ($this->isExportCompleted && !$this->isCloudAvailable)
793 {
794 // adjust completed local file size
795 $this->fileSize = $this->getSizeTempFile();
796 }
797
798 $result = $this->preformAnswer(self::ACTION_FINISH);
799
800 $result['STATUS'] = self::STATUS_COMPLETED;
801
802 return $result;
803 }
804
805
811 protected function preformAnswer($action)
812 {
813 if ($action == self::ACTION_CLEAR || $action == self::ACTION_CANCEL || $action == self::ACTION_PURGE)
814 {
815 $result = [];
816 }
817 else
818 {
820 if (!$this->isExportCompleted && $this->uploadedSize > 0)
821 {
822 $fileSize = $this->uploadedSize + $this->fileSize;
823 }
824 elseif ($this->isExportCompleted && $this->uploadedSize > $this->fileSize)
825 {
827 }
828
829 $result = array(
830 'STATUS' => ($this->isExportCompleted ? self::STATUS_COMPLETED : self::STATUS_PROGRESS),
831 'PROCESSED_ITEMS' => $this->processedItems,
832 'TOTAL_ITEMS' => $this->totalItems,
833 'UPLOADED_PART' => $this->uploadedPart,
834 'UPLOADED_SIZE' => $this->uploadedSize,
835 'UPLOADED_SIZE_FORMAT' => \CFile::FormatSize($this->uploadedSize),
836 'FILE_SIZE_FORMAT' => \CFile::FormatSize($fileSize),
837 );
838
839 $messagePlaceholders = array(
840 '#PROCESSED_ITEMS#' => $this->processedItems,
841 '#TOTAL_ITEMS#' => $this->totalItems,
842 '#UPLOADED_PART#' => $this->uploadedPart,
843 '#UPLOADED_SIZE#' => $this->uploadedSize,
844 '#UPLOADED_SIZE_FORMAT#' => \CFile::FormatSize($this->uploadedSize),
845 '#FILE_SIZE_FORMAT#' => \CFile::FormatSize($fileSize),
846 );
847 }
848
849 if ($action == self::ACTION_VOID)
850 {
851 $message = htmlspecialcharsbx(Loc::getMessage('MAIN_EXPORT_VOID'));
852 }
853 elseif ($action == self::ACTION_PURGE)
854 {
855 $message = htmlspecialcharsbx(Loc::getMessage('MAIN_EXPORT_PURGE'));
856 }
857 elseif ($action == self::ACTION_CLEAR)
858 {
859 $message = htmlspecialcharsbx(Loc::getMessage('MAIN_EXPORT_FILE_DROPPED'));
860 }
861 elseif ($action == self::ACTION_CANCEL)
862 {
863 $message = htmlspecialcharsbx(Loc::getMessage('MAIN_EXPORT_ACTION_CANCEL'));
864 }
865 elseif ($action == self::ACTION_UPLOAD)
866 {
867 $message =
868 htmlspecialcharsbx(Loc::getMessage('MAIN_EXPORT_ACTION_UPLOAD', $messagePlaceholders)). '<br>'.
870 }
871 elseif ($action == self::ACTION_EXPORT)
872 {
873 $message =
874 htmlspecialcharsbx(Loc::getMessage('MAIN_EXPORT_ACTION_EXPORT', $messagePlaceholders)). '<br>'.
876 }
877 elseif ($this->isExportCompleted)
878 {
879 $message =
880 htmlspecialcharsbx(Loc::getMessage('MAIN_EXPORT_COMPLETED')). '<br>'.
881 htmlspecialcharsbx(Loc::getMessage('MAIN_EXPORT_ACTION_EXPORT', $messagePlaceholders));
882
883 $downloadLink = '';
884 if ($this->isBitrix24 || $this->useCloud === true)
885 {
886 if ($this->isUploadFinished)
887 {
888 $downloadLink = $this->generateCloudLink();
889 }
890 }
891 else
892 {
893 $downloadLink = $this->generateDownloadLink();
894 }
895 if ($downloadLink !== '')
896 {
897 $result['DOWNLOAD_LINK'] = $downloadLink;
898 $result['FILE_NAME'] = $this->fileName;
899 $result['DOWNLOAD_LINK_NAME'] = htmlspecialcharsbx(Loc::getMessage('MAIN_EXPORT_DOWNLOAD'));
900 $result['CLEAR_LINK_NAME'] = htmlspecialcharsbx(Loc::getMessage('MAIN_EXPORT_CLEAR'));
901 }
902 }
903 else
904 {
905 $message =
906 htmlspecialcharsbx(Loc::getMessage('MAIN_EXPORT_ACTION_EXPORT', $messagePlaceholders)). '<br>'.
908 }
909
910 $result['SUMMARY_HTML'] = $message;
911
912 return $result;
913 }
914
920 protected function preformExpectedDuration()
921 {
922 $message = '';
923 $avgStepDuration = $predictedStepCount = $predictedTimeDuration = 0;
924 $avgRowsPerStep = $this->getRowsPerPage();
925 if ($this->stepCount > 0 && $this->timeStart > 0)
926 {
927 $avgStepDuration = ceil((time() - $this->timeStart) / $this->stepCount);
928 if ($this->processedItems > 0)
929 {
930 $avgRowsPerStep = ceil($this->processedItems / $this->stepCount);
931 }
932 if ($avgRowsPerStep < 1)
933 {
934 $avgRowsPerStep = 1;
935 }
936 }
937 if ($this->totalItems > 0)
938 {
939 $predictedStepCount = ceil(($this->totalItems - $this->processedItems) / $avgRowsPerStep);
940 if ($this->useCloud === true)
941 {
942 $predictedStepCount *= 2;
943 }
944 }
945 if ($avgStepDuration > 0 && $predictedStepCount > 0)
946 {
947 $predictedTimeDuration = $avgStepDuration * $predictedStepCount * 1.1;
948 }
949 if ($predictedTimeDuration > 0)
950 {
951 $predictedTimeDurationHours = floor($predictedTimeDuration / 3600);
952 if ($predictedTimeDurationHours > 0)
953 {
954 $predictedTimeDurationMinutes = ceil(($predictedTimeDuration - $predictedTimeDurationHours * 3600) / 60);
955 $message =
956 Loc::getMessage('MAIN_EXPORT_EXPECTED_DURATION').' '.
957 Loc::getMessage('MAIN_EXPORT_EXPECTED_DURATION_HOURS', array(
958 '#HOURS#' => $predictedTimeDurationHours,
959 '#MINUTES#' => $predictedTimeDurationMinutes,
960 ));
961 }
962 else
963 {
964 $predictedTimeDurationMinutes = ceil($predictedTimeDuration / 60);
965 $message =
966 Loc::getMessage('MAIN_EXPORT_EXPECTED_DURATION').' '.
967 Loc::getMessage('MAIN_EXPORT_EXPECTED_DURATION_MINUTES', array(
968 '#MINUTES#' => ($predictedTimeDurationMinutes < 1 ? "&lt;&nbsp;1" : $predictedTimeDurationMinutes),
969 ));
970 }
971 }
972
973 return $message;
974 }
975
976
982 protected function generateDownloadLink()
983 {
984 $params = array(
985 'PROCESS_TOKEN' => $this->processToken,
986 'EXPORT_TYPE' => $this->exportType,
987 'COMPONENT_NAME' => $this->componentName,
988 );
989
990 return $this->getActionUri(self::ACTION_DOWNLOAD, $params);
991 }
992
998 protected function generateCloudLink()
999 {
1000 $this->instanceBucket();
1001
1002 if ($this->checkCloudErrors())
1003 {
1004 return $this->bucket->GetFileSRC($this->uploadPath);
1005 }
1006
1007 return '';
1008 }
1009
1010
1017 protected function checkCommonErrors($action)
1018 {
1019 if (empty($this->module))
1020 {
1021 $this->addError(new Error('Module Id property is not filled.'));
1022 }
1023 if (!Main\Loader::includeModule($this->module))
1024 {
1025 $this->addError(new Error('Module '.$this->module.' is not included.'));
1026 }
1027
1028 if ($this->isBitrix24)
1029 {
1030 if ($this->isCloudAvailable !== true)
1031 {
1032 $this->addError(new Error(Loc::getMessage('MAIN_EXPORT_ERROR_NO_CLOUD_BUCKET')));
1033 }
1034 }
1035
1036 if ($action->getName() === self::ACTION_PURGE)
1037 {
1038 return true;
1039 }
1040
1041 if ($this->componentName === '')
1042 {
1043 $this->addError(new Error('Component name is not specified.'));
1044 }
1045 if (!in_array($this->exportType, array(self::EXPORT_TYPE_CSV, self::EXPORT_TYPE_EXCEL), true))
1046 {
1047 $this->addError(new Error('The export type is not supported.'));
1048 }
1049
1050 if($this->processToken === '')
1051 {
1052 $this->addError(new Error('Process token is not specified.'));
1053 }
1054
1055 return empty($this->getErrors());
1056 }
1057
1058
1064 protected function checkCloudErrors()
1065 {
1066 if (!($this->bucket instanceof \CCloudStorageBucket))
1067 {
1068 $this->errorCollection[] = new Error(Loc::getMessage('MAIN_EXPORT_ERROR_NO_CLOUD_BUCKET'));
1069 }
1070
1071 return empty($this->getErrors());
1072 }
1073
1074
1082 protected function findInitBucket(array $attributes)
1083 {
1084 if (!($this->bucket instanceof \CCloudStorageBucket))
1085 {
1086 $this->bucket = \CCloudStorage::FindBucketForFile(
1087 array(
1088 'FILE_SIZE' => $attributes['fileSize'],
1089 'MODULE_ID' => $this->module,
1090 ),
1091 $attributes['fileName']
1092 );
1093
1094 if (
1095 $this->bucket === null ||
1096 !($this->bucket instanceof \CCloudStorageBucket) ||
1097 !$this->bucket->init()
1098 )
1099 {
1100 return false;
1101 }
1102
1103 $this->bucketId = $this->bucket->ID;
1104 $this->cloudMinChunkSize = $this->bucket->GetService()->GetMinUploadPartSize(); //5M
1105 }
1106
1107 return true;
1108 }
1109
1115 protected function instanceBucket()
1116 {
1117 if (!($this->bucket instanceof \CCloudStorageBucket))
1118 {
1119 if ($this->bucketId > 0)
1120 {
1121 $this->bucket = new \CCloudStorageBucket($this->bucketId);
1122 $this->bucket->init();
1123 }
1124 }
1125
1126 return $this->bucket;
1127 }
1128
1136 protected function getBucketList($filter = [])
1137 {
1138 $result = [];
1139 $res = \CCloudStorageBucket::GetList([], array_merge(array('ACTIVE' => 'Y', 'READ_ONLY' => 'N'), $filter));
1140 while($bucket = $res->Fetch())
1141 {
1142 $result[] = $bucket;
1143 }
1144
1145 return $result;
1146 }
1147
1153 protected function saveProgressParameters(): void
1154 {
1155 // store state
1156 $progressData = [
1157 'processToken' => $this->processToken,
1158 ];
1159 foreach ($this->fieldToStoreInProcess as $fieldName)
1160 {
1161 $progressData[$fieldName] = $this->{$fieldName};
1162 }
1163
1164 $progressData = (new Signer())->sign(Json::encode($progressData));
1165
1166 $localStorage = Application::getInstance()->getLocalSession($this->module.'_export_stepper');
1167 $localStorage->set($this->getProgressParameterOptionName(), $progressData);
1168 }
1169
1175 protected function getProgressParameters()
1176 {
1177 $localStorage = Application::getInstance()->getLocalSession($this->module.'_export_stepper');
1178 $progressData = $localStorage->get($this->getProgressParameterOptionName());
1179
1180 if ($progressData)
1181 {
1182 try
1183 {
1184 $progressData = Json::decode((new Signer())->unsign($progressData));
1185 }
1186 catch (BadSignatureException $exception)
1187 {
1188 $progressData = [];
1189 }
1190 catch (ArgumentException $exception)
1191 {
1192 $progressData = [];
1193 }
1194 }
1195
1196 if (!is_array($progressData))
1197 {
1198 $progressData = [];
1199 }
1200
1201 return $progressData;
1202 }
1203
1209 protected function clearProgressParameters(): void
1210 {
1211 $localStorage = Application::getInstance()->getLocalSession($this->module.'_export_stepper');
1212 $localStorage->offsetUnset($this->getProgressParameterOptionName());
1213 }
1214
1221 {
1222 return $this->module. '_cloud_export';
1223 }
1224
1230 protected function generateExportFileName()
1231 {
1232 if ($this->exportType === self::EXPORT_TYPE_CSV)
1233 {
1234 $fileExt = 'csv';
1235 }
1236 elseif ($this->exportType === self::EXPORT_TYPE_EXCEL)
1237 {
1238 $fileExt = 'xls';
1239 }
1240
1241 $prefix = date('Ymd');
1242 $hash = str_pad(dechex(crc32($prefix)), 8, '0', STR_PAD_LEFT);
1243
1244 return uniqid($prefix. '_'. $hash. '_', false).'.'.$fileExt;
1245 }
1246
1252 protected function getFileType()
1253 {
1254 if ($this->exportType === self::EXPORT_TYPE_CSV)
1255 {
1256 $fileType = 'application/csv';
1257 }
1258 elseif ($this->exportType === self::EXPORT_TYPE_EXCEL)
1259 {
1260 $fileType = 'application/vnd.ms-excel';
1261 }
1262
1263 return $fileType;
1264 }
1265
1271 protected function generateTempDirPath()
1272 {
1273 $tempDir = \CTempFile::GetDirectoryName(self::KEEP_FILE_HOURS, array($this->module, uniqid( 'export_', true)));
1274
1275 \CheckDirPath($tempDir);
1276
1277 return $tempDir;
1278 }
1279
1285 protected function generateUploadDir()
1286 {
1287 return self::EXPORT_PATH;
1288 }
1289
1290
1304
1305
1315 protected function decodeSignedParameters($componentName, $signedParameters)
1316 {
1318 }
1319
1325 protected function writeTempFile($data, $precedeUtf8Bom = true)
1326 {
1327 $file = fopen($this->filePath, 'ab');
1328 if(is_resource($file))
1329 {
1330 // add UTF-8 BOM marker
1331 if($precedeUtf8Bom === true && (filesize($this->filePath) === 0))
1332 {
1333 fwrite($file, chr(239).chr(187).chr(191));
1334 }
1335
1336 fwrite($file, $data);
1337 fclose($file);
1338 unset($file);
1339
1340 $this->fileSize = filesize($this->filePath);
1341 }
1342 }
1343
1347 protected function dropTempFile()
1348 {
1349 if (file_exists($this->filePath))
1350 {
1351 @unlink($this->filePath);
1352 }
1353 $this->fileSize = 0;
1354 }
1355
1359 protected function getContentTempFile()
1360 {
1361 return file_get_contents($this->filePath);
1362 }
1363
1367 protected function getSizeTempFile()
1368 {
1369 $this->fileSize = filesize($this->filePath);
1370
1371 return $this->fileSize;
1372 }
1373
1380 protected function startTimer($timeLimit = 25): void
1381 {
1382 $this->timeLimit = $timeLimit;
1383
1384 if (defined('START_EXEC_TIME') && START_EXEC_TIME > 0)
1385 {
1386 $this->hitStartTime = (int)START_EXEC_TIME;
1387 }
1388 else
1389 {
1390 $this->hitStartTime = time();
1391 }
1392 }
1393
1394
1400 protected function hasTimeLimitReached(): bool
1401 {
1402 if ($this->timeLimit > 0)
1403 {
1404 if ($this->timeLimitReached)
1405 {
1406 return true;
1407 }
1408 if ((time() - $this->hitStartTime) >= $this->timeLimit)
1409 {
1410 $this->timeLimitReached = true;
1411
1412 return true;
1413 }
1414 }
1415
1416 return false;
1417 }
1418
1423 protected function getRowsPerPage(): int
1424 {
1425 return static::ROWS_PER_PAGE;
1426 }
1427}
$path
Определения access_edit.php:21
$hash
Определения ajax_redirector.php:8
global $APPLICATION
Определения include.php:80
static getInstance()
Определения application.php:98
static signParameters($componentName, $parameters)
Определения parametersigner.php:19
static unsignParameters($componentName, $signedParameters)
Определения parametersigner.php:37
generateExportFileName()
Определения export.php:1230
const ACTION_CANCEL
Определения export.php:27
const ACTION_FINISH
Определения export.php:28
generateCloudLink()
Определения export.php:998
dispatcherAction()
Определения export.php:272
const EXPORT_PATH
Определения export.php:20
keepFieldInProcess($fieldName)
Определения export.php:262
getSignedParameters($componentName, array $params)
Определения export.php:1300
generateTempDirPath()
Определения export.php:1271
getContentTempFile()
Определения export.php:1359
const TIME_LIMIT
Определения export.php:158
const ACTION_VOID
Определения export.php:31
const ACTION_UPLOAD
Определения export.php:26
const ACTION_CLEAR
Определения export.php:30
decodeSignedParameters($componentName, $signedParameters)
Определения export.php:1315
const STATUS_COMPLETED
Определения export.php:34
preformExpectedDuration()
Определения export.php:920
getProgressParameters()
Определения export.php:1175
findInitBucket(array $attributes)
Определения export.php:1082
getProgressParameterOptionName()
Определения export.php:1220
const KEEP_FILE_HOURS
Определения export.php:69
generateDownloadLink()
Определения export.php:982
getBucketList($filter=[])
Определения export.php:1136
preformAnswer($action)
Определения export.php:811
const ROWS_PER_PAGE
Определения export.php:81
hasTimeLimitReached()
Определения export.php:1400
const EXPORT_TYPE_CSV
Определения export.php:47
const ACTION_DOWNLOAD
Определения export.php:29
downloadAction()
Определения export.php:395
checkCommonErrors($action)
Определения export.php:1017
generateUploadDir()
Определения export.php:1285
const EXPIRE_DAYS
Определения export.php:23
configureActions()
Определения export.php:164
const ACTION_PURGE
Определения export.php:32
$componentParameters
Определения export.php:42
startTimer($timeLimit=25)
Определения export.php:1380
$fieldToStoreInProcess
Определения export.php:105
saveProgressParameters()
Определения export.php:1153
processBeforeAction(Main\Engine\Action $action)
Определения export.php:242
const EXPORT_TYPE_EXCEL
Определения export.php:48
checkCloudErrors()
Определения export.php:1064
writeTempFile($data, $precedeUtf8Bom=true)
Определения export.php:1325
clearProgressParameters()
Определения export.php:1209
getSizeTempFile()
Определения export.php:1367
const ACTION_EXPORT
Определения export.php:25
const STATUS_PROGRESS
Определения export.php:35
addError(Error $error)
Определения controller.php:1070
getActionUri(string $actionName, array $params=[], bool $absolute=false)
Определения controller.php:207
Определения error.php:15
static includeModule($moduleName)
Определения loader.php:67
static isModuleInstalled($moduleName)
Определения modulemanager.php:125
static GetList($arOrder=[], $arFilter=[], $arSelect=[])
Определения storage_bucket.php:791
static FindBucketForFile($arFile, $strFileName)
Определения storage.php:106
$data['IS_AVAILABLE']
Определения .description.php:13
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$res
Определения filter_act.php:7
$result
Определения get_property_values.php:14
$filter
Определения iblock_catalog_list.php:54
const START_EXEC_TIME
Определения start.php:12
CheckDirPath($path)
Определения tools.php:2707
htmlspecialcharsbx($string, $flags=ENT_COMPAT, $doubleEncode=true)
Определения tools.php:2701
Определения action.php:3
Определения Image.php:9
$message
Определения payment.php:8
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
if($inWords) echo htmlspecialcharsbx(Number2Word_Rus(roundEx($totalVatSum $params['CURRENCY']
Определения template.php:799
$response
Определения result.php:21
$action
Определения file_dialog.php:21