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:  */
   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_Plugin_Settings
 135:    */
 136:   protected $_plugin_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: https://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: https://wordpress.stackexchange.com/questions/15202/plugins-in-symlinked-directories
 346:        * @see: https://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:     $this->_plugin_settings = new Sidecar_Plugin_Settings( $this, $this->option_name );
 374: 
 375:     /**
 376:      * Ask subclass to initialize plugin which includes admin pages
 377:      */
 378:     $this->initialize_plugin();
 379: 
 380:   }
 381: 
 382:   /**
 383:    * Delete the flag indicating that a post needs external files (CSS styles and JS scripts) for each
 384:    * shortcode we have in case the newly saved post now has changed the use of the shortcodes.
 385:    */
 386:   function _save_post( $post_id ) {
 387:     $this->initialize_shortcodes();
 388:     $shortcodes = $this->get_shortcodes();
 389:     if ( is_array( $shortcodes ) ) {
 390:       /**
 391:        * @var Sidecar_Shortcode $shortcode
 392:        */
 393:       foreach( $shortcodes as $shortcode )
 394:         $shortcode->delete_has_shortcode( $post_id );
 395:       /**
 396:        * Now load the post asynchronously via HTTP to pre-set the meta value for $this->HAS_SHORTCODE_KEY.
 397:        */
 398:       wp_remote_request( get_permalink( $post_id ), array( 'blocking' => false ) );
 399:     }
 400:   }
 401: 
 402:   /**
 403:    * Used to check if we are on a plugin page that is asking about deletion.
 404:    *
 405:    * @return bool
 406:    */
 407:   function is_confirm_plugin_deletion() {
 408:     return $this->is_plugin_deletion()
 409:       && ! isset( $_POST['verify-delete'] );
 410:   }
 411: 
 412:   /**
 413:    * Used to check if we are on a plugin page that is deleting (a) plugin(s).
 414:    *
 415:    * @return bool
 416:    */
 417:   function is_verified_plugin_deletion() {
 418:     return $this->is_plugin_deletion()
 419:       && isset( $_POST['verify-delete'] ) &&  '1' == $_POST['verify-delete'];
 420:   }
 421: 
 422:   /**
 423:    * Used to check if we are a plugin page askin about deletion or processing deletion request.
 424:    *
 425:    * @return bool
 426:    */
 427:   function is_plugin_deletion() {
 428:     return $this->is_plugin_page()
 429:       && isset( $_GET['action'] ) &&  'delete-selected' == $_GET['action']
 430:       && isset( $_REQUEST['checked'] ) && count( $_REQUEST['checked'] );
 431:   }
 432: 
 433:   /**
 434:    *
 435:    */
 436:   static function uninstall() {
 437: 
 438:     /**
 439:      * @var Sidecar_Plugin_Base $plugin
 440:      */
 441:     $plugin = self::this();
 442: 
 443:     /**
 444:      * Initialize it so we can ensure all properties are set in case $plugin->uninstall_plugin() needs them.
 445:      */
 446:     $plugin->initialize();
 447: 
 448:     /**
 449:      * Delete settings
 450:      */
 451:     $plugin->delete_settings();
 452: 
 453:     /*
 454:      * Call subclass' uninstall if applicable.
 455:      */
 456:     $plugin->uninstall_plugin();
 457: 
 458:   //    /**
 459:   //     * Delete cron tasks
 460:   //     */
 461:   //    $next_run = wp_next_scheduled( $plugin->cron_key );
 462:   //    wp_unschedule_event( $next_run, $plugin->cron_key );
 463: 
 464:   }
 465: 
 466:   /**
 467:    * Used to check if we are in an activation callback on the Plugins page.
 468:    *
 469:    * @return bool
 470:    */
 471:   function is_plugin_page() {
 472:     global $pagenow;
 473:     return 'plugins.php' == $pagenow;
 474:   }
 475: 
 476:   /**
 477:    * Used to check if we are in an activation callback on the Plugins page.
 478:    *
 479:    * @return bool
 480:    */
 481:   function is_plugin_page_action() {
 482:     return $this->is_plugin_page()
 483:       && isset( $_GET['action'] )
 484:       && isset( $_GET['plugin'] );
 485:   }
 486: 
 487: //  /**
 488: //   * Used to check if we are activating a plugin.
 489: //   *
 490: //   * @return bool
 491: //   */
 492: //  function is_plugin_activation() {
 493: //    return $this->is_plugin_page_action()
 494: //      && 'activate' == $_GET['action'];
 495: //  }
 496: 
 497:   /**
 498:    * This is used for the "activate_{$this->plugin_id}" hook
 499:    * when $this->is_plugin_page_action().
 500:    */
 501:   function _activate_plugin() {
 502:     $this->initialize();
 503:   }
 504: 
 505:   /**
 506:    * This is used for the "activate_{$this->plugin_id}" hook
 507:    * when $this->is_plugin_page_action().
 508:    */
 509:   function _plugins_loaded() {
 510:     $this->initialize();
 511:   }
 512: 
 513:   /**
 514:    * @return array|bool
 515:    */
 516:   function get_forms() {
 517:     foreach( $this->_forms as $form_name => $form )
 518:       if ( is_array( $form ) )
 519:         $this->_forms[$form_name] = $this->promote_form( $form );
 520:     return $this->_forms;
 521:   }
 522: 
 523:   /**
 524:    * @return bool
 525:    */
 526:   function has_forms() {
 527:     return is_array( $this->_forms ) && count( $this->_forms );
 528:   }
 529: 
 530:   /**
 531:    *
 532:    */
 533:   function _wp_print_styles() {
 534:       $localfile = 'css/style.css';
 535:     $args = apply_filters( "sidecar_print_{$this->plugin_name}_styles", array(
 536:       'name'  => "{$this->plugin_name}_style",
 537:       'path'  => "{$this->plugin_path}/{$localfile}",
 538:       'url'   => plugins_url( $localfile, $this->plugin_file ),
 539:     ));
 540:       if ( file_exists( $args['path'] ) )
 541:           wp_enqueue_style( $args['name'], $args['url'] );
 542:     }
 543: 
 544:   /**
 545:    * @return bool
 546:    */
 547:   function has_admin_pages() {
 548:     return 0 < count( $this->_admin_pages );
 549:   }
 550:   /**
 551:    * @param string $form_name
 552:    *
 553:    * @return bool
 554:    */
 555:   function has_form( $form_name ) {
 556:     return isset( $this->_forms[$form_name] );
 557:   }
 558: 
 559:   function initialize() {
 560:     if ( $this->_initialized )
 561:       return;
 562: 
 563:     /*
 564:      * Avoid potential infinite loop.
 565:      */
 566:     $this->_initialized = true;
 567: 
 568:     if ( ! $this->plugin_title )
 569:       /*
 570:        * Hope we can get something better in the subclass' $this->initialize()...
 571:        */
 572:       $this->plugin_title = ucwords( str_replace( '_', ' ', $this->plugin_name ) );
 573: 
 574:     if ( ! $this->plugin_label )
 575:       /*
 576:        * Used for menu title for default
 577:        */
 578:       $this->plugin_label = $this->plugin_title;
 579: 
 580:     if ( ! $this->plugin_file )
 581:       Sidecar::show_error( '%s->plugin_file must be set in an %s->initialize_admin() method', get_class( $this), get_class( $this) );
 582: 
 583: //    $this->plugin_file_base = preg_replace( '#^(.*)-plugin\.php$#', '$1', $this->plugin_file );
 584: 
 585:     if ( ! $this->plugin_path )
 586:       $this->plugin_path = dirname( $this->plugin_file );
 587: 
 588:     if ( ! $this->plugin_slug )
 589:       $this->plugin_slug = str_replace( '_', '-', $this->plugin_name );
 590: 
 591:     if ( ! $this->css_base )
 592:       $this->css_base = $this->plugin_slug;
 593: 
 594:     $this->_plugin_settings->load_settings();
 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()->set_values( $settings_values );
 620:   }
 621: 
 622:   /**
 623:    * @param string|Sidecar_Form $form
 624:    * @param string $setting_name
 625:    * @return array
 626:    */
 627:   function get_form_settings_value( $form, $setting_name ) {
 628:     if ( ! $form instanceof Sidecar_Form )
 629:       $form = $this->get_form( $form );
 630:     return $this->get_form_settings( $form )->get_setting( $setting_name );
 631: 
 632:   }
 633: 
 634:   /**
 635:    * @param string|Sidecar_Form $form
 636:    * @param string $setting_name
 637:    * @param string $value
 638:    * @return array
 639:    */
 640:   function update_form_settings_value( $form, $setting_name, $value ) {
 641:     if ( ! $form instanceof Sidecar_Form )
 642:       $form = $this->get_form( $form );
 643:     return $form->update_settings_value( $setting_name, $value );
 644:   }
 645: 
 646:   /**
 647:    * @return bool
 648:    */
 649:   function has_required_settings() {
 650:     $has_required_settings = true;
 651:     if ( ! $this->_initialized )
 652:       $this->initialize();
 653:     if ( $this->has_forms() ) {
 654:       /** @var Sidecar_Form $form */
 655:       foreach( $this->get_forms() as $form_name => $form ) {
 656:         $form_settings = $this->get_form_settings( $form->form_name );
 657:         $form_settings->set_required_fields( $form->get_required_field_names() );
 658:         if ( ! $form_settings->has_required_fields() ) {
 659:           $has_required_settings = false;
 660:           break;
 661:         }
 662:       }
 663:     }
 664:     if ( method_exists( $this, 'filter_has_required_settings' ) ) {
 665:       $has_required_settings = $this->filter_has_required_settings( $has_required_settings, $this->get_settings() );
 666:     }
 667:     return $has_required_settings;
 668:   }
 669: 
 670: 
 671:   /**
 672:    * @param Sidecar_Settings $settings
 673:    */
 674:   function set_settings( $settings ) {
 675:     $this->_plugin_settings = $settings;
 676:   }
 677: 
 678:   /**
 679:    * @return Sidecar_Plugin_Settings
 680:    */
 681:   function get_settings() {
 682:     if ( ! $this->_initialized )
 683:       $this->initialize();
 684:     return $this->_plugin_settings;
 685:   }
 686: 
 687:   /**
 688:    * @param string|Sidecar_Form $form
 689:    * @return mixed|Sidecar_Form_Settings
 690:    */
 691:   function get_form_field_values( $form ) {
 692:     return $this->get_form_settings( $form );
 693:   }
 694: 
 695:   /**
 696:    * @param string|Sidecar_Form $form
 697:    * @param string $field_name
 698:    * @return mixed|Sidecar_Form_Settings
 699:    */
 700:   function get_form_field_value( $form, $field_name ) {
 701:     return $this->get_form_settings( $form )->get_values();
 702:   }
 703: 
 704:   /**
 705:    * @param string|Sidecar_Form $form
 706:    * @param array $field_values
 707:    * @return mixed|Sidecar_Form_Settings
 708:    */
 709:   function set_form_field_values( $form, $field_values ) {
 710:     $this->set_form_settings( $form, $field_values );
 711:   }
 712: 
 713:   /**
 714:    * @param string|Sidecar_Form $form
 715:    * @return mixed|Sidecar_Form_Settings
 716:    */
 717:   function get_form_settings( $form ) {
 718:     if ( ! $form instanceof Sidecar_Form )
 719:       $form = $this->get_form( $form );
 720:     return $this->get_settings()->get_setting( $form->form_name );
 721:   }
 722: 
 723:   /**
 724:    * @param string|Sidecar_Form $form
 725:    * @param mixed $setting_value
 726:    * @return mixed|Sidecar_Form_Settings
 727:    */
 728:   function set_form_settings( $form, $setting_value ) {
 729:     if ( ! $form instanceof Sidecar_Form )
 730:       $form = $this->get_form( $form );
 731:     $this->get_settings()->set_setting( $form->form_name, $setting_value );
 732:   }
 733: 
 734:   /**
 735:    * Delete the persisted settings on disk.
 736:    *
 737:    * @return bool
 738:    */
 739:   function delete_settings() {
 740:     $this->get_settings()->delete_settings();
 741:   }
 742: 
 743:   /**
 744:    *
 745:    */
 746:   function _wp_loaded() {
 747:     if ( is_admin() ) {
 748:       add_action( 'admin_notices',       array( $this, '_admin_notices' ) );
 749:       if ( $this->is_plugin_page() ) {
 750:         add_filter( 'plugin_action_links', array( $this, '_plugin_action_links' ), 10, 2 );
 751:         add_filter( 'plugin_row_meta',     array( $this, '_plugin_meta_links' ), 10, 2 );
 752:       }
 753:     } else {
 754:       $shortcodes = method_exists( $this->plugin_class, 'initialize_shortcodes' );
 755:       $template = method_exists( $this, 'initialize_template' );
 756:       if ( $shortcodes || $template ) {
 757:         // @todo Should we always initialize or only when we need it?
 758:         $this->initialize();
 759:         if ( $shortcodes )
 760:           $this->initialize_shortcodes();
 761:         if ( $template )
 762:           $this->initialize_template();
 763:         add_filter( 'the_content', array( $this, '_the_content' ), -1000 );
 764:       }
 765:     }
 766:   }
 767:   /**
 768:    */
 769:   function _admin_notices() {
 770:     if ( $this->needs_settings && ! $this->has_required_settings() && $this->is_plugin_page() && ! $this->is_confirm_plugin_deletion() ) {
 771:       $icon_html = $this->has_url( 'logo_icon' ) ? "<span class=\"sidecar-logo-icon\"></span><img src=\"{$this->logo_icon_url}\" /></span>" : '';
 772:       $message = sprintf( __( 'The <em>%s</em> plugin is now activated. Please configure it\'s <a href="%s"><strong>settings</strong></a>.', 'sidecar' ),
 773:       $this->plugin_title,
 774:       $this->get_settings_url()
 775:     );
 776:     $html = <<<HTML
 777: <div id="message" class="error settings-error">
 778:     <p>{$icon_html}{$message}</p>
 779: </div>
 780: HTML;
 781:     echo $html;
 782:     }
 783:   }
 784: 
 785:   /**
 786:    * @param $links
 787:    * @param $file
 788:    *
 789:    * @return array
 790:    */
 791:   function _plugin_action_links( $links, $file ){
 792:     if ( $file == $this->plugin_id  ) {
 793:       $url = $this->get_settings_url();
 794:       $link_text = __( 'Settings', 'sidecar' );
 795:       $links[] = "<a href=\"{$url}\">{$link_text}</a>";
 796:     }
 797:     return $links;
 798:   }
 799: 
 800:   /**
 801:    * @return bool|string|void
 802:    */
 803:   function get_settings_url() {
 804:     $settings_url = false;
 805:     if ( $settings_page = $this->get_admin_page( 'settings' ) ) {
 806:       $settings_page->initialize();
 807:       $settings_url = $this->get_admin_page( 'settings' )->get_page_url( null );
 808:     }
 809:     if ( method_exists( $this, 'filter_settings_url' ) )
 810:       $settings_url = $this->filter_settings_url( $settings_url );
 811:     return $settings_url;
 812:   }
 813:   /**
 814:    * @param array $links
 815:    * @param string $file
 816:    *
 817:    * @return array
 818:    */
 819:   function _plugin_meta_links( $links, $file ){
 820:     if ( $file == $this->plugin_id ) {
 821:       foreach( $this->_meta_links as $link_text => $link ) {
 822:         $title = isset( $link['title'] ) ? " title=\"{$link['title']}\"" : '';
 823:         $links[] = "<a target=\"_blank\" href=\"{$link['url']}\"{$title}>{$link_text}</a>";
 824:       }
 825:     }
 826:     return $links;
 827:   }
 828: 
 829:   /**
 830:    * @param $content
 831:    */
 832:   function _the_content( $content ) {
 833:     $shortcodes = $this->get_shortcodes();
 834:     if ( is_array( $shortcodes ) )
 835:       /**
 836:        * @var Sidecar_Shortcode $shortcode
 837:        */
 838:       foreach( $shortcodes as $shortcode_name => $shortcode ) {
 839:         if ( method_exists( $this, 'initialize_shortcode' ) ) {
 840:           $this->initialize_shortcode( $shortcode );
 841:         }
 842:         add_shortcode( $shortcode_name, array( $shortcode, 'do_shortcode' ) );
 843:         /**
 844:          * Now get each shortcode to monitor for it's own use.
 845:          */
 846:         $shortcode->add_the_content_filter();
 847:       }
 848:     return $content;
 849:   }
 850: 
 851:   function _init() {
 852: //    add_action( 'cron_schedules', array( $this, '_cron_schedules' ) );
 853: //    add_action( 'cron', array( $this, '_cron' ) );
 854:     /**
 855:      * @todo Figure out how to load this only if needed
 856:      */
 857:     load_plugin_textdomain( $this->plugin_slug, false, '/' . basename( dirname( $this->plugin_file ) ) . '/languages' );
 858: 
 859:     if ( is_admin() ) {
 860: 
 861:       /**
 862:        * @todo Can we initialize only what's needed if it is not using one of the pages
 863:        */
 864:       if (true) {
 865:         /**
 866:          * Now set all the defaults
 867:          */
 868:         $this->initialize();
 869: 
 870:         /**
 871:          * Call the subclass and ask it to initialize itself
 872:          */
 873:         $this->_initialize_admin();
 874: 
 875: //        if ( $this->plugin_version )
 876: //          $this->plugin_title .= sprintf( ' v%s', $this->plugin_version );
 877: 
 878:       }
 879:     }
 880:   }
 881:   /**
 882:    * @param string $to 'api_vars' or 'fields'
 883:    * @param Sidecar_Form $form
 884:    * @param array $fields
 885:    * @return array
 886:    */
 887:   function transform_form_fields_to( $to, $form, $fields ) {
 888:     $to = rtrim( $to, 's' );
 889:     $field_objects = $form->get_fields();
 890:     foreach( $fields as $field_name => $value ) {
 891:       if ( isset( $field_objects[$field_name] ) ) {
 892:         if ( $field_name != ( $new_name = $field_objects[$field_name]->$to ) ) {
 893:           if ( $new_name )  // If false then it's not an API var
 894:             $fields[$new_name] = $value;
 895:           unset( $fields[$field_name] );
 896:         }
 897:       }
 898:     }
 899:     return $fields;
 900:   }
 901: 
 902:   /**
 903:    * @param string $to 'api_vars' or 'settings'
 904:    * @param Sidecar_Shortcode $shortcode
 905:    * @param array $attributes
 906:    * @return array
 907:    */
 908:   function transform_shortcode_attributes_to( $to, $shortcode, $attributes ) {
 909:     $to = rtrim( $to, 's' );
 910:     $attribute_objects = $shortcode->get_attributes();
 911:     foreach( $attributes as $attribute_name => $attribute ) {
 912:       if ( isset( $attribute_objects[$attribute_name] ) ) {
 913:         if ( $attribute_name != ( $new_name = $attribute_objects[$attribute_name]->$to ) ) {
 914:           $attributes[$new_name] = $attribute;
 915:           unset( $attributes[$attribute_name] );
 916:         }
 917:       }
 918:     }
 919:     return $attributes;
 920:   }
 921: 
 922:   /**
 923:    * @param Sidecar_Form $form
 924:    */
 925:   function initialize_form( $form ) {
 926:     // Only here to keep PhpStorm from complaining that it's not defined.
 927:   }
 928: 
 929:   /**
 930:    *
 931:    */
 932:   function initialize_shortcodes() {
 933:     // Only here to keep PhpStorm from complaining that it's not defined.
 934:   }
 935: 
 936:   /**
 937:    *
 938:    */
 939:   function initialize_postback() {
 940:     // Only here to keep PhpStorm from complaining that it's not defined.
 941:   }
 942: 
 943:   /**
 944:    *
 945:    */
 946:   function initialize_template() {
 947:     // Only here to keep PhpStorm from complaining that it's not defined.
 948:   }
 949: 
 950:   /**
 951:    *
 952:    */
 953:   function uninstall_plugin() {
 954:     // Only here to keep PhpStorm from complaining that it's not defined.
 955:   }
 956: 
 957:   /**
 958:    * @throws Exception
 959:    */
 960:   function initialize_plugin() {
 961:     throw new Exception( 'Class ' . get_class($this) . ' [subclass of ' . __CLASS__ . '] must define an initialize_plugin() method.' );
 962:   }
 963: 
 964:   /**
 965:    * @throws Exception
 966:    */
 967:   function initialize_admin() {
 968:     throw new Exception( 'Class ' . get_class($this) . ' [subclass of ' . __CLASS__ . '] must define an initialize_admin() method.' );
 969:   }
 970: 
 971:   /**
 972:    * @param Sidecar_Admin_Page $admin_page
 973:    * @throws Exception
 974:    */
 975:   function initialize_admin_page( $admin_page ) {
 976:     throw new Exception( 'Class ' . get_class($this) . ' [subclass of ' . __CLASS__ . '] must define an initialize_admin_page() method.' );
 977:   }
 978: 
 979:   /**
 980:    * @param Sidecar_Shortcode $shortcode
 981:    * @throws Exception
 982:    */
 983:   function initialize_shortcode( $shortcode ) {
 984:     throw new Exception( 'Class ' . get_class($this) . ' [subclass of ' . __CLASS__ . '] must define an initialize_shortcode() method.' );
 985:   }
 986: 
 987:   /**
 988:    * @param Sidecar_Shortcode $shortcode
 989:    * @param array() $attributes
 990:    * @param string $content
 991:    *
 992:    * @throws Exception
 993:    * @return string
 994:    */
 995:   function do_shortcode( $shortcode, $attributes, $content = null ) {
 996:     if (1) // Only here to keep PhpStorm from flagging the return as an error.
 997:       throw new Exception( 'Class ' . get_class($this) . ' [subclass of ' . __CLASS__ . '] must define an do_shortcode() method.' );
 998:     return '';
 999:   }
