PluginProbe
VikBooking Hotel Booking Engine & PMS / 1.8.16
VikBooking Hotel Booking Engine & PMS v1.8.16
1.8.16 1.8.15 1.8.14 1.8.13 1.8.12 1.8.11 1.8.10 1.8.9 1.8.6 1.8.7 1.8.8 trunk 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 All 37 releases
← All changes | admin/helpers/report/report.php +1597 -1542 1.8.6 → 1.8.16 View file →
@@ -15,1768 +15,1823 @@
15 15 */
16 16 #[\AllowDynamicProperties]
17 17 abstract class VikBookingReport
18 18 {
19 - /**
20 - * @var string
21 - */
22 - protected $reportName = '';
19 + /**
20 + * @var string
21 + */
22 + protected $reportName = '';
23 23
24 - /**
25 - * @var string
26 - */
27 - protected $reportFile = '';
24 + /**
25 + * @var string
26 + */
27 + protected $reportFile = '';
28 28
29 - /**
30 - * @var array
31 - */
32 - protected $reportFilters = [];
29 + /**
30 + * @var array
31 + */
32 + protected $reportFilters = [];
33 33
34 - /**
35 - * @var string
36 - */
37 - protected $reportScript = '';
34 + /**
35 + * @var string
36 + */
37 + protected $reportScript = '';
38 38
39 - /**
40 - * @var string
41 - */
42 - protected $warning = '';
39 + /**
40 + * @var string
41 + */
42 + protected $warning = '';
43 43
44 - /**
45 - * @var string
46 - */
47 - protected $error = '';
44 + /**
45 + * @var string
46 + */
47 + protected $error = '';
48 48
49 - /**
50 - * @var object
51 - */
52 - protected $dbo;
49 + /**
50 + * @var object
51 + */
52 + protected $dbo;
53 53
54 - /**
55 - * @var array
56 - */
57 - protected $cols = [];
54 + /**
55 + * @var array
56 + */
57 + protected $cols = [];
58 58
59 - /**
60 - * @var array
61 - */
62 - protected $rows = [];
59 + /**
60 + * @var array
61 + */
62 + protected $rows = [];
63 63
64 - /**
65 - * @var array
66 - */
67 - protected $footerRow = [];
64 + /**
65 + * @var array
66 + */
67 + protected $footerRow = [];
68 68
69 - /**
70 - * An array of custom options to be passed to the report.
71 - * Reports can use them before generating the report data.
72 - *
73 - * @var array
74 - *
75 - * @since 1.15.0 (J) - 1.5.0 (WP)
76 - */
77 - protected $options = [];
69 + /**
70 + * An array of custom options to be passed to the report.
71 + * Reports can use them before generating the report data.
72 + *
73 + * @var array
74 + *
75 + * @since 1.15.0 (J) - 1.5.0 (WP)
76 + */
77 + protected $options = [];
78 78
79 - /**
80 - * @var resource
81 - *
82 - * @since 1.16.1 (J) - 1.6.1 (WP)
83 - */
84 - protected $fp_export = null;
79 + /**
80 + * @var resource
81 + *
82 + * @since 1.16.1 (J) - 1.6.1 (WP)
83 + */
84 + protected $fp_export = null;
85 85
86 - /**
87 - * @var string
88 - *
89 - * @since 1.16.1 (J) - 1.6.1 (WP)
90 - */
91 - protected $csv_export_format = 'csv';
86 + /**
87 + * @var string
88 + *
89 + * @since 1.16.1 (J) - 1.6.1 (WP)
90 + */
91 + protected $csv_export_format = 'csv';
92 92
93 - /**
94 - * @var string
95 - *
96 - * @since 1.16.1 (J) - 1.6.1 (WP)
97 - */
98 - protected $csv_export_fname = '';
93 + /**
94 + * @var string
95 + *
96 + * @since 1.16.1 (J) - 1.6.1 (WP)
97 + */
98 + protected $csv_export_fname = '';
99 99
100 - /**
101 - * @var array
102 - *
103 - * @since 1.17.1 (J) - 1.7.1 (WP)
104 - */
105 - protected $actionData = [];
100 + /**
101 + * @var array
102 + *
103 + * @since 1.17.1 (J) - 1.7.1 (WP)
104 + */
105 + protected $actionData = [];
106 106
107 - /**
108 - * @var array
109 - *
110 - * @since 1.17.1 (J) - 1.7.1 (WP)
111 - */
112 - protected $resourceFiles = [];
107 + /**
108 + * @var array
109 + *
110 + * @since 1.17.1 (J) - 1.7.1 (WP)
111 + */
112 + protected $resourceFiles = [];
113 113
114 - /**
115 - * @var ?string
116 - *
117 - * @since 1.17.1 (J) - 1.7.1 (WP)
118 - */
119 - protected $scope = null;
114 + /**
115 + * @var ?string
116 + *
117 + * @since 1.17.1 (J) - 1.7.1 (WP)
118 + */
119 + protected $scope = null;
120 120
121 - /**
122 - * @var array
123 - *
124 - * @since 1.18.6 (J) - 1.8.6 (WP)
125 - */
126 - protected $reportRowClasses = [];
121 + /**
122 + * @var array
123 + *
124 + * @since 1.18.6 (J) - 1.8.6 (WP)
125 + */
126 + protected $reportRowClasses = [];
127 127
128 - /**
129 - * Class constructor should define the name of
130 - * the report and the filters to be displayed.
131 - */
132 - public function __construct()
133 - {
134 - $this->dbo = JFactory::getDbo();
135 - }
128 + /**
129 + * Class constructor should define the name of
130 + * the report and the filters to be displayed.
131 + */
132 + public function __construct()
133 + {
134 + $this->dbo = JFactory::getDbo();
135 + }
136 136
137 - /**
138 - * Extending Classes should define this method
139 - * to get the name of the report.
140 - */
141 - abstract public function getName();
137 + /**
138 + * Extending Classes should define this method
139 + * to get the name of the report.
140 + */
141 + abstract public function getName();
142 142
143 - /**
144 - * Extending Classes should define this method
145 - * to get the name of class file.
146 - */
147 - abstract public function getFileName();
143 + /**
144 + * Extending Classes should define this method
145 + * to get the name of class file.
146 + */
147 + abstract public function getFileName();
148 148
149 - /**
150 - * Extending Classes should define this method
151 - * to get the filters of the report.
152 - */
153 - abstract public function getFilters();
149 + /**
150 + * Extending Classes should define this method
151 + * to get the filters of the report.
152 + */
153 + abstract public function getFilters();
154 154
155 - /**
156 - * Extending Classes should define this method
157 - * to generate the report data (cols and rows).
158 - */
159 - abstract public function getReportData();
155 + /**
156 + * Extending Classes should define this method
157 + * to generate the report data (cols and rows).
158 + */
159 + abstract public function getReportData();
160 160
161 - /**
162 - * Allows the report to perform a preflight compatibility check with the environment.
163 - *
164 - * @return ?bool False if the report is not compatible with the current environment.
165 - *
166 - * @since 1.18.5 (J) - 1.8.5 (WP)
167 - */
168 - public function preflight()
169 - {
170 - return true;
171 - }
161 + /**
162 + * Allows the report to perform a preflight compatibility check with the environment.
163 + *
164 + * @return ?bool False if the report is not compatible with the current environment.
165 + *
166 + * @since 1.18.5 (J) - 1.8.5 (WP)
167 + */
168 + public function preflight()
169 + {
170 + return true;
171 + }
172 172
173 - /**
174 - * Allows the report to define the sub-filters template.
175 - *
176 - * @return ?string
177 - *
178 - * @since 1.18.6 (J) - 1.8.6 (WP)
179 - */
180 - public function getSubFiltersTpl()
181 - {
182 - return null;
183 - }
173 + /**
174 + * Allows the report to define the sub-filters template.
175 + *
176 + * @return ?string
177 + *
178 + * @since 1.18.6 (J) - 1.8.6 (WP)
179 + */
180 + public function getSubFiltersTpl()
181 + {
182 + return null;
183 + }
184 184
185 - /**
186 - * Allows reports to define the default layout type to display.
187 - *
188 - * @param bool $hasChart True if the report has defined a chart.
189 - *
190 - * @return string
191 - *
192 - * @since 1.18.6 (J) - 1.8.6 (WP)
193 - */
194 - public function getDefaultLayoutType(bool $hasChart = false)
195 - {
196 - return $hasChart ? 'sheetnchart' : 'sheet';
197 - }
185 + /**
186 + * Allows reports to define the default layout type to display.
187 + *
188 + * @param bool $hasChart True if the report has defined a chart.
189 + *
190 + * @return string
191 + *
192 + * @since 1.18.6 (J) - 1.8.6 (WP)
193 + */
194 + public function getDefaultLayoutType(bool $hasChart = false)
195 + {
196 + return $hasChart ? 'sheetnchart' : 'sheet';
197 + }
198 198
199 - /**
200 - * Returns the AJAX endpoint URL for the current report.
201 - *
202 - * @return string
203 - *
204 - * @since 1.18.6 (J) - 1.8.6 (WP)
205 - */
206 - public function getAjaxUrl()
207 - {
208 - return VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=invoke_report&report=' . $this->reportFile);
209 - }
199 + /**
200 + * Returns the AJAX endpoint URL for the current report.
201 + *
202 + * @return string
203 + *
204 + * @since 1.18.6 (J) - 1.8.6 (WP)
205 + */
206 + public function getAjaxUrl()
207 + {
208 + return VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=invoke_report&report=' . $this->reportFile);
209 + }
210 210
211 - /**
212 - * Main method to generate the report columns and rows. Stores on the current
213 - * resource the CSV lines and forces the browser to download the file. In case
214 - * of errors, the process is not terminated to let the View display the errors.
215 - *
216 - * @return void|bool script termination on success, false otherwise.
217 - *
218 - * @since 1.16.1 (J) - 1.6.1 (WP) drivers no longer need to implement it,
219 - * unless the driver needs to override it.
220 - */
221 - public function exportCSV()
222 - {
223 - // grab all report lines to export
224 - $csvlines = $this->getExportCSVLines();
211 + /**
212 + * Main method to generate the report columns and rows. Stores on the current
213 + * resource the CSV lines and forces the browser to download the file. In case
214 + * of errors, the process is not terminated to let the View display the errors.
215 + *
216 + * @return void|bool script termination on success, false otherwise.
217 + *
218 + * @since 1.16.1 (J) - 1.6.1 (WP) drivers no longer need to implement it,
219 + * unless the driver needs to override it.
220 + */
221 + public function exportCSV()
222 + {
223 + // grab all report lines to export
224 + $csvlines = $this->getExportCSVLines();
225 225
226 - if (!$csvlines) {
227 - // no data to export
228 - return false;
229 - }
226 + if (!$csvlines) {
227 + // no data to export
228 + return false;
229 + }
230 230
231 - // force the download of the CSV file
232 - $this->outputHeaders();
231 + // force the download of the CSV file
232 + $this->outputHeaders();
233 233
234 - /**
235 - * Trigger event to allow third-party plugins to set additional headers or contents to output.
236 - *
237 - * @since 1.16.8 (J) - 1.6.8 (WP)
238 - */
239 - VBOFactory::getPlatform()->getDispatcher()->trigger('onBeforeExportOutputCSV', [$this, $this->csv_export_format]);
234 + /**
235 + * Trigger event to allow third-party plugins to set additional headers or contents to output.
236 + *
237 + * @since 1.16.8 (J) - 1.6.8 (WP)
238 + */
239 + VBOFactory::getPlatform()->getDispatcher()->trigger('onBeforeExportOutputCSV', [$this, $this->csv_export_format]);
240 240
241 - // send lines to output
242 - $this->outputCSV($csvlines);
241 + // send lines to output
242 + $this->outputCSV($csvlines);
243 243
244 - exit;
245 - }
244 + exit;
245 + }
246 246
247 - /**
248 - * Returns the name to give to the CSV (or custom) file being exported.
249 - *
250 - * @param bool $cut_suffix if true the file name suffix will be cut off.
251 - * @param string $suffix the optional file name suffix, hence the extension.
252 - * @param bool $pretty if true, dashes will be converted to spaces and more.
253 - *
254 - * @return string
255 - *
256 - * @since 1.16.1 (J) - 1.6.1 (WP)
257 - */
258 - public function getExportCSVFileName($cut_suffix = false, $suffix = '.csv', $pretty = false)
259 - {
260 - if ($this->csv_export_fname) {
261 - // use the exact report file name set
262 - $export_fname = $this->csv_export_fname;
263 - } else {
264 - // use a generic file name
265 - $export_fname = date('Y-m-d_H.i.s') . '-' . $this->reportFile . '.csv';
266 - }
247 + /**
248 + * Returns the name to give to the CSV (or custom) file being exported.
249 + *
250 + * @param bool $cut_suffix if true the file name suffix will be cut off.
251 + * @param string $suffix the optional file name suffix, hence the extension.
252 + * @param bool $pretty if true, dashes will be converted to spaces and more.
253 + *
254 + * @return string
255 + *
256 + * @since 1.16.1 (J) - 1.6.1 (WP)
257 + */
258 + public function getExportCSVFileName($cut_suffix = false, $suffix = '.csv', $pretty = false)
259 + {
260 + if ($this->csv_export_fname) {
261 + // use the exact report file name set
262 + $export_fname = $this->csv_export_fname;
263 + } else {
264 + // use a generic file name
265 + $export_fname = date('Y-m-d_H.i.s') . '-' . $this->reportFile . '.csv';
266 + }
267 267
268 - if ($cut_suffix) {
269 - // cut off the file name suffix
270 - if (!$suffix) {
271 - // detect file extension
272 - $suffix = substr($export_fname, strrpos($export_fname, '.'));
273 - }
274 - $export_fname = basename($export_fname, $suffix);
275 - }
268 + if ($cut_suffix) {
269 + // cut off the file name suffix
270 + if (!$suffix) {
271 + // detect file extension
272 + $suffix = substr($export_fname, strrpos($export_fname, '.'));
273 + }
274 + $export_fname = basename($export_fname, $suffix);
275 + }
276 276
277 - if ($pretty) {
278 - // get the default date separator char
279 - $datesep = VikBooking::getDateSeparator();
280 - // add a dash between a range of dates
281 - $export_fname = preg_replace("/([0-9])-([0-9])/i", '$1 - $2', $export_fname);
282 - // add an empty space between report name and channel name
283 - $export_fname = preg_replace("/([A-Z])-([A-Z])/i", '$1 $2', $export_fname);
284 - // add an empty space between report name and date
285 - $export_fname = preg_replace("/([A-Z0-9])-([0-9])/i", '$1 $2', $export_fname);
286 - // convert the dashes to the actual date separator char
287 - $export_fname = preg_replace("/([0-9])_([0-9])/i", '$1' . $datesep . '$2', $export_fname);
288 - }
277 + if ($pretty) {
278 + // get the default date separator char
279 + $datesep = VikBooking::getDateSeparator();
280 + // add a dash between a range of dates
281 + $export_fname = preg_replace("/([0-9])-([0-9])/i", '$1 - $2', $export_fname);
282 + // add an empty space between report name and channel name
283 + $export_fname = preg_replace("/([A-Z])-([A-Z])/i", '$1 $2', $export_fname);
284 + // add an empty space between report name and date
285 + $export_fname = preg_replace("/([A-Z0-9])-([0-9])/i", '$1 $2', $export_fname);
286 + // convert the dashes to the actual date separator char
287 + $export_fname = preg_replace("/([0-9])_([0-9])/i", '$1' . $datesep . '$2', $export_fname);
288 + }
289 289
290 - return $export_fname;
291 - }
290 + return $export_fname;
291 + }
292 292
293 - /**
294 - * Sets the name to give to the CSV (or custom) file being exported.
295 - *
296 - * @param string $fname the full name of the file to export.
297 - *
298 - * @return self
299 - *
300 - * @since 1.16.1 (J) - 1.6.1 (WP)
301 - */
302 - public function setExportCSVFileName($fname)
303 - {
304 - $this->csv_export_fname = (string)$fname;
293 + /**
294 + * Sets the name to give to the CSV (or custom) file being exported.
295 + *
296 + * @param string $fname the full name of the file to export.
297 + *
298 + * @return self
299 + *
300 + * @since 1.16.1 (J) - 1.6.1 (WP)
301 + */
302 + public function setExportCSVFileName($fname)
303 + {
304 + $this->csv_export_fname = (string)$fname;
305 305
306 - return $this;
307 - }
306 + return $this;
307 + }
308 308
309 - /**
310 - * Builds the list of the CSV lines to be exported from the current report data.
311 - *
312 - * @param bool $no_data true for actually not letting the report run.
313 - *
314 - * @return array list of CSV lines containing lists of CSV fields.
315 - *
316 - * @since 1.16.1 (J) - 1.6.1 (WP)
317 - */
318 - public function getExportCSVLines($no_data = false)
319 - {
320 - if (!$no_data && !$this->getReportData()) {
321 - // nothing to export
322 - return [];
323 - }
309 + /**
310 + * Builds the list of the CSV lines to be exported from the current report data.
311 + *
312 + * @param bool $no_data true for actually not letting the report run.
313 + *
314 + * @return array list of CSV lines containing lists of CSV fields.
315 + *
316 + * @since 1.16.1 (J) - 1.6.1 (WP)
317 + */
318 + public function getExportCSVLines($no_data = false)
319 + {
320 + if (!$no_data && !$this->getReportData()) {
321 + // nothing to export
322 + return [];
323 + }
324 324
325 - // list of CSV lines
326 - $csvlines = [];
325 + // list of CSV lines
326 + $csvlines = [];
327 327
328 - if (!strcasecmp($this->csv_export_format, 'excel')) {
329 - // add instructions for Excel
328 + if (!strcasecmp($this->csv_export_format, 'excel')) {
329 + // add instructions for Excel
330 330
331 - // UTF-8 BOM (not needed because we convert the encoding to UTF-16LE, which has BOM)
332 - // $csvlines[] = chr(0xEF) . chr(0xBB) . chr(0xBF);
331 + // UTF-8 BOM (not needed because we convert the encoding to UTF-16LE, which has BOM)
332 + // $csvlines[] = chr(0xEF) . chr(0xBB) . chr(0xBF);
333 333
334 - // define the separator
335 - $csvlines[] = "sep=;\n";
336 - }
334 + // define the separator
335 + $csvlines[] = "sep=;\n";
336 + }
337 337
338 - // push the head of the CSV file
339 - $csvcols = [];
340 - foreach ($this->cols as $col) {
341 - if (!is_array($col) || isset($col['ignore_export'])) {
342 - // skip column
343 - continue;
344 - }
338 + // push the head of the CSV file
339 + $csvcols = [];
340 + foreach ($this->cols as $col) {
341 + if (!is_array($col) || isset($col['ignore_export'])) {
342 + // skip column
343 + continue;
344 + }
345 345
346 - // push column label
347 - $csvcols[] = $this->encodeExportCSVField($col['label']);
348 - }
346 + // push column label
347 + $csvcols[] = $this->encodeExportCSVField($col['label']);
348 + }
349 349
350 - // push all columns
351 - $csvlines[] = $csvcols;
350 + // push all columns
351 + $csvlines[] = $csvcols;
352 352
353 - // push the rows + footer row of the CSV file
354 - foreach (array_merge($this->rows, $this->footerRow) as $row) {
355 - $csvrow = [];
356 - foreach ($row as $field) {
357 - if (!is_array($field) || isset($field['ignore_export'])) {
358 - // skip value
359 - continue;
360 - }
353 + // push the rows + footer row of the CSV file
354 + foreach (array_merge($this->rows, $this->footerRow) as $row) {
355 + $csvrow = [];
356 + foreach ($row as $field) {
357 + if (!is_array($field) || isset($field['ignore_export'])) {
358 + // skip value
359 + continue;
360 + }
361 361
362 - // build value for export
363 - $export_value = $field['value'];
364 - if (!isset($field['no_export_callback']) && !isset($field['no_csv_callback'])) {
365 - if (is_callable($field['export_callback'] ?? null)) {
366 - // trigger closure callback to prepare the value for export
367 - $export_value = $field['export_callback']($field['value']);
368 - } elseif (is_callable($field['callback_export'] ?? null)) {
369 - // trigger closure callback to prepare the value for export
370 - $export_value = $field['callback_export']($field['value']);
371 - } elseif (is_callable($field['callback'] ?? null)) {
372 - // trigger closure callback to prepare the value for export
373 - $export_value = $field['callback']($field['value']);
374 - }
375 - }
362 + // build value for export
363 + $export_value = $field['value'];
364 + if (!isset($field['no_export_callback']) && !isset($field['no_csv_callback'])) {
365 + if (is_callable($field['export_callback'] ?? null)) {
366 + // trigger closure callback to prepare the value for export
367 + $export_value = $field['export_callback']($field['value']);
368 + } elseif (is_callable($field['callback_export'] ?? null)) {
369 + // trigger closure callback to prepare the value for export
370 + $export_value = $field['callback_export']($field['value']);
371 + } elseif (is_callable($field['callback'] ?? null)) {
372 + // trigger closure callback to prepare the value for export
373 + $export_value = $field['callback']($field['value']);
374 + }
375 + }
376 376
377 - // ensure no HTML is present
378 - $export_value = strip_tags($export_value);
377 + // ensure no HTML is present
378 + $export_value = strip_tags($export_value);
379 379
380 - // apply encoding and transliteration, if needed
381 - $enc_trans_value = $this->encodeExportCSVField($export_value);
380 + // apply encoding and transliteration, if needed
381 + $enc_trans_value = $this->encodeExportCSVField($export_value);
382 382
383 - // make sure transliteration or encoding did not break the string
384 - if (empty($enc_trans_value) && !empty($export_value)) {
385 - // fallback to original and raw value
386 - $enc_trans_value = $export_value;
387 - }
383 + // make sure transliteration or encoding did not break the string
384 + if (empty($enc_trans_value) && !empty($export_value)) {
385 + // fallback to original and raw value
386 + $enc_trans_value = $export_value;
387 + }
388 388
389 - // push row value
390 - $csvrow[] = $enc_trans_value;
391 - }
389 + // push row value
390 + $csvrow[] = $enc_trans_value;
391 + }
392 392
393 - // push the whole row
394 - $csvlines[] = $csvrow;
395 - }
393 + // push the whole row
394 + $csvlines[] = $csvrow;
395 + }
396 396
397 - // return the list of lines
398 - return $csvlines;
399 - }
397 + // return the list of lines
398 + return $csvlines;
399 + }
400 400
401 - /**
402 - * Checks if a custom resource file pointer to export the file has been set.
403 - *
404 - * @return bool
405 - *
406 - * @since 1.16.1 (J) - 1.6.1 (WP)
407 - */
408 - public function hasExportHandler()
409 - {
410 - return is_resource($this->fp_export);
411 - }
401 + /**
402 + * Checks if a custom resource file pointer to export the file has been set.
403 + *
404 + * @return bool
405 + *
406 + * @since 1.16.1 (J) - 1.6.1 (WP)
407 + */
408 + public function hasExportHandler()
409 + {
410 + return is_resource($this->fp_export);
411 + }
412 412
413 - /**
414 - * Returns the resource file pointer on which the CSV lines will be exported.
415 - *
416 - * @return resource
417 - *
418 - * @since 1.16.1 (J) - 1.6.1 (WP)
419 - */
420 - public function getExportCSVHandler()
421 - {
422 - if (is_null($this->fp_export)) {
423 - // set the default resource file pointer (output)
424 - $this->setExportCSVHandler();
425 - }
413 + /**
414 + * Returns the resource file pointer on which the CSV lines will be exported.
415 + *
416 + * @return resource
417 + *
418 + * @since 1.16.1 (J) - 1.6.1 (WP)
419 + */
420 + public function getExportCSVHandler()
421 + {
422 + if (is_null($this->fp_export)) {
423 + // set the default resource file pointer (output)
424 + $this->setExportCSVHandler();
425 + }
426 426
427 - return $this->fp_export;
428 - }
427 + return $this->fp_export;
428 + }
429 429
430 - /**
431 - * Sets the resource file pointer on which the CSV lines will be exported.
432 - * Useful in case the export should be made on a file rather than on output.
433 - *
434 - * @param string|resource $filename file path, identifier or resource.
435 - * @param string $mode the mode for opening the file pointer.
436 - *
437 - * @return self
438 - *
439 - * @since 1.16.1 (J) - 1.6.1 (WP)
440 - */
441 - public function setExportCSVHandler($filename = 'php://output', $mode = 'w')
442 - {
443 - // set resource file pointer
444 - if (is_resource($filename)) {
445 - $this->fp_export = $filename;
446 - } else {
447 - $this->fp_export = fopen($filename, $mode);
448 - }
430 + /**
431 + * Sets the resource file pointer on which the CSV lines will be exported.
432 + * Useful in case the export should be made on a file rather than on output.
433 + *
434 + * @param string|resource $filename file path, identifier or resource.
435 + * @param string $mode the mode for opening the file pointer.
436 + *
437 + * @return self
438 + *
439 + * @since 1.16.1 (J) - 1.6.1 (WP)
440 + */
441 + public function setExportCSVHandler($filename = 'php://output', $mode = 'w')
442 + {
443 + // set resource file pointer
444 + if (is_resource($filename)) {
445 + $this->fp_export = $filename;
446 + } else {
447 + $this->fp_export = fopen($filename, $mode);
448 + }
449 449
450 - return $this;
451 - }
450 + return $this;
451 + }
452 452
453 - /**
454 - * Sets the current type of CSV export format.
455 - *
456 - * @param string $format either "csv" or "excel".
457 - *
458 - * @return self
459 - *
460 - * @since 1.16.1 (J) - 1.6.1 (WP)
461 - */
462 - public function setExportCSVFormat($format)
463 - {
464 - $export_format = 'csv';
465 - if (!strcasecmp($format, 'excel')) {
466 - $export_format = 'excel';
467 - }
453 + /**
454 + * Sets the current type of CSV export format.
455 + *
456 + * @param string $format either "csv" or "excel".
457 + *
458 + * @return self
459 + *
460 + * @since 1.16.1 (J) - 1.6.1 (WP)
461 + */
462 + public function setExportCSVFormat($format)
463 + {
464 + $export_format = 'csv';
465 + if (!strcasecmp($format, 'excel')) {
466 + $export_format = 'excel';
467 + }
468 468
469 - // set format type
470 - $this->csv_export_format = $export_format;
469 + // set format type
470 + $this->csv_export_format = $export_format;
471 471
472 - return $this;
473 - }
472 + return $this;
473 + }
474 474
475 - /**
476 - * Sends to output the necessary headers to download the export file.
477 - *
478 - * @param array $headers optional additional or actual headers.
479 - * @param bool $replace if true, only the provided headers will be used.
480 - *
481 - * @return void
482 - */
483 - public function outputHeaders(array $headers = [], $replace = false)
484 - {
485 - foreach ($headers as $header) {
486 - // send custom header
487 - header($header);
488 - }
475 + /**
476 + * Sends to output the necessary headers to download the export file.
477 + *
478 + * @param array $headers optional additional or actual headers.
479 + * @param bool $replace if true, only the provided headers will be used.
480 + *
481 + * @return void
482 + */
483 + public function outputHeaders(array $headers = [], $replace = false)
484 + {
485 + foreach ($headers as $header) {
486 + // send custom header
487 + header($header);
488 + }
489 489
490 - if ($replace) {
491 - // do not send any other header
492 - return;
493 - }
490 + if ($replace) {
491 + // do not send any other header
492 + return;
493 + }
494 494
495 - // force the download of the CSV file
496 - if (!strcasecmp($this->csv_export_format, 'excel')) {
497 - // file compatible with Excel
498 - header('Content-type: text/csv; charset=UTF-16LE');
499 - } else {
500 - // regular CSV
501 - header('Content-type: text/csv; charset=UTF-8');
502 - }
503 - header('Cache-Control: no-store, no-cache');
504 - header('Content-Disposition: attachment; filename="' . addslashes(basename($this->getExportCSVFileName(), '.csv')) . '.csv"');
505 - }
495 + // force the download of the CSV file
496 + if (!strcasecmp($this->csv_export_format, 'excel')) {
497 + // file compatible with Excel
498 + header('Content-type: text/csv; charset=UTF-16LE');
499 + } else {
500 + // regular CSV
501 + header('Content-type: text/csv; charset=UTF-8');
502 + }
503 + header('Cache-Control: no-store, no-cache');
504 + header('Content-Disposition: attachment; filename="' . addslashes(basename($this->getExportCSVFileName(), '.csv')) . '.csv"');
505 + }
506 506
507 - /**
508 - * Writes the CSV lines to export onto the current resource handler.
509 - *
510 - * @return bool
511 - *
512 - * @since 1.16.1 (J) - 1.6.1 (WP)
513 - */
514 - public function outputCSV(array $csvlines)
515 - {
516 - // get resource file pointer
517 - $fp = $this->getExportCSVHandler();
507 + /**
508 + * Writes the CSV lines to export onto the current resource handler.
509 + *
510 + * @return bool
511 + *
512 + * @since 1.16.1 (J) - 1.6.1 (WP)
513 + */
514 + public function outputCSV(array $csvlines)
515 + {
516 + // get resource file pointer
517 + $fp = $this->getExportCSVHandler();
518 518
519 - if (!$fp) {
520 - // resource file pointer unavailable
521 - return false;
522 - }
519 + if (!$fp) {
520 + // resource file pointer unavailable
521 + return false;
522 + }
523 523
524 - // default CSV delimiter and enclosure
525 - $separator = ',';
526 - $enclosure = '"';
527 - if (!strcasecmp($this->csv_export_format, 'excel')) {
528 - // for Excel we use the semicolon as separator and double quotes as enclosure
529 - $separator = ';';
530 - }
524 + // default CSV delimiter and enclosure
525 + $separator = ',';
526 + $enclosure = '"';
527 + if (!strcasecmp($this->csv_export_format, 'excel')) {
528 + // for Excel we use the semicolon as separator and double quotes as enclosure
529 + $separator = ';';
530 + }
531 531
532 - // send lines to output
533 - foreach ($csvlines as $csvline) {
534 - if (is_string($csvline)) {
535 - // must be the first line instructions for the export format
536 - fputs($fp, $csvline);
532 + // send lines to output
533 + foreach ($csvlines as $csvline) {
534 + if (is_string($csvline)) {
535 + // must be the first line instructions for the export format
536 + fputs($fp, $csvline);
537 537
538 - // go to the next line
539 - continue;
540 - }
538 + // go to the next line
539 + continue;
540 + }
541 541
542 - // put the array of values as a new CSV line
543 - fputcsv($fp, $csvline, $separator, $enclosure, $escape = '');
544 - }
542 + // put the array of values as a new CSV line
543 + fputcsv($fp, $csvline, $separator, $enclosure, $escape = '');
544 + }
545 545
546 - // close the file pointer
547 - fclose($fp);
546 + // close the file pointer
547 + fclose($fp);
548 548
549 - return true;
550 - }
549 + return true;
550 + }
551 551
552 - /**
553 - * Loads a specific report class and returns its instance.
554 - * Should be called for instantiating any report sub-class.
555 - *
556 - * @param string $report the report file name (i.e. "revenue").
557 - *
558 - * @return mixed false or requested report object.
559 - */
560 - public static function getInstanceOf($report)
561 - {
562 - if (empty($report) || !is_string($report)) {
563 - return false;
564 - }
552 + /**
553 + * Loads a specific report class and returns its instance.
554 + * Should be called for instantiating any report sub-class.
555 + *
556 + * @param string $report the report file name (i.e. "revenue").
557 + *
558 + * @return mixed false or requested report object.
559 + */
560 + public static function getInstanceOf($report)
561 + {
562 + if (empty($report) || !is_string($report)) {
563 + return false;
564 + }
565 565
566 - if (substr($report, -4) != '.php') {
567 - $report .= '.php';
568 - }
566 + if (substr($report, -4) != '.php') {
567 + $report .= '.php';
568 + }
569 569
570 - $report_path = dirname(__FILE__) . DIRECTORY_SEPARATOR . $report;
571 - $classname = 'VikBookingReport' . str_replace(' ', '', ucwords(str_replace('.php', '', str_replace('_', ' ', $report))));
570 + $report_path = dirname(__FILE__) . DIRECTORY_SEPARATOR . $report;
571 + $classname = 'VikBookingReport' . str_replace(' ', '', ucwords(str_replace('.php', '', str_replace('_', ' ', $report))));
572 572
573 - if (!is_file($report_path)) {
574 - /**
575 - * Trigger event to let other plugins register additional drivers.
576 - *
577 - * @since 1.16.0 (J) - 1.6.0 (WP)
578 - */
579 - $list = VBOFactory::getPlatform()->getDispatcher()->filter('onLoadPmsReports');
580 - foreach ($list as $chunk) {
581 - if (!is_array($chunk) || !$chunk) {
582 - continue;
583 - }
584 - foreach ($chunk as $thirdp_report) {
585 - if (basename($thirdp_report) == $report) {
586 - // driver found
587 - $report_path = $thirdp_report;
588 - break;
589 - }
590 - }
591 - }
592 - }
573 + if (!is_file($report_path)) {
574 + /**
575 + * Trigger event to let other plugins register additional drivers.
576 + *
577 + * @since 1.16.0 (J) - 1.6.0 (WP)
578 + */
579 + $list = VBOFactory::getPlatform()->getDispatcher()->filter('onLoadPmsReports');
580 + foreach ($list as $chunk) {
581 + if (!is_array($chunk) || !$chunk) {
582 + continue;
583 + }
584 + foreach ($chunk as $thirdp_report) {
585 + if (basename($thirdp_report) == $report) {
586 + // driver found
587 + $report_path = $thirdp_report;
588 + break;
589 + }
590 + }
591 + }
592 + }
593 593
594 - if (!is_file($report_path)) {
595 - // report driver file not found
596 - return false;
597 - }
594 + if (!is_file($report_path)) {
595 + // report driver file not found
596 + return false;
597 + }
598 598
599 - // load report
600 - require_once $report_path;
599 + // load report
600 + require_once $report_path;
601 601
602 - if (!class_exists($classname)) {
603 - // report class does not exist
604 - return false;
605 - }
602 + if (!class_exists($classname)) {
603 + // report class does not exist
604 + return false;
605 + }
606 606
607 - // instantiate the report object
608 - $reportInstance = new $classname;
607 + // instantiate the report object
608 + $reportInstance = new $classname;
609 609
610 - // ensure the report is compatible with the current environment
611 - if ($reportInstance->preflight() === false) {
612 - // the report is not compatible
613 - return false;
614 - }
610 + // ensure the report is compatible with the current environment
611 + if ($reportInstance->preflight() === false) {
612 + // the report is not compatible
613 + return false;
614 + }
615 615
616 - // return the instance of the report object found
617 - return $reportInstance;
618 - }
616 + // return the instance of the report object found
617 + return $reportInstance;
618 + }
619 619
620 - /**
621 - * Injects request variables for the report like if some filters were set.
622 - *
623 - * @param array $vars associative list of request vars to inject.
624 - *
625 - * @return void
626 - *
627 - * @since 1.16.1 (J) - 1.6.1 (WP) static-context used to construct the report object later.
628 - */
629 - public static function setRequestVars(array $vars)
630 - {
631 - foreach ($vars as $key => $value) {
632 - /**
633 - * For more safety across different platforms and versions (J3/J4 or WP)
634 - * we inject values in the super global array as well as in the input object.
635 - */
636 - VikRequest::setVar($key, $value, 'request');
637 - VikRequest::setVar($key, $value);
638 - }
639 - }
620 + /**
621 + * Injects request variables for the report like if some filters were set.
622 + *
623 + * @param array $vars associative list of request vars to inject.
624 + *
625 + * @return void
626 + *
627 + * @since 1.16.1 (J) - 1.6.1 (WP) static-context used to construct the report object later.
628 + */
629 + public static function setRequestVars(array $vars)
630 + {
631 + foreach ($vars as $key => $value) {
632 + /**
633 + * For more safety across different platforms and versions (J3/J4 or WP)
634 + * we inject values in the super global array as well as in the input object.
635 + */
636 + VikRequest::setVar($key, $value, 'request');
637 + VikRequest::setVar($key, $value);
638 + }
639 + }
640 640
641 - /**
642 - * Proxy for object-context to inject request variables for the report.
643 - *
644 - * @param array $params associative list of request vars to inject.
645 - *
646 - * @return self
647 - */
648 - public function injectParams($params)
649 - {
650 - if (is_array($params) && $params) {
651 - self::setRequestVars($params);
652 - }
641 + /**
642 + * Proxy for object-context to inject request variables for the report.
643 + *
644 + * @param array $params associative list of request vars to inject.
645 + *
646 + * @return self
647 + */
648 + public function injectParams($params)
649 + {
650 + if (is_array($params) && $params) {
651 + self::setRequestVars($params);
652 + }
653 653
654 - return $this;
655 - }
654 + return $this;
655 + }
656 656
657 - /**
658 - * Loads Charts CSS/JS assets.
659 - *
660 - * @return self
661 - */
662 - public function loadChartsAssets()
663 - {
664 - $document = JFactory::getDocument();
665 - $document->addStyleSheet(VBO_ADMIN_URI . 'resources/Chart.min.css', ['version' => VIKBOOKING_SOFTWARE_VERSION]);
666 - $document->addScript(VBO_ADMIN_URI . 'resources/Chart.min.js', ['version' => VIKBOOKING_SOFTWARE_VERSION]);
657 + /**
658 + * Loads Charts CSS/JS assets.
659 + *
660 + * @return self
661 + */
662 + public function loadChartsAssets()
663 + {
664 + $document = JFactory::getDocument();
665 + $document->addStyleSheet(VBO_ADMIN_URI . 'resources/Chart.min.css', ['version' => VIKBOOKING_SOFTWARE_VERSION]);
666 + $document->addScript(VBO_ADMIN_URI . 'resources/Chart.min.js', ['version' => VIKBOOKING_SOFTWARE_VERSION]);
667 667
668 - return $this;
669 - }
668 + return $this;
669 + }
670 670
671 - /**
672 - * Loads the jQuery UI Datepicker.
673 - * Method used only by sub-classes.
674 - *
675 - * @return self
676 - */
677 - protected function loadDatePicker()
678 - {
679 - $vbo_app = VikBooking::getVboApplication();
680 - $vbo_app->loadDatePicker();
671 + /**
672 + * Loads the jQuery UI Datepicker.
673 + * Method used only by sub-classes.
674 + *
675 + * @return self
676 + */
677 + protected function loadDatePicker()
678 + {
679 + $vbo_app = VikBooking::getVboApplication();
680 + $vbo_app->loadDatePicker();
681 681
682 - return $this;
683 - }
682 + return $this;
683 + }
684 684
685 - /**
686 - * Parses a report row into an associative list of key-value pairs.
687 - *
688 - * @param array $row The report row to parse.
689 - * @param bool $format True to format values with callbacks.
690 - *
691 - * @return array Associative list of row keys and values.
692 - *
693 - * @since 1.18.4 (J) - 1.8.4 (WP)
694 - */
695 - protected function getAssocRowFields(array $row, bool $format = false)
696 - {
697 - $row_data = [];
685 + /**
686 + * Parses a report row into an associative list of key-value pairs.
687 + *
688 + * @param array $row The report row to parse.
689 + * @param bool $format True to format values with callbacks.
690 + *
691 + * @return array Associative list of row keys and values.
692 + *
693 + * @since 1.18.4 (J) - 1.8.4 (WP)
694 + */
695 + protected function getAssocRowFields(array $row, bool $format = false)
696 + {
697 + $row_data = [];
698 698
699 - foreach ($row as $field) {
700 - $field_val = $field['value'];
701 - if (!isset($field['callback_export']) && isset($field['callback'])) {
702 - $field['callback_export'] = $field['callback'];
703 - }
704 - if ($format === true && !($field['no_export_callback'] ?? 0) && is_callable($field['callback_export'] ?? null)) {
705 - $field_val = $field['callback_export']($field_val);
706 - }
707 - $row_data[$field['key']] = $field_val;
708 - }
699 + foreach ($row as $field) {
700 + $field_val = $field['value'];
701 + if (!isset($field['callback_export']) && isset($field['callback'])) {
702 + $field['callback_export'] = $field['callback'];
703 + }
704 + if ($format === true && !($field['no_export_callback'] ?? 0) && is_callable($field['callback_export'] ?? null)) {
705 + $field_val = $field['callback_export']($field_val);
706 + }
707 + $row_data[$field['key']] = $field_val;
708 + }
709 709
710 - return $row_data;
711 - }
710 + return $row_data;
711 + }
712 712
713 - /**
714 - * Gets the CSS classes for each report row.
715 - *
716 - * @return array
717 - *
718 - * @since 1.18.6 (J) - 1.8.6 (WP)
719 - */
720 - public function getReportRowClasses()
721 - {
722 - return $this->reportRowClasses;
723 - }
713 + /**
714 + * Gets the CSS classes for each report row.
715 + *
716 + * @return array
717 + *
718 + * @since 1.18.6 (J) - 1.8.6 (WP)
719 + */
720 + public function getReportRowClasses()
721 + {
722 + return $this->reportRowClasses;
723 + }
724 724
725 - /**
726 - * Sets the CSS classes for each report row.
727 - *
728 - * @param array $rowClasses List of row class values.
729 - *
730 - * @return void
731 - *
732 - * @since 1.18.6 (J) - 1.8.6 (WP)
733 - */
734 - protected function setReportRowClasses(array $rowClasses)
735 - {
736 - $this->reportRowClasses = $rowClasses;
725 + /**
726 + * Sets the CSS classes for each report row.
727 + *
728 + * @param array $rowClasses List of row class values.
729 + *
730 + * @return void
731 + *
732 + * @since 1.18.6 (J) - 1.8.6 (WP)
733 + */
734 + protected function setReportRowClasses(array $rowClasses)
735 + {
736 + $this->reportRowClasses = $rowClasses;
737 737
738 - return;
739 - }
738 + return;
739 + }
740 740
741 - /**
742 - * Applies the proper encoding to the field being added to
743 - * the CSV lines for export, depending on CSV or Excel.
744 - *
745 - * @param string $field the value being added to the export line.
746 - *
747 - * @return string either the original or the properly encoded field.
748 - *
749 - * @since 1.16.1 (J) - 1.6.1 (WP)
750 - * @since 1.16.8 (J) - 1.6.8 (WP) introduced hook to allow to manipulate encoding.
751 - */
752 - protected function encodeExportCSVField($field)
753 - {
754 - /**
755 - * Trigger event to allow third-party plugins to manipulate encoding,
756 - * in case certain dependencies are not available, such as "mb" support,
757 - * PECL intl for "transliterator_transliterate" or "iconv" to ASCII.
758 - *
759 - * @since 1.16.8 (J) - 1.6.8 (WP)
760 - */
761 - $apply_encoding = VBOFactory::getPlatform()->getDispatcher()->filter('onBeforeEncodingCSVField', [&$field, $this->csv_export_format]);
741 + /**
742 + * Applies the proper encoding to the field being added to
743 + * the CSV lines for export, depending on CSV or Excel.
744 + *
745 + * @param string $field the value being added to the export line.
746 + *
747 + * @return string either the original or the properly encoded field.
748 + *
749 + * @since 1.16.1 (J) - 1.6.1 (WP)
750 + * @since 1.16.8 (J) - 1.6.8 (WP) introduced hook to allow to manipulate encoding.
751 + */
752 + protected function encodeExportCSVField($field)
753 + {
754 + /**
755 + * Trigger event to allow third-party plugins to manipulate encoding,
756 + * in case certain dependencies are not available, such as "mb" support,
757 + * PECL intl for "transliterator_transliterate" or "iconv" to ASCII.
758 + *
759 + * @since 1.16.8 (J) - 1.6.8 (WP)
760 + */
761 + $apply_encoding = VBOFactory::getPlatform()->getDispatcher()->filter('onBeforeEncodingCSVField', [&$field, $this->csv_export_format]);
762 762
763 - if (in_array(false, $apply_encoding, true)) {
764 - // the hook ordered to not proceed with applying any encoding
765 - return $field;
766 - }
763 + if (in_array(false, $apply_encoding, true)) {
764 + // the hook ordered to not proceed with applying any encoding
765 + return $field;
766 + }
767 767
768 - if (!is_string($field) || !strcasecmp($this->csv_export_format, 'csv')) {
769 - // apply no encoding in case of regular CSV or if non-string data type
770 - return $field;
771 - }
768 + if (!is_string($field) || !strcasecmp($this->csv_export_format, 'csv')) {
769 + // apply no encoding in case of regular CSV or if non-string data type
770 + return $field;
771 + }
772 772
773 - // process the Excel-like string field
774 - if (preg_match('/[\\x80-\\xff]/', $field)) {
775 - // UTF-8 encoding detected
776 - if (function_exists('transliterator_transliterate')) {
777 - // if Transliterator is available (PECL intl >= 2.0.0), transliterate to ASCII
778 - $field = transliterator_transliterate('Any-Latin; Latin-ASCII;', $field);
779 - }
773 + // process the Excel-like string field
774 + if (preg_match('/[\\x80-\\xff]/', $field)) {
775 + // UTF-8 encoding detected
776 + if (function_exists('transliterator_transliterate')) {
777 + // if Transliterator is available (PECL intl >= 2.0.0), transliterate to ASCII
778 + $field = transliterator_transliterate('Any-Latin; Latin-ASCII;', $field);
779 + }
780 780
781 - // attempt to convert UTF-8 to ASCII to support currencies
782 - $field = iconv("UTF-8", "ASCII//TRANSLIT//IGNORE", $field);
783 - }
781 + // attempt to convert UTF-8 to ASCII to support currencies
782 + $field = iconv("UTF-8", "ASCII//TRANSLIT//IGNORE", $field);
783 + }
784 784
785 - if (!function_exists('mb_convert_encoding')) {
786 - // abort to prevent server errors
787 - return $field;
788 - }
785 + if (!function_exists('mb_convert_encoding')) {
786 + // abort to prevent server errors
787 + return $field;
788 + }
789 789
790 - // convert encoding to UTF-16LE (low-endian with BOM)
791 - return mb_convert_encoding($field, 'UTF-16LE', ['ASCII', 'UTF-8', 'ISO-8859-1']);
792 - }
790 + // convert encoding to UTF-16LE (low-endian with BOM)
791 + return mb_convert_encoding($field, 'UTF-16LE', ['ASCII', 'UTF-8', 'ISO-8859-1']);
792 + }
793 793
794 - /**
795 - * Used to apply transliteration over UTF-8 characters for having only latins chars.
796 - *
797 - * @param string $value The original string value.
798 - *
799 - * @return string The transliterated string or the original string.
800 - *
801 - * @since 1.18.0 (J) - 1.8.0 (WP)
802 - */
803 - protected function transliterateToAscii(string $value)
804 - {
805 - if (!preg_match('/[\\x80-\\xff]/', $value)) {
806 - // no UTF-8 encoding (special character) detected
807 - return $value;
808 - }
794 + /**
795 + * Used to apply transliteration over UTF-8 characters for having only latins chars.
796 + *
797 + * @param string $value The original string value.
798 + *
799 + * @return string The transliterated string or the original string.
800 + *
801 + * @since 1.18.0 (J) - 1.8.0 (WP)
802 + */
803 + protected function transliterateToAscii(string $value)
804 + {
805 + if (!preg_match('/[\\x80-\\xff]/', $value)) {
806 + // no UTF-8 encoding (special character) detected
807 + return $value;
808 + }
809 809
810 - // make a safe copy of the original string
811 - $copy_value = $value;
810 + // make a safe copy of the original string
811 + $copy_value = $value;
812 812
813 - if (function_exists('transliterator_transliterate')) {
814 - // if Transliterator is available (PECL intl >= 2.0.0), transliterate to ASCII
815 - $copy_value = transliterator_transliterate('Any-Latin; Latin-ASCII;', $copy_value);
816 - }
813 + if (function_exists('transliterator_transliterate')) {
814 + // if Transliterator is available (PECL intl >= 2.0.0), transliterate to ASCII
815 + $copy_value = transliterator_transliterate('Any-Latin; Latin-ASCII;', $copy_value);
816 + }
817 817
818 - if (function_exists('iconv')) {
819 - // attempt to convert UTF-8 to ASCII
820 - $copy_value = iconv("UTF-8", "ASCII//TRANSLIT//IGNORE", $copy_value);
821 - }
818 + if (function_exists('iconv')) {
819 + // attempt to convert UTF-8 to ASCII
820 + $copy_value = iconv("UTF-8", "ASCII//TRANSLIT//IGNORE", $copy_value);
821 + }
822 822
823 - if (empty($copy_value)) {
824 - // revert to the original string value
825 - $copy_value = $value;
826 - }
823 + if (empty($copy_value)) {
824 + // revert to the original string value
825 + $copy_value = $value;
826 + }
827 827
828 - return $copy_value;
829 - }
828 + return $copy_value;
829 + }
830 830
831 - /**
832 - * Loads all the rooms in VBO and returns the array.
833 - *
834 - * @return array
835 - */
836 - protected function getRooms()
837 - {
838 - $q = "SELECT * FROM `#__vikbooking_rooms` ORDER BY `name` ASC;";
839 - $this->dbo->setQuery($q);
840 - $rooms = $this->dbo->loadAssocList();
831 + /**
832 + * Loads all the rooms in VBO and returns the array.
833 + *
834 + * @return array
835 + */
836 + protected function getRooms()
837 + {
838 + $q = "SELECT * FROM `#__vikbooking_rooms` ORDER BY `name` ASC;";
839 + $this->dbo->setQuery($q);
840 + $rooms = $this->dbo->loadAssocList();
841 841
842 - return $rooms;
843 - }
842 + return $rooms;
843 + }
844 844
845 - /**
846 - * Loads all the rate plans in VBO and returns the array.
847 - *
848 - * @return array
849 - *
850 - * @since 1.15.0 (J) - 1.5.0 (WP)
851 - */
852 - protected function getRatePlans()
853 - {
854 - $q = "SELECT * FROM `#__vikbooking_prices` ORDER BY `name` ASC;";
855 - $this->dbo->setQuery($q);
856 - $rplans = $this->dbo->loadAssocList();
845 + /**
846 + * Loads all the rate plans in VBO and returns the array.
847 + *
848 + * @return array
849 + *
850 + * @since 1.15.0 (J) - 1.5.0 (WP)
851 + */
852 + protected function getRatePlans()
853 + {
854 + $q = "SELECT * FROM `#__vikbooking_prices` ORDER BY `name` ASC;";
855 + $this->dbo->setQuery($q);
856 + $rplans = $this->dbo->loadAssocList();
857 857
858 - return VikBooking::sortRatePlans($rplans);
859 - }
858 + return VikBooking::sortRatePlans($rplans);
859 + }
860 860
861 - /**
862 - * Returns the number of total units for all rooms, or for a specific room.
863 - * By default, the rooms unpublished are skipped, and all rooms are used.
864 - *
865 - * @param [mixed] $idroom int or array.
866 - * @param [int] $published true or false.
867 - *
868 - * @return int
869 - */
870 - protected function countRooms($idroom = 0, $published = 1)
871 - {
872 - $clauses = [];
873 - if (is_int($idroom) && $idroom > 0) {
874 - $clauses[] = "`id`=".(int)$idroom;
875 - } elseif (is_array($idroom) && $idroom) {
876 - $clauses[] = "`id` IN (" . implode(', ', $idroom) . ")";
877 - }
878 - if ($published) {
879 - $clauses[] = "`avail`=1";
880 - }
861 + /**
862 + * Returns the number of total units for all rooms, or for a specific room.
863 + * By default, the rooms unpublished are skipped, and all rooms are used.
864 + *
865 + * @param [mixed] $idroom int or array.
866 + * @param [int] $published true or false.
867 + *
868 + * @return int
869 + */
870 + protected function countRooms($idroom = 0, $published = 1)
871 + {
872 + $clauses = [];
873 + if (is_int($idroom) && $idroom > 0) {
874 + $clauses[] = "`id`=".(int)$idroom;
875 + } elseif (is_array($idroom) && $idroom) {
876 + $clauses[] = "`id` IN (" . implode(', ', $idroom) . ")";
877 + }
878 + if ($published) {
879 + $clauses[] = "`avail`=1";
880 + }
881 881
882 - $q = "SELECT SUM(`units`) FROM `#__vikbooking_rooms`".($clauses ? " WHERE ".implode(' AND ', $clauses) : "").";";
883 - $this->dbo->setQuery($q);
884 - $totrooms = (int)$this->dbo->loadResult();
882 + $q = "SELECT SUM(`units`) FROM `#__vikbooking_rooms`".($clauses ? " WHERE ".implode(' AND ', $clauses) : "").";";
883 + $this->dbo->setQuery($q);
884 + $totrooms = (int)$this->dbo->loadResult();
885 885
886 - return $totrooms;
887 - }
886 + return $totrooms;
887 + }
888 888
889 - /**
890 - * Concatenates the JavaScript rules.
891 - * Method used only by sub-classes.
892 - *
893 - * @param string $str
894 - *
895 - * @return self
896 - */
897 - protected function setScript($str)
898 - {
899 - $this->reportScript .= $str."\n";
889 + /**
890 + * Concatenates the JavaScript rules.
891 + * Method used only by sub-classes.
892 + *
893 + * @param string $str
894 + *
895 + * @return self
896 + */
897 + protected function setScript($str)
898 + {
899 + $this->reportScript .= $str."\n";
900 900
901 - return $this;
902 - }
901 + return $this;
902 + }
903 903
904 - /**
905 - * Gets the current script string.
906 - *
907 - * @return string
908 - */
909 - public function getScript()
910 - {
911 - return rtrim($this->reportScript, "\n");
912 - }
904 + /**
905 + * Gets the current script string.
906 + *
907 + * @return string
908 + */
909 + public function getScript()
910 + {
911 + return rtrim($this->reportScript, "\n");
912 + }
913 913
914 - /**
915 - * Returns the date format in VBO for date, jQuery UI, Joomla/WordPress.
916 - * The visibility of this method should be public for anyone who needs it.
917 - *
918 - * @param string $type
919 - *
920 - * @return string
921 - */
922 - public function getDateFormat($type = 'date')
923 - {
924 - $nowdf = VikBooking::getDateFormat();
925 - if ($nowdf == "%d/%m/%Y") {
926 - $df = 'd/m/Y';
927 - $juidf = 'dd/mm/yy';
928 - } elseif ($nowdf == "%m/%d/%Y") {
929 - $df = 'm/d/Y';
930 - $juidf = 'mm/dd/yy';
931 - } else {
932 - $df = 'Y/m/d';
933 - $juidf = 'yy/mm/dd';
934 - }
914 + /**
915 + * Returns the date format in VBO for date, jQuery UI, Joomla/WordPress.
916 + * The visibility of this method should be public for anyone who needs it.
917 + *
918 + * @param string $type
919 + *
920 + * @return string
921 + */
922 + public function getDateFormat($type = 'date')
923 + {
924 + $nowdf = VikBooking::getDateFormat();
925 + if ($nowdf == "%d/%m/%Y") {
926 + $df = 'd/m/Y';
927 + $juidf = 'dd/mm/yy';
928 + } elseif ($nowdf == "%m/%d/%Y") {
929 + $df = 'm/d/Y';
930 + $juidf = 'mm/dd/yy';
931 + } else {
932 + $df = 'Y/m/d';
933 + $juidf = 'yy/mm/dd';
934 + }
935 935
936 - switch ($type) {
937 - case 'jui':
938 - return $juidf;
939 - case 'joomla':
940 - case 'wordpress':
941 - return $nowdf;
942 - default:
943 - return $df;
944 - }
945 - }
936 + switch ($type) {
937 + case 'jui':
938 + return $juidf;
939 + case 'joomla':
940 + case 'wordpress':
941 + return $nowdf;
942 + default:
943 + return $df;
944 + }
945 + }
946 946
947 - /**
948 - * Returns the translated weekday.
949 - * Uses the back-end language definitions.
950 - *
951 - * @param int $wday
952 - * @param string $type use 'long' for the full name of the week, short for the 3-char version
953 - *
954 - * @return string
955 - */
956 - protected function getWdayString($wday, $type = 'long')
957 - {
958 - $wdays_map_long = [
959 - JText::translate('VBWEEKDAYZERO'),
960 - JText::translate('VBWEEKDAYONE'),
961 - JText::translate('VBWEEKDAYTWO'),
962 - JText::translate('VBWEEKDAYTHREE'),
963 - JText::translate('VBWEEKDAYFOUR'),
964 - JText::translate('VBWEEKDAYFIVE'),
965 - JText::translate('VBWEEKDAYSIX')
966 - ];
947 + /**
948 + * Returns the translated weekday.
949 + * Uses the back-end language definitions.
950 + *
951 + * @param int $wday
952 + * @param string $type use 'long' for the full name of the week, short for the 3-char version
953 + *
954 + * @return string
955 + */
956 + protected function getWdayString($wday, $type = 'long')
957 + {
958 + $wdays_map_long = [
959 + JText::translate('VBWEEKDAYZERO'),
960 + JText::translate('VBWEEKDAYONE'),
961 + JText::translate('VBWEEKDAYTWO'),
962 + JText::translate('VBWEEKDAYTHREE'),
963 + JText::translate('VBWEEKDAYFOUR'),
964 + JText::translate('VBWEEKDAYFIVE'),
965 + JText::translate('VBWEEKDAYSIX')
966 + ];
967 967
968 - $wdays_map_short = [
969 - JText::translate('VBSUN'),
970 - JText::translate('VBMON'),
971 - JText::translate('VBTUE'),
972 - JText::translate('VBWED'),
973 - JText::translate('VBTHU'),
974 - JText::translate('VBFRI'),
975 - JText::translate('VBSAT')
976 - ];
968 + $wdays_map_short = [
969 + JText::translate('VBSUN'),
970 + JText::translate('VBMON'),
971 + JText::translate('VBTUE'),
972 + JText::translate('VBWED'),
973 + JText::translate('VBTHU'),
974 + JText::translate('VBFRI'),
975 + JText::translate('VBSAT')
976 + ];
977 977
978 - if ($type != 'long') {
979 - return isset($wdays_map_short[(int)$wday]) ? $wdays_map_short[(int)$wday] : '';
980 - }
978 + if ($type != 'long') {
979 + return isset($wdays_map_short[(int)$wday]) ? $wdays_map_short[(int)$wday] : '';
980 + }
981 981
982 - return isset($wdays_map_long[(int)$wday]) ? $wdays_map_long[(int)$wday] : '';
983 - }
982 + return isset($wdays_map_long[(int)$wday]) ? $wdays_map_long[(int)$wday] : '';
983 + }
984 984
985 - /**
986 - * Sets the columns for this report.
987 - *
988 - * @param array $arr
989 - *
990 - * @return self
991 - */
992 - public function setReportCols($arr)
993 - {
994 - $this->cols = $arr;
985 + /**
986 + * Sets the columns for this report.
987 + *
988 + * @param array $arr
989 + *
990 + * @return self
991 + */
992 + public function setReportCols($arr)
993 + {
994 + $this->cols = $arr;
995 995
996 - return $this;
997 - }
996 + return $this;
997 + }
998 998
999 - /**
1000 - * Returns the columns for this report.
1001 - * Should be called after getReportData()
1002 - * or the returned array will be empty.
1003 - *
1004 - * @return array
1005 - */
1006 - public function getReportCols()
1007 - {
1008 - return $this->cols;
1009 - }
999 + /**
1000 + * Returns the columns for this report.
1001 + * Should be called after getReportData()
1002 + * or the returned array will be empty.
1003 + *
1004 + * @return array
1005 + */
1006 + public function getReportCols()
1007 + {
1008 + return $this->cols;
1009 + }
1010 1010
1011 - /**
1012 - * Sorts the rows of the report by key.
1013 - *
1014 - * @param string $krsort the key attribute of the array pairs
1015 - * @param string $krorder ascending (ASC) or descending (DESC)
1016 - *
1017 - * @return void
1018 - */
1019 - protected function sortRows($krsort, $krorder)
1020 - {
1021 - if (empty($krsort) || !$this->rows) {
1022 - return;
1023 - }
1011 + /**
1012 + * Sorts the rows of the report by key.
1013 + *
1014 + * @param string $krsort the key attribute of the array pairs
1015 + * @param string $krorder ascending (ASC) or descending (DESC)
1016 + *
1017 + * @return void
1018 + */
1019 + protected function sortRows($krsort, $krorder)
1020 + {
1021 + if (empty($krsort) || !$this->rows) {
1022 + return;
1023 + }
1024 1024
1025 - $map = [];
1026 - foreach ($this->rows as $k => $row) {
1027 - foreach ($row as $kk => $v) {
1028 - if (isset($v['key']) && $v['key'] == $krsort) {
1029 - $map[$k] = $v['value'];
1030 - }
1031 - }
1032 - }
1033 - if (!$map) {
1034 - return;
1035 - }
1025 + $map = [];
1026 + foreach ($this->rows as $k => $row) {
1027 + foreach ($row as $kk => $v) {
1028 + if (isset($v['key']) && $v['key'] == $krsort) {
1029 + $map[$k] = $v['value'];
1030 + }
1031 + }
1032 + }
1033 + if (!$map) {
1034 + return;
1035 + }
1036 1036
1037 - if ($krorder == 'ASC') {
1038 - asort($map);
1039 - } else {
1040 - arsort($map);
1041 - }
1037 + if ($krorder == 'ASC') {
1038 + asort($map);
1039 + } else {
1040 + arsort($map);
1041 + }
1042 1042
1043 - $sorted = [];
1044 - foreach ($map as $k => $v) {
1045 - $sorted[$k] = $this->rows[$k];
1046 - }
1043 + $sorted = [];
1044 + foreach ($map as $k => $v) {
1045 + $sorted[$k] = $this->rows[$k];
1046 + }
1047 1047
1048 - $this->rows = $sorted;
1049 - }
1048 + $this->rows = $sorted;
1049 + }
1050 1050
1051 - /**
1052 - * Sets the rows for this report.
1053 - *
1054 - * @param array $arr
1055 - *
1056 - * @return self
1057 - */
1058 - public function setReportRows($arr)
1059 - {
1060 - $this->rows = $arr;
1051 + /**
1052 + * Sets the rows for this report.
1053 + *
1054 + * @param array $arr
1055 + *
1056 + * @return self
1057 + */
1058 + public function setReportRows($arr)
1059 + {
1060 + $this->rows = $arr;
1061 1061
1062 - return $this;
1063 - }
1062 + return $this;
1063 + }
1064 1064
1065 - /**
1066 - * Returns the rows for this report.
1067 - * Should be called after getReportData()
1068 - * or the returned array will be empty.
1069 - *
1070 - * @return array
1071 - */
1072 - public function getReportRows()
1073 - {
1074 - return $this->rows;
1075 - }
1065 + /**
1066 + * Returns the rows for this report.
1067 + * Should be called after getReportData()
1068 + * or the returned array will be empty.
1069 + *
1070 + * @return array
1071 + */
1072 + public function getReportRows()
1073 + {
1074 + return $this->rows;
1075 + }
1076 1076
1077 - /**
1078 - * This method returns one or more rows (given the depth) generated by
1079 - * the current report invoked. It is useful to clean up the callbacks
1080 - * of the various cell-rows, to obtain a parsable result.
1081 - * Can be called as first method, by skipping also getReportData().
1082 - *
1083 - * @param int $depth how many records to obtain, null for all.
1084 - *
1085 - * @return array the queried report value in the given depth.
1086 - *
1087 - * @uses getReportData()
1088 - */
1089 - public function getReportValues($depth = null)
1090 - {
1091 - if (!$this->rows && !$this->getReportData()) {
1092 - return [];
1093 - }
1077 + /**
1078 + * This method returns one or more rows (given the depth) generated by
1079 + * the current report invoked. It is useful to clean up the callbacks
1080 + * of the various cell-rows, to obtain a parsable result.
1081 + * Can be called as first method, by skipping also getReportData().
1082 + *
1083 + * @param ?int $depth how many records to obtain, null for all.
1084 + *
1085 + * @return array the queried report value in the given depth.
1086 + *
1087 + * @uses getReportData()
1088 + */
1089 + public function getReportValues(?int $depth = null)
1090 + {
1091 + if (!$this->rows && !$this->getReportData()) {
1092 + return [];
1093 + }
1094 1094
1095 - $report_values = [];
1095 + $report_values = [];
1096 1096
1097 - foreach ($this->rows as $rk => $row) {
1098 - $report_values[$rk] = [];
1099 - foreach ($row as $col => $coldata) {
1100 - $display_value = $coldata['value'];
1101 - if (isset($coldata['callback']) && is_callable($coldata['callback'])) {
1102 - // launch callback
1103 - $display_value = $coldata['callback']($coldata['value']);
1104 - }
1105 - // push column value
1106 - $report_values[$rk][$coldata['key']] = [
1107 - 'value' => $coldata['value'],
1108 - 'display_value' => $display_value,
1109 - ];
1110 - /**
1111 - * We also pass along any reserved key for this row-data.
1112 - *
1113 - * @since 1.15.0 (J) - 1.5.0 (WP)
1114 - */
1115 - foreach ($coldata as $res_key => $data_val) {
1116 - if (substr($res_key, 0, 1) == '_') {
1117 - // push this reserved key
1118 - $report_values[$rk][$coldata['key']][$res_key] = $data_val;
1119 - }
1120 - }
1121 - }
1122 - }
1097 + foreach ($this->rows as $rk => $row) {
1098 + $report_values[$rk] = [];
1099 + foreach ($row as $col => $coldata) {
1100 + $display_value = $coldata['value'];
1101 + if (isset($coldata['callback']) && is_callable($coldata['callback'])) {
1102 + // launch callback
1103 + $display_value = $coldata['callback']($coldata['value']);
1104 + }
1105 + // push column value
1106 + $report_values[$rk][$coldata['key']] = [
1107 + 'value' => $coldata['value'],
1108 + 'display_value' => $display_value,
1109 + ];
1110 + /**
1111 + * We also pass along any reserved key for this row-data.
1112 + *
1113 + * @since 1.15.0 (J) - 1.5.0 (WP)
1114 + */
1115 + foreach ($coldata as $res_key => $data_val) {
1116 + if (substr($res_key, 0, 1) == '_') {
1117 + // push this reserved key
1118 + $report_values[$rk][$coldata['key']][$res_key] = $data_val;
1119 + }
1120 + }
1121 + }
1122 + }
1123 1123
1124 - if (!$report_values) {
1125 - return [];
1126 - }
1124 + if (!$report_values) {
1125 + return [];
1126 + }
1127 1127
1128 - if ($depth === 1) {
1129 - // get an associative array with the first row calculated
1130 - return $report_values[0];
1131 - }
1128 + if ($depth === 1) {
1129 + // get an associative array with the first row calculated
1130 + return $report_values[0];
1131 + }
1132 1132
1133 - if (is_int($depth) && $depth > 0 && count($report_values) >= $depth) {
1134 - // get the requested portion of the array
1135 - return array_slice($report_values, 0, $depth);
1136 - }
1133 + if (is_int($depth) && $depth > 0 && count($report_values) >= $depth) {
1134 + // get the requested portion of the array
1135 + return array_slice($report_values, 0, $depth);
1136 + }
1137 1137
1138 - return $report_values;
1139 - }
1138 + return $report_values;
1139 + }
1140 1140
1141 - /**
1142 - * Maps the columns labels to an associative array to be used for the values.
1143 - *
1144 - * @return array associative list of column keys and related values.
1145 - */
1146 - public function getColumnsValues()
1147 - {
1148 - if (!$this->cols) {
1149 - return [];
1150 - }
1141 + /**
1142 + * Maps the columns labels to an associative array to be used for the values.
1143 + *
1144 + * @return array associative list of column keys and related values.
1145 + */
1146 + public function getColumnsValues()
1147 + {
1148 + if (!$this->cols) {
1149 + return [];
1150 + }
1151 1151
1152 - $col_values = [];
1152 + $col_values = [];
1153 1153
1154 - foreach ($this->cols as $col) {
1155 - if (!isset($col['key'])) {
1156 - continue;
1157 - }
1158 - $col_values[$col['key']] = $col;
1159 - unset($col_values[$col['key']]['key']);
1160 - }
1154 + foreach ($this->cols as $col) {
1155 + if (!isset($col['key'])) {
1156 + continue;
1157 + }
1158 + $col_values[$col['key']] = $col;
1159 + unset($col_values[$col['key']]['key']);
1160 + }
1161 1161
1162 - return $col_values;
1163 - }
1162 + return $col_values;
1163 + }
1164 1164
1165 - /**
1166 - * Gets a property defined by the report. Useful to get custom
1167 - * properties set up by a specific report maybe for the Chart.
1168 - *
1169 - * @param string $property the name of the property needed.
1170 - * @param mixed $def default value to return.
1171 - *
1172 - * @return mixed false on failure, property requested otherwise.
1173 - */
1174 - public function getProperty($property, $def = false)
1175 - {
1176 - if (isset($this->{$property})) {
1177 - return $this->{$property};
1178 - }
1165 + /**
1166 + * Gets a property defined by the report. Useful to get custom
1167 + * properties set up by a specific report maybe for the Chart.
1168 + *
1169 + * @param string $property the name of the property needed.
1170 + * @param mixed $def default value to return.
1171 + *
1172 + * @return mixed false on failure, property requested otherwise.
1173 + */
1174 + public function getProperty($property, $def = false)
1175 + {
1176 + if (isset($this->{$property})) {
1177 + return $this->{$property};
1178 + }
1179 1179
1180 - return $def;
1181 - }
1180 + return $def;
1181 + }
1182 1182
1183 - /**
1184 - * Counts the number of days of difference between two timestamps.
1185 - *
1186 - * @param int $to_ts the target end date timestamp.
1187 - * @param int $from_ts the starting date timestamp.
1188 - *
1189 - * @return int the days of difference between from and to timestamps.
1190 - */
1191 - public function countDaysTo($to_ts, $from_ts = 0)
1192 - {
1193 - if (empty($from_ts)) {
1194 - $from_ts = time();
1195 - }
1183 + /**
1184 + * Counts the number of days of difference between two timestamps.
1185 + *
1186 + * @param int $to_ts the target end date timestamp.
1187 + * @param int $from_ts the starting date timestamp.
1188 + *
1189 + * @return int the days of difference between from and to timestamps.
1190 + */
1191 + public function countDaysTo($to_ts, $from_ts = 0)
1192 + {
1193 + if (empty($from_ts)) {
1194 + $from_ts = time();
1195 + }
1196 1196
1197 - // whether DateTime can be used
1198 - $usedt = false;
1197 + // whether DateTime can be used
1198 + $usedt = false;
1199 1199
1200 - if (class_exists('DateTime')) {
1201 - $from_date = new DateTime(date('Y-m-d', $from_ts));
1202 - if (method_exists($from_date, 'diff')) {
1203 - $usedt = true;
1204 - }
1205 - }
1200 + if (class_exists('DateTime')) {
1201 + $from_date = new DateTime(date('Y-m-d', $from_ts));
1202 + if (method_exists($from_date, 'diff')) {
1203 + $usedt = true;
1204 + }
1205 + }
1206 1206
1207 - if ($usedt) {
1208 - $to_date = new DateTime(date('Y-m-d', $to_ts));
1209 - $daysdiff = (int)$from_date->diff($to_date)->format('%a');
1210 - if ($to_ts < $from_ts) {
1211 - // we need a negative integer number
1212 - $daysdiff = $daysdiff - ($daysdiff * 2);
1213 - }
1214 - return $daysdiff;
1215 - }
1207 + if ($usedt) {
1208 + $to_date = new DateTime(date('Y-m-d', $to_ts));
1209 + $daysdiff = (int)$from_date->diff($to_date)->format('%a');
1210 + if ($to_ts < $from_ts) {
1211 + // we need a negative integer number
1212 + $daysdiff = $daysdiff - ($daysdiff * 2);
1213 + }
1214 + return $daysdiff;
1215 + }
1216 1216
1217 - return (int)round(($to_ts - $from_ts) / 86400);
1218 - }
1217 + return (int)round(($to_ts - $from_ts) / 86400);
1218 + }
1219 1219
1220 - /**
1221 - * Counts the average difference between two integers.
1222 - *
1223 - * @param int $in_days_from days to the lowest timestamp.
1224 - * @param int $in_days_to days to the highest timestamp.
1225 - *
1226 - * @return int the average number between the two values.
1227 - */
1228 - public function countAverageDays($in_days_from, $in_days_to)
1229 - {
1230 - return (int)floor(($in_days_from + $in_days_to) / 2);
1231 - }
1220 + /**
1221 + * Counts the average difference between two integers.
1222 + *
1223 + * @param int $in_days_from days to the lowest timestamp.
1224 + * @param int $in_days_to days to the highest timestamp.
1225 + *
1226 + * @return int the average number between the two values.
1227 + */
1228 + public function countAverageDays($in_days_from, $in_days_to)
1229 + {
1230 + return (int)floor(($in_days_from + $in_days_to) / 2);
1231 + }
1232 1232
1233 - /**
1234 - * Sets the footer row (the totals) for this report.
1235 - *
1236 - * @param array $arr
1237 - *
1238 - * @return self
1239 - */
1240 - protected function setReportFooterRow($arr)
1241 - {
1242 - $this->footerRow = $arr;
1233 + /**
1234 + * Sets the footer row (the totals) for this report.
1235 + *
1236 + * @param array $arr
1237 + *
1238 + * @return self
1239 + */
1240 + protected function setReportFooterRow($arr)
1241 + {
1242 + $this->footerRow = $arr;
1243 1243
1244 - return $this;
1245 - }
1244 + return $this;
1245 + }
1246 1246
1247 - /**
1248 - * Returns the footer row for this report.
1249 - * Should be called after getReportData()
1250 - * or the returned array will be empty.
1251 - *
1252 - * @return array
1253 - */
1254 - public function getReportFooterRow()
1255 - {
1256 - return $this->footerRow;
1257 - }
1247 + /**
1248 + * Returns the footer row for this report.
1249 + * Should be called after getReportData()
1250 + * or the returned array will be empty.
1251 + *
1252 + * @return array
1253 + */
1254 + public function getReportFooterRow()
1255 + {
1256 + return $this->footerRow;
1257 + }
1258 1258
1259 - /**
1260 - * Sub-classes can extend this method to define the
1261 - * the canvas HTML tag for rendenring the Chart.
1262 - * Any necessary script shall be set within this method.
1263 - * Data can be passed as a mixed value through the argument.
1264 - * This is the first method to be called when working with the Chart.
1265 - *
1266 - * @param ?array $data any necessary value to render the Chart.
1267 - *
1268 - * @return string the HTML of the canvas element.
1269 - */
1270 - public function getChart(?array $data = null)
1271 - {
1272 - return '';
1273 - }
1259 + /**
1260 + * Sub-classes can extend this method to define the
1261 + * the canvas HTML tag for rendenring the Chart.
1262 + * Any necessary script shall be set within this method.
1263 + * Data can be passed as a mixed value through the argument.
1264 + * This is the first method to be called when working with the Chart.
1265 + *
1266 + * @param ?array $data any necessary value to render the Chart.
1267 + *
1268 + * @return string the HTML of the canvas element.
1269 + */
1270 + public function getChart(?array $data = null)
1271 + {
1272 + return '';
1273 + }
1274 1274
1275 - /**
1276 - * Sub-classes can extend this method to define the
1277 - * the title of the Chart to be rendered.
1278 - *
1279 - * @return string the title of the Chart.
1280 - */
1281 - public function getChartTitle()
1282 - {
1283 - return '';
1284 - }
1275 + /**
1276 + * Sub-classes can extend this method to define the
1277 + * the title of the Chart to be rendered.
1278 + *
1279 + * @return string the title of the Chart.
1280 + */
1281 + public function getChartTitle()
1282 + {
1283 + return '';
1284 + }
1285 1285
1286 - /**
1287 - * Sub-classes can extend this method to define
1288 - * the meta data for the Chart containing stats.
1289 - * An array for each meta-data should be returned.
1290 - *
1291 - * @param mixed $position string for the meta-data position
1292 - * in the Chart (top, right, bottom).
1293 - * @param mixed $data some arguments to be passed.
1294 - *
1295 - * @return array
1296 - */
1297 - public function getChartMetaData($position = null, $data = null)
1298 - {
1299 - return [];
1300 - }
1286 + /**
1287 + * Sub-classes can extend this method to define
1288 + * the meta data for the Chart containing stats.
1289 + * An array for each meta-data should be returned.
1290 + *
1291 + * @param mixed $position string for the meta-data position
1292 + * in the Chart (top, right, bottom).
1293 + * @param mixed $data some arguments to be passed.
1294 + *
1295 + * @return array
1296 + */
1297 + public function getChartMetaData($position = null, $data = null)
1298 + {
1299 + return [];
1300 + }
1301 1301
1302 - /**
1303 - * Sets an array of custom options for this report. Useful to inject
1304 - * params before getting the report data and changing the behavior.
1305 - *
1306 - * @param array $options The associative options to set.
1307 - *
1308 - * @return self
1309 - *
1310 - * @since 1.15.0 (J) - 1.5.0 (WP)
1311 - */
1312 - public function setReportOptions(array $options = [])
1313 - {
1314 - $this->options = $options;
1302 + /**
1303 + * Sets an array of custom options for this report. Useful to inject
1304 + * params before getting the report data and changing the behavior.
1305 + *
1306 + * @param array $options The associative options to set.
1307 + *
1308 + * @return self
1309 + *
1310 + * @since 1.15.0 (J) - 1.5.0 (WP)
1311 + */
1312 + public function setReportOptions(array $options = [])
1313 + {
1314 + $this->options = $options;
1315 1315
1316 - return $this;
1317 - }
1316 + return $this;
1317 + }
1318 1318
1319 - /**
1320 - * Returns the custom options for the report. Useful to
1321 - * behave differently depending on who calls the report.
1322 - * By default, the method returns an instance of JObject
1323 - * to easily access all custom options defined, if any.
1324 - *
1325 - * @param bool $registry true to get a JObject instance.
1326 - *
1327 - * @return mixed instance of JObject or raw array.
1328 - *
1329 - * @since 1.15.0 (J) - 1.5.0 (WP)
1330 - */
1331 - public function getReportOptions($registry = true)
1332 - {
1333 - if ($registry) {
1334 - return new JObject($this->options);
1335 - }
1319 + /**
1320 + * Returns the custom options for the report. Useful to
1321 + * behave differently depending on who calls the report.
1322 + *
1323 + * @param bool $registry True to wrap options into a registry.
1324 + * @param bool $profileListings True to merge report profile listings setting.
1325 + *
1326 + * @return JObject|array
1327 + *
1328 + * @since 1.15.0 (J) - 1.5.0 (WP)
1329 + * @since 1.18.7 (J) - 1.8.7 (WP) added argument $profileListings.
1330 + */
1331 + public function getReportOptions(bool $registry = true, bool $profileListings = true)
1332 + {
1333 + if ($profileListings && $this->allowsProfileListings()) {
1334 + // load report settings
1335 + $reportSettings = $this->loadSettings();
1336 + if ($reportSettings['_listings'] ?? null) {
1337 + // options should force profile listings
1338 + $this->options['listings'] = (array) $reportSettings['_listings'];
1339 + }
1340 + }
1336 1341
1337 - return $this->options;
1338 - }
1342 + if ($registry) {
1343 + return new JObject($this->options);
1344 + }
1339 1345
1340 - /**
1341 - * Defines an associative list of action data.
1342 - *
1343 - * @param array $data Associative list of action data.
1344 - *
1345 - * @return self
1346 - *
1347 - * @since 1.17.1 (J) - 1.7.1 (WP)
1348 - */
1349 - public function setActionData(array $data)
1350 - {
1351 - $this->actionData = $data;
1346 + return $this->options;
1347 + }
1352 1348
1353 - return $this;
1354 - }
1349 + /**
1350 + * Defines an associative list of action data.
1351 + *
1352 + * @param array $data Associative list of action data.
1353 + *
1354 + * @return self
1355 + *
1356 + * @since 1.17.1 (J) - 1.7.1 (WP)
1357 + */
1358 + public function setActionData(array $data)
1359 + {
1360 + $this->actionData = $data;
1355 1361
1356 - /**
1357 - * Returns the action data, either raw or as a registry.
1358 - *
1359 - * @param bool $registry True to get a JObject instance.
1360 - *
1361 - * @return array|JObject
1362 - *
1363 - * @since 1.17.1 (J) - 1.7.1 (WP)
1364 - */
1365 - public function getActionData($registry = true)
1366 - {
1367 - if ($registry) {
1368 - return new JObject($this->actionData);
1369 - }
1362 + return $this;
1363 + }
1370 1364
1371 - return $this->actionData;
1372 - }
1365 + /**
1366 + * Returns the action data, either raw or as a registry.
1367 + *
1368 + * @param bool $registry True to get a JObject instance.
1369 + *
1370 + * @return array|JObject
1371 + *
1372 + * @since 1.17.1 (J) - 1.7.1 (WP)
1373 + */
1374 + public function getActionData($registry = true)
1375 + {
1376 + if ($registry) {
1377 + return new JObject($this->actionData);
1378 + }
1373 1379
1374 - /**
1375 - * Sets the global scope for the report.
1376 - *
1377 - * @param string $scope The scope to set.
1378 - *
1379 - * @return self
1380 - *
1381 - * @since 1.17.1 (J) - 1.7.1 (WP)
1382 - */
1383 - public function setScope($scope)
1384 - {
1385 - $this->scope = $scope;
1380 + return $this->actionData;
1381 + }
1386 1382
1387 - return $this;
1388 - }
1383 + /**
1384 + * Sets the global scope for the report.
1385 + *
1386 + * @param string $scope The scope to set.
1387 + *
1388 + * @return self
1389 + *
1390 + * @since 1.17.1 (J) - 1.7.1 (WP)
1391 + */
1392 + public function setScope($scope)
1393 + {
1394 + $this->scope = $scope;
1389 1395
1390 - /**
1391 - * Returns the global scope of the invoked report.
1392 - *
1393 - * @return ?string
1394 - *
1395 - * @since 1.17.1 (J) - 1.7.1 (WP)
1396 - */
1397 - public function getScope()
1398 - {
1399 - return $this->scope;
1400 - }
1396 + return $this;
1397 + }
1401 1398
1402 - /**
1403 - * Returns the path to the PMS media data directory.
1404 - *
1405 - * @return string
1406 - *
1407 - * @since 1.17.1 (J) - 1.7.1 (WP)
1408 - */
1409 - public function getDataMediaPath()
1410 - {
1411 - return implode(DIRECTORY_SEPARATOR, [VBO_ADMIN_PATH, 'resources', 'pmsdata']);
1412 - }
1399 + /**
1400 + * Returns the global scope of the invoked report.
1401 + *
1402 + * @return ?string
1403 + *
1404 + * @since 1.17.1 (J) - 1.7.1 (WP)
1405 + */
1406 + public function getScope()
1407 + {
1408 + return $this->scope;
1409 + }
1413 1410
1414 - /**
1415 - * Returns the URL to the PMS media data directory.
1416 - *
1417 - * @return string
1418 - *
1419 - * @since 1.17.1 (J) - 1.7.1 (WP)
1420 - */
1421 - public function getDataMediaUrl()
1422 - {
1423 - return VBO_ADMIN_URI . 'resources/pmsdata/';
1424 - }
1411 + /**
1412 + * Returns the path to the PMS media data directory.
1413 + *
1414 + * @return string
1415 + *
1416 + * @since 1.17.1 (J) - 1.7.1 (WP)
1417 + */
1418 + public function getDataMediaPath()
1419 + {
1420 + return implode(DIRECTORY_SEPARATOR, [VBO_ADMIN_PATH, 'resources', 'pmsdata']);
1421 + }
1425 1422
1426 - /**
1427 - * Returns the optional report custom setting fields.
1428 - *
1429 - * @return array
1430 - *
1431 - * @since 1.17.1 (J) - 1.7.1 (WP)
1432 - */
1433 - public function getSettingFields()
1434 - {
1435 - return [];
1436 - }
1423 + /**
1424 + * Returns the URL to the PMS media data directory.
1425 + *
1426 + * @return string
1427 + *
1428 + * @since 1.17.1 (J) - 1.7.1 (WP)
1429 + */
1430 + public function getDataMediaUrl()
1431 + {
1432 + return VBO_ADMIN_URI . 'resources/pmsdata/';
1433 + }
1437 1434
1438 - /**
1439 - * Tells whether the current report allows to save
1440 - * the settings across multiple profile identifiers.
1441 - *
1442 - * @return bool
1443 - *
1444 - * @since 1.17.7 (J) - 1.7.7 (WP)
1445 - */
1446 - public function allowsProfileSettings()
1447 - {
1448 - // report profile settings disabled by default
1449 - return false;
1450 - }
1435 + /**
1436 + * Returns the optional report custom setting fields at profile level.
1437 + * This method allows to load the report setting fields by injecting
1438 + * profile-level (protected) preferences, such as listings filtered.
1439 + *
1440 + * @return array
1441 + *
1442 + * @since 1.18.7 (J) - 1.8.7 (WP)
1443 + */
1444 + public function getProfileSettingFields()
1445 + {
1446 + // load all report setting fields
1447 + $reportSettingFields = $this->getSettingFields();
1451 1448
1452 - /**
1453 - * Returns the report currently active profile identifier, if any.
1454 - *
1455 - * @return string
1456 - *
1457 - * @since 1.17.7 (J) - 1.7.7 (WP)
1458 - */
1459 - public function getActiveProfile()
1460 - {
1461 - $profile = (string) VBOFactory::getConfig()->getString('report_active_profile_' . $this->getFileName(), '');
1449 + if ($reportSettingFields && $this->allowsProfileListings()) {
1450 + // set protected listing setting field
1451 + $reportSettingFields['_listings'] = [
1452 + 'type' => 'listings',
1453 + 'label' => JText::translate('VBO_LISTINGS'),
1454 + 'help' => sprintf('(%s)', JText::translate('VBOFILTEISROPTIONAL')),
1455 + 'multiple' => true,
1456 + 'asset_options' => [
1457 + 'placeholder' => JText::translate('VBO_LISTINGS'),
1458 + 'allowClear' => true,
1459 + ],
1460 + ];
1461 + }
1462 1462
1463 - return $profile;
1464 - }
1463 + // return the full list of setting fields
1464 + return $reportSettingFields;
1465 + }
1465 1466
1466 - /**
1467 - * Sets the report currently active profile identifier.
1468 - *
1469 - * @param string $profile_id The profile identifier to set.
1470 - *
1471 - * @return void
1472 - *
1473 - * @since 1.17.7 (J) - 1.7.7 (WP)
1474 - */
1475 - public function setActiveProfile(string $profile_id)
1476 - {
1477 - VBOFactory::getConfig()->set('report_active_profile_' . $this->getFileName(), $profile_id);
1478 - }
1467 + /**
1468 + * Returns the optional report custom setting fields.
1469 + *
1470 + * @return array
1471 + *
1472 + * @since 1.17.1 (J) - 1.7.1 (WP)
1473 + */
1474 + public function getSettingFields()
1475 + {
1476 + return [];
1477 + }
1479 1478
1480 - /**
1481 - * Returns an associative list for the report setting profiles available.
1482 - *
1483 - * @return array
1484 - *
1485 - * @since 1.17.7 (J) - 1.7.7 (WP)
1486 - */
1487 - public function getSettingProfiles()
1488 - {
1489 - return (array) VBOFactory::getConfig()->getArray('report_profile_list_' . $this->getFileName(), []);
1490 - }
1479 + /**
1480 + * Tells whether the current report allows to save
1481 + * the settings across multiple profile identifiers.
1482 + *
1483 + * @return bool
1484 + *
1485 + * @since 1.17.7 (J) - 1.7.7 (WP)
1486 + */
1487 + public function allowsProfileSettings()
1488 + {
1489 + // report profile settings disabled by default
1490 + return false;
1491 + }
1491 1492
1492 - /**
1493 - * Sets a new report setting profile identifier.
1494 - *
1495 - * @param string $profile_name The profile name.
1496 - *
1497 - * @return array List of profile identifier and name.
1498 - *
1499 - * @since 1.17.7 (J) - 1.7.7 (WP)
1500 - */
1501 - public function setSettingProfile(string $profile_name)
1502 - {
1503 - // get all report profiles
1504 - $profiles = $this->getSettingProfiles();
1493 + /**
1494 + * Tells whether the current report allows to save
1495 + * the listings across multiple profile identifiers.
1496 + *
1497 + * @return bool
1498 + *
1499 + * @since 1.18.7 (J) - 1.8.7 (WP)
1500 + */
1501 + public function allowsProfileListings()
1502 + {
1503 + // report profile listings enabled as long as multiple profiles are enabled
1504 + return $this->allowsProfileSettings();
1505 + }
1505 1506
1506 - // build profile identifier
1507 - $profile_id = preg_replace('/[^A-Z0-9]/i', '', strtolower($profile_name));
1508 - $profile_id = $profile_id ?: uniqid();
1507 + /**
1508 + * Returns the report currently active profile identifier, if any.
1509 + *
1510 + * @return string
1511 + *
1512 + * @since 1.17.7 (J) - 1.7.7 (WP)
1513 + */
1514 + public function getActiveProfile()
1515 + {
1516 + $profile = (string) VBOFactory::getConfig()->getString('report_active_profile_' . $this->getFileName(), '');
1509 1517
1510 - // set report profile
1511 - $profiles[$profile_id] = $profile_name;
1512 - VBOFactory::getConfig()->set('report_profile_list_' . $this->getFileName(), $profiles);
1518 + return $profile;
1519 + }
1513 1520
1514 - return [$profile_id, $profile_name];
1515 - }
1521 + /**
1522 + * Sets the report currently active profile identifier.
1523 + *
1524 + * @param string $profile_id The profile identifier to set.
1525 + *
1526 + * @return void
1527 + *
1528 + * @since 1.17.7 (J) - 1.7.7 (WP)
1529 + */
1530 + public function setActiveProfile(string $profile_id)
1531 + {
1532 + VBOFactory::getConfig()->set('report_active_profile_' . $this->getFileName(), $profile_id);
1533 + }
1516 1534
1517 - /**
1518 - * Clears all profile settings and related data.
1519 - *
1520 - * @return void
1521 - *
1522 - * @since 1.17.7 (J) - 1.7.7 (WP)
1523 - */
1524 - public function clearProfiles()
1525 - {
1526 - // unset active profile
1527 - VBOFactory::getConfig()->set('report_active_profile_' . $this->getFileName(), '');
1535 + /**
1536 + * Returns an associative list for the report setting profiles available.
1537 + *
1538 + * @return array
1539 + *
1540 + * @since 1.17.7 (J) - 1.7.7 (WP)
1541 + */
1542 + public function getSettingProfiles()
1543 + {
1544 + return (array) VBOFactory::getConfig()->getArray('report_profile_list_' . $this->getFileName(), []);
1545 + }
1528 1546
1529 - // unset profiles list
1530 - VBOFactory::getConfig()->set('report_profile_list_' . $this->getFileName(), []);
1547 + /**
1548 + * Sets a new report setting profile identifier.
1549 + *
1550 + * @param string $profile_name The profile name.
1551 + *
1552 + * @return array List of profile identifier and name.
1553 + *
1554 + * @since 1.17.7 (J) - 1.7.7 (WP)
1555 + */
1556 + public function setSettingProfile(string $profile_name)
1557 + {
1558 + // get all report profiles
1559 + $profiles = $this->getSettingProfiles();
1531 1560
1532 - // clear profile settings
1533 - VBOFactory::getConfig()->set('report_profile_settings_' . $this->getFileName(), []);
1534 - }
1561 + // build profile identifier
1562 + $profile_id = preg_replace('/[^A-Z0-9]/i', '', strtolower($profile_name));
1563 + $profile_id = $profile_id ?: uniqid();
1535 1564
1536 - /**
1537 - * Returns the current report custom settings, optionally loaded from a
1538 - * given profile identifier in case the report supports multiple settings.
1539 - *
1540 - * @param string $profile Optional settings profile identifier.
1541 - *
1542 - * @return array
1543 - *
1544 - * @since 1.17.1 (J) - 1.7.1 (WP)
1545 - */
1546 - public function loadSettings(string $profile = '')
1547 - {
1548 - // access report current settings
1549 - $current_settings = (array) VBOFactory::getConfig()->getArray('report_settings_' . $this->getFileName(), []);
1565 + // set report profile
1566 + $profiles[$profile_id] = $profile_name;
1567 + VBOFactory::getConfig()->set('report_profile_list_' . $this->getFileName(), $profiles);
1550 1568
1551 - // default settings
1552 - $default_settings = [];
1569 + return [$profile_id, $profile_name];
1570 + }
1553 1571
1554 - if (!$profile && $this->allowsProfileSettings()) {
1555 - // load current profile settings
1556 - $profile = $this->getActiveProfile();
1557 - $default_settings = $current_settings;
1558 - }
1572 + /**
1573 + * Clears all profile settings and related data.
1574 + *
1575 + * @return void
1576 + *
1577 + * @since 1.17.7 (J) - 1.7.7 (WP)
1578 + */
1579 + public function clearProfiles()
1580 + {
1581 + // unset active profile
1582 + VBOFactory::getConfig()->set('report_active_profile_' . $this->getFileName(), '');
1559 1583
1560 - if ($profile) {
1561 - // check if the requested profile settings are available
1562 - $profile_settings = (array) VBOFactory::getConfig()->getArray('report_profile_settings_' . $this->getFileName(), []);
1584 + // unset profiles list
1585 + VBOFactory::getConfig()->set('report_profile_list_' . $this->getFileName(), []);
1563 1586
1564 - // overwrite report current settings
1565 - $current_settings = $profile_settings[$profile] ?? $default_settings;
1566 - }
1587 + // clear profile settings
1588 + VBOFactory::getConfig()->set('report_profile_settings_' . $this->getFileName(), []);
1589 + }
1567 1590
1568 - return $current_settings;
1569 - }
1591 + /**
1592 + * Returns the current report custom settings, optionally loaded from a
1593 + * given profile identifier in case the report supports multiple settings.
1594 + *
1595 + * @param string $profile Optional settings profile identifier.
1596 + *
1597 + * @return array
1598 + *
1599 + * @since 1.17.1 (J) - 1.7.1 (WP)
1600 + */
1601 + public function loadSettings(string $profile = '')
1602 + {
1603 + // access report current settings
1604 + $current_settings = (array) VBOFactory::getConfig()->getArray('report_settings_' . $this->getFileName(), []);
1570 1605
1571 - /**
1572 - * Saves the report custom settings defined.
1573 - * The visibility should be public.
1574 - *
1575 - * @param array $data The associative list of settings to save.
1576 - * @param bool $merge If true, the previous settings will be merged.
1577 - * @param string $profile Optional settings profile identifier.
1578 - *
1579 - * @return void
1580 - *
1581 - * @since 1.17.1 (J) - 1.7.1 (WP)
1582 - * @since 1.17.7 (J) - 1.7.7 (WP) added 3rd argument $profile and related support.
1583 - */
1584 - public function saveSettings(array $data, $merge = true, string $profile = '')
1585 - {
1586 - if ($merge) {
1587 - // build report global settings
1588 - $data = array_merge($this->loadSettings($profile), $data);
1589 - }
1606 + // default settings
1607 + $default_settings = [];
1590 1608
1591 - // save report global settings
1592 - VBOFactory::getConfig()->set('report_settings_' . $this->getFileName(), $data);
1609 + if (!$profile && $this->allowsProfileSettings()) {
1610 + // load current profile settings
1611 + $profile = $this->getActiveProfile();
1612 + $default_settings = $current_settings;
1613 + }
1593 1614
1594 - if ($profile) {
1595 - // get report current profile settings
1596 - $profile_settings = (array) VBOFactory::getConfig()->getArray('report_profile_settings_' . $this->getFileName(), []);
1615 + if ($profile) {
1616 + // check if the requested profile settings are available
1617 + $profile_settings = (array) VBOFactory::getConfig()->getArray('report_profile_settings_' . $this->getFileName(), []);
1597 1618
1598 - // set new profile settings
1599 - $profile_settings[$profile] = $data;
1619 + // overwrite report current settings
1620 + $current_settings = $profile_settings[$profile] ?? $default_settings;
1621 + }
1600 1622
1601 - // save report profile settings
1602 - VBOFactory::getConfig()->set('report_profile_settings_' . $this->getFileName(), $profile_settings);
1603 - }
1604 - }
1623 + return $current_settings;
1624 + }
1605 1625
1606 - /**
1607 - * Returns a numeric list of scoped extra actions.
1608 - *
1609 - * @param string $scope Optional scope identifier (cron, web, etc..).
1610 - * @param bool $visible If true, the hidden actions will not be returned.
1611 - *
1612 - * @return array
1613 - *
1614 - * @since 1.17.1 (J) - 1.7.1 (WP)
1615 - */
1616 - public function getScopedActions($scope = null, $visible = true)
1617 - {
1618 - return [];
1619 - }
1626 + /**
1627 + * Saves the report custom settings defined.
1628 + * The visibility should be public.
1629 + *
1630 + * @param array $data The associative list of settings to save.
1631 + * @param bool $merge If true, the previous settings will be merged.
1632 + * @param string $profile Optional settings profile identifier.
1633 + *
1634 + * @return void
1635 + *
1636 + * @since 1.17.1 (J) - 1.7.1 (WP)
1637 + * @since 1.17.7 (J) - 1.7.7 (WP) added 3rd argument $profile and related support.
1638 + */
1639 + public function saveSettings(array $data, $merge = true, string $profile = '')
1640 + {
1641 + if ($merge) {
1642 + // build report global settings
1643 + $data = array_merge($this->loadSettings($profile), $data);
1644 + }
1620 1645
1621 - /**
1622 - * Executes a custom scoped action within the report.
1623 - *
1624 - * @param string $action The custom action to invoke.
1625 - * @param string $scope Optional scope identifier (cron, web, etc..).
1626 - * @param array $data Optional data for the action to execute.
1627 - *
1628 - * @return mixed
1629 - *
1630 - * @throws Exception
1631 - *
1632 - * @since 1.17.1 (J) - 1.7.1 (WP)
1633 - */
1634 - public function executeAction($action, $scope = null, array $data = [])
1635 - {
1636 - if (is_null($scope)) {
1637 - $scope = $this->getScope();
1638 - }
1646 + // save report global settings
1647 + VBOFactory::getConfig()->set('report_settings_' . $this->getFileName(), $data);
1639 1648
1640 - if (!$data) {
1641 - $data = $this->getActionData($registry = false);
1642 - }
1649 + if ($profile) {
1650 + // get report current profile settings
1651 + $profile_settings = (array) VBOFactory::getConfig()->getArray('report_profile_settings_' . $this->getFileName(), []);
1643 1652
1644 - $callable = [$this, $action];
1653 + // set new profile settings
1654 + $profile_settings[$profile] = $data;
1645 1655
1646 - if (!is_callable($callable)) {
1647 - throw new Exception('Could not call the requested report action.', 500);
1648 - }
1656 + // save report profile settings
1657 + VBOFactory::getConfig()->set('report_profile_settings_' . $this->getFileName(), $profile_settings);
1658 + }
1659 + }
1649 1660
1650 - return call_user_func_array($callable, [$scope, $data]);
1651 - }
1661 + /**
1662 + * Returns a numeric list of scoped extra actions.
1663 + *
1664 + * @param string $scope Optional scope identifier (cron, web, etc..).
1665 + * @param bool $visible If true, the hidden actions will not be returned.
1666 + *
1667 + * @return array
1668 + *
1669 + * @since 1.17.1 (J) - 1.7.1 (WP)
1670 + */
1671 + public function getScopedActions($scope = null, $visible = true)
1672 + {
1673 + return [];
1674 + }
1652 1675
1653 - /**
1654 - * Proxy for executing a custom scoped action and returning a property.
1655 - *
1656 - * @param string $action The custom action to invoke.
1657 - * @param string $return The action result property to return.
1658 - * @param string $scope Optional scope identifier (cron, web, etc..).
1659 - * @param array $data Optional data for the action to execute.
1660 - *
1661 - * @return mixed
1662 - *
1663 - * @throws Exception
1664 - *
1665 - * @since 1.17.1 (J) - 1.7.1 (WP)
1666 - */
1667 - public function _callActionReturn($action, $return, $scope = null, array $data = [])
1668 - {
1669 - if (is_null($scope)) {
1670 - $scope = $this->getScope();
1671 - }
1676 + /**
1677 + * Executes a custom scoped action within the report.
1678 + *
1679 + * @param string $action The custom action to invoke.
1680 + * @param string $scope Optional scope identifier (cron, web, etc..).
1681 + * @param array $data Optional data for the action to execute.
1682 + *
1683 + * @return mixed
1684 + *
1685 + * @throws Exception
1686 + *
1687 + * @since 1.17.1 (J) - 1.7.1 (WP)
1688 + */
1689 + public function executeAction($action, $scope = null, array $data = [])
1690 + {
1691 + if (is_null($scope)) {
1692 + $scope = $this->getScope();
1693 + }
1672 1694
1673 - if (!$data) {
1674 - $data = $this->getActionData($registry = false);
1675 - }
1695 + if (!$data) {
1696 + $data = $this->getActionData($registry = false);
1697 + }
1676 1698
1677 - $result = (array) $this->executeAction($action, $scope, $data);
1699 + $callable = [$this, $action];
1678 1700
1679 - if (!isset($result[$return])) {
1680 - throw new Exception('Could not return the requested action result property.', 500);
1681 - }
1701 + if (!is_callable($callable)) {
1702 + throw new Exception('Could not call the requested report action.', 500);
1703 + }
1682 1704
1683 - return $result[$return];
1684 - }
1705 + return call_user_func_array($callable, [$scope, $data]);
1706 + }
1685 1707
1686 - /**
1687 - * Defines a new resource file generated through a custom action.
1688 - *
1689 - * @param array $data Resource file data to bind.
1690 - *
1691 - * @return self
1692 - *
1693 - * @since 1.17.1 (J) - 1.7.1 (WP)
1694 - */
1695 - protected function defineResourceFile(array $data)
1696 - {
1697 - $element = new VBOReportResourceElement($data);
1708 + /**
1709 + * Proxy for executing a custom scoped action and returning a property.
1710 + *
1711 + * @param string $action The custom action to invoke.
1712 + * @param string $return The action result property to return.
1713 + * @param string $scope Optional scope identifier (cron, web, etc..).
1714 + * @param array $data Optional data for the action to execute.
1715 + *
1716 + * @return mixed
1717 + *
1718 + * @throws Exception
1719 + *
1720 + * @since 1.17.1 (J) - 1.7.1 (WP)
1721 + */
1722 + public function _callActionReturn($action, $return, $scope = null, array $data = [])
1723 + {
1724 + if (is_null($scope)) {
1725 + $scope = $this->getScope();
1726 + }
1698 1727
1699 - if ($element->getUrl()) {
1700 - // ensure the resource is available
1701 - $this->resourceFiles[] = $element;
1702 - }
1728 + if (!$data) {
1729 + $data = $this->getActionData($registry = false);
1730 + }
1703 1731
1704 - return $this;
1705 - }
1732 + $result = (array) $this->executeAction($action, $scope, $data);
1706 1733
1707 - /**
1708 - * Returns the resource files generated through custom actions.
1709 - *
1710 - * @return array
1711 - *
1712 - * @since 1.17.1 (J) - 1.7.1 (WP)
1713 - */
1714 - public function getResourceFiles()
1715 - {
1716 - return $this->resourceFiles;
1717 - }
1734 + if (!isset($result[$return])) {
1735 + throw new Exception('Could not return the requested action result property.', 500);
1736 + }
1718 1737
1719 - /**
1720 - * Sets warning messages by concatenating the existing ones.
1721 - * Method used only by sub-classes.
1722 - *
1723 - * @param string $str
1724 - *
1725 - * @return self
1726 - */
1727 - protected function setWarning($str)
1728 - {
1729 - $this->warning .= $str."\n";
1738 + return $result[$return];
1739 + }
1730 1740
1731 - return $this;
1732 - }
1741 + /**
1742 + * Defines a new resource file generated through a custom action.
1743 + *
1744 + * @param array $data Resource file data to bind.
1745 + *
1746 + * @return self
1747 + *
1748 + * @since 1.17.1 (J) - 1.7.1 (WP)
1749 + */
1750 + protected function defineResourceFile(array $data)
1751 + {
1752 + $element = new VBOReportResourceElement($data);
1733 1753
1734 - /**
1735 - * Gets the current warning string.
1736 - *
1737 - * @return string
1738 - */
1739 - public function getWarning()
1740 - {
1741 - return rtrim($this->warning, "\n");
1742 - }
1754 + if ($element->getUrl()) {
1755 + // ensure the resource is available
1756 + $this->resourceFiles[] = $element;
1757 + }
1743 1758
1744 - /**
1745 - * Resets any errors previously set.
1746 - *
1747 - * @return self
1748 - *
1749 - * @since 1.18.3 (J) - 1.8.3 (WP)
1750 - */
1751 - protected function resetErrors()
1752 - {
1753 - $this->error = '';
1759 + return $this;
1760 + }
1754 1761
1755 - return $this;
1756 - }
1762 + /**
1763 + * Returns the resource files generated through custom actions.
1764 + *
1765 + * @return array
1766 + *
1767 + * @since 1.17.1 (J) - 1.7.1 (WP)
1768 + */
1769 + public function getResourceFiles()
1770 + {
1771 + return $this->resourceFiles;
1772 + }
1757 1773
1758 - /**
1759 - * Sets errors by concatenating the existing ones.
1760 - * Method used only by sub-classes.
1761 - *
1762 - * @param string $str
1763 - *
1764 - * @return self
1765 - */
1766 - protected function setError($str)
1767 - {
1768 - $this->error .= $str."\n";
1774 + /**
1775 + * Sets warning messages by concatenating the existing ones.
1776 + * Method used only by sub-classes.
1777 + *
1778 + * @param string $str
1779 + *
1780 + * @return self
1781 + */
1782 + protected function setWarning($str)
1783 + {
1784 + $this->warning .= $str."\n";
1769 1785
1770 - return $this;
1771 - }
1786 + return $this;
1787 + }
1772 1788
1773 - /**
1774 - * Gets the current error string.
1775 - *
1776 - * @return string
1777 - */
1778 - public function getError()
1779 - {
1780 - return rtrim($this->error, "\n");
1781 - }
1789 + /**
1790 + * Gets the current warning string.
1791 + *
1792 + * @return string
1793 + */
1794 + public function getWarning()
1795 + {
1796 + return rtrim($this->warning, "\n");
1797 + }
1798 +
1799 + /**
1800 + * Resets any errors previously set.
1801 + *
1802 + * @return self
1803 + *
1804 + * @since 1.18.3 (J) - 1.8.3 (WP)
1805 + */
1806 + protected function resetErrors()
1807 + {
1808 + $this->error = '';
1809 +
1810 + return $this;
1811 + }
1812 +
1813 + /**
1814 + * Sets errors by concatenating the existing ones.
1815 + * Method used only by sub-classes.
1816 + *
1817 + * @param string $str
1818 + *
1819 + * @return self
1820 + */
1821 + protected function setError($str)
1822 + {
1823 + $this->error .= $str."\n";
1824 +
1825 + return $this;
1826 + }
1827 +
1828 + /**
1829 + * Gets the current error string.
1830 + *
1831 + * @return string
1832 + */
1833 + public function getError()
1834 + {
1835 + return rtrim($this->error, "\n");
1836 + }
1782 1837 }