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