PluginProbe ʕ •ᴥ•ʔ
Independent Analytics – WordPress Analytics Plugin / 1.24.1
Independent Analytics – WordPress Analytics Plugin v1.24.1
2.15.5 2.15.4 2.15.3 2.15.2 2.15.1 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 3 years ago Table.php 3 years ago Table_Campaigns.php 3 years ago Table_Geo.php 3 years ago Table_Pages.php 3 years ago Table_Referrers.php 3 years ago
Table.php
415 lines
1 <?php
2
3 namespace IAWP_SCOPED\IAWP\Tables;
4
5 use IAWP_SCOPED\IAWP\Dashboard_Options;
6 use IAWP_SCOPED\IAWP\Date_Range\Relative_Date_Range;
7 use IAWP_SCOPED\IAWP\Filters;
8 use IAWP_SCOPED\IAWP\Statistics\Statistics;
9 use IAWP_SCOPED\IAWP\Tables\Columns\Column;
10 use IAWP_SCOPED\IAWP\Utils\Array_To_CSV;
11 use IAWP_SCOPED\IAWP\Utils\Currency;
12 use IAWP_SCOPED\IAWP\Utils\Number_Formatter;
13 use IAWP_SCOPED\IAWP\Utils\Security;
14 use IAWP_SCOPED\IAWP\Utils\Timezone;
15 use IAWP_SCOPED\IAWP\Utils\WordPress_Site_Date_Format_Pattern;
16 use IAWP_SCOPED\Proper\Number;
17 abstract class Table
18 {
19 /**
20 * @return array<Column>
21 */
22 protected abstract function local_columns() : array;
23 protected abstract function table_name() : string;
24 private $filters;
25 private $visible_columns;
26 private $statistics;
27 public function __construct($visible_columns = null)
28 {
29 $this->filters = new Filters();
30 $this->visible_columns = $visible_columns;
31 }
32 public function get_columns() : array
33 {
34 if ($this->visible_columns === null) {
35 return $this->local_columns();
36 }
37 return \array_map(function ($column) {
38 if (\in_array($column->id(), $this->visible_columns)) {
39 $column->set_visibility(\true);
40 } else {
41 $column->set_visibility(\false);
42 }
43 return $column;
44 }, $this->local_columns());
45 }
46 public function visible_column_ids()
47 {
48 $visible_columns = [];
49 foreach ($this->get_columns() as $column) {
50 if ($column->visible()) {
51 $visible_columns[] = $column->id();
52 }
53 }
54 return $visible_columns;
55 }
56 public function get_table_markup()
57 {
58 $opts = new Dashboard_Options();
59 $templates = \IAWP_SCOPED\iawp()->templates();
60 return $templates->render('table/index', ['just_rows' => \false, 'table_name' => $this->table_name(), 'all_columns' => $this->get_columns(), 'visible_column_count' => $this->visible_column_count(), 'row_count' => 0, 'rows' => [], 'render_skeleton' => \true, 'page_size' => \IAWP_SCOPED\iawp()->pagination_page_size(), 'opts' => $opts]);
61 }
62 public function set_statistics(Statistics $statistics)
63 {
64 $this->statistics = $statistics;
65 }
66 public function get_row_markup($rows)
67 {
68 return $this->get_rendered_template($rows, \true);
69 }
70 /**
71 * Get the number of visible columns
72 *
73 * @return int
74 */
75 private function visible_column_count() : int
76 {
77 $visible_columns = 0;
78 foreach ($this->get_columns() as $column) {
79 if ($column->visible()) {
80 $visible_columns++;
81 }
82 }
83 return $visible_columns;
84 }
85 private function get_rendered_template($rows, $just_rows = \false)
86 {
87 $opts = new Dashboard_Options();
88 $templates = \IAWP_SCOPED\iawp()->templates();
89 $templates->registerFunction('row_data_attributes', [$this, 'get_row_data_attributes']);
90 $templates->registerFunction('cell_content', [$this, 'get_cell_content']);
91 return $templates->render('table/index', ['just_rows' => $just_rows, 'table_name' => $this->table_name(), 'all_columns' => $this->get_columns(), 'visible_column_count' => $this->visible_column_count(), 'row_count' => \count($rows), 'rows' => $rows, 'render_skeleton' => \false, 'page_size' => \IAWP_SCOPED\iawp()->pagination_page_size(), 'opts' => $opts]);
92 }
93 public function get_row_data_attributes($row)
94 {
95 $html = '';
96 foreach ($this->get_columns() as $column) {
97 $id = $column->id();
98 $data_val = $row->{$id}();
99 $html .= ' data-' . \esc_attr($column->id()) . '="' . \esc_attr($data_val) . '"';
100 }
101 return $html;
102 }
103 public function get_cell_content($row, Column $column)
104 {
105 $column_id = $column->id();
106 if (\is_null($row->{$column_id}())) {
107 return '-';
108 }
109 if ($column_id == 'title' && $row->is_deleted()) {
110 return Security::string($row->{$column_id}()) . ' <span class="deleted-label">' . \esc_html__('(deleted)', 'independent-analytics') . '</span>';
111 } elseif ($column_id == 'views') {
112 $views = Number_Formatter::format($row->views());
113 // Getting a divide by zero error from the line below?
114 // It's likely an issue with $this->views which is an instance of Views. Make sure the queries there are working.
115 $views_percentage = Number_Formatter::format($row->views() / $this->statistics->views() * 100, 'percent', 2);
116 return Security::string($views) . ' <span class="percentage">(' . Security::string($views_percentage) . ')</span>';
117 } elseif ($column_id == 'visitors') {
118 $visitors = Number_Formatter::format($row->visitors());
119 $visitors_percentage = Number_Formatter::format($row->visitors() / $this->statistics->visitors() * 100, 'percent', 2);
120 return Security::string($visitors) . ' <span class="percentage">(' . Security::string($visitors_percentage) . ')</span>';
121 } elseif ($column_id == 'sessions') {
122 $sessions = Number_Formatter::format($row->sessions());
123 $sessions_percentage = Number_Formatter::format($row->sessions() / $this->statistics->sessions() * 100, 'percent', 2);
124 return Security::string($sessions) . ' <span class="percentage">(' . Security::string($sessions_percentage) . ')</span>';
125 } elseif ($column_id === 'bounce_rate') {
126 return Security::string(Number_Formatter::format($row->bounce_rate(), 'percent'));
127 } elseif ($column_id === 'average_session_duration' || $column_id === 'average_view_duration') {
128 return Number_Formatter::second_to_minute_timestamp($row->{$column_id}());
129 } elseif ($column_id === 'views_growth' || $column_id === 'visitors_growth' || $column_id === 'woocommerce_conversion_rate') {
130 return Number_Formatter::format($row->{$column_id}(), 'percent', 2);
131 } elseif ($column_id == 'url') {
132 if ($row->is_deleted()) {
133 return Security::string(\esc_url($row->url()));
134 } else {
135 return '<a href="' . Security::string(\esc_url($row->url(\true))) . '" target="_blank" class="external-link">' . Security::string(\esc_url($row->url())) . '<span class="dashicons dashicons-external"></span></a>';
136 }
137 } elseif ($column_id == 'author') {
138 return Security::html($row->avatar()) . ' ' . Security::string($row->author());
139 } elseif ($column_id == 'date') {
140 return Security::string(\date(WordPress_Site_Date_Format_Pattern::for_php(), $row->date()));
141 } elseif ($column_id == 'type' && \method_exists($row, 'icon') && \method_exists($row, 'type')) {
142 return $row->icon(0) . ' ' . Security::string($row->type());
143 } elseif ($column_id == 'referrer' && !$row->is_direct()) {
144 return '<a href="' . \esc_url($row->referrer_url()) . '" target="_blank" class="external-link">' . Security::string($row->referrer()) . '<span class="dashicons dashicons-external"></span></a>';
145 } elseif ($column_id === 'country') {
146 return '<img class="flag" alt="Country flag" src="' . Security::string($row->flag()) . '"/>' . Security::string($row->country());
147 } elseif ($column_id === 'wc_gross_sales' || $column_id === 'wc_refunded_amount' || $column_id === 'wc_net_sales' || $column_id === 'woocommerce_earnings_per_visitor' || $column_id === 'woocommerce_average_order_volume') {
148 return Security::string(Currency::format($row->{$column_id}()));
149 } else {
150 return Security::string($row->{$column_id}());
151 }
152 }
153 private function filters()
154 {
155 return $this->filters;
156 }
157 public function output_toolbar()
158 {
159 $opts = new Dashboard_Options();
160 ?>
161 <div class="toolbar">
162 <div class="buttons">
163 <div class="modal-parent"
164 data-controller="dates"
165 data-dates-relative-range-id-value="<?php
166 \esc_html_e($opts->relative_range_id());
167 ?>"
168 data-dates-exact-start-value="<?php
169 \esc_html_e($opts->get_date_range()->start()->setTimezone(Timezone::wordpress_site_timezone())->format('Y-m-d'));
170 ?>"
171 data-dates-exact-end-value="<?php
172 \esc_html_e($opts->get_date_range()->end()->setTimezone(Timezone::wordpress_site_timezone())->format('Y-m-d'));
173 ?>"
174 data-dates-first-day-of-week-value="<?php
175 echo \absint(\IAWP_SCOPED\iawp()->get_option('iawp_dow', 0));
176 ?>"
177 data-dates-css-url-value="<?php
178 echo \esc_url(\IAWP_SCOPED\iawp_url_to('dist/styles/easepick/datepicker.css'));
179 ?>"
180 data-dates-format-value="<?php
181 echo \esc_attr(WordPress_Site_Date_Format_Pattern::for_javascript());
182 ?>"
183 >
184 <button id="dates-button"
185 class="iawp-button ghost-white toolbar-button"
186 data-action="dates#toggleModal"
187 data-dates-target="modalButton"
188 >
189 <span class="dashicons dashicons-calendar-alt"></span>
190 <span class="iawp-label"><?php
191 \esc_html_e($opts->get_date_range()->label());
192 ?></span>
193 </button>
194 <div id="modal-dates"
195 class="modal large flex"
196 data-dates-target="modal"
197 >
198 <div class="modal-inner">
199 <div id="easepick-picker"
200 data-dates-target="easepick"
201 style="display: none;"
202 >
203 </div>
204 <div class="relative-dates">
205 <?php
206 foreach (Relative_Date_Range::ranges() as $date_range) {
207 ?>
208 <button class="iawp-button ghost-purple"
209 data-dates-target="relativeRange"
210 data-action="dates#relativeRangeSelected"
211 data-relative-range-id="<?php
212 \esc_html_e($date_range->relative_range_id());
213 ?>"
214 data-relative-range-label="<?php
215 \esc_html_e($date_range->label());
216 ?>"
217 data-relative-range-start="<?php
218 \esc_html_e($date_range->start()->setTimezone(Timezone::wordpress_site_timezone())->format('Y-m-d'));
219 ?>"
220 data-relative-range-end="<?php
221 \esc_html_e($date_range->end()->setTimezone(Timezone::wordpress_site_timezone())->format('Y-m-d'));
222 ?>"
223 >
224 <?php
225 \esc_html_e($date_range->label());
226 ?>
227 </button>
228 <?php
229 }
230 ?>
231 </div>
232 <div>
233 <hr/>
234 </div>
235 <div>
236 <button class="iawp-button purple"
237 data-dates-target="apply"
238 data-action="dates#apply"
239 >
240 <?php
241 \esc_html_e('Apply', 'independent-analytics');
242 ?>
243 </button>
244 <button class="iawp-button ghost-purple"
245 data-action="dates#closeModal"
246 >
247 <?php
248 \esc_html_e('Cancel', 'independent-analytics');
249 ?>
250 </button>
251 </div>
252 </div>
253 </div>
254 </div>
255 <?php
256 $this->filters()->output_filters($this->get_columns());
257 ?>
258 <?php
259 $this->column_picker();
260 ?>
261 </div>
262 <div class="buttons">
263 <button class="iawp-button ghost-white toolbar-button"
264 data-controller="clipboard"
265 data-action="clipboard#copy"
266 >
267 <span class="dashicons dashicons-admin-page"></span>
268 <span data-clipboard-target="statusTextElement" class="iawp-label">
269 <?php
270 \esc_html_e('Copy Dashboard URL', 'independent-analytics');
271 ?>
272 </span>
273 </button>
274 <a class="learn-more"
275 href="https://independentwp.com/knowledgebase/dashboard/save-reports-revisit-later/"
276 target="_blank"><span class="dashicons dashicons-info-outline"></span></a>
277 </div>
278 </div><?php
279 }
280 private function column_picker()
281 {
282 ?>
283
284 <div class="customize-columns modal-parent small"
285 data-controller="columns"
286 >
287 <button id="customize-columns"
288 class="iawp-button ghost-white toolbar-button"
289 data-action="columns#toggleModal"
290 data-columns-target="modalButton"
291 >
292 <span class="dashicons dashicons-columns"></span>
293 <span class="iawp-label"><?php
294 \esc_html_e('Edit Columns', 'independent-analytics');
295 ?></span>
296 </button>
297 <div id="modal-columns"
298 class="modal small"
299 data-columns-target="modal"
300 >
301 <div class="modal-inner">
302 <div class="title-small">
303 <?php
304 \esc_html_e('Columns', 'independent-analytics');
305 ?>
306 </div>
307 <?php
308 foreach ($this->get_columns() as $column) {
309 ?>
310 <?php
311 if ($column->id() === 'wc_orders') {
312 ?>
313 <p class="title-small wc-title">
314 <?php
315 \esc_html_e('WooCommerce', 'independent-analytics');
316 ?>
317 <?php
318 if (\IAWP_SCOPED\iawp_is_free()) {
319 ?>
320 <span class="pro-label"><?php
321 \esc_html_e('PRO', 'independent-analytics');
322 ?></span>
323 <?php
324 }
325 ?>
326 </p>
327 <?php
328 }
329 ?>
330
331 <label class="column-label"
332 for="iawp-column-<?php
333 echo \esc_attr($column->id());
334 ?>"
335 >
336 <input id="iawp-column-<?php
337 echo \esc_attr($column->id());
338 ?>"
339 <?php
340 if ($column->requires_woocommerce() && (\IAWP_SCOPED\iawp_is_free() || !\IAWP_SCOPED\iawp_using_woocommerce())) {
341 ?>
342 <?php
343 \checked(\true, \false, \true);
344 ?>
345 disabled="disabled"
346 data-locked-behind-pro="true"
347 <?php
348 } else {
349 ?>
350 <?php
351 \checked(\true, $column->visible(), \true);
352 ?>
353 data-columns-target="checkbox"
354 data-action="columns#check"
355 <?php
356 }
357 ?>
358 type="checkbox"
359 name="<?php
360 \esc_attr_e($column->id());
361 ?>"
362 data-test-visibility="<?php
363 echo $column->visible() ? 'visible' : 'hidden';
364 ?>"
365 >
366 <span><?php
367 echo \esc_html($column->label());
368 ?></span>
369 </label>
370 <?php
371 }
372 ?>
373 </div>
374 </div>
375 </div>
376 <?php
377 }
378 public final function csv($rows)
379 {
380 $columns = $this->get_columns();
381 $csv_header = [];
382 $csv_rows = [];
383 foreach ($columns as $column) {
384 if (!$column->exportable() || $column->requires_woocommerce() && \IAWP_SCOPED\iawp_is_free()) {
385 continue;
386 }
387 $csv_header[] = $column->label();
388 }
389 foreach ($rows as $row) {
390 $csv_row = [];
391 foreach ($columns as $column) {
392 if (!$column->exportable() || $column->requires_woocommerce() && \IAWP_SCOPED\iawp_is_free()) {
393 continue;
394 }
395 $column_id = $column->id();
396 $value = $row->{$column_id}();
397 // Todo - This logic is similar to the rendering logic for table cells. This should
398 // all be handled via the column class itself.
399 if (\is_null($value)) {
400 $csv_row[] = '-';
401 } elseif ($column_id === 'date') {
402 $csv_row[] = \date(WordPress_Site_Date_Format_Pattern::for_php(), $value);
403 } elseif ($column_id === 'average_session_duration' || $column_id === 'average_view_duration') {
404 $csv_row[] = Number_Formatter::second_to_minute_timestamp($value);
405 } else {
406 $csv_row[] = $row->{$column_id}();
407 }
408 }
409 $csv_rows[] = $csv_row;
410 }
411 $csv = \array_merge([$csv_header], $csv_rows);
412 return Array_To_CSV::array2csv($csv);
413 }
414 }
415