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:  */
   5: class Sidecar_Plugin_Base extends Sidecar_Singleton_Base {
   6:   /**
   7:    * @var string
   8:    */
   9:   var $plugin_class;
  10: 
  11:   /**
  12:    * @var string
  13:    */
  14:   var $plugin_class_base;
  15: 
  16:   /**
  17:    * @var string
  18:    */
  19:   var $plugin_name;
  20: 
  21:   /**
  22:    * Dashed version of $this->plugin_name
  23:    *
  24:    * @var string
  25:    */
  26:   var $plugin_slug;
  27: 
  28:   /**
  29:    * ID of plugin used by WordPress in get_option('active_plugins')
  30:    *
  31:    * @var string
  32:    */
  33:   var $plugin_id;
  34: 
  35:   /**
  36:    * @var string
  37:    */
  38:   var $plugin_version;
  39: 
  40:   /**
  41:    * @var string
  42:    */
  43:   var $plugin_title;
  44: 
  45:   /**
  46:    * @var string
  47:    */
  48:   var $plugin_label;
  49: 
  50:   /**
  51:    * @var string
  52:    */
  53:   var $css_base;
  54: 
  55:   /**
  56:    * @var string
  57:    */
  58:   var $plugin_file;
  59: 
  60:   /**
  61:    * @var string
  62:    */
  63:   var $plugin_path;
  64: 
  65:   /**
  66:    * @var string Minimum PHP version, defaults to min version for WordPress
  67:    */
  68:   var $min_php = '5.2.4';
  69: 
  70:   /**
  71:    * @var string Minimum WordPress version, defaults to first version requiring PHP 5.2.4.
  72:    */
  73:   var $min_wp = '3.2';
  74: //  /**
  75: //   * @var string Cron recurrance
  76: //   */
  77: //  var $cron_recurrance = 'hourly';
  78: //  /**
  79: //   * @var string Key used for cron for this plugin
  80: //   */
  81: //  var $cron_key;
  82: 
  83:   /**
  84:    * @var array Array of URLs defined for handle use by plugin.
  85:    */
  86:   protected $_urls = array();
  87: 
  88:   /**
  89:    * @var array Array of Image file names defined for handle use by plugin.
  90:    */
  91:   protected $_images = array();
  92: 
  93:   /**
  94:    * @var bool|array
  95:    */
  96:   protected $_shortcodes = false;
  97: 
  98:   /**
  99:    * @var bool|array
 100:    */
 101:   protected $_forms = false;
 102: 
 103:   /**
 104:    * @var bool|array
 105:    */
 106:   protected $_admin_pages = array();
 107: 
 108:   /**
 109:    * @var array Array of Meta Links for the WordPress plugin page.
 110:    */
 111:   protected $_meta_links = array();
 112: 
 113:   /**
 114:    * @var bool|array|RESTian_Client
 115:    */
 116:   protected $_api = false;
 117: 
 118:   /**
 119:    * @var Sidecar_Admin_Page
 120:    */
 121:   protected $_current_admin_page;
 122: 
 123:   /**
 124:    * @var Sidecar_Form
 125:    */
 126:   protected $_current_form;
 127: 
 128:   /**
 129:    * @var bool
 130:    */
 131:   protected $_initialized = false;
 132: 
 133:   /**
 134:    * @var Sidecar_Settings
 135:    */
 136:   protected $_settings;
 137: 
 138:   /**
 139:    * @var
 140:    */
 141:   protected $_default_settings_values;
 142: 
 143:   /**
 144:    * @var string
 145:    */
 146:   var $option_name;
 147: 
 148:   /**
 149:    * @var string
 150:    */
 151:   var $needs_ajax = false;
 152: 
 153:   /**
 154:    * @var bool
 155:    */
 156:   var $needs_settings = true;
 157: 
 158:   /**
 159:    * @var bool
 160:    */
 161:   protected $_admin_initialized = false;
 162: 
 163: 
 164: 
 165: 
 166:   /**
 167:    * @param $class_name
 168:    * @param $filepath
 169:    */
 170:   function set_api_loader( $class_name, $filepath ) {
 171:     $this->_api = array(
 172:       'class_name' => $class_name,
 173:       'filepath' => $filepath,
 174:     );
 175:   }
 176: 
 177:   /**
 178:    * @return bool
 179:    */
 180:   function has_api() {
 181:     return false !== $this->get_api();
 182:   }
 183: 
 184:   /**
 185:    * Returns a RESTian_Client object if set_api_loader() has previously been called with correct info, false otherwise.
 186:    * @return bool|RESTian_Client
 187:    */
 188:   function get_api() {
 189:     if ( ! $this->_api instanceof RESTian_Client ) {
 190:       /**
 191:        * @todo fix this to work on Windows
 192:        */
 193:       $filepath= '/' == $this->_api['filepath'][0] ? $this->_api['filepath'] : "{$this->plugin_path}/{$this->_api['filepath']}";
 194: 
 195:       if ( is_file( $filepath) ) {
 196:         require_once( $filepath );
 197:         if ( class_exists( $this->_api['class_name'] ) ) {
 198:           $class_name = $this->_api['class_name'];
 199:           // @var RESTian_Client $this->_api
 200:           $this->_api = new $class_name( $this );
 201:         }
 202:         /**
 203:          * @todo Verify we still need this
 204:          */
 205:         $this->_api->initialize_client();
 206:       }
 207:     }
 208:     return $this->_api;
 209:   }
 210: 
 211:   /**
 212:    * @param RESTian_Client $api
 213:    */
 214:   function set_api( $api ) {
 215:     if ( empty( $this->_forms ) ) {
 216:       /**
 217:        * What about plugins with an API but no need for forms?  Is that possible since forms=settings in Sidecar?
 218:        */
 219:       $error_message = __( '$plugin->set_api($api) cannot be called before forms have been added. Please call $plugin->add_form() in $plugin->initialize_plugin() at least once prior to calling set_api().', 'sidecar' );
 220:       trigger_error( $error_message );
 221:       exit;
 222:     }
 223:     $api->caller = $this;
 224:     $api->initialize_client();
 225:     $this->_api = $api;
 226:     if ( $this->_admin_initialized ) {
 227:       /**
 228:        * If Admin has been initialized then set grant.
 229:        * If not, wait to set until after initialization
 230:        * otherwise we get into a bad spiral of not-defined-yet.
 231:        */
 232:       $this->_api->set_grant( $this->get_grant() );
 233:     }
 234:   }
 235: 
 236:   function is_saving_widget() {
 237:     global $pagenow;
 238:     return isset( $_POST['action'] ) && isset( $_POST['id_base'] )
 239:       && 'admin-ajax.php' == $pagenow && 'save-widget' == $_POST['action'];
 240:   }
 241:   /**
 242:    * @param array $args
 243:    */
 244:   function on_load( $args = array() ) {
 245: 
 246:     /**
 247:      * If we are saving a widget then of course we need ajax,
 248:      * and I'm pretty sure we'll need to add a lot of checks here
 249:      * as new AJAX use-cases are discovered.
 250:      */
 251:     if ( ! $this->needs_ajax && $this->is_saving_widget() ) {
 252:       $this->needs_ajax = true;
 253:     }
 254:     /*
 255:      * If running an AJAX callback and either $this->needs_ajax or $args['needs_ajax'] is false then bypass the plugin.
 256:      *
 257:      * To enable AJAX support in a plugin either:
 258:      *
 259:      *  1. Create a __construct in plugin class, set $this->needs_ajax=true then call parent::_construct(), or
 260:      *
 261:      *  2. Pass array( 'needs_ajax' => true ) to plugin's required constructor at end of plugin file,
 262:      *     i.e. new MyPlugin( array( 'needs_ajax' => true ) );
 263:      *
 264:      */
 265:     if ( defined( 'DOING_AJAX' ) && DOING_AJAX && ( ! $this->needs_ajax || ( isset( $args['needs_ajax'] ) && ! $args['needs_ajax'] ) ) )
 266:       return;
 267: 
 268:     $this->plugin_class = get_class( $this );
 269: 
 270:     /**
 271:      * Copy properties in from $args, if they exist.
 272:      */
 273:     foreach( $args as $property => $value )
 274:       if ( property_exists(  $this, $property ) )
 275:         $this->$property = $value;
 276: 
 277:     add_action( 'init', array( $this, '_init' ) );
 278:     add_action( 'wp_loaded', array( $this, '_wp_loaded' ) );
 279:     add_action( 'wp_print_styles', array( $this, '_wp_print_styles' ) );
 280:     add_action( 'save_post', array( $this, '_save_post' ) );
 281: 
 282:     $this->plugin_class_base = preg_replace( '#^(.*?)_Plugin$#', '$1', $this->plugin_class );
 283: 
 284:     if ( ! $this->plugin_name )
 285:       $this->plugin_name = strtolower( $this->plugin_class_base );
 286: 
 287:     if ( ! $this->option_name )
 288:       $this->option_name = "{$this->plugin_name}_settings";
 289: 
 290: //    if ( ! $this->cron_key )
 291: //      $this->cron_key = "{$this->plugin_name}_cron";
 292: 
 293:     if ( $this->is_plugin_page_action() ) {
 294:       global $plugin;
 295:       if ( ! isset( $plugin ) ) {
 296:         /*
 297:          * This plugin is being activated
 298:          */
 299:         $this->plugin_id = filter_input( INPUT_GET, 'plugin', FILTER_SANITIZE_STRING );
 300:         $this->plugin_file = WP_PLUGIN_DIR . '/' . $this->plugin_id;
 301:       } else if ( file_exists( $plugin ) ) {
 302:         /**
 303:          * This is evidently the case during activation using Imperative
 304:          */
 305:         $this->plugin_file = $plugin;
 306:         $this->plugin_id = basename( dirname( $plugin ) ) . '/' . basename( $plugin );
 307:       } else {
 308:         /*
 309:          * Another plugin is being activated
 310:          */
 311:         $this->plugin_file = WP_PLUGIN_DIR . '/' . $plugin;
 312:         $this->plugin_id = $plugin;
 313:       }
 314:       add_action( 'plugins_loaded', array( $this, '_plugins_loaded' ) );
 315:       add_action( "activate_{$this->plugin_id}", array( $this, '_activate_plugin' ), 0 );
 316:       register_activation_hook( $this->plugin_id, array( $this, '_activate' ) );
 317:     } else if ( ! WP_Library_Manager::$loading_plugin_loaders && $this->is_verified_plugin_deletion() ) {
 318:       if ( preg_match( '#^uninstall_(.*?)$#', current_filter(), $match ) ) {
 319:         $this->plugin_file = WP_PLUGIN_DIR . "/{$match[1]}";
 320:       } else {
 321:         /*
 322:          * @todo My god this is a hack! I really need help from WordPress core here.
 323:          * @todo Blame: http://core.trac.wordpress.org/ticket/22802#comment:41
 324:          */
 325:         $backtrace = debug_backtrace();
 326:         foreach( $backtrace as $index => $call ) {
 327:           if ( preg_match( '#/wp-admin/includes/plugin.php$#', $call['file'] ) ) {
 328:             $this->plugin_file = $backtrace[$index-1]['file'];
 329:             break;
 330:           }
 331:         }
 332:       }
 333:       $this->plugin_id = basename( dirname( $this->plugin_file ) ) . '/' . basename( $this->plugin_file );
 334:     } else if ( false !== WP_Library_Manager::$uninstalling_plugin && preg_match( '#' . preg_quote( WP_Library_Manager::$uninstalling_plugin ) . '$#', $GLOBALS['plugin'] ) ) {
 335:       /**
 336:        * We are uninstalling a plugin, and the plugin we are uninstalling matches the global $plugin value
 337:        * which means we ar loading the plugin we want to uninstall (vs. loading a different plugin on `same page load.)
 338:        */
 339:       global $plugin;
 340:       $this->plugin_file = $plugin;
 341:       $this->plugin_id = WP_Library_Manager::$uninstalling_plugin;
 342:     } else {
 343:       /**
 344:        * Grab the plugin file name from one the global values set when the plugin is included.
 345:        * @see: http://wordpress.stackexchange.com/questions/15202/plugins-in-symlinked-directories
 346:        * @see: http://wordpress.stackexchange.com/a/15204/89
 347:        */
 348:       global $mu_plugin, $network_plugin, $plugin;
 349:       if ( isset( $mu_plugin ) ) {
 350:         $this->plugin_file = $mu_plugin;
 351:       } else if ( isset( $network_plugin ) ) {
 352:         $this->plugin_file = $network_plugin;
 353:       } else if ( isset( $plugin ) ) {
 354:         $this->plugin_file = $plugin;
 355:       } else {
 356:         trigger_error( sprintf( __( 'Plugin %s only works when loaded by WordPress.' ), $this->plugin_name ) );
 357:         exit;
 358:       }
 359:       $this->plugin_id = basename( dirname( $this->plugin_file ) ) . '/' . basename( $this->plugin_file );
 360:       require_once(ABSPATH . 'wp-admin/includes/plugin.php');
 361:       if ( ! is_plugin_active( $this->plugin_id ) ) {
 362:         trigger_error( sprintf( __( 'Plugin %s is not an active plugin or is not installed in a subdirectory of %s.' ),
 363:           $this->plugin_name,
 364:           WP_PLUGIN_DIR
 365:         ));
 366:         exit;
 367:       }
 368:       register_deactivation_hook( $this->plugin_id, array( $this, 'deactivate' ) );
 369:     }
 370: 
 371:     register_uninstall_hook( $this->plugin_id, array( $this->plugin_class, 'uninstall' ) );
 372: 
 373:     /**
 374:      * Ask subclass to initialize plugin which includes admin pages
 375:      */
 376:     $this->initialize_plugin();
 377: 
 378:   }
 379: 
 380:   /**
 381:    * Delete the flag indicating that a post needs external files (CSS styles and JS scripts) for each
 382:    * shortcode we have in case the newly saved post now has changed the use of the shortcodes.
 383:    */
 384:   function _save_post( $post_id ) {
 385:     $this->initialize_shortcodes();
 386:     $shortcodes = $this->get_shortcodes();
 387:     if ( is_array( $shortcodes ) ) {
 388:       /**
 389:        * @var Sidecar_Shortcode $shortcode
 390:        */
 391:       foreach( $shortcodes as $shortcode )
 392:         $shortcode->delete_has_shortcode( $post_id );
 393:       /**
 394:        * Now load the post asynchronously via HTTP to pre-set the meta value for $this->HAS_SHORTCODE_KEY.
 395:        */
 396:       wp_remote_request( get_permalink( $post_id ), array( 'blocking' => false ) );
 397:     }
 398:   }
 399: 
 400:   /**
 401:    * Used to check if we are on a plugin page that is asking about deletion.
 402:    *
 403:    * @return bool
 404:    */
 405:   function is_confirm_plugin_deletion() {
 406:     return $this->is_plugin_deletion()
 407:       && ! isset( $_POST['verify-delete'] );
 408:   }
 409: 
 410:   /**
 411:    * Used to check if we are on a plugin page that is deleting (a) plugin(s).
 412:    *
 413:    * @return bool
 414:    */
 415:   function is_verified_plugin_deletion() {
 416:     return $this->is_plugin_deletion()
 417:       && isset( $_POST['verify-delete'] ) &&  '1' == $_POST['verify-delete'];
 418:   }
 419: 
 420:   /**
 421:    * Used to check if we are a plugin page askin about deletion or processing deletion request.
 422:    *
 423:    * @return bool
 424:    */
 425:   function is_plugin_deletion() {
 426:     return $this->is_plugin_page()
 427:       && isset( $_GET['action'] ) &&  'delete-selected' == $_GET['action']
 428:       && isset( $_REQUEST['checked'] ) && count( $_REQUEST['checked'] );
 429:   }
 430: 
 431:   /**
 432:    *
 433:    */
 434:   static function uninstall() {
 435: 
 436:     /**
 437:      * @var Sidecar_Plugin_Base $plugin
 438:      */
 439:     $plugin = self::this();
 440: 
 441:     /**
 442:      * Initialize it so we can ensure all properties are set in case $plugin->uninstall_plugin() needs them.
 443:      */
 444:     $plugin->initialize();
 445: 
 446:     /**
 447:      * Delete settings
 448:      */
 449:     $plugin->delete_settings();
 450: 
 451:     /*
 452:      * Call subclass' uninstall if applicable.
 453:      */
 454:     $plugin->uninstall_plugin();
 455: 
 456:   //    /**
 457:   //     * Delete cron tasks
 458:   //     */
 459:   //    $next_run = wp_next_scheduled( $plugin->cron_key );
 460:   //    wp_unschedule_event( $next_run, $plugin->cron_key );
 461: 
 462:   }
 463: 
 464:   /**
 465:    * Used to check if we are in an activation callback on the Plugins page.
 466:    *
 467:    * @return bool
 468:    */
 469:   function is_plugin_page() {
 470:     global $pagenow;
 471:     return 'plugins.php' == $pagenow;
 472:   }
 473: 
 474:   /**
 475:    * Used to check if we are in an activation callback on the Plugins page.
 476:    *
 477:    * @return bool
 478:    */
 479:   function is_plugin_page_action() {
 480:     return $this->is_plugin_page()
 481:       && isset( $_GET['action'] )
 482:       && isset( $_GET['plugin'] );
 483:   }
 484: 
 485: //  /**
 486: //   * Used to check if we are activating a plugin.
 487: //   *
 488: //   * @return bool
 489: //   */
 490: //  function is_plugin_activation() {
 491: //    return $this->is_plugin_page_action()
 492: //      && 'activate' == $_GET['action'];
 493: //  }
 494: 
 495:   /**
 496:    * This is used for the "activate_{$this->plugin_id}" hook
 497:    * when $this->is_plugin_page_action().
 498:    */
 499:   function _activate_plugin() {
 500:     $this->initialize();
 501:   }
 502: 
 503:   /**
 504:    * This is used for the "activate_{$this->plugin_id}" hook
 505:    * when $this->is_plugin_page_action().
 506:    */
 507:   function _plugins_loaded() {
 508:     $this->initialize();
 509:   }
 510: 
 511:   /**
 512:    * @return array|bool
 513:    */
 514:   function get_forms() {
 515:     foreach( $this->_forms as $form_name => $form )
 516:       if ( is_array( $form ) )
 517:         $this->_forms[$form_name] = $this->promote_form( $form );
 518:     return $this->_forms;
 519:   }
 520: 
 521:   /**
 522:    * @return bool
 523:    */
 524:   function has_forms() {
 525:     return is_array( $this->_forms ) && count( $this->_forms );
 526:   }
 527: 
 528:   /**
 529:    *
 530:    */
 531:   function _wp_print_styles() {
 532:       $localfile = 'css/style.css';
 533:     $args = apply_filters( "sidecar_print_{$this->plugin_name}_styles", array(
 534:       'name'  => "{$this->plugin_name}_style",
 535:       'path'  => "{$this->plugin_path}/{$localfile}",
 536:       'url'   => plugins_url( $localfile, $this->plugin_file ),
 537:     ));
 538:       if ( file_exists( $args['path'] ) )
 539:           wp_enqueue_style( $args['name'], $args['url'] );
 540:     }
 541: 
 542:   /**
 543:    * @return bool
 544:    */
 545:   function has_admin_pages() {
 546:     return 0 < count( $this->_admin_pages );
 547:   }
 548:   /**
 549:    * @param string $form_name
 550:    *
 551:    * @return bool
 552:    */
 553:   function has_form( $form_name ) {
 554:     return isset( $this->_forms[$form_name] );
 555:   }
 556: 
 557:   function initialize() {
 558:     if ( $this->_initialized )
 559:       return;
 560: 
 561:     /*
 562:      * Avoid potential infinite loop.
 563:      */
 564:     $this->_initialized = true;
 565: 
 566:     if ( ! $this->plugin_title )
 567:       /*
 568:        * Hope we can get something better in the subclass' $this->initialize()...
 569:        */
 570:       $this->plugin_title = ucwords( str_replace( '_', ' ', $this->plugin_name ) );
 571: 
 572:     if ( ! $this->plugin_label )
 573:       /*
 574:        * Used for menu title for default
 575:        */
 576:       $this->plugin_label = $this->plugin_title;
 577: 
 578:     if ( ! $this->plugin_file )
 579:       Sidecar::show_error( '%s->plugin_file must be set in an %s->initialize_admin() method', get_class( $this), get_class( $this) );
 580: 
 581: //    $this->plugin_file_base = preg_replace( '#^(.*)-plugin\.php$#', '$1', $this->plugin_file );
 582: 
 583:     if ( ! $this->plugin_path )
 584:       $this->plugin_path = dirname( $this->plugin_file );
 585: 
 586:     if ( ! $this->plugin_slug )
 587:       $this->plugin_slug = str_replace( '_', '-', $this->plugin_name );
 588: 
 589:     if ( ! $this->css_base )
 590:       $this->css_base = $this->plugin_slug;
 591: 
 592:     $this->_settings = new Sidecar_Settings( $this->option_name, $this );
 593: 
 594:     $this->_settings->initialize_settings( $this->get_default_settings_values() );
 595: 
 596:   }
 597: 
 598:   /**
 599:    * @return array
 600:    */
 601:   function get_default_settings_values() {
 602:     if ( ! isset( $this->_default_settings_values ) ) {
 603:       $default_settings_values = array();
 604:       foreach( $this->get_forms() as $form ) {
 605:         /**
 606:          * @var Sidecar_Form $form
 607:          */
 608:         $default_settings_values[$form->form_name] = $form->get_default_settings_values();
 609:       }
 610:       $this->_default_settings_values = $default_settings_values;
 611:     }
 612:     return $this->_default_settings_values;
 613:   }
 614: 
 615:   /**
 616:    * @param array $settings_values
 617:    */
 618:   function update_settings_values( $settings_values ) {
 619:     $this->get_settings()->update_settings_values( $settings_values );
 620:   }
 621: 
 622:   /**
 623:    * @param object $settings_option
 624:    * @param bool $set_dirty
 625:    */
 626:   function update_settings_option( $settings_option, $set_dirty = true ) {
 627:     $this->get_settings()->update_settings_option( $settings_option, $set_dirty );
 628:   }
 629: 
 630:   /**
 631:    * @param string|Sidecar_Form $form
 632:    * @param string $setting_name
 633:    * @return array
 634:    */
 635:   function get_form_settings_value( $form, $setting_name ) {
 636:     if ( ! $form instanceof Sidecar_Form )
 637:       $form = $this->get_form( $form );
 638:     return $this->get_setting( $form )->get_setting( $setting_name );
 639: 
 640:   }
 641: 
 642:   /**
 643:    * @param string|Sidecar_Form $form
 644:    * @param string $setting_name
 645:    * @param string $value
 646:    * @return array
 647:    */
 648:   function update_form_settings_value( $form, $setting_name, $value ) {
 649:     if ( ! $form instanceof Sidecar_Form )
 650:       $form = $this->get_form( $form );
 651:     return $form->update_settings_value( $setting_name, $value );
 652:   }
 653: 
 654:   /**
 655:    * @return bool
 656:    */
 657:   function has_required_settings() {
 658:     $has_required_settings = true;
 659:     if ( ! $this->_initialized )
 660:       $this->initialize();
 661:     if ( $this->has_forms() ) {
 662:       $settings = $this->get_settings();
 663:       /** @var Sidecar_Form $form */
 664:       foreach( $this->get_forms() as $form_name => $form ) {
 665:         $form_settings = $settings->get_setting( $form->form_name );
 666:         if ( ! $form_settings->has_required_settings( $form->get_required_field_names() ) ) {
 667:           $has_required_settings = false;
 668:           break;
 669:         }
 670:       }
 671:     }
 672:     if ( method_exists( $this, 'filter_has_required_settings' ) ) {
 673:       $has_required_settings = $this->filter_has_required_settings( $has_required_settings, $settings );
 674:     }
 675:     return $has_required_settings;
 676:   }
 677: 
 678: 
 679:   /**
 680:    * @param Sidecar_Settings $settings
 681:    */
 682:   function set_settings( $settings ) {
 683:     $this->_settings = $settings;
 684:   }
 685: 
 686:   /**
 687:    * @return Sidecar_Settings
 688:    */
 689:   function get_settings() {
 690:     if ( ! $this->_initialized )
 691:       $this->initialize();
 692:     return $this->_settings;
 693:   }
 694:   /**
 695:    * @param string|Sidecar_Form $form
 696:    * @return mixed
 697:    */
 698:   function get_form_settings( $form ) {
 699:     if ( ! $form instanceof Sidecar_Form )
 700:       $form = $this->get_form( $form );
 701:     return $this->get_settings()->get_setting( $form->form_name );
 702:   }
 703: 
 704:   /**
 705:    * Delete the persisted settings on disk.
 706:    *
 707:    * @return bool
 708:    */
 709:   function delete_settings() {
 710:     $this->get_settings()->delete_settings();
 711:   }
 712: 
 713:   /**
 714:    *
 715:    */
 716:   function _wp_loaded() {
 717:     if ( is_admin() ) {
 718:       add_action( 'admin_notices',       array( $this, '_admin_notices' ) );
 719:       if ( $this->is_plugin_page() ) {
 720:         add_filter( 'plugin_action_links', array( $this, '_plugin_action_links' ), 10, 2 );
 721:         add_filter( 'plugin_row_meta',     array( $this, '_plugin_meta_links' ), 10, 2 );
 722:       }
 723:     } else {
 724:       $shortcodes = method_exists( $this->plugin_class, 'initialize_shortcodes' );
 725:       $template = method_exists( $this, 'initialize_template' );
 726:       if ( $shortcodes || $template ) {
 727:         // @todo Should we always initialize or only when we need it?
 728:         $this->initialize();
 729:         if ( $shortcodes )
 730:           $this->initialize_shortcodes();
 731:         if ( $template )
 732:           $this->initialize_template();
 733:         add_filter( 'the_content', array( $this, '_the_content' ), -1000 );
 734:       }
 735:     }
 736:   }
 737:   /**
 738:    */
 739:   function _admin_notices() {
 740:     if ( $this->needs_settings && ! $this->has_required_settings() && $this->is_plugin_page() && ! $this->is_confirm_plugin_deletion() ) {
 741:       $icon_html = $this->has_url( 'logo_icon' ) ? "<span class=\"sidecar-logo-icon\"></span><img src=\"{$this->logo_icon_url}\" /></span>" : '';
 742:       $message = sprintf( __( 'The <em>%s</em> plugin is now activated. Please configure it\'s <a href="%s"><strong>settings</strong></a>.', 'sidecar' ),
 743:       $this->plugin_title,
 744:       $this->get_settings_url()
 745:     );
 746:     $html = <<<HTML
 747: <div id="message" class="error settings-error">
 748:     <p>{$icon_html}{$message}</p>
 749: </div>
 750: HTML;
 751:     echo $html;
 752:     }
 753:   }
 754: 
 755:   /**
 756:    * @param $links
 757:    * @param $file
 758:    *
 759:    * @return array
 760:    */
 761:   function _plugin_action_links( $links, $file ){
 762:     if ( $file == $this->plugin_id  ) {
 763:       $url = $this->get_settings_url();
 764:       $link_text = __( 'Settings', 'sidecar' );
 765:       $links[] = "<a href=\"{$url}\">{$link_text}</a>";
 766:     }
 767:     return $links;
 768:   }
 769: 
 770:   /**
 771:    * @return bool|string|void
 772:    */
 773:   function get_settings_url() {
 774:     $settings_url = false;
 775:     if ( $settings_page = $this->get_admin_page( 'settings' ) ) {
 776:       $settings_page->initialize();
 777:       $settings_url = $this->get_admin_page( 'settings' )->get_page_url( null );
 778:     }
 779:     if ( method_exists( $this, 'filter_settings_url' ) )
 780:       $settings_url = $this->filter_settings_url( $settings_url );
 781:     return $settings_url;
 782:   }
 783:   /**
 784:    * @param array $links
 785:    * @param string $file
 786:    *
 787:    * @return array
 788:    */
 789:   function _plugin_meta_links( $links, $file ){
 790:     if ( $file == $this->plugin_id ) {
 791:       foreach( $this->_meta_links as $link_text => $link ) {
 792:         $title = isset( $link['title'] ) ? " title=\"{$link['title']}\"" : '';
 793:         $links[] = "<a target=\"_blank\" href=\"{$link['url']}\"{$title}>{$link_text}</a>";
 794:       }
 795:     }
 796:     return $links;
 797:   }
 798: 
 799:   /**
 800:    * @param $content
 801:    */
 802:   function _the_content( $content ) {
 803:     $shortcodes = $this->get_shortcodes();
 804:     if ( is_array( $shortcodes ) )
 805:       /**
 806:        * @var Sidecar_Shortcode $shortcode
 807:        */
 808:       foreach( $shortcodes as $shortcode_name => $shortcode ) {
 809:         if ( method_exists( $this, 'initialize_shortcode' ) ) {
 810:           $this->initialize_shortcode( $shortcode );
 811:         }
 812:         add_shortcode( $shortcode_name, array( $shortcode, 'do_shortcode' ) );
 813:         /**
 814:          * Now get each shortcode to monitor for it's own use.
 815:          */
 816:         $shortcode->add_the_content_filter();
 817:       }
 818:     return $content;
 819:   }
 820: 
 821:   function _init() {
 822: //    add_action( 'cron_schedules', array( $this, '_cron_schedules' ) );
 823: //    add_action( 'cron', array( $this, '_cron' ) );
 824:     /**
 825:      * @todo Figure out how to load this only if needed
 826:      */
 827:     load_plugin_textdomain( $this->plugin_slug, false, '/' . basename( dirname( $this->plugin_file ) ) . '/languages' );
 828: 
 829:     if ( is_admin() ) {
 830: 
 831:       /**
 832:        * @todo Can we initialize only what's needed if it is not using one of the pages
 833:        */
 834:       if (true) {
 835:         /**
 836:          * Now set all the defaults
 837:          */
 838:         $this->initialize();
 839: 
 840:         /**
 841:          * Call the subclass and ask it to initialize itself
 842:          */
 843:         $this->_initialize_admin();
 844: 
 845: //        if ( $this->plugin_version )
 846: //          $this->plugin_title .= sprintf( ' v%s', $this->plugin_version );
 847: 
 848:       }
 849:     }
 850:   }
 851:   /**
 852:    * @param string $to 'api_vars' or 'fields'
 853:    * @param Sidecar_Form $form
 854:    * @param array $fields
 855:    * @return array
 856:    */
 857:   function transform_form_fields_to( $to, $form, $fields ) {
 858:     $to = rtrim( $to, 's' );
 859:     $field_objects = $form->get_fields();
 860:     foreach( $fields as $field_name => $value ) {
 861:       if ( isset( $field_objects[$field_name] ) ) {
 862:         if ( $field_name != ( $new_name = $field_objects[$field_name]->$to ) ) {
 863:           if ( $new_name )  // If false then it's not an API var
 864:             $fields[$new_name] = $value;
 865:           unset( $fields[$field_name] );
 866:         }
 867:       }
 868:     }
 869:     return $fields;
 870:   }
 871: 
 872:   /**
 873:    * @param string $to 'api_vars' or 'settings'
 874:    * @param Sidecar_Shortcode $shortcode
 875:    * @param array $attributes
 876:    * @return array
 877:    */
 878:   function transform_shortcode_attributes_to( $to, $shortcode, $attributes ) {
 879:     $to = rtrim( $to, 's' );
 880:     $attribute_objects = $shortcode->get_attributes();
 881:     foreach( $attributes as $attribute_name => $attribute ) {
 882:       if ( isset( $attribute_objects[$attribute_name] ) ) {
 883:         if ( $attribute_name != ( $new_name = $attribute_objects[$attribute_name]->$to ) ) {
 884:           $attributes[$new_name] = $attribute;
 885:           unset( $attributes[$attribute_name] );
 886:         }
 887:       }
 888:     }
 889:     return $attributes;
 890:   }
 891:   /**
 892:    * @param Sidecar_Form $form
 893:    */
 894:   function initialize_form( $form ) {
 895:     // Only here to keep PhpStorm from complaining that it's not defined.
 896:   }
 897:   /**
 898:    *
 899:    */
 900:   function initialize_shortcodes() {
 901:     // Only here to keep PhpStorm from complaining that it's not defined.
 902:   }
 903:   /**
 904:    *
 905:    */
 906:   function initialize_postback() {
 907:     // Only here to keep PhpStorm from complaining that it's not defined.
 908:   }
 909: 
 910:   /**
 911:    *
 912:    */
 913:   function initialize_template() {
 914:     // Only here to keep PhpStorm from complaining that it's not defined.
 915:   }
 916:   /**
 917:    *
 918:    */
 919:   function uninstall_plugin() {
 920:     // Only here to keep PhpStorm from complaining that it's not defined.
 921:   }
 922: 
 923:   /**
 924:    * @throws Exception
 925:    */
 926:   function initialize_plugin() {
 927:     throw new Exception( 'Class ' . get_class($this) . ' [subclass of ' . __CLASS__ . '] must define an initialize_plugin() method.' );
 928:   }
 929:   /**
 930:    * @throws Exception
 931:    */
 932:   function initialize_admin() {
 933:     throw new Exception( 'Class ' . get_class($this) . ' [subclass of ' . __CLASS__ . '] must define an initialize_admin() method.' );
 934:   }
 935:   /**
 936:    * @param Sidecar_Admin_Page $admin_page
 937:    * @throws Exception
 938:    */
 939:   function initialize_admin_page( $admin_page ) {
 940:     throw new Exception( 'Class ' . get_class($this) . ' [subclass of ' . __CLASS__ . '] must define an initialize_admin_page() method.' );
 941:   }
 942:   /**
 943:    * @param Sidecar_Shortcode $shortcode
 944:    * @throws Exception
 945:    */
 946:   function initialize_shortcode( $shortcode ) {
 947:     throw new Exception( 'Class ' . get_class($this) . ' [subclass of ' . __CLASS__ . '] must define an initialize_shortcode() method.' );
 948:   }
 949: 
 950:   /**
 951:    * @param Sidecar_Shortcode $shortcode
 952:    * @param array() $attributes
 953:    * @param string $content
 954:    *
 955:    * @throws Exception
 956:    * @return string
 957:    */
 958:   function do_shortcode( $shortcode, $attributes, $content = null ) {
 959:     if (1) // Only here to keep PhpStorm from flagging the return as an error.
 960:       throw new Exception( 'Class ' . get_class($this) . ' [subclass of ' . __CLASS__ . '] must define an do_shortcode() method.' );
 961:     return '';
 962:   }
 963: 
 964:   /**
 965:    * @param array $args
 966:    *
 967:    * @return mixed
 968:    */
 969:   function add_default_button( $args = array() ) {
 970:     /**
 971:      * @var Sidecar_Form
 972:      */
 973:     $form = isset( $args['form'] ) ? $args['form'] : end( $this->_forms );
 974:     return $form->add_button( 'save', __( 'Save Settings', 'sidecar' ), $args );
 975:   }
 976:   /**
 977:    * @param string $button_name
 978:    * @param string $button_text
 979:    * @param array $args
 980:    *
 981:    * @return mixed
 982:    */
 983:   function add_button( $button_name, $button_text, $args = array() ) {
 984:     /**
 985:      * @var Sidecar_Form
 986:      */
 987:     $form = isset( $args['form'] ) ? $args['form'] : end( $this->_forms );
 988:     return $form->add_button( $button_name, $button_text, $args );
 989:   }
 990: 
 991:   /**
 992:    * @param       $page_name
 993:    * @param array $args
 994:    *
 995:    * @return mixed
 996:    */
 997:   function add_admin_page( $page_name, $args = array() ) {
 998:     /**
 999:      * Give the admin page access back to this plugin.
1000:      */
1001:     $args['plugin'] = $this;
1002:     /**
1003:      * @var Sidecar_Admin_Page $admin_page
1004:      */
1005:     $this->_admin_pages[$page_name] = new Sidecar_Admin_Page( $page_name, $args );
1006:   }
1007:   /**
1008:    * @param string $link_text
1009:    * @param string $url
1010:    * @param array $args
1011:    */
1012:   function add_meta_link( $link_text, $url, $args = array() ) {
1013:     $args['link_text'] = $link_text;
1014:     $args['url'] = isset( $this->_urls[$url] ) ? $this->_urls[$url]['url_template'] : $url;
1015:     $this->_meta_links[$link_text] = $args;
1016:   }
1017: 
1018:   /**
1019:    * @param string $page_name
1020:    *
1021:    * @return Sidecar_Admin_Page
1022:    */
1023:   function get_admin_page( $page_name ) {
1024:     $page_slug = preg_replace( "#^{$this->plugin_slug}-(.*)$#", '$1', $page_name );
1025:     return isset( $this->_admin_pages[$page_slug] ) ? $this->_admin_pages[$page_slug] : false;
1026:   }
1027: 
1028:   /**
1029:    *
1030:    */
1031:   function add_default_shortcode() {
1032:     $this->add_shortcode( $this->plugin_slug );
1033:   }
1034: 
1035:   /**
1036:    * @param       $shortcode_name
1037:    * @param array $args
1038:    */
1039:   function add_shortcode( $shortcode_name, $args = array() ) {
1040:     $args['plugin'] = $this;
1041:     $this->_shortcodes[ $shortcode_name ] = new Sidecar_Shortcode( $shortcode_name, $args );
1042:   }
1043: 
1044:   /**
1045:    * @return array|bool
1046:    */
1047:   function get_shortcodes() {
1048:     return $this->_shortcodes;
1049:   }
1050:   /**
1051:    * @param bool|string $shortcode_name
1052:    *
1053:    * @return Sidecar_Shortcode
1054:    */
1055:   function get_shortcode( $shortcode_name = false ) {
1056:     $shortcode = false;
1057: 
1058:     if ( ! $shortcode_name )
1059:       $shortcode_name = $this->plugin_slug; // This is the 'default' shortcode
1060: 
1061:     if ( ! isset( $this->_shortcodes[$shortcode_name] ) ) {
1062:       trigger_error( sprintf( __( 'Need to call %s->initialize_shortcodes() before using %s->get_shortcode().' ), $this->plugin_class, $this->plugin_class ) );
1063:     } else {
1064:       /**
1065:        * @var Sidecar_Shortcode $shortcode
1066:        */
1067:       $shortcode = $this->_shortcodes[$shortcode_name];
1068:       if ( ! $shortcode->initialized ) {
1069:         $this->initialize_shortcode($shortcode);
1070:         $shortcode->initialized = true;
1071:       }
1072:     }
1073:     return $shortcode;
1074:   }
1075: 
1076:   /**
1077:    * @param bool|string $shortcode_name
1078:    *
1079:    * @return bool|Sidecar_Shortcode
1080:    */
1081:   function get_shortcode_attributes( $shortcode_name = false ) {
1082:     $shortcode = $this->get_shortcode($shortcode_name);
1083:     return $shortcode ? $shortcode->get_attributes() : false;
1084:   }
1085: 
1086: //  /**
1087: //   * @return mixed
1088: //   */
1089: //  protected function _get_subclass_filename() {
1090: //    $subclass = new ReflectionObject( $this );
1091: //    return $subclass->getFileName();
1092: //  }
1093: //
1094: //  /**
1095: //   * @param $schedules
1096: //   *
1097: //   * @return mixed
1098: //   */
1099: //  function _cron_schedules( $schedules ) {
1100: //      $schedules['fifteenseconds'] = array( 'interval' => 15, 'display' => __( 'Once Every Fifteen Seconds' ) );
1101: //      return $schedules;
1102: //  }
1103: 
1104: //  /**
1105: //   * @return bool
1106: //   */
1107: //  function _cron() {
1108: //    return true;
1109: //  }
1110: 
1111: //  function _deactivate() {
1112: //      /**
1113: //       * Unschedule cron
1114: //       */
1115: //      $next_run = wp_next_scheduled( $this->cron_key );
1116: //      wp_unschedule_event( $next_run, $this->cron_key );
1117: //  }
1118: 
1119:   /**
1120:    * @param string $url_name
1121:    * @param string $url_template
1122:    * @param array $args
1123:    */
1124:   function register_url( $url_name, $url_template, $args = array() ) {
1125:     $args['url_name'] = $url_name;
1126:     $args['url_template'] = $url_template;
1127:     if ( ! isset( $args['url_vars'] ) )
1128:       $args['url_vars'] = false;
1129:     $this->_urls[$url_name] = $args;
1130:   }
1131: 
1132:   /**
1133:    * @param string $url_name
1134:    *
1135:    * @return bool
1136:    */
1137:   function has_url( $url_name ) {
1138:     return isset( $this->_urls[$url_name] );
1139:   }
1140:   /**
1141:    * Get by name a previously registered URL with optional variable value replacement
1142:    *
1143:    * @param             $url_name
1144:    * @param mixed|array $values
1145:      * @example:
1146:    *
1147:    *    $this->register_url( 'my_named_url', 'http://example.com/?item={item_id}&color={color}' );
1148:    *    $this->get_url( 'my_named_url', 1234, 'red' );
1149:      *    $this->get_url( 'my_named_url', array( 2345, 'red' ) );
1150:      *    $this->get_url( 'my_named_url', array( 'color' => 'red', 'item_id' => 3456 ) );
1151:    *
1152:    * @return string
1153:    */
1154:   function get_url( $url_name, $values = array() ) {
1155:     if ( ! is_array( $values ) ) {
1156:       /**
1157:        * $values passed as additional function parameters instead of as single array of parameters.
1158:        */
1159:       $values = func_get_args();
1160:       array_shift( $values );
1161:     }
1162:     $url = $this->_urls[$url_name]['url_template'];
1163:     if ( is_array( $values ) ) {
1164:       $vars = $this->_urls[$url_name]['url_vars'];
1165:       if ( ! $vars ) {
1166:         preg_match_all( '#([^{]+)\{([^}]+)\}#', $url, $matches );
1167:         $vars = $matches[2];
1168:       }
1169:       if ( is_numeric( key($values) ) ) {
1170:         /**
1171:          * The $values array contains name/value pairs.
1172:          */
1173:         foreach( $values as $name => $value ) {
1174:           if ( isset( $vars[0] ) )
1175:             $url = str_replace( "{{$vars[0]}}", $value, $url );
1176:         }
1177:       } else {
1178:         /**
1179:          * The $values array just contains values in same order as the vars specified in the URL.
1180:          */
1181:         foreach( $vars as $name ) {
1182:           if ( isset( $values[$name] ) )
1183:             $url = str_replace( "{{$name}}", $values[$name], $url );
1184:         }
1185:       }
1186:     }
1187:     return $url;
1188:     }
1189: 
1190:   /**
1191:    * @param $url_name
1192:    *
1193:    * @return bool
1194:    */
1195:   function get_link_text( $url_name ) {
1196:     return isset( $this->_urls[$url_name]['link_text'] ) ? $this->_urls[$url_name]['link_text'] : false;
1197:     }
1198: 
1199:   /**
1200:    * @param $url_name
1201:    *
1202:    * @return bool
1203:    */
1204:   function get_link_class( $url_name ) {
1205:     return isset( $this->_urls[$url_name]['link_class'] ) ? " class=\"{$this->_urls[$url_name]['link_class']}\"" : false;
1206:     }
1207: 
1208:   /**
1209:    * @param string $image_name
1210:    * @param string $image_url
1211:    * @param array $args
1212:    */
1213:   function register_image( $image_name, $image_url, $args = array() ) {
1214:     $args['image_name'] = $image_name;
1215:     $args['image_url'] = $image_url;
1216:     if ( ! isset( $args['url_vars'] ) )
1217:       $args['image_vars'] = false;
1218:     $this->_images[$image_name] = $args;
1219:   }
1220: 
1221:   /**
1222:    * @param string $image_name
1223:    *
1224:    * @return bool
1225:    */
1226:   function has_image( $image_name ) {
1227:     return isset( $this->_images[$image_name] );
1228:   }
1229: 
1230:   /**
1231:    * Get by name a previously registered image with optional variable value replacement
1232:    *
1233:    * @param             $image_name
1234:    * @param mixed|array $values
1235:      * @example:
1236:    *
1237:    *    $this->register_image( 'my_logo', 'my-logo.png' );
1238:    *    echo $this->my_logo_image_url;
1239:    *
1240:    *    $this->register_image( 'my_icon', '{icon_type}.png' );
1241:    *    echo $this->get_image_url( 'my_icon', 'pdf' );
1242:    *    echo $this->get_image_url( 'my_icon', array('pdf') );
1243:    *    echo $this->get_image_url( 'my_icon', array('icon_type' => 'pdf') );
1244:    *
1245:    * @return string
1246:    */
1247:   function get_image_url( $image_name, $values = array() ) {
1248:     if ( ! is_array( $values ) ) {
1249:       /**
1250:        * $values passed as additional function parameters instead of as single array of parameters.
1251:        */
1252:       $values = func_get_args();
1253:       array_shift( $values );
1254:     }
1255:     $image_url = $this->_images[$image_name]['image_url'];
1256:     if ( is_array( $values ) ) {
1257:       $vars = $this->_images[$image_name]['image_vars'];
1258:       if ( ! $vars ) {
1259:         preg_match_all( '#\{([^}]+)\}#', $image_url, $matches );
1260:         $vars = $matches[1];
1261:       }
1262:       if ( is_numeric( key($values) ) ) {
1263:         /**
1264:          * The $values array contains name/value pairs.
1265:          */
1266:         foreach( $values as $value ) {
1267:           $image_url = str_replace( "{{$vars[0]}}", $value, $image_url );
1268:         }
1269:       } else {
1270:         /**
1271:          * The $values array just contains values in same order as the vars specified in the image.
1272:          */
1273:         foreach( $vars as $name ) {
1274:           $image_url = str_replace( "{{$name}}", $values[$name], $image_url );
1275:         }
1276:       }
1277:     }
1278:     if ( ! preg_match( '#^https?//#', $image_url ) ) {
1279:       $image_url = plugins_url( "/images/{$image_url}", $this->plugin_file );
1280:     }
1281:     return $image_url;
1282:     }
1283: //  /**
1284: //   * @return bool
1285: //   */
1286: //  function is_authenticated() {
1287: //      return false;
1288: //  }
1289: 
1290:   /**
1291:    * Echo the current or specified form.
1292:    *
1293:    * @param bool|Sidecar_Form $form
1294:    *
1295:    * @return Sidecar_Form
1296:    */
1297:   function the_form( $form = false ) {
1298:     if ( ! $form )
1299:       $form = $this->get_current_form();
1300:     return $form->the_form();
1301:     }
1302: 
1303:   /**
1304:    * @param   array   $form
1305:    * @return  Sidecar_Form
1306:    */
1307:   function promote_form( $form ) {
1308:     /**
1309:      * @var array $form
1310:      */
1311:     $form_name = $form['form_name'];
1312:     $form['plugin'] = $this;
1313: 
1314:     if ( ! isset( $form['admin_page'] ) )
1315:       $form['admin_page'] = end( $this->_admin_pages );
1316: 
1317:     /**
1318:      * @var array|Sidecar_Form $form
1319:      */
1320:     $form = $this->_forms[$form_name] = new Sidecar_Form( $form_name, $form );
1321: 
1322:     $this->set_current_form( $form );
1323:     $this->initialize_form( $form );
1324:     $form->initialize();
1325:     return $form;
1326:   }
1327:   /**
1328:    * @param array $args
1329:    */
1330:   function the_form_section( $args ) {
1331:     if ( ! empty( $args['section']->section_text ) )
1332:       echo $args['section']->section_text;
1333:   }
1334: 
1335:   /**
1336:    * @param $args
1337:    *
1338:    * @return Sidecar_Field
1339:    */
1340:   function get_form_field( $field_name, $form_name ) {
1341:     /**
1342:      * @var Sidecar_Form $form
1343:      */
1344:     $form = $this->get_form( $form_name );
1345:     return $form ? $form->get_field( $field_name ) : false;
1346:   }
1347:   /**
1348:    * @param array $args
1349:    */
1350:   function get_form_field_html( $field_name, $form_name ) {
1351:     /**
1352:      * @var Sidecar_Field $field
1353:      */
1354:     $field = $this->get_form_field( $field_name, $form_name );
1355:     return $field->get_html();
1356:   }
1357: 
1358:   /**
1359:    * @param array $args
1360:    */
1361:   function the_form_field( $field_name, $form_name ) {
1362:     echo $this->get_form_field_html( $field_name, $form_name );
1363:   }
1364: 
1365:   /**
1366:    * @param string $shortcode_name
1367:    * @return bool
1368:    */
1369:   function has_shortcode( $shortcode_name ) {
1370:     return isset( $this->_shortcodes[$shortcode_name] );
1371:   }
1372:   /**
1373:    * @param   string|Sidecar_Form $form
1374:    * @return  array|Sidecar_Form
1375:    */
1376:   function get_form( $form ) {
1377:     /*
1378:      * Could be a string or already Sidecar_Form.
1379:      */
1380:     $form = is_string( $form ) && isset( $this->_forms[$form] ) ? $this->_forms[$form] : $form;
1381:     if ( is_array( $form ) )
1382:       $form = $this->promote_form( $form );
1383:     return is_object( $form ) ? $form : false;
1384:   }
1385:   /**
1386:    * @param string  $form_name
1387:    * @param array   $args
1388:    */
1389:   function add_form( $form_name, $args = array() ) {
1390:     $args['form_name'] = $form_name;
1391:     if ( ! isset( $args['requires_api'] ) && 'account' == $form_name )
1392:       $args['requires_api'] = true;
1393:     $this->_forms[$form_name] = $args;
1394:   }
1395:   /**
1396:    * @param       $form_name
1397:    * @param array $args
1398:    */
1399: 
1400:   /**
1401:    * @param string  $form_name
1402:    * @param array   $args
1403:    * @return Sidecar_Field
1404:    */
1405:   function add_field( $form_name, $args = array() ) {
1406:     /**
1407:      * @var Sidecar_Form
1408:      */
1409:     $form = isset( $args['form'] ) ? $args['form'] : end( $this->_forms );
1410:     return $form->add_field( $form_name, $args );
1411:   }
1412: 
1413:   /**
1414:    * @return bool
1415:    */
1416:   function needs_grant() {
1417:     return $this->has_api();
1418:   }
1419:   /**
1420:    * Determines if the currently stored settings contain a grant to access the API.
1421:    *
1422:    * @return bool
1423:    */
1424:   function has_grant() {
1425:     $has_grant = false;
1426:     if ( $this->needs_grant() ) {
1427:       $has_grant = $this->get_api()->is_grant( $this->get_auth_form()->get_settings_values() );
1428:     }
1429:     return $has_grant;
1430:   }
1431: 
1432:   /**
1433:    * Get grant from the currently stored account settings
1434:    *
1435:    * @return array
1436:    */
1437:   function get_grant() {
1438:     /**
1439:      * @var RESTian_Auth_Provider_Base $auth_provider
1440:      */
1441:     $auth_provider = $this->get_api()->get_auth_provider();
1442:     return $auth_provider->extract_grant( $this->get_auth_form()->get_settings_values() );
1443:   }
1444: 
1445:   /**
1446:    * Get credentials from the currently stored account settings
1447:    *
1448:    * @return array
1449:    */
1450:   function get_credentials() {
1451:     /**
1452:      * @var RESTian_Auth_Provider_Base $auth_provider
1453:      */
1454:     $auth_provider = $this->get_api()->get_auth_provider();
1455:     return $auth_provider->extract_credentials( $this->get_auth_form()->get_settings_values() );
1456:   }
1457: 
1458:   /**
1459:    * @return Sidecar_Admin_Page
1460:    */
1461:   function get_current_admin_page() {
1462:     if ( ! isset( $this->_current_admin_page ) ) {
1463:       if ( ! isset( $_GET['page'] ) || ! is_admin() ) {
1464:         $this->_current_admin_page = false;
1465:       } else {
1466:         $tab = isset( $_GET['tab'] ) ? $_GET['tab'] : $this->get_admin_page($_GET['page'])->get_default_tab()->tab_slug;
1467:         /**
1468:          * If we have a $_GET['page'] then is should be "{$plugin_slug}-{$page_slug}"
1469:          * Scan through the array to find it.
1470:          */
1471:         foreach( array_keys( $this->_admin_pages ) as $admin_page_slug ) {
1472:           if ( "{$this->plugin_slug}-{$admin_page_slug}" == $_GET['page'] ) {
1473:             $this->_current_admin_page = $this->get_admin_page( $admin_page_slug );
1474:             break;
1475:           }
1476:         }
1477:       }
1478:     }
1479:     return $this->_current_admin_page;
1480:   }
1481: 
1482:   /**
1483:    * @param Sidecar_Admin_Page $current_admin_page
1484:    */
1485:   function set_current_admin_page( $current_admin_page ) {
1486:     $this->_current_admin_page = $current_admin_page;
1487:   }
1488: 
1489:   /**
1490:    * @return Sidecar_Form
1491:    */
1492:   function get_current_form() {
1493:     return $this->_current_form;
1494:   }
1495: 
1496:   /**
1497:    * @param Sidecar_Admin_Page $current_form
1498:    */
1499:   function set_current_form( $current_form ) {
1500:     $this->_current_form = $current_form;
1501:   }
1502: 
1503:   /**
1504:    * Capture values from form but cause update_option() to be bypassed. We'll update in the shutdown hook.
1505:    *
1506:    * @param array $new_value
1507:    * @param array $old_value
1508:    * @return array
1509:    */
1510:   function _pre_update_option( $new_value, $old_value ) {
1511:     /**
1512:      * This only going to be saving one form's worth of data yet the settings can have many forms, like:
1513:      *
1514:      *    $settings = array(
1515:      *      '_form1' => array( ... ),
1516:      *      '_form2' => array( ... ),
1517:      *      '_form3' => array( ... ),
1518:      *    );
1519:      *
1520:      * So the next 3 lines grab all the old values of the other forms and uses the new values for this form.
1521:      */
1522:     if ( ! isset( $new_value['state'] ) ) {
1523:       $return_value = $new_value;
1524:     } else {
1525:       $return_value = $old_value;
1526:       $form_name = $new_value['state']['form'];
1527:       $old_value[$form_name] = $new_value[$form_name];
1528:       $old_value['state'] = $new_value['state'];
1529:       /**
1530:        * Set the 'decrytped' value to 'true' for the form that is being submitted.
1531:        */
1532:       $old_value['state']['decrypted'][$new_value['state']['form']] = true;
1533: 
1534:       /*
1535:        * @todo Need to fix this update_settings_option() to save the correct info.
1536:        * @todo Also need to provide an extensibility method.
1537:        */
1538:       $this->update_settings_option( (object)array(
1539: 
1540:       ));
1541:     }
1542: 
1543:     return $return_value;
1544:   }
1545:   /**
1546: 
1547:    * @todo Decide if trigger_error() is the best for error messages
1548:    */
1549:   function _activate() {
1550:     if ( ! $this->_initialized )
1551:       $this->initialize();
1552: 
1553:     if ( method_exists( $this, 'activate' ) )
1554:       $this->activate();
1555: 
1556:     global $wp_version;
1557:     if ( version_compare( $wp_version, $this->min_wp, '<' ) ) {
1558:       deactivate_plugins( basename( $this->plugin_file ) );
1559:       $msg = __( 'Your site needs to be running WordPress %s or later in order to use %s.', 'sidecar' );
1560:       trigger_error( sprintf( $msg, $this->min_wp, $this->plugin_title ), E_USER_ERROR );
1561:     } if ( version_compare( PHP_VERSION, $this->min_php, '<' ) ) {
1562:       deactivate_plugins( basename( $this->plugin_file ) );
1563:       $msg = __( 'Your site needs to be running PHP %s or later in order to use %s.', 'sidecar' );
1564:       trigger_error( sprintf( $msg, $this->min_php, $this->plugin_title ), E_USER_ERROR );
1565:     } else {
1566:       // @todo Add simplified support for cron when we see a use-case for it.
1567:       //if ( ! wp_next_scheduled( $this->cron_key ) ) {
1568:       //  wp_schedule_event( time(), $this->cron_recurrance, $this->cron_key );
1569:       //}
1570:       /*
1571:        * If we have existing settings and we are either upgrading or reactivating we
1572:        * previously had a _credentials element then reauthenticate and record that change.
1573:        */
1574:       if ( $this->has_api() ) {
1575:         $api = $this->get_api();
1576:         /**
1577:          * @var RESTian_Auth_Provider_Base $auth_provider
1578:          */
1579:         $auth_provider = $api->get_auth_provider();
1580:         $auth_form = $this->get_auth_form();
1581:         if ( ! $auth_form )
1582:           wp_die( __( 'There is no auth form configured. Call $admin_page->set_auth_form( $form_name ) inside initialize_admin_page( $admin_page ).', 'sidecar' ) );
1583: 
1584:         $account_settings = $auth_form->get_settings();
1585:         $credentials = $auth_provider->extract_credentials( $account_settings->get_settings_values() );
1586:         $credentials = array_merge( $auth_provider->get_new_credentials(), $credentials );
1587:         $credentials = $auth_provider->prepare_credentials( $credentials );
1588: 
1589:         /**
1590:          * 'account' contains credentials and grant merged
1591:          */
1592:         if ( ! $auth_provider->is_credentials( $credentials ) ) {
1593:           /**
1594:            * Allow the auth provider to establish defaults in the grant if needed.
1595:            * This is an unusual need, but Lexity.com needed it.
1596:            */
1597:           $grant = $auth_provider->prepare_grant( $auth_provider->get_new_grant(), $credentials );
1598: 
1599:         } else {
1600:           /**
1601:            * Attempt to authenticate with available credentials
1602:            */
1603:           $response = $api->authenticate( $credentials );
1604:           /**
1605:            * If authenticated get the updated grant otherwise get an empty grant
1606:            */
1607:           $grant = $response->authenticated ? $response->grant : $auth_provider->get_new_grant();
1608: 
1609:           /**
1610:            * Allow the auth provider to establish defaults in the grant if needed.
1611:            * This is an unusual need, but Lexity.com needed it.
1612:            */
1613:           $grant = $auth_provider->prepare_grant( $grant, $credentials );
1614: 
1615:         }
1616:         /**
1617:          * Merge credentials and grant back into $settings['_account']
1618:          */
1619:         $account_settings->update_settings_values( array_merge( $credentials, $grant ) );
1620: 
1621:       }
1622: 
1623:       $settings = $this->get_settings();
1624:       $settings->installed_version = $this->plugin_version;
1625:       $settings->update_settings();
1626: 
1627:     }
1628:   }
1629: 
1630:   /**
1631:    *
1632:    */
1633:   function _initialize_admin() {
1634:     if ( ! $this->_admin_initialized ) {
1635:       if ( ! method_exists( $this, 'initialize_admin' ) ) {
1636:         trigger_error( __( 'Plugin must define a $plugin->initialize_admin() method..', 'sidecar' ) );
1637:         exit;
1638:       }
1639:       $this->initialize_admin();
1640: 
1641:       $this->_admin_initialized = true;
1642: 
1643:       if ( $this->_api )
1644:         $this->_api->maybe_set_grant( $this->get_grant() );
1645: 
1646:     }
1647:   }
1648:   /**
1649:    * @return Sidecar_Form
1650:    */
1651:   function get_auth_form() {
1652:     $this->_initialize_admin();
1653:     $auth_form = false;
1654:     /**
1655:      * @var Sidecar_Admin_Page $page
1656:      */
1657:     foreach( $this->_admin_pages as $page ) {
1658:       $this->initialize_admin_page( $page );
1659:       if ( $test = $page->get_auth_form() ) {
1660:         $auth_form = $test;
1661:         break;
1662:       }
1663:     }
1664:     return $auth_form;
1665:   }
1666: 
1667:   /**
1668:    * @param $property_name
1669:    * @return bool|string
1670:    */
1671:   function __get( $property_name ) {
1672:     $value = false;
1673:     if ( preg_match( '#^(.*?_(icon|image|photo))_url$#', $property_name, $match ) && $this->has_image( $match[1] ) ) {
1674:       $value = call_user_func( array( $this, "get_image_url" ), $match[1] );
1675:     } else if ( preg_match( '#^(.*?)_url$#', $property_name, $match ) && $this->has_url( $match[1] ) ) {
1676:       /**
1677:        * Allows magic property syntax for any registered URL
1678:        * @example: $this->foobar_url calls $this-get_url( 'foobar' )
1679:        * Enables embedding in a HEREDOC or other doublequoted string
1680:        * without requiring an intermediate variable.
1681:        */
1682:       $value = $this->get_url( $match[1] );
1683:     } else if ( preg_match( '#^(.*?)_link$#', $property_name, $match ) && $this->has_url( $match[1] ) ) {
1684:       /**
1685:        * Same kind of this as _url above.
1686:        */
1687:       $url = $this->get_url( $match[1] );
1688:       $link_text = $this->get_link_text( $match[1] );
1689:       $class = $this->get_link_class( $match[1] );
1690:       $value = "<a target=\"_blank\"{$class} href=\"{$url}\">{$link_text}</A>";
1691:     } else {
1692:       Sidecar::show_error( 'No property named %s on %s class.', $property_name, get_class( $this ) );
1693:     }
1694:     return $value;
1695:   }
1696: }
1697: 
API documentation generated by ApiGen 2.8.0