Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
98.28% covered (success)
98.28%
57 / 58
100.00% covered (success)
100.00%
8 / 8
CRAP
100.00% covered (success)
100.00%
1 / 1
Dashboard_Section_Registry
100.00% covered (success)
100.00%
57 / 57
100.00% covered (success)
100.00%
8 / 8
18
100.00% covered (success)
100.00%
1 / 1
 register
100.00% covered (success)
100.00%
30 / 30
100.00% covered (success)
100.00%
1 / 1
5
 get_registered
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 get_all_registered
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 get_available_sections
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
2
 is_registered
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_instance
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 is_valid_dashboard_name
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 is_valid_section_id
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2/**
3 * Dashboard Sections API: Dashboard_Section_Registry class.
4 *
5 * @package automattic/jetpack-premium-analytics
6 */
7
8namespace Automattic\Jetpack\PremiumAnalytics;
9
10require_once __DIR__ . '/dashboard-grammar.php';
11
12/**
13 * Stores Dashboard_Section instances keyed by dashboard and section ID.
14 */
15final class Dashboard_Section_Registry {
16
17    /**
18     * Registered sections, as `$dashboard_name => $id => $section` pairs.
19     *
20     * @var array<string, Dashboard_Section[]>
21     */
22    private $registered_sections = array();
23
24    /**
25     * Container for the main instance of the class.
26     *
27     * @var Dashboard_Section_Registry|null
28     */
29    private static $instance = null;
30
31    /**
32     * Registers a dashboard section.
33     *
34     * @param string $dashboard_name Dashboard identifier.
35     * @param string $id             Section identifier.
36     * @param array  $args           Optional. Section arguments.
37     * @return Dashboard_Section|false The registered section on success, or false on failure.
38     */
39    public function register( $dashboard_name, $id, $args = array() ) {
40        if ( ! $this->is_valid_dashboard_name( $dashboard_name ) ) {
41            _doing_it_wrong(
42                __METHOD__,
43                esc_html__( 'Dashboard names must be lowercase strings of letters, numbers, and hyphens, optionally separated by underscores.', 'jetpack-premium-analytics' ),
44                '0.1.0'
45            );
46            return false;
47        }
48
49        if ( ! $this->is_valid_section_id( $id ) ) {
50            _doing_it_wrong(
51                __METHOD__,
52                esc_html__( 'Dashboard section IDs must contain a namespace prefix. Example: my-plugin/my-custom-section', 'jetpack-premium-analytics' ),
53                '0.1.0'
54            );
55            return false;
56        }
57
58        if ( $this->is_registered( $dashboard_name, $id ) ) {
59            _doing_it_wrong(
60                __METHOD__,
61                sprintf(
62                    /* translators: 1: Dashboard name. 2: Dashboard section ID. */
63                    esc_html__( 'Dashboard section "%2$s" is already registered for dashboard "%1$s".', 'jetpack-premium-analytics' ),
64                    esc_html( $dashboard_name ),
65                    esc_html( $id )
66                ),
67                '0.1.0'
68            );
69            return false;
70        }
71
72        $section = new Dashboard_Section( $dashboard_name, $id, $args );
73
74        if ( ! isset( $this->registered_sections[ $dashboard_name ] ) ) {
75            $this->registered_sections[ $dashboard_name ] = array();
76        }
77
78        $this->registered_sections[ $dashboard_name ][ $id ] = $section;
79
80        return $section;
81    }
82
83    /**
84     * Retrieves a registered section.
85     *
86     * @param string $dashboard_name Dashboard identifier.
87     * @param string $id             Section identifier.
88     * @return Dashboard_Section|null The registered section, or null when absent.
89     */
90    public function get_registered( $dashboard_name, $id ) {
91        if ( ! $this->is_registered( $dashboard_name, $id ) ) {
92            return null;
93        }
94
95        return $this->registered_sections[ $dashboard_name ][ $id ];
96    }
97
98    /**
99     * Retrieves all registered sections for a dashboard.
100     *
101     * @param string $dashboard_name Dashboard identifier.
102     * @return Dashboard_Section[] Map of `$id => $section` pairs.
103     */
104    public function get_all_registered( $dashboard_name ) {
105        if ( ! isset( $this->registered_sections[ $dashboard_name ] ) ) {
106            // Unknown dashboards may be valid REST targets but have no sections registered.
107            return array();
108        }
109
110        return $this->registered_sections[ $dashboard_name ];
111    }
112
113    /**
114     * Retrieves available sections sorted by order.
115     *
116     * @param string $dashboard_name Dashboard identifier.
117     * @return Dashboard_Section[] Ordered list of available sections.
118     */
119    public function get_available_sections( $dashboard_name ) {
120        $sections = array_filter(
121            $this->get_all_registered( $dashboard_name ),
122            static function ( Dashboard_Section $section ) {
123                return $section->is_available();
124            }
125        );
126
127        uasort(
128            $sections,
129            static function ( Dashboard_Section $a, Dashboard_Section $b ) {
130                if ( $a->order === $b->order ) {
131                    return strcmp( $a->id, $b->id );
132                }
133
134                return $a->order <=> $b->order;
135            }
136        );
137
138        return array_values( $sections );
139    }
140
141    /**
142     * Checks if a section is registered.
143     *
144     * @param string $dashboard_name Dashboard identifier.
145     * @param string $id             Section identifier.
146     * @return bool True if registered.
147     */
148    public function is_registered( $dashboard_name, $id ) {
149        return isset( $this->registered_sections[ $dashboard_name ][ $id ] );
150    }
151
152    /**
153     * Utility method to retrieve the main instance of the class.
154     *
155     * @return Dashboard_Section_Registry The main instance.
156     */
157    public static function get_instance() {
158        if ( null === self::$instance ) {
159            self::$instance = new self();
160        }
161
162        return self::$instance;
163    }
164
165    /**
166     * Checks whether a dashboard name can be registered.
167     *
168     * @param mixed $dashboard_name Candidate dashboard name.
169     * @return bool
170     */
171    private function is_valid_dashboard_name( $dashboard_name ) {
172        return is_string( $dashboard_name ) && 1 === preg_match( '/^' . get_dashboard_name_pattern() . '$/', $dashboard_name );
173    }
174
175    /**
176     * Checks whether a section ID can be registered.
177     *
178     * @param mixed $id Candidate section ID.
179     * @return bool
180     */
181    private function is_valid_section_id( $id ) {
182        return is_string( $id ) && 1 === preg_match( '/^' . get_dashboard_section_id_pattern() . '$/', $id );
183    }
184}