├── readme.md ├── wm-settings.css ├── wm-settings.js └── plugin.php /readme.md: -------------------------------------------------------------------------------- 1 | ## Synopsis 2 | 3 | Based on the WordPress Settings API, a class to generate options pages. Create settings forms with all basic input types, selects, textareas and media uploads. 4 | 5 | ## Basic Example 6 | 7 | ```php 8 | // Define the page 9 | $my_page = create_settings_page( 10 | 'my_page_id', 11 | __( 'My Page' ), 12 | array( 13 | 'title' => __( 'My Menu' ) 14 | ), 15 | array( 16 | 'my_setting_id' => array( 17 | 'title' => __( 'My Setting' ), 18 | 'description' => __( 'This is my section description.' ), 19 | 'fields' => array( 20 | 'my_option_name' => array( 21 | 'label' => __( 'My Option' ), 22 | 'description' => __( 'This is my field description.' ) 23 | ) 24 | ) 25 | ) 26 | ) 27 | ); 28 | 29 | // Access the values 30 | $my_value = get_setting( 'my_setting_id', 'my_option_name' ); 31 | ``` 32 | 33 | ## Motivation 34 | 35 | Settings are really useful to provide an easy configuration of themes and plugins to our users within their administration panel. But the creation of options pages often ends up in a messy and repetitive use of the great WordPress Settings API. 36 | 37 | Considering generic form fields, this is a class to clean and simplify the process. It’s something light that shall be used on the admin side. 38 | 39 | ## Installation 40 | 41 | 1. Download the last release 42 | 2. Unzip it into your theme or plugin 43 | 3. `require_once( 'path/to/wm-settings/plugin.php' );` 44 | 45 | ## Documentation 46 | 47 | [Read the documentation](http://webmaestro.fr/wordpress-settings-api-options-pages/#wm-settings-doc). 48 | 49 | ## Contributors 50 | 51 | If you are interested by this project, please feel free to contribute in any way you like. 52 | 53 | You can contact [@WebmaestroFR](https://twitter.com/WebmaestroFR) on twitter. 54 | 55 | ## License 56 | 57 | [WTFPL](http://www.wtfpl.net/) – Do What the Fuck You Want to Public License 58 | -------------------------------------------------------------------------------- /wm-settings.css: -------------------------------------------------------------------------------- 1 | .notice.info { 2 | border-color: #2ea2cc; 3 | } 4 | .notice.warning { 5 | border-color: #ffba00; 6 | } 7 | 8 | .wm-settings-form h3, 9 | .wm-settings-form .submit { 10 | padding-top: 20px; 11 | border-top: 1px solid #ccc; 12 | } 13 | 14 | .wm-settings-form .wm-settings-tabs { 15 | padding: 10px 15px 0 10px; 16 | line-height: 29px; 17 | border-bottom: 1px solid #ccc; 18 | } 19 | .wm-settings-form .wm-settings-tabs h3 { 20 | padding: 6px 10px; 21 | font-weight: bold; 22 | font-size: 15px; 23 | line-height: 24px; 24 | border: 1px solid #ccc; 25 | border-bottom: none; 26 | background: #e4e4e4; 27 | color: #555; 28 | display: inline-block; 29 | text-decoration: none; 30 | margin: 0 4px -1px 0; 31 | cursor: pointer; 32 | -webkit-transition-property: border, background, color; 33 | transition-property: border, background, color; 34 | -webkit-transition-duration: .05s; 35 | transition-duration: .05s; 36 | -webkit-transition-timing-function: ease-in-out; 37 | transition-timing-function: ease-in-out; 38 | } 39 | .wm-settings-form .wm-settings-tabs h3:hover { 40 | background: #fff; 41 | color: #464646; 42 | } 43 | .wm-settings-form .wm-settings-tabs h3.active { 44 | border-bottom: 1px solid #f1f1f1; 45 | background: #f1f1f1; 46 | color: #000; 47 | } 48 | .wm-settings-form .wm-settings-tab { 49 | margin: 23px 10px 0; 50 | } 51 | .wm-settings-form .wm-preview-media { 52 | height: auto; 53 | width: auto; 54 | max-height: 240px; 55 | max-width: 100%; 56 | } 57 | 58 | tr.hide-label th { 59 | display: none; 60 | } 61 | tr.hide-label td { 62 | padding-left: 0; 63 | } 64 | 65 | #wm_settings_reset { 66 | float: right; 67 | color: #a00; 68 | height: auto; 69 | margin: 0 0 0 8px; 70 | padding: 0; 71 | line-height: 15px; 72 | font-size: 13px; 73 | text-decoration: underline; 74 | background: none; 75 | border: none; 76 | outline: 0; 77 | -webkit-box-shadow: none; 78 | box-shadow: none; 79 | } 80 | #wm_settings_reset:hover { 81 | color: #f00; 82 | text-decoration: none; 83 | } 84 | 85 | @media screen and (max-width: 782px) { 86 | input.regular-text { 87 | width: 100%; 88 | font-size: 16px; 89 | line-height: 1.5; 90 | padding: 7px 10px; 91 | display: block; 92 | max-width: none; 93 | -webkit-box-sizing: border-box; 94 | -moz-box-sizing: border-box; 95 | box-sizing: border-box; 96 | } 97 | #wm_settings_reset { 98 | font-size: 14px; 99 | margin-top: 12px; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /wm-settings.js: -------------------------------------------------------------------------------- 1 | jQuery(document).ready(function ($) { 2 | 'use strict'; 3 | var $form = $('.wm-settings-form'), 4 | $tabs = $('.wm-settings-tabs', $form), 5 | page = $('input[name="option_page"]', $form).val(), 6 | current = parseInt(sessionStorage.getItem(page + '_current_tab'), 10) || 0; 7 | // $Section hidden inputs 8 | $('.wm-settings-section', $form).each(function (i, el) { 9 | var setting = $(el).val(), 10 | $title = $(el).prev('h3'), 11 | $section = $('
').attr('id', page + '_' + setting); 12 | // Wrap section content in div 13 | $(el).nextAll().each(function () { 14 | var tag = $(this).prop('tagName'); 15 | if (tag === 'H3' || tag === 'INPUT') { 16 | return false; 17 | } 18 | $section.append(this); 19 | }); 20 | if ($tabs.length && $title.length) { 21 | // Prepare tab 22 | $section.addClass('wm-settings-tab').hide(); 23 | $title.appendTo($tabs).click(function (e) { 24 | e.preventDefault(); 25 | if (!$title.hasClass('active')) { 26 | $('.wm-settings-tab.active', $form).fadeOut('fast', function () { 27 | $('.active', $form).removeClass('active'); 28 | $title.addClass('active'); 29 | $section.fadeIn('fast').addClass('active'); 30 | }); 31 | sessionStorage.setItem(page + '_current_tab', i); 32 | } 33 | }); 34 | if (current === i) { 35 | $title.addClass('active'); 36 | $section.show().addClass('active'); 37 | } 38 | } else { 39 | $section.prepend($title); 40 | } 41 | // Insert section wrapper 42 | $(el).after($section); 43 | }); 44 | $('label[for="hidden"]', $form).each(function () { 45 | $(this).parents('tr').addClass('hide-label'); 46 | }); 47 | $('.wm-settings-media', $form).each(function () { 48 | var frame, 49 | $input = $('input', this), 50 | $select = $('.wm-select-media', this), 51 | $remove = $('.wm-remove-media', this).toggle(!!$input.val()), 52 | $preview = $('.wm-preview-media', this), 53 | title = $select.attr('title'), 54 | text = $select.text(), 55 | type = $(this).data('type'); 56 | $select.click(function (e) { 57 | e.preventDefault(); 58 | if (frame) { 59 | frame.open(); 60 | return; 61 | } 62 | var media = { 63 | title: title, 64 | button: { text: text }, 65 | multiple: false 66 | }; 67 | if ( type === 'image' ) { 68 | media.library = { type: 'image' }; 69 | } 70 | frame = wp.media(media); 71 | frame.on('select', function () { 72 | var attachment = frame.state().get('selection').first().toJSON(); 73 | $input.val(type === 'media' ? attachment.id : attachment.url); 74 | $preview.attr({ src: attachment.sizes 75 | ? attachment.sizes.full.url 76 | : attachment.icon 77 | }).show(); 78 | $remove.show(); 79 | }); 80 | frame.open(); 81 | }); 82 | $remove.click(function (e) { 83 | e.preventDefault(); 84 | $input.val(''); 85 | $preview.hide(); 86 | $remove.hide(); 87 | }); 88 | }); 89 | $('.wm-settings-action', $form).each(function () { 90 | var $submit = $('[type="button"]', this), 91 | $spinner = $('').attr({ 92 | src: ajax.spinner, 93 | alt: 'loading' 94 | }).insertAfter($submit).hide(), 95 | $notice = $('
').addClass('settings-error').insertBefore($submit).hide(), 96 | action = { 97 | data: { 98 | action: $submit.attr('id') 99 | }, 100 | dataType: 'json', 101 | type: 'POST', 102 | url: ajax.url, 103 | beforeSend: function () { 104 | $spinner.fadeIn('fast'); 105 | $submit.hide(); 106 | }, 107 | success: function (r) { 108 | var noticeClass = 'error', 109 | showNotice = function (msg) { 110 | $notice.html('

