ExportSubmissions.php
8.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
<?php if (!defined('ABSPATH')) exit;
use NinjaForms\Includes\Factories\SubmissionAggregateFactory;
use NinjaForms\Includes\Factories\SubmissionFilterFactory;
/**
* Class NF_Abstracts_Batch_Process
*/
class NF_Admin_Processes_ExportSubmissions extends NF_Abstracts_BatchProcess
{
protected $_slug = 'export_submissions';
protected $form = '';
protected $subs_per_step = 25;
protected $sub_count = 0;
protected $format = 'csv';
protected $offset = 0;
protected $delimiter = ',';
protected $enclosure = '"';
protected $terminator = "\n";
protected $currentPosition = 0;
/**
* Filepath of downloaded file in default directory
*
* @param array $forms
*/
protected $file_path = '';
/**
* File URL of downloaded file in default directory
*
* @param array $forms
*/
protected $fileUrl = '';
/**
* @var NF_Exports_SubmissionCsvExport
*/
protected $csvObject;
/**
* Aggregated submission keys in output order
*
* @var array
*/
protected $indexedLookup;
/**
* Override parent construct to pass form Ids
*
* @param array $forms
*/
public function __construct($form = '')
{
global $wpdb;
$this->form = $form;
/**
* Set $_db to $wpdb.
* This helps us by not requiring us to declare global $wpdb in every class method.
*/
$this->_db = $wpdb;
// Run init.
$this->init();
}
/**
* Function to run any setup steps necessary to begin processing.
*/
public function startup()
{
//TODO: Verify what type of export this is (filter) later
//TODO: Read in submission IDs later
//TODO: Read in start/end date params later
// Right now, we assume that we have a single form ID and that the export is always a csv file.
// Verify filterable values.
$this->subs_per_step = apply_filters('ninja_forms_export_subs_per_step', $this->subs_per_step);
$this->delimiter = apply_filters('nf_sub_csv_delimiter', $this->delimiter);
$this->enclosure = apply_filters('nf_sub_csv_enclosure', $this->enclosure);
$this->terminator = apply_filters('nf_sub_csv_terminator', $this->terminator);
// Construct a new submission aggregate.
$params = (new SubmissionFilterFactory())->maybeLimitByLoggedInUser()->setNfFormIds([$this->form]);
$params->setEndDate(time());
$params->setStartDate(0);
$params->setStatus(["active", "publish"]);
$submissionAggregateCsvAdapter = (new SubmissionAggregateFactory())->SubmissionAggregateCsvExportAdapter();
$submissionAggregateCsvAdapter->submissionAggregate->filterSubmissions($params);
$this->csvObject = (new NF_Exports_SubmissionCsvExport())->setUseAdminLabels(true)->setSubmissionAggregateCsvExportAdapter($submissionAggregateCsvAdapter);
$this->indexedLookup = $this->csvObject->reverseSubmissionOrder();
//Get a count of how many submissions we're dealing with.
$this->sub_count = $submissionAggregateCsvAdapter->submissionAggregate->getSubmissionCount();
// If there are no subs, bail.
if (0 === $this->sub_count) {
$this->add_error('no_submissions', esc_html__('No Submissions to export.', 'ninja-forms'), 'fatal');
$this->batch_complete();
}
// Initialize our file.
$this->file_path = $this->constructFilepath();
// Use 'w' to delete the original file if one exists and replace it with a new one.
if (!$file = fopen($this->file_path, 'w')) {
$this->add_error('write_failure', esc_html__('Unable to write file.', 'ninja-forms'), 'fatal');
$this->batch_complete();
}
// Add headers to the file.
// We can only do this outside of the process method under the assumption that a single form ID is provided.
$labels = $this->csvObject->getLabels();
$glue = $this->enclosure . $this->delimiter . $this->enclosure;
$constructed = $this->enclosure . implode($glue, $labels) . $this->enclosure . $this->terminator;
fwrite($file, $constructed);
fclose($file);
}
/**
* Function to run any setup steps necessary to begin processing for steps after the first.
*
* @since 3.5.0
* @return void
*/
public function restart()
{
//TODO: Read back in our unfinished data.
}
/**
* Function to loop over the batch.
*
* @since 3.5.0
* @return void
*/
public function process()
{
if($this->currentPosition >= $this->sub_count-1){
$this->batch_complete();
return;
}
$this->writeBatch();
// Continue looping until end
$this->process();
}
/**
* Delete temp file before calling parent method
* @inheritDoc
*/
public function batch_complete( ): void
{
parent::batch_complete();
}
public function writeBatch( ): void
{
if (!$file = fopen($this->file_path, 'a')) {
$this->add_error('write_failure', esc_html__('Unable to write file.', 'ninja-forms'), 'fatal');
$this->batch_complete();
}
$glue = $this->enclosure . $this->delimiter . $this->enclosure;
// for each submission within the step
for ($i = 0; $i < $this->subs_per_step; $i++) {
if (!isset($this->indexedLookup[$this->currentPosition])) {
continue;
}
$aggregatedKey = $this->indexedLookup[$this->currentPosition];
$row = $this->csvObject->constructRow($aggregatedKey);
$constructed = $this->enclosure . implode($glue, $row) . $this->enclosure . $this->terminator;
fwrite($file, $constructed);
$this->currentPosition++;
}
}
/**
* Method that encodes $this->response and sends the data to the front-end.
*
* @since 3.4.0
* @updated 3.4.11
* @return void
*/
public function respond()
{
if (!empty($this->response['errors'])) {
$this->response['errors'] = array_unique($this->response['errors']);
}
return wp_json_encode($this->response);
}
/**
* Function to cleanup any lingering temporary elements of a batch process after completion.
*
* @since 3.5.0
* @return void
*/
public function cleanup()
{
//TODO: Get rid of our data option.
/**
* We shouldn't need to delete our csv file,
* as that will be overwritten the next time this process is called.
*/
}
/**
* Get Steps
* Determines the amount of steps needed for the step processors.
*
* @since 3.5.0
* @return int of the number of steps.
*/
public function get_steps()
{
//TODO: Refactor this when multiple form IDs are introduced.
// Ensure we convent our numbers from int to float to ensure that ceil works.
// Get the amount of steps and return.
$steps = ceil(floatval($this->sub_count) / floatval($this->subs_per_step));
return $steps;
}
/**
* Get the filepath of our constructed csv.
*
* @return string
*/
protected function constructFilepath()
{
$filename = time() . base64_encode( 'form-' . $this->form . '-all-subs' );
$upload_dir = wp_upload_dir();
$file_path = trailingslashit($upload_dir['path']) . $filename . '.' . $this->format;
$this->fileUrl = trailingslashit($upload_dir['url']) . $filename . '.' . $this->format;
return $file_path;
}
/**
* Overwrites the default flag method to use user options
* instead of using the default options.
*
* @since 3.5.0
* @param $flag (String) The flag to check
* @param $action (String) The type of interaction to be performed
* @return Mixed
*/
public function flag($flag, $action)
{
switch ($action) {
case 'add':
return update_user_option(get_current_user_id(), $flag, true);
case 'remove':
return delete_user_option(get_current_user_id(), $flag);
default:
// Default to 'check'.
return get_user_option(get_current_user_id(), $flag);
}
}
/**
* Get file URL of downloaded file in default directory
*/
public function getFileUrl(): string
{
return $this->fileUrl;
}
/**
* Get filepath of downloaded file in default directory
*/
public function getFilePath(): string
{
return $this->file_path;
}
}