Overview

Namespaces

  • None
  • PHP

Classes

  • Sidecar
  • Sidecar_Admin_Page
  • Sidecar_Admin_Tab
  • Sidecar_Field
  • Sidecar_Form
  • Sidecar_Plugin_Base
  • Sidecar_Settings
  • Sidecar_Shortcode
  • Sidecar_Singleton_Base

Functions

  • body
  • format_gmt_string
  • headers
  • output_css
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  • Todo
  • Download
   1: <?php
   2: 
   3: /**
   4:  * Class Sidecar_Admin_Page
   5:  */
   6: class Sidecar_Admin_Page {
   7: 
   8:   /**
   9:    * @var Sidecar_Plugin_Base
  10:    */
  11:   var $plugin;
  12: 
  13:   /**
  14:    * @var null|array Array contains Sidecar_Form objects
  15:    */
  16:   protected $_forms = array();
  17: 
  18:   /**
  19:    * @var array
  20:    */
  21:   protected $_tabs = array();
  22: 
  23:   /**
  24:    * @var string
  25:    */
  26:   protected $_page_url;
  27: 
  28:   /**
  29:    * @var bool
  30:    */
  31:   protected $_is_page_url;
  32: 
  33:   /**
  34:    * @var Sidecar_Admin_Tab
  35:    */
  36:   protected $_authentication_tab;
  37: 
  38:   /**
  39:    * @var string
  40:    */
  41:   protected $_settings_group_name;
  42: 
  43:   /**
  44:    * @var bool
  45:    */
  46:   protected $_initialized = false;
  47: 
  48:   /**
  49:    * @var string
  50:    */
  51:   var $parent_slug = 'options-general.php';
  52: 
  53:   /**
  54:    * @var string
  55:    */
  56:   var $page_name;
  57: 
  58:   /**
  59:    * @var string
  60:    */
  61:   var $page_slug;
  62: 
  63:   /**
  64:    * @var string
  65:    */
  66:   var $page_title;
  67: 
  68:   /**
  69:    * @var string
  70:    */
  71:   var $menu_title;
  72: 
  73:   /**
  74:    * @var string
  75:    */
  76:   var $menu_page;
  77: 
  78:   /**
  79:    * @var string
  80:    */
  81:   var $capability_required = 'manage_options';
  82: 
  83:   /**
  84:    * @var string One of the built in icons (below), or a custom icon starting with 'http://' or 'https://'
  85:    * @example:
  86:    *    admin, appearance,
  87:    *    comments,
  88:    *    dashboard,
  89:    *    edit, edit-comments, edit-pages,
  90:    *    index,
  91:    *    link, links, link-category, link-manager,
  92:    *    media, ms-admin,
  93:    *    options-general,
  94:    *    page, plugins, post, profile,
  95:    *    settings, site, sitescreen,
  96:    *    themes, tools,
  97:    *    upload, user-edit, users
  98:    */
  99:   var $icon = 'options-general';  // Default
 100: 
 101:   /**
 102:    * @var string
 103:    */
 104:   protected $_auth_form = false;
 105: 
 106:   /**
 107:    * @param $page_name
 108:    * @param array $args
 109:    */
 110:   function __construct( $page_name, $args = array() ) {
 111:     $this->page_name = $page_name;
 112: 
 113:     /**
 114:      * Copy properties in from $args, if they exist.
 115:      */
 116:     foreach( $args as $property => $value )
 117:       if ( property_exists(  $this, $property ) )
 118:         $this->$property = $value;
 119: 
 120:     /**
 121:      * Check $this->plugin first so we don't couple these if we don't have to.
 122:      */
 123:     if ( $this->plugin instanceof Sidecar_Plugin_Base ) {
 124:       if ( ! $this->page_title )
 125:         $this->page_title = $this->plugin->plugin_label;
 126: 
 127:       if ( ! $this->menu_title )
 128:         $this->menu_title = $this->plugin->plugin_label;
 129: 
 130:       if ( ! $this->page_slug )
 131:         $this->page_slug = "{$this->plugin->plugin_slug}-{$page_name}";
 132:     }
 133: 
 134:     add_action( 'admin_menu', array( $this, 'admin_menu' ) );
 135: 
 136:   }
 137: 
 138:   /**
 139:    * @throws Exception
 140:    */
 141:   function initialize() {
 142:     $this->_do_plugin_action( 'initialize_admin_page', $this );
 143:     $current_tab = $this->has_tabs() ? $this->get_current_tab() : false;
 144:     if ( $current_tab && $current_tab->has_forms() ) {
 145:       register_setting( $this->plugin->option_name, $this->plugin->option_name, array( $this, 'filter_postback' ) );
 146:       $plugin = $this->plugin;
 147:       /**
 148:        * Sidecar_Form $form
 149:        */
 150:       foreach( $current_tab->forms as $form ) {
 151:         if ( $plugin->has_form( $form ) ) {
 152:           $form = $this->plugin->get_form( $form );
 153:           $form->admin_page = $this;
 154:           $form->initialize_sections( $this->plugin );
 155:           $form->initialize_buttons( $this->plugin );
 156:           $this->_forms[$form->form_name] = $form;
 157:         }
 158:       }
 159:     }
 160:     $this->_initialized = true;
 161:   }
 162:   /**
 163:    * @param array $input
 164:    *
 165:    * @return array
 166:    */
 167:   function filter_postback( $input ) {
 168:     static $called_already;
 169:     if ( isset( $called_already ) || empty( $_POST ) ) {
 170:       /**
 171:        * When using the Settings API this filter will be called twice when the option needs to be added.
 172:        * This happens because of how WordPress is implemented and not something we can control.
 173:        * IOW, it's a hack but not a hack we can avoid unless WordPress makes changes.
 174:        */
 175:       return $input;
 176:     }
 177:     $unfiltered_input = $input;
 178:     $called_already = true;
 179:     if ( ! current_user_can( 'manage_options' ) ) {
 180:       /**
 181:        * TODO: Verify someone without proper options can actually get here.
 182:        */
 183:       wp_die( __( 'Sorry, you do not have sufficient priviledges.' ) );
 184:     }
 185: 
 186:     $this->_do_plugin_action( 'initialize_postback' );
 187: 
 188:     /**
 189:      * Get the array that contains names of 'plugin', 'page', 'tab', 'form' and 'settings'
 190:      * as well as special 'clear' and 'reset' for clearing and resetting the form respectively.
 191:      */
 192:     $post_values = $_POST[$_POST['option_page']];
 193:     $this->plugin->set_current_admin_page( $this );
 194:     $form = $this->plugin->get_form( $post_values['state']['form'] );
 195:     $this->plugin->set_current_form( $form );
 196: 
 197:     $form_values = $input[$form->form_name];
 198:     /**
 199:      * Check with the API to see if we are authenticated
 200:      * @var RESTian_Client $api
 201:      */
 202:     $api = $this->plugin->get_api();
 203:     if ( $api && ( $this->is_authentication_tab() || ! $this->has_tabs() ) && $form == $this->get_auth_form() ) {
 204:       if ( ! $api->is_credentials( $form_values ) ) {
 205:         add_settings_error( $this->plugin->option_name, 'sidecar-no-credentials', $api->get_message() );
 206:       } else {
 207:         /**
 208:          * @var RESTian_Response
 209:          */
 210:         $response = $api->authenticate( $form_values );
 211:         if ( $response->has_error() ) {
 212:           $form_values['authenticated'] = false;
 213:           if ( ! ( $message = $response->get_error()->message ) ) {
 214:             $message = 'Please try again.';
 215:           };
 216:           add_settings_error( $this->plugin->option_name, 'sidecar-not-authenticated', __( "Authentication Failed. {$message}", 'sidecar' ) );
 217:         } else {
 218:           $form_values = array_merge( $form_values, $response->grant );
 219:           $form_values['authenticated'] = true;
 220:           $message = $this->_apply_plugin_filter( 'filter_authentication_success_message', __( 'Authentication successful. Settings saved.', 'sidecar' ) );
 221:           if ( $message )
 222:             add_settings_error( $this->plugin->option_name, 'sidecar-authenticated', $message, 'updated' );
 223:         }
 224:       }
 225:     }
 226:     //$this->plugin->set_api( $api );
 227: 
 228:     if ( isset( $post_values['action']['clear'] ) ) {
 229:       $form_values = $form->get_empty_settings_values();
 230:       $message = __( 'Form values cleared.%s%sNOTE:%s Your browser may still be displaying values from its cache but this plugin has indeed cleared these values.%s', 'sidecar' );
 231:       add_settings_error( $this->plugin->option_name, "sidecar-clear", sprintf( $message, "<br/><br/>&nbsp;&nbsp;&nbsp;", '<em>', '</em>', '<br/><br/>' ), 'updated' );
 232:     } else if ( isset( $post_values['action']['reset'] ) ) {
 233:       $form_values = $this->plugin->get_current_form()->get_default_settings_values();
 234:       add_settings_error( $this->plugin->option_name, 'sidecar-reset', __( 'Defaults reset.', 'sidecar' ), 'updated' );
 235:     } else {
 236:       $form_values = array_map( 'rtrim', (array)$form_values );
 237:       add_filter( $action_key = "pre_update_option_{$this->plugin->option_name}", array( $this->plugin, '_pre_update_option' ), 10, 2 );
 238:       /**
 239:        * @todo How to signal a failed validation?
 240:        */
 241:       $form_values = $this->_apply_plugin_filter( 'validate_settings_values', $form_values, $form );
 242:       /**
 243:        * @var Sidecar_Field $field
 244:        */
 245:       foreach( $form->get_fields() as $field_name => $field ) {
 246:         $validation_options = false;
 247:         if ( $field->field_allow_html )
 248:           $form_values[$field_name] = htmlentities( $form_values[$field_name] );
 249:             /**
 250:              * Default to FILTER_SANITIZE_STRING if ['validator'] not set.
 251:              */
 252:             if ( $field->field_options ) {
 253:           $validated_value = isset( $field->field_options[$form_values[$field_name]] ) ? $form_values[$field_name] : false;
 254:         } else if ( isset( $field->field_validator['filter'] ) ) {
 255:            $validated_value = filter_var( $form_values[$field_name], $field->field_validator['filter'] );
 256:            if ( isset( $field->field_validator['options'] ) ) {
 257:             $validation_options = $field->field_validator['options'];
 258:            }
 259:         } else {
 260:           $validator = $field->field_validator ? $field->field_validator : FILTER_SANITIZE_STRING;
 261:           $validated_value = filter_var( $form_values[$field_name], $validator );
 262:         }
 263:         $validated_value = $this->_apply_plugin_filter( "sanitize_setting_{$field_name}", $validated_value, $field, $form );
 264:         if ( $validation_options || $validated_value != $form_values[$field_name] ) {
 265:           if ( ! $validation_options ) {
 266:             add_settings_error( $this->plugin->option_name, 'sidecar-value', sprintf(
 267:               __( 'Please enter a valid value for "%s."', 'sidecar' ), $field->field_label
 268:             ));
 269:           } else {
 270:             if ( isset( $validation_options['min'] ) && $validation_options['min'] > intval( $form_values[$field_name] ) ) {
 271:               add_settings_error( $this->plugin->option_name, 'sidecar-min', sprintf(
 272:                 __( 'Please enter a value greater than or equal to %d for "%s."', 'sidecar' ),
 273:                   $validation_options['min'],
 274:                   $field->field_label
 275:               ));
 276:             }
 277:             if ( isset( $validation_options['max'] ) && $validation_options['max'] < intval( $form_values[$field_name] ) ) {
 278:               add_settings_error( $this->plugin->option_name, 'sidecar-max', sprintf(
 279:                 __( 'Please enter a value less than or equal to %d for "%s."', 'sidecar' ),
 280:                   $validation_options['max'],
 281:                   $field->field_label
 282:               ));
 283:               $continue = true;
 284:             }
 285:           }
 286:         }
 287:       }
 288:     }
 289: 
 290:     $form_values = $this->_apply_plugin_filter( $method_name = "process_form_{$form->form_name}", $form_values );
 291:     if ( method_exists( $this->plugin, $method_name ) ) {
 292:       /**
 293:        * This presumes that "process_form_{$form->form_name}" uses the $api.
 294:        * We may need to make it a bit more generic, i.e. allow setting a message on the plugin
 295:        * and then our process form would need to set the plugin's message.
 296:        */
 297:       if ( ! empty( $api->response->message ) ) {
 298:         $message_type = $api->response->has_error() ? 'error' : 'updated';
 299:         add_settings_error( $this->plugin->option_name, "sidecar-form-processed-{$form->form_name}", $api->response->message, $message_type );
 300:       }
 301:     }
 302: 
 303: 
 304:     $input[$form->form_name] = $form_values;
 305: 
 306:     $input = $this->_apply_plugin_filter( 'filter_postback', $input );
 307: 
 308:     $postback_info = (object)array(
 309:       'admin_page' => $this,
 310:       'form' => $form,
 311:       'form_values' => $form_values,
 312:       'input' => $input,
 313:       'unfiltered' => $unfiltered_input
 314:     );
 315: 
 316:     $this->_do_plugin_action( "set_postback_{$form->form_name}_{$this->page_name}_message", $postback_info );
 317:     $this->_do_plugin_action( 'set_postback_message', $this, $form, $postback_info );
 318: 
 319:     return $input;
 320:   }
 321: 
 322: 
 323:   /**
 324:    * @return array
 325:    */
 326:   function get_auth_credentials() {
 327:     return $this->get_auth_form()->get_settings_values();
 328:   }
 329: 
 330:   /**
 331:    * @return Sidecar_Form
 332:    */
 333:   function get_auth_form() {
 334:     return $this->plugin->get_form( $this->_auth_form );
 335:   }
 336: 
 337:   /**
 338:    * @param string|Sidecar_Form $form
 339:    */
 340:   function set_auth_form( $form ) {
 341:     if ( is_string( $form ) ) {
 342:       $this->_auth_form = $form;
 343:     } else if ( isset( $form->form_name ) ) {
 344:       $this->_auth_form = $form->form_name;
 345:     } else if ( WP_DEBUG ) {
 346:       $message = __( '%s->set_auth_form() must be passed a string, an array with a \'form_name\' element or an object with a \'form_name\' property.', 'sidecar' );
 347:       trigger_error( sprintf( $message, $this->plugin_class ) );
 348:     }
 349:   }
 350: 
 351:   /**
 352:    * @param string $tab_slug
 353:    * @param string $tab_text
 354:    * @param array $args
 355:    */
 356:   function add_tab( $tab_slug, $tab_text, $args = array() ) {
 357:     $args['plugin'] = $this->plugin;
 358:     if ( ! $this->_auth_form && 'account' == $tab_slug )
 359:       $this->_auth_form = 'account';
 360:     $this->_tabs[$tab_slug] = $tab = new Sidecar_Admin_Tab( $tab_slug, $tab_text, $args );
 361:     }
 362: 
 363:   /**
 364:    * @return Sidecar_Admin_Tab
 365:    */
 366:   function get_default_tab() {
 367:     return reset( $this->_tabs );
 368:   }
 369: 
 370:   /**
 371:    * Test to see if the current tab is the authentication tab
 372:    *
 373:    * @return bool
 374:    */
 375:   function is_authentication_tab() {
 376:     $tab = $this->get_authentication_tab();
 377:     return is_object( $tab ) && $tab === $this->get_current_tab();
 378:   }
 379:   /**
 380:    * @return Sidecar_Admin_Tab
 381:    */
 382:   function get_authentication_tab() {
 383:     if ( ! $this->_authentication_tab ) {
 384:       /**
 385:        * @var Sidecar_Admin_Tab $tab
 386:        */
 387:       foreach( $this->_tabs as $tab ) {
 388:         if ( in_array( $this->_auth_form, $tab->forms ) ) {
 389:           $this->_authentication_tab = $tab;
 390:           break;
 391:         }
 392:       }
 393:     }
 394:     return $this->_authentication_tab;
 395:   }
 396: 
 397:   /**
 398:    * Filters a value by calling a method in the plugin.
 399:    *
 400:    * Captures whatever additional parameters and passes them on.
 401:    *
 402:    * @param string $method
 403:    *
 404:    * @return bool|mixed
 405:    */
 406:   protected function _apply_plugin_filter( $method ) {
 407:     $args = func_get_args();
 408:     array_shift( $args );
 409:     $result = $args[0];
 410:     if ( method_exists( $this->plugin, $method ) )
 411:       $result = call_user_func_array( array( $this->plugin, $method ), $args );
 412:     return $result;
 413:   }
 414:   /**
 415:    * Executes an action by calling a method in the plugin.
 416:    *
 417:    * Captures whatever additional parameters and passes them on.
 418:    *
 419:    * @param string $method
 420:    *
 421:    * @return bool|mixed
 422:    */
 423:   protected function _do_plugin_action( $method ) {
 424:     $result = false;
 425:     $args = func_get_args();
 426:     array_shift( $args );
 427:     if ( method_exists( $this->plugin, $method ) )
 428:       $result = call_user_func_array( array( $this->plugin, $method ), $args );
 429:     return $result;
 430:   }
 431:   /**
 432:    * Retrieves a value from the plugin.
 433:    *
 434:    * Captures whatever additional parameters and passes them on.
 435:    *
 436:    * @param string $method
 437:    *
 438:    * @return bool|mixed
 439:    */
 440:   protected function _get_plugin_value( $method ) {
 441:     return call_user_func_array( array( $this, '_do_plugin_action' ), func_get_args() );
 442:   }
 443: 
 444:   /**
 445:    *
 446:    */
 447:   function admin_menu() {
 448: 
 449:     if ( $this->is_postback_update() )
 450:       return;
 451: 
 452:     if ( $this->is_page_url() ) {
 453:       /**
 454:        * Call the plugin's $this->initialize_admin_page() method, if it exists.
 455:        */
 456:       $this->initialize();
 457: 
 458:       /**
 459:        * Call the plugin's $this->verify_current_tab() method, if it exists.
 460:        * Return true if all is okay and you don't want to run the default,
 461:        * return false if you want to run the default, or
 462:        * redirect and exit inside the method.
 463:        */
 464:       $this->_get_plugin_value( 'verify_current_tab', $this );
 465:     }
 466: 
 467:     /**
 468:      * Add in menu option, if one doesn't already exist
 469:      */
 470:     $this->menu_page = add_submenu_page(
 471:           $this->parent_slug,
 472:       $this->page_title,
 473:       $this->menu_title,
 474:       $this->capability_required,
 475:       $this->page_slug,
 476:       array( $this, 'the_page' )
 477:     );
 478: 
 479:     add_action( "admin_print_styles-{$this->menu_page}", array( $this, 'admin_print_styles' ));
 480:     }
 481: 
 482:   /**
 483:    *
 484:    */
 485:   function admin_print_styles() {
 486:     $plugin = $this->plugin;
 487:       $localfile = 'css/admin-style.css';
 488:       $filepath = "{$plugin->plugin_path}/{$localfile}";
 489:       if ( file_exists( $filepath ) ) {
 490:           wp_enqueue_style( "{$plugin->plugin_name}_admin_styles", plugins_url( $localfile, $plugin->plugin_file ) );
 491:      }
 492:     }
 493: 
 494:   /**
 495:    * @todo Call this from HEAD instead of from here.
 496:    */
 497:   function the_css() {
 498:     $css = $this->_get_plugin_value( 'get_admin_page_css', $this );
 499:     if ( $css ) {
 500:       $css_html =<<<HTML
 501: <style type="text/css">
 502: {$css}
 503: </style>
 504: HTML;
 505:       echo $css_html;
 506:     }
 507:   }
 508:   /**
 509:      * Displays admin page
 510:      */
 511:     function the_page() {
 512:      /**
 513:       * @todo Call this from HEAD instead of from here.
 514:       */
 515:     $this->the_css();
 516:     $tab = $this->get_current_tab();
 517:     $tab_class= $tab ? " tab-{$tab->tab_slug}" : false;
 518:     $id = rtrim( "{$this->page_slug}-" . ltrim( $tab->tab_slug ), '-' );
 519:       echo <<<HTML
 520: \n<div id="{$id}" class="wrap {$this->plugin->css_base}-admin {$this->page_name}-page{$tab_class}">
 521: HTML;
 522:       $this->the_icon();
 523:     $this->the_title_and_tabs( $tab );
 524:     $this->the_page_content();
 525:       echo "\n" . '</div>';
 526:     }
 527: 
 528:   /**
 529:    * @param string $tab
 530:    * @param string $content_type
 531:    * @param array $args
 532:    */
 533:   function the_tab_specific_content( $tab, $content_type, $args = array() ) {
 534:     $args = wp_parse_args( $args, array( 'wrap' => true ) );
 535:     $content = false;
 536:     if ( ! $tab || ! isset( $tab->{$content_type} ) )
 537:       return;
 538:     if ( is_string( $tab->{$content_type} ) ) {
 539:       $content = $tab->{$content_type};
 540:     } else {
 541:       $handler = $tab->{$content_type};
 542:       if ( ! is_callable( $tab->{$content_type} ) ) {
 543:         $method = "the_{$this->page_name}_{$tab->tab_slug}_tab_{$content_type}";
 544:         $handler = method_exists( $this->plugin, $method ) ? array( $this->plugin, $method ) : false;
 545:       }
 546:       if ( $handler ) {
 547:         ob_start();
 548:         call_user_func( $handler, $this, $tab );
 549:         $content = ob_get_clean();
 550:       }
 551:     }
 552:     if ( $content ) {
 553:       if ( $args['wrap'] ) {
 554:         $content_type_slug = str_replace( '_', '-', $content_type );
 555:         $content =<<< HTML
 556: <div id="tab-{$content_type_slug}">
 557: {$content}
 558: </div>
 559: HTML;
 560:       }
 561:       echo $content;
 562:     }
 563:   }
 564:   /**
 565:    *
 566:    */
 567:   function the_page_content() {
 568:     echo <<<HTML
 569: <div id="admin-content">
 570: HTML;
 571:     /**
 572:      * @var bool|Sidecar_Admin_Tab
 573:      */
 574:     $current_tab = $this->has_tabs() ? $this->get_current_tab() : false;
 575: 
 576:     $this->the_tab_specific_content( $current_tab, 'before_page_title', array( 'wrap' => false ) );
 577: 
 578:     if ( $current_tab && $current_tab->page_title ) {
 579:       echo "<h1 class=\"admin-page-title\">";
 580:       echo "\n" . $current_tab->page_title;
 581:       echo "\n" . '</h1>';
 582:     }
 583: 
 584:     $this->the_tab_specific_content( $current_tab, 'before_content' );
 585: 
 586:     if ( $current_tab && $current_tab->tab_handler ) {
 587:       $handler =  $current_tab->tab_handler;
 588:       if ( ! is_callable( $handler ) ) {
 589:         $method = $handler;
 590:         if ( is_array( $method ) ){
 591:           $method = ( is_string( $handler[0] ) ? "{$handler[0]}::" : get_class( $handler[0] ) .'->' ) . $handler[1];
 592:         }
 593:         $message = __( '%s provided as %s for admin tab %s of admin page %s is not a valid callable.', 'sidecar' );
 594:         Sidecar::show_error( $message,
 595:           "<strong><code>{$method}()</code></strong>",
 596:           "<strong><code>tab_handler</code></strong>",
 597:           "<strong><em>\"{$current_tab->tab_slug}\"</em></strong>",
 598:           "<strong><em>\"{$this->page_name}\"</em></strong>"
 599:           );
 600:       }
 601:     } else {
 602:       $page_name = str_replace( '-', '_', $this->page_name );
 603:       $handler = array( $this->plugin, "the_{$page_name}_admin_page" );
 604:       if ( $current_tab ) {
 605:         $tab_slug = str_replace( '-', '_', $current_tab->tab_slug );
 606:         $tab_handler = array( $this->plugin, "the_{$this->page_name}_{$tab_slug}_tab" );
 607:         /**
 608:          * Fallback to page handler if tab handler is not callable
 609:          */
 610:         $handler = is_callable( $tab_handler ) ? $tab_handler : $handler;
 611:       }
 612:       if ( ! is_callable( $handler ) && $this->plugin->has_form( $current_tab->tab_slug ) ) {
 613:         /*
 614:          * If we have no handler but do have a form with same name as the tab slug, show it.
 615:          */
 616:         $handler = array( $this->plugin->get_form( $current_tab->tab_slug ), 'the_form' );
 617:       }
 618:       if ( ! is_callable( $handler ) ) {
 619:         if ( isset( $tab_handler ) )
 620:           /**
 621:            * If it was a tab then report the more specific function as the one we are missing
 622:            */
 623:           $handler = $tab_handler;
 624:         $message = __( 'No method named %s defined yet for %s.', 'sidecar' );
 625:         Sidecar::show_error( $message,
 626:           "<strong><code>{$handler[1]}()</code></strong>",
 627:           "<strong><code>{$this->plugin->plugin_class}</code></strong>"
 628:           );
 629:       }
 630:     }
 631:     if ( is_callable( $handler ) ) {
 632:       $this->_do_plugin_action( 'initialize_tab', $current_tab, $this );
 633:       call_user_func( $handler, $this );
 634:     }
 635: 
 636:     $this->the_tab_specific_content( $current_tab, 'after_content' );
 637:     echo '</div>';
 638:   }
 639:   /**
 640:    * Displays the icon for the plugin page
 641:    *
 642:    * @todo Research to see if we need to support something other than icon32
 643:    *
 644:    */
 645:   function the_icon() {
 646:     if ( $icon_html = $this->_get_plugin_value( 'get_icon_html', $this ) ) {
 647:       $icon_html = <<<HTML
 648: <div class="icon32">{$icon_html}</div>
 649: HTML;
 650:     } else if ( $this->icon ) {
 651:       /**
 652:        * $this->icon contains a URL for an image.
 653:        */
 654:       if ( preg_match( '#^https?://#', $this->icon, $m ) ) {
 655:         list( $width, $height ) = explode( 'x', $this->_apply_plugin_filter( 'get_icon_dimensions', '36x34' ) );
 656:         $icon_html_fragment =<<<HTML
 657: ><img height="{$height}" style="background:none;" width="{$width}" src="{$this->icon}>
 658: HTML;
 659:       } else {
 660:         /**
 661:          * This means $this->icon contains an idea value such as
 662:          */
 663:         $icon_html_fragment = " id=\"icon-{$this->icon}\"><br/>";
 664:       }
 665:       /**
 666:        * Note that there is no trailing ">" on the first <div>.
 667:        * Instead, the $icon_html_fragment contains it.
 668:        */
 669:       $icon_html = <<<HTML
 670: <div class="icon32"{$icon_html_fragment}</div>
 671: HTML;
 672:     }
 673:     if ( $icon_html )
 674:       echo $icon_html;
 675:   }
 676:   /**
 677:    *
 678:    */
 679:   function get_tabs() {
 680:       return $this->_tabs;
 681:     }
 682: 
 683:   /**
 684:    * @param string $tab_name
 685:    *
 686:    * @return bool|Sidecar_Admin_Tab
 687:    */
 688:   function get_tab( $tab_name ) {
 689:       return isset( $this->_tabs[$tab_name] ) ? $this->_tabs[$tab_name] : false;
 690:     }
 691:   /**
 692:    *
 693:    */
 694:   function has_tabs() {
 695:       return 0 < count( $this->_tabs );
 696:     }
 697: 
 698:   /**
 699:    * Display the row of tabs at the top of a page with the <h2> tab wrapper element
 700:    */
 701:   function the_title_and_tabs() {
 702:     if ( $this->page_title || $this->has_tabs() ) {
 703:       echo "\n" . '<h2 class="nav-tab-wrapper">';
 704:       if ( $this->page_title )
 705:         echo "\n<span class=\"admin-page-title\">{$this->page_title}</span>";
 706:       if ( $this->has_tabs() )
 707:         echo "\n" . $this->get_tabs_html();
 708:       echo "\n" . '</h2>';
 709:     }
 710:     }
 711: 
 712:   /**
 713:    * Returns the tabs as a block of HTML for display at the top of the admin page.
 714:    *
 715:    * @return string
 716:    */
 717:   function get_tabs_html() {
 718:       return implode( "\n", $this->get_tab_links_html() );
 719:     }
 720: 
 721:   /**
 722:    * Returns an array of HTML for each tabs for display at the top of the admin page.
 723:    *
 724:    */
 725:   function get_tab_links_html() {
 726:     $links_html = array();
 727:     $current_tab = $this->get_current_tab();
 728:     if ( $current_tab ) {
 729:       foreach ( $this->get_tabs() as $tab_slug => $tab ) {
 730:         $class = ( $tab_slug == $current_tab->tab_slug ) ? ' nav-tab-active' : '';
 731:         $url = $this->get_tab_url( $tab_slug );
 732:         $links_html[] =<<<HTML
 733:   <a class="nav-tab{$class}" href="{$url}">{$tab->tab_text}</a>
 734: HTML;
 735:       }
 736:     }
 737:     return $links_html;
 738:   }
 739: 
 740: 
 741:   /**
 742:    * @return string|void
 743:    */
 744:   function get_page_url() {
 745:     return $this->get_tab_url(null);
 746:     }
 747: 
 748:   /**
 749:    * @return string|void
 750:    */
 751:   /**
 752:    * @param null|bool|string|Sidecar_Admin_Tab $tab
 753:    * @param string $text
 754:    * @param bool $blank
 755:    *
 756:    * @return string|void
 757:    */
 758:   function get_tab_link( $tab, $text, $blank = false ) {
 759:     $url = $this->get_tab_url( $tab );
 760:     if ( $blank )
 761:       $blank = ' target="_blank"';
 762:     return <<<HTML
 763: <a{$blank} href="{$url}">{$text}</a>
 764: HTML;
 765:     }
 766: 
 767:   /**
 768:    * @param null|bool|string|Sidecar_Admin_Tab $tab If null is passed then don't add a default tab.
 769:    * @return string|void
 770:    */
 771:   function get_tab_url( $tab = false ) {
 772:     if ( ! $this->_initialized ) {
 773:       $message = __( '%s->get_page_url() cannot be called prior to %s->initialize_admin_page() being called.', 'sidecar' );
 774:       Sidecar::show_error( $message, __CLASS__, $this->plugin->plugin_class );
 775:     }
 776: 
 777:     if ( $tab instanceof Sidecar_Admin_Tab )
 778:       $tab = $tab->tab_slug;
 779:     if ( $this->has_tabs() ) {
 780:       if ( isset( $this->_page_url[$tab] ) ) {
 781:         $url = $this->_page_url[$tab];
 782:       } else {
 783:         if ( false === $tab )
 784:           $tab = $this->get_default_tab()->tab_slug;
 785:         $url = $this->_page_url[$tab] = $this->get_base_page_url() . ( is_null($tab) ? '' : "&tab={$tab}" );
 786:       }
 787:       ;
 788:     } else {
 789:       if ( isset( $this->_page_url ) ) {
 790:         $url = $this->_page_url;
 791:       } else {
 792:         $url = $this->_page_url = $this->get_base_page_url();
 793:       }
 794:     }
 795:     return $url;
 796:     }
 797: 
 798:   /**
 799:    * @return string|void
 800:    */
 801:   function get_base_page_url() {
 802:     return admin_url( "{$this->parent_slug}?page={$this->page_slug}" );
 803:   }
 804: 
 805:   /**
 806:      * Check if the passed $tab variable matches the URL's tab parameter.
 807:      *
 808:      * @param string $tab
 809:      * @return bool
 810:      */
 811:     function is_current_tab( $tab ) {
 812:         /**
 813:          * IF the the current page has a valid tab,
 814:          * AND the URL's 'tab' parameter matches the function's $tab parameter
 815:          * THEN *YES*, it's the tab specified
 816:          */
 817:         return $this->is_current_tab_valid() && $tab == $_GET['tab'];
 818:     }
 819: 
 820:   /**
 821:      * Check if the passed $tab variable matches the URL's tab parameter.
 822:      *
 823:      * @return Sidecar_Admin_Tab
 824:      */
 825:     function get_current_tab() {
 826:       static $current_tab;
 827:       if ( ! isset( $current_tab ) ) {
 828:       $tab_slug = false;
 829:       if ( isset( $_GET['tab'] ) ) {
 830:          $tab_slug = $_GET['tab'];
 831:       } else if ( isset( $_POST['option_page'] ) && isset( $_POST[$_POST['option_page']]['state']['tab'] )) {
 832:         /*
 833:          * This is used during HTTP postback from an admin form.
 834:          * The <input type="hidden" name="option_page"> added by settings_fields() and
 835:          * referencing <input type="hidden" name="{option_page}[tab]"> generated by $form->get_html()
 836:          */
 837:         $tab_slug = $_POST[$_POST['option_page']]['state']['tab'];
 838:       }
 839:       $current_tab = $tab_slug && isset( $this->_tabs[$tab_slug] ) ? $this->_tabs[$tab_slug] : reset( $this->_tabs );
 840:     }
 841:    return $current_tab;
 842:   }
 843: 
 844:   /**
 845:      * Check if the passed $tab variable matches the URL's tab parameter.
 846:      *
 847:      * @param string|Sidecar_Admin_Tab $tab
 848:      * @return bool
 849:      */
 850:     function has_tab( $tab ) {
 851:       if ( isset( $tab->tab_slug ) ) {
 852:         $tab = $tab->tab_slug;
 853:      }
 854:         return isset( $this->_tabs[$tab] );
 855:     }
 856: 
 857:   /**
 858:      *
 859:      * @return int
 860:      */
 861:     function tab_count() {
 862:         return count( $this->_tabs );
 863:     }
 864: 
 865:   /**
 866:    * Validates to ensure that we have a URL tab parameter that is one of the valid tabs.
 867:    *
 868:    * @return bool
 869:    */
 870:   function is_current_tab_valid() {
 871:     return $this->has_tab( $this->get_current_tab() ) && $this->is_page_url();
 872:   }
 873: 
 874:   /**
 875:      * Check to see if we are on the admin URL for this plugin.
 876:      *
 877:      * @return bool
 878:      */
 879:     function is_page_url() {
 880:         if ( ! isset( $this->_is_page_url ) ) {
 881:             $base_url = $this->get_base_page_url();
 882:             /*
 883:              * Test to see if the left-most characters of this URL match the base URL, i.e. this is match
 884:              *
 885:              *  base_url = http://example.com/wp/wp-admin/options-general.php?page=my-plugin-settings
 886:              *  this_url = http://example.com/wp/wp-admin/options-general.php?page=my-plugin-settings OR
 887:              *  this_url = http://example.com/wp/wp-admin/options-general.php?page=my-plugin-settings&tab=support
 888:              *
 889:              * This is NOT a match:
 890:              *
 891:              *  base_url = http://example.com/wp/wp-admin/options-general.php?page=my-plugin-settings
 892:              *  this_url = http://example.com/wp/wp-admin/options-general.php OR
 893:              *  this_url = http://example.com/wp/wp-admin/edit-comments.php OR
 894:              *  this_url = http://example.com/wp/wp-admin/
 895:              *
 896:              */
 897:       $this->_is_page_url = $base_url == substr( Sidecar::this_url(), 0, strlen( $base_url ) );
 898:         }
 899:         return $this->_is_page_url;
 900:     }
 901: 
 902:   /**
 903:    * @return bool
 904:    */
 905:   function is_postback_update() {
 906:     global $pagenow;
 907:     $is_postback_update = false;
 908:     if ( isset( $_POST['action'] ) && 'update' == $_POST['action'] && 'options.php' == $pagenow ) {
 909:       $this->initialize();
 910:       $is_postback_update = isset( $_POST[$this->plugin->option_name] );
 911:     }
 912:     return $is_postback_update;
 913:   }
 914: 
 915:   /**
 916:      * Check to make sure we are on the right tab.
 917:      *
 918:      * Redirect if we are not on the right tab based on authentication status or invalid tab,
 919:      * OR return if not even on this plugin's Admin URL
 920:      * Register an error message for later display if not authenticated.
 921:      *
 922:      * @return bool Returns true if we are on a verified tab, false or URL redirect otherwise.
 923:      */
 924:     function verify_current_tab() {
 925:      /*
 926:       * If we have no tabs trying to verify is a moot point. Punt.
 927:       */
 928:     if ( ! $this->has_tabs() )
 929:       return true;
 930: 
 931:     $needs_grant = $this->plugin->needs_grant();
 932:     $has_grant = $this->plugin->has_grant();
 933: 
 934:         if ( ! $this->is_current_tab_valid() ) {
 935:             /**
 936:              * If we don't have a valid tab and we are using an API, redirect to first tab if we have an grant or authentication tab if not.
 937:        *
 938:        * We redirect to avoid having multiple URLs mean the same thing which would not be optimal for bookmarking, caching, etc.
 939:              */
 940:             if ( $needs_grant && $has_grant ) {
 941:                 /**
 942:                  * If authenticated we redirect with a "301 - This URL has changed" status code so the browser can know
 943:                  * to go to 'usage' whenever is sees this URL and avoid the round trip next time.
 944:                  */
 945:                 wp_safe_redirect( $this->get_tab_url( $this->get_default_tab()->tab_slug ), 301 );
 946:         exit;
 947:             } else if ( $needs_grant && $auth_tab = $this->get_authentication_tab() ) {
 948:                 /**
 949:                  * If not authenticated we redirect with a "302 - This URL has moved temporarily" status code
 950:                  * because normally we'd want to go to usage, so don't cause browser to thing this URL w/o a
 951:                  * valid tab should always go to 'account.'
 952:                  */
 953:                 wp_safe_redirect( $this->get_tab_url( $auth_tab->tab_slug ), 302 );
 954:         exit;
 955:             }
 956:         } else if ( $needs_grant && ! $has_grant ) {
 957:             /**
 958:              * If we are using an API but we don't have a grant
 959:              */
 960:       $auth_tab = $this->get_authentication_tab();
 961:        /**
 962:         * If there is a tab and we are not already on the authentication tab
 963:         */
 964:        if ( $auth_tab && ( ! isset( $_GET['tab'] ) || $auth_tab->tab_slug != $_GET['tab'] ) ) {
 965:                 /**
 966:                  * ...and we are NOT on the authentication tab then redirect to the 'account' tab.
 967:                  *
 968:                  * We redirect with a "302 - This URL has moved temporarily" because it's still a good URL and
 969:                  * we want the browser to be happy to return here later.
 970:                  */
 971:                 wp_safe_redirect( $this->get_tab_url( $auth_tab ), 302 );
 972:                 exit;
 973:             } else {
 974:                 /**
 975:                  * ...and we ARE on the authentication tab then prepare a "Need to authenticate" message for later display.
 976:                  */
 977:                 add_settings_error(
 978:                     $this->plugin->plugin_slug, // @todo Switch to $this->form_name,
 979:                     'need-info',
 980:                     __( 'You must have an account to use this plugin.  Please enter your credentials.', 'sidecar' )
 981:                 );
 982:             }
 983:         }
 984:         return true;
 985:     }
 986:   /**
 987:    * @param $property_name
 988:    * @return bool|string
 989:    */
 990:   function __get( $property_name ) {
 991:     $value = false;
 992:     if ( preg_match( '#^(.+?)_tab_url$#', $property_name, $match ) && $this->has_tab( $match[1] ) ) {
 993:       /**
 994:        * Allows magic property syntax for any registered URL
 995:        * @example: $this->foobar_url calls $this-get_url( 'foobar' )
 996:        * Enables embedding in a HEREDOC or other doublequoted string
 997:        * without requiring an intermediate variable.
 998:        */
 999:       $value = call_user_func( array( $this, "get_tab_url" ), $match[1] );
1000:     } else {
1001:       Sidecar::show_error( 'No property named %s on %s class.', $property_name, get_class( $this ) );
1002:     }
1003:     return $value;
1004:   }
1005: 
1006: }
1007: 
API documentation generated by ApiGen 2.8.0