' + String(msg) + '

').addClass(noticeClass).show(); 111 | }; 112 | if (typeof r === 'object') { 113 | if (r.hasOwnProperty('success') && r.success) { 114 | noticeClass = 'updated'; 115 | } 116 | if (r.hasOwnProperty('data') && r.data) { 117 | if (typeof r.data === 'object') { 118 | if (r.data.hasOwnProperty('reload') && r.data.reload) { 119 | document.location.reload(); 120 | return; 121 | } 122 | if (r.data.hasOwnProperty('message') && r.data.message) { 123 | showNotice(r.data.message); 124 | } 125 | } else { 126 | showNotice(r.data); 127 | } 128 | } 129 | } else if (r) { 130 | showNotice(r); 131 | } 132 | $spinner.hide(); 133 | $submit.fadeIn('fast'); 134 | $notice.show('fast'); 135 | }, 136 | error: function (jqXHR, textStatus, errorThrown) { 137 | $notice.addClass('error').append('

' + jqXHR.responseText + '

').show('fast'); 138 | console.log(textStatus, jqXHR, errorThrown); 139 | } 140 | }; 141 | $submit.click(function (e) { 142 | e.preventDefault(); 143 | $notice.hide('fast', function () { 144 | $notice.removeClass('error updated').empty(); 145 | $.ajax(action); 146 | }); 147 | }); 148 | }); 149 | $('.wm-settings-color').wpColorPicker(); 150 | }); 151 | -------------------------------------------------------------------------------- /plugin.php: -------------------------------------------------------------------------------- 1 | array( 50 | 'title' => $title, 51 | 'description' => $description, 52 | 'fields' => $fields, 53 | 'customize' => true 54 | ) 55 | ) ); 56 | } 57 | 58 | 59 | // PLUGIN CLASS 60 | 61 | class WM_Settings { 62 | 63 | private $name, // Page id 64 | $title, // Page title 65 | $menu, // Menu configuration 66 | $config, // Page configuration 67 | $settings = array(), // User defined settings 68 | $sections = array(), // Settings list 69 | $empty = true, // Page status 70 | $notices; // Page notices 71 | 72 | private static $instances = array(), // Page instances 73 | $actions = array(), // Registered actions 74 | $alerts = array(); // Global notices 75 | 76 | 77 | // PAGE CONSTRUCTOR 78 | 79 | public function __construct( $name = 'custom_settings', $title = null, $menu = array(), $settings = array(), array $config = array() ) 80 | { 81 | $this->name = (string) $name; 82 | $this->title = $title ? (string) $title : __( 'Custom Settings', 'wm-settings' ); 83 | $this->menu = ( $menu || is_array( $menu ) ) ? array( 84 | 'parent' => 'options-general.php', // Parent page id 85 | 'title' => $this->title, // Menu item title 86 | 'capability' => 'manage_options', // User capability to access 87 | 'icon_url' => null, // Menu item icon (for parent page only) 88 | 'position' => null // Menu item priority 89 | ) : false; 90 | if ( is_array( $menu ) ) { 91 | $this->menu = array_merge( $this->menu, $menu ); 92 | } 93 | $this->config = array_merge( array( 94 | 'description' => null, // Page description 95 | 'submit' => __( 'Save Settings', 'wm-settings' ), // Submit button text 96 | 'reset' => __( 'Reset Settings', 'wm-settings' ), // Reset button text (false to disable) 97 | 'tabs' => true, // Use tabs to switch sections 98 | 'updated' => __( 'Settings saved.', 'wm-settings') // Custom success message 99 | ), $config ); 100 | 101 | // Get cached notices 102 | $this->notices = array_filter( (array) get_transient( "wm_settings_{$this->name}_notices" ) ); 103 | 104 | // Register user defined settings 105 | $this->apply_settings( $settings ); 106 | 107 | // Record this instance 108 | self::$instances[$this->name] = $this; 109 | 110 | // Admin menu hook 111 | add_action( 'admin_init', array( $this, 'admin_init' ), 101 ); 112 | } 113 | 114 | 115 | // USER METHODS 116 | 117 | // Register settings callbacks 118 | public function apply_settings( $settings ) 119 | { 120 | if ( is_callable( $settings ) ) { 121 | add_filter( "wm_settings_{$this->name}", $settings ); 122 | } else { 123 | $this->settings += (array) $settings; 124 | } 125 | } 126 | 127 | // Get default values 128 | public function get_defaults( $section_id ) 129 | { 130 | return $this->sanitize_setting( array( 131 | 'wm_settings_defaults' => true 132 | ), $section_id ); 133 | } 134 | 135 | // Register page notice 136 | public function add_notice( $message, $type = 'info', $title = null, $backtrace = false ) 137 | { 138 | $this->notices[] = array( 139 | 'type' => $type, 140 | 'message' => self::get_notice_message( $message, $title, $backtrace ), 141 | 'setting' => $this->name, 142 | 'code' => "{$type}_notice" 143 | ); 144 | // Cache notices until they're shown 145 | set_transient( "wm_settings_{$this->name}_notices", $this->notices ); 146 | } 147 | 148 | // Register global alert 149 | public static function add_alert( $message, $type = 'error', $title = null, $backtrace = false ) 150 | { 151 | self::$alerts[] = array( 152 | 'type' => $type, 153 | 'message' => self::get_notice_message( $message, $title, $backtrace ) 154 | ); 155 | // Cache alerts until they're shown 156 | set_transient( 'wm_settings_alerts', self::$alerts ); 157 | } 158 | 159 | private static function get_notice_message( $message, $title, $backtrace ) 160 | { 161 | $message = $title ? "{$title}
{$message}" : $message; 162 | if ( $backtrace && $stack = array_slice( debug_backtrace(), 2 ) ) { 163 | if ( true === $backtrace ) { 164 | $message .= "
    "; 165 | foreach ( $stack as $i => $backtrace ) { 166 | $message .= '
  1. ' . self::get_backtrace( $backtrace ) . '
  2. '; 167 | } 168 | $message .= "
