PluginProbe ʕ •ᴥ•ʔ
Independent Analytics – WordPress Analytics Plugin / 2.15.0
Independent Analytics – WordPress Analytics Plugin v2.15.0
2.15.0 2.14.10 trunk 1.1 1.10 1.10.1 1.11 1.12 1.13 1.14 1.15 1.16 1.17 1.17.1 1.17.2 1.17.3 1.17.4 1.18 1.18.1 1.19.0 1.19.1 1.2 1.20.0 1.21.0 1.22.0 1.22.1 1.23.0 1.23.1 1.24.0 1.24.1 1.25.0 1.25.1 1.26.0 1.27.0 1.28.0 1.28.1 1.28.2 1.28.3 1.29.0 1.3 1.30.0 1.30.1 1.4 1.5 1.6 1.7 1.8 1.9 2.0.0 2.0.1 2.1.4 2.1.5 2.1.6 2.10.0 2.10.1 2.10.2 2.10.3 2.10.4 2.11.0 2.11.1 2.11.10 2.11.2 2.11.3 2.11.4 2.11.5 2.11.6 2.11.7 2.11.8 2.11.9 2.12.0 2.12.1 2.12.2 2.13.1 2.13.2 2.13.5 2.13.6 2.14.0 2.14.1 2.14.2 2.14.4 2.14.6 2.14.7 2.14.8 2.14.9 2.2.0 2.2.1 2.3.1 2.3.2 2.4.2 2.4.3 2.5.0 2.5.1 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.7.0 2.7.1 2.7.2 2.7.3 2.8.2 2.8.3 2.8.4 2.8.5 2.8.6 2.8.7 2.8.8 2.8.9 2.9.2 2.9.3 2.9.4 2.9.5 2.9.6 2.9.7
independent-analytics / IAWP / Tables / Table.php
independent-analytics / IAWP / Tables Last commit date
Columns 6 months ago Groups 1 year ago Table.php 4 days ago Table_Campaigns.php 4 days ago Table_Clicks.php 4 days ago Table_Devices.php 4 days ago Table_Geo.php 4 days ago Table_Journeys.php 4 days ago Table_Pages.php 4 days ago Table_Referrers.php 4 days ago
Table.php
489 lines
1 <?php
2
3 namespace IAWP\Tables;
4
5 use IAWP\Campaign_Builder;
6 use IAWP\Dashboard_Options;
7 use IAWP\Date_Picker\Date_Picker;
8 use IAWP\Filters;
9 use IAWP\Form_Submissions\Form;
10 use IAWP\Icon_Directory_Factory;
11 use IAWP\Plugin_Group;
12 use IAWP\Rows\Filter;
13 use IAWP\Sort_Configuration;
14 use IAWP\Statistics\Statistics;
15 use IAWP\Tables\Columns\Column;
16 use IAWP\Tables\Groups\Group;
17 use IAWP\Tables\Groups\Groups;
18 use IAWP\Utils\CSV;
19 use IAWP\Utils\Currency;
20 use IAWP\Utils\Format;
21 use IAWP\Utils\Number_Formatter;
22 use IAWP\Utils\Security;
23 use IAWP\Utils\Timezone;
24 use IAWP\Utils\URL;
25 use IAWPSCOPED\Illuminate\Support\Collection;
26 use IAWPSCOPED\Illuminate\Support\Str;
27 /** @internal */
28 abstract class Table
29 {
30 protected $default_sorting_column = 'visitors';
31 private $filters;
32 private $visible_columns;
33 private $group;
34 private $is_new_group;
35 /** @var ?Statistics */
36 private $statistics;
37 /**
38 * @param string|null $group_id
39 * @param bool $is_new_group
40 */
41 public function __construct(?string $group_id = null, bool $is_new_group = \false)
42 {
43 $this->visible_columns = Dashboard_Options::getInstance()->visible_columns();
44 $this->group = $this->groups()->find_by_id($group_id);
45 $this->is_new_group = $is_new_group;
46 $this->filters = new Filters();
47 }
48 public abstract function name() : string;
49 public abstract function groups() : Groups;
50 /**
51 * @return array<Column>
52 */
53 protected abstract function local_columns() : array;
54 public abstract function id() : string;
55 /**
56 * @return string[]
57 */
58 public function visible_column_ids() : array
59 {
60 $visible_columns = [];
61 foreach ($this->get_columns() as $column) {
62 if ($column->is_visible()) {
63 $visible_columns[] = $column->id();
64 }
65 }
66 return $visible_columns;
67 }
68 public function allow_downloading() : bool
69 {
70 return \true;
71 }
72 public function group() : Group
73 {
74 return $this->group;
75 }
76 public function column_picker_html() : string
77 {
78 return \IAWPSCOPED\iawp_render('plugin-group-options', ['option_type' => 'columns', 'option_name' => \__('Toggle Columns', 'independent-analytics'), 'option_icon' => 'columns', 'plugin_groups' => Plugin_Group::get_plugin_groups(), 'options' => $this->get_columns(\true)]);
79 }
80 public function get_table_toolbar_markup()
81 {
82 return \IAWPSCOPED\iawp_render('tables.table-toolbar', ['plugin_groups' => Plugin_Group::get_plugin_groups(), 'columns' => $this->get_columns(\true), 'groups' => $this->groups(), 'current_group' => $this->group()]);
83 }
84 public function get_table_markup(string $sort_column, string $sort_direction)
85 {
86 return \IAWPSCOPED\iawp_render('tables.table', ['table' => $this, 'all_columns' => $this->get_columns(), 'visible_column_count' => $this->visible_column_count(), 'number_of_shown_rows' => 0, 'rows' => [], 'render_skeleton' => \true, 'page_size' => \IAWPSCOPED\iawp()->pagination_page_size(), 'sort_column' => $sort_column, 'sort_direction' => $sort_direction, 'has_campaigns' => Campaign_Builder::has_campaigns()]);
87 }
88 public function set_statistics(Statistics $statistics)
89 {
90 $this->statistics = $statistics;
91 }
92 public function get_row_data_attributes($row)
93 {
94 $html = '';
95 foreach ($this->get_columns() as $column) {
96 $id = $column->id();
97 $data_val = $row->{$id}();
98 $html .= ' data-' . \esc_attr($column->id()) . '="' . \esc_attr($data_val) . '"';
99 }
100 return $html;
101 }
102 public function get_cell_content($row, Column $column)
103 {
104 $column_id = $column->id();
105 if (\is_null($row->{$column_id}())) {
106 return '-';
107 }
108 if ($column_id == 'title' && $row->is_deleted()) {
109 return Security::string($row->{$column_id}()) . ' <span class="deleted-label">' . \esc_html__('(deleted)', 'independent-analytics') . '</span>';
110 } elseif ($column_id == 'views') {
111 $views = Number_Formatter::decimal($row->views());
112 // Getting a divide by zero error from the line below?
113 // It's likely an issue with $this->views which is an instance of Views. Make sure the queries there are working.
114 $views_percentage = Number_Formatter::percent($row->views() / $this->statistics->get_statistic('views')->value() * 100, 2);
115 return '<span class="no-wrap">' . Security::string($views) . '</span> <span class="percentage">(' . Security::string($views_percentage) . ')</span>';
116 } elseif ($column_id == 'visitors') {
117 $visitors = Number_Formatter::decimal($row->visitors());
118 $visitors_percentage = Number_Formatter::percent($row->visitors() / $this->statistics->get_statistic('visitors')->value() * 100, 2);
119 return '<span class="no-wrap">' . Security::string($visitors) . '</span> <span class="percentage">(' . Security::string($visitors_percentage) . ')</span>';
120 } elseif ($column_id == 'sessions') {
121 $sessions = Number_Formatter::decimal($row->sessions());
122 $sessions_percentage = Number_Formatter::percent($row->sessions() / $this->statistics->get_statistic('sessions')->value() * 100, 2);
123 return '<span class="no-wrap">' . Security::string($sessions) . '</span> <span class="percentage">(' . Security::string($sessions_percentage) . ')</span>';
124 } elseif ($column_id === 'entrances') {
125 $entrances = Number_Formatter::decimal($row->entrances());
126 $entrances_percentage = Number_Formatter::percent($row->entrances() / $this->statistics->get_statistic('sessions')->value() * 100, 2);
127 return '<span class="no-wrap">' . Security::string($entrances) . '</span> <span class="percentage">(' . Security::string($entrances_percentage) . ')</span>';
128 } elseif ($column_id === 'exits') {
129 $exits = Number_Formatter::decimal($row->exits());
130 $exits_percentage = Number_Formatter::percent($row->exits() / $this->statistics->get_statistic('sessions')->value() * 100, 2);
131 return '<span class="no-wrap">' . Security::string($exits) . '</span> <span class="percentage">(' . Security::string($exits_percentage) . ')</span>';
132 } elseif ($column_id === 'bounce_rate') {
133 return Security::string(Number_Formatter::percent($row->bounce_rate()));
134 } elseif ($column_id === 'average_session_duration' || $column_id === 'average_view_duration') {
135 return Number_Formatter::second_to_minute_timestamp($row->{$column_id}());
136 } elseif ($column_id === 'views_growth' || $column_id === 'visitors_growth' || $column_id === 'wc_conversion_rate' || $column_id === 'exit_percent' || Str::startsWith($column_id, 'form_conversion_rate')) {
137 return Number_Formatter::percent($row->{$column_id}(), 2);
138 } elseif ($column_id == 'url') {
139 if ($row->is_deleted()) {
140 return \urldecode(\esc_url($row->url()));
141 } else {
142 return '<a href="' . \esc_url($row->url(\true)) . '" target="_blank" class="external-link">' . \urldecode(\esc_url($row->url())) . '<span class="dashicons dashicons-external"></span></a>';
143 }
144 } elseif ($column_id == 'author') {
145 return Security::html($row->avatar()) . ' ' . Security::string($row->author());
146 } elseif ($column_id == 'date') {
147 return Security::string(\date(Format::date(), \strtotime($row->date())));
148 } elseif ($column_id == 'type' && \method_exists($row, 'icon') && \method_exists($row, 'type')) {
149 return $row->icon(0) . ' ' . Security::string($row->type());
150 } elseif ($column_id == 'referrer') {
151 return \IAWPSCOPED\iawp_render('tables.referrer-column', ['row' => $row]);
152 } elseif ($column_id === 'device_type') {
153 return Icon_Directory_Factory::device_types()->find($row->device_type()) . Security::string($row->device_type());
154 } elseif ($column_id === 'browser') {
155 return Icon_Directory_Factory::browsers()->find($row->browser()) . Security::string($row->browser());
156 } elseif ($column_id === 'os') {
157 return Icon_Directory_Factory::operating_systems()->find($row->os()) . Security::string($row->os());
158 } elseif ($column_id === 'country') {
159 return Icon_Directory_Factory::flags()->find($row->country_code()) . Security::string($row->country());
160 } elseif ($column_id === 'wc_gross_sales' || $column_id === 'wc_refunded_amount' || $column_id === 'wc_net_sales' || $column_id === 'wc_average_order_volume') {
161 return Security::string(Currency::format($row->{$column_id}()));
162 } elseif ($column_id === 'wc_earnings_per_visitor') {
163 return Security::string(Currency::format($row->{$column_id}(), \false));
164 } elseif ($column_id === 'views_per_session') {
165 return Number_Formatter::decimal($row->{$column_id}(), 2);
166 } elseif ($column_id === 'link_target') {
167 $value = $row->{$column_id}();
168 if (\is_string($value) && URL::new($value)->is_valid_url()) {
169 return '<a href="' . \esc_url($value) . '" target="_blank" class="external-link">' . \esc_url(\urldecode($value)) . '<span class="dashicons dashicons-external"></span></a>';
170 }
171 return Security::string($value);
172 } else {
173 return Security::string($row->{$column_id}());
174 }
175 }
176 public function output_report_toolbar()
177 {
178 $options = Dashboard_Options::getInstance();
179 $start = $options->get_date_range()->start()->setTimezone(Timezone::site_timezone());
180 $end = $options->get_date_range()->end()->setTimezone(Timezone::site_timezone());
181 ?>
182 <div id="toolbar" class="toolbar" data-filter-count="<?php
183 echo \count($options->filters());
184 ?>">
185 <div class="date-picker-parent">
186 <div class="modal-parent dates">
187 <button id="dates-button"
188 data-testid="open-calendar"
189 class="iawp-button"
190 data-action="dates#toggleModal"
191 data-dates-target="modalButton"
192 >
193 <span class="dashicons dashicons-calendar-alt"></span>
194 <span class="iawp-label"><?php
195 echo \esc_html($options->get_date_range()->label());
196 ?></span>
197 </button>
198 <div id="modal-dates"
199 class="iawp-modal large dates"
200 data-dates-target="modal"
201 >
202 <?php
203 echo (new Date_Picker($start, $end, $options->relative_range_id()))->calendar_html();
204 ?>
205 </div>
206 </div>
207 </div>
208 <div class="filter-parent">
209 <?php
210 echo $this->filters()->get_filters_html($this->columns_that_can_be_filtered());
211 ?>
212 </div>
213 <div class="download-options-parent <?php
214 echo $this->allow_downloading() ? "" : "hide";
215 ?>" data-controller="modal">
216 <div class="modal-parent downloads">
217 <button id="download-options" data-modal-target="modalButton" data-action="click->modal#toggleModal" class="download-options">
218 <?php
219 \esc_html_e('Download Report', 'independent-analytics');
220 ?>
221 </button>
222 <div class="iawp-modal small downloads" data-modal-target="modal">
223 <div class="modal-inner">
224 <div class="title-small">
225 <?php
226 \esc_html_e('Choose a format', 'independent-analytics');
227 ?>
228 <span data-report-target="spinner" class="dashicons dashicons-update iawp-spin hidden"></span>
229 </div>
230 <div class="download-button-container">
231 <button id="download-csv" class="iawp-button" data-report-target="exportReportTable" data-action="report#exportReportTable">
232 <span class="dashicons dashicons-media-spreadsheet"></span>
233 <span class="iawp-label">
234 <?php
235 \esc_html_e('Download Table CSV', 'independent-analytics');
236 ?>
237 </span>
238 </button>
239 <button id="download-report-statistics-csv" class="iawp-button" data-report-target="exportReportStatistics" data-action="report#exportReportStatistics">
240 <span class="dashicons dashicons-media-spreadsheet"></span>
241 <span class="iawp-label">
242 <?php
243 \esc_html_e('Download Daily Metrics CSV', 'independent-analytics');
244 ?>
245 </span>
246 </button>
247 </div>
248 <div class="download-button-container">
249 <button id="download-pdf" class="iawp-button" data-report-target="exportPDF" data-action="report#exportPDF" disabled="disabled">
250 <span class="dashicons dashicons-pdf"></span>
251 <span class="iawp-label">
252 <?php
253 \esc_html_e('Download PDF', 'independent-analytics');
254 ?>
255 </span>
256 </button>
257 </div>
258 </div>
259 </div>
260 </div>
261 </div>
262 </div><?php
263 }
264 public function filters_template_html() : string
265 {
266 return $this->filters()->get_condition_html($this->columns_that_can_be_filtered());
267 }
268 public function filters_condition_buttons_html(array $filters) : string
269 {
270 return $this->filters()->condition_buttons_html($filters);
271 }
272 public final function csv(array $rows, bool $is_dashboard_export = \false) : CSV
273 {
274 $columns = $this->get_columns();
275 $csv_header = [];
276 $csv_rows = [];
277 foreach ($columns as $column) {
278 if (!$this->include_column_in_csv($column, $is_dashboard_export)) {
279 continue;
280 }
281 $csv_header[] = $column->name();
282 }
283 foreach ($rows as $row) {
284 $csv_row = [];
285 foreach ($columns as $column) {
286 if (!$this->include_column_in_csv($column, $is_dashboard_export)) {
287 continue;
288 }
289 $column_id = $column->id();
290 $value = $row->{$column_id}();
291 if (\is_string($value)) {
292 $value = \html_entity_decode($value);
293 // Fix apostrophes for Excel
294 $value = \str_replace("’", "'", $value);
295 }
296 $csv_row[] = $this->formatted_csv_cell_content($column, $value);
297 }
298 $csv_rows[] = $csv_row;
299 }
300 $csv = new CSV($csv_header, $csv_rows);
301 return $csv;
302 }
303 public function formatted_csv_cell_content(Column $column, $value) : string
304 {
305 $column_id = $column->id();
306 // Todo - This logic is similar to the rendering logic for table cells. This should
307 // all be handled via the column class itself.
308 if (\is_null($value)) {
309 return '-';
310 } elseif (\in_array($column_id, ['views', 'visitors', 'sessions', 'clicks', 'form_submissions']) || Str::startsWith($column_id, 'form_submissions_for_')) {
311 return Number_Formatter::integer($value);
312 } elseif (\in_array($column_id, ['bounce_route', 'views_growth', 'visitors_growth', 'form_conversion_rate']) || Str::startsWith($column_id, 'form_conversion_rate_for_')) {
313 return Number_Formatter::percent($value);
314 } elseif ($column_id === 'date') {
315 return \date(Format::date(), \strtotime($value));
316 } elseif ($column_id === 'average_session_duration' || $column_id === 'average_view_duration') {
317 return Number_Formatter::second_to_minute_timestamp($value);
318 } elseif ($column_id === 'views_per_session') {
319 return Number_Formatter::decimal($value, 2);
320 } elseif ($column_id === 'wc_gross_sales' || $column_id === 'wc_refunded_amount' || $column_id === 'wc_net_sales' || $column_id === 'wc_average_order_volume') {
321 return Currency::format($value);
322 } elseif ($column_id === 'wc_earnings_per_visitor') {
323 return Currency::format($value);
324 } else {
325 return $value;
326 }
327 }
328 /**
329 * @param array[] $filters Raw filter associative arrays
330 *
331 * @return Filter[]
332 */
333 public function sanitize_filters(array $filters) : array
334 {
335 return Collection::make($filters)->map(function ($filter) {
336 return $this->sanitize_filter($filter);
337 })->filter()->values()->all();
338 }
339 public function resolve_filter_conflicts(array $filters, string $filter_logic) : array
340 {
341 return $filters;
342 }
343 public function sanitize_filter(array $filter) : ?Filter
344 {
345 // column, inclusion, operator, operand
346 $column = $this->get_column($filter['column']);
347 if ($column === null || !$column->can_be_filtered()) {
348 return null;
349 }
350 $valid_inclusions = ['include', 'exclude'];
351 if (!\in_array($filter['inclusion'], $valid_inclusions)) {
352 return null;
353 }
354 if (!$column->is_valid_filter_operator($filter['operator'])) {
355 return null;
356 }
357 if (!$column->is_enabled_for_group($this->group())) {
358 return null;
359 }
360 $operand = \trim(Security::string($filter['operand']));
361 if (\strlen($operand) === 0) {
362 return null;
363 }
364 $utm_columns = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'];
365 if (\in_array($column->id(), $utm_columns)) {
366 $filter['operand'] = \str_replace('+', ' ', $filter['operand']);
367 }
368 if ($column->id() === 'url' && $filter['operator'] === 'exact') {
369 $url = new URL($filter['operand']);
370 if (!$url->is_valid_url()) {
371 $filter['operand'] = \site_url($filter['operand']);
372 }
373 }
374 if ($column->options() && !$column->options()->contains($filter['operand'])) {
375 return null;
376 }
377 return new Filter(['inclusion' => Security::string($filter['inclusion']), 'operator' => Security::string($filter['operator']), 'operand' => Security::string($filter['operand']), 'column' => $column]);
378 }
379 public function get_column(string $id) : ?Column
380 {
381 $matches = \array_filter($this->local_columns(), function (Column $column) use($id) {
382 return $column->id() === $id;
383 });
384 $column = \count($matches) === 1 ? \reset($matches) : null;
385 if (\is_null($column) || !$column->is_enabled()) {
386 return null;
387 }
388 return $column;
389 }
390 public function sanitize_sort_parameters(?string $sort_column = null, ?string $sort_direction = 'desc') : Sort_Configuration
391 {
392 if ($sort_column === null) {
393 $sort_column = $this->default_sorting_column;
394 }
395 $column = $this->get_column($sort_column);
396 if ($column === null || !$column->is_enabled_for_group($this->group)) {
397 $column = $this->get_column($this->default_sorting_column);
398 }
399 return new Sort_Configuration($column, $sort_direction);
400 }
401 public function get_rendered_template(array $rows, bool $just_rows, string $sort_column, string $sort_direction)
402 {
403 if ($just_rows) {
404 return \IAWPSCOPED\iawp_render('tables.rows', ['table' => $this, 'all_columns' => $this->get_columns(), 'visible_column_count' => $this->visible_column_count(), 'number_of_shown_rows' => \count($rows), 'rows' => $rows, 'render_skeleton' => \false, 'page_size' => \IAWPSCOPED\iawp()->pagination_page_size(), 'sort_column' => $sort_column, 'sort_direction' => $sort_direction, 'has_campaigns' => Campaign_Builder::has_campaigns()]);
405 }
406 return \IAWPSCOPED\iawp_render('tables.table', ['table' => $this, 'all_columns' => $this->get_columns(), 'visible_column_count' => $this->visible_column_count(), 'number_of_shown_rows' => \count($rows), 'rows' => $rows, 'render_skeleton' => \false, 'page_size' => \IAWPSCOPED\iawp()->pagination_page_size(), 'sort_column' => $sort_column, 'sort_direction' => $sort_direction, 'has_campaigns' => Campaign_Builder::has_campaigns()]);
407 }
408 /**
409 * @return Column[]
410 */
411 public function get_columns($show_disabled_columns = \false) : array
412 {
413 $columns_for_group = \array_filter($this->local_columns(), function (Column $column) {
414 return $column->is_enabled() && $column->is_enabled_for_group($this->group) && $column->is_subgroup_plugin_enabled();
415 });
416 if (\false === $show_disabled_columns) {
417 $columns_for_group = \array_filter($columns_for_group, function (Column $column) {
418 return $column->is_group_plugin_enabled();
419 });
420 }
421 if (\is_null($this->visible_columns) || \count($this->visible_columns) === 0) {
422 return $columns_for_group;
423 }
424 if ($this->is_new_group) {
425 return \array_map(function ($column) {
426 if ($column->is_group_dependent()) {
427 $column->set_visibility(\true);
428 } else {
429 $column->set_visibility(\in_array($column->id(), $this->visible_columns));
430 }
431 return $column;
432 }, $columns_for_group);
433 }
434 return \array_map(function ($column) {
435 $column->set_visibility(\in_array($column->id(), $this->visible_columns));
436 return $column;
437 }, $columns_for_group);
438 }
439 protected function get_woocommerce_columns() : array
440 {
441 return [new Column(['id' => 'wc_orders', 'name' => \__('Orders', 'independent-analytics'), 'plugin_group' => 'ecommerce', 'type' => 'int', 'aggregatable' => \true]), new Column(['id' => 'wc_gross_sales', 'name' => \__('Gross Sales', 'independent-analytics'), 'plugin_group' => 'ecommerce', 'type' => 'int', 'aggregatable' => \true]), new Column(['id' => 'wc_refunds', 'name' => \__('Refunds', 'independent-analytics'), 'plugin_group' => 'ecommerce', 'type' => 'int', 'aggregatable' => \true]), new Column(['id' => 'wc_refunded_amount', 'name' => \__('Refunded Amount', 'independent-analytics'), 'plugin_group' => 'ecommerce', 'type' => 'int', 'aggregatable' => \true]), new Column(['id' => 'wc_net_sales', 'name' => \__('Total Sales', 'independent-analytics'), 'plugin_group' => 'ecommerce', 'type' => 'int', 'aggregatable' => \true]), new Column(['id' => 'wc_conversion_rate', 'name' => \__('Conversion Rate', 'independent-analytics'), 'plugin_group' => 'ecommerce', 'type' => 'int']), new Column(['id' => 'wc_earnings_per_visitor', 'name' => \__('Earnings Per Visitor', 'independent-analytics'), 'plugin_group' => 'ecommerce', 'type' => 'int']), new Column(['id' => 'wc_average_order_volume', 'name' => \__('Average Order Volume', 'independent-analytics'), 'plugin_group' => 'ecommerce', 'type' => 'int'])];
442 }
443 protected function get_form_columns() : array
444 {
445 $columns = [new Column(['id' => 'form_submissions', 'name' => \__('Submissions', 'independent-analytics'), 'plugin_group' => 'forms', 'type' => 'int', 'aggregatable' => \true]), new Column(['id' => 'form_conversion_rate', 'name' => \__('Conversion Rate', 'independent-analytics'), 'plugin_group' => 'forms', 'type' => 'int'])];
446 foreach (Form::get_forms() as $form) {
447 $columns[] = new Column(['id' => $form->submissions_column(), 'name' => $form->title() . ' ' . \__('Submissions', 'independent-analytics'), 'plugin_group' => 'forms', 'is_subgroup_plugin_active' => $form->is_plugin_active(), 'plugin_group_header' => $form->plugin_name(), 'type' => 'int', 'aggregatable' => \true]);
448 $columns[] = new Column(['id' => $form->conversion_rate_column(), 'name' => $form->title() . ' ' . \__('Conversion Rate', 'independent-analytics'), 'plugin_group' => 'forms', 'is_subgroup_plugin_active' => $form->is_plugin_active(), 'plugin_group_header' => $form->plugin_name(), 'type' => 'int']);
449 }
450 return $columns;
451 }
452 private function include_column_in_csv(Column $column, bool $is_dashboard_export) : bool
453 {
454 if (!$column->is_visible() && $is_dashboard_export) {
455 return \false;
456 }
457 if (!$column->exportable() && !$is_dashboard_export) {
458 return \false;
459 }
460 if (!$column->is_group_plugin_enabled()) {
461 return \false;
462 }
463 return \true;
464 }
465 /**
466 * Get the number of visible columns
467 *
468 * @return int
469 */
470 private function visible_column_count() : int
471 {
472 $visible_columns = 0;
473 foreach ($this->get_columns() as $column) {
474 if ($column->is_visible()) {
475 $visible_columns++;
476 }
477 }
478 return $visible_columns;
479 }
480 private function filters()
481 {
482 return $this->filters;
483 }
484 private function columns_that_can_be_filtered() : array
485 {
486 return Collection::make($this->get_columns())->filter(fn(Column $column) => $column->can_be_filtered())->all();
487 }
488 }
489