1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
class GenerateCSVJob extends AbstractQueuedJob { |
|
|
|
|
4
|
|
|
|
5
|
|
|
public function __construct() { |
6
|
|
|
$this->ID = uniqid(); |
7
|
|
|
$this->Seperator = ','; |
8
|
|
|
$this->IncludeHeader = true; |
9
|
|
|
$this->HeadersOutput = false; |
10
|
|
|
$this->totalSteps = 1; |
11
|
|
|
} |
12
|
|
|
|
13
|
|
|
public function getJobType() { |
14
|
|
|
return QueuedJob::QUEUED; |
15
|
|
|
} |
16
|
|
|
|
17
|
|
|
public function getTitle() { |
18
|
|
|
return "Export a CSV of a Gridfield"; |
19
|
|
|
} |
20
|
|
|
|
21
|
|
|
public function getSignature() { |
22
|
|
|
return md5(get_class($this) . '-' . $this->ID); |
23
|
|
|
} |
24
|
|
|
|
25
|
|
|
function setGridField($gridField) { |
|
|
|
|
26
|
|
|
$this->GridFieldName = $gridField->getName(); |
27
|
|
|
$this->GridFieldURL = $gridField->Link(); |
28
|
|
|
} |
29
|
|
|
|
30
|
|
|
function setSession($session) { |
|
|
|
|
31
|
|
|
// None of the gridfield actions are needed, and they make the stored session bigger, so pull |
32
|
|
|
// them out. |
33
|
|
|
$actionkeys = array_filter(array_keys($session), function ($i) { |
34
|
|
|
return strpos($i, 'gf_') === 0; |
35
|
|
|
}); |
36
|
|
|
|
37
|
|
|
$session = array_diff_key($session, array_flip($actionkeys)); |
38
|
|
|
|
39
|
|
|
// This causes problems with logins |
40
|
|
|
unset($session['HTTP_USER_AGENT']); |
41
|
|
|
|
42
|
|
|
$this->Session = $session; |
43
|
|
|
} |
44
|
|
|
|
45
|
|
|
function setColumns($columns) { |
|
|
|
|
46
|
|
|
$this->Columns = $columns; |
47
|
|
|
} |
48
|
|
|
|
49
|
|
|
function setSeparator($seperator) { |
|
|
|
|
50
|
|
|
$this->Separator = $seperator; |
51
|
|
|
} |
52
|
|
|
|
53
|
|
|
function setIncludeHeader($includeHeader) { |
|
|
|
|
54
|
|
|
$this->IncludeHeader = $includeHeader; |
55
|
|
|
} |
56
|
|
|
|
57
|
|
|
protected function getGridField() { |
58
|
|
|
$session = $this->Session; |
59
|
|
|
|
60
|
|
|
// Store state in session, and pass ID to client side. |
61
|
|
|
$state = array( |
62
|
|
|
'grid' => $this->GridFieldName, |
63
|
|
|
'actionName' => 'findgridfield', |
64
|
|
|
'args' => null |
65
|
|
|
); |
66
|
|
|
|
67
|
|
|
// Ensure $id doesn't contain only numeric characters |
68
|
|
|
$id = 'gf_' . substr(md5(serialize($state)), 0, 8); |
69
|
|
|
|
70
|
|
|
// Add new form action into session for GridField to find when Director::test is called below |
71
|
|
|
$session[$id] = $state; |
72
|
|
|
$session['SecurityID'] = '1'; |
73
|
|
|
|
74
|
|
|
// Construct the URL |
75
|
|
|
$actionKey = 'action_gridFieldAlterAction?' . http_build_query(['StateID' => $id]); |
76
|
|
|
$actionValue = 'Find Gridfield'; |
77
|
|
|
|
78
|
|
|
$url = $this->GridFieldURL . '?' .http_build_query([ |
79
|
|
|
$actionKey => $actionValue, |
80
|
|
|
'SecurityID' => 1 |
81
|
|
|
]); |
82
|
|
|
|
83
|
|
|
// Restore into the current session the user the job is exporting as |
84
|
|
|
Session::set("loggedInAs", $session['loggedInAs']); |
85
|
|
|
|
86
|
|
|
// Then make a sub-query that should return a special SS_HTTPResponse with the gridfield object |
87
|
|
|
$res = Director::test($url, null, new Session($session), 'GET'); |
88
|
|
|
|
89
|
|
|
// Great, it did, we can return it |
90
|
|
|
if ($res instanceof GridFieldQueuedExportButton_Response) { |
91
|
|
|
$gridField = $res->getGridField(); |
92
|
|
|
$gridField->getConfig()->removeComponentsByType('GridFieldPaginator'); |
93
|
|
|
$gridField->getConfig()->removeComponentsByType('GridFieldPageCount'); |
94
|
|
|
|
95
|
|
|
return $gridField; |
96
|
|
|
} else { |
97
|
|
|
user_error('Couldn\'t restore GridField', E_USER_ERROR); |
98
|
|
|
} |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
protected function outputHeader($gridField, $columns) { |
102
|
|
|
$fileData = ''; |
103
|
|
|
$separator = $this->Separator; |
104
|
|
|
|
105
|
|
|
$headers = array(); |
106
|
|
|
|
107
|
|
|
// determine the CSV headers. If a field is callable (e.g. anonymous function) then use the |
108
|
|
|
// source name as the header instead |
109
|
|
|
foreach ($columns as $columnSource => $columnHeader) { |
110
|
|
|
$headers[] = (!is_string($columnHeader) && is_callable($columnHeader)) ? $columnSource : $columnHeader; |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
$fileData .= "\"" . implode("\"{$separator}\"", array_values($headers)) . "\""; |
114
|
|
|
$fileData .= "\n"; |
115
|
|
|
|
116
|
|
|
file_put_contents('/tmp/' . $this->getSignature() . '.csv', $fileData, FILE_APPEND); |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
protected function outputRows($gridField, $columns, $start, $count) { |
120
|
|
|
$fileData = ''; |
121
|
|
|
$separator = $this->Separator; |
122
|
|
|
|
123
|
|
|
$items = $gridField->getManipulatedList(); |
124
|
|
|
$items = $items->limit($count, $start); |
125
|
|
|
|
126
|
|
|
foreach ($items as $item) { |
127
|
|
|
if (!$item->hasMethod('canView') || $item->canView()) { |
128
|
|
|
$columnData = array(); |
129
|
|
|
|
130
|
|
|
foreach ($columns as $columnSource => $columnHeader) { |
131
|
|
|
if (!is_string($columnHeader) && is_callable($columnHeader)) { |
132
|
|
|
if ($item->hasMethod($columnSource)) { |
133
|
|
|
$relObj = $item->{$columnSource}(); |
134
|
|
|
} else { |
135
|
|
|
$relObj = $item->relObject($columnSource); |
136
|
|
|
} |
137
|
|
|
|
138
|
|
|
$value = $columnHeader($relObj); |
139
|
|
|
} else { |
140
|
|
|
$value = $gridField->getDataFieldValue($item, $columnSource); |
141
|
|
|
|
142
|
|
|
if ($value === null) { |
143
|
|
|
$value = $gridField->getDataFieldValue($item, $columnHeader); |
144
|
|
|
} |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
$value = str_replace(array("\r", "\n"), "\n", $value); |
148
|
|
|
$columnData[] = '"' . str_replace('"', '""', $value) . '"'; |
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
$fileData .= implode($separator, $columnData); |
152
|
|
|
$fileData .= "\n"; |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
if ($item->hasMethod('destroy')) { |
156
|
|
|
$item->destroy(); |
157
|
|
|
} |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
file_put_contents('/tmp/' . $this->getSignature() . '.csv', $fileData, FILE_APPEND); |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
|
164
|
|
|
public function setup() { |
165
|
|
|
$gridField = $this->getGridField(); |
166
|
|
|
$this->totalSteps = $gridField->getManipulatedList()->count(); |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
/** |
170
|
|
|
* Generate export fields for CSV. |
171
|
|
|
* |
172
|
|
|
* @param GridField $gridField |
|
|
|
|
173
|
|
|
* @return array |
174
|
|
|
*/ |
175
|
|
|
public function process() { |
176
|
|
|
$gridField = $this->getGridField(); |
177
|
|
|
$columns = $this->Columns ?: singleton($gridField->getModelClass())->summaryFields(); |
178
|
|
|
|
179
|
|
|
if ($this->IncludeHeader && !$this->HeadersOutput) { |
180
|
|
|
$this->outputHeader($gridField, $columns); |
181
|
|
|
$this->HeadersOutput = true; |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
$this->outputRows($gridField, $columns, $this->currentStep, 100); |
185
|
|
|
|
186
|
|
|
$this->currentStep += 100; |
187
|
|
|
|
188
|
|
|
if ($this->currentStep >= $this->totalSteps) { |
189
|
|
|
$this->isComplete = true; |
190
|
|
|
} |
191
|
|
|
} |
192
|
|
|
} |
193
|
|
|
|
You can fix this by adding a namespace to your class:
When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.