"; 169 | } else if ( ! empty( $stack[$backtrace] ) ) { 170 | $message .= self::get_backtrace( $stack[$backtrace] ); 171 | } 172 | } 173 | return $message; 174 | } 175 | 176 | private static function get_backtrace( $backtrace ) 177 | { 178 | $output = "
";
179 |             if ( ! empty( $backtrace['class'] ) ) {
180 |                 $output .= "{$backtrace['class']}";
181 |                 if ( ! empty( $backtrace['type'] ) ) {
182 |                     $output .= $backtrace['type'];
183 |                 }
184 |             }
185 |             if ( ! empty( $backtrace['function'] ) ) {
186 |                 $output .= "{$backtrace['function']}(";
187 |                 if ( ! empty( $backtrace['args'] ) ) {
188 |                     $args = implode( ', ', array_map( function ( $arg ) {
189 |                         if ( is_scalar( $arg ) ) {
190 |                             return var_export( $arg, true );
191 |                         }
192 |                         $type = gettype( $arg );
193 |                         return "{$type}";
194 |                     }, $backtrace['args'] ) );
195 |                     $output .= " {$args} ";
196 |                 }
197 |                 $output .= ");\n";
198 |             }
199 |             if ( ! empty( $backtrace['file'] ) ) {
200 |                 $output .= "In {$backtrace['file']}";
201 |                 if ( ! empty( $backtrace['line'] ) ) {
202 |                     $output .= " on line {$backtrace['line']}";
203 |                 }
204 |             }
205 |             return $output . "
"; 206 | } 207 | 208 | 209 | // WORDPRESS ACTIONS 210 | 211 | // Register Ajax Actions 212 | public static function init() 213 | { 214 | if ( defined( 'DOING_AJAX' ) && DOING_AJAX && $actions = array_filter( (array) get_transient( 'wm_settings_actions' ) ) ) { 215 | foreach ( $actions as $field_id => $action ) { 216 | add_action( "wp_ajax_{$field_id}", $action ); 217 | } 218 | } 219 | self::$alerts = array_filter( (array) get_transient( 'wm_settings_alerts' ) ); 220 | } 221 | 222 | // Register menu items 223 | public static function admin_menu() 224 | { 225 | // Public hook to register pages 226 | do_action( 'wm_settings_register_pages' ); 227 | // Add each instance menu page 228 | foreach ( self::$instances as $page ) { 229 | if ( $page->menu ) { 230 | if ( $page->menu['parent'] ) { 231 | // As child item 232 | $hookname = add_submenu_page( $page->menu['parent'], $page->title, $page->menu['title'], $page->menu['capability'], $page->name, array( $page, 'do_page' ) ); 233 | } else { 234 | // As parent item 235 | $hookname = add_menu_page( $page->title, $page->menu['title'], $page->menu['capability'], $page->name, array( $page, 'do_page' ), $page->menu['icon_url'], $page->menu['position'] ); 236 | if ( $page->title !== $page->menu['title'] ) { 237 | // "Main" child item 238 | add_submenu_page( $page->name, $page->title, $page->title, $page->menu['capability'], $page->name ); 239 | } 240 | } 241 | // Enable page display 242 | add_action( "load-{$hookname}", array( $page, 'load_page' ) ); 243 | } 244 | } 245 | } 246 | 247 | // Register settings 248 | public function admin_init() 249 | { 250 | // Reset request 251 | if ( $reset = isset( $_POST["wm_settings_reset"] ) ) { 252 | // Prepare notice 253 | $this->add_notice( __( 'Settings have been reset to their default values.', 'wm-settings' ) ); 254 | } 255 | 256 | // Prepare sections 257 | foreach ( $this->set_sections() as $id => $section ) { 258 | 259 | // Prefixed section id 260 | $setting_id = "wm_settings_{$id}"; 261 | 262 | if ( $reset ) { 263 | // Force default values 264 | $_POST[$setting_id]['wm_settings_defaults'] = true; 265 | } 266 | 267 | // Register setting 268 | register_setting( $this->name, $setting_id, array( $this, 'sanitize_setting' ) ); 269 | // Register section 270 | add_settings_section( $setting_id, $section['title'], array( $this, 'do_section' ), $this->name ); 271 | 272 | if ( ! get_option( $setting_id ) ) { 273 | // Compatibility < v1.4 274 | if ( $old_option = get_option( $id ) ) { 275 | add_option( $setting_id, $this->sanitize_setting( $old_option, $id ) ); 276 | } else { 277 | // Initialise option with default values 278 | add_option( $setting_id, $this->get_defaults( $id ) ); 279 | } 280 | } 281 | 282 | // Register fields 283 | foreach ( $section['fields'] as $name => $field ) { 284 | $field = array_merge( $field, array( 285 | 'id' => "{$id}_{$name}", 286 | 'name' => "{$setting_id}[{$name}]", 287 | 'value' => get_setting( $id, $name ), 288 | 'label_for' => $field['label'] === false 289 | ? 'hidden' 290 | : $name 291 | ) ); 292 | add_settings_field( $field['id'], $field['label'], array( __CLASS__, 'do_field' ), $this->name, $setting_id, $field ); 293 | 294 | // Update page status 295 | $this->empty = false; 296 | 297 | // Register callback for "action" field type 298 | if ( $field['type'] === 'action' && is_callable( $field['action'] ) ) { 299 | self::$actions[$field['id']] = $field['action']; 300 | } 301 | } 302 | } 303 | } 304 | 305 | // Prepare sections 306 | private function set_sections() 307 | { 308 | // Settings are registered through filters callbacks 309 | $settings = array_filter( (array) apply_filters( "wm_settings_{$this->name}", $this->settings ) ); 310 | 311 | foreach ( $settings as $id => $section ) { 312 | $this->sections[$id] = array_merge( array( 313 | 'title' => null, // Section title 314 | 'description' => null, // Section description 315 | 'fields' => array(), // Controls list 316 | 'customize' => false 317 | ), (array) $section ); 318 | 319 | // Prepare section's fields 320 | foreach ( array_filter( (array) $this->sections[$id]['fields'] ) as $name => $field ) { 321 | 322 | // Set field 323 | $this->sections[$id]['fields'][$name] = array_merge( array( 324 | 'type' => 'text', // Input type 325 | 'label' => is_string( $field ) // Field title 326 | ? $field 327 | : null, 328 | 'description' => null, // Field description 329 | 'default' => null, // Default value 330 | 'sanitize' => null, // Sanitation callback 331 | 'attributes' => array(), // HTML input attributes 332 | 'choices' => null, // Options list (for "radio", "select" or "multi" types) 333 | 'action' => null // Callback function (for "action" type) 334 | ), (array) $field ); 335 | 336 | // Compatibility < v1.5 337 | if ( null === $this->sections[$id]['fields'][$name]['choices'] && ! empty( $field['options'] ) ) { 338 | $this->sections[$id]['fields'][$name]['choices'] = $field['options']; 339 | } 340 | } 341 | } 342 | 343 | return $this->sections; 344 | } 345 | 346 | // Display global notices 347 | public static function admin_notices() 348 | { 349 | foreach ( array_map( 'unserialize', array_unique( array_map( 'serialize', self::$alerts ) ) ) as $alert ) { 350 | echo "

