@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.)
hq.recaptime.dev/wiki/Phorge
phorge
phabricator
1<?php
2
3final class PhabricatorExportEngine
4 extends Phobject {
5
6 private $viewer;
7 private $searchEngine;
8 private $savedQuery;
9 private $exportFormat;
10 private $filename;
11 private $title;
12
13 public function setViewer(PhabricatorUser $viewer) {
14 $this->viewer = $viewer;
15 return $this;
16 }
17
18 public function getViewer() {
19 return $this->viewer;
20 }
21
22 public function setSearchEngine(
23 PhabricatorApplicationSearchEngine $search_engine) {
24 $this->searchEngine = $search_engine;
25 return $this;
26 }
27
28 public function getSearchEngine() {
29 return $this->searchEngine;
30 }
31
32 public function setSavedQuery(PhabricatorSavedQuery $saved_query) {
33 $this->savedQuery = $saved_query;
34 return $this;
35 }
36
37 public function getSavedQuery() {
38 return $this->savedQuery;
39 }
40
41 public function setExportFormat(
42 PhabricatorExportFormat $export_format) {
43 $this->exportFormat = $export_format;
44 return $this;
45 }
46
47 public function getExportFormat() {
48 return $this->exportFormat;
49 }
50
51 public function setFilename($filename) {
52 $this->filename = $filename;
53 return $this;
54 }
55
56 public function getFilename() {
57 return $this->filename;
58 }
59
60 public function setTitle($title) {
61 $this->title = $title;
62 return $this;
63 }
64
65 public function getTitle() {
66 return $this->title;
67 }
68
69 public function newBulkJob(AphrontRequest $request) {
70 $viewer = $this->getViewer();
71 $engine = $this->getSearchEngine();
72 $saved_query = $this->getSavedQuery();
73 $format = $this->getExportFormat();
74
75 $params = array(
76 'engineClass' => get_class($engine),
77 'queryKey' => $saved_query->getQueryKey(),
78 'formatKey' => $format->getExportFormatKey(),
79 'title' => $this->getTitle(),
80 'filename' => $this->getFilename(),
81 );
82
83 $job = PhabricatorWorkerBulkJob::initializeNewJob(
84 $viewer,
85 new PhabricatorExportEngineBulkJobType(),
86 $params);
87
88 // We queue these jobs directly into STATUS_WAITING without requiring
89 // a confirmation from the user.
90
91 $xactions = array();
92
93 $xactions[] = id(new PhabricatorWorkerBulkJobTransaction())
94 ->setTransactionType(PhabricatorWorkerBulkJobTransaction::TYPE_STATUS)
95 ->setNewValue(PhabricatorWorkerBulkJob::STATUS_WAITING);
96
97 $editor = id(new PhabricatorWorkerBulkJobEditor())
98 ->setActor($viewer)
99 ->setContentSourceFromRequest($request)
100 ->setContinueOnMissingFields(true)
101 ->applyTransactions($job, $xactions);
102
103 return $job;
104 }
105
106 public function exportFile() {
107 $viewer = $this->getViewer();
108 $engine = $this->getSearchEngine();
109 $saved_query = $this->getSavedQuery();
110 $format = $this->getExportFormat();
111 $title = $this->getTitle();
112 $filename = $this->getFilename();
113
114 $query = $engine->buildQueryFromSavedQuery($saved_query);
115
116 $extension = $format->getFileExtension();
117 $mime_type = $format->getMIMEContentType();
118 $filename = $filename.'.'.$extension;
119
120 $format = id(clone $format)
121 ->setViewer($viewer)
122 ->setTitle($title);
123
124 $field_list = $engine->newExportFieldList();
125 $field_list = mpull($field_list, null, 'getKey');
126 $format->addHeaders($field_list);
127
128 // Iterate over the query results in large pages so we don't have to hold
129 // too much stuff in memory.
130 $page_size = 1000;
131 $page_cursor = null;
132 do {
133 $pager = $engine->newPagerForSavedQuery($saved_query);
134 $pager->setPageSize($page_size);
135
136 if ($page_cursor !== null) {
137 $pager->setAfterID($page_cursor);
138 }
139
140 $objects = $engine->executeQuery($query, $pager);
141 $objects = array_values($objects);
142 $page_cursor = $pager->getNextPageID();
143
144 $export_data = $engine->newExport($objects);
145 for ($ii = 0; $ii < count($objects); $ii++) {
146 $format->addObject($objects[$ii], $field_list, $export_data[$ii]);
147 }
148 } while ($pager->getHasMoreResults());
149
150 $export_result = $format->newFileData();
151
152 // We have all the data in one big string and aren't actually
153 // streaming it, but pretending that we are allows us to actviate
154 // the chunk engine and store large files.
155 $iterator = new ArrayIterator(array($export_result));
156
157 $source = id(new PhabricatorIteratorFileUploadSource())
158 ->setName($filename)
159 ->setViewPolicy(PhabricatorPolicies::POLICY_NOONE)
160 ->setMIMEType($mime_type)
161 ->setRelativeTTL(phutil_units('60 minutes in seconds'))
162 ->setAuthorPHID($viewer->getPHID())
163 ->setIterator($iterator);
164
165 return $source->uploadFile();
166 }
167
168}