1000: 
1001:   /**
1002:    * @param array $args
1003:    *
1004:    * @return mixed
1005:    */
1006:   function add_default_button( $args = array() ) {
1007:     /**
1008:      * @var Sidecar_Form
1009:      */
1010:     $form = isset( $args['form'] ) ? $args['form'] : end( $this->_forms );
1011:     return $form->add_button( 'save', __( 'Save Settings', 'sidecar' ), $args );
1012:   }
1013: 
1014:   /**
1015:    * @param string $button_name
1016:    * @param string $button_text
1017:    * @param array $args
1018:    *
1019:    * @return mixed
1020:    */
1021:   function add_button( $button_name, $button_text, $args = array() ) {
1022:     /**
1023:      * @var Sidecar_Form
1024:      */
1025:     $form = isset( $args['form'] ) ? $args['form'] : end( $this->_forms );
1026:     return $form->add_button( $button_name, $button_text, $args );
1027:   }
1028: 
1029:   /**
1030:    * @param       $page_name
1031:    * @param array $args
1032:    *
1033:    * @return mixed
1034:    */
1035:   function add_admin_page( $page_name, $args = array() ) {
1036:     /**
1037:      * Give the admin page access back to this plugin.
1038:      */
1039:     $args['plugin'] = $this;
1040:     /**
1041:      * @var Sidecar_Admin_Page $admin_page
1042:      */
1043:     $this->_admin_pages[$page_name] = new Sidecar_Admin_Page( $page_name, $args );
1044:   }
1045: 
1046:   /**
1047:    * @param string $link_text
1048:    * @param string $url
1049:    * @param array $args
1050:    */
1051:   function add_meta_link( $link_text, $url, $args = array() ) {
1052:     $args['link_text'] = $link_text;
1053:     $args['url'] = isset( $this->_urls[$url] ) ? $this->_urls[$url]['url_template'] : $url;
1054:     $this->_meta_links[$link_text] = $args;
1055:   }
1056: 
1057:   /**
1058:    * @param string $page_name
1059:    *
1060:    * @return Sidecar_Admin_Page
1061:    */
1062:   function get_admin_page( $page_name ) {
1063:     $page_slug = preg_replace( "#^{$this->plugin_slug}-(.*)$#", '$1', $page_name );
1064:     return isset( $this->_admin_pages[$page_slug] ) ? $this->_admin_pages[$page_slug] : false;
1065:   }
1066: 
1067:   /**
1068:    *
1069:    */
1070:   function add_default_shortcode() {
1071:     $this->add_shortcode( $this->plugin_slug );
1072:   }
1073: 
1074:   /**
1075:    * @param       $shortcode_name
1076:    * @param array $args
1077:    */
1078:   function add_shortcode( $shortcode_name, $args = array() ) {
1079:     $args['plugin'] = $this;
1080:     $this->_shortcodes[ $shortcode_name ] = new Sidecar_Shortcode( $shortcode_name, $args );
1081:   }
1082: 
1083:   /**
1084:    * @return array|bool
1085:    */
1086:   function get_shortcodes() {
1087:     return $this->_shortcodes;
1088:   }
1089: 
1090:   /**
1091:    * @param bool|string $shortcode_name
1092:    *
1093:    * @return Sidecar_Shortcode
1094:    */
1095:   function get_shortcode( $shortcode_name = false ) {
1096:     $shortcode = false;
1097: 
1098:     if ( ! $shortcode_name )
1099:       $shortcode_name = $this->plugin_slug; // This is the 'default' shortcode
1100: 
1101:     if ( ! isset( $this->_shortcodes[$shortcode_name] ) ) {
1102:       trigger_error( sprintf( __( 'Need to call %s->initialize_shortcodes() before using %s->get_shortcode().' ), $this->plugin_class, $this->plugin_class ) );
1103:     } else {
1104:       /**
1105:        * @var Sidecar_Shortcode $shortcode
1106:        */
1107:       $shortcode = $this->_shortcodes[$shortcode_name];
1108:       if ( ! $shortcode->initialized ) {
1109:         $this->initialize_shortcode($shortcode);
1110:         $shortcode->initialized = true;
1111:       }
1112:     }
1113:     return $shortcode;
1114:   }
1115: 
1116:   /**
1117:    * @param bool|string $shortcode_name
1118:    *
1119:    * @return bool|Sidecar_Shortcode
1120:    */
1121:   function get_shortcode_attributes( $shortcode_name = false ) {
1122:     $shortcode = $this->get_shortcode($shortcode_name);
1123:     return $shortcode ? $shortcode->get_attributes() : false;
1124:   }
1125: 
1126: //  /**
1127: //   * @return mixed
1128: //   */
1129: //  protected function _get_subclass_filename() {
1130: //    $subclass = new ReflectionObject( $this );
1131: //    return $subclass->getFileName();
1132: //  }
1133: //
1134: //  /**
1135: //   * @param $schedules
1136: //   *
1137: //   * @return mixed
1138: //   */
1139: //  function _cron_schedules( $schedules ) {
1140: //      $schedules['fifteenseconds'] = array( 'interval' => 15, 'display' => __( 'Once Every Fifteen Seconds' ) );
1141: //      return $schedules;
1142: //  }
1143: 
1144: //  /**
1145: //   * @return bool
1146: //   */
1147: //  function _cron() {
1148: //    return true;
1149: //  }
1150: 
1151: //  function _deactivate() {
1152: //      /**
1153: //       * Unschedule cron
1154: //       */
1155: //      $next_run = wp_next_scheduled( $this->cron_key );
1156: //      wp_unschedule_event( $next_run, $this->cron_key );
1157: //  }
1158: 
1159:   /**
1160:    * @param string $url_name
1161:    * @param string $url_template
1162:    * @param array $args
1163:    */
1164:   function register_url( $url_name, $url_template, $args = array() ) {
1165:     $args['url_name'] = $url_name;
1166:     $args['url_template'] = $url_template;
1167:     if ( ! isset( $args['url_vars'] ) )
1168:       $args['url_vars'] = false;
1169:     $this->_urls[$url_name] = $args;
1170:   }
1171: 
1172:   /**
1173:    * @param string $url_name
1174:    *
1175:    * @return bool
1176:    */
1177:   function has_url( $url_name ) {
1178:     return isset( $this->_urls[$url_name] );
1179:   }
1180:   /**
1181:    * Get by name a previously registered URL with optional variable value replacement
1182:    *
1183:    * @param             $url_name
1184:    * @param mixed|array $values
1185:      * @example:
1186:    *
1187:    *    $this->register_url( 'my_named_url', 'https://example.com/?item={item_id}&color={color}' );
1188:    *    $this->get_url( 'my_named_url', 1234, 'red' );
1189:      *    $this->get_url( 'my_named_url', array( 2345, 'red' ) );
1190:      *    $this->get_url( 'my_named_url', array( 'color' => 'red', 'item_id' => 3456 ) );
1191:    *
1192:    * @return string
1193:    */
1194:   function get_url( $url_name, $values = array() ) {
1195:     if ( ! is_array( $values ) ) {
1196:       /**
1197:        * $values passed as additional function parameters instead of as single array of parameters.
1198:        */
1199:       $values = func_get_args();
1200:       array_shift( $values );
1201:     }
1202:     $url = $this->_urls[$url_name]['url_template'];
1203:     if ( is_array( $values ) ) {
1204:       $vars = $this->_urls[$url_name]['url_vars'];
1205:       if ( ! $vars ) {
1206:         preg_match_all( '#([^{]+)\{([^}]+)\}#', $url, $matches );
1207:         $vars = $matches[2];
1208:       }
1209:       if ( is_numeric( key($values) ) ) {
1210:         /**
1211:          * The $values array contains name/value pairs.
1212:          */
1213:         foreach( $values as $name => $value ) {
1214:           if ( isset( $vars[0] ) )
1215:             $url = str_replace( "{{$vars[0]}}", $value, $url );
1216:         }
1217:       } else {
1218:         /**
1219:          * The $values array just contains values in same order as the vars specified in the URL.
1220:          */
1221:         foreach( $vars as $name ) {
1222:           if ( isset( $values[$name] ) )
1223:             $url = str_replace( "{{$name}}", $values[$name], $url );
1224:         }
1225:       }
1226:     }
1227:     return $url;
1228:     }
1229: 
1230:   /**
1231:    * @param $url_name
1232:    *
1233:    * @return bool
1234:    */
1235:   function get_link_text( $url_name ) {
1236:     return isset( $this->_urls[$url_name]['link_text'] ) ? $this->_urls[$url_name]['link_text'] : false;
1237:     }
1238: 
1239:   /**
1240:    * @param $url_name
1241:    *
1242:    * @return bool
1243:    */
1244:   function get_link_class( $url_name ) {
1245:     return isset( $this->_urls[$url_name]['link_class'] ) ? " class=\"{$this->_urls[$url_name]['link_class']}\"" : false;
1246:     }
1247: 
1248:   /**
1249:    * @param string $image_name
1250:    * @param string $image_url
1251:    * @param array $args
1252:    */
1253:   function register_image( $image_name, $image_url, $args = array() ) {
1254:     $args['image_name'] = $image_name;
1255:     $args['image_url'] = $image_url;
1256:     if ( ! isset( $args['url_vars'] ) )
1257:       $args['image_vars'] = false;
1258:     $this->_images[$image_name] = $args;
1259:   }
1260: 
1261:   /**
1262:    * @param string $image_name
1263:    *
1264:    * @return bool
1265:    */
1266:   function has_image( $image_name ) {
1267:     return isset( $this->_images[$image_name] );
1268:   }
1269: 
1270:   /**
1271:    * Get by name a previously registered image with optional variable value replacement
1272:    *
1273:    * @param             $image_name
1274:    * @param mixed|array $values
1275:      * @example:
1276:    *
1277:    *    $this->register_image( 'my_logo', 'my-logo.png' );
1278:    *    echo $this->my_logo_image_url;
1279:    *
1280:    *    $this->register_image( 'my_icon', '{icon_type}.png' );
1281:    *    echo $this->get_image_url( 'my_icon', 'pdf' );
1282:    *    echo $this->get_image_url( 'my_icon', array('pdf') );
1283:    *    echo $this->get_image_url( 'my_icon', array('icon_type' => 'pdf') );
1284:    *
1285:    * @return string
1286:    */
1287:   function get_image_url( $image_name, $values = array() ) {
1288:     if ( ! is_array( $values ) ) {
1289:       /**
1290:        * $values passed as additional function parameters instead of as single array of parameters.
1291:        */
1292:       $values = func_get_args();
1293:       array_shift( $values );
1294:     }
1295:     $image_url = $this->_images[$image_name]['image_url'];
1296:     if ( is_array( $values ) ) {
1297:       $vars = $this->_images[$image_name]['image_vars'];
1298:       if ( ! $vars ) {
1299:         preg_match_all( '#\{([^}]+)\}#', $image_url, $matches );
1300:         $vars = $matches[1];
1301:       }
1302:       if ( is_numeric( key($values) ) ) {
1303:         /**
1304:          * The $values array contains name/value pairs.
1305:          */
1306:         foreach( $values as $value ) {
1307:           $image_url = str_replace( "{{$vars[0]}}", $value, $image_url );
1308:         }
1309:       } else {
1310:         /**
1311:          * The $values array just contains values in same order as the vars specified in the image.
1312:          */
1313:         foreach( $vars as $name ) {
1314:           $image_url = str_replace( "{{$name}}", $values[$name], $image_url );
1315:         }
1316:       }
1317:     }
1318:     if ( ! preg_match( '#^https?//#', $image_url ) ) {
1319:       $image_url = plugins_url( "/images/{$image_url}", $this->plugin_file );
1320:     }
1321:     return $image_url;
1322:     }
1323: //  /**
1324: //   * @return bool
1325: //   */
1326: //  function is_authenticated() {
1327: //      return false;
1328: //  }
1329: 
1330:   /**
1331:    * Echo the current or specified form.
1332:    *
1333:    * @param bool|Sidecar_Form $form
1334:    *
1335:    * @return Sidecar_Form
1336:    */
1337:   function the_form( $form = false ) {
1338:     if ( ! $form )
1339:       $form = $this->get_current_form();
1340:     return $form->the_form();
1341:     }
1342: 
1343:   /**
1344:    * @param   array   $form
1345:    * @return  Sidecar_Form
1346:    */
1347:   function promote_form( $form ) {
1348:     /**
1349:      * @var array $form
1350:      */
1351:     $form_name = $form['form_name'];
1352:     $form['plugin'] = $this;
1353: 
1354:     if ( ! isset( $form['admin_page'] ) )
1355:       $form['admin_page'] = end( $this->_admin_pages );
1356: 
1357:     /**
1358:      * @var array|Sidecar_Form $form
1359:      */
1360:     $form = $this->_forms[$form_name] = new Sidecar_Form( $form_name, $form );
1361: 
1362:     $this->set_current_form( $form );
1363:     $this->initialize_form( $form );
1364:     $form->initialize();
1365:     return $form;
1366:   }
1367:   /**
1368:    * @param array $args
1369:    */
1370:   function the_form_section( $args ) {
1371:     if ( ! empty( $args['section']->section_text ) )
1372:       echo $args['section']->section_text;
1373:   }
1374: 
1375:   /**
1376:    * @param $args
1377:    *
1378:    * @return Sidecar_Field
1379:    */
1380:   function get_form_field( $field_name, $form_name ) {
1381:     /**
1382:      * @var Sidecar_Form $form
1383:      */
1384:     $form = $this->get_form( $form_name );
1385:     return $form ? $form->get_field( $field_name ) : false;
1386:   }
1387:   /**
1388:    * @param array $args
1389:    */
1390:   function get_form_field_html( $field_name, $form_name ) {
1391:     /**
1392:      * @var Sidecar_Field $field
1393:      */
1394:     $field = $this->get_form_field( $field_name, $form_name );
1395:     return $field->get_html();
1396:   }
1397: 
1398:   /**
1399:    * @param array $args
1400:    */
1401:   function the_form_field( $field_name, $form_name ) {
1402:     echo $this->get_form_field_html( $field_name, $form_name );
1403:   }
1404: 
1405:   /**
1406:    * @param string $shortcode_name
1407:    * @return bool
1408:    */
1409:   function has_shortcode( $shortcode_name ) {
1410:     return isset( $this->_shortcodes[$shortcode_name] );
1411:   }
1412:   /**
1413:    * @param   string|Sidecar_Form $form
1414:    * @return  array|Sidecar_Form
1415:    */
1416:   function get_form( $form ) {
1417:     /*
1418:      * Could be a string or already Sidecar_Form.
1419:      */
1420:     $form = is_string( $form ) && isset( $this->_forms[$form] ) ? $this->_forms[$form] : $form;
1421:     if ( is_array( $form ) )
1422:       $form = $this->promote_form( $form );
1423:     return is_object( $form ) ? $form : false;
1424:   }
1425:   /**
1426:    * @param string  $form_name
1427:    * @param array   $args
1428:    */
1429:   function add_form( $form_name, $args = array() ) {
1430:     $args['form_name'] = $form_name;
1431:     if ( ! isset( $args['requires_api'] ) && 'account' == $form_name )
1432:       $args['requires_api'] = true;
1433:     $this->_forms[$form_name] = $args;
1434:     $this->_plugin_settings->register_form_settings( $form_name );
1435:   }
1436: 
1437:   /**
1438:    * @param       $form_name
1439:    * @param array $args
1440:    */
1441: 
1442:   /**
1443:    * @param string  $form_name
1444:    * @param array   $args
1445:    * @return Sidecar_Field
1446:    */
1447:   function add_field( $form_name, $args = array() ) {
1448:     /**
1449:      * @var Sidecar_Form
1450:      */
1451:     $form = isset( $args['form'] ) ? $args['form'] : end( $this->_forms );
1452:     return $form->add_field( $form_name, $args );
1453:   }
1454: 
1455:   /**
1456:    * This function name will be more logical to people, but underneath it is 'has_grant()'
1457:    *
1458:    * @return bool
1459:    */
1460:   function is_authenticated() {
1461:     return $this->has_grant();
1462:   }
1463: 
1464:   /**
1465:    * Determines if the currently stored settings contain a grant to access the API.
1466:    *
1467:    * @return bool
1468:    */
1469:   function has_grant() {
1470:     $has_grant = false;
1471:     if ( $this->needs_grant() ) {
1472:       $auth_form_values = $this->get_auth_form()->get_settings_values();
1473:       $has_grant = $this->get_api()->is_grant( $auth_form_values );
1474:     }
1475:     return $has_grant;
1476:   }
1477: 
1478:   /**
1479:    * @return bool
1480:    */
1481:   function needs_grant() {
1482:     return $this->has_api();
1483:   }
1484: 
1485:   /**
1486:    * Get grant from the currently stored account settings
1487:    *
1488:    * @return array
1489:    */
1490:   function get_grant() {
1491:     /**
1492:      * @var RESTian_Auth_Provider_Base $auth_provider
1493:      */
1494:     $auth_provider = $this->get_api()->get_auth_provider();
1495:     return $auth_provider->extract_grant( $this->get_auth_form()->get_settings_values() );
1496:   }
1497: 
1498:   /**
1499:    * Get credentials from the currently stored account settings
1500:    *
1501:    * @return array
1502:    */
1503:   function get_credentials() {
1504:     /**
1505:      * @var RESTian_Auth_Provider_Base $auth_provider
1506:      */
1507:     $auth_provider = $this->get_api()->get_auth_provider();
1508:     return $auth_provider->extract_credentials( $this->get_auth_form()->get_settings_values() );
1509:   }
1510: 
1511:   /**
1512:    * @return Sidecar_Admin_Page
1513:    */
1514:   function get_current_admin_page() {
1515:     if ( ! isset( $this->_current_admin_page ) ) {
1516:       if ( ! isset( $_GET['page'] ) || ! is_admin() ) {
1517:         $this->_current_admin_page = false;
1518:       } else {
1519:         if ( isset( $_GET['tab'] ) ) {
1520:           $tab = $_GET['tab'];
1521:         } else {
1522:           $page = $this->get_admin_page($_GET['page']);
1523:           if ( $page && method_exists( $page, 'get_default_tab' ) ) {
1524:             $tab = $page->get_default_tab()->tab_slug;
1525:           }
1526:         };
1527:         /**
1528:          * If we have a $_GET['page'] then is should be "{$plugin_slug}-{$page_slug}"
1529:          * Scan through the array to find it.
1530:          */
1531:         foreach( array_keys( $this->_admin_pages ) as $admin_page_slug ) {
1532:           if ( "{$this->plugin_slug}-{$admin_page_slug}" == $_GET['page'] ) {
1533:             $this->_current_admin_page = $this->get_admin_page( $admin_page_slug );
1534:             break;
1535:           }
1536:         }
1537:       }
1538:     }
1539:     return $this->_current_admin_page;
1540:   }
1541: 
1542:   /**
1543:    * @param Sidecar_Admin_Page $current_admin_page
1544:    */
1545:   function set_current_admin_page( $current_admin_page ) {
1546:     $this->_current_admin_page = $current_admin_page;
1547:   }
1548: 
1549:   /**
1550:    * @return Sidecar_Form
1551:    */
1552:   function get_current_form() {
1553:     return $this->_current_form;
1554:   }
1555: 
1556:   /**
1557:    * @param Sidecar_Admin_Page $current_form
1558:    */
1559:   function set_current_form( $current_form ) {
1560:     $this->_current_form = $current_form;
1561:   }
1562: 
1563:   /**
1564:    * Capture values from form but cause update_option() to be bypassed. We'll update in the shutdown hook.
1565:    *
1566:    * @param array $new_value
1567:    * @param array $old_value
1568:    * @return array
1569:    */
1570:   function _pre_update_option( $new_value, $old_value ) {
1571:     /**
1572:      * This only going to be saving one form's worth of data yet the settings can have many forms, like:
1573:      *
1574:      *    $settings = array(
1575:      *      'form1' => array( ... ),
1576:      *      'form2' => array( ... ),
1577:      *      'form3' => array( ... ),
1578:      *    );
1579:      *
1580:      * So the next 3 lines grab all the old values of the other forms and uses the new values for this form.
1581:      */
1582:     if ( ! isset( $new_value['_sidecar_form_meta'] ) ) {
1583:       $return_value = $new_value;
1584:     } else {
1585:       $form_name = $new_value['_sidecar_form_meta']['form'];
1586:       $this->set_form_settings( $form_name, $new_value[$form_name] );
1587:       $return_value = $this->get_settings()->get_data();
1588:     }
1589: 
1590:     return $return_value;
1591:   }
1592:   /**
1593: 
1594:    * @todo Decide if trigger_error() is the best for error messages
1595:    */
1596:   function _activate() {
1597:     if ( ! $this->_initialized )
1598:       $this->initialize();
1599: 
1600:     if ( method_exists( $this, 'activate' ) )
1601:       $this->activate();
1602: 
1603:     global $wp_version;
1604:     if ( version_compare( $wp_version, $this->min_wp, '<' ) ) {
1605:       deactivate_plugins( basename( $this->plugin_file ) );
1606:       $msg = __( 'Your site needs to be running WordPress %s or later in order to use %s.', 'sidecar' );
1607:       trigger_error( sprintf( $msg, $this->min_wp, $this->plugin_title ), E_USER_ERROR );
1608:     } if ( version_compare( PHP_VERSION, $this->min_php, '<' ) ) {
1609:       deactivate_plugins( basename( $this->plugin_file ) );
1610:       $msg = __( 'Your site needs to be running PHP %s or later in order to use %s.', 'sidecar' );
1611:       trigger_error( sprintf( $msg, $this->min_php, $this->plugin_title ), E_USER_ERROR );
1612:     } else {
1613:       // @todo Add simplified support for cron when we see a use-case for it.
1614:       //if ( ! wp_next_scheduled( $this->cron_key ) ) {
1615:       //  wp_schedule_event( time(), $this->cron_recurrance, $this->cron_key );
1616:       //}
1617:       /*
1618:        * If we have existing settings and we are either upgrading or reactivating we
1619:        * previously had a _credentials element then reauthenticate and record that change.
1620:        */
1621:       if ( $this->has_api() ) {
1622:         $api = $this->get_api();
1623:         /**
1624:          * @var RESTian_Auth_Provider_Base $auth_provider
1625:          */
1626:         $auth_provider = $api->get_auth_provider();
1627:         $auth_form = $this->get_auth_form();
1628:         if ( ! $auth_form )
1629:           wp_die( __( 'There is no auth form configured. Call $admin_page->set_auth_form( $form_name ) inside initialize_admin_page( $admin_page ).', 'sidecar' ) );
1630: 
1631:         $account_settings = $auth_form->get_settings();
1632:         $credentials = $auth_provider->extract_credentials( $account_settings->get_values() );
1633:         $credentials = array_merge( $auth_provider->get_new_credentials(), $credentials );
1634:         $credentials = $auth_provider->prepare_credentials( $credentials );
1635: 
1636:         /**
1637:          * 'account' contains credentials and grant merged
1638:          */
1639:         if ( ! $auth_provider->is_credentials( $credentials ) ) {
1640:           /**
1641:            * Allow the auth provider to establish defaults in the grant if needed.
1642:            * This is an unusual need, but Lexity.com needed it.
1643:            */
1644:           $grant = $auth_provider->prepare_grant( $auth_provider->get_new_grant(), $credentials );
1645: 
1646:         } else {
1647:           /**
1648:            * Attempt to authenticate with available credentials
1649:            */
1650:           $response = $api->authenticate( $credentials );
1651:           /**
1652:            * If authenticated get the updated grant otherwise get an empty grant
1653:            */
1654:           $grant = $response->authenticated ? $response->grant : $auth_provider->get_new_grant();
1655: 
1656:           /**
1657:            * Allow the auth provider to establish defaults in the grant if needed.
1658:            * This is an unusual need, but Lexity.com needed it.
1659:            */
1660:           $grant = $auth_provider->prepare_grant( $grant, $credentials );
1661: 
1662:         }
1663:         /**
1664:          * Merge credentials and grant back into $settings['account']
1665:          */
1666:         $account_settings->set_values( array_merge( $credentials, $grant ) );
1667: 
1668:       }
1669: 
1670:       $settings = $this->get_settings();
1671:       $settings->installed_version = $this->plugin_version;
1672:       $settings->update_settings();
1673: 
1674:     }
1675:   }
1676: 
1677:   /**
1678:    *
1679:    */
1680:   function _initialize_admin() {
1681:     if ( ! $this->_admin_initialized ) {
1682:       if ( ! method_exists( $this, 'initialize_admin' ) ) {
1683:         trigger_error( __( 'Plugin must define a $plugin->initialize_admin() method..', 'sidecar' ) );
1684:         exit;
1685:       }
1686:       $this->initialize_admin();
1687: 
1688:       $this->_admin_initialized = true;
1689: 
1690:       if ( $this->_api )
1691:         $this->_api->maybe_set_grant( $this->get_grant() );
1692: 
1693:     }
1694:   }
1695:   /**
1696:    * @return Sidecar_Form
1697:    */
1698:   function get_auth_form() {
1699:     $this->_initialize_admin();
1700:     $auth_form = false;
1701:     /**
1702:      * @var Sidecar_Admin_Page $page
1703:      */
1704:     foreach( $this->_admin_pages as $page ) {
1705:       $this->initialize_admin_page( $page );
1706:       if ( $test = $page->get_auth_form() ) {
1707:         $auth_form = $test;
1708:         break;
1709:       }
1710:     }
1711:     return $auth_form;
1712:   }
1713: 
1714:   /**
1715:    * @param $property_name
1716:    * @return bool|string
1717:    */
1718:   function __get( $property_name ) {
1719:     $value = false;
1720:     if ( preg_match( '#^(.*?_(icon|image|photo))_url$#', $property_name, $match ) && $this->has_image( $match[1] ) ) {
1721:       $value = call_user_func( array( $this, "get_image_url" ), $match[1] );
1722:     } else if ( preg_match( '#^(.*?)_url$#', $property_name, $match ) && $this->has_url( $match[1] ) ) {
1723:       /**
1724:        * Allows magic property syntax for any registered URL
1725:        * @example: $this->foobar_url calls $this-get_url( 'foobar' )
1726:        * Enables embedding in a HEREDOC or other doublequoted string
1727:        * without requiring an intermediate variable.
1728:        */
1729:       $value = $this->get_url( $match[1] );
1730:     } else if ( preg_match( '#^(.*?)_link$#', $property_name, $match ) && $this->has_url( $match[1] ) ) {
1731:       /**
1732:        * Same kind of this as _url above.
1733:        */
1734:       $url = $this->get_url( $match[1] );
1735:       $link_text = $this->get_link_text( $match[1] );
1736:       $class = $this->get_link_class( $match[1] );
1737:       $value = "<a target=\"_blank\"{$class} href=\"{$url}\">{$link_text}</A>";
1738:     } else {
1739:       Sidecar::show_error( 'No property named %s on %s class.', $property_name, get_class( $this ) );
1740:     }
1741:     return $value;
1742:   }
1743: }
1744: 
API documentation generated by ApiGen 2.8.0