{$alert['message']}

"; 351 | } 352 | // Delete cached alerts 353 | delete_transient( 'wm_settings_alerts' ); 354 | } 355 | 356 | // Load page 357 | public function load_page() 358 | { 359 | // Update callback 360 | if ( isset( $_GET['settings-updated'] ) && $_GET['settings-updated'] ) { 361 | do_action( "{$this->name}_settings_updated" ); 362 | } 363 | 364 | // Record actions 365 | set_transient( 'wm_settings_actions', self::$actions ); 366 | 367 | // Enqueue page scripts 368 | add_action( 'admin_enqueue_scripts', array( __CLASS__, 'admin_enqueue_scripts' ) ); 369 | } 370 | 371 | // Page scripts and styles 372 | public static function admin_enqueue_scripts() 373 | { 374 | // Media upload 375 | wp_enqueue_media(); 376 | // Main script 377 | wp_enqueue_script( 'wm-settings', plugins_url( 'wm-settings.js' , __FILE__ ), array( 'jquery', 'wp-color-picker' ) ); 378 | // Data 379 | wp_localize_script( 'wm-settings', 'ajax', array( 380 | 'url' => admin_url( 'admin-ajax.php' ), 381 | 'spinner' => admin_url( 'images/spinner.gif' ) 382 | ) ); 383 | // Styles 384 | wp_enqueue_style( 'wm-settings', plugins_url( 'wm-settings.css' , __FILE__ ) ); 385 | wp_enqueue_style( 'wp-color-picker' ); 386 | } 387 | 388 | public static function customize_register( $wp_customize ) 389 | { 390 | // Public hook to register pages 391 | do_action( 'wm_settings_register_pages' ); 392 | 393 | foreach ( self::$instances as $page ) { 394 | 395 | foreach ( $page->set_sections() as $id => $section ) { 396 | if ( $section['customize'] ) { 397 | $wp_customize->add_section( $id , $section ); 398 | 399 | foreach ( $section['fields'] as $name => $field ) { 400 | $wp_customize->add_setting( "wm_settings_{$id}[{$name}]", array( 401 | 'default' => $field['default'], 402 | 'type' => 'option', 403 | // 'transport' => 'refresh', 404 | // 'sanitize_js_callback' => 'esc_js', 405 | 'sanitize_callback' => function ( $input ) use ( $page, $id, $name ) { 406 | return $page->sanitize_setting( array( 407 | $name => $input 408 | ), $id )[$name]; 409 | }, 410 | 'capability' => $page->menu['capability'] 411 | ) ); 412 | $args = array_merge( $field, array( 413 | 'settings' => "wm_settings_{$id}[{$name}]", 414 | 'section' => $id 415 | ) ); 416 | switch ( $field['type'] ) { 417 | case 'color': 418 | $control = new WP_Customize_Color_Control( $wp_customize, "{$id}_{$name}", $args ); 419 | break; 420 | case 'upload': 421 | $control = new WP_Customize_Upload_Control( $wp_customize, "{$id}_{$name}", $args ); 422 | break; 423 | case 'image': 424 | $control = new WP_Customize_Image_Control( $wp_customize, "{$id}_{$name}", $args ); 425 | break; 426 | case 'multi': 427 | case 'action': 428 | case 'media': 429 | self::add_alert( sprintf( __( 'Sorry but "%s" is not a valid Customize Control type quite yet.', 'wm-settings' ), $field['type'] ), 'error', __( 'WebMaestro Settings', 'wm-settings' ) ); 430 | continue; 431 | default: 432 | $control = new WP_Customize_Control( $wp_customize, "{$id}_{$name}", $args ); 433 | } 434 | $wp_customize->add_control( $control ); 435 | } 436 | } 437 | } 438 | } 439 | } 440 | 441 | 442 | // SETTINGS API CALLBACKS 443 | 444 | // Page display callback 445 | public function do_page() 446 | { ?> 447 |
448 |

