PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.3
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.3
16.3-beta 16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 All 507 releases
jetpack / jetpack_vendor / automattic / jetpack-premium-analytics / src / class-dashboard-section.php

class-dashboard-section.php in Jetpack – WP Security, Backup, Speed, & Growth 16.3-a.3, at jetpack_vendor/automattic/jetpack-premium-analytics/src/class-dashboard-section.php

291 lines 7.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Dashboard Sections API: Dashboard_Section class.
4 *
5 * @package automattic/jetpack-premium-analytics
6 */
7
8 namespace Automattic\Jetpack\PremiumAnalytics;
9
10 /**
11 * Represents a dashboard section.
12 *
13 * The server-owned model behind a top-level dashboard tab.
14 */
15 final class Dashboard_Section {
16
17 /**
18 * Date-filter surface offering the rolling date-range picker (today, last 7
19 * days, a custom range, …) plus the comparison control. The default.
20 *
21 * @since 0.2.0
22 * @var string
23 */
24 const DATE_FILTER_RANGE = 'range';
25
26 /**
27 * Date-filter surface offering all time plus one entry per calendar year,
28 * for sections whose data is read as whole history rather than as a
29 * rolling window.
30 *
31 * @since 0.2.0
32 * @var string
33 */
34 const DATE_FILTER_YEAR = 'year';
35
36 /**
37 * Date-filter surfaces a section may declare.
38 *
39 * @since 0.2.0
40 * @var string[]
41 */
42 const DATE_FILTERS = array( self::DATE_FILTER_RANGE, self::DATE_FILTER_YEAR );
43
44 /**
45 * Dashboard identifier.
46 *
47 * @var string
48 */
49 public $dashboard_name;
50
51 /**
52 * Section identifier.
53 *
54 * @var string
55 */
56 public $id;
57
58 /**
59 * URL-facing section slug, derived from the identifier.
60 *
61 * @var string
62 */
63 public $slug;
64
65 /**
66 * Display label, naming the section's tab.
67 *
68 * @var string
69 */
70 public $label;
71
72 /**
73 * Section heading, deliberately distinct from the tab label: the tab reads
74 * `Traffic` where the heading reads `Site traffic`. Null falls back to the label.
75 *
76 * @since 0.3.0
77 * @var string|null
78 */
79 public $title = null;
80
81 /**
82 * Sort order.
83 *
84 * @var int
85 */
86 public $order = 10;
87
88 /**
89 * Which shape the section's date filter takes, as one of self::DATE_FILTERS.
90 *
91 * Shape only. Where it renders and what it supports are
92 * self::$date_filter_options.
93 *
94 * @since 0.2.0
95 * @var string
96 */
97 public $date_filter = self::DATE_FILTER_RANGE;
98
99 /**
100 * What the section's date filter supports, and where it renders.
101 *
102 * - `with_date_comparison`: false drops the comparison param from every widget fetch in the
103 * section, not just the chrome.
104 * - `with_header_date_control`: false hands the control to the section's widgets, which may
105 * save the range onto the widget instance rather than the URL.
106 *
107 * @since 0.3.0
108 * @since 0.5.0 Added `with_header_date_control`.
109 * @var array
110 */
111 public $date_filter_options = array(
112 'with_date_comparison' => true,
113 'with_header_date_control' => true,
114 );
115
116 /**
117 * Whether the section's data only reaches WordPress.com through the analytics
118 * full sync, so its numbers are incomplete until that sync has finished once.
119 *
120 * @since 0.4.0
121 * @var bool
122 */
123 public $requires_sync = false;
124
125 /**
126 * Availability flag or callback.
127 *
128 * @var bool|callable
129 */
130 private $is_available = true;
131
132 /**
133 * Default layout array or callback.
134 *
135 * @var array|callable
136 */
137 private $default_layout = array();
138
139 /**
140 * Constructor.
141 *
142 * @param string $dashboard_name Dashboard identifier.
143 * @param string $id Section identifier.
144 * @param array $args Optional. Section arguments.
145 */
146 public function __construct( $dashboard_name, $id, $args = array() ) {
147 $this->dashboard_name = $dashboard_name;
148 $this->id = $id;
149 $this->slug = self::derive_slug( $id );
150 $this->label = $id;
151
152 $this->set_props( $args );
153 }
154
155 /**
156 * Derives the URL-facing slug from a namespaced section identifier.
157 *
158 * @param string $id Section identifier, e.g. `analytics/traffic`.
159 * @return string The segment after the namespace, e.g. `traffic`.
160 */
161 private static function derive_slug( $id ) {
162 $separator = strpos( (string) $id, '/' );
163
164 return false === $separator ? (string) $id : substr( $id, $separator + 1 );
165 }
166
167 /**
168 * Returns whether this section should be exposed.
169 *
170 * @return bool
171 */
172 public function is_available() {
173 // The preview scope is about the rollout rather than the site, so it sits ahead of the
174 // section's own check.
175 if ( ! is_dashboard_section_in_preview_scope( $this->dashboard_name, $this->slug ) ) {
176 return false;
177 }
178
179 if ( is_callable( $this->is_available ) ) {
180 return (bool) call_user_func( $this->is_available, $this );
181 }
182
183 return (bool) $this->is_available;
184 }
185
186 /**
187 * Returns the section's default widget layout, run through the default-layout filter.
188 *
189 * @return array Array of widget instances.
190 */
191 public function get_default_layout() {
192 $layout = is_callable( $this->default_layout )
193 ? call_user_func( $this->default_layout, $this )
194 : $this->default_layout;
195 $layout = is_array( $layout ) ? array_values( $layout ) : array();
196
197 /**
198 * Filters a dashboard section's default widget layout.
199 *
200 * Each entry matches the dashboard's widget instance shape: `uuid`, `type`, optional
201 * `attributes`, optional `placement`. Runs for every section, so a callback adding an
202 * instance to one switches on `$section_id`.
203 *
204 * @since 0.8.0 Runs from the section, with its declared layout and its
205 * namespaced id; it received an empty array and any alias before.
206 *
207 * @param array $layout The section's declared default widget instances.
208 * @param string $section_id Namespaced section identifier, e.g. `analytics/traffic`.
209 * @param Dashboard_Section $section The section.
210 */
211 $layout = apply_filters( DASHBOARD_DEFAULT_LAYOUT_FILTER, $layout, $this->id, $this );
212
213 return is_array( $layout ) ? array_values( $layout ) : array();
214 }
215
216 /**
217 * Returns the public REST representation.
218 *
219 * @return array
220 */
221 public function to_array() {
222 return array(
223 'id' => $this->id,
224 'slug' => $this->slug,
225 'label' => $this->label,
226 'title' => $this->title,
227 'order' => (int) $this->order,
228 'date_filter' => $this->date_filter,
229 'date_filter_options' => $this->date_filter_options,
230 'requires_sync' => $this->requires_sync,
231 'default_layout' => $this->get_default_layout(),
232 );
233 }
234
235 /**
236 * Hydrates section properties from the args array.
237 *
238 * @param array $args Section arguments.
239 * @return void
240 */
241 private function set_props( $args ) {
242 if ( ! is_array( $args ) ) {
243 return;
244 }
245
246 if ( isset( $args['label'] ) ) {
247 $this->label = (string) $args['label'];
248 }
249
250 // An empty string is a registrant saying "none", not a heading: kept as-is it
251 // would defeat the label fallback and render an `<h2>` with no accessible name.
252 if ( isset( $args['title'] ) ) {
253 $title = (string) $args['title'];
254 $this->title = '' === $title ? null : $title;
255 }
256
257 if ( isset( $args['order'] ) ) {
258 $this->order = (int) $args['order'];
259 }
260
261 // An unrecognized surface keeps the default rather than reaching the
262 // dashboard, where the frontend has no filter to render for it.
263 if ( isset( $args['date_filter'] ) && in_array( $args['date_filter'], self::DATE_FILTERS, true ) ) {
264 $this->date_filter = (string) $args['date_filter'];
265 }
266
267 // Merged over the defaults so a partial array keeps the rest, and narrowed
268 // to the known options, which is all the dashboard renders.
269 if ( isset( $args['date_filter_options'] ) && is_array( $args['date_filter_options'] ) ) {
270 $options = array_merge( $this->date_filter_options, $args['date_filter_options'] );
271
272 $this->date_filter_options = array(
273 'with_date_comparison' => (bool) $options['with_date_comparison'],
274 'with_header_date_control' => (bool) $options['with_header_date_control'],
275 );
276 }
277
278 if ( isset( $args['requires_sync'] ) ) {
279 $this->requires_sync = (bool) $args['requires_sync'];
280 }
281
282 if ( array_key_exists( 'is_available', $args ) ) {
283 $this->is_available = $args['is_available'];
284 }
285
286 if ( array_key_exists( 'default_layout', $args ) ) {
287 $this->default_layout = $args['default_layout'];
288 }
289 }
290 }
291