PluginProbe
Gutenberg / 23.5.2
Gutenberg v23.5.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.5.2, at lib/class-wp-icons-registry-gutenberg.php

298 lines 8.2 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 'file_path' => $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 `file_path` if set.
63 * If both `content` and `file_path` are not set, the icon will not be registered.
64 * @type string $file_path 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 public 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', 'file_path' ), 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['file_path'] ) ) ||
133 ( isset( $icon_properties['content'] ) && isset( $icon_properties['file_path'] ) )
134 ) {
135 _doing_it_wrong(
136 __METHOD__,
137 __( 'Icons must provide either `content` or `file_path`.', '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 * Redefined to read the icon content from the `file_path` property.
176 *
177 * @param string $icon_name Icon name including namespace.
178 * @return string|null The content of the icon, if found.
179 */
180 protected function get_content( $icon_name ) {
181 if ( ! isset( $this->registered_icons[ $icon_name ]['content'] ) ) {
182 $content = file_get_contents(
183 $this->registered_icons[ $icon_name ]['file_path']
184 );
185 $content = $this->sanitize_icon_content( $content );
186
187 if ( empty( $content ) ) {
188 wp_trigger_error(
189 __METHOD__,
190 __( 'Icon content does not contain valid SVG markup.', 'gutenberg' )
191 );
192 return null;
193 }
194
195 $this->registered_icons[ $icon_name ]['content'] = $content;
196 }
197 return $this->registered_icons[ $icon_name ]['content'];
198 }
199
200 /**
201 * Modified to also search in icon labels
202 */
203 public function get_registered_icons( $search = '' ) {
204 $icons = array();
205
206 foreach ( $this->registered_icons as $icon ) {
207 if ( ! empty( $search )
208 && false === stripos( $icon['name'], $search )
209 && false === stripos( $icon['label'], $search )
210 ) {
211 continue;
212 }
213
214 $icon['content'] = $icon['content'] ?? $this->get_content( $icon['name'] );
215 $icons[] = $icon;
216 }
217
218 return $icons;
219 }
220
221 /**
222 * Redefined to break away from base class.
223 */
224 protected static $instance = null;
225
226 /**
227 * Redefined to access new `$instance`
228 */
229 public static function get_instance() {
230 if ( null === self::$instance ) {
231 self::$instance = new self();
232 }
233
234 return self::$instance;
235 }
236 }
237
238 /**
239 * Forces WP_Icons_Registry_Gutenberg instantiation and overrides WP_Icons_Registry
240 * so that all code using WP_Icons_Registry::{method_name}() receives the Gutenberg
241 * registry.
242 */
243 function gutenberg_override_wp_icons_registry() {
244 /*
245 * The plugin registers the `core/` icons from its own manifest, so core's
246 * registration would only re-register the same names on this registry.
247 */
248 $wp_priority = has_action( 'init', '_wp_register_default_icons' );
249 if ( false !== $wp_priority ) {
250 remove_action( 'init', '_wp_register_default_icons', $wp_priority );
251 }
252
253 $reflection = new ReflectionClass( WP_Icons_Registry::class );
254 $property = $reflection->getProperty( 'instance' );
255 /*
256 * ReflectionProperty::setAccessible is:
257 * - redundant as of 8.1.0, which made all properties accessible
258 * - deprecated as of 8.5.0
259 * - needed until 8.1.0, as property `instance` is private
260 */
261 if ( PHP_VERSION_ID < 80100 ) {
262 $property->setAccessible( true );
263 }
264 $original_registry = $property->getValue( null );
265 $gutenberg_registry = WP_Icons_Registry_Gutenberg::get_instance();
266
267 // If the original registry was already instantiated, replay any icons outside
268 // the `core/` namespace onto the Gutenberg registry so they are not lost.
269 if ( null !== $original_registry ) {
270 $register_method = new ReflectionMethod( WP_Icons_Registry_Gutenberg::class, 'register' );
271 /*
272 * ReflectionMethod::setAccessible is:
273 * - redundant as of 8.1.0, which made all properties accessible
274 * - deprecated as of 8.5.0
275 * - needed until 8.1.0, as property `instance` is private
276 */
277 if ( PHP_VERSION_ID < 80100 ) {
278 $register_method->setAccessible( true );
279 }
280 foreach ( $original_registry->get_registered_icons() as $icon ) {
281 if ( strpos( $icon['name'], 'core/' ) === 0 ) {
282 continue;
283 }
284 $icon_properties = array( 'label' => $icon['label'] );
285 if ( ! empty( $icon['content'] ) ) {
286 $icon_properties['content'] = $icon['content'];
287 } elseif ( ! empty( $icon['file_path'] ) ) {
288 $icon_properties['file_path'] = $icon['file_path'];
289 } else {
290 continue;
291 }
292 $register_method->invoke( $gutenberg_registry, $icon['name'], $icon_properties );
293 }
294 }
295 $property->setValue( null, $gutenberg_registry );
296 }
297 add_action( 'init', 'gutenberg_override_wp_icons_registry', 1 );
298