title; ?>

449 | set_notices(); 452 | // Display notices 453 | settings_errors( $this->name ); 454 | if ( $text = $this->config['description'] ) { 455 | echo wpautop( $text ); 456 | } 457 | ?> 458 |
empty && $this->config['tabs'] && count( $this->sections ) > 1 ) { 461 | echo "
"; 462 | } 463 | // Display sections 464 | do_settings_sections( $this->name ); 465 | ?>
466 | empty ) { ?> 468 |

config['submit'], 'large primary', 'wm_settings_submit', false ); 471 | // Reset button 472 | if ( $this->config['reset'] ) { 473 | $confirm = esc_js( __( 'Do you really want to reset all these settings to their default values ?', 'wm-settings' ) ); 474 | submit_button( $this->config['reset'], 'small', 'wm_settings_reset', false, array( 475 | 'onclick' => "return confirm('{$confirm}');" 476 | ) ); 477 | } 478 | ?>

479 | name ); 481 | ?> 482 |
483 | notices 494 | ) ) ); 495 | // Redefine global 496 | $wp_settings_errors = array_filter( array_map( function ( $notice ) { 497 | if ( ! $notice = unserialize( $notice ) ) { 498 | return null; 499 | } 500 | // Custom updated 501 | if ( $notice['code'] === 'settings_updated' ) { 502 | if ( false === $this->config['updated'] ) { 503 | return null; 504 | } 505 | if ( $this->config['updated'] ) { 506 | $notice['message'] = (string) $this->config['updated']; 507 | } 508 | // If null : unchanged 509 | } 510 | return $notice; 511 | }, $notices ) ); 512 | // Delete cached 513 | delete_transient( 'settings_errors' ); 514 | delete_transient( "wm_settings_{$this->name}_notices" ); 515 | } 516 | 517 | // Section display callback 518 | public function do_section( $args ) 519 | { 520 | extract( $args ); 521 | $section_id = preg_replace( '/^wm_settings_/', '', $id ); 522 | echo ""; 523 | if ( $text = $this->sections[$section_id]['description'] ) { 524 | echo wpautop( $text ); 525 | } 526 | } 527 | 528 | // Control display callback 529 | public static function do_field( $args ) 530 | { 531 | extract( $args ); 532 | // HTML attributes 533 | $attrs = "name=\"{$name}\""; 534 | foreach ( $attributes as $k => $v ) { 535 | $k = sanitize_key( $k ); 536 | $v = esc_attr( $v ); 537 | $attrs .= " {$k}='{$v}'"; 538 | } 539 | // Field description "paragraphed" 540 | $desc = $description ? "

