Overview

Namespaces

  • None
  • PHP

Classes

  • Sidecar
  • Sidecar_Admin_Page
  • Sidecar_Admin_Tab
  • Sidecar_Field
  • Sidecar_Form
  • Sidecar_Form_Settings
  • Sidecar_Plugin_Base
  • Sidecar_Plugin_Settings
  • Sidecar_Settings_Base
  • 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 'https://' 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['_sidecar_form_meta']['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_field_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:       /**
 244:        * Esnure that all fields have a value in the $form_values array.
 245:        * Checkboxes and radio buttons might not.
 246:        */
 247:       $form_values = $form->ensure_default_values( $form_values );
 248: 
 249:       /**
 250:        * @var Sidecar_Field $field
 251:        */
 252:       foreach( $form->get_fields() as $field_name => $field ) {
 253:         $validation_options = false;
 254:         if ( $field->field_allow_html )
 255:           $form_values[$field_name] = htmlentities( $form_values[$field_name] );
 256:             /**
 257:              * Default to FILTER_SANITIZE_STRING if ['validator'] not set.
 258:              */
 259:             if ( $field->field_options ) {
 260:           $validated_value = isset( $field->field_options[$form_values[$field_name]] ) ? $form_values[$field_name] : false;
 261:         } else if ( isset( $field->field_validator['filter'] ) ) {
 262:            $validated_value = filter_var( $form_values[$field_name], $field->field_validator['filter'] );
 263:            if ( isset( $field->field_validator['options'] ) ) {
 264:             $validation_options = $field->field_validator['options'];
 265:            }
 266:         } else {
 267:           $validator = $field->field_validator ? $field->field_validator : FILTER_SANITIZE_STRING;
 268:           $validated_value = filter_var( $form_values[$field_name], $validator );
 269:         }
 270:         $validated_value = $this->_apply_plugin_filter( "sanitize_setting_{$field_name}", $validated_value, $field, $form );
 271:         if ( $validation_options || $validated_value != $form_values[$field_name] ) {
 272:           if ( ! $validation_options ) {
 273:             add_settings_error( $this->plugin->option_name, 'sidecar-value', sprintf(
 274:               __( 'Please enter a valid value for "%s."', 'sidecar' ), $field->field_label
 275:             ));
 276:           } else {
 277:             if ( isset( $validation_options['min'] ) && $validation_options['min'] > intval( $form_values[$field_name] ) ) {
 278:               add_settings_error( $this->plugin->option_name, 'sidecar-min', sprintf(
 279:                 __( 'Please enter a value greater than or equal to %d for "%s."', 'sidecar' ),
 280:                   $validation_options['min'],
 281:                   $field->field_label
 282:               ));
 283:             }
 284:             if ( isset( $validation_options['max'] ) && $validation_options['max'] < intval( $form_values[$field_name] ) ) {
 285:               add_settings_error( $this->plugin->option_name, 'sidecar-max', sprintf(
 286:                 __( 'Please enter a value less than or equal to %d for "%s."', 'sidecar' ),
 287:                   $validation_options['max'],
 288:                   $field->field_label
 289:               ));
 290:               $continue = true;
 291:             }
 292:           }
 293:         }
 294:       }
 295:     }
 296: 
 297:     $form_values = $this->_apply_plugin_filter( $method_name = "process_form_{$form->form_name}", $form_values );
 298:     if ( method_exists( $this->plugin, $method_name ) ) {
 299:       /**
 300:        * This presumes that "process_form_{$form->form_name}" uses the $api.
 301:        * We may need to make it a bit more generic, i.e. allow setting a message on the plugin
 302:        * and then our process form would need to set the plugin's message.
 303:        */
 304:       if ( ! empty( $api->response->message ) ) {
 305:         $message_type = $api->response->has_error() ? 'error' : 'updated';
 306:         add_settings_error( $this->plugin->option_name, "sidecar-form-processed-{$form->form_name}", $api->response->message, $message_type );
 307:       }
 308:     }
 309: 
 310: 
 311:     $input[$form->form_name] = $form_values;
 312: 
 313:     $input = $this->_apply_plugin_filter( 'filter_postback', $input );
 314: 
 315:     $postback_info = (object)array(
 316:       'admin_page' => $this,
 317:       'form' => $form,
 318:       'form_values' => $form_values,
 319:       'input' => $input,
 320:       'unfiltered' => $unfiltered_input
 321:     );
 322: 
 323:     $this->_do_plugin_action( "set_postback_{$form->form_name}_{$this->page_name}_message", $postback_info );
 324:     $this->_do_plugin_action( 'set_postback_message', $this, $form, $postback_info );
 325: 
 326:     return $input;
 327:   }
 328: 
 329: 
 330:   /**
 331:    * @return array
 332:    */
 333:   function get_auth_credentials() {
 334:     return $this->get_auth_form()->get_settings_values();
 335:   }
 336: 
 337:   /**
 338:    * @return Sidecar_Form
 339:    */
 340:   function get_auth_form() {
 341:     return $this->plugin->get_form( $this->_auth_form );
 342:   }
 343: 
 344:   /**
 345:    * @param string|Sidecar_Form $form
 346:    */
 347:   function set_auth_form( $form ) {
 348:     if ( is_string( $form ) ) {
 349:       $this->_auth_form = $form;
 350:     } else if ( isset( $form->form_name ) ) {
 351:       $this->_auth_form = $form->form_name;
 352:     } else if ( WP_DEBUG ) {
 353:       $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' );
 354:       trigger_error( sprintf( $message, $this->plugin_class ) );
 355:     }
 356:   }
 357: 
 358:   /**
 359:    * @param string $tab_slug
 360:    * @param string $tab_text
 361:    * @param array $args
 362:    */
 363:   function add_tab( $tab_slug, $tab_text, $args = array() ) {
 364:     $args['plugin'] = $this->plugin;
 365:     if ( ! $this->_auth_form && 'account' == $tab_slug )
 366:       $this->_auth_form = 'account';
 367:     $this->_tabs[$tab_slug] = $tab = new Sidecar_Admin_Tab( $tab_slug, $tab_text, $args );
 368:     }
 369: 
 370:   /**
 371:    * @return Sidecar_Admin_Tab
 372:    */
 373:   function get_default_tab() {
 374:     return reset( $this->_tabs );
 375:   }
 376: 
 377:   /**
 378:    * Test to see if the current tab is the authentication tab
 379:    *
 380:    * @return bool
 381:    */
 382:   function is_authentication_tab() {
 383:     $tab = $this->get_authentication_tab();
 384:     return is_object( $tab ) && $tab === $this->get_current_tab();
 385:   }
 386:   /**
 387:    * @return Sidecar_Admin_Tab
 388:    */
 389:   function get_authentication_tab() {
 390:     if ( ! $this->_authentication_tab ) {
 391:       /**
 392:        * @var Sidecar_Admin_Tab $tab
 393:        */
 394:       foreach( $this->_tabs as $tab ) {
 395:         if ( in_array( $this->_auth_form, $tab->forms ) ) {
 396:           $this->_authentication_tab = $tab;
 397:           break;
 398:         }
 399:       }
 400:     }
 401:     return $this->_authentication_tab;
 402:   }
 403: 
 404:   /**
 405:    * Filters a value by calling a method in the plugin.
 406:    *
 407:    * Captures whatever additional parameters and passes them on.
 408:    *
 409:    * @param string $method
 410:    *
 411:    * @return bool|mixed
 412:    */
 413:   protected function _apply_plugin_filter( $method ) {
 414:     $args = func_get_args();
 415:     array_shift( $args );
 416:     $result = $args[0];
 417:     if ( method_exists( $this->plugin, $method ) )
 418:       $result = call_user_func_array( array( $this->plugin, $method ), $args );
 419:     return $result;
 420:   }
 421:   /**
 422:    * Executes an action by calling a method in the plugin.
 423:    *
 424:    * Captures whatever additional parameters and passes them on.
 425:    *
 426:    * @param string $method
 427:    *
 428:    * @return bool|mixed
 429:    */
 430:   protected function _do_plugin_action( $method ) {
 431:     $result = false;
 432:     $args = func_get_args();
 433:     array_shift( $args );
 434:     if ( method_exists( $this->plugin, $method ) )
 435:       $result = call_user_func_array( array( $this->plugin, $method ), $args );
 436:     return $result;
 437:   }
 438:   /**
 439:    * Retrieves a value from the plugin.
 440:    *
 441:    * Captures whatever additional parameters and passes them on.
 442:    *
 443:    * @param string $method
 444:    *
 445:    * @return bool|mixed
 446:    */
 447:   protected function _get_plugin_value( $method ) {
 448:     $args = func_get_args();
 449:     return call_user_func_array( array( $this, '_do_plugin_action' ), $args );
 450:   }
 451: 
 452:   /**
 453:    *
 454:    */
 455:   function admin_menu() {
 456: 
 457:     if ( $this->is_postback_update() )
 458:       return;
 459: 
 460:     if ( $this->is_page_url() ) {
 461:       /**
 462:        * Call the plugin's $this->initialize_admin_page() method, if it exists.
 463:        */
 464:       $this->initialize();
 465: 
 466:       /**
 467:        * Call the plugin's $this->verify_current_tab() method, if it exists.
 468:        * Return true if all is okay and you don't want to run the default,
 469:        * return false if you want to run the default, or
 470:        * redirect and exit inside the method.
 471:        */
 472:       $this->_get_plugin_value( 'verify_current_tab', $this );
 473:     }
 474: 
 475:     /**
 476:      * Add in menu option, if one doesn't already exist
 477:      */
 478:     $this->menu_page = add_submenu_page(
 479:           $this->parent_slug,
 480:       $this->page_title,
 481:       $this->menu_title,
 482:       $this->capability_required,
 483:       $this->page_slug,
 484:       array( $this, 'the_page' )
 485:     );
 486: 
 487:     add_action( "admin_print_styles-{$this->menu_page}", array( $this, 'admin_print_styles' ));
 488:     }
 489: 
 490:   /**
 491:    *
 492:    */
 493:   function admin_print_styles() {
 494:     $plugin = $this->plugin;
 495:       $localfile = 'css/admin-style.css';
 496:       $filepath = "{$plugin->plugin_path}/{$localfile}";
 497:       if ( file_exists( $filepath ) ) {
 498:           wp_enqueue_style( "{$plugin->plugin_name}_admin_styles", plugins_url( $localfile, $plugin->plugin_file ) );
 499:      }
 500:     }
 501: 
 502:   /**
 503:    * @todo Call this from HEAD instead of from here.
 504:    */
 505:   function the_css() {
 506:     $css = $this->_get_plugin_value( 'get_admin_page_css', $this );
 507:     if ( $css ) {
 508:       $css_html =<<<HTML
 509: <style type="text/css">
 510: {$css}
 511: </style>
 512: HTML;
 513:       echo $css_html;
 514:     }
 515:   }
 516:   /**
 517:      * Displays admin page
 518:      */
 519:     function the_page() {
 520:      /**
 521:       * @todo Call this from HEAD instead of from here.
 522:       */
 523:     $this->the_css();
 524:     $tab = $this->get_current_tab();
 525:     $tab_class= $tab ? " tab-{$tab->tab_slug}" : false;
 526:     $id = rtrim( "{$this->page_slug}-" . ltrim( $tab->tab_slug ), '-' );
 527:       echo <<<HTML
 528: \n<div id="{$id}" class="wrap {$this->plugin->css_base}-admin {$this->page_name}-page{$tab_class}">
 529: HTML;
 530:       $this->the_icon();
 531:     $this->the_title_and_tabs( $tab );
 532:     $this->the_page_content();
 533:       echo "\n" . '</div>';
 534:     }
 535: 
 536:   /**
 537:    * @param string $tab
 538:    * @param string $content_type
 539:    * @param array $args
 540:    */
 541:   function the_tab_specific_content( $tab, $content_type, $args = array() ) {
 542:     $args = wp_parse_args( $args, array( 'wrap' => true ) );
 543:     $content = false;
 544:     if ( ! $tab || ! isset( $tab->{$content_type} ) )
 545:       return;
 546:     if ( is_string( $tab->{$content_type} ) ) {
 547:       $content = $tab->{$content_type};
 548:     } else {
 549:       $handler = $tab->{$content_type};
 550:       if ( ! is_callable( $tab->{$content_type} ) ) {
 551:         $method = "the_{$this->page_name}_{$tab->tab_slug}_tab_{$content_type}";
 552:         $handler = method_exists( $this->plugin, $method ) ? array( $this->plugin, $method ) : false;
 553:       }
 554:       if ( $handler ) {
 555:         ob_start();
 556:         call_user_func( $handler, $this, $tab );
 557:         $content = ob_get_clean();
 558:       }
 559:     }
 560:     if ( $content ) {
 561:       if ( $args['wrap'] ) {
 562:         $content_type_slug = str_replace( '_', '-', $content_type );
 563:         $content =<<< HTML
 564: <div id="tab-{$content_type_slug}">
 565: {$content}
 566: </div>
 567: HTML;
 568:       }
 569:       echo $content;
 570:     }
 571:   }
 572:   /**
 573:    *
 574:    */
 575:   function the_page_content() {
 576:     echo <<<HTML
 577: <div id="admin-content">
 578: HTML;
 579:     /**
 580:      * @var bool|Sidecar_Admin_Tab
 581:      */
 582:     $current_tab = $this->has_tabs() ? $this->get_current_tab() : false;
 583: 
 584:     $this->the_tab_specific_content( $current_tab, 'before_page_title', array( 'wrap' => false ) );
 585: 
 586:     if ( $current_tab && $current_tab->page_title ) {
 587:       echo "<h1 class=\"admin-page-title\">";
 588:       echo "\n" . $current_tab->page_title;
 589:       echo "\n" . '</h1>';
 590:     }
 591: 
 592:     $this->the_tab_specific_content( $current_tab, 'before_content' );
 593: 
 594:     if ( $current_tab && $current_tab->tab_handler ) {
 595:       $handler =  $current_tab->tab_handler;
 596:       if ( ! is_callable( $handler ) ) {
 597:         $method = $handler;
 598:         if ( is_array( $method ) ){
 599:           $method = ( is_string( $handler[0] ) ? "{$handler[0]}::" : get_class( $handler[0] ) .'->' ) . $handler[1];
 600:         }
 601:         $message = __( '%s provided as %s for admin tab %s of admin page %s is not a valid callable.', 'sidecar' );
 602:         Sidecar::show_error( $message,
 603:           "<strong><code>{$method}()</code></strong>",
 604:           "<strong><code>tab_handler</code></strong>",
 605:           "<strong><em>\"{$current_tab->tab_slug}\"</em></strong>",
 606:           "<strong><em>\"{$this->page_name}\"</em></strong>"
 607:           );
 608:       }
 609:     } else {
 610:       $page_name = str_replace( '-', '_', $this->page_name );
 611:       $handler = array( $this->plugin, "the_{$page_name}_admin_page" );
 612:       if ( $current_tab ) {
 613:         $tab_slug = str_replace( '-', '_', $current_tab->tab_slug );
 614:         $tab_handler = array( $this->plugin, "the_{$this->page_name}_{$tab_slug}_tab" );
 615:         /**
 616:          * Fallback to page handler if tab handler is not callable
 617:          */
 618:         $handler = is_callable( $tab_handler ) ? $tab_handler : $handler;
 619:       }
 620:       if ( ! is_callable( $handler ) && $this->plugin->has_form( $current_tab->tab_slug ) ) {
 621:         /*
 622:          * If we have no handler but do have a form with same name as the tab slug, show it.
 623:          */
 624:         $handler = array( $this->plugin->get_form( $current_tab->tab_slug ), 'the_form' );
 625:       }
 626:       if ( ! is_callable( $handler ) ) {
 627:         if ( isset( $tab_handler ) )
 628:           /**
 629:            * If it was a tab then report the more specific function as the one we are missing
 630:            */
 631:           $handler = $tab_handler;
 632:         $message = __( 'No method named %s defined yet for %s.', 'sidecar' );
 633:         Sidecar::show_error( $message,
 634:           "<strong><code>{$handler[1]}()</code></strong>",
 635:           "<strong><code>{$this->plugin->plugin_class}</code></strong>"
 636:           );
 637:       }
 638:     }
 639:     if ( is_callable( $handler ) ) {
 640:       $this->_do_plugin_action( 'initialize_tab', $current_tab, $this );
 641:       call_user_func( $handler, $this );
 642:     }
 643: 
 644:     $this->the_tab_specific_content( $current_tab, 'after_content' );
 645:     echo '</div>';
 646:   }
 647:   /**
 648:    * Displays the icon for the plugin page
 649:    *
 650:    * @todo Research to see if we need to support something other than icon32
 651:    *
 652:    */
 653:   function the_icon() {
 654:     if ( $icon_html = $this->_get_plugin_value( 'get_icon_html', $this ) ) {
 655:       $icon_html = <<<HTML
 656: <div class="icon32">{$icon_html}</div>
 657: HTML;
 658:     } else if ( $this->icon ) {
 659:       /**
 660:        * $this->icon contains a URL for an image.
 661:        */
 662:       if ( preg_match( '#^https?://#', $this->icon, $m ) ) {
 663:         list( $width, $height ) = explode( 'x', $this->_apply_plugin_filter( 'get_icon_dimensions', '36x34' ) );
 664:         $icon_html_fragment =<<<HTML
 665: ><img height="{$height}" style="background:none;" width="{$width}" src="{$this->icon}>
 666: HTML;
 667:       } else {
 668:         /**
 669:          * This means $this->icon contains an idea value such as
 670:          */
 671:         $icon_html_fragment = " id=\"icon-{$this->icon}\"><br/>";
 672:       }
 673:       /**
 674:        * Note that there is no trailing ">" on the first <div>.
 675:        * Instead, the $icon_html_fragment contains it.
 676:        */
 677:       $icon_html = <<<HTML
 678: <div class="icon32"{$icon_html_fragment}</div>
 679: HTML;
 680:     }
 681:     if ( $icon_html )
 682:       echo $icon_html;
 683:   }
 684:   /**
 685:    *
 686:    */
 687:   function get_tabs() {
 688:       return $this->_tabs;
 689:     }
 690: 
 691:   /**
 692:    * @param string $tab_name
 693:    *
 694:    * @return bool|Sidecar_Admin_Tab
 695:    */
 696:   function get_tab( $tab_name ) {
 697:       return isset( $this->_tabs[$tab_name] ) ? $this->_tabs[$tab_name] : false;
 698:     }
 699:   /**
 700:    *
 701:    */
 702:   function has_tabs() {
 703:       return 0 < count( $this->_tabs );
 704:     }
 705: 
 706:   /**
 707:    * Display the row of tabs at the top of a page with the <h2> tab wrapper element
 708:    */
 709:   function the_title_and_tabs() {
 710:     if ( $this->page_title || $this->has_tabs() ) {
 711:       echo "\n" . '<h2 class="nav-tab-wrapper">';
 712:       if ( $this->page_title )
 713:         echo "\n<span class=\"admin-page-title\">{$this->page_title}</span>";
 714:       if ( $this->has_tabs() )
 715:         echo "\n" . $this->get_tabs_html();
 716:       echo "\n" . '</h2>';
 717:     }
 718:     }
 719: 
 720:   /**
 721:    * Returns the tabs as a block of HTML for display at the top of the admin page.
 722:    *
 723:    * @return string
 724:    */
 725:   function get_tabs_html() {
 726:       return implode( "\n", $this->get_tab_links_html() );
 727:     }
 728: 
 729:   /**
 730:    * Returns an array of HTML for each tabs for display at the top of the admin page.
 731:    *
 732:    */
 733:   function get_tab_links_html() {
 734:     $links_html = array();
 735:     $current_tab = $this->get_current_tab();
 736:     if ( $current_tab ) {
 737:       foreach ( $this->get_tabs() as $tab_slug => $tab ) {
 738:         $class = ( $tab_slug == $current_tab->tab_slug ) ? ' nav-tab-active' : '';
 739:         $url = $this->get_tab_url( $tab_slug );
 740:         $links_html[] =<<<HTML
 741:   <a class="nav-tab{$class}" href="{$url}">{$tab->tab_text}</a>
 742: HTML;
 743:       }
 744:     }
 745:     return $links_html;
 746:   }
 747: 
 748: 
 749:   /**
 750:    * @return string|void
 751:    */
 752:   function get_page_url() {
 753:     return $this->get_tab_url(null);
 754:     }
 755: 
 756:   /**
 757:    * @return string|void
 758:    */
 759:   /**
 760:    * @param null|bool|string|Sidecar_Admin_Tab $tab
 761:    * @param string $text
 762:    * @param bool $blank
 763:    *
 764:    * @return string|void
 765:    */
 766:   function get_tab_link( $tab, $text, $blank = false ) {
 767:     $url = $this->get_tab_url( $tab );
 768:     if ( $blank )
 769:       $blank = ' target="_blank"';
 770:     return <<<HTML
 771: <a{$blank} href="{$url}">{$text}</a>
 772: HTML;
 773:     }
 774: 
 775:   /**
 776:    * @param null|bool|string|Sidecar_Admin_Tab $tab If null is passed then don't add a default tab.
 777:    * @return string|void
 778:    */
 779:   function get_tab_url( $tab = false ) {
 780:     if ( ! $this->_initialized ) {
 781:       $message = __( '%s->get_page_url() cannot be called prior to %s->initialize_admin_page() being called.', 'sidecar' );
 782:       Sidecar::show_error( $message, __CLASS__, $this->plugin->plugin_class );
 783:     }
 784: 
 785:     if ( $tab instanceof Sidecar_Admin_Tab )
 786:       $tab = $tab->tab_slug;
 787:     if ( $this->has_tabs() ) {
 788:       if ( isset( $this->_page_url[$tab] ) ) {
 789:         $url = $this->_page_url[$tab];
 790:       } else {
 791:         if ( false === $tab )
 792:           $tab = $this->get_default_tab()->tab_slug;
 793:         $url = $this->_page_url[$tab] = $this->get_base_page_url() . ( is_null($tab) ? '' : "&tab={$tab}" );
 794:       }
 795:       ;
 796:     } else {
 797:       if ( isset( $this->_page_url ) ) {
 798:         $url = $this->_page_url;
 799:       } else {
 800:         $url = $this->_page_url = $this->get_base_page_url();
 801:       }
 802:     }
 803:     return $url;
 804:     }
 805: 
 806:   /**
 807:    * @return string|void
 808:    */
 809:   function get_base_page_url() {
 810:     return admin_url( "{$this->parent_slug}?page={$this->page_slug}" );
 811:   }
 812: 
 813:   /**
 814:      * Check if the passed $tab variable matches the URL's tab parameter.
 815:      *
 816:      * @param string $tab
 817:      * @return bool
 818:      */
 819:     function is_current_tab( $tab ) {
 820:         /**
 821:          * IF the the current page has a valid tab,
 822:          * AND the URL's 'tab' parameter matches the function's $tab parameter
 823:          * THEN *YES*, it's the tab specified
 824:          */
 825:         return $this->is_current_tab_valid() && $tab == $_GET['tab'];
 826:     }
 827: 
 828:   /**
 829:      * Check if the passed $tab variable matches the URL's tab parameter.
 830:      *
 831:      * @return Sidecar_Admin_Tab
 832:      */
 833:     function get_current_tab() {
 834:       static $current_tab;
 835:       if ( ! isset( $current_tab ) ) {
 836:       $tab_slug = false;
 837:       if ( isset( $_GET['tab'] ) ) {
 838:          $tab_slug = $_GET['tab'];
 839:       } else if ( isset( $_POST['option_page'] ) && isset( $_POST[$_POST['option_page']]['_sidecar_form_meta']['tab'] )) {
 840:         /*
 841:          * This is used during HTTP postback from an admin form.
 842:          * The <input type="hidden" name="option_page"> added by settings_fields() and
 843:          * referencing <input type="hidden" name="{option_page}[tab]"> generated by $form->get_html()
 844:          */
 845:         $tab_slug = $_POST[$_POST['option_page']]['_sidecar_form_meta']['tab'];
 846:       }
 847:       $current_tab = $tab_slug && isset( $this->_tabs[$tab_slug] ) ? $this->_tabs[$tab_slug] : reset( $this->_tabs );
 848:     }
 849:    return $current_tab;
 850:   }
 851: 
 852:   /**
 853:      * Check if the passed $tab variable matches the URL's tab parameter.
 854:      *
 855:      * @param string|Sidecar_Admin_Tab $tab
 856:      * @return bool
 857:      */
 858:     function has_tab( $tab ) {
 859:       if ( isset( $tab->tab_slug ) ) {
 860:         $tab = $tab->tab_slug;
 861:      }
 862:         return isset( $this->_tabs[$tab] );
 863:     }
 864: 
 865:   /**
 866:      *
 867:      * @return int
 868:      */
 869:     function tab_count() {
 870:         return count( $this->_tabs );
 871:     }
 872: 
 873:   /**
 874:    * Validates to ensure that we have a URL tab parameter that is one of the valid tabs.
 875:    *
 876:    * @return bool
 877:    */
 878:   function is_current_tab_valid() {
 879:     return $this->has_tab( $this->get_current_tab() ) && $this->is_page_url();
 880:   }
 881: 
 882:   /**
 883:      * Check to see if we are on the admin URL for this plugin.
 884:      *
 885:      * @return bool
 886:      */
 887:     function is_page_url() {
 888:         if ( ! isset( $this->_is_page_url ) ) {
 889:             $base_url = $this->get_base_page_url();
 890:             /*
 891:              * Test to see if the left-most characters of this URL match the base URL, i.e. this is match
 892:              *
 893:              *  base_url = https://example.com/wp/wp-admin/options-general.php?page=my-plugin-settings
 894:              *  this_url = https://example.com/wp/wp-admin/options-general.php?page=my-plugin-settings OR
 895:              *  this_url = https://example.com/wp/wp-admin/options-general.php?page=my-plugin-settings&tab=support
 896:              *
 897:              * This is NOT a match:
 898:              *
 899:              *  base_url = https://example.com/wp/wp-admin/options-general.php?page=my-plugin-settings
 900:              *  this_url = https://example.com/wp/wp-admin/options-general.php OR
 901:              *  this_url = https://example.com/wp/wp-admin/edit-comments.php OR
 902:              *  this_url = https://example.com/wp/wp-admin/
 903:              *
 904:              */
 905:       $this->_is_page_url = $base_url == substr( Sidecar::this_url(), 0, strlen( $base_url ) );
 906:         }
 907:         return $this->_is_page_url;
 908:     }
 909: 
 910:   /**
 911:    * @return bool
 912:    */
 913:   function is_postback_update() {
 914:     global $pagenow;
 915:     $is_postback_update = false;
 916:     if ( isset( $_POST['action'] ) && 'update' == $_POST['action'] && 'options.php' == $pagenow ) {
 917:       $this->initialize();
 918:       $is_postback_update = isset( $_POST[$this->plugin->option_name] );
 919:     }
 920:     return $is_postback_update;
 921:   }
 922: 
 923:   /**
 924:      * Check to make sure we are on the right tab.
 925:      *
 926:      * Redirect if we are not on the right tab based on authentication status or invalid tab,
 927:      * OR return if not even on this plugin's Admin URL
 928:      * Register an error message for later display if not authenticated.
 929:      *
 930:      * @return bool Returns true if we are on a verified tab, false or URL redirect otherwise.
 931:      */
 932:     function verify_current_tab() {
 933:      /*
 934:       * If we have no tabs trying to verify is a moot point. Punt.
 935:       */
 936:     if ( ! $this->has_tabs() )
 937:       return true;
 938: 
 939:     $needs_grant = $this->plugin->needs_grant();
 940:     $has_grant = $this->plugin->has_grant();
 941: 
 942:         if ( ! $this->is_current_tab_valid() ) {
 943:             /**
 944:              * 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.
 945:        *
 946:        * We redirect to avoid having multiple URLs mean the same thing which would not be optimal for bookmarking, caching, etc.
 947:              */
 948:             if ( $needs_grant && $has_grant ) {
 949:                 /**
 950:                  * If authenticated we redirect with a "301 - This URL has changed" status code so the browser can know
 951:                  * to go to 'usage' whenever is sees this URL and avoid the round trip next time.
 952:                  */
 953:                 wp_safe_redirect( $this->get_tab_url( $this->get_default_tab()->tab_slug ), 301 );
 954:         exit;
 955:             } else if ( $needs_grant && $auth_tab = $this->get_authentication_tab() ) {
 956:                 /**
 957:                  * If not authenticated we redirect with a "302 - This URL has moved temporarily" status code
 958:                  * because normally we'd want to go to usage, so don't cause browser to thing this URL w/o a
 959:                  * valid tab should always go to 'account.'
 960:                  */
 961:                 wp_safe_redirect( $this->get_tab_url( $auth_tab->tab_slug ), 302 );
 962:         exit;
 963:             }
 964:         } else if ( $needs_grant && ! $has_grant ) {
 965:             /**
 966:              * If we are using an API but we don't have a grant
 967:              */
 968:       $auth_tab = $this->get_authentication_tab();
 969:        /**
 970:         * If there is a tab and we are not already on the authentication tab
 971:         */
 972:        if ( $auth_tab && ( ! isset( $_GET['tab'] ) || $auth_tab->tab_slug != $_GET['tab'] ) ) {
 973:                 /**
 974:                  * ...and we are NOT on the authentication tab then redirect to the 'account' tab.
 975:                  *
 976:                  * We redirect with a "302 - This URL has moved temporarily" because it's still a good URL and
 977:                  * we want the browser to be happy to return here later.
 978:                  */
 979:                 wp_safe_redirect( $this->get_tab_url( $auth_tab ), 302 );
 980:                 exit;
 981:             } else {
 982:                 /**
 983:                  * ...and we ARE on the authentication tab then prepare a "Need to authenticate" message for later display.
 984:                  */
 985:                 add_settings_error(
 986:                     $this->plugin->plugin_slug, // @todo Switch to $this->form_name,
 987:                     'need-info',
 988:                     __( 'You must have an account to use this plugin.  Please enter your credentials.', 'sidecar' )
 989:                 );
 990:             }
 991:         }
 992:         return true;
 993:     }
 994:   /**
 995:    * @param $property_name
 996:    * @return bool|string
 997:    */
 998:   function __get( $property_name ) {
 999:     $value = false;
1000:     if ( preg_match( '#^(.+?)_tab_url$#', $property_name, $match ) && $this->has_tab( $match[1] ) ) {
1001:       /**
1002:        * Allows magic property syntax for any registered URL
1003:        * @example: $this->foobar_url calls $this-get_url( 'foobar' )
1004:        * Enables embedding in a HEREDOC or other doublequoted string
1005:        * without requiring an intermediate variable.
1006:        */
1007:       $value = call_user_func( array( $this, "get_tab_url" ), $match[1] );
1008:     } else {
1009:       Sidecar::show_error( 'No property named %s on %s class.', $property_name, get_class( $this ) );
1010:     }
1011:     return $value;
1012:   }
1013: 
1014: }
1015: 
API documentation generated by ApiGen 2.8.0