PluginProbe
Code Snippets / 4.0.0-beta.1
Code Snippets v4.0.0-beta.1
4.0.0-beta.2 3.10.2 3.10.1 3.10.0 3.10.0-beta.2 3.10.0-beta.1 4.0.0-beta.1 3.9.6 trunk 2.10.0 2.10.1 2.12.0 2.12.1 2.13.0 2.13.1 2.13.2 2.13.3 2.14.0 2.14.1 2.14.2 2.14.3 2.14.4 2.14.5 2.14.6 3.0.0 All 65 releases
code-snippets / php / settings / settings.php

settings.php in Code Snippets 4.0.0-beta.1, at php/settings/settings.php

378 lines 10.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * This file registers the settings.
4 *
5 * @package Code_Snippets
6 */
7
8 namespace Code_Snippets\Settings;
9
10 use Code_Snippets\Client\Welcome_API;
11 use function add_action;
12 use function Code_Snippets\clean_snippets_cache;
13 use function Code_Snippets\code_snippets;
14 use function Code_Snippets\Utils\add_self_option;
15 use function Code_Snippets\Utils\get_self_option;
16 use function Code_Snippets\Utils\update_self_option;
17 use const Code_Snippets\CACHE_GROUP;
18
19 const CACHE_KEY = 'code_snippets_settings';
20 const OPTION_GROUP = 'code-snippets';
21 const OPTION_NAME = 'code_snippets_settings';
22
23 /**
24 * Returns 'true' if plugin settings are unified on a multisite installation
25 * under the Network Admin settings menu
26 *
27 * This option is controlled by the "Enable administration menus" setting on the Network Settings menu
28 *
29 * @return bool
30 */
31 function are_settings_unified(): bool {
32 if ( ! is_multisite() ) {
33 return false;
34 }
35
36 $menu_perms = get_site_option( 'menu_items', [] );
37 return empty( $menu_perms['snippets_settings'] );
38 }
39
40 /**
41 * Retrieve the setting values from the database.
42 *
43 * If a setting does not exist in the database, the default value will be returned.
44 *
45 * @return array<string, array<string, mixed>>
46 */
47 function get_settings_values(): array {
48 $settings = wp_cache_get( CACHE_KEY, CACHE_GROUP );
49 if ( $settings ) {
50 return $settings;
51 }
52
53 $settings = Settings_Fields::get_default_values();
54 $saved = get_self_option( are_settings_unified(), OPTION_NAME, [] );
55
56 // Deep merge the saved settings with the default values.
57 foreach ( $settings as $section => $section_fields ) {
58 if ( isset( $saved[ $section ] ) ) {
59 $settings[ $section ] = array_replace( $section_fields, $saved[ $section ] );
60 }
61 }
62
63 wp_cache_set( CACHE_KEY, $settings, CACHE_GROUP );
64 return $settings;
65 }
66
67 /**
68 * Retrieve an individual setting field value
69 *
70 * @param string $section ID of the section the setting belongs to.
71 * @param string $field ID of the setting field.
72 *
73 * @return mixed
74 */
75 function get_setting( string $section, string $field ) {
76 $settings = get_settings_values();
77
78 return $settings[ $section ][ $field ] ?? null;
79 }
80
81 /**
82 * Update a single setting to a new value.
83 *
84 * @param string $section ID of the section the setting belongs to.
85 * @param string $field ID of the setting field.
86 * @param mixed $new_value Setting value. Expected to not be SQL-escaped.
87 *
88 * @return bool False if value was not updated. True if value was updated.
89 */
90 function update_setting( string $section, string $field, $new_value ): bool {
91 $settings = get_settings_values();
92
93 $settings[ $section ][ $field ] = $new_value;
94
95 wp_cache_set( CACHE_KEY, $settings, CACHE_GROUP );
96 return update_self_option( are_settings_unified(), OPTION_NAME, $settings );
97 }
98
99 /**
100 * Retrieve the settings sections
101 *
102 * @return array<string, string> Settings sections.
103 */
104 function get_settings_sections(): array {
105 $sections = array(
106 'general' => __( 'General', 'code-snippets' ),
107 'editor' => __( 'Code Editor', 'code-snippets' ),
108 'debug' => __( 'Debug', 'code-snippets' ),
109 );
110
111 // Only show the Version section when the debug setting to enable version changes is enabled.
112 $enable_version = get_setting( 'debug', 'enable_version_change' );
113 if ( $enable_version ) {
114 $sections['version-switch'] = __( 'Version', 'code-snippets' );
115 }
116
117 return apply_filters( 'code_snippets_settings_sections', $sections );
118 }
119
120 /**
121 * Register settings sections, fields, etc
122 */
123 function register_plugin_settings() {
124 if ( ! get_self_option( are_settings_unified(), OPTION_NAME ) ) {
125 add_self_option( are_settings_unified(), OPTION_NAME, Settings_Fields::get_default_values() );
126 }
127
128 $current_settings = get_settings_values();
129
130 // Register the setting.
131 register_setting(
132 OPTION_GROUP,
133 OPTION_NAME,
134 [ 'sanitize_callback' => __NAMESPACE__ . '\\sanitize_settings' ]
135 );
136
137 // Register settings sections.
138 foreach ( get_settings_sections() as $section_id => $section_name ) {
139 add_settings_section( $section_id, $section_name, '__return_empty_string', 'code-snippets' );
140 }
141
142 // Register settings fields. Only register fields for sections that exist (some sections may be gated by settings).
143 $registered_sections = get_settings_sections();
144 foreach ( Settings_Fields::get_field_definitions() as $section_id => $fields ) {
145 if ( ! isset( $registered_sections[ $section_id ] ) ) {
146 continue;
147 }
148
149 foreach ( $fields as $field_id => $field ) {
150 if ( ! should_render_setting_field( $field, $current_settings ) ) {
151 continue;
152 }
153
154 $field_object = new Setting_Field( $section_id, $field_id, $field );
155 add_settings_field( $field_id, $field['name'], [ $field_object, 'render' ], 'code-snippets', $section_id );
156 }
157 }
158
159 $editor_preview = new Editor_Preview();
160
161 // Add editor preview as a field.
162 add_settings_field(
163 'editor_preview',
164 __( 'Editor Preview', 'code-snippets' ),
165 [ $editor_preview, 'render' ],
166 'code-snippets',
167 'editor'
168 );
169
170 Version_Switch::init();
171 }
172
173 add_action( 'admin_init', __NAMESPACE__ . '\\register_plugin_settings' );
174
175 /**
176 * Determine whether a setting field should be rendered.
177 *
178 * @param array<string, mixed> $field Field definition.
179 * @param array<string, array<string,mixed>> $settings Current settings values.
180 * @param array<string, array<string,mixed>>|null $input Optional raw input values.
181 *
182 * @return bool
183 */
184 function should_render_setting_field( array $field, array $settings, ?array $input = null ): bool {
185 if ( empty( $field['show_if'] ) || ! is_array( $field['show_if'] ) ) {
186 return true;
187 }
188
189 $show_if = array_merge(
190 [
191 'section' => '',
192 'field' => '',
193 'value' => true,
194 ],
195 $field['show_if']
196 );
197
198 $section = is_string( $show_if['section'] ) ? $show_if['section'] : '';
199 $field_id = is_string( $show_if['field'] ) ? $show_if['field'] : '';
200 $expected = $show_if['value'];
201
202 if ( '' === $section || '' === $field_id ) {
203 return true;
204 }
205
206 $actual = null;
207
208 if ( is_array( $input ) && isset( $input[ $section ] ) && is_array( $input[ $section ] ) && array_key_exists( $field_id, $input[ $section ] ) ) {
209 $actual = $input[ $section ][ $field_id ];
210 } elseif ( isset( $settings[ $section ] ) && array_key_exists( $field_id, $settings[ $section ] ) ) {
211 $actual = $settings[ $section ][ $field_id ];
212 }
213
214 if ( is_bool( $expected ) ) {
215 if ( is_bool( $actual ) ) {
216 return $actual === $expected;
217 }
218
219 return ( 'on' === $actual ) === $expected;
220 }
221
222 return $actual === $expected;
223 }
224
225 /**
226 * Sanitize a single setting value.
227 *
228 * @param array<string, mixed> $field Setting field information.
229 * @param mixed $input_value User input setting value, or null if missing.
230 *
231 * @return mixed Sanitized setting value, or null if unset.
232 */
233 function sanitize_setting_value( array $field, $input_value ) {
234 switch ( $field['type'] ) {
235
236 case 'checkbox':
237 return 'on' === $input_value;
238
239 case 'number':
240 return intval( $input_value );
241
242 case 'select':
243 $select_options = array_map( 'strval', array_keys( $field['options'] ) );
244 return in_array( strval( $input_value ), $select_options, true ) ? $input_value : null;
245
246 case 'checkboxes':
247 $results = [];
248
249 if ( ! empty( $input_value ) ) {
250 foreach ( $field['options'] as $option_id => $option_label ) {
251 if ( isset( $input_value[ $option_id ] ) && 'on' === $input_value[ $option_id ] ) {
252 $results[] = $option_id;
253 }
254 }
255 }
256
257 return $results;
258
259 case 'text':
260 case 'hidden':
261 return trim( sanitize_text_field( $input_value ) );
262
263 case 'callback':
264 return isset( $field['sanitize_callback'] ) && is_callable( $field['sanitize_callback'] ) ?
265 call_user_func( $field['sanitize_callback'], $input_value ) :
266 null;
267
268 default:
269 return null;
270 }
271 }
272
273 /**
274 * Process settings actions.
275 *
276 * @param array $input Provided settings input.
277 *
278 * @return array|null New $input value to return, or null to continue with settings update process.
279 */
280 function process_settings_actions( array $input ): ?array {
281
282 if ( isset( $input['reset_settings'] ) ) {
283 add_settings_error(
284 OPTION_NAME,
285 'settings_reset',
286 __( 'All settings have been reset to their defaults.', 'code-snippets' ),
287 'updated'
288 );
289
290 delete_option( 'code_snippets_cloud_settings' );
291 return [];
292 }
293
294 if ( isset( $input['debug']['database_update'] ) ) {
295 code_snippets()->db->create_or_upgrade_tables();
296
297 add_settings_error(
298 OPTION_NAME,
299 'database_update_done',
300 __( 'Successfully performed database table upgrade.', 'code-snippets' ),
301 'updated'
302 );
303 }
304
305 if ( isset( $input['debug']['reset_caches'] ) ) {
306 Welcome_API::clear_cache();
307 code_snippets()->cloud_api->clear_caches();
308 clean_snippets_cache( code_snippets()->db->get_table_name( false ) );
309
310 if ( is_multisite() ) {
311 clean_snippets_cache( code_snippets()->db->get_table_name( true ) );
312 }
313
314 add_settings_error(
315 OPTION_NAME,
316 'snippet_caches_reset',
317 __( 'Successfully reset snippets caches.', 'code-snippets' ),
318 'updated'
319 );
320 }
321
322 return null;
323 }
324
325 /**
326 * Validate the settings
327 *
328 * @param array<string, array<string, mixed>> $input The received settings.
329 *
330 * @return array<string, array<string, mixed>> The validated settings.
331 */
332 function sanitize_settings( array $input ): array {
333 wp_cache_delete( CACHE_KEY, CACHE_GROUP );
334 $result = process_settings_actions( $input );
335
336 if ( ! is_null( $result ) ) {
337 return $result;
338 }
339
340 $settings = get_settings_values();
341 $updated = false;
342
343 // Don't directly loop through $input as it does not include as deselected checkboxes.
344 foreach ( Settings_Fields::get_field_definitions() as $section_id => $fields ) {
345 foreach ( $fields as $field_id => $field ) {
346 if ( ! should_render_setting_field( $field, $settings, $input ) ) {
347 continue;
348 }
349
350 // Fetch the corresponding input value from the posted data.
351 $input_value = $input[ $section_id ][ $field_id ] ?? null;
352 $stored_value = $settings[ $section_id ][ $field_id ] ?? null;
353
354 // Attempt to sanitize the setting value.
355 $sanitized_value = sanitize_setting_value( $field, $input_value );
356
357 if ( ! is_null( $sanitized_value ) && $stored_value !== $sanitized_value ) {
358 $settings[ $section_id ][ $field_id ] = $sanitized_value;
359 $updated = true;
360 }
361 }
362 }
363
364 // Add an updated message.
365 if ( $updated ) {
366 add_settings_error(
367 OPTION_NAME,
368 'settings-saved',
369 __( 'Settings saved.', 'code-snippets' ),
370 'updated'
371 );
372
373 do_action( 'code_snippets/settings_updated', $settings, $input );
374 }
375
376 return $settings;
377 }
378