{$description}

" : ''; 541 | // Display control 542 | switch ( $type ) 543 | { 544 | // Checkbox 545 | case 'checkbox': 546 | $check = checked( 1, $value, false ); 547 | echo ""; 552 | break; 553 | // Radio options 554 | case 'radio': 555 | if ( ! is_array( $choices ) ) { 556 | _e( 'No options defined.', 'wm-settings' ); 557 | } else { 558 | echo "
"; 559 | foreach ( $choices as $v => $label ) { 560 | $check = checked( $v, $value, false ); 561 | $choices[$v] = ""; 562 | } 563 | echo implode( '
', $choices ); 564 | echo "{$desc}
"; 565 | } 566 | break; 567 | // Select options 568 | case 'select': 569 | if ( ! is_array( $choices ) ) { 570 | _e( 'No options defined.', 'wm-settings' ); 571 | } else { 572 | echo "{$desc}"; 578 | } 579 | break; 580 | // Media upload button 581 | case 'media': 582 | case 'upload': 583 | case 'image': 584 | $v = $value ? esc_attr( $value ) : ''; 585 | echo "
"; 586 | if ( 'upload' === $type ) { 587 | echo ""; 588 | } else { 589 | echo ""; 590 | $src = ( $value && 'media' === $type ) 591 | ? wp_get_attachment_image_src( $value, 'full', true )[0] 592 | : $v; 593 | echo wpautop( "" ); 594 | } 595 | $select_text = sprintf( __( 'Select %s', 'wm-settings' ), $label ); 596 | echo "

