PluginProbe
AutomatorWP – No-Code Workflow Automation, Integration & Webhooks Plugin, now with AI / 6.0.3
AutomatorWP – No-Code Workflow Automation, Integration & Webhooks Plugin, now with AI v6.0.3
6.0.3 6.0.2 6.0.1 6.0.0 5.8.6 5.8.5 5.8.4 5.8.3 5.8.2 5.8.1 5.8.0 5.7.9.2 5.7.9.1 5.7.8 5.7.9 5.7.6 5.7.7 5.7.5 5.7.4 5.7.3 5.7.2 5.7.1 trunk 5.6.0 5.6.1 All 34 releases
← All changes | libraries/cmb2/includes/CMB2_Options_Hookup.php +392 -0 5.8.3 → 6.0.3 View file →
@@ -1,0 +1,392 @@
1 +<?php
2 +/**
3 + * Handles hooking CMB2 forms/metaboxes into the post/attachement/user screens
4 + * and handles hooking in and saving those fields.
5 + *
6 + * @since 2.0.0
7 + *
8 + * @category WordPress_Plugin
9 + * @package CMB2
10 + * @author CMB2 team
11 + * @license GPL-2.0+
12 + * @link https://cmb2.io
13 + */
14 +class CMB2_Options_Hookup extends CMB2_Hookup {
15 +
16 + /**
17 + * The object type we are performing the hookup for
18 + *
19 + * @var string
20 + * @since 2.0.9
21 + */
22 + protected $object_type = 'options-page';
23 +
24 + /**
25 + * Options page key.
26 + *
27 + * @var string
28 + * @since 2.2.5
29 + */
30 + protected $option_key = '';
31 +
32 + /**
33 + * Constructor
34 + *
35 + * @since 2.0.0
36 + * @param CMB2 $cmb The CMB2 object to hookup.
37 + * @param string $option_key Option key to use.
38 + */
39 + public function __construct( CMB2 $cmb, $option_key ) {
40 + $this->cmb = $cmb;
41 + $this->option_key = $option_key;
42 + }
43 +
44 + public function hooks() {
45 + if ( empty( $this->option_key ) ) {
46 + return;
47 + }
48 +
49 + if ( ! $this->cmb->prop( 'autoload', true ) ) {
50 + // Disable option autoload if requested.
51 + add_filter( "cmb2_should_autoload_{$this->option_key}", '__return_false' );
52 + }
53 +
54 + /**
55 + * For WP < 4.7. Ensure the register_setting function exists.
56 + */
57 + if ( ! CMB2_Utils::wp_at_least( '4.7' ) && ! function_exists( 'register_setting' ) ) {
58 + require_once ABSPATH . 'wp-admin/includes/plugin.php';
59 + }
60 +
61 + // Register setting to cmb2 group.
62 + register_setting( 'cmb2', $this->option_key, array(
63 + 'type' => 'array',
64 + 'sanitize_callback' => 'cmb2_settings_sanitization_cb',
65 + ) );
66 +
67 + // Handle saving the data.
68 + add_action( 'admin_post_' . $this->option_key, array( $this, 'save_options' ) );
69 +
70 + // Optionally network_admin_menu.
71 + $hook = $this->cmb->prop( 'admin_menu_hook' );
72 +
73 + // Hook in to add our menu.
74 + add_action( $hook, array( $this, 'options_page_menu_hooks' ), $this->get_priority() );
75 +
76 + // If in the network admin, need to use get/update_site_option.
77 + if ( 'network_admin_menu' === $hook ) {
78 + // Override CMB's getter.
79 + add_filter( "cmb2_override_option_get_{$this->option_key}", array( $this, 'network_get_override' ), 10, 2 );
80 + // Override CMB's setter.
81 + add_filter( "cmb2_override_option_save_{$this->option_key}", array( $this, 'network_update_override' ), 10, 2 );
82 + }
83 + }
84 +
85 + /**
86 + * Hook up our admin menu item and admin page.
87 + *
88 + * @since 2.2.5
89 + *
90 + * @return void
91 + */
92 + public function options_page_menu_hooks() {
93 + $parent_slug = $this->cmb->prop( 'parent_slug' );
94 + $title = $this->cmb->prop( 'title' );
95 + $menu_title = $this->cmb->prop( 'menu_title', $title );
96 + $capability = $this->cmb->prop( 'capability' );
97 + $callback = array( $this, 'options_page_output' );
98 +
99 + if ( $parent_slug ) {
100 + $page_hook = add_submenu_page(
101 + $parent_slug,
102 + $title,
103 + $menu_title,
104 + $capability,
105 + $this->option_key,
106 + $callback
107 + );
108 + } else {
109 + $page_hook = add_menu_page(
110 + $title,
111 + $menu_title,
112 + $capability,
113 + $this->option_key,
114 + $callback,
115 + $this->cmb->prop( 'icon_url' ),
116 + $this->cmb->prop( 'position' )
117 + );
118 + }
119 +
120 + if ( $this->cmb->prop( 'cmb_styles' ) ) {
121 + // Include CMB CSS in the head to avoid FOUC.
122 + add_action( "admin_print_styles-{$page_hook}", array( 'CMB2_Hookup', 'enqueue_cmb_css' ) );
123 + }
124 +
125 + $this->maybe_register_message();
126 + }
127 +
128 + /**
129 + * If there is a message callback, let it determine how to register the message,
130 + * else add a settings message if on this settings page.
131 + *
132 + * @since 2.2.6
133 + *
134 + * @return void
135 + */
136 + public function maybe_register_message() {
137 + $is_options_page = self::is_page( $this->option_key );
138 + $should_notify = ! $this->cmb->prop( 'disable_settings_errors' ) && isset( $_GET['settings-updated'] ) && $is_options_page;
139 + $is_updated = $should_notify && 'true' === $_GET['settings-updated'];
140 + $setting = "{$this->option_key}-notices";
141 + $code = '';
142 + $message = __( 'Nothing to update.', 'cmb2' );
143 + $type = 'notice-warning';
144 +
145 + if ( $is_updated ) {
146 + $message = __( 'Settings updated.', 'cmb2' );
147 + $type = 'updated';
148 + }
149 +
150 + // Check if parameter has registered a callback.
151 + if ( $cb = $this->cmb->maybe_callback( 'message_cb' ) ) {
152 +
153 + /**
154 + * The 'message_cb' callback will receive the following parameters.
155 + * Unless there are other reasons for notifications, the callback should only
156 + * `add_settings_error()` if `$args['should_notify']` is truthy.
157 + *
158 + * @param CMB2 $cmb The CMB2 object.
159 + * @param array $args {
160 + * An array of message arguments
161 + *
162 + * @type bool $is_options_page Whether current page is this options page.
163 + * @type bool $should_notify Whether options were saved and we should be notified.
164 + * @type bool $is_updated Whether options were updated with save (or stayed the same).
165 + * @type string $setting For add_settings_error(), Slug title of the setting to which
166 + * this error applies.
167 + * @type string $code For add_settings_error(), Slug-name to identify the error.
168 + * Used as part of 'id' attribute in HTML output.
169 + * @type string $message For add_settings_error(), The formatted message text to display
170 + * to the user (will be shown inside styled `<div>` and `<p>` tags).
171 + * Will be 'Settings updated.' if $is_updated is true, else 'Nothing to update.'
172 + * @type string $type For add_settings_error(), Message type, controls HTML class.
173 + * Accepts 'error', 'updated', '', 'notice-warning', etc.
174 + * Will be 'updated' if $is_updated is true, else 'notice-warning'.
175 + * }
176 + */
177 + $args = compact( 'is_options_page', 'should_notify', 'is_updated', 'setting', 'code', 'message', 'type' );
178 +
179 + $this->cmb->do_callback( $cb, $args );
180 +
181 + } elseif ( $should_notify ) {
182 +
183 + add_settings_error( $setting, $code, $message, $type );
184 + }
185 + }
186 +
187 + /**
188 + * Display options-page output. To override, set 'display_cb' box property.
189 + *
190 + * @since 2.2.5
191 + */
192 + public function options_page_output() {
193 + $this->maybe_output_settings_notices();
194 +
195 + $callback = $this->cmb->prop( 'display_cb' );
196 + if ( is_callable( $callback ) ) {
197 + return call_user_func( $callback, $this );
198 + }
199 +
200 + ?>
201 + <div class="wrap cmb2-options-page option-<?php echo esc_attr( sanitize_html_class( $this->option_key ) ); ?>">
202 + <?php if ( $this->cmb->prop( 'title' ) ) : ?>
203 + <h2><?php echo wp_kses_post( $this->cmb->prop( 'title' ) ); ?></h2>
204 + <?php endif; ?>
205 + <?php $this->options_page_tab_nav_output(); ?>
206 + <form class="cmb-form" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" method="POST" id="<?php echo $this->cmb->cmb_id; ?>" enctype="multipart/form-data" encoding="multipart/form-data">
207 + <input type="hidden" name="action" value="<?php echo esc_attr( $this->option_key ); ?>">
208 + <?php $this->options_page_metabox(); ?>
209 + <?php submit_button( esc_attr( $this->cmb->prop( 'save_button' ) ), 'primary', 'submit-cmb' ); ?>
210 + </form>
211 + </div>
212 + <?php
213 + }
214 +
215 + /**
216 + * Display options-page Tab Navigation output.
217 + *
218 + * @since 2.9.0
219 + */
220 + public function options_page_tab_nav_output() {
221 + $tabs = $this->get_tab_group_tabs();
222 + if ( empty( $tabs ) ) {
223 + return;
224 + }
225 + ?>
226 + <h2 class="nav-tab-wrapper">
227 + <?php foreach ( $tabs as $option_key => $tab_title ) : ?>
228 + <?php // phpcs:ignore Squiz.ControlStructures.ControlSignature.NewlineAfterOpenBrace -- Inline alternative-syntax conditional in template markup; keep on one line. ?>
229 + <a class="nav-tab<?php if ( self::is_page( $option_key ) ) : ?> nav-tab-active<?php endif; ?>" href="<?php menu_page_url( $option_key ); ?>"><?php echo wp_kses_post( $tab_title ); ?></a>
230 + <?php endforeach; ?>
231 + </h2>
232 + <?php
233 + }
234 +
235 + /**
236 + * Outputs the settings notices if a) not a sub-page of 'options-general.php'
237 + * (because settings_errors() already called in wp-admin/options-head.php),
238 + * and b) the 'disable_settings_errors' prop is not set or truthy.
239 + *
240 + * @since 2.2.5
241 + * @return void
242 + */
243 + public function maybe_output_settings_notices() {
244 + global $parent_file;
245 +
246 + // The settings sub-pages will already have settings_errors() called in wp-admin/options-head.php.
247 + if ( 'options-general.php' !== $parent_file ) {
248 + settings_errors( "{$this->option_key}-notices" );
249 + }
250 + }
251 +
252 + /**
253 + * Gets navigation tabs array for CMB2 options pages which share the
254 + * same tab_group property.
255 + *
256 + * @since 2.4.0
257 + * @return array Array of tab information ($option_key => $tab_title)
258 + */
259 + public function get_tab_group_tabs() {
260 + $tab_group = $this->cmb->prop( 'tab_group' );
261 + $tabs = array();
262 +
263 + if ( $tab_group ) {
264 + $boxes = CMB2_Boxes::get_by( 'tab_group', $tab_group );
265 +
266 + foreach ( $boxes as $cmb ) {
267 + $option_key = $cmb->options_page_keys();
268 +
269 + // Must have an option key, must be an options page box.
270 + if ( ! isset( $option_key[0] ) || 'options-page' !== $cmb->mb_object_type() ) {
271 + continue;
272 + }
273 +
274 + $tabs[ $option_key[0] ] = $cmb->prop( 'tab_title', $cmb->prop( 'title' ) );
275 + }
276 + }
277 +
278 + return apply_filters( 'cmb2_tab_group_tabs', $tabs, $tab_group );
279 + }
280 +
281 + /**
282 + * Display metaboxes for an options-page object.
283 + *
284 + * @since 2.2.5
285 + */
286 + public function options_page_metabox() {
287 + $this->show_form_for_type( 'options-page' );
288 + }
289 +
290 + /**
291 + * Save data from options page, then redirects back.
292 + *
293 + * @since 2.2.5
294 + * @return void
295 + */
296 + public function save_options() {
297 + $url = wp_get_referer();
298 + if ( ! $url ) {
299 + $url = admin_url();
300 + }
301 +
302 + if (
303 + $this->can_save( 'options-page' )
304 + // check params.
305 + && isset( $_POST['submit-cmb'], $_POST['action'] )
306 + && $this->option_key === $_POST['action']
307 + ) {
308 +
309 + $updated = $this->cmb
310 + ->save_fields( $this->option_key, $this->cmb->object_type(), $_POST )
311 + ->was_updated(); // Will be false if no values were changed/updated.
312 +
313 + $url = add_query_arg( 'settings-updated', $updated ? 'true' : 'false', $url );
314 + }
315 +
316 + wp_safe_redirect( esc_url_raw( $url ), 303 /* WP_Http::SEE_OTHER */ );
317 + exit;
318 + }
319 +
320 + /**
321 + * Replaces get_option with get_site_option.
322 + *
323 + * @since 2.2.5
324 + *
325 + * @param mixed $test Not used.
326 + * @param mixed $default Default value to use.
327 + * @return mixed Value set for the network option.
328 + */
329 + public function network_get_override( $test, $default = false ) {
330 + return get_site_option( $this->option_key, $default );
331 + }
332 +
333 + /**
334 + * Replaces update_option with update_site_option.
335 + *
336 + * @since 2.2.5
337 + *
338 + * @param mixed $test Not used.
339 + * @param mixed $option_value Value to use.
340 + * @return bool Success/Failure
341 + */
342 + public function network_update_override( $test, $option_value ) {
343 + return update_site_option( $this->option_key, $option_value );
344 + }
345 +
346 + /**
347 + * Determines if given page slug matches the 'page' GET query variable.
348 + *
349 + * @since 2.4.0
350 + *
351 + * @param string $page Page slug.
352 + * @return boolean
353 + */
354 + public static function is_page( $page ) {
355 + return isset( $_GET['page'] ) && $page === $_GET['page'];
356 + }
357 +
358 + /**
359 + * Magic getter for our object.
360 + *
361 + * @param string $field Property to retrieve.
362 + *
363 + * @throws Exception Throws an exception if the field is invalid.
364 + * @return mixed
365 + */
366 + public function __get( $field ) {
367 + switch ( $field ) {
368 + case 'object_type':
369 + case 'option_key':
370 + case 'cmb':
371 + return $this->{$field};
372 + default:
373 + // translators: 1: class name, 2: property name.
374 + throw new Exception( sprintf( esc_html__( 'Invalid %1$s property: %2$s', 'cmb2' ), __CLASS__, $field ) );
375 + }
376 + }
377 +}
378 +
379 +if( ! function_exists( 'cmb2_settings_sanitization_cb' ) ) {
380 + function cmb2_settings_sanitization_cb( $settings ) {
381 + global $cmb2_settings_sanitizated;
382 +
383 + if( $cmb2_settings_sanitizated === true ) {
384 + return $settings;
385 + }
386 +
387 + $cmb2_settings_sanitizated = true;
388 +
389 + return map_deep( $settings, 'sanitize_text_field' );
390 + }
391 +
392 +}