PluginProbe
Gutenberg / 23.2.2
Gutenberg v23.2.2
23.9.1 23.9.0 23.8.0 23.7.2 23.7.1 23.7.0 23.6.1 23.6.2 23.6.0 23.5.3 23.5.2 23.5.1 23.5.0 23.4.0 23.3.2 23.3.1 23.3.0 23.2.0 23.2.1 23.2.2 23.1.1 23.1.0 23.0.1 12.6.0 7.4.0 All 402 releases
gutenberg / lib / class-wp-icons-registry-gutenberg.php

class-wp-icons-registry-gutenberg.php in Gutenberg 23.2.2, at lib/class-wp-icons-registry-gutenberg.php

263 lines 7.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 class WP_Icons_Registry_Gutenberg extends WP_Icons_Registry {
4 /**
5 * Modified to point $manifest_path to Gutenberg packages
6 */
7 protected function __construct() {
8 $icons_directory = gutenberg_dir_path() . 'packages/icons/src';
9 $icons_directory = trailingslashit( $icons_directory );
10 $manifest_path = $icons_directory . 'manifest.php';
11
12 if ( ! is_readable( $manifest_path ) ) {
13 wp_trigger_error(
14 __METHOD__,
15 __( 'Core icon collection manifest is missing or unreadable.', 'gutenberg' )
16 );
17 return;
18 }
19
20 $collection = include $manifest_path;
21
22 if ( empty( $collection ) ) {
23 wp_trigger_error(
24 __METHOD__,
25 __( 'Core icon collection manifest is empty or invalid.', 'gutenberg' )
26 );
27 return;
28 }
29
30 foreach ( $collection as $icon_name => $icon_data ) {
31 if (
32 empty( $icon_data['filePath'] )
33 || ! is_string( $icon_data['filePath'] )
34 ) {
35 _doing_it_wrong(
36 __METHOD__,
37 __( 'Core icon collection manifest must provide valid a "filePath" for each icon.', 'gutenberg' ),
38 '7.0.0'
39 );
40 return;
41 }
42
43 $this->register(
44 'core/' . $icon_name,
45 array(
46 'label' => $icon_data['label'],
47 'filePath' => $icons_directory . $icon_data['filePath'],
48 )
49 );
50 }
51 }
52
53 /**
54 * Registers an icon.
55 *
56 * @param string $icon_name Icon name including namespace.
57 * @param array $icon_properties {
58 * List of properties for the icon.
59 *
60 * @type string $label Required. A human-readable label for the icon.
61 * @type string $content Optional. SVG markup for the icon.
62 * If not provided, the content will be retrieved from the `filePath` if set.
63 * If both `content` and `filePath` are not set, the icon will not be registered.
64 * @type string $filePath Optional. The full path to the file containing the icon content.
65 * }
66 * @return bool True if the icon was registered with success and false otherwise.
67 */
68 protected function register( $icon_name, $icon_properties ) {
69 if ( ! isset( $icon_name ) || ! is_string( $icon_name ) ) {
70 _doing_it_wrong(
71 __METHOD__,
72 __( 'Icon name must be a string.', 'gutenberg' ),
73 '7.0.0'
74 );
75 return false;
76 }
77
78 if ( preg_match( '/[A-Z]/', $icon_name ) ) {
79 _doing_it_wrong(
80 __METHOD__,
81 __( 'Icon names must not contain uppercase characters.', 'gutenberg' ),
82 '7.1.0'
83 );
84 return false;
85 }
86
87 $name_matcher = '/^[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$/';
88 if ( ! preg_match( $name_matcher, $icon_name ) ) {
89 _doing_it_wrong(
90 __METHOD__,
91 __( 'Icon names must contain a namespace prefix. Example: my-plugin/my-custom-icon', 'gutenberg' ),
92 '7.1.0'
93 );
94 return false;
95 }
96
97 if ( $this->is_registered( $icon_name ) ) {
98 _doing_it_wrong(
99 __METHOD__,
100 __( 'Icon is already registered.', 'gutenberg' ),
101 '7.1.0'
102 );
103 return false;
104 }
105
106 $allowed_keys = array_fill_keys( array( 'label', 'content', 'filePath' ), 1 );
107 foreach ( array_keys( $icon_properties ) as $key ) {
108 if ( ! array_key_exists( $key, $allowed_keys ) ) {
109 _doing_it_wrong(
110 __METHOD__,
111 sprintf(
112 // translators: %s is the name of any user-provided key
113 __( 'Invalid icon property: "%s".', 'gutenberg' ),
114 $key
115 ),
116 '7.0.0'
117 );
118 return false;
119 }
120 }
121
122 if ( ! isset( $icon_properties['label'] ) || ! is_string( $icon_properties['label'] ) ) {
123 _doing_it_wrong(
124 __METHOD__,
125 __( 'Icon label must be a string.', 'gutenberg' ),
126 '7.0.0'
127 );
128 return false;
129 }
130
131 if (
132 ( ! isset( $icon_properties['content'] ) && ! isset( $icon_properties['filePath'] ) ) ||
133 ( isset( $icon_properties['content'] ) && isset( $icon_properties['filePath'] ) )
134 ) {
135 _doing_it_wrong(
136 __METHOD__,
137 __( 'Icons must provide either `content` or `filePath`.', 'gutenberg' ),
138 '7.0.0'
139 );
140 return false;
141 }
142
143 if ( isset( $icon_properties['content'] ) ) {
144 if ( ! is_string( $icon_properties['content'] ) ) {
145 _doing_it_wrong(
146 __METHOD__,
147 __( 'Icon content must be a string.', 'gutenberg' ),
148 '7.0.0'
149 );
150 return false;
151 }
152
153 $sanitized_icon_content = $this->sanitize_icon_content( $icon_properties['content'] );
154 if ( empty( $sanitized_icon_content ) ) {
155 _doing_it_wrong(
156 __METHOD__,
157 __( 'Icon content does not contain valid SVG markup.', 'gutenberg' ),
158 '7.0.0'
159 );
160 return false;
161 }
162 }
163
164 $icon = array_merge(
165 $icon_properties,
166 array( 'name' => $icon_name )
167 );
168
169 $this->registered_icons[ $icon_name ] = $icon;
170
171 return true;
172 }
173
174 /**
175 * Modified to also search in icon labels
176 */
177 public function get_registered_icons( $search = '' ) {
178 $icons = array();
179
180 foreach ( $this->registered_icons as $icon ) {
181 if ( ! empty( $search )
182 && false === stripos( $icon['name'], $search )
183 && false === stripos( $icon['label'], $search )
184 ) {
185 continue;
186 }
187
188 $icon['content'] = $icon['content'] ?? $this->get_content( $icon['name'] );
189 $icons[] = $icon;
190 }
191
192 return $icons;
193 }
194
195 /**
196 * Redefined to break away from base class.
197 */
198 protected static $instance = null;
199
200 /**
201 * Redefined to access new `$instance`
202 */
203 public static function get_instance() {
204 if ( null === self::$instance ) {
205 self::$instance = new self();
206 }
207
208 return self::$instance;
209 }
210 }
211
212 /**
213 * Forces WP_Icons_Registry_Gutenberg instantiation and overrides WP_Icons_Registry
214 * so that all code using WP_Icons_Registry::{method_name}() receives the Gutenberg
215 * registry.
216 */
217 function gutenberg_override_wp_icons_registry() {
218 $reflection = new ReflectionClass( WP_Icons_Registry::class );
219 $property = $reflection->getProperty( 'instance' );
220 /*
221 * ReflectionProperty::setAccessible is:
222 * - redundant as of 8.1.0, which made all properties accessible
223 * - deprecated as of 8.5.0
224 * - needed until 8.1.0, as property `instance` is private
225 */
226 if ( PHP_VERSION_ID < 80100 ) {
227 $property->setAccessible( true );
228 }
229 $original_registry = $property->getValue( null );
230 $gutenberg_registry = WP_Icons_Registry_Gutenberg::get_instance();
231
232 // If the original registry was already instantiated, replay any icons outside
233 // the `core/` namespace onto the Gutenberg registry so they are not lost.
234 if ( null !== $original_registry ) {
235 $register_method = new ReflectionMethod( WP_Icons_Registry_Gutenberg::class, 'register' );
236 /*
237 * ReflectionMethod::setAccessible is:
238 * - redundant as of 8.1.0, which made all properties accessible
239 * - deprecated as of 8.5.0
240 * - needed until 8.1.0, as property `instance` is private
241 */
242 if ( PHP_VERSION_ID < 80100 ) {
243 $register_method->setAccessible( true );
244 }
245 foreach ( $original_registry->get_registered_icons() as $icon ) {
246 if ( strpos( $icon['name'], 'core/' ) === 0 ) {
247 continue;
248 }
249 $icon_properties = array( 'label' => $icon['label'] );
250 if ( ! empty( $icon['content'] ) ) {
251 $icon_properties['content'] = $icon['content'];
252 } elseif ( ! empty( $icon['filePath'] ) ) {
253 $icon_properties['filePath'] = $icon['filePath'];
254 } else {
255 continue;
256 }
257 $register_method->invoke( $gutenberg_registry, $icon['name'], $icon_properties );
258 }
259 }
260 $property->setValue( null, $gutenberg_registry );
261 }
262 add_action( 'init', 'gutenberg_override_wp_icons_registry', 1 );
263