{$select_text} "; 597 | $remove_text = sprintf( __( 'Remove %s', 'wm-settings' ), $label ); 598 | echo "{$remove_text}

"; 599 | echo "{$desc}
"; 600 | break; 601 | // Text bloc 602 | case 'textarea': 603 | echo "{$desc}"; 604 | break; 605 | // Multiple checkboxes 606 | case 'multi': 607 | if ( ! is_array( $choices ) ) { 608 | _e( 'No options defined.', 'wm-settings' ); 609 | } else { 610 | echo "
"; 611 | foreach ( $choices as $n => $label ) { 612 | $a = preg_replace( "/name\=\"(.+)\"/", "name='$1[{$n}]'", $attrs ); 613 | $check = checked( 1, $value[$n], false ); 614 | $choices[$n] = ""; 615 | } 616 | echo implode( '
', $choices ); 617 | echo "{$desc}
"; 618 | } 619 | break; 620 | // Ajax action button 621 | case 'action': 622 | if ( ! is_callable( $action ) ) { 623 | _e( 'No action defined.', 'wm-settings' ); 624 | } 625 | echo "

{$desc}"; 626 | break; 627 | // Color picker 628 | case 'color': 629 | $v = esc_attr( $value ); 630 | echo "{$desc}"; 631 | break; 632 | // HTML5 input ("text", "email"...) 633 | default: 634 | $v = esc_attr( $value ); 635 | echo "{$desc}"; 636 | break; 637 | } 638 | } 639 | 640 | // Sanitize values before save 641 | public function sanitize_setting( array $inputs, $id = null ) 642 | { 643 | if ( ! $id ) { 644 | if ( ! isset( $inputs["wm_settings_id"] ) ) { 645 | return $inputs; 646 | } 647 | // Get id from hidden section input 648 | $id = $inputs["wm_settings_id"]; 649 | } 650 | 651 | $defaults = isset( $inputs["wm_settings_defaults"] ); 652 | 653 | // Array of sanitized values 654 | $values = array(); 655 | 656 | // Section's fields 657 | foreach ( $this->sections[$id]['fields'] as $name => $field ) { 658 | 659 | if ( $defaults ) { 660 | // Set default as input 661 | $input = $field['default']; 662 | } else { 663 | // Set posted input 664 | $input = isset( $inputs[$name] ) 665 | ? $inputs[$name] 666 | : null; 667 | } 668 | 669 | // "Custom" sanitation 670 | if ( $field['sanitize'] ) { 671 | $values[$name] = call_user_func( $field['sanitize'], $input, $name ); 672 | } else { 673 | 674 | // "Default" sanitation 675 | switch ( $field['type'] ) 676 | { 677 | case 'checkbox': 678 | // Boolean 679 | $values[$name] = $input ? 1 : 0; 680 | break; 681 | 682 | case 'radio': 683 | case 'select': 684 | case 'dropdown-pages': 685 | // Field option key 686 | $values[$name] = sanitize_key( $input ); 687 | break; 688 | 689 | case 'media': 690 | // Attachment id 691 | $values[$name] = absint( $input ); 692 | break; 693 | 694 | case 'color': 695 | // Hex color 696 | $values[$name] = preg_match( '/^#[a-f0-9]{6}$/', $input ) ? $input : "#808080"; 697 | break; 698 | 699 | case 'textarea': 700 | $text = ""; 701 | // Convert and keep new lines and tabulations 702 | $nl = "WM-SETTINGS-NEW-LINE"; 703 | $tb = "WM-SETTINGS-TABULATION"; 704 | $lines = explode( $nl, sanitize_text_field( str_replace( "\t", $tb, str_replace( "\n", $nl, $input ) ) ) ); 705 | foreach ( $lines as $line ) { 706 | $text .= str_replace( $tb, "\t", trim( $line ) ) . "\n"; 707 | } 708 | $values[$name] = trim( $text ); 709 | break; 710 | 711 | case 'multi': 712 | $values[$name] = array(); 713 | foreach ( $field['options'] as $n => $opt ) { 714 | $values[$name][$n] = empty( $input[$n] ) ? 0 : 1; 715 | } 716 | break; 717 | 718 | case 'action': 719 | break; 720 | 721 | case 'email': 722 | $values[$name] = sanitize_email( $input ); 723 | break; 724 | 725 | case 'url': 726 | case 'image': 727 | case 'upload': 728 | $values[$name] = esc_url_raw( $input ); 729 | break; 730 | 731 | case 'number': 732 | $values[$name] = floatval( $input ); 733 | break; 734 | 735 | default: 736 | $values[$name] = sanitize_text_field( $input ); 737 | break; 738 | } 739 | } 740 | } 741 | return $values; 742 | } 743 | 744 | 745 | // This plugin may be used by other ones, so let's try to load it first 746 | public static function plugin_priority() 747 | { 748 | $wm_settings = plugin_basename( __FILE__ ); 749 | $active_plugins = get_option( 'active_plugins' ); 750 | if ( $position = array_search( $wm_settings, $active_plugins ) ) { 751 | array_splice( $active_plugins, $position, 1 ); 752 | array_unshift( $active_plugins, $wm_settings ); 753 | update_option( 'active_plugins', $active_plugins ); 754 | } 755 | } 756 | } 757 | 758 | 759 | // ACTIONS 760 | 761 | add_action( 'activated_plugin', array( 'WM_Settings', 'plugin_priority' ) ); 762 | add_action( 'init', array( 'WM_Settings', 'init' ) ); 763 | add_action( 'admin_menu', array( 'WM_Settings', 'admin_menu' ) ); 764 | add_action( 'admin_notices', array( 'WM_Settings', 'admin_notices' ) ); 765 | add_action( 'customize_register', array( 'WM_Settings', 'customize_register' ) ); 766 | // add_action( 'wp_before_admin_bar_render', array( 'WM_Settings', 'admin_notices' ) ); 767 | } 768 | 769 | ?> 770 | --------------------------------------------------------------------------------