PluginProbe
Edit Flow / trunk
Edit Flow vtrunk
0.11.1 0.11.0 0.7.2 0.7.3 0.7.4 0.7.5 0.7.6 0.8 0.8.1 0.8.2 0.9 0.9.1 0.9.2 0.9.3 0.9.4 0.9.5 0.9.6 0.9.7 0.9.8 0.9.9 trunk 0.1.5 0.10.0 0.10.1 0.10.2 All 44 releases
edit-flow / modules / settings / settings.php

settings.php in Edit Flow trunk, at modules/settings/settings.php

483 lines 18.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Settings module for Edit Flow.
4 *
5 * @package EditFlow
6 */
7
8 if ( ! class_exists( 'EF_Settings' ) ) {
9
10 /**
11 * Settings module class for Edit Flow.
12 */
13 class EF_Settings extends EF_Module {
14
15 /**
16 * The module object.
17 *
18 * @var object
19 */
20 public $module;
21
22 /**
23 * Validation errors for the settings and term forms, keyed by field name.
24 *
25 * The module form handlers populate this on a failed submission and
26 * helper_print_error_or_description() reads it back when the form re-renders in the
27 * same request. Holding it here rather than on $_REQUEST means a crafted query string
28 * cannot inject fake inline error messages onto the settings screens.
29 *
30 * @var array
31 */
32 public $form_errors = array();
33
34 /**
35 * Register the module with Edit Flow but don't do anything else.
36 */
37 public function __construct() {
38 // Register the module with Edit Flow.
39 $this->module_url = $this->get_module_url( __FILE__ );
40 $args = array(
41 'title' => __( 'Edit Flow', 'edit-flow' ),
42 'short_description' => __( 'Edit Flow redefines your WordPress publishing workflow.', 'edit-flow' ),
43 'extended_description' => __( 'Enable any of the features below to take control of your workflow. Custom statuses, email notifications, editorial comments, and more help you and your team save time so everyone can focus on what matters most: the content.', 'edit-flow' ),
44 'module_url' => $this->module_url,
45 'img_url' => $this->module_url . 'lib/eflogo_s128.png',
46 'slug' => 'settings',
47 'settings_slug' => 'ef-settings',
48 'default_options' => array(
49 'enabled' => 'on',
50 ),
51 'configure_page_cb' => 'print_default_settings',
52 'autoload' => true,
53 );
54 $this->module = EditFlow()->register_module( 'settings', $args );
55 }
56
57 /**
58 * Initialize the rest of the stuff in the class if the module is active.
59 */
60 public function init() {
61 add_action( 'admin_init', array( $this, 'helper_settings_validate_and_save' ), 100 );
62
63 add_action( 'admin_print_styles', array( $this, 'action_admin_print_styles' ) );
64 add_action( 'admin_print_scripts', array( $this, 'action_admin_print_scripts' ) );
65 add_action( 'admin_enqueue_scripts', array( $this, 'action_admin_enqueue_scripts' ) );
66 add_action( 'admin_menu', array( $this, 'action_admin_menu' ) );
67
68 add_action( 'wp_ajax_change_edit_flow_module_state', array( $this, 'ajax_change_edit_flow_module_state' ) );
69 }
70
71 /**
72 * Add necessary things to the admin menu.
73 */
74 public function action_admin_menu() {
75 global $edit_flow;
76
77 $ef_logo = 'lib/eflogo_s32w.png';
78
79 add_menu_page( $this->module->title, $this->module->title, 'manage_options', $this->module->settings_slug, array( $this, 'settings_page_controller' ), $this->module->module_url . $ef_logo );
80
81 // Add "Features" as the first submenu item (replaces the duplicate "Edit Flow" item).
82 add_submenu_page( $this->module->settings_slug, __( 'Features', 'edit-flow' ), __( 'Features', 'edit-flow' ), 'manage_options', $this->module->settings_slug, array( $this, 'settings_page_controller' ) );
83
84 foreach ( $edit_flow->modules as $mod_name => $mod_data ) {
85 if ( isset( $mod_data->options->enabled ) && 'on' == $mod_data->options->enabled
86 && $mod_data->configure_page_cb && $mod_name != $this->module->name ) {
87 add_submenu_page( $this->module->settings_slug, $mod_data->title, $mod_data->title, 'manage_options', $mod_data->settings_slug, array( $this, 'settings_page_controller' ) );
88 }
89 }
90 }
91
92 /**
93 * Enqueue scripts for the settings page.
94 */
95 public function action_admin_enqueue_scripts() {
96 if ( $this->is_whitelisted_settings_view() ) {
97 wp_enqueue_script( 'edit-flow-settings-js', $this->module_url . 'lib/settings.js', array( 'jquery' ), EDIT_FLOW_VERSION, true );
98 }
99 }
100
101 /**
102 * Add settings styles to the settings page.
103 */
104 public function action_admin_print_styles() {
105 if ( $this->is_whitelisted_settings_view() ) {
106 wp_enqueue_style( 'edit_flow-settings-css', $this->module_url . 'lib/settings.css', false, EDIT_FLOW_VERSION );
107 }
108 }
109
110 /**
111 * Extra data we need on the page for transitions, etc.
112 *
113 * @since 0.7
114 */
115 public function action_admin_print_scripts() {
116 ?>
117 <script type="text/javascript">
118 var ef_admin_url = '<?php echo esc_url( get_admin_url() ); ?>';
119 </script>
120 <?php
121 }
122
123 /**
124 * AJAX handler to enable or disable an Edit Flow module.
125 */
126 public function ajax_change_edit_flow_module_state() {
127 global $edit_flow;
128
129 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nonces don't need sanitization, just verification.
130 if ( ! isset( $_POST['change_module_nonce'] ) || ! wp_verify_nonce( $_POST['change_module_nonce'], 'change-edit-flow-module-nonce' ) || ! current_user_can( 'manage_options' ) ) {
131 wp_die( esc_html__( 'Cheatin’ uh?', 'edit-flow' ) );
132 }
133
134 if ( ! isset( $_POST['module_action'], $_POST['slug'] ) ) {
135 wp_die( '-1' );
136 }
137
138 $module_action = sanitize_key( $_POST['module_action'] );
139 $slug = sanitize_key( $_POST['slug'] );
140
141 $module = $edit_flow->get_module_by( 'slug', $slug );
142
143 if ( ! $module ) {
144 wp_die( '-1' );
145 }
146
147 $return = false;
148
149 if ( 'enable' === $module_action ) {
150 $return = $edit_flow->update_module_option( $module->name, 'enabled', 'on' );
151 } elseif ( 'disable' === $module_action ) {
152 $return = $edit_flow->update_module_option( $module->name, 'enabled', 'off' );
153 }
154
155 if ( $return ) {
156 wp_die( '1' );
157 } else {
158 wp_die( '-1' );
159 }
160 }
161
162 /**
163 * Handles all settings and configuration page requests. Required element for Edit Flow.
164 *
165 * phpcs:disable WordPress.Security.NonceVerification.Recommended -- Rendering only, no data modification.
166 */
167 public function settings_page_controller() {
168 global $edit_flow;
169
170 $page_requested = isset( $_GET['page'] ) ? sanitize_key( $_GET['page'] ) : 'settings';
171 // phpcs:enable WordPress.Security.NonceVerification
172 $requested_module = $edit_flow->get_module_by( 'settings_slug', $page_requested );
173 if ( ! $requested_module ) {
174 wp_die( esc_html__( 'Not a registered Edit Flow module', 'edit-flow' ) );
175 }
176
177 $configure_callback = $requested_module->configure_page_cb;
178 $requested_module_name = $requested_module->name;
179
180 // Don't show the settings page for the module if the module isn't activated.
181 if ( ! $this->module_enabled( $requested_module_name ) ) {
182 /* translators: 1: link to the settings page for Edit Flow */
183 echo '<div class="message error"><p>' . wp_kses( sprintf( __( 'Module not enabled. Please enable it from the <a href="%1$s">Edit Flow settings page</a>.', 'edit-flow' ), esc_url( EDIT_FLOW_SETTINGS_PAGE ) ), 'a' ) . '</p></div>';
184 return;
185 }
186
187 $this->print_default_header( $requested_module );
188 $edit_flow->$requested_module_name->$configure_callback();
189 $this->print_default_footer( $requested_module );
190 }
191
192 /**
193 * Print the default header for the settings page.
194 *
195 * Nonce verification is not available here - it's just rendering. The actual save
196 * is done in helper_settings_validate_and_save and that's guarded well.
197 *
198 * @param object $current_module The current module being displayed.
199 *
200 * phpcs:disable WordPress.Security.NonceVerification
201 */
202 public function print_default_header( $current_module ) {
203 // Register admin notices for standard WordPress display.
204 if ( isset( $_GET['message'] ) ) {
205 $message = sanitize_key( $_GET['message'] );
206 } elseif ( isset( $_REQUEST['message'] ) ) {
207 $message = sanitize_key( $_REQUEST['message'] );
208 } elseif ( isset( $_POST['message'] ) ) {
209 $message = sanitize_key( $_POST['message'] );
210 } else {
211 $message = false;
212 }
213 if ( $message && isset( $current_module->messages[ $message ] ) ) {
214 add_settings_error(
215 'edit-flow',
216 'edit-flow-' . $message,
217 $current_module->messages[ $message ],
218 'success'
219 );
220 }
221
222 // If there's been an error, register it as an admin notice.
223 if ( isset( $_GET['error'] ) ) {
224 $error = sanitize_key( $_GET['error'] );
225 } elseif ( isset( $_REQUEST['error'] ) ) {
226 $error = sanitize_key( $_REQUEST['error'] );
227 } elseif ( isset( $_POST['error'] ) ) {
228 $error = sanitize_key( $_POST['error'] );
229 } else {
230 $error = false;
231 }
232 if ( $error && isset( $current_module->messages[ $error ] ) ) {
233 add_settings_error(
234 'edit-flow',
235 'edit-flow-' . $error,
236 $current_module->messages[ $error ],
237 'error'
238 );
239 }
240
241 // Build the page title.
242 if ( 'settings' !== $current_module->name ) {
243 $page_title = sprintf(
244 /* translators: %s: module title */
245 __( 'Edit Flow: %s', 'edit-flow' ),
246 $current_module->title
247 );
248 } else {
249 $page_title = __( 'Edit Flow: Features', 'edit-flow' );
250 }
251 ?>
252 <div class="wrap edit-flow-admin">
253 <h1><?php echo esc_html( $page_title ); ?></h1>
254 <?php settings_errors( 'edit-flow' ); ?>
255
256 <?php if ( $current_module->short_description || $current_module->extended_description ) : ?>
257 <div class="explanation">
258 <?php if ( $current_module->short_description ) : ?>
259 <p class="description"><?php echo wp_kses_post( $current_module->short_description ); ?></p>
260 <?php endif; ?>
261 <?php if ( $current_module->extended_description ) : ?>
262 <p><?php echo wp_kses_post( $current_module->extended_description ); ?></p>
263 <?php endif; ?>
264 </div>
265 <?php endif; ?>
266 <?php
267 }
268 // phpcs:enable WordPress.Security.NonceVerification
269
270 /**
271 * Adds Settings page for Edit Flow.
272 */
273 public function print_default_settings() {
274 ?>
275 <div class="edit-flow-modules">
276 <?php $this->print_modules(); ?>
277 </div>
278 <form class="basic-settings" action="<?php echo esc_url( menu_page_url( $this->module->settings_slug, false ) ); ?>" method="post">
279 <?php settings_fields( $this->module->options_group_name ); ?>
280 <?php do_settings_sections( $this->module->options_group_name ); ?>
281 <input id="edit_flow_module_name" name="edit_flow_module_name" type="hidden" value="<?php echo esc_attr( $this->module->name ); ?>" />
282 <?php submit_button(); ?>
283 </form>
284 <?php
285 }
286
287 /**
288 * Print the default footer for the settings page.
289 *
290 * @param object $current_module The current module being displayed.
291 */
292 public function print_default_footer( $current_module ) {
293 ?>
294 </div>
295 <?php
296 }
297
298 /**
299 * Print the list of Edit Flow modules on the settings page.
300 */
301 public function print_modules() {
302 global $edit_flow;
303
304 if ( ! $edit_flow->modules_count ) {
305 echo '<div class="message error">' . esc_html__( 'There are no Edit Flow modules registered', 'edit-flow' ) . '</div>';
306 } else {
307 foreach ( $edit_flow->modules as $mod_name => $mod_data ) {
308 if ( $mod_data->autoload ) {
309 continue;
310 }
311
312 $classes = array(
313 'edit-flow-module',
314 );
315 if ( 'on' == $mod_data->options->enabled ) {
316 $classes[] = 'module-enabled';
317 } elseif ( 'off' == $mod_data->options->enabled ) {
318 $classes[] = 'module-disabled';
319 }
320 if ( $mod_data->configure_page_cb ) {
321 $classes[] = 'has-configure-link';
322 }
323 echo '<div class="' . esc_attr( implode( ' ', $classes ) ) . '" id="' . esc_attr( $mod_data->slug ) . '">';
324 if ( $mod_data->img_url ) {
325 echo '<img src="' . esc_url( $mod_data->img_url ) . '" height="24px" width="24px" class="float-right module-icon" />';
326 }
327 echo '<form method="get" action="' . esc_url( get_admin_url( null, 'options.php' ) ) . '">';
328 echo '<h4>' . esc_html( $mod_data->title ) . '</h4>';
329 echo '<p>' . wp_kses( $mod_data->short_description, 'post' ) . '</p>';
330 echo '<p class="edit-flow-module-actions">';
331 if ( $mod_data->configure_page_cb ) {
332 $configure_url = add_query_arg( 'page', $mod_data->settings_slug, get_admin_url( null, 'admin.php' ) );
333 echo '<a href="' . esc_url( $configure_url ) . '" class="configure-edit-flow-module';
334 if ( 'off' == $mod_data->options->enabled ) {
335 echo ' hidden" style="display:none;';
336 }
337 // phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText -- Dynamic configure link text.
338 echo '">' . esc_html__( $mod_data->configure_link_text, 'edit-flow' ) . '</a>';
339 }
340 echo '<input type="submit" class="button-primary button enable-disable-edit-flow-module"';
341 if ( 'on' == $mod_data->options->enabled ) {
342 echo ' style="display:none;"';
343 }
344 echo ' value="' . esc_textarea( __( 'Enable', 'edit-flow' ) ) . '" />';
345 echo '<input type="submit" class="button-secondary button-remove button enable-disable-edit-flow-module"';
346 if ( 'off' == $mod_data->options->enabled ) {
347 echo ' style="display:none;"';
348 }
349 echo ' value="' . esc_textarea( __( 'Disable', 'edit-flow' ) ) . '" />';
350 echo '</p>';
351 wp_nonce_field( 'change-edit-flow-module-nonce', 'change-module-nonce-' . $mod_data->slug, false );
352 echo '</form>';
353 echo '</div>';
354 }
355 }
356 }
357
358 /**
359 * Given a form field and a description, prints either the error associated with the field or the description.
360 *
361 * @since 0.7
362 *
363 * @param string $field The form field for which to check for an error.
364 * @param string $description Unlocalized string to display if there was no error with the given field.
365 */
366 public function helper_print_error_or_description( $field, $description ) {
367 if ( isset( $this->form_errors[ $field ] ) ) :
368 ?>
369 <div class="form-error">
370 <p><?php echo esc_html( $this->form_errors[ $field ] ); ?></p>
371 </div>
372 <?php else : ?>
373 <p class="description"><?php echo esc_html( $description ); ?></p>
374 <?php
375 endif;
376 }
377
378 /**
379 * Generate an option field to turn post type support on/off for a given module.
380 *
381 * @since 0.7
382 *
383 * @param object $module Edit Flow module we're generating the option field for.
384 * @param array $args Optional. Additional arguments.
385 */
386 public function helper_option_custom_post_type( $module, $args = array() ) {
387
388 $all_post_types = array(
389 'post' => __( 'Posts', 'edit-flow' ),
390 'page' => __( 'Pages', 'edit-flow' ),
391 );
392 $custom_post_types = $this->get_supported_post_types_for_module();
393 if ( count( $custom_post_types ) ) {
394 foreach ( $custom_post_types as $custom_post_type => $args ) {
395 $all_post_types[ $custom_post_type ] = $args->label;
396 }
397 }
398
399 foreach ( $all_post_types as $post_type => $title ) {
400 echo '<label for="' . esc_attr( $post_type ) . '">';
401 echo '<input id="' . esc_attr( $post_type ) . '" name="'
402 . esc_attr( $module->options_group_name ) . '[post_types][' . esc_attr( $post_type ) . ']"';
403 if ( isset( $module->options->post_types[ $post_type ] ) ) {
404 checked( $module->options->post_types[ $post_type ], 'on' );
405 }
406 // Defining post_type_supports in the functions.php file or similar should disable the checkbox.
407 disabled( post_type_supports( $post_type, $module->post_type_support ), true );
408 echo ' type="checkbox" />&nbsp;&nbsp;&nbsp;' . esc_html( $title ) . '</label>';
409 // Leave a note to the admin as a reminder that add_post_type_support has been used somewhere in their code.
410 if ( post_type_supports( $post_type, $module->post_type_support ) ) {
411 /* translators: 1: post type, 2: post type support */
412 echo '&nbsp&nbsp;&nbsp;<span class="description">' . esc_html( sprintf( __( 'Disabled because add_post_type_support( \'%1$s\', \'%2$s\' ) is included in a loaded file.', 'edit-flow' ), $post_type, $module->post_type_support ) ) . '</span>';
413 }
414 echo '<br />';
415 }
416 }
417
418 /**
419 * Validation and sanitization on the settings field.
420 *
421 * This method is called automatically and doesn't need to be registered anywhere.
422 *
423 * @since 0.7
424 *
425 * @return false|void Returns false if validation fails, otherwise redirects.
426 */
427 public function helper_settings_validate_and_save() {
428
429 if ( ! isset( $_POST['action'], $_POST['_wpnonce'], $_POST['option_page'], $_POST['_wp_http_referer'], $_POST['edit_flow_module_name'], $_POST['submit'] ) || ! is_admin() ) {
430 return false;
431 }
432
433 global $edit_flow;
434 $module_name = sanitize_key( $_POST['edit_flow_module_name'] );
435
436 // Validate the submitted module name resolves to a real module before
437 // dereferencing it; this runs ahead of the capability check below, so a bogus
438 // name must not reach a property access on a non-object.
439 if ( ! isset( $edit_flow->$module_name ) || ! is_object( $edit_flow->$module_name ) ) {
440 return false;
441 }
442
443 if ( 'update' != $_POST['action']
444 || $_POST['option_page'] != $edit_flow->$module_name->module->options_group_name ) {
445 return false;
446 }
447
448 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nonces don't need sanitization, just verification.
449 if ( ! current_user_can( 'manage_options' ) || ! wp_verify_nonce( $_POST['_wpnonce'], $edit_flow->$module_name->module->options_group_name . '-options' ) ) {
450 wp_die( esc_html__( 'Cheatin’ uh?', 'edit-flow' ) );
451 }
452
453 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitization is handled by each module's settings_validate method.
454 $new_options = ( isset( $_POST[ $edit_flow->$module_name->module->options_group_name ] ) ) ? $_POST[ $edit_flow->$module_name->module->options_group_name ] : array();
455
456 // Only call the validation callback if it exists.
457 if ( method_exists( $edit_flow->$module_name, 'settings_validate' ) ) {
458 $new_options = $edit_flow->$module_name->settings_validate( $new_options );
459 } else {
460 // Without a module validator, accept only keys that already exist in the
461 // module's options and sanitise their values, rather than persisting
462 // arbitrary submitted keys into the autoloaded options row.
463 $existing = (array) $edit_flow->$module_name->module->options;
464 $new_options = map_deep( array_intersect_key( (array) $new_options, $existing ), 'sanitize_text_field' );
465 }
466
467 // Cast our object and save the data.
468 $new_options = (object) array_merge( (array) $edit_flow->$module_name->module->options, $new_options );
469 $edit_flow->update_all_module_options( $edit_flow->$module_name->module->name, $new_options );
470
471 // Redirect back to the settings page that was submitted without any previous messages.
472 $referer = wp_get_referer();
473 if ( ! $referer ) {
474 $referer = admin_url( 'admin.php?page=' . $edit_flow->$module_name->module->settings_slug );
475 }
476 $goback = add_query_arg( 'message', 'settings-updated', remove_query_arg( array( 'message' ), $referer ) );
477 wp_safe_redirect( $goback );
478 exit;
479 }
480 }
